@amalgm/automations 0.1.1 → 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.
Files changed (67) hide show
  1. package/AXIOMS.md +14 -0
  2. package/PURPOSE.md +15 -3
  3. package/README.md +61 -100
  4. package/dist/host/auth.d.ts +11 -0
  5. package/dist/host/auth.js +89 -0
  6. package/dist/host/config.d.ts +8 -0
  7. package/dist/host/config.js +29 -0
  8. package/dist/host/index.d.ts +3 -0
  9. package/dist/host/index.js +3 -0
  10. package/dist/host/main.d.ts +1 -0
  11. package/dist/host/main.js +47 -0
  12. package/dist/host/server.d.ts +11 -0
  13. package/dist/host/server.js +53 -0
  14. package/dist/src/automations.js +4 -1
  15. package/dist/src/client.d.ts +3 -1
  16. package/dist/src/client.js +6 -4
  17. package/dist/src/contract.d.ts +17 -1
  18. package/dist/src/crud/automations.d.ts +3 -0
  19. package/dist/src/crud/automations.js +55 -0
  20. package/dist/src/crud/context.d.ts +13 -0
  21. package/dist/src/crud/context.js +34 -0
  22. package/dist/src/crud/repository.d.ts +35 -0
  23. package/dist/src/crud/repository.js +1 -0
  24. package/dist/src/crud/runs.d.ts +3 -0
  25. package/dist/src/crud/runs.js +19 -0
  26. package/dist/src/crud/triggers.d.ts +3 -0
  27. package/dist/src/crud/triggers.js +118 -0
  28. package/dist/src/crud/workflow.d.ts +3 -0
  29. package/dist/src/crud/workflow.js +42 -0
  30. package/dist/src/crud.d.ts +3 -33
  31. package/dist/src/crud.js +10 -237
  32. package/dist/src/events-http.d.ts +19 -0
  33. package/dist/src/events-http.js +107 -0
  34. package/dist/src/executor.d.ts +16 -0
  35. package/dist/src/executor.js +76 -0
  36. package/dist/src/index.d.ts +9 -2
  37. package/dist/src/index.js +6 -0
  38. package/dist/src/machine-client.d.ts +7 -0
  39. package/dist/src/machine-client.js +30 -0
  40. package/dist/src/machine-http.d.ts +5 -0
  41. package/dist/src/machine-http.js +40 -0
  42. package/dist/src/machine.d.ts +41 -0
  43. package/dist/src/machine.js +43 -0
  44. package/dist/src/mcp.js +2 -2
  45. package/dist/src/schema.d.ts +8 -0
  46. package/dist/src/schema.js +2 -0
  47. package/dist/src/supabase-crud/automations.d.ts +5 -0
  48. package/dist/src/supabase-crud/automations.js +36 -0
  49. package/dist/src/supabase-crud/mappers.d.ts +8 -0
  50. package/dist/src/supabase-crud/mappers.js +81 -0
  51. package/dist/src/supabase-crud/rows.d.ts +56 -0
  52. package/dist/src/supabase-crud/rows.js +1 -0
  53. package/dist/src/supabase-crud/rpc.d.ts +10 -0
  54. package/dist/src/supabase-crud/rpc.js +19 -0
  55. package/dist/src/supabase-crud/triggers.d.ts +5 -0
  56. package/dist/src/supabase-crud/triggers.js +46 -0
  57. package/dist/src/supabase-crud/workflow-runs.d.ts +5 -0
  58. package/dist/src/supabase-crud/workflow-runs.js +41 -0
  59. package/dist/src/supabase-crud.d.ts +5 -41
  60. package/dist/src/supabase-crud.js +4 -267
  61. package/dist/src/supabase-machine.d.ts +16 -0
  62. package/dist/src/supabase-machine.js +63 -0
  63. package/dist/src/supabase-store.js +1 -0
  64. package/dist/src/types.d.ts +1 -0
  65. package/package.json +13 -4
  66. package/skills/automations/SKILL.md +52 -0
  67. package/supabase/migrations/20260829010000_bounded_schedules_and_machine_claims.sql +311 -0
