@amigo-ai/platform-sdk 0.3.0 → 0.4.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.
Files changed (71) hide show
  1. package/README.md +112 -49
  2. package/dist/core/errors.js +2 -3
  3. package/dist/core/errors.js.map +1 -1
  4. package/dist/core/webhooks.js +133 -13
  5. package/dist/core/webhooks.js.map +1 -1
  6. package/dist/index.cjs +131 -18
  7. package/dist/index.cjs.map +2 -2
  8. package/dist/index.js +1 -1
  9. package/dist/index.js.map +1 -1
  10. package/dist/index.mjs +131 -18
  11. package/dist/index.mjs.map +2 -2
  12. package/dist/types/core/errors.d.ts.map +1 -1
  13. package/dist/types/core/webhooks.d.ts +19 -1
  14. package/dist/types/core/webhooks.d.ts.map +1 -1
  15. package/dist/types/generated/api.d.ts +12694 -11729
  16. package/dist/types/generated/api.d.ts.map +1 -1
  17. package/dist/types/index.d.ts +2 -2
  18. package/dist/types/index.d.ts.map +1 -1
  19. package/dist/types/resources/actions.d.ts +83 -83
  20. package/dist/types/resources/actions.d.ts.map +1 -1
  21. package/dist/types/resources/agents.d.ts +30 -30
  22. package/dist/types/resources/agents.d.ts.map +1 -1
  23. package/dist/types/resources/analytics.d.ts +12 -12
  24. package/dist/types/resources/analytics.d.ts.map +1 -1
  25. package/dist/types/resources/api-keys.d.ts +15 -15
  26. package/dist/types/resources/api-keys.d.ts.map +1 -1
  27. package/dist/types/resources/audit.d.ts +11 -11
  28. package/dist/types/resources/audit.d.ts.map +1 -1
  29. package/dist/types/resources/billing.d.ts +9 -9
  30. package/dist/types/resources/billing.d.ts.map +1 -1
  31. package/dist/types/resources/calls.d.ts +31 -45
  32. package/dist/types/resources/calls.d.ts.map +1 -1
  33. package/dist/types/resources/compliance.d.ts +17 -17
  34. package/dist/types/resources/context-graphs.d.ts +35 -35
  35. package/dist/types/resources/context-graphs.d.ts.map +1 -1
  36. package/dist/types/resources/data-sources.d.ts +47 -47
  37. package/dist/types/resources/data-sources.d.ts.map +1 -1
  38. package/dist/types/resources/functions.d.ts +12 -12
  39. package/dist/types/resources/functions.d.ts.map +1 -1
  40. package/dist/types/resources/integrations.d.ts +43 -43
  41. package/dist/types/resources/integrations.d.ts.map +1 -1
  42. package/dist/types/resources/memory.d.ts +10 -10
  43. package/dist/types/resources/memory.d.ts.map +1 -1
  44. package/dist/types/resources/operators.d.ts +53 -53
  45. package/dist/types/resources/operators.d.ts.map +1 -1
  46. package/dist/types/resources/personas.d.ts +23 -23
  47. package/dist/types/resources/personas.d.ts.map +1 -1
  48. package/dist/types/resources/phone-numbers.d.ts +38 -38
  49. package/dist/types/resources/phone-numbers.d.ts.map +1 -1
  50. package/dist/types/resources/recordings.d.ts +10 -10
  51. package/dist/types/resources/review-queue.d.ts +71 -71
  52. package/dist/types/resources/review-queue.d.ts.map +1 -1
  53. package/dist/types/resources/safety.d.ts +23 -23
  54. package/dist/types/resources/safety.d.ts.map +1 -1
  55. package/dist/types/resources/services.d.ts +50 -50
  56. package/dist/types/resources/services.d.ts.map +1 -1
  57. package/dist/types/resources/settings.d.ts +24 -24
  58. package/dist/types/resources/settings.d.ts.map +1 -1
  59. package/dist/types/resources/simulations.d.ts +12 -12
  60. package/dist/types/resources/simulations.d.ts.map +1 -1
  61. package/dist/types/resources/skills.d.ts +83 -83
  62. package/dist/types/resources/skills.d.ts.map +1 -1
  63. package/dist/types/resources/triggers.d.ts +55 -55
  64. package/dist/types/resources/triggers.d.ts.map +1 -1
  65. package/dist/types/resources/webhook-destinations.d.ts +35 -35
  66. package/dist/types/resources/webhook-destinations.d.ts.map +1 -1
  67. package/dist/types/resources/workspaces.d.ts +26 -26
  68. package/dist/types/resources/workspaces.d.ts.map +1 -1
  69. package/dist/types/resources/world.d.ts +41 -41
  70. package/dist/types/resources/world.d.ts.map +1 -1
  71. package/package.json +36 -14
