@agentuity/server 0.1.15 → 0.1.17

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 (113) hide show
  1. package/dist/api/api.d.ts +11 -6
  2. package/dist/api/api.d.ts.map +1 -1
  3. package/dist/api/api.js +19 -12
  4. package/dist/api/api.js.map +1 -1
  5. package/dist/api/index.d.ts +1 -0
  6. package/dist/api/index.d.ts.map +1 -1
  7. package/dist/api/index.js +1 -0
  8. package/dist/api/index.js.map +1 -1
  9. package/dist/api/org/env-delete.d.ts +16 -0
  10. package/dist/api/org/env-delete.d.ts.map +1 -0
  11. package/dist/api/org/env-delete.js +25 -0
  12. package/dist/api/org/env-delete.js.map +1 -0
  13. package/dist/api/org/env-get.d.ts +20 -0
  14. package/dist/api/org/env-get.d.ts.map +1 -0
  15. package/dist/api/org/env-get.js +26 -0
  16. package/dist/api/org/env-get.js.map +1 -0
  17. package/dist/api/org/env-update.d.ts +17 -0
  18. package/dist/api/org/env-update.d.ts.map +1 -0
  19. package/dist/api/org/env-update.js +26 -0
  20. package/dist/api/org/env-update.js.map +1 -0
  21. package/dist/api/org/index.d.ts +3 -0
  22. package/dist/api/org/index.d.ts.map +1 -1
  23. package/dist/api/org/index.js +3 -0
  24. package/dist/api/org/index.js.map +1 -1
  25. package/dist/api/queue/analytics.d.ts +108 -0
  26. package/dist/api/queue/analytics.d.ts.map +1 -0
  27. package/dist/api/queue/analytics.js +245 -0
  28. package/dist/api/queue/analytics.js.map +1 -0
  29. package/dist/api/queue/destinations.d.ts +108 -0
  30. package/dist/api/queue/destinations.d.ts.map +1 -0
  31. package/dist/api/queue/destinations.js +238 -0
  32. package/dist/api/queue/destinations.js.map +1 -0
  33. package/dist/api/queue/dlq.d.ts +100 -0
  34. package/dist/api/queue/dlq.d.ts.map +1 -0
  35. package/dist/api/queue/dlq.js +204 -0
  36. package/dist/api/queue/dlq.js.map +1 -0
  37. package/dist/api/queue/index.d.ts +55 -0
  38. package/dist/api/queue/index.d.ts.map +1 -0
  39. package/dist/api/queue/index.js +86 -0
  40. package/dist/api/queue/index.js.map +1 -0
  41. package/dist/api/queue/messages.d.ts +332 -0
  42. package/dist/api/queue/messages.d.ts.map +1 -0
  43. package/dist/api/queue/messages.js +637 -0
  44. package/dist/api/queue/messages.js.map +1 -0
  45. package/dist/api/queue/queues.d.ts +153 -0
  46. package/dist/api/queue/queues.d.ts.map +1 -0
  47. package/dist/api/queue/queues.js +319 -0
  48. package/dist/api/queue/queues.js.map +1 -0
  49. package/dist/api/queue/sources.d.ts +132 -0
  50. package/dist/api/queue/sources.d.ts.map +1 -0
  51. package/dist/api/queue/sources.js +285 -0
  52. package/dist/api/queue/sources.js.map +1 -0
  53. package/dist/api/queue/types.d.ts +1129 -0
  54. package/dist/api/queue/types.d.ts.map +1 -0
  55. package/dist/api/queue/types.js +949 -0
  56. package/dist/api/queue/types.js.map +1 -0
  57. package/dist/api/queue/util.d.ts +262 -0
  58. package/dist/api/queue/util.d.ts.map +1 -0
  59. package/dist/api/queue/util.js +171 -0
  60. package/dist/api/queue/util.js.map +1 -0
  61. package/dist/api/queue/validation.d.ts +247 -0
  62. package/dist/api/queue/validation.d.ts.map +1 -0
  63. package/dist/api/queue/validation.js +513 -0
  64. package/dist/api/queue/validation.js.map +1 -0
  65. package/dist/api/sandbox/client.d.ts +56 -2
  66. package/dist/api/sandbox/client.d.ts.map +1 -1
  67. package/dist/api/sandbox/client.js +51 -0
  68. package/dist/api/sandbox/client.js.map +1 -1
  69. package/dist/api/sandbox/get.d.ts.map +1 -1
  70. package/dist/api/sandbox/get.js +5 -0
  71. package/dist/api/sandbox/get.js.map +1 -1
  72. package/dist/api/sandbox/index.d.ts +3 -3
  73. package/dist/api/sandbox/index.d.ts.map +1 -1
  74. package/dist/api/sandbox/index.js +1 -1
  75. package/dist/api/sandbox/index.js.map +1 -1
  76. package/dist/api/sandbox/run.d.ts.map +1 -1
  77. package/dist/api/sandbox/run.js +5 -2
  78. package/dist/api/sandbox/run.js.map +1 -1
  79. package/dist/api/sandbox/runtime.d.ts.map +1 -1
  80. package/dist/api/sandbox/runtime.js +14 -0
  81. package/dist/api/sandbox/runtime.js.map +1 -1
  82. package/dist/api/sandbox/snapshot-build.d.ts +2 -0
  83. package/dist/api/sandbox/snapshot-build.d.ts.map +1 -1
  84. package/dist/api/sandbox/snapshot-build.js +4 -0
  85. package/dist/api/sandbox/snapshot-build.js.map +1 -1
  86. package/dist/api/sandbox/snapshot.d.ts +44 -1
  87. package/dist/api/sandbox/snapshot.d.ts.map +1 -1
  88. package/dist/api/sandbox/snapshot.js +77 -4
  89. package/dist/api/sandbox/snapshot.js.map +1 -1
  90. package/package.json +4 -4
  91. package/src/api/api.ts +62 -13
  92. package/src/api/index.ts +1 -0
  93. package/src/api/org/env-delete.ts +37 -0
  94. package/src/api/org/env-get.ts +43 -0
  95. package/src/api/org/env-update.ts +38 -0
  96. package/src/api/org/index.ts +3 -0
  97. package/src/api/queue/analytics.ts +313 -0
  98. package/src/api/queue/destinations.ts +321 -0
  99. package/src/api/queue/dlq.ts +283 -0
  100. package/src/api/queue/index.ts +261 -0
  101. package/src/api/queue/messages.ts +875 -0
  102. package/src/api/queue/queues.ts +448 -0
  103. package/src/api/queue/sources.ts +384 -0
  104. package/src/api/queue/types.ts +1253 -0
  105. package/src/api/queue/util.ts +204 -0
  106. package/src/api/queue/validation.ts +560 -0
  107. package/src/api/sandbox/client.ts +86 -1
  108. package/src/api/sandbox/get.ts +5 -0
  109. package/src/api/sandbox/index.ts +9 -1
  110. package/src/api/sandbox/run.ts +5 -2
  111. package/src/api/sandbox/runtime.ts +15 -0
  112. package/src/api/sandbox/snapshot-build.ts +4 -0
  113. package/src/api/sandbox/snapshot.ts +96 -5