@@ -0,0 +1,56 @@
1
+ import type { AutomationRun, Workflow } from '../contract.js';
2
+ export type AutomationRow = {
3
+ id: string;
4
+ target_id: string;
5
+ name: string | null;
6
+ description: string | null;
7
+ enabled: boolean;
8
+ created_at: Date | string;
9
+ updated_at: Date | string;
10
+ };
11
+ export type TriggerRow = {
12
+ id: string;
13
+ automation_id: string;
14
+ kind: 'schedule' | 'webhook';
15
+ enabled: boolean;
16
+ cron: string | null;
17
+ timezone: string | null;
18
+ next_run_at: Date | string | null;
19
+ max_occurrences: number | null;
20
+ remaining_occurrences: number | null;
21
+ source: string | null;
22
+ event: string | null;
23
+ secret_configured: boolean;
24
+ created_at: Date | string;
25
+ updated_at: Date | string;
26
+ };
27
+ export type WorkflowRow = {
28
+ id: string;
29
+ automation_id: string;
30
+ name: string | null;
31
+ script: string;
32
+ compiled: Workflow['compiled'] | null;
33
+ allowlist: Workflow['allowlist'] | null;
34
+ limits: Workflow['limits'] | null;
35
+ created_at: Date | string;
36
+ updated_at: Date | string;
37
+ };
38
+ export type RunRow = {
39
+ id: string;
40
+ automation_id: string;
41
+ trigger_id: string;
42
+ workflow_id: string;
43
+ target_id: string;
44
+ status: AutomationRun['status'];
45
+ input: AutomationRun['input'];
46
+ created_at: Date | string;
47
+ sent_at: Date | string | null;
48
+ started_at: Date | string | null;
49
+ finished_at: Date | string | null;
50
+ output: AutomationRun['output'] | null;
51
+ error: string | null;
52
+ };
53
+ export type PageResult<T> = {
54
+ items: T[];
55
+ total: number;
56
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,10 @@
1
+ export interface SupabaseRpcClient {
2
+ rpc(functionName: string, arguments_: Record<string, unknown>): PromiseLike<{
3
+ data: unknown;
4
+ error: {
5
+ code?: string;
6
+ message?: string;
7
+ } | null;
8
+ }>;
9
+ }
10
+ export declare function call<T>(client: SupabaseRpcClient, functionName: string, arguments_: Record<string, unknown>): Promise<T>;
@@ -0,0 +1,19 @@
1
+ import { ConflictError } from '../errors.js';
2
+ export async function call(client, functionName, arguments_) {
3
+ const { data, error } = await client.rpc(functionName, arguments_);
4
+ if (!error)
5
+ return data;
6
+ if (error.code === '23505' || /duplicate key|unique constraint/i.test(error.message || '')) {
7
+ throw new ConflictError(resource(functionName));
8
+ }
9
+ throw new Error(error.message || `Supabase function ${functionName} failed`);
10
+ }
11
+ function resource(functionName) {
12
+ if (functionName.includes('workflow'))
13
+ return 'Workflow';
14
+ if (functionName.includes('trigger'))
15
+ return 'Trigger';
16
+ if (functionName.includes('automation'))
17
+ return 'Automation';
18
+ return 'Automation resource';
19
+ }
@@ -0,0 +1,5 @@
1
+ import type { AutomationCrudRepository } from '../crud.js';
2
+ import { type SupabaseRpcClient } from './rpc.js';
3
+ type Operations = Pick<AutomationCrudRepository, 'createScheduleTrigger' | 'listScheduleTriggers' | 'getScheduleTrigger' | 'updateScheduleTrigger' | 'deleteScheduleTrigger' | 'createWebhookTrigger' | 'listWebhookTriggers' | 'getWebhookTrigger' | 'updateWebhookTrigger' | 'deleteWebhookTrigger' | 'listTriggers'>;
4
+ export declare function triggerRepository(client: SupabaseRpcClient): Operations;
5
+ export {};
@@ -0,0 +1,46 @@
1
+ import { paged, schedule, webhook } from './mappers.js';
2
+ import { call } from './rpc.js';
3
+ export function triggerRepository(client) {
4
+ const list = async (functionName, userId, automationId, query, mapper) => paged(await call(client, functionName, {
5
+ p_user_id: userId,
6
+ p_automation_id: automationId,
7
+ p_limit: query.limit,
8
+ p_offset: query.offset,
9
+ }), mapper, query);
10
+ return {
11
+ createScheduleTrigger: async (userId, automationId, input) => schedule(await call(client, 'create_amalgm_schedule_trigger', { p_user_id: userId, p_automation_id: automationId, p_trigger: input })),
12
+ listScheduleTriggers: (userId, automationId, query) => list('list_amalgm_schedule_triggers', userId, automationId, query, schedule),
13
+ getScheduleTrigger: async (userId, automationId, triggerId) => {
14
+ const row = await call(client, 'get_amalgm_schedule_trigger', {
15
+ p_user_id: userId, p_automation_id: automationId, p_trigger_id: triggerId,
16
+ });
17
+ return row ? schedule(row) : null;
18
+ },
19
+ updateScheduleTrigger: async (userId, automationId, triggerId, patch) => {
20
+ const row = await call(client, 'update_amalgm_schedule_trigger', {
21
+ p_user_id: userId, p_automation_id: automationId, p_trigger_id: triggerId, p_patch: patch,
22
+ });
23
+ return row ? schedule(row) : null;
24
+ },
25
+ deleteScheduleTrigger: (userId, automationId, triggerId) => call(client, 'delete_amalgm_schedule_trigger', { p_user_id: userId, p_automation_id: automationId, p_trigger_id: triggerId }),
26
+ createWebhookTrigger: async (userId, automationId, input) => webhook(await call(client, 'create_amalgm_webhook_trigger', { p_user_id: userId, p_automation_id: automationId, p_trigger: input })),
27
+ listWebhookTriggers: (userId, automationId, query) => list('list_amalgm_webhook_triggers', userId, automationId, query, webhook),
28
+ getWebhookTrigger: async (userId, automationId, triggerId) => {
29
+ const row = await call(client, 'get_amalgm_webhook_trigger', {
30
+ p_user_id: userId, p_automation_id: automationId, p_trigger_id: triggerId,
31
+ });
32
+ return row ? webhook(row) : null;
33
+ },
34
+ updateWebhookTrigger: async (userId, automationId, triggerId, patch) => {
35
+ const row = await call(client, 'update_amalgm_webhook_trigger', {
36
+ p_user_id: userId, p_automation_id: automationId, p_trigger_id: triggerId, p_patch: patch,
37
+ });
38
+ return row ? webhook(row) : null;
39
+ },
40
+ deleteWebhookTrigger: (userId, automationId, triggerId) => call(client, 'delete_amalgm_webhook_trigger', { p_user_id: userId, p_automation_id: automationId, p_trigger_id: triggerId }),
41
+ listTriggers: async (userId, automationId) => (await call(client, 'list_amalgm_triggers', {
42
+ p_user_id: userId,
43
+ p_automation_id: automationId,
44
+ })).map((row) => row.kind === 'schedule' ? schedule(row) : webhook(row)),
45
+ };
46
+ }
@@ -0,0 +1,5 @@
1
+ import type { AutomationCrudRepository } from '../crud.js';
2
+ import { type SupabaseRpcClient } from './rpc.js';
3
+ type Operations = Pick<AutomationCrudRepository, 'createWorkflow' | 'getWorkflow' | 'updateWorkflow' | 'deleteWorkflow' | 'listRuns' | 'getRun'>;
4
+ export declare function workflowRunRepository(client: SupabaseRpcClient): Operations;
5
+ export {};
@@ -0,0 +1,41 @@
1
+ import { paged, run, workflow } from './mappers.js';
2
+ import { call } from './rpc.js';
3
+ export function workflowRunRepository(client) {
4
+ return {
5
+ createWorkflow: async (userId, automationId, input) => workflow(await call(client, 'create_amalgm_workflow', { p_user_id: userId, p_automation_id: automationId, p_workflow: input })),
6
+ getWorkflow: async (userId, automationId) => {
7
+ const row = await call(client, 'get_amalgm_workflow', {
8
+ p_user_id: userId,
9
+ p_automation_id: automationId,
10
+ });
11
+ return row ? workflow(row) : null;
12
+ },
13
+ updateWorkflow: async (userId, automationId, patch) => {
14
+ const row = await call(client, 'update_amalgm_workflow', {
15
+ p_user_id: userId,
16
+ p_automation_id: automationId,
17
+ p_patch: patch,
18
+ });
19
+ return row ? workflow(row) : null;
20
+ },
21
+ deleteWorkflow: (userId, automationId) => call(client, 'delete_amalgm_workflow', {
22
+ p_user_id: userId,
23
+ p_automation_id: automationId,
24
+ }),
25
+ listRuns: async (userId, automationId, query) => paged(await call(client, 'list_amalgm_runs', {
26
+ p_user_id: userId,
27
+ p_automation_id: automationId,
28
+ p_status: query.status || null,
29
+ p_limit: query.limit,
30
+ p_offset: query.offset,
31
+ }), run, query),
32
+ getRun: async (userId, automationId, runId) => {
33
+ const row = await call(client, 'get_amalgm_run', {
34
+ p_user_id: userId,
35
+ p_automation_id: automationId,
36
+ p_run_id: runId,
37
+ });
38
+ return row ? run(row) : null;
39
+ },
40
+ };
41
+ }
@@ -1,44 +1,8 @@
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
- export interface SupabaseRpcClient {
4
- rpc(functionName: string, arguments_: Record<string, unknown>): PromiseLike<{
5
- data: unknown;
6
- error: {
7
- code?: string;
8
- message?: string;
9
- } | null;
10
- }>;
1
+ import type { AutomationCrudRepository } from './crud.js';
2
+ import type { SupabaseRpcClient } from './supabase-crud/rpc.js';
3
+ export type { SupabaseRpcClient } from './supabase-crud/rpc.js';
4
+ export interface SupabaseAutomationCrudRepository extends AutomationCrudRepository {
11
5
  }
12
- export declare class SupabaseAutomationCrudRepository implements AutomationCrudRepository {
13
- #private;
14
- private readonly client;
6
+ export declare class SupabaseAutomationCrudRepository {
15
7
  constructor(client: SupabaseRpcClient);
16
- createAutomation(userId: string, input: Required<CreateAutomation>): Promise<Automation>;
17
- listAutomations(userId: string, query: NormalizedListAutomations): Promise<Page<Automation>>;
18
- getAutomation(userId: string, automationId: string): Promise<Automation | null>;
19
- updateAutomation(userId: string, automationId: string, patch: UpdateAutomation): Promise<Automation | null>;
20
- deleteAutomation(userId: string, automationId: string): Promise<boolean>;
21
- createScheduleTrigger(userId: string, automationId: string, input: StoredScheduleCreate): Promise<ScheduleTrigger>;
22
- listScheduleTriggers(userId: string, automationId: string, query: Required<{
23
- limit?: number;
24
- offset?: number;
25
- }>): Promise<Page<ScheduleTrigger>>;
26
- getScheduleTrigger(userId: string, automationId: string, triggerId: string): Promise<ScheduleTrigger | null>;
27
- updateScheduleTrigger(userId: string, automationId: string, triggerId: string, patch: StoredScheduleUpdate): Promise<ScheduleTrigger | null>;
28
- deleteScheduleTrigger(userId: string, automationId: string, triggerId: string): Promise<boolean>;
29
- createWebhookTrigger(userId: string, automationId: string, input: Required<CreateWebhookTrigger>): Promise<WebhookTrigger>;
30
- listWebhookTriggers(userId: string, automationId: string, query: Required<{
31
- limit?: number;
32
- offset?: number;
33
- }>): Promise<Page<WebhookTrigger>>;
34
- getWebhookTrigger(userId: string, automationId: string, triggerId: string): Promise<WebhookTrigger | null>;
35
- updateWebhookTrigger(userId: string, automationId: string, triggerId: string, patch: UpdateWebhookTrigger): Promise<WebhookTrigger | null>;
36
- deleteWebhookTrigger(userId: string, automationId: string, triggerId: string): Promise<boolean>;
37
- listTriggers(userId: string, automationId: string): Promise<Trigger[]>;
38
- createWorkflow(userId: string, automationId: string, input: Required<CreateWorkflow>): Promise<Workflow>;
39
- getWorkflow(userId: string, automationId: string): Promise<Workflow | null>;
40
- updateWorkflow(userId: string, automationId: string, patch: UpdateWorkflow): Promise<Workflow | null>;
41
- deleteWorkflow(userId: string, automationId: string): Promise<boolean>;
42
- listRuns(userId: string, automationId: string, query: NormalizedListRuns): Promise<Page<AutomationRun>>;
43
- getRun(userId: string, automationId: string, runId: string): Promise<AutomationRun | null>;
44
8
  }
@@ -1,271 +1,8 @@
1
- import { ConflictError } from './errors.js';
1
+ import { automationRepository } from './supabase-crud/automations.js';
2
+ import { triggerRepository } from './supabase-crud/triggers.js';
3
+ import { workflowRunRepository } from './supabase-crud/workflow-runs.js';
2
4
  export class SupabaseAutomationCrudRepository {
3
- client;
4
5
  constructor(client) {
5
- this.client = client;
6
+ Object.assign(this, automationRepository(client), triggerRepository(client), workflowRunRepository(client));
6
7
  }
7
- async createAutomation(userId, input) {
8
- return automation(await this.#call('create_amalgm_automation', {
9
- p_user_id: userId,
10
- p_automation: input,
11
- }));
12
- }
13
- async listAutomations(userId, query) {
14
- const result = await this.#call('list_amalgm_automations', {
15
- p_user_id: userId,
16
- p_target_id: query.targetId || null,
17
- p_enabled: query.enabled ?? null,
18
- p_limit: query.limit,
19
- p_offset: query.offset,
20
- });
21
- return paged(result, automation, query);
22
- }
23
- async getAutomation(userId, automationId) {
24
- const row = await this.#call('get_amalgm_automation', {
25
- p_user_id: userId,
26
- p_automation_id: automationId,
27
- });
28
- return row ? automation(row) : null;
29
- }
30
- async updateAutomation(userId, automationId, patch) {
31
- const row = await this.#call('update_amalgm_automation', {
32
- p_user_id: userId,
33
- p_automation_id: automationId,
34
- p_patch: patch,
35
- });
36
- return row ? automation(row) : null;
37
- }
38
- deleteAutomation(userId, automationId) {
39
- return this.#call('delete_amalgm_automation', {
40
- p_user_id: userId,
41
- p_automation_id: automationId,
42
- });
43
- }
44
- async createScheduleTrigger(userId, automationId, input) {
45
- return schedule(await this.#call('create_amalgm_schedule_trigger', {
46
- p_user_id: userId,
47
- p_automation_id: automationId,
48
- p_trigger: input,
49
- }));
50
- }
51
- async listScheduleTriggers(userId, automationId, query) {
52
- return this.#listTriggers('list_amalgm_schedule_triggers', userId, automationId, query, schedule);
53
- }
54
- async getScheduleTrigger(userId, automationId, triggerId) {
55
- const row = await this.#call('get_amalgm_schedule_trigger', {
56
- p_user_id: userId,
57
- p_automation_id: automationId,
58
- p_trigger_id: triggerId,
59
- });
60
- return row ? schedule(row) : null;
61
- }
62
- async updateScheduleTrigger(userId, automationId, triggerId, patch) {
63
- const row = await this.#call('update_amalgm_schedule_trigger', {
64
- p_user_id: userId,
65
- p_automation_id: automationId,
66
- p_trigger_id: triggerId,
67
- p_patch: patch,
68
- });
69
- return row ? schedule(row) : null;
70
- }
71
- deleteScheduleTrigger(userId, automationId, triggerId) {
72
- return this.#call('delete_amalgm_schedule_trigger', {
73
- p_user_id: userId,
74
- p_automation_id: automationId,
75
- p_trigger_id: triggerId,
76
- });
77
- }
78
- async createWebhookTrigger(userId, automationId, input) {
79
- return webhook(await this.#call('create_amalgm_webhook_trigger', {
80
- p_user_id: userId,
81
- p_automation_id: automationId,
82
- p_trigger: input,
83
- }));
84
- }
85
- async listWebhookTriggers(userId, automationId, query) {
86
- return this.#listTriggers('list_amalgm_webhook_triggers', userId, automationId, query, webhook);
87
- }
88
- async getWebhookTrigger(userId, automationId, triggerId) {
89
- const row = await this.#call('get_amalgm_webhook_trigger', {
90
- p_user_id: userId,
91
- p_automation_id: automationId,
92
- p_trigger_id: triggerId,
93
- });
94
- return row ? webhook(row) : null;
95
- }
96
- async updateWebhookTrigger(userId, automationId, triggerId, patch) {
97
- const row = await this.#call('update_amalgm_webhook_trigger', {
98
- p_user_id: userId,
99
- p_automation_id: automationId,
100
- p_trigger_id: triggerId,
101
- p_patch: patch,
102
- });
103
- return row ? webhook(row) : null;
104
- }
105
- deleteWebhookTrigger(userId, automationId, triggerId) {
106
- return this.#call('delete_amalgm_webhook_trigger', {
107
- p_user_id: userId,
108
- p_automation_id: automationId,
109
- p_trigger_id: triggerId,
110
- });
111
- }
112
- async listTriggers(userId, automationId) {
113
- const rows = await this.#call('list_amalgm_triggers', {
114
- p_user_id: userId,
115
- p_automation_id: automationId,
116
- });
117
- return rows.map((row) => row.kind === 'schedule' ? schedule(row) : webhook(row));
118
- }
119
- async createWorkflow(userId, automationId, input) {
120
- return workflow(await this.#call('create_amalgm_workflow', {
121
- p_user_id: userId,
122
- p_automation_id: automationId,
123
- p_workflow: input,
124
- }));
125
- }
126
- async getWorkflow(userId, automationId) {
127
- const row = await this.#call('get_amalgm_workflow', {
128
- p_user_id: userId,
129
- p_automation_id: automationId,
130
- });
131
- return row ? workflow(row) : null;
132
- }
133
- async updateWorkflow(userId, automationId, patch) {
134
- const row = await this.#call('update_amalgm_workflow', {
135
- p_user_id: userId,
136
- p_automation_id: automationId,
137
- p_patch: patch,
138
- });
139
- return row ? workflow(row) : null;
140
- }
141
- deleteWorkflow(userId, automationId) {
142
- return this.#call('delete_amalgm_workflow', {
143
- p_user_id: userId,
144
- p_automation_id: automationId,
145
- });
146
- }
147
- async listRuns(userId, automationId, query) {
148
- const result = await this.#call('list_amalgm_runs', {
149
- p_user_id: userId,
150
- p_automation_id: automationId,
151
- p_status: query.status || null,
152
- p_limit: query.limit,
153
- p_offset: query.offset,
154
- });
155
- return paged(result, run, query);
156
- }
157
- async getRun(userId, automationId, runId) {
158
- const row = await this.#call('get_amalgm_run', {
159
- p_user_id: userId,
160
- p_automation_id: automationId,
161
- p_run_id: runId,
162
- });
163
- return row ? run(row) : null;
164
- }
165
- async #listTriggers(functionName, userId, automationId, query, mapper) {
166
- const result = await this.#call(functionName, {
167
- p_user_id: userId,
168
- p_automation_id: automationId,
169
- p_limit: query.limit,
170
- p_offset: query.offset,
171
- });
172
- return paged(result, mapper, query);
173
- }
174
- async #call(functionName, arguments_) {
175
- const { data, error } = await this.client.rpc(functionName, arguments_);
176
- if (error) {
177
- if (error.code === '23505' || /duplicate key|unique constraint/i.test(error.message || '')) {
178
- throw new ConflictError(resource(functionName));
179
- }
180
- throw new Error(error.message || `Supabase function ${functionName} failed`);
181
- }
182
- return data;
183
- }
184
- }
185
- function resource(functionName) {
186
- if (functionName.includes('workflow'))
187
- return 'Workflow';
188
- if (functionName.includes('trigger'))
189
- return 'Trigger';
190
- if (functionName.includes('automation'))
191
- return 'Automation';
192
- return 'Automation resource';
193
- }
194
- function automation(row) {
195
- return {
196
- id: row.id,
197
- targetId: row.target_id,
198
- ...(row.name ? { name: row.name } : {}),
199
- ...(row.description ? { description: row.description } : {}),
200
- enabled: row.enabled,
201
- createdAt: timestamp(row.created_at),
202
- updatedAt: timestamp(row.updated_at),
203
- };
204
- }
205
- function schedule(row) {
206
- return {
207
- id: row.id,
208
- automationId: row.automation_id,
209
- kind: 'schedule',
210
- enabled: row.enabled,
211
- cron: row.cron,
212
- timezone: row.timezone,
213
- createdAt: timestamp(row.created_at),
214
- updatedAt: timestamp(row.updated_at),
215
- };
216
- }
217
- function webhook(row) {
218
- return {
219
- id: row.id,
220
- automationId: row.automation_id,
221
- kind: 'webhook',
222
- enabled: row.enabled,
223
- source: row.source,
224
- event: row.event,
225
- secretConfigured: row.secret_configured,
226
- createdAt: timestamp(row.created_at),
227
- updatedAt: timestamp(row.updated_at),
228
- };
229
- }
230
- function workflow(row) {
231
- return {
232
- id: row.id,
233
- automationId: row.automation_id,
234
- ...(row.name ? { name: row.name } : {}),
235
- script: row.script,
236
- ...(row.compiled !== null ? { compiled: row.compiled } : {}),
237
- ...(row.allowlist !== null ? { allowlist: row.allowlist } : {}),
238
- ...(row.limits !== null ? { limits: row.limits } : {}),
239
- createdAt: timestamp(row.created_at),
240
- updatedAt: timestamp(row.updated_at),
241
- };
242
- }
243
- function run(row) {
244
- return {
245
- id: row.id,
246
- automationId: row.automation_id,
247
- triggerId: row.trigger_id,
248
- workflowId: row.workflow_id,
249
- targetId: row.target_id,
250
- status: row.status,
251
- input: row.input,
252
- createdAt: timestamp(row.created_at),
253
- ...(row.sent_at ? { sentAt: timestamp(row.sent_at) } : {}),
254
- ...(row.started_at ? { startedAt: timestamp(row.started_at) } : {}),
255
- ...(row.finished_at ? { finishedAt: timestamp(row.finished_at) } : {}),
256
- ...(row.output !== null ? { output: row.output } : {}),
257
- ...(row.error !== null ? { error: row.error } : {}),
258
- };
259
- }
260
- function paged(result, mapper, query) {
261
- return {
262
- items: result.items.map(mapper),
263
- total: result.total,
264
- limit: query.limit,
265
- offset: query.offset,
266
- hasMore: query.offset + result.items.length < result.total,
267
- };
268
- }
269
- function timestamp(value) {
270
- return new Date(value).toISOString();
271
8
  }
