@kaiban/sdk 0.1.9 → 0.2.0

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 (90) hide show
  1. package/README.md +142 -19
  2. package/dist/cjs/index.js +21 -0
  3. package/dist/cjs/lib/client.js +54 -0
  4. package/dist/cjs/lib/http/HttpClient.js +243 -0
  5. package/dist/cjs/lib/http/errors.js +143 -0
  6. package/dist/cjs/lib/http/types.js +2 -0
  7. package/dist/cjs/lib/resources/ActivitiesClient.js +164 -0
  8. package/dist/cjs/lib/resources/AgentsClient.js +196 -0
  9. package/dist/cjs/lib/resources/BoardsClient.js +78 -0
  10. package/dist/cjs/lib/resources/CardsClient.js +352 -0
  11. package/dist/cjs/lib/resources/ExternalChannelsClient.js +78 -0
  12. package/dist/cjs/lib/resources/ModelCost.js +87 -0
  13. package/dist/cjs/lib/resources/ResourcesClient.js +78 -0
  14. package/dist/cjs/lib/resources/TeamsClient.js +96 -0
  15. package/dist/cjs/package.json +3 -0
  16. package/dist/cjs/test/helpers/mockFetch.js +27 -0
  17. package/dist/cjs/types/entities/a2a-dataparts.js +17 -0
  18. package/dist/cjs/types/entities/activities.js +76 -0
  19. package/dist/cjs/types/entities/agent.js +28 -0
  20. package/dist/cjs/types/entities/board.js +2 -0
  21. package/dist/cjs/types/entities/card.js +29 -0
  22. package/dist/cjs/types/entities/costs.js +2 -0
  23. package/dist/cjs/types/entities/external-channel.js +19 -0
  24. package/dist/cjs/types/entities/index.js +25 -0
  25. package/dist/cjs/types/entities/resource.js +10 -0
  26. package/dist/cjs/types/entities/shared.js +3 -0
  27. package/dist/cjs/types/entities/team.js +2 -0
  28. package/dist/cjs/types/entities.js +17 -0
  29. package/dist/cjs/types/responses.js +2 -0
  30. package/dist/esm/index.js +5 -0
  31. package/dist/{lib → esm/lib}/client.js +9 -9
  32. package/dist/{lib → esm/lib}/http/HttpClient.js +1 -1
  33. package/dist/{lib → esm/lib}/resources/CardsClient.js +1 -1
  34. package/dist/esm/types/entities/index.js +9 -0
  35. package/dist/esm/types/entities.js +1 -0
  36. package/dist/esm/types/requests.js +19 -0
  37. package/package.json +16 -7
  38. package/dist/index.js +0 -5
  39. package/dist/types/entities/index.js +0 -9
  40. package/dist/types/entities.js +0 -1
  41. /package/dist/{types → cjs/types}/requests.js +0 -0
  42. /package/dist/{lib → esm/lib}/http/errors.js +0 -0
  43. /package/dist/{lib → esm/lib}/http/types.js +0 -0
  44. /package/dist/{lib → esm/lib}/resources/ActivitiesClient.js +0 -0
  45. /package/dist/{lib → esm/lib}/resources/AgentsClient.js +0 -0
  46. /package/dist/{lib → esm/lib}/resources/BoardsClient.js +0 -0
  47. /package/dist/{lib → esm/lib}/resources/ExternalChannelsClient.js +0 -0
  48. /package/dist/{lib → esm/lib}/resources/ModelCost.js +0 -0
  49. /package/dist/{lib → esm/lib}/resources/ResourcesClient.js +0 -0
  50. /package/dist/{lib → esm/lib}/resources/TeamsClient.js +0 -0
  51. /package/dist/{test → esm/test}/helpers/mockFetch.js +0 -0
  52. /package/dist/{types → esm/types}/entities/a2a-dataparts.js +0 -0
  53. /package/dist/{types → esm/types}/entities/activities.js +0 -0
  54. /package/dist/{types → esm/types}/entities/agent.js +0 -0
  55. /package/dist/{types → esm/types}/entities/board.js +0 -0
  56. /package/dist/{types → esm/types}/entities/card.js +0 -0
  57. /package/dist/{types → esm/types}/entities/costs.js +0 -0
  58. /package/dist/{types → esm/types}/entities/external-channel.js +0 -0
  59. /package/dist/{types → esm/types}/entities/resource.js +0 -0
  60. /package/dist/{types → esm/types}/entities/shared.js +0 -0
  61. /package/dist/{types → esm/types}/entities/team.js +0 -0
  62. /package/dist/{types → esm/types}/responses.js +0 -0
  63. /package/dist/{index.d.ts → types/index.d.ts} +0 -0
  64. /package/dist/{lib → types/lib}/client.d.ts +0 -0
  65. /package/dist/{lib → types/lib}/http/HttpClient.d.ts +0 -0
  66. /package/dist/{lib → types/lib}/http/errors.d.ts +0 -0
  67. /package/dist/{lib → types/lib}/http/types.d.ts +0 -0
  68. /package/dist/{lib → types/lib}/resources/ActivitiesClient.d.ts +0 -0
  69. /package/dist/{lib → types/lib}/resources/AgentsClient.d.ts +0 -0
  70. /package/dist/{lib → types/lib}/resources/BoardsClient.d.ts +0 -0
  71. /package/dist/{lib → types/lib}/resources/CardsClient.d.ts +0 -0
  72. /package/dist/{lib → types/lib}/resources/ExternalChannelsClient.d.ts +0 -0
  73. /package/dist/{lib → types/lib}/resources/ModelCost.d.ts +0 -0
  74. /package/dist/{lib → types/lib}/resources/ResourcesClient.d.ts +0 -0
  75. /package/dist/{lib → types/lib}/resources/TeamsClient.d.ts +0 -0
  76. /package/dist/{test → types/test}/helpers/mockFetch.d.ts +0 -0
  77. /package/dist/types/{entities → types/entities}/a2a-dataparts.d.ts +0 -0
  78. /package/dist/types/{entities → types/entities}/activities.d.ts +0 -0
  79. /package/dist/types/{entities → types/entities}/agent.d.ts +0 -0
  80. /package/dist/types/{entities → types/entities}/board.d.ts +0 -0
  81. /package/dist/types/{entities → types/entities}/card.d.ts +0 -0
  82. /package/dist/types/{entities → types/entities}/costs.d.ts +0 -0
  83. /package/dist/types/{entities → types/entities}/external-channel.d.ts +0 -0
  84. /package/dist/types/{entities → types/entities}/index.d.ts +0 -0
  85. /package/dist/types/{entities → types/entities}/resource.d.ts +0 -0
  86. /package/dist/types/{entities → types/entities}/shared.d.ts +0 -0
  87. /package/dist/types/{entities → types/entities}/team.d.ts +0 -0
  88. /package/dist/types/{entities.d.ts → types/entities.d.ts} +0 -0
  89. /package/dist/types/{requests.d.ts → types/requests.d.ts} +0 -0
  90. /package/dist/types/{responses.d.ts → types/responses.d.ts} +0 -0
