@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
@@ -14,6 +14,32 @@ describe('FilesAPI', () => {
14
14
  jest.clearAllMocks();
15
15
  });
16
16
  const api = () => new FilesAPI(new HttpClient({ apiKey: 'test-key' }));
17
+ it('should POST /files/list for list()', async () => {
18
+ const page = { items: [{ id: 'file-1' }], next_cursor: null };
19
+ mockJsonResponse(page);
20
+ const result = await api().list({ limit: 10 });
21
+ expect(result).toEqual(page);
22
+ const [url, init] = mockFetch.mock.calls[0];
23
+ expect(url).toContain('/files/list');
24
+ expect(init.method).toBe('POST');
25
+ expect(JSON.parse(init.body)).toEqual({ limit: 10 });
26
+ });
27
+ it('should GET /files/{id} for get()', async () => {
28
+ const file = { id: 'file-1', uri: 'inf://files/abc' };
29
+ mockJsonResponse(file);
30
+ const result = await api().get('file-1');
31
+ expect(result).toEqual(file);
32
+ const [url, init] = mockFetch.mock.calls[0];
33
+ expect(url).toContain('/files/file-1');
34
+ expect(init.method).toBe('GET');
35
+ });
36
+ it('should DELETE /files/{id} for delete()', async () => {
37
+ mockJsonResponse(null);
38
+ await api().delete('file-1');
39
+ const [url, init] = mockFetch.mock.calls[0];
40
+ expect(url).toContain('/files/file-1');
41
+ expect(init.method).toBe('DELETE');
42
+ });
17
43
  describe('processInput', () => {
18
44
  it('should not treat short plain strings as base64 file uploads', async () => {
19
45
  const result = await api().processInput({ key: 'key1', note: 'hello' });
@@ -27,7 +53,7 @@ describe('FilesAPI', () => {
27
53
  upload_url: 'https://upload.example.com/put',
28
54
  content_type: 'image/png',
29
55
  };
30
- mockJsonResponse({ success: true, data: [fileRecord] });
56
+ mockJsonResponse([fileRecord]);
31
57
  mockFetch.mockResolvedValueOnce({ ok: true, status: 200 });
32
58
  const input = {
33
59
  prompt: 'draw',
@@ -38,15 +64,169 @@ describe('FilesAPI', () => {
38
64
  expect(result.image).toBe('inf://files/abc');
39
65
  expect(mockFetch).toHaveBeenCalledTimes(2);
40
66
  });
67
+ it('should upload top-level Blob values', async () => {
68
+ const fileRecord = {
69
+ id: 'file-blob',
70
+ uri: 'inf://files/blob',
71
+ upload_url: 'https://upload.example.com/put',
72
+ content_type: 'image/png',
73
+ };
74
+ mockJsonResponse([fileRecord]);
75
+ mockFetch.mockResolvedValueOnce({ ok: true, status: 200 });
76
+ const blob = new Blob(['png-bytes'], { type: 'image/png' });
77
+ const result = await api().processInput(blob);
78
+ expect(result).toBe('inf://files/blob');
79
+ expect(mockFetch).toHaveBeenCalledTimes(2);
80
+ });
81
+ it('should process arrays recursively', async () => {
82
+ const fileRecord = {
83
+ id: 'file-arr',
84
+ uri: 'inf://files/arr',
85
+ upload_url: 'https://upload.example.com/put',
86
+ content_type: 'image/png',
87
+ };
88
+ mockJsonResponse([fileRecord]);
89
+ mockFetch.mockResolvedValueOnce({ ok: true, status: 200 });
90
+ const input = ['plain', 'data:image/png;base64,iVBORw0KGgo='];
91
+ const result = (await api().processInput(input));
92
+ expect(result[0]).toBe('plain');
93
+ expect(result[1]).toBe('inf://files/arr');
94
+ });
95
+ it('should upload long raw base64 strings', async () => {
96
+ const fileRecord = {
97
+ id: 'file-b64',
98
+ uri: 'inf://files/b64',
99
+ upload_url: 'https://upload.example.com/put',
100
+ content_type: 'application/octet-stream',
101
+ };
102
+ mockJsonResponse([fileRecord]);
103
+ mockFetch.mockResolvedValueOnce({ ok: true, status: 200 });
104
+ const rawBase64 = 'A'.repeat(64);
105
+ const result = await api().processInput(rawBase64);
106
+ expect(result).toBe('inf://files/b64');
107
+ expect(mockFetch).toHaveBeenCalledTimes(2);
108
+ });
41
109
  });
42
110
  describe('upload', () => {
43
111
  it('should reject invalid data URI format when uploading content', async () => {
44
- mockJsonResponse({
45
- success: true,
46
- data: [{ id: 'file-x', uri: '', upload_url: 'https://upload.example.com/put' }],
47
- });
112
+ mockJsonResponse([{ id: 'file-x', uri: '', upload_url: 'https://upload.example.com/put' }]);
48
113
  await expect(api().upload('data:invalid')).rejects.toThrow('Invalid data URI format');
49
114
  });
115
+ it('should decode URL-encoded (non-base64) data URIs', async () => {
116
+ const fileRecord = {
117
+ id: 'file-3',
118
+ uri: 'inf://files/url-encoded',
119
+ upload_url: 'https://upload.example.com/put',
120
+ content_type: 'text/plain',
121
+ };
122
+ mockJsonResponse([fileRecord]);
123
+ mockFetch.mockResolvedValueOnce({ ok: true, status: 200 });
124
+ const result = await api().upload('data:text/plain,Hello%20World');
125
+ expect(result.uri).toBe('inf://files/url-encoded');
126
+ const putCall = mockFetch.mock.calls[1];
127
+ expect(putCall[1]?.headers).toMatchObject({ 'Content-Type': 'text/plain' });
128
+ });
129
+ it('should default media type to text/plain for data URIs without explicit type', async () => {
130
+ const fileRecord = {
131
+ id: 'file-4',
132
+ uri: 'inf://files/default-mt',
133
+ upload_url: 'https://upload.example.com/put',
134
+ content_type: 'text/plain',
135
+ };
136
+ mockJsonResponse([fileRecord]);
137
+ mockFetch.mockResolvedValueOnce({ ok: true, status: 200 });
138
+ await api().upload('data:;base64,SGVsbG8=');
139
+ const putCall = mockFetch.mock.calls[1];
140
+ expect(putCall[1]?.headers).toMatchObject({ 'Content-Type': 'text/plain' });
141
+ });
142
+ it('should throw when server does not return upload_url', async () => {
143
+ mockJsonResponse([{ id: 'file-x', uri: '', upload_url: undefined }]);
144
+ await expect(api().upload('data:text/plain,hello')).rejects.toThrow('No upload URL provided by the server');
145
+ });
146
+ it('should throw when PUT to upload_url fails', async () => {
147
+ const fileRecord = {
148
+ id: 'file-5',
149
+ uri: 'inf://files/fail-put',
150
+ upload_url: 'https://upload.example.com/put',
151
+ content_type: 'text/plain',
152
+ };
153
+ mockJsonResponse([fileRecord]);
154
+ mockFetch.mockResolvedValueOnce({
155
+ ok: false,
156
+ status: 500,
157
+ statusText: 'Internal Server Error',
158
+ });
159
+ await expect(api().upload('data:text/plain,hello')).rejects.toThrow('Failed to upload file content');
160
+ });
161
+ it('should upload Blob content with inferred content type and size', async () => {
162
+ const fileRecord = {
163
+ id: 'file-blob',
164
+ uri: 'inf://files/blob-direct',
165
+ upload_url: 'https://upload.example.com/put',
166
+ content_type: 'image/jpeg',
167
+ };
168
+ mockJsonResponse([fileRecord]);
169
+ mockFetch.mockResolvedValueOnce({ ok: true, status: 200 });
170
+ const blob = new Blob(['jpeg-bytes'], { type: 'image/jpeg' });
171
+ const result = await api().upload(blob, { filename: 'photo.jpg' });
172
+ expect(result.uri).toBe('inf://files/blob-direct');
173
+ const [, createInit] = mockFetch.mock.calls[0];
174
+ const createBody = JSON.parse(createInit.body);
175
+ expect(createBody.files[0]).toMatchObject({
176
+ filename: 'photo.jpg',
177
+ content_type: 'image/jpeg',
178
+ size: blob.size,
179
+ });
180
+ const [, putInit] = mockFetch.mock.calls[1];
181
+ expect(putInit.method).toBe('PUT');
182
+ expect(putInit.body).toBe(blob);
183
+ expect(putInit.headers).toMatchObject({ 'Content-Type': 'image/jpeg' });
184
+ });
185
+ it('should extract filename from File objects when options.filename is omitted', async () => {
186
+ const fileRecord = {
187
+ id: 'file-named',
188
+ uri: 'inf://files/named',
189
+ upload_url: 'https://upload.example.com/put',
190
+ content_type: 'application/pdf',
191
+ };
192
+ mockJsonResponse([fileRecord]);
193
+ mockFetch.mockResolvedValueOnce({ ok: true, status: 200 });
194
+ const file = new File(['pdf-bytes'], 'report.pdf', { type: 'application/pdf' });
195
+ await api().upload(file);
196
+ const [, createInit] = mockFetch.mock.calls[0];
197
+ const createBody = JSON.parse(createInit.body);
198
+ expect(createBody.files[0].filename).toBe('report.pdf');
199
+ expect(createBody.files[0].content_type).toBe('application/pdf');
200
+ });
201
+ it('should use explicit contentType when Blob type is empty', async () => {
202
+ const fileRecord = {
203
+ id: 'file-octet',
204
+ uri: 'inf://files/octet',
205
+ upload_url: 'https://upload.example.com/put',
206
+ content_type: 'application/octet-stream',
207
+ };
208
+ mockJsonResponse([fileRecord]);
209
+ mockFetch.mockResolvedValueOnce({ ok: true, status: 200 });
210
+ const blob = new Blob(['raw']);
211
+ await api().upload(blob, { contentType: 'application/octet-stream' });
212
+ const [, createInit] = mockFetch.mock.calls[0];
213
+ const createBody = JSON.parse(createInit.body);
214
+ expect(createBody.files[0].content_type).toBe('application/octet-stream');
215
+ });
216
+ it('should upload clean base64 strings without a data URI prefix', async () => {
217
+ const fileRecord = {
218
+ id: 'file-b64-direct',
219
+ uri: 'inf://files/b64-direct',
220
+ upload_url: 'https://upload.example.com/put',
221
+ content_type: 'text/plain',
222
+ };
223
+ mockJsonResponse([fileRecord]);
224
+ mockFetch.mockResolvedValueOnce({ ok: true, status: 200 });
225
+ const result = await api().upload('SGVsbG8=', { contentType: 'text/plain' });
226
+ expect(result.uri).toBe('inf://files/b64-direct');
227
+ const [, putInit] = mockFetch.mock.calls[1];
228
+ expect(putInit.headers).toMatchObject({ 'Content-Type': 'text/plain' });
229
+ });
50
230
  it('should decode URL-safe base64 in data URIs', async () => {
51
231
  const fileRecord = {
52
232
  id: 'file-2',
@@ -54,7 +234,7 @@ describe('FilesAPI', () => {
54
234
  upload_url: 'https://upload.example.com/put',
55
235
  content_type: 'text/plain',
56
236
  };
57
- mockJsonResponse({ success: true, data: [fileRecord] });
237
+ mockJsonResponse([fileRecord]);
58
238
  mockFetch.mockResolvedValueOnce({ ok: true, status: 200 });
59
239
  // "SGVsbG8" is "Hello" in standard base64; URL-safe variant uses '-' instead of '+'
60
240
  const dataUri = 'data:text/plain;base64,SGVsbG8';
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,97 @@
1
+ import { HttpClient } from '../http/client';
2
+ import { FlowRunsAPI } from './flow-runs';
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('FlowRunsAPI', () => {
13
+ beforeEach(() => {
14
+ jest.clearAllMocks();
15
+ });
16
+ const api = () => new FlowRunsAPI(new HttpClient({ apiKey: 'test-key' }));
17
+ it('should POST flow and input for create()', async () => {
18
+ const flowRun = { id: 'fr-1', flow: 'flow-1' };
19
+ mockJsonResponse(flowRun);
20
+ const result = await api().create('flow-1', { prompt: 'hi' });
21
+ expect(result).toEqual(flowRun);
22
+ const [, init] = mockFetch.mock.calls[0];
23
+ expect(JSON.parse(init.body)).toEqual({
24
+ flow: 'flow-1',
25
+ input: { prompt: 'hi' },
26
+ });
27
+ });
28
+ it('should POST /flowruns/{id}/cancel for cancel()', async () => {
29
+ mockJsonResponse(null);
30
+ await api().cancel('fr-1');
31
+ const [url, init] = mockFetch.mock.calls[0];
32
+ expect(url).toContain('/flowruns/fr-1/cancel');
33
+ expect(init.method).toBe('POST');
34
+ });
35
+ it('should open SSE on /flowruns/{id}/stream for stream()', async () => {
36
+ const http = new HttpClient({ apiKey: 'test-key' });
37
+ const createEventSource = jest
38
+ .spyOn(http, 'createEventSource')
39
+ .mockResolvedValue(null);
40
+ const flowRuns = new FlowRunsAPI(http);
41
+ await flowRuns.stream('fr-42');
42
+ expect(createEventSource).toHaveBeenCalledWith('/flowruns/fr-42/stream');
43
+ createEventSource.mockRestore();
44
+ });
45
+ it('should open task SSE for streamTasks()', async () => {
46
+ const http = new HttpClient({ apiKey: 'test-key' });
47
+ const createEventSource = jest
48
+ .spyOn(http, 'createEventSource')
49
+ .mockResolvedValue(null);
50
+ const flowRuns = new FlowRunsAPI(http);
51
+ await flowRuns.streamTasks('fr-42');
52
+ expect(createEventSource).toHaveBeenCalledWith('/flowruns/fr-42/tasks/stream');
53
+ createEventSource.mockRestore();
54
+ });
55
+ it('should POST /flowruns/list for list()', async () => {
56
+ const page = { items: [{ id: 'fr-1' }], next_cursor: null };
57
+ mockJsonResponse(page);
58
+ const result = await api().list({ limit: 10 });
59
+ expect(result).toEqual(page);
60
+ const [url, init] = mockFetch.mock.calls[0];
61
+ expect(url).toContain('/flowruns/list');
62
+ expect(init.method).toBe('POST');
63
+ });
64
+ it('should GET /flowruns/{id} for get()', async () => {
65
+ const flowRun = { id: 'fr-1' };
66
+ mockJsonResponse(flowRun);
67
+ const result = await api().get('fr-1');
68
+ expect(result).toEqual(flowRun);
69
+ const [url, init] = mockFetch.mock.calls[0];
70
+ expect(url).toContain('/flowruns/fr-1');
71
+ expect(init.method).toBe('GET');
72
+ });
73
+ it('should POST /flowruns/{id}/clone for clone()', async () => {
74
+ const flowRun = { id: 'fr-clone' };
75
+ mockJsonResponse(flowRun);
76
+ const result = await api().clone('fr-1');
77
+ expect(result).toEqual(flowRun);
78
+ const [url, init] = mockFetch.mock.calls[0];
79
+ expect(url).toContain('/flowruns/fr-1/clone');
80
+ expect(init.method).toBe('POST');
81
+ });
82
+ it('should POST /flowruns/{id} for update()', async () => {
83
+ const flowRun = { id: 'fr-1', fail_on_error: false };
84
+ mockJsonResponse(flowRun);
85
+ const result = await api().update('fr-1', { fail_on_error: false });
86
+ expect(result).toEqual(flowRun);
87
+ const [, init] = mockFetch.mock.calls[0];
88
+ expect(JSON.parse(init.body)).toEqual({ fail_on_error: false });
89
+ });
90
+ it('should POST visibility for updateVisibility()', async () => {
91
+ const flowRun = { id: 'fr-1', visibility: 'public' };
92
+ mockJsonResponse(flowRun);
93
+ await api().updateVisibility('fr-1', 'public');
94
+ const [, init] = mockFetch.mock.calls[0];
95
+ expect(JSON.parse(init.body)).toEqual({ visibility: 'public' });
96
+ });
97
+ });
@@ -22,6 +22,10 @@ export declare class FlowsAPI {
22
22
  * Update a flow
23
23
  */
24
24
  update(flowId: string, data: Partial<Flow>): Promise<Flow>;
25
+ /**
26
+ * Update flow visibility
27
+ */
28
+ updateVisibility(flowId: string, visibility: string): Promise<Flow>;
25
29
  /**
26
30
  * Delete a flow
27
31
  */
package/dist/api/flows.js CHANGED
@@ -29,6 +29,12 @@ export class FlowsAPI {
29
29
  async update(flowId, data) {
30
30
  return this.http.request('post', `/flows/${flowId}`, { data });
31
31
  }
32
+ /**
33
+ * Update flow visibility
34
+ */
35
+ async updateVisibility(flowId, visibility) {
36
+ return this.http.request('post', `/flows/${flowId}/visibility`, { data: { visibility } });
37
+ }
32
38
  /**
33
39
  * Delete a flow
34
40
  */
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,118 @@
1
+ import { HttpClient } from '../http/client';
2
+ import { FlowsAPI } from './flows';
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('FlowsAPI', () => {
13
+ beforeEach(() => {
14
+ jest.clearAllMocks();
15
+ });
16
+ const api = () => new FlowsAPI(new HttpClient({ apiKey: 'test-key' }));
17
+ it('should POST name for create()', async () => {
18
+ const flow = { id: 'flow-1', name: 'My Flow' };
19
+ mockJsonResponse(flow);
20
+ const result = await api().create('My Flow');
21
+ expect(result).toEqual(flow);
22
+ const [, init] = mockFetch.mock.calls[0];
23
+ expect(JSON.parse(init.body)).toEqual({ name: 'My Flow' });
24
+ });
25
+ it('should POST /flows/{id}/app for createApp()', async () => {
26
+ const app = { id: 'app-from-flow' };
27
+ mockJsonResponse(app);
28
+ const result = await api().createApp('flow-1');
29
+ expect(result).toEqual(app);
30
+ const [url] = mockFetch.mock.calls[0];
31
+ expect(url).toContain('/flows/flow-1/app');
32
+ });
33
+ it('should open SSE on /flows/{id}/stream for stream()', async () => {
34
+ const http = new HttpClient({ apiKey: 'test-key' });
35
+ const createEventSource = jest
36
+ .spyOn(http, 'createEventSource')
37
+ .mockResolvedValue(null);
38
+ const flows = new FlowsAPI(http);
39
+ await flows.stream('flow-5');
40
+ expect(createEventSource).toHaveBeenCalledWith('/flows/flow-5/stream');
41
+ createEventSource.mockRestore();
42
+ });
43
+ it('should POST /flows/list for list()', async () => {
44
+ const page = { items: [{ id: 'flow-1' }], next_cursor: null };
45
+ mockJsonResponse(page);
46
+ const result = await api().list({ limit: 10 });
47
+ expect(result).toEqual(page);
48
+ const [url, init] = mockFetch.mock.calls[0];
49
+ expect(url).toContain('/flows/list');
50
+ expect(init.method).toBe('POST');
51
+ });
52
+ it('should GET /flows/{id} for get()', async () => {
53
+ const flow = { id: 'flow-1', name: 'Demo' };
54
+ mockJsonResponse(flow);
55
+ const result = await api().get('flow-1');
56
+ expect(result).toEqual(flow);
57
+ const [url, init] = mockFetch.mock.calls[0];
58
+ expect(url).toContain('/flows/flow-1');
59
+ expect(init.method).toBe('GET');
60
+ });
61
+ it('should POST /flows/{id} for update()', async () => {
62
+ const flow = { id: 'flow-1', name: 'Renamed' };
63
+ mockJsonResponse(flow);
64
+ const result = await api().update('flow-1', { name: 'Renamed' });
65
+ expect(result).toEqual(flow);
66
+ const [, init] = mockFetch.mock.calls[0];
67
+ expect(JSON.parse(init.body)).toEqual({ name: 'Renamed' });
68
+ });
69
+ it('should POST visibility for updateVisibility()', async () => {
70
+ const flow = { id: 'flow-1', visibility: 'public' };
71
+ mockJsonResponse(flow);
72
+ const result = await api().updateVisibility('flow-1', 'public');
73
+ expect(result).toEqual(flow);
74
+ const [url, init] = mockFetch.mock.calls[0];
75
+ expect(url).toContain('/flows/flow-1/visibility');
76
+ expect(JSON.parse(init.body)).toEqual({ visibility: 'public' });
77
+ });
78
+ it('should DELETE /flows/{id} for delete()', async () => {
79
+ mockJsonResponse(null);
80
+ await api().delete('flow-1');
81
+ const [url, init] = mockFetch.mock.calls[0];
82
+ expect(url).toContain('/flows/flow-1');
83
+ expect(init.method).toBe('DELETE');
84
+ });
85
+ it('should POST /flows/{id}/duplicate for duplicate()', async () => {
86
+ const flow = { id: 'flow-copy' };
87
+ mockJsonResponse(flow);
88
+ const result = await api().duplicate('flow-1');
89
+ expect(result).toEqual(flow);
90
+ const [url] = mockFetch.mock.calls[0];
91
+ expect(url).toContain('/flows/flow-1/duplicate');
92
+ });
93
+ it('should POST /flows/{id}/versions/list for listVersions()', async () => {
94
+ const versions = { items: [{ id: 'ver-1' }], next_cursor: null };
95
+ mockJsonResponse(versions);
96
+ const result = await api().listVersions('flow-1');
97
+ expect(result).toEqual(versions);
98
+ const [url] = mockFetch.mock.calls[0];
99
+ expect(url).toContain('/flows/flow-1/versions/list');
100
+ });
101
+ it('should POST transfer with team_id for transferOwnership()', async () => {
102
+ const flow = { id: 'flow-1' };
103
+ mockJsonResponse(flow);
104
+ await api().transferOwnership('flow-1', 'team-42');
105
+ const [, init] = mockFetch.mock.calls[0];
106
+ expect(JSON.parse(init.body)).toEqual({ team_id: 'team-42' });
107
+ });
108
+ it('should POST /flows/{id}/visibility for updateVisibility()', async () => {
109
+ const flow = { id: 'flow-1', visibility: 'public' };
110
+ mockJsonResponse(flow);
111
+ const result = await api().updateVisibility('flow-1', 'public');
112
+ expect(result).toEqual(flow);
113
+ const [url, init] = mockFetch.mock.calls[0];
114
+ expect(url).toContain('/flows/flow-1/visibility');
115
+ expect(init.method).toBe('POST');
116
+ expect(JSON.parse(init.body)).toEqual({ visibility: 'public' });
117
+ });
118
+ });
@@ -0,0 +1,41 @@
1
+ import { HttpClient } from '../http/client';
2
+ import { IntegrationDTO, IntegrationConfigDTO, IntegrationConnectRequest, IntegrationConnectResponse, CursorListRequest, CursorListResponse } from '../types';
3
+ /**
4
+ * Integrations API
5
+ */
6
+ export declare class IntegrationsAPI {
7
+ private readonly http;
8
+ constructor(http: HttpClient);
9
+ /**
10
+ * List integrations with cursor-based pagination
11
+ */
12
+ list(params?: Partial<CursorListRequest>): Promise<CursorListResponse<IntegrationDTO>>;
13
+ /**
14
+ * Get available integrations
15
+ */
16
+ listAvailable(): Promise<IntegrationConfigDTO[]>;
17
+ /**
18
+ * Get integration configs
19
+ */
20
+ getConfigs(): Promise<IntegrationConfigDTO[]>;
21
+ /**
22
+ * Get capabilities
23
+ */
24
+ getCapabilities(): Promise<unknown>;
25
+ /**
26
+ * Check requirements
27
+ */
28
+ checkRequirements(data: unknown): Promise<unknown>;
29
+ /**
30
+ * Connect an integration
31
+ */
32
+ connect(data: IntegrationConnectRequest): Promise<IntegrationConnectResponse>;
33
+ /**
34
+ * Get an integration by provider key
35
+ */
36
+ get(provider: string): Promise<IntegrationDTO>;
37
+ /**
38
+ * Disconnect an integration
39
+ */
40
+ disconnect(provider: string): Promise<void>;
41
+ }
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Integrations API
3
+ */
4
+ export class IntegrationsAPI {
5
+ constructor(http) {
6
+ this.http = http;
7
+ }
8
+ /**
9
+ * List integrations with cursor-based pagination
10
+ */
11
+ async list(params) {
12
+ return this.http.request('post', '/integrations/list', { data: params });
13
+ }
14
+ /**
15
+ * Get available integrations
16
+ */
17
+ async listAvailable() {
18
+ return this.http.request('get', '/integrations/available');
19
+ }
20
+ /**
21
+ * Get integration configs
22
+ */
23
+ async getConfigs() {
24
+ return this.http.request('get', '/integrations/configs');
25
+ }
26
+ /**
27
+ * Get capabilities
28
+ */
29
+ async getCapabilities() {
30
+ return this.http.request('get', '/integrations/capabilities');
31
+ }
32
+ /**
33
+ * Check requirements
34
+ */
35
+ async checkRequirements(data) {
36
+ return this.http.request('post', '/integrations/check', { data });
37
+ }
38
+ /**
39
+ * Connect an integration
40
+ */
41
+ async connect(data) {
42
+ return this.http.request('post', '/integrations', { data });
43
+ }
44
+ /**
45
+ * Get an integration by provider key
46
+ */
47
+ async get(provider) {
48
+ return this.http.request('get', `/integrations/${provider}`);
49
+ }
50
+ /**
51
+ * Disconnect an integration
52
+ */
53
+ async disconnect(provider) {
54
+ return this.http.request('delete', `/integrations/${provider}`);
55
+ }
56
+ }
@@ -0,0 +1 @@
1
+ export {};