@checkstack/automation-backend 0.2.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 +453 -0
- package/drizzle/0000_acoustic_diamondback.sql +80 -0
- package/drizzle/0001_mute_vindicator.sql +12 -0
- package/drizzle/0002_silky_omega_red.sql +12 -0
- package/drizzle/meta/0000_snapshot.json +688 -0
- package/drizzle/meta/0001_snapshot.json +785 -0
- package/drizzle/meta/0002_snapshot.json +861 -0
- package/drizzle/meta/_journal.json +27 -0
- package/drizzle.config.ts +12 -0
- package/package.json +41 -0
- package/src/action-registry.ts +83 -0
- package/src/action-types.ts +324 -0
- package/src/artifact-store.ts +140 -0
- package/src/artifact-type-registry.ts +64 -0
- package/src/automation-store.ts +227 -0
- package/src/builtin-actions.test.ts +185 -0
- package/src/builtin-actions.ts +132 -0
- package/src/builtin-triggers.test.ts +264 -0
- package/src/builtin-triggers.ts +365 -0
- package/src/dispatch/action-kind.ts +44 -0
- package/src/dispatch/condition.ts +61 -0
- package/src/dispatch/delay-queue.ts +91 -0
- package/src/dispatch/engine.test.ts +1198 -0
- package/src/dispatch/engine.ts +1672 -0
- package/src/dispatch/path-nav.ts +65 -0
- package/src/dispatch/render.test.ts +75 -0
- package/src/dispatch/render.ts +136 -0
- package/src/dispatch/run-state-store.ts +143 -0
- package/src/dispatch/run-state.ts +298 -0
- package/src/dispatch/scope.test.ts +40 -0
- package/src/dispatch/scope.ts +125 -0
- package/src/dispatch/stalled-sweeper.ts +164 -0
- package/src/dispatch/test-fixtures.ts +558 -0
- package/src/dispatch/trigger-subscriber.ts +397 -0
- package/src/dispatch/types.ts +259 -0
- package/src/extension-points.ts +88 -0
- package/src/index.ts +379 -0
- package/src/migration/from-webhook-subscriptions.test.ts +237 -0
- package/src/migration/from-webhook-subscriptions.ts +398 -0
- package/src/registries.test.ts +357 -0
- package/src/router.test.ts +724 -0
- package/src/router.ts +556 -0
- package/src/schema.ts +310 -0
- package/src/trigger-registry.ts +99 -0
- package/src/validate-definition.test.ts +306 -0
- package/src/validate-definition.ts +304 -0
- package/tsconfig.json +41 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,453 @@
|
|
|
1
|
+
# @checkstack/automation-backend
|
|
2
|
+
|
|
3
|
+
## 0.2.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- e2d6f25: feat(automation): connection picker for integration actions + restore Integrations menu
|
|
8
|
+
|
|
9
|
+
Connection-backed automation actions (Jira, Teams, Webex) now render a
|
|
10
|
+
working connection picker plus cascading provider dropdowns in the
|
|
11
|
+
visual editor, and the Integrations entry is back in the user menu.
|
|
12
|
+
|
|
13
|
+
**Contract.** `ActionDefinition` gained an optional
|
|
14
|
+
`connectionProviderId` (and it is surfaced on `ActionInfoSchema` and
|
|
15
|
+
mapped in the `listActions` router). It carries the integration
|
|
16
|
+
provider's fully-qualified id, derived from the provider plugin's own
|
|
17
|
+
`pluginMetadata.pluginId` (never a hardcoded string), so the editor
|
|
18
|
+
knows which provider backs an action's dropdowns and it matches the
|
|
19
|
+
`qualifiedId` the integration provider registry assigns.
|
|
20
|
+
|
|
21
|
+
**Providers.** Jira, Teams and Webex each export
|
|
22
|
+
`*_PROVIDER_LOCAL_ID` / `*_PROVIDER_QUALIFIED_ID`, register their
|
|
23
|
+
provider with the local id, and add a `CONNECTION_OPTIONS`
|
|
24
|
+
(`"connectionOptions"`) resolver name. Their `post_message` /
|
|
25
|
+
issue actions set `connectionProviderId` and expose `connectionId`
|
|
26
|
+
as an `x-options-resolver` dropdown instead of a hidden field.
|
|
27
|
+
|
|
28
|
+
**Frontend bridge.** A new `useConnectionOptionResolvers` hook
|
|
29
|
+
(`@checkstack/automation-frontend`, which now depends on
|
|
30
|
+
`@checkstack/integration-common`) turns an action's
|
|
31
|
+
`x-options-resolver` schema fields into live data: the
|
|
32
|
+
`connectionOptions` resolver lists the provider's connections via
|
|
33
|
+
`listConnections`, and every other resolver name is forwarded to
|
|
34
|
+
`getConnectionOptions` for the selected `connectionId`, passing the
|
|
35
|
+
live form values as `context` for dependent fields. `ProviderActionBody`
|
|
36
|
+
now passes this map to `DynamicForm` (it was previously missing
|
|
37
|
+
entirely, so connection-backed actions had no working dropdowns).
|
|
38
|
+
|
|
39
|
+
**frontend-api.** `usePluginClient` procedures now also expose a typed
|
|
40
|
+
imperative `.call(input)` alongside `.useQuery` / `.useMutation`, for
|
|
41
|
+
async callbacks that cannot host a hook (such as a `DynamicForm`
|
|
42
|
+
options resolver). Additive, non-breaking.
|
|
43
|
+
|
|
44
|
+
**Integrations menu.** Re-added `IntegrationMenuItem` and a new
|
|
45
|
+
`IntegrationsLandingPage`, wired into `integration-frontend` as a list
|
|
46
|
+
route and a `UserMenuItemsSlot` entry under the "Configuration" group.
|
|
47
|
+
|
|
48
|
+
**Action card polish.** The action editor's secondary metadata (id,
|
|
49
|
+
description, failure behaviour) is now grouped into one quiet settings
|
|
50
|
+
panel with consistent small uppercase "eyebrow" labels, so the action's
|
|
51
|
+
own configuration stays the focal point. The raw failure checkbox was
|
|
52
|
+
replaced with the standard `Checkbox` control, and the provider action
|
|
53
|
+
picker / configuration sections gained consistent section headers and a
|
|
54
|
+
divider. The per-step "type" dropdown was removed: an action's kind is
|
|
55
|
+
fixed at creation, so changing it now means adding a new step and
|
|
56
|
+
deleting the old one (avoids the surprising full-config reset that
|
|
57
|
+
switching kinds used to trigger).
|
|
58
|
+
|
|
59
|
+
**Add-step picker.** Adding a step now opens a Home-Assistant-style
|
|
60
|
+
dialog where the operator decides the step type up front: an "Actions"
|
|
61
|
+
tab lists the registered provider actions grouped by category
|
|
62
|
+
(searchable; picking one presets the step's `action`), and a "Blocks"
|
|
63
|
+
tab lists the structural building blocks (choose / parallel / repeat /
|
|
64
|
+
etc.). Because the concrete action is chosen here, the in-card action
|
|
65
|
+
switcher was removed - a step's action is fixed once created. Composite
|
|
66
|
+
blocks now start with an empty child list (filled via the nested
|
|
67
|
+
add-step picker) instead of seeding an unconfigurable empty action.
|
|
68
|
+
|
|
69
|
+
- 41c77f4: feat(automation): deep + live definition validation surfaces invalid values, keys and ids — marked inline
|
|
70
|
+
|
|
71
|
+
Previously `validateDefinition` only checked the structural shape via
|
|
72
|
+
`AutomationDefinitionSchema`, where an action's `config` is typed as
|
|
73
|
+
`z.record(z.unknown())`. So a bad config value (e.g. `level:
|
|
74
|
+
debugthisiswrong` on `automation.log`) passed validation, and switching
|
|
75
|
+
to the visual editor just showed an empty dropdown with no explanation.
|
|
76
|
+
|
|
77
|
+
**Backend — deep validation.** New `collectDefinitionIssues` walker
|
|
78
|
+
validates the whole definition semantically, not just structurally:
|
|
79
|
+
|
|
80
|
+
- unknown trigger `event` / action `action` ids,
|
|
81
|
+
- each provider action's `config` against the registered action's own
|
|
82
|
+
schema (wrong enum value, missing required field, wrong type),
|
|
83
|
+
- each trigger's `config` against the trigger's `configSchema`,
|
|
84
|
+
- **unknown / typo'd config keys** — object configs are validated in
|
|
85
|
+
strict mode, so `levle: "info"` is reported rather than silently
|
|
86
|
+
stripped,
|
|
87
|
+
- recurses through `choose` / `parallel` / `repeat` / `sequence` so
|
|
88
|
+
nested action configs are covered too.
|
|
89
|
+
|
|
90
|
+
Issues come back with a dot-joinable `path` (e.g.
|
|
91
|
+
`actions.0.config.level`, `triggers.1.event`). The `validateDefinition`
|
|
92
|
+
RPC now returns these.
|
|
93
|
+
|
|
94
|
+
**Frontend — live + inline.** The automation editor re-validates on
|
|
95
|
+
every edit (debounced ~400ms) in BOTH tabs, and marks the offending
|
|
96
|
+
content in place rather than in a separate alert panel:
|
|
97
|
+
|
|
98
|
+
- **YAML tab** — issues (and YAML syntax errors) are squiggled at the
|
|
99
|
+
exact node. `@checkstack/ui`'s `CodeEditor` gained a `markers` prop;
|
|
100
|
+
the editor maps each issue's `path` onto the YAML document's node
|
|
101
|
+
range via a new `computeYamlMarkers` helper (walking up to the
|
|
102
|
+
nearest existing ancestor when a key is absent, e.g. a missing
|
|
103
|
+
required field).
|
|
104
|
+
- **Visual tab** — the specific card carrying an issue is marked: a
|
|
105
|
+
destructive border + warning icon + the field-level messages. A
|
|
106
|
+
`ValidationProvider` context partitions issues by owner (action card
|
|
107
|
+
/ trigger card / condition / top-level) using the action-node path
|
|
108
|
+
grammar, so a nested action's config error attaches to the nested
|
|
109
|
+
card, and a `choose`'s own `when` error attaches to the choose card.
|
|
110
|
+
`ActionCard` gained an `errors` prop. So importing YAML with a bad
|
|
111
|
+
value (the empty-dropdown case) now visibly flags the card instead of
|
|
112
|
+
being silent.
|
|
113
|
+
|
|
114
|
+
The big error alert is gone; the only residual panel is a slim fallback
|
|
115
|
+
for the rare top-level issue that can't attach to any card.
|
|
116
|
+
|
|
117
|
+
Note: strict config validation means an action whose config schema
|
|
118
|
+
intentionally allowed extra keys would now flag them; action configs
|
|
119
|
+
across the platform declare all their fields, so this only catches
|
|
120
|
+
genuine typos.
|
|
121
|
+
|
|
122
|
+
- e1a2077: feat(automation): reference artifacts by explicit action id (`artifacts.<id>.<name>`)
|
|
123
|
+
|
|
124
|
+
Multiple actions of the same type (e.g. two "create Jira issue" steps) used
|
|
125
|
+
to collide: both produced the artifact type `integration-jira.issue`, so a
|
|
126
|
+
template could only ever reach "the most recent one of that type". Artifacts
|
|
127
|
+
are now addressed by the producing action's instance `id` instead.
|
|
128
|
+
|
|
129
|
+
- Templates reference a produced artifact solely as
|
|
130
|
+
`{{ artifacts.<actionId>.<localArtifactName>.<field> }}`, e.g.
|
|
131
|
+
`{{ artifacts.open_jira.issue.issueKey }}`. The local artifact name is the
|
|
132
|
+
producing action's `produces` id with the owning plugin prefix stripped
|
|
133
|
+
(`integration-jira.issue` -> `issue`).
|
|
134
|
+
- `@checkstack/automation-backend`: the dispatch engine nests each produced
|
|
135
|
+
artifact under `artifacts[actionId][localName]` in the template scope and
|
|
136
|
+
records the `actionId` on the artifact row. `validate-definition` now
|
|
137
|
+
enforces that action ids are unique within an automation and that every
|
|
138
|
+
artifact-producing action carries an id.
|
|
139
|
+
- `@checkstack/automation-common`: action `id` is constrained to an
|
|
140
|
+
identifier (`/^[a-zA-Z_][a-zA-Z0-9_]*$/`) so it is always usable as a
|
|
141
|
+
plain template segment. The variable-scope resolver surfaces
|
|
142
|
+
`artifacts.<id>.<name>` (with full field completion) in the editor.
|
|
143
|
+
- `@checkstack/automation-frontend`: the action editor now has editable `Id`
|
|
144
|
+
and `Description` inputs (previously settable only via the YAML view), and
|
|
145
|
+
new steps get an auto-assigned, unique, log-friendly default id that the
|
|
146
|
+
operator can rename. Action ids are recorded on every run step, so run
|
|
147
|
+
logs are parseable by id regardless of kind.
|
|
148
|
+
|
|
149
|
+
**BREAKING (beta):** the previous flat, type-keyed scope form
|
|
150
|
+
`{{ artifacts["integration-jira.issue"] }}` is removed. Reference artifacts
|
|
151
|
+
by the producing action's id instead. Action ids may no longer contain
|
|
152
|
+
hyphens or dots (identifier characters only). Artifacts are per-run and
|
|
153
|
+
ephemeral, so no stored-data migration is needed.
|
|
154
|
+
|
|
155
|
+
- 41c77f4: feat(automation): native per-editor context for script actions (typed `context` for TS, `$ENV` for shell)
|
|
156
|
+
|
|
157
|
+
Script action editors had a confusing dual system: the TypeScript editor
|
|
158
|
+
type-checked `{{ }}` template text as code (so `{{ artifact.x }}` errored
|
|
159
|
+
with "Cannot find name"), and the runtime never actually populated the
|
|
160
|
+
`context` object. This standardises on a single, native context-access
|
|
161
|
+
mechanism per editor kind.
|
|
162
|
+
|
|
163
|
+
**Run scope reaches actions.** `ActionExecutionContext` gains a `scope`
|
|
164
|
+
(`{ trigger, artifacts, vars, repeat? }`), populated by the dispatch
|
|
165
|
+
engine from the same scope it already uses for `{{ }}` rendering. Actions
|
|
166
|
+
that need broad context (the script actions) read from it instead of
|
|
167
|
+
having to declare every artifact type in `consumes`. Additive and
|
|
168
|
+
optional, so existing actions are unaffected.
|
|
169
|
+
|
|
170
|
+
**TypeScript / JavaScript → typed `context`.** `run_script` now builds
|
|
171
|
+
`context` from the run scope, so `context.trigger.payload`,
|
|
172
|
+
`context.artifacts`, `context.var`, `context.repeat`, and
|
|
173
|
+
`context.automation` are populated at run time (previously
|
|
174
|
+
`context.trigger` was always empty). The editor types match via
|
|
175
|
+
`generateAutomationContextTypes`.
|
|
176
|
+
|
|
177
|
+
**Shell → `$CHECKSTACK_*` env vars.** `run_shell` flattens the run scope
|
|
178
|
+
into environment variables (e.g. `$CHECKSTACK_TRIGGER_PAYLOAD_TITLE`,
|
|
179
|
+
`$CHECKSTACK_ARTIFACT_INTEGRATION_JIRA_ISSUE_ISSUEKEY`). Arrays become a
|
|
180
|
+
single newline-separated var (iterate with `while IFS= read -r x; do …;
|
|
181
|
+
done <<< "$VAR"`). Every value is a plain string — no JSON blob, since
|
|
182
|
+
the container has no `jq` to parse one. A shared `toShellEnvKey`
|
|
183
|
+
helper (in `@checkstack/automation-common`) derives the names so the
|
|
184
|
+
shell editor's `$` autocomplete lists exactly what the runtime injects.
|
|
185
|
+
|
|
186
|
+
**One syntax per field kind (editor + runtime).** `MultiTypeEditorField`
|
|
187
|
+
no longer offers `{{ }}` autocomplete in `typescript` / `javascript` /
|
|
188
|
+
`shell` editors, and the dispatch engine no longer template-renders
|
|
189
|
+
native-code config fields (those whose `x-editor-types` is a code type) —
|
|
190
|
+
so `{{ }}` can't be used in a script by accident. Text / markup editors
|
|
191
|
+
(`raw`, `json`, `yaml`, `xml`, `markdown`, `formdata`) and plain string
|
|
192
|
+
fields keep `{{ }}` as before. Because both the automation and
|
|
193
|
+
health-check editors share `MultiTypeEditorField`, they behave
|
|
194
|
+
identically.
|
|
195
|
+
|
|
196
|
+
**Script-editor IntelliSense polish.** The code editors got a few
|
|
197
|
+
ergonomic fixes so the typed context is actually usable: the suggestion
|
|
198
|
+
**details panel auto-opens** (so long completion names are legible
|
|
199
|
+
on-focus, not hidden behind the chevron); word-based keyword noise is
|
|
200
|
+
disabled in favour of language-service + provider completions; and a
|
|
201
|
+
TS/JS completion provider makes `context.artifacts.` list the in-scope
|
|
202
|
+
artifact ids and **auto-convert the dot to bracket notation** —
|
|
203
|
+
`context.artifacts["integration-jira.issue"]` — since those ids aren't
|
|
204
|
+
valid identifiers. (Driven by a new opt-in `dottedKeyCompletions` prop on
|
|
205
|
+
the editor / `DynamicForm`.)
|
|
206
|
+
|
|
207
|
+
**BREAKING (beta):** `{{ }}` interpolation inside a script action's
|
|
208
|
+
`script` field (shell or TypeScript) is no longer expanded at run time —
|
|
209
|
+
read run data via the typed `context` object (TS) or `$CHECKSTACK_*` env
|
|
210
|
+
vars (shell) instead. Non-script config fields are unchanged.
|
|
211
|
+
|
|
212
|
+
Also fixes: switching a provider action in the visual editor now resets
|
|
213
|
+
its config, so the validator no longer reports the previous action's keys
|
|
214
|
+
as unrecognised.
|
|
215
|
+
|
|
216
|
+
- 41c77f4: feat(automation): Phase 10 — built-in triggers + actions
|
|
217
|
+
|
|
218
|
+
Ships the core automation catalog every install has out of the box:
|
|
219
|
+
|
|
220
|
+
**Triggers** (setup-backed via the shared
|
|
221
|
+
`automation-builtin-triggers` queue):
|
|
222
|
+
|
|
223
|
+
- `automation.cron` — recurring queue job on a cron pattern. Config:
|
|
224
|
+
`{ cronPattern }`. Payload: `{ firedAt }`.
|
|
225
|
+
- `automation.interval` — recurring queue job on a fixed interval.
|
|
226
|
+
Config: `{ intervalSeconds }`. `startDelay = intervalSeconds` so an
|
|
227
|
+
operator doesn't see a tick the instant they save the automation.
|
|
228
|
+
- `automation.template` — polls a boolean template at `intervalSeconds`
|
|
229
|
+
cadence and fires on the false → true edge. Uses
|
|
230
|
+
`template-engine.evaluateBoolean` with `{ now }` in scope; invalid
|
|
231
|
+
templates throw at setup so the operator sees the error in the
|
|
232
|
+
editor rather than as silently-never-firing.
|
|
233
|
+
|
|
234
|
+
All three share a single consumer + module-scoped `tickHandlers` map
|
|
235
|
+
keyed by jobId. Restart semantics work the same way regardless of the
|
|
236
|
+
queue backend: `setupTriggerSubscriptions` re-runs every enabled
|
|
237
|
+
automation's `setup()` in `afterPluginsReady` on every boot, and
|
|
238
|
+
`setup()` calls `scheduleRecurring(...)` with a deterministic jobId.
|
|
239
|
+
On a persistent queue (BullMQ/Redis), the second call is an in-place
|
|
240
|
+
update of the surviving recurring job. On the in-memory queue — whose
|
|
241
|
+
recurring-schedule map is wiped at shutdown — it re-creates the
|
|
242
|
+
schedule from scratch. Either way the schedule is back in place
|
|
243
|
+
before the consumer would dispatch.
|
|
244
|
+
|
|
245
|
+
**Actions**:
|
|
246
|
+
|
|
247
|
+
- `automation.log` — write a single line to the run logger at the
|
|
248
|
+
requested level (debug/info/warn/error). No artifact, no external
|
|
249
|
+
delivery — the cheapest "I want to see something happened here"
|
|
250
|
+
primitive, useful inside `choose` / `parallel` branches as a no-op
|
|
251
|
+
placeholder until the operator wires the real action.
|
|
252
|
+
- `automation.notify_user` — thin wrapper over
|
|
253
|
+
`NotificationApi.sendTransactional` so the core install has a
|
|
254
|
+
"notify a user" action without depending on the integration-
|
|
255
|
+
notification plugin. Produces `automation.notify_user_result`
|
|
256
|
+
(per-strategy outcome).
|
|
257
|
+
|
|
258
|
+
The built-in catalog is registered directly via the trigger/action
|
|
259
|
+
registries in `init()` — no extension-point round-trip needed, since
|
|
260
|
+
automation-backend owns the registry. Pulls in
|
|
261
|
+
`@checkstack/notification-common` as a runtime dep for the
|
|
262
|
+
service-mode RPC call.
|
|
263
|
+
|
|
264
|
+
- 41c77f4: feat(automation): backend RPC router with the full 15-endpoint contract
|
|
265
|
+
|
|
266
|
+
Wires up `core/automation-backend/src/router.ts` covering automation CRUD,
|
|
267
|
+
definition validation, manual runs, run history, registry introspection,
|
|
268
|
+
and a template playground. The contract is refactored to use the
|
|
269
|
+
project's `proc()` pattern so `autoAuthMiddleware` enforces `read` /
|
|
270
|
+
`manage` access automatically, and `AutomationApi` is exported via
|
|
271
|
+
`createClientDefinition` for the frontend client.
|
|
272
|
+
|
|
273
|
+
- 41c77f4: feat(automation): one-time migration of webhook subscriptions + remove legacy integration backend
|
|
274
|
+
|
|
275
|
+
**BREAKING CHANGES** (platform is in BETA — no major bump):
|
|
276
|
+
|
|
277
|
+
- `IntegrationProvider` no longer carries `config` (subscription
|
|
278
|
+
config) or `deliver`. The interface now models a connection provider
|
|
279
|
+
only: connection schema + `getConnectionOptions` + `testConnection`.
|
|
280
|
+
- The legacy subscription / delivery-log / event endpoints
|
|
281
|
+
(`listSubscriptions`, `createSubscription`, `getDeliveryLogs`,
|
|
282
|
+
`listEventTypes`, …) are removed from `integrationContract`.
|
|
283
|
+
- `delivery-coordinator`, `hook-subscriber`, `event-registry`, and the
|
|
284
|
+
`integrationEventExtensionPoint` are deleted. Plugins that
|
|
285
|
+
previously called `integrationEvents.registerEvent(...)` now
|
|
286
|
+
register their hooks as automation triggers via
|
|
287
|
+
`automationTriggerExtensionPoint.registerTrigger(...)`.
|
|
288
|
+
- Frontend pages `IntegrationsPage` and `DeliveryLogsPage` are gone;
|
|
289
|
+
the integration plugin's only remaining UI is connection
|
|
290
|
+
management. Subscription management lives under `/automation/...`.
|
|
291
|
+
- `webhook_subscriptions` and `delivery_logs` tables stay in the
|
|
292
|
+
database for one release as a safety net (no code reads or writes
|
|
293
|
+
them), and will be dropped in a follow-up migration.
|
|
294
|
+
|
|
295
|
+
**New**:
|
|
296
|
+
|
|
297
|
+
- `jira.create_issue`, `teams.post_message`, `webex.post_message`,
|
|
298
|
+
`webhook.send`, `integration-script.run_shell`, and
|
|
299
|
+
`integration-script.run_script` actions registered against the
|
|
300
|
+
Automation Platform with matching `*.message`, `*.delivery`,
|
|
301
|
+
`shell.result`, and `script.result` artifact types. The script
|
|
302
|
+
plugin exposes **two** actions — `run_shell` runs bash via the
|
|
303
|
+
shared `ShellScriptRunner` (Monaco `shell` editor), `run_script`
|
|
304
|
+
runs an ESM module in a Bun subprocess via `EsmScriptRunner`
|
|
305
|
+
(Monaco `typescript` editor + `defineIntegration` helper) — to
|
|
306
|
+
preserve the legacy provider split. `jira.create_issue` keeps the
|
|
307
|
+
dynamic field-mapping dropdown (driven by
|
|
308
|
+
`JIRA_RESOLVERS.FIELD_OPTIONS`).
|
|
309
|
+
- One-time data migration runs on boot in
|
|
310
|
+
`automation-backend.afterPluginsReady`. It reads
|
|
311
|
+
`webhook_subscriptions` via a new service RPC
|
|
312
|
+
`IntegrationApi.listLegacySubscriptions`, translates each row into
|
|
313
|
+
a single-trigger / single-action automation (marked with
|
|
314
|
+
`managed_by = "migrated-subscription:<id>"`), and is idempotent
|
|
315
|
+
across restarts.
|
|
316
|
+
- Failed translations are recorded in a new
|
|
317
|
+
`automation_migration_failures` table and surfaced via
|
|
318
|
+
`AutomationApi.listMigrationFailures` /
|
|
319
|
+
`acknowledgeMigrationFailure` so admins can review and re-create
|
|
320
|
+
failed entries by hand.
|
|
321
|
+
|
|
322
|
+
- 41c77f4: fix(automation): qualify action `produces` / `consumes` with the owning plugin id
|
|
323
|
+
|
|
324
|
+
`context.artifacts` showed up untyped (no fields) in the script editor
|
|
325
|
+
because action `produces` / `consumes` were hand-written full strings
|
|
326
|
+
(`"jira.issue"`) that did not match the artifact-type registry's
|
|
327
|
+
qualified id. The registry derives `${pluginId}.${id}`, and the plugin's
|
|
328
|
+
id is the package name `integration-jira`, so the artifact type actually
|
|
329
|
+
registers as `integration-jira.issue` — the editor's schema lookup
|
|
330
|
+
(`produces` vs registered `qualifiedId`) missed, leaving the artifact's
|
|
331
|
+
fields unknown. (Runtime store/consume happened to agree with each other
|
|
332
|
+
on the short string, so it "worked" but typed nothing.)
|
|
333
|
+
|
|
334
|
+
The action registry now qualifies `produces` with the owning plugin id,
|
|
335
|
+
exactly as it already qualifies the action's own `id` and as the
|
|
336
|
+
artifact-type registry qualifies the artifact type id — so the three can
|
|
337
|
+
never drift. Actions declare the **local** artifact id:
|
|
338
|
+
|
|
339
|
+
- `produces: "issue"` → registered as `integration-jira.issue`,
|
|
340
|
+
- `consumes: ["issue"]` → resolved against the owning plugin's namespace
|
|
341
|
+
at run time; `consumedArtifacts` is keyed by the local id, so an
|
|
342
|
+
action's `execute` reads `consumedArtifacts["issue"]`.
|
|
343
|
+
|
|
344
|
+
All five artifact-producing integration plugins (jira / teams / webex /
|
|
345
|
+
webhook / script) now declare local ids. With `produces` matching the
|
|
346
|
+
registered artifact type, the editor types `context.artifacts[...]` with
|
|
347
|
+
the real schema (e.g. `issueKey`, `projectKey`, `issueUrl`).
|
|
348
|
+
|
|
349
|
+
**BREAKING (beta):** the fully-qualified artifact type ids change from
|
|
350
|
+
the short form to the plugin-prefixed form, e.g. `jira.issue` →
|
|
351
|
+
`integration-jira.issue`. This affects how artifacts are referenced in
|
|
352
|
+
templates (`{{ artifact.integration-jira.issue.issueKey }}`), the TS
|
|
353
|
+
script `context.artifacts["integration-jira.issue"]`, and shell env names
|
|
354
|
+
(`$CHECKSTACK_ARTIFACT_INTEGRATION_JIRA_ISSUE_ISSUEKEY`). Artifacts are
|
|
355
|
+
per-run and ephemeral, so no stored-data migration is needed.
|
|
356
|
+
|
|
357
|
+
Note: this keeps the same-plugin produce→consume handoff (the current
|
|
358
|
+
pattern). Cross-plugin artifact consumption would need a follow-up to
|
|
359
|
+
allow a fully-qualified `consumes` ref.
|
|
360
|
+
|
|
361
|
+
- 6d52276: feat(automation): expose `trigger.actor` so automations can filter on who/what caused an event
|
|
362
|
+
|
|
363
|
+
Every platform event now carries an **actor** - the user, application (API
|
|
364
|
+
client), service (backend-to-backend), or `system` (background /
|
|
365
|
+
unauthenticated) that caused it - and the automation engine surfaces it to
|
|
366
|
+
automations as `trigger.actor`. This lets a trigger filter gate on the
|
|
367
|
+
origin of the event it reacts to:
|
|
368
|
+
|
|
369
|
+
```text
|
|
370
|
+
{{ trigger.actor.type == "system" }} # auto-created by the platform
|
|
371
|
+
{{ trigger.actor.type == "user" }} # a human
|
|
372
|
+
{{ trigger.actor.id == "app-deploybot" }} # a specific application
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
`trigger.actor` is available on **every** trigger - it is injected by the
|
|
376
|
+
platform, not declared per trigger - and editor autocomplete + Run Script
|
|
377
|
+
context types include `trigger.actor.{type,id,name}`.
|
|
378
|
+
|
|
379
|
+
How it works:
|
|
380
|
+
|
|
381
|
+
- **`@checkstack/common`** adds the canonical `Actor` type / `ActorSchema`
|
|
382
|
+
and `SYSTEM_ACTOR`.
|
|
383
|
+
- **`@checkstack/backend-api`** adds `resolveActor(user)` and a
|
|
384
|
+
`HookEventMeta` envelope. The hook listener / `onHook` signature gains an
|
|
385
|
+
optional second `meta` argument (additive, backward compatible).
|
|
386
|
+
- **`@checkstack/backend`** wraps emitted hooks in an envelope so the actor
|
|
387
|
+
travels with the payload through the distributed queue, unwrapping it
|
|
388
|
+
before delivery. The RPC emit path captures the authenticated caller;
|
|
389
|
+
background emits default to the system actor. Raw/legacy queue data is
|
|
390
|
+
treated as a system-actor payload, so delivery stays backward compatible.
|
|
391
|
+
- **`@checkstack/automation-backend`** threads the actor into the dispatch
|
|
392
|
+
scope (`trigger.actor`), available to trigger filters, top-level
|
|
393
|
+
conditions, and all run templates, and persisted in the run's scope
|
|
394
|
+
snapshot. Manual runs are attributed to the invoking user.
|
|
395
|
+
- **`@checkstack/automation-common`** / **`@checkstack/automation-frontend`**
|
|
396
|
+
expose `trigger.actor` in the editor variable scope and the generated
|
|
397
|
+
Run Script `context.trigger.actor` types.
|
|
398
|
+
|
|
399
|
+
No database migration and no per-trigger schema changes: the actor rides as
|
|
400
|
+
event-envelope metadata and in the run scope snapshot.
|
|
401
|
+
|
|
402
|
+
- 6d52276: feat(automation): expose `trigger.id` and reconcile the trigger scope so multiple triggers are distinguishable
|
|
403
|
+
|
|
404
|
+
Automations with more than one trigger could not tell which trigger fired:
|
|
405
|
+
the trigger id wasn't queryable, and scripts only received `trigger.event`
|
|
406
|
+
(so two triggers on the same event were indistinguishable). This exposes a
|
|
407
|
+
consistent trigger contract everywhere - `trigger.id`, `trigger.event`,
|
|
408
|
+
`trigger.actor`, `trigger.payload` - in templates, shell, and TypeScript
|
|
409
|
+
scripts.
|
|
410
|
+
|
|
411
|
+
- **`trigger.id` is now available** in templates (`{{ trigger.id }}`) and in
|
|
412
|
+
the script context (`context.trigger.id`). It is typed as the **literal
|
|
413
|
+
union** of the automation's trigger ids, so it discriminates triggers -
|
|
414
|
+
including two subscribed to the same `event`.
|
|
415
|
+
- **Auto-generated trigger ids.** The editor now assigns a unique, log-
|
|
416
|
+
friendly id to every trigger (derived from its event, e.g.
|
|
417
|
+
`incident_created`, deduped as `incident_created_2`), mirroring action ids:
|
|
418
|
+
seeded on the starter automation, assigned on add, and re-filled on blur.
|
|
419
|
+
- **Scripts now receive `trigger.id` and `trigger.actor`.** The
|
|
420
|
+
`ActionRunScope` projection previously dropped both (it only forwarded
|
|
421
|
+
`event` + `payload`), so `context.trigger.actor` was typed but never
|
|
422
|
+
populated - that gap is fixed.
|
|
423
|
+
- **Scope key reconciled.** The internal dispatch scope now exposes
|
|
424
|
+
`trigger.event` as the canonical key (matching the editor and script
|
|
425
|
+
contract) instead of leaking `trigger.eventId`; `trigger.eventId` is kept
|
|
426
|
+
as a back-compat alias, so `{{ trigger.event }}` now resolves in template
|
|
427
|
+
fields where it previously returned `undefined`.
|
|
428
|
+
|
|
429
|
+
No database migration: the actor and id ride in the run scope snapshot. A
|
|
430
|
+
shared `deriveTriggerId` is exported from `@checkstack/automation-common` so
|
|
431
|
+
the editor, generated script types, and the runtime all agree on derived ids.
|
|
432
|
+
|
|
433
|
+
### Patch Changes
|
|
434
|
+
|
|
435
|
+
- Updated dependencies [e2d6f25]
|
|
436
|
+
- Updated dependencies [e1a2077]
|
|
437
|
+
- Updated dependencies [41c77f4]
|
|
438
|
+
- Updated dependencies [41c77f4]
|
|
439
|
+
- Updated dependencies [41c77f4]
|
|
440
|
+
- Updated dependencies [41c77f4]
|
|
441
|
+
- Updated dependencies [4832e33]
|
|
442
|
+
- Updated dependencies [6d52276]
|
|
443
|
+
- Updated dependencies [6d52276]
|
|
444
|
+
- Updated dependencies [35bc682]
|
|
445
|
+
- @checkstack/automation-common@0.2.0
|
|
446
|
+
- @checkstack/template-engine@0.2.0
|
|
447
|
+
- @checkstack/integration-common@0.6.0
|
|
448
|
+
- @checkstack/common@0.12.0
|
|
449
|
+
- @checkstack/backend-api@0.18.0
|
|
450
|
+
- @checkstack/command-backend@0.1.31
|
|
451
|
+
- @checkstack/notification-common@1.2.1
|
|
452
|
+
- @checkstack/signal-common@0.2.5
|
|
453
|
+
- @checkstack/queue-api@0.3.6
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
CREATE TABLE "automation_artifacts" (
|
|
2
|
+
"id" text PRIMARY KEY NOT NULL,
|
|
3
|
+
"automation_id" text NOT NULL,
|
|
4
|
+
"run_id" text NOT NULL,
|
|
5
|
+
"step_id" text NOT NULL,
|
|
6
|
+
"action_id" text,
|
|
7
|
+
"artifact_type" text NOT NULL,
|
|
8
|
+
"data" jsonb NOT NULL,
|
|
9
|
+
"context_key" text,
|
|
10
|
+
"closed_at" timestamp,
|
|
11
|
+
"created_at" timestamp DEFAULT now() NOT NULL
|
|
12
|
+
);
|
|
13
|
+
--> statement-breakpoint
|
|
14
|
+
CREATE TABLE "automation_run_steps" (
|
|
15
|
+
"id" text PRIMARY KEY NOT NULL,
|
|
16
|
+
"run_id" text NOT NULL,
|
|
17
|
+
"action_path" text NOT NULL,
|
|
18
|
+
"action_id" text,
|
|
19
|
+
"action_kind" text NOT NULL,
|
|
20
|
+
"provider_action_id" text,
|
|
21
|
+
"status" text DEFAULT 'pending' NOT NULL,
|
|
22
|
+
"attempts" integer DEFAULT 0 NOT NULL,
|
|
23
|
+
"error_message" text,
|
|
24
|
+
"result_payload" jsonb,
|
|
25
|
+
"started_at" timestamp DEFAULT now() NOT NULL,
|
|
26
|
+
"finished_at" timestamp
|
|
27
|
+
);
|
|
28
|
+
--> statement-breakpoint
|
|
29
|
+
CREATE TABLE "automation_runs" (
|
|
30
|
+
"id" text PRIMARY KEY NOT NULL,
|
|
31
|
+
"automation_id" text NOT NULL,
|
|
32
|
+
"trigger_id" text NOT NULL,
|
|
33
|
+
"trigger_event_id" text NOT NULL,
|
|
34
|
+
"trigger_payload" jsonb NOT NULL,
|
|
35
|
+
"context_key" text,
|
|
36
|
+
"status" text DEFAULT 'pending' NOT NULL,
|
|
37
|
+
"error_message" text,
|
|
38
|
+
"started_at" timestamp DEFAULT now() NOT NULL,
|
|
39
|
+
"finished_at" timestamp
|
|
40
|
+
);
|
|
41
|
+
--> statement-breakpoint
|
|
42
|
+
CREATE TABLE "automation_wait_locks" (
|
|
43
|
+
"id" text PRIMARY KEY NOT NULL,
|
|
44
|
+
"run_id" text NOT NULL,
|
|
45
|
+
"action_path" text NOT NULL,
|
|
46
|
+
"event_id" text NOT NULL,
|
|
47
|
+
"context_key" text,
|
|
48
|
+
"filter_template" text,
|
|
49
|
+
"timeout_at" timestamp,
|
|
50
|
+
"created_at" timestamp DEFAULT now() NOT NULL
|
|
51
|
+
);
|
|
52
|
+
--> statement-breakpoint
|
|
53
|
+
CREATE TABLE "automations" (
|
|
54
|
+
"id" text PRIMARY KEY NOT NULL,
|
|
55
|
+
"name" text NOT NULL,
|
|
56
|
+
"description" text,
|
|
57
|
+
"status" text DEFAULT 'enabled' NOT NULL,
|
|
58
|
+
"definition" jsonb NOT NULL,
|
|
59
|
+
"managed_by" text,
|
|
60
|
+
"created_at" timestamp DEFAULT now() NOT NULL,
|
|
61
|
+
"updated_at" timestamp DEFAULT now() NOT NULL
|
|
62
|
+
);
|
|
63
|
+
--> statement-breakpoint
|
|
64
|
+
ALTER TABLE "automation_artifacts" ADD CONSTRAINT "automation_artifacts_automation_id_automations_id_fk" FOREIGN KEY ("automation_id") REFERENCES "automations"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
|
65
|
+
ALTER TABLE "automation_artifacts" ADD CONSTRAINT "automation_artifacts_run_id_automation_runs_id_fk" FOREIGN KEY ("run_id") REFERENCES "automation_runs"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
|
66
|
+
ALTER TABLE "automation_artifacts" ADD CONSTRAINT "automation_artifacts_step_id_automation_run_steps_id_fk" FOREIGN KEY ("step_id") REFERENCES "automation_run_steps"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
|
67
|
+
ALTER TABLE "automation_run_steps" ADD CONSTRAINT "automation_run_steps_run_id_automation_runs_id_fk" FOREIGN KEY ("run_id") REFERENCES "automation_runs"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
|
68
|
+
ALTER TABLE "automation_runs" ADD CONSTRAINT "automation_runs_automation_id_automations_id_fk" FOREIGN KEY ("automation_id") REFERENCES "automations"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
|
69
|
+
ALTER TABLE "automation_wait_locks" ADD CONSTRAINT "automation_wait_locks_run_id_automation_runs_id_fk" FOREIGN KEY ("run_id") REFERENCES "automation_runs"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
|
70
|
+
CREATE INDEX "automation_artifacts_context_lookup_idx" ON "automation_artifacts" USING btree ("automation_id","context_key","artifact_type","created_at");--> statement-breakpoint
|
|
71
|
+
CREATE INDEX "automation_artifacts_action_lookup_idx" ON "automation_artifacts" USING btree ("automation_id","action_id","created_at");--> statement-breakpoint
|
|
72
|
+
CREATE INDEX "automation_artifacts_open_idx" ON "automation_artifacts" USING btree ("automation_id","closed_at");--> statement-breakpoint
|
|
73
|
+
CREATE INDEX "automation_run_steps_run_idx" ON "automation_run_steps" USING btree ("run_id");--> statement-breakpoint
|
|
74
|
+
CREATE INDEX "automation_runs_automation_idx" ON "automation_runs" USING btree ("automation_id","started_at");--> statement-breakpoint
|
|
75
|
+
CREATE INDEX "automation_runs_status_idx" ON "automation_runs" USING btree ("status");--> statement-breakpoint
|
|
76
|
+
CREATE INDEX "automation_runs_context_key_idx" ON "automation_runs" USING btree ("automation_id","context_key");--> statement-breakpoint
|
|
77
|
+
CREATE INDEX "automation_wait_locks_event_lookup_idx" ON "automation_wait_locks" USING btree ("event_id","context_key");--> statement-breakpoint
|
|
78
|
+
CREATE INDEX "automation_wait_locks_timeout_idx" ON "automation_wait_locks" USING btree ("timeout_at");--> statement-breakpoint
|
|
79
|
+
CREATE INDEX "automations_status_idx" ON "automations" USING btree ("status");--> statement-breakpoint
|
|
80
|
+
CREATE INDEX "automations_managed_by_idx" ON "automations" USING btree ("managed_by");
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
CREATE TABLE "automation_run_state" (
|
|
2
|
+
"run_id" text PRIMARY KEY NOT NULL,
|
|
3
|
+
"scope_snapshot" jsonb NOT NULL,
|
|
4
|
+
"last_action_path" text,
|
|
5
|
+
"last_heartbeat_at" timestamp DEFAULT now() NOT NULL,
|
|
6
|
+
"updated_at" timestamp DEFAULT now() NOT NULL
|
|
7
|
+
);
|
|
8
|
+
--> statement-breakpoint
|
|
9
|
+
ALTER TABLE "automation_wait_locks" ADD COLUMN "kind" text DEFAULT 'trigger' NOT NULL;--> statement-breakpoint
|
|
10
|
+
ALTER TABLE "automation_run_state" ADD CONSTRAINT "automation_run_state_run_id_automation_runs_id_fk" FOREIGN KEY ("run_id") REFERENCES "automation_runs"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
|
11
|
+
CREATE INDEX "automation_run_state_heartbeat_idx" ON "automation_run_state" USING btree ("last_heartbeat_at");--> statement-breakpoint
|
|
12
|
+
CREATE INDEX "automation_wait_locks_run_idx" ON "automation_wait_locks" USING btree ("run_id");
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
CREATE TABLE "automation_migration_failures" (
|
|
2
|
+
"id" text PRIMARY KEY NOT NULL,
|
|
3
|
+
"subscription_id" text NOT NULL,
|
|
4
|
+
"subscription_name" text NOT NULL,
|
|
5
|
+
"provider_id" text NOT NULL,
|
|
6
|
+
"event_id" text NOT NULL,
|
|
7
|
+
"reason" text NOT NULL,
|
|
8
|
+
"detail" text,
|
|
9
|
+
"provider_config" jsonb NOT NULL,
|
|
10
|
+
"created_at" timestamp DEFAULT now() NOT NULL,
|
|
11
|
+
CONSTRAINT "automation_migration_failures_subscription_id_unique" UNIQUE("subscription_id")
|
|
12
|
+
);
|