@@ -0,0 +1,16 @@
1
+ import type { ClaimedAutomationRun, MachineRunRepository } from './machine.js';
2
+ export interface MachineRpcClient {
3
+ rpc(name: string, arguments_: Record<string, unknown>): PromiseLike<{
4
+ data: unknown;
5
+ error: {
6
+ message?: string;
7
+ } | null;
8
+ }>;
9
+ }
10
+ export declare class SupabaseMachineRunRepository implements MachineRunRepository {
11
+ #private;
12
+ private readonly client;
13
+ constructor(client: MachineRpcClient);
14
+ claim(input: Parameters<MachineRunRepository['claim']>[0]): Promise<ClaimedAutomationRun[]>;
15
+ update(input: Parameters<MachineRunRepository['update']>[0]): Promise<ClaimedAutomationRun | null>;
16
+ }
@@ -0,0 +1,63 @@
1
+ import { AutomationError } from './errors.js';
2
+ export class SupabaseMachineRunRepository {
3
+ client;
4
+ constructor(client) {
5
+ this.client = client;
6
+ }
7
+ async claim(input) {
8
+ const rows = await this.#call('claim_amalgm_machine_runs', {
9
+ p_user_id: input.userId,
10
+ p_target_id: input.targetId,
11
+ p_limit: input.limit,
12
+ p_lease_seconds: input.leaseSeconds,
13
+ });
14
+ return rows.map(run);
15
+ }
16
+ async update(input) {
17
+ const rows = await this.#call('update_amalgm_claimed_run', {
18
+ p_user_id: input.userId,
19
+ p_target_id: input.targetId,
20
+ p_run_id: input.runId,
21
+ p_lease_token: input.update.leaseToken,
22
+ p_update: update(input.update),
23
+ p_now: input.now.toISOString(),
24
+ });
25
+ return rows[0] ? run(rows[0]) : null;
26
+ }
27
+ async #call(name, arguments_) {
28
+ const { data, error } = await this.client.rpc(name, arguments_);
29
+ if (error)
30
+ throw new AutomationError('internal', error.message ?? `${name} failed`);
31
+ return data;
32
+ }
33
+ }
34
+ function run(row) {
35
+ return {
36
+ id: row.id,
37
+ userId: row.user_id,
38
+ targetId: row.target_id,
39
+ automationId: row.automation_id,
40
+ triggerId: row.trigger_id,
41
+ workflowId: row.workflow_id,
42
+ automation: row.automation_payload,
43
+ input: row.input,
44
+ status: row.status,
45
+ attempts: row.attempts,
46
+ leaseToken: row.lease_token,
47
+ leaseExpiresAt: timestamp(row.lease_expires_at),
48
+ createdAt: timestamp(row.created_at),
49
+ ...(row.sent_at ? { sentAt: timestamp(row.sent_at) } : {}),
50
+ ...(row.started_at ? { startedAt: timestamp(row.started_at) } : {}),
51
+ ...(row.finished_at ? { finishedAt: timestamp(row.finished_at) } : {}),
52
+ ...(row.output === null ? {} : { output: row.output }),
53
+ ...(row.error === null ? {} : { error: row.error }),
54
+ };
55
+ }
56
+ function update(value) {
57
+ return {
58
+ status: value.status,
59
+ ...(value.output === undefined ? {} : { output: value.output }),
60
+ ...(value.error === undefined ? {} : { error: value.error }),
61
+ };
62
+ }
63
+ const timestamp = (value) => new Date(value).toISOString();
@@ -49,6 +49,7 @@ export class SupabaseStore {
49
49
  cron: row.cron,
50
50
  timezone: row.timezone,
51
51
  nextRunAt: new Date(row.next_run_at).toISOString(),
52
+ ...(row.remaining_occurrences === null ? {} : { remainingOccurrences: row.remaining_occurrences }),
52
53
  }));
