@inferencesh/sdk 0.6.8 → 0.6.12

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 (76) hide show
  1. package/CHANGELOG.md +7 -1
  2. package/README.md +70 -1
  3. package/dist/agent/actions.js +3 -3
  4. package/dist/agent/actions.test.js +89 -0
  5. package/dist/agent/api.test.js +86 -8
  6. package/dist/api/agents.d.ts +10 -0
  7. package/dist/api/agents.js +17 -3
  8. package/dist/api/agents.test.js +303 -92
  9. package/dist/api/api-keys.d.ts +24 -0
  10. package/dist/api/api-keys.js +26 -0
  11. package/dist/api/api-keys.test.d.ts +1 -0
  12. package/dist/api/api-keys.test.js +44 -0
  13. package/dist/api/apps.js +1 -1
  14. package/dist/api/apps.test.d.ts +1 -0
  15. package/dist/api/apps.test.js +135 -0
  16. package/dist/api/chats.d.ts +14 -0
  17. package/dist/api/chats.js +18 -0
  18. package/dist/api/chats.test.d.ts +1 -0
  19. package/dist/api/chats.test.js +85 -0
  20. package/dist/api/engines.d.ts +12 -0
  21. package/dist/api/engines.js +18 -0
  22. package/dist/api/engines.test.d.ts +1 -0
  23. package/dist/api/engines.test.js +133 -0
  24. package/dist/api/files.test.js +186 -6
  25. package/dist/api/flow-runs.test.d.ts +1 -0
  26. package/dist/api/flow-runs.test.js +97 -0
  27. package/dist/api/flows.d.ts +4 -0
  28. package/dist/api/flows.js +6 -0
  29. package/dist/api/flows.test.d.ts +1 -0
  30. package/dist/api/flows.test.js +118 -0
  31. package/dist/api/integrations.d.ts +41 -0
  32. package/dist/api/integrations.js +56 -0
  33. package/dist/api/integrations.test.d.ts +1 -0
  34. package/dist/api/integrations.test.js +109 -0
  35. package/dist/api/knowledge.d.ts +112 -0
  36. package/dist/api/knowledge.js +163 -0
  37. package/dist/api/knowledge.test.d.ts +1 -0
  38. package/dist/api/knowledge.test.js +94 -0
  39. package/dist/api/mcp-servers.d.ts +45 -0
  40. package/dist/api/mcp-servers.js +62 -0
  41. package/dist/api/mcp-servers.test.d.ts +1 -0
  42. package/dist/api/mcp-servers.test.js +64 -0
  43. package/dist/api/projects.d.ts +29 -0
  44. package/dist/api/projects.js +38 -0
  45. package/dist/api/projects.test.d.ts +1 -0
  46. package/dist/api/projects.test.js +52 -0
  47. package/dist/api/search.d.ts +21 -0
  48. package/dist/api/search.js +20 -0
  49. package/dist/api/search.test.d.ts +1 -0
  50. package/dist/api/search.test.js +39 -0
  51. package/dist/api/secrets.d.ts +29 -0
  52. package/dist/api/secrets.js +38 -0
  53. package/dist/api/secrets.test.d.ts +1 -0
  54. package/dist/api/secrets.test.js +61 -0
  55. package/dist/api/sessions.test.js +4 -4
  56. package/dist/api/tasks.d.ts +13 -1
  57. package/dist/api/tasks.js +18 -0
  58. package/dist/api/tasks.test.js +174 -22
  59. package/dist/api/teams.d.ts +71 -0
  60. package/dist/api/teams.js +92 -0
  61. package/dist/api/teams.test.d.ts +1 -0
  62. package/dist/api/teams.test.js +79 -0
  63. package/dist/client.test.js +8 -8
  64. package/dist/http/client.d.ts +5 -0
  65. package/dist/http/client.js +46 -48
  66. package/dist/http/client.test.js +296 -13
  67. package/dist/http/errors.test.js +13 -0
  68. package/dist/index.d.ts +25 -0
  69. package/dist/index.js +25 -0
  70. package/dist/proxy/express.test.d.ts +1 -0
  71. package/dist/proxy/express.test.js +106 -0
  72. package/dist/proxy/index.test.js +10 -1
  73. package/dist/tool-builder.test.js +11 -1
  74. package/dist/types.d.ts +793 -23
  75. package/dist/types.js +180 -8
  76. package/package.json +4 -4
