@checkstack/automation-frontend 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +496 -0
- package/package.json +18 -13
- package/src/components/AutomationGroupCombobox.tsx +133 -0
- package/src/components/RunAsServiceAccountPicker.tsx +170 -0
- package/src/editor/ActionEditor.tsx +180 -90
- package/src/editor/ActionListEditor.tsx +27 -1
- package/src/editor/AddActionDialog.tsx +15 -45
- package/src/editor/AddConditionDialog.tsx +86 -0
- package/src/editor/AddTriggerDialog.tsx +97 -0
- package/src/editor/AutomationDefinitionEditor.tsx +41 -2
- package/src/editor/ConditionEditor.tsx +359 -70
- package/src/editor/ConditionsEditor.tsx +113 -44
- package/src/editor/ItemSheet.tsx +51 -0
- package/src/editor/RunReplayPicker.tsx +97 -0
- package/src/editor/ScriptServicesBooter.tsx +57 -0
- package/src/editor/ScriptTestRenderer.tsx +150 -0
- package/src/editor/SystemEntityPicker.test.ts +37 -0
- package/src/editor/SystemEntityPicker.tsx +109 -0
- package/src/editor/TriggersEditor.tsx +345 -137
- package/src/editor/action-helpers.test.ts +107 -0
- package/src/editor/action-helpers.ts +72 -0
- package/src/editor/action-leaf-cards.tsx +105 -1
- package/src/editor/condition-kind.test.ts +126 -0
- package/src/editor/condition-kind.ts +130 -0
- package/src/editor/item-summary.test.ts +171 -0
- package/src/editor/item-summary.ts +210 -0
- package/src/editor/picker-dialog.tsx +156 -0
- package/src/editor/registry-context.tsx +9 -2
- package/src/editor/script-actions.test.ts +184 -0
- package/src/editor/script-actions.ts +146 -0
- package/src/editor/system-entity-picker.logic.ts +23 -0
- package/src/editor/template-completion.test.ts +22 -3
- package/src/editor/template-completion.ts +16 -8
- package/src/editor/template-helpers.ts +4 -0
- package/src/editor/trigger-helpers.test.ts +28 -0
- package/src/editor/trigger-helpers.ts +17 -0
- package/src/editor/useScriptDiagnostics.ts +108 -0
- package/src/index.tsx +26 -24
- package/src/pages/AutomationEditPage.tsx +116 -48
- package/src/pages/AutomationListPage.tsx +172 -123
- package/src/pages/automation-grouping.test.ts +87 -0
- package/src/pages/automation-grouping.ts +65 -0
- package/src/script-context.test.ts +142 -1
- package/src/script-context.ts +115 -0
- package/tsconfig.json +15 -0
- package/src/components/AutomationMenuItems.tsx +0 -37
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,501 @@
|
|
|
1
1
|
# @checkstack/automation-frontend
|
|
2
2
|
|
|
3
|
+
## 0.4.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 9dcc848: Plugin-owned AI tools: every domain plugin contributes its own AI tools (chat assistant + automation AI action), and `ai-backend` is platform-only.
|
|
8
|
+
|
|
9
|
+
Every plugin-specific AI tool is owned by the plugin whose domain it acts on, registered via that plugin's own `aiToolExtensionPoint` / `aiToolProjectionExtensionPoint` from its init - the same path an external plugin author uses. `ai-backend` no longer imports or depends on any capability plugin's `*-common`; the dependency direction is strictly plugin -> ai-platform. Pure helpers (`computeFieldDiff`, capability-summary, `ScriptContextKind`) live in `@checkstack/ai-common`.
|
|
10
|
+
|
|
11
|
+
Tools shipped:
|
|
12
|
+
|
|
13
|
+
- Health checks and automations: full CRUD - `healthcheck.propose` / `automation.propose` and `*.update` (`mutate`, deep-validated) and `*.delete` (`destructive`, always confirm-gated). `healthcheck.propose`'s dry-run calls the new deep `validateConfiguration` so propose-time validation matches apply-time. Assertions are validated against the collector's result schema and the canonical operator vocabulary. Capability-catalog tools (`ai.listCapabilities`, `ai.getCapabilitySchema`), script context tools (`ai.getScriptContext`, `ai.testScript`), and notify-subscriber tools (`healthcheck.notifySystemSubscribers` / `...GroupSubscribers`).
|
|
14
|
+
- Catalog: `catalog.createSystem` / `updateSystem` / `createGroup` / `updateGroup` (`mutate`), `catalog.deleteSystem` / `deleteGroup` (`destructive`), membership tools (`mutate`), plus `catalog.listSystems` / `listGroups` read projections.
|
|
15
|
+
- Incident: `incident.create` / `update` / `addUpdate` / `resolve` / `addLink` (`mutate`), `incident.delete` / `removeLink` (`destructive`), and `incident.get` / `incident.list` read projections.
|
|
16
|
+
- Maintenance: `maintenance.create` / `update` / `addUpdate` / `close` / `addLink` (`mutate`), `maintenance.delete` / `removeLink` (`destructive`), and `maintenance.list` / `get` read projections.
|
|
17
|
+
- Read projections for SLO (`slo.listObjectives`), dependency (`dependency.list`), incident (`incident.list`), healthcheck (`healthcheck.status`), and anomaly (`anomaly.explain`), each gated by the source procedure's own access rule and routed as the principal.
|
|
18
|
+
- Documentation grounding: `ai.searchDocs` / `ai.getDoc` over a build-time bundled docs index (BM25-ish ranking), so the assistant grounds how-to answers in Checkstack's own docs offline.
|
|
19
|
+
- URL introspection: `ai.probeUrl`, an SSRF-guarded read tool the assistant uses to inspect a real endpoint before drafting a health check. Update tools compute a before -> after field diff rendered on the confirm card (approve mode) or an "Applied" card (auto mode), so a change is never silent.
|
|
20
|
+
|
|
21
|
+
`ai_analyze` automation action (automation-backend, with an editor connection picker + audited tool calls): runs a bounded AI agent on the run context as the automation's `runAs` service account, so it can never exceed that identity's permissions; destructive tools are never offered; mutating tools auto-apply through the service account's client. Produces an `automation.analysis` artifact downstream actions can branch on. The agent loop is exposed as a headless `aiAgentRunnerRef` service so automation-backend can drive it without depending on ai-backend.
|
|
22
|
+
|
|
23
|
+
`notification.notifyForSubscription` is now callable by user / application principals holding `notification.send` (previously service-only). Every tool routes through the user-scoped client, so handler-side authorization is enforced exactly as a direct UI/RPC action; the resolver gate plus the propose/apply re-check at propose AND apply are the additional authority. A systemic authz regression test asserts every registered tool falls into exactly one safe authorization category.
|
|
24
|
+
|
|
25
|
+
A new `ai_transport` enum value `automation` records the AI action's tool calls in the `ai_tool_calls` audit log. No new durable state beyond that; each tool is a thin, deterministic wrapper over an existing RPC, so every pod behaves identically.
|
|
26
|
+
|
|
27
|
+
This is a beta minor.
|
|
28
|
+
|
|
29
|
+
- 9dcc848: Automations now run as a configured service account, removing implicit god-mode from the dispatch path.
|
|
30
|
+
|
|
31
|
+
BREAKING: every automation must declare a `runAs` application (service account). Previously every automation action ran as the trusted service client, bypassing all access-rule, per-resource, and team-scope checks - so an automation could touch any team's data. Now each automation runs as a bounded `application` principal, and every data-access call an action makes is authorized exactly as that identity. An automation with no `runAs` fails to run with a clear error rather than falling back to the trusted client; legacy automations must be assigned a service account before they run again.
|
|
32
|
+
|
|
33
|
+
What changed:
|
|
34
|
+
|
|
35
|
+
- New top-level field `runAs` on automations (a `run_as_application_id` column + create/update inputs; `AutomationSchema.runAs`). Required on create; GitOps sets it via the `run-as` metadata label.
|
|
36
|
+
- A new `coreServices.rpcClientAs(applicationId)` mints a short-lived, backend-signed app-principal token; the auth service resolves it LIVE to an `application` principal (reusing `enrichApplicationPrincipal`), so it flows through full `autoAuthMiddleware` enforcement. The dispatch engine threads this client into every action's `execute` as the required `context.rpcClient`.
|
|
37
|
+
- Bind authority (anti-escalation): a user may only bind an application whose access rules are a subset of their own (`isApplicationBindable`); `getBindableApplications` lists only bindable apps, and the create/update handlers enforce the check.
|
|
38
|
+
- `notification.sendTransactional` moves from service-only to access-gated (`notification.send`, a new access rule), so an automation's `runAs` can call the built-in `notify_user` / `notification.send` actions; trusted services still bypass via short-circuit.
|
|
39
|
+
- A "Run as (Service Account)" picker in the automation editor, populated from `getBindableApplications` (server-side filtered to bindable apps), seeding from the loaded `runAs` on edit and passing it into create + update. First-class teaching UX: an inline info banner, a blocked Save with an inline hint until one is chosen, and an empty state linking to the Applications admin + docs when none are bindable.
|
|
40
|
+
|
|
41
|
+
State and scale: `runAs` resolution is a pure read over shared tables; the app-principal token is self-contained and verified statelessly, so the per-run client is correct under horizontal scale.
|
|
42
|
+
|
|
43
|
+
This is a beta minor.
|
|
44
|
+
|
|
45
|
+
- 9dcc848: Cut initial-load JS: lazy plugin contributions, a hardened lazy-by-default contribution contract, on-demand Monaco, and a lighter icon/chart load.
|
|
46
|
+
|
|
47
|
+
- Lazy plugin route pages: each plugin's route `element` references a `React.lazy`-wrapped page rendered inside a shared `<Suspense>` boundary. Plugins still register synchronously, so nav, slots, commands, API factories, and `foreignSignals` are available on first paint. This moves ~37 route-page chunks (~600 KB) out of the entry; the entry chunk drops from ~2.4 MB to ~190 KB. Auth flow pages stay eager. The `@checkstack/scripts` scaffold template generates lazy route pages too.
|
|
48
|
+
- Hardened contribution contract (BREAKING, frontend plugin contract): plugins declare contributions lazily and let the framework own code-splitting, Suspense, and per-plugin error isolation. Routes use `load: () => import("./Page").then((m) => ({ default: m.Page }))` instead of `element: <Page />` (`element` is still accepted for the rare page that must paint without a chunk fetch; provide exactly one). Slot extensions accept either an eager `component` or a lazy `load`; new `getLazyContribution` + `ExtensionComponent` exports from `@checkstack/frontend-api` render either kind. This also fixes runtime-installed plugins: `ExtensionSlot` subscribes to the plugin registry, and the API registry rebuilds when the plugin set changes (`getPlugins()` returns an immutable snapshot via `useSyncExternalStore`). A per-plugin error boundary contains a bad contribution.
|
|
49
|
+
- On-demand Monaco: the `@checkstack/ui` barrel no longer pulls the `@codingame/*` / `monaco-languageclient` stack into the initial load. `CodeEditor` lazy-loads its Monaco-backed editor behind `React.lazy` + Suspense, `validateTypeScriptSources` imports the editor API via in-body `await import(...)`, and the "vscode services ready" signal moved to a Monaco-free module. The ~10 MB editor body loads only when a `CodeEditor` mounts. A `react-vendor` `manualChunks` split was added for stable vendor caching.
|
|
50
|
+
- lucide-react 1.x + lighter icons/charts (BREAKING for icon consumers): lucide-react unified from three drifting ranges to `^1.17.0`. lucide v1 removed brand icons, so the GitHub/GitLab marks are vendored in `@checkstack/ui` (`GithubIcon`, `GitlabIcon`, `brandIcons`); a new `IconName` type (`LucideIconName | BrandIconName`) in `@checkstack/common` is canonical, accepted by `AuthStrategy.icon` and the card components, so data-driven brand names keep working. `DynamicIcon` no longer eagerly imports lucide's ~1600-icon map (~1 MB) - it lives in a `React.lazy` `iconRegistry` chunk fetched on first data-driven render, while statically named-imported icons tree-shake normally. The recharts-backed health-check charts (~300 KB) and the `HealthCheckSystemOverview` drawer leave the initial load.
|
|
51
|
+
|
|
52
|
+
BREAKING CHANGES:
|
|
53
|
+
|
|
54
|
+
- Frontend plugin contract: routes/slot contributions are lazy-by-default (`load` instead of `element`/eager elements) as described above.
|
|
55
|
+
- Any external consumer importing a brand icon from `lucide-react` (e.g. `import { Github } from "lucide-react"`) must switch to the vendored `@checkstack/ui` brand icons or a custom SVG.
|
|
56
|
+
|
|
57
|
+
This is a beta minor.
|
|
58
|
+
|
|
59
|
+
- 9dcc848: Add the auto-generated, version-pinned `@checkstack/sdk` package + codegen, and serve its types live to the in-app editor.
|
|
60
|
+
|
|
61
|
+
- A new committed workspace package `@checkstack/sdk`, generated from the platform's source of truth by `scripts/generate-sdk.ts` (`generate:sdk` / `generate:sdk:check`): a fully-typed oRPC client (`createCheckstackClient`) over the REST surface with one `InferClient` per plugin contract, real script-authoring helpers (`@checkstack/sdk/healthcheck`, `@checkstack/sdk/integration`) whose runtime body is the same identity function the in-app runner injects, per-subpath `.d.ts` under the package `exports` map, and an editor-only ambient bundle. A `generate:sdk:check` CI guard fails when the committed SDK files drift from a fresh generation. The `@checkstack/sdk` version is stamped from `@checkstack/release` and MUST NOT appear in a changeset (a guard enforces this); the `@checkstack/release` bump here advances the release version so the generated SDK can be published later. The generated client also normalizes its base URL without a backtracking-prone regex, closing a CodeQL `js/polynomial-redos` finding.
|
|
62
|
+
- Live editor type injection: a new version-keyed route `GET /api/script-packages/sdk-types/:releaseVersion` (raw handler in `@checkstack/script-packages-backend`) serves the generated SDK editor bundle with `Cache-Control: private, max-age=1y, immutable`; the pure path-build/parse module lives in `@checkstack/script-packages-common`, shared by backend and frontend. A mismatched version returns `409` so the editor refetches and never serves stale types after an upgrade. The frontend `useSdkTypeInjection` hook fetches the bundle once per session and mounts it into Monaco via `addExtraLib`. Schema-narrowed `context.config` / `context.event.payload` editor types stay local; the package-resolving module declarations come from the one published `@checkstack/sdk` source.
|
|
63
|
+
|
|
64
|
+
BREAKING CHANGES: the script-authoring import surface moves from the bare `@checkstack/healthcheck` / `@checkstack/integration` virtual modules to the `@checkstack/sdk/healthcheck` / `@checkstack/sdk/integration` subpaths of the published `@checkstack/sdk` package. The old bare-name imports no longer resolve (an old import now errors in the editor, surfacing the migration). Existing scripts must update the module specifier:
|
|
65
|
+
|
|
66
|
+
- import { defineHealthCheck } from "@checkstack/healthcheck";
|
|
67
|
+
+ import { defineHealthCheck } from "@checkstack/sdk/healthcheck";
|
|
68
|
+
|
|
69
|
+
- import { defineIntegration } from "@checkstack/integration";
|
|
70
|
+
+ import { defineIntegration } from "@checkstack/sdk/integration";
|
|
71
|
+
|
|
72
|
+
The helper names and their runtime behaviour are unchanged - only the module specifier moves. The global (no-import) helper form continues to work unchanged.
|
|
73
|
+
|
|
74
|
+
This is a beta minor.
|
|
75
|
+
|
|
76
|
+
- 9dcc848: Align workspace dependency versions and migrate React Router to v7.
|
|
77
|
+
|
|
78
|
+
BREAKING CHANGES (React Router v7): All frontend packages now depend on `react-router-dom@^7.16.0`. Previously the workspace declared four divergent ranges (`^6.20.0`, `^6.22.0`, `^7.1.1`, `^7.14.2`), which resolved both `react-router@6` and `react-router@7` into a single bundle. Everything is now unified on v7. The public imports the app uses (`BrowserRouter`, `Routes`, `Route`, `Link`, `NavLink`, `MemoryRouter`, `useNavigate`, `useParams`, `useSearchParams`, `useLocation`) are unchanged between v6 and v7, so no source rewrites were required - but any out-of-tree plugin still on react-router v6 should upgrade to v7 (see the React Router v6 -> v7 upgrade guide) to share the host's single router instance via the import map.
|
|
79
|
+
|
|
80
|
+
Other unified ranges (no API change): `react` -> `^18.3.1`, the `@orpc/*` family (`contract`, `server`, `client`, `tanstack-query`, `openapi`, `zod`) -> `^1.14.4`, and `better-auth` -> `^1.6.13`.
|
|
81
|
+
|
|
82
|
+
Removed the pre-rename `@orpc/react-query` leftover from `@checkstack/frontend-api`; its `createRouterUtils` / `RouterUtils` / `ProcedureUtils` now come from `@orpc/tanstack-query` (the package already in use).
|
|
83
|
+
|
|
84
|
+
Stale in-range runtime deps pulled up to current published versions: `hono` `^4.12.23`, `@tanstack/react-query` (+devtools) `^5.100.14`, `date-fns` `^4.4.0`, `jose` `^6.2.3`, `tar` `^7.5.16`, `semver` `^7.8.1`, `@xyflow/react` `^12.11.0`.
|
|
85
|
+
|
|
86
|
+
### Patch Changes
|
|
87
|
+
|
|
88
|
+
- 9dcc848: Assorted bug fixes and small hardening across the platform.
|
|
89
|
+
|
|
90
|
+
- announcement-backend: `updateAnnouncement` now invalidates the active-announcements and admin-list caches (it was missing the `invalidateAllActive` / `invalidateListAll` calls), so an edited announcement no longer stays stale up to the 45s TTL.
|
|
91
|
+
- anomaly-backend: anomaly/drift state transitions (confirmations, recoveries, self-resolutions) now log at `debug` instead of info/warn - they are already surfaced via the `ANOMALY_STATE_CHANGED` signal, so logging them louder just added noise; genuine failure paths stay `warn`.
|
|
92
|
+
- backend: the `/api/:pluginId/*` dispatcher now populates `requestHeaders` on the per-request RPC context, so a handler that re-enters the router as the originating user (e.g. an AI tool's user-scoped client) can forward the caller's session cookie / bearer - previously the loopback failed with "Authentication required". Guarded by a real end-to-end integration test. The HTTP server idle timeout is also raised (default 255s, configurable via `CHECKSTACK_SERVER_IDLE_TIMEOUT_SECONDS`, clamped 0-255, reset on each streamed chunk) so long AI chat SSE turns are not severed mid-stream.
|
|
93
|
+
- backend: a request for an unknown plugin id (`/api/<unknown>/...`) now returns `404 Not Found` instead of `500` (and logs at warn, not error, since it is a client request) - an unknown _procedure_ on a known plugin already 404'd. The in-app docs namespace `/checkstack/*` now serves Starlight's own `404.html` with a real 404 status for a missing doc, instead of falling through to the SPA catch-all and 200-ing the app shell. Both guarded by tests.
|
|
94
|
+
- automation-common: remove polynomial-time backtracking from `toShellEnvKey`'s underscore-trim (CodeQL `js/polynomial-redos`); a negative look-behind anchors the trailing run, keeping the trim linear.
|
|
95
|
+
- common + script-packages-common: the pure transport-safe sandbox-policy schema (`sandboxPolicySchema` and its sub-schemas + inferred types) moved to `@checkstack/common` (the neutral base), removing two inverted deps that existed only to reach the shape; `@checkstack/backend-api` continues to re-export it. The schema is no longer exported from `@checkstack/script-packages-common`. Pure refactor, no behavior change.
|
|
96
|
+
- catalog-backend: reject duplicate system names (a `CONFLICT` on create/rename, enforced by a pre-write check AND a new DB unique index on `systems.name`, migration 0004 which first resolves pre-existing duplicates by suffixing).
|
|
97
|
+
- catalog-frontend: detail-page cleanups (use `<NotFound />` not `<AccessDenied />` on the not-found branch, a readable key/value metadata list via `normalizeMetadata`, runtime locale via `formatDate`); and stop the browse view re-rendering on every health report (adopt a new statuses report only when a value actually changed, via `healthStatusesEqual`, so rows stay stable and interactive).
|
|
98
|
+
- healthcheck-backend: fix the daily-rollup retention step failing with an `ON CONFLICT` mismatch (SQLSTATE 42P10) after `environmentId` joined the `health_check_aggregates` unique constraint - the rollup now groups by (day, environmentId, sourceId) and uses a single exported conflict-target constant (`DAILY_AGGREGATE_CONFLICT_TARGET`) kept in lock-step with the schema by a unit test.
|
|
99
|
+
- automation-frontend: the service-account picker's "Learn more" links are now absolute URLs to the deployed Astro docs site (they 404ed as in-app relative paths). The Monaco script editor double-init crash is fixed (serialized cold init, a guarded `monacoGuard` accessor, theme/type effects gated on `apiReady`).
|
|
100
|
+
- auth-frontend: bound the desktop user-menu popover height (`max-h-[var(--radix-popover-content-available-height)]` + `overflow-y-auto`) so it no longer clips on short viewports, and fold the standalone `Account > Profile` item into a focusable name/email header (`profileHref` on `UserMenu`); the now-empty `Account` group no longer renders.
|
|
101
|
+
- satellite-frontend: picked up via the sidebar-nav migration (account-only user menu).
|
|
102
|
+
|
|
103
|
+
(Related UI fixes - the Monaco editor following the app theme, the `DynamicOptionsField` no-flash fix, the shared `Spinner`, GFM tables, and the user-menu popover bound - land their `@checkstack/ui` bump in the UI/perf changesets where `@checkstack/ui` is already minored.)
|
|
104
|
+
|
|
105
|
+
This is a beta patch.
|
|
106
|
+
|
|
107
|
+
- 9dcc848: Move primary navigation into a left sidebar, and serve the user guide in-app.
|
|
108
|
+
|
|
109
|
+
Feature navigation (a ~20-item user-menu dropdown) now lives in a persistent left sidebar (a slide-over drawer on mobile), grouped by section with the active route highlighted; the user menu keeps only account actions. A route opts into the sidebar with new `nav` metadata (`{ group, icon, label?, order?, accessRule? }`) on its registration, co-located with path + access + title. The sidebar filters entries with the same access check as page guards. `@checkstack/common` gains `isAccessRuleSatisfied` and a centralized set of in-app doc slugs (`APP_DOC_SLUGS` + `docsPath`, with a test asserting each resolves to a real docs page); `@checkstack/auth-frontend` exports `useAccessRules`.
|
|
110
|
+
|
|
111
|
+
The backend now serves the Astro Starlight docs build same-origin at `/checkstack/*` (the same artifact deployed to GitHub Pages), so the user guide is available inside the app including for self-hosted / air-gapped installs (served verbatim, no rebuild, no link rewriting; from `CHECKSTACK_DOCS_DIST`, before the SPA catch-all, degrading gracefully when absent; the Docker image builds and ships `docs/dist`; Vite proxies `/checkstack` in dev). The "Docs" link is a shell-owned external sidebar entry under the Documentation group (book icon), opening `/checkstack/user-guide/` in a new tab; the group renders even when no plugin route contributes to it.
|
|
112
|
+
|
|
113
|
+
BREAKING (plugin authors): `UserMenuItemsSlot` is no longer the way to add navigation - registering a top user-menu item no longer surfaces it anywhere. Add `nav` to the page's route instead. `UserMenuItemsBottomSlot` (account items) is unchanged. All bundled plugins have been migrated.
|
|
114
|
+
|
|
115
|
+
This is a beta minor.
|
|
116
|
+
|
|
117
|
+
- Updated dependencies [9dcc848]
|
|
118
|
+
- Updated dependencies [9dcc848]
|
|
119
|
+
- Updated dependencies [9dcc848]
|
|
120
|
+
- Updated dependencies [9dcc848]
|
|
121
|
+
- Updated dependencies [9dcc848]
|
|
122
|
+
- Updated dependencies [9dcc848]
|
|
123
|
+
- Updated dependencies [9dcc848]
|
|
124
|
+
- Updated dependencies [9dcc848]
|
|
125
|
+
- Updated dependencies [9dcc848]
|
|
126
|
+
- Updated dependencies [9dcc848]
|
|
127
|
+
- Updated dependencies [9dcc848]
|
|
128
|
+
- Updated dependencies [9dcc848]
|
|
129
|
+
- Updated dependencies [9dcc848]
|
|
130
|
+
- Updated dependencies [9dcc848]
|
|
131
|
+
- Updated dependencies [9dcc848]
|
|
132
|
+
- Updated dependencies [9dcc848]
|
|
133
|
+
- Updated dependencies [9dcc848]
|
|
134
|
+
- @checkstack/ui@1.13.0
|
|
135
|
+
- @checkstack/auth-common@0.8.0
|
|
136
|
+
- @checkstack/automation-common@0.4.0
|
|
137
|
+
- @checkstack/catalog-common@2.3.0
|
|
138
|
+
- @checkstack/common@0.13.0
|
|
139
|
+
- @checkstack/template-engine@0.4.0
|
|
140
|
+
- @checkstack/frontend-api@0.7.0
|
|
141
|
+
- @checkstack/gitops-frontend@0.5.0
|
|
142
|
+
- @checkstack/script-packages-frontend@0.3.0
|
|
143
|
+
- @checkstack/secrets-frontend@0.2.0
|
|
144
|
+
- @checkstack/integration-common@0.7.0
|
|
145
|
+
- @checkstack/signal-frontend@0.2.0
|
|
146
|
+
|
|
147
|
+
## 0.3.0
|
|
148
|
+
|
|
149
|
+
### Minor Changes
|
|
150
|
+
|
|
151
|
+
- b995afb: Redesign the automation visual editor to a Home-Assistant-style collapsed-card UX.
|
|
152
|
+
|
|
153
|
+
Every item in all three sections (actions, triggers, conditions) now renders as a compact summary row by default - icon, title, and a one-line summary derived from its config. Clicking the row opens the item's full configuration in a right-side sheet that edits the same live definition (no draft/commit step), so closing the sheet keeps the changes. The saved `definition` is unchanged - only the editor presentation - so the visual and YAML views still round-trip losslessly.
|
|
154
|
+
|
|
155
|
+
- `@checkstack/ui` `ActionCard` gains three optional, backward-compatible props: `onOpenSheet` (turns the card into a non-expanding summary row that opens a host-supplied sheet on header click), `summary` (the compact one-line hint shown under the title), and `actions` (a typed `ActionCardMenuItem[]` rendered as a three-dot overflow menu). The new `ActionCardMenuItem` type is exported. Existing inline-expand usages are unaffected when the new props are omitted.
|
|
156
|
+
- Per-card commands move into the overflow menu: Disable/Enable, a new Duplicate, and Delete. The drag grip stays on the action card header; actions keep dnd-kit reordering and the parallel id array. Triggers and conditions remain non-reorderable.
|
|
157
|
+
- Duplicate clones an item with fresh, unique ids (via the existing id helpers) and inserts it directly after the original, keeping the editor's parallel id array in sync.
|
|
158
|
+
- Composite actions (choose / parallel / repeat / sequence) keep nesting: a child card inside a parent's sheet opens its OWN sheet, stacking via Radix Dialog's portal + overlay.
|
|
159
|
+
- Cards with validation errors auto-open their sheet and show an error badge on the collapsed row, so problems are never hidden behind a collapsed row plus a closed sheet.
|
|
160
|
+
|
|
161
|
+
- b995afb: Show auto-generated trigger ids in the automation editor without clicking the field.
|
|
162
|
+
|
|
163
|
+
Previously, loading a stored definition (a seeded default, a GitOps-managed automation, or hand-written YAML) whose triggers carried no `id` left the Id field blank until the operator focused and blurred it. The editor now materializes the derived id eagerly on load - the same way the starter automation and "Add step" path already do - so the id is shown (and referenceable as `trigger.id`) immediately. The runtime already derived these ids, so saved definitions are unchanged.
|
|
164
|
+
|
|
165
|
+
The auto-incident migration also now writes explicit trigger ids (matching `deriveTriggerId(event)`) into the seeded sustained and flapping automations, so newly seeded defaults carry the same id the editor shows.
|
|
166
|
+
|
|
167
|
+
- 270ef29: Add progressive disclosure and a live system picker to the automation visual editor.
|
|
168
|
+
|
|
169
|
+
The saved `definition` is unchanged - only the editor layout - so the visual and YAML views still round-trip losslessly.
|
|
170
|
+
|
|
171
|
+
- Triggers: the event picker and trigger config stay prominent; the optional `id`, gating `filter`, and `for:` dwell move into a per-trigger "Advanced" disclosure that auto-opens when a filter or dwell is set.
|
|
172
|
+
- Actions: per-action metadata (`id`, `description`, `continue_on_error`) moves into an "Advanced" disclosure inside the action card so the action's own configuration leads. Enable/disable stays on the card header.
|
|
173
|
+
- Conditions: the kind selector is grouped so the structured kinds (`numeric_state` / `time` / `state`) lead, the logical combinators follow, and the raw-expression escape hatch is de-emphasised under "Advanced" - all kinds stay reachable.
|
|
174
|
+
- The `state` condition's `entity` is now a live system picker backed by the catalog `getSystems` RPC, with a manual-entry fallback so an id not in the catalog (or a `{{ template }}`) still round-trips losslessly.
|
|
175
|
+
|
|
176
|
+
- b995afb: Add grouping to automations so they are easier to find.
|
|
177
|
+
|
|
178
|
+
Each automation now carries an optional single free-text `group` label (HA-style "category"), stored as its own column on the `automations` row alongside `name` / `description` / `status` - it is NOT part of the definition / YAML. The automations list renders one collapsible section per group (sorted alphabetically, with an implicit "Ungrouped" bucket last), and the edit page gains a type-new-or-pick-existing group picker fed by the new `listAutomationGroups` query. `listAutomations` accepts an optional `group` filter.
|
|
179
|
+
|
|
180
|
+
Declaratively managed automations express their group via GitOps `metadata.labels.group`; the reconciler threads it onto the row (blank clears it).
|
|
181
|
+
|
|
182
|
+
A Drizzle migration adds the nullable `"group"` column and an index. Existing automations default to no group (Ungrouped) and behave exactly as before.
|
|
183
|
+
|
|
184
|
+
- 270ef29: Add live state in scope plus duration helpers to the automation sensing layer (Wave 2 Phase 14).
|
|
185
|
+
|
|
186
|
+
- `@checkstack/template-engine` ships four pure, synchronous duration filters: `minutes` and `hours` (number to milliseconds), `duration_since` (ms elapsed since an ISO timestamp), and `older_than(thresholdMs)` (boolean dwell check). They compute against real time at call time, so "now" is fresh per evaluation. Fail-safe on null/unparseable input.
|
|
187
|
+
- The dispatch engine pre-resolves live health state into scope before any condition or template evaluation (the engine is synchronous, so inline state queries are impossible). State is folded under a `health` namespace - `health.system.*` for the trigger's context system and `health.systems[<id>]` for ids listed in the automation's new `uses_state` field. One batched `getBulkHealthState` query per evaluation, wired at the fresh-run, resume, and trigger-gate sites. Fail-open: a missing client or provider error yields an empty namespace and a warning, never wedging unrelated automations.
|
|
188
|
+
- New `automationFilterExtensionPoint` lets plugins contribute pure template filters without forking the engine's default registry. Name collisions with built-ins are skipped with a warning.
|
|
189
|
+
- The editor variable-scope resolver and autocomplete catalogue now surface the `health.*` namespace and the new duration filters.
|
|
190
|
+
|
|
191
|
+
With this phase alone, an operator can build "notify me when a system has been unhealthy for 30 minutes" using an interval trigger plus a single `health.*` condition - no dwell timer required (the precise event-driven path lands in Phase 15).
|
|
192
|
+
|
|
193
|
+
- 270ef29: Add the `wait_until` action primitive (Wave 2 Phase 17) - suspend a running automation until a condition becomes true, with an optional timeout (HA's `wait_template`).
|
|
194
|
+
|
|
195
|
+
- New `wait_until: { condition, timeout_seconds?, continue_on_timeout? }` primitive. `continue_on_timeout` defaults to true (HA semantics). Added to the schema, the action union, and `detectActionKind`. (The wait is fully reactive - see the reactive-dispatch-pipeline changeset; there is no `poll_seconds`.)
|
|
196
|
+
- `condition` accepts any condition shape - a template string or the Phase 16 structured `numeric_state` / `time` / `state` variants.
|
|
197
|
+
- Reactive resume: if the condition is already true it continues inline; otherwise it persists a `kind: "until"` wait lock (carrying the condition + timeout policy in a new `wait_config` jsonb column). The reactive-dispatch-pipeline changeset replaces the original poll-based re-check with a wake-index + a single timeout timer, so the wait is woken by a relevant entity change rather than ticked on an interval. Resumes take the per-run advisory lock so a wake and a sweep can't double-resume.
|
|
198
|
+
- Survives restart: the wait lock is the source of truth, and the stalled sweeper applies the timeout policy as a backstop if the wake/timer signal is lost.
|
|
199
|
+
- Works nested inside `choose` / `parallel` / `repeat` via the existing resume-remainder mechanism.
|
|
200
|
+
- Editor: a `wait_until` action card (frontend) mirroring `wait_for_trigger` - a `ConditionEditor` plus timeout and continue-on-timeout inputs. The structured numeric/time/state ConditionEditor branches land with the rest of the sensing-layer editor work; the card uses the expression-based editor for now.
|
|
201
|
+
|
|
202
|
+
- 270ef29: Add the sensing-layer editor UX (Wave 2 Phase 19) - the visual widgets for the duration-aware and structured-condition building blocks from Phases 15-18.
|
|
203
|
+
|
|
204
|
+
- New `@checkstack/ui` components (each with a Storybook story):
|
|
205
|
+
- `DurationInput` - number + unit (`seconds` / `minutes` / `hours`) picker emitting the single-unit `Duration` object the backend accepts, so it round-trips losslessly through YAML.
|
|
206
|
+
- `TimeOfDayInput` - HH:MM (24h) input emitting the `"HH:mm"` string the `time` condition's `after` / `before` accept. Both are plain inputs (no animations), so no `usePerformance` gating is needed.
|
|
207
|
+
- `DynamicForm`'s `FormField` gains an additive `x-duration` / `format: "duration"` branch that renders `DurationInput` for schema-driven duration configs. (Additive alongside the existing dispatch; reconciles cleanly with the parallel branch's `FormField` edits.)
|
|
208
|
+
- The `ConditionEditor` kind selector gains `numeric_state` / `time` / `state` structured branches: an operator dropdown (above / below / between) + threshold for numeric, `TimeOfDayInput` + weekday toggles + timezone for time, and a status dropdown + optional `DurationInput` dwell for state. The raw-expression escape hatch is kept. Pure `kindOf` / `defaultForKind` helpers are split into a UI-free `condition-kind` module so they unit-test under bun (the UI barrel drags Monaco).
|
|
209
|
+
- The trigger card gains a `for:` dwell toggle + `DurationInput` (Phase 15's schema was already round-tripping in YAML).
|
|
210
|
+
|
|
211
|
+
Visual and YAML views stay lossless; structured conditions authored in either are editable in the other.
|
|
212
|
+
|
|
213
|
+
- 270ef29: Add the GitOps `Automation` entity kind (Wave 2 Phase 21).
|
|
214
|
+
|
|
215
|
+
- `automation-backend` registers an `Automation` kind with the GitOps entity-kind registry (`specSchema: AutomationDefinitionSchema`). Reconcile upserts by name (identity tracked via the returned entity id + provenance); reconciled rows are tagged `managed_by = "gitops"`. Delete is guarded to GitOps-managed rows. An automation's full definition - triggers (with `for:` dwells), structured conditions, the action catalog, mode, `concurrency_scope`, `uses_state`, `state_window_minutes` - can now be declared in Git.
|
|
216
|
+
- `automation-frontend`: the editor reads the GitOps provenance lock (`useProvenanceLock({ kind: "Automation", entityId })`) and, when locked, disables Save / Run-now / Delete and the form fields and shows a `GitOpsLockBanner`.
|
|
217
|
+
- Documented the `Automation` YAML format under the GitOps kinds reference, plus new automation platform overview + plugin-author ("extending") developer-guide pages.
|
|
218
|
+
|
|
219
|
+
- b995afb: Surface inline-script type errors as automation action badges.
|
|
220
|
+
|
|
221
|
+
Every inline `run_script` action in the automation editor is now type-checked
|
|
222
|
+
against its generated `context` types continuously - including actions whose
|
|
223
|
+
cards are collapsed - and any errors show up as the action card's error badge
|
|
224
|
+
(and in the definition issue list), the same surface structural validation
|
|
225
|
+
uses. Previously a type error was only visible as a red squiggle inside the
|
|
226
|
+
open Monaco editor, so a broken script behind a collapsed card (or one
|
|
227
|
+
invalidated by adding a new trigger) went unnoticed until runtime, where the
|
|
228
|
+
bad property access silently read `undefined`.
|
|
229
|
+
|
|
230
|
+
Validation runs entirely in the browser via the same standalone TypeScript
|
|
231
|
+
worker the editor uses (new `validateTypeScriptSources` export on
|
|
232
|
+
`@checkstack/ui`), so there is no backend round-trip. Each script is checked by
|
|
233
|
+
prepending its generated `context.d.ts` to the source, which keeps the
|
|
234
|
+
`context` global scoped to that one off-screen file and avoids colliding with
|
|
235
|
+
any open editor. When an automation already contains scripts, a hidden editor
|
|
236
|
+
boots the shared editor services on open so validation runs immediately rather
|
|
237
|
+
than only after the first script card is expanded.
|
|
238
|
+
|
|
239
|
+
This covers the automation currently open in the editor. Scripts in other
|
|
240
|
+
automations, or definitions authored via YAML/API, are not type-checked here -
|
|
241
|
+
that platform-wide coverage remains future work for a backend typecheck.
|
|
242
|
+
|
|
243
|
+
Also: action cards no longer auto-open their detail sheet when they have
|
|
244
|
+
validation issues; issues now surface only as the card badge, so multiple
|
|
245
|
+
flagged actions no longer pop several sheets open at once.
|
|
246
|
+
|
|
247
|
+
- b995afb: Improve the automation Run Script secret → env mapping editor and script IntelliSense.
|
|
248
|
+
|
|
249
|
+
- **Searchable secret picker with existence validation.** The secret → env mapping editor (`SecretEnvEditor`) now uses a searchable, keyboard-navigable combobox (modeled on `VariablePicker` / `PackageNameCombobox`, `isLowPower`-aware) populated from the secrets plugin's `listSecretNames`, replacing the plain `<input>` + `<datalist>`. A free-typed name still round-trips (a secret may be created later). When a row references a name that the loaded list does not contain, the row shows a non-blocking warning (red border + message); save is not prevented. The existence check lives in a pure, unit-tested `unknownSecretNames` helper.
|
|
250
|
+
- **Clearer field description.** The `secretEnv` field descriptions on the `run_script` / `run_shell` actions no longer show the stored `${{ secrets.NAME }}` template (which is confusing in a UI that takes a bare name); they now describe the actual UI behavior and how the value is injected (`process.env.<ENV_NAME>` / `$<ENV_NAME>`) and masked.
|
|
251
|
+
- **`process.env.<ENV_NAME>` autocomplete.** Declared `secretEnv` env-var names now autocomplete under `process.env.` in the Run Script (TypeScript) Monaco editor and are typed `string`, via an ambient `NodeJS.ProcessEnv` augmentation merged into the editor type definitions. New pure, unit-tested generators `generateSecretEnvTypes` and `secretEnvEnvNames` (exported from `@checkstack/automation-frontend`) drive this; the augmentation coexists with `@types/node`'s existing index signature.
|
|
252
|
+
- **Shared combobox-interaction helper.** The "opens-then-immediately-closes" popover guard (`comboboxAnchorProps` / `isAnchorInteraction`) is promoted from `@checkstack/script-packages-frontend` into `@checkstack/ui` so the new secret picker and the existing package/version comboboxes share one implementation; the package comboboxes now import it from `@checkstack/ui` and the local copy is removed.
|
|
253
|
+
|
|
254
|
+
- b995afb: Add type-picker modals for the automation editor's Triggers and Conditions sections, matching the Actions "Add step" picker.
|
|
255
|
+
|
|
256
|
+
Instead of immediately creating a default element, both sections now open a searchable, grouped picker dialog so the operator chooses the type up front. The "Add" button moves out of each card's header to a bottom button styled exactly like the Actions "+ Add step" button.
|
|
257
|
+
|
|
258
|
+
- Triggers: a new "Add trigger" picker over the registry's trigger events (grouped by category, searchable). On pick the trigger is created with a unique default id (deduped against siblings) and appended.
|
|
259
|
+
- Conditions: a new "Add condition" picker over the condition kinds (grouped Structured / Logical / Advanced, searchable). On pick a schema-seeded default for that kind is appended.
|
|
260
|
+
- The shared `PickerRow`, add button and search input are extracted into a reusable `picker-dialog` module; `AddActionDialog` now consumes them.
|
|
261
|
+
- Condition kinds gain a `CONDITION_KIND_META` registry (label, description, icon, group) as the single source of metadata for the picker.
|
|
262
|
+
- Since the type is now chosen up front, the redundant in-sheet selectors are removed: the trigger config sheet drops its editable "Event" dropdown (keeping a read-only owner/description context line), and the top-level condition sheet drops its kind selector (swap kind = delete + re-add). Nested combinator clauses and the action `condition`-guard body keep their inline kind selector.
|
|
263
|
+
- New automations now start empty (no pre-filled trigger or action); the empty-state hints guide the operator to add a trigger and steps via the pickers.
|
|
264
|
+
|
|
265
|
+
The saved `definition` is unchanged - only how items are added - so the visual and YAML views still round-trip losslessly. Triggers and conditions remain non-reorderable.
|
|
266
|
+
|
|
267
|
+
- b995afb: Add the entity state machine core (`defineEntity`) - the foundational primitive of the reactive automation engine - as a Model-B plugin-backed reactive WRAPPER with NO framework-owned current-state storage.
|
|
268
|
+
|
|
269
|
+
`defineEntity` owns NO current-state storage of its own. Each kind declares a required plugin `read` accessor pointing at wherever its state lives (its own durable table, or a value computed on read from its own durable tables), and `defineEntity` makes that state reactive. There is no framework current-state store and no "homeless" fallback: every kind is plugin-backed. This makes a non-reactive write structurally impossible and guarantees every transition is durably logged without duplicating the plugin's state.
|
|
270
|
+
|
|
271
|
+
- `@checkstack/automation-backend`:
|
|
272
|
+
|
|
273
|
+
- New `automation.entity` extension point exposing `defineEntity(input)`, `declareNonReactiveState(input)`, `onEntityChanged(...)`, and `registerChangeDeriver(...)`. automation-backend registers the impl in `register`, so other plugins can resolve it and declare entities during their own `register`/`init` (Proxy-buffered until the impl registers).
|
|
274
|
+
- **Driven single mutation entry point.** All reactive-state writes go through `handle.mutate({ id, opts?, apply: () => Promise<TState> })`. The handle snapshots `prev` via `read` BEFORE the write, runs the plugin's `apply` (the actual write, committed in the PLUGIN's own transaction, returning the resulting state), validates `next` (zod), masks run-originated writes through the run-secret registry, diffs prev to next, and on a real diff appends the field-level transition rows to `entity_transitions` and emits `ENTITY_CHANGED` - both AFTER the plugin write commits (never on a rolled-back / throwing write). A structurally-unchanged write is a no-op. `handle.remove({ id, opts?, apply: () => Promise<void> })` is the tombstone counterpart (records the tombstone transition, emits next = null).
|
|
275
|
+
- **Cross-plugin transaction boundary.** `apply` takes NO framework tx: a plugin-backed kind lives behind a DIFFERENT drizzle client than `entity_transitions`, and two clients cannot share one transaction. The plugin write is authoritative; the transition-log append runs in the framework's own transaction AFTER the plugin write commits. A failure between them leaves correct plugin state with a missing history row (a gap, never a corruption).
|
|
276
|
+
- **`get` / `getMany`** route to the kind's `read`; **`inStateSince` / `inStateForMs` / `transitionCount`** read the per-field `entity_transitions` log (generalizing Phase-13 health transitions to any entity).
|
|
277
|
+
- **No framework keyed store.** There is no generic `entity_state` table, no `createKeyedStore`, and no `entityKeyedStoreServiceRef`: kinds whose state has no domain table of their own (the `health` aggregate, the `slo` budget/streak view) compute their `read` on demand from their own durable data instead of materializing a framework copy. `entity_transitions` (the change-history log) is the framework's ONLY persistent table and is written for EVERY kind regardless of where current state lives.
|
|
278
|
+
- **`entityResolverFor(kind)`** routes scope enrichment + the reactive `wait_until` wake re-eval to each kind's `read` accessor. Generalized scope enrichment (`enrichScopeWithEntities`) folds any `state.<kind>.<id>` ref into `scope.state.<kind>.<id>.<field>`. The rich `scope.health.*` condition snapshot (status, latency, success rate, in-maintenance, transitions-in-window, ...) is resolved EXCLUSIVELY through the healthcheck RPC path (the health aggregate is computed on read, not stored as a framework row) and the generic entity pass never writes `scope.health`; `state.health.*` remains the minimal reactive entity view. These are two complementary projections by design, not a migration shim.
|
|
279
|
+
- **Horizontal-scale read-consistency guard.** A reactive entity's current state MUST be globally readable from shared/durable storage, never process-local memory (`.agent/rules/state-and-scale.md`). Enforced by the `checkstack/no-pod-local-entity-state` ESLint tripwire at the `defineEntity({ read })` boundary (wired at `warn`) and the deterministic `cross-pod-read-consistency.it.test.ts` integration test.
|
|
280
|
+
- Load-time validation hard-fails a malformed registration (non-`z.object` state, missing/duplicate `kind`, or a missing / non-function `read`).
|
|
281
|
+
- The `ENTITY_CHANGED` hook is internal (not exported); the change emitter buffers events produced during the init window and flushes them in order once the hook wiring is available in `afterPluginsReady`.
|
|
282
|
+
|
|
283
|
+
- `@checkstack/automation-common`:
|
|
284
|
+
|
|
285
|
+
- New `EntityChangedSchema` (the `ENTITY_CHANGED` payload - `kind`, `id`, `prev`, `next`, `delta`, `changedFields`, `actor`, `occurredAt`) and `DispatchJobSchema` (the Stage-2 `trigger` / `wake` dispatch job).
|
|
286
|
+
|
|
287
|
+
- `@checkstack/automation-frontend`: the `wait_until` editor no longer offers the inert `poll_seconds` field (reactive waits don't poll).
|
|
288
|
+
|
|
289
|
+
This phase adds the primitive only: domains are migrated in their own changesets. No external behavior changes for existing automations.
|
|
290
|
+
|
|
291
|
+
BREAKING CHANGES: There is no framework current-state store. Any out-of-tree plugin must own its entity state in its own durable storage (its own table, or a compute-on-read over it) and pass a `read` accessor to `defineEntity`. `createKeyedStore` / `KeyedStore` / `entityKeyedStoreServiceRef` / `EntityKeyedStoreService` do not exist, and there is no `entity_state` table. `handle.set` / `handle.patch` and the `indexes` option do not exist; all writes go through `handle.mutate` / `handle.remove`.
|
|
292
|
+
|
|
293
|
+
- b995afb: Fix `context.*` IntelliSense disappearing in the automation inline-script editor.
|
|
294
|
+
|
|
295
|
+
The action editor concatenates the scope-derived `declare const context`
|
|
296
|
+
global with the `secretEnv` `process.env` augmentation into a single Monaco
|
|
297
|
+
extra-lib. `generateSecretEnvTypes` emitted module-form output
|
|
298
|
+
(`declare global { … } export {};`), and the top-level `export {};` turned the
|
|
299
|
+
whole concatenated `.d.ts` into a module - which silently demoted
|
|
300
|
+
`declare const context` from a global ambient to a module-local binding, so
|
|
301
|
+
`context.trigger.payload` (and everything under `context`) stopped
|
|
302
|
+
autocompleting. Because the empty case also emitted `export {};`, every
|
|
303
|
+
automation script action was affected regardless of declared secrets. Health
|
|
304
|
+
check script editors were unaffected (they never merge the secretEnv lib).
|
|
305
|
+
|
|
306
|
+
`generateSecretEnvTypes` now emits a global-script-compatible ambient
|
|
307
|
+
augmentation (`declare namespace NodeJS { interface ProcessEnv { … } }`) and an
|
|
308
|
+
empty string when there is nothing to declare, so the merged extra-lib stays a
|
|
309
|
+
global script and `context` remains globally visible. A regression test guards
|
|
310
|
+
that the merged `context + secretEnv` output contains no top-level
|
|
311
|
+
`export`/`import`.
|
|
312
|
+
|
|
313
|
+
- 270ef29: Add in-UI script testing for automation `run_script` / `run_shell` actions.
|
|
314
|
+
|
|
315
|
+
A new `testScript` RPC runs a TypeScript or shell script against an
|
|
316
|
+
editable, auto-seeded sample context using the same sandboxed runner the
|
|
317
|
+
real action uses, so operators can test scripts directly in the editor
|
|
318
|
+
without dispatching a whole automation. Surfaces beneath any script field
|
|
319
|
+
flagged `x-script-testable` via the new `ScriptTestPanel` /
|
|
320
|
+
`ContextSampleEditor` components in `@checkstack/ui` and the
|
|
321
|
+
`scriptTestRenderer` prop threaded through `DynamicForm`.
|
|
322
|
+
|
|
323
|
+
- `@checkstack/automation-common`: adds the `testScript` contract +
|
|
324
|
+
`ScriptTest*` schemas (gated by `automation.manage`).
|
|
325
|
+
- `@checkstack/automation-backend`: implements `testScript` reusing the
|
|
326
|
+
shared ESM / shell runners; central-only, time-bounded.
|
|
327
|
+
- `@checkstack/backend-api`: new `x-script-testable` config-schema
|
|
328
|
+
metadata propagated to the frontend JSON Schema.
|
|
329
|
+
- `@checkstack/ui`: new `ScriptTestPanel` + `ContextSampleEditor`
|
|
330
|
+
components and a `scriptTestRenderer` prop on `DynamicForm`.
|
|
331
|
+
- `@checkstack/automation-frontend`: wires the test panel into the action
|
|
332
|
+
editor.
|
|
333
|
+
- `@checkstack/integration-script-backend`: marks the `run_script` /
|
|
334
|
+
`run_shell` script fields as testable.
|
|
335
|
+
|
|
336
|
+
- 270ef29: Extend in-UI script testing to health-check collectors, and add
|
|
337
|
+
load-from-run replay for automation script tests.
|
|
338
|
+
|
|
339
|
+
- Health-check collectors: a new `testCollectorScript` RPC runs the
|
|
340
|
+
inline-script (TypeScript) collector and the shell `script` collector
|
|
341
|
+
against an editable, auto-seeded sample context using the same
|
|
342
|
+
sandboxed runner the real collector uses. Surfaces beneath the
|
|
343
|
+
collector script fields in the collector editor (both marked
|
|
344
|
+
`x-script-testable`). Gated by `healthcheck.configuration.manage`.
|
|
345
|
+
- Automation replay: a new `getRunScopeForReplay` RPC reconstructs an
|
|
346
|
+
editable test context from a real run (trigger + persisted artifacts,
|
|
347
|
+
plus the durable scope snapshot when the run is still in-flight), and
|
|
348
|
+
the script-test panel gains a "Load from run" picker that seeds the
|
|
349
|
+
sample context from a past run.
|
|
350
|
+
|
|
351
|
+
Note: health-check executions do not persist the script / config /
|
|
352
|
+
check / system that produced a result, so there is no health-check
|
|
353
|
+
replay - auto-seed is the only context source for collector tests. This
|
|
354
|
+
is by design; see the feature plan.
|
|
355
|
+
|
|
356
|
+
- b995afb: Autocomplete the import specifier itself in script editors.
|
|
357
|
+
|
|
358
|
+
Lazy type acquisition only loads a package's types once its name is already in the buffer, so while you were still typing the import specifier (`import {} from "lod"`) there were no suggestions - the lazy-ATA catch-22. Script editors now suggest installed package names directly in import-specifier position; selecting one (e.g. `lodash`) inserts the name, and the existing ATA loop then loads its `@types/lodash` closure so members complete.
|
|
359
|
+
|
|
360
|
+
- `@checkstack/ui`: `CodeEditor`/`TypefoxEditor` gained an injected `importablePackages?: string[]` prop and a dedicated Monaco completion provider (registered once per `typescript`/`javascript` language, scoped to the editor's model, disposed on unmount). It fires ONLY when the cursor is inside an import/require module-specifier string - detected by a new pure, unit-tested helper `importSpecifierCompletionContext(lineUpToCursor)` that handles `from "…"`, bare `import "…"`, `require("…")`, and dynamic `import("…")`, returns the partial specifier + the replace range, and returns null once the string is closed or outside an import. Items are `kind: Module`, insert the bare name without touching the quotes, and coexist with (do not replace) the TS worker's own completions. Trigger characters: `"`, `'`, and `/` (for scoped subpaths); manual invoke (Ctrl+Space) also works. A new pure helper `importablePackageNames` filters a raw manifest name list (excludes `@types/*`, dedupes, sorts).
|
|
361
|
+
- `@checkstack/script-packages-frontend`: `useScriptPackageTypeAcquisition()` now also returns `importablePackages`, derived from the installed manifest (what is actually resolvable at runtime) with `@types/*` companions excluded - you import `lodash`, never `@types/lodash` (the `@types` package still backs the closure types).
|
|
362
|
+
- `@checkstack/automation-frontend` / `@checkstack/healthcheck-frontend`: pass `importablePackages` into `DynamicForm` alongside the existing `acquireTypes` wiring, so both the Run Script action editor and healthcheck collector editors get import-name completion.
|
|
363
|
+
|
|
364
|
+
The completion list is plugin-agnostic in `@checkstack/ui` (the names are injected); it never fires outside import-string positions, so normal completions are unaffected.
|
|
365
|
+
|
|
366
|
+
- b995afb: Fix package IntelliSense in script editors: lazy Automatic Type Acquisition (ATA) with proper `@types/*` resolution.
|
|
367
|
+
|
|
368
|
+
Script editors (automation "Run Script (TypeScript)" and healthcheck collectors) now provide real autocomplete for installed npm packages. Importing a package whose types live in DefinitelyTyped - e.g. `import { debounce } from "lodash"` (lodash ships no own types; `@types/lodash` does) - now yields member completions. Previously no package completions appeared at all.
|
|
369
|
+
|
|
370
|
+
Root cause: the old rollup wrapped each package's raw, multi-file `.d.ts` (with `export =`, `export as namespace`, and triple-slash `/// <reference path>` chains) inside a single `declare module "<name>" { ... }`, which the TypeScript worker silently rejected, and it truncated large type sets (lodash is ~866 KB across ~700 files) at a 256 KB cap.
|
|
371
|
+
|
|
372
|
+
The fix registers the REAL declaration files at their `node_modules/...` virtual paths and lets TypeScript's own NodeJs + `@types` resolution do the work:
|
|
373
|
+
|
|
374
|
+
- `@checkstack/script-packages-backend`: replaced `rollupPackageTypes` with a tree-driven closure extractor (`resolvePackageTypeClosure`). Given a bare specifier, it resolves against the materialized tree - own types via `package.json` `types`/`typings`/`exports` (bundled-types packages like `zod`/`dayjs`), the `@types/<mangled>` companion when it exists (`lodash` -> `@types/lodash`, scoped `@babel/core` -> `@types/babel__core`), or both, or neither (graceful empty, never a throw). It follows `/// <reference path|types>` and relative imports, includes each package's `package.json`, leaves every file UNWRAPPED, and surfaces a `truncated` flag instead of silently capping. Served from a new raw, HTTP-cacheable route `GET /api/script-packages/types/:lockfileHash/:specifier` (`Cache-Control: private, max-age=1y, immutable`), auth-gated by `script-packages.read`.
|
|
375
|
+
- `@checkstack/script-packages-common`: **BREAKING** - replaced the `listPackageTypes` RPC procedure and `PackageTypesSchema { name, version, dts }` with `PackageTypeClosureSchema` (a `{ path, content }` file-map plus `hasOwnTypes`/`hasAtTypes`/`notFound`/`truncated`) served over the cacheable HTTP route. Added a shared `buildTypeAcquisitionPath`/`parseTypeAcquisitionPath` path contract.
|
|
376
|
+
- `@checkstack/ui`: `CodeEditor`/`TypefoxEditor` gained an injected `acquireTypes` resolver + `acquireResetKey`. On debounced buffer change it parses bare `import`/`require` specifiers (pure, unit-tested) and lazily fetches + registers each NEW package's closure via `addExtraLib` at `file:///node_modules/...`, deduped by a shared acquired-set that resets when the install hash changes. Compiler options set `moduleResolution: NodeJs`, `baseUrl: "file:///"`, and `typeRoots` so a bare import resolves to its `@types` companion. The `context` ambient global keeps working unchanged.
|
|
377
|
+
- `@checkstack/script-packages-frontend`: replaced the old `useScriptPackageTypes` (which concatenated the broken `dts`) with `useScriptPackageTypeAcquisition()`, returning the `acquireTypes` resolver (targets the cacheable route, zod-validates the response) and the current `lockfileHash` as `acquireResetKey`.
|
|
378
|
+
- `@checkstack/automation-frontend` / `@checkstack/healthcheck-frontend`: wired the resolver into the Run Script and collector editors.
|
|
379
|
+
|
|
380
|
+
State & scale: the type closure is derived on read from the materialized package tree (no new durable state). The editor's acquired-set is pod-local UI bookkeeping; the route is keyed by the cluster-wide `lockfileHash`, so the browser HTTP cache is correct across pods and only refetches after a new install changes the hash.
|
|
381
|
+
|
|
382
|
+
- 270ef29: Wire up the script-packages RPC router, admin UI, and editor IntelliSense.
|
|
383
|
+
|
|
384
|
+
- `script-packages-backend`: the oRPC router implementing the full
|
|
385
|
+
contract (allowlist CRUD, registry config with encrypted write-only auth
|
|
386
|
+
token, `installNow` via the elected installer, size cap, storage backend
|
|
387
|
+
selection, install state, `getManifest` / `downloadBlob` for reconcilers,
|
|
388
|
+
and `listPackageTypes`), the `installNow` controller (election, size-cap
|
|
389
|
+
enforcement, `script-packages.changed` emit, blocked during migration),
|
|
390
|
+
the `.d.ts` rollup, the singleton config stores, and the full plugin
|
|
391
|
+
wiring (broadcast-hook reconcile + startup backstop).
|
|
392
|
+
- `script-packages-common`: admin route for the settings page.
|
|
393
|
+
- `script-packages-frontend`: the Settings -> Script Packages admin page
|
|
394
|
+
(allowlist, install state + size, registry/storage summary, satellite
|
|
395
|
+
sync) and the `useScriptPackageTypes()` hook.
|
|
396
|
+
- `automation-frontend` / `healthcheck-frontend`: merge installed-package
|
|
397
|
+
`.d.ts` into the script-editor `typeDefinitions` so `import` from an
|
|
398
|
+
allowlisted package autocompletes in every script field.
|
|
399
|
+
|
|
400
|
+
- b995afb: Fix the automation Run Script action's `secretEnv` (secret → env mapping) test wiring and tolerate bare secret names.
|
|
401
|
+
|
|
402
|
+
- `@checkstack/ui` `ScriptTestPanel` now accepts the script field's declared `secretEnv` and renders an optional per-secret test-override input. The `ScriptTestRenderer` callback (DynamicForm) receives the SIBLING `x-secret-env` mapping value, located by annotation (not by field name), so a testable script field forwards it to the panel. Previously the test path never sent `secretEnv`, so `buildTestSecretEnv` got `undefined` and `process.env.<env>` was undefined in an in-UI test. Now an override-less test injects `__SECRET_<NAME>__` placeholders, and any operator override is masked from the output. Real secret values are still NEVER resolved in the test path.
|
|
403
|
+
- `@checkstack/automation-frontend` forwards the action's `secretEnv` and the collected overrides to `testScript`.
|
|
404
|
+
- `@checkstack/secrets-common`: the `secretEnv` mapping VALUE now accepts EITHER a `${{ secrets.NAME }}` template OR a bare secret name, normalizing a bare name to the canonical `${{ secrets.NAME }}` template on parse. This is a forgiving / NARROWING input change (more inputs accepted; stored/output form is unchanged and still the template), not a breaking change. Existing data and YAML shorthand like `secretEnv: { secret: SECRET }` now pass config validation instead of failing with "Must contain a ${{ secrets.NAME }} reference". Partial inline interpolation (e.g. `u:${{ secrets.pw }}@host`) keeps working unchanged; values that are neither a secret reference nor a valid secret name are still rejected.
|
|
405
|
+
- `@checkstack/ui` `parseSecretName` tolerates a legacy bare secret name for display so the picker shows the same name for both the template and the bare form.
|
|
406
|
+
|
|
407
|
+
The healthcheck collector test panel was checked: its config has no `x-secret-env` field, so it needed no secret wiring (only the `onRun` signature change, which is backward compatible).
|
|
408
|
+
|
|
409
|
+
- 270ef29: Secrets platform Phase 2: secret -> env-var mapping with central resolve, inject, and mask.
|
|
410
|
+
|
|
411
|
+
- Script consumers declare a least-privilege `secretEnv` allowlist
|
|
412
|
+
(`{ ENV_NAME: "${{ secrets.NAME }}" }`). The automation `run_script` /
|
|
413
|
+
`run_shell` actions resolve ONLY the declared secrets via
|
|
414
|
+
`secretResolverRef.resolveForRun`, inject them into the runner env for
|
|
415
|
+
that run (memory-only; the ESM runner gained a per-run `env` option), and
|
|
416
|
+
mask their values out of stdout/stderr/result/error via the run-scoped
|
|
417
|
+
masking context. A missing required secret fails the run clearly. No
|
|
418
|
+
ambient secret access.
|
|
419
|
+
- Test panel: `testScript` / `testCollectorScript` inject named
|
|
420
|
+
`__SECRET_<NAME>__` placeholders by default, or user-supplied per-secret
|
|
421
|
+
overrides; real production values are never resolved in the test path,
|
|
422
|
+
and overrides are masked out of the result.
|
|
423
|
+
- Healthcheck collectors carry the `secretEnv` field for authoring +
|
|
424
|
+
the test panel; runtime injection on satellites lands in Phase 3.
|
|
425
|
+
- Editor UX: a new `@checkstack/ui` `SecretEnvEditor` renders `x-secret-env`
|
|
426
|
+
record fields with `${{ secrets.* }}` name autocomplete (from
|
|
427
|
+
`listSecretNames`), wired into the automation action editor and the
|
|
428
|
+
healthcheck collector editor. New `withConfigMeta` helper +
|
|
429
|
+
`x-secret-env` config-meta key in `@checkstack/backend-api`.
|
|
430
|
+
|
|
431
|
+
- b995afb: Add an optional `partitionBy` override to the windowed-count trigger gate.
|
|
432
|
+
|
|
433
|
+
A trigger's `window` block now accepts `partitionBy`, a bare expression (same flavour as `filter`, no `{{ }}`) that controls the key the occurrence count is bucketed by. When omitted, the gate keys by the trigger's built-in context key exactly as before (per system for health triggers), so existing automations are unchanged. When set, the expression is evaluated against the same trigger scope `filter` uses and coerced to a string - e.g. `trigger.payload.severity` for a per-severity rate, or `trigger.payload.systemId + ":" + trigger.payload.checkId` for a composite key. If the expression evaluates to null/undefined/empty or fails to evaluate, the gate falls back to the built-in context key (never global counting); eval errors are logged, matching the gate's fail-open posture.
|
|
434
|
+
|
|
435
|
+
Triggers can now declare `contextKeyLabel` (a UI hint, e.g. `"system"`) describing their built-in context dimension. It is surfaced through `TriggerInfo` so the editor's window "Partition by" field shows the default partition ("Leave blank to count per system" / "per automation" when a trigger has no context key). The healthcheck system triggers (`system_health_changed`, `system_degraded`, `system_healthy`, `check_failed`) and the built-in `numeric_state` trigger set it to `"system"`. This is a pure UI hint with no runtime behaviour.
|
|
436
|
+
|
|
437
|
+
The automation editor's window block gains a "Partition by" expression input (reusing the trigger filter's `trigger.payload.*` autocomplete), and the collapsed trigger card summary shows the partition when set.
|
|
438
|
+
|
|
439
|
+
- b995afb: Add a generic windowed-count / rate trigger gate, and express flapping detection on it.
|
|
440
|
+
|
|
441
|
+
Any trigger can now carry a `window: { count, minutes, refire }` block: the automation engine records each qualifying occurrence (after the structured config gate and the operator's `filter`) in a durable append log and counts rows within the trailing sliding window, scoped per context key (e.g. per system). `refire: "every"` (default) fires on every occurrence at/over the threshold; `refire: "once"` fires only on the crossing edge and re-arms as old occurrences age out. The gate runs in `maybeStartRun` after `filter` and before the `for:` dwell, so it composes with both.
|
|
442
|
+
|
|
443
|
+
Flapping is now an instance of this mechanism rather than a bespoke detector. The healthcheck `system_health_changed` raw change event plus a `filter` (`trigger.payload.newStatus != "healthy"`) plus `window: { count: 3, minutes: 60, refire: "once" }` reproduces flapping in the engine.
|
|
444
|
+
|
|
445
|
+
State-and-scale: window state lives in the new `automation_window_events` Postgres table (FK-cascade on the automation, the same delete-lifecycle as `automation_dwell_timers`). The count is read with pure SQL so every pod computes the same answer; the work-queue claim gives exactly one INSERT per emission, so there is no double-count. Rows older than the 24h schema cap are pruned by the existing stalled-sweeper. The `once` policy is best-effort under at-least-once redelivery (a redelivered emission can skip the exact crossing edge; `every` is redelivery-tolerant).
|
|
446
|
+
|
|
447
|
+
**BREAKING CHANGES:**
|
|
448
|
+
|
|
449
|
+
- The `healthcheck.flapping_detected` automation trigger and the `healthcheck.flapping_detected` hook are REMOVED. Flapping is now detected by the windowed-count gate on the `healthcheck.system_health_changed` trigger (`window` block, `refire: "once"`).
|
|
450
|
+
- Flapping is now PER-SYSTEM (the aggregated `health` entity), not per-`(system, configuration)`. Subscribe to `check_failed` with a `window` instead if you need per-check rate detection.
|
|
451
|
+
- The healthcheck `health_check_unhealthy_transitions` table is DROPPED (the per-check flapping audit log is no longer kept; counting moved into the engine).
|
|
452
|
+
- The backend-only `automation.subscriptions` service ref (`automationSubscriptionsRef` / `AutomationSubscriptions`) is REMOVED. The engine enumerates subscribers internally and the window gate runs per-automation inside `maybeStartRun`, so the external read-ref is no longer needed.
|
|
453
|
+
- Existing user-created flapping automations are AUTO-MIGRATED on boot: any trigger on `healthcheck.flapping_detected` is rewritten to `healthcheck.system_health_changed` + the canonical unhealthy-transition filter + `window: { count: transitions ?? 3, minutes: windowMinutes ?? 60, refire: "once" }`, dropping the old `config`. A pre-existing trigger filter is replaced with the canonical one (logged per row). An enabled automation that still references the removed event after migration logs a warning.
|
|
454
|
+
|
|
455
|
+
### Patch Changes
|
|
456
|
+
|
|
457
|
+
- Updated dependencies [b995afb]
|
|
458
|
+
- Updated dependencies [b995afb]
|
|
459
|
+
- Updated dependencies [270ef29]
|
|
460
|
+
- Updated dependencies [270ef29]
|
|
461
|
+
- Updated dependencies [270ef29]
|
|
462
|
+
- Updated dependencies [270ef29]
|
|
463
|
+
- Updated dependencies [270ef29]
|
|
464
|
+
- Updated dependencies [270ef29]
|
|
465
|
+
- Updated dependencies [270ef29]
|
|
466
|
+
- Updated dependencies [b995afb]
|
|
467
|
+
- Updated dependencies [b995afb]
|
|
468
|
+
- Updated dependencies [b995afb]
|
|
469
|
+
- Updated dependencies [b995afb]
|
|
470
|
+
- Updated dependencies [b995afb]
|
|
471
|
+
- Updated dependencies [b995afb]
|
|
472
|
+
- Updated dependencies [270ef29]
|
|
473
|
+
- Updated dependencies [270ef29]
|
|
474
|
+
- Updated dependencies [b995afb]
|
|
475
|
+
- Updated dependencies [270ef29]
|
|
476
|
+
- Updated dependencies [270ef29]
|
|
477
|
+
- Updated dependencies [b995afb]
|
|
478
|
+
- Updated dependencies [b995afb]
|
|
479
|
+
- Updated dependencies [b995afb]
|
|
480
|
+
- Updated dependencies [270ef29]
|
|
481
|
+
- Updated dependencies [270ef29]
|
|
482
|
+
- Updated dependencies [b995afb]
|
|
483
|
+
- Updated dependencies [270ef29]
|
|
484
|
+
- Updated dependencies [270ef29]
|
|
485
|
+
- Updated dependencies [270ef29]
|
|
486
|
+
- Updated dependencies [b995afb]
|
|
487
|
+
- Updated dependencies [b995afb]
|
|
488
|
+
- Updated dependencies [270ef29]
|
|
489
|
+
- Updated dependencies [b995afb]
|
|
490
|
+
- Updated dependencies [b995afb]
|
|
491
|
+
- Updated dependencies [b995afb]
|
|
492
|
+
- @checkstack/ui@1.12.0
|
|
493
|
+
- @checkstack/automation-common@0.3.0
|
|
494
|
+
- @checkstack/template-engine@0.3.0
|
|
495
|
+
- @checkstack/script-packages-frontend@0.2.0
|
|
496
|
+
- @checkstack/secrets-frontend@0.1.0
|
|
497
|
+
- @checkstack/gitops-frontend@0.4.7
|
|
498
|
+
|
|
3
499
|
## 0.2.0
|
|
4
500
|
|
|
5
501
|
### Minor Changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@checkstack/automation-frontend",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"license": "Elastic-2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.tsx",
|
|
@@ -13,26 +13,31 @@
|
|
|
13
13
|
"lint:code": "eslint . --max-warnings 0"
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"@checkstack/
|
|
17
|
-
"@checkstack/common": "0.
|
|
18
|
-
"@checkstack/
|
|
19
|
-
"@checkstack/
|
|
20
|
-
"@checkstack/
|
|
21
|
-
"@checkstack/
|
|
22
|
-
"@checkstack/
|
|
16
|
+
"@checkstack/auth-common": "0.7.2",
|
|
17
|
+
"@checkstack/automation-common": "0.3.0",
|
|
18
|
+
"@checkstack/catalog-common": "2.2.3",
|
|
19
|
+
"@checkstack/common": "0.12.0",
|
|
20
|
+
"@checkstack/frontend-api": "0.6.0",
|
|
21
|
+
"@checkstack/gitops-frontend": "0.4.7",
|
|
22
|
+
"@checkstack/integration-common": "0.6.0",
|
|
23
|
+
"@checkstack/script-packages-frontend": "0.2.0",
|
|
24
|
+
"@checkstack/secrets-frontend": "0.1.0",
|
|
25
|
+
"@checkstack/signal-frontend": "0.1.5",
|
|
26
|
+
"@checkstack/template-engine": "0.3.0",
|
|
27
|
+
"@checkstack/ui": "1.12.0",
|
|
23
28
|
"@dnd-kit/core": "^6.3.1",
|
|
24
29
|
"@dnd-kit/sortable": "^8.0.0",
|
|
25
30
|
"@dnd-kit/utilities": "^3.2.2",
|
|
26
|
-
"date-fns": "^4.
|
|
27
|
-
"lucide-react": "^
|
|
28
|
-
"react": "^18.
|
|
29
|
-
"react-router-dom": "^
|
|
31
|
+
"date-fns": "^4.4.0",
|
|
32
|
+
"lucide-react": "^1.17.0",
|
|
33
|
+
"react": "^18.3.1",
|
|
34
|
+
"react-router-dom": "^7.16.0",
|
|
30
35
|
"yaml": "^2.6.1"
|
|
31
36
|
},
|
|
32
37
|
"devDependencies": {
|
|
33
38
|
"typescript": "^5.0.0",
|
|
34
39
|
"@types/react": "^18.2.0",
|
|
35
40
|
"@checkstack/tsconfig": "0.0.7",
|
|
36
|
-
"@checkstack/scripts": "0.3.
|
|
41
|
+
"@checkstack/scripts": "0.3.4"
|
|
37
42
|
}
|
|
38
43
|
}
|