@chusky/sdk 0.2.0 → 0.3.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.
@@ -0,0 +1,31 @@
1
+ # SDK examples
2
+
3
+ These examples are server-side TypeScript programs. They call the real Chusky
4
+ Developer API; they are not mocks and they do not contain credentials.
5
+
6
+ ## Run one
7
+
8
+ From the SDK directory:
9
+
10
+ ```bash
11
+ npm install
12
+ export CHUSKY_API_KEY=chsk_...
13
+ export CHUSKY_USER_ID=example-user
14
+ npx tsx examples/quickstart.ts
15
+ ```
16
+
17
+ PowerShell:
18
+
19
+ ```powershell
20
+ $env:CHUSKY_API_KEY = "chsk_..."
21
+ $env:CHUSKY_USER_ID = "example-user"
22
+ npx tsx examples/quickstart.ts
23
+ ```
24
+
25
+ Set `CHUSKY_BASE_URL` when using a self-hosted or staging API. Use a test
26
+ project key and test identity. Some examples create durable state, upload a
27
+ file, register a webhook, or display an approval; review the source before
28
+ running them against production.
29
+
30
+ The `_client.ts` helper creates the authenticated SDK client. Each example can
31
+ also be copied into an application and inlined without depending on the helper.
@@ -0,0 +1,17 @@
1
+ import { Chusky } from "@chusky/sdk";
2
+
3
+ export function createClient(): Chusky {
4
+ const apiKey = process.env.CHUSKY_API_KEY;
5
+ if (!apiKey) throw new Error("Set CHUSKY_API_KEY before running this example.");
6
+
7
+ return new Chusky({
8
+ apiKey,
9
+ baseUrl: process.env.CHUSKY_BASE_URL,
10
+ userId: process.env.CHUSKY_USER_ID ?? "example-user",
11
+ });
12
+ }
13
+
14
+ export function required<T>(value: T | undefined, name: string): T {
15
+ if (value === undefined) throw new Error(`Missing ${name}.`);
16
+ return value;
17
+ }
@@ -0,0 +1,27 @@
1
+ import { createClient } from "./_client.js";
2
+
3
+ const chusky = createClient();
4
+ const page = await chusky.approvals.list();
5
+ const pending = page.data.find((approval) => approval.status === "pending");
6
+
7
+ if (!pending) {
8
+ console.log("There are no pending approvals.");
9
+ process.exit(0);
10
+ }
11
+
12
+ console.log({
13
+ id: pending.id,
14
+ action: pending.toolSlug,
15
+ request: pending.request,
16
+ expiresAt: pending.expiresAt,
17
+ });
18
+
19
+ // Replace this with an authenticated human decision in your application UI.
20
+ const humanApproved = process.env.APPROVE_CHUSKY_ACTION === "true";
21
+ const decision = await chusky.approvals.decide(
22
+ pending.id,
23
+ humanApproved ? "approve" : "deny",
24
+ { idempotencyKey: `approval:${pending.id}:${humanApproved ? "approve" : "deny"}` },
25
+ );
26
+
27
+ console.log(`Approval ${pending.id}:`, decision);
@@ -0,0 +1,36 @@
1
+ import { createClient } from "./_client.js";
2
+
3
+ const chusky = createClient();
4
+ const templates = await chusky.agents.templates();
5
+ const template = templates.data.find((item) => item.slug === "lead-research");
6
+
7
+ if (!template) {
8
+ throw new Error("The lead-research template is not available for this project.");
9
+ }
10
+
11
+ const agent = await chusky.agents.create(
12
+ {
13
+ template: template.slug,
14
+ name: "Fintech lead scout",
15
+ instructions: "Return sourced, deduplicated company profiles. Draft external communication only.",
16
+ policy: {
17
+ tools: {
18
+ allow: ["crm.read", "web.search", "email.draft"],
19
+ requireApproval: ["crm.write", "email.send"],
20
+ },
21
+ budget: { duration: "30m", maxToolCalls: 80, maxCost: 8 },
22
+ },
23
+ },
24
+ { idempotencyKey: "create-fintech-lead-agent-v1" },
25
+ );
26
+
27
+ const { thread, run } = await chusky.runs.create(
28
+ {
29
+ agentId: agent.id,
30
+ input: "Find qualified fintech companies with more than 50 employees and prepare CRM-ready profiles.",
31
+ wait: false,
32
+ },
33
+ { idempotencyKey: "fintech-lead-research-customer-123-v1" },
34
+ );
35
+
36
+ console.log({ agentId: agent.id, threadId: thread.id, runId: run.id, status: run.status });
@@ -0,0 +1,41 @@
1
+ import { createClient } from "./_client.js";
2
+
3
+ const chusky = createClient();
4
+ const customerId = process.env.CHUSKY_USER_ID ?? "example-user";
5
+
6
+ await chusky.context.save(
7
+ {
8
+ scope: "customer",
9
+ scopeId: customerId,
10
+ kind: "preference",
11
+ key: "renewal_window",
12
+ value: "Customer prefers renewal discussions in October.",
13
+ source: "crm",
14
+ confidence: 0.9,
15
+ sensitivity: "normal",
16
+ },
17
+ { idempotencyKey: `context-renewal-window-${customerId}` },
18
+ );
19
+
20
+ const context = await chusky.context.list({
21
+ scope: "customer",
22
+ scopeId: customerId,
23
+ purpose: "renewal",
24
+ });
25
+
26
+ const catalog = await chusky.departments.catalog();
27
+ console.log("Available departments:", catalog.data.map((item) => item.slug));
28
+
29
+ const packet = await chusky.departments.handoff(
30
+ "customer-success",
31
+ {
32
+ objective: "Prepare a renewal risk review for the account team.",
33
+ inputs: { customerId },
34
+ constraints: ["Use verified CRM facts only."],
35
+ evidenceRequired: ["account health source", "open risk owner"],
36
+ approvalBoundary: "Draft only; do not contact the customer.",
37
+ },
38
+ { idempotencyKey: `handoff-renewal-risk-${customerId}` },
39
+ );
40
+
41
+ console.log({ contextNodes: context.data.length, handoffId: packet.id, status: packet.status });
@@ -0,0 +1,23 @@
1
+ import { createClient } from "./_client.js";
2
+
3
+ const chusky = createClient();
4
+ const content = "customer_id,renewal_date\n123,2026-10-01\n";
5
+ const body = new TextEncoder().encode(content);
6
+
7
+ const upload = await chusky.files.create(
8
+ { name: "renewals.csv", contentType: "text/csv", size: body.byteLength },
9
+ { idempotencyKey: "upload-renewals-csv-v1" },
10
+ );
11
+
12
+ const response = await fetch(upload.uploadUrl, {
13
+ method: "PUT",
14
+ headers: { "Content-Type": "text/csv" },
15
+ body,
16
+ });
17
+ if (!response.ok) throw new Error(`Upload failed with HTTP ${response.status}.`);
18
+
19
+ const file = await chusky.files.complete(
20
+ upload.id,
21
+ { idempotencyKey: `complete-upload:${upload.id}` },
22
+ );
23
+ console.log({ id: file.id, name: file.name, status: file.status });
@@ -0,0 +1,46 @@
1
+ import { createClient } from "./_client.js";
2
+
3
+ const chusky = createClient();
4
+ const mission = await chusky.missions.create(
5
+ {
6
+ title: "Qualified fintech leads",
7
+ objective: "Find 20 fintech companies matching our ICP and prepare CRM-ready profiles.",
8
+ definitionOfDone: "Every lead has a source URL, qualification reason, duplicate check, and approval-ready CRM payload.",
9
+ verificationMode: "strict",
10
+ requiredEvidence: ["source URL", "qualification assertion", "deduplication check"],
11
+ steps: [
12
+ { id: "research", title: "Research companies", objective: "Collect source-backed company facts." },
13
+ { id: "qualify", title: "Qualify and deduplicate", objective: "Apply the ICP and remove duplicates.", dependsOn: ["research"] },
14
+ { id: "prepare", title: "Prepare CRM payload", objective: "Draft the bounded CRM import.", dependsOn: ["qualify"] },
15
+ ],
16
+ maxDurationSeconds: 3 * 60 * 60,
17
+ maxSteps: 30,
18
+ maxToolCalls: 100,
19
+ maxCost: 15,
20
+ },
21
+ { idempotencyKey: "mission-fintech-leads-customer-123-v1" },
22
+ );
23
+
24
+ console.log(`Mission ${mission.id} is ${mission.status}.`);
25
+
26
+ const proof = await chusky.missions.proof(mission.id);
27
+ console.log({
28
+ status: proof.status,
29
+ nextAction: proof.nextAction,
30
+ completedSteps: proof.steps.filter((step) => step.status === "completed").length,
31
+ evidence: proof.evidence.length,
32
+ });
33
+
34
+ // Attach evidence only after your application has actually verified it.
35
+ if (proof.status === "completed" && proof.steps[0]?.status === "completed") {
36
+ await chusky.missions.evidence(mission.id, {
37
+ stepId: "research",
38
+ evidence: [{
39
+ id: `source-check-${Date.now()}`,
40
+ kind: "source",
41
+ summary: "Company facts were checked against the cited source URLs.",
42
+ verified: true,
43
+ verifiedBy: "system",
44
+ }],
45
+ });
46
+ }
@@ -0,0 +1,25 @@
1
+ import { createClient } from "./_client.js";
2
+
3
+ const chusky = createClient();
4
+ const requestId = `quickstart-${Date.now()}`;
5
+
6
+ const { thread, run } = await chusky.runs.create(
7
+ {
8
+ input: "Prepare a concise renewal brief from the available account context.",
9
+ wait: false,
10
+ budget: { duration: "5m", maxToolCalls: 20, maxCost: 1 },
11
+ },
12
+ { idempotencyKey: requestId },
13
+ );
14
+
15
+ console.log(`Started run ${run.id} in thread ${thread.id}.`);
16
+ const completed = await chusky.runs.wait(thread.id, run.id, {
17
+ timeoutMs: 120_000,
18
+ intervalMs: 1_000,
19
+ });
20
+
21
+ if (completed.status === "failed") {
22
+ throw new Error(completed.error?.message ?? "Chusky run failed.");
23
+ }
24
+
25
+ console.log(completed.output ?? "The run completed without text output.");
@@ -0,0 +1,37 @@
1
+ import { createClient } from "./_client.js";
2
+
3
+ const chusky = createClient();
4
+ const thread = await chusky.threads.create(
5
+ { metadata: { source: "sdk-example" } },
6
+ { idempotencyKey: `stream-thread-${Date.now()}` },
7
+ );
8
+
9
+ const controller = new AbortController();
10
+ const stopAfterMs = setTimeout(() => controller.abort(), 60_000);
11
+
12
+ try {
13
+ for await (const event of chusky.threads.runs(thread.id).stream(
14
+ { input: "Summarize the account's current priorities in three bullets." },
15
+ { signal: controller.signal },
16
+ )) {
17
+ switch (event.type) {
18
+ case "run.delta":
19
+ process.stdout.write(event.text);
20
+ break;
21
+ case "run.tool_started":
22
+ console.error(`\nUsing ${event.toolSlug}...`);
23
+ break;
24
+ case "run.approval_required":
25
+ console.error(`\nApproval required: ${event.approval.request ?? event.approval.toolSlug}`);
26
+ console.error(`Approval ID: ${event.approval.id}`);
27
+ break;
28
+ case "run.failed":
29
+ throw new Error(event.error.message);
30
+ case "run.completed":
31
+ console.error(`\nCompleted with status ${event.run.status}.`);
32
+ break;
33
+ }
34
+ }
35
+ } finally {
36
+ clearTimeout(stopAfterMs);
37
+ }
@@ -0,0 +1,16 @@
1
+ import { createClient } from "./_client.js";
2
+
3
+ const chusky = createClient();
4
+ const webhook = await chusky.webhooks.create(
5
+ "https://app.example.com/api/chusky/events",
6
+ { idempotencyKey: "register-chusky-events-v1" },
7
+ );
8
+
9
+ const deliveries = await chusky.webhooks.deliveries(webhook.id);
10
+ console.log({
11
+ webhookId: webhook.id,
12
+ deliveryStatuses: deliveries.data.map((delivery) => delivery.status),
13
+ });
14
+
15
+ // Your receiving endpoint should verify the Chusky signature, persist the
16
+ // delivery ID before applying the event, and return 2xx only after that write.
package/openapi.yaml CHANGED
@@ -63,7 +63,7 @@ paths:
63
63
  post: { summary: Create a Telegram linking code for the account, parameters: [{ $ref: '#/components/parameters/UserId' }, { $ref: '#/components/parameters/IdempotencyKey' }], responses: { '200': { description: Linking code } } }