package/README.md CHANGED
@@ -23,29 +23,38 @@ const client = new AmigoClient({
23
23
  })
24
24
 
25
25
  // List agents
26
- const { items: agents } = await client.agents.list()
27
-
28
- // Emit a world event
29
- await client.world.emitEvent({
30
- entity_id: 'entity-id',
31
- event_type: 'appointment_scheduled',
32
- data: { appointment_id: 'appt-001' },
26
+ const { items: agents } = await client.agents.list({ limit: 10 })
27
+ console.log(agents.map((agent) => agent.name))
28
+
29
+ // Search entities in the world model
30
+ const entityResults = await client.world.listEntities({
31
+ q: 'Jane Doe',
32
+ entity_type: ['patient'],
33
+ limit: 5,
33
34
  })
35
+ console.log(entityResults.entities[0]?.display_name)
34
36
 
35
37
  // Get call analytics for the last 30 days
36
- const stats = await client.analytics.getCalls({ period: '30d' })
38
+ const stats = await client.analytics.getCalls({ days: 30 })
37
39
  console.log(stats.total_calls, stats.avg_duration_seconds)
38
40
  ```
39
41
 
42
+ ## Examples and Docs
43
+
44
+ - Product docs and API reference: [docs.amigo.ai](https://docs.amigo.ai/)
45
+ - Repo-local SDK examples: [examples/README.md](./examples/README.md)
46
+
47
+ The docs site remains the primary reference. The examples in this repo stay close to the package surface and are typechecked in CI to reduce drift.
48
+
40
49
  ## Configuration
41
50
 
42
- | Option | Type | Required | Description |
43
- |--------|------|----------|-------------|
44
- | `apiKey` | `string` | Yes | Your Platform API key — create one at Workspace Settings > API Keys |
45
- | `workspaceId` | `string` | Yes | Your workspace ID — all resource operations are scoped to this |
46
- | `baseUrl` | `string` | No | Override the API base URL (default: `https://api.platform.amigo.ai`) |
47
- | `retry` | `RetryOptions` | No | Retry configuration for transient failures |
48
- | `fetch` | `typeof fetch` | No | Custom fetch for BFF proxy, cookie forwarding, or test mocking |
51
+ | Option | Type | Required | Description |
52
+ | ------------- | -------------- | -------- | -------------------------------------------------------------------- |
53
+ | `apiKey` | `string` | Yes | Your Platform API key — create one at Workspace Settings > API Keys |
54
+ | `workspaceId` | `string` | Yes | Your workspace ID — all resource operations are scoped to this |
55
+ | `baseUrl` | `string` | No | Override the API base URL (default: `https://api.platform.amigo.ai`) |
56
+ | `retry` | `RetryOptions` | No | Retry configuration for transient failures |
57
+ | `fetch` | `typeof fetch` | No | Custom fetch for BFF proxy, cookie forwarding, or test mocking |
49
58
 
50
59
  ### Retry options
51
60
 
@@ -54,8 +63,8 @@ const client = new AmigoClient({
54
63
  apiKey: 'your-key',
55
64
  workspaceId: 'your-workspace-id',
56
65
  retry: {
57
- maxAttempts: 3, // Total attempts including first. Default: 3
58
- baseDelayMs: 250, // Base delay for exponential backoff. Default: 250
66
+ maxAttempts: 3, // Total attempts including first. Default: 3
67
+ baseDelayMs: 250, // Base delay for exponential backoff. Default: 250
59
68
  maxDelayMs: 30000, // Cap on delay. Default: 30_000
60
69
  },
61
70
  })
@@ -63,6 +72,23 @@ const client = new AmigoClient({
63
72
 
64
73
  GET requests are retried on 408, 429, 500, 502, 503, 504. POST requests are only retried on 429 with a `Retry-After` header. Backoff uses full jitter.
65
74
 
75
+ ## Generated Types
76
+
77
+ The SDK ships with generated OpenAPI types and re-exports them for direct use:
78
+
79
+ ```typescript
80
+ import type { components, operations, paths } from '@amigo-ai/platform-sdk'
81
+
82
+ type Agent = components['schemas']['AgentResponse']
83
+ type ListAgentsQuery = operations['list_agents_v1__workspace_id__agents_get']['parameters']['query']
84
+ ```
85
+
86
+ Public builds are generated from the committed [`openapi.json`](./openapi.json) snapshot in this repo so type output stays deterministic across machines and CI runs. When you need to refresh that snapshot, run:
87
+
88
+ ```bash
89
+ npm run openapi:sync
90
+ ```
91
+
66
92
  ## Resources
67
93
 
68
94
  ### Agents
@@ -139,25 +165,27 @@ console.log(service.agent_name, service.channel_type, service.version_sets)
139
165
  The world model tracks entities (patients, contacts, appointments) and the events that flow through them.
140
166
 
141
167
  ```typescript
142
- // Create an entity
143
- const patient = await client.world.createEntity({
144
- entity_type: 'patient',
145
- canonical_id: 'MRN-12345',
146
- display_name: 'Jane Doe',
168
+ // Filter entities with simple list queries
169
+ const patients = await client.world.listEntities({
170
+ q: 'Jane Doe',
171
+ entity_type: ['patient'],
172
+ limit: 10,
147
173
  })
174
+ console.log(patients.entities.length)
148
175
 
149
- // Emit an event
150
- await client.world.emitEvent({
151
- entity_id: patient.id,
152
- event_type: 'call_completed',
153
- data: { duration_seconds: 180, outcome: 'appointment_scheduled' },
154
- })
176
+ // Get a single entity
177
+ const patient = await client.world.getEntity('entity-id')
178
+ console.log(patient.display_name, patient.entity_type)
155
179
 
156
180
  // Query timeline
157
- const timeline = await client.world.getTimeline(patient.id)
181
+ const timeline = await client.world.getTimeline('entity-id', { limit: 20 })
158
182
 
159
- // Search entities
160
- const results = await client.world.search('Jane Doe', { entity_type: 'patient' })
183
+ // Semantic search over the world model
184
+ const results = await client.world.search({
185
+ q: 'Jane Doe',
186
+ entity_type: 'patient',
187
+ limit: 5,
188
+ })
161
189
 
162
190
  // View sync status from connectors
163
191
  const syncStatus = await client.world.getSyncStatusBySink()
@@ -191,7 +219,7 @@ console.log(dashboard.call_volume.value, dashboard.call_volume.delta_pct)
191
219
  console.log(dashboard.avg_quality.value)
192
220
 
193
221
  // Call volume time series
194
- const calls = await client.analytics.getCalls({ period: '30d' })
222
+ const calls = await client.analytics.getCalls({ days: 30, interval: '1d' })
195
223
  console.log(calls.total_calls, calls.calls_by_date)
196
224
 
197
225
  // Per-agent performance
@@ -229,11 +257,9 @@ console.log(analytics.coverage_rate, analytics.total_facts)
229
257
  const { items: integrations } = await client.integrations.list({ enabled: true })
230
258
 
231
259
  // Test a specific endpoint
232
- const result = await client.integrations.testEndpoint(
233
- 'integration-id',
234
- 'geocode',
235
- { textQuery: '123 Main St, Springfield' },
236
- )
260
+ const result = await client.integrations.testEndpoint('integration-id', 'geocode', {
261
+ textQuery: '123 Main St, Springfield',
262
+ })
237
263
  ```
238
264
 
239
265
  ### Data Sources
@@ -367,6 +393,41 @@ const dest = await client.webhookDestinations.create({
367
393
  const deliveries = await client.webhookDestinations.listDeliveries(dest.id)
368
394
  ```
369
395
 
396
+ ## Webhook Verification
397
+
398
+ Use the raw request body when verifying webhook deliveries. Timestamped signatures are replay-protected by default.
399
+
400
+ ```typescript
401
+ import { parseWebhookEvent, WebhookVerificationError } from '@amigo-ai/platform-sdk'
402
+
403
+ const body = await request.text()
404
+
405
+ try {
406
+ const event = await parseWebhookEvent({
407
+ payload: body,
408
+ signature: request.headers.get('x-amigo-signature') ?? '',
409
+ timestamp: request.headers.get('x-amigo-timestamp') ?? undefined,
410
+ secret: process.env.AMIGO_WEBHOOK_SECRET!,
411
+ })
412
+
413
+ console.log(event.type, event.data)
414
+ } catch (error) {
415
+ if (error instanceof WebhookVerificationError) {
416
+ console.error('Rejected webhook:', error.message)
417
+ } else {
418
+ throw error
419
+ }
420
+ }
421
+ ```
422
+
423
+ If your delivery channel only provides a legacy HMAC without a timestamp, the original helper signature still works:
424
+
425
+ ```typescript
426
+ import { parseWebhookEvent } from '@amigo-ai/platform-sdk'
427
+
428
+ const event = await parseWebhookEvent(rawBody, signature, secret)
429
+ ```
430
+
370
431
  ## BFF Proxy (Next.js)
371
432
 
372
433
  For frontend apps that use a Backend-for-Frontend proxy:
@@ -422,20 +483,22 @@ try {
422
483
  }
423
484
  ```
424
485
 
486
+ Webhook verification errors are separate from API transport errors and throw `WebhookVerificationError`.
487
+
425
488
  ### Error classes
426
489
 
427
- | Class | HTTP Status | Description |
428
- |-------|-------------|-------------|
429
- | `BadRequestError` | 400 | Malformed request |
430
- | `AuthenticationError` | 401 | Invalid or expired API key |
431
- | `PermissionError` | 403 | Insufficient permissions |
432
- | `NotFoundError` | 404 | Resource does not exist |
433
- | `ConflictError` | 409 | Duplicate slug or version conflict |
434
- | `ValidationError` | 422 | Request body validation failure |
435
- | `RateLimitError` | 429 | Too many requests — check `.retryAfter` |
436
- | `ServerError` | 5xx | Server-side error |
437
- | `ConfigurationError` | — | SDK misconfiguration at init time |
438
- | `NetworkError` | — | Fetch/network failure |
490
+ | Class | HTTP Status | Description |
491
+ | --------------------- | ----------- | --------------------------------------- |
492
+ | `BadRequestError` | 400 | Malformed request |
493
+ | `AuthenticationError` | 401 | Invalid or expired API key |
494
+ | `PermissionError` | 403 | Insufficient permissions |
495
+ | `NotFoundError` | 404 | Resource does not exist |
496
+ | `ConflictError` | 409 | Duplicate slug or version conflict |
497
+ | `ValidationError` | 422 | Request body validation failure |
498
+ | `RateLimitError` | 429 | Too many requests — check `.retryAfter` |
499
+ | `ServerError` | 5xx | Server-side error |
500
+ | `ConfigurationError` | — | SDK misconfiguration at init time |
501
+ | `NetworkError` | — | Fetch/network failure |
439
502
 
440
503
  ## CommonJS (CJS) usage
441
504
 
@@ -140,9 +140,8 @@ export class ConfigurationError extends AmigoError {
140
140
  }
141
141
  export async function createApiError(response) {
142
142
  let body = {};
143
- let rawBody;
144
143
  try {
145
- rawBody = await response.text();
144
+ const rawBody = await response.text();
146
145
  body = JSON.parse(rawBody);
147
146
  }
148
147
  catch {
@@ -151,7 +150,7 @@ export async function createApiError(response) {
151
150
  const ctx = {
152
151
  statusCode: response.status,
153
152
  errorCode: body.error_code,
154
- requestId: body.request_id,
153
+ requestId: body.request_id ?? response.headers.get('x-request-id') ?? undefined,
155
154
  detail: body.detail,
156
155
  context: { url: response.url, response: body },
157
156
  };
@@ -1 +1 @@
1
- {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/core/errors.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/core/errors.ts"],"names":[],"mappings":"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"}
@@ -1,17 +1,137 @@
1
- export async function verifyWebhookSignature(payload, signature, secret) {
2
- const encoder = new TextEncoder();
3
- const key = await crypto.subtle.importKey('raw', encoder.encode(secret), { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']);
4
- const mac = await crypto.subtle.sign('HMAC', key, typeof payload === 'string' ? encoder.encode(payload) : payload);
5
- const expected = Array.from(new Uint8Array(mac))
6
- .map((b) => b.toString(16).padStart(2, '0'))
7
- .join('');
8
- return signature === `sha256=${expected}`;
9
- }
10
- export async function parseWebhookEvent(payload, signature, secret) {
11
- const valid = await verifyWebhookSignature(payload, signature, secret);
1
+ const textEncoder = new TextEncoder();
2
+ const MAX_TIMESTAMP_SKEW_MS = 5 * 60 * 1000;
3
+ export class WebhookVerificationError extends Error {
4
+ constructor(message) {
5
+ super(message);
6
+ this.name = 'WebhookVerificationError';
7
+ Object.setPrototypeOf(this, new.target.prototype);
8
+ }
9
+ }
10
+ export async function verifyWebhookSignature(payloadOrOptions, signature, secret) {
11
+ const options = normalizeVerificationOptions(payloadOrOptions, signature, secret);
12
+ const payloadBytes = toUint8Array(options.payload);
13
+ const expectedSignature = await signWebhookPayload(payloadBytes, options.secret, options.timestamp);
14
+ const actualSignature = normalizeSignature(options.signature);
15
+ if (!actualSignature || !constantTimeEqual(expectedSignature, actualSignature)) {
16
+ return false;
17
+ }
18
+ if (options.timestamp) {
19
+ const timestampMs = parseTimestamp(options.timestamp);
20
+ if (timestampMs === undefined)
21
+ return false;
22
+ const maxAgeMs = options.maxAgeMs ?? MAX_TIMESTAMP_SKEW_MS;
23
+ const now = Date.now();
24
+ if (timestampMs > now + maxAgeMs || now - timestampMs > maxAgeMs) {
25
+ return false;
26
+ }
27
+ }
28
+ return true;
29
+ }
30
+ export async function parseWebhookEvent(payloadOrOptions, signature, secret) {
31
+ const options = normalizeParseOptions(payloadOrOptions, signature, secret);
32
+ const valid = await verifyWebhookSignature(options);
12
33
  if (!valid) {
13
- throw new Error('Invalid webhook signature');
34
+ throw new WebhookVerificationError('Invalid or expired webhook signature');
35
+ }
36
+ const payloadText = decodePayload(options.payload);
37
+ let event;
38
+ try {
39
+ event = JSON.parse(payloadText, options.reviver);
40
+ }
41
+ catch {
42
+ throw new WebhookVerificationError('Invalid JSON webhook payload');
43
+ }
44
+ if (options.expectedType && event.type !== options.expectedType) {
45
+ throw new WebhookVerificationError(`Unexpected webhook event type: expected ${options.expectedType}, received ${event.type}`);
46
+ }
47
+ options.validate?.(event);
48
+ return event;
49
+ }
50
+ function normalizeVerificationOptions(payloadOrOptions, signature, secret) {
51
+ if (typeof payloadOrOptions === 'object' &&
52
+ payloadOrOptions !== null &&
53
+ 'payload' in payloadOrOptions) {
54
+ return payloadOrOptions;
55
+ }
56
+ if (!signature || !secret) {
57
+ throw new TypeError('signature and secret are required');
58
+ }
59
+ return {
60
+ payload: payloadOrOptions,
61
+ signature,
62
+ secret,
63
+ };
64
+ }
65
+ function normalizeParseOptions(payloadOrOptions, signature, secret) {
66
+ if (typeof payloadOrOptions === 'object' &&
67
+ payloadOrOptions !== null &&
68
+ 'payload' in payloadOrOptions) {
69
+ return payloadOrOptions;
14
70
  }
15
- return JSON.parse(payload);
71
+ if (!signature || !secret) {
72
+ throw new TypeError('signature and secret are required');
73
+ }
74
+ return {
75
+ payload: payloadOrOptions,
76
+ signature,
77
+ secret,
78
+ };
79
+ }
80
+ function decodePayload(payload) {
81
+ if (typeof payload === 'string')
82
+ return payload;
83
+ return new TextDecoder().decode(toUint8Array(payload));
84
+ }
85
+ function toUint8Array(payload) {
86
+ if (typeof payload === 'string')
87
+ return textEncoder.encode(payload);
88
+ if (payload instanceof Uint8Array)
89
+ return payload;
90
+ return new Uint8Array(payload);
91
+ }
92
+ async function signWebhookPayload(payload, secret, timestamp) {
93
+ const key = await crypto.subtle.importKey('raw', textEncoder.encode(secret), { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']);
94
+ const message = timestamp
95
+ ? concatUint8Arrays(textEncoder.encode(`v1:${timestamp}:`), payload)
96
+ : payload;
97
+ const mac = await crypto.subtle.sign('HMAC', key, toCryptoBuffer(message));
98
+ return new Uint8Array(mac);
99
+ }
100
+ function normalizeSignature(signature) {
101
+ const normalized = signature.startsWith('sha256=') ? signature.slice(7) : signature;
102
+ if (!/^[a-fA-F0-9]+$/.test(normalized) || normalized.length % 2 !== 0) {
103
+ return undefined;
104
+ }
105
+ const bytes = new Uint8Array(normalized.length / 2);
106
+ for (let index = 0; index < normalized.length; index += 2) {
107
+ bytes[index / 2] = Number.parseInt(normalized.slice(index, index + 2), 16);
108
+ }
109
+ return bytes;
110
+ }
111
+ function constantTimeEqual(expected, actual) {
112
+ const maxLength = Math.max(expected.length, actual.length);
113
+ let diff = expected.length ^ actual.length;
114
+ for (let index = 0; index < maxLength; index += 1) {
115
+ diff |= (expected[index] ?? 0) ^ (actual[index] ?? 0);
116
+ }
117
+ return diff === 0;
118
+ }
119
+ function parseTimestamp(timestamp) {
120
+ const numeric = Number(timestamp);
121
+ if (Number.isFinite(numeric)) {
122
+ return numeric < 1_000_000_000_000 ? numeric * 1000 : numeric;
123
+ }
124
+ const parsed = Date.parse(timestamp);
125
+ return Number.isNaN(parsed) ? undefined : parsed;
126
+ }
127
+ function concatUint8Arrays(left, right) {
128
+ const combined = new Uint8Array(left.length + right.length);
129
+ combined.set(left, 0);
130
+ combined.set(right, left.length);
131
+ return combined;
132
+ }
133
+ function toCryptoBuffer(bytes) {
134
+ const copy = Uint8Array.from(bytes);
135
+ return copy.buffer;
16
136
  }
17
137
  //# sourceMappingURL=webhooks.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"webhooks.js","sourceRoot":"","sources":["../../src/core/webhooks.ts"],"names":[],"mappings":"AAOA,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,OAA4B,EAC5B,SAAiB,EACjB,MAAc;IAEd,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAA;IACjC,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,SAAS,CACvC,KAAK,EACL,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,EACtB,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,EACjC,KAAK,EACL,CAAC,MAAM,CAAC,CACT,CAAA;IACD,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,IAAI,CAClC,MAAM,EACN,GAAG,EACH,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAChE,CAAA;IACD,MAAM,QAAQ,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,UAAU,CAAC,GAAG,CAAC,CAAC;SAC7C,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;SAC3C,IAAI,CAAC,EAAE,CAAC,CAAA;IACX,OAAO,SAAS,KAAK,UAAU,QAAQ,EAAE,CAAA;AAC3C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,OAAe,EACf,SAAiB,EACjB,MAAc;IAEd,MAAM,KAAK,GAAG,MAAM,sBAAsB,CAAC,OAAO,EAAE,SAAS,EAAE,MAAM,CAAC,CAAA;IACtE,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAA;IAC9C,CAAC;IACD,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAoB,CAAA;AAC/C,CAAC"}
1
+ {"version":3,"file":"webhooks.js","sourceRoot":"","sources":["../../src/core/webhooks.ts"],"names":[],"mappings":"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"}
package/dist/index.cjs CHANGED
@@ -45,6 +45,7 @@ __export(index_exports, {
45
45
  ServerError: () => ServerError,
46
46
  ServiceUnavailableError: () => ServiceUnavailableError,
47
47
  ValidationError: () => ValidationError,
48
+ WebhookVerificationError: () => WebhookVerificationError,
48
49
  actionId: () => actionId,
49
50
  agentId: () => agentId,
50
51
  apiKeyId: () => apiKeyId,
@@ -204,16 +205,15 @@ var ConfigurationError = class extends AmigoError {
204
205
  };
205
206
  async function createApiError(response) {
206
207
  let body = {};
207
- let rawBody;
208
208
  try {
209
- rawBody = await response.text();
209
+ const rawBody = await response.text();
210
210
  body = JSON.parse(rawBody);
211
211
  } catch {
212
212
  }
213
213
  const ctx = {
214
214
  statusCode: response.status,
215
215
  errorCode: body.error_code,
216
- requestId: body.request_id,
216
+ requestId: body.request_id ?? response.headers.get("x-request-id") ?? void 0,
217
217
  detail: body.detail,
218
218
  context: { url: response.url, response: body }
219
219
  };
@@ -2199,29 +2199,142 @@ function parseRateLimitHeaders(headers) {
2199
2199
  }
2200
2200
 
2201
2201
  // src/core/webhooks.ts
2202
- async function verifyWebhookSignature(payload, signature, secret) {
2203
- const encoder = new TextEncoder();
2202
+ var textEncoder = new TextEncoder();
2203
+ var MAX_TIMESTAMP_SKEW_MS = 5 * 60 * 1e3;
2204
+ var WebhookVerificationError = class extends Error {
2205
+ constructor(message) {
2206
+ super(message);
2207
+ this.name = "WebhookVerificationError";
2208
+ Object.setPrototypeOf(this, new.target.prototype);
2209
+ }
2210
+ };
2211
+ async function verifyWebhookSignature(payloadOrOptions, signature, secret) {
2212
+ const options = normalizeVerificationOptions(payloadOrOptions, signature, secret);
2213
+ const payloadBytes = toUint8Array(options.payload);
2214
+ const expectedSignature = await signWebhookPayload(
2215
+ payloadBytes,
2216
+ options.secret,
2217
+ options.timestamp
2218
+ );
2219
+ const actualSignature = normalizeSignature(options.signature);
2220
+ if (!actualSignature || !constantTimeEqual(expectedSignature, actualSignature)) {
2221
+ return false;
2222
+ }
2223
+ if (options.timestamp) {
2224
+ const timestampMs = parseTimestamp(options.timestamp);
2225
+ if (timestampMs === void 0) return false;
2226
+ const maxAgeMs = options.maxAgeMs ?? MAX_TIMESTAMP_SKEW_MS;
2227
+ const now = Date.now();
2228
+ if (timestampMs > now + maxAgeMs || now - timestampMs > maxAgeMs) {
2229
+ return false;
2230
+ }
2231
+ }
2232
+ return true;
2233
+ }
2234
+ async function parseWebhookEvent(payloadOrOptions, signature, secret) {
2235
+ const options = normalizeParseOptions(payloadOrOptions, signature, secret);
2236
+ const valid = await verifyWebhookSignature(options);
2237
+ if (!valid) {
2238
+ throw new WebhookVerificationError("Invalid or expired webhook signature");
2239
+ }
2240
+ const payloadText = decodePayload(options.payload);
2241
+ let event;
2242
+ try {
2243
+ event = JSON.parse(payloadText, options.reviver);
2244
+ } catch {
2245
+ throw new WebhookVerificationError("Invalid JSON webhook payload");
2246
+ }
2247
+ if (options.expectedType && event.type !== options.expectedType) {
2248
+ throw new WebhookVerificationError(
2249
+ `Unexpected webhook event type: expected ${options.expectedType}, received ${event.type}`
2250
+ );
2251
+ }
2252
+ options.validate?.(event);
2253
+ return event;
2254
+ }
2255
+ function normalizeVerificationOptions(payloadOrOptions, signature, secret) {
2256
+ if (typeof payloadOrOptions === "object" && payloadOrOptions !== null && "payload" in payloadOrOptions) {
2257
+ return payloadOrOptions;
2258
+ }
2259
+ if (!signature || !secret) {
2260
+ throw new TypeError("signature and secret are required");
2261
+ }
2262
+ return {
2263
+ payload: payloadOrOptions,
2264
+ signature,
2265
+ secret
2266
+ };
2267
+ }
2268
+ function normalizeParseOptions(payloadOrOptions, signature, secret) {
2269
+ if (typeof payloadOrOptions === "object" && payloadOrOptions !== null && "payload" in payloadOrOptions) {
2270
+ return payloadOrOptions;
2271
+ }
2272
+ if (!signature || !secret) {
2273
+ throw new TypeError("signature and secret are required");
2274
+ }
2275
+ return {
2276
+ payload: payloadOrOptions,
2277
+ signature,
2278
+ secret
2279
+ };
2280
+ }
2281
+ function decodePayload(payload) {
2282
+ if (typeof payload === "string") return payload;
2283
+ return new TextDecoder().decode(toUint8Array(payload));
2284
+ }
2285
+ function toUint8Array(payload) {
2286
+ if (typeof payload === "string") return textEncoder.encode(payload);
2287
+ if (payload instanceof Uint8Array) return payload;
2288
+ return new Uint8Array(payload);
2289
+ }
2290
+ async function signWebhookPayload(payload, secret, timestamp) {
2204
2291
  const key = await crypto.subtle.importKey(
2205
2292
  "raw",
2206
- encoder.encode(secret),
2293
+ textEncoder.encode(secret),
2207
2294
  { name: "HMAC", hash: "SHA-256" },
2208
2295
  false,
2209
2296
  ["sign"]
2210
2297
  );
2211
- const mac = await crypto.subtle.sign(
2212
- "HMAC",
2213
- key,
2214
- typeof payload === "string" ? encoder.encode(payload) : payload
2215
- );
2216
- const expected = Array.from(new Uint8Array(mac)).map((b) => b.toString(16).padStart(2, "0")).join("");
2217
- return signature === `sha256=${expected}`;
2298
+ const message = timestamp ? concatUint8Arrays(textEncoder.encode(`v1:${timestamp}:`), payload) : payload;
2299
+ const mac = await crypto.subtle.sign("HMAC", key, toCryptoBuffer(message));
2300
+ return new Uint8Array(mac);
2218
2301
  }
2219
- async function parseWebhookEvent(payload, signature, secret) {
2220
- const valid = await verifyWebhookSignature(payload, signature, secret);
2221
- if (!valid) {
2222
- throw new Error("Invalid webhook signature");
2302
+ function normalizeSignature(signature) {
2303
+ const normalized = signature.startsWith("sha256=") ? signature.slice(7) : signature;
2304
+ if (!/^[a-fA-F0-9]+$/.test(normalized) || normalized.length % 2 !== 0) {
2305
+ return void 0;
2306
+ }
2307
+ const bytes = new Uint8Array(normalized.length / 2);
2308
+ for (let index = 0; index < normalized.length; index += 2) {
2309
+ bytes[index / 2] = Number.parseInt(normalized.slice(index, index + 2), 16);
2310
+ }
2311
+ return bytes;
2312
+ }
2313
+ function constantTimeEqual(expected, actual) {
2314
+ const maxLength = Math.max(expected.length, actual.length);
2315
+ let diff = expected.length ^ actual.length;
2316
+ for (let index = 0; index < maxLength; index += 1) {
2317
+ diff |= (expected[index] ?? 0) ^ (actual[index] ?? 0);
2223
2318
  }
2224
- return JSON.parse(payload);
2319
+ return diff === 0;
2320
+ }
2321
+ function parseTimestamp(timestamp) {
2322
+ const numeric = Number(timestamp);
2323
+ if (Number.isFinite(numeric)) {
2324
+ return numeric < 1e12 ? numeric * 1e3 : numeric;
2325
+ }
2326
+ const parsed = Date.parse(timestamp);
2327
+ return Number.isNaN(parsed) ? void 0 : parsed;
2328
+ }
2329
+ function concatUint8Arrays(left, right) {
2330
+ const combined = new Uint8Array(left.length + right.length);
2331
+ combined.set(left, 0);
2332
+ combined.set(right, left.length);
2333
+ return combined;
2334
+ }
2335
+ function toCryptoBuffer(bytes) {
2336
+ const copy = Uint8Array.from(bytes);
2337
+ return copy.buffer;
2225
2338
  }
2226
2339
 
2227
2340
  // src/index.ts