@kaiban/sdk 0.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/README.md +225 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +15 -0
- package/dist/lib/client.d.ts +65 -0
- package/dist/lib/client.js +51 -0
- package/dist/lib/http/HttpClient.d.ts +16 -0
- package/dist/lib/http/HttpClient.js +237 -0
- package/dist/lib/http/errors.d.ts +50 -0
- package/dist/lib/http/errors.js +126 -0
- package/dist/lib/http/types.d.ts +40 -0
- package/dist/lib/http/types.js +1 -0
- package/dist/lib/resources/ActivitiesClient.d.ts +89 -0
- package/dist/lib/resources/ActivitiesClient.js +99 -0
- package/dist/lib/resources/AgentsClient.d.ts +121 -0
- package/dist/lib/resources/AgentsClient.js +134 -0
- package/dist/lib/resources/BenchmarkExecutionsClient.d.ts +250 -0
- package/dist/lib/resources/BenchmarkExecutionsClient.js +261 -0
- package/dist/lib/resources/BenchmarksClient.d.ts +159 -0
- package/dist/lib/resources/BenchmarksClient.js +158 -0
- package/dist/lib/resources/BoardsClient.d.ts +65 -0
- package/dist/lib/resources/BoardsClient.js +74 -0
- package/dist/lib/resources/CardsClient.d.ts +161 -0
- package/dist/lib/resources/CardsClient.js +202 -0
- package/dist/lib/resources/ExternalChannelsClient.d.ts +65 -0
- package/dist/lib/resources/ExternalChannelsClient.js +74 -0
- package/dist/lib/resources/ResourcesClient.d.ts +92 -0
- package/dist/lib/resources/ResourcesClient.js +98 -0
- package/dist/lib/resources/SupervisorFeedbackClient.d.ts +75 -0
- package/dist/lib/resources/SupervisorFeedbackClient.js +67 -0
- package/dist/lib/resources/TeamMembersClient.d.ts +66 -0
- package/dist/lib/resources/TeamMembersClient.js +75 -0
- package/dist/lib/resources/TeamsClient.d.ts +83 -0
- package/dist/lib/resources/TeamsClient.js +92 -0
- package/dist/lib/resources/__tests__/ActivitiesClient.test.d.ts +1 -0
- package/dist/lib/resources/__tests__/ActivitiesClient.test.js +33 -0
- package/dist/lib/resources/__tests__/AgentsClient.test.d.ts +1 -0
- package/dist/lib/resources/__tests__/AgentsClient.test.js +37 -0
- package/dist/lib/resources/__tests__/BenchmarkExecutionsClient.test.d.ts +1 -0
- package/dist/lib/resources/__tests__/BenchmarkExecutionsClient.test.js +59 -0
- package/dist/lib/resources/__tests__/BenchmarksClient.test.d.ts +1 -0
- package/dist/lib/resources/__tests__/BenchmarksClient.test.js +42 -0
- package/dist/lib/resources/__tests__/BoardsClient.test.d.ts +1 -0
- package/dist/lib/resources/__tests__/BoardsClient.test.js +26 -0
- package/dist/lib/resources/__tests__/CardsClient.test.d.ts +1 -0
- package/dist/lib/resources/__tests__/CardsClient.test.js +62 -0
- package/dist/lib/resources/__tests__/ExternalChannelsClient.test.d.ts +1 -0
- package/dist/lib/resources/__tests__/ExternalChannelsClient.test.js +26 -0
- package/dist/lib/resources/__tests__/ResourcesClient.test.d.ts +1 -0
- package/dist/lib/resources/__tests__/ResourcesClient.test.js +28 -0
- package/dist/lib/resources/__tests__/SupervisorFeedbackClient.test.d.ts +1 -0
- package/dist/lib/resources/__tests__/SupervisorFeedbackClient.test.js +24 -0
- package/dist/lib/resources/__tests__/TeamMembersClient.test.d.ts +1 -0
- package/dist/lib/resources/__tests__/TeamMembersClient.test.js +26 -0
- package/dist/lib/resources/__tests__/TeamsClient.test.d.ts +1 -0
- package/dist/lib/resources/__tests__/TeamsClient.test.js +26 -0
- package/dist/test/helpers/mockFetch.d.ts +8 -0
- package/dist/test/helpers/mockFetch.js +22 -0
- package/dist/types/a2a-data-parts.d.ts +64 -0
- package/dist/types/a2a-data-parts.js +13 -0
- package/dist/types/entities/activities.d.ts +32 -0
- package/dist/types/entities/activities.js +1 -0
- package/dist/types/entities/agent.d.ts +16 -0
- package/dist/types/entities/agent.js +1 -0
- package/dist/types/entities/benchmark.d.ts +82 -0
- package/dist/types/entities/benchmark.js +1 -0
- package/dist/types/entities/board.d.ts +20 -0
- package/dist/types/entities/board.js +1 -0
- package/dist/types/entities/card.d.ts +50 -0
- package/dist/types/entities/card.js +1 -0
- package/dist/types/entities/external-channel.d.ts +12 -0
- package/dist/types/entities/external-channel.js +1 -0
- package/dist/types/entities/index.d.ts +9 -0
- package/dist/types/entities/index.js +9 -0
- package/dist/types/entities/resource.d.ts +16 -0
- package/dist/types/entities/resource.js +1 -0
- package/dist/types/entities/shared.d.ts +1 -0
- package/dist/types/entities/shared.js +2 -0
- package/dist/types/entities/team.d.ts +14 -0
- package/dist/types/entities/team.js +1 -0
- package/dist/types/entities.d.ts +1 -0
- package/dist/types/entities.js +1 -0
- package/dist/types/requests.d.ts +22 -0
- package/dist/types/requests.js +1 -0
- package/dist/types/responses.d.ts +77 -0
- package/dist/types/responses.js +1 -0
- package/package.json +54 -0
package/README.md
ADDED
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
# Kaiban TypeScript SDK
|
|
2
|
+
|
|
3
|
+
Official TypeScript SDK for the Kaiban API.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @kaiban-sdk
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quickstart
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { createKaibanClient } from '@kaiban-sdk';
|
|
15
|
+
|
|
16
|
+
const client = createKaibanClient({
|
|
17
|
+
tenant: 'agi',
|
|
18
|
+
token: process.env.KAIBAN_TOKEN,
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
const agents = await client.agents.list();
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Auth and tenancy
|
|
25
|
+
|
|
26
|
+
- Uses `Authorization: Bearer <token>` automatically when provided
|
|
27
|
+
- Sends `x-tenant` with the configured tenant
|
|
28
|
+
- Default base URL: `https://{tenant}.kaiban.io`
|
|
29
|
+
|
|
30
|
+
## Resources
|
|
31
|
+
|
|
32
|
+
- activities: list, create, updateBulk
|
|
33
|
+
- agents: list, get, update, feedback
|
|
34
|
+
- boards: list, get
|
|
35
|
+
- cards: list, get, create, update, delete, moveToColumn
|
|
36
|
+
- external-channels: list, get
|
|
37
|
+
- resources: list, get, publish
|
|
38
|
+
- supervisor-feedback: list by agent
|
|
39
|
+
- teams: list, get
|
|
40
|
+
|
|
41
|
+
## Types
|
|
42
|
+
|
|
43
|
+
Types are published under the package and re-exported from `@kaiban-sdk` (e.g., `Agent`, `Card`, `BenchmarkExecution`).
|
|
44
|
+
|
|
45
|
+
## Documentation
|
|
46
|
+
|
|
47
|
+
The SDK includes comprehensive JSDoc documentation for all client methods and types.
|
|
48
|
+
|
|
49
|
+
### Generate Documentation
|
|
50
|
+
|
|
51
|
+
To generate the full API documentation:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
npm run docs:generate
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
This will create a `docs` folder with HTML documentation that you can open in your browser.
|
|
58
|
+
|
|
59
|
+
### Build Documentation
|
|
60
|
+
|
|
61
|
+
To clean and rebuild the documentation from scratch:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
npm run docs:build
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Serve Documentation Locally
|
|
68
|
+
|
|
69
|
+
To generate and serve the documentation locally:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
npm run docs:serve
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
This will generate the docs and start a local server (requires `npx serve` to be available).
|
|
76
|
+
|
|
77
|
+
### Clean Documentation
|
|
78
|
+
|
|
79
|
+
To remove the generated documentation:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
npm run docs:clean
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Configuration
|
|
86
|
+
|
|
87
|
+
```ts
|
|
88
|
+
createKaibanClient({
|
|
89
|
+
tenant: 'agi',
|
|
90
|
+
token: '...optional...',
|
|
91
|
+
baseUrl: 'https://custom-host',
|
|
92
|
+
timeoutMs: 30000,
|
|
93
|
+
});
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Per-call options
|
|
97
|
+
|
|
98
|
+
All resource methods accept optional per-call overrides:
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
// Headers, query params, timeout override, and AbortSignal
|
|
102
|
+
await client.cards.list({
|
|
103
|
+
headers: { 'x-request-id': 'abc-123' },
|
|
104
|
+
query: { status: 'doing' },
|
|
105
|
+
timeoutMs: 10_000,
|
|
106
|
+
signal: new AbortController().signal,
|
|
107
|
+
});
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Error handling
|
|
111
|
+
|
|
112
|
+
```ts
|
|
113
|
+
import { createKaibanClient, HttpError } from '@kaiban-sdk';
|
|
114
|
+
|
|
115
|
+
try {
|
|
116
|
+
await client.cards.get({ id: 'non-existent' });
|
|
117
|
+
} catch (err) {
|
|
118
|
+
if (err instanceof HttpError) {
|
|
119
|
+
console.error('HTTP error', err.status, err.url, err.body);
|
|
120
|
+
} else {
|
|
121
|
+
console.error('Unexpected error', err);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Examples by resource
|
|
127
|
+
|
|
128
|
+
### Agents
|
|
129
|
+
|
|
130
|
+
```ts
|
|
131
|
+
// List agents
|
|
132
|
+
const agents = await client.agents.list();
|
|
133
|
+
|
|
134
|
+
// Get agent by id
|
|
135
|
+
const agent = await client.agents.get({ id: 'agent_123' });
|
|
136
|
+
|
|
137
|
+
// Update agent
|
|
138
|
+
const updated = await client.agents.update({ id: 'agent_123', body: { description: 'New desc' } });
|
|
139
|
+
|
|
140
|
+
// Create agent feedback
|
|
141
|
+
await client.agents.createFeedback({ id: 'agent_123', feedback: { rating: 5, comment: 'Great' } });
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Teams
|
|
145
|
+
|
|
146
|
+
```ts
|
|
147
|
+
const teams = await client.teams.list();
|
|
148
|
+
const team = await client.teams.get({ id: 'team_123' });
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Boards
|
|
152
|
+
|
|
153
|
+
```ts
|
|
154
|
+
const boards = await client.boards.list();
|
|
155
|
+
const board = await client.boards.get({ id: 'board_123' });
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Cards
|
|
159
|
+
|
|
160
|
+
```ts
|
|
161
|
+
import type { CreateCardInput, UpdateCardInput } from '@kaiban-sdk';
|
|
162
|
+
|
|
163
|
+
// Create a card
|
|
164
|
+
const createBody: CreateCardInput = {
|
|
165
|
+
team_id: 'team_123',
|
|
166
|
+
board_id: 'board_123',
|
|
167
|
+
owner_id: 'user_123',
|
|
168
|
+
agent_id: 'agent_123',
|
|
169
|
+
title: 'Investigate issue',
|
|
170
|
+
status: 'backlog',
|
|
171
|
+
column_key: 'inbox',
|
|
172
|
+
priority: 'medium',
|
|
173
|
+
member_ids: [],
|
|
174
|
+
};
|
|
175
|
+
const newCard = await client.cards.create({ body: createBody });
|
|
176
|
+
|
|
177
|
+
// Get a card
|
|
178
|
+
const card = await client.cards.get({ id: newCard.id });
|
|
179
|
+
|
|
180
|
+
// Update a card
|
|
181
|
+
const updateBody: UpdateCardInput = { status: 'doing', priority: 'high' };
|
|
182
|
+
const updatedCard = await client.cards.update({ id: card.id, body: updateBody });
|
|
183
|
+
|
|
184
|
+
// List cards
|
|
185
|
+
const cards = await client.cards.list();
|
|
186
|
+
|
|
187
|
+
// Delete a card
|
|
188
|
+
await client.cards.delete({ id: card.id });
|
|
189
|
+
|
|
190
|
+
// Activities
|
|
191
|
+
const activities = await client.activities.list({ card_id: card.id });
|
|
192
|
+
await client.activities.create({
|
|
193
|
+
card_id: card.id,
|
|
194
|
+
body: {
|
|
195
|
+
board_id: card.board_id,
|
|
196
|
+
team_id: card.team_id,
|
|
197
|
+
card_id: card.id,
|
|
198
|
+
type: 'card_comment_added',
|
|
199
|
+
description: 'FYI',
|
|
200
|
+
actor: { id: 'user_1', type: 'user', name: 'Alice' },
|
|
201
|
+
},
|
|
202
|
+
});
|
|
203
|
+
await client.activities.updateBulk({ card_id: card.id, body: activities });
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### Resources
|
|
207
|
+
|
|
208
|
+
```ts
|
|
209
|
+
const resources = await client.resources.list();
|
|
210
|
+
const resource = await client.resources.get({ id: 'resource_123' });
|
|
211
|
+
await client.resources.publish({ resource_id: 'resource_123', userId: 'user_123' });
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### External Channels
|
|
215
|
+
|
|
216
|
+
```ts
|
|
217
|
+
const channels = await client.externalChannels.list();
|
|
218
|
+
const channel = await client.externalChannels.get({ id: 'ext_123' });
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### Supervisor Feedback
|
|
222
|
+
|
|
223
|
+
```ts
|
|
224
|
+
const feedback = await client.supervisorFeedback.listByAgent({ agent_id: 'agent_123' });
|
|
225
|
+
```
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export * from './lib/client';
|
|
2
|
+
export * from './lib/http/errors';
|
|
3
|
+
export * from './lib/resources/AgentsClient';
|
|
4
|
+
export * from './lib/resources/CardsClient';
|
|
5
|
+
export * from './lib/resources/ActivitiesClient';
|
|
6
|
+
export * from './lib/resources/TeamsClient';
|
|
7
|
+
export * from './lib/resources/BoardsClient';
|
|
8
|
+
export * from './lib/resources/ResourcesClient';
|
|
9
|
+
export * from './lib/resources/ExternalChannelsClient';
|
|
10
|
+
export * from './lib/resources/SupervisorFeedbackClient';
|
|
11
|
+
export * from './lib/http/types';
|
|
12
|
+
export * from './types/entities';
|
|
13
|
+
export * from './types/requests';
|
|
14
|
+
export * from './types/responses';
|
|
15
|
+
export * from './types/a2a-data-parts';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export * from './lib/client';
|
|
2
|
+
export * from './lib/http/errors';
|
|
3
|
+
export * from './lib/resources/AgentsClient';
|
|
4
|
+
export * from './lib/resources/CardsClient';
|
|
5
|
+
export * from './lib/resources/ActivitiesClient';
|
|
6
|
+
export * from './lib/resources/TeamsClient';
|
|
7
|
+
export * from './lib/resources/BoardsClient';
|
|
8
|
+
export * from './lib/resources/ResourcesClient';
|
|
9
|
+
export * from './lib/resources/ExternalChannelsClient';
|
|
10
|
+
export * from './lib/resources/SupervisorFeedbackClient';
|
|
11
|
+
export * from './lib/http/types';
|
|
12
|
+
export * from './types/entities';
|
|
13
|
+
export * from './types/requests';
|
|
14
|
+
export * from './types/responses';
|
|
15
|
+
export * from './types/a2a-data-parts';
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { KaibanClientConfig } from './http/types';
|
|
2
|
+
import { AgentsClient } from './resources/AgentsClient';
|
|
3
|
+
import { CardsClient } from './resources/CardsClient';
|
|
4
|
+
import { TeamsClient } from './resources/TeamsClient';
|
|
5
|
+
import { BoardsClient } from './resources/BoardsClient';
|
|
6
|
+
import { ResourcesClient } from './resources/ResourcesClient';
|
|
7
|
+
import { ExternalChannelsClient } from './resources/ExternalChannelsClient';
|
|
8
|
+
import { SupervisorFeedbackClient } from './resources/SupervisorFeedbackClient';
|
|
9
|
+
import { ActivitiesClient } from './resources/ActivitiesClient';
|
|
10
|
+
/**
|
|
11
|
+
* Main Kaiban SDK client interface providing access to all API resources
|
|
12
|
+
* @category Client
|
|
13
|
+
*/
|
|
14
|
+
export interface KaibanClient {
|
|
15
|
+
/**
|
|
16
|
+
* Set or clear the bearer token used for authorization
|
|
17
|
+
* @param token - Optional bearer token. If omitted, clears the current token
|
|
18
|
+
*/
|
|
19
|
+
setToken: (token?: string) => void;
|
|
20
|
+
/** Client for managing agents */
|
|
21
|
+
agents: AgentsClient;
|
|
22
|
+
/** Client for managing cards */
|
|
23
|
+
cards: CardsClient;
|
|
24
|
+
/** Client for managing card activities */
|
|
25
|
+
activities: ActivitiesClient;
|
|
26
|
+
/** Client for managing teams */
|
|
27
|
+
teams: TeamsClient;
|
|
28
|
+
/** Client for managing boards */
|
|
29
|
+
boards: BoardsClient;
|
|
30
|
+
/** Client for managing resources */
|
|
31
|
+
resources: ResourcesClient;
|
|
32
|
+
/** Client for managing external channels */
|
|
33
|
+
externalChannels: ExternalChannelsClient;
|
|
34
|
+
/** Client for managing supervisor feedback */
|
|
35
|
+
supervisorFeedback: SupervisorFeedbackClient;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Create a Kaiban SDK client instance
|
|
39
|
+
*
|
|
40
|
+
* @param config - Client configuration object
|
|
41
|
+
* @param config.tenant - The tenant identifier for your Kaiban instance
|
|
42
|
+
* @param config.token - Optional bearer token for authentication
|
|
43
|
+
* @param config.baseUrl - Optional custom base URL for the API
|
|
44
|
+
* @param config.maxRetries - Optional maximum number of request retries (default: 3)
|
|
45
|
+
* @param config.timeout - Optional request timeout in milliseconds (default: 30000)
|
|
46
|
+
*
|
|
47
|
+
* @returns A configured Kaiban client instance
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* ```typescript
|
|
51
|
+
* import { createKaibanClient } from 'kaiban-sdk';
|
|
52
|
+
*
|
|
53
|
+
* const client = createKaibanClient({
|
|
54
|
+
* tenant: 'my-tenant',
|
|
55
|
+
* token: 'my-auth-token',
|
|
56
|
+
* baseUrl: 'https://api.kaiban.ai'
|
|
57
|
+
* });
|
|
58
|
+
*
|
|
59
|
+
* // List all agents
|
|
60
|
+
* const agents = await client.agents.list();
|
|
61
|
+
* ```
|
|
62
|
+
*
|
|
63
|
+
* @category Client
|
|
64
|
+
*/
|
|
65
|
+
export declare function createKaibanClient(config: KaibanClientConfig): KaibanClient;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { HttpClient } from './http/HttpClient';
|
|
2
|
+
import { AgentsClient } from './resources/AgentsClient';
|
|
3
|
+
import { CardsClient } from './resources/CardsClient';
|
|
4
|
+
import { TeamsClient } from './resources/TeamsClient';
|
|
5
|
+
import { BoardsClient } from './resources/BoardsClient';
|
|
6
|
+
import { ResourcesClient } from './resources/ResourcesClient';
|
|
7
|
+
import { ExternalChannelsClient } from './resources/ExternalChannelsClient';
|
|
8
|
+
import { SupervisorFeedbackClient } from './resources/SupervisorFeedbackClient';
|
|
9
|
+
import { ActivitiesClient } from './resources/ActivitiesClient';
|
|
10
|
+
/**
|
|
11
|
+
* Create a Kaiban SDK client instance
|
|
12
|
+
*
|
|
13
|
+
* @param config - Client configuration object
|
|
14
|
+
* @param config.tenant - The tenant identifier for your Kaiban instance
|
|
15
|
+
* @param config.token - Optional bearer token for authentication
|
|
16
|
+
* @param config.baseUrl - Optional custom base URL for the API
|
|
17
|
+
* @param config.maxRetries - Optional maximum number of request retries (default: 3)
|
|
18
|
+
* @param config.timeout - Optional request timeout in milliseconds (default: 30000)
|
|
19
|
+
*
|
|
20
|
+
* @returns A configured Kaiban client instance
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```typescript
|
|
24
|
+
* import { createKaibanClient } from 'kaiban-sdk';
|
|
25
|
+
*
|
|
26
|
+
* const client = createKaibanClient({
|
|
27
|
+
* tenant: 'my-tenant',
|
|
28
|
+
* token: 'my-auth-token',
|
|
29
|
+
* baseUrl: 'https://api.kaiban.ai'
|
|
30
|
+
* });
|
|
31
|
+
*
|
|
32
|
+
* // List all agents
|
|
33
|
+
* const agents = await client.agents.list();
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* @category Client
|
|
37
|
+
*/
|
|
38
|
+
export function createKaibanClient(config) {
|
|
39
|
+
const http = new HttpClient(config);
|
|
40
|
+
return {
|
|
41
|
+
setToken: (t) => http.setToken(t),
|
|
42
|
+
agents: new AgentsClient(http),
|
|
43
|
+
cards: new CardsClient(http),
|
|
44
|
+
activities: new ActivitiesClient(http),
|
|
45
|
+
teams: new TeamsClient(http),
|
|
46
|
+
boards: new BoardsClient(http),
|
|
47
|
+
resources: new ResourcesClient(http),
|
|
48
|
+
externalChannels: new ExternalChannelsClient(http),
|
|
49
|
+
supervisorFeedback: new SupervisorFeedbackClient(http),
|
|
50
|
+
};
|
|
51
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { ListParams } from '../../types/responses';
|
|
2
|
+
import { KaibanClientConfig, RequestOptions } from './types';
|
|
3
|
+
export declare class HttpClient {
|
|
4
|
+
private config;
|
|
5
|
+
private _token?;
|
|
6
|
+
private retryCfg;
|
|
7
|
+
constructor(config: KaibanClientConfig);
|
|
8
|
+
setToken(token?: string): void;
|
|
9
|
+
private attempt;
|
|
10
|
+
private request;
|
|
11
|
+
get<T>(path: string, options?: RequestOptions): Promise<T>;
|
|
12
|
+
post<T>(path: string, body?: unknown, options?: RequestOptions): Promise<T>;
|
|
13
|
+
put<T>(path: string, body?: unknown, options?: RequestOptions): Promise<T>;
|
|
14
|
+
delete<T>(path: string, options?: RequestOptions): Promise<T>;
|
|
15
|
+
list<T>(path: string, listParams?: ListParams, options?: RequestOptions): Promise<T>;
|
|
16
|
+
}
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import { TimeoutError, AbortedError, RateLimitError, UnavailableError, ServerError, mapHttpToSdkError, } from './errors';
|
|
2
|
+
function buildQuery(query) {
|
|
3
|
+
if (!query)
|
|
4
|
+
return '';
|
|
5
|
+
const params = new URLSearchParams();
|
|
6
|
+
for (const [k, v] of Object.entries(query)) {
|
|
7
|
+
if (v !== undefined && v !== null)
|
|
8
|
+
params.append(k, String(v));
|
|
9
|
+
}
|
|
10
|
+
const qs = params.toString();
|
|
11
|
+
return qs ? `?${qs}` : '';
|
|
12
|
+
}
|
|
13
|
+
function sleep(ms) {
|
|
14
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
15
|
+
}
|
|
16
|
+
export class HttpClient {
|
|
17
|
+
constructor(config) {
|
|
18
|
+
const defaultRetry = {
|
|
19
|
+
maxAttempts: 3,
|
|
20
|
+
backoffMs: 250,
|
|
21
|
+
maxBackoffMs: 4000,
|
|
22
|
+
jitter: true,
|
|
23
|
+
retryOn: [429, 500, 502, 503, 504],
|
|
24
|
+
retryMethods: ['GET', 'PUT', 'DELETE'],
|
|
25
|
+
};
|
|
26
|
+
const baseUrl = config.baseUrl || `https://${config.tenant}.kaiban.io`;
|
|
27
|
+
this.config = {
|
|
28
|
+
tenant: config.tenant,
|
|
29
|
+
token: config.token,
|
|
30
|
+
baseUrl,
|
|
31
|
+
timeoutMs: config.timeoutMs ?? 30000,
|
|
32
|
+
retry: {
|
|
33
|
+
...defaultRetry,
|
|
34
|
+
...(config.retry || {}),
|
|
35
|
+
retryOn: [
|
|
36
|
+
...(Array.isArray((config.retry || {}).retryOn)
|
|
37
|
+
? (config.retry || {}).retryOn
|
|
38
|
+
: defaultRetry.retryOn),
|
|
39
|
+
],
|
|
40
|
+
retryMethods: [
|
|
41
|
+
...(Array.isArray((config.retry || {}).retryMethods)
|
|
42
|
+
? (config.retry || {}).retryMethods
|
|
43
|
+
: defaultRetry.retryMethods),
|
|
44
|
+
],
|
|
45
|
+
},
|
|
46
|
+
fetch: config.fetch || fetch,
|
|
47
|
+
onRequest: config.onRequest || (() => { }),
|
|
48
|
+
onResponse: config.onResponse || (() => { }),
|
|
49
|
+
};
|
|
50
|
+
this._token = config.token;
|
|
51
|
+
// Normalize retry config with required fields for internal use
|
|
52
|
+
const r = this.config.retry;
|
|
53
|
+
this.retryCfg = {
|
|
54
|
+
maxAttempts: r.maxAttempts ?? defaultRetry.maxAttempts,
|
|
55
|
+
backoffMs: r.backoffMs ?? defaultRetry.backoffMs,
|
|
56
|
+
maxBackoffMs: r.maxBackoffMs ?? defaultRetry.maxBackoffMs,
|
|
57
|
+
jitter: r.jitter ?? defaultRetry.jitter,
|
|
58
|
+
retryOn: Array.isArray(r.retryOn) ? r.retryOn : defaultRetry.retryOn,
|
|
59
|
+
retryMethods: Array.isArray(r.retryMethods) ? r.retryMethods : defaultRetry.retryMethods,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
setToken(token) {
|
|
63
|
+
this._token = token;
|
|
64
|
+
}
|
|
65
|
+
async attempt(fn, retryCfg) {
|
|
66
|
+
const { maxAttempts = 3, backoffMs = 250, maxBackoffMs = 4000, jitter = true } = retryCfg;
|
|
67
|
+
let attempt = 0;
|
|
68
|
+
let delay = backoffMs;
|
|
69
|
+
while (true) {
|
|
70
|
+
try {
|
|
71
|
+
return await fn();
|
|
72
|
+
}
|
|
73
|
+
catch (err) {
|
|
74
|
+
attempt++;
|
|
75
|
+
if (attempt >= maxAttempts)
|
|
76
|
+
throw err;
|
|
77
|
+
const wait = jitter ? Math.min(maxBackoffMs, delay * (1 + Math.random())) : delay;
|
|
78
|
+
await sleep(wait);
|
|
79
|
+
delay = Math.min(maxBackoffMs, delay * 2);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
async request(method, path, body, options) {
|
|
84
|
+
const normalizedPath = path.startsWith('/v1') ? path : `/v1${path}`;
|
|
85
|
+
const url = `${this.config.baseUrl}${normalizedPath}${buildQuery(options?.query)}`;
|
|
86
|
+
const headers = {
|
|
87
|
+
'content-type': 'application/json',
|
|
88
|
+
'x-tenant': this.config.tenant,
|
|
89
|
+
...(this._token ? { authorization: `Bearer ${this._token}` } : {}),
|
|
90
|
+
...(options?.headers || {}),
|
|
91
|
+
};
|
|
92
|
+
// Compose abort signals: user-provided and timeout
|
|
93
|
+
const combinedController = new AbortController();
|
|
94
|
+
const userSignal = options?.signal;
|
|
95
|
+
let timedOut = false;
|
|
96
|
+
const timeout = setTimeout(() => {
|
|
97
|
+
timedOut = true;
|
|
98
|
+
combinedController.abort();
|
|
99
|
+
}, options?.timeoutMs ?? this.config.timeoutMs);
|
|
100
|
+
const onUserAbort = () => combinedController.abort();
|
|
101
|
+
if (userSignal) {
|
|
102
|
+
if (userSignal.aborted) {
|
|
103
|
+
// propagate immediately
|
|
104
|
+
combinedController.abort();
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
userSignal.addEventListener('abort', onUserAbort);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
const init = {
|
|
111
|
+
method,
|
|
112
|
+
headers,
|
|
113
|
+
body: body !== undefined ? JSON.stringify(body) : undefined,
|
|
114
|
+
signal: combinedController.signal,
|
|
115
|
+
};
|
|
116
|
+
this.config.onRequest({ method, url, init });
|
|
117
|
+
const doFetch = async () => {
|
|
118
|
+
try {
|
|
119
|
+
const res = await this.config.fetch(url, init);
|
|
120
|
+
this.config.onResponse({ status: res.status, url, response: res });
|
|
121
|
+
let parsed;
|
|
122
|
+
const contentType = res.headers.get('content-type') || '';
|
|
123
|
+
if (contentType.includes('application/json')) {
|
|
124
|
+
try {
|
|
125
|
+
parsed = await res.json();
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
parsed = undefined;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
parsed = await res.text();
|
|
133
|
+
}
|
|
134
|
+
if (!res.ok) {
|
|
135
|
+
throw mapHttpToSdkError(res.status, parsed);
|
|
136
|
+
}
|
|
137
|
+
return parsed;
|
|
138
|
+
}
|
|
139
|
+
catch (e) {
|
|
140
|
+
if (isAbortError(e)) {
|
|
141
|
+
if (timedOut)
|
|
142
|
+
throw new TimeoutError();
|
|
143
|
+
throw new AbortedError();
|
|
144
|
+
}
|
|
145
|
+
throw e;
|
|
146
|
+
}
|
|
147
|
+
finally {
|
|
148
|
+
clearTimeout(timeout);
|
|
149
|
+
if (userSignal)
|
|
150
|
+
userSignal.removeEventListener('abort', onUserAbort);
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
const retryCfg = mergeRetryConfig(this.retryCfg, options?.retry);
|
|
154
|
+
const shouldRetry = (err) => {
|
|
155
|
+
const methodUpper = method.toUpperCase();
|
|
156
|
+
const retryableByMethod = retryCfg.retryMethods?.includes(methodUpper);
|
|
157
|
+
if (!retryableByMethod)
|
|
158
|
+
return false;
|
|
159
|
+
if (err instanceof TimeoutError)
|
|
160
|
+
return true;
|
|
161
|
+
// Retry on known transient SDK errors
|
|
162
|
+
if (err instanceof RateLimitError)
|
|
163
|
+
return retryCfg.retryOn?.includes(429) ?? false;
|
|
164
|
+
if (err instanceof UnavailableError)
|
|
165
|
+
return retryCfg.retryOn?.some((c) => c === 502 || c === 503 || c === 504) ?? false;
|
|
166
|
+
if (err instanceof ServerError)
|
|
167
|
+
return retryCfg.retryOn?.includes(500) ?? false;
|
|
168
|
+
return false;
|
|
169
|
+
};
|
|
170
|
+
return await this.attempt(async () => {
|
|
171
|
+
try {
|
|
172
|
+
return await doFetch();
|
|
173
|
+
}
|
|
174
|
+
catch (err) {
|
|
175
|
+
if (shouldRetry(err))
|
|
176
|
+
throw err;
|
|
177
|
+
throw err;
|
|
178
|
+
}
|
|
179
|
+
}, retryCfg);
|
|
180
|
+
}
|
|
181
|
+
get(path, options) {
|
|
182
|
+
return this.request('GET', path, undefined, options);
|
|
183
|
+
}
|
|
184
|
+
post(path, body, options) {
|
|
185
|
+
return this.request('POST', path, body, options);
|
|
186
|
+
}
|
|
187
|
+
put(path, body, options) {
|
|
188
|
+
return this.request('PUT', path, body, options);
|
|
189
|
+
}
|
|
190
|
+
delete(path, options) {
|
|
191
|
+
return this.request('DELETE', path, undefined, options);
|
|
192
|
+
}
|
|
193
|
+
list(path, listParams, options) {
|
|
194
|
+
const queryParams = {};
|
|
195
|
+
if (listParams) {
|
|
196
|
+
// Handle pagination
|
|
197
|
+
if (listParams.cursor)
|
|
198
|
+
queryParams.cursor = listParams.cursor;
|
|
199
|
+
if (listParams.limit)
|
|
200
|
+
queryParams.limit = listParams.limit;
|
|
201
|
+
// Handle sorting (already a string)
|
|
202
|
+
if (listParams.order_by)
|
|
203
|
+
queryParams.order_by = listParams.order_by;
|
|
204
|
+
// Handle filters
|
|
205
|
+
if (listParams.filters) {
|
|
206
|
+
Object.entries(listParams.filters).forEach(([key, value]) => {
|
|
207
|
+
queryParams[key] = value;
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
// Handle metadata filters for cards
|
|
211
|
+
if (listParams.metadata) {
|
|
212
|
+
Object.entries(listParams.metadata).forEach(([key, value]) => {
|
|
213
|
+
queryParams[`metadata.${key}`] = value;
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return this.request('GET', path, undefined, { ...options, query: queryParams });
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
function isAbortError(e) {
|
|
221
|
+
if (!e)
|
|
222
|
+
return false;
|
|
223
|
+
const anyErr = e;
|
|
224
|
+
return anyErr && anyErr.name === 'AbortError';
|
|
225
|
+
}
|
|
226
|
+
function mergeRetryConfig(base, override) {
|
|
227
|
+
if (!override)
|
|
228
|
+
return base;
|
|
229
|
+
return {
|
|
230
|
+
maxAttempts: override.maxAttempts ?? base.maxAttempts,
|
|
231
|
+
backoffMs: override.backoffMs ?? base.backoffMs,
|
|
232
|
+
maxBackoffMs: override.maxBackoffMs ?? base.maxBackoffMs,
|
|
233
|
+
jitter: override.jitter ?? base.jitter,
|
|
234
|
+
retryOn: override.retryOn ? [...override.retryOn] : base.retryOn,
|
|
235
|
+
retryMethods: override.retryMethods ? [...override.retryMethods] : base.retryMethods,
|
|
236
|
+
};
|
|
237
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
export declare class HttpError extends Error {
|
|
2
|
+
readonly status: number;
|
|
3
|
+
readonly url: string;
|
|
4
|
+
readonly body?: unknown;
|
|
5
|
+
constructor(message: string, status: number, url: string, body?: unknown);
|
|
6
|
+
}
|
|
7
|
+
export declare class TimeoutError extends Error {
|
|
8
|
+
constructor(message?: string);
|
|
9
|
+
}
|
|
10
|
+
export declare class AbortedError extends Error {
|
|
11
|
+
constructor(message?: string);
|
|
12
|
+
}
|
|
13
|
+
export interface ApiErrorShape {
|
|
14
|
+
code?: string;
|
|
15
|
+
message: string;
|
|
16
|
+
details?: unknown;
|
|
17
|
+
}
|
|
18
|
+
export declare class ApiError extends Error {
|
|
19
|
+
readonly code?: string;
|
|
20
|
+
readonly details?: unknown;
|
|
21
|
+
constructor(shape: ApiErrorShape);
|
|
22
|
+
}
|
|
23
|
+
export declare class BadRequestError extends ApiError {
|
|
24
|
+
constructor(shape: ApiErrorShape);
|
|
25
|
+
}
|
|
26
|
+
export declare class ValidationError extends ApiError {
|
|
27
|
+
constructor(shape: ApiErrorShape);
|
|
28
|
+
}
|
|
29
|
+
export declare class UnauthorizedError extends ApiError {
|
|
30
|
+
constructor(shape: ApiErrorShape);
|
|
31
|
+
}
|
|
32
|
+
export declare class ForbiddenError extends ApiError {
|
|
33
|
+
constructor(shape: ApiErrorShape);
|
|
34
|
+
}
|
|
35
|
+
export declare class NotFoundError extends ApiError {
|
|
36
|
+
constructor(shape: ApiErrorShape);
|
|
37
|
+
}
|
|
38
|
+
export declare class ConflictError extends ApiError {
|
|
39
|
+
constructor(shape: ApiErrorShape);
|
|
40
|
+
}
|
|
41
|
+
export declare class RateLimitError extends ApiError {
|
|
42
|
+
constructor(shape: ApiErrorShape);
|
|
43
|
+
}
|
|
44
|
+
export declare class UnavailableError extends ApiError {
|
|
45
|
+
constructor(shape: ApiErrorShape);
|
|
46
|
+
}
|
|
47
|
+
export declare class ServerError extends ApiError {
|
|
48
|
+
constructor(shape: ApiErrorShape);
|
|
49
|
+
}
|
|
50
|
+
export declare function mapHttpToSdkError(status: number, body: unknown): ApiError;
|