64
64
  /account/calls:
65
65
  get: { summary: List account voice calls with redacted destinations, parameters: [{ $ref: '#/components/parameters/UserId' }], responses: { '200': { description: OK } } }
66
- post: { summary: Create an approval-gated outbound voice call request, parameters: [{ $ref: '#/components/parameters/UserId' }, { $ref: '#/components/parameters/IdempotencyKey' }], responses: { '201': { description: Pending approval } } }
66
+ post: { summary: Validate and start an outbound voice call, parameters: [{ $ref: '#/components/parameters/UserId' }, { $ref: '#/components/parameters/IdempotencyKey' }], responses: { '201': { description: Started call record } } }
67
67
  /meetings:
68
68
  get: { summary: List owner-scoped Recall meeting preparations, meetings, participants, and contacts, parameters: [{ $ref: '#/components/parameters/UserId' }], responses: { '200': { description: OK } } }
69
69
  post: { summary: Join or schedule a Recall meeting, parameters: [{ $ref: '#/components/parameters/UserId' }, { $ref: '#/components/parameters/IdempotencyKey' }], responses: { '201': { description: Meeting lifecycle record } } }
@@ -224,11 +224,69 @@ paths:
224
224
  post: { summary: Schedule a one-time reminder, parameters: [{ $ref: '#/components/parameters/UserId' }, { $ref: '#/components/parameters/IdempotencyKey' }], responses: { '201': { description: Scheduled } } }
