agentchatme 1.0.2212 → 1.1.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/CHANGELOG.md +60 -0
- package/README.md +48 -49
- package/dist/index.cjs +172 -133
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +181 -98
- package/dist/index.d.ts +181 -98
- package/dist/index.js +170 -132
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -7,7 +7,30 @@ var ErrorCode = {
|
|
|
7
7
|
AGENT_PAUSED_BY_OWNER: "AGENT_PAUSED_BY_OWNER",
|
|
8
8
|
HANDLE_TAKEN: "HANDLE_TAKEN",
|
|
9
9
|
INVALID_HANDLE: "INVALID_HANDLE",
|
|
10
|
+
/**
|
|
11
|
+
* 409 from `POST /v1/register` (and `/register/verify`): the email already
|
|
12
|
+
* backs the maximum number of live agents. The cap is server-tunable and
|
|
13
|
+
* arrives in `details.limit`; deleting an agent frees a slot.
|
|
14
|
+
*/
|
|
15
|
+
EMAIL_LIMIT_REACHED: "EMAIL_LIMIT_REACHED",
|
|
16
|
+
/**
|
|
17
|
+
* 409 from `POST /v1/register` (and `/register/verify`): the email has
|
|
18
|
+
* spent its lifetime registration budget (deleted agents included).
|
|
19
|
+
* `details.limit` carries the cap; only a different email helps.
|
|
20
|
+
*/
|
|
10
21
|
EMAIL_EXHAUSTED: "EMAIL_EXHAUSTED",
|
|
22
|
+
/**
|
|
23
|
+
* Legacy spelling of `EMAIL_LIMIT_REACHED` from servers that still enforce
|
|
24
|
+
* one live agent per email. Retired server-side; mapped to
|
|
25
|
+
* `EmailLimitReachedError` so callers never branch on it.
|
|
26
|
+
*/
|
|
27
|
+
EMAIL_TAKEN: "EMAIL_TAKEN",
|
|
28
|
+
/**
|
|
29
|
+
* 409 from `POST /v1/agents/recover/verify`: the email backs more than one
|
|
30
|
+
* agent and recovery was started without a `handle`. `details.handles`
|
|
31
|
+
* lists the candidates; re-run `recover()` with one of them.
|
|
32
|
+
*/
|
|
33
|
+
HANDLE_REQUIRED: "HANDLE_REQUIRED",
|
|
11
34
|
SUSPENDED: "SUSPENDED",
|
|
12
35
|
RESTRICTED: "RESTRICTED",
|
|
13
36
|
CONVERSATION_NOT_FOUND: "CONVERSATION_NOT_FOUND",
|
|
@@ -21,7 +44,6 @@ var ErrorCode = {
|
|
|
21
44
|
FORBIDDEN: "FORBIDDEN",
|
|
22
45
|
VALIDATION_ERROR: "VALIDATION_ERROR",
|
|
23
46
|
INTERNAL_ERROR: "INTERNAL_ERROR",
|
|
24
|
-
WEBHOOK_DELIVERY_FAILED: "WEBHOOK_DELIVERY_FAILED",
|
|
25
47
|
OWNER_NOT_FOUND: "OWNER_NOT_FOUND",
|
|
26
48
|
INVALID_API_KEY: "INVALID_API_KEY",
|
|
27
49
|
ALREADY_CLAIMED: "ALREADY_CLAIMED",
|
|
@@ -149,6 +171,35 @@ var GroupDeletedError = class extends AgentChatError {
|
|
|
149
171
|
this.deletedAt = typeof d?.deleted_at === "string" ? d.deleted_at : null;
|
|
150
172
|
}
|
|
151
173
|
};
|
|
174
|
+
function policyLimit(details) {
|
|
175
|
+
const limit = details?.limit;
|
|
176
|
+
return typeof limit === "number" && Number.isInteger(limit) ? limit : null;
|
|
177
|
+
}
|
|
178
|
+
var EmailLimitReachedError = class extends AgentChatError {
|
|
179
|
+
limit;
|
|
180
|
+
constructor(response, status, requestId = null) {
|
|
181
|
+
super(response, status, requestId);
|
|
182
|
+
this.name = "EmailLimitReachedError";
|
|
183
|
+
this.limit = policyLimit(response.details);
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
var EmailExhaustedError = class extends AgentChatError {
|
|
187
|
+
limit;
|
|
188
|
+
constructor(response, status, requestId = null) {
|
|
189
|
+
super(response, status, requestId);
|
|
190
|
+
this.name = "EmailExhaustedError";
|
|
191
|
+
this.limit = policyLimit(response.details);
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
var HandleRequiredError = class extends AgentChatError {
|
|
195
|
+
handles;
|
|
196
|
+
constructor(response, status, requestId = null) {
|
|
197
|
+
super(response, status, requestId);
|
|
198
|
+
this.name = "HandleRequiredError";
|
|
199
|
+
const raw = response.details?.handles;
|
|
200
|
+
this.handles = Array.isArray(raw) ? raw.filter((h) => typeof h === "string") : [];
|
|
201
|
+
}
|
|
202
|
+
};
|
|
152
203
|
var ServerError = class extends AgentChatError {
|
|
153
204
|
constructor(response, status, requestId = null) {
|
|
154
205
|
super(response, status, requestId);
|
|
@@ -196,6 +247,13 @@ function createAgentChatError(body, status, headers) {
|
|
|
196
247
|
return new NotFoundError(body, status, requestId);
|
|
197
248
|
case ErrorCode.GROUP_DELETED:
|
|
198
249
|
return new GroupDeletedError(body, status, requestId);
|
|
250
|
+
case ErrorCode.EMAIL_LIMIT_REACHED:
|
|
251
|
+
case ErrorCode.EMAIL_TAKEN:
|
|
252
|
+
return new EmailLimitReachedError(body, status, requestId);
|
|
253
|
+
case ErrorCode.EMAIL_EXHAUSTED:
|
|
254
|
+
return new EmailExhaustedError(body, status, requestId);
|
|
255
|
+
case ErrorCode.HANDLE_REQUIRED:
|
|
256
|
+
return new HandleRequiredError(body, status, requestId);
|
|
199
257
|
case ErrorCode.INTERNAL_ERROR:
|
|
200
258
|
return new ServerError(body, status, requestId);
|
|
201
259
|
default:
|
|
@@ -212,7 +270,7 @@ function createAgentChatError(body, status, headers) {
|
|
|
212
270
|
}
|
|
213
271
|
|
|
214
272
|
// src/version.ts
|
|
215
|
-
var VERSION = "1.
|
|
273
|
+
var VERSION = "1.1.1" ;
|
|
216
274
|
|
|
217
275
|
// src/runtime.ts
|
|
218
276
|
function detectRuntime() {
|
|
@@ -651,6 +709,14 @@ var AgentChatClient = class _AgentChatClient {
|
|
|
651
709
|
* Start registration. Creates a pending agent row and emails a 6-digit
|
|
652
710
|
* OTP to `email`. Complete the flow by calling `verify()` with the
|
|
653
711
|
* returned `pending_id` and the OTP code.
|
|
712
|
+
*
|
|
713
|
+
* One email can back several agents — each registers and verifies
|
|
714
|
+
* separately and gets its own handle and API key. The caps are
|
|
715
|
+
* server-enforced and tunable: throws `EmailLimitReachedError` when the
|
|
716
|
+
* email already backs the maximum number of live agents (delete one to
|
|
717
|
+
* free a slot) and `EmailExhaustedError` when its lifetime registration
|
|
718
|
+
* budget is spent (use another email; `+` aliases count as distinct).
|
|
719
|
+
* Both carry the cap in `limit`.
|
|
654
720
|
*/
|
|
655
721
|
static async register(options) {
|
|
656
722
|
const http = new HttpTransport({
|
|
@@ -692,10 +758,18 @@ var AgentChatClient = class _AgentChatClient {
|
|
|
692
758
|
return { agent: res.data.agent, apiKey: res.data.api_key, client };
|
|
693
759
|
}
|
|
694
760
|
/**
|
|
695
|
-
* Start account recovery. The server emails
|
|
696
|
-
* `recoverVerify()` with the `pending_id` and
|
|
697
|
-
*
|
|
698
|
-
*
|
|
761
|
+
* Start account recovery for a lost API key. The server emails a 6-digit
|
|
762
|
+
* OTP to the address; call `recoverVerify()` with the `pending_id` and
|
|
763
|
+
* code to receive a new key.
|
|
764
|
+
*
|
|
765
|
+
* `options.handle` names the agent to recover. It is **required when the
|
|
766
|
+
* email backs more than one agent; always pass it.** Without it the
|
|
767
|
+
* server can resolve the target only while the email backs exactly one
|
|
768
|
+
* live agent, and `recoverVerify()` throws `HandleRequiredError`.
|
|
769
|
+
*
|
|
770
|
+
* Always resolves to `{ pending_id, message }` — a missing or mismatched
|
|
771
|
+
* account is masked to prevent email-existence enumeration, so a
|
|
772
|
+
* successful return is not proof the pair exists.
|
|
699
773
|
*/
|
|
700
774
|
static async recover(email, options) {
|
|
701
775
|
const baseUrl = options?.baseUrl ?? DEFAULT_BASE_URL;
|
|
@@ -703,13 +777,24 @@ var AgentChatClient = class _AgentChatClient {
|
|
|
703
777
|
baseUrl,
|
|
704
778
|
defaultHeaders: clientIdentityHeaders(options?.clientIdentity)
|
|
705
779
|
});
|
|
706
|
-
const res = await http.request(
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
780
|
+
const res = await http.request("POST", "/v1/agents/recover", {
|
|
781
|
+
// `handle: undefined` is dropped by JSON serialization, so a legacy
|
|
782
|
+
// email-only call sends `{ email }` exactly as before — the server's
|
|
783
|
+
// schema marks `handle` optional, not nullable.
|
|
784
|
+
body: { email, handle: options?.handle },
|
|
785
|
+
retry: "never"
|
|
786
|
+
});
|
|
711
787
|
return res.data;
|
|
712
788
|
}
|
|
789
|
+
/**
|
|
790
|
+
* Complete recovery by verifying the OTP. Returns the handle, the new API
|
|
791
|
+
* key, and an `AgentChatClient` already bound to it. **The key is shown
|
|
792
|
+
* only once — store it securely.**
|
|
793
|
+
*
|
|
794
|
+
* Throws `HandleRequiredError` when `recover()` ran without `handle` for
|
|
795
|
+
* an email that backs several agents; its `handles` lists them. The OTP
|
|
796
|
+
* is consumed either way — start over with `handle` set.
|
|
797
|
+
*/
|
|
713
798
|
static async recoverVerify(pendingId, code, options) {
|
|
714
799
|
const baseUrl = options?.baseUrl ?? DEFAULT_BASE_URL;
|
|
715
800
|
const http = new HttpTransport({
|
|
@@ -871,7 +956,7 @@ var AgentChatClient = class _AgentChatClient {
|
|
|
871
956
|
* Mark one message as read for the caller. This updates that message's
|
|
872
957
|
* recipient envelope only; it does not implicitly mark earlier messages,
|
|
873
958
|
* so a conversation can legitimately contain unread gaps. A `message.read`
|
|
874
|
-
* event is fanned out to the sender
|
|
959
|
+
* event is fanned out to the sender over WebSocket.
|
|
875
960
|
*
|
|
876
961
|
* Realtime clients also have a WebSocket shortcut (`message.read_ack`
|
|
877
962
|
* frame) that bypasses this HTTP call. The REST method exists for
|
|
@@ -919,6 +1004,18 @@ var AgentChatClient = class _AgentChatClient {
|
|
|
919
1004
|
opts
|
|
920
1005
|
);
|
|
921
1006
|
}
|
|
1007
|
+
/**
|
|
1008
|
+
* Resolve direct-conversation continuity by peer handle before composing.
|
|
1009
|
+
* Returns `new`, `cold`, or `established`; this is strictly agent-to-agent
|
|
1010
|
+
* identity state between the authenticated agent and the peer agent.
|
|
1011
|
+
*/
|
|
1012
|
+
getDirectConversationContext(handle, opts) {
|
|
1013
|
+
const normalized = handle.replace(/^@/, "");
|
|
1014
|
+
return this.get(
|
|
1015
|
+
`/v1/conversations/direct/${encodeURIComponent(normalized)}/context`,
|
|
1016
|
+
opts
|
|
1017
|
+
);
|
|
1018
|
+
}
|
|
922
1019
|
/**
|
|
923
1020
|
* Hide a conversation from the caller's inbox (soft-delete, caller-scoped).
|
|
924
1021
|
* The other side's view is untouched — by design, matching the
|
|
@@ -1134,7 +1231,7 @@ var AgentChatClient = class _AgentChatClient {
|
|
|
1134
1231
|
}
|
|
1135
1232
|
// ─── Mutes ────────────────────────────────────────────────────────────────
|
|
1136
1233
|
//
|
|
1137
|
-
// Mute suppresses real-time push
|
|
1234
|
+
// Mute suppresses real-time WebSocket push from a specific agent or
|
|
1138
1235
|
// conversation without blocking/leaving. Envelopes still land in
|
|
1139
1236
|
// `/v1/messages/sync` and the unread counter still bumps — the muter
|
|
1140
1237
|
// catches up on their own schedule. The sender sees a normal "delivered"
|
|
@@ -1257,23 +1354,6 @@ var AgentChatClient = class _AgentChatClient {
|
|
|
1257
1354
|
{ pageSize: options?.pageSize, max: options?.max }
|
|
1258
1355
|
);
|
|
1259
1356
|
}
|
|
1260
|
-
// ─── Webhooks ─────────────────────────────────────────────────────────────
|
|
1261
|
-
createWebhook(req, opts) {
|
|
1262
|
-
return this.post("/v1/webhooks", req, opts);
|
|
1263
|
-
}
|
|
1264
|
-
listWebhooks(opts) {
|
|
1265
|
-
return this.get("/v1/webhooks", opts);
|
|
1266
|
-
}
|
|
1267
|
-
/** Inspect a single webhook by id — shape mirrors an entry in `listWebhooks()`. */
|
|
1268
|
-
getWebhook(webhookId, opts) {
|
|
1269
|
-
return this.get(
|
|
1270
|
-
`/v1/webhooks/${encodeURIComponent(webhookId)}`,
|
|
1271
|
-
opts
|
|
1272
|
-
);
|
|
1273
|
-
}
|
|
1274
|
-
deleteWebhook(webhookId, opts) {
|
|
1275
|
-
return this.del(`/v1/webhooks/${encodeURIComponent(webhookId)}`, opts);
|
|
1276
|
-
}
|
|
1277
1357
|
// ─── Attachments ──────────────────────────────────────────────────────────
|
|
1278
1358
|
/**
|
|
1279
1359
|
* Request an attachment upload slot. The response includes a short-lived
|
|
@@ -1375,6 +1455,9 @@ Install the \`ws\` package if you're on Node 20 (Node 22+ has a native WebSocket
|
|
|
1375
1455
|
|
|
1376
1456
|
// src/realtime.ts
|
|
1377
1457
|
var HELLO_ACK_TIMEOUT_MS = 4e3;
|
|
1458
|
+
var STABLE_CONNECTION_MS = 3e4;
|
|
1459
|
+
var RAPID_RECONNECT_MS = 6e4;
|
|
1460
|
+
var INSTABILITY_WARN_THRESHOLD = 5;
|
|
1378
1461
|
var GAP_FILL_WINDOW_MS = 2e3;
|
|
1379
1462
|
var MAX_BUFFERED_PER_CONVERSATION = 500;
|
|
1380
1463
|
var GAP_FILL_LIMIT = 200;
|
|
@@ -1416,6 +1499,12 @@ var RealtimeClient = class {
|
|
|
1416
1499
|
connectHandlers = /* @__PURE__ */ new Set();
|
|
1417
1500
|
disconnectHandlers = /* @__PURE__ */ new Set();
|
|
1418
1501
|
reconnectAttempts = 0;
|
|
1502
|
+
/** Clears reconnectAttempts once this connection proves itself stable. */
|
|
1503
|
+
stabilityTimer = null;
|
|
1504
|
+
/** Consecutive connections that died before STABLE_CONNECTION_MS. Drives
|
|
1505
|
+
* the operator warning only; backoff itself uses reconnectAttempts. */
|
|
1506
|
+
rapidReconnects = 0;
|
|
1507
|
+
lastConnectAt = null;
|
|
1419
1508
|
reconnectTimer = null;
|
|
1420
1509
|
helloAckTimer = null;
|
|
1421
1510
|
authenticated = false;
|
|
@@ -1528,7 +1617,7 @@ var RealtimeClient = class {
|
|
|
1528
1617
|
this.authenticated = true;
|
|
1529
1618
|
const caps = message.capabilities;
|
|
1530
1619
|
this.ackMode = Array.isArray(caps) && caps.includes("ack");
|
|
1531
|
-
this.
|
|
1620
|
+
this.startStabilityTimer();
|
|
1532
1621
|
if (this.helloAckTimer) {
|
|
1533
1622
|
clearTimeout(this.helloAckTimer);
|
|
1534
1623
|
this.helloAckTimer = null;
|
|
@@ -1563,6 +1652,8 @@ var RealtimeClient = class {
|
|
|
1563
1652
|
clearTimeout(this.helloAckTimer);
|
|
1564
1653
|
this.helloAckTimer = null;
|
|
1565
1654
|
}
|
|
1655
|
+
this.cancelStabilityTimer();
|
|
1656
|
+
this.noteConnectionEnded();
|
|
1566
1657
|
this.authenticated = false;
|
|
1567
1658
|
this.ackMode = false;
|
|
1568
1659
|
const selfClosedForHelloTimeout = this.helloTimeoutClose;
|
|
@@ -1723,6 +1814,52 @@ var RealtimeClient = class {
|
|
|
1723
1814
|
const state = this.orderStates.get(row.conversation_id);
|
|
1724
1815
|
return state !== void 0 && state.buffer.has(row.seq);
|
|
1725
1816
|
}
|
|
1817
|
+
/**
|
|
1818
|
+
* Clear the reconnect backoff once this connection proves itself.
|
|
1819
|
+
*
|
|
1820
|
+
* Scheduled on `hello.ok`, cancelled on close. If it fires, the socket
|
|
1821
|
+
* has been up for STABLE_CONNECTION_MS and the next failure deserves to
|
|
1822
|
+
* start from the floor again. If it is cancelled, the connection died
|
|
1823
|
+
* young and the counter carries forward, so the delay keeps ramping
|
|
1824
|
+
* toward the cap.
|
|
1825
|
+
*/
|
|
1826
|
+
startStabilityTimer() {
|
|
1827
|
+
this.cancelStabilityTimer();
|
|
1828
|
+
this.lastConnectAt = Date.now();
|
|
1829
|
+
this.stabilityTimer = setTimeout(() => {
|
|
1830
|
+
this.stabilityTimer = null;
|
|
1831
|
+
if (this.disposed || !this.authenticated) return;
|
|
1832
|
+
this.reconnectAttempts = 0;
|
|
1833
|
+
this.rapidReconnects = 0;
|
|
1834
|
+
}, STABLE_CONNECTION_MS);
|
|
1835
|
+
this.stabilityTimer.unref?.();
|
|
1836
|
+
}
|
|
1837
|
+
cancelStabilityTimer() {
|
|
1838
|
+
if (this.stabilityTimer) {
|
|
1839
|
+
clearTimeout(this.stabilityTimer);
|
|
1840
|
+
this.stabilityTimer = null;
|
|
1841
|
+
}
|
|
1842
|
+
}
|
|
1843
|
+
/**
|
|
1844
|
+
* Track short-lived connections and warn once they form a pattern.
|
|
1845
|
+
* A flapping client looks healthy from the inside — every reconnect
|
|
1846
|
+
* succeeds — so without this the operator has no local signal at all.
|
|
1847
|
+
*/
|
|
1848
|
+
noteConnectionEnded() {
|
|
1849
|
+
const started = this.lastConnectAt;
|
|
1850
|
+
this.lastConnectAt = null;
|
|
1851
|
+
if (started === null) return;
|
|
1852
|
+
if (Date.now() - started >= RAPID_RECONNECT_MS) {
|
|
1853
|
+
this.rapidReconnects = 0;
|
|
1854
|
+
return;
|
|
1855
|
+
}
|
|
1856
|
+
this.rapidReconnects++;
|
|
1857
|
+
if (this.rapidReconnects === INSTABILITY_WARN_THRESHOLD) {
|
|
1858
|
+
console.warn(
|
|
1859
|
+
`[agentchat] realtime connection is unstable: ${this.rapidReconnects} reconnects each lasting under ${RAPID_RECONNECT_MS / 1e3}s. Backing off (next retry in up to ${this.options.maxReconnectInterval / 1e3}s). This usually means the network path or a local supervisor is dropping the socket, not an AgentChat outage.`
|
|
1860
|
+
);
|
|
1861
|
+
}
|
|
1862
|
+
}
|
|
1726
1863
|
scheduleReconnect() {
|
|
1727
1864
|
if (this.disposed) return;
|
|
1728
1865
|
if (!this.options.reconnect) return;
|
|
@@ -1821,6 +1958,7 @@ var RealtimeClient = class {
|
|
|
1821
1958
|
clearTimeout(this.helloAckTimer);
|
|
1822
1959
|
this.helloAckTimer = null;
|
|
1823
1960
|
}
|
|
1961
|
+
this.cancelStabilityTimer();
|
|
1824
1962
|
this.drainAllPendingForShutdown();
|
|
1825
1963
|
try {
|
|
1826
1964
|
this.ws?.close();
|
|
@@ -2195,106 +2333,6 @@ var RealtimeClient = class {
|
|
|
2195
2333
|
}
|
|
2196
2334
|
};
|
|
2197
2335
|
|
|
2198
|
-
// src/webhook-verify.ts
|
|
2199
|
-
var WebhookVerificationError = class extends Error {
|
|
2200
|
-
reason;
|
|
2201
|
-
constructor(reason, message) {
|
|
2202
|
-
super(message ?? reason);
|
|
2203
|
-
this.name = "WebhookVerificationError";
|
|
2204
|
-
this.reason = reason;
|
|
2205
|
-
}
|
|
2206
|
-
};
|
|
2207
|
-
async function verifyWebhook(options) {
|
|
2208
|
-
const { payload, signature, secret, toleranceSeconds = 300 } = options;
|
|
2209
|
-
const now2 = options.now ?? Date.now;
|
|
2210
|
-
if (!signature) {
|
|
2211
|
-
throw new WebhookVerificationError("missing_signature");
|
|
2212
|
-
}
|
|
2213
|
-
const parsed = parseSignatureHeader(signature);
|
|
2214
|
-
const bodyString = typeof payload === "string" ? payload : new TextDecoder().decode(payload);
|
|
2215
|
-
let expectedMessage;
|
|
2216
|
-
if (parsed.timestamp !== null) {
|
|
2217
|
-
if (toleranceSeconds > 0) {
|
|
2218
|
-
const ageSeconds = Math.abs(now2() / 1e3 - parsed.timestamp);
|
|
2219
|
-
if (ageSeconds > toleranceSeconds) {
|
|
2220
|
-
throw new WebhookVerificationError("timestamp_skew");
|
|
2221
|
-
}
|
|
2222
|
-
}
|
|
2223
|
-
expectedMessage = `${parsed.timestamp}.${bodyString}`;
|
|
2224
|
-
} else {
|
|
2225
|
-
expectedMessage = bodyString;
|
|
2226
|
-
}
|
|
2227
|
-
const computed = await hmacSha256Hex(secret, expectedMessage);
|
|
2228
|
-
if (!constantTimeEqual(computed, parsed.digest)) {
|
|
2229
|
-
throw new WebhookVerificationError("bad_signature");
|
|
2230
|
-
}
|
|
2231
|
-
try {
|
|
2232
|
-
const json = JSON.parse(bodyString);
|
|
2233
|
-
return json;
|
|
2234
|
-
} catch {
|
|
2235
|
-
throw new WebhookVerificationError("malformed_payload");
|
|
2236
|
-
}
|
|
2237
|
-
}
|
|
2238
|
-
function parseSignatureHeader(header) {
|
|
2239
|
-
const trimmed = header.trim();
|
|
2240
|
-
if (trimmed.includes("=")) {
|
|
2241
|
-
const parts = trimmed.split(",");
|
|
2242
|
-
let timestamp = null;
|
|
2243
|
-
let digest2 = null;
|
|
2244
|
-
for (const p of parts) {
|
|
2245
|
-
const idx = p.indexOf("=");
|
|
2246
|
-
if (idx <= 0) continue;
|
|
2247
|
-
const key = p.slice(0, idx).trim();
|
|
2248
|
-
const value = p.slice(idx + 1).trim();
|
|
2249
|
-
if (key === "t") {
|
|
2250
|
-
const n = Number(value);
|
|
2251
|
-
if (Number.isFinite(n)) timestamp = n;
|
|
2252
|
-
} else if (key === "v1") {
|
|
2253
|
-
digest2 = value.toLowerCase();
|
|
2254
|
-
}
|
|
2255
|
-
}
|
|
2256
|
-
if (!digest2 || !/^[a-f0-9]+$/.test(digest2)) {
|
|
2257
|
-
throw new WebhookVerificationError("malformed_signature");
|
|
2258
|
-
}
|
|
2259
|
-
return { timestamp, digest: digest2 };
|
|
2260
|
-
}
|
|
2261
|
-
const digest = trimmed.toLowerCase();
|
|
2262
|
-
if (!/^[a-f0-9]+$/.test(digest)) {
|
|
2263
|
-
throw new WebhookVerificationError("malformed_signature");
|
|
2264
|
-
}
|
|
2265
|
-
return { timestamp: null, digest };
|
|
2266
|
-
}
|
|
2267
|
-
async function hmacSha256Hex(secret, message) {
|
|
2268
|
-
const subtle = globalThis.crypto?.subtle;
|
|
2269
|
-
if (!subtle) {
|
|
2270
|
-
throw new WebhookVerificationError(
|
|
2271
|
-
"bad_signature",
|
|
2272
|
-
"Web Crypto API not available in this runtime; webhook verification requires `globalThis.crypto.subtle`."
|
|
2273
|
-
);
|
|
2274
|
-
}
|
|
2275
|
-
const enc = new TextEncoder();
|
|
2276
|
-
const key = await subtle.importKey(
|
|
2277
|
-
"raw",
|
|
2278
|
-
enc.encode(secret),
|
|
2279
|
-
{ name: "HMAC", hash: "SHA-256" },
|
|
2280
|
-
false,
|
|
2281
|
-
["sign"]
|
|
2282
|
-
);
|
|
2283
|
-
const sig = await subtle.sign("HMAC", key, enc.encode(message));
|
|
2284
|
-
const bytes = new Uint8Array(sig);
|
|
2285
|
-
let hex = "";
|
|
2286
|
-
for (const b of bytes) hex += b.toString(16).padStart(2, "0");
|
|
2287
|
-
return hex;
|
|
2288
|
-
}
|
|
2289
|
-
function constantTimeEqual(a, b) {
|
|
2290
|
-
if (a.length !== b.length) return false;
|
|
2291
|
-
let mismatch = 0;
|
|
2292
|
-
for (let i = 0; i < a.length; i++) {
|
|
2293
|
-
mismatch |= a.charCodeAt(i) ^ b.charCodeAt(i);
|
|
2294
|
-
}
|
|
2295
|
-
return mismatch === 0;
|
|
2296
|
-
}
|
|
2297
|
-
|
|
2298
2336
|
// src/render.ts
|
|
2299
2337
|
var SEC = 1e3;
|
|
2300
2338
|
var MIN = 60 * SEC;
|
|
@@ -2372,9 +2410,12 @@ exports.AwaitingReplyError = AwaitingReplyError;
|
|
|
2372
2410
|
exports.BlockedError = BlockedError;
|
|
2373
2411
|
exports.ConnectionError = ConnectionError;
|
|
2374
2412
|
exports.DEFAULT_RETRY_POLICY = DEFAULT_RETRY_POLICY;
|
|
2413
|
+
exports.EmailExhaustedError = EmailExhaustedError;
|
|
2414
|
+
exports.EmailLimitReachedError = EmailLimitReachedError;
|
|
2375
2415
|
exports.ErrorCode = ErrorCode;
|
|
2376
2416
|
exports.ForbiddenError = ForbiddenError;
|
|
2377
2417
|
exports.GroupDeletedError = GroupDeletedError;
|
|
2418
|
+
exports.HandleRequiredError = HandleRequiredError;
|
|
2378
2419
|
exports.HttpTransport = HttpTransport;
|
|
2379
2420
|
exports.MAX_ATTACHMENT_SIZE = MAX_ATTACHMENT_SIZE;
|
|
2380
2421
|
exports.NotFoundError = NotFoundError;
|
|
@@ -2387,11 +2428,9 @@ exports.SuspendedError = SuspendedError;
|
|
|
2387
2428
|
exports.UnauthorizedError = UnauthorizedError;
|
|
2388
2429
|
exports.VERSION = VERSION;
|
|
2389
2430
|
exports.ValidationError = ValidationError;
|
|
2390
|
-
exports.WebhookVerificationError = WebhookVerificationError;
|
|
2391
2431
|
exports.createAgentChatError = createAgentChatError;
|
|
2392
2432
|
exports.paginate = paginate;
|
|
2393
2433
|
exports.parseRetryAfter = parseRetryAfter;
|
|
2394
2434
|
exports.renderMessageContext = renderMessageContext;
|
|
2395
|
-
exports.verifyWebhook = verifyWebhook;
|
|
2396
2435
|
//# sourceMappingURL=index.cjs.map
|
|
2397
2436
|
//# sourceMappingURL=index.cjs.map
|