package/README.md CHANGED
@@ -14,7 +14,7 @@ npm install @kaiban/sdk
14
14
  import { createKaibanClient } from '@kaiban/sdk';
15
15
 
16
16
  const client = createKaibanClient({
17
- tenant: 'agi',
17
+ tenant: 'your-tenant',
18
18
  token: process.env.KAIBAN_TOKEN,
19
19
  });
20
20
 
@@ -22,11 +22,12 @@ const result = await client.agents.list({ limit: 10 });
22
22
  console.log(result.data); // Array of agents
23
23
  ```
24
24
 
25
- ## Auth and tenancy
25
+ ## Auth and Tenancy
26
26
 
27
27
  - Uses `Authorization: Bearer <token>` automatically when provided
28
- - Sends `x-tenant` with the configured tenant
28
+ - Sends `x-tenant` header with the configured tenant
29
29
  - Default base URL: `https://{tenant}.kaiban.io`
30
+ - Custom base URL can be configured via `baseUrl` option
30
31
 
31
32
  ## Resources
32
33
 
@@ -41,13 +42,14 @@ The SDK provides clients for the following API resources:
41
42
  - **boards**: `list`, `listAll`, `get`
42
43
  - **resources**: `list`, `listAll`, `get`
43
44
  - **external_channels**: `list`, `listAll`, `get`
