@amalgm/automations 0.1.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.
Files changed (41) hide show
  1. package/AXIOMS.md +32 -0
  2. package/PURPOSE.md +35 -0
  3. package/README.md +130 -0
  4. package/dist/src/automations.d.ts +27 -0
  5. package/dist/src/automations.js +199 -0
  6. package/dist/src/cli-main.d.ts +2 -0
  7. package/dist/src/cli-main.js +21 -0
  8. package/dist/src/cli.d.ts +10 -0
  9. package/dist/src/cli.js +183 -0
  10. package/dist/src/client.d.ts +7 -0
  11. package/dist/src/client.js +85 -0
  12. package/dist/src/contract.d.ts +176 -0
  13. package/dist/src/contract.js +1 -0
  14. package/dist/src/crud.d.ts +32 -0
  15. package/dist/src/crud.js +241 -0
  16. package/dist/src/errors.d.ts +17 -0
  17. package/dist/src/errors.js +32 -0
  18. package/dist/src/http.d.ts +7 -0
  19. package/dist/src/http.js +154 -0
  20. package/dist/src/index.d.ts +14 -0
  21. package/dist/src/index.js +18 -0
  22. package/dist/src/mcp-main.d.ts +2 -0
  23. package/dist/src/mcp-main.js +17 -0
  24. package/dist/src/mcp.d.ts +3 -0
  25. package/dist/src/mcp.js +88 -0
  26. package/dist/src/schedule.d.ts +2 -0
  27. package/dist/src/schedule.js +10 -0
  28. package/dist/src/schema.d.ts +236 -0
  29. package/dist/src/schema.js +119 -0
  30. package/dist/src/supabase-crud.d.ts +44 -0
  31. package/dist/src/supabase-crud.js +271 -0
  32. package/dist/src/supabase-store.d.ts +26 -0
  33. package/dist/src/supabase-store.js +123 -0
  34. package/dist/src/types.d.ts +128 -0
  35. package/dist/src/types.js +1 -0
  36. package/dist/src/validation.d.ts +11 -0
  37. package/dist/src/validation.js +69 -0
  38. package/dist/src/webhook.d.ts +18 -0
  39. package/dist/src/webhook.js +60 -0
  40. package/package.json +66 -0
  41. package/supabase/migrations/20260802000000_create_automations.sql +609 -0
