@amalgm/automations 0.2.0 → 0.2.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 CHANGED
@@ -10,42 +10,47 @@
10
10
  key authentication all resolve to the same principal shape.
11
11
  4. An automation belongs to exactly one user and exactly one target. It may
12
12
  have zero or more triggers and zero or one workflow.
13
- 5. Scheduled triggers and webhook triggers are distinct first-class resources
13
+ 5. A principal bound to exactly one target supplies that target implicitly on
14
+ create. Unbound or multi-target principals must choose explicitly; adapters
15
+ and models never guess opaque target ids.
16
+ 6. Scheduled triggers and webhook triggers are distinct first-class resources
14
17
  with their own CRUD operations and validation rules.
15
- 6. A workflow is the one script resource owned by its automation. It may be
18
+ 7. A workflow is the one script resource owned by its automation. It may be
16
19
  created, read, updated, or deleted independently of the automation and its
17
20
  triggers.
18
- 7. An incomplete automation is valid configuration. Future execution behavior
21
+ 8. An incomplete automation is valid configuration. Future execution behavior
19
22
  must be derived from its persisted configuration rather than repaired by the
20
23
  CRUD layer.
21
- 8. Supabase is authoritative for automations, triggers, workflow source, and
24
+ 9. Supabase is authoritative for automations, triggers, workflow source, and
22
25
  permanent run history. A definition edit or deletion never rewrites prior
23
26
  runs.
24
- 9. Webhook secrets are persisted but write-only: normal reads reveal only that
27
+ 10. Webhook secrets are persisted but write-only: normal reads reveal only that
25
28
  a secret is configured.
26
- 10. Run history is read-only in the control plane and always scoped to its
29
+ 11. Run history is read-only in the control plane and always scoped to its
27
30
  automation's owner.
28
- 11. Storage records, Supabase RPCs, HTTP details, and MCP protocol details are
31
+ 12. Storage records, Supabase RPCs, HTTP details, and MCP protocol details are
29
32
  implementation concerns, not SDK concepts.
30
- 12. The control-plane SDK is the only configuration write path. The delivery
33
+ 13. The control-plane SDK is the only configuration write path. The delivery
31
34
  rail reads that configuration and writes only schedule clocks and run
32
35
  state; it never owns a second automation-definition API.
33
- 13. Admitting a run atomically verifies the current enabled configuration,
36
+ 14. Admitting a run atomically verifies the current enabled configuration,
34
37
  stores a secret-free snapshot, and, for a schedule, advances exactly the
35
38
  firing instant that was claimed.
36
- 14. Legacy local automation storage is not a compatibility authority. Engine
39
+ 15. Legacy local automation storage is not a compatibility authority. Engine
37
40
  cutover migrates callers to this SDK and then deletes the old store.
38
- 15. Event ingress returns success only after every admitted run is durable in
41
+ 16. Event ingress returns success only after every admitted run is durable in
39
42
  Supabase. Its recent-event list is a bounded, secret-free operational view,
40
43
  never a second event or run authority.
41
- 16. A finite schedule decrements its durable remaining occurrence count in the
44
+ 17. A finite schedule decrements its durable remaining occurrence count in the
42
45
  same transaction that admits a run; zero disables the trigger.
43
- 17. A machine receives work only by exclusively leasing runs whose persisted
46
+ 18. A machine receives work only by exclusively leasing runs whose persisted
44
47
  target equals the `computer_id` in its DPoP-bound access token.
45
- 18. Machine execution consumes the immutable workflow snapshot stored on the
48
+ 19. The configured public origin, never forwarding headers, reconstructs the
49
+ DPoP request URL behind the Fly proxy.
50
+ 20. Machine execution consumes the immutable workflow snapshot stored on the
46
51
  run. It never rediscovers or silently updates the automation definition.
47
- 19. A compiled workflow is a small declarative sequence of tool actions. The
52
+ 21. A compiled workflow is a small declarative sequence of tool actions. The
48
53
  executor receives tool calling as a host capability and never embeds a
49
54
  Channels, Shell, CLI, or provider special case.
50
- 20. Automations is a standalone hosted service. Gateway owns none of its API,
55
+ 22. Automations is a standalone hosted service. Gateway owns none of its API,
51
56
  scheduling, claim, execution, or persistence path.
