feat(local): add command to run a local Spotlight sidecar#888
feat(local): add command to run a local Spotlight sidecar#888MathurAditya724 wants to merge 1 commit intomainfrom
Conversation
Adds 'sentry local', a long-running command that starts a minimal Hono
HTTP server wire-compatible with the Spotlight sidecar protocol. The
server uses @spotlightjs/spotlight/sdk's createSpotlightBuffer +
pushToSpotlightBuffer helpers to ingest envelopes from any Sentry SDK
running in the user's dev stack and tails them to the terminal.
Endpoints exposed:
POST /stream - Spotlight ingest
POST /api/{projectId}/envelope/ - Sentry SDK ingest path
GET /stream - SSE feed for the Spotlight overlay
GET /health - liveness check
Why a thin in-tree server instead of spawning npx @spotlightjs/spotlight:
the SDK helpers give us decompression + lazy parsing for free while
keeping the surface focused on a CLI-friendly tail UX, and bundling
through esbuild keeps the published binary self-contained per the
no-runtime-dependencies rule.
The command runs without auth (it's a local dev tool) and shuts down
gracefully on SIGINT/SIGTERM, force-closing keep-alive connections so
SSE subscribers don't block exit.
|
Codecov Results 📊✅ 6343 passed | Total: 6343 | Pass Rate: 100% | Execution Time: 0ms 📊 Comparison with Base Branch
✨ No test changes detected All tests are passing successfully. ❌ Patch coverage is 25.10%. Project has 13230 uncovered lines. Files with missing lines (1)
Coverage diff@@ Coverage Diff @@
## main #PR +/-##
==========================================
- Coverage 76.03% 75.80% -0.23%
==========================================
Files 294 295 +1
Lines 54404 54659 +255
Branches 0 0 —
==========================================
+ Hits 41362 41429 +67
- Misses 13042 13230 +188
- Partials 0 0 —Generated by Codecov Action |
| process.once("SIGINT", () => shutdown("SIGINT")); | ||
| process.once("SIGTERM", () => shutdown("SIGTERM")); |
There was a problem hiding this comment.
Bug: process.once removes the SIGINT handler after one use, so the intended force-exit logic on a second signal is never triggered.
Severity: MEDIUM
Suggested Fix
Replace process.once('SIGINT', ...) with process.on('SIGINT', ...). This will keep the handler registered, allowing the if (shuttingDown) check to correctly detect a second signal and trigger the intended force-exit by calling process.exit(0).
Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.
Location: src/commands/local.ts#L261-L262
Potential issue: The code uses `process.once('SIGINT', ...)` to handle shutdown signals.
This correctly initiates a graceful shutdown on the first signal. However, the handler
is removed after its first invocation. The code intends for a second signal to trigger a
force-exit via `if (shuttingDown) { process.exit(0); }`, bypassing shutdown hooks for
stuck connections. Because the handler is removed, a second `SIGINT` signal does not
trigger this logic, making the force-exit path unreachable. Instead, Node.js's default
exit behavior is invoked, which does not bypass the hooks as intended.
Did we get this right? 👍 / 👎 to inform future reviews.
Summary
Adds
sentry local, a long-running command that starts a minimal Hono HTTP server wire-compatible with the Spotlight sidecar protocol. It uses@spotlightjs/spotlight/sdk'screateSpotlightBufferandpushToSpotlightBufferhelpers to ingest envelopes from any Sentry SDK running in the user's dev stack and tails them to the terminal.This gives users a one-command path to "Sentry for development" without leaving the CLI: errors, traces, and logs from their dev stack stream straight into their shell, and the SSE endpoint stays compatible with the Spotlight overlay for richer rendering.
What's new
src/commands/local.ts— new command. Flags:--port/-p(default8969),--host/-H(defaultlocalhost),--open/-o,--quiet/-q. Runs without auth.src/app.ts— wireslocalinto the top-level route map.package.json— adds@spotlightjs/spotlight,hono, and@hono/node-serveras devDependencies (per the no-runtime-deps rule; everything is bundled at build time).docs/src/fragments/commands/local.md— hand-written examples + endpoint table.Endpoints exposed
POST/streamPOST/api/{projectId}/envelope/GET/streamGET/healthWhy a thin in-tree server instead of spawning
npx @spotlightjs/spotlightThe SDK helpers give us decompression (gzip/deflate/br) + lazy envelope parsing for free, while keeping the command's surface focused on a CLI-friendly tail UX. Bundling through esbuild also keeps the published binary self-contained per the no-runtime-dependencies rule — users don't need a separate npx install or a network connection on first run.
Testing
bun run typecheckbun x ultracite check(only the pre-existing unrelatedmarkdown.tswarning)bun run check:deps/check:fragments/check:errors/check:docs-sectionsbun test --timeout 15000 --isolate --parallel test/lib test/commands test/types— 6343 pass / 0 failSENTRY_CLIENT_ID=test bun run script/bundle.ts— bundle builds cleanly/stream, observed204 No Contentand the tail lineHH:MM:SS.sss • eventappear; SIGTERM shut down gracefully. Same flow against the bundlednode dist/bin.cjs.Out of scope
setupSpotlight()from the upstream package can serve it but pulls in MCP / @sentry/node / a much larger dependency tree; this PR sticks to the small Hono server to keep the CLI lean.