graceful-boundaries 1.5.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.
package/CONFORMANCE.md ADDED
@@ -0,0 +1,78 @@
1
+ # Conformance
2
+
3
+ Graceful Boundaries defines five conformance levels plus a "no agentic surface" baseline. Each level is independently testable via the eval suite at `evals/`.
4
+
5
+ ## Levels
6
+
7
+ | Level | Key requirement |
8
+ |---|---|
9
+ | N/A | No agentic interaction surface — the service does not expose an HTTP API or agentic surface that callers consume programmatically |
10
+ | 0 | Limits exist but are not described per the spec — operational behavior only, no normative response bodies |
11
+ | 1 | All non-success responses include `error`, `detail`, `why`. 429 responses also include `limit` and `retryAfterSeconds`. |
12
+ | 2 | Level 1 + a discoverable limits endpoint at `/.well-known/limits` (or the path advertised in `Link: rel="limits"`) returning a structured limits document |
13
+ | 3 | Level 2 + constructive guidance fields where applicable: `cachedResultUrl`, `alternativeEndpoint`, `upgradeUrl`. Guidance MUST be honest (point at working alternatives, not dead ends). |
14
+ | 4 | Level 3 + proactive `RateLimit` headers on success responses, per RFC 9239-equivalent semantics, so callers can adapt before hitting refusal |
15
+
16
+ A higher level subsumes all lower levels. A Level 3 service is also Level 2, 1, and not N/A.
17
+
18
+ ## Verifier
19
+
20
+ The reference verifier is `evals/check.js`. Run against any HTTPS service:
21
+
22
+ ```bash
23
+ node evals/check.js https://example.com
24
+ node evals/check.js https://example.com --json
25
+ node evals/check.js https://example.com --limits-path /custom-limits
26
+ ```
27
+
28
+ A passing run reports the highest level the service satisfies. The verifier is the conformance target — any implementation passing the eval suite is conformant.
29
+
30
+ ## Test suite
31
+
32
+ `evals/` ships 250 tests across 12 files:
33
+
34
+ | File | Coverage |
35
+ |---|---|
36
+ | `test-conformance.js` | End-to-end level checks |
37
+ | `test-response-classes.js` | Response class structure |
38
+ | `test-refusal.js` | Refusal body shape (`error`, `detail`, `why`, `limit`, `retryAfterSeconds`) |
39
+ | `test-discovery.js` | Limits discovery endpoint |
40
+ | `test-action-boundaries.js` | Action boundary fields |
41
+ | `test-agentic-surfaces.js` | Agent-facing surface inventory |
42
+ | `test-proactive-headers.js` | Level 4 proactive headers |
43
+ | `test-html-refusal.js` | HTML refusal binding |
44
+ | `test-security.js` | Security-case responses (SC-1 through SC-16) |
45
+ | `test-schemas.js` | Published JSON Schemas vs spec examples, worked limits examples, and checker agreement |
46
+ | `test-middleware-examples.js` | Drop-in middleware examples emit conformant bodies |
47
+ | `test-agent-behavior.js` | Agent-side compliance fixtures (retry, guidance, SC-6, SC-16) |
48
+
49
+ Run all: `npm test`. No npm dependencies — vanilla Node.js.
50
+
51
+ ## Reference implementation
52
+
53
+ Siteline (https://siteline.to/) is the canonical Level 4 reference implementation.
54
+
55
+ ```bash
56
+ node evals/check.js https://siteline.to
57
+ ```
58
+
59
+ When proposing a normative change that would alter Siteline's level, coordinate the release with Siteline so the reference stays current.
60
+
61
+ ## Independent verifiers
62
+
63
+ Anyone may build a conformant verifier. The eval suite is the agreement target. The hosted verifier at https://gracefulboundaries.dev/ is published for convenience and MUST NOT be treated as the only authoritative implementation.
64
+
65
+ ## What conformance does not assert
66
+
67
+ - **Safety.** Conformance verifies form. It does not assert that the service is benign, well-operated, or appropriate for any use case.
68
+ - **Fairness.** Conformance does not evaluate whether the limit values are reasonable, only whether they are communicated correctly.
69
+ - **Availability.** Conformance does not measure uptime, latency, or operational quality.
70
+ - **Authorization correctness.** Conformance does not validate that the service's authorization decisions are correct, only that refusals are well-formed.
71
+
72
+ ## Reporting findings
73
+
74
+ When `evals/check.js` reports a level lower than claimed, file a finding in the service operator's issue tracker with the failing test output. The eval suite produces machine-readable JSON via `--json` for inclusion in tickets.
75
+
76
+ ## Versioning
77
+
78
+ Conformance is versioned with the spec. A service claiming "Level 3 conformance" SHOULD specify the spec version (e.g. "Level 3 against Graceful Boundaries 1.4.1"). Major spec version increments require re-evaluation; minor and patch versions are backward-compatible.
package/LICENSE ADDED
@@ -0,0 +1,36 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 PAICE.work PBC
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
23
+ ---
24
+
25
+ License scope
26
+
27
+ - LICENSE (this file, MIT): applies to all code in the repository — eval suite,
28
+ conformance checker (evals/), reference implementations, scripts, and any
29
+ example code embedded in documentation.
30
+ - LICENSE-SPEC (CC BY 4.0): applies to spec.md, docs/*.md, and the textual
31
+ content of the specification itself.
32
+
33
+ Files containing both code and prose follow the prose license for the prose
34
+ portions and the MIT license for the code portions. The split is non-binding
35
+ when a single file mixes both; treat the file as MIT for code reuse and
36
+ CC BY 4.0 for textual quotation.
package/LICENSE-SPEC ADDED
@@ -0,0 +1,12 @@
1
+ Creative Commons Attribution 4.0 International (CC BY 4.0)
2
+
3
+ Copyright (c) 2026 PAICE.work PBC
4
+
5
+ You are free to:
6
+ - Share: copy and redistribute the material in any medium or format
7
+ - Adapt: remix, transform, and build upon the material for any purpose, even commercially
8
+
9
+ Under the following terms:
10
+ - Attribution: You must give appropriate credit, provide a link to the license, and indicate if changes were made.
11
+
12
+ Full license text: https://creativecommons.org/licenses/by/4.0/legalcode
package/README.md ADDED
@@ -0,0 +1,262 @@
1
+ # Graceful Boundaries
2
+
3
+ A specification for how services communicate their operational limits to humans and autonomous agents.
4
+
5
+ **[gracefulboundaries.dev](https://gracefulboundaries.dev)**
6
+
7
+ [![License: CC-BY-4.0](https://img.shields.io/badge/License-CC--BY--4.0-lightgrey.svg)](https://creativecommons.org/licenses/by/4.0/)
8
+ [![Version](https://img.shields.io/badge/version-1.5.0-blue.svg)](CHANGELOG.md)
9
+ [![Tests](https://img.shields.io/github/actions/workflow/status/snapsynapse/graceful-boundaries/test.yml?label=tests)](https://github.com/snapsynapse/graceful-boundaries/actions)
10
+ [![ClawHub](https://img.shields.io/badge/ClawHub-83%20installs-blue)](https://clawhub.ai/snapsynapse/graceful-boundaries)
11
+
12
+ ## Who this is for
13
+
14
+ API and service operators, plus the agent builders calling them, who need operational limits expressed in a way autonomous callers can actually act on.
15
+
16
+ ## What problem it solves
17
+
18
+ Services signal limits with status codes (429, 403, 500) that agents can't interpret, so agents retry blindly and the waste compounds. Graceful Boundaries is a specification for communicating operational limits to humans and autonomous agents.
19
+
20
+ ## Canonical URL
21
+
22
+ https://gracefulboundaries.dev/
23
+
24
+ ## The problem
25
+
26
+ Every unclear response generates follow-up traffic. A vague `429` causes blind retries. A vague `403` causes re-attempts with different credentials. A generic `500` causes indefinite retries. When autonomous agents are the caller, the waste compounds: agents retry faster, probe more systematically, and lack the human judgment to know when to stop.
27
+
28
+ Most services enforce rate limits but communicate them poorly. A `429 Too Many Requests` with `Retry-After: 60` tells a retry loop what to do. It doesn't tell an autonomous agent whether to retry, use a cached result, try a different endpoint, or inform the human. It doesn't tell a developer what the limits are before they hit them. It doesn't tell anyone *why* the limit exists.
29
+
30
+ ## What Graceful Boundaries does
31
+
32
+ The specification addresses three gaps that existing standards cover separately but no specification combines:
33
+
34
+ 1. **Proactive discovery** -- limits are machine-readable before they are hit
35
+ 2. **Structured refusal** -- every non-success response explains what happened, why, and what to do next
36
+ 3. **Constructive guidance** -- refusals include a useful next step, not just a block
37
+
38
+ This applies to **every HTTP error class**, not just rate limits. A `400` explains the validation rule and its security rationale. A `404` tells you whether the resource never existed or expired, and offers a creation path. A `500` names the affected subsystem and suggests a retry window. Every non-success response MUST include `error`, `detail`, and `why`.
39
+
40
+ **Read the full specification:** **[spec.md](spec.md)**
41
+
42
+ ## See it in action
43
+
44
+ These examples use [Siteline](https://siteline.to/), a Level 4 conformant reference implementation.
45
+
46
+ **Discover limits before hitting them:**
47
+
48
+ ```bash
49
+ curl -s https://siteline.to/api/limits | jq '{service, limits: .limits.scan}'
50
+ ```
51
+
52
+ ```json
53
+ {
54
+ "service": "Siteline",
55
+ "limits": {
56
+ "scan": {
57
+ "endpoint": "/api/scan",
58
+ "method": "GET",
59
+ "limits": [
60
+ {
61
+ "type": "ip-rate",
62
+ "maxRequests": 10,
63
+ "windowSeconds": 3600,
64
+ "description": "10 scans per IP per hour."
65
+ }
66
+ ]
67
+ }
68
+ }
69
+ }
70
+ ```
71
+
72
+ **Structured refusal with constructive guidance** (when a rate limit is exceeded):
73
+
74
+ ```json
75
+ {
76
+ "error": "rate_limit_exceeded",
77
+ "detail": "You can run up to 10 scans per hour. Try again in 2400 seconds.",
78
+ "limit": "10 scans per IP per hour",
79
+ "retryAfterSeconds": 2400,
80
+ "why": "Siteline is a free service. Rate limits keep it available for everyone and prevent abuse.",
81
+ "alternativeEndpoint": "/api/result?id=example.com"
82
+ }
83
+ ```
84
+
85
+ The caller knows the limit, when to retry, *why* the limit exists, and where to get the result without waiting.
86
+
87
+ **Every error class is self-explanatory**, not just 429s:
88
+
89
+ ```json
90
+ {
91
+ "error": "invalid_input",
92
+ "detail": "This URL is outside the scanner's accepted public-target policy.",
93
+ "why": "Siteline accepts only public scan targets to prevent the scanner from being used as a proxy.",
94
+ "field": "url",
95
+ "expected": "A public URL with a resolvable hostname."
96
+ }
97
+ ```
98
+
99
+ An agent reading this `400` understands the input-safety policy and can fix the input. Without `why`, it would blindly retry with different URLs.
100
+
101
+ **Proactive headers on successful responses:**
102
+
103
+ ```bash
104
+ curl -s 'https://siteline.to/api/result?id=example.com' \
105
+ -D - -o /dev/null 2>&1 | grep ratelimit
106
+ ```
107
+
108
+ ```
109
+ ratelimit: limit=60, remaining=59, reset=60
110
+ ratelimit-policy: 60;w=60
111
+ ```
112
+
113
+ A caller seeing `remaining=1` self-throttles before the next request. A caller seeing `remaining=59` knows it has budget.
114
+
115
+ For more examples, see **[docs/curl-examples.md](docs/curl-examples.md)**.
116
+
117
+ ## Conformance levels
118
+
119
+ Services self-declare a conformance level. The eval suite validates the claim.
120
+
121
+ | Level | What it requires |
122
+ |---|---|
123
+ | **N/A: Not Applicable** | No API endpoints, rate limits, or agentic interaction surface. |
124
+ | **Level 0: Non-Conformant** | Limits exist but are not described per this specification. |
125
+ | **Level 1: Structured Refusal** | All non-success responses include `error`, `detail`, and `why`. All `429`s add `limit` and `retryAfterSeconds`. |
126
+ | **Level 2: Discoverable** | Level 1 + a limits discovery endpoint. |
127
+ | **Level 3: Constructive** | Level 2 + refusal responses include constructive guidance when applicable. |
128
+ | **Level 4: Proactive** | Level 3 + successful responses include proactive limit headers. |
129
+
130
+ ## Evaluate conformance
131
+
132
+ Run the checker directly via npx (no install, no dependencies):
133
+
134
+ ```bash
135
+ npx graceful-boundaries check https://siteline.to # Level 4 — proactive headers
136
+ npx graceful-boundaries check https://google.com # Level 0 — no conformance
137
+ npx graceful-boundaries check https://your-service.com --json
138
+ npx graceful-boundaries check https://your-service.com --min-level 2 # nonzero exit below Level 2
139
+ npx graceful-boundaries check https://your-service.com --check-cloaking
140
+ ```
141
+
142
+ ![Checker output: Siteline confirms Level 4, Google confirms Level 0](imgs/checker-demo.svg)
143
+
144
+ Or clone and run from the project root with `node evals/check.js <url>`. Run the unit test suite (250 tests, no dependencies):
145
+
146
+ ```bash
147
+ npm test
148
+ ```
149
+
150
+ ### Gate conformance in CI
151
+
152
+ The repo doubles as a composite GitHub Action. Add a job that fails when your deployed service drops below its declared level:
153
+
154
+ ```yaml
155
+ - uses: snapsynapse/graceful-boundaries@v1
156
+ with:
157
+ url: https://staging.your-service.com
158
+ min-level: "2"
159
+ ```
160
+
161
+ Levels 2 and 4 are confirmable passively and make reliable CI gates; Levels 1 and 3 require observing a live refusal (see [CONFORMANCE.md](CONFORMANCE.md)).
162
+
163
+ ## Assistant guide
164
+
165
+ Graceful Boundaries adopts the [GuideCheck](https://guidecheck.org/) Human-Verifiable Assistant Guide profile. The assistant-facing guide is committed at [assistant-guide.txt](assistant-guide.txt) and published from `https://gracefulboundaries.dev/.well-known/assistant-guide.txt`.
166
+
167
+ Verify the guide before asking an assistant to follow it. Use the hosted verifier at [guidecheck.org/verify](https://guidecheck.org/verify) or a conformant local verifier:
168
+
169
+ ```bash
170
+ python3 /path/to/guidecheck/scripts/guidecheck_verify.py assistant-guide.txt
171
+ ```
172
+
173
+ The committed root copy and the well-known copy must remain byte-identical.
174
+
175
+ The current GuideCheck implementation and all agent-facing repository surfaces are documented in [docs/agentic-surfaces.md](docs/agentic-surfaces.md). Current local verification: GuideCheck reference verifier 0.3.2, achieved Level 3, guide SHA-256 `7dbf6472d5a49905054b0d541c27a4246bdc1f10e5d7bb9c16c028fa04b8bfdd`, with 0 blocking findings and 0 warnings.
176
+
177
+ ## Which level should you target?
178
+
179
+ - **No API or agentic surface?** Declare `not-applicable` — takes 5 minutes.
180
+ - **API with rate limits?** Start at **Level 1** (structured refusals). This is the minimum useful level.
181
+ - **Agents call your API?** Target **Level 2** (add discovery) so agents learn the rules before breaking them.
182
+ - **Want to reduce 429 traffic?** Target **Level 3** (constructive guidance) — offer cached results and alternatives instead of bare refusals.
183
+ - **High-traffic API with agent callers?** Target **Level 4** (proactive headers) — callers self-throttle before hitting limits.
184
+
185
+ For a step-by-step walkthrough with code samples, see the **[implementation guide](docs/implementation-guide.md)**.
186
+
187
+ ## Adopt in an afternoon
188
+
189
+ The shortest path from "read the spec" to "conformant service":
190
+
191
+ 1. **Copy a middleware example** -- dependency-free Level 2 implementations (Level 4 with one flag) for [Express, FastAPI, Cloudflare Workers, and Hono](examples/middleware/).
192
+ 2. **Copy the nearest limits.json** -- complete discovery responses for a [SaaS API, free scanner, LLM API, and content site](examples/limits/).
193
+ 3. **Validate your bodies** -- published [JSON Schemas](schema/) for refusals, 429s, and the discovery endpoint, served at `https://gracefulboundaries.dev/schema/`. Importable into OpenAPI and CI validators.
194
+ 4. **Verify** -- `npx graceful-boundaries check https://your-service.example`, then gate it in CI with the GitHub Action above.
195
+ 5. **Declare** -- add yourself to [ADOPTERS.md](ADOPTERS.md) and embed a [conformance badge](ADOPTERS.md#badges).
196
+
197
+ Already emitting RFC 9457 Problem Details? You can adopt without changing your content type: see the [RFC 9457 compatibility profile](docs/rfc9457-profile.md).
198
+
199
+ Building the agent side? `evals/test-agent-behavior.js` is an [agent compliance suite](evals/test-agent-behavior.js): point it at your retry/refusal-handling logic and it checks you respect `retryAfterSeconds`, prefer cached results, ignore off-origin guidance URLs (SC-6), and treat guidance text as untrusted data (SC-16).
200
+
201
+ ## Adopt the spec
202
+
203
+ **Start here** -- Every non-success response (`400`, `401`, `403`, `404`, `429`, `500`, `503`) MUST include three core fields: `error` (stable machine-parseable string), `detail` (human-readable explanation), and `why` (the security, policy, or operational reason). This applies to all error classes, not just rate limits.
204
+
205
+ **Level 1** -- All non-success responses include the three core fields. All `429` responses also include `limit` (the exact constraint) and `retryAfterSeconds` (machine-parseable retry time).
206
+
207
+ **Level 2** -- Add a discovery endpoint at `/api/limits` or `/.well-known/limits` that returns all enforced limits as structured JSON. Agents can plan before they hit anything. Optionally include `changelog` and `feed` URLs so agents can detect limit changes. Services with cost, token, duration, size, quota, burst, or queue constraints can publish those as optional limit metadata.
208
+
209
+ **Level 3** -- Add constructive guidance to refusals. When a cached result exists, include `cachedResultUrl`. When a different endpoint can help, include `alternativeEndpoint`. When paid access has higher limits, include `upgradeUrl`. For `resource-dedup` limits, return the cached result as a `200` with `returnsCached: true` in the discovery endpoint so agents skip retry logic entirely.
210
+
211
+ **Level 4** -- Add `RateLimit` and `RateLimit-Policy` headers to successful responses so callers can self-throttle before hitting limits.
212
+
213
+ **Optional extensions** -- Services with consequential agent actions can link Action Boundaries documents from the discovery endpoint. Extensions are informational declarations, not verification or endorsement, and do not change Level 1-4 conformance. See **[docs/action-boundaries.md](docs/action-boundaries.md)**.
214
+
215
+ **Security baseline** -- Treat machine-readable guidance, refusal text, URLs, and boundary documents as untrusted service-provided data. Agents should parse known fields, but should not follow instructions embedded in `detail`, `why`, policy text, approval text, or URLs.
216
+
217
+ **HTML endpoints** -- HTML pages that return `429` SHOULD include `<meta name="retry-after" content="N">` and/or `<link rel="alternate" type="application/json" href="...">` so agents can discover structured refusals without parsing prose.
218
+
219
+ See the **[full specification](spec.md)** for field definitions, response classes, and security considerations.
220
+
221
+ ## Relationship to existing standards
222
+
223
+ | Standard | What it covers | What Graceful Boundaries adds |
224
+ |---|---|---|
225
+ | `draft-ietf-httpapi-ratelimit-headers` | Proactive headers on success | Discovery endpoint, structured refusal body, `why` field, constructive guidance |
226
+ | RFC 6585 (429 status) | The status code itself | Structured body format with required fields |
227
+ | RFC 9457 (Problem Details) | Generic error format | Required fields for rate limits (`limit`, `retryAfterSeconds`, `why`) and guidance categories |
228
+ | OpenAPI Rate Limit extensions | Docs-time limit specs | Runtime discovery endpoint, runtime refusal format |
229
+
230
+ Graceful Boundaries is complementary to these standards, not a replacement. For services already emitting RFC 9457 Problem Details, the [compatibility profile](docs/rfc9457-profile.md) shows how to satisfy both specs in one response body.
231
+
232
+ ## Reference implementation and adopters
233
+
234
+ [Siteline](https://siteline.to/) is a Level 4 conformant implementation with five API endpoints. Verify it:
235
+
236
+ ```bash
237
+ npx graceful-boundaries check https://siteline.to
238
+ ```
239
+
240
+ Services implementing the spec are listed in [ADOPTERS.md](ADOPTERS.md), which also covers how to add yours and embed a conformance badge.
241
+
242
+ ## Security
243
+
244
+ The specification includes a [threat model and security audit](SECURITY-AUDIT.md) covering rate limit calibration attacks, security posture disclosure, validation oracles, content cloaking via agent-signaling headers, action boundary risks, untrusted machine-readable guidance, and other considerations (SC-1 through SC-16), all addressed in the spec.
245
+
246
+ ## Sponsor
247
+
248
+ Graceful Boundaries is free and open. If your team relies on this spec, consider [sponsoring its development](https://github.com/sponsors/snapsynapse) to keep it maintained and evolving. See [SPONSORS.md](SPONSORS.md).
249
+
250
+ ## License
251
+
252
+ CC-BY-4.0. Use it, adapt it, build on it. Attribution required.
253
+
254
+ ## About
255
+
256
+ Graceful Boundaries is a [PAICE.work](https://paice.work/) project. PAICE.work PBC is a public benefit corporation dedicated to enabling safer and more effective People+AI collaboration. We believe that clear, honest communication between services and their callers (human or AI agent) is foundational to trustworthy AI infrastructure. This specification is part of that mission.
257
+
258
+ The patterns in this spec emerged from building [Siteline](https://siteline.to/), an AI agent readiness scanner, where the quality of the refusal matters as much as the enforcement.
259
+
260
+ The conformance audit skill is available on **[ClawHub](https://clawhub.ai/snapsynapse/graceful-boundaries)**.
261
+
262
+ See also: **[GuideCheck](https://guidecheck.org/)** -- human-verifiable assistant guides, and **[Skill Provenance](https://skillprovenance.dev/)** -- version identity that travels with agent skill bundles. Also PAICE.work projects.
package/bin/cli.js ADDED
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * npx entry point for the Graceful Boundaries conformance checker.
5
+ *
6
+ * Usage:
7
+ * npx graceful-boundaries check <base-url> [--limits-path /path] [--json] [--check-cloaking] [--min-level N]
8
+ * npx graceful-boundaries <base-url> # "check" subcommand is optional
9
+ *
10
+ * Thin wrapper around evals/check.js. All flags pass through unchanged.
11
+ */
12
+
13
+ const { main } = require("../evals/check.js");
14
+
15
+ // Strip the optional "check" subcommand so check.js sees the URL as the first arg.
16
+ if (process.argv[2] === "check") {
17
+ process.argv.splice(2, 1);
18
+ }
19
+
20
+ main().catch((error) => {
21
+ console.error(error);
22
+ process.exit(1);
23
+ });