45
+ - **costs**: `calculateCosts` (offline utility for model cost calculations)
44
46
 
45
47
  ### Pagination
46
48
 
47
49
  All `list` methods return paginated results with cursor-based navigation:
48
50
 
49
51
  ```ts
50
- import { type Paginated, type Agent, type ListParams } from '@kaiban/sdk';
52
+ import { type Paginated, type Agent, type Card, type ListParams } from '@kaiban/sdk';
51
53
 
52
54
  // Basic pagination
53
55
  const params: ListParams = {
@@ -76,6 +78,13 @@ if (result.pagination.prev_cursor) {
76
78
  before: result.pagination.prev_cursor,
77
79
  });
78
80
  }
81
+
82
+ // Filter cards by metadata (cards only)
83
+ const cardsResult: Paginated<Card> = await client.cards.list({
84
+ limit: 20,
85
+ metadata: { thread_id: 'thread-123', user_id: 'user-456' },
86
+ });
87
+ // This becomes: ?metadata.thread_id=thread-123&metadata.user_id=user-456
79
88
  ```
80
89
 
81
90
  All `listAll` methods return async generators that automatically handle pagination:
@@ -91,13 +100,19 @@ for await (const agent of client.agents.listAll({ limit: 100 })) {
91
100
 
92
101
  All types are published and re-exported from `@kaiban/sdk`. Available types include:
93
102
 
94
- - **Entities**: `Agent`, `Card`, `Activity`, `Team`, `Board`, `Resource`, `ExternalChannel`
95
- - **Request/Response**: `ActivityCreate`, `ListParams`, `Paginated`, `PaginationMeta`
96
- - **Configuration**: `KaibanClientConfig`, `RequestOptions`
97
- - **Actors & Changes**: `ActivityActor`, `ActivityChange`
103
+ - **Entities**: `Agent`, `Card`, `Activity`, `Team`, `Board`, `Resource`, `ExternalChannel`, `TeamMember`, `Column`
104
+ - **Agent Types**: `AgentFeedback`, `AgentSupervisorFeedback`, `AgentType`, `AgentStatus`, `AgentFeedbackStatus`, `AgentFeedbackType`, `AgentFeedbackEvaluation`
105
+ - **Activity Types**: `ActivityCreate`, `ActivityActor`, `ActivityChange`, `ActivityType`
98
106
  - **Card Types**: `CardStatus`, `CardPart`
99
- - **Enums/Constants**: `AgentType`, `AgentStatus`, `ActivityType`, `ResourceStatus`, `ExternalChannelType`, `ExternalChannelStatus`, `AgentFeedbackType`, `AgentFeedbackEvaluation`
100
- - **Error classes**: `ApiError`, `NotFoundError`, `BadRequestError`, `UnauthorizedError`, `ForbiddenError`, `ConflictError`, `TooManyRequestsError`, `InternalServerError`, `TimeoutError`, `AbortedError`
107
+ - **Board Types**: `Column`
108
+ - **Resource Types**: `ResourceStatus`
109
+ - **External Channel Types**: `ExternalChannelType`, `ExternalChannelStatus`, `ExternalChannelPriority`
110
+ - **Cost Types**: `ModelCostInput`, `ModelCostOutput`
111
+ - **A2A Data Parts**: `A2ADataPartType`, `ToolCallStartPart`, `ToolCallArgsPart`, `ToolCallEndPart`, `ToolCallResultPart`, `TaskInfoMessagePart`, `TaskStepOutputPart`, `UserEvaluationPart`, `UserThreadFeedbackPart`, `UserCloseThreadPart`, `GenerateReportPart`, `KaibanActivityPart`, `AgentStatusPart`
112
+ - **Request/Response**: `ListParams`, `Paginated`, `PaginationMeta`
113
+ - **Configuration**: `KaibanClientConfig`, `RequestOptions`
114
+ - **Shared Types**: `ISODate`
115
+ - **Error classes**: `HttpError`, `ApiError`, `BadRequestError`, `ValidationError`, `UnauthorizedError`, `ForbiddenError`, `NotFoundError`, `ConflictError`, `RateLimitError`, `UnavailableError`, `ServerError`, `TimeoutError`, `AbortedError`
101
116
 
102
117
  ### Example: Using Types
103
118
 
@@ -207,21 +222,36 @@ import { createKaibanClient, type KaibanClientConfig } from '@kaiban/sdk';
207
222
  const config: KaibanClientConfig = {
208
223
  tenant: 'your-tenant', // Required: Your tenant identifier
209
224
  token: 'your-api-token', // Required: Your API authentication token
210
- baseUrl: 'https://custom-host', // Optional: Custom API base URL
225
+ baseUrl: 'https://custom-host', // Optional: Custom API base URL (default: https://{tenant}.kaiban.io)
211
226
  timeoutMs: 30000, // Optional: Request timeout in milliseconds (default: 30000)
212
227
  retry: {
213
228
  // Optional: Retry configuration
214
229
  maxAttempts: 3, // Maximum number of retry attempts (default: 3)
215
- backoffMs: 1000, // Initial backoff delay (default: 1000)
216
- maxBackoffMs: 30000, // Maximum backoff delay (default: 30000)
230
+ backoffMs: 1000, // Initial backoff delay in ms (default: 1000)
231
+ maxBackoffMs: 30000, // Maximum backoff delay in ms (default: 30000)
217
232
  jitter: true, // Add randomness to backoff (default: true)
233
+ retryOn: [429, 502, 503, 504], // Optional: HTTP status codes to retry on
234
+ retryMethods: ['GET', 'PUT', 'DELETE'], // Optional: HTTP methods to retry
235
+ },
236
+ fetch: globalThis.fetch, // Optional: Custom fetch implementation
237
+ onRequest: ({ method, url, init }) => {
238
+ // Optional: Hook called before each request
239
+ console.log(`Making ${method} request to ${url}`);
240
+ },
241
+ onResponse: ({ status, url, response }) => {
242
+ // Optional: Hook called after each response
243
+ console.log(`Received ${status} from ${url}`);
218
244
  },
219
245
  };
220
246
 
221
247
  const client = createKaibanClient(config);
248
+
249
+ // You can also update the token dynamically after client creation
250
+ client.setToken('new-token'); // Set a new token
251
+ client.setToken(); // Clear the token
222
252
  ```
223
253
 
224
- ## Per-call options
254
+ ## Per-call Options
225
255
 
226
256
  All resource methods accept an optional `RequestOptions` parameter for per-call overrides:
227
257
 
@@ -242,29 +272,70 @@ await client.cards.list({ limit: 20 }, options);
242
272
  await client.agents.get('agent-123', options);
243
273
  ```
244
274
 
245
- ## Error handling
275
+ ## Error Handling
246
276
 
247
- The SDK provides typed error classes for different HTTP status codes:
277
+ The SDK provides typed error classes for different HTTP status codes and network errors:
248
278
 
249
279
  ```ts
250
- import { createKaibanClient, ApiError, NotFoundError, BadRequestError } from '@kaiban/sdk';
280
+ import {
281
+ createKaibanClient,
282
+ ApiError,
283
+ NotFoundError,
284
+ BadRequestError,
285
+ ValidationError,
286
+ UnauthorizedError,
287
+ RateLimitError,
288
+ TimeoutError,
289
+ AbortedError,
290
+ } from '@kaiban/sdk';
291
+
292
+ const client = createKaibanClient({
293
+ tenant: 'your-tenant',
294
+ token: process.env.KAIBAN_TOKEN!,
295
+ });
251
296
 
252
297
  try {
253
298
  await client.cards.get('non-existent-id');
254
299
  } catch (err) {
255
300
  if (err instanceof NotFoundError) {
256
301
  console.error('Card not found:', err.message);
302
+ console.error('Error code:', err.code); // Optional error code from API
303
+ } else if (err instanceof ValidationError) {
304
+ console.error('Validation failed:', err.message);
305
+ console.error('Details:', err.details); // Additional error details from API
257
306
  } else if (err instanceof BadRequestError) {
258
307
  console.error('Invalid request:', err.message);
308
+ } else if (err instanceof UnauthorizedError) {
309
+ console.error('Authentication required:', err.message);
310
+ } else if (err instanceof RateLimitError) {
311
+ console.error('Rate limit exceeded:', err.message);
312
+ } else if (err instanceof TimeoutError) {
313
+ console.error('Request timed out');
314
+ } else if (err instanceof AbortedError) {
315
+ console.error('Request was cancelled');
259
316
  } else if (err instanceof ApiError) {
260
- console.error('API error:', err.status, err.message);
317
+ console.error('API error:', err.message);
261
318
  } else {
262
319
  console.error('Unexpected error:', err);
263
320
  }
264
321
  }
265
322
  ```
266
323
 
267
- Available error classes: `ApiError`, `BadRequestError`, `UnauthorizedError`, `ForbiddenError`, `NotFoundError`, `ConflictError`, `TooManyRequestsError`, `InternalServerError`, `TimeoutError`, `AbortedError`.
324
+ ### Available Error Classes
325
+
326
+ - **`ApiError`**: Base class for all API errors (includes `code`, `message`, `details` properties)
327
+ - **`BadRequestError`**: 400 - Invalid request format or parameters
328
+ - **`ValidationError`**: 422 - Request validation failed
329
+ - **`UnauthorizedError`**: 401 - Authentication required or failed
330
+ - **`ForbiddenError`**: 403 - Insufficient permissions
331
+ - **`NotFoundError`**: 404 - Resource not found
332
+ - **`ConflictError`**: 409 - Resource conflict
333
+ - **`RateLimitError`**: 429 - Too many requests
334
+ - **`UnavailableError`**: 502, 503, 504 - Service temporarily unavailable
335
+ - **`ServerError`**: 500 or other server errors
336
+ - **`TimeoutError`**: Request exceeded configured timeout
337
+ - **`AbortedError`**: Request was cancelled via AbortSignal
338
+ - **`HttpError`**: Low-level HTTP error (includes `status`, `url`, `body` properties)
268
339
 
269
340
  ## Examples by resource
270
341
 
@@ -519,3 +590,55 @@ for await (const channel of client.external_channels.listAll()) {
519
590
  // Get a specific channel
520
591
  const channel = await client.external_channels.get('channel-123');
521
592
  ```
593
+
594
+ ### Model Costs
595
+
596
+ The `costs` client provides an offline utility to calculate costs for LLM usage based on token counts:
597
+
598
+ ```ts
599
+ import { type ModelCostInput } from '@kaiban/sdk';
600
+
601
+ // Calculate costs for multiple model usages
602
+ const usages: ModelCostInput[] = [
603
+ {
604
+ model: 'gpt-4o',
605
+ inputTokens: 1500,
606
+ outputTokens: 500,
607
+ reasoningTokens: 200, // Optional: reasoning tokens for models that support it
608
+ },
609
+ {
610
+ model: 'gpt-4o-mini',
611
+ inputTokens: 3000,
612
+ outputTokens: 1000,
613
+ },
614
+ ];
615
+
616
+ const result = client.costs.calculateCosts(usages);
617
+
618
+ console.log('Total cost:', result.totalCost); // Total cost in dollars
619
+ console.log('Total tokens:', result.totalTokens); // Total tokens used
620
+
621
+ // Get detailed costs per model
622
+ result.costsByModel.forEach((modelCost) => {
623
+ console.log(`Model: ${modelCost.model}`);
624
+ console.log(` Input tokens: ${modelCost.inputTokens}`);
625
+ console.log(` Output tokens: ${modelCost.outputTokens}`);
626
+ console.log(` Reasoning tokens: ${modelCost.reasoningTokens}`);
627
+ console.log(` Charged output tokens: ${modelCost.chargedOutputTokens}`); // output + reasoning
628
+ console.log(` Total tokens: ${modelCost.totalTokens}`);
629
+ console.log(` Input cost: $${modelCost.inputCost.toFixed(6)}`);
630
+ console.log(` Output cost: $${modelCost.outputCost.toFixed(6)}`);
631
+ console.log(` Total cost: $${modelCost.totalCost.toFixed(6)}`);
632
+ });
633
+ ```
634
+
635
+ Supported models and their pricing (per 1M tokens):
636
+
637
+ - **gpt-4o**: $5.00 input, $20.00 output
638
+ - **gpt-4o-mini**: $0.60 input, $2.40 output
639
+ - **gpt-5**: $1.50 input, $10.00 output
640
+ - **gpt-5-mini**: $0.25 input, $2.00 output
641
+ - **gpt-5-nano**: $0.05 input, $0.40 output
642
+ - **text-embedding-3-small**: $0.02 input, $0.00 output
643
+
644
+ **Note:** Reasoning tokens (when provided) are billed as output tokens.
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./lib/client"), exports);
18
+ __exportStar(require("./lib/http/errors"), exports);
19
+ __exportStar(require("./lib/http/types"), exports);
20
+ __exportStar(require("./types/entities"), exports);
21
+ __exportStar(require("./types/responses"), exports);
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createKaibanClient = createKaibanClient;
4
+ const HttpClient_1 = require("./http/HttpClient");
5
+ const ActivitiesClient_1 = require("./resources/ActivitiesClient");
6
+ const AgentsClient_1 = require("./resources/AgentsClient");
7
+ const BoardsClient_1 = require("./resources/BoardsClient");
8
+ const CardsClient_1 = require("./resources/CardsClient");
9
+ const ExternalChannelsClient_1 = require("./resources/ExternalChannelsClient");
10
+ const ModelCost_1 = require("./resources/ModelCost");
11
+ const ResourcesClient_1 = require("./resources/ResourcesClient");
12
+ const TeamsClient_1 = require("./resources/TeamsClient");
13
+ /**
14
+ * Create a Kaiban SDK client instance
15
+ *
16
+ * @param config - Client configuration object
17
+ * @param config.tenant - The tenant identifier for your Kaiban instance
18
+ * @param config.token - Optional bearer token for authentication
19
+ * @param config.baseUrl - Optional custom base URL for the API
20
+ * @param config.maxRetries - Optional maximum number of request retries (default: 3)
21
+ * @param config.timeout - Optional request timeout in milliseconds (default: 30000)
22
+ *
23
+ * @returns A configured Kaiban client instance
24
+ *
25
+ * @example
26
+ * ```typescript
27
+ * import { createKaibanClient } from 'kaiban-sdk';
28
+ *
29
+ * const client = createKaibanClient({
30
+ * tenant: 'my-tenant',
31
+ * token: 'my-auth-token',
32
+ * baseUrl: 'https://api.kaiban.ai'
33
+ * });
34
+ *
35
+ * // List all agents
36
+ * const agents = await client.agents.list();
37
+ * ```
38
+ *
39
+ * @category Client
40
+ */
41
+ function createKaibanClient(config) {
42
+ const http = new HttpClient_1.HttpClient(config);
43
+ return {
44
+ setToken: (t) => http.setToken(t),
45
+ agents: new AgentsClient_1.AgentsClient(http),
46
+ cards: new CardsClient_1.CardsClient(http),
47
+ activities: new ActivitiesClient_1.ActivitiesClient(http),
48
+ teams: new TeamsClient_1.TeamsClient(http),
49
+ boards: new BoardsClient_1.BoardsClient(http),
50
+ resources: new ResourcesClient_1.ResourcesClient(http),
51
+ external_channels: new ExternalChannelsClient_1.ExternalChannelsClient(http),
52
+ costs: new ModelCost_1.ModelCost(),
53
+ };
54
+ }
@@ -0,0 +1,243 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HttpClient = void 0;
4
+ const errors_1 = require("./errors");
5
+ function buildQuery(query) {
6
+ if (!query)
7
+ return '';
8
+ const params = new URLSearchParams();
9
+ for (const [k, v] of Object.entries(query)) {
10
+ if (v !== undefined && v !== null)
11
+ params.append(k, String(v));
12
+ }
13
+ const qs = params.toString();
14
+ return qs ? `?${qs}` : '';
15
+ }
16
+ function sleep(ms) {
17
+ return new Promise((r) => setTimeout(r, ms));
18
+ }
19
+ class HttpClient {
20
+ constructor(config) {
21
+ const defaultRetry = {
22
+ maxAttempts: 3,
23
+ backoffMs: 250,
24
+ maxBackoffMs: 4000,
25
+ jitter: true,
26
+ retryOn: [429, 500, 502, 503, 504],
27
+ retryMethods: ['GET', 'PUT', 'DELETE'],
28
+ };
29
+ const baseUrl = config.baseUrl || `https://${config.tenant}.kaiban.io/api`;
30
+ this.config = {
31
+ tenant: config.tenant,
32
+ token: config.token,
33
+ baseUrl,
34
+ timeoutMs: config.timeoutMs ?? 30000,
35
+ retry: {
36
+ ...defaultRetry,
37
+ ...(config.retry || {}),
38
+ retryOn: [
39
+ ...(Array.isArray((config.retry || {}).retryOn)
40
+ ? (config.retry || {}).retryOn
41
+ : defaultRetry.retryOn),
42
+ ],
43
+ retryMethods: [
44
+ ...(Array.isArray((config.retry || {}).retryMethods)
45
+ ? (config.retry || {}).retryMethods
46
+ : defaultRetry.retryMethods),
47
+ ],
48
+ },
49
+ fetch: config.fetch || fetch,
50
+ onRequest: config.onRequest || (() => { }),
51
+ onResponse: config.onResponse || (() => { }),
52
+ };
53
+ this._token = config.token;
54
+ // Normalize retry config with required fields for internal use
55
+ const r = this.config.retry;
56
+ this.retryCfg = {
57
+ maxAttempts: r.maxAttempts ?? defaultRetry.maxAttempts,
58
+ backoffMs: r.backoffMs ?? defaultRetry.backoffMs,
59
+ maxBackoffMs: r.maxBackoffMs ?? defaultRetry.maxBackoffMs,
60
+ jitter: r.jitter ?? defaultRetry.jitter,
61
+ retryOn: Array.isArray(r.retryOn) ? r.retryOn : defaultRetry.retryOn,
62
+ retryMethods: Array.isArray(r.retryMethods) ? r.retryMethods : defaultRetry.retryMethods,
63
+ };
64
+ }
65
+ setToken(token) {
66
+ this._token = token;
67
+ }
68
+ async attempt(fn, retryCfg) {
69
+ const { maxAttempts = 3, backoffMs = 250, maxBackoffMs = 4000, jitter = true } = retryCfg;
70
+ let attempt = 0;
71
+ let delay = backoffMs;
72
+ while (true) {
73
+ try {
74
+ return await fn();
75
+ }
76
+ catch (err) {
77
+ attempt++;
78
+ if (attempt >= maxAttempts)
79
+ throw err;
80
+ const wait = jitter ? Math.min(maxBackoffMs, delay * (1 + Math.random())) : delay;
81
+ await sleep(wait);
82
+ delay = Math.min(maxBackoffMs, delay * 2);
83
+ }
84
+ }
85
+ }
86
+ async request(method, path, body, options) {
87
+ const normalizedPath = path.startsWith('/v1') ? path : `/v1${path}`;
88
+ const url = `${this.config.baseUrl}${normalizedPath}${buildQuery(options?.query)}`;
89
+ const headers = {
90
+ 'content-type': 'application/json',
91
+ 'x-tenant': this.config.tenant,
92
+ ...(this._token ? { authorization: `Bearer ${this._token}` } : {}),
93
+ ...(options?.headers || {}),
94
+ };
95
+ // Compose abort signals: user-provided and timeout
96
+ const combinedController = new AbortController();
97
+ const userSignal = options?.signal;
98
+ let timedOut = false;
99
+ const timeout = setTimeout(() => {
100
+ timedOut = true;
101
+ combinedController.abort();
102
+ }, options?.timeoutMs ?? this.config.timeoutMs);
103
+ const onUserAbort = () => combinedController.abort();
104
+ if (userSignal) {
105
+ if (userSignal.aborted) {
106
+ // propagate immediately
107
+ combinedController.abort();
108
+ }
109
+ else {
110
+ userSignal.addEventListener('abort', onUserAbort);
111
+ }
112
+ }
113
+ const init = {
114
+ method,
115
+ headers,
116
+ body: body !== undefined ? JSON.stringify(body) : undefined,
117
+ signal: combinedController.signal,
118
+ };
119
+ this.config.onRequest({ method, url, init });
120
+ const doFetch = async () => {
121
+ try {
122
+ const res = await this.config.fetch(url, init);
123
+ this.config.onResponse({ status: res.status, url, response: res });
124
+ let parsed;
125
+ const contentType = res.headers.get('content-type') || '';
126
+ if (contentType.includes('application/json')) {
127
+ try {
128
+ parsed = await res.json();
129
+ }
130
+ catch {
131
+ parsed = undefined;
132
+ }
133
+ }
134
+ else {
135
+ parsed = await res.text();
136
+ }
137
+ if (!res.ok) {
138
+ throw (0, errors_1.mapHttpToSdkError)(res.status, parsed);
139
+ }
140
+ return parsed;
141
+ }
142
+ catch (e) {
143
+ if (isAbortError(e)) {
144
+ if (timedOut)
145
+ throw new errors_1.TimeoutError();
146
+ throw new errors_1.AbortedError();
147
+ }
148
+ throw e;
149
+ }
150
+ finally {
151
+ clearTimeout(timeout);
152
+ if (userSignal)
153
+ userSignal.removeEventListener('abort', onUserAbort);
154
+ }
155
+ };
156
+ const retryCfg = mergeRetryConfig(this.retryCfg, options?.retry);
157
+ const shouldRetry = (err) => {
158
+ const methodUpper = method.toUpperCase();
159
+ const retryableByMethod = retryCfg.retryMethods?.includes(methodUpper);
160
+ if (!retryableByMethod)
161
+ return false;
162
+ if (err instanceof errors_1.TimeoutError)
163
+ return true;
164
+ // Retry on known transient SDK errors
165
+ if (err instanceof errors_1.RateLimitError)
166
+ return retryCfg.retryOn?.includes(429) ?? false;
167
+ if (err instanceof errors_1.UnavailableError)
168
+ return retryCfg.retryOn?.some((c) => c === 502 || c === 503 || c === 504) ?? false;
169
+ if (err instanceof errors_1.ServerError)
170
+ return retryCfg.retryOn?.includes(500) ?? false;
171
+ return false;
172
+ };
173
+ return await this.attempt(async () => {
174
+ try {
175
+ return await doFetch();
176
+ }
177
+ catch (err) {
178
+ if (shouldRetry(err))
179
+ throw err;
180
+ throw err;
181
+ }
182
+ }, retryCfg);
183
+ }
184
+ get(path, options) {
185
+ return this.request('GET', path, undefined, options);
186
+ }
187
+ post(path, body, options) {
188
+ return this.request('POST', path, body, options);
189
+ }
190
+ put(path, body, options) {
191
+ return this.request('PUT', path, body, options);
192
+ }
193
+ delete(path, options) {
194
+ return this.request('DELETE', path, undefined, options);
195
+ }
196
+ list(path, listParams, options) {
197
+ const queryParams = {};
198
+ if (listParams) {
199
+ // Handle pagination
200
+ if (listParams.limit)
201
+ queryParams.limit = listParams.limit;
202
+ if (listParams.before)
203
+ queryParams.before = listParams.before;
204
+ if (listParams.after)
205
+ queryParams.after = listParams.after;
206
+ // Handle sorting (already a string)
207
+ if (listParams.order_by)
208
+ queryParams.order_by = listParams.order_by;
209
+ // Handle filters
210
+ if (listParams.filters) {
211
+ Object.entries(listParams.filters).forEach(([key, value]) => {
212
+ queryParams[key] = value;
213
+ });
214
+ }
215
+ // Handle metadata filters for cards
216
+ if (listParams.metadata) {
217
+ Object.entries(listParams.metadata).forEach(([key, value]) => {
218
+ queryParams[`metadata.${key}`] = value;
219
+ });
220
+ }
221
+ }
222
+ return this.request('GET', path, undefined, { ...options, query: queryParams });
223
+ }
224
+ }
225
+ exports.HttpClient = HttpClient;
226
+ function isAbortError(e) {
227
+ if (!e)
228
+ return false;
229
+ const anyErr = e;
230
+ return anyErr && anyErr.name === 'AbortError';
231
+ }
232
+ function mergeRetryConfig(base, override) {
233
+ if (!override)
234
+ return base;
235
+ return {
236
+ maxAttempts: override.maxAttempts ?? base.maxAttempts,
237
+ backoffMs: override.backoffMs ?? base.backoffMs,
238
+ maxBackoffMs: override.maxBackoffMs ?? base.maxBackoffMs,
239
+ jitter: override.jitter ?? base.jitter,
240
+ retryOn: override.retryOn ? [...override.retryOn] : base.retryOn,
241
+ retryMethods: override.retryMethods ? [...override.retryMethods] : base.retryMethods,
242
+ };
243
+ }