225
225
  /reminders/{id}:
226
226
  delete: { summary: Cancel an owned reminder, parameters: [{ $ref: '#/components/parameters/UserId' }], responses: { '204': { description: Cancelled } } }
227
+ /reminders/{id}/pause:
228
+ post: { summary: Pause an owned reminder, parameters: [{ $ref: '#/components/parameters/UserId' }, { $ref: '#/components/parameters/IdempotencyKey' }], responses: { '200': { description: Paused } } }
229
+ /reminders/{id}/resume:
230
+ post: { summary: Resume an owned reminder, parameters: [{ $ref: '#/components/parameters/UserId' }, { $ref: '#/components/parameters/IdempotencyKey' }], responses: { '200': { description: Resumed } } }
231
+ /reminders/{id}/run:
232
+ post: { summary: Queue an immediate reminder execution, parameters: [{ $ref: '#/components/parameters/UserId' }, { $ref: '#/components/parameters/IdempotencyKey' }], responses: { '202': { description: Queued } } }
227
233
  /jobs:
228
234
  get: { summary: List recurring schedules, parameters: [{ $ref: '#/components/parameters/UserId' }], responses: { '200': { description: OK } } }
229
235
  post: { summary: Create a recurring QStash schedule, parameters: [{ $ref: '#/components/parameters/UserId' }, { $ref: '#/components/parameters/IdempotencyKey' }], responses: { '201': { description: Scheduled } } }
