@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
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Projects API
|
|
3
|
+
*/
|
|
4
|
+
export class ProjectsAPI {
|
|
5
|
+
constructor(http) {
|
|
6
|
+
this.http = http;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* List projects with cursor-based pagination
|
|
10
|
+
*/
|
|
11
|
+
async list(params) {
|
|
12
|
+
return this.http.request('post', '/projects/list', { data: params });
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Get a project by ID
|
|
16
|
+
*/
|
|
17
|
+
async get(id) {
|
|
18
|
+
return this.http.request('get', `/projects/${id}`);
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Create a project
|
|
22
|
+
*/
|
|
23
|
+
async create(data) {
|
|
24
|
+
return this.http.request('post', '/projects', { data });
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Update a project
|
|
28
|
+
*/
|
|
29
|
+
async update(id, data) {
|
|
30
|
+
return this.http.request('post', `/projects/${id}`, { data });
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Delete a project
|
|
34
|
+
*/
|
|
35
|
+
async delete(id) {
|
|
36
|
+
return this.http.request('delete', `/projects/${id}`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { HttpClient } from '../http/client';
|
|
2
|
+
import { ProjectsAPI } from './projects';
|
|
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('ProjectsAPI', () => {
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
jest.clearAllMocks();
|
|
15
|
+
});
|
|
16
|
+
const api = () => new ProjectsAPI(new HttpClient({ apiKey: 'test-key' }));
|
|
17
|
+
it('should POST /projects/list for list()', async () => {
|
|
18
|
+
const page = { items: [{ id: 'proj-1' }], next_cursor: null };
|
|
19
|
+
mockJsonResponse(page);
|
|
20
|
+
const result = await api().list({ limit: 20 });
|
|
21
|
+
expect(result).toEqual(page);
|
|
22
|
+
const [url, init] = mockFetch.mock.calls[0];
|
|
23
|
+
expect(url).toContain('/projects/list');
|
|
24
|
+
expect(init.method).toBe('POST');
|
|
25
|
+
});
|
|
26
|
+
it('should GET /projects/{id} for get()', async () => {
|
|
27
|
+
const project = { id: 'proj-1', name: 'Demo' };
|
|
28
|
+
mockJsonResponse(project);
|
|
29
|
+
const result = await api().get('proj-1');
|
|
30
|
+
expect(result).toEqual(project);
|
|
31
|
+
const [url, init] = mockFetch.mock.calls[0];
|
|
32
|
+
expect(url).toContain('/projects/proj-1');
|
|
33
|
+
expect(init.method).toBe('GET');
|
|
34
|
+
});
|
|
35
|
+
it('should POST /projects for create()', async () => {
|
|
36
|
+
const payload = { name: 'New Project' };
|
|
37
|
+
const project = { id: 'proj-new', ...payload };
|
|
38
|
+
mockJsonResponse(project);
|
|
39
|
+
const result = await api().create(payload);
|
|
40
|
+
expect(result).toEqual(project);
|
|
41
|
+
const [url, init] = mockFetch.mock.calls[0];
|
|
42
|
+
expect(url).toContain('/projects');
|
|
43
|
+
expect(init.method).toBe('POST');
|
|
44
|
+
});
|
|
45
|
+
it('should DELETE /projects/{id} for delete()', async () => {
|
|
46
|
+
mockJsonResponse(null);
|
|
47
|
+
await api().delete('proj-1');
|
|
48
|
+
const [url, init] = mockFetch.mock.calls[0];
|
|
49
|
+
expect(url).toContain('/projects/proj-1');
|
|
50
|
+
expect(init.method).toBe('DELETE');
|
|
51
|
+
});
|
|
52
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { HttpClient } from '../http/client';
|
|
2
|
+
import { SuggestRequest, SuggestResponse } from '../types';
|
|
3
|
+
/**
|
|
4
|
+
* Search API
|
|
5
|
+
*/
|
|
6
|
+
export declare class SearchAPI {
|
|
7
|
+
private readonly http;
|
|
8
|
+
constructor(http: HttpClient);
|
|
9
|
+
/**
|
|
10
|
+
* Unified search across skills, knowledge, and apps
|
|
11
|
+
*/
|
|
12
|
+
suggest(params: Partial<SuggestRequest>): Promise<SuggestResponse>;
|
|
13
|
+
/**
|
|
14
|
+
* Full-text search via Meilisearch
|
|
15
|
+
*/
|
|
16
|
+
search(params: {
|
|
17
|
+
q: string;
|
|
18
|
+
type?: string;
|
|
19
|
+
limit?: number;
|
|
20
|
+
}): Promise<unknown>;
|
|
21
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Search API
|
|
3
|
+
*/
|
|
4
|
+
export class SearchAPI {
|
|
5
|
+
constructor(http) {
|
|
6
|
+
this.http = http;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Unified search across skills, knowledge, and apps
|
|
10
|
+
*/
|
|
11
|
+
async suggest(params) {
|
|
12
|
+
return this.http.request('post', '/suggest', { data: params });
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Full-text search via Meilisearch
|
|
16
|
+
*/
|
|
17
|
+
async search(params) {
|
|
18
|
+
return this.http.request('post', '/search', { data: params });
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { HttpClient } from '../http/client';
|
|
2
|
+
import { SearchAPI } from './search';
|
|
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('SearchAPI', () => {
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
jest.clearAllMocks();
|
|
15
|
+
});
|
|
16
|
+
const api = () => new SearchAPI(new HttpClient({ apiKey: 'test-key' }));
|
|
17
|
+
it('should POST /suggest for suggest()', async () => {
|
|
18
|
+
const payload = { query: 'image gen', types: ['apps', 'skills'] };
|
|
19
|
+
const response = { results: [{ type: 'app', id: 'app-1' }] };
|
|
20
|
+
mockJsonResponse(response);
|
|
21
|
+
const result = await api().suggest(payload);
|
|
22
|
+
expect(result).toEqual(response);
|
|
23
|
+
const [url, init] = mockFetch.mock.calls[0];
|
|
24
|
+
expect(url).toContain('/suggest');
|
|
25
|
+
expect(init.method).toBe('POST');
|
|
26
|
+
expect(JSON.parse(init.body)).toEqual(payload);
|
|
27
|
+
});
|
|
28
|
+
it('should POST /search for search()', async () => {
|
|
29
|
+
const payload = { q: 'claude', type: 'apps', limit: 5 };
|
|
30
|
+
const response = { hits: [{ id: 'app-1' }] };
|
|
31
|
+
mockJsonResponse(response);
|
|
32
|
+
const result = await api().search(payload);
|
|
33
|
+
expect(result).toEqual(response);
|
|
34
|
+
const [url, init] = mockFetch.mock.calls[0];
|
|
35
|
+
expect(url).toContain('/search');
|
|
36
|
+
expect(init.method).toBe('POST');
|
|
37
|
+
expect(JSON.parse(init.body)).toEqual(payload);
|
|
38
|
+
});
|
|
39
|
+
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { HttpClient } from '../http/client';
|
|
2
|
+
import { SecretDTO, SecretCreateRequest, CursorListRequest, CursorListResponse } from '../types';
|
|
3
|
+
/**
|
|
4
|
+
* Secrets API
|
|
5
|
+
*/
|
|
6
|
+
export declare class SecretsAPI {
|
|
7
|
+
private readonly http;
|
|
8
|
+
constructor(http: HttpClient);
|
|
9
|
+
/**
|
|
10
|
+
* List secrets with cursor-based pagination
|
|
11
|
+
*/
|
|
12
|
+
list(params?: Partial<CursorListRequest>): Promise<CursorListResponse<SecretDTO>>;
|
|
13
|
+
/**
|
|
14
|
+
* Create a secret
|
|
15
|
+
*/
|
|
16
|
+
create(data: SecretCreateRequest): Promise<SecretDTO>;
|
|
17
|
+
/**
|
|
18
|
+
* Update a secret
|
|
19
|
+
*/
|
|
20
|
+
update(key: string, data: Partial<SecretCreateRequest>): Promise<SecretDTO>;
|
|
21
|
+
/**
|
|
22
|
+
* Delete a secret
|
|
23
|
+
*/
|
|
24
|
+
delete(key: string): Promise<void>;
|
|
25
|
+
/**
|
|
26
|
+
* Reveal a secret value
|
|
27
|
+
*/
|
|
28
|
+
reveal(key: string): Promise<SecretDTO>;
|
|
29
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Secrets API
|
|
3
|
+
*/
|
|
4
|
+
export class SecretsAPI {
|
|
5
|
+
constructor(http) {
|
|
6
|
+
this.http = http;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* List secrets with cursor-based pagination
|
|
10
|
+
*/
|
|
11
|
+
async list(params) {
|
|
12
|
+
return this.http.request('post', '/secrets/list', { data: params });
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Create a secret
|
|
16
|
+
*/
|
|
17
|
+
async create(data) {
|
|
18
|
+
return this.http.request('post', '/secrets', { data });
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Update a secret
|
|
22
|
+
*/
|
|
23
|
+
async update(key, data) {
|
|
24
|
+
return this.http.request('put', `/secrets/${key}`, { data });
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Delete a secret
|
|
28
|
+
*/
|
|
29
|
+
async delete(key) {
|
|
30
|
+
return this.http.request('delete', `/secrets/${key}`);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Reveal a secret value
|
|
34
|
+
*/
|
|
35
|
+
async reveal(key) {
|
|
36
|
+
return this.http.request('get', `/secrets/reveal/${key}`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { HttpClient } from '../http/client';
|
|
2
|
+
import { SecretsAPI } from './secrets';
|
|
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('SecretsAPI', () => {
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
jest.clearAllMocks();
|
|
15
|
+
});
|
|
16
|
+
const api = () => new SecretsAPI(new HttpClient({ apiKey: 'test-key' }));
|
|
17
|
+
it('should POST /secrets/list for list()', async () => {
|
|
18
|
+
const page = { items: [{ key: 'API_KEY' }], 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('/secrets/list');
|
|
24
|
+
expect(init.method).toBe('POST');
|
|
25
|
+
});
|
|
26
|
+
it('should POST /secrets for create()', async () => {
|
|
27
|
+
const payload = { key: 'DB_PASSWORD', value: 'secret' };
|
|
28
|
+
const secret = { key: 'DB_PASSWORD' };
|
|
29
|
+
mockJsonResponse(secret);
|
|
30
|
+
const result = await api().create(payload);
|
|
31
|
+
expect(result).toEqual(secret);
|
|
32
|
+
const [url, init] = mockFetch.mock.calls[0];
|
|
33
|
+
expect(url).toContain('/secrets');
|
|
34
|
+
expect(init.method).toBe('POST');
|
|
35
|
+
expect(JSON.parse(init.body)).toEqual(payload);
|
|
36
|
+
});
|
|
37
|
+
it('should PUT /secrets/{key} for update()', async () => {
|
|
38
|
+
const secret = { key: 'DB_PASSWORD' };
|
|
39
|
+
mockJsonResponse(secret);
|
|
40
|
+
await api().update('DB_PASSWORD', { value: 'new-secret' });
|
|
41
|
+
const [url, init] = mockFetch.mock.calls[0];
|
|
42
|
+
expect(url).toContain('/secrets/DB_PASSWORD');
|
|
43
|
+
expect(init.method).toBe('PUT');
|
|
44
|
+
});
|
|
45
|
+
it('should GET /secrets/reveal/{key} for reveal()', async () => {
|
|
46
|
+
const secret = { key: 'API_KEY', value: 'sk-live-abc' };
|
|
47
|
+
mockJsonResponse(secret);
|
|
48
|
+
const result = await api().reveal('API_KEY');
|
|
49
|
+
expect(result).toEqual(secret);
|
|
50
|
+
const [url, init] = mockFetch.mock.calls[0];
|
|
51
|
+
expect(url).toContain('/secrets/reveal/API_KEY');
|
|
52
|
+
expect(init.method).toBe('GET');
|
|
53
|
+
});
|
|
54
|
+
it('should DELETE /secrets/{key} for delete()', async () => {
|
|
55
|
+
mockJsonResponse(null);
|
|
56
|
+
await api().delete('OLD_KEY');
|
|
57
|
+
const [url, init] = mockFetch.mock.calls[0];
|
|
58
|
+
expect(url).toContain('/secrets/OLD_KEY');
|
|
59
|
+
expect(init.method).toBe('DELETE');
|
|
60
|
+
});
|
|
61
|
+
});
|
package/dist/api/tasks.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { HttpClient } from '../http/client';
|
|
2
|
-
import { TaskDTO as Task, ApiAppRunRequest, CursorListRequest, CursorListResponse } from '../types';
|
|
2
|
+
import { TaskDTO as Task, TaskLogsDTO, TaskTimingsDTO, ApiAppRunRequest, CursorListRequest, CursorListResponse } from '../types';
|
|
3
3
|
export interface RunOptions {
|
|
4
4
|
/** Callback for real-time status updates */
|
|
5
5
|
onUpdate?: (update: Task) => void;
|
|
@@ -62,5 +62,17 @@ export declare class TasksAPI {
|
|
|
62
62
|
* Feature/unfeature a task
|
|
63
63
|
*/
|
|
64
64
|
feature(taskId: string, featured: boolean): Promise<Task>;
|
|
65
|
+
/**
|
|
66
|
+
* Get task logs
|
|
67
|
+
*/
|
|
68
|
+
getLogs(taskId: string): Promise<TaskLogsDTO>;
|
|
69
|
+
/**
|
|
70
|
+
* Get task timings
|
|
71
|
+
*/
|
|
72
|
+
getTimings(taskId: string): Promise<TaskTimingsDTO>;
|
|
73
|
+
/**
|
|
74
|
+
* Get task telemetry
|
|
75
|
+
*/
|
|
76
|
+
getTelemetry(taskId: string): Promise<Record<string, unknown>[]>;
|
|
65
77
|
}
|
|
66
78
|
export declare function createTasksAPI(http: HttpClient): TasksAPI;
|
package/dist/api/tasks.js
CHANGED
|
@@ -193,6 +193,24 @@ export class TasksAPI {
|
|
|
193
193
|
async feature(taskId, featured) {
|
|
194
194
|
return this.http.request('post', `/tasks/${taskId}/featured`, { data: { is_featured: featured } });
|
|
195
195
|
}
|
|
196
|
+
/**
|
|
197
|
+
* Get task logs
|
|
198
|
+
*/
|
|
199
|
+
async getLogs(taskId) {
|
|
200
|
+
return this.http.request('get', `/tasks/${taskId}/logs`);
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Get task timings
|
|
204
|
+
*/
|
|
205
|
+
async getTimings(taskId) {
|
|
206
|
+
return this.http.request('get', `/tasks/${taskId}/timings`);
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Get task telemetry
|
|
210
|
+
*/
|
|
211
|
+
async getTelemetry(taskId) {
|
|
212
|
+
return this.http.request('get', `/tasks/${taskId}/telemetry`);
|
|
213
|
+
}
|
|
196
214
|
}
|
|
197
215
|
export function createTasksAPI(http) {
|
|
198
216
|
return new TasksAPI(http);
|
package/dist/api/tasks.test.js
CHANGED
|
@@ -76,6 +76,20 @@ describe('TasksAPI.run (polling mode)', () => {
|
|
|
76
76
|
mockFetch.mockRejectedValueOnce(new Error('status endpoint down'));
|
|
77
77
|
await expect(api().run({ app: 'test-app', input: {} }, {}, { wait: true, stream: false })).rejects.toThrow('status endpoint down');
|
|
78
78
|
});
|
|
79
|
+
it('should not refetch full task when poll status is unchanged', async () => {
|
|
80
|
+
const runningTask = makeTask();
|
|
81
|
+
const completedTask = makeTask({ status: TaskStatusCompleted, output: { ok: true } });
|
|
82
|
+
mockJsonResponse(runningTask);
|
|
83
|
+
mockJsonResponse({ status: TaskStatusRunning });
|
|
84
|
+
mockJsonResponse({ status: TaskStatusRunning });
|
|
85
|
+
mockJsonResponse({ status: TaskStatusCompleted });
|
|
86
|
+
mockJsonResponse(completedTask);
|
|
87
|
+
await api().run({ app: 'test-app', input: {} }, {}, { wait: true, stream: false });
|
|
88
|
+
const fullTaskGets = mockFetch.mock.calls.filter(([url, init]) => String(url).includes('/tasks/task-1') &&
|
|
89
|
+
!String(url).includes('/status') &&
|
|
90
|
+
init.method === 'GET');
|
|
91
|
+
expect(fullTaskGets).toHaveLength(1);
|
|
92
|
+
});
|
|
79
93
|
it('should parse string terminal statuses from the status endpoint', async () => {
|
|
80
94
|
const runningTask = makeTask();
|
|
81
95
|
const completedTask = makeTask({ status: TaskStatusCompleted });
|
|
@@ -187,3 +201,123 @@ describe('TasksAPI.run (streaming mode)', () => {
|
|
|
187
201
|
expect(onPartialUpdate).toHaveBeenCalledWith(expect.objectContaining({ id: 'task-1', status: TaskStatusCompleted }), ['status']);
|
|
188
202
|
});
|
|
189
203
|
});
|
|
204
|
+
describe('TasksAPI.create', () => {
|
|
205
|
+
beforeEach(() => {
|
|
206
|
+
jest.clearAllMocks();
|
|
207
|
+
});
|
|
208
|
+
const api = () => new TasksAPI(new HttpClient({ apiKey: 'test-key' }));
|
|
209
|
+
it('should POST /apps/run for create()', async () => {
|
|
210
|
+
const task = makeTask();
|
|
211
|
+
mockJsonResponse(task);
|
|
212
|
+
const result = await api().create({ app: 'test-app', input: { prompt: 'hi' } });
|
|
213
|
+
expect(result).toEqual(task);
|
|
214
|
+
const [url, init] = mockFetch.mock.calls[0];
|
|
215
|
+
expect(url).toContain('/apps/run');
|
|
216
|
+
expect(init.method).toBe('POST');
|
|
217
|
+
expect(JSON.parse(init.body)).toEqual({
|
|
218
|
+
app: 'test-app',
|
|
219
|
+
input: { prompt: 'hi' },
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
describe('TasksAPI (CRUD and admin)', () => {
|
|
224
|
+
beforeEach(() => {
|
|
225
|
+
jest.clearAllMocks();
|
|
226
|
+
});
|
|
227
|
+
const api = () => new TasksAPI(new HttpClient({ apiKey: 'test-key' }));
|
|
228
|
+
it('should POST /tasks/list for list()', async () => {
|
|
229
|
+
const page = { items: [{ id: 'task-1' }], next_cursor: null };
|
|
230
|
+
mockJsonResponse(page);
|
|
231
|
+
const result = await api().list({ limit: 5 });
|
|
232
|
+
expect(result).toEqual(page);
|
|
233
|
+
const [url, init] = mockFetch.mock.calls[0];
|
|
234
|
+
expect(url).toContain('/tasks/list');
|
|
235
|
+
expect(init.method).toBe('POST');
|
|
236
|
+
expect(JSON.parse(init.body)).toEqual({ limit: 5 });
|
|
237
|
+
});
|
|
238
|
+
it('should GET /tasks/featured for listFeatured()', async () => {
|
|
239
|
+
const page = { items: [{ id: 'task-f' }], next_cursor: null };
|
|
240
|
+
mockJsonResponse(page);
|
|
241
|
+
const result = await api().listFeatured({ cursor: 'abc' });
|
|
242
|
+
expect(result).toEqual(page);
|
|
243
|
+
const [url, init] = mockFetch.mock.calls[0];
|
|
244
|
+
expect(url).toContain('/tasks/featured');
|
|
245
|
+
expect(init.method).toBe('GET');
|
|
246
|
+
});
|
|
247
|
+
it('should GET /tasks/{id} for get()', async () => {
|
|
248
|
+
const task = makeTask();
|
|
249
|
+
mockJsonResponse(task);
|
|
250
|
+
const result = await api().get('task-1');
|
|
251
|
+
expect(result).toEqual(task);
|
|
252
|
+
const [url, init] = mockFetch.mock.calls[0];
|
|
253
|
+
expect(url).toContain('/tasks/task-1');
|
|
254
|
+
expect(init.method).toBe('GET');
|
|
255
|
+
});
|
|
256
|
+
it('should DELETE /tasks/{id} for delete()', async () => {
|
|
257
|
+
mockJsonResponse(null);
|
|
258
|
+
await api().delete('task-1');
|
|
259
|
+
const [url, init] = mockFetch.mock.calls[0];
|
|
260
|
+
expect(url).toContain('/tasks/task-1');
|
|
261
|
+
expect(init.method).toBe('DELETE');
|
|
262
|
+
});
|
|
263
|
+
it('should POST /tasks/{id}/cancel for cancel()', async () => {
|
|
264
|
+
mockJsonResponse(null);
|
|
265
|
+
await api().cancel('task-1');
|
|
266
|
+
const [url, init] = mockFetch.mock.calls[0];
|
|
267
|
+
expect(url).toContain('/tasks/task-1/cancel');
|
|
268
|
+
expect(init.method).toBe('POST');
|
|
269
|
+
});
|
|
270
|
+
it('should POST visibility for updateVisibility()', async () => {
|
|
271
|
+
const task = makeTask({ visibility: 'public' });
|
|
272
|
+
mockJsonResponse(task);
|
|
273
|
+
const result = await api().updateVisibility('task-1', 'public');
|
|
274
|
+
expect(result).toEqual(task);
|
|
275
|
+
const [, init] = mockFetch.mock.calls[0];
|
|
276
|
+
expect(JSON.parse(init.body)).toEqual({ visibility: 'public' });
|
|
277
|
+
});
|
|
278
|
+
it('should POST is_featured for feature()', async () => {
|
|
279
|
+
const task = makeTask({ is_featured: true });
|
|
280
|
+
mockJsonResponse(task);
|
|
281
|
+
await api().feature('task-1', true);
|
|
282
|
+
const [url, init] = mockFetch.mock.calls[0];
|
|
283
|
+
expect(url).toContain('/tasks/task-1/featured');
|
|
284
|
+
expect(JSON.parse(init.body)).toEqual({ is_featured: true });
|
|
285
|
+
});
|
|
286
|
+
it('should open SSE on /tasks/{id}/stream for stream()', async () => {
|
|
287
|
+
const http = new HttpClient({ apiKey: 'test-key' });
|
|
288
|
+
const createEventSource = jest
|
|
289
|
+
.spyOn(http, 'createEventSource')
|
|
290
|
+
.mockResolvedValue(null);
|
|
291
|
+
const tasks = new TasksAPI(http);
|
|
292
|
+
await tasks.stream('task-42');
|
|
293
|
+
expect(createEventSource).toHaveBeenCalledWith('/tasks/task-42/stream');
|
|
294
|
+
createEventSource.mockRestore();
|
|
295
|
+
});
|
|
296
|
+
it('should GET /tasks/{id}/logs for getLogs()', async () => {
|
|
297
|
+
const logs = { logs: [{ message: 'started', timestamp: '2026-01-01T00:00:00Z' }] };
|
|
298
|
+
mockJsonResponse(logs);
|
|
299
|
+
const result = await api().getLogs('task-1');
|
|
300
|
+
expect(result).toEqual(logs);
|
|
301
|
+
const [url, init] = mockFetch.mock.calls[0];
|
|
302
|
+
expect(url).toContain('/tasks/task-1/logs');
|
|
303
|
+
expect(init.method).toBe('GET');
|
|
304
|
+
});
|
|
305
|
+
it('should GET /tasks/{id}/timings for getTimings()', async () => {
|
|
306
|
+
const timings = { total_ms: 1200, stages: [{ name: 'inference', ms: 900 }] };
|
|
307
|
+
mockJsonResponse(timings);
|
|
308
|
+
const result = await api().getTimings('task-1');
|
|
309
|
+
expect(result).toEqual(timings);
|
|
310
|
+
const [url, init] = mockFetch.mock.calls[0];
|
|
311
|
+
expect(url).toContain('/tasks/task-1/timings');
|
|
312
|
+
expect(init.method).toBe('GET');
|
|
313
|
+
});
|
|
314
|
+
it('should GET /tasks/{id}/telemetry for getTelemetry()', async () => {
|
|
315
|
+
const telemetry = [{ metric: 'gpu_util', value: 0.82 }];
|
|
316
|
+
mockJsonResponse(telemetry);
|
|
317
|
+
const result = await api().getTelemetry('task-1');
|
|
318
|
+
expect(result).toEqual(telemetry);
|
|
319
|
+
const [url, init] = mockFetch.mock.calls[0];
|
|
320
|
+
expect(url).toContain('/tasks/task-1/telemetry');
|
|
321
|
+
expect(init.method).toBe('GET');
|
|
322
|
+
});
|
|
323
|
+
});
|
|
@@ -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 {};
|