@synapsor/runner 0.1.0 → 0.1.2

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 (33) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/README.md +750 -245
  3. package/dist/cli.d.ts.map +1 -1
  4. package/dist/runner.mjs +1621 -163
  5. package/docs/README.md +85 -47
  6. package/docs/conformance.md +91 -0
  7. package/docs/migrating-to-synapsor-spec.md +191 -0
  8. package/docs/production.md +289 -0
  9. package/docs/release-notes.md +32 -0
  10. package/examples/app-owned-writeback/command-handler.mjs +0 -0
  11. package/examples/claude-desktop-postgres/Makefile +6 -0
  12. package/examples/claude-desktop-postgres/README.md +40 -0
  13. package/examples/cursor-postgres/Makefile +6 -0
  14. package/examples/cursor-postgres/README.md +30 -0
  15. package/examples/mcp-postgres-billing-app-handler/scripts/run-demo.sh +0 -0
  16. package/examples/mysql-refund-agent/Makefile +4 -0
  17. package/examples/mysql-refund-agent/README.md +36 -0
  18. package/examples/openai-agents-http/Makefile +6 -0
  19. package/examples/openai-agents-http/README.md +14 -0
  20. package/examples/openai-agents-stdio/Makefile +6 -0
  21. package/examples/openai-agents-stdio/README.md +14 -0
  22. package/examples/raw-sql-vs-synapsor/Makefile +11 -0
  23. package/examples/raw-sql-vs-synapsor/README.md +41 -0
  24. package/examples/reference-support-billing-app/scripts/run-demo.sh +0 -0
  25. package/examples/support-billing-agent/Makefile +19 -0
  26. package/examples/support-billing-agent/README.md +89 -0
  27. package/examples/support-billing-agent/app/README.md +13 -0
  28. package/examples/support-billing-agent/db/schema.sql +91 -0
  29. package/examples/support-billing-agent/db/seed.sql +43 -0
  30. package/examples/support-billing-agent/docker-compose.yml +13 -0
  31. package/examples/support-billing-agent/scripts/run-demo.sh +15 -0
  32. package/examples/support-billing-agent/synapsor.runner.json +233 -0
  33. package/package.json +27 -10
