@amalgm/automations 0.1.0 → 0.1.1
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/AXIOMS.md +8 -3
- package/PURPOSE.md +9 -6
- package/README.md +10 -12
- package/dist/src/automations.d.ts +1 -4
- package/dist/src/automations.js +1 -59
- package/dist/src/crud.d.ts +10 -3
- package/dist/src/crud.js +11 -3
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.js +2 -2
- package/dist/src/supabase-crud.d.ts +4 -4
- package/dist/src/supabase-store.d.ts +1 -4
- package/dist/src/supabase-store.js +0 -16
- package/dist/src/types.d.ts +2 -54
- package/package.json +2 -2
- package/supabase/migrations/20260802000000_create_automations.sql +238 -8
package/AXIOMS.md
CHANGED
|
@@ -27,6 +27,11 @@
|
|
|
27
27
|
automation's owner.
|
|
28
28
|
11. Storage records, Supabase RPCs, HTTP details, and MCP protocol details are
|
|
29
29
|
implementation concerns, not SDK concepts.
|
|
30
|
-
12.
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
12. The control-plane SDK is the only configuration write path. The delivery
|
|
31
|
+
rail reads that configuration and writes only schedule clocks and run
|
|
32
|
+
state; it never owns a second automation-definition API.
|
|
33
|
+
13. Admitting a run atomically verifies the current enabled configuration,
|
|
34
|
+
stores a secret-free snapshot, and, for a schedule, advances exactly the
|
|
35
|
+
firing instant that was claimed.
|
|
36
|
+
14. Legacy local automation storage is not a compatibility authority. Engine
|
|
37
|
+
cutover migrates callers to this SDK and then deletes the old store.
|
package/PURPOSE.md
CHANGED
|
@@ -17,10 +17,13 @@ The product has two composable halves over that one state:
|
|
|
17
17
|
skill are adapters over that contract; they do not access Supabase directly
|
|
18
18
|
or implement lifecycle rules.
|
|
19
19
|
- **The delivery rail** — trigger admission, scheduling, and run state: the
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
authentication, atomic admission, schedule-claim, reconnect, and
|
|
21
|
+
idempotency laws that decide when a run happens. It consumes the control
|
|
22
|
+
plane's persisted configuration; it does not expose another definition
|
|
23
|
+
write path. When a machine is offline, new runs remain pending; when it
|
|
24
|
+
reconnects, every pending run is sent through Amalgm's existing transport
|
|
25
|
+
and started by the existing host runtime. Execution itself stays on the
|
|
26
|
+
machine.
|
|
24
27
|
|
|
25
28
|
Amalgm supplies a resolved authenticated principal from its user session or
|
|
26
29
|
HMAC-refresh flow; future API keys resolve to the same principal capability.
|
|
@@ -31,5 +34,5 @@ the other's storage or reimplements the other's decisions.
|
|
|
31
34
|
|
|
32
35
|
This extraction is complete only when every Automations surface in Engine calls
|
|
33
36
|
this product service, product state has one writer, migrated behavior has parity
|
|
34
|
-
tests, and the
|
|
35
|
-
deleted.
|
|
37
|
+
tests, and the local `amalgm-mcp` automation implementation and storage are
|
|
38
|
+
deleted rather than retained as a compatibility layer.
|
package/README.md
CHANGED
|
@@ -81,8 +81,6 @@ const automations = new Automations(new SupabaseStore(getSupabase()), {
|
|
|
81
81
|
send: (run) => existingTunnel.send(run.targetId, run),
|
|
82
82
|
});
|
|
83
83
|
|
|
84
|
-
await automations.save(authenticatedUser.id, definition);
|
|
85
|
-
|
|
86
84
|
await automations.receiveEvent({
|
|
87
85
|
target: await core.resolveEventTarget(eventRef),
|
|
88
86
|
headers,
|
|
@@ -97,24 +95,24 @@ await automations.updateRun({ userId, targetId }, runId, {
|
|
|
97
95
|
output,
|
|
98
96
|
});
|
|
99
97
|
|
|
100
|
-
const history = await
|
|
98
|
+
const history = await sdk.runs.list(automationId);
|
|
101
99
|
```
|
|
102
100
|
|
|
103
|
-
|
|
104
|
-
`
|
|
105
|
-
|
|
106
|
-
only translate and call
|
|
101
|
+
Configuration is written only through the auth-bound SDK shown above. The
|
|
102
|
+
delivery rail exposes `receiveEvent`, `fireDueCrons`, `targetOnline`, and
|
|
103
|
+
`updateRun`; it has no parallel save, delete, or history API. The surrounding
|
|
104
|
+
Engine route, scheduler callback, and tunnel callback only translate and call
|
|
105
|
+
these SDK operations.
|
|
107
106
|
|
|
108
107
|
Every firing stores an immutable run snapshot in Supabase. Delivery and machine
|
|
109
108
|
execution update that record instead of deleting it. When a machine is offline,
|
|
110
109
|
new runs remain pending; on reconnect every pending run drains through the
|
|
111
110
|
existing transport.
|
|
112
111
|
|
|
113
|
-
The
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
move behind this same service boundary before legacy code is removed.
|
|
112
|
+
The implementation covers Supabase definitions, cron and event admission,
|
|
113
|
+
secret verification, permanent run history, and reconnect drain. Engine
|
|
114
|
+
cutover must call these published contracts and delete its local automation
|
|
115
|
+
store; this package intentionally provides no compatibility storage path.
|
|
118
116
|
|
|
119
117
|
The purpose is in [PURPOSE.md](./PURPOSE.md), and the non-negotiable ownership
|
|
120
118
|
rules are in [AXIOMS.md](./AXIOMS.md).
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { AutomationLog, AutomationRun, AutomationStore, AutomationTarget, AutomationTransport, Json, RunUpdate } from './types.js';
|
|
2
2
|
export declare class EventRejectedError extends Error {
|
|
3
3
|
constructor();
|
|
4
4
|
}
|
|
@@ -8,9 +8,6 @@ export declare class Automations {
|
|
|
8
8
|
readonly transport: AutomationTransport;
|
|
9
9
|
readonly log: AutomationLog;
|
|
10
10
|
constructor(store: AutomationStore, transport: AutomationTransport, log?: AutomationLog);
|
|
11
|
-
save(userId: string, definition: AutomationDefinition, now?: Date): Promise<void>;
|
|
12
|
-
delete(userId: string, automationId: string): Promise<boolean>;
|
|
13
|
-
listRuns(userId: string, automationId?: string): Promise<AutomationRun[]>;
|
|
14
11
|
receiveEvent(input: {
|
|
15
12
|
target: AutomationTarget;
|
|
16
13
|
headers: Record<string, string>;
|
package/dist/src/automations.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { nextCronAt
|
|
1
|
+
import { nextCronAt } from './schedule.js';
|
|
2
2
|
import { eventReferences, matchesEvent, verifyEventSecret } from './webhook.js';
|
|
3
|
-
const ID = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,199}$/;
|
|
4
3
|
const noop = () => { };
|
|
5
4
|
export class EventRejectedError extends Error {
|
|
6
5
|
constructor() {
|
|
@@ -18,16 +17,6 @@ export class Automations {
|
|
|
18
17
|
this.transport = transport;
|
|
19
18
|
this.log = log;
|
|
20
19
|
}
|
|
21
|
-
async save(userId, definition, now = new Date()) {
|
|
22
|
-
const automation = prepareAutomation(userId, definition, now);
|
|
23
|
-
await this.store.saveAutomation(automation);
|
|
24
|
-
}
|
|
25
|
-
delete(userId, automationId) {
|
|
26
|
-
return this.store.deleteAutomation(userId, automationId);
|
|
27
|
-
}
|
|
28
|
-
listRuns(userId, automationId) {
|
|
29
|
-
return this.store.listRuns(userId, automationId);
|
|
30
|
-
}
|
|
31
20
|
async receiveEvent(input) {
|
|
32
21
|
assertJson(input.payload, 'Event payload');
|
|
33
22
|
const candidates = await this.store.eventTriggers(input.target);
|
|
@@ -135,53 +124,6 @@ export class Automations {
|
|
|
135
124
|
return sent;
|
|
136
125
|
}
|
|
137
126
|
}
|
|
138
|
-
function prepareAutomation(userId, definition, now) {
|
|
139
|
-
if (!ID.test(definition.id))
|
|
140
|
-
throw new Error('Automation id is invalid');
|
|
141
|
-
if (!userId)
|
|
142
|
-
throw new Error('Automation userId is required');
|
|
143
|
-
if (!definition.targetId)
|
|
144
|
-
throw new Error('Automation targetId is required');
|
|
145
|
-
if (!ID.test(definition.trigger.id))
|
|
146
|
-
throw new Error('Trigger id is invalid');
|
|
147
|
-
if (!ID.test(definition.workflow.id))
|
|
148
|
-
throw new Error('Workflow id is invalid');
|
|
149
|
-
if (!definition.workflow.script.trim())
|
|
150
|
-
throw new Error('Workflow script is required');
|
|
151
|
-
if (definition.workflow.compiled !== undefined) {
|
|
152
|
-
assertJson(definition.workflow.compiled, 'Compiled workflow');
|
|
153
|
-
}
|
|
154
|
-
if (definition.workflow.allowlist !== undefined) {
|
|
155
|
-
assertJson(definition.workflow.allowlist, 'Workflow allowlist');
|
|
156
|
-
}
|
|
157
|
-
if (definition.workflow.limits !== undefined) {
|
|
158
|
-
assertJson(definition.workflow.limits, 'Workflow limits');
|
|
159
|
-
}
|
|
160
|
-
const trigger = definition.trigger.kind === 'event'
|
|
161
|
-
? prepareEventTrigger(definition.trigger)
|
|
162
|
-
: prepareCronTrigger(definition.trigger, now);
|
|
163
|
-
return { ...definition, userId, enabled: definition.enabled !== false, trigger };
|
|
164
|
-
}
|
|
165
|
-
function prepareEventTrigger(trigger) {
|
|
166
|
-
if (trigger.secret.length < 16)
|
|
167
|
-
throw new Error(`Event trigger ${trigger.id} secret is too short`);
|
|
168
|
-
return {
|
|
169
|
-
...trigger,
|
|
170
|
-
enabled: trigger.enabled !== false,
|
|
171
|
-
source: trigger.source || '*',
|
|
172
|
-
event: trigger.event || '*',
|
|
173
|
-
};
|
|
174
|
-
}
|
|
175
|
-
function prepareCronTrigger(trigger, now) {
|
|
176
|
-
const timezone = trigger.timezone || 'UTC';
|
|
177
|
-
validateCron(trigger.cron, timezone);
|
|
178
|
-
return {
|
|
179
|
-
...trigger,
|
|
180
|
-
enabled: trigger.enabled !== false,
|
|
181
|
-
timezone,
|
|
182
|
-
nextRunAt: nextCronAt(trigger.cron, timezone, now),
|
|
183
|
-
};
|
|
184
|
-
}
|
|
185
127
|
function assertJson(value, label) {
|
|
186
128
|
if (value === null || typeof value === 'string' || typeof value === 'boolean')
|
|
187
129
|
return;
|
package/dist/src/crud.d.ts
CHANGED
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
import type { Automation, AutomationCrud, AutomationCrudService as AutomationCrudServiceContract, AutomationPrincipal, AutomationRun, CreateAutomation, CreateScheduleTrigger, CreateWebhookTrigger, CreateWorkflow, ListAutomations, ListRuns, Page, PageQuery, ScheduleTrigger, Trigger, UpdateAutomation, UpdateScheduleTrigger, UpdateWebhookTrigger, UpdateWorkflow, WebhookTrigger, Workflow } from './contract.js';
|
|
2
|
+
export type StoredScheduleCreate = Required<CreateScheduleTrigger> & {
|
|
3
|
+
nextRunAt: string;
|
|
4
|
+
};
|
|
5
|
+
export type StoredScheduleUpdate = UpdateScheduleTrigger & {
|
|
6
|
+
nextRunAt?: string;
|
|
7
|
+
};
|
|
2
8
|
export interface AutomationCrudRepository {
|
|
3
9
|
createAutomation(userId: string, input: Required<CreateAutomation>): Promise<Automation>;
|
|
4
10
|
listAutomations(userId: string, query: NormalizedListAutomations): Promise<Page<Automation>>;
|
|
5
11
|
getAutomation(userId: string, automationId: string): Promise<Automation | null>;
|
|
6
12
|
updateAutomation(userId: string, automationId: string, patch: UpdateAutomation): Promise<Automation | null>;
|
|
7
13
|
deleteAutomation(userId: string, automationId: string): Promise<boolean>;
|
|
8
|
-
createScheduleTrigger(userId: string, automationId: string, input:
|
|
14
|
+
createScheduleTrigger(userId: string, automationId: string, input: StoredScheduleCreate): Promise<ScheduleTrigger>;
|
|
9
15
|
listScheduleTriggers(userId: string, automationId: string, query: Required<PageQuery>): Promise<Page<ScheduleTrigger>>;
|
|
10
16
|
getScheduleTrigger(userId: string, automationId: string, triggerId: string): Promise<ScheduleTrigger | null>;
|
|
11
|
-
updateScheduleTrigger(userId: string, automationId: string, triggerId: string, patch:
|
|
17
|
+
updateScheduleTrigger(userId: string, automationId: string, triggerId: string, patch: StoredScheduleUpdate): Promise<ScheduleTrigger | null>;
|
|
12
18
|
deleteScheduleTrigger(userId: string, automationId: string, triggerId: string): Promise<boolean>;
|
|
13
19
|
createWebhookTrigger(userId: string, automationId: string, input: Required<CreateWebhookTrigger>): Promise<WebhookTrigger>;
|
|
14
20
|
listWebhookTriggers(userId: string, automationId: string, query: Required<PageQuery>): Promise<Page<WebhookTrigger>>;
|
|
@@ -27,6 +33,7 @@ export type NormalizedListAutomations = Required<PageQuery> & Omit<ListAutomatio
|
|
|
27
33
|
export type NormalizedListRuns = Required<PageQuery> & Omit<ListRuns, keyof PageQuery>;
|
|
28
34
|
export declare class AutomationCrudService implements AutomationCrudServiceContract {
|
|
29
35
|
private readonly repository;
|
|
30
|
-
|
|
36
|
+
private readonly clock;
|
|
37
|
+
constructor(repository: AutomationCrudRepository, clock?: () => Date);
|
|
31
38
|
for(principal: AutomationPrincipal): AutomationCrud;
|
|
32
39
|
}
|
package/dist/src/crud.js
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { ConflictError, ForbiddenError, NotFoundError } from './errors.js';
|
|
2
2
|
import { parseCreateAutomation, parseCreateScheduleTrigger, parseCreateWebhookTrigger, parseCreateWorkflow, parseListAutomations, parseListRuns, parsePageQuery, parseUpdateAutomation, parseUpdateScheduleTrigger, parseUpdateWebhookTrigger, parseUpdateWorkflow, } from './schema.js';
|
|
3
3
|
import { id, newId, page, requiredText, schedule, } from './validation.js';
|
|
4
|
+
import { nextCronAt } from './schedule.js';
|
|
4
5
|
export class AutomationCrudService {
|
|
5
6
|
repository;
|
|
6
|
-
|
|
7
|
+
clock;
|
|
8
|
+
constructor(repository, clock = () => new Date()) {
|
|
7
9
|
this.repository = repository;
|
|
10
|
+
this.clock = clock;
|
|
8
11
|
}
|
|
9
12
|
for(principal) {
|
|
10
13
|
assertPrincipal(principal);
|
|
@@ -88,6 +91,7 @@ export class AutomationCrudService {
|
|
|
88
91
|
cron: parsed.cron,
|
|
89
92
|
timezone,
|
|
90
93
|
enabled: parsed.enabled !== false,
|
|
94
|
+
nextRunAt: nextCronAt(parsed.cron, timezone, this.clock()),
|
|
91
95
|
});
|
|
92
96
|
},
|
|
93
97
|
list: async (automationId, query = {}) => {
|
|
@@ -104,13 +108,17 @@ export class AutomationCrudService {
|
|
|
104
108
|
write();
|
|
105
109
|
await exists(automationId);
|
|
106
110
|
const parsed = parseUpdateScheduleTrigger(patch);
|
|
111
|
+
let storedPatch = parsed;
|
|
107
112
|
if (parsed.cron !== undefined || parsed.timezone !== undefined) {
|
|
108
113
|
const existing = await this.repository.getScheduleTrigger(principal.userId, automationId, triggerId);
|
|
109
114
|
if (!existing)
|
|
110
115
|
throw new NotFoundError('Schedule trigger');
|
|
111
|
-
|
|
116
|
+
const cron = parsed.cron ?? existing.cron;
|
|
117
|
+
const timezone = parsed.timezone ?? existing.timezone;
|
|
118
|
+
schedule(cron, timezone);
|
|
119
|
+
storedPatch = { ...parsed, nextRunAt: nextCronAt(cron, timezone, this.clock()) };
|
|
112
120
|
}
|
|
113
|
-
const updated = await this.repository.updateScheduleTrigger(principal.userId, automationId, id(triggerId, 'Schedule trigger id'),
|
|
121
|
+
const updated = await this.repository.updateScheduleTrigger(principal.userId, automationId, id(triggerId, 'Schedule trigger id'), storedPatch);
|
|
114
122
|
if (!updated)
|
|
115
123
|
throw new NotFoundError('Schedule trigger');
|
|
116
124
|
return updated;
|
package/dist/src/index.d.ts
CHANGED
|
@@ -11,4 +11,4 @@ export { nextCronAt, validateCron } from './schedule.js';
|
|
|
11
11
|
export { eventReference, eventReferences, matchesEvent, normalizeHeaders, verifyEventSecret, } from './webhook.js';
|
|
12
12
|
export { SupabaseStore } from './supabase-store.js';
|
|
13
13
|
export type { SupabaseRpcClient as SupabaseStoreRpcClient } from './supabase-store.js';
|
|
14
|
-
export type {
|
|
14
|
+
export type { AutomationLog, AutomationRun as AutomationRunRecord, AutomationStore, AutomationTarget, AutomationTransport, DueCronTrigger, RunInput, RunUpdate, StoredEventTrigger, } from './types.js';
|
package/dist/src/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// The automation product has two composable halves:
|
|
2
2
|
// - the configuration service (contract/crud/http/mcp/cli) — Supabase owns
|
|
3
3
|
// everything except execution;
|
|
4
|
-
// - the delivery rail (automations/webhook/schedule/store) —
|
|
5
|
-
//
|
|
4
|
+
// - the delivery rail (automations/webhook/schedule/store) — authenticated
|
|
5
|
+
// admission, schedule claims, reconnect drain, and run-state transitions.
|
|
6
6
|
// Colliding names keep the contract's spelling; the rail's store-level
|
|
7
7
|
// records are exported under Store-scoped aliases.
|
|
8
8
|
export { createAutomationClient } from './client.js';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { Automation, AutomationRun, CreateAutomation,
|
|
2
|
-
import type { AutomationCrudRepository, NormalizedListAutomations, NormalizedListRuns } from './crud.js';
|
|
1
|
+
import type { Automation, AutomationRun, CreateAutomation, CreateWebhookTrigger, CreateWorkflow, Page, ScheduleTrigger, Trigger, UpdateAutomation, UpdateWebhookTrigger, UpdateWorkflow, WebhookTrigger, Workflow } from './contract.js';
|
|
2
|
+
import type { AutomationCrudRepository, NormalizedListAutomations, NormalizedListRuns, StoredScheduleCreate, StoredScheduleUpdate } from './crud.js';
|
|
3
3
|
export interface SupabaseRpcClient {
|
|
4
4
|
rpc(functionName: string, arguments_: Record<string, unknown>): PromiseLike<{
|
|
5
5
|
data: unknown;
|
|
@@ -18,13 +18,13 @@ export declare class SupabaseAutomationCrudRepository implements AutomationCrudR
|
|
|
18
18
|
getAutomation(userId: string, automationId: string): Promise<Automation | null>;
|
|
19
19
|
updateAutomation(userId: string, automationId: string, patch: UpdateAutomation): Promise<Automation | null>;
|
|
20
20
|
deleteAutomation(userId: string, automationId: string): Promise<boolean>;
|
|
21
|
-
createScheduleTrigger(userId: string, automationId: string, input:
|
|
21
|
+
createScheduleTrigger(userId: string, automationId: string, input: StoredScheduleCreate): Promise<ScheduleTrigger>;
|
|
22
22
|
listScheduleTriggers(userId: string, automationId: string, query: Required<{
|
|
23
23
|
limit?: number;
|
|
24
24
|
offset?: number;
|
|
25
25
|
}>): Promise<Page<ScheduleTrigger>>;
|
|
26
26
|
getScheduleTrigger(userId: string, automationId: string, triggerId: string): Promise<ScheduleTrigger | null>;
|
|
27
|
-
updateScheduleTrigger(userId: string, automationId: string, triggerId: string, patch:
|
|
27
|
+
updateScheduleTrigger(userId: string, automationId: string, triggerId: string, patch: StoredScheduleUpdate): Promise<ScheduleTrigger | null>;
|
|
28
28
|
deleteScheduleTrigger(userId: string, automationId: string, triggerId: string): Promise<boolean>;
|
|
29
29
|
createWebhookTrigger(userId: string, automationId: string, input: Required<CreateWebhookTrigger>): Promise<WebhookTrigger>;
|
|
30
30
|
listWebhookTriggers(userId: string, automationId: string, query: Required<{
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AutomationStore, AutomationTarget, AutomationRun, DueCronTrigger,
|
|
1
|
+
import type { AutomationStore, AutomationTarget, AutomationRun, DueCronTrigger, RunInput, RunUpdate, StoredEventTrigger } from './types.js';
|
|
2
2
|
export interface SupabaseRpcClient {
|
|
3
3
|
rpc(functionName: string, arguments_: Record<string, unknown>): PromiseLike<{
|
|
4
4
|
data: unknown;
|
|
@@ -11,8 +11,6 @@ export declare class SupabaseStore implements AutomationStore {
|
|
|
11
11
|
#private;
|
|
12
12
|
readonly client: SupabaseRpcClient;
|
|
13
13
|
constructor(client: SupabaseRpcClient);
|
|
14
|
-
saveAutomation(automation: PreparedAutomation): Promise<void>;
|
|
15
|
-
deleteAutomation(userId: string, automationId: string): Promise<boolean>;
|
|
16
14
|
eventTriggers(target: AutomationTarget): Promise<StoredEventTrigger[]>;
|
|
17
15
|
dueCronTriggers(now: Date): Promise<DueCronTrigger[]>;
|
|
18
16
|
enqueueEvent(trigger: StoredEventTrigger, input: Extract<RunInput, {
|
|
@@ -20,7 +18,6 @@ export declare class SupabaseStore implements AutomationStore {
|
|
|
20
18
|
}>, now: Date): Promise<AutomationRun | null>;
|
|
21
19
|
enqueueCron(trigger: DueCronTrigger, nextRunAt: string, now: Date): Promise<AutomationRun | null>;
|
|
22
20
|
pendingRuns(target: AutomationTarget): Promise<AutomationRun[]>;
|
|
23
|
-
listRuns(userId: string, automationId?: string): Promise<AutomationRun[]>;
|
|
24
21
|
markSent(runId: string, target: AutomationTarget, now: Date): Promise<boolean>;
|
|
25
22
|
updateRun(target: AutomationTarget, runId: string, update: RunUpdate): Promise<AutomationRun | null>;
|
|
26
23
|
}
|
|
@@ -22,15 +22,6 @@ export class SupabaseStore {
|
|
|
22
22
|
constructor(client) {
|
|
23
23
|
this.client = client;
|
|
24
24
|
}
|
|
25
|
-
async saveAutomation(automation) {
|
|
26
|
-
await this.#call('save_amalgm_automation', { p_automation: automation });
|
|
27
|
-
}
|
|
28
|
-
deleteAutomation(userId, automationId) {
|
|
29
|
-
return this.#call('delete_amalgm_automation', {
|
|
30
|
-
p_user_id: userId,
|
|
31
|
-
p_automation_id: automationId,
|
|
32
|
-
});
|
|
33
|
-
}
|
|
34
25
|
async eventTriggers(target) {
|
|
35
26
|
const rows = await this.#call('list_amalgm_event_triggers', {
|
|
36
27
|
p_user_id: target.userId,
|
|
@@ -76,13 +67,6 @@ export class SupabaseStore {
|
|
|
76
67
|
});
|
|
77
68
|
return rows.map(automationRun);
|
|
78
69
|
}
|
|
79
|
-
async listRuns(userId, automationId) {
|
|
80
|
-
const rows = await this.#call('list_amalgm_runs', {
|
|
81
|
-
p_user_id: userId,
|
|
82
|
-
p_automation_id: automationId || null,
|
|
83
|
-
});
|
|
84
|
-
return rows.map(automationRun);
|
|
85
|
-
}
|
|
86
70
|
markSent(runId, target, now) {
|
|
87
71
|
return this.#call('mark_amalgm_run_sent', {
|
|
88
72
|
p_run_id: runId,
|
package/dist/src/types.d.ts
CHANGED
|
@@ -1,58 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
};
|
|
4
|
-
interface TriggerBase {
|
|
5
|
-
id: string;
|
|
6
|
-
enabled?: boolean;
|
|
7
|
-
}
|
|
8
|
-
export interface CronTrigger extends TriggerBase {
|
|
9
|
-
kind: 'cron';
|
|
10
|
-
cron: string;
|
|
11
|
-
timezone?: string;
|
|
12
|
-
}
|
|
13
|
-
export interface EventTrigger extends TriggerBase {
|
|
14
|
-
kind: 'event';
|
|
15
|
-
source?: string;
|
|
16
|
-
event?: string;
|
|
17
|
-
secret: string;
|
|
18
|
-
}
|
|
19
|
-
export type Trigger = CronTrigger | EventTrigger;
|
|
20
|
-
export interface WorkflowDefinition {
|
|
21
|
-
id: string;
|
|
22
|
-
name?: string;
|
|
23
|
-
script: string;
|
|
24
|
-
compiled?: Json;
|
|
25
|
-
allowlist?: Json;
|
|
26
|
-
limits?: Json;
|
|
27
|
-
}
|
|
28
|
-
export interface AutomationDefinition {
|
|
29
|
-
id: string;
|
|
30
|
-
targetId: string;
|
|
31
|
-
name?: string;
|
|
32
|
-
description?: string;
|
|
33
|
-
enabled?: boolean;
|
|
34
|
-
trigger: Trigger;
|
|
35
|
-
workflow: WorkflowDefinition;
|
|
36
|
-
}
|
|
1
|
+
import type { Json } from './contract.js';
|
|
2
|
+
export type { Json } from './contract.js';
|
|
37
3
|
/** The only authenticated identity Automations accepts from Core. */
|
|
38
4
|
export interface AutomationTarget {
|
|
39
5
|
userId: string;
|
|
40
6
|
targetId: string;
|
|
41
7
|
}
|
|
42
|
-
export type PreparedTrigger = (CronTrigger & {
|
|
43
|
-
enabled: boolean;
|
|
44
|
-
timezone: string;
|
|
45
|
-
nextRunAt: string;
|
|
46
|
-
}) | (EventTrigger & {
|
|
47
|
-
enabled: boolean;
|
|
48
|
-
source: string;
|
|
49
|
-
event: string;
|
|
50
|
-
});
|
|
51
|
-
export interface PreparedAutomation extends Omit<AutomationDefinition, 'enabled' | 'trigger'> {
|
|
52
|
-
userId: string;
|
|
53
|
-
enabled: boolean;
|
|
54
|
-
trigger: PreparedTrigger;
|
|
55
|
-
}
|
|
56
8
|
interface StoredTriggerBase {
|
|
57
9
|
userId: string;
|
|
58
10
|
automationId: string;
|
|
@@ -106,8 +58,6 @@ export interface RunUpdate {
|
|
|
106
58
|
error?: string;
|
|
107
59
|
}
|
|
108
60
|
export interface AutomationStore {
|
|
109
|
-
saveAutomation(automation: PreparedAutomation): Promise<void>;
|
|
110
|
-
deleteAutomation(userId: string, automationId: string): Promise<boolean>;
|
|
111
61
|
eventTriggers(target: AutomationTarget): Promise<StoredEventTrigger[]>;
|
|
112
62
|
dueCronTriggers(now: Date): Promise<DueCronTrigger[]>;
|
|
113
63
|
enqueueEvent(trigger: StoredEventTrigger, input: Extract<RunInput, {
|
|
@@ -115,7 +65,6 @@ export interface AutomationStore {
|
|
|
115
65
|
}>, now: Date): Promise<AutomationRun | null>;
|
|
116
66
|
enqueueCron(trigger: DueCronTrigger, nextRunAt: string, now: Date): Promise<AutomationRun | null>;
|
|
117
67
|
pendingRuns(target: AutomationTarget): Promise<AutomationRun[]>;
|
|
118
|
-
listRuns(userId: string, automationId?: string): Promise<AutomationRun[]>;
|
|
119
68
|
markSent(runId: string, target: AutomationTarget, now: Date): Promise<boolean>;
|
|
120
69
|
updateRun(target: AutomationTarget, runId: string, update: RunUpdate): Promise<AutomationRun | null>;
|
|
121
70
|
}
|
|
@@ -125,4 +74,3 @@ export interface AutomationTransport {
|
|
|
125
74
|
send(run: AutomationRun): Promise<boolean>;
|
|
126
75
|
}
|
|
127
76
|
export type AutomationLog = (event: 'event.rejected' | 'run.pending' | 'drain.started' | 'run.sent' | 'run.failed', details: Readonly<Record<string, string>>) => void;
|
|
128
|
-
export {};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@amalgm/automations",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "Amalgm's automation
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Amalgm's cloud automation SDK: Supabase-backed configuration, trigger admission, and run delivery.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
--
|
|
2
|
-
--
|
|
3
|
-
-- Keep their SQL bodies equivalent.
|
|
1
|
+
-- Automations owns this schema contract. Deployments consume this migration;
|
|
2
|
+
-- no UI or local-runtime store is an authority for these records.
|
|
4
3
|
|
|
5
4
|
CREATE EXTENSION IF NOT EXISTS pgcrypto;
|
|
6
5
|
|
|
@@ -40,6 +39,7 @@ CREATE TABLE amalgm_automation_triggers (
|
|
|
40
39
|
enabled boolean NOT NULL DEFAULT true,
|
|
41
40
|
cron text,
|
|
42
41
|
timezone text,
|
|
42
|
+
next_run_at timestamptz,
|
|
43
43
|
source text,
|
|
44
44
|
event text,
|
|
45
45
|
secret text,
|
|
@@ -50,16 +50,22 @@ CREATE TABLE amalgm_automation_triggers (
|
|
|
50
50
|
REFERENCES amalgm_automations(user_id, id) ON DELETE CASCADE,
|
|
51
51
|
CHECK (
|
|
52
52
|
(kind = 'schedule' AND cron IS NOT NULL AND timezone IS NOT NULL
|
|
53
|
-
AND
|
|
53
|
+
AND next_run_at IS NOT NULL AND source IS NULL AND event IS NULL
|
|
54
|
+
AND secret IS NULL)
|
|
54
55
|
OR
|
|
55
|
-
(kind = 'webhook' AND cron IS NULL AND timezone IS NULL
|
|
56
|
-
AND
|
|
56
|
+
(kind = 'webhook' AND cron IS NULL AND timezone IS NULL
|
|
57
|
+
AND next_run_at IS NULL AND source IS NOT NULL AND event IS NOT NULL
|
|
58
|
+
AND length(secret) >= 16)
|
|
57
59
|
)
|
|
58
60
|
);
|
|
59
61
|
|
|
60
62
|
CREATE INDEX amalgm_automation_triggers_by_automation
|
|
61
63
|
ON amalgm_automation_triggers(user_id, automation_id, kind, created_at, id);
|
|
62
64
|
|
|
65
|
+
CREATE INDEX amalgm_automation_schedules_due
|
|
66
|
+
ON amalgm_automation_triggers(next_run_at, user_id, automation_id, id)
|
|
67
|
+
WHERE kind = 'schedule' AND enabled;
|
|
68
|
+
|
|
63
69
|
CREATE TABLE amalgm_automation_runs (
|
|
64
70
|
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
65
71
|
user_id uuid NOT NULL,
|
|
@@ -82,6 +88,10 @@ CREATE TABLE amalgm_automation_runs (
|
|
|
82
88
|
CREATE INDEX amalgm_automation_runs_history
|
|
83
89
|
ON amalgm_automation_runs(user_id, automation_id, created_at DESC, id DESC);
|
|
84
90
|
|
|
91
|
+
CREATE INDEX amalgm_automation_runs_pending
|
|
92
|
+
ON amalgm_automation_runs(user_id, target_id, created_at, id)
|
|
93
|
+
WHERE status = 'pending';
|
|
94
|
+
|
|
85
95
|
ALTER TABLE amalgm_automations ENABLE ROW LEVEL SECURITY;
|
|
86
96
|
ALTER TABLE amalgm_automation_workflows ENABLE ROW LEVEL SECURITY;
|
|
87
97
|
ALTER TABLE amalgm_automation_triggers ENABLE ROW LEVEL SECURITY;
|
|
@@ -100,6 +110,7 @@ AS $$
|
|
|
100
110
|
'enabled', p_trigger.enabled,
|
|
101
111
|
'cron', p_trigger.cron,
|
|
102
112
|
'timezone', p_trigger.timezone,
|
|
113
|
+
'next_run_at', p_trigger.next_run_at,
|
|
103
114
|
'source', p_trigger.source,
|
|
104
115
|
'event', p_trigger.event,
|
|
105
116
|
'secret_configured', p_trigger.secret IS NOT NULL,
|
|
@@ -221,10 +232,11 @@ DECLARE
|
|
|
221
232
|
v_row amalgm_automation_triggers;
|
|
222
233
|
BEGIN
|
|
223
234
|
INSERT INTO amalgm_automation_triggers (
|
|
224
|
-
user_id, automation_id, id, kind, enabled, cron, timezone
|
|
235
|
+
user_id, automation_id, id, kind, enabled, cron, timezone, next_run_at
|
|
225
236
|
)
|
|
226
237
|
SELECT p_user_id, p_automation_id, p_trigger->>'id', 'schedule',
|
|
227
|
-
(p_trigger->>'enabled')::boolean, p_trigger->>'cron', p_trigger->>'timezone'
|
|
238
|
+
(p_trigger->>'enabled')::boolean, p_trigger->>'cron', p_trigger->>'timezone',
|
|
239
|
+
(p_trigger->>'nextRunAt')::timestamptz
|
|
228
240
|
WHERE EXISTS (
|
|
229
241
|
SELECT 1 FROM amalgm_automations
|
|
230
242
|
WHERE user_id = p_user_id AND id = p_automation_id
|
|
@@ -291,6 +303,10 @@ BEGIN
|
|
|
291
303
|
UPDATE amalgm_automation_triggers SET
|
|
292
304
|
cron = CASE WHEN p_patch ? 'cron' THEN p_patch->>'cron' ELSE cron END,
|
|
293
305
|
timezone = CASE WHEN p_patch ? 'timezone' THEN p_patch->>'timezone' ELSE timezone END,
|
|
306
|
+
next_run_at = CASE
|
|
307
|
+
WHEN p_patch ? 'nextRunAt' THEN (p_patch->>'nextRunAt')::timestamptz
|
|
308
|
+
ELSE next_run_at
|
|
309
|
+
END,
|
|
294
310
|
enabled = CASE WHEN p_patch ? 'enabled' THEN (p_patch->>'enabled')::boolean ELSE enabled END,
|
|
295
311
|
updated_at = now()
|
|
296
312
|
WHERE user_id = p_user_id AND automation_id = p_automation_id
|
|
@@ -561,6 +577,208 @@ AS $$
|
|
|
561
577
|
WHERE r.user_id = p_user_id AND r.automation_id = p_automation_id AND r.id::text = p_run_id;
|
|
562
578
|
$$;
|
|
563
579
|
|
|
580
|
+
CREATE FUNCTION list_amalgm_event_triggers(p_user_id uuid, p_target_id text)
|
|
581
|
+
RETURNS TABLE (
|
|
582
|
+
user_id uuid,
|
|
583
|
+
automation_id text,
|
|
584
|
+
trigger_id text,
|
|
585
|
+
target_id text,
|
|
586
|
+
source text,
|
|
587
|
+
event text,
|
|
588
|
+
secret text
|
|
589
|
+
)
|
|
590
|
+
LANGUAGE sql
|
|
591
|
+
SECURITY DEFINER
|
|
592
|
+
SET search_path = public
|
|
593
|
+
AS $$
|
|
594
|
+
SELECT t.user_id, t.automation_id, t.id, a.target_id, t.source, t.event, t.secret
|
|
595
|
+
FROM amalgm_automation_triggers t
|
|
596
|
+
JOIN amalgm_automations a
|
|
597
|
+
ON a.user_id = t.user_id AND a.id = t.automation_id
|
|
598
|
+
WHERE t.kind = 'webhook' AND t.user_id = p_user_id
|
|
599
|
+
AND a.target_id = p_target_id AND t.enabled AND a.enabled;
|
|
600
|
+
$$;
|
|
601
|
+
|
|
602
|
+
CREATE FUNCTION list_due_amalgm_crons(p_now timestamptz)
|
|
603
|
+
RETURNS TABLE (
|
|
604
|
+
user_id uuid,
|
|
605
|
+
automation_id text,
|
|
606
|
+
trigger_id text,
|
|
607
|
+
target_id text,
|
|
608
|
+
cron text,
|
|
609
|
+
timezone text,
|
|
610
|
+
next_run_at timestamptz
|
|
611
|
+
)
|
|
612
|
+
LANGUAGE sql
|
|
613
|
+
SECURITY DEFINER
|
|
614
|
+
SET search_path = public
|
|
615
|
+
AS $$
|
|
616
|
+
SELECT t.user_id, t.automation_id, t.id, a.target_id,
|
|
617
|
+
t.cron, t.timezone, t.next_run_at
|
|
618
|
+
FROM amalgm_automation_triggers t
|
|
619
|
+
JOIN amalgm_automations a
|
|
620
|
+
ON a.user_id = t.user_id AND a.id = t.automation_id
|
|
621
|
+
WHERE t.kind = 'schedule' AND t.next_run_at <= p_now
|
|
622
|
+
AND t.enabled AND a.enabled
|
|
623
|
+
ORDER BY t.next_run_at, t.user_id, t.automation_id, t.id;
|
|
624
|
+
$$;
|
|
625
|
+
|
|
626
|
+
CREATE FUNCTION enqueue_amalgm_run(
|
|
627
|
+
p_kind text,
|
|
628
|
+
p_user_id uuid,
|
|
629
|
+
p_automation_id text,
|
|
630
|
+
p_trigger_id text,
|
|
631
|
+
p_target_id text,
|
|
632
|
+
p_expected_next_run_at timestamptz,
|
|
633
|
+
p_next_run_at timestamptz,
|
|
634
|
+
p_input jsonb,
|
|
635
|
+
p_now timestamptz
|
|
636
|
+
)
|
|
637
|
+
RETURNS SETOF amalgm_automation_runs
|
|
638
|
+
LANGUAGE plpgsql
|
|
639
|
+
SECURITY DEFINER
|
|
640
|
+
SET search_path = public
|
|
641
|
+
AS $$
|
|
642
|
+
BEGIN
|
|
643
|
+
IF p_kind = 'cron' THEN
|
|
644
|
+
PERFORM 1
|
|
645
|
+
FROM amalgm_automation_triggers t
|
|
646
|
+
JOIN amalgm_automations a
|
|
647
|
+
ON a.user_id = t.user_id AND a.id = t.automation_id
|
|
648
|
+
WHERE t.user_id = p_user_id AND t.automation_id = p_automation_id
|
|
649
|
+
AND t.id = p_trigger_id AND a.target_id = p_target_id
|
|
650
|
+
AND t.kind = 'schedule' AND t.next_run_at = p_expected_next_run_at
|
|
651
|
+
AND t.enabled AND a.enabled
|
|
652
|
+
FOR UPDATE OF t;
|
|
653
|
+
ELSIF p_kind = 'event' THEN
|
|
654
|
+
PERFORM 1
|
|
655
|
+
FROM amalgm_automation_triggers t
|
|
656
|
+
JOIN amalgm_automations a
|
|
657
|
+
ON a.user_id = t.user_id AND a.id = t.automation_id
|
|
658
|
+
WHERE t.user_id = p_user_id AND t.automation_id = p_automation_id
|
|
659
|
+
AND t.id = p_trigger_id AND a.target_id = p_target_id
|
|
660
|
+
AND t.kind = 'webhook' AND t.enabled AND a.enabled;
|
|
661
|
+
ELSE
|
|
662
|
+
RAISE EXCEPTION 'Invalid automation trigger kind';
|
|
663
|
+
END IF;
|
|
664
|
+
IF NOT FOUND THEN RETURN; END IF;
|
|
665
|
+
|
|
666
|
+
RETURN QUERY
|
|
667
|
+
INSERT INTO amalgm_automation_runs (
|
|
668
|
+
user_id, target_id, automation_id, trigger_id, workflow_id,
|
|
669
|
+
automation_payload, input, status, created_at
|
|
670
|
+
)
|
|
671
|
+
SELECT
|
|
672
|
+
a.user_id,
|
|
673
|
+
a.target_id,
|
|
674
|
+
a.id,
|
|
675
|
+
t.id,
|
|
676
|
+
w.id,
|
|
677
|
+
jsonb_strip_nulls(jsonb_build_object(
|
|
678
|
+
'id', a.id,
|
|
679
|
+
'targetId', a.target_id,
|
|
680
|
+
'name', a.name,
|
|
681
|
+
'description', a.description,
|
|
682
|
+
'enabled', a.enabled,
|
|
683
|
+
'trigger', jsonb_strip_nulls(jsonb_build_object(
|
|
684
|
+
'id', t.id,
|
|
685
|
+
'kind', CASE WHEN t.kind = 'schedule' THEN 'cron' ELSE 'event' END,
|
|
686
|
+
'enabled', t.enabled,
|
|
687
|
+
'cron', t.cron,
|
|
688
|
+
'timezone', t.timezone,
|
|
689
|
+
'source', t.source,
|
|
690
|
+
'event', t.event
|
|
691
|
+
)),
|
|
692
|
+
'workflow', jsonb_strip_nulls(jsonb_build_object(
|
|
693
|
+
'id', w.id,
|
|
694
|
+
'name', w.name,
|
|
695
|
+
'script', w.script,
|
|
696
|
+
'compiled', w.compiled,
|
|
697
|
+
'allowlist', w.allowlist,
|
|
698
|
+
'limits', w.limits
|
|
699
|
+
))
|
|
700
|
+
)),
|
|
701
|
+
p_input,
|
|
702
|
+
'pending',
|
|
703
|
+
p_now
|
|
704
|
+
FROM amalgm_automations a
|
|
705
|
+
JOIN amalgm_automation_triggers t
|
|
706
|
+
ON t.user_id = a.user_id AND t.automation_id = a.id
|
|
707
|
+
JOIN amalgm_automation_workflows w
|
|
708
|
+
ON w.user_id = a.user_id AND w.automation_id = a.id
|
|
709
|
+
WHERE a.user_id = p_user_id AND a.id = p_automation_id
|
|
710
|
+
AND t.id = p_trigger_id
|
|
711
|
+
RETURNING *;
|
|
712
|
+
|
|
713
|
+
IF p_kind = 'cron' THEN
|
|
714
|
+
UPDATE amalgm_automation_triggers
|
|
715
|
+
SET next_run_at = p_next_run_at, updated_at = p_now
|
|
716
|
+
WHERE user_id = p_user_id AND automation_id = p_automation_id
|
|
717
|
+
AND id = p_trigger_id AND kind = 'schedule'
|
|
718
|
+
AND next_run_at = p_expected_next_run_at;
|
|
719
|
+
END IF;
|
|
720
|
+
END;
|
|
721
|
+
$$;
|
|
722
|
+
|
|
723
|
+
CREATE FUNCTION list_amalgm_pending_runs(p_user_id uuid, p_target_id text)
|
|
724
|
+
RETURNS SETOF amalgm_automation_runs
|
|
725
|
+
LANGUAGE sql
|
|
726
|
+
SECURITY DEFINER
|
|
727
|
+
SET search_path = public
|
|
728
|
+
AS $$
|
|
729
|
+
SELECT * FROM amalgm_automation_runs
|
|
730
|
+
WHERE user_id = p_user_id AND target_id = p_target_id AND status = 'pending'
|
|
731
|
+
ORDER BY created_at, id;
|
|
732
|
+
$$;
|
|
733
|
+
|
|
734
|
+
CREATE FUNCTION mark_amalgm_run_sent(
|
|
735
|
+
p_run_id uuid,
|
|
736
|
+
p_user_id uuid,
|
|
737
|
+
p_target_id text,
|
|
738
|
+
p_now timestamptz
|
|
739
|
+
)
|
|
740
|
+
RETURNS boolean
|
|
741
|
+
LANGUAGE plpgsql
|
|
742
|
+
SECURITY DEFINER
|
|
743
|
+
SET search_path = public
|
|
744
|
+
AS $$
|
|
745
|
+
BEGIN
|
|
746
|
+
UPDATE amalgm_automation_runs SET status = 'sent', sent_at = p_now
|
|
747
|
+
WHERE id = p_run_id AND user_id = p_user_id AND target_id = p_target_id
|
|
748
|
+
AND status = 'pending';
|
|
749
|
+
RETURN FOUND;
|
|
750
|
+
END;
|
|
751
|
+
$$;
|
|
752
|
+
|
|
753
|
+
CREATE FUNCTION update_amalgm_run(
|
|
754
|
+
p_run_id uuid,
|
|
755
|
+
p_user_id uuid,
|
|
756
|
+
p_target_id text,
|
|
757
|
+
p_update jsonb
|
|
758
|
+
)
|
|
759
|
+
RETURNS SETOF amalgm_automation_runs
|
|
760
|
+
LANGUAGE plpgsql
|
|
761
|
+
SECURITY DEFINER
|
|
762
|
+
SET search_path = public
|
|
763
|
+
AS $$
|
|
764
|
+
BEGIN
|
|
765
|
+
IF p_update->>'status' NOT IN ('running', 'completed', 'failed') THEN
|
|
766
|
+
RAISE EXCEPTION 'Invalid run status';
|
|
767
|
+
END IF;
|
|
768
|
+
|
|
769
|
+
RETURN QUERY
|
|
770
|
+
UPDATE amalgm_automation_runs SET
|
|
771
|
+
status = p_update->>'status',
|
|
772
|
+
started_at = coalesce((p_update->>'startedAt')::timestamptz, started_at),
|
|
773
|
+
finished_at = coalesce((p_update->>'finishedAt')::timestamptz, finished_at),
|
|
774
|
+
output = CASE WHEN p_update ? 'output' THEN p_update->'output' ELSE output END,
|
|
775
|
+
error = CASE WHEN p_update ? 'error' THEN p_update->>'error' ELSE error END
|
|
776
|
+
WHERE id = p_run_id AND user_id = p_user_id AND target_id = p_target_id
|
|
777
|
+
AND status IN ('sent', 'running')
|
|
778
|
+
RETURNING *;
|
|
779
|
+
END;
|
|
780
|
+
$$;
|
|
781
|
+
|
|
564
782
|
REVOKE ALL ON FUNCTION amalgm_trigger_json(amalgm_automation_triggers) FROM PUBLIC;
|
|
565
783
|
REVOKE ALL ON FUNCTION create_amalgm_automation(uuid, jsonb) FROM PUBLIC;
|
|
566
784
|
REVOKE ALL ON FUNCTION list_amalgm_automations(uuid, text, boolean, integer, integer) FROM PUBLIC;
|
|
@@ -584,6 +802,12 @@ REVOKE ALL ON FUNCTION update_amalgm_workflow(uuid, text, jsonb) FROM PUBLIC;
|
|
|
584
802
|
REVOKE ALL ON FUNCTION delete_amalgm_workflow(uuid, text) FROM PUBLIC;
|
|
585
803
|
REVOKE ALL ON FUNCTION list_amalgm_runs(uuid, text, text, integer, integer) FROM PUBLIC;
|
|
586
804
|
REVOKE ALL ON FUNCTION get_amalgm_run(uuid, text, text) FROM PUBLIC;
|
|
805
|
+
REVOKE ALL ON FUNCTION list_amalgm_event_triggers(uuid, text) FROM PUBLIC;
|
|
806
|
+
REVOKE ALL ON FUNCTION list_due_amalgm_crons(timestamptz) FROM PUBLIC;
|
|
807
|
+
REVOKE ALL ON FUNCTION enqueue_amalgm_run(text, uuid, text, text, text, timestamptz, timestamptz, jsonb, timestamptz) FROM PUBLIC;
|
|
808
|
+
REVOKE ALL ON FUNCTION list_amalgm_pending_runs(uuid, text) FROM PUBLIC;
|
|
809
|
+
REVOKE ALL ON FUNCTION mark_amalgm_run_sent(uuid, uuid, text, timestamptz) FROM PUBLIC;
|
|
810
|
+
REVOKE ALL ON FUNCTION update_amalgm_run(uuid, uuid, text, jsonb) FROM PUBLIC;
|
|
587
811
|
|
|
588
812
|
GRANT EXECUTE ON FUNCTION create_amalgm_automation(uuid, jsonb) TO service_role;
|
|
589
813
|
GRANT EXECUTE ON FUNCTION list_amalgm_automations(uuid, text, boolean, integer, integer) TO service_role;
|
|
@@ -607,3 +831,9 @@ GRANT EXECUTE ON FUNCTION update_amalgm_workflow(uuid, text, jsonb) TO service_r
|
|
|
607
831
|
GRANT EXECUTE ON FUNCTION delete_amalgm_workflow(uuid, text) TO service_role;
|
|
608
832
|
GRANT EXECUTE ON FUNCTION list_amalgm_runs(uuid, text, text, integer, integer) TO service_role;
|
|
609
833
|
GRANT EXECUTE ON FUNCTION get_amalgm_run(uuid, text, text) TO service_role;
|
|
834
|
+
GRANT EXECUTE ON FUNCTION list_amalgm_event_triggers(uuid, text) TO service_role;
|
|
835
|
+
GRANT EXECUTE ON FUNCTION list_due_amalgm_crons(timestamptz) TO service_role;
|
|
836
|
+
GRANT EXECUTE ON FUNCTION enqueue_amalgm_run(text, uuid, text, text, text, timestamptz, timestamptz, jsonb, timestamptz) TO service_role;
|
|
837
|
+
GRANT EXECUTE ON FUNCTION list_amalgm_pending_runs(uuid, text) TO service_role;
|
|
838
|
+
GRANT EXECUTE ON FUNCTION mark_amalgm_run_sent(uuid, uuid, text, timestamptz) TO service_role;
|
|
839
|
+
GRANT EXECUTE ON FUNCTION update_amalgm_run(uuid, uuid, text, jsonb) TO service_role;
|