@agentpress/sdk 0.5.4 → 0.5.5
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/README.md +69 -7
- package/dist/index.cjs +114 -53
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +127 -48
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +127 -48
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +114 -53
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@ TypeScript SDK for the AgentPress webhook API.
|
|
|
5
5
|
- **Package:** `@agentpress/sdk` (npm, public)
|
|
6
6
|
- **Output:** Dual ESM (`.mjs`) + CJS (`.cjs`)
|
|
7
7
|
- **Node:** >= 22.0.0
|
|
8
|
-
- **Dependencies:** `jose` for Partner MCP JWT verification; webhook signing uses `node:crypto`
|
|
8
|
+
- **Dependencies:** `jose` for Partner MCP JWT verification; action webhook signing uses `node:crypto`
|
|
9
9
|
|
|
10
10
|
## Installation
|
|
11
11
|
|
|
@@ -35,7 +35,7 @@ await client.webhooks.send({
|
|
|
35
35
|
|
|
36
36
|
```typescript
|
|
37
37
|
const client = new AgentPress({
|
|
38
|
-
webhookSecret: "whsec_...", // Required for
|
|
38
|
+
webhookSecret: "whsec_...", // Required for default Svix sends, verify, and action management
|
|
39
39
|
baseUrl: "https://api.agent.press", // Default
|
|
40
40
|
timeout: 30_000, // Default, in milliseconds
|
|
41
41
|
org: "default-org", // Default org slug
|
|
@@ -45,7 +45,9 @@ const client = new AgentPress({
|
|
|
45
45
|
});
|
|
46
46
|
```
|
|
47
47
|
|
|
48
|
-
All options are optional. `webhookSecret` is required when calling `send
|
|
48
|
+
All options are optional. `webhookSecret` is required when calling `send` with
|
|
49
|
+
the default Svix signing mode, HMAC/shared-token sends without an auth override,
|
|
50
|
+
`verify`, `verifyOrThrow`, `constructEvent`, or action management methods.
|
|
49
51
|
|
|
50
52
|
**Validation rules:**
|
|
51
53
|
- `webhookSecret` must start with `"whsec_"` or a `ConfigurationError` is thrown
|
|
@@ -58,7 +60,10 @@ The client exposes `client.webhooks`, `client.actions`, `client.userApprovals`,
|
|
|
58
60
|
|
|
59
61
|
### client.webhooks.send(params)
|
|
60
62
|
|
|
61
|
-
|
|
63
|
+
Sends an arbitrary action webhook payload. By default the SDK uses Svix-style
|
|
64
|
+
signing with the client's `webhookSecret`, which matches the default
|
|
65
|
+
verification scheme for new AgentPress action webhooks. Pass `auth` to match
|
|
66
|
+
another verification scheme configured for the action webhook.
|
|
62
67
|
|
|
63
68
|
```typescript
|
|
64
69
|
const response = await client.webhooks.send({
|
|
@@ -73,26 +78,55 @@ const response = await client.webhooks.send({
|
|
|
73
78
|
|
|
74
79
|
**Endpoint:** `POST {baseUrl}/webhooks/actions/{org}/{action}`
|
|
75
80
|
|
|
81
|
+
`action` is the action webhook identifier shown on the AgentPress webhook detail
|
|
82
|
+
page. The older `/webhooks/ingest/{org}/{identifier}` listener endpoint is kept
|
|
83
|
+
only for compatibility APIs; new integrations should use `send()`.
|
|
84
|
+
|
|
76
85
|
**Params (`WebhookSendParams`):**
|
|
77
86
|
|
|
78
87
|
| Field | Type | Description |
|
|
79
88
|
|-------|------|-------------|
|
|
80
89
|
| `action` | `string` | Webhook action identifier (used in URL path) |
|
|
81
90
|
| `payload` | `Record<string, unknown>` | Arbitrary JSON payload to sign and send |
|
|
91
|
+
| `auth` | `WebhookSendAuth` | Optional verification override. Defaults to Svix with the client's `webhookSecret`. |
|
|
82
92
|
|
|
83
93
|
**Returns (`WebhookResponse`):**
|
|
84
94
|
|
|
85
95
|
```typescript
|
|
86
96
|
{
|
|
87
97
|
success: boolean;
|
|
88
|
-
actionId?: string; // ID of created action
|
|
98
|
+
actionId?: string; // ID of created or existing action
|
|
89
99
|
alreadyExists?: boolean; // Duplicate externalId
|
|
90
|
-
skipped?: boolean;
|
|
100
|
+
skipped?: boolean; // Compatibility field for skipped deliveries
|
|
101
|
+
buffered?: boolean; // HTTP 202: accepted but waiting on configuration
|
|
102
|
+
eventId?: string; // Buffered/skipped webhook_events row
|
|
103
|
+
reason?: string; // Machine-readable buffered/skipped reason
|
|
91
104
|
data?: Record<string, unknown>;
|
|
92
105
|
}
|
|
93
106
|
```
|
|
94
107
|
|
|
95
|
-
|
|
108
|
+
Configuration/resolution gaps such as a missing action rule, disabled rule, or
|
|
109
|
+
unresolved user return HTTP `202` with `buffered: true` instead of throwing.
|
|
110
|
+
The delivery is persisted and appears in the Actions ledger for debugging and
|
|
111
|
+
retry.
|
|
112
|
+
|
|
113
|
+
**Compatibility notes:**
|
|
114
|
+
- Existing `client.webhooks.send()` callers continue to work for action
|
|
115
|
+
webhooks using the default Svix verification scheme. No SDK method was
|
|
116
|
+
removed.
|
|
117
|
+
- If the webhook is configured for `hmac_sha256`, `shared_token`, or `none`,
|
|
118
|
+
pass the matching `auth` option. Manual sender scripts must send the matching
|
|
119
|
+
headers.
|
|
120
|
+
- Existing listener aliases and `/webhooks/ingest/{org}/{identifier}` remain
|
|
121
|
+
compatibility paths, but new integrations should use `send()` and
|
|
122
|
+
`/webhooks/actions/{org}/{action}`.
|
|
123
|
+
- `202` with `buffered: true` is accepted delivery without a created action yet;
|
|
124
|
+
check `response.buffered` before using `response.actionId`.
|
|
125
|
+
- For `verificationScheme: "none"`, the URL is the credential and can rotate
|
|
126
|
+
when the webhook is created, switched to `none`, or rotated. Copy the latest
|
|
127
|
+
URL after those operations.
|
|
128
|
+
|
|
129
|
+
**Throws:** `ConfigurationError` (missing required secret/token), `HttpError` (non-2xx), `TimeoutError`
|
|
96
130
|
|
|
97
131
|
**Signing details:** Each request gets three headers automatically:
|
|
98
132
|
- `svix-id` -- unique message ID (`msg_<uuid>`)
|
|
@@ -101,6 +135,34 @@ const response = await client.webhooks.send({
|
|
|
101
135
|
|
|
102
136
|
The signature input is `${msgId}.${timestamp}.${body}`.
|
|
103
137
|
|
|
138
|
+
```typescript
|
|
139
|
+
await client.webhooks.send({
|
|
140
|
+
action: "billing_events",
|
|
141
|
+
auth: {
|
|
142
|
+
scheme: "hmac_sha256",
|
|
143
|
+
secret: process.env.AGENTPRESS_WEBHOOK_SECRET!,
|
|
144
|
+
},
|
|
145
|
+
payload: {
|
|
146
|
+
eventType: "invoice.created",
|
|
147
|
+
data: { invoiceId: "inv_123" },
|
|
148
|
+
},
|
|
149
|
+
});
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
**Auth schemes:**
|
|
153
|
+
|
|
154
|
+
| Scheme | Params | Notes |
|
|
155
|
+
|--------|--------|-------|
|
|
156
|
+
| `svix` | `{ secret?, msgId?, timestamp? }` | Adds `svix-id`, `svix-timestamp`, and `svix-signature`. Uses `webhookSecret` when `secret` is omitted. |
|
|
157
|
+
| `hmac_sha256` | `{ secret?, timestamp? }` | Adds `x-webhook-timestamp` and `x-webhook-signature` using `signHmacWebhookRequest`. Uses `webhookSecret` when `secret` is omitted. |
|
|
158
|
+
| `shared_token` | `{ token? }` | Adds `x-webhook-token`. Uses `webhookSecret` when `token` is omitted. |
|
|
159
|
+
| `none` | none | Sends no verification headers. Only use for capability-URL action webhooks intentionally configured with no header verification. |
|
|
160
|
+
|
|
161
|
+
For `verificationScheme: "none"`, the action webhook URL itself is the
|
|
162
|
+
credential. Creating, switching, or rotating such a webhook may mint a new
|
|
163
|
+
unguessable `webhookIdentifier`; copy the latest URL from AgentPress before
|
|
164
|
+
updating manual sender scripts.
|
|
165
|
+
|
|
104
166
|
---
|
|
105
167
|
|
|
106
168
|
### client.webhooks.verify(params)
|
package/dist/index.cjs
CHANGED
|
@@ -607,7 +607,57 @@ function validatePayload(raw) {
|
|
|
607
607
|
return event;
|
|
608
608
|
}
|
|
609
609
|
//#endregion
|
|
610
|
+
//#region src/webhooks/actionWebhookSigning.ts
|
|
611
|
+
/**
|
|
612
|
+
* Sign an outbound request for an AgentPress action webhook that uses the
|
|
613
|
+
* `hmac_sha256` verification scheme
|
|
614
|
+
* (`POST /webhooks/actions/:org/:identifier`).
|
|
615
|
+
*
|
|
616
|
+
* Produces the two headers AgentPress verifies:
|
|
617
|
+
*
|
|
618
|
+
* - `x-webhook-timestamp` — unix seconds; AgentPress rejects timestamps more
|
|
619
|
+
* than 5 minutes from its own clock.
|
|
620
|
+
* - `x-webhook-signature` — `v1=<hex HMAC-SHA256 of "${timestamp}.${rawBody}">`.
|
|
621
|
+
*
|
|
622
|
+
* Send the exact `rawBody` string you signed — any re-serialization after
|
|
623
|
+
* signing (re-ordered keys, whitespace changes) invalidates the signature.
|
|
624
|
+
*
|
|
625
|
+
* @example
|
|
626
|
+
* ```ts
|
|
627
|
+
* const rawBody = JSON.stringify({ eventType: "review.created", data: {...} });
|
|
628
|
+
* const { headers } = signHmacWebhookRequest({ secret, rawBody });
|
|
629
|
+
* await fetch(actionWebhookUrl, {
|
|
630
|
+
* method: "POST",
|
|
631
|
+
* body: rawBody,
|
|
632
|
+
* headers: { "content-type": "application/json", ...headers },
|
|
633
|
+
* });
|
|
634
|
+
* ```
|
|
635
|
+
*/
|
|
636
|
+
function signHmacWebhookRequest(params) {
|
|
637
|
+
const timestamp = Math.floor(params.timestamp ?? Date.now() / 1e3).toString();
|
|
638
|
+
return { headers: {
|
|
639
|
+
"x-webhook-timestamp": timestamp,
|
|
640
|
+
"x-webhook-signature": `v1=${(0, node_crypto.createHmac)("sha256", params.secret).update(`${timestamp}.${params.rawBody}`).digest("hex")}`
|
|
641
|
+
} };
|
|
642
|
+
}
|
|
643
|
+
/**
|
|
644
|
+
* Build the auth header for an AgentPress action webhook that uses the
|
|
645
|
+
* `shared_token` verification scheme. AgentPress also accepts
|
|
646
|
+
* `Authorization: Bearer <token>`; this helper uses the dedicated
|
|
647
|
+
* `x-webhook-token` header so it never collides with other auth middleware.
|
|
648
|
+
*/
|
|
649
|
+
function sharedTokenHeaders(token) {
|
|
650
|
+
return { "x-webhook-token": token };
|
|
651
|
+
}
|
|
652
|
+
//#endregion
|
|
610
653
|
//#region src/webhooks/client.ts
|
|
654
|
+
function pathSegment(value) {
|
|
655
|
+
return encodeURIComponent(value);
|
|
656
|
+
}
|
|
657
|
+
function requireSecret(scheme, secret) {
|
|
658
|
+
if (!secret) throw new ConfigurationError(`webhookSecret or auth.${scheme === "shared_token" ? "token" : "secret"} is required for ${scheme} action webhook sends`);
|
|
659
|
+
return secret;
|
|
660
|
+
}
|
|
611
661
|
var WebhooksClient = class {
|
|
612
662
|
options;
|
|
613
663
|
http;
|
|
@@ -617,7 +667,8 @@ var WebhooksClient = class {
|
|
|
617
667
|
}
|
|
618
668
|
/**
|
|
619
669
|
* Send an arbitrary webhook payload to AgentPress.
|
|
620
|
-
* Signs the payload with
|
|
670
|
+
* Signs the payload with the verification scheme configured on the action
|
|
671
|
+
* webhook. Defaults to Svix-compatible signing.
|
|
621
672
|
*
|
|
622
673
|
* On the happy path the response is synchronous: `{ success: true,
|
|
623
674
|
* actionId, data }`, or `{ success, actionId, alreadyExists: true, data }`
|
|
@@ -630,28 +681,81 @@ var WebhooksClient = class {
|
|
|
630
681
|
* auto-processes once an operator fixes the configuration, so check
|
|
631
682
|
* {@link WebhookResponse.buffered} before relying on `actionId`.
|
|
632
683
|
*
|
|
633
|
-
* @throws ConfigurationError if
|
|
684
|
+
* @throws ConfigurationError if the selected verification scheme needs a
|
|
685
|
+
* secret/token and none is configured
|
|
634
686
|
* @throws HttpError on non-2xx response
|
|
635
687
|
* @throws TimeoutError if request exceeds timeout
|
|
636
688
|
*/
|
|
637
689
|
async send(params) {
|
|
638
|
-
|
|
639
|
-
const path = `/webhooks/actions/${this.options.org}/${params.action}`;
|
|
690
|
+
const path = `/webhooks/actions/${pathSegment(this.options.org)}/${pathSegment(params.action)}`;
|
|
640
691
|
const body = JSON.stringify(params.payload);
|
|
641
|
-
const
|
|
642
|
-
const
|
|
643
|
-
|
|
692
|
+
const auth = params.auth ?? { scheme: "svix" };
|
|
693
|
+
const headers = {};
|
|
694
|
+
if (auth.scheme === "svix") {
|
|
695
|
+
const secret = requireSecret("svix", auth.secret ?? this.options.webhookSecret);
|
|
696
|
+
const msgId = auth.msgId ?? randomMessageId();
|
|
697
|
+
const timestamp = Math.floor(auth.timestamp ?? Date.now() / 1e3);
|
|
698
|
+
Object.assign(headers, {
|
|
699
|
+
"svix-id": msgId,
|
|
700
|
+
"svix-timestamp": String(timestamp),
|
|
701
|
+
"svix-signature": sign(secret, msgId, timestamp, body)
|
|
702
|
+
});
|
|
703
|
+
} else if (auth.scheme === "hmac_sha256") {
|
|
704
|
+
const secret = requireSecret("hmac_sha256", auth.secret ?? this.options.webhookSecret);
|
|
705
|
+
Object.assign(headers, signHmacWebhookRequest({
|
|
706
|
+
secret,
|
|
707
|
+
rawBody: body,
|
|
708
|
+
timestamp: auth.timestamp
|
|
709
|
+
}).headers);
|
|
710
|
+
} else if (auth.scheme === "shared_token") {
|
|
711
|
+
const token = requireSecret("shared_token", auth.token ?? this.options.webhookSecret);
|
|
712
|
+
Object.assign(headers, sharedTokenHeaders(token));
|
|
713
|
+
}
|
|
644
714
|
return this.http.request(path, {
|
|
645
715
|
method: "POST",
|
|
646
716
|
body,
|
|
647
|
-
headers
|
|
717
|
+
headers
|
|
718
|
+
});
|
|
719
|
+
}
|
|
720
|
+
/**
|
|
721
|
+
* Send a payload to the legacy Actions listener ingestion endpoint
|
|
722
|
+
* (`POST /webhooks/ingest/:org/:identifier`).
|
|
723
|
+
*
|
|
724
|
+
* New integrations should prefer {@link send}; this method is kept for
|
|
725
|
+
* existing listener integrations and SDK patch compatibility.
|
|
726
|
+
*/
|
|
727
|
+
async sendToActionsListener(params) {
|
|
728
|
+
const path = `/webhooks/ingest/${pathSegment(this.options.org)}/${pathSegment(params.identifier)}`;
|
|
729
|
+
const body = JSON.stringify(params.payload);
|
|
730
|
+
const headers = {};
|
|
731
|
+
if (params.auth.scheme === "svix") {
|
|
732
|
+
const msgId = params.auth.msgId ?? randomMessageId();
|
|
733
|
+
const timestamp = Math.floor(params.auth.timestamp ?? Date.now() / 1e3);
|
|
734
|
+
Object.assign(headers, {
|
|
648
735
|
"svix-id": msgId,
|
|
649
736
|
"svix-timestamp": String(timestamp),
|
|
650
|
-
"svix-signature":
|
|
651
|
-
}
|
|
737
|
+
"svix-signature": sign(params.auth.secret, msgId, timestamp, body)
|
|
738
|
+
});
|
|
739
|
+
} else if (params.auth.scheme === "hmac_sha256") Object.assign(headers, signHmacWebhookRequest({
|
|
740
|
+
secret: params.auth.secret,
|
|
741
|
+
rawBody: body,
|
|
742
|
+
timestamp: params.auth.timestamp
|
|
743
|
+
}).headers);
|
|
744
|
+
else if (params.auth.scheme === "shared_token") Object.assign(headers, sharedTokenHeaders(params.auth.token));
|
|
745
|
+
return this.http.request(path, {
|
|
746
|
+
method: "POST",
|
|
747
|
+
body,
|
|
748
|
+
headers
|
|
652
749
|
});
|
|
653
750
|
}
|
|
654
751
|
/**
|
|
752
|
+
* @deprecated Use {@link sendToActionsListener}. Kept as a compatibility
|
|
753
|
+
* alias for integrations created before the Actions listener naming update.
|
|
754
|
+
*/
|
|
755
|
+
async sendToListener(params) {
|
|
756
|
+
return this.sendToActionsListener(params);
|
|
757
|
+
}
|
|
758
|
+
/**
|
|
655
759
|
* Verify an inbound Svix webhook signature.
|
|
656
760
|
*
|
|
657
761
|
* @returns true if valid, false if invalid or expired
|
|
@@ -793,49 +897,6 @@ const ACTION_EVENT_TYPES = [
|
|
|
793
897
|
"action.expired"
|
|
794
898
|
];
|
|
795
899
|
//#endregion
|
|
796
|
-
//#region src/webhooks/ingestSigning.ts
|
|
797
|
-
/**
|
|
798
|
-
* Sign an outbound request for an AgentPress inbound webhook listener that
|
|
799
|
-
* uses the `hmac_sha256` verification scheme
|
|
800
|
-
* (`POST /webhooks/ingest/:org/:identifier`).
|
|
801
|
-
*
|
|
802
|
-
* Produces the two headers AgentPress verifies:
|
|
803
|
-
*
|
|
804
|
-
* - `x-webhook-timestamp` — unix seconds; AgentPress rejects timestamps more
|
|
805
|
-
* than 5 minutes from its own clock.
|
|
806
|
-
* - `x-webhook-signature` — `v1=<hex HMAC-SHA256 of "${timestamp}.${rawBody}">`.
|
|
807
|
-
*
|
|
808
|
-
* Send the exact `rawBody` string you signed — any re-serialization after
|
|
809
|
-
* signing (re-ordered keys, whitespace changes) invalidates the signature.
|
|
810
|
-
*
|
|
811
|
-
* @example
|
|
812
|
-
* ```ts
|
|
813
|
-
* const rawBody = JSON.stringify({ eventType: "review.created", data: {...} });
|
|
814
|
-
* const { headers } = signHmacWebhookRequest({ secret, rawBody });
|
|
815
|
-
* await fetch(ingestUrl, {
|
|
816
|
-
* method: "POST",
|
|
817
|
-
* body: rawBody,
|
|
818
|
-
* headers: { "content-type": "application/json", ...headers },
|
|
819
|
-
* });
|
|
820
|
-
* ```
|
|
821
|
-
*/
|
|
822
|
-
function signHmacWebhookRequest(params) {
|
|
823
|
-
const timestamp = Math.floor(params.timestamp ?? Date.now() / 1e3).toString();
|
|
824
|
-
return { headers: {
|
|
825
|
-
"x-webhook-timestamp": timestamp,
|
|
826
|
-
"x-webhook-signature": `v1=${(0, node_crypto.createHmac)("sha256", params.secret).update(`${timestamp}.${params.rawBody}`).digest("hex")}`
|
|
827
|
-
} };
|
|
828
|
-
}
|
|
829
|
-
/**
|
|
830
|
-
* Build the auth header for an AgentPress inbound webhook listener that uses
|
|
831
|
-
* the `shared_token` verification scheme. AgentPress also accepts
|
|
832
|
-
* `Authorization: Bearer <token>`; this helper uses the dedicated
|
|
833
|
-
* `x-webhook-token` header so it never collides with other auth middleware.
|
|
834
|
-
*/
|
|
835
|
-
function sharedTokenHeaders(token) {
|
|
836
|
-
return { "x-webhook-token": token };
|
|
837
|
-
}
|
|
838
|
-
//#endregion
|
|
839
900
|
exports.ACTION_EVENT_TYPES = ACTION_EVENT_TYPES;
|
|
840
901
|
exports.ActionsClient = ActionsClient;
|
|
841
902
|
exports.AgentPress = AgentPress;
|