@@ -0,0 +1,609 @@
1
+ -- Isolated integration-test schema for this service. The canonical deployment
2
+ -- copy is amalgm-ui/supabase/migrations/20260806210000_create_automations.sql.
3
+ -- Keep their SQL bodies equivalent.
4
+
5
+ CREATE EXTENSION IF NOT EXISTS pgcrypto;
6
+
7
+ CREATE TABLE amalgm_automations (
8
+ user_id uuid NOT NULL,
9
+ id text NOT NULL,
10
+ target_id text NOT NULL,
11
+ name text,
12
+ description text,
13
+ enabled boolean NOT NULL DEFAULT true,
14
+ created_at timestamptz NOT NULL DEFAULT now(),
15
+ updated_at timestamptz NOT NULL DEFAULT now(),
16
+ PRIMARY KEY (user_id, id)
17
+ );
18
+
19
+ CREATE TABLE amalgm_automation_workflows (
20
+ user_id uuid NOT NULL,
21
+ automation_id text NOT NULL,
22
+ id text NOT NULL,
23
+ name text,
24
+ script text NOT NULL,
25
+ compiled jsonb,
26
+ allowlist jsonb,
27
+ limits jsonb,
28
+ created_at timestamptz NOT NULL DEFAULT now(),
29
+ updated_at timestamptz NOT NULL DEFAULT now(),
30
+ PRIMARY KEY (user_id, automation_id),
31
+ FOREIGN KEY (user_id, automation_id)
32
+ REFERENCES amalgm_automations(user_id, id) ON DELETE CASCADE
33
+ );
34
+
35
+ CREATE TABLE amalgm_automation_triggers (
36
+ user_id uuid NOT NULL,
37
+ automation_id text NOT NULL,
38
+ id text NOT NULL,
39
+ kind text NOT NULL CHECK (kind IN ('schedule', 'webhook')),
40
+ enabled boolean NOT NULL DEFAULT true,
41
+ cron text,
42
+ timezone text,
43
+ source text,
44
+ event text,
45
+ secret text,
46
+ created_at timestamptz NOT NULL DEFAULT now(),
47
+ updated_at timestamptz NOT NULL DEFAULT now(),
48
+ PRIMARY KEY (user_id, automation_id, id),
49
+ FOREIGN KEY (user_id, automation_id)
50
+ REFERENCES amalgm_automations(user_id, id) ON DELETE CASCADE,
51
+ CHECK (
52
+ (kind = 'schedule' AND cron IS NOT NULL AND timezone IS NOT NULL
53
+ AND source IS NULL AND event IS NULL AND secret IS NULL)
54
+ OR
55
+ (kind = 'webhook' AND cron IS NULL AND timezone IS NULL AND source IS NOT NULL
56
+ AND event IS NOT NULL AND length(secret) >= 16)
57
+ )
58
+ );
59
+
60
+ CREATE INDEX amalgm_automation_triggers_by_automation
61
+ ON amalgm_automation_triggers(user_id, automation_id, kind, created_at, id);
62
+
63
+ CREATE TABLE amalgm_automation_runs (
64
+ id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
65
+ user_id uuid NOT NULL,
66
+ target_id text NOT NULL,
67
+ automation_id text NOT NULL,
68
+ trigger_id text NOT NULL,
69
+ workflow_id text NOT NULL,
70
+ automation_payload jsonb NOT NULL DEFAULT '{}'::jsonb,
71
+ input jsonb NOT NULL DEFAULT '{}'::jsonb,
72
+ status text NOT NULL DEFAULT 'pending'
73
+ CHECK (status IN ('pending', 'sent', 'running', 'completed', 'failed')),
74
+ created_at timestamptz NOT NULL DEFAULT now(),
75
+ sent_at timestamptz,
76
+ started_at timestamptz,
77
+ finished_at timestamptz,
78
+ output jsonb,
79
+ error text
80
+ );
81
+
82
+ CREATE INDEX amalgm_automation_runs_history
83
+ ON amalgm_automation_runs(user_id, automation_id, created_at DESC, id DESC);
84
+
85
+ ALTER TABLE amalgm_automations ENABLE ROW LEVEL SECURITY;
86
+ ALTER TABLE amalgm_automation_workflows ENABLE ROW LEVEL SECURITY;
87
+ ALTER TABLE amalgm_automation_triggers ENABLE ROW LEVEL SECURITY;
88
+ ALTER TABLE amalgm_automation_runs ENABLE ROW LEVEL SECURITY;
89
+
90
+ CREATE FUNCTION amalgm_trigger_json(p_trigger amalgm_automation_triggers)
91
+ RETURNS jsonb
92
+ LANGUAGE sql
93
+ IMMUTABLE
94
+ SET search_path = public
95
+ AS $$
96
+ SELECT jsonb_build_object(
97
+ 'id', p_trigger.id,
98
+ 'automation_id', p_trigger.automation_id,
99
+ 'kind', p_trigger.kind,
100
+ 'enabled', p_trigger.enabled,
101
+ 'cron', p_trigger.cron,
102
+ 'timezone', p_trigger.timezone,
103
+ 'source', p_trigger.source,
104
+ 'event', p_trigger.event,
105
+ 'secret_configured', p_trigger.secret IS NOT NULL,
106
+ 'created_at', p_trigger.created_at,
107
+ 'updated_at', p_trigger.updated_at
108
+ );
109
+ $$;
110
+
111
+ CREATE FUNCTION create_amalgm_automation(p_user_id uuid, p_automation jsonb)
112
+ RETURNS jsonb
113
+ LANGUAGE plpgsql
114
+ SECURITY DEFINER
115
+ SET search_path = public
116
+ AS $$
117
+ DECLARE
118
+ v_row amalgm_automations;
119
+ BEGIN
120
+ INSERT INTO amalgm_automations (user_id, id, target_id, name, description, enabled)
121
+ VALUES (
122
+ p_user_id,
123
+ p_automation->>'id',
124
+ p_automation->>'targetId',
125
+ NULLIF(p_automation->>'name', ''),
126
+ NULLIF(p_automation->>'description', ''),
127
+ (p_automation->>'enabled')::boolean
128
+ )
129
+ RETURNING * INTO v_row;
130
+ RETURN to_jsonb(v_row) - 'user_id';
131
+ END;
132
+ $$;
133
+
134
+ CREATE FUNCTION list_amalgm_automations(
135
+ p_user_id uuid,
136
+ p_target_id text,
137
+ p_enabled boolean,
138
+ p_limit integer,
139
+ p_offset integer
140
+ )
141
+ RETURNS jsonb
142
+ LANGUAGE sql
143
+ SECURITY DEFINER
144
+ SET search_path = public
145
+ AS $$
146
+ WITH filtered AS (
147
+ SELECT id, target_id, name, description, enabled, created_at, updated_at
148
+ FROM amalgm_automations
149
+ WHERE user_id = p_user_id
150
+ AND (p_target_id IS NULL OR target_id = p_target_id)
151
+ AND (p_enabled IS NULL OR enabled = p_enabled)
152
+ ), page_rows AS (
153
+ SELECT * FROM filtered ORDER BY created_at DESC, id DESC LIMIT p_limit OFFSET p_offset
154
+ )
155
+ SELECT jsonb_build_object(
156
+ 'items', COALESCE((SELECT jsonb_agg(to_jsonb(page_rows)) FROM page_rows), '[]'::jsonb),
157
+ 'total', (SELECT count(*) FROM filtered)
158
+ );
159
+ $$;
160
+
161
+ CREATE FUNCTION get_amalgm_automation(p_user_id uuid, p_automation_id text)
162
+ RETURNS jsonb
163
+ LANGUAGE sql
164
+ SECURITY DEFINER
165
+ SET search_path = public
166
+ AS $$
167
+ SELECT to_jsonb(a) - 'user_id'
168
+ FROM amalgm_automations a
169
+ WHERE a.user_id = p_user_id AND a.id = p_automation_id;
170
+ $$;
171
+
172
+ CREATE FUNCTION update_amalgm_automation(
173
+ p_user_id uuid,
174
+ p_automation_id text,
175
+ p_patch jsonb
176
+ )
177
+ RETURNS jsonb
178
+ LANGUAGE plpgsql
179
+ SECURITY DEFINER
180
+ SET search_path = public
181
+ AS $$
182
+ DECLARE
183
+ v_row amalgm_automations;
184
+ BEGIN
185
+ UPDATE amalgm_automations SET
186
+ target_id = CASE WHEN p_patch ? 'targetId' THEN p_patch->>'targetId' ELSE target_id END,
187
+ name = CASE WHEN p_patch ? 'name' THEN NULLIF(p_patch->>'name', '') ELSE name END,
188
+ description = CASE WHEN p_patch ? 'description' THEN NULLIF(p_patch->>'description', '') ELSE description END,
189
+ enabled = CASE WHEN p_patch ? 'enabled' THEN (p_patch->>'enabled')::boolean ELSE enabled END,
190
+ updated_at = now()
191
+ WHERE user_id = p_user_id AND id = p_automation_id
192
+ RETURNING * INTO v_row;
193
+ IF NOT FOUND THEN RETURN NULL; END IF;
194
+ RETURN to_jsonb(v_row) - 'user_id';
195
+ END;
196
+ $$;
197
+
198
+ CREATE FUNCTION delete_amalgm_automation(p_user_id uuid, p_automation_id text)
199
+ RETURNS boolean
200
+ LANGUAGE plpgsql
201
+ SECURITY DEFINER
202
+ SET search_path = public
203
+ AS $$
204
+ BEGIN
205
+ DELETE FROM amalgm_automations WHERE user_id = p_user_id AND id = p_automation_id;
206
+ RETURN FOUND;
207
+ END;
208
+ $$;
209
+
210
+ CREATE FUNCTION create_amalgm_schedule_trigger(
211
+ p_user_id uuid,
212
+ p_automation_id text,
213
+ p_trigger jsonb
214
+ )
215
+ RETURNS jsonb
216
+ LANGUAGE plpgsql
217
+ SECURITY DEFINER
218
+ SET search_path = public
219
+ AS $$
220
+ DECLARE
221
+ v_row amalgm_automation_triggers;
222
+ BEGIN
223
+ INSERT INTO amalgm_automation_triggers (
224
+ user_id, automation_id, id, kind, enabled, cron, timezone
225
+ )
226
+ SELECT p_user_id, p_automation_id, p_trigger->>'id', 'schedule',
227
+ (p_trigger->>'enabled')::boolean, p_trigger->>'cron', p_trigger->>'timezone'
228
+ WHERE EXISTS (
229
+ SELECT 1 FROM amalgm_automations
230
+ WHERE user_id = p_user_id AND id = p_automation_id
231
+ )
232
+ RETURNING * INTO v_row;
233
+ IF NOT FOUND THEN RETURN NULL; END IF;
234
+ RETURN amalgm_trigger_json(v_row);
235
+ END;
236
+ $$;
237
+
238
+ CREATE FUNCTION list_amalgm_schedule_triggers(
239
+ p_user_id uuid,
240
+ p_automation_id text,
241
+ p_limit integer,
242
+ p_offset integer
243
+ )
244
+ RETURNS jsonb
245
+ LANGUAGE sql
246
+ SECURITY DEFINER
247
+ SET search_path = public
248
+ AS $$
249
+ WITH filtered AS (
250
+ SELECT * FROM amalgm_automation_triggers
251
+ WHERE user_id = p_user_id AND automation_id = p_automation_id AND kind = 'schedule'
252
+ ), page_rows AS (
253
+ SELECT * FROM filtered ORDER BY created_at DESC, id DESC LIMIT p_limit OFFSET p_offset
254
+ )
255
+ SELECT jsonb_build_object(
256
+ 'items', COALESCE((SELECT jsonb_agg(amalgm_trigger_json(ROW(page_rows.*)::amalgm_automation_triggers)) FROM page_rows), '[]'::jsonb),
257
+ 'total', (SELECT count(*) FROM filtered)
258
+ );
259
+ $$;
260
+
261
+ CREATE FUNCTION get_amalgm_schedule_trigger(
262
+ p_user_id uuid,
263
+ p_automation_id text,
264
+ p_trigger_id text
265
+ )
266
+ RETURNS jsonb
267
+ LANGUAGE sql
268
+ SECURITY DEFINER
269
+ SET search_path = public
270
+ AS $$
271
+ SELECT amalgm_trigger_json(t)
272
+ FROM amalgm_automation_triggers t
273
+ WHERE t.user_id = p_user_id AND t.automation_id = p_automation_id
274
+ AND t.id = p_trigger_id AND t.kind = 'schedule';
275
+ $$;
276
+
277
+ CREATE FUNCTION update_amalgm_schedule_trigger(
278
+ p_user_id uuid,
279
+ p_automation_id text,
280
+ p_trigger_id text,
281
+ p_patch jsonb
282
+ )
283
+ RETURNS jsonb
284
+ LANGUAGE plpgsql
285
+ SECURITY DEFINER
286
+ SET search_path = public
287
+ AS $$
288
+ DECLARE
289
+ v_row amalgm_automation_triggers;
290
+ BEGIN
291
+ UPDATE amalgm_automation_triggers SET
292
+ cron = CASE WHEN p_patch ? 'cron' THEN p_patch->>'cron' ELSE cron END,
293
+ timezone = CASE WHEN p_patch ? 'timezone' THEN p_patch->>'timezone' ELSE timezone END,
294
+ enabled = CASE WHEN p_patch ? 'enabled' THEN (p_patch->>'enabled')::boolean ELSE enabled END,
295
+ updated_at = now()
296
+ WHERE user_id = p_user_id AND automation_id = p_automation_id
297
+ AND id = p_trigger_id AND kind = 'schedule'
298
+ RETURNING * INTO v_row;
299
+ IF NOT FOUND THEN RETURN NULL; END IF;
300
+ RETURN amalgm_trigger_json(v_row);
301
+ END;
302
+ $$;
303
+
304
+ CREATE FUNCTION delete_amalgm_schedule_trigger(
305
+ p_user_id uuid,
306
+ p_automation_id text,
307
+ p_trigger_id text
308
+ )
309
+ RETURNS boolean
310
+ LANGUAGE plpgsql
311
+ SECURITY DEFINER
312
+ SET search_path = public
313
+ AS $$
314
+ BEGIN
315
+ DELETE FROM amalgm_automation_triggers
316
+ WHERE user_id = p_user_id AND automation_id = p_automation_id
317
+ AND id = p_trigger_id AND kind = 'schedule';
318
+ RETURN FOUND;
319
+ END;
320
+ $$;
321
+
322
+ CREATE FUNCTION create_amalgm_webhook_trigger(
323
+ p_user_id uuid,
324
+ p_automation_id text,
325
+ p_trigger jsonb
326
+ )
327
+ RETURNS jsonb
328
+ LANGUAGE plpgsql
329
+ SECURITY DEFINER
330
+ SET search_path = public
331
+ AS $$
332
+ DECLARE
333
+ v_row amalgm_automation_triggers;
334
+ BEGIN
335
+ INSERT INTO amalgm_automation_triggers (
336
+ user_id, automation_id, id, kind, enabled, source, event, secret
337
+ )
338
+ SELECT p_user_id, p_automation_id, p_trigger->>'id', 'webhook',
339
+ (p_trigger->>'enabled')::boolean, p_trigger->>'source', p_trigger->>'event', p_trigger->>'secret'
340
+ WHERE EXISTS (
341
+ SELECT 1 FROM amalgm_automations
342
+ WHERE user_id = p_user_id AND id = p_automation_id
343
+ )
344
+ RETURNING * INTO v_row;
345
+ IF NOT FOUND THEN RETURN NULL; END IF;
346
+ RETURN amalgm_trigger_json(v_row);
347
+ END;
348
+ $$;
349
+
350
+ CREATE FUNCTION list_amalgm_webhook_triggers(
351
+ p_user_id uuid,
352
+ p_automation_id text,
353
+ p_limit integer,
354
+ p_offset integer
355
+ )
356
+ RETURNS jsonb
357
+ LANGUAGE sql
358
+ SECURITY DEFINER
359
+ SET search_path = public
360
+ AS $$
361
+ WITH filtered AS (
362
+ SELECT * FROM amalgm_automation_triggers
363
+ WHERE user_id = p_user_id AND automation_id = p_automation_id AND kind = 'webhook'
364
+ ), page_rows AS (
365
+ SELECT * FROM filtered ORDER BY created_at DESC, id DESC LIMIT p_limit OFFSET p_offset
366
+ )
367
+ SELECT jsonb_build_object(
368
+ 'items', COALESCE((SELECT jsonb_agg(amalgm_trigger_json(ROW(page_rows.*)::amalgm_automation_triggers)) FROM page_rows), '[]'::jsonb),
369
+ 'total', (SELECT count(*) FROM filtered)
370
+ );
371
+ $$;
372
+
373
+ CREATE FUNCTION get_amalgm_webhook_trigger(
374
+ p_user_id uuid,
375
+ p_automation_id text,
376
+ p_trigger_id text
377
+ )
378
+ RETURNS jsonb
379
+ LANGUAGE sql
380
+ SECURITY DEFINER
381
+ SET search_path = public
382
+ AS $$
383
+ SELECT amalgm_trigger_json(t)
384
+ FROM amalgm_automation_triggers t
385
+ WHERE t.user_id = p_user_id AND t.automation_id = p_automation_id
386
+ AND t.id = p_trigger_id AND t.kind = 'webhook';
387
+ $$;
388
+
389
+ CREATE FUNCTION update_amalgm_webhook_trigger(
390
+ p_user_id uuid,
391
+ p_automation_id text,
392
+ p_trigger_id text,
393
+ p_patch jsonb
394
+ )
395
+ RETURNS jsonb
396
+ LANGUAGE plpgsql
397
+ SECURITY DEFINER
398
+ SET search_path = public
399
+ AS $$
400
+ DECLARE
401
+ v_row amalgm_automation_triggers;
402
+ BEGIN
403
+ UPDATE amalgm_automation_triggers SET
404
+ source = CASE WHEN p_patch ? 'source' THEN p_patch->>'source' ELSE source END,
405
+ event = CASE WHEN p_patch ? 'event' THEN p_patch->>'event' ELSE event END,
406
+ secret = CASE WHEN p_patch ? 'secret' THEN p_patch->>'secret' ELSE secret END,
407
+ enabled = CASE WHEN p_patch ? 'enabled' THEN (p_patch->>'enabled')::boolean ELSE enabled END,
408
+ updated_at = now()
409
+ WHERE user_id = p_user_id AND automation_id = p_automation_id
410
+ AND id = p_trigger_id AND kind = 'webhook'
411
+ RETURNING * INTO v_row;
412
+ IF NOT FOUND THEN RETURN NULL; END IF;
413
+ RETURN amalgm_trigger_json(v_row);
414
+ END;
415
+ $$;
416
+
417
+ CREATE FUNCTION delete_amalgm_webhook_trigger(
418
+ p_user_id uuid,
419
+ p_automation_id text,
420
+ p_trigger_id text
421
+ )
422
+ RETURNS boolean
423
+ LANGUAGE plpgsql
424
+ SECURITY DEFINER
425
+ SET search_path = public
426
+ AS $$
427
+ BEGIN
428
+ DELETE FROM amalgm_automation_triggers
429
+ WHERE user_id = p_user_id AND automation_id = p_automation_id
430
+ AND id = p_trigger_id AND kind = 'webhook';
431
+ RETURN FOUND;
432
+ END;
433
+ $$;
434
+
435
+ CREATE FUNCTION list_amalgm_triggers(p_user_id uuid, p_automation_id text)
436
+ RETURNS jsonb
437
+ LANGUAGE sql
438
+ SECURITY DEFINER
439
+ SET search_path = public
440
+ AS $$
441
+ SELECT COALESCE(jsonb_agg(amalgm_trigger_json(t) ORDER BY t.created_at DESC, t.id DESC), '[]'::jsonb)
442
+ FROM amalgm_automation_triggers t
443
+ WHERE t.user_id = p_user_id AND t.automation_id = p_automation_id;
444
+ $$;
445
+
446
+ CREATE FUNCTION create_amalgm_workflow(
447
+ p_user_id uuid,
448
+ p_automation_id text,
449
+ p_workflow jsonb
450
+ )
451
+ RETURNS jsonb
452
+ LANGUAGE plpgsql
453
+ SECURITY DEFINER
454
+ SET search_path = public
455
+ AS $$
456
+ DECLARE
457
+ v_row amalgm_automation_workflows;
458
+ BEGIN
459
+ INSERT INTO amalgm_automation_workflows (
460
+ user_id, automation_id, id, name, script, compiled, allowlist, limits
461
+ )
462
+ SELECT p_user_id, p_automation_id, p_workflow->>'id',
463
+ NULLIF(p_workflow->>'name', ''), p_workflow->>'script',
464
+ p_workflow->'compiled', p_workflow->'allowlist', p_workflow->'limits'
465
+ WHERE EXISTS (
466
+ SELECT 1 FROM amalgm_automations
467
+ WHERE user_id = p_user_id AND id = p_automation_id
468
+ )
469
+ RETURNING * INTO v_row;
470
+ IF NOT FOUND THEN RETURN NULL; END IF;
471
+ RETURN to_jsonb(v_row) - 'user_id';
472
+ END;
473
+ $$;
474
+
475
+ CREATE FUNCTION get_amalgm_workflow(p_user_id uuid, p_automation_id text)
476
+ RETURNS jsonb
477
+ LANGUAGE sql
478
+ SECURITY DEFINER
479
+ SET search_path = public
480
+ AS $$
481
+ SELECT to_jsonb(w) - 'user_id'
482
+ FROM amalgm_automation_workflows w
483
+ WHERE w.user_id = p_user_id AND w.automation_id = p_automation_id;
484
+ $$;
485
+
486
+ CREATE FUNCTION update_amalgm_workflow(
487
+ p_user_id uuid,
488
+ p_automation_id text,
489
+ p_patch jsonb
490
+ )
491
+ RETURNS jsonb
492
+ LANGUAGE plpgsql
493
+ SECURITY DEFINER
494
+ SET search_path = public
495
+ AS $$
496
+ DECLARE
497
+ v_row amalgm_automation_workflows;
498
+ BEGIN
499
+ UPDATE amalgm_automation_workflows SET
500
+ name = CASE WHEN p_patch ? 'name' THEN NULLIF(p_patch->>'name', '') ELSE name END,
501
+ script = CASE WHEN p_patch ? 'script' THEN p_patch->>'script' ELSE script END,
502
+ compiled = CASE WHEN p_patch ? 'compiled' THEN p_patch->'compiled' ELSE compiled END,
503
+ allowlist = CASE WHEN p_patch ? 'allowlist' THEN p_patch->'allowlist' ELSE allowlist END,
504
+ limits = CASE WHEN p_patch ? 'limits' THEN p_patch->'limits' ELSE limits END,
505
+ updated_at = now()
506
+ WHERE user_id = p_user_id AND automation_id = p_automation_id
507
+ RETURNING * INTO v_row;
508
+ IF NOT FOUND THEN RETURN NULL; END IF;
509
+ RETURN to_jsonb(v_row) - 'user_id';
510
+ END;
511
+ $$;
512
+
513
+ CREATE FUNCTION delete_amalgm_workflow(p_user_id uuid, p_automation_id text)
514
+ RETURNS boolean
515
+ LANGUAGE plpgsql
516
+ SECURITY DEFINER
517
+ SET search_path = public
518
+ AS $$
519
+ BEGIN
520
+ DELETE FROM amalgm_automation_workflows
521
+ WHERE user_id = p_user_id AND automation_id = p_automation_id;
522
+ RETURN FOUND;
523
+ END;
524
+ $$;
525
+
526
+ CREATE FUNCTION list_amalgm_runs(
527
+ p_user_id uuid,
528
+ p_automation_id text,
529
+ p_status text,
530
+ p_limit integer,
531
+ p_offset integer
532
+ )
533
+ RETURNS jsonb
534
+ LANGUAGE sql
535
+ SECURITY DEFINER
536
+ SET search_path = public
537
+ AS $$
538
+ WITH filtered AS (
539
+ SELECT id, automation_id, trigger_id, workflow_id, target_id, status, input,
540
+ created_at, sent_at, started_at, finished_at, output, error
541
+ FROM amalgm_automation_runs
542
+ WHERE user_id = p_user_id AND automation_id = p_automation_id
543
+ AND (p_status IS NULL OR status = p_status)
544
+ ), page_rows AS (
545
+ SELECT * FROM filtered ORDER BY created_at DESC, id DESC LIMIT p_limit OFFSET p_offset
546
+ )
547
+ SELECT jsonb_build_object(
548
+ 'items', COALESCE((SELECT jsonb_agg(to_jsonb(page_rows)) FROM page_rows), '[]'::jsonb),
549
+ 'total', (SELECT count(*) FROM filtered)
550
+ );
551
+ $$;
552
+
553
+ CREATE FUNCTION get_amalgm_run(p_user_id uuid, p_automation_id text, p_run_id text)
554
+ RETURNS jsonb
555
+ LANGUAGE sql
556
+ SECURITY DEFINER
557
+ SET search_path = public
558
+ AS $$
559
+ SELECT to_jsonb(r) - 'user_id' - 'automation_payload'
560
+ FROM amalgm_automation_runs r
561
+ WHERE r.user_id = p_user_id AND r.automation_id = p_automation_id AND r.id::text = p_run_id;
562
+ $$;
563
+
564
+ REVOKE ALL ON FUNCTION amalgm_trigger_json(amalgm_automation_triggers) FROM PUBLIC;
565
+ REVOKE ALL ON FUNCTION create_amalgm_automation(uuid, jsonb) FROM PUBLIC;
566
+ REVOKE ALL ON FUNCTION list_amalgm_automations(uuid, text, boolean, integer, integer) FROM PUBLIC;
567
+ REVOKE ALL ON FUNCTION get_amalgm_automation(uuid, text) FROM PUBLIC;
568
+ REVOKE ALL ON FUNCTION update_amalgm_automation(uuid, text, jsonb) FROM PUBLIC;
569
+ REVOKE ALL ON FUNCTION delete_amalgm_automation(uuid, text) FROM PUBLIC;
570
+ REVOKE ALL ON FUNCTION create_amalgm_schedule_trigger(uuid, text, jsonb) FROM PUBLIC;
571
+ REVOKE ALL ON FUNCTION list_amalgm_schedule_triggers(uuid, text, integer, integer) FROM PUBLIC;
572
+ REVOKE ALL ON FUNCTION get_amalgm_schedule_trigger(uuid, text, text) FROM PUBLIC;
573
+ REVOKE ALL ON FUNCTION update_amalgm_schedule_trigger(uuid, text, text, jsonb) FROM PUBLIC;
574
+ REVOKE ALL ON FUNCTION delete_amalgm_schedule_trigger(uuid, text, text) FROM PUBLIC;
575
+ REVOKE ALL ON FUNCTION create_amalgm_webhook_trigger(uuid, text, jsonb) FROM PUBLIC;
576
+ REVOKE ALL ON FUNCTION list_amalgm_webhook_triggers(uuid, text, integer, integer) FROM PUBLIC;
577
+ REVOKE ALL ON FUNCTION get_amalgm_webhook_trigger(uuid, text, text) FROM PUBLIC;
578
+ REVOKE ALL ON FUNCTION update_amalgm_webhook_trigger(uuid, text, text, jsonb) FROM PUBLIC;
579
+ REVOKE ALL ON FUNCTION delete_amalgm_webhook_trigger(uuid, text, text) FROM PUBLIC;
580
+ REVOKE ALL ON FUNCTION list_amalgm_triggers(uuid, text) FROM PUBLIC;
581
+ REVOKE ALL ON FUNCTION create_amalgm_workflow(uuid, text, jsonb) FROM PUBLIC;
582
+ REVOKE ALL ON FUNCTION get_amalgm_workflow(uuid, text) FROM PUBLIC;
583
+ REVOKE ALL ON FUNCTION update_amalgm_workflow(uuid, text, jsonb) FROM PUBLIC;
584
+ REVOKE ALL ON FUNCTION delete_amalgm_workflow(uuid, text) FROM PUBLIC;
585
+ REVOKE ALL ON FUNCTION list_amalgm_runs(uuid, text, text, integer, integer) FROM PUBLIC;
586
+ REVOKE ALL ON FUNCTION get_amalgm_run(uuid, text, text) FROM PUBLIC;
587
+
588
+ GRANT EXECUTE ON FUNCTION create_amalgm_automation(uuid, jsonb) TO service_role;
589
+ GRANT EXECUTE ON FUNCTION list_amalgm_automations(uuid, text, boolean, integer, integer) TO service_role;
590
+ GRANT EXECUTE ON FUNCTION get_amalgm_automation(uuid, text) TO service_role;
591
+ GRANT EXECUTE ON FUNCTION update_amalgm_automation(uuid, text, jsonb) TO service_role;
592
+ GRANT EXECUTE ON FUNCTION delete_amalgm_automation(uuid, text) TO service_role;
593
+ GRANT EXECUTE ON FUNCTION create_amalgm_schedule_trigger(uuid, text, jsonb) TO service_role;
594
+ GRANT EXECUTE ON FUNCTION list_amalgm_schedule_triggers(uuid, text, integer, integer) TO service_role;
595
+ GRANT EXECUTE ON FUNCTION get_amalgm_schedule_trigger(uuid, text, text) TO service_role;
596
+ GRANT EXECUTE ON FUNCTION update_amalgm_schedule_trigger(uuid, text, text, jsonb) TO service_role;
597
+ GRANT EXECUTE ON FUNCTION delete_amalgm_schedule_trigger(uuid, text, text) TO service_role;
598
+ GRANT EXECUTE ON FUNCTION create_amalgm_webhook_trigger(uuid, text, jsonb) TO service_role;
599
+ GRANT EXECUTE ON FUNCTION list_amalgm_webhook_triggers(uuid, text, integer, integer) TO service_role;
600
+ GRANT EXECUTE ON FUNCTION get_amalgm_webhook_trigger(uuid, text, text) TO service_role;
601
+ GRANT EXECUTE ON FUNCTION update_amalgm_webhook_trigger(uuid, text, text, jsonb) TO service_role;
602
+ GRANT EXECUTE ON FUNCTION delete_amalgm_webhook_trigger(uuid, text, text) TO service_role;
603
+ GRANT EXECUTE ON FUNCTION list_amalgm_triggers(uuid, text) TO service_role;
604
+ GRANT EXECUTE ON FUNCTION create_amalgm_workflow(uuid, text, jsonb) TO service_role;
605
+ GRANT EXECUTE ON FUNCTION get_amalgm_workflow(uuid, text) TO service_role;
606
+ GRANT EXECUTE ON FUNCTION update_amalgm_workflow(uuid, text, jsonb) TO service_role;
607
+ GRANT EXECUTE ON FUNCTION delete_amalgm_workflow(uuid, text) TO service_role;
608
+ GRANT EXECUTE ON FUNCTION list_amalgm_runs(uuid, text, text, integer, integer) TO service_role;
609
+ GRANT EXECUTE ON FUNCTION get_amalgm_run(uuid, text, text) TO service_role;