53
54
  }
54
55
  enqueueEvent(trigger, input, now) {
@@ -22,6 +22,7 @@ export interface DueCronTrigger extends StoredTriggerBase {
22
22
  cron: string;
23
23
  timezone: string;
24
24
  nextRunAt: string;
25
+ remainingOccurrences?: number;
25
26
  }
26
27
  export type RunInput = {
27
28
  kind: 'event';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@amalgm/automations",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "description": "Amalgm's cloud automation SDK: Supabase-backed configuration, trigger admission, and run delivery.",
5
5
  "license": "UNLICENSED",
6
6
  "repository": {
@@ -26,14 +26,19 @@
26
26
  "./mcp": {
27
27
  "types": "./dist/src/mcp.d.ts",
28
28
  "import": "./dist/src/mcp.js"
29
+ },
30
+ "./host": {
31
+ "types": "./dist/host/index.d.ts",
32
+ "import": "./dist/host/index.js"
29
33
  }
30
34
  },
31
35
  "bin": {
32
- "amalgm-automations": "./dist/src/cli-main.js",
33
- "amalgm-automations-mcp": "./dist/src/mcp-main.js"
36
+ "amalgm-automations": "dist/src/cli-main.js",
37
+ "amalgm-automations-mcp": "dist/src/mcp-main.js"
34
38
  },
35
39
  "files": [
36
40
  "dist",
41
+ "skills",
37
42
  "supabase",
38
43
  "AXIOMS.md",
39
44
  "PURPOSE.md",
@@ -46,14 +51,18 @@
46
51
  "test:supabase": "tsx --test test/integration/postgres.test.ts",
47
52
  "verify": "npm run check && npm test && npm run build",
48
53
  "release:check": "npm run verify && npm run test:supabase",
49
- "prepack": "npm run build"
54
+ "prepack": "npm run build",
55
+ "start": "node dist/host/main.js"
50
56
  },
51
57
  "engines": {
52
58
  "node": ">=20"
53
59
  },
54
60
  "dependencies": {
61
+ "@amalgm/core": "0.4.2",
55
62
  "@modelcontextprotocol/sdk": "^1.30.0",
63
+ "@supabase/supabase-js": "2.57.4",
56
64
  "cron-parser": "^5.4.0",
65
+ "jose": "^6.1.0",
57
66
  "zod": "^4.4.3"
58
67
  },
59
68
  "devDependencies": {