@inferencesh/sdk 0.6.10 → 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.
- package/README.md +1 -1
- package/dist/agent/actions.js +3 -3
- package/dist/agent/actions.test.js +6 -0
- package/dist/api/agents.d.ts +10 -0
- package/dist/api/agents.js +17 -3
- package/dist/api/agents.test.js +215 -0
- package/dist/api/api-keys.d.ts +24 -0
- package/dist/api/api-keys.js +26 -0
- package/dist/api/api-keys.test.d.ts +1 -0
- package/dist/api/api-keys.test.js +44 -0
- package/dist/api/apps.js +1 -1
- package/dist/api/apps.test.js +71 -3
- package/dist/api/chats.d.ts +14 -0
- package/dist/api/chats.js +18 -0
- package/dist/api/chats.test.js +52 -0
- package/dist/api/engines.d.ts +12 -0
- package/dist/api/engines.js +18 -0
- package/dist/api/engines.test.js +78 -0
- package/dist/api/files.test.js +183 -0
- package/dist/api/flow-runs.test.js +42 -0
- package/dist/api/flows.d.ts +4 -0
- package/dist/api/flows.js +6 -0
- package/dist/api/flows.test.js +75 -0
- package/dist/api/integrations.d.ts +41 -0
- package/dist/api/integrations.js +56 -0
- package/dist/api/integrations.test.d.ts +1 -0
- package/dist/api/integrations.test.js +109 -0
- package/dist/api/knowledge.d.ts +112 -0
- package/dist/api/knowledge.js +163 -0
- package/dist/api/knowledge.test.d.ts +1 -0
- package/dist/api/knowledge.test.js +94 -0
- package/dist/api/mcp-servers.d.ts +45 -0
- package/dist/api/mcp-servers.js +62 -0
- package/dist/api/mcp-servers.test.d.ts +1 -0
- package/dist/api/mcp-servers.test.js +64 -0
- package/dist/api/projects.d.ts +29 -0
- package/dist/api/projects.js +38 -0
- package/dist/api/projects.test.d.ts +1 -0
- package/dist/api/projects.test.js +52 -0
- package/dist/api/search.d.ts +21 -0
- package/dist/api/search.js +20 -0
- package/dist/api/search.test.d.ts +1 -0
- package/dist/api/search.test.js +39 -0
- package/dist/api/secrets.d.ts +29 -0
- package/dist/api/secrets.js +38 -0
- package/dist/api/secrets.test.d.ts +1 -0
- package/dist/api/secrets.test.js +61 -0
- package/dist/api/tasks.d.ts +13 -1
- package/dist/api/tasks.js +18 -0
- package/dist/api/tasks.test.js +134 -0
- package/dist/api/teams.d.ts +71 -0
- package/dist/api/teams.js +92 -0
- package/dist/api/teams.test.d.ts +1 -0
- package/dist/api/teams.test.js +79 -0
- package/dist/http/client.d.ts +5 -0
- package/dist/http/client.js +42 -23
- package/dist/http/client.test.js +245 -0
- package/dist/http/errors.test.js +13 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.js +25 -0
- package/dist/tool-builder.test.js +11 -1
- package/dist/types.d.ts +110 -5
- package/dist/types.js +58 -2
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
official javascript/typescript sdk for [inference.sh](https://inference.sh) — the ai agent runtime for serverless ai inference.
|
|
9
9
|
|
|
10
|
-
run ai models, build ai agents, and deploy generative ai applications with a simple api. access
|
|
10
|
+
run ai models, build ai agents, and deploy generative ai applications with a simple api. access models including flux, stable diffusion, llms (claude, gpt, gemini), video generation (veo, seedance), and more.
|
|
11
11
|
|
|
12
12
|
## Installation
|
|
13
13
|
|
package/dist/agent/actions.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Action creators that handle side effects (API calls, streaming).
|
|
5
5
|
* These are created once per provider instance with access to dispatch.
|
|
6
6
|
*/
|
|
7
|
-
import { ToolInvocationStatusAwaitingInput, ToolTypeClient, ChatStatusBusy, } from '../types';
|
|
7
|
+
import { ToolInvocationStatusAwaitingInput, ToolInvocationStatusInProgress, ToolTypeClient, ChatStatusBusy, } from '../types';
|
|
8
8
|
import { StreamableManager } from '../http/streamable';
|
|
9
9
|
import { PollManager } from '../http/poll';
|
|
10
10
|
import { isAdHocConfig, extractClientToolHandlers } from './types';
|
|
@@ -35,12 +35,12 @@ export function createActions(ctx) {
|
|
|
35
35
|
if (chatId && message.chat_id !== chatId && !message.chat_id.startsWith(chatId))
|
|
36
36
|
return;
|
|
37
37
|
dispatch({ type: 'UPDATE_MESSAGE', payload: message });
|
|
38
|
-
//
|
|
38
|
+
// Dispatch client tool handlers when ready (in_progress or awaiting_input for backwards compat)
|
|
39
39
|
const clientToolHandlers = getClientToolHandlers();
|
|
40
40
|
if (message.tool_invocations && chatId && clientToolHandlers.size > 0) {
|
|
41
41
|
for (const invocation of message.tool_invocations) {
|
|
42
42
|
if (invocation.type === ToolTypeClient &&
|
|
43
|
-
invocation.status === ToolInvocationStatusAwaitingInput) {
|
|
43
|
+
(invocation.status === ToolInvocationStatusInProgress || invocation.status === ToolInvocationStatusAwaitingInput)) {
|
|
44
44
|
// Skip if already dispatched
|
|
45
45
|
if (dispatchedToolInvocations.has(invocation.id)) {
|
|
46
46
|
continue;
|
|
@@ -391,6 +391,12 @@ describe('createActions', () => {
|
|
|
391
391
|
publicActions.stopGeneration();
|
|
392
392
|
expect(mockAgentApi.stopChat).toHaveBeenCalledWith(ctx.client, 'chat-short');
|
|
393
393
|
});
|
|
394
|
+
it('stopGeneration should no-op when there is no chatId', () => {
|
|
395
|
+
const { ctx } = createTestContext({ getChatId: () => null });
|
|
396
|
+
const { publicActions } = createActions(ctx);
|
|
397
|
+
publicActions.stopGeneration();
|
|
398
|
+
expect(mockAgentApi.stopChat).not.toHaveBeenCalled();
|
|
399
|
+
});
|
|
394
400
|
it('submitToolResult should set error state when API fails', async () => {
|
|
395
401
|
mockAgentApi.submitToolResult.mockRejectedValueOnce(new Error('submit failed'));
|
|
396
402
|
const onError = jest.fn();
|
package/dist/api/agents.d.ts
CHANGED
|
@@ -144,10 +144,20 @@ export declare class AgentsAPI {
|
|
|
144
144
|
* Get a specific agent version
|
|
145
145
|
*/
|
|
146
146
|
getVersion(agentId: string, versionId: string): Promise<AgentVersionDTO>;
|
|
147
|
+
/**
|
|
148
|
+
* Get an agent by namespace/name (e.g., "inference/my-agent")
|
|
149
|
+
*/
|
|
150
|
+
getByName(namespace: string, name: string): Promise<AgentDTO>;
|
|
147
151
|
/**
|
|
148
152
|
* Get internal tools for the agent
|
|
149
153
|
*/
|
|
150
154
|
getInternalTools(): Promise<InternalToolDefinition[]>;
|
|
155
|
+
/**
|
|
156
|
+
* Get A2A protocol agent card for an agent.
|
|
157
|
+
* Returns raw JSON (not wrapped in {data:...}) for direct upload to GCP Producer Portal.
|
|
158
|
+
* Spec: https://a2a-protocol.org/latest/specification/
|
|
159
|
+
*/
|
|
160
|
+
getA2ACard(agentId: string): Promise<Record<string, unknown>>;
|
|
151
161
|
/**
|
|
152
162
|
* Create an agent instance for chat interactions
|
|
153
163
|
*/
|
package/dist/api/agents.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { StreamableManager } from '../http/streamable';
|
|
2
2
|
import { PollManager } from '../http/poll';
|
|
3
|
-
import { ToolTypeClient, ToolInvocationStatusAwaitingInput, ChatStatusBusy, } from '../types';
|
|
3
|
+
import { ToolTypeClient, ToolInvocationStatusAwaitingInput, ToolInvocationStatusInProgress, ChatStatusBusy, } from '../types';
|
|
4
4
|
/**
|
|
5
5
|
* Agent for chat interactions
|
|
6
6
|
*
|
|
@@ -166,7 +166,7 @@ export class Agent {
|
|
|
166
166
|
for (const inv of message.tool_invocations) {
|
|
167
167
|
if (this.dispatchedToolCalls.has(inv.id))
|
|
168
168
|
continue;
|
|
169
|
-
if (inv.type === ToolTypeClient && inv.status === ToolInvocationStatusAwaitingInput) {
|
|
169
|
+
if (inv.type === ToolTypeClient && (inv.status === ToolInvocationStatusInProgress || inv.status === ToolInvocationStatusAwaitingInput)) {
|
|
170
170
|
this.dispatchedToolCalls.add(inv.id);
|
|
171
171
|
options.onToolCall({
|
|
172
172
|
id: inv.id,
|
|
@@ -222,7 +222,7 @@ export class Agent {
|
|
|
222
222
|
for (const inv of message.tool_invocations) {
|
|
223
223
|
if (this.dispatchedToolCalls.has(inv.id))
|
|
224
224
|
continue;
|
|
225
|
-
if (inv.type === ToolTypeClient && inv.status === ToolInvocationStatusAwaitingInput) {
|
|
225
|
+
if (inv.type === ToolTypeClient && (inv.status === ToolInvocationStatusInProgress || inv.status === ToolInvocationStatusAwaitingInput)) {
|
|
226
226
|
this.dispatchedToolCalls.add(inv.id);
|
|
227
227
|
options.onToolCall({
|
|
228
228
|
id: inv.id,
|
|
@@ -319,12 +319,26 @@ export class AgentsAPI {
|
|
|
319
319
|
async getVersion(agentId, versionId) {
|
|
320
320
|
return this.http.request('get', `/agents/${agentId}/versions/${versionId}`);
|
|
321
321
|
}
|
|
322
|
+
/**
|
|
323
|
+
* Get an agent by namespace/name (e.g., "inference/my-agent")
|
|
324
|
+
*/
|
|
325
|
+
async getByName(namespace, name) {
|
|
326
|
+
return this.http.request('get', `/agents/${namespace}/${name}`);
|
|
327
|
+
}
|
|
322
328
|
/**
|
|
323
329
|
* Get internal tools for the agent
|
|
324
330
|
*/
|
|
325
331
|
async getInternalTools() {
|
|
326
332
|
return this.http.request('get', '/agents/internal-tools');
|
|
327
333
|
}
|
|
334
|
+
/**
|
|
335
|
+
* Get A2A protocol agent card for an agent.
|
|
336
|
+
* Returns raw JSON (not wrapped in {data:...}) for direct upload to GCP Producer Portal.
|
|
337
|
+
* Spec: https://a2a-protocol.org/latest/specification/
|
|
338
|
+
*/
|
|
339
|
+
async getA2ACard(agentId) {
|
|
340
|
+
return this.http.request('get', `/agents/${agentId}/card`);
|
|
341
|
+
}
|
|
328
342
|
// ==========================================================================
|
|
329
343
|
// Agent Runtime (chat interactions)
|
|
330
344
|
// ==========================================================================
|
package/dist/api/agents.test.js
CHANGED
|
@@ -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';
|
|
@@ -281,6 +282,44 @@ describe('Agent.sendMessage (file attachments)', () => {
|
|
|
281
282
|
expect(mockFetch.mock.calls.filter(([url]) => String(url).includes('/files')).length).toBe(0);
|
|
282
283
|
});
|
|
283
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
|
+
});
|
|
284
323
|
describe('Agent lifecycle', () => {
|
|
285
324
|
beforeEach(() => {
|
|
286
325
|
jest.clearAllMocks();
|
|
@@ -317,6 +356,66 @@ describe('Agent lifecycle', () => {
|
|
|
317
356
|
await agentInstance.stopChat();
|
|
318
357
|
expect(mockFetch).toHaveBeenCalledWith(expect.stringContaining('/chats/chat-1/stop'), expect.anything());
|
|
319
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
|
+
});
|
|
320
419
|
it('reset should clear chat state so stopChat is a no-op', async () => {
|
|
321
420
|
const agentInstance = agent();
|
|
322
421
|
mockJsonResponse({
|
|
@@ -338,6 +437,29 @@ describe('Agent lifecycle', () => {
|
|
|
338
437
|
expect(mockFetch).not.toHaveBeenCalled();
|
|
339
438
|
});
|
|
340
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
|
+
});
|
|
341
463
|
describe('Agent.submitToolResult', () => {
|
|
342
464
|
beforeEach(() => {
|
|
343
465
|
jest.clearAllMocks();
|
|
@@ -356,6 +478,23 @@ describe('Agent.submitToolResult', () => {
|
|
|
356
478
|
expect(body.result).toBe(JSON.stringify(payload));
|
|
357
479
|
});
|
|
358
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
|
+
});
|
|
359
498
|
describe('AgentsAPI (template CRUD)', () => {
|
|
360
499
|
beforeEach(() => {
|
|
361
500
|
jest.clearAllMocks();
|
|
@@ -392,4 +531,80 @@ describe('AgentsAPI (template CRUD)', () => {
|
|
|
392
531
|
expect(init.method).toBe('POST');
|
|
393
532
|
expect(JSON.parse(init.body)).toEqual(payload);
|
|
394
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
|
+
});
|
|
395
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('
|
|
90
|
+
return this.http.request('put', `/apps/${appId}/versions/${versionId}/current`);
|
|
91
91
|
}
|
|
92
92
|
}
|
|
93
93
|
export function createAppsAPI(http) {
|
package/dist/api/apps.test.js
CHANGED
|
@@ -48,12 +48,13 @@ describe('AppsAPI', () => {
|
|
|
48
48
|
const [, init] = mockFetch.mock.calls[0];
|
|
49
49
|
expect(JSON.parse(init.body)).toEqual({ license: 'key-abc' });
|
|
50
50
|
});
|
|
51
|
-
it('should
|
|
51
|
+
it('should PUT /apps/{id}/versions/{versionId}/current for setCurrentVersion()', async () => {
|
|
52
52
|
const app = { id: 'app-1', current_version_id: 'ver-2' };
|
|
53
53
|
mockJsonResponse(app);
|
|
54
54
|
await api().setCurrentVersion('app-1', 'ver-2');
|
|
55
|
-
const [, init] = mockFetch.mock.calls[0];
|
|
56
|
-
expect(
|
|
55
|
+
const [url, init] = mockFetch.mock.calls[0];
|
|
56
|
+
expect(url).toContain('/apps/app-1/versions/ver-2/current');
|
|
57
|
+
expect(init.method).toBe('PUT');
|
|
57
58
|
});
|
|
58
59
|
it('should POST /apps/{id}/duplicate for duplicate()', async () => {
|
|
59
60
|
const app = { id: 'app-copy' };
|
|
@@ -64,4 +65,71 @@ describe('AppsAPI', () => {
|
|
|
64
65
|
expect(url).toContain('/apps/app-1/duplicate');
|
|
65
66
|
expect(init.method).toBe('POST');
|
|
66
67
|
});
|
|
68
|
+
it('should GET /apps/{id} for get()', async () => {
|
|
69
|
+
const app = { id: 'app-1', name: 'demo' };
|
|
70
|
+
mockJsonResponse(app);
|
|
71
|
+
const result = await api().get('app-1');
|
|
72
|
+
expect(result).toEqual(app);
|
|
73
|
+
const [url, init] = mockFetch.mock.calls[0];
|
|
74
|
+
expect(url).toContain('/apps/app-1');
|
|
75
|
+
expect(init.method).toBe('GET');
|
|
76
|
+
});
|
|
77
|
+
it('should GET versioned app for getByVersionId()', async () => {
|
|
78
|
+
const app = { id: 'app-1', version_id: 'ver-1' };
|
|
79
|
+
mockJsonResponse(app);
|
|
80
|
+
const result = await api().getByVersionId('app-1', 'ver-1');
|
|
81
|
+
expect(result).toEqual(app);
|
|
82
|
+
const [url] = mockFetch.mock.calls[0];
|
|
83
|
+
expect(url).toContain('/apps/app-1/versions/ver-1');
|
|
84
|
+
});
|
|
85
|
+
it('should POST /apps for create()', async () => {
|
|
86
|
+
const app = { id: 'app-new', name: 'New App' };
|
|
87
|
+
mockJsonResponse(app);
|
|
88
|
+
const result = await api().create({ name: 'New App' });
|
|
89
|
+
expect(result).toEqual(app);
|
|
90
|
+
const [url, init] = mockFetch.mock.calls[0];
|
|
91
|
+
expect(url).toContain('/apps');
|
|
92
|
+
expect(init.method).toBe('POST');
|
|
93
|
+
expect(JSON.parse(init.body)).toEqual({ name: 'New App' });
|
|
94
|
+
});
|
|
95
|
+
it('should POST /apps/{id} for update()', async () => {
|
|
96
|
+
const app = { id: 'app-1', description: 'updated' };
|
|
97
|
+
mockJsonResponse(app);
|
|
98
|
+
const result = await api().update('app-1', { description: 'updated' });
|
|
99
|
+
expect(result).toEqual(app);
|
|
100
|
+
const [url, init] = mockFetch.mock.calls[0];
|
|
101
|
+
expect(url).toContain('/apps/app-1');
|
|
102
|
+
expect(JSON.parse(init.body)).toEqual({ description: 'updated' });
|
|
103
|
+
});
|
|
104
|
+
it('should DELETE /apps/{id} for delete()', async () => {
|
|
105
|
+
mockJsonResponse(null);
|
|
106
|
+
await api().delete('app-1');
|
|
107
|
+
const [url, init] = mockFetch.mock.calls[0];
|
|
108
|
+
expect(url).toContain('/apps/app-1');
|
|
109
|
+
expect(init.method).toBe('DELETE');
|
|
110
|
+
});
|
|
111
|
+
it('should POST /apps/{id}/versions/list for listVersions()', async () => {
|
|
112
|
+
const versions = { items: [{ id: 'ver-1' }], next_cursor: null };
|
|
113
|
+
mockJsonResponse(versions);
|
|
114
|
+
const result = await api().listVersions('app-1', { limit: 20 });
|
|
115
|
+
expect(result).toEqual(versions);
|
|
116
|
+
const [url, init] = mockFetch.mock.calls[0];
|
|
117
|
+
expect(url).toContain('/apps/app-1/versions/list');
|
|
118
|
+
expect(JSON.parse(init.body)).toEqual({ limit: 20 });
|
|
119
|
+
});
|
|
120
|
+
it('should POST visibility for updateVisibility()', async () => {
|
|
121
|
+
const app = { id: 'app-1', visibility: 'private' };
|
|
122
|
+
mockJsonResponse(app);
|
|
123
|
+
await api().updateVisibility('app-1', 'private');
|
|
124
|
+
const [, init] = mockFetch.mock.calls[0];
|
|
125
|
+
expect(JSON.parse(init.body)).toEqual({ visibility: 'private' });
|
|
126
|
+
});
|
|
127
|
+
it('should GET /apps/{id}/license for getLicense()', async () => {
|
|
128
|
+
const license = { app_id: 'app-1', license: 'MIT' };
|
|
129
|
+
mockJsonResponse(license);
|
|
130
|
+
const result = await api().getLicense('app-1');
|
|
131
|
+
expect(result).toEqual(license);
|
|
132
|
+
const [url] = mockFetch.mock.calls[0];
|
|
133
|
+
expect(url).toContain('/apps/app-1/license');
|
|
134
|
+
});
|
|
67
135
|
});
|
package/dist/api/chats.d.ts
CHANGED
|
@@ -26,5 +26,19 @@ export declare class ChatsAPI {
|
|
|
26
26
|
* Get chat trace (for debugging/observability)
|
|
27
27
|
*/
|
|
28
28
|
getTrace(chatId: string): Promise<ChatTraceDTO>;
|
|
29
|
+
/**
|
|
30
|
+
* Get chat status
|
|
31
|
+
*/
|
|
32
|
+
getStatus(chatId: string): Promise<{
|
|
33
|
+
status: string;
|
|
34
|
+
}>;
|
|
35
|
+
/**
|
|
36
|
+
* Stop chat generation
|
|
37
|
+
*/
|
|
38
|
+
stop(chatId: string): Promise<void>;
|
|
39
|
+
/**
|
|
40
|
+
* Stream chat updates
|
|
41
|
+
*/
|
|
42
|
+
stream(chatId: string): Promise<import("eventsource").EventSource | null>;
|
|
29
43
|
}
|
|
30
44
|
export declare function createChatsAPI(http: HttpClient): ChatsAPI;
|
package/dist/api/chats.js
CHANGED
|
@@ -35,6 +35,24 @@ export class ChatsAPI {
|
|
|
35
35
|
async getTrace(chatId) {
|
|
36
36
|
return this.http.request('get', `/chats/${chatId}/trace`);
|
|
37
37
|
}
|
|
38
|
+
/**
|
|
39
|
+
* Get chat status
|
|
40
|
+
*/
|
|
41
|
+
async getStatus(chatId) {
|
|
42
|
+
return this.http.request('get', `/chats/${chatId}/status`);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Stop chat generation
|
|
46
|
+
*/
|
|
47
|
+
async stop(chatId) {
|
|
48
|
+
return this.http.request('post', `/chats/${chatId}/stop`);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Stream chat updates
|
|
52
|
+
*/
|
|
53
|
+
stream(chatId) {
|
|
54
|
+
return this.http.createEventSource(`/chats/${chatId}/stream`);
|
|
55
|
+
}
|
|
38
56
|
}
|
|
39
57
|
export function createChatsAPI(http) {
|
|
40
58
|
return new ChatsAPI(http);
|