Repository navigation
Add Cloud VM default-provider stress check - #3697
Conversation
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds a VM stress-test CLI and concurrent worker implementation with websocket RPC health checks, switches internal doc anchors to Next.js Link components, initializes DevPanel position from a safe helper, and replaces untyped bun:test declarations with typed interfaces. ChangesVM Stress Testing Infrastructure
UI Component & Navigation Updates
Type System Improvements
Sequence Diagram(s) sequenceDiagram
participant CLI
participant Worker
participant StackAPI
participant VMAPI
participant RPC
CLI->>Worker: start N workers
Worker->>StackAPI: create user / impersonation
Worker->>VMAPI: POST /api/vm (create)
Worker->>VMAPI: POST /api/vm/:id/attach-endpoint
Worker->>RPC: websocket connect + auth
Worker->>RPC: proxy.open / proxy.stream.subscribe / proxy.write
RPC-->>Worker: healthz response
Worker->>VMAPI: DELETE VM
Worker->>StackAPI: DELETE user
CLI->>Worker: aggregate results and exit
Estimated code review effort: 🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs:
🚥 Pre-merge checks | ✅ 13 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (13 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
|
Warning Review the following alerts detected in dependencies. According to your organization's Security Policy, it is recommended to resolve "Warn" alerts. Learn more about Socket for GitHub.
|
df74b01 to
c4e11f8
Compare
Greptile SummaryThis PR ships a new
Confidence Score: 5/5Safe to merge; the new stress script is operator-only tooling and all production-path changes are small, self-contained fixes. The stress script is well-structured: it uses bounded AbortController timeouts for HTTP calls, a 30-second ws.terminate deadline for the RPC healthz flow, a settled flag that prevents double-settle after ws.close(), and interrupt-safe cleanup via SIGINT/SIGTERM handlers. The previous thread's concern about the missing close handler has been addressed in this revision. The SSR fix in spacing-control.tsx is correct — a lazy initializer with a typeof window guard is the standard Next.js pattern. The to Link replacements use the already-imported locale-aware component. No production data paths are touched. No files require special attention. Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[Parse args: count / concurrency / provider] --> B[Load Stack credentials from env]
B --> C{Run N workers in parallel}
C --> D[Create temporary Stack user]
D --> E[POST /api/vm]
E --> F[POST /api/vm/:id/attach-endpoint]
F --> G[Open daemon RPC WebSocket]
G --> H[proxy.open port 7777]
H --> I[proxy.write GET /healthz]
I --> J{healthz 200 + ok:true?}
J -- yes --> K[Record success]
J -- no --> L[Record failure]
K --> M[Cleanup: DELETE /api/vm/:id]
L --> M
M --> N{VM deleted?}
N -- yes --> O[Delete Stack user]
N -- no --> P[Skip user delete, log error]
O --> Q[Print JSON summary]
P --> Q
Q --> R{Any failures?}
R -- yes --> S[exit 1]
R -- no --> T[exit 0]
Reviews (4): Last reviewed commit: "Address Cloud VM stress review feedback" | Re-trigger Greptile |
| function rpcProxyHealthz(url, token, sessionId) { | ||
| return new Promise((resolve, reject) => { | ||
| const ws = new WebSocket(url); | ||
| const chunks = []; | ||
| let streamId = ""; | ||
| let capabilities = []; | ||
| const timer = setTimeout(() => { | ||
| ws.terminate(); | ||
| reject(new Error("rpc proxy timeout")); | ||
| }, 30_000); | ||
| ws.on("error", (error) => { | ||
| clearTimeout(timer); | ||
| reject(error); | ||
| }); | ||
| ws.on("open", () => { | ||
| ws.send(JSON.stringify({ type: "auth", token, session_id: sessionId })); | ||
| }); | ||
| ws.on("message", (data, isBinary) => { | ||
| try { | ||
| if (isBinary) return; | ||
| const msg = JSON.parse(data.toString()); | ||
| if (msg.type === "ready") { | ||
| ws.send(JSON.stringify({ id: 1, method: "hello", params: {} })); | ||
| return; | ||
| } | ||
| if (msg.id === 1) { | ||
| if (msg.ok !== true) throw new Error(`hello failed: ${JSON.stringify(msg)}`); | ||
| capabilities = msg.result?.capabilities ?? []; | ||
| ws.send(JSON.stringify({ id: 2, method: "proxy.open", params: { host: "127.0.0.1", port: 7777 } })); | ||
| return; | ||
| } | ||
| if (msg.id === 2) { | ||
| if (msg.ok !== true) throw new Error(`proxy.open failed: ${JSON.stringify(msg)}`); | ||
| streamId = msg.result?.stream_id; | ||
| if (!streamId) throw new Error(`proxy.open missing stream_id: ${JSON.stringify(msg)}`); | ||
| ws.send(JSON.stringify({ id: 3, method: "proxy.stream.subscribe", params: { stream_id: streamId } })); | ||
| return; | ||
| } | ||
| if (msg.id === 3) { | ||
| if (msg.ok !== true) throw new Error(`proxy.stream.subscribe failed: ${JSON.stringify(msg)}`); | ||
| const request = Buffer | ||
| .from("GET /healthz HTTP/1.1\r\nHost: 127.0.0.1\r\nConnection: close\r\n\r\n") | ||
| .toString("base64"); | ||
| ws.send(JSON.stringify({ | ||
| id: 4, | ||
| method: "proxy.write", | ||
| params: { stream_id: streamId, data_base64: request }, | ||
| })); | ||
| return; | ||
| } | ||
| if (msg.id === 4 && msg.ok !== true) throw new Error(`proxy.write failed: ${JSON.stringify(msg)}`); | ||
| if (msg.event === "proxy.stream.data" && msg.data_base64) { | ||
| chunks.push(Buffer.from(msg.data_base64, "base64")); | ||
| } | ||
| if (msg.event === "proxy.stream.eof") { | ||
| const response = Buffer.concat(chunks).toString(); | ||
| clearTimeout(timer); | ||
| ws.close(); | ||
| if (!response.includes("HTTP/1.1 200 OK") || !response.includes('"ok":true')) { | ||
| reject(new Error(`unexpected proxied healthz response: ${response.slice(0, 200)}`)); | ||
| } else { | ||
| resolve({ capabilities }); | ||
| } | ||
| } | ||
| if (msg.event === "proxy.stream.error") throw new Error(`proxy.stream.error: ${JSON.stringify(msg)}`); | ||
| } catch (error) { | ||
| clearTimeout(timer); | ||
| ws.terminate(); | ||
| reject(error); | ||
| } | ||
| }); | ||
| }); | ||
| } |
There was a problem hiding this comment.
Missing
close event handler leaves promise pending on unexpected disconnect
rpcProxyHealthz handles error and message events but has no ws.on("close") handler. If the server drops the connection before sending proxy.stream.eof (network blip, server-side crash, premature close frame), the promise neither resolves nor rejects until the 30-second setTimeout fires. Under concurrency, several slow-draining workers all stall at the timeout deadline simultaneously, making a partial outage look like a full 30-second hang rather than a fast failure. Adding ws.on("close", () => { clearTimeout(timer); reject(new Error("rpc proxy: connection closed unexpectedly")); }) would let failures surface immediately.
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@web/app/`[locale]/page.tsx:
- Around line 179-187: The two Link renderers cliLink and hooksLink in page.tsx
both point to "/docs/notifications" but should use distinct anchors; update
cliLink (the cliLink function) to href="/docs/notifications#cli-usage" (or the
actual "CLI Usage" anchor) and update hooksLink (the hooksLink function) to
href="/docs/notifications#integration-examples" (or the actual "Integration
Examples" anchor) so each link navigates to the correct section.
In `@web/scripts/cloud-vm/stress-vm-api.mjs`:
- Around line 41-77: Add SIGINT and SIGTERM handlers that ensure in-flight case
cleanups run before exit: maintain a shared inFlight registry (e.g., a Map or
Set of cleanup callbacks) and have runCase register its per-case cleanup closure
(capturing vmId, authHeaders, user) at start and unregister it on normal
completion; on signal iterate the registry, run each cleanup callback (awaiting
or handling errors), log results, then exit non-zero. Hook the handlers up
around the concurrency loop that uses nextIndex/results/Promise.all so any
in-flight work is cleaned if the process is interrupted, and ensure the final
results/cleanupSummary reporting still runs only after signal-driven cleanup.
- Around line 240-250: The proxy.stream.eof handler currently calls resolve or
reject then continues execution, so add an early return after the EOF branch to
prevent fall-through into the proxy.stream.error check; locate the if (msg.event
=== "proxy.stream.eof") block (which builds response from Buffer.concat(chunks),
calls clearTimeout(timer), ws.close(), and then reject(...) or resolve({
capabilities })) and insert a return immediately after those resolve/reject
paths so that the subsequent if (msg.event === "proxy.stream.error") check
cannot run for the same message.
- Around line 154-164: The DELETE response handling only sets cleanup.vmDeleted
on status 200 and only records vmDeleteError when fetchWithTimeout throws;
change the post-request logic in the block that calls fetchWithTimeout (the
variable destroy/response from fetchWithTimeout to
`${project.url}/api/vm/${encodeURIComponent(vmId)}`) to treat any non-200 status
as a failure: set cleanup.vmDeleted = false and set cleanup.vmDeleteError to a
concise error string containing the HTTP status and statusText (and include
response body or text if available) so non-exceptional error responses (5xx,
4xx, 409, etc.) are captured in the cleanup summary.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 03de3702-48cf-4ddf-aa01-29b5152ea488
📒 Files selected for processing (6)
web/app/[locale]/components/spacing-control.tsxweb/app/[locale]/page.tsxweb/package.jsonweb/scripts/cloud-vm/stress-vm-api.mjsweb/services/vms/README.mdweb/tests/bun-test.d.ts
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (3)
web/app/[locale]/page.tsx (1)
179-187:⚠️ Potential issue | 🟡 Minor | ⚡ Quick win
cliLinkandhooksLinkstill resolve to the same URL — add distinct anchors.Both renderers use
href="/docs/notifications"with no fragment, so they navigate to the same page section despite representing semantically distinct concepts ("cmux CLI" vs "Claude Code hooks"). The docs at./docs/notifications.mdhas separate headings for each. Add fragment anchors so each link lands on the relevant section.🔗 Proposed fix
- cliLink: (chunks) => ( - <Link href="/docs/notifications" className={linkClass}> - {chunks} - </Link> - ), - hooksLink: (chunks) => ( - <Link href="/docs/notifications" className={linkClass}> - {chunks} - </Link> - ), + cliLink: (chunks) => ( + <Link href="/docs/notifications#cli-usage" className={linkClass}> + {chunks} + </Link> + ), + hooksLink: (chunks) => ( + <Link href="/docs/notifications#integration-examples" className={linkClass}> + {chunks} + </Link> + ),🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@web/app/`[locale]/page.tsx around lines 179 - 187, The cliLink and hooksLink renderers currently both point to "/docs/notifications"; update the hrefs in the cliLink and hooksLink functions to include the correct fragment identifiers that target the specific headings in the notifications doc (e.g., "/docs/notifications#cmux-cli" for the cmux CLI section and "/docs/notifications#claude-code-hooks" for the Claude Code hooks section), keeping the existing className usage (linkClass) and children rendering intact so each link lands on its respective anchor in the document.web/scripts/cloud-vm/stress-vm-api.mjs (2)
156-160:⚠️ Potential issue | 🟠 Major | ⚡ Quick winRecord non-200 VM destroy responses as cleanup errors.
Line 160 only flips
vmDeleted. A409or500still leavesvmDeleteErrorsat zero, so the JSON summary under-reports cleanup failures and hides leaked VMs from the operator.🛠️ Minimal fix
const destroy = await fetchWithTimeout(`${project.url}/api/vm/${encodeURIComponent(vmId)}`, { method: "DELETE", headers: authHeaders, }, 45_000); cleanup.vmDeleted = destroy.status === 200; + if (!cleanup.vmDeleted) { + const body = await destroy.text().catch(() => ""); + cleanup.vmDeleteError = `DELETE /api/vm/${vmId} expected 200, got ${destroy.status}${body ? `: ${body.slice(0, 200)}` : ""}`; + } } catch (error) { cleanup.vmDeleteError = error instanceof Error ? error.message : String(error); }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@web/scripts/cloud-vm/stress-vm-api.mjs` around lines 156 - 160, The destroy call using fetchWithTimeout may return non-200 statuses (e.g., 409/500) but only sets cleanup.vmDeleted and never records failures; update the destroy handling so that after calling fetchWithTimeout(`${project.url}/api/vm/${encodeURIComponent(vmId)}`, ...) you check if destroy.status === 200 to set cleanup.vmDeleted, and otherwise increment cleanup.vmDeleteErrors (and optionally capture status/text for logging) so non-200 responses are counted as cleanup failures; reference the variables/fns fetchWithTimeout, project.url, vmId, cleanup.vmDeleted, and cleanup.vmDeleteErrors when making the change.
41-52:⚠️ Potential issue | 🟠 Major | 🏗️ Heavy liftHandle SIGINT/SIGTERM so interrupted runs still clean up in-flight cases.
Line 45 starts long-running workers, but
Ctrl+C/SIGTERMwill terminate the process before the in-flightrunCase()cleanups finish. With--concurrency 4, one interrupted run can still leak multiple paid VMs and Stack test users.🛡️ Minimal direction
const startedAt = Date.now(); const results = []; let nextIndex = 0; +const inFlight = new Set(); + +async function drainInFlight(signal) { + console.error(`received ${signal}; cleaning ${inFlight.size} in-flight case(s)`); + await Promise.allSettled([...inFlight].map((cleanupCase) => cleanupCase())); +} + +for (const signal of ["SIGINT", "SIGTERM"]) { + process.once(signal, () => { + void drainInFlight(signal).finally(() => process.exit(130)); + }); +} async function runCase(index) { ... + const cleanupCase = async () => { + // current finally-body cleanup + }; + inFlight.add(cleanupCase); try { ... } finally { - // current cleanup + await cleanupCase(); + inFlight.delete(cleanupCase); } }Also applies to: 79-173
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@web/scripts/cloud-vm/stress-vm-api.mjs` around lines 41 - 52, The worker loop can be terminated by SIGINT/SIGTERM before in-flight runCase() calls finish, so add graceful shutdown: register process.on('SIGINT') and process.on('SIGTERM') handlers that set a shared cancellation flag (or AbortController) and stop starting new cases, then wait for any currently running runCase(index) to finish so their cleanup runs. Update the worker function that uses nextIndex and runCase to check the cancellation flag before assigning a new index and to pass the abort signal into runCase (or have runCase check the flag) so cleanup logic always runs; ensure the outer Promise.all resolves only after all in-flight tasks complete. Reference runCase, nextIndex, the worker loop that builds Array.from({ length: concurrency }, ...), and the signal handlers to locate where to change behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@web/scripts/cloud-vm/stress-vm-api.mjs`:
- Around line 154-170: The user deletion runs regardless of VM deletion outcome,
which can orphan VMs; change the logic so the call to user.delete() (and setting
cleanup.userDeleted/cleanup.userDeleteError) only executes when
cleanup.vmDeleted is true (i.e., the VM destroy succeeded) or when there was no
VM to delete (vmId is falsy); update the conditional around the user deletion
block to check cleanup.vmDeleted (or !vmId) before invoking user.delete() to
avoid removing the Stack user after a failed VM destroy.
---
Duplicate comments:
In `@web/app/`[locale]/page.tsx:
- Around line 179-187: The cliLink and hooksLink renderers currently both point
to "/docs/notifications"; update the hrefs in the cliLink and hooksLink
functions to include the correct fragment identifiers that target the specific
headings in the notifications doc (e.g., "/docs/notifications#cmux-cli" for the
cmux CLI section and "/docs/notifications#claude-code-hooks" for the Claude Code
hooks section), keeping the existing className usage (linkClass) and children
rendering intact so each link lands on its respective anchor in the document.
In `@web/scripts/cloud-vm/stress-vm-api.mjs`:
- Around line 156-160: The destroy call using fetchWithTimeout may return
non-200 statuses (e.g., 409/500) but only sets cleanup.vmDeleted and never
records failures; update the destroy handling so that after calling
fetchWithTimeout(`${project.url}/api/vm/${encodeURIComponent(vmId)}`, ...) you
check if destroy.status === 200 to set cleanup.vmDeleted, and otherwise
increment cleanup.vmDeleteErrors (and optionally capture status/text for
logging) so non-200 responses are counted as cleanup failures; reference the
variables/fns fetchWithTimeout, project.url, vmId, cleanup.vmDeleted, and
cleanup.vmDeleteErrors when making the change.
- Around line 41-52: The worker loop can be terminated by SIGINT/SIGTERM before
in-flight runCase() calls finish, so add graceful shutdown: register
process.on('SIGINT') and process.on('SIGTERM') handlers that set a shared
cancellation flag (or AbortController) and stop starting new cases, then wait
for any currently running runCase(index) to finish so their cleanup runs. Update
the worker function that uses nextIndex and runCase to check the cancellation
flag before assigning a new index and to pass the abort signal into runCase (or
have runCase check the flag) so cleanup logic always runs; ensure the outer
Promise.all resolves only after all in-flight tasks complete. Reference runCase,
nextIndex, the worker loop that builds Array.from({ length: concurrency }, ...),
and the signal handlers to locate where to change behavior.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 67f48d22-d60a-4c1b-8ee5-acbea56274cd
📒 Files selected for processing (6)
web/app/[locale]/components/spacing-control.tsxweb/app/[locale]/page.tsxweb/package.jsonweb/scripts/cloud-vm/stress-vm-api.mjsweb/services/vms/README.mdweb/tests/bun-test.d.ts
c4e11f8 to
27aa8b2
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@web/scripts/cloud-vm/stress-vm-api.mjs`:
- Around line 280-332: The ws message handler (ws.on("message", ...)) currently
only handles specific msg.type/ids/events and silently ignores unknown message
shapes; update the handler to detect and surface unexpected or error-shaped
messages by checking for msg.error or msg.type === "error" and by adding a final
fallback branch (after the existing ifs for type/ids/events) that throws or
calls fail() with a descriptive error including the raw msg and streamId/chunks
context; ensure this uses the existing fail(error) path so ws.terminate() is
invoked and diagnostics are captured (refer to the msg parsing block, the id
checks for 1-4, event checks like
"proxy.stream.data"/"proxy.stream.eof"/"proxy.stream.error", and the
fail/succeed calls).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 5a4bcd65-72f5-4959-bd10-8773f4548817
📒 Files selected for processing (6)
web/app/[locale]/components/spacing-control.tsxweb/app/[locale]/page.tsxweb/package.jsonweb/scripts/cloud-vm/stress-vm-api.mjsweb/services/vms/README.mdweb/tests/bun-test.d.ts
27aa8b2 to
cebe3ec
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@web/scripts/cloud-vm/stress-vm-api.mjs`:
- Around line 77-94: The JSON summary currently computes "ok" from
failures.length only, which can be true when interruptedSignal is set; update
the console.log block to compute ok as failures.length === 0 &&
!interruptedSignal (or Boolean(interruptedSignal) negated) so interrupted runs
are marked as failed; adjust the expression where "ok" is defined in the
console.log call that includes target, project, provider, etc., to use both
failures and interruptedSignal.
In `@web/services/vms/README.md`:
- Line 230: Remove the hard-coded deployment state from the "Operational note"
(the line containing `sh-6ch5p9k23xrcx24056n8` / `freestyle-rpclease-20260502a`)
and replace it with a short runbook-style checklist that documents validation
criteria and where to look for the active provider/snapshot (e.g., which env
vars, deployment/operator objects, and commands to query current snapshot and
default provider). Ensure the new text names the concrete checks to run (e.g.,
inspect provider env var, query deployment/operator status, verify WebSocket PTY
and daemon RPC lease health) rather than embedding a specific snapshot id so the
README remains accurate across rollouts.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 2df2dc30-e6ae-4dc1-9996-ed9e9c9cbfd8
📒 Files selected for processing (6)
web/app/[locale]/components/spacing-control.tsxweb/app/[locale]/page.tsxweb/package.jsonweb/scripts/cloud-vm/stress-vm-api.mjsweb/services/vms/README.mdweb/tests/bun-test.d.ts
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit cebe3ec. Configure here.

Summary
Verification
Live checks
Note
Medium Risk
Adds a new operator-facing stress runner that creates and destroys real VMs/users against staging/production APIs, so misconfiguration or cleanup failures could consume quota or leave resources behind. Other changes are low-risk doc/link/typing fixes.
Overview
Adds a new
cloud-vm:stresscommand that repeatedly provisions Cloud VMs viaPOST /api/vm, attaches via WebSocket daemon RPC, browser-proxies a/healthzrequest, then attempts best-effort VM + Stack test-user cleanup and emits a JSON result summary/exit code.Docs are updated to recommend running this stress check before provider default rollouts/rollbacks and to clarify Freestyle default-provider operational guidance. Separately fixes a couple of production/lint blockers: SSR-safe DevPanel positioning, anchorable docs sections + internal links migrated to Next
Link, and strongerbun:testTypeScript declarations.Reviewed by Cursor Bugbot for commit 898fd7b. Bugbot is set up for automated code reviews on this repo. Configure here.
Summary by cubic
Adds a Cloud VM default‑provider stress check and documents Freestyle as the default in staging/production (E2B kept as rollback). Also improves SSR safety and docs anchors, and tightens test types.
New Features
cloud-vm:stress(scripts/cloud-vm/stress-vm-api.mjs) to create VMs, attach via daemon RPC, proxy/healthz, then clean up; supports--count,--concurrency,--provider (default|e2b|freestyle), timeouts, and graceful SIGINT/SIGTERM cleanup; outputs a JSON summary with provider/image counts and cleanup stats. Uses@stackframe/jsandws.Bug Fixes
Linkand added anchor IDs to docs for deep links.bun:testTypeScript types (lifecycle hooks, focused/skipped tests, matchers, mocks) to clear lint/type errors.Written for commit 898fd7b. Summary will update on new commits.
Summary by CodeRabbit
Bug Fixes
Chores
Documentation
Tests