hippo-memory 1.70.0 → 1.71.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 +1 -1
- package/dist/agent-memories/claude-code.d.ts +4 -2
- package/dist/agent-memories/claude-code.js +14 -5
- 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/report.js +2 -1
- package/dist/agent-memories/sync.d.ts +4 -2
- package/dist/agent-memories/sync.js +10 -5
- package/dist/api/assemble.d.ts +7 -44
- package/dist/api/assemble.js +2 -22
- package/dist/api/audit.d.ts +5 -1
- package/dist/api/audit.js +9 -1
- package/dist/api/auth.d.ts +13 -22
- package/dist/api/auth.js +34 -24
- package/dist/api/auto-sleep.js +2 -2
- package/dist/api/availability.d.ts +2 -15
- package/dist/api/availability.js +2 -15
- package/dist/api/context-select.js +3 -4
- package/dist/api/context-types.d.ts +10 -35
- package/dist/api/context.d.ts +5 -22
- package/dist/api/context.js +21 -38
- package/dist/api/dormant.d.ts +5 -21
- package/dist/api/dormant.js +7 -26
- package/dist/api/drill-down.d.ts +7 -46
- package/dist/api/drill-down.js +5 -26
- package/dist/api/forget.d.ts +5 -24
- package/dist/api/forget.js +5 -28
- package/dist/api/graph.d.ts +8 -0
- package/dist/api/graph.js +7 -0
- package/dist/api/index.d.ts +2 -0
- package/dist/api/index.js +2 -0
- package/dist/api/instruction-detect.js +16 -5
- package/dist/api/ledger-db.d.ts +2 -1
- package/dist/api/ledger-db.js +5 -3
- package/dist/api/memories.d.ts +5 -0
- package/dist/api/memories.js +7 -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/predictions.d.ts +9 -0
- package/dist/api/predictions.js +18 -0
- package/dist/api/promote.d.ts +13 -17
- package/dist/api/promote.js +11 -19
- package/dist/api/prompt-hook.d.ts +4 -4
- package/dist/api/prompt-hook.js +10 -9
- package/dist/api/recall-cli.d.ts +55 -0
- package/dist/api/recall-cli.js +94 -0
- package/dist/api/recall-core.js +14 -6
- 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 +16 -9
- package/dist/api/recall-types.d.ts +64 -304
- package/dist/api/recall.d.ts +5 -33
- package/dist/api/recall.js +59 -60
- 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 +18 -1
- package/dist/api/remember.js +13 -1
- package/dist/api/sleep-run.d.ts +1 -3
- package/dist/api/sleep-run.js +17 -21
- package/dist/api/sleep.d.ts +9 -28
- package/dist/api/tokens.d.ts +3 -10
- package/dist/api/tokens.js +3 -10
- package/dist/api/types.d.ts +6 -29
- package/dist/api/types.js +4 -22
- package/dist/capture/command.d.ts +4 -12
- package/dist/capture/command.js +6 -22
- package/dist/capture/compact.d.ts +4 -15
- package/dist/capture/compact.js +16 -25
- package/dist/capture/compaction-record.js +101 -69
- package/dist/capture/compaction-spool.d.ts +1 -1
- package/dist/capture/compaction-spool.js +1 -1
- package/dist/capture/session-digest.d.ts +1 -1
- package/dist/capture/session-digest.js +13 -7
- package/dist/capture/transcript.d.ts +2 -10
- package/dist/capture/transcript.js +4 -18
- package/dist/cli/api-context.d.ts +3 -0
- package/dist/cli/api-context.js +6 -0
- package/dist/cli/audit-prune.d.ts +6 -39
- package/dist/cli/audit-prune.js +6 -39
- package/dist/cli/audit.d.ts +1 -1
- package/dist/cli/audit.js +33 -34
- package/dist/cli/auth.d.ts +2 -2
- package/dist/cli/auth.js +57 -39
- package/dist/cli/briefs.d.ts +4 -4
- package/dist/cli/briefs.js +18 -22
- package/dist/cli/card.d.ts +2 -2
- package/dist/cli/card.js +31 -32
- package/dist/cli/client.d.ts +7 -27
- package/dist/cli/client.js +24 -43
- package/dist/cli/compact-resume-payload.js +3 -2
- package/dist/cli/context.d.ts +1 -2
- package/dist/cli/context.js +12 -8
- package/dist/cli/continuity.d.ts +6 -6
- package/dist/cli/continuity.js +62 -64
- package/dist/cli/correction-latency.d.ts +2 -14
- package/dist/cli/correction-latency.js +2 -14
- package/dist/cli/curate.d.ts +13 -17
- package/dist/cli/curate.js +68 -92
- package/dist/cli/dag.d.ts +4 -4
- package/dist/cli/dag.js +33 -27
- package/dist/cli/decisions.d.ts +4 -4
- package/dist/cli/decisions.js +33 -47
- package/dist/cli/eval.js +15 -13
- package/dist/cli/exit.d.ts +6 -0
- package/dist/cli/exit.js +11 -0
- package/dist/cli/explain.d.ts +1 -2
- package/dist/cli/explain.js +6 -4
- package/dist/cli/flag-values.d.ts +6 -1
- package/dist/cli/flag-values.js +27 -10
- package/dist/cli/flags.d.ts +1 -0
- package/dist/cli/flags.js +1 -1
- 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/hook-runtime.d.ts +9 -27
- package/dist/cli/hook-runtime.js +15 -31
- package/dist/cli/init.d.ts +2 -2
- package/dist/cli/init.js +6 -12
- package/dist/cli/install-steps.js +3 -12
- package/dist/cli/last-sleep.d.ts +2 -1
- package/dist/cli/last-sleep.js +3 -0
- package/dist/cli/maintenance.d.ts +4 -4
- package/dist/cli/maintenance.js +14 -19
- package/dist/cli/object-verbs.js +4 -3
- package/dist/cli/owner-validation.d.ts +5 -28
- package/dist/cli/owner-validation.js +5 -28
- package/dist/cli/playbooks.d.ts +4 -4
- package/dist/cli/playbooks.js +23 -28
- package/dist/cli/print.d.ts +2 -5
- package/dist/cli/print.js +1 -4
- package/dist/cli/projects.d.ts +2 -2
- package/dist/cli/projects.js +1 -3
- package/dist/cli/provenance-coverage.d.ts +2 -6
- package/dist/cli/provenance-coverage.js +2 -6
- package/dist/cli/quality-repair-once.js +2 -2
- package/dist/cli/recall.d.ts +2 -4
- package/dist/cli/recall.js +55 -131
- package/dist/cli/refine-llm.d.ts +5 -31
- package/dist/cli/refine-llm.js +9 -36
- package/dist/cli/remember.d.ts +3 -3
- package/dist/cli/remember.js +122 -180
- package/dist/cli/scheduler.d.ts +2 -17
- package/dist/cli/scheduler.js +12 -18
- package/dist/cli/serve.js +21 -12
- package/dist/cli/session-hooks.d.ts +10 -18
- package/dist/cli/session-hooks.js +112 -113
- package/dist/cli/setup.d.ts +4 -4
- package/dist/cli/setup.js +19 -21
- package/dist/cli/shared.d.ts +9 -23
- package/dist/cli/shared.js +9 -32
- package/dist/cli/slack.d.ts +2 -2
- package/dist/cli/slack.js +19 -23
- package/dist/cli/sleep.d.ts +3 -2
- package/dist/cli/sleep.js +15 -16
- package/dist/cli/status.d.ts +9 -12
- package/dist/cli/status.js +55 -98
- 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 +66 -89
- package/dist/cli/usage.js +11 -5
- package/dist/cli.d.ts +66 -448
- package/dist/cli.js +87 -263
- package/dist/connectors/github/backfill.d.ts +5 -25
- package/dist/connectors/github/backfill.js +14 -49
- 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 +12 -29
- package/dist/connectors/github/ingest.d.ts +2 -6
- package/dist/connectors/github/ingest.js +4 -10
- 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 +2 -16
- package/dist/connectors/github/webhook.js +6 -22
- 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 +2 -19
- package/dist/connectors/slack/ingest.d.ts +2 -3
- package/dist/connectors/slack/ingest.js +2 -3
- 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 +2 -19
- package/dist/connectors/slack/webhook.js +5 -26
- package/dist/connectors/slack/workspaces.d.ts +4 -24
- package/dist/connectors/slack/workspaces.js +4 -24
- package/dist/consolidate/conflicts.js +6 -14
- 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 +14 -75
- package/dist/consolidate/decay.js +7 -19
- package/dist/consolidate/dedupe.d.ts +6 -52
- package/dist/consolidate/dedupe.js +13 -71
- 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 +4 -21
- 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 +16 -104
- package/dist/consolidate/merge.js +10 -31
- package/dist/consolidate/merged-row.js +2 -1
- package/dist/consolidate/physics-pass.js +1 -3
- package/dist/consolidate/replay.d.ts +6 -38
- package/dist/consolidate/replay.js +14 -52
- package/dist/consolidate/run.js +1 -1
- package/dist/consolidate/sleep.d.ts +1 -8
- package/dist/consolidate/sleep.js +5 -17
- package/dist/consolidate/trace.d.ts +3 -18
- package/dist/consolidate/trace.js +3 -18
- package/dist/consolidate/traces.js +6 -22
- package/dist/core/ablation.d.ts +4 -91
- package/dist/core/ablation.js +4 -91
- package/dist/core/ambient.d.ts +1 -8
- package/dist/core/ambient.js +1 -8
- 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 +8 -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 +13 -14
- package/dist/core/handoff.d.ts +1 -7
- package/dist/core/handoff.js +1 -7
- package/dist/core/memory-quality.js +1 -1
- package/dist/core/memory.d.ts +41 -164
- package/dist/core/memory.js +51 -221
- 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 +59 -122
- package/dist/core/project-identity.d.ts +17 -58
- package/dist/core/project-identity.js +17 -38
- 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/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 +1 -1
- package/dist/db/busy.js +17 -8
- package/dist/db/connect.d.ts +1 -0
- package/dist/db/connect.js +3 -3
- package/dist/db/migrate.js +3 -3
- 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/open.d.ts +2 -0
- package/dist/db/open.js +17 -1
- package/dist/db/physics-state.d.ts +6 -22
- package/dist/db/physics-state.js +25 -36
- package/dist/db/raw-archive-mirror-cleanup.d.ts +2 -17
- package/dist/db/raw-archive-mirror-cleanup.js +2 -17
- package/dist/db/vector-store.d.ts +2 -0
- package/dist/db/vector-store.js +8 -1
- package/dist/doctor.js +74 -19
- package/dist/entry/session-text.js +2 -1
- 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 +74 -34
- package/dist/eval/eval.d.ts +6 -24
- package/dist/eval/eval.js +7 -34
- package/dist/eval/judgment.d.ts +0 -3
- package/dist/eval/judgment.js +19 -11
- package/dist/extensions/openclaw-plugin/index.js +1 -1
- package/dist/graph/extract.d.ts +5 -27
- package/dist/graph/extract.js +7 -33
- package/dist/graph/recall.d.ts +7 -20
- package/dist/graph/recall.js +14 -76
- 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 +11 -39
- package/dist/hooks/codex-wrapper.js +2 -2
- 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 +18 -57
- package/dist/hooks/opencode.d.ts +0 -38
- package/dist/hooks/opencode.js +9 -76
- package/dist/hooks/shared.d.ts +8 -17
- package/dist/hooks/shared.js +29 -20
- package/dist/importers/core.d.ts +12 -37
- package/dist/importers/core.js +9 -27
- 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 +7 -35
- package/dist/importers/vault.d.ts +2 -8
- package/dist/importers/vault.js +25 -81
- package/dist/index.d.ts +6 -2
- package/dist/index.js +6 -2
- package/dist/learn/autolearn.d.ts +14 -41
- package/dist/learn/autolearn.js +14 -41
- package/dist/learn/extract.js +2 -4
- package/dist/learn/forward-claim-detector.d.ts +6 -30
- package/dist/learn/forward-claim-detector.js +14 -66
- package/dist/learn/invalidation.d.ts +6 -18
- package/dist/learn/invalidation.js +9 -26
- package/dist/mcp/admin-tools.js +3 -5
- package/dist/mcp/memory-tools.js +4 -9
- package/dist/mcp/protocol.d.ts +8 -26
- package/dist/mcp/protocol.js +3 -6
- package/dist/mcp/recall-tools.js +3 -3
- package/dist/mcp/request.d.ts +3 -11
- package/dist/mcp/request.js +10 -26
- 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 +48 -12
- package/dist/mcp/tools.js +5 -3
- package/dist/objects/customer-notes.d.ts +7 -42
- package/dist/objects/customer-notes.js +7 -48
- package/dist/objects/decisions.d.ts +12 -50
- package/dist/objects/decisions.js +15 -53
- package/dist/objects/incidents.d.ts +7 -38
- package/dist/objects/incidents.js +7 -41
- package/dist/objects/policies.d.ts +11 -89
- package/dist/objects/policies.js +11 -98
- package/dist/objects/processes.d.ts +7 -52
- package/dist/objects/processes.js +7 -61
- package/dist/objects/project-briefs.d.ts +11 -55
- package/dist/objects/project-briefs.js +14 -75
- package/dist/objects/skills.d.ts +7 -42
- package/dist/objects/skills.js +8 -54
- package/dist/postinstall.js +6 -19
- package/dist/predictions/planning-fallacy.d.ts +11 -34
- package/dist/rerankers/clef.js +3 -31
- package/dist/rerankers/cross-encoder.js +4 -6
- package/dist/rerankers/jev.js +9 -6
- package/dist/rerankers/llm.d.ts +2 -11
- package/dist/rerankers/llm.js +14 -14
- package/dist/rerankers/outage-warning.d.ts +2 -1
- package/dist/rerankers/outage-warning.js +2 -1
- 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/boosts.d.ts +0 -2
- package/dist/search/boosts.js +2 -2
- package/dist/search/goal-boost.d.ts +23 -0
- package/dist/search/goal-boost.js +90 -0
- package/dist/search/hybrid.d.ts +2 -0
- 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.js +11 -8
- package/dist/search/rrf.d.ts +7 -35
- package/dist/search/rrf.js +5 -30
- package/dist/search/vector.d.ts +7 -0
- package/dist/search/vector.js +35 -5
- package/dist/server/auth.d.ts +9 -28
- package/dist/server/auth.js +32 -25
- package/dist/server/boot.d.ts +2 -21
- package/dist/server/boot.js +63 -96
- package/dist/server/client-ip.d.ts +3 -19
- package/dist/server/client-ip.js +5 -28
- package/dist/server/defaults.d.ts +3 -0
- package/dist/server/defaults.js +4 -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 +10 -44
- 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 +9 -17
- package/dist/server/route-table.js +42 -49
- package/dist/server/routes/admin.js +22 -28
- package/dist/server/routes/customer-notes.js +7 -12
- package/dist/server/routes/decisions.js +10 -17
- package/dist/server/routes/incidents.js +12 -20
- package/dist/server/routes/memories.js +12 -32
- package/dist/server/routes/object-routes.d.ts +0 -5
- package/dist/server/routes/object-routes.js +7 -15
- package/dist/server/routes/policies.js +10 -18
- package/dist/server/routes/predictions.js +13 -25
- package/dist/server/routes/processes.js +16 -26
- package/dist/server/routes/project-briefs.js +9 -16
- package/dist/server/routes/recall.js +10 -35
- package/dist/server/routes/skills.js +8 -18
- package/dist/server/server-detect.d.ts +9 -53
- package/dist/server/server-detect.js +24 -90
- package/dist/server/sleep-offload.js +4 -1
- package/dist/server/types.d.ts +12 -10
- package/dist/server/validation.d.ts +3 -1
- package/dist/server/validation.js +10 -23
- package/dist/server.d.ts +2 -2
- package/dist/server.js +3 -2
- 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 +80 -0
- package/dist/sharing/project-merge.d.ts +4 -5
- package/dist/sharing/project-merge.js +9 -7
- package/dist/sharing/scope.d.ts +4 -11
- package/dist/sharing/scope.js +4 -11
- package/dist/sharing/search-both.d.ts +60 -0
- package/dist/sharing/search-both.js +138 -0
- package/dist/sharing/share.d.ts +38 -0
- package/dist/sharing/share.js +226 -0
- package/dist/store/ambient.js +4 -9
- package/dist/store/audit-event.d.ts +4 -12
- package/dist/store/audit-event.js +4 -12
- package/dist/store/audit.d.ts +2 -5
- package/dist/store/audit.js +4 -12
- package/dist/store/auth.d.ts +6 -6
- package/dist/store/auth.js +10 -9
- package/dist/store/candidates.d.ts +20 -3
- package/dist/store/candidates.js +77 -48
- 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/conflicts.d.ts +4 -21
- package/dist/store/conflicts.js +71 -150
- package/dist/store/connectors/github.d.ts +10 -10
- package/dist/store/connectors/github.js +10 -10
- package/dist/store/dashboard-reads.js +4 -8
- package/dist/store/delete-and-batch.d.ts +11 -31
- package/dist/store/delete-and-batch.js +48 -64
- package/dist/store/delivery-recorder.d.ts +8 -4
- package/dist/store/delivery-recorder.js +25 -15
- package/dist/store/diagnostics.js +6 -13
- package/dist/store/dormant.d.ts +7 -29
- package/dist/store/dormant.js +15 -56
- package/dist/store/embeddings/index.d.ts +11 -80
- package/dist/store/embeddings/index.js +71 -207
- package/dist/store/embeddings/local.js +15 -32
- package/dist/store/embeddings/provider.d.ts +16 -53
- package/dist/store/embeddings/provider.js +41 -69
- package/dist/store/entry-reads.d.ts +21 -57
- package/dist/store/entry-reads.js +65 -126
- package/dist/store/entry-row.d.ts +6 -35
- package/dist/store/entry-row.js +9 -37
- package/dist/store/entry-writes.d.ts +10 -16
- package/dist/store/entry-writes.js +17 -35
- package/dist/store/goals.d.ts +3 -40
- package/dist/store/goals.js +21 -160
- 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 +20 -58
- 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 +6 -31
- package/dist/store/graph-writes.js +13 -37
- package/dist/store/half-life.d.ts +2 -1
- package/dist/store/half-life.js +14 -16
- package/dist/store/handoffs.d.ts +5 -11
- package/dist/store/handoffs.js +16 -34
- package/dist/store/index-and-stats.d.ts +6 -19
- package/dist/store/index-and-stats.js +34 -82
- package/dist/store/markdown.d.ts +2 -6
- package/dist/store/markdown.js +21 -20
- package/dist/store/mirrors.d.ts +2 -6
- package/dist/store/mirrors.js +14 -23
- package/dist/store/open.js +6 -13
- 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 +82 -79
- package/dist/store/predictions.d.ts +11 -51
- package/dist/store/predictions.js +38 -119
- package/dist/{cli → store}/quality-repair.js +15 -31
- package/dist/store/raw-archive.d.ts +4 -16
- package/dist/store/raw-archive.js +14 -29
- package/dist/store/recall-scope.d.ts +19 -79
- package/dist/store/recall-scope.js +21 -82
- package/dist/store/recall-trace.d.ts +10 -77
- package/dist/store/recall-trace.js +19 -95
- package/dist/store/rejected-values.js +7 -19
- package/dist/store/rejection.d.ts +12 -58
- package/dist/store/rejection.js +13 -65
- 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 +5 -0
- package/dist/store/rule-sql.js +37 -0
- package/dist/store/search-rows.d.ts +7 -38
- package/dist/store/search-rows.js +14 -50
- package/dist/store/sessions.d.ts +10 -47
- package/dist/store/sessions.js +17 -73
- package/dist/store/sqlite/connector-events-group.js +2 -2
- package/dist/store/sqlite/entry-writes-group.js +2 -2
- package/dist/store/sqlite/error-codec.js +4 -2
- package/dist/store/sqlite/executor.d.ts +2 -1
- package/dist/store/sqlite/executor.js +2 -1
- package/dist/store/sqlite/local.d.ts +3 -3
- package/dist/store/sqlite/local.js +1 -0
- package/dist/store/sqlite/object-rows.js +6 -3
- package/dist/store/sqlite/objects-group.js +4 -2
- package/dist/store/sqlite/quarantine-group.js +2 -2
- package/dist/store/sqlite/store.d.ts +1 -4
- package/dist/store/sqlite/store.js +2 -3
- 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 +10 -62
- package/dist/store/summaries.js +34 -146
- 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 -43
- package/dist/store/token-ledger.js +19 -50
- package/dist/store/usage-reports.js +5 -13
- package/dist/store/vector-index.d.ts +46 -0
- package/dist/store/vector-index.js +170 -0
- package/dist/store/working-memory.d.ts +6 -25
- package/dist/store/working-memory.js +18 -52
- package/dist/store/yaml.d.ts +1 -5
- package/dist/store/yaml.js +2 -6
- package/dist/support-bundle.js +7 -5
- package/dist/trust/reject-flow.js +1 -3
- package/dist/util/anthropic-messages.js +14 -2
- 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/crash-handlers.d.ts +8 -1
- package/dist/util/crash-handlers.js +52 -10
- package/dist/util/env.d.ts +13 -6
- package/dist/util/env.js +14 -7
- package/dist/util/home-path.d.ts +2 -1
- package/dist/util/home-path.js +4 -2
- package/dist/util/http-retry.d.ts +3 -1
- package/dist/util/http-retry.js +26 -5
- package/dist/util/log.d.ts +3 -2
- package/dist/util/log.js +25 -9
- package/dist/util/request-scope.d.ts +2 -1
- package/dist/util/request-scope.js +2 -1
- package/dist/util/secret-detect.d.ts +5 -30
- package/dist/util/secret-detect.js +13 -39
- package/dist/util/stored-json.d.ts +1 -1
- package/dist/util/stored-json.js +1 -1
- 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/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 +1 -1
- package/dist/connectors/github/cli-impl.d.ts +0 -25
- package/dist/sharing/shared.d.ts +0 -187
- package/dist/sharing/shared.js +0 -563
- package/dist/store/vector-writes.d.ts +0 -13
- package/dist/store/vector-writes.js +0 -89
- /package/dist/{cli → store}/quality-repair.d.ts +0 -0
package/dist/server/boot.js
CHANGED
|
@@ -14,17 +14,18 @@ import { createRateLimiter } from './rate-limit.js';
|
|
|
14
14
|
import { RecallContractError } from '../api/index.js';
|
|
15
15
|
import { handleSlackEventsWebhook } from '../connectors/slack/webhook.js';
|
|
16
16
|
import { handleGitHubEventsWebhook } from '../connectors/github/webhook.js';
|
|
17
|
-
import {
|
|
17
|
+
import { BodyTimeoutError, BodyTooLargeError, closeAfterReply, DeadlineExceededError, HttpError, JSON_HEADERS, sendJson } from '../util/http-util.js';
|
|
18
18
|
import { workerCounts } from '../store/sqlite/executor-counts.js';
|
|
19
|
-
import {
|
|
19
|
+
import { isLocalCaller, LIMITER_MAX_KEYS } from './auth.js';
|
|
20
20
|
import { enforceRateLimit, warnIfClientIpHeaderUnpinned } from './client-ip.js';
|
|
21
21
|
import { answerAtDeadline, handlerDeadlineCount, isAbandoned, requestDeadlineFor } from './deadline.js';
|
|
22
|
-
import { drainAndClose } from './lifecycle.js';
|
|
22
|
+
import { DEFAULT_SHUTDOWN_DRAIN_MS, drainAndClose, setKeepAliveTimeouts, shutdownBoundMs } from './lifecycle.js';
|
|
23
23
|
import { readyProbeFor } from './ready.js';
|
|
24
|
-
import {
|
|
24
|
+
import { DEFAULT_SERVER_HOST, DEFAULT_SERVER_PORT } from './defaults.js';
|
|
25
|
+
import { installCrashHandlers, installSignalHandlers } from '../util/crash-handlers.js';
|
|
25
26
|
import { handleMcpPost, handleMcpStream } from './mcp-http.js';
|
|
26
27
|
import { MCP_PROJECT_SCOPED_HEADER } from '../core/project-identity.js';
|
|
27
|
-
import { logRequestFailure, noteAccess, openRequest, parseRequest, rejectEncodedSlash, replyFor, sendError } from './request.js';
|
|
28
|
+
import { accessRouteOf, logRequestFailure, noteAccess, openRequest, parseRequest, rejectEncodedSlash, replyFor, sendError } from './request.js';
|
|
28
29
|
import { createListener, warnIfCleartext } from './tls.js';
|
|
29
30
|
import { assertAddonRoutes, assertPublicJson, dispatchAddonRoute, dispatchPublicJson, dispatchV1Route, isPublicRoute } from './route-table.js';
|
|
30
31
|
// server.address() returns AddressInfo once a TCP socket is bound; null before
|
|
@@ -32,10 +33,8 @@ import { assertAddonRoutes, assertPublicJson, dispatchAddonRoute, dispatchPublic
|
|
|
32
33
|
function isAddressInfo(a) {
|
|
33
34
|
return a !== null && typeof a !== 'string';
|
|
34
35
|
}
|
|
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.
|
|
36
|
+
// Pinned at module load, bumped with package.json on releases; reading package.json synchronously here would couple the daemon to its install path,
|
|
37
|
+
// which tests that mkdtemp a hippoRoot avoid.
|
|
39
38
|
const VERSION = PACKAGE_VERSION;
|
|
40
39
|
const LOOPBACK_HOSTS = new Set(['127.0.0.1', '::1', 'localhost']);
|
|
41
40
|
async function handleRequest(req, res, opts, options) {
|
|
@@ -61,7 +60,8 @@ async function handleRequest(req, res, opts, options) {
|
|
|
61
60
|
const routeRequest = { req, res, opts, query };
|
|
62
61
|
if (await runWithRequestStores(() => dispatchScopedRoute(routeRequest, method, path), { busyWaitMs: SERVER_DB_WAIT_MS }))
|
|
63
62
|
return;
|
|
64
|
-
// A scope of its own, which the heartbeat timer keeps after it closes, so the key check and every
|
|
63
|
+
// A scope of its own, which the heartbeat timer keeps after it closes, so the key check and every
|
|
64
|
+
// heartbeat wait the server's lock wait. Store-ready: the stream only authenticates, through the port.
|
|
65
65
|
if (method === 'GET' && path === '/mcp/stream') {
|
|
66
66
|
noteAccess(req, { route: path });
|
|
67
67
|
await runWithRequestStores(() => handleMcpStream(req, res, opts, streamSlots), { busyWaitMs: SERVER_DB_WAIT_MS });
|
|
@@ -114,11 +114,9 @@ async function dispatchScopedRoute(r, method, path) {
|
|
|
114
114
|
return false;
|
|
115
115
|
}
|
|
116
116
|
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)) {
|
|
117
|
+
// 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,
|
|
118
|
+
// gets liveness only: the version would fingerprint the build and the pid is noise. Platform health checks only need the 200.
|
|
119
|
+
if (isLocalCaller(req)) {
|
|
122
120
|
sendJson(res, 200, {
|
|
123
121
|
ok: true,
|
|
124
122
|
version: VERSION,
|
|
@@ -135,7 +133,8 @@ function sendHealth(req, res, startedAt) {
|
|
|
135
133
|
sendJson(res, 200, { ok: true });
|
|
136
134
|
}
|
|
137
135
|
}
|
|
138
|
-
/** Readiness: one cheap read on the served store under the server's lock wait. /health stays
|
|
136
|
+
/** Readiness: one cheap read on the served store under the server's lock wait. /health stays
|
|
137
|
+
* liveness only, so a probe can tell a store that does not answer from a dead process. */
|
|
139
138
|
async function sendReady(res, store) {
|
|
140
139
|
const { readiness } = store;
|
|
141
140
|
if (readiness === undefined) {
|
|
@@ -156,10 +155,8 @@ function assertBindable(host) {
|
|
|
156
155
|
}
|
|
157
156
|
}
|
|
158
157
|
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.
|
|
158
|
+
// Refuse to start if a live hippo server already serves this hippoRoot (detectServer probes the recorded /health and unlinks a stale pidfile):
|
|
159
|
+
// a concurrent `hippo serve` would race for the port and clobber the pidfile.
|
|
163
160
|
const existing = await detectServer(hippoRoot);
|
|
164
161
|
if (existing) {
|
|
165
162
|
throw new Error(`hippo serve: already running on port ${existing.port} (pid ${existing.pid}). ` +
|
|
@@ -175,10 +172,8 @@ function bootRateLimiter(perAddress) {
|
|
|
175
172
|
return undefined;
|
|
176
173
|
if (perAddress !== undefined)
|
|
177
174
|
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).
|
|
175
|
+
// 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().
|
|
176
|
+
// A non-positive or non-finite value disables limiting (the opt-out knob).
|
|
182
177
|
const v1Rps = Number(envV1Rps() ?? 20);
|
|
183
178
|
return Number.isFinite(v1Rps) && v1Rps > 0 ? limiterFor({ ratePerSec: v1Rps, burst: v1Rps * 2 }) : undefined;
|
|
184
179
|
}
|
|
@@ -218,8 +213,7 @@ function createStoreHolder(hippoRoot, store) {
|
|
|
218
213
|
if (heldDb || stopHolding || !existsSync(getHippoDbPath(hippoRoot)))
|
|
219
214
|
return;
|
|
220
215
|
try {
|
|
221
|
-
// The 'finish' listener can fire inside a request scope
|
|
222
|
-
// and inside the block of a `loop: 'off'` route, which would refuse the open.
|
|
216
|
+
// The 'finish' listener can fire inside a request scope (closing this connection with the request) and inside a `loop: 'off'` block (refusing the open).
|
|
223
217
|
// 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
218
|
heldDb = outsideSqliteOffLoop(() => outsideRequestStores(() => openHippoDb(hippoRoot, { busyWaitMs: SERVER_DB_WAIT_MS })));
|
|
225
219
|
checkpointer = startWalCheckpointer(getHippoDbPath(hippoRoot));
|
|
@@ -281,9 +275,23 @@ function replyWithFailure(req, res, err, requestId) {
|
|
|
281
275
|
sendError(res, mapped.status, mapped.message);
|
|
282
276
|
}
|
|
283
277
|
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
|
-
|
|
278
|
+
// 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.
|
|
279
|
+
function logLateFailure(req, err, requestId) {
|
|
280
|
+
const fields = { requestId, method: req.method ?? 'GET', route: accessRouteOf(req), failureStatus: replyFor(err).status };
|
|
281
|
+
log.warn('request failed after its deadline reply', { ...fields, errorClass: errorFields(err).errorClass });
|
|
282
|
+
}
|
|
283
|
+
// A handler that ends after the 504 finds the response finished, so sending its own reply throws this: its work ran to the end.
|
|
284
|
+
function isDroppedLateReply(err) {
|
|
285
|
+
return err instanceof Error && 'code' in err && err.code === 'ERR_HTTP_HEADERS_SENT';
|
|
286
|
+
}
|
|
287
|
+
/** A GET only reads; every other method this server routes can write. */
|
|
288
|
+
const isRead = (method) => method === 'GET';
|
|
289
|
+
// 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.
|
|
290
|
+
function logLateFinish(req, requestId) {
|
|
291
|
+
const method = req.method ?? 'GET';
|
|
292
|
+
if (isRead(method))
|
|
293
|
+
return;
|
|
294
|
+
log.info('request finished after its deadline reply; its own reply was dropped', { requestId, method, route: accessRouteOf(req) });
|
|
287
295
|
}
|
|
288
296
|
function replyOrClose(req, res, err, requestId) {
|
|
289
297
|
try {
|
|
@@ -291,7 +299,7 @@ function replyOrClose(req, res, err, requestId) {
|
|
|
291
299
|
}
|
|
292
300
|
catch (replyErr) {
|
|
293
301
|
// 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)}
|
|
302
|
+
log.error(`serve: failure reply not sent, socket closed: ${errorMessage(replyErr)}`, errorFields(replyErr));
|
|
295
303
|
res.destroy();
|
|
296
304
|
}
|
|
297
305
|
}
|
|
@@ -303,24 +311,19 @@ function answerRequest(req, res, handle, slowWarnMs) {
|
|
|
303
311
|
runWithRequestId(requestId, () => {
|
|
304
312
|
if (deadline)
|
|
305
313
|
answerAtDeadline(res, deadline, () => replyOrClose(req, res, new DeadlineExceededError(HANDLER_LATE), requestId));
|
|
306
|
-
handle().
|
|
314
|
+
handle().then(() => {
|
|
307
315
|
if (isAbandoned(res))
|
|
308
|
-
|
|
309
|
-
|
|
316
|
+
logLateFinish(req, requestId);
|
|
317
|
+
}, (err) => {
|
|
318
|
+
if (!isAbandoned(res))
|
|
310
319
|
replyOrClose(req, res, err, requestId);
|
|
320
|
+
else if (isDroppedLateReply(err))
|
|
321
|
+
logLateFinish(req, requestId);
|
|
322
|
+
else
|
|
323
|
+
logLateFailure(req, err, requestId);
|
|
311
324
|
});
|
|
312
325
|
}, deadline);
|
|
313
326
|
}
|
|
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
327
|
function listenOn(server, port, host) {
|
|
325
328
|
return new Promise((resolve, reject) => {
|
|
326
329
|
const onError = (err) => {
|
|
@@ -338,57 +341,23 @@ function listenOn(server, port, host) {
|
|
|
338
341
|
server.listen(port, host);
|
|
339
342
|
});
|
|
340
343
|
}
|
|
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'); });
|
|
344
|
+
function exitOnSignalOrCrash(stop, drainMs) {
|
|
345
|
+
const shutdown = { run: stop, boundMs: shutdownBoundMs(drainMs) };
|
|
346
|
+
installSignalHandlers('serve', shutdown);
|
|
347
|
+
installCrashHandlers('serve', shutdown);
|
|
359
348
|
}
|
|
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
|
-
*/
|
|
349
|
+
/** Boots the HTTP daemon on host:port and writes the pidfile under hippoRoot; refuses non-loopback hosts unless HIPPO_REQUIRE_AUTH=1.
|
|
350
|
+
* Use port: 0 in tests for an ephemeral port. See docs/ARCHITECTURE.md#srcserverbootts. */
|
|
381
351
|
export async function serve(opts) {
|
|
382
|
-
const host = opts.host ??
|
|
383
|
-
const requestedPort = opts.port ?? Number(envPort() ??
|
|
352
|
+
const host = opts.host ?? DEFAULT_SERVER_HOST;
|
|
353
|
+
const requestedPort = opts.port ?? Number(envPort() ?? DEFAULT_SERVER_PORT);
|
|
384
354
|
const routes = frozenAddonRoutes(opts.routes);
|
|
385
355
|
const publicJsonBodies = assertPublicJson(opts.publicJson ?? {});
|
|
386
356
|
const limiters = bootLimiters(opts.rateLimits);
|
|
387
357
|
assertBindable(host);
|
|
388
358
|
await assertNoLiveServer(opts.hippoRoot);
|
|
389
|
-
//
|
|
390
|
-
//
|
|
391
|
-
// can match the two and prove a pid-reusing impostor is not the real server.
|
|
359
|
+
// 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
|
|
360
|
+
// this server.
|
|
392
361
|
const startedAt = new Date().toISOString();
|
|
393
362
|
// Open /mcp/stream count per client key, so the cap is per server rather than per process.
|
|
394
363
|
const streamSlots = new Map();
|
|
@@ -414,15 +383,15 @@ export async function serve(opts) {
|
|
|
414
383
|
await served.store.close();
|
|
415
384
|
await holder.release();
|
|
416
385
|
};
|
|
417
|
-
if (opts.handleSignals)
|
|
418
|
-
|
|
419
|
-
installCrashHandlers('serve', stop);
|
|
420
|
-
}
|
|
386
|
+
if (opts.handleSignals)
|
|
387
|
+
exitOnSignalOrCrash(stop, opts.shutdownDrainMs ?? DEFAULT_SHUTDOWN_DRAIN_MS);
|
|
421
388
|
return { port: actualPort, url, stop, server };
|
|
422
389
|
}
|
|
423
390
|
function frozenAddonRoutes(addonRoutes) {
|
|
424
391
|
// 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
|
|
392
|
+
const routes = Object.freeze((addonRoutes ?? []).map(({ path, handler, storeReady }) => Object.freeze(storeReady === undefined
|
|
393
|
+
? { path, handler }
|
|
394
|
+
: { path, handler, storeReady })));
|
|
426
395
|
assertAddonRoutes(routes);
|
|
427
396
|
return routes;
|
|
428
397
|
}
|
|
@@ -456,10 +425,8 @@ async function listenAndDescribe(server, port, host, tls) {
|
|
|
456
425
|
return { port: actualPort, url };
|
|
457
426
|
}
|
|
458
427
|
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.
|
|
428
|
+
// 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
429
|
removePidfileIfOwned(opts.hippoRoot, { pid: process.pid, startedAt });
|
|
463
|
-
await drainAndClose(server, inflight, opts.shutdownDrainMs ??
|
|
430
|
+
await drainAndClose(server, inflight, opts.shutdownDrainMs ?? DEFAULT_SHUTDOWN_DRAIN_MS);
|
|
464
431
|
}
|
|
465
432
|
//# sourceMappingURL=boot.js.map
|
|
@@ -1,25 +1,9 @@
|
|
|
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;
|
package/dist/server/client-ip.js
CHANGED
|
@@ -5,25 +5,9 @@ import { log } from '../util/log.js';
|
|
|
5
5
|
import { HttpError } from '../util/http-util.js';
|
|
6
6
|
const IPV6_PREFIX_GROUPS = 4;
|
|
7
7
|
/**
|
|
8
|
-
* Rate-limit key
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* carries the proxy's address, so per-IP buckets collapse into one global
|
|
12
|
-
* bucket that unauthenticated traffic can drain before auth runs. Set
|
|
13
|
-
* HIPPO_CLIENT_IP_HEADER to the header the proxy stamps with the real
|
|
14
|
-
* client address (fly-client-ip on Fly, which the edge always overwrites)
|
|
15
|
-
* to key buckets per client instead.
|
|
16
|
-
*
|
|
17
|
-
* Only set this when a trusted proxy fronts EVERY request: a directly
|
|
18
|
-
* reachable server honoring the header would let clients mint a fresh
|
|
19
|
-
* bucket per request and bypass the limiter entirely.
|
|
20
|
-
*
|
|
21
|
-
* HIPPO_TRUSTED_PROXIES (comma-separated IPs or CIDRs) pins which peers count
|
|
22
|
-
* as that proxy: the header is read only when the socket peer is listed, and
|
|
23
|
-
* listed hops are skipped. In a comma-joined chain (X-Forwarded-For) the key is
|
|
24
|
-
* the rightmost hop no trusted proxy added; entries to its left are whatever
|
|
25
|
-
* the client sent, so they never pick the bucket.
|
|
26
|
-
*/
|
|
8
|
+
* Rate-limit key: the socket's remote address, which behind a TLS proxy collapses into one bucket unauthenticated traffic can drain.
|
|
9
|
+
* 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.
|
|
10
|
+
* 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. */
|
|
27
11
|
export function clientIpForRateLimit(req) {
|
|
28
12
|
const socketIp = req.socket.remoteAddress ?? 'unknown';
|
|
29
13
|
const header = envClientIpHeader();
|
|
@@ -101,15 +85,8 @@ function isTrustedProxy(list, ip) {
|
|
|
101
85
|
return family !== 0 && list.check(bare, family === 6 ? 'ipv6' : 'ipv4');
|
|
102
86
|
}
|
|
103
87
|
export function enforceRateLimit(req, path, limiter) {
|
|
104
|
-
// Per-IP rate limit on /v1/* and /mcp* to bound api-key-id enumeration
|
|
105
|
-
// (
|
|
106
|
-
// here lands in the createServer catch like any other HttpError.
|
|
107
|
-
//
|
|
108
|
-
// Keyed on the socket's remote address by default. Behind a TLS-terminating
|
|
109
|
-
// proxy every socket carries the proxy's address, collapsing the per-IP
|
|
110
|
-
// buckets into one global bucket that pre-auth traffic can drain; set
|
|
111
|
-
// HIPPO_CLIENT_IP_HEADER there so each real client gets its own bucket
|
|
112
|
-
// (see clientIpForRateLimit).
|
|
88
|
+
// Per-IP rate limit on /v1/* and /mcp* to bound api-key-id enumeration; /health and other paths are never throttled, and a 429 lands in the createServer
|
|
89
|
+
// catch. Keyed per clientIpForRateLimit (set HIPPO_CLIENT_IP_HEADER behind a TLS-terminating proxy, or all clients share one bucket).
|
|
113
90
|
if (limiter && (path.startsWith('/v1/') || path === '/mcp' || path === '/mcp/stream')) {
|
|
114
91
|
if (!limiter.check(clientLimitKey(req))) {
|
|
115
92
|
throw new HttpError(429, 'rate limit exceeded', limiter.retryAfterSec);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export interface KeyCheckBounds {
|
|
2
|
-
/** Throws the 429 a refused key check answers, before any derivation: no room left to
|
|
2
|
+
/** Throws the 429 a refused key check answers, before any derivation: no room left to
|
|
3
|
+
* wait (Retry-After 1), or `address` has spent its tries on `keyId` (Retry-After 12). */
|
|
3
4
|
admit(keyId: string, address: string): void;
|
|
4
5
|
/** Runs `derive` now or once a slot frees; `admit` in the same tick keeps the queue within its cap. */
|
|
5
6
|
run<T>(derive: () => Promise<T>): Promise<T>;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { Server, ServerResponse } from 'node:http';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
export declare function setKeepAliveTimeouts(server: Server): void;
|
|
3
|
+
export declare const DEFAULT_SHUTDOWN_DRAIN_MS = 5000;
|
|
4
|
+
/** How long a signal or crash shutdown may run before the process is ended without it. */
|
|
5
|
+
export declare function shutdownBoundMs(drainMs: number): number;
|
|
6
|
+
/** Stops accepting, ends streams at once (they never finish on their own), gives other in-flight requests up to `drainMs`, then closes what is left. */
|
|
6
7
|
export declare function drainAndClose(server: Server, inflight: ReadonlySet<ServerResponse>, drainMs: number): Promise<void>;
|
|
7
8
|
//# sourceMappingURL=lifecycle.d.ts.map
|
package/dist/server/lifecycle.js
CHANGED
|
@@ -1,8 +1,23 @@
|
|
|
1
|
+
import { bodyDeadlineMs } from '../util/http-util.js';
|
|
1
2
|
import { log } from '../util/log.js';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
// 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.
|
|
4
|
+
const REQUEST_RECEIVE_TIMEOUT_MS = 300_000;
|
|
5
|
+
export function setKeepAliveTimeouts(server) {
|
|
6
|
+
// The default 5s keepAliveTimeout closes idle sockets just as clients reuse them (ECONNRESET).
|
|
7
|
+
// headersTimeout must stay ABOVE keepAliveTimeout + keepAliveTimeoutBuffer (1s), or it closes idle reused sockets itself.
|
|
8
|
+
server.keepAliveTimeout = 65_000;
|
|
9
|
+
server.headersTimeout = 70_000;
|
|
10
|
+
// Never below what a raised body deadline allows, or Node's bare 408 would come before the route's own.
|
|
11
|
+
server.requestTimeout = Math.max(REQUEST_RECEIVE_TIMEOUT_MS, server.headersTimeout + bodyDeadlineMs());
|
|
12
|
+
}
|
|
13
|
+
export const DEFAULT_SHUTDOWN_DRAIN_MS = 5000;
|
|
14
|
+
// Past the request drain, a stop only waits for each store thread to end its statement and close: seconds at most, so longer means a thread that will not end.
|
|
15
|
+
const STORE_CLOSE_GRACE_MS = 10_000;
|
|
16
|
+
/** How long a signal or crash shutdown may run before the process is ended without it. */
|
|
17
|
+
export function shutdownBoundMs(drainMs) {
|
|
18
|
+
return drainMs + STORE_CLOSE_GRACE_MS;
|
|
19
|
+
}
|
|
20
|
+
/** Stops accepting, ends streams at once (they never finish on their own), gives other in-flight requests up to `drainMs`, then closes what is left. */
|
|
6
21
|
export async function drainAndClose(server, inflight, drainMs) {
|
|
7
22
|
const closed = new Promise((resolve, reject) => {
|
|
8
23
|
server.close((err) => (err ? reject(err) : resolve()));
|
package/dist/server/mcp-http.js
CHANGED
|
@@ -12,17 +12,8 @@ import { isJsonString, isJsonObject } from '../util/json.js';
|
|
|
12
12
|
import { FINGERPRINT_HEX_CHARS } from '../util/token-text.js';
|
|
13
13
|
const DEFAULT_SSE_HEARTBEAT_MS = 60000;
|
|
14
14
|
const DEFAULT_SSE_MAX_AGE_SEC = 3600;
|
|
15
|
-
/**
|
|
16
|
-
*
|
|
17
|
-
* mcp/server.ts to scope `lastRecalledIds` to the calling client so two
|
|
18
|
-
* clients on the same tenant cannot poison each other's outcome feedback.
|
|
19
|
-
*
|
|
20
|
-
* Token is hashed (sha256, 16-hex-char prefix) so we never log or persist
|
|
21
|
-
* the raw bearer. Combined with remoteAddress so two clients sharing a key
|
|
22
|
-
* (e.g. on a shared Postman environment) are still separable in the common
|
|
23
|
-
* case. 'noauth' covers loopback no-auth and is acceptable because that
|
|
24
|
-
* path is single-host single-user.
|
|
25
|
-
*/
|
|
15
|
+
/** Per-client key for MCP state isolation under HTTP-MCP (scopes `lastRecalledIds`): sha256 of the token (16 hex chars, so the raw bearer is never logged)
|
|
16
|
+
* plus remoteAddress, so clients sharing a key stay separable; 'noauth' covers loopback no-auth, which is single-host single-user. */
|
|
26
17
|
function buildMcpClientKey(req) {
|
|
27
18
|
const auth = readAuthHeader(req);
|
|
28
19
|
const tokenHash = auth.kind === 'bearer'
|
|
@@ -31,24 +22,8 @@ function buildMcpClientKey(req) {
|
|
|
31
22
|
const addr = subscriberKey(req.socket.remoteAddress ?? 'unknown');
|
|
32
23
|
return `http:${tokenHash}:${addr}`;
|
|
33
24
|
}
|
|
34
|
-
//
|
|
35
|
-
//
|
|
36
|
-
// Two routes implement an MCP HTTP transport alongside the stdio one. Both
|
|
37
|
-
// dispatch to the same `handleMcpRequest` as the stdio loop in src/mcp/server.ts.
|
|
38
|
-
//
|
|
39
|
-
// POST /mcp — Send a JSON-RPC request, get a JSON-RPC response synchronously
|
|
40
|
-
// in the body. Content-type: application/json both ways.
|
|
41
|
-
// GET /mcp/stream — Open an SSE stream for server-initiated messages.
|
|
42
|
-
// v1 simplification: this stream is keepalive-only. Clients
|
|
43
|
-
// that need server-pushed notifications/progress will see
|
|
44
|
-
// only `: ping` comments every 30s. All real responses come
|
|
45
|
-
// back synchronously on POST /mcp. This matches the
|
|
46
|
-
// "synchronous JSON in body" leg of the MCP HTTP spec and
|
|
47
|
-
// is enough for `tools/list` / `tools/call` round-trips.
|
|
48
|
-
// Server-initiated SSE messages will be wired in a later task.
|
|
49
|
-
//
|
|
50
|
-
// Auth: same as /v1/* — Bearer token validated via `requireAuth`, with the
|
|
51
|
-
// loopback no-auth fallback. SSE check runs once at stream-open.
|
|
25
|
+
// MCP-over-HTTP/SSE: POST /mcp answers a JSON-RPC request synchronously; GET /mcp/stream is a keepalive-only SSE stream (`: ping` every 30s) in v1.
|
|
26
|
+
// Both dispatch to handleMcpRequest as stdio does. Auth matches /v1/* (Bearer via `requireAuth`, loopback fallback); SSE is checked once at stream-open.
|
|
52
27
|
// Percent-encoded by the client, so any lowercase Unicode name fits a Latin-1 header.
|
|
53
28
|
const HEADER_PIECE = /^[A-Za-z0-9\-_.!~*'()%]+$/;
|
|
54
29
|
function decodeHeaderPiece(raw, header) {
|
|
@@ -101,11 +76,8 @@ function mcpContextFor(ctx, clientKey, autoSleep, project) {
|
|
|
101
76
|
return mcpCtx;
|
|
102
77
|
}
|
|
103
78
|
export async function handleMcpPost(req, res, opts) {
|
|
104
|
-
//
|
|
105
|
-
//
|
|
106
|
-
// Without this, executeTool would walk from cwd via findHippoRoot() and
|
|
107
|
-
// pull tenant from HIPPO_TENANT, dropping a valid Bearer for tenant B
|
|
108
|
-
// back to whatever the env says.
|
|
79
|
+
// Same Context the /v1/* routes use, so MCP tool calls inherit the bound hippoRoot and the auth-resolved tenantId/actor; otherwise executeTool would walk
|
|
80
|
+
// from cwd and read HIPPO_TENANT, dropping a valid Bearer for tenant B to the env's tenant.
|
|
109
81
|
const ctx = await buildContextWithAuth(req, opts);
|
|
110
82
|
noteAccess(req, { tenant: ctx.tenantId });
|
|
111
83
|
const project = callerProjectFromHeaders(req, ctx.hippoRoot);
|
|
@@ -120,9 +92,8 @@ export async function handleMcpPost(req, res, opts) {
|
|
|
120
92
|
if (!isJsonObject(mcpReq) || !isJsonString(mcpReq.method)) {
|
|
121
93
|
throw new HttpError(400, 'JSON-RPC body must include a method string');
|
|
122
94
|
}
|
|
123
|
-
// SAFETY: validated above as a plain JSON object
|
|
124
|
-
//
|
|
125
|
-
// safely defaulted inside handleMcpRequest's JSON-RPC dispatch.
|
|
95
|
+
// SAFETY: validated above as a plain JSON object with a string method; the other McpRequest wire fields (jsonrpc, id, params) are checked or defaulted in
|
|
96
|
+
// handleMcpRequest.
|
|
126
97
|
const rpcReq = mcpReq;
|
|
127
98
|
let mcpRes;
|
|
128
99
|
try {
|
|
@@ -178,13 +149,8 @@ export async function handleMcpStream(req, res, opts, streamSlots) {
|
|
|
178
149
|
// Initial ping so smoke tests can confirm the stream is live without
|
|
179
150
|
// waiting for the first keepalive interval.
|
|
180
151
|
res.write(': ping\n\n');
|
|
181
|
-
// SSE hardening:
|
|
182
|
-
//
|
|
183
|
-
// revoked or rotated, close the stream with reason='auth_revoked'.
|
|
184
|
-
// - MCP_SSE_MAX_AGE_SEC (default 3600) caps stream lifetime; close
|
|
185
|
-
// with reason='max_age_exceeded' when reached.
|
|
186
|
-
// - MCP_SSE_HEARTBEAT_MS (default 60000) lets tests run with a short
|
|
187
|
-
// interval without waiting a full minute.
|
|
152
|
+
// SSE hardening: the heartbeat re-validates the bearer (MCP_SSE_HEARTBEAT_MS, default 60000) and closes with 'auth_revoked' if the key was revoked or
|
|
153
|
+
// rotated; MCP_SSE_MAX_AGE_SEC (default 3600) caps stream lifetime and closes with 'max_age_exceeded'.
|
|
188
154
|
keepStreamAlive(req, res, opts);
|
|
189
155
|
}
|
|
190
156
|
function keepStreamAlive(req, res, opts) {
|
|
@@ -1,16 +1,7 @@
|
|
|
1
|
-
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
* Bounds api-key-id enumeration: a
|
|
5
|
-
* client that drains its bucket is denied until it refills. Dependency-free
|
|
6
|
-
* and unit-testable in isolation via the injectable `now`.
|
|
7
|
-
*/
|
|
1
|
+
/** Per-key token-bucket rate limiter for inbound /v1/* requests, bounding api-key-id enumeration. Dependency-free; the injectable `now` makes it
|
|
2
|
+
* unit-testable. */
|
|
8
3
|
export interface RateLimiter {
|
|
9
|
-
/**
|
|
10
|
-
* Consume one token for `key`. Returns true if the request is allowed,
|
|
11
|
-
* false if the key's bucket is exhausted. `now` (epoch ms) is injectable
|
|
12
|
-
* for deterministic tests.
|
|
13
|
-
*/
|
|
4
|
+
/** Consume one token for `key`: true if allowed, false if the bucket is exhausted; `now` (epoch ms) is injectable for deterministic tests. */
|
|
14
5
|
check(key: string, now?: number): boolean;
|
|
15
6
|
readonly retryAfterSec: number;
|
|
16
7
|
}
|
|
@@ -24,11 +15,7 @@ export interface RateLimiterOpts {
|
|
|
24
15
|
/** Hard cap on tracked keys; the least-recently-used key is evicted on overflow. */
|
|
25
16
|
maxKeys: number;
|
|
26
17
|
}
|
|
27
|
-
/**
|
|
28
|
-
*
|
|
29
|
-
* to once per `idleEvictMs`) drops idle buckets, and a hard `maxKeys` cap evicts
|
|
30
|
-
* the least-recently-used key, so a client rotating source addresses cannot
|
|
31
|
-
* grow the map without bound between sweeps.
|
|
32
|
-
*/
|
|
18
|
+
/** Token-bucket limiter with bounded memory: a sweep (at most once per `idleEvictMs`) drops idle buckets and a `maxKeys` cap evicts the least-recently-used
|
|
19
|
+
* key, so a client rotating source addresses cannot grow the map without bound. */
|
|
33
20
|
export declare function createRateLimiter(opts: RateLimiterOpts): RateLimiter;
|
|
34
21
|
//# sourceMappingURL=rate-limit.d.ts.map
|
|
@@ -1,16 +1,7 @@
|
|
|
1
|
-
/**
|
|
2
|
-
*
|
|
3
|
-
|
|
4
|
-
*
|
|
5
|
-
* client that drains its bucket is denied until it refills. Dependency-free
|
|
6
|
-
* and unit-testable in isolation via the injectable `now`.
|
|
7
|
-
*/
|
|
8
|
-
/**
|
|
9
|
-
* Build a token-bucket limiter. Memory is bounded two ways: a sweep (throttled
|
|
10
|
-
* to once per `idleEvictMs`) drops idle buckets, and a hard `maxKeys` cap evicts
|
|
11
|
-
* the least-recently-used key, so a client rotating source addresses cannot
|
|
12
|
-
* grow the map without bound between sweeps.
|
|
13
|
-
*/
|
|
1
|
+
/** Per-key token-bucket rate limiter for inbound /v1/* requests, bounding api-key-id enumeration. Dependency-free; the injectable `now` makes it
|
|
2
|
+
* unit-testable. */
|
|
3
|
+
/** Token-bucket limiter with bounded memory: a sweep (at most once per `idleEvictMs`) drops idle buckets and a `maxKeys` cap evicts the least-recently-used
|
|
4
|
+
* key, so a client rotating source addresses cannot grow the map without bound. */
|
|
14
5
|
export function createRateLimiter(opts) {
|
|
15
6
|
const { ratePerSec, burst, idleEvictMs, maxKeys } = opts;
|
|
16
7
|
const buckets = new Map();
|
package/dist/server/request.d.ts
CHANGED
|
@@ -13,6 +13,8 @@ interface AccessNote {
|
|
|
13
13
|
}
|
|
14
14
|
/** Names the matched route (the table's pattern, never the path sent) or the caller's tenant for the request's access line. */
|
|
15
15
|
export declare function noteAccess(req: IncomingMessage, note: AccessNote): void;
|
|
16
|
+
/** The route name the request's access line carries, for a line about the same request logged after its reply. */
|
|
17
|
+
export declare function accessRouteOf(req: IncomingMessage): string;
|
|
16
18
|
/** The request's id, after stamping the headers every reply carries and starting the slow-request watchdog and the access line. */
|
|
17
19
|
export declare function openRequest(req: IncomingMessage, res: ServerResponse, slowWarnMs?: number): string;
|
|
18
20
|
export declare function sendError(res: ServerResponse, status: number, message: string): void;
|
|
@@ -23,14 +25,8 @@ interface ParsedRoute {
|
|
|
23
25
|
}
|
|
24
26
|
export declare function parseRequest(req: IncomingMessage): ParsedRoute;
|
|
25
27
|
export declare function matchPath(pattern: string, path: string): Record<string, string> | null;
|
|
26
|
-
/**
|
|
27
|
-
*
|
|
28
|
-
* them — otherwise `%2F` becomes `/`, path-split runs, and the route either
|
|
29
|
-
* silently 404s or matches the wrong template.
|
|
30
|
-
*
|
|
31
|
-
* Only the PATHNAME is scanned (split on the first `?`), so recall queries
|
|
32
|
-
* containing URLs like `?q=https%3A%2F%2Fexample.com` are not rejected.
|
|
33
|
-
*/
|
|
28
|
+
/** Rejects URL-encoded slashes in path segments BEFORE the URL parser decodes them (`%2F` would become `/` and 404 or match the wrong template).
|
|
29
|
+
* Only the PATHNAME is scanned, so recall queries like `?q=https%3A%2F%2Fexample.com` are not rejected. */
|
|
34
30
|
export declare function rejectEncodedSlash(rawUrl: string): void;
|
|
35
31
|
export {};
|
|
36
32
|
//# sourceMappingURL=request.d.ts.map
|