@@ -0,0 +1,71 @@
1
+ import { HttpClient } from '../http/client';
2
+ import { UserDTO, TeamRelationDTO, TeamMemberDTO, TeamInviteDTO, TeamCreateRequest, TeamMemberAddRequest, TeamInviteCreateRequest } from '../types';
3
+ export interface MeResponse {
4
+ user: UserDTO;
5
+ team?: TeamRelationDTO;
6
+ }
7
+ /**
8
+ * Teams API
9
+ */
10
+ export declare class TeamsAPI {
11
+ private readonly http;
12
+ constructor(http: HttpClient);
13
+ /**
14
+ * Get current user and team context
15
+ */
16
+ me(): Promise<MeResponse>;
17
+ /**
18
+ * List user's teams
19
+ */
20
+ list(): Promise<TeamRelationDTO[]>;
21
+ /**
22
+ * Get a team by ID
23
+ */
24
+ get(teamId: string): Promise<TeamRelationDTO>;
25
+ /**
26
+ * Create a new team
27
+ */
28
+ create(data: TeamCreateRequest): Promise<TeamRelationDTO>;
29
+ /**
30
+ * Update a team
31
+ */
32
+ update(teamId: string, data: Partial<TeamCreateRequest>): Promise<TeamRelationDTO>;
33
+ /**
34
+ * Delete a team
35
+ */
36
+ delete(teamId: string): Promise<void>;
37
+ /**
38
+ * Check username availability
39
+ */
40
+ checkUsername(username: string): Promise<{
41
+ available: boolean;
42
+ }>;
43
+ /**
44
+ * Get team members
45
+ */
46
+ getMembers(teamId: string): Promise<TeamMemberDTO[]>;
47
+ /**
48
+ * Add a team member
49
+ */
50
+ addMember(teamId: string, data: TeamMemberAddRequest): Promise<TeamMemberDTO>;
51
+ /**
52
+ * Remove a team member
53
+ */
54
+ removeMember(teamId: string, userId: string): Promise<void>;
55
+ /**
56
+ * Update a member's role
57
+ */
58
+ updateMemberRole(teamId: string, userId: string, role: string): Promise<TeamMemberDTO>;
59
+ /**
60
+ * List team invites
61
+ */
62
+ listInvites(teamId: string): Promise<TeamInviteDTO[]>;
63
+ /**
64
+ * Create an invite
65
+ */
66
+ createInvite(teamId: string, data: TeamInviteCreateRequest): Promise<TeamInviteDTO>;
67
+ /**
68
+ * Revoke an invite
69
+ */
70
+ revokeInvite(teamId: string, inviteId: string): Promise<void>;
71
+ }
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Teams API
3
+ */
4
+ export class TeamsAPI {
5
+ constructor(http) {
6
+ this.http = http;
7
+ }
8
+ /**
9
+ * Get current user and team context
10
+ */
11
+ async me() {
12
+ return this.http.request('get', '/me');
13
+ }
14
+ /**
15
+ * List user's teams
16
+ */
17
+ async list() {
18
+ return this.http.request('get', '/teams');
19
+ }
20
+ /**
21
+ * Get a team by ID
22
+ */
23
+ async get(teamId) {
24
+ return this.http.request('get', `/teams/${teamId}`);
25
+ }
26
+ /**
27
+ * Create a new team
28
+ */
29
+ async create(data) {
30
+ return this.http.request('post', '/teams', { data });
31
+ }
32
+ /**
33
+ * Update a team
34
+ */
35
+ async update(teamId, data) {
36
+ return this.http.request('post', `/teams/${teamId}`, { data });
37
+ }
38
+ /**
39
+ * Delete a team
40
+ */
41
+ async delete(teamId) {
42
+ return this.http.request('delete', `/teams/${teamId}`);
43
+ }
44
+ /**
45
+ * Check username availability
46
+ */
47
+ async checkUsername(username) {
48
+ return this.http.request('get', '/teams/check-username', { params: { username } });
49
+ }
50
+ /**
51
+ * Get team members
52
+ */
53
+ async getMembers(teamId) {
54
+ return this.http.request('get', `/teams/${teamId}/members`);
55
+ }
56
+ /**
57
+ * Add a team member
58
+ */
59
+ async addMember(teamId, data) {
60
+ return this.http.request('post', `/teams/${teamId}/members`, { data });
61
+ }
62
+ /**
63
+ * Remove a team member
64
+ */
65
+ async removeMember(teamId, userId) {
66
+ return this.http.request('delete', `/teams/${teamId}/members/${userId}`);
67
+ }
68
+ /**
69
+ * Update a member's role
70
+ */
71
+ async updateMemberRole(teamId, userId, role) {
72
+ return this.http.request('post', `/teams/${teamId}/members/${userId}/role`, { data: { role } });
73
+ }
74
+ /**
75
+ * List team invites
76
+ */
77
+ async listInvites(teamId) {
78
+ return this.http.request('get', `/teams/${teamId}/invites`);
79
+ }
80
+ /**
81
+ * Create an invite
82
+ */
83
+ async createInvite(teamId, data) {
84
+ return this.http.request('post', `/teams/${teamId}/invites`, { data });
85
+ }
86
+ /**
87
+ * Revoke an invite
88
+ */
89
+ async revokeInvite(teamId, inviteId) {
90
+ return this.http.request('delete', `/teams/${teamId}/invites/${inviteId}`);
91
+ }
92
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,79 @@
1
+ import { HttpClient } from '../http/client';
2
+ import { TeamsAPI } from './teams';
3
+ const mockFetch = jest.fn();
4
+ global.fetch = mockFetch;
5
+ function mockJsonResponse(body) {
6
+ mockFetch.mockResolvedValueOnce({
7
+ ok: true,
8
+ status: 200,
9
+ text: () => Promise.resolve(JSON.stringify(body)),
10
+ });
11
+ }
12
+ describe('TeamsAPI', () => {
13
+ beforeEach(() => {
14
+ jest.clearAllMocks();
15
+ });
16
+ const api = () => new TeamsAPI(new HttpClient({ apiKey: 'test-key' }));
17
+ it('should GET /me for me()', async () => {
18
+ const me = { user: { id: 'user-1' }, team: { id: 'team-1' } };
19
+ mockJsonResponse(me);
20
+ const result = await api().me();
21
+ expect(result).toEqual(me);
22
+ const [url, init] = mockFetch.mock.calls[0];
23
+ expect(url).toContain('/me');
24
+ expect(init.method).toBe('GET');
25
+ });
26
+ it('should GET /teams for list()', async () => {
27
+ const teams = [{ id: 'team-1', name: 'Acme' }];
28
+ mockJsonResponse(teams);
29
+ const result = await api().list();
30
+ expect(result).toEqual(teams);
31
+ const [url, init] = mockFetch.mock.calls[0];
32
+ expect(url).toContain('/teams');
33
+ expect(init.method).toBe('GET');
34
+ });
35
+ it('should POST /teams for create()', async () => {
36
+ const payload = { name: 'New Team', username: 'new-team' };
37
+ const team = { id: 'team-new', ...payload };
38
+ mockJsonResponse(team);
39
+ const result = await api().create(payload);
40
+ expect(result).toEqual(team);
41
+ const [url, init] = mockFetch.mock.calls[0];
42
+ expect(url).toContain('/teams');
43
+ expect(init.method).toBe('POST');
44
+ expect(JSON.parse(init.body)).toEqual(payload);
45
+ });
46
+ it('should GET /teams/check-username with username param for checkUsername()', async () => {
47
+ mockJsonResponse({ available: true });
48
+ const result = await api().checkUsername('acme-corp');
49
+ expect(result).toEqual({ available: true });
50
+ const [url] = mockFetch.mock.calls[0];
51
+ expect(url).toContain('/teams/check-username');
52
+ expect(url).toContain('username=acme-corp');
53
+ });
54
+ it('should POST role for updateMemberRole()', async () => {
55
+ const member = { user_id: 'user-2', role: 'admin' };
56
+ mockJsonResponse(member);
57
+ await api().updateMemberRole('team-1', 'user-2', 'admin');
58
+ const [url, init] = mockFetch.mock.calls[0];
59
+ expect(url).toContain('/teams/team-1/members/user-2/role');
60
+ expect(JSON.parse(init.body)).toEqual({ role: 'admin' });
61
+ });
62
+ it('should DELETE /teams/{id}/members/{userId} for removeMember()', async () => {
63
+ mockJsonResponse(null);
64
+ await api().removeMember('team-1', 'user-3');
65
+ const [url, init] = mockFetch.mock.calls[0];
66
+ expect(url).toContain('/teams/team-1/members/user-3');
67
+ expect(init.method).toBe('DELETE');
68
+ });
69
+ it('should POST /teams/{id}/invites for createInvite()', async () => {
70
+ const payload = { email: 'dev@example.com', role: 'member' };
71
+ const invite = { id: 'inv-1', ...payload };
72
+ mockJsonResponse(invite);
73
+ const result = await api().createInvite('team-1', payload);
74
+ expect(result).toEqual(invite);
75
+ const [url, init] = mockFetch.mock.calls[0];
76
+ expect(url).toContain('/teams/team-1/invites');
77
+ expect(JSON.parse(init.body)).toEqual(payload);
78
+ });
79
+ });
@@ -38,7 +38,7 @@ describe('Inference', () => {
38
38
  input: { message: 'hello world' },
39
39
  output: { result: 'success' },
40
40
  };
41
- const responseData = { success: true, data: mockTask };
41
+ const responseData = mockTask;
42
42
  mockFetch.mockResolvedValueOnce({
43
43
  ok: true,
44
44
  status: 200,
@@ -58,7 +58,7 @@ describe('Inference', () => {
58
58
  }));
59
59
  });
60
60
  it('should throw error on API failure', async () => {
61
- const responseData = { success: false, error: { message: 'Invalid app' } };
61
+ const responseData = { message: 'Invalid app' };
62
62
  mockFetch.mockResolvedValueOnce({
63
63
  ok: false,
64
64
  status: 400,
@@ -141,7 +141,7 @@ describe('Inference', () => {
141
141
  });
142
142
  describe('cancel', () => {
143
143
  it('should make a POST request to cancel endpoint', async () => {
144
- const responseData = { success: true, data: null };
144
+ const responseData = null;
145
145
  mockFetch.mockResolvedValueOnce({
146
146
  ok: true,
147
147
  status: 200,
@@ -185,7 +185,7 @@ describe('namespaced APIs', () => {
185
185
  updated_at: new Date().toISOString(),
186
186
  input: { message: 'hello world' },
187
187
  };
188
- const responseData = { success: true, data: mockTask };
188
+ const responseData = mockTask;
189
189
  mockFetch.mockResolvedValueOnce({
190
190
  ok: true,
191
191
  status: 200,
@@ -202,7 +202,7 @@ describe('namespaced APIs', () => {
202
202
  });
203
203
  it('should get task via tasks.get()', async () => {
204
204
  const mockTask = { id: 'task-123', status: 7 };
205
- const responseData = { success: true, data: mockTask };
205
+ const responseData = mockTask;
206
206
  mockFetch.mockResolvedValueOnce({
207
207
  ok: true,
208
208
  status: 200,
@@ -215,7 +215,7 @@ describe('namespaced APIs', () => {
215
215
  expect(mockFetch).toHaveBeenCalledWith(expect.stringContaining('/tasks/task-123'), expect.objectContaining({ method: 'GET' }));
216
216
  });
217
217
  it('should cancel task via tasks.cancel()', async () => {
218
- const responseData = { success: true, data: null };
218
+ const responseData = null;
219
219
  mockFetch.mockResolvedValueOnce({
220
220
  ok: true,
221
221
  status: 200,
@@ -298,7 +298,7 @@ describe('uploadFile', () => {
298
298
  uri: 'https://example.com/file.png',
299
299
  upload_url: 'https://upload.example.com/signed-url',
300
300
  };
301
- const responseData = { success: true, data: [mockFile] };
301
+ const responseData = [mockFile];
302
302
  mockFetch
303
303
  .mockResolvedValueOnce({
304
304
  ok: true,
@@ -322,7 +322,7 @@ describe('uploadFile', () => {
322
322
  uri: 'https://example.com/file.png',
323
323
  // Missing upload_url
324
324
  };
325
- const responseData = { success: true, data: [mockFile] };
325
+ const responseData = [mockFile];
326
326
  mockFetch.mockResolvedValueOnce({
327
327
  ok: true,
328
328
  status: 200,
@@ -84,6 +84,11 @@ export declare class HttpClient {
84
84
  url: string;
85
85
  headers: Record<string, string>;
86
86
  };
87
+ /**
88
+ * Pull a human-readable detail string from a parsed problem+json / API error
89
+ * body (RFC 9457 `detail`/`title`, or a plain `message`).
90
+ */
91
+ private extractErrorDetail;
87
92
  /**
88
93
  * Create an EventSource for SSE streaming
89
94
  * @deprecated Use getStreamableConfig() with StreamableManager instead
@@ -16,7 +16,7 @@ export class HttpClient {
16
16
  this.baseUrl = config.baseUrl || 'https://api.inference.sh';
17
17
  this.proxyUrl = config.proxyUrl;
18
18
  this.getToken = config.getToken;
19
- this.customHeaders = { 'X-Client-Source': 'inference-sdk-js/0.5.13', ...config.headers };
19
+ this.customHeaders = { 'X-Client-Source': 'inference-sdk-js/0.5.13', 'X-API-Version': '2', ...config.headers };
20
20
  this.credentials = config.credentials || 'include';
21
21
  this.onError = config.onError;
22
22
  this.streamDefault = config.stream ?? true;
@@ -114,7 +114,6 @@ export class HttpClient {
114
114
  }
115
115
  const response = await fetch(fetchUrl, fetchOptions);
116
116
  const responseText = await response.text();
117
- // Try to parse as JSON
118
117
  let data = null;
119
118
  try {
120
119
  data = JSON.parse(responseText);
@@ -122,44 +121,24 @@ export class HttpClient {
122
121
  catch {
123
122
  // Not JSON
124
123
  }
125
- // Check for HTTP errors
124
+ // HTTP errors RFC 9457 problem+json
126
125
  if (!response.ok) {
127
- // Check for RequirementsNotMetException (412 with errors array)
128
126
  if (response.status === 412 && data && 'errors' in data && Array.isArray(data.errors)) {
129
127
  throw RequirementsNotMetException.fromResponse(data, response.status);
130
128
  }
131
- // General error handling
132
129
  let errorDetail;
133
130
  if (data && typeof data === 'object') {
134
- const apiData = data;
135
- if (apiData.error) {
136
- errorDetail = typeof apiData.error === 'object' ? apiData.error.message : String(apiData.error);
137
- }
138
- else if ('message' in data) {
139
- errorDetail = String(data.message);
140
- }
141
- else {
142
- errorDetail = JSON.stringify(data);
143
- }
131
+ errorDetail = this.extractErrorDetail(data) ?? JSON.stringify(data);
144
132
  }
145
133
  else if (responseText) {
146
134
  errorDetail = responseText.slice(0, 500);
147
135
  }
148
136
  throw new InferenceError(response.status, errorDetail || 'Request failed', responseText);
149
137
  }
150
- // Handle 204 No Content (e.g., DELETE requests)
151
- if (response.status === 204) {
138
+ if (response.status === 204 || !responseText) {
152
139
  return undefined;
153
140
  }
154
- const apiResponse = data;
155
- if (!apiResponse?.success) {
156
- let errorMessage = apiResponse?.error?.message;
157
- if (!errorMessage) {
158
- errorMessage = `Request failed (success=false). Response: ${responseText.slice(0, 500)}`;
159
- }
160
- throw new InferenceError(response.status, errorMessage, responseText);
161
- }
162
- return (apiResponse.data ?? null);
141
+ return data;
163
142
  }
164
143
  /**
165
144
  * Get URL and headers for NDJSON streaming.
@@ -185,6 +164,16 @@ export class HttpClient {
185
164
  }
186
165
  return { url, headers };
187
166
  }
167
+ /**
168
+ * Pull a human-readable detail string from a parsed problem+json / API error
169
+ * body (RFC 9457 `detail`/`title`, or a plain `message`).
170
+ */
171
+ extractErrorDetail(data) {
172
+ if (!data || typeof data !== 'object')
173
+ return undefined;
174
+ const d = data;
175
+ return (d.detail || d.title || d.message);
176
+ }
188
177
  /**
189
178
  * Create an EventSource for SSE streaming
190
179
  * @deprecated Use getStreamableConfig() with StreamableManager instead
@@ -204,30 +193,39 @@ export class HttpClient {
204
193
  fetchUrl = targetUrl.toString();
205
194
  }
206
195
  const resolvedHeaders = this.resolveHeaders();
207
- return Promise.resolve(new EventSource(fetchUrl, {
208
- fetch: (input, init) => {
209
- const headers = {
210
- ...init?.headers,
211
- ...resolvedHeaders,
212
- };
213
- if (isProxyMode) {
214
- // Proxy mode: also send target URL as header (for non-browser clients)
215
- headers['x-inf-target-url'] = targetUrl.toString();
196
+ // The EventSource error event omits the response body, so a failed initial
197
+ // connection (e.g. 403 otp_required) is handled here in the fetch wrapper:
198
+ // route it through onError like request() does, and hand the retried
199
+ // response (e.g. after OTP verification) back so the stream connects.
200
+ const doFetch = async (input, init) => {
201
+ const headers = {
202
+ ...init?.headers,
203
+ ...resolvedHeaders,
204
+ };
205
+ if (isProxyMode) {
206
+ headers['x-inf-target-url'] = targetUrl.toString();
207
+ }
208
+ else {
209
+ const token = this.getAuthToken();
210
+ if (token) {
211
+ headers['Authorization'] = `Bearer ${token}`;
216
212
  }
217
- else {
218
- // Direct mode: include authorization header
219
- const token = this.getAuthToken();
220
- if (token) {
221
- headers['Authorization'] = `Bearer ${token}`;
222
- }
213
+ }
214
+ const response = await fetch(input, { ...init, headers, credentials: this.credentials });
215
+ if (!response.ok && this.onError) {
216
+ const body = await response.clone().text().catch(() => '');
217
+ let data;
218
+ try {
219
+ data = JSON.parse(body);
223
220
  }
224
- return fetch(input, {
225
- ...init,
226
- headers,
227
- credentials: this.credentials,
228
- });
229
- },
230
- }));
221
+ catch { /* non-JSON body */ }
222
+ const error = new InferenceError(response.status, this.extractErrorDetail(data) ?? 'Request failed', body);
223
+ const handled = await this.onError(error, () => doFetch(input, init));
224
+ return handled ?? response;
225
+ }
226
+ return response;
227
+ };
228
+ return Promise.resolve(new EventSource(fetchUrl, { fetch: doFetch }));
231
229
  }
232
230
  }
233
231
  /**