@@ -0,0 +1,313 @@
1
+ import { z } from 'zod';
2
+ import { APIClient, APIResponseSchema } from '../api';
3
+ import {
4
+ OrgAnalyticsSchema,
5
+ QueueAnalyticsSchema,
6
+ TimeSeriesDataSchema,
7
+ SSEStatsEventSchema,
8
+ type OrgAnalytics,
9
+ type QueueAnalytics,
10
+ type TimeSeriesData,
11
+ type SSEStatsEvent,
12
+ type AnalyticsOptions,
13
+ type StreamAnalyticsOptions,
14
+ } from './types';
15
+ import { QueueError, QueueNotFoundError, queueApiPathWithQuery, buildQueueHeaders } from './util';
16
+ import { validateQueueName } from './validation';
17
+
18
+ const OrgAnalyticsResponseSchema = APIResponseSchema(z.object({ analytics: OrgAnalyticsSchema }));
19
+ const QueueAnalyticsResponseSchema = APIResponseSchema(
20
+ z.object({ analytics: QueueAnalyticsSchema })
21
+ );
22
+ const TimeSeriesResponseSchema = APIResponseSchema(z.object({ timeseries: TimeSeriesDataSchema }));
23
+
24
+ /**
25
+ * Build query string from analytics options.
26
+ */
27
+ function buildAnalyticsQuery(options?: AnalyticsOptions): string | undefined {
28
+ if (!options) return undefined;
29
+
30
+ const params = new URLSearchParams();
31
+ if (options.start) params.set('start', options.start);
32
+ if (options.end) params.set('end', options.end);
33
+ if (options.granularity) params.set('granularity', options.granularity);
34
+ if (options.projectId) params.set('project_id', options.projectId);
35
+ if (options.agentId) params.set('agent_id', options.agentId);
36
+
37
+ const query = params.toString();
38
+ return query || undefined;
39
+ }
40
+
41
+ /**
42
+ * Get org-level analytics for all queues.
43
+ *
44
+ * Returns aggregated statistics across all queues in the organization.
45
+ *
46
+ * @param client - The API client instance
47
+ * @param options - Analytics options (time range, filters)
48
+ * @returns Org-level analytics summary
49
+ * @throws {QueueError} If the API request fails
50
+ *
51
+ * @example
52
+ * ```typescript
53
+ * const analytics = await getOrgAnalytics(client, {
54
+ * start: '2026-01-14T00:00:00Z',
55
+ * end: '2026-01-15T00:00:00Z',
56
+ * });
57
+ * console.log(`Total queues: ${analytics.summary.total_queues}`);
58
+ * console.log(`Messages published: ${analytics.summary.total_messages_published}`);
59
+ * ```
60
+ */
61
+ export async function getOrgAnalytics(
62
+ client: APIClient,
63
+ options?: AnalyticsOptions
64
+ ): Promise<OrgAnalytics> {
65
+ const queryString = buildAnalyticsQuery(options);
66
+ const url = queueApiPathWithQuery('analytics/org', queryString);
67
+ const resp = await client.get(
68
+ url,
69
+ OrgAnalyticsResponseSchema,
70
+ undefined,
71
+ buildQueueHeaders(options?.orgId)
72
+ );
73
+
74
+ if (resp.success) {
75
+ return resp.data.analytics;
76
+ }
77
+
78
+ throw new QueueError({
79
+ message: resp.message || 'Failed to get org analytics',
80
+ });
81
+ }
82
+
83
+ /**
84
+ * Get detailed analytics for a specific queue.
85
+ *
86
+ * Returns current state, period statistics, latency metrics, and destination stats.
87
+ *
88
+ * @param client - The API client instance
89
+ * @param name - The queue name
90
+ * @param options - Analytics options (time range, filters)
91
+ * @returns Queue analytics
92
+ * @throws {QueueNotFoundError} If the queue does not exist
93
+ * @throws {QueueError} If the API request fails
94
+ *
95
+ * @example
96
+ * ```typescript
97
+ * const analytics = await getQueueAnalytics(client, 'order-processing', {
98
+ * start: '2026-01-14T00:00:00Z',
99
+ * });
100
+ * console.log(`Backlog: ${analytics.current.backlog}`);
101
+ * console.log(`P95 latency: ${analytics.latency.p95_ms}ms`);
102
+ * ```
103
+ */
104
+ export async function getQueueAnalytics(
105
+ client: APIClient,
106
+ name: string,
107
+ options?: AnalyticsOptions
108
+ ): Promise<QueueAnalytics> {
109
+ validateQueueName(name);
110
+ const queryString = buildAnalyticsQuery(options);
111
+ const url = queueApiPathWithQuery('analytics/queue', queryString, name);
112
+ const resp = await client.get(
113
+ url,
114
+ QueueAnalyticsResponseSchema,
115
+ undefined,
116
+ buildQueueHeaders(options?.orgId)
117
+ );
118
+
119
+ if (resp.success) {
120
+ return resp.data.analytics;
121
+ }
122
+
123
+ if (resp.message?.includes('not found')) {
124
+ throw new QueueNotFoundError({
125
+ queueName: name,
126
+ message: resp.message,
127
+ });
128
+ }
129
+
130
+ throw new QueueError({
131
+ queueName: name,
132
+ message: resp.message || 'Failed to get queue analytics',
133
+ });
134
+ }
135
+
136
+ /**
137
+ * Get time series analytics data for a queue.
138
+ *
139
+ * Returns time-bucketed metrics for visualization in charts and graphs.
140
+ *
141
+ * @param client - The API client instance
142
+ * @param name - The queue name
143
+ * @param options - Analytics options (time range, granularity)
144
+ * @returns Time series data
145
+ * @throws {QueueNotFoundError} If the queue does not exist
146
+ * @throws {QueueError} If the API request fails
147
+ *
148
+ * @example
149
+ * ```typescript
150
+ * const timeseries = await getQueueTimeSeries(client, 'order-processing', {
151
+ * granularity: 'hour',
152
+ * start: '2026-01-14T00:00:00Z',
153
+ * });
154
+ * for (const point of timeseries.series) {
155
+ * console.log(`${point.timestamp}: ${point.throughput} msg/h`);
156
+ * }
157
+ * ```
158
+ */
159
+ export async function getQueueTimeSeries(
160
+ client: APIClient,
161
+ name: string,
162
+ options?: AnalyticsOptions
163
+ ): Promise<TimeSeriesData> {
164
+ validateQueueName(name);
165
+ const queryString = buildAnalyticsQuery(options);
166
+ const url = queueApiPathWithQuery('analytics/timeseries', queryString, name);
167
+ const resp = await client.get(
168
+ url,
169
+ TimeSeriesResponseSchema,
170
+ undefined,
171
+ buildQueueHeaders(options?.orgId)
172
+ );
173
+
174
+ if (resp.success) {
175
+ return resp.data.timeseries;
176
+ }
177
+
178
+ if (resp.message?.includes('not found')) {
179
+ throw new QueueNotFoundError({
180
+ queueName: name,
181
+ message: resp.message,
182
+ });
183
+ }
184
+
185
+ throw new QueueError({
186
+ queueName: name,
187
+ message: resp.message || 'Failed to get queue time series',
188
+ });
189
+ }
190
+
191
+ /**
192
+ * Stream real-time analytics for all queues via SSE.
193
+ *
194
+ * Returns an async iterator that yields stats events at the specified interval.
195
+ * The connection stays open until the iterator is closed or an error occurs.
196
+ *
197
+ * @param client - The API client instance
198
+ * @param options - Stream options (interval, orgId)
199
+ * @returns Async iterator of SSE stats events
200
+ *
201
+ * @example
202
+ * ```typescript
203
+ * const stream = streamOrgAnalytics(client, { interval: 5 });
204
+ * for await (const event of stream) {
205
+ * console.log(`Backlog: ${event.backlog}, Throughput: ${event.throughput_1m}/min`);
206
+ * }
207
+ * ```
208
+ */
209
+ export async function* streamOrgAnalytics(
210
+ client: APIClient,
211
+ options?: StreamAnalyticsOptions
212
+ ): AsyncGenerator<SSEStatsEvent, void, unknown> {
213
+ const params = new URLSearchParams();
214
+ if (options?.interval) params.set('interval', String(options.interval));
215
+ const queryString = params.toString() || undefined;
216
+
217
+ const url = queueApiPathWithQuery('analytics/stream', queryString);
218
+
219
+ yield* streamSSE(client, url, options?.orgId);
220
+ }
221
+
222
+ /**
223
+ * Stream real-time analytics for a specific queue via SSE.
224
+ *
225
+ * Returns an async iterator that yields stats events at the specified interval.
226
+ *
227
+ * @param client - The API client instance
228
+ * @param name - The queue name
229
+ * @param options - Stream options (interval, orgId)
230
+ * @returns Async iterator of SSE stats events
231
+ *
232
+ * @example
233
+ * ```typescript
234
+ * const stream = streamQueueAnalytics(client, 'order-processing', { interval: 5 });
235
+ * for await (const event of stream) {
236
+ * console.log(`Backlog: ${event.backlog}, In-flight: ${event.messages_in_flight}`);
237
+ * }
238
+ * ```
239
+ */
240
+ export async function* streamQueueAnalytics(
241
+ client: APIClient,
242
+ name: string,
243
+ options?: StreamAnalyticsOptions
244
+ ): AsyncGenerator<SSEStatsEvent, void, unknown> {
245
+ validateQueueName(name);
246
+
247
+ const params = new URLSearchParams();
248
+ if (options?.interval) params.set('interval', String(options.interval));
249
+ const queryString = params.toString() || undefined;
250
+
251
+ const url = queueApiPathWithQuery('analytics/stream', queryString, name);
252
+
253
+ yield* streamSSE(client, url, options?.orgId, name);
254
+ }
255
+
256
+ /**
257
+ * Internal helper to stream SSE events from a URL.
258
+ * Uses rawGet for streaming response access.
259
+ */
260
+ async function* streamSSE(
261
+ client: APIClient,
262
+ url: string,
263
+ orgId?: string,
264
+ queueName?: string
265
+ ): AsyncGenerator<SSEStatsEvent, void, unknown> {
266
+ const response = await client.rawGet(url, undefined, buildQueueHeaders(orgId));
267
+
268
+ if (!response.ok) {
269
+ if (response.status === 404 && queueName) {
270
+ throw new QueueNotFoundError({ queueName });
271
+ }
272
+ throw new QueueError({
273
+ message: `SSE connection failed: ${response.status} ${response.statusText}`,
274
+ });
275
+ }
276
+
277
+ const reader = response.body?.getReader();
278
+ if (!reader) {
279
+ throw new QueueError({ message: 'No response body for SSE stream' });
280
+ }
281
+
282
+ const decoder = new TextDecoder();
283
+ let buffer = '';
284
+
285
+ try {
286
+ while (true) {
287
+ const { done, value } = await reader.read();
288
+ if (done) break;
289
+
290
+ buffer += decoder.decode(value, { stream: true });
291
+ const lines = buffer.split('\n');
292
+ buffer = lines.pop() || '';
293
+
294
+ for (const line of lines) {
295
+ if (line.startsWith('data: ')) {
296
+ const jsonStr = line.slice(6).trim();
297
+ if (jsonStr && jsonStr !== '{}') {
298
+ try {
299
+ const parsed = JSON.parse(jsonStr);
300
+ const event = SSEStatsEventSchema.parse(parsed);
301
+ yield event;
302
+ } catch {
303
+ // Skip malformed events
304
+ }
305
+ }
306
+ }
307
+ }
308
+ }
309
+ } finally {
310
+ await reader.cancel();
311
+ reader.releaseLock();
312
+ }
313
+ }
@@ -0,0 +1,321 @@
1
+ import { z } from 'zod';
2
+ import { APIClient, APIResponseSchema, APIResponseSchemaNoData, APIError } from '../api';
3
+ import {
4
+ DestinationSchema,
5
+ type Destination,
6
+ type CreateDestinationRequest,
7
+ type UpdateDestinationRequest,
8
+ type QueueApiOptions,
9
+ CreateDestinationRequestSchema,
10
+ UpdateDestinationRequestSchema,
11
+ } from './types';
12
+ import {
13
+ QueueError,
14
+ QueueNotFoundError,
15
+ DestinationNotFoundError,
16
+ DestinationAlreadyExistsError,
17
+ queueApiPath,
18
+ buildQueueHeaders,
19
+ } from './util';
20
+ import { validateQueueName, validateDestinationId, validateDestinationConfig } from './validation';
21
+
22
+ const DestinationResponseSchema = APIResponseSchema(z.object({ destination: DestinationSchema }));
23
+ const DestinationsListResponseSchema = APIResponseSchema(
24
+ z.object({
25
+ destinations: z.array(DestinationSchema),
26
+ })
27
+ );
28
+ const DeleteDestinationResponseSchema = APIResponseSchemaNoData();
29
+
30
+ /**
31
+ * Create a destination for a queue.
32
+ *
33
+ * Destinations are webhook endpoints that automatically receive messages when
34
+ * they are published to a queue. When a message is published, it will be
35
+ * delivered via HTTP POST to all active destinations configured for that queue.
36
+ *
37
+ * @param client - The API client instance
38
+ * @param queueName - The name of the queue to add the destination to
39
+ * @param params - Destination configuration including URL and optional settings
40
+ * @returns The created destination with assigned ID
41
+ * @throws {QueueValidationError} If validation fails (invalid queue name or config)
42
+ * @throws {QueueNotFoundError} If the queue does not exist
43
+ * @throws {QueueError} If the API request fails
44
+ *
45
+ * @example
46
+ * ```typescript
47
+ * const destination = await createDestination(client, 'order-events', {
48
+ * url: 'https://api.example.com/webhooks/orders',
49
+ * config: {
50
+ * retry_attempts: 3,
51
+ * timeout_seconds: 30,
52
+ * },
53
+ * });
54
+ * console.log(`Created destination ${destination.id}`);
55
+ * ```
56
+ */
57
+ export async function createDestination(
58
+ client: APIClient,
59
+ queueName: string,
60
+ params: CreateDestinationRequest,
61
+ options?: QueueApiOptions
62
+ ): Promise<Destination> {
63
+ validateQueueName(queueName);
64
+ if (params.config) {
65
+ validateDestinationConfig(params.config);
66
+ }
67
+
68
+ const url = queueApiPath('destinations/create', queueName);
69
+
70
+ try {
71
+ const resp = await client.post(
72
+ url,
73
+ params,
74
+ DestinationResponseSchema,
75
+ CreateDestinationRequestSchema,
76
+ undefined,
77
+ buildQueueHeaders(options?.orgId)
78
+ );
79
+
80
+ if (resp.success) {
81
+ return resp.data.destination;
82
+ }
83
+
84
+ if (resp.message?.includes('queue') && resp.message?.includes('not found')) {
85
+ throw new QueueNotFoundError({
86
+ queueName,
87
+ message: resp.message,
88
+ });
89
+ }
90
+
91
+ if (resp.message?.includes('already exists')) {
92
+ throw new DestinationAlreadyExistsError({
93
+ queueName,
94
+ url: params.config?.url,
95
+ message: `A destination with URL "${params.config?.url}" already exists for queue "${queueName}"`,
96
+ });
97
+ }
98
+
99
+ throw new QueueError({
100
+ queueName,
101
+ message: resp.message || 'Failed to create destination',
102
+ });
103
+ } catch (error) {
104
+ if (error instanceof APIError) {
105
+ const message = error.message || '';
106
+ if (message.includes('already exists')) {
107
+ throw new DestinationAlreadyExistsError({
108
+ queueName,
109
+ url: params.config?.url,
110
+ message: `A destination with URL "${params.config?.url}" already exists for queue "${queueName}"`,
111
+ });
112
+ }
113
+ if (message.includes('queue') && message.includes('not found')) {
114
+ throw new QueueNotFoundError({
115
+ queueName,
116
+ message,
117
+ });
118
+ }
119
+ throw new QueueError({
120
+ queueName,
121
+ message: message || 'Failed to create destination',
122
+ });
123
+ }
124
+ throw error;
125
+ }
126
+ }
127
+
128
+ /**
129
+ * List all destinations for a queue.
130
+ *
131
+ * Retrieves all webhook destinations configured for a queue. Each destination
132
+ * represents an endpoint that receives messages when they are published.
133
+ *
134
+ * @param client - The API client instance
135
+ * @param queueName - The name of the queue
136
+ * @returns Array of destinations configured for the queue
137
+ * @throws {QueueValidationError} If validation fails (invalid queue name)
138
+ * @throws {QueueNotFoundError} If the queue does not exist
139
+ * @throws {QueueError} If the API request fails
140
+ *
141
+ * @example
142
+ * ```typescript
143
+ * const destinations = await listDestinations(client, 'order-events');
144
+ * for (const dest of destinations) {
145
+ * console.log(`Destination ${dest.id}: ${dest.url} (${dest.enabled ? 'enabled' : 'disabled'})`);
146
+ * }
147
+ * ```
148
+ */
149
+ export async function listDestinations(
150
+ client: APIClient,
151
+ queueName: string,
152
+ options?: QueueApiOptions
153
+ ): Promise<Destination[]> {
154
+ validateQueueName(queueName);
155
+ const url = queueApiPath('destinations/list', queueName);
156
+ const resp = await client.get(
157
+ url,
158
+ DestinationsListResponseSchema,
159
+ undefined,
160
+ buildQueueHeaders(options?.orgId)
161
+ );
162
+
163
+ if (resp.success) {
164
+ return resp.data.destinations;
165
+ }
166
+
167
+ if (resp.message?.includes('not found')) {
168
+ throw new QueueNotFoundError({
169
+ queueName,
170
+ message: resp.message,
171
+ });
172
+ }
173
+
174
+ throw new QueueError({
175
+ queueName,
176
+ message: resp.message || 'Failed to list destinations',
177
+ });
178
+ }
179
+
180
+ /**
181
+ * Update a destination's configuration.
182
+ *
183
+ * Modifies an existing destination's settings such as URL, enabled status,
184
+ * or retry configuration. Only the fields provided in params will be updated.
185
+ *
186
+ * @param client - The API client instance
187
+ * @param queueName - The name of the queue
188
+ * @param destinationId - The destination ID to update (prefixed with dest_)
189
+ * @param params - Fields to update (partial update supported)
190
+ * @returns The updated destination
191
+ * @throws {QueueValidationError} If validation fails (invalid queue name, destination ID, or config)
192
+ * @throws {DestinationNotFoundError} If the destination does not exist
193
+ * @throws {QueueNotFoundError} If the queue does not exist
194
+ * @throws {QueueError} If the API request fails
195
+ *
196
+ * @example
197
+ * ```typescript
198
+ * // Disable a destination temporarily
199
+ * const updated = await updateDestination(client, 'order-events', 'dest_abc123', {
200
+ * enabled: false,
201
+ * });
202
+ *
203
+ * // Update URL and retry settings
204
+ * const updated = await updateDestination(client, 'order-events', 'dest_abc123', {
205
+ * url: 'https://api.example.com/v2/webhooks/orders',
206
+ * config: {
207
+ * retry_attempts: 5,
208
+ * },
209
+ * });
210
+ * ```
211
+ */
212
+ export async function updateDestination(
213
+ client: APIClient,
214
+ queueName: string,
215
+ destinationId: string,
216
+ params: UpdateDestinationRequest,
217
+ options?: QueueApiOptions
218
+ ): Promise<Destination> {
219
+ validateQueueName(queueName);
220
+ validateDestinationId(destinationId);
221
+ if (params.config) {
222
+ validateDestinationConfig(params.config);
223
+ }
224
+
225
+ const url = queueApiPath('destinations/update', queueName, destinationId);
226
+ const resp = await client.patch(
227
+ url,
228
+ params,
229
+ DestinationResponseSchema,
230
+ UpdateDestinationRequestSchema,
231
+ undefined,
232
+ buildQueueHeaders(options?.orgId)
233
+ );
234
+
235
+ if (resp.success) {
236
+ return resp.data.destination;
237
+ }
238
+
239
+ if (resp.message?.includes('destination') && resp.message?.includes('not found')) {
240
+ throw new DestinationNotFoundError({
241
+ queueName,
242
+ destinationId,
243
+ message: resp.message,
244
+ });
245
+ }
246
+
247
+ if (resp.message?.includes('queue') && resp.message?.includes('not found')) {
248
+ throw new QueueNotFoundError({
249
+ queueName,
250
+ message: resp.message,
251
+ });
252
+ }
253
+
254
+ throw new QueueError({
255
+ queueName,
256
+ message: resp.message || 'Failed to update destination',
257
+ });
258
+ }
259
+
260
+ /**
261
+ * Delete a destination from a queue.
262
+ *
263
+ * Permanently removes a webhook destination. Messages will no longer be
264
+ * delivered to this endpoint. This action cannot be undone.
265
+ *
266
+ * @param client - The API client instance
267
+ * @param queueName - The name of the queue
268
+ * @param destinationId - The destination ID to delete (prefixed with dest_)
269
+ * @returns void
270
+ * @throws {QueueValidationError} If validation fails (invalid queue name or destination ID)
271
+ * @throws {DestinationNotFoundError} If the destination does not exist
272
+ * @throws {QueueNotFoundError} If the queue does not exist
273
+ * @throws {QueueError} If the API request fails
274
+ *
275
+ * @example
276
+ * ```typescript
277
+ * await deleteDestination(client, 'order-events', 'dest_abc123');
278
+ * console.log('Destination deleted');
279
+ * ```
280
+ */
281
+ export async function deleteDestination(
282
+ client: APIClient,
283
+ queueName: string,
284
+ destinationId: string,
285
+ options?: QueueApiOptions
286
+ ): Promise<void> {
287
+ validateQueueName(queueName);
288
+ validateDestinationId(destinationId);
289
+
290
+ const url = queueApiPath('destinations/delete', queueName, destinationId);
291
+ const resp = await client.delete(
292
+ url,
293
+ DeleteDestinationResponseSchema,
294
+ undefined,
295
+ buildQueueHeaders(options?.orgId)
296
+ );
297
+
298
+ if (resp.success) {
299
+ return;
300
+ }
301
+
302
+ if (resp.message?.includes('destination') && resp.message?.includes('not found')) {
303
+ throw new DestinationNotFoundError({
304
+ queueName,
305
+ destinationId,
306
+ message: resp.message,
307
+ });
308
+ }
309
+
310
+ if (resp.message?.includes('queue') && resp.message?.includes('not found')) {
311
+ throw new QueueNotFoundError({
312
+ queueName,
313
+ message: resp.message,
314
+ });
315
+ }
316
+
317
+ throw new QueueError({
318
+ queueName,
319
+ message: resp.message || 'Failed to delete destination',
320
+ });
321
+ }