Connection health checks (liveness) with OpenAPI backing#1108
Connection health checks (liveness) with OpenAPI backing#1108RhysSullivan wants to merge 1 commit into
Conversation
15950e9 to
522f93b
Compare
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
executor-marketing | d1aa3ea | Commit Preview URL Branch Preview URL |
Jun 26 2026, 02:28 AM |
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
executor-cloud | d1aa3ea | Jun 26 2026, 02:30 AM |
Cloudflare preview
Sign-in is Cloudflare Access (one-time PIN to an allowed email). The preview has its own database and encryption key; it is destroyed when this PR closes. |
@executor-js/cli
@executor-js/config
@executor-js/execution
@executor-js/sdk
@executor-js/codemode-core
@executor-js/runtime-quickjs
@executor-js/plugin-file-secrets
@executor-js/plugin-graphql
@executor-js/plugin-keychain
@executor-js/plugin-mcp
@executor-js/plugin-onepassword
@executor-js/plugin-openapi
executor
commit: |
Greptile SummaryThis PR introduces connection health checks (liveness probes) backed by OpenAPI operations. A connection declares one authenticated operation; its HTTP response is mapped to
Confidence Score: 5/5Safe to merge. The core vocabulary, ranking, HTTP classification, and probe dispatch are all straightforward and well-tested by the new e2e scenarios. The health-check spec save in the add-connection modal is a secondary UX path (the primary key validation still works correctly), and a silent failure there is non-destructive. No data integrity, security, or correctness issues were found in the probe dispatch, HTTP status classification, config read-modify-write, or the non-modal sheet design. packages/react/src/components/add-account-modal.tsx — the inline spec save after a healthy probe has no error feedback path. Important Files Changed
Sequence Diagram%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant UI as React UI
participant API as Connections API
participant Plugin as OpenAPI Plugin
participant Upstream as Upstream API
Note over UI,Upstream: Add Connection - inline key check
UI->>API: POST /connections/validate (credential + spec)
API->>Plugin: connections.validate → checkHealth(spec override)
Plugin->>Upstream: HTTP probe (e.g. GET /me with Bearer token)
Upstream-->>Plugin: 200 / 401 / other
Plugin-->>API: "HealthCheckResult {status, httpStatus}"
API-->>UI: HealthCheckResult
alt "status = healthy AND inlineSpec set"
UI->>API: PUT /integrations/:slug/health-check (spec)
API-->>UI: saved
end
Note over UI,Upstream: Check saved connection health
UI->>API: POST /connections/:owner/:integration/:name/health
API->>Plugin: connections.checkHealth
Plugin->>Upstream: HTTP probe (from stored HealthCheckSpec)
Upstream-->>Plugin: HTTP status
Plugin-->>API: HealthCheckResult
API-->>UI: "{status: healthy|expired|degraded|unknown}"
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant UI as React UI
participant API as Connections API
participant Plugin as OpenAPI Plugin
participant Upstream as Upstream API
Note over UI,Upstream: Add Connection - inline key check
UI->>API: POST /connections/validate (credential + spec)
API->>Plugin: connections.validate → checkHealth(spec override)
Plugin->>Upstream: HTTP probe (e.g. GET /me with Bearer token)
Upstream-->>Plugin: 200 / 401 / other
Plugin-->>API: "HealthCheckResult {status, httpStatus}"
API-->>UI: HealthCheckResult
alt "status = healthy AND inlineSpec set"
UI->>API: PUT /integrations/:slug/health-check (spec)
API-->>UI: saved
end
Note over UI,Upstream: Check saved connection health
UI->>API: POST /connections/:owner/:integration/:name/health
API->>Plugin: connections.checkHealth
Plugin->>Upstream: HTTP probe (from stored HealthCheckSpec)
Upstream-->>Plugin: HTTP status
Plugin-->>API: HealthCheckResult
API-->>UI: "{status: healthy|expired|degraded|unknown}"
Reviews (5): Last reviewed commit: "feat: connection health checks (liveness..." | Re-trigger Greptile |
| // Cross-target (browser): the UI side of connection health checks, the feature | ||
| // that answers "has this credential expired?" (the Google 7-day dev-token case) | ||
| // and, optionally, "whose account is this?". These scenarios pin the redesign: | ||
| // | ||
| // 1. Edit sheet, WITH identity: the operation and identity-field pickers are | ||
| // comboboxes (the identity options are the operation's typed response | ||
| // fields). A live preview probes a pasted key and shows the actual response | ||
| // (path -> value rows) plus "Resolves to: <identity>". The saved check then | ||
| // drives "Check now" on a live connection: healthy, then expired once the | ||
| // upstream revokes the key. | ||
| // 2. Edit sheet, NO identity: picking "None - health check only" leaves a pure | ||
| // alive/expired probe. The preview still shows the response sample but no | ||
| // "Resolves to" line; the persisted spec carries no identity field. | ||
| // 3. Add screen: the same check is configurable while adding the integration, | ||
| // and it persists. | ||
| // 4. Connect modal: key-first. The credential field comes before the display | ||
| // name; a valid key auto-fills the name from the resolved identity (and for | ||
| // a no-identity integration leaves the name for the user). | ||
| // | ||
| // The upstream API is a real node:http server on 127.0.0.1 that gates `GET /me` |
There was a problem hiding this comment.
Missing
needs: ["browser"] capability declaration
All 6 scenarios in this file depend on the Browser service but are defined with {} options. Per e2e/AGENTS.md, every scenario that uses browser capabilities must declare needs: ["browser"] (e.g., test("...", { needs: ["browser"] }, ({ browser }) => ...)). Without the declaration the test runner may not provision the browser context, causing the tests to fail or be silently skipped in environments that gate capability provisioning.
Context Used: e2e/AGENTS.md (source)
522f93b to
59fd53f
Compare
dc99124 to
d6b3eac
Compare
A connection can declare ONE authenticated operation that answers "is this
credential still alive?". The probe runs the operation, maps the HTTP status to
a health state (2xx healthy, 401/403 expired, other degraded, unset unknown),
and reports it. Built for short-lived OAuth tokens (Google's 7-day dev-token
revocation): a credential that authenticated yesterday and now 401s reads as
expired.
Core owns the shared vocabulary (HealthStatus, HealthCheckSpec, HealthCheckResult,
HealthCheckCandidate, classifyHttpStatus, candidate ranking); plugins own which
operation runs, stored in their opaque integration config and picked the same way
auth methods are. Dispatch adds integrations.healthCheck.{get,candidates,set} and
connections.{checkHealth,validate}.
OpenAPI backing: list ranked candidates (non-destructive GET first), persist the
chosen spec (merging over the raw config so provider supersets keep their keys),
and run the probe against a resolved credential.
React surfaces:
- a per-connection status dot + "Check now",
- a self-hiding operation editor (a searchable freeform combobox over the whole
spec),
- "Check the key works" in the Add Connection modal: probe the pasted key before
saving; when no check is configured yet, pick a read-only operation inline and
a healthy probe saves it as the integration's health check.
The operation picker is a base-ui combobox whose popup portals out of the
surrounding Radix modal, so this also carries the fixes that make it usable in a
modal: pointer-events on the popup, an outside-interaction guard on both the
dialog and the sheet (so clicking an option doesn't dismiss the modal), and a
non-modal editor sheet (so the popup list can scroll).
Covered by e2e: saved connection healthy -> expired; unconfigured -> unknown; the
operation picker filters a hundreds-long list on the edit sheet and add screen;
Add Connection checks the key against an inline-picked operation (by mouse) and
persists it; the editor-sheet combobox is clickable and scrollable.
d6b3eac to
d1aa3ea
Compare
Base of a 4-PR stack. A connection declares ONE authenticated operation that answers "is this credential still alive?". The probe maps HTTP status to a health state (2xx healthy, 401/403 expired, other degraded, unset unknown) — built for short-lived OAuth tokens (Google's 7-day dev-token revocation).
integrations.healthCheck.{get,candidates,set},connections.{checkHealth,validate}).The operation picker is a base-ui combobox whose popup portals out of the surrounding Radix modal, so this PR also carries the fixes that make it usable in a modal (folded in from the former combobox-sheet PR, since both the editor sheet and the Add Connection dialog need them): pointer-events on the popup, an outside-interaction guard on the dialog AND the sheet so clicking an option does not dismiss the modal, and a non-modal editor sheet so the popup list can scroll.
Built on the self-contained add-connection modal (#1126): the key-check state lives inside the modal view, so closing unmounts and resets it (no hand-reset).
e2e: saved connection healthy -> expired; unconfigured -> unknown; the operation picker filters a hundreds-long list on the edit sheet and add screen; Add Connection checks the key against an inline-picked operation BY MOUSE and persists it; the editor-sheet combobox is clickable and scrollable.