chore: regen API types + 5 DevEx round-3 CLI fixes#28
Merged
Conversation
The previous auto-regen commit refreshed `api.generated.ts` and the checked-in OpenAPI spec, but did not propagate the new `MCP` / `API` `managedBy` enum values into the downstream generated artifacts. This runs `npm run zodgen` and `npm run skillgen` so all generated files line up with the spec and the openapi-drift skill test passes. Co-authored-by: Cursor <cursoragent@cursor.com>
`devhelm monitors create --type HTTP …` without `--regions` previously hit the API and surfaced an unhelpful `400: At least one region is required for HTTP monitors` (DevEx P1.Bug9). Most users hit this on their first `monitors create` invocation, since region selection is a plan-tier concern they shouldn't have to think about for a sanity check. When `--type` is one of HTTP, TCP, DNS, ICMP, HTTP_HEADLESS, or HTTP_BROWSER and `--regions` is omitted, default to `["us-east"]` and print a one-line stderr notice so the choice isn't invisible. HEARTBEAT (push-based) and MCP_SERVER (region semantics still in flux) are intentionally excluded — both have legitimate "no regions" semantics. Co-authored-by: Cursor <cursoragent@cursor.com>
Follow-up to the previous regen commits — descriptions.generated.ts is produced by `node scripts/extract-descriptions.mjs` from the same OpenAPI spec and was missed in the auto-regen run. The diff is purely the updated `managedBy` description (DASHBOARD, CLI, TERRAFORM, MCP, or API). Co-authored-by: Cursor <cursoragent@cursor.com>
DevEx P1.Bug2: there was no way to create a monitor with assertions in
a single CLI call. Users had to `monitors create`, then call the
assertions API one assertion at a time — which the CLI didn't expose at
all, so most people gave up and used the dashboard.
This adds a repeatable `--assertion` flag to `monitors create` that
accepts either:
- JSON form (any of the ~40 assertion types in the spec):
--assertion '{"severity":"fail","config":{"type":"status_code",
"expected":"200","operator":"equals"}}'
- Shorthand DSL for the three most common assertions:
--assertion 'status_code=200' → fail / status_code equals 200
--assertion 'response_time<5000' → warn / thresholdMs 5000
--assertion 'ssl_expiry>=14' → warn / minDaysRemaining 14
Each parsed assertion is POSTed to `/api/v1/monitors/{id}/assertions`
after the monitor is created. If any POST fails, the monitor is deleted
so users aren't left with a half-configured resource. Best-effort
rollback messaging mentions the orphaned id when DELETE itself fails.
Also adds `afterCreate` / `afterUpdate` hooks to `crud-commands.ts` so
post-create side effects can be wired without forking the CRUD factory
(used here for assertions; reused in the next commit for alert channels).
Co-authored-by: Cursor <cursoragent@cursor.com>
DevEx P1.Bug3: there was no way to attach an alert channel to a monitor
from the CLI. Users had to switch to the dashboard, which broke the
"deploy from CLI / test from dashboard" loop most teams use.
Two surfaces ship together so each common workflow has a one-liner:
- `monitors create --alert-channels ch-1,ch-2`
Comma-separated channel IDs. After the create succeeds, the CLI calls
`PUT /api/v1/monitors/{id}/alert-channels`. On failure the monitor is
deleted (mirrors the assertion rollback added in the previous commit).
- `monitors update --alert-channels ch-1,ch-2`
Same flag on update. An empty string explicitly clears all channels;
omitting the flag preserves the current set (no PUT issued).
- `monitors set-channels <id> --channel-ids ch-1,ch-2`
Standalone subcommand for the "I just want to attach channels to this
existing monitor" workflow. Required flag, empty string clears.
The two `--alert-channels` flag descriptions differ deliberately: on
create it warns about rollback semantics, on update it documents the
clear-by-empty-string behaviour.
Co-authored-by: Cursor <cursoragent@cursor.com>
DevEx P0.Bug1 (multi-team safety): the previous `--prune` deleted any
CLI-managed resource org-wide that wasn't in the local devhelm.yml.
Two teams sharing one workspace ran into this immediately — running
`deploy --prune --yes` in their own config silently destroyed the
other team's monitors because both were tagged `managedBy: CLI`.
New scope semantics:
- `--prune` (NEW DEFAULT): only deletes resources tracked in this
config's `.devhelm/state.json`. Other CLI-managed resources are left
alone — they belong to a different config.
- `--prune-org-cli`: legacy `--prune` behaviour — also deletes
CLI-managed resources elsewhere in the org. Required for cleanup
after `state.json` was rotated, or when migrating between configs.
- `--prune-all`: unchanged. Deletes everything not in YAML, including
dashboard- and Terraform-managed resources.
Plan output groups destroys by scope so multi-team users can tell at a
glance which deletes are theirs vs widened by `--prune-org-cli`:
Tracked by this config:
- monitor "mine"
Other CLI-managed resources:
- monitor "theirs"
When every delete shares one scope (the common case) the headers are
omitted and the plan looks identical to before.
Each `Change` of action `delete` now carries a `pruneScope` field
('state' | 'org-cli' | 'org-all') for downstream JSON consumers (CI,
dashboards, automation). Existing tests that exercised the legacy
broad behaviour were retargeted at `--prune-org-cli`; new tests cover
the state-scoped default and the grouped plan output.
Co-authored-by: Cursor <cursoragent@cursor.com>
…ook-url
DevEx P1.Bug7 + P1.Bug8 (CLI polish):
1. Topic descriptions in `--help` previously inherited the first
subcommand's description ("monitors → Create a new monitor",
"alert-channels → Create a new alert channel"). Add explicit
per-topic descriptions in `package.json`'s `oclif.topics` for every
top-level topic and the nested subtopics under `auth`, `data`,
`monitors`, and `status-pages`.
2. CRUD command descriptions hardcoded `Get a ${name}` / `Update a ${name}`
/ `Delete a ${name}`, which produced ungrammatical "a alert channel",
"a API key", "a environment", "a incident". Add an `aOrAn(word)`
helper to `crud-commands.ts` that picks the article based on the
first letter and use it from get/update/delete factories. Tested
directly so future resource names with vowel starts stay covered.
3. The `--webhook-url` flag on `alert-channels create` / `update`
inherited the OpenAPI description "Slack incoming webhook URL", but
the same flag works for webhook, slack, discord, and teams channels.
Override it locally with "Webhook URL (used by webhook, slack,
discord, teams channel types)" so users don't think it's slack-only.
Co-authored-by: Cursor <cursoragent@cursor.com>
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.
Summary
Builds on the auto-regen of API types (the new
MCP/APImanagedByenum values) with five DevEx round-3 CLI fixes. Each fix is its own commit so review can be done incrementally.Commits
chore: update generated API types from latest spec(auto-regen) — refreshedapi.generated.tsand the checked-in OpenAPI spec to addMCP/APIto themanagedByenum.chore: complete API regen (zod schemas + spec-facts + skill refs)— propagated the enum intoapi-zod.generated.ts,spec-facts.generated.ts, and the openapi-drift skill reference so the generated artifacts line up with the spec.fix(monitors): default --regions to us-east for probe-driven types(P1.Bug9) —devhelm monitors create --type HTTP …no longer errors with400: At least one region is required for HTTP monitors. Probe-driven types (HTTP, TCP, DNS, ICMP, HTTP_HEADLESS, HTTP_BROWSER) get[us-east]as a fallback with a stderr notice. HEARTBEAT and MCP_SERVER are deliberately excluded.chore: regenerate field descriptions for MCP/API enum— follow-up regen ofdescriptions.generated.ts(also auto-derived from the spec).feat(monitors): add --assertion flag for inline assertions on create(P1.Bug2) — repeatable--assertionflag onmonitors createaccepts JSON or a shorthand DSL (status_code=200,response_time<5000,ssl_expiry>=14). Each assertion is POSTed after the monitor is created; failures roll back the monitor.feat(monitors): wire --alert-channels and add monitors set-channels(P1.Bug3) —--alert-channels ch-1,ch-2onmonitors createandupdate, plus a standalonemonitors set-channels <id> --channel-ids …subcommand. Empty string explicitly clears all channels on update / set-channels; failed attach on create rolls back the monitor.fix(deploy): scope --prune to local state file; add --prune-org-cli(P0.Bug1, multi-team safety) —--prunenow only deletes resources tracked in this config's.devhelm/state.json, so two teams sharing one workspace can no longer destroy each other's monitors. New--prune-org-clipreserves the legacy org-wide CLI scope;--prune-allis unchanged. Plan output groups destroys by scope ("Tracked by this config:" vs "Other CLI-managed resources:") when scopes differ.chore(cli): polish topic descriptions, fix a/an typos, clarify --webhook-url(P1.Bug7 + P1.Bug8) — explicit per-topic descriptions inpackage.jsonsomonitorsno longer reads "Create a new monitor";aOrAn()helper incrud-commands.tsso we get "Delete an alert channel" / "Update an environment" instead of "a alert channel" / "a environment"; literal description for--webhook-url(used by webhook, slack, discord, teams).Test plan
npm run lintcleannpm run typecheckcleannpm test— 978 passednpm run buildrebuilds manifest & skill refs cleanlynode bin/run.js --helpshows polished topic descriptionsnode bin/run.js monitors create --helpshows--assertion,--alert-channels, and corrected article ("Create a new monitor")node bin/run.js monitors set-channels --helpis registerednode bin/run.js alert-channels delete --helpreads "Delete an alert channel"node bin/run.js deploy --helpshows the new--prune-org-cliflag and updated--prunedescription