@@ -0,0 +1,13 @@
1
+ # App Surface
2
+
3
+ This folder marks the customer application boundary for the flagship demo.
4
+
5
+ The model talks to Synapsor Runner MCP tools. It does not receive application
6
+ write credentials or direct SQL authority. After approval, the trusted runner
7
+ uses the reviewed `synapsor.runner.json` contract to apply the one-row
8
+ writeback with tenant, primary-key, allowed-column, idempotency, and
9
+ `updated_at` conflict guards.
10
+
11
+ For richer app actions, such as inserting a credit ledger row and emitting an
12
+ event, use the app-owned handler examples in `../app-owned-writeback/` and
13
+ `../mcp-postgres-billing-app-handler/`.
@@ -0,0 +1,91 @@
1
+ CREATE TABLE IF NOT EXISTS public.tenants (
2
+ id text PRIMARY KEY,
3
+ name text NOT NULL,
4
+ created_at timestamptz NOT NULL DEFAULT now(),
5
+ updated_at timestamptz NOT NULL DEFAULT now()
6
+ );
7
+
8
+ CREATE TABLE IF NOT EXISTS public.customers (
9
+ id text PRIMARY KEY,
10
+ tenant_id text NOT NULL REFERENCES public.tenants(id),
11
+ name text NOT NULL,
12
+ email text,
13
+ plan text NOT NULL,
14
+ plan_credit_cents integer NOT NULL DEFAULT 0,
15
+ credit_reason text,
16
+ created_at timestamptz NOT NULL DEFAULT now(),
17
+ updated_at timestamptz NOT NULL DEFAULT now()
18
+ );
19
+
20
+ CREATE TABLE IF NOT EXISTS public.support_tickets (
21
+ id text PRIMARY KEY,
22
+ tenant_id text NOT NULL REFERENCES public.tenants(id),
23
+ customer_id text NOT NULL REFERENCES public.customers(id),
24
+ subject text NOT NULL,
25
+ status text NOT NULL,
26
+ resolution_note text,
27
+ updated_at timestamptz NOT NULL DEFAULT now()
28
+ );
29
+
30
+ CREATE TABLE IF NOT EXISTS public.invoices (
31
+ id text PRIMARY KEY,
32
+ tenant_id text NOT NULL REFERENCES public.tenants(id),
33
+ customer_id text NOT NULL REFERENCES public.customers(id),
34
+ status text NOT NULL,
35
+ balance_cents integer NOT NULL,
36
+ late_fee_cents integer NOT NULL,
37
+ waiver_reason text,
38
+ updated_at timestamptz NOT NULL DEFAULT now()
39
+ );
40
+
41
+ CREATE TABLE IF NOT EXISTS public.credits (
42
+ id text PRIMARY KEY,
43
+ tenant_id text NOT NULL REFERENCES public.tenants(id),
44
+ customer_id text NOT NULL REFERENCES public.customers(id),
45
+ invoice_id text REFERENCES public.invoices(id),
46
+ amount_cents integer NOT NULL,
47
+ reason text NOT NULL,
48
+ status text NOT NULL,
49
+ created_at timestamptz NOT NULL DEFAULT now(),
50
+ updated_at timestamptz NOT NULL DEFAULT now()
51
+ );
52
+
53
+ CREATE TABLE IF NOT EXISTS public.agent_actions (
54
+ id text PRIMARY KEY,
55
+ tenant_id text NOT NULL REFERENCES public.tenants(id),
56
+ action_type text NOT NULL,
57
+ target_type text NOT NULL,
58
+ target_id text NOT NULL,
59
+ proposal_id text,
60
+ status text NOT NULL,
61
+ created_at timestamptz NOT NULL DEFAULT now()
62
+ );
63
+
64
+ CREATE TABLE IF NOT EXISTS public.orders (
65
+ id text PRIMARY KEY,
66
+ tenant_id text NOT NULL REFERENCES public.tenants(id),
67
+ customer_id text NOT NULL REFERENCES public.customers(id),
68
+ status text NOT NULL,
69
+ status_change_reason text,
70
+ updated_at timestamptz NOT NULL DEFAULT now()
71
+ );
72
+
73
+ DO $$
74
+ BEGIN
75
+ IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'synapsor_reader') THEN
76
+ CREATE ROLE synapsor_reader LOGIN PASSWORD 'synapsor_reader_password';
77
+ END IF;
78
+ IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'synapsor_writer') THEN
79
+ CREATE ROLE synapsor_writer LOGIN PASSWORD 'synapsor_writer_password';
80
+ END IF;
81
+ END
82
+ $$;
83
+
84
+ GRANT CONNECT ON DATABASE synapsor_support_billing_agent TO synapsor_reader, synapsor_writer;
85
+ GRANT USAGE ON SCHEMA public TO synapsor_reader, synapsor_writer;
86
+ GRANT CREATE ON SCHEMA public TO synapsor_writer;
87
+ GRANT SELECT ON public.tenants, public.customers, public.support_tickets, public.invoices, public.credits, public.agent_actions, public.orders TO synapsor_reader, synapsor_writer;
88
+ GRANT UPDATE (plan_credit_cents, credit_reason, updated_at) ON public.customers TO synapsor_writer;
89
+ GRANT UPDATE (status, resolution_note, updated_at) ON public.support_tickets TO synapsor_writer;
90
+ GRANT UPDATE (late_fee_cents, waiver_reason, updated_at) ON public.invoices TO synapsor_writer;
91
+ GRANT UPDATE (status, status_change_reason, updated_at) ON public.orders TO synapsor_writer;
@@ -0,0 +1,43 @@
1
+ INSERT INTO public.tenants (id, name, created_at, updated_at)
2
+ VALUES
3
+ ('acme', 'Acme Robotics', '2026-06-20T10:00:00Z', '2026-06-20T10:00:00Z'),
4
+ ('otherco', 'OtherCo Labs', '2026-06-20T10:00:00Z', '2026-06-20T10:00:00Z')
5
+ ON CONFLICT (id) DO UPDATE SET name = EXCLUDED.name, updated_at = EXCLUDED.updated_at;
6
+
7
+ INSERT INTO public.customers (id, tenant_id, name, email, plan, plan_credit_cents, credit_reason, created_at, updated_at)
8
+ VALUES
9
+ ('cust_acme_1', 'acme', 'Acme Robotics', 'ops@example.invalid', 'enterprise', 0, NULL, '2026-06-20T10:00:00Z', '2026-06-20T10:00:00Z'),
10
+ ('cust_acme_2', 'acme', 'Acme Field Ops', 'field@example.invalid', 'builder', 0, NULL, '2026-06-20T10:00:00Z', '2026-06-20T10:00:00Z'),
11
+ ('cust_other_1', 'otherco', 'OtherCo Labs', 'ops@otherco.invalid', 'builder', 0, NULL, '2026-06-20T10:00:00Z', '2026-06-20T10:00:00Z')
12
+ ON CONFLICT (id) DO UPDATE SET tenant_id = EXCLUDED.tenant_id, name = EXCLUDED.name, email = EXCLUDED.email, plan = EXCLUDED.plan, plan_credit_cents = EXCLUDED.plan_credit_cents, credit_reason = EXCLUDED.credit_reason, updated_at = EXCLUDED.updated_at;
13
+
14
+ INSERT INTO public.support_tickets (id, tenant_id, customer_id, subject, status, resolution_note, updated_at)
15
+ VALUES
16
+ ('T-1042', 'acme', 'cust_acme_1', 'Late fee waiver request for INV-3001', 'open', NULL, '2026-06-20T12:00:00Z'),
17
+ ('T-1043', 'acme', 'cust_acme_2', 'Duplicate card charge question', 'open', NULL, '2026-06-20T12:05:00Z'),
18
+ ('T-9001', 'otherco', 'cust_other_1', 'OtherCo private billing ticket', 'open', NULL, '2026-06-20T12:00:00Z')
19
+ ON CONFLICT (id) DO UPDATE SET tenant_id = EXCLUDED.tenant_id, customer_id = EXCLUDED.customer_id, subject = EXCLUDED.subject, status = EXCLUDED.status, resolution_note = EXCLUDED.resolution_note, updated_at = EXCLUDED.updated_at;
20
+
21
+ INSERT INTO public.invoices (id, tenant_id, customer_id, status, balance_cents, late_fee_cents, waiver_reason, updated_at)
22
+ VALUES
23
+ ('INV-3001', 'acme', 'cust_acme_1', 'overdue', 25500, 5500, NULL, '2026-06-20T14:31:08Z'),
24
+ ('INV-3002', 'acme', 'cust_acme_2', 'paid', 0, 0, NULL, '2026-06-20T14:40:00Z'),
25
+ ('INV-9001', 'otherco', 'cust_other_1', 'overdue', 25500, 5500, NULL, '2026-06-20T14:31:08Z')
26
+ ON CONFLICT (id) DO UPDATE SET tenant_id = EXCLUDED.tenant_id, customer_id = EXCLUDED.customer_id, status = EXCLUDED.status, balance_cents = EXCLUDED.balance_cents, late_fee_cents = EXCLUDED.late_fee_cents, waiver_reason = EXCLUDED.waiver_reason, updated_at = EXCLUDED.updated_at;
27
+
28
+ INSERT INTO public.credits (id, tenant_id, customer_id, invoice_id, amount_cents, reason, status, created_at, updated_at)
29
+ VALUES
30
+ ('CR-1001', 'acme', 'cust_acme_1', 'INV-3001', 1000, 'Seeded goodwill credit for review flow', 'draft', '2026-06-20T15:00:00Z', '2026-06-20T15:00:00Z')
31
+ ON CONFLICT (id) DO UPDATE SET tenant_id = EXCLUDED.tenant_id, customer_id = EXCLUDED.customer_id, invoice_id = EXCLUDED.invoice_id, amount_cents = EXCLUDED.amount_cents, reason = EXCLUDED.reason, status = EXCLUDED.status, updated_at = EXCLUDED.updated_at;
32
+
33
+ INSERT INTO public.agent_actions (id, tenant_id, action_type, target_type, target_id, proposal_id, status, created_at)
34
+ VALUES
35
+ ('ACT-1001', 'acme', 'late_fee_review', 'invoice', 'INV-3001', NULL, 'seeded', '2026-06-20T15:05:00Z')
36
+ ON CONFLICT (id) DO UPDATE SET tenant_id = EXCLUDED.tenant_id, action_type = EXCLUDED.action_type, target_type = EXCLUDED.target_type, target_id = EXCLUDED.target_id, proposal_id = EXCLUDED.proposal_id, status = EXCLUDED.status, created_at = EXCLUDED.created_at;
37
+
38
+ INSERT INTO public.orders (id, tenant_id, customer_id, status, status_change_reason, updated_at)
39
+ VALUES
40
+ ('O-1001', 'acme', 'cust_acme_1', 'paid', NULL, '2026-06-20T13:00:00Z'),
41
+ ('O-1002', 'acme', 'cust_acme_2', 'processing', NULL, '2026-06-20T13:05:00Z'),
42
+ ('O-9001', 'otherco', 'cust_other_1', 'paid', NULL, '2026-06-20T13:00:00Z')
43
+ ON CONFLICT (id) DO UPDATE SET tenant_id = EXCLUDED.tenant_id, customer_id = EXCLUDED.customer_id, status = EXCLUDED.status, status_change_reason = EXCLUDED.status_change_reason, updated_at = EXCLUDED.updated_at;
@@ -0,0 +1,13 @@
1
+ services:
2
+ postgres:
3
+ image: postgres:16
4
+ container_name: synapsor_runner_support_billing_agent
5
+ environment:
6
+ POSTGRES_DB: synapsor_support_billing_agent
7
+ POSTGRES_USER: synapsor_admin
8
+ POSTGRES_PASSWORD: synapsor_admin_password
9
+ ports:
10
+ - "55436:5432"
11
+ volumes:
12
+ - ./db/schema.sql:/docker-entrypoint-initdb.d/001_schema.sql:ro
13
+ - ./db/seed.sql:/docker-entrypoint-initdb.d/002_seed.sql:ro
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)"
5
+ cd "$ROOT_DIR"
6
+
7
+ SYNAPSOR_REFERENCE_EXAMPLE_DIR="examples/support-billing-agent" \
8
+ SYNAPSOR_REFERENCE_CONFIG_PATH="examples/support-billing-agent/synapsor.runner.json" \
9
+ SYNAPSOR_REFERENCE_TMP_DIR="tmp/support-billing-agent" \
10
+ SYNAPSOR_REFERENCE_CONTAINER="synapsor_runner_support_billing_agent" \
11
+ SYNAPSOR_REFERENCE_DB="synapsor_support_billing_agent" \
12
+ SYNAPSOR_REFERENCE_PORT="55436" \
13
+ SYNAPSOR_REFERENCE_EXPECTED_TOOLS="support.inspect_ticket,support.propose_plan_credit,billing.inspect_invoice,billing.propose_late_fee_waiver" \
14
+ SYNAPSOR_REFERENCE_EXACT_TOOLS="1" \
15
+ node scripts/smoke-reference-support-billing-app.mjs
@@ -0,0 +1,233 @@
1
+ {
2
+ "version": 1,
3
+ "mode": "review",
4
+ "storage": {
5
+ "sqlite_path": "./tmp/support-billing-agent/local.db"
6
+ },
7
+ "sources": {
8
+ "app_postgres": {
9
+ "engine": "postgres",
10
+ "read_url_env": "REFERENCE_POSTGRES_READ_URL",
11
+ "write_url_env": "REFERENCE_POSTGRES_WRITE_URL",
12
+ "statement_timeout_ms": 3000
13
+ }
14
+ },
15
+ "trusted_context": {
16
+ "provider": "environment",
17
+ "values": {
18
+ "tenant_id_env": "SYNAPSOR_TENANT_ID",
19
+ "principal_env": "SYNAPSOR_PRINCIPAL"
20
+ }
21
+ },
22
+ "contexts": {
23
+ "local_operator": {
24
+ "provider": "environment",
25
+ "values": {
26
+ "tenant_id_env": "SYNAPSOR_TENANT_ID",
27
+ "principal_env": "SYNAPSOR_PRINCIPAL"
28
+ }
29
+ }
30
+ },
31
+ "capabilities": [
32
+ {
33
+ "name": "support.inspect_ticket",
34
+ "kind": "read",
35
+ "source": "app_postgres",
36
+ "context": "local_operator",
37
+ "target": {
38
+ "schema": "public",
39
+ "table": "support_tickets",
40
+ "primary_key": "id",
41
+ "tenant_key": "tenant_id"
42
+ },
43
+ "args": {
44
+ "ticket_id": {
45
+ "type": "string",
46
+ "required": true,
47
+ "max_length": 128
48
+ }
49
+ },
50
+ "lookup": {
51
+ "id_from_arg": "ticket_id"
52
+ },
53
+ "visible_columns": [
54
+ "id",
55
+ "tenant_id",
56
+ "customer_id",
57
+ "subject",
58
+ "status",
59
+ "resolution_note",
60
+ "updated_at"
61
+ ],
62
+ "evidence": "required",
63
+ "max_rows": 1
64
+ },
65
+ {
66
+ "name": "support.propose_plan_credit",
67
+ "kind": "proposal",
68
+ "source": "app_postgres",
69
+ "context": "local_operator",
70
+ "target": {
71
+ "schema": "public",
72
+ "table": "customers",
73
+ "primary_key": "id",
74
+ "tenant_key": "tenant_id"
75
+ },
76
+ "args": {
77
+ "customer_id": {
78
+ "type": "string",
79
+ "required": true,
80
+ "max_length": 128
81
+ },
82
+ "credit_cents": {
83
+ "type": "number",
84
+ "required": true,
85
+ "minimum": 0,
86
+ "maximum": 5000
87
+ },
88
+ "reason": {
89
+ "type": "string",
90
+ "required": true,
91
+ "max_length": 500
92
+ }
93
+ },
94
+ "lookup": {
95
+ "id_from_arg": "customer_id"
96
+ },
97
+ "visible_columns": [
98
+ "id",
99
+ "tenant_id",
100
+ "name",
101
+ "plan",
102
+ "plan_credit_cents",
103
+ "credit_reason",
104
+ "updated_at"
105
+ ],
106
+ "evidence": "required",
107
+ "max_rows": 1,
108
+ "patch": {
109
+ "plan_credit_cents": {
110
+ "from_arg": "credit_cents"
111
+ },
112
+ "credit_reason": {
113
+ "from_arg": "reason"
114
+ }
115
+ },
116
+ "allowed_columns": [
117
+ "plan_credit_cents",
118
+ "credit_reason"
119
+ ],
120
+ "numeric_bounds": {
121
+ "plan_credit_cents": {
122
+ "minimum": 0,
123
+ "maximum": 5000
124
+ }
125
+ },
126
+ "conflict_guard": {
127
+ "column": "updated_at"
128
+ },
129
+ "approval": {
130
+ "mode": "human",
131
+ "required_role": "support_lead"
132
+ }
133
+ },
134
+ {
135
+ "name": "billing.inspect_invoice",
136
+ "kind": "read",
137
+ "source": "app_postgres",
138
+ "context": "local_operator",
139
+ "target": {
140
+ "schema": "public",
141
+ "table": "invoices",
142
+ "primary_key": "id",
143
+ "tenant_key": "tenant_id"
144
+ },
145
+ "args": {
146
+ "invoice_id": {
147
+ "type": "string",
148
+ "required": true,
149
+ "max_length": 128
150
+ }
151
+ },
152
+ "lookup": {
153
+ "id_from_arg": "invoice_id"
154
+ },
155
+ "visible_columns": [
156
+ "id",
157
+ "tenant_id",
158
+ "customer_id",
159
+ "status",
160
+ "balance_cents",
161
+ "late_fee_cents",
162
+ "waiver_reason",
163
+ "updated_at"
164
+ ],
165
+ "evidence": "required",
166
+ "max_rows": 1
167
+ },
168
+ {
169
+ "name": "billing.propose_late_fee_waiver",
170
+ "kind": "proposal",
171
+ "source": "app_postgres",
172
+ "context": "local_operator",
173
+ "target": {
174
+ "schema": "public",
175
+ "table": "invoices",
176
+ "primary_key": "id",
177
+ "tenant_key": "tenant_id"
178
+ },
179
+ "args": {
180
+ "invoice_id": {
181
+ "type": "string",
182
+ "required": true,
183
+ "max_length": 128
184
+ },
185
+ "reason": {
186
+ "type": "string",
187
+ "required": true,
188
+ "max_length": 500
189
+ }
190
+ },
191
+ "lookup": {
192
+ "id_from_arg": "invoice_id"
193
+ },
194
+ "visible_columns": [
195
+ "id",
196
+ "tenant_id",
197
+ "customer_id",
198
+ "status",
199
+ "balance_cents",
200
+ "late_fee_cents",
201
+ "waiver_reason",
202
+ "updated_at"
203
+ ],
204
+ "evidence": "required",
205
+ "max_rows": 1,
206
+ "patch": {
207
+ "late_fee_cents": {
208
+ "fixed": 0
209
+ },
210
+ "waiver_reason": {
211
+ "from_arg": "reason"
212
+ }
213
+ },
214
+ "allowed_columns": [
215
+ "late_fee_cents",
216
+ "waiver_reason"
217
+ ],
218
+ "numeric_bounds": {
219
+ "late_fee_cents": {
220
+ "minimum": 0,
221
+ "maximum": 10000
222
+ }
223
+ },
224
+ "conflict_guard": {
225
+ "column": "updated_at"
226
+ },
227
+ "approval": {
228
+ "mode": "human",
229
+ "required_role": "billing_lead"
230
+ }
231
+ }
232
+ ]
233
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@synapsor/runner",
3
- "version": "0.1.0",
4
- "description": "Commit-safe MCP runner for Postgres and MySQL agents",
3
+ "version": "0.1.2",
4
+ "description": "Stop giving AI agents execute_sql; expose reviewed Postgres/MySQL MCP actions with proposals, approval, writeback, and replay.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
7
7
  "bin": {
@@ -17,10 +17,15 @@
17
17
  "docs/**/*.md",
18
18
  "examples/dangerous-mcp-tools.json",
19
19
  "examples/app-owned-writeback/**",
20
+ "examples/claude-desktop-postgres/**",
21
+ "examples/cursor-postgres/**",
20
22
  "examples/mcp-postgres-billing-app-handler/**",
23
+ "examples/mysql-refund-agent/**",
21
24
  "examples/openai-agents-http/**",
22
25
  "examples/openai-agents-stdio/**",
26
+ "examples/raw-sql-vs-synapsor/**",
23
27
  "examples/reference-support-billing-app/**",
28
+ "examples/support-billing-agent/**",
24
29
  "fixtures/**",
25
30
  "schemas/**",
26
31
  "recipes/**/*.json",
@@ -30,14 +35,23 @@
30
35
  "NOTICE",
31
36
  "TRADEMARKS.md"
32
37
  ],
38
+ "keywords": [
39
+ "mcp",
40
+ "postgres",
41
+ "mysql",
42
+ "ai-agents",
43
+ "sql-safety",
44
+ "database-safety",
45
+ "openai-agents",
46
+ "claude",
47
+ "cursor",
48
+ "writeback",
49
+ "replay",
50
+ "model-context-protocol"
51
+ ],
33
52
  "engines": {
34
53
  "node": ">=22.5.0"
35
54
  },
36
- "scripts": {
37
- "build": "tsc -b && node ../../scripts/build-runner-package.mjs",
38
- "prepack": "cd ../.. && corepack pnpm build:runner-package",
39
- "test": "vitest run"
40
- },
41
55
  "dependencies": {
42
56
  "@modelcontextprotocol/sdk": "1.29.0",
43
57
  "mysql2": "^3.11.0",
@@ -57,7 +71,10 @@
57
71
  "url": "https://synapsor.ai/contact"
58
72
  },
59
73
  "publishConfig": {
60
- "access": "public",
61
- "tag": "alpha"
74
+ "access": "public"
75
+ },
76
+ "scripts": {
77
+ "build": "tsc -b && node ../../scripts/build-runner-package.mjs",
78
+ "test": "vitest run"
62
79
  }
63
- }
80
+ }