230
236
  /jobs/{id}:
231
237
  delete: { summary: Cancel an owned recurring schedule, parameters: [{ $ref: '#/components/parameters/UserId' }], responses: { '204': { description: Cancelled } } }
238
+ /jobs/{id}/pause:
239
+ post: { summary: Pause an owned recurring schedule, parameters: [{ $ref: '#/components/parameters/UserId' }, { $ref: '#/components/parameters/IdempotencyKey' }], responses: { '200': { description: Paused } } }
240
+ /jobs/{id}/resume:
241
+ post: { summary: Resume an owned recurring schedule, parameters: [{ $ref: '#/components/parameters/UserId' }, { $ref: '#/components/parameters/IdempotencyKey' }], responses: { '200': { description: Resumed } } }
242
+ /jobs/{id}/run:
243
+ post: { summary: Queue an immediate recurring-job execution, parameters: [{ $ref: '#/components/parameters/UserId' }, { $ref: '#/components/parameters/IdempotencyKey' }], responses: { '202': { description: Queued } } }
244
+ /jobs/{id}/occurrences:
245
+ get: { summary: List owner-scoped recurring-job occurrences, parameters: [{ $ref: '#/components/parameters/UserId' }, { name: limit, in: query, schema: { type: integer, minimum: 1, maximum: 100 } }], responses: { '200': { description: Occurrence history } } }
246
+ /missions:
247
+ get: { summary: List owner-scoped autonomous missions, parameters: [{ $ref: '#/components/parameters/UserId' }], responses: { '200': { description: Mission records } } }
248
+ post: { summary: Create and start a durable dependency-aware mission, parameters: [{ $ref: '#/components/parameters/UserId' }, { $ref: '#/components/parameters/IdempotencyKey' }], responses: { '201': { description: Mission started } } }
249
+ /missions/{missionId}:
250
+ get: { summary: Get an autonomous mission, parameters: [{ $ref: '#/components/parameters/UserId' }, { $ref: '#/components/parameters/MissionId' }], responses: { '200': { description: Mission record } } }
251
+ /missions/{missionId}/events:
252
+ get: { summary: List mission lifecycle events, parameters: [{ $ref: '#/components/parameters/UserId' }, { $ref: '#/components/parameters/MissionId' }], responses: { '200': { description: Mission events } } }
253
+ post: { summary: Resume a mission for its exact expected provider event, parameters: [{ $ref: '#/components/parameters/UserId' }, { $ref: '#/components/parameters/MissionId' }, { $ref: '#/components/parameters/IdempotencyKey' }], responses: { '202': { description: Continuation queued } } }
254
+ /missions/{missionId}/events/signed:
255
+ post: { summary: Verify and ingest a signed provider event for a mission, parameters: [{ $ref: '#/components/parameters/UserId' }, { $ref: '#/components/parameters/MissionId' }], responses: { '202': { description: Continuation queued } } }
256
+ /missions/{missionId}/proof:
257
+ get: { summary: Get bounded mission proof with budget, evidence, and verification, parameters: [{ $ref: '#/components/parameters/UserId' }, { $ref: '#/components/parameters/MissionId' }], responses: { '200': { description: Mission proof } } }
258
+ /missions/{missionId}/evidence:
259
+ post: { summary: Attach verified evidence records to a mission, parameters: [{ $ref: '#/components/parameters/UserId' }, { $ref: '#/components/parameters/MissionId' }], responses: { '200': { description: Updated mission } } }
260
+ /missions/{missionId}/verify:
261
+ post: { summary: Verify a mission definition of done, parameters: [{ $ref: '#/components/parameters/UserId' }, { $ref: '#/components/parameters/MissionId' }], responses: { '200': { description: Verification result } } }
262
+ /missions/{missionId}/repair:
263
+ post: { summary: Move a mission into an explicit repair state, parameters: [{ $ref: '#/components/parameters/UserId' }, { $ref: '#/components/parameters/MissionId' }], responses: { '200': { description: Repair state } } }
264
+ /missions/{missionId}/pause:
265
+ post: { summary: Pause a mission and cancel active slices, parameters: [{ $ref: '#/components/parameters/UserId' }, { $ref: '#/components/parameters/MissionId' }], responses: { '200': { description: Paused mission } } }
266
+ /missions/{missionId}/resume:
267
+ post: { summary: Resume a paused, blocked, or failed mission, parameters: [{ $ref: '#/components/parameters/UserId' }, { $ref: '#/components/parameters/MissionId' }], responses: { '200': { description: Resumed mission } } }
268
+ /missions/{missionId}/replan:
269
+ post: { summary: Replace the unfinished portion of a mission plan while preserving completed work, parameters: [{ $ref: '#/components/parameters/UserId' }, { $ref: '#/components/parameters/MissionId' }], responses: { '200': { description: Replanned mission } } }
270
+ /missions/{missionId}/steps/{stepId}/complete:
271
+ post: { summary: Complete the currently executable mission step, parameters: [{ $ref: '#/components/parameters/UserId' }, { $ref: '#/components/parameters/MissionId' }, { name: stepId, in: path, required: true, schema: { type: string } }], responses: { '200': { description: Updated mission } } }
272
+ /missions/{missionId}/cancel:
273
+ post: { summary: Cancel a mission and its active tasks, parameters: [{ $ref: '#/components/parameters/UserId' }, { $ref: '#/components/parameters/MissionId' }], responses: { '200': { description: Cancelled mission } } }
274
+ /context:
275
+ get: { summary: Select owner-scoped operating context, parameters: [{ $ref: '#/components/parameters/UserId' }], responses: { '200': { description: Context nodes and prompt projection } } }
276
+ post: { summary: Save a sensitivity-aware context node, parameters: [{ $ref: '#/components/parameters/UserId' }, { $ref: '#/components/parameters/IdempotencyKey' }], responses: { '201': { description: Context node } } }
277
+ /departments/catalog:
278
+ get: { summary: List supported department operating templates, parameters: [{ $ref: '#/components/parameters/UserId' }], responses: { '200': { description: Department catalogue } } }
279
+ /departments:
280
+ get: { summary: List owner department spaces, parameters: [{ $ref: '#/components/parameters/UserId' }], responses: { '200': { description: Department spaces } } }
281
+ post: { summary: Provision a department space, parameters: [{ $ref: '#/components/parameters/UserId' }, { $ref: '#/components/parameters/IdempotencyKey' }], responses: { '201': { description: Department space } } }
282
+ /departments/{department}/handoffs:
283
+ post: { summary: Create a typed department work packet, parameters: [{ $ref: '#/components/parameters/UserId' }, { name: department, in: path, required: true, schema: { type: string } }], responses: { '201': { description: Handoff packet } } }
284
+ /outcomes:
285
+ get: { summary: List governed business outcome packages, parameters: [{ $ref: '#/components/parameters/UserId' }], responses: { '200': { description: Outcome packages } } }
286
+ /outcomes/{slug}:
287
+ get: { summary: Get one business outcome package, parameters: [{ $ref: '#/components/parameters/UserId' }, { name: slug, in: path, required: true, schema: { type: string } }], responses: { '200': { description: Outcome package } } }
288
+ /outcomes/{slug}/plan:
289
+ post: { summary: Plan a governed outcome from supplied business inputs, parameters: [{ $ref: '#/components/parameters/UserId' }, { name: slug, in: path, required: true, schema: { type: string } }], responses: { '200': { description: Outcome plan } } }
232
290
  /scratchpad:
233
291
  get: { summary: Search private scratchpad notes, parameters: [{ $ref: '#/components/parameters/UserId' }], responses: { '200': { description: OK } } }
234
292
  delete: { summary: Clear private scratchpad notes, parameters: [{ $ref: '#/components/parameters/UserId' }], responses: { '204': { description: Cleared } } }
@@ -250,3 +308,4 @@ components:
250
308
  parameters:
251
309
  UserId: { name: X-Chusky-User-Id, in: header, required: true, schema: { type: string, maxLength: 200 } }
252
310
  IdempotencyKey: { name: Idempotency-Key, in: header, required: false, schema: { type: string, maxLength: 255 } }
311
+ MissionId: { name: missionId, in: path, required: true, schema: { type: string, pattern: '^mis_[A-Za-z0-9_-]+$' } }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
- {
2
- "name": "@chusky/sdk",
3
- "version": "0.2.0",
1
+ {
2
+ "name": "@chusky/sdk",
3
+ "version": "0.3.1",
4
4
  "description": "TypeScript SDK for Chusky's developer API.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -14,11 +14,11 @@
14
14
  "publishConfig": {
15
15
  "access": "public"
16
16
  },
17
- "type": "module",
18
- "sideEffects": false,
19
- "main": "./dist/index.js",
20
- "types": "./dist/index.d.ts",
21
- "exports": {
17
+ "type": "module",
18
+ "sideEffects": false,
19
+ "main": "./dist/index.js",
20
+ "types": "./dist/index.d.ts",
21
+ "exports": {
22
22
  ".": {
23
23
  "types": "./dist/index.d.ts",
24
24
  "import": "./dist/index.js"
@@ -27,28 +27,29 @@
27
27
  "types": "./dist/widget.d.ts",
28
28
  "import": "./dist/widget.js"
29
29
  }
30
- },
31
- "files": [
32
- "dist",
33
- "README.md",
30
+ },
31
+ "files": [
32
+ "dist",
33
+ "README.md",
34
+ "examples",
34
35
  "docs",
35
36
  "docs.json",
36
37
  "openapi.yaml",
37
- "CHANGELOG.md",
38
- "LICENSE"
39
- ],
40
- "engines": {
41
- "node": ">=18"
42
- },
43
- "scripts": {
44
- "build": "tsc -p tsconfig.json",
45
- "typecheck": "tsc --noEmit -p tsconfig.json",
46
- "test": "tsx --test --test-concurrency=1 tests/*.test.ts",
47
- "prepublishOnly": "npm run typecheck && npm run build && npm test"
48
- },
49
- "devDependencies": {
50
- "@types/node": "^22.0.0",
51
- "tsx": "^4.19.0",
52
- "typescript": "^5.7.0"
53
- }
54
- }
38
+ "CHANGELOG.md",
39
+ "LICENSE"
40
+ ],
41
+ "engines": {
42
+ "node": ">=18"
43
+ },
44
+ "scripts": {
45
+ "build": "tsc -p tsconfig.json",
46
+ "typecheck": "tsc --noEmit -p tsconfig.json",
47
+ "test": "tsx --test --test-concurrency=1 tests/*.test.ts",
48
+ "prepublishOnly": "npm run typecheck && npm run build && npm test"
49
+ },
50
+ "devDependencies": {
51
+ "@types/node": "^22.0.0",
52
+ "tsx": "^4.19.0",
53
+ "typescript": "^5.7.0"
54
+ }
55
+ }