@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
@@ -1,4 +1,5 @@
1
1
  import { HttpClient } from '../http/client';
2
+ import { StreamableManager } from '../http/streamable';
2
3
  import { ChatStatusBusy, ChatStatusIdle, ToolInvocationStatusAwaitingInput, ToolTypeClient, } from '../types';
3
4
  import { FilesAPI } from './files';
4
5
  import { AgentsAPI } from './agents';
@@ -53,18 +54,15 @@ describe('Agent.sendMessage (polling mode)', () => {
53
54
  const userMessage = makeMessage({ id: 'user-1', role: 'user' });
54
55
  const assistantMessage = makeMessage({ id: 'asst-1' });
55
56
  mockJsonResponse({
56
- success: true,
57
- data: { user_message: userMessage, assistant_message: assistantMessage },
57
+ user_message: userMessage, assistant_message: assistantMessage,
58
58
  });
59
- mockJsonResponse({ success: true, data: { status: ChatStatusBusy } });
59
+ mockJsonResponse({ status: ChatStatusBusy });
60
60
  mockJsonResponse({
61
- success: true,
62
- data: { id: 'chat-1', status: ChatStatusBusy, chat_messages: [] },
61
+ id: 'chat-1', status: ChatStatusBusy, chat_messages: [],
63
62
  });
64
- mockJsonResponse({ success: true, data: { status: ChatStatusIdle } });
63
+ mockJsonResponse({ status: ChatStatusIdle });
65
64
  mockJsonResponse({
66
- success: true,
67
- data: { id: 'chat-1', status: ChatStatusIdle, chat_messages: [] },
65
+ id: 'chat-1', status: ChatStatusIdle, chat_messages: [],
68
66
  });
69
67
  const onChat = jest.fn();
70
68
  const result = await agent().sendMessage('hello', { stream: false, onChat });
@@ -81,27 +79,20 @@ describe('Agent.sendMessage (polling mode)', () => {
81
79
  };
82
80
  const messageWithTool = makeMessage({ tool_invocations: [toolInvocation] });
83
81
  mockJsonResponse({
84
- success: true,
85
- data: {
86
- user_message: makeMessage({ id: 'user-1', role: 'user' }),
87
- assistant_message: makeMessage(),
88
- },
82
+ user_message: makeMessage({ id: 'user-1', role: 'user' }),
83
+ assistant_message: makeMessage(),
89
84
  });
90
- mockJsonResponse({ success: true, data: { status: ChatStatusBusy } });
85
+ mockJsonResponse({ status: ChatStatusBusy });
91
86
  mockJsonResponse({
92
- success: true,
93
- data: {
94
- id: 'chat-1',
95
- status: ChatStatusBusy,
96
- chat_messages: [messageWithTool],
97
- },
87
+ id: 'chat-1',
88
+ status: ChatStatusBusy,
89
+ chat_messages: [messageWithTool],
98
90
  });
99
91
  // Same status again — stub poll should not re-dispatch tool
100
- mockJsonResponse({ success: true, data: { status: ChatStatusBusy } });
101
- mockJsonResponse({ success: true, data: { status: ChatStatusIdle } });
92
+ mockJsonResponse({ status: ChatStatusBusy });
93
+ mockJsonResponse({ status: ChatStatusIdle });
102
94
  mockJsonResponse({
103
- success: true,
104
- data: { id: 'chat-1', status: ChatStatusIdle, chat_messages: [messageWithTool] },
95
+ id: 'chat-1', status: ChatStatusIdle, chat_messages: [messageWithTool],
105
96
  });
106
97
  const onMessage = jest.fn();
107
98
  const onToolCall = jest.fn();
@@ -117,22 +108,18 @@ describe('Agent.sendMessage (polling mode)', () => {
117
108
  const userMessage = makeMessage({ id: 'user-1', role: 'user' });
118
109
  const assistantMessage = makeMessage();
119
110
  mockJsonResponse({
120
- success: true,
121
- data: { user_message: userMessage, assistant_message: assistantMessage },
111
+ user_message: userMessage, assistant_message: assistantMessage,
122
112
  });
123
- mockJsonResponse({ success: true, data: { status: ChatStatusBusy } });
113
+ mockJsonResponse({ status: ChatStatusBusy });
124
114
  mockJsonResponse({
125
- success: true,
126
- data: { id: 'chat-1', status: ChatStatusBusy },
115
+ id: 'chat-1', status: ChatStatusBusy,
127
116
  });
128
- mockJsonResponse({ success: true, data: { status: ChatStatusIdle } });
117
+ mockJsonResponse({ status: ChatStatusIdle });
129
118
  mockJsonResponse({
130
- success: true,
131
- data: { id: 'chat-1', status: ChatStatusIdle },
119
+ id: 'chat-1', status: ChatStatusIdle,
132
120
  });
133
121
  mockJsonResponse({
134
- success: true,
135
- data: { id: 'chat-1', status: ChatStatusIdle, output: { answer: 42 } },
122
+ id: 'chat-1', status: ChatStatusIdle, output: { answer: 42 },
136
123
  });
137
124
  const output = await agent().run('compute');
138
125
  expect(output).toEqual({ answer: 42 });
@@ -155,8 +142,7 @@ describe('Agent.sendMessage (streaming mode)', () => {
155
142
  ok: true,
156
143
  status: 200,
157
144
  text: () => Promise.resolve(JSON.stringify({
158
- success: true,
159
- data: { user_message: userMessage, assistant_message: assistantMessage },
145
+ user_message: userMessage, assistant_message: assistantMessage,
160
146
  })),
161
147
  });
162
148
  }
@@ -184,11 +170,8 @@ describe('Agent.sendMessage (streaming mode)', () => {
184
170
  ok: true,
185
171
  status: 200,
186
172
  text: () => Promise.resolve(JSON.stringify({
187
- success: true,
188
- data: {
189
- user_message: makeMessage({ id: 'user-1', role: 'user' }),
190
- assistant_message: makeMessage(),
191
- },
173
+ user_message: makeMessage({ id: 'user-1', role: 'user' }),
174
+ assistant_message: makeMessage(),
192
175
  })),
193
176
  });
194
177
  }
@@ -214,21 +197,16 @@ describe('Agent.sendMessage (streaming mode)', () => {
214
197
  });
215
198
  const agentInstance = new AgentsAPI(http, new FilesAPI(http)).create('my-agent');
216
199
  mockJsonResponse({
217
- success: true,
218
- data: {
219
- user_message: makeMessage({ id: 'user-1', role: 'user' }),
220
- assistant_message: makeMessage(),
221
- },
200
+ user_message: makeMessage({ id: 'user-1', role: 'user' }),
201
+ assistant_message: makeMessage(),
222
202
  });
223
- mockJsonResponse({ success: true, data: { status: ChatStatusBusy } });
203
+ mockJsonResponse({ status: ChatStatusBusy });
224
204
  mockJsonResponse({
225
- success: true,
226
- data: { id: 'chat-1', status: ChatStatusBusy, chat_messages: [] },
205
+ id: 'chat-1', status: ChatStatusBusy, chat_messages: [],
227
206
  });
228
- mockJsonResponse({ success: true, data: { status: ChatStatusIdle } });
207
+ mockJsonResponse({ status: ChatStatusIdle });
229
208
  mockJsonResponse({
230
- success: true,
231
- data: { id: 'chat-1', status: ChatStatusIdle, chat_messages: [] },
209
+ id: 'chat-1', status: ChatStatusIdle, chat_messages: [],
232
210
  });
233
211
  await agentInstance.sendMessage('first', { stream: false });
234
212
  const callOrder = [];
@@ -239,11 +217,8 @@ describe('Agent.sendMessage (streaming mode)', () => {
239
217
  ok: true,
240
218
  status: 200,
241
219
  text: () => Promise.resolve(JSON.stringify({
242
- success: true,
243
- data: {
244
- user_message: makeMessage({ id: 'user-2', role: 'user' }),
245
- assistant_message: makeMessage({ id: 'asst-2' }),
246
- },
220
+ user_message: makeMessage({ id: 'user-2', role: 'user' }),
221
+ assistant_message: makeMessage({ id: 'asst-2' }),
247
222
  })),
248
223
  });
249
224
  }
@@ -285,21 +260,16 @@ describe('Agent.sendMessage (file attachments)', () => {
285
260
  content_type: 'application/pdf',
286
261
  };
287
262
  mockJsonResponse({
288
- success: true,
289
- data: {
290
- user_message: makeMessage({ id: 'user-1', role: 'user' }),
291
- assistant_message: makeMessage(),
292
- },
263
+ user_message: makeMessage({ id: 'user-1', role: 'user' }),
264
+ assistant_message: makeMessage(),
293
265
  });
294
- mockJsonResponse({ success: true, data: { status: ChatStatusBusy } });
266
+ mockJsonResponse({ status: ChatStatusBusy });
295
267
  mockJsonResponse({
296
- success: true,
297
- data: { id: 'chat-1', status: ChatStatusBusy, chat_messages: [] },
268
+ id: 'chat-1', status: ChatStatusBusy, chat_messages: [],
298
269
  });
299
- mockJsonResponse({ success: true, data: { status: ChatStatusIdle } });
270
+ mockJsonResponse({ status: ChatStatusIdle });
300
271
  mockJsonResponse({
301
- success: true,
302
- data: { id: 'chat-1', status: ChatStatusIdle, chat_messages: [] },
272
+ id: 'chat-1', status: ChatStatusIdle, chat_messages: [],
303
273
  });
304
274
  await agent().sendMessage('see attachments', {
305
275
  stream: false,
@@ -312,6 +282,44 @@ describe('Agent.sendMessage (file attachments)', () => {
312
282
  expect(mockFetch.mock.calls.filter(([url]) => String(url).includes('/files')).length).toBe(0);
313
283
  });
314
284
  });
285
+ describe('Agent.sendMessage (ad-hoc config)', () => {
286
+ beforeEach(() => {
287
+ jest.clearAllMocks();
288
+ });
289
+ const adHocAgent = () => {
290
+ const http = new HttpClient({
291
+ apiKey: 'test-key',
292
+ stream: false,
293
+ pollIntervalMs: 20,
294
+ });
295
+ return new AgentsAPI(http, new FilesAPI(http)).create({
296
+ core_app: { ref: 'openrouter/claude@latest' },
297
+ system_prompt: 'You are helpful',
298
+ name: 'adhoc-bot',
299
+ });
300
+ };
301
+ it('should POST agent_config and agent_name instead of agent template ref', async () => {
302
+ mockJsonResponse({
303
+ user_message: makeMessage({ id: 'user-1', role: 'user' }),
304
+ assistant_message: makeMessage(),
305
+ });
306
+ mockJsonResponse({ status: ChatStatusBusy });
307
+ mockJsonResponse({ id: 'chat-1', status: ChatStatusBusy, chat_messages: [] });
308
+ mockJsonResponse({ status: ChatStatusIdle });
309
+ mockJsonResponse({ id: 'chat-1', status: ChatStatusIdle, chat_messages: [] });
310
+ await adHocAgent().sendMessage('hello', { stream: false });
311
+ const runCall = mockFetch.mock.calls.find(([url]) => String(url).includes('/agents/run'));
312
+ const body = JSON.parse(String(runCall[1].body));
313
+ expect(body.agent).toBeUndefined();
314
+ expect(body.agent_config).toEqual({
315
+ core_app: { ref: 'openrouter/claude@latest' },
316
+ system_prompt: 'You are helpful',
317
+ name: 'adhoc-bot',
318
+ });
319
+ expect(body.agent_name).toBe('adhoc-bot');
320
+ expect(body.input.text).toBe('hello');
321
+ });
322
+ });
315
323
  describe('Agent lifecycle', () => {
316
324
  beforeEach(() => {
317
325
  jest.clearAllMocks();
@@ -331,46 +339,96 @@ describe('Agent lifecycle', () => {
331
339
  it('stopChat should POST to /chats/{id}/stop when a chat exists', async () => {
332
340
  const agentInstance = agent();
333
341
  mockJsonResponse({
334
- success: true,
335
- data: {
336
- user_message: makeMessage({ id: 'user-1', role: 'user' }),
337
- assistant_message: makeMessage(),
338
- },
342
+ user_message: makeMessage({ id: 'user-1', role: 'user' }),
343
+ assistant_message: makeMessage(),
339
344
  });
340
- mockJsonResponse({ success: true, data: { status: ChatStatusBusy } });
345
+ mockJsonResponse({ status: ChatStatusBusy });
341
346
  mockJsonResponse({
342
- success: true,
343
- data: { id: 'chat-1', status: ChatStatusBusy, chat_messages: [] },
347
+ id: 'chat-1', status: ChatStatusBusy, chat_messages: [],
344
348
  });
345
- mockJsonResponse({ success: true, data: { status: ChatStatusIdle } });
349
+ mockJsonResponse({ status: ChatStatusIdle });
346
350
  mockJsonResponse({
347
- success: true,
348
- data: { id: 'chat-1', status: ChatStatusIdle, chat_messages: [] },
351
+ id: 'chat-1', status: ChatStatusIdle, chat_messages: [],
349
352
  });
350
353
  await agentInstance.sendMessage('hello', { stream: false });
351
354
  jest.clearAllMocks();
352
- mockJsonResponse({ success: true, data: null });
355
+ mockJsonResponse(null);
353
356
  await agentInstance.stopChat();
354
357
  expect(mockFetch).toHaveBeenCalledWith(expect.stringContaining('/chats/chat-1/stop'), expect.anything());
355
358
  });
359
+ it('disconnect should stop active stream managers', async () => {
360
+ const http = new HttpClient({
361
+ apiKey: 'test-key',
362
+ stream: true,
363
+ pollIntervalMs: 20,
364
+ });
365
+ const agentInstance = new AgentsAPI(http, new FilesAPI(http)).create('my-agent');
366
+ const stopSpy = jest.spyOn(StreamableManager.prototype, 'stop');
367
+ mockFetch.mockImplementation((url) => {
368
+ if (url.includes('/agents/run')) {
369
+ return Promise.resolve({
370
+ ok: true,
371
+ status: 200,
372
+ text: () => Promise.resolve(JSON.stringify({
373
+ user_message: makeMessage({ id: 'user-1', role: 'user' }),
374
+ assistant_message: makeMessage(),
375
+ })),
376
+ });
377
+ }
378
+ return Promise.resolve(mockNdjsonStream([
379
+ `${JSON.stringify({ event: 'chats', data: { id: 'chat-1', status: ChatStatusIdle } })}\n`,
380
+ ]));
381
+ });
382
+ await agentInstance.sendMessage('hello', { onChat: jest.fn() });
383
+ agentInstance.disconnect();
384
+ expect(stopSpy).toHaveBeenCalled();
385
+ stopSpy.mockRestore();
386
+ });
387
+ it('startStreaming should no-op when there is no active chat', () => {
388
+ const agentInstance = agent();
389
+ agentInstance.startStreaming();
390
+ expect(mockFetch).not.toHaveBeenCalled();
391
+ });
392
+ it('startStreaming should open the chat stream when chatId exists', async () => {
393
+ const http = new HttpClient({
394
+ apiKey: 'test-key',
395
+ stream: true,
396
+ pollIntervalMs: 20,
397
+ });
398
+ const agentInstance = new AgentsAPI(http, new FilesAPI(http)).create('my-agent');
399
+ mockJsonResponse({
400
+ user_message: makeMessage({ id: 'user-1', role: 'user' }),
401
+ assistant_message: makeMessage(),
402
+ });
403
+ mockJsonResponse({ status: ChatStatusBusy });
404
+ mockJsonResponse({
405
+ id: 'chat-1', status: ChatStatusBusy, chat_messages: [],
406
+ });
407
+ mockJsonResponse({ status: ChatStatusIdle });
408
+ mockJsonResponse({
409
+ id: 'chat-1', status: ChatStatusIdle, chat_messages: [],
410
+ });
411
+ await agentInstance.sendMessage('hello', { stream: false });
412
+ jest.clearAllMocks();
413
+ mockFetch.mockResolvedValue(mockNdjsonStream([
414
+ `${JSON.stringify({ event: 'chats', data: { id: 'chat-1', status: ChatStatusIdle } })}\n`,
415
+ ]));
416
+ agentInstance.startStreaming({ onChat: jest.fn() });
417
+ expect(mockFetch).toHaveBeenCalledWith(expect.stringContaining('/chats/chat-1/stream'), expect.anything());
418
+ });
356
419
  it('reset should clear chat state so stopChat is a no-op', async () => {
357
420
  const agentInstance = agent();
358
421
  mockJsonResponse({
359
- success: true,
360
- data: {
361
- user_message: makeMessage({ id: 'user-1', role: 'user' }),
362
- assistant_message: makeMessage(),
363
- },
422
+ user_message: makeMessage({ id: 'user-1', role: 'user' }),
423
+ assistant_message: makeMessage(),
364
424
  });
365
- mockJsonResponse({ success: true, data: { status: ChatStatusBusy } });
425
+ mockJsonResponse({ status: ChatStatusBusy });
366
426
  mockJsonResponse({
367
- success: true,
368
- data: { id: 'chat-1', status: ChatStatusBusy, chat_messages: [] },
427
+ id: 'chat-1', status: ChatStatusBusy, chat_messages: [],
369
428
  });
370
- mockJsonResponse({ success: true, data: { status: ChatStatusIdle } });
429
+ mockJsonResponse({ status: ChatStatusIdle });
371
430
  mockJsonResponse({
372
- success: true,
373
- data: { id: 'chat-1', status: ChatStatusIdle, chat_messages: [] },
431
+ id: 'chat-1', status: ChatStatusIdle, chat_messages: [],
374
432
  });
375
433
  await agentInstance.sendMessage('hello', { stream: false });
376
434
  agentInstance.reset();
@@ -379,6 +437,29 @@ describe('Agent lifecycle', () => {
379
437
  expect(mockFetch).not.toHaveBeenCalled();
380
438
  });
381
439
  });
440
+ describe('Agent.getChat', () => {
441
+ beforeEach(() => {
442
+ jest.clearAllMocks();
443
+ });
444
+ const agent = () => {
445
+ const http = new HttpClient({ apiKey: 'test-key' });
446
+ return new AgentsAPI(http, new FilesAPI(http)).create('my-agent');
447
+ };
448
+ it('should return null without a chat id and no active chat', async () => {
449
+ const result = await agent().getChat();
450
+ expect(result).toBeNull();
451
+ expect(mockFetch).not.toHaveBeenCalled();
452
+ });
453
+ it('should GET /chats/{id} when chatId is provided', async () => {
454
+ const chat = { id: 'chat-42', status: 'idle', chat_messages: [] };
455
+ mockJsonResponse(chat);
456
+ const result = await agent().getChat('chat-42');
457
+ expect(result).toEqual(chat);
458
+ const [url, init] = mockFetch.mock.calls[0];
459
+ expect(url).toContain('/chats/chat-42');
460
+ expect(init.method).toBe('GET');
461
+ });
462
+ });
382
463
  describe('Agent.submitToolResult', () => {
383
464
  beforeEach(() => {
384
465
  jest.clearAllMocks();
@@ -386,7 +467,7 @@ describe('Agent.submitToolResult', () => {
386
467
  it('should JSON-stringify structured action results', async () => {
387
468
  const http = new HttpClient({ apiKey: 'test-key' });
388
469
  const agentInstance = new AgentsAPI(http, new FilesAPI(http)).create('my-agent');
389
- mockJsonResponse({ success: true, data: null });
470
+ mockJsonResponse(null);
390
471
  const payload = {
391
472
  action: { type: 'form_submit', payload: { field: 'value' } },
392
473
  form_data: { field: 'value' },
@@ -397,3 +478,133 @@ describe('Agent.submitToolResult', () => {
397
478
  expect(body.result).toBe(JSON.stringify(payload));
398
479
  });
399
480
  });
481
+ describe('AgentsAPI.submitToolResult', () => {
482
+ beforeEach(() => {
483
+ jest.clearAllMocks();
484
+ });
485
+ const api = () => {
486
+ const http = new HttpClient({ apiKey: 'test-key' });
487
+ return new AgentsAPI(http, new FilesAPI(http));
488
+ };
489
+ it('should POST plain string results to /tools/{invocationId}', async () => {
490
+ mockJsonResponse(null);
491
+ await api().submitToolResult('inv-plain', 'done');
492
+ const [url, init] = mockFetch.mock.calls[0];
493
+ expect(url).toContain('/tools/inv-plain');
494
+ expect(init.method).toBe('POST');
495
+ expect(JSON.parse(String(init.body))).toEqual({ result: 'done' });
496
+ });
497
+ });
498
+ describe('AgentsAPI (template CRUD)', () => {
499
+ beforeEach(() => {
500
+ jest.clearAllMocks();
501
+ });
502
+ const api = () => {
503
+ const http = new HttpClient({ apiKey: 'test-key' });
504
+ return new AgentsAPI(http, new FilesAPI(http));
505
+ };
506
+ it('should GET /agents/internal-tools for getInternalTools()', async () => {
507
+ const tools = [{ name: 'search', description: 'Search the web' }];
508
+ mockJsonResponse(tools);
509
+ const result = await api().getInternalTools();
510
+ expect(result).toEqual(tools);
511
+ const [url, init] = mockFetch.mock.calls[0];
512
+ expect(url).toContain('/agents/internal-tools');
513
+ expect(init.method).toBe('GET');
514
+ });
515
+ it('should POST team_id for transferOwnership()', async () => {
516
+ const agent = { id: 'agent-1' };
517
+ mockJsonResponse(agent);
518
+ await api().transferOwnership('agent-1', 'team-42');
519
+ const [url, init] = mockFetch.mock.calls[0];
520
+ expect(url).toContain('/agents/agent-1/transfer');
521
+ expect(JSON.parse(init.body)).toEqual({ team_id: 'team-42' });
522
+ });
523
+ it('should POST /agents for createAgent()', async () => {
524
+ const payload = { name: 'support-bot', core_app: { ref: 'app/ref' } };
525
+ const created = { id: 'agent-new', ...payload };
526
+ mockJsonResponse(created);
527
+ const result = await api().createAgent(payload);
528
+ expect(result).toEqual(created);
529
+ const [url, init] = mockFetch.mock.calls[0];
530
+ expect(url).toContain('/agents');
531
+ expect(init.method).toBe('POST');
532
+ expect(JSON.parse(init.body)).toEqual(payload);
533
+ });
534
+ it('should GET /agents/{namespace}/{name} for getByName()', async () => {
535
+ const agent = { id: 'agent-1', name: 'my-agent' };
536
+ mockJsonResponse(agent);
537
+ const result = await api().getByName('inference', 'my-agent');
538
+ expect(result).toEqual(agent);
539
+ const [url, init] = mockFetch.mock.calls[0];
540
+ expect(url).toContain('/agents/inference/my-agent');
541
+ expect(init.method).toBe('GET');
542
+ });
543
+ it('should POST /agents/list for list()', async () => {
544
+ const page = { items: [{ id: 'agent-1' }], next_cursor: null };
545
+ mockJsonResponse(page);
546
+ const result = await api().list({ limit: 10 });
547
+ expect(result).toEqual(page);
548
+ const [url, init] = mockFetch.mock.calls[0];
549
+ expect(url).toContain('/agents/list');
550
+ expect(init.method).toBe('POST');
551
+ });
552
+ it('should GET /agents/{id} for get()', async () => {
553
+ const agent = { id: 'agent-1', name: 'support-bot' };
554
+ mockJsonResponse(agent);
555
+ const result = await api().get('agent-1');
556
+ expect(result).toEqual(agent);
557
+ const [url, init] = mockFetch.mock.calls[0];
558
+ expect(url).toContain('/agents/agent-1');
559
+ expect(init.method).toBe('GET');
560
+ });
561
+ it('should POST /agents/{id} for update()', async () => {
562
+ const agent = { id: 'agent-1', name: 'updated' };
563
+ mockJsonResponse(agent);
564
+ const result = await api().update('agent-1', { name: 'updated' });
565
+ expect(result).toEqual(agent);
566
+ const [, init] = mockFetch.mock.calls[0];
567
+ expect(JSON.parse(init.body)).toEqual({ name: 'updated' });
568
+ });
569
+ it('should DELETE /agents/{id} for delete()', async () => {
570
+ mockJsonResponse(null);
571
+ await api().delete('agent-1');
572
+ const [url, init] = mockFetch.mock.calls[0];
573
+ expect(url).toContain('/agents/agent-1');
574
+ expect(init.method).toBe('DELETE');
575
+ });
576
+ it('should POST /agents/{id}/duplicate for duplicate()', async () => {
577
+ const agent = { id: 'agent-copy' };
578
+ mockJsonResponse(agent);
579
+ const result = await api().duplicate('agent-1');
580
+ expect(result).toEqual(agent);
581
+ const [url, init] = mockFetch.mock.calls[0];
582
+ expect(url).toContain('/agents/agent-1/duplicate');
583
+ expect(init.method).toBe('POST');
584
+ });
585
+ it('should POST /agents/{id}/versions/list for listVersions()', async () => {
586
+ const page = { items: [{ id: 'ver-1' }], next_cursor: null };
587
+ mockJsonResponse(page);
588
+ const result = await api().listVersions('agent-1', { limit: 5 });
589
+ expect(result).toEqual(page);
590
+ const [url, init] = mockFetch.mock.calls[0];
591
+ expect(url).toContain('/agents/agent-1/versions/list');
592
+ expect(JSON.parse(init.body)).toEqual({ limit: 5 });
593
+ });
594
+ it('should GET /agents/{id}/versions/{versionId} for getVersion()', async () => {
595
+ const version = { id: 'ver-1', agent_id: 'agent-1' };
596
+ mockJsonResponse(version);
597
+ const result = await api().getVersion('agent-1', 'ver-1');
598
+ expect(result).toEqual(version);
599
+ const [url, init] = mockFetch.mock.calls[0];
600
+ expect(url).toContain('/agents/agent-1/versions/ver-1');
601
+ expect(init.method).toBe('GET');
602
+ });
603
+ it('should POST visibility for updateVisibility()', async () => {
604
+ const agent = { id: 'agent-1', visibility: 'team' };
605
+ mockJsonResponse(agent);
606
+ await api().updateVisibility('agent-1', 'team');
607
+ const [, init] = mockFetch.mock.calls[0];
608
+ expect(JSON.parse(init.body)).toEqual({ visibility: 'team' });
609
+ });
610
+ });
@@ -0,0 +1,24 @@
1
+ import { HttpClient } from '../http/client';
2
+ import { ApiKeyDTO, CursorListRequest, CursorListResponse } from '../types';
3
+ /**
4
+ * API Keys API
5
+ */
6
+ export declare class ApiKeysAPI {
7
+ private readonly http;
8
+ constructor(http: HttpClient);
9
+ /**
10
+ * List API keys with cursor-based pagination
11
+ */
12
+ list(params?: Partial<CursorListRequest>): Promise<CursorListResponse<ApiKeyDTO>>;
13
+ /**
14
+ * Create an API key
15
+ */
16
+ create(data: {
17
+ name: string;
18
+ scopes?: string[];
19
+ }): Promise<ApiKeyDTO>;
20
+ /**
21
+ * Delete an API key
22
+ */
23
+ delete(id: string): Promise<void>;
24
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * API Keys API
3
+ */
4
+ export class ApiKeysAPI {
5
+ constructor(http) {
6
+ this.http = http;
7
+ }
8
+ /**
9
+ * List API keys with cursor-based pagination
10
+ */
11
+ async list(params) {
12
+ return this.http.request('post', '/apikeys/list', { data: params });
13
+ }
14
+ /**
15
+ * Create an API key
16
+ */
17
+ async create(data) {
18
+ return this.http.request('post', '/apikeys', { data });
19
+ }
20
+ /**
21
+ * Delete an API key
22
+ */
23
+ async delete(id) {
24
+ return this.http.request('delete', `/apikeys/${id}`);
25
+ }
26
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,44 @@
1
+ import { HttpClient } from '../http/client';
2
+ import { ApiKeysAPI } from './api-keys';
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('ApiKeysAPI', () => {
13
+ beforeEach(() => {
14
+ jest.clearAllMocks();
15
+ });
16
+ const api = () => new ApiKeysAPI(new HttpClient({ apiKey: 'test-key' }));
17
+ it('should POST /apikeys/list for list()', async () => {
18
+ const page = { items: [{ id: 'key-1', name: 'CI' }], next_cursor: null };
19
+ mockJsonResponse(page);
20
+ const result = await api().list();
21
+ expect(result).toEqual(page);
22
+ const [url, init] = mockFetch.mock.calls[0];
23
+ expect(url).toContain('/apikeys/list');
24
+ expect(init.method).toBe('POST');
25
+ });
26
+ it('should POST /apikeys for create()', async () => {
27
+ const payload = { name: 'deploy-bot', scopes: ['tasks:read'] };
28
+ const key = { id: 'key-new', ...payload, key: 'inf_sk_abc' };
29
+ mockJsonResponse(key);
30
+ const result = await api().create(payload);
31
+ expect(result).toEqual(key);
32
+ const [url, init] = mockFetch.mock.calls[0];
33
+ expect(url).toContain('/apikeys');
34
+ expect(init.method).toBe('POST');
35
+ expect(JSON.parse(init.body)).toEqual(payload);
36
+ });
37
+ it('should DELETE /apikeys/{id} for delete()', async () => {
38
+ mockJsonResponse(null);
39
+ await api().delete('key-9');
40
+ const [url, init] = mockFetch.mock.calls[0];
41
+ expect(url).toContain('/apikeys/key-9');
42
+ expect(init.method).toBe('DELETE');
43
+ });
44
+ });
package/dist/api/apps.js CHANGED
@@ -87,7 +87,7 @@ export class AppsAPI {
87
87
  * Set the current (active) version of an app
88
88
  */
89
89
  async setCurrentVersion(appId, versionId) {
90
- return this.http.request('post', `/apps/${appId}/current-version`, { data: { version_id: versionId } });
90
+ return this.http.request('put', `/apps/${appId}/versions/${versionId}/current`);
91
91
  }
92
92
  }
93
93
  export function createAppsAPI(http) {
@@ -0,0 +1 @@
1
+ export {};