package/PURPOSE.md CHANGED
@@ -39,6 +39,9 @@ recent-event response is an ephemeral, secret-free operational projection.
39
39
 
40
40
  Amalgm supplies a resolved authenticated principal from its user session or
41
41
  HMAC-refresh flow; future API keys resolve to the same principal capability.
42
+ When that principal is bound to exactly one machine, automation creation uses
43
+ that target without asking a human or model to copy an opaque machine id. An
44
+ explicit target remains mandatory for unbound or multi-target principals.
42
45
  Core provides authenticated user identity, opaque machine identity,
43
46
  connectivity, and narrow host capabilities. The product receives identity and
44
47
  scopes — never raw credentials or Core storage — and neither side reaches into
@@ -1,5 +1,6 @@
1
1
  export interface AutomationsHostConfig {
2
2
  readonly port: number;
3
+ readonly publicOrigin: string;
3
4
  readonly supabaseUrl: string;
4
5
  readonly supabaseServiceRoleKey: string;
5
6
  readonly authorizationIssuer: string;
@@ -1,6 +1,7 @@
1
1
  export function automationsHostConfig(env = process.env) {
2
2
  return Object.freeze({
3
3
  port: integer(env.PORT, 8080, 1, 65_535),
4
+ publicOrigin: url(env.AMALGAM_PUBLIC_ORIGIN, 'AMALGAM_PUBLIC_ORIGIN'),
4
5
  supabaseUrl: url(env.SUPABASE_URL ?? env.NEXT_PUBLIC_SUPABASE_URL, 'SUPABASE_URL'),
5
6
  supabaseServiceRoleKey: required(env.SUPABASE_SERVICE_ROLE_KEY, 'SUPABASE_SERVICE_ROLE_KEY'),
6
7
  authorizationIssuer: url(env.AMALGM_AUTHORIZATION_ISSUER, 'AMALGM_AUTHORIZATION_ISSUER'),
package/dist/host/main.js CHANGED
@@ -32,6 +32,7 @@ const machineApi = createMachineRunsApi({
32
32
  runsFor: (principal) => createMachineRuns(machineRepository, principal),
33
33
  });
34
34
  const host = createAutomationsHost({
35
+ publicOrigin: config.publicOrigin,
35
36
  controlApi,
36
37
  machineApi,
37
38
  fireSchedules: () => delivery.fireDueCrons(),
@@ -1,5 +1,6 @@
1
1
  import { type IncomingMessage, type ServerResponse } from 'node:http';
2
2
  export declare function createAutomationsHost(options: {
3
+ readonly publicOrigin: string;
3
4
  readonly controlApi: (request: Request) => Promise<Response>;
4
5
  readonly machineApi: (request: Request) => Promise<Response>;
5
6
  readonly fireSchedules: () => Promise<unknown>;
@@ -1,4 +1,5 @@
1
1
  import { createServer } from 'node:http';
2
+ import { publicResourceRequestUrl } from '@amalgm/core/authorization';
2
3
  export function createAutomationsHost(options) {
3
4
  const log = options.log ?? (() => { });
4
5
  let scheduling = null;
@@ -16,7 +17,7 @@ export function createAutomationsHost(options) {
16
17
  try {
17
18
  if (incoming.url === '/healthz')
18
19
  return send(outgoing, Response.json({ ok: true }));
19
- const request = await webRequest(incoming);
20
+ const request = await webRequest(incoming, options.publicOrigin);
20
21
  const api = new URL(request.url).pathname.startsWith('/v1/machine/')
21
22
  ? options.machineApi : options.controlApi;
22
23
  await send(outgoing, await api(request));
@@ -35,12 +36,12 @@ export function createAutomationsHost(options) {
35
36
  },
36
37
  };
37
38
  }
38
- async function webRequest(request) {
39
+ async function webRequest(request, publicOrigin) {
39
40
  const chunks = [];
40
41
  for await (const chunk of request)
41
42
  chunks.push(Buffer.from(chunk));
42
43
  const body = Buffer.concat(chunks);
43
- return new Request(new URL(request.url ?? '/', `http://${request.headers.host ?? '127.0.0.1'}`), {
44
+ return new Request(publicResourceRequestUrl(publicOrigin, request.url ?? '/'), {
44
45
  method: request.method ?? 'GET',
45
46
  headers: request.headers,
46
47
  ...(body.length ? { body } : {}),
@@ -31,7 +31,8 @@ export interface Automation {
31
31
  }
32
32
  export interface CreateAutomation {
33
33
  id?: string;
34
- targetId: string;
34
+ /** Omit when the authenticated principal resolves exactly one target. */
35
+ targetId?: string;
35
36
  name?: string;
36
37
  description?: string;
37
38
  enabled?: boolean;
@@ -1,20 +1,20 @@
1
1
  import { ConflictError, NotFoundError } from '../errors.js';
2
2
  import { parseCreateAutomation, parseListAutomations, parseUpdateAutomation } from '../schema.js';
3
3
  import { id, newId, page } from '../validation.js';
4
- import { assertTarget } from './context.js';
4
+ import { assertTarget, resolveTarget } from './context.js';
5
5
  export function automationOperations(context) {
6
6
  const { principal, repository } = context;
7
7
  return {
8
8
  create: async (input) => {
9
9
  context.write();
10
10
  const parsed = parseCreateAutomation(input);
11
- assertTarget(principal, parsed.targetId);
11
+ const targetId = resolveTarget(principal, parsed.targetId);
12
12
  if (parsed.id && await repository.getAutomation(principal.userId, id(parsed.id, 'Automation id'))) {
13
13
  throw new ConflictError('Automation');
14
14
  }
15
15
  return repository.createAutomation(principal.userId, {
16
16
  id: parsed.id ? id(parsed.id, 'Automation id') : newId('automation'),
17
- targetId: parsed.targetId,
17
+ targetId,
18
18
  name: parsed.name || '',
19
19
  description: parsed.description || '',
20
20
  enabled: parsed.enabled !== false,
@@ -11,3 +11,4 @@ export interface CrudContext {
11
11
  }
12
12
  export declare function createContext(repository: AutomationCrudRepository, principal: AutomationPrincipal, clock: () => Date): CrudContext;
13
13
  export declare function assertTarget(principal: AutomationPrincipal, targetId: string): void;
14
+ export declare function resolveTarget(principal: AutomationPrincipal, targetId?: string): string;
@@ -1,4 +1,4 @@
1
- import { ForbiddenError, NotFoundError } from '../errors.js';
1
+ import { ForbiddenError, NotFoundError, ValidationError } from '../errors.js';
2
2
  import { id, requiredText } from '../validation.js';
3
3
  export function createContext(repository, principal, clock) {
4
4
  assertPrincipal(principal);
@@ -21,6 +21,15 @@ export function assertTarget(principal, targetId) {
21
21
  throw new ForbiddenError('This credential cannot access that target');
22
22
  }
23
23
  }
24
+ export function resolveTarget(principal, targetId) {
25
+ if (targetId) {
26
+ assertTarget(principal, targetId);
27
+ return id(targetId, 'targetId');
28
+ }
29
+ if (principal.targetIds?.length === 1)
30
+ return principal.targetIds[0];
31
+ throw new ValidationError('targetId is required unless the credential resolves exactly one target');
32
+ }
24
33
  function assertPrincipal(principal) {
25
34
  requiredText(principal.userId, 'Authenticated user id');
26
35
  if (!Array.isArray(principal.scopes))
package/dist/src/mcp.js CHANGED
@@ -7,7 +7,7 @@ const triggerIdInput = { ...automationIdInput, trigger_id: identifierSchema };
7
7
  const runIdInput = { ...automationIdInput, run_id: identifierSchema };
8
8
  export function createAutomationMcpServer(sdk) {
9
9
  const server = new McpServer({ name: 'amalgm-automations-mcp-server', version: '0.1.0' });
10
- register(server, 'amalgm_automations_create', 'Create automation', 'Create an automation configuration without triggers or workflow.', { input: createAutomationSchema }, write(false), ({ input }) => sdk.automations.create(input));
10
+ register(server, 'amalgm_automations_create', 'Create automation', 'Create an automation configuration without triggers or workflow. In an authenticated machine session, omit input.targetId so the service uses that machine; never guess a target id.', { input: createAutomationSchema }, write(false), ({ input }) => sdk.automations.create(input));
11
11
  register(server, 'amalgm_automations_list', 'List automations', 'List the caller\'s automations with optional target and enabled filters.', { query: listAutomationsSchema.optional() }, read(), ({ query }) => sdk.automations.list(query));
12
12
  register(server, 'amalgm_automations_get', 'Get automation', 'Get one automation by id.', automationIdInput, read(), ({ automation_id }) => sdk.automations.get(automation_id));
13
13
  register(server, 'amalgm_automations_update', 'Update automation', 'Update automation metadata, target, or enabled state.', { automation_id: identifierSchema, patch: updateAutomationSchema }, write(true), ({ automation_id, patch }) => sdk.automations.update(automation_id, patch));
@@ -13,22 +13,22 @@ export declare const pageQuerySchema: z.ZodObject<{
13
13
  }>;
14
14
  export declare const createAutomationSchema: z.ZodObject<{
15
15
  id: z.ZodOptional<z.ZodString>;
16
- targetId: z.ZodString;
16
+ targetId: z.ZodOptional<z.ZodString>;
17
17
  name: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
18
18
  description: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
19
19
  enabled: z.ZodOptional<z.ZodBoolean>;
20
20
  }, "strict", z.ZodTypeAny, {
21
- id?: string | undefined;
22
- targetId: string;
21
+ targetId?: string | undefined;
22
+ enabled?: boolean | undefined;
23
23
  name?: string | undefined;
24
+ id?: string | undefined;
24
25
  description?: string | undefined;
25
- enabled?: boolean | undefined;
26
26
  }, {
27
- id?: string | undefined;
28
- targetId: string;
27
+ targetId?: string | undefined;
28
+ enabled?: boolean | undefined;
29
29
  name?: string | undefined;
30
+ id?: string | undefined;
30
31
  description?: string | undefined;
31
- enabled?: boolean | undefined;
32
32
  }>;
33
33
  export declare const updateAutomationSchema: z.ZodEffects<z.ZodObject<{
34
34
  targetId: z.ZodOptional<z.ZodString>;
@@ -37,24 +37,24 @@ export declare const updateAutomationSchema: z.ZodEffects<z.ZodObject<{
37
37
  enabled: z.ZodOptional<z.ZodBoolean>;
38
38
  }, "strict", z.ZodTypeAny, {
39
39
  targetId?: string | undefined;
40
+ enabled?: boolean | undefined;
40
41
  name?: string | null | undefined;
41
42
  description?: string | null | undefined;
42
- enabled?: boolean | undefined;
43
43
  }, {
44
44
  targetId?: string | undefined;
45
+ enabled?: boolean | undefined;
45
46
  name?: string | null | undefined;
46
47
  description?: string | null | undefined;
47
- enabled?: boolean | undefined;
48
48
  }>, {
49
49
  targetId?: string | undefined;
50
+ enabled?: boolean | undefined;
50
51
  name?: string | null | undefined;
51
52
  description?: string | null | undefined;
52
- enabled?: boolean | undefined;
53
53
  }, {
54
54
  targetId?: string | undefined;
55
+ enabled?: boolean | undefined;
55
56
  name?: string | null | undefined;
56
57
  description?: string | null | undefined;
57
- enabled?: boolean | undefined;
58
58
  }>;
59
59
  export declare const listAutomationsSchema: z.ZodObject<{
60
60
  limit: z.ZodOptional<z.ZodNumber>;
@@ -63,15 +63,15 @@ export declare const listAutomationsSchema: z.ZodObject<{
63
63
  targetId: z.ZodOptional<z.ZodString>;
64
64
  enabled: z.ZodOptional<z.ZodBoolean>;
65
65
  }, "strict", z.ZodTypeAny, {
66
- limit?: number | undefined;
67
- offset?: number | undefined;
68
66
  targetId?: string | undefined;
69
67
  enabled?: boolean | undefined;
70
- }, {
71
68
  limit?: number | undefined;
72
69
  offset?: number | undefined;
70
+ }, {
73
71
  targetId?: string | undefined;
74
72
  enabled?: boolean | undefined;
73
+ limit?: number | undefined;
74
+ offset?: number | undefined;
75
75
  }>;
76
76
  export declare const createScheduleTriggerSchema: z.ZodObject<{
77
77
  id: z.ZodOptional<z.ZodString>;
@@ -80,16 +80,16 @@ export declare const createScheduleTriggerSchema: z.ZodObject<{
80
80
  enabled: z.ZodOptional<z.ZodBoolean>;
81
81
  maxOccurrences: z.ZodOptional<z.ZodNumber>;
82
82
  }, "strict", z.ZodTypeAny, {
83
- id?: string | undefined;
84
83
  cron: string;
85
- timezone?: string | undefined;
86
84
  enabled?: boolean | undefined;
85
+ id?: string | undefined;
86
+ timezone?: string | undefined;
87
87
  maxOccurrences?: number | undefined;
88
88
  }, {
89
- id?: string | undefined;
90
89
  cron: string;
91
- timezone?: string | undefined;
92
90
  enabled?: boolean | undefined;
91
+ id?: string | undefined;
92
+ timezone?: string | undefined;
93
93
  maxOccurrences?: number | undefined;
94
94
  }>;
95
95
  export declare const updateScheduleTriggerSchema: z.ZodEffects<z.ZodObject<{
@@ -98,24 +98,24 @@ export declare const updateScheduleTriggerSchema: z.ZodEffects<z.ZodObject<{
98
98
  enabled: z.ZodOptional<z.ZodBoolean>;
99
99
  maxOccurrences: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
100
100
  }, "strict", z.ZodTypeAny, {
101
+ enabled?: boolean | undefined;
101
102
  cron?: string | undefined;
102
103
  timezone?: string | undefined;
103
- enabled?: boolean | undefined;
104
104
  maxOccurrences?: number | null | undefined;
105
105
  }, {
106
+ enabled?: boolean | undefined;
106
107
  cron?: string | undefined;
107
108
  timezone?: string | undefined;
108
- enabled?: boolean | undefined;
109
109
  maxOccurrences?: number | null | undefined;
110
110
  }>, {
111
+ enabled?: boolean | undefined;
111
112
  cron?: string | undefined;
112
113
  timezone?: string | undefined;
113
- enabled?: boolean | undefined;
114
114
  maxOccurrences?: number | null | undefined;
115
115
  }, {
116
+ enabled?: boolean | undefined;
116
117
  cron?: string | undefined;
117
118
  timezone?: string | undefined;
118
- enabled?: boolean | undefined;
119
119
  maxOccurrences?: number | null | undefined;
120
120
  }>;
121
121
  export declare const createWebhookTriggerSchema: z.ZodObject<{
@@ -125,17 +125,17 @@ export declare const createWebhookTriggerSchema: z.ZodObject<{
125
125
  secret: z.ZodString;
126
126
  enabled: z.ZodOptional<z.ZodBoolean>;
127
127
  }, "strict", z.ZodTypeAny, {
128
- id?: string | undefined;
129
- source?: string | undefined;
130
- event?: string | undefined;
131
128
  secret: string;
132
129
  enabled?: boolean | undefined;
133
- }, {
134
- id?: string | undefined;
135
- source?: string | undefined;
136
130
  event?: string | undefined;
131
+ source?: string | undefined;
132
+ id?: string | undefined;
133
+ }, {
137
134
  secret: string;
138
135
  enabled?: boolean | undefined;
136
+ event?: string | undefined;
137
+ source?: string | undefined;
138
+ id?: string | undefined;
139
139
  }>;
140
140
  export declare const updateWebhookTriggerSchema: z.ZodEffects<z.ZodObject<{
141
141
  source: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
@@ -143,25 +143,25 @@ export declare const updateWebhookTriggerSchema: z.ZodEffects<z.ZodObject<{
143
143
  secret: z.ZodOptional<z.ZodString>;
144
144
  enabled: z.ZodOptional<z.ZodBoolean>;
145
145
  }, "strict", z.ZodTypeAny, {
146
- source?: string | undefined;
146
+ enabled?: boolean | undefined;
147
147
  event?: string | undefined;
148
148
  secret?: string | undefined;
149
- enabled?: boolean | undefined;
150
- }, {
151
149
  source?: string | undefined;
150
+ }, {
151
+ enabled?: boolean | undefined;
152
152
  event?: string | undefined;
153
153
  secret?: string | undefined;
154
- enabled?: boolean | undefined;
155
- }>, {
156
154
  source?: string | undefined;
155
+ }>, {
156
+ enabled?: boolean | undefined;
157
157
  event?: string | undefined;
158
158
  secret?: string | undefined;
159
- enabled?: boolean | undefined;
160
- }, {
161
159
  source?: string | undefined;
160
+ }, {
161
+ enabled?: boolean | undefined;
162
162
  event?: string | undefined;
163
163
  secret?: string | undefined;
164
- enabled?: boolean | undefined;
164
+ source?: string | undefined;
165
165
  }>;
166
166
  export declare const createWorkflowSchema: z.ZodObject<{
167
167
  id: z.ZodOptional<z.ZodString>;
@@ -171,16 +171,16 @@ export declare const createWorkflowSchema: z.ZodObject<{
171
171
  allowlist: z.ZodOptional<z.ZodType<Json, z.ZodTypeDef, Json>>;
172
172
  limits: z.ZodOptional<z.ZodType<Json, z.ZodTypeDef, Json>>;
173
173
  }, "strict", z.ZodTypeAny, {
174
- id?: string | undefined;
175
- name?: string | undefined;
176
174
  script: string;
175
+ name?: string | undefined;
176
+ id?: string | undefined;
177
177
  compiled?: Json | undefined;
178
178
  allowlist?: Json | undefined;
179
179
  limits?: Json | undefined;
180
180
  }, {
181
- id?: string | undefined;
182
- name?: string | undefined;
183
181
  script: string;
182
+ name?: string | undefined;
183
+ id?: string | undefined;
184
184
  compiled?: Json | undefined;
185
185
  allowlist?: Json | undefined;
186
186
  limits?: Json | undefined;
@@ -222,13 +222,13 @@ export declare const listRunsSchema: z.ZodObject<{
222
222
  } & {
223
223
  status: z.ZodOptional<z.ZodEnum<["pending", "sent", "running", "completed", "failed"]>>;
224
224
  }, "strict", z.ZodTypeAny, {
225
+ status?: "pending" | "sent" | "running" | "completed" | "failed" | undefined;
225
226
  limit?: number | undefined;
226
227
  offset?: number | undefined;
227
- status?: "completed" | "failed" | "pending" | "running" | "sent" | undefined;
228
228
  }, {
229
+ status?: "pending" | "sent" | "running" | "completed" | "failed" | undefined;
229
230
  limit?: number | undefined;
230
231
  offset?: number | undefined;
231
- status?: "completed" | "failed" | "pending" | "running" | "sent" | undefined;
232
232
  }>;
233
233
  export declare function parseCreateAutomation(value: unknown): CreateAutomation;
234
234
  export declare function parseUpdateAutomation(value: unknown): UpdateAutomation;
@@ -21,7 +21,7 @@ const jsonSchema = z.lazy(() => z.union([
21
21
  ]));
22
22
  export const createAutomationSchema = z.object({
23
23
  id: identifierSchema.optional(),
24
- targetId: identifierSchema,
24
+ targetId: identifierSchema.optional(),
25
25
  name: text(500, 'Automation name').optional(),
26
26
  description: text(10_000, 'Automation description').optional(),
27
27
  enabled: z.boolean().optional(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@amalgm/automations",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Amalgm's cloud automation SDK: Supabase-backed configuration, trigger admission, and run delivery.",
5
5
  "license": "UNLICENSED",
6
6
  "repository": {
@@ -58,7 +58,7 @@
58
58
  "node": ">=20"
59
59
  },
60
60
  "dependencies": {
61
- "@amalgm/core": "0.4.2",
61
+ "@amalgm/core": "0.4.4",
62
62
  "@modelcontextprotocol/sdk": "^1.30.0",
63
63
  "@supabase/supabase-js": "2.57.4",
64
64
  "cron-parser": "^5.4.0",
@@ -70,6 +70,6 @@
70
70
  "@types/pg": "^8.20.1",
71
71
  "pg": "^8.16.3",
72
72
  "tsx": "^4.23.1",
73
- "typescript": "^7.0.2"
73
+ "typescript": "5.9.3"
74
74
  }
75
75
  }