chore(reconcile): bring main up to published npm line (0.4.4 -> 0.4.5)#9
Merged
Conversation
The MCP server used the local SQLite store unconditionally (getDb/ingestLog), so with the fleet flip env set (HASNA_LOGS_API_URL + HASNA_LOGS_API_KEY) the CLI routed to cloud but the MCP kept reading/writing local — the installed HTTP MCP service (dist/mcp) never carried the cloud path because it never imported the resolver (tree-shaken out). Mirror the CLI's logsCloud() in the MCP: log_push, log_push_batch, log_search, log_tail, log_count and log_recent_errors now resolve resolveLogsCloudStore() per call and hit the cloud HTTP API when the flip resolves, else fall back to the local store (fully reversible: unset the env vars -> local). Contracts is already inlined into every entry bundle, so adding the import pulls the cloud client into dist/mcp. Bump to 0.3.37-cloud2 so fixed vs stale is grep-distinct. Verified end-to-end over the streamable-HTTP MCP against https://logs.hasna.xyz/v1: push returns a cloud UUID, search reads it back, zero local-sqlite leak, on spark01 + station02/03/05. Tests: 25 pass / 0 fail.
Introduce a single Store interface with two transports resolved from the
environment: LocalStore (on-box SQLite, sole owner of getDb) and ApiStore
(HTTP /v1 + bearer key; self_hosted and cloud both use it). Route every CLI
command and MCP tool data-plane operation through resolveStore(), removing
the per-command cloud?:local split-brain branches and handler-level getDb.
- Add src/store/{types,local,api,index}.ts; export via @hasna/logs/storage.
- Add /v1/logs/count, /v1/logs/summary, /v1/health cloud endpoints (+ cloud
store aggregates) for the ApiStore data-plane. Requires ECS redeploy.
- Local-only infra/compute commands+tools (run, import-jsonl, scan, watch,
events push/export, diagnose, event-store repair, alerts, issues, perf,
session-context) refuse cleanly in api mode via requireLocalMode.
- ApiStore surfaces a clear local-only error for features with no cloud data
model (events catalog, test reports, pages, scan jobs).
- Add top-level CLI error boundary; delete dead cloud-store.ts + test; prune
dead imports; bump 0.3.37-cloud2 -> 0.4.0.
…the Store - Delete forbidden DSN-on-client storage push/pull/sync: drop src/lib/storage-sync.ts and src/lib/remote-storage.ts (pg Pool from raw RDS DSN), the CLI 'logs storage' command group, and the MCP storage_* tools. The raw RDS DSN is never distributed to clients (CLAUDE.md §2). - Remove the forbidden 'hybrid'/'remote' StorageMode type (lived in storage-sync). - Confine getDb()/bun:sqlite to the store implementation: all CLI commands and MCP tools now route through Store. Local-only maintenance/compute ops (import-jsonl, run, db doctor, events push/export, scan, diagnose, watch, log_* analytics, alerts, issues, perf, feedback, event_watch) become LocalStore methods reached via requireLocalStore(); MCP self-telemetry uses a best-effort local store. - Extract event-catalog watch queries into src/lib/event-watch.ts (db-parameterized). - Preserve the table inventory in src/lib/storage-tables.ts (no connection code).
…ch to a transport
- watch called requireLocalStore("watch") before the --server branch, so
'logs watch --server <url>' threw the local-only error in self_hosted/cloud
mode. Handle --server first (out-of-band operator-named SSE tail), resolving
the project id best-effort via the mode-resolved store; require the local
store only for the on-box event-catalog/log-row paths.
- Move the raw SSE fetch out of the CLI command body into a documented
event-stream transport (src/lib/event-stream-client.ts); no command handler
performs raw fetch to the app's own event data.
- Add an api-mode regression test for watch --server.
Route the entire data plane through the Store in self_hosted/cloud mode so the fleet flip keeps parity with the local SQLite store, and delete orphaned modules flagged by the adversarial review. ApiStore: replace the unsupported() throwers (events, test reports, pages, scan jobs) with real /v1-backed implementations, and add the operations that were previously forced local via requireLocalStore — perf snapshots/trend, issues list/update, alert rules, feedback, session context, log-context-by-id, events export, and the diagnose/compare analytics. These now sit on the Store interface (types.ts) and are implemented by both LocalStore (async) and ApiStore. Cloud server (src/server/cloud): add the matching /v1 routes and CloudLogStore methods backed by the existing shared Postgres tables (all authed with logs:read / logs:write scope guards). Raw event envelopes stay null in cloud (they live in local append-only segment files). CLI/MCP: stop shadowing the resolved store with requireLocalStore for these data operations; route through the unified store instead. Dead code: delete src/lib/lighthouse.ts (orphaned) and src/lib/rotate.ts + its test (superseded by retention.ts). Drop the now-dead --external lighthouse build flag. Local-only compute that legitimately has no cloud data model (universal-event push, event_watch tail, log watch/follow, subprocess capture, db repair, browser scan) remains on the concrete LocalStore via requireLocalStore. Tests: add cloud /v1 parity coverage. bun test green (396), build + biome clean.
…ease 0.4.2
Close the last cloud data-plane 404: the ApiStore posts telemetry to
POST /v1/events (used by `logs run` capture, `events push`, and the SDK/MCP)
but the cloud Hono app only served GET /v1/events, so every event write 404'd
in api mode. Add v1.post("/events") (single + `{events:[...]}` batch, same
response shapes as the local /api/events route) backed by a new
CloudLogStore.createEvent that validates+normalizes+redacts through the shared
DB-agnostic pass (identical redaction to local; secrets never reach Postgres),
dedupes on event_id, and persists to event_records (raw envelope stays
local-only, so raw is null in cloud).
Extract normalizeAndRedactUniversalEvent from universal-ingest so local SQLite
and cloud Postgres ingest share one redaction pipeline.
Safety: `logs doctor rebuild-index` clears + replays SQLite projections
(logs/issues/spans/test reports/...) from raw segments via DELETE FROM logs.
It now snapshots the DB first (backupLogsDb, WAL-checkpointed copy) and prints
the backup path, so a pruned/damaged segment store can never silently drop
rows without recourse.
Tests: cloud POST /v1/events round-trip + idempotency + 422/403; full suite green.
NOTE: the cloud endpoints require an ECS redeploy of this server build to take
effect on the live https://logs.hasna.xyz service.
…literal Local-mode `logs list --text`/`log_search` passed raw user input straight to the SQLite FTS5 MATCH expression, so any query-syntax character (e.g. a hyphen) was parsed as an operator: 'mcp-qa' -> 'mcp NOT column:qa' -> throws "no such column: qa" (exit 1). Cloud/Postgres (ILIKE) was unaffected. Add toFtsMatchQuery() to tokenize on whitespace and wrap each token in a quoted literal phrase (doubling embedded quotes), neutralizing FTS5 operators while preserving implicit AND-of-terms for multi-word queries. Regression tests cover hyphens, multi-hyphen ids, embedded quotes, and multi-word AND.
main had diverged behind the registry: it sat at 0.3.36 (082c698) while npm latest was 0.4.4. The published tag npm/logs/v0.4.4 was 8 commits ahead of main (Store unification, cloud /v1 data-plane parity + POST /v1/events ingest, watch --server SSE fix, FTS5 query sanitization, releases 0.4.2/0.4.3) and main had zero commits absent from the tag. main was a strict ancestor of the tag, so this reconcile is a clean fast-forward; no main-only commits to re-apply and no history lost. Bump 0.4.4 -> 0.4.5 + CHANGELOG; no functional code change.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
mainhad diverged behind the published npm line. Deployed/published code was not onmain:origin/mainsat at 0.3.36 (tip082c698, "route ALL log reads+writes to cloud API in self_hosted mode").@hasna/logslatestwas 0.4.4.npm/logs/v0.4.4was 8 commits ahead of main (Store unification /LocalStore+ApiStore, cloud/v1data-plane parity +POST /v1/eventsingest,watch --serverSSE fix, FTS5 query sanitization, releases 0.4.2/0.4.3).mainhad zero commits that were not already on the tag (git rev-list --count npm/logs/v0.4.4..origin/main= 0).So
mainwas a strict ancestor of the published tag — a clean fast-forward, not a true fork. No main-only commits needed re-applying and no history is lost.What
mainup to the published tagnpm/logs/v0.4.4.0.4.4->0.4.5somainnow sits at/above the published line.git diff npm/logs/v0.4.4 HEAD= onlypackage.jsonversion +CHANGELOG.md).Verification
git rev-list --count HEAD..origin/main= 0 (everymaincommit is preserved).git diff npm/logs/v0.4.4 HEAD --stat= only CHANGELOG + package.json version.Note: repo-local
bun install/typecheck fail on pre-existingfile:dev-dep resolution (@hasna/agent-registry,@hasna/contractssubpath exports) — a fleet-wide environment issue identical at the base tag, not introduced here; the published 0.4.4 built and shipped fine.After merge, the 4 open PRs (#8 security, #2, #3, #1) should be rebased onto reconciled
mainand re-triaged against the correct (published) line.