Skip to content
22 changes: 15 additions & 7 deletions docs/openapi/monitoring-api.json
Original file line number Diff line number Diff line change
Expand Up @@ -6412,7 +6412,9 @@
"enum": [
"DASHBOARD",
"CLI",
"TERRAFORM"
"TERRAFORM",
"MCP",
"API"
]
}
},
Expand Down Expand Up @@ -23307,11 +23309,13 @@
},
"managedBy": {
"type": "string",
"description": "Who manages this monitor: DASHBOARD or CLI",
"description": "Source that created/owns this monitor: DASHBOARD, CLI, TERRAFORM, MCP, or API. Use the value matching your surface so audit logs, drift detection, and analytics attribute correctly.",
"enum": [
"DASHBOARD",
"CLI",
"TERRAFORM"
"TERRAFORM",
"MCP",
"API"
]
},
"environmentId": {
Expand Down Expand Up @@ -27194,11 +27198,13 @@
},
"managedBy": {
"type": "string",
"description": "Management source: DASHBOARD or CLI",
"description": "Source that created/owns this monitor: DASHBOARD, CLI, TERRAFORM, MCP, or API",
"enum": [
"DASHBOARD",
"CLI",
"TERRAFORM"
"TERRAFORM",
"MCP",
"API"
]
},
"createdAt": {
Expand Down Expand Up @@ -33345,12 +33351,14 @@
},
"managedBy": {
"type": "string",
"description": "New management source; null preserves current",
"description": "New ownership source: DASHBOARD, CLI, TERRAFORM, MCP, or API; null preserves current value",
"nullable": true,
"enum": [
"DASHBOARD",
"CLI",
"TERRAFORM"
"TERRAFORM",
"MCP",
"API"
]
},
"environmentId": {
Expand Down
79 changes: 78 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,84 @@
"@oclif/plugin-help",
"@oclif/plugin-not-found"
],
"topicSeparator": " "
"topicSeparator": " ",
"topics": {
"alert-channels": {
"description": "Manage alert channels (slack, webhook, email, pagerduty, opsgenie, teams, discord)"
},
"api-keys": {
"description": "Manage API keys for the DevHelm public API"
},
"auth": {
"description": "Manage CLI authentication, contexts, and current identity"
},
"auth:context": {
"description": "Switch between saved CLI contexts (workspace + token pairs)"
},
"data": {
"description": "Inspect runtime data — service catalog status, monitor results, and uptime windows"
},
"data:services": {
"description": "Look up the current status and metadata of upstream services"
},
"dependencies": {
"description": "Track upstream service dependencies (Stripe, GitHub, etc.) for incident correlation"
},
"environments": {
"description": "Manage logical environments (production, staging, …) used to scope monitors and policies"
},
"forensics": {
"description": "Inspect detection forensics: rule evaluations, state transitions, policy snapshots, check traces"
},
"incidents": {
"description": "Create, inspect, and resolve incidents"
},
"monitors": {
"description": "Manage HTTP, TCP, DNS, ICMP, MCP, and heartbeat monitors"
},
"monitors:versions": {
"description": "Inspect historical monitor configuration versions"
},
"notification-policies": {
"description": "Manage escalation policies that route incidents to alert channels"
},
"resource-groups": {
"description": "Manage resource groups bundling monitors and services for shared health rollups"
},
"secrets": {
"description": "Manage workspace secrets used in monitor configurations and headers"
},
"skills": {
"description": "Generate and install Cursor / Claude agent skill packages for DevHelm"
},
"state": {
"description": "Manage the local deploy state file (.devhelm/state.json)"
},
"status-pages": {
"description": "Manage public status pages with components, branding, custom domains, and incidents"
},
"status-pages:components": {
"description": "Manage components on a status page (monitor, group, or static rows)"
},
"status-pages:domains": {
"description": "Manage custom domains attached to a status page"
},
"status-pages:groups": {
"description": "Manage component groups that organise rows on a status page"
},
"status-pages:incidents": {
"description": "Manage status-page-only incidents (separate from monitor-driven incidents)"
},
"status-pages:subscribers": {
"description": "Manage email and webhook subscribers for a status page"
},
"tags": {
"description": "Manage tags applied to monitors for filtering, grouping, and routing"
},
"webhooks": {
"description": "Manage outbound webhook subscriptions for monitor and incident lifecycle events"
}
}
},
"engines": {
"node": ">=18.0.0"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
| `frequencySeconds` | integer (int32) | | ✓ | Check frequency in seconds (30–86400); null defaults to plan minimum (60s on most paid plans) |
| `enabled` | boolean | | ✓ | Whether the monitor is active (default: true) |
| `regions` | string[] | | ✓ | Probe regions to run checks from, e.g. us-east, eu-west |
| `managedBy` | "DASHBOARD" \| "CLI" \| "TERRAFORM" | ✓ | | Who manages this monitor: DASHBOARD or CLI |
| `managedBy` | "DASHBOARD" \| "CLI" \| "TERRAFORM" \| "MCP" \| "API" | ✓ | | Source that created/owns this monitor: DASHBOARD, CLI, TERRAFORM, MCP, or API. Use the value matching your surface so audit logs, drift detection, and analytics attribute correctly. |
| `environmentId` | string (uuid) | | ✓ | Environment to associate with this monitor |
| `assertions` | CreateAssertionRequest[] | | ✓ | Assertions to evaluate against each check result |
| `auth` | any | | ✓ | |
Expand All @@ -30,7 +30,7 @@
| `frequencySeconds` | integer (int32) | | ✓ | New check frequency in seconds (30–86400); null preserves current |
| `enabled` | boolean | | ✓ | Enable or disable the monitor; null preserves current |
| `regions` | string[] | | ✓ | New probe regions; null preserves current |
| `managedBy` | "DASHBOARD" \| "CLI" \| "TERRAFORM" | | ✓ | New management source; null preserves current |
| `managedBy` | "DASHBOARD" \| "CLI" \| "TERRAFORM" \| "MCP" \| "API" | | ✓ | New ownership source: DASHBOARD, CLI, TERRAFORM, MCP, or API; null preserves current value |
| `environmentId` | string (uuid) | | ✓ | New environment ID; null preserves current (use clearEnvironmentId to unset) |
| `clearEnvironmentId` | boolean | | ✓ | Set to true to remove the environment association |
| `assertions` | CreateAssertionRequest[] | | ✓ | Replace all assertions; null preserves current |
Expand All @@ -52,7 +52,7 @@
| `frequencySeconds` | integer (int32) | ✓ | | Check frequency in seconds (30–86400) |
| `enabled` | boolean | ✓ | | Whether the monitor is active |
| `regions` | string[] | ✓ | | Probe regions where checks are executed |
| `managedBy` | "DASHBOARD" \| "CLI" \| "TERRAFORM" | ✓ | | Management source: DASHBOARD or CLI |
| `managedBy` | "DASHBOARD" \| "CLI" \| "TERRAFORM" \| "MCP" \| "API" | ✓ | | Source that created/owns this monitor: DASHBOARD, CLI, TERRAFORM, MCP, or API |
| `createdAt` | string (date-time) | ✓ | | Timestamp when the monitor was created |
| `updatedAt` | string (date-time) | ✓ | | Timestamp when the monitor was last updated |
| `assertions` | MonitorAssertionDto[] | | ✓ | Assertions evaluated against each check result; null on list responses |
Expand Down
26 changes: 23 additions & 3 deletions src/commands/deploy/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export default class Deploy extends Command {
'<%= config.bin %> deploy --yes',
'<%= config.bin %> deploy -f monitors.yml',
'<%= config.bin %> deploy --prune --yes',
'<%= config.bin %> deploy --prune-org-cli --yes',
'<%= config.bin %> deploy --prune-all --yes',
'<%= config.bin %> deploy --dry-run',
'<%= config.bin %> deploy --dry-run --detailed-exitcode',
Expand All @@ -36,11 +37,21 @@ export default class Deploy extends Command {
default: false,
}),
prune: Flags.boolean({
description: 'Delete CLI-managed resources not present in config',
description:
'Delete resources tracked in this config\'s .devhelm/state.json that are absent from YAML. ' +
'Safe in multi-config orgs — does NOT touch CLI-managed resources from other configs.',
default: false,
}),
'prune-org-cli': Flags.boolean({
description:
'Also delete CLI-managed resources elsewhere in the org that are absent from YAML. ' +
'Use with caution in shared workspaces; widens --prune to the legacy org-wide CLI scope.',
default: false,
}),
'prune-all': Flags.boolean({
description: 'Delete ALL resources not in config, including those not managed by the CLI (use with caution)',
description:
'Delete ALL resources not in YAML, including dashboard- and Terraform-managed ones. ' +
'The most destructive option; intended for single-tenant workspaces or scripted teardowns.',
default: false,
}),
'dry-run': Flags.boolean({
Expand Down Expand Up @@ -155,7 +166,16 @@ export default class Deploy extends Command {
const currentChildren = await prefetchChildSnapshots(config, refs, client)
const changeset = await diff(
config, refs,
{prune: flags.prune || flags['prune-all'], pruneAll: flags['prune-all']},
{
// `--prune` is the safe default (state-scoped). The wider scopes
// imply `--prune` so users don't have to combine flags to enable
// the broader behaviour — `--prune-org-cli` deletes state-tracked
// resources AND legacy CLI-managed orphans, and `--prune-all`
// also picks up dashboard- and Terraform-managed resources.
prune: flags.prune || flags['prune-org-cli'] || flags['prune-all'],
pruneOrgCli: flags['prune-org-cli'] || flags['prune-all'],
pruneAll: flags['prune-all'],
},
currentState,
currentChildren,
)
Expand Down
49 changes: 49 additions & 0 deletions src/commands/monitors/set-channels.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import {Command, Flags} from '@oclif/core'
import {globalFlags, buildClient} from '../../lib/base-command.js'
import {parseAlertChannelsFlag, setAlertChannels} from '../../lib/monitor-alert-channels.js'
import {uuidArg} from '../../lib/validators.js'

/**
* Replace the alert channel set linked to a monitor.
*
* Standalone subcommand for the common workflow of attaching channels
* to an existing monitor without re-running `monitors update`. Pass
* `--channel-ids ""` (empty) to clear all channels — matching the
* semantics of the underlying `PUT /api/v1/monitors/{id}/alert-channels`
* which always replaces the full list.
*/
export default class MonitorsSetChannels extends Command {
static description = 'Replace the alert channel set linked to a monitor'

static examples = [
'<%= config.bin %> monitors set-channels <id> --channel-ids ch-1,ch-2',
'<%= config.bin %> monitors set-channels <id> --channel-ids ""',
]

static args = {
id: uuidArg({description: 'monitor id', required: true}),
}

static flags = {
...globalFlags,
'channel-ids': Flags.string({
description:
'Comma-separated alert channel IDs (replaces current list; pass empty string to clear all channels)',
required: true,
}),
}

async run() {
const {args, flags} = await this.parse(MonitorsSetChannels)
const client = buildClient(flags)
const ids = parseAlertChannelsFlag(flags['channel-ids']) ?? []
await setAlertChannels(args.id as string, ids, client)
if (ids.length === 0) {
this.log(`Cleared all alert channels from monitor '${args.id}'.`)
} else {
this.log(
`Linked ${ids.length} alert channel(s) to monitor '${args.id}': ${ids.join(', ')}`,
)
}
}
}
8 changes: 5 additions & 3 deletions src/lib/api-zod.generated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -672,7 +672,7 @@ const CreateMonitorRequest = z
frequencySeconds: z.number().int().nullish(),
enabled: z.boolean().nullish(),
regions: z.array(z.string()).nullish(),
managedBy: z.enum(["DASHBOARD", "CLI", "TERRAFORM"]),
managedBy: z.enum(["DASHBOARD", "CLI", "TERRAFORM", "MCP", "API"]),
environmentId: z.string().uuid().nullish(),
assertions: z.array(CreateAssertionRequest).nullish(),
auth: MonitorAuthConfig.nullish(),
Expand All @@ -697,7 +697,9 @@ const UpdateMonitorRequest = z
frequencySeconds: z.number().int().nullable(),
enabled: z.boolean().nullable(),
regions: z.array(z.string()).nullable(),
managedBy: z.enum(["DASHBOARD", "CLI", "TERRAFORM"]).nullable(),
managedBy: z
.enum(["DASHBOARD", "CLI", "TERRAFORM", "MCP", "API"])
.nullable(),
environmentId: z.string().uuid().nullable(),
clearEnvironmentId: z.boolean().nullable(),
assertions: z.array(CreateAssertionRequest).nullable(),
Expand Down Expand Up @@ -2219,7 +2221,7 @@ const MonitorDto = z
frequencySeconds: z.number().int(),
enabled: z.boolean(),
regions: z.array(z.string()),
managedBy: z.enum(["DASHBOARD", "CLI", "TERRAFORM"]),
managedBy: z.enum(["DASHBOARD", "CLI", "TERRAFORM", "MCP", "API"]),
createdAt: z.string().datetime({ offset: true }),
updatedAt: z.string().datetime({ offset: true }),
assertions: z.array(MonitorAssertionDto).nullish(),
Expand Down
14 changes: 7 additions & 7 deletions src/lib/api.generated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3188,10 +3188,10 @@ export interface components {
/** @description Probe regions to run checks from, e.g. us-east, eu-west */
regions?: string[] | null;
/**
* @description Who manages this monitor: DASHBOARD or CLI
* @description Source that created/owns this monitor: DASHBOARD, CLI, TERRAFORM, MCP, or API. Use the value matching your surface so audit logs, drift detection, and analytics attribute correctly.
* @enum {string}
*/
managedBy: "DASHBOARD" | "CLI" | "TERRAFORM";
managedBy: "DASHBOARD" | "CLI" | "TERRAFORM" | "MCP" | "API";
/**
* Format: uuid
* @description Environment to associate with this monitor
Expand Down Expand Up @@ -4834,10 +4834,10 @@ export interface components {
/** @description Probe regions where checks are executed */
regions: string[];
/**
* @description Management source: DASHBOARD or CLI
* @description Source that created/owns this monitor: DASHBOARD, CLI, TERRAFORM, MCP, or API
* @enum {string}
*/
managedBy: "DASHBOARD" | "CLI" | "TERRAFORM";
managedBy: "DASHBOARD" | "CLI" | "TERRAFORM" | "MCP" | "API";
/**
* Format: date-time
* @description Timestamp when the monitor was created
Expand Down Expand Up @@ -7181,10 +7181,10 @@ export interface components {
/** @description New probe regions; null preserves current */
regions?: string[] | null;
/**
* @description New management source; null preserves current
* @description New ownership source: DASHBOARD, CLI, TERRAFORM, MCP, or API; null preserves current value
* @enum {string|null}
*/
managedBy?: "DASHBOARD" | "CLI" | "TERRAFORM" | null;
managedBy?: "DASHBOARD" | "CLI" | "TERRAFORM" | "MCP" | "API" | null;
/**
* Format: uuid
* @description New environment ID; null preserves current (use clearEnvironmentId to unset)
Expand Down Expand Up @@ -12790,7 +12790,7 @@ export interface operations {
/** @description Filter by monitor type */
type?: "HTTP" | "DNS" | "MCP_SERVER" | "TCP" | "ICMP" | "HEARTBEAT";
/** @description Filter by managed-by source */
managedBy?: "DASHBOARD" | "CLI" | "TERRAFORM";
managedBy?: "DASHBOARD" | "CLI" | "TERRAFORM" | "MCP" | "API";
/** @description Filter by tag names, comma-separated (e.g. prod,critical) */
tags?: string;
/** @description Case-insensitive name search */
Expand Down
Loading
Loading