@inferencesh/sdk 0.6.10 → 0.6.13
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 +231 -1
- package/dist/agent/api.test.js +18 -1
- package/dist/api/agents.d.ts +10 -0
- package/dist/api/agents.js +17 -3
- package/dist/api/agents.test.js +500 -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 +238 -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 +98 -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 +61 -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/sessions.test.js +12 -0
- package/dist/api/tasks.d.ts +13 -1
- package/dist/api/tasks.js +18 -0
- package/dist/api/tasks.test.js +171 -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 +139 -0
- package/dist/client.test.js +112 -1
- 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/proxy/hono.test.d.ts +1 -0
- package/dist/proxy/hono.test.js +90 -0
- package/dist/proxy/nextjs.test.d.ts +1 -0
- package/dist/proxy/nextjs.test.js +125 -0
- package/dist/proxy/remix.test.d.ts +1 -0
- package/dist/proxy/remix.test.js +66 -0
- package/dist/proxy/svelte.test.d.ts +1 -0
- package/dist/proxy/svelte.test.js +69 -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 +6 -6
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import { HttpClient } from '../http/client';
|
|
2
|
+
import { KnowledgeAPI, SkillsAPI } from './knowledge';
|
|
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('KnowledgeAPI', () => {
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
jest.clearAllMocks();
|
|
15
|
+
});
|
|
16
|
+
const api = () => new KnowledgeAPI(new HttpClient({ apiKey: 'test-key' }));
|
|
17
|
+
it('should POST /knowledge/list for list()', async () => {
|
|
18
|
+
const page = { items: [{ id: 'know-1' }], 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('/knowledge/list');
|
|
24
|
+
expect(init.method).toBe('POST');
|
|
25
|
+
});
|
|
26
|
+
it('should GET /knowledge/{namespace}/{name} for getByName()', async () => {
|
|
27
|
+
const entry = { id: 'know-1', namespace: 'acme', name: 'docs' };
|
|
28
|
+
mockJsonResponse(entry);
|
|
29
|
+
const result = await api().getByName('acme', 'docs');
|
|
30
|
+
expect(result).toEqual(entry);
|
|
31
|
+
const [url, init] = mockFetch.mock.calls[0];
|
|
32
|
+
expect(url).toContain('/knowledge/acme/docs');
|
|
33
|
+
expect(init.method).toBe('GET');
|
|
34
|
+
});
|
|
35
|
+
it('should POST /knowledge for create()', async () => {
|
|
36
|
+
const payload = { namespace: 'acme', name: 'docs', content: 'hello' };
|
|
37
|
+
const entry = { id: 'know-new', ...payload };
|
|
38
|
+
mockJsonResponse(entry);
|
|
39
|
+
const result = await api().create(payload);
|
|
40
|
+
expect(result).toEqual(entry);
|
|
41
|
+
const [url, init] = mockFetch.mock.calls[0];
|
|
42
|
+
expect(url).toContain('/knowledge');
|
|
43
|
+
expect(init.method).toBe('POST');
|
|
44
|
+
});
|
|
45
|
+
it('should POST team_id for transferOwnership()', async () => {
|
|
46
|
+
const entry = { id: 'know-1' };
|
|
47
|
+
mockJsonResponse(entry);
|
|
48
|
+
await api().transferOwnership('know-1', 'team-5');
|
|
49
|
+
const [url, init] = mockFetch.mock.calls[0];
|
|
50
|
+
expect(url).toContain('/knowledge/know-1/transfer');
|
|
51
|
+
expect(JSON.parse(init.body)).toEqual({ team_id: 'team-5' });
|
|
52
|
+
});
|
|
53
|
+
it('should GET /knowledge/{id} for get()', async () => {
|
|
54
|
+
const entry = { id: 'know-1', name: 'docs' };
|
|
55
|
+
mockJsonResponse(entry);
|
|
56
|
+
const result = await api().get('know-1');
|
|
57
|
+
expect(result).toEqual(entry);
|
|
58
|
+
const [url, init] = mockFetch.mock.calls[0];
|
|
59
|
+
expect(url).toContain('/knowledge/know-1');
|
|
60
|
+
expect(init.method).toBe('GET');
|
|
61
|
+
});
|
|
62
|
+
it('should POST /knowledge/{id} for update()', async () => {
|
|
63
|
+
const entry = { id: 'know-1', name: 'updated' };
|
|
64
|
+
mockJsonResponse(entry);
|
|
65
|
+
const result = await api().update('know-1', { name: 'updated' });
|
|
66
|
+
expect(result).toEqual(entry);
|
|
67
|
+
const [, init] = mockFetch.mock.calls[0];
|
|
68
|
+
expect(JSON.parse(init.body)).toEqual({ name: 'updated' });
|
|
69
|
+
});
|
|
70
|
+
it('should DELETE /knowledge/{id} for delete()', async () => {
|
|
71
|
+
mockJsonResponse(null);
|
|
72
|
+
await api().delete('know-1');
|
|
73
|
+
const [url, init] = mockFetch.mock.calls[0];
|
|
74
|
+
expect(url).toContain('/knowledge/know-1');
|
|
75
|
+
expect(init.method).toBe('DELETE');
|
|
76
|
+
});
|
|
77
|
+
it('should POST /knowledge/{id}/versions/list for listVersions()', async () => {
|
|
78
|
+
const page = { items: [{ id: 'ver-1' }], next_cursor: null };
|
|
79
|
+
mockJsonResponse(page);
|
|
80
|
+
const result = await api().listVersions('know-1', { limit: 10 });
|
|
81
|
+
expect(result).toEqual(page);
|
|
82
|
+
const [url, init] = mockFetch.mock.calls[0];
|
|
83
|
+
expect(url).toContain('/knowledge/know-1/versions/list');
|
|
84
|
+
expect(JSON.parse(init.body)).toEqual({ limit: 10 });
|
|
85
|
+
});
|
|
86
|
+
it('should GET /knowledge/{id}/versions/{versionId} for getVersion()', async () => {
|
|
87
|
+
const version = { id: 'ver-1', knowledge_id: 'know-1' };
|
|
88
|
+
mockJsonResponse(version);
|
|
89
|
+
const result = await api().getVersion('know-1', 'ver-1');
|
|
90
|
+
expect(result).toEqual(version);
|
|
91
|
+
const [url, init] = mockFetch.mock.calls[0];
|
|
92
|
+
expect(url).toContain('/knowledge/know-1/versions/ver-1');
|
|
93
|
+
expect(init.method).toBe('GET');
|
|
94
|
+
});
|
|
95
|
+
it('should POST visibility for updateVisibility()', async () => {
|
|
96
|
+
const entry = { id: 'know-1', visibility: 'team' };
|
|
97
|
+
mockJsonResponse(entry);
|
|
98
|
+
await api().updateVisibility('know-1', 'team');
|
|
99
|
+
const [, init] = mockFetch.mock.calls[0];
|
|
100
|
+
expect(JSON.parse(init.body)).toEqual({ visibility: 'team' });
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
describe('SkillsAPI', () => {
|
|
104
|
+
beforeEach(() => {
|
|
105
|
+
jest.clearAllMocks();
|
|
106
|
+
});
|
|
107
|
+
const api = () => new SkillsAPI(new HttpClient({ apiKey: 'test-key' }));
|
|
108
|
+
it('should GET /skills/resolve with ref param for resolve()', async () => {
|
|
109
|
+
const resolved = { ref: 'acme/skill@v1', content: '{}' };
|
|
110
|
+
mockJsonResponse(resolved);
|
|
111
|
+
const result = await api().resolve('acme/skill@v1', 'main');
|
|
112
|
+
expect(result).toEqual(resolved);
|
|
113
|
+
const [url] = mockFetch.mock.calls[0];
|
|
114
|
+
expect(url).toContain('/skills/resolve');
|
|
115
|
+
expect(url).toContain('ref=acme');
|
|
116
|
+
expect(url).toContain('skill=main');
|
|
117
|
+
});
|
|
118
|
+
it('should GET /skills/{namespace}/{name} for getByName()', async () => {
|
|
119
|
+
const skill = { id: 'skill-1', namespace: 'acme', name: 'research' };
|
|
120
|
+
mockJsonResponse(skill);
|
|
121
|
+
const result = await api().getByName('acme', 'research');
|
|
122
|
+
expect(result).toEqual(skill);
|
|
123
|
+
const [url, init] = mockFetch.mock.calls[0];
|
|
124
|
+
expect(url).toContain('/skills/acme/research');
|
|
125
|
+
expect(init.method).toBe('GET');
|
|
126
|
+
});
|
|
127
|
+
it('should GET /skills/{namespace}/{name}/download for download()', async () => {
|
|
128
|
+
mockJsonResponse({ url: 'https://cdn.example.com/skill.zip' });
|
|
129
|
+
await api().download('acme', 'research');
|
|
130
|
+
const [url, init] = mockFetch.mock.calls[0];
|
|
131
|
+
expect(url).toContain('/skills/acme/research/download');
|
|
132
|
+
expect(init.method).toBe('GET');
|
|
133
|
+
});
|
|
134
|
+
it('should POST /store/skills/list for listStore()', async () => {
|
|
135
|
+
const page = { items: [{ namespace: 'public', name: 'starter' }], next_cursor: null };
|
|
136
|
+
mockJsonResponse(page);
|
|
137
|
+
const result = await api().listStore();
|
|
138
|
+
expect(result).toEqual(page);
|
|
139
|
+
const [url, init] = mockFetch.mock.calls[0];
|
|
140
|
+
expect(url).toContain('/store/skills/list');
|
|
141
|
+
expect(init.method).toBe('POST');
|
|
142
|
+
});
|
|
143
|
+
it('should GET /skills/resolve with ref only when skill is omitted', async () => {
|
|
144
|
+
const resolved = { ref: 'acme/skill@v1' };
|
|
145
|
+
mockJsonResponse(resolved);
|
|
146
|
+
const result = await api().resolve('acme/skill@v1');
|
|
147
|
+
expect(result).toEqual(resolved);
|
|
148
|
+
const [url] = mockFetch.mock.calls[0];
|
|
149
|
+
expect(url).toContain('/skills/resolve');
|
|
150
|
+
expect(url).toContain('ref=acme');
|
|
151
|
+
expect(url).not.toContain('skill=');
|
|
152
|
+
});
|
|
153
|
+
it('should GET /skills/{namespace}/{name}/content for getContent()', async () => {
|
|
154
|
+
mockJsonResponse({ body: '# Skill instructions' });
|
|
155
|
+
const result = await api().getContent('acme', 'research');
|
|
156
|
+
expect(result).toEqual({ body: '# Skill instructions' });
|
|
157
|
+
const [url, init] = mockFetch.mock.calls[0];
|
|
158
|
+
expect(url).toContain('/skills/acme/research/content');
|
|
159
|
+
expect(init.method).toBe('GET');
|
|
160
|
+
});
|
|
161
|
+
it('should POST team_id for transferOwnership()', async () => {
|
|
162
|
+
const skill = { id: 'skill-1' };
|
|
163
|
+
mockJsonResponse(skill);
|
|
164
|
+
await api().transferOwnership('skill-1', 'team-8');
|
|
165
|
+
const [url, init] = mockFetch.mock.calls[0];
|
|
166
|
+
expect(url).toContain('/skills/skill-1/transfer');
|
|
167
|
+
expect(JSON.parse(init.body)).toEqual({ team_id: 'team-8' });
|
|
168
|
+
});
|
|
169
|
+
it('should POST /skills/list for list()', async () => {
|
|
170
|
+
const page = { items: [{ id: 'skill-1' }], next_cursor: null };
|
|
171
|
+
mockJsonResponse(page);
|
|
172
|
+
const result = await api().list({ limit: 10 });
|
|
173
|
+
expect(result).toEqual(page);
|
|
174
|
+
const [url, init] = mockFetch.mock.calls[0];
|
|
175
|
+
expect(url).toContain('/skills/list');
|
|
176
|
+
expect(init.method).toBe('POST');
|
|
177
|
+
});
|
|
178
|
+
it('should GET /skills/{id} for get()', async () => {
|
|
179
|
+
const skill = { id: 'skill-1', name: 'research' };
|
|
180
|
+
mockJsonResponse(skill);
|
|
181
|
+
const result = await api().get('skill-1');
|
|
182
|
+
expect(result).toEqual(skill);
|
|
183
|
+
const [url, init] = mockFetch.mock.calls[0];
|
|
184
|
+
expect(url).toContain('/skills/skill-1');
|
|
185
|
+
expect(init.method).toBe('GET');
|
|
186
|
+
});
|
|
187
|
+
it('should POST /skills for create()', async () => {
|
|
188
|
+
const payload = { namespace: 'acme', name: 'research' };
|
|
189
|
+
const skill = { id: 'skill-new', ...payload };
|
|
190
|
+
mockJsonResponse(skill);
|
|
191
|
+
const result = await api().create(payload);
|
|
192
|
+
expect(result).toEqual(skill);
|
|
193
|
+
const [url, init] = mockFetch.mock.calls[0];
|
|
194
|
+
expect(url).toContain('/skills');
|
|
195
|
+
expect(init.method).toBe('POST');
|
|
196
|
+
expect(JSON.parse(init.body)).toEqual(payload);
|
|
197
|
+
});
|
|
198
|
+
it('should POST /skills/{id} for update()', async () => {
|
|
199
|
+
const skill = { id: 'skill-1', name: 'updated' };
|
|
200
|
+
mockJsonResponse(skill);
|
|
201
|
+
const result = await api().update('skill-1', { name: 'updated' });
|
|
202
|
+
expect(result).toEqual(skill);
|
|
203
|
+
const [, init] = mockFetch.mock.calls[0];
|
|
204
|
+
expect(JSON.parse(init.body)).toEqual({ name: 'updated' });
|
|
205
|
+
});
|
|
206
|
+
it('should DELETE /skills/{id} for delete()', async () => {
|
|
207
|
+
mockJsonResponse(null);
|
|
208
|
+
await api().delete('skill-1');
|
|
209
|
+
const [url, init] = mockFetch.mock.calls[0];
|
|
210
|
+
expect(url).toContain('/skills/skill-1');
|
|
211
|
+
expect(init.method).toBe('DELETE');
|
|
212
|
+
});
|
|
213
|
+
it('should POST /skills/{id}/versions/list for listVersions()', async () => {
|
|
214
|
+
const page = { items: [{ id: 'ver-1' }], next_cursor: null };
|
|
215
|
+
mockJsonResponse(page);
|
|
216
|
+
const result = await api().listVersions('skill-1', { limit: 5 });
|
|
217
|
+
expect(result).toEqual(page);
|
|
218
|
+
const [url, init] = mockFetch.mock.calls[0];
|
|
219
|
+
expect(url).toContain('/skills/skill-1/versions/list');
|
|
220
|
+
expect(JSON.parse(init.body)).toEqual({ limit: 5 });
|
|
221
|
+
});
|
|
222
|
+
it('should GET /skills/{id}/versions/{versionId} for getVersion()', async () => {
|
|
223
|
+
const version = { id: 'ver-1', skill_id: 'skill-1' };
|
|
224
|
+
mockJsonResponse(version);
|
|
225
|
+
const result = await api().getVersion('skill-1', 'ver-1');
|
|
226
|
+
expect(result).toEqual(version);
|
|
227
|
+
const [url, init] = mockFetch.mock.calls[0];
|
|
228
|
+
expect(url).toContain('/skills/skill-1/versions/ver-1');
|
|
229
|
+
expect(init.method).toBe('GET');
|
|
230
|
+
});
|
|
231
|
+
it('should POST visibility for updateVisibility()', async () => {
|
|
232
|
+
const skill = { id: 'skill-1', visibility: 'team' };
|
|
233
|
+
mockJsonResponse(skill);
|
|
234
|
+
await api().updateVisibility('skill-1', 'team');
|
|
235
|
+
const [, init] = mockFetch.mock.calls[0];
|
|
236
|
+
expect(JSON.parse(init.body)).toEqual({ visibility: 'team' });
|
|
237
|
+
});
|
|
238
|
+
});
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { HttpClient } from '../http/client';
|
|
2
|
+
import { MCPServerDTO, CursorListRequest, CursorListResponse } from '../types';
|
|
3
|
+
/**
|
|
4
|
+
* MCP Servers API
|
|
5
|
+
*/
|
|
6
|
+
export declare class MCPServersAPI {
|
|
7
|
+
private readonly http;
|
|
8
|
+
constructor(http: HttpClient);
|
|
9
|
+
/**
|
|
10
|
+
* List public MCP servers
|
|
11
|
+
*/
|
|
12
|
+
list(params?: Partial<CursorListRequest>): Promise<CursorListResponse<MCPServerDTO>>;
|
|
13
|
+
/**
|
|
14
|
+
* Get an MCP server by slug
|
|
15
|
+
*/
|
|
16
|
+
get(slug: string): Promise<MCPServerDTO>;
|
|
17
|
+
/**
|
|
18
|
+
* List tools for an MCP server
|
|
19
|
+
*/
|
|
20
|
+
listTools(slug: string): Promise<unknown[]>;
|
|
21
|
+
/**
|
|
22
|
+
* Call a tool on an MCP server
|
|
23
|
+
*/
|
|
24
|
+
callTool(slug: string, tool: string, input: unknown): Promise<unknown>;
|
|
25
|
+
/**
|
|
26
|
+
* List user's own MCP servers
|
|
27
|
+
*/
|
|
28
|
+
listOwned(params?: Partial<CursorListRequest>): Promise<CursorListResponse<MCPServerDTO>>;
|
|
29
|
+
/**
|
|
30
|
+
* Get a user's MCP server by ID
|
|
31
|
+
*/
|
|
32
|
+
getOwned(id: string): Promise<MCPServerDTO>;
|
|
33
|
+
/**
|
|
34
|
+
* Create an MCP server
|
|
35
|
+
*/
|
|
36
|
+
create(data: Partial<MCPServerDTO>): Promise<MCPServerDTO>;
|
|
37
|
+
/**
|
|
38
|
+
* Update an MCP server
|
|
39
|
+
*/
|
|
40
|
+
update(id: string, data: Partial<MCPServerDTO>): Promise<MCPServerDTO>;
|
|
41
|
+
/**
|
|
42
|
+
* Delete an MCP server
|
|
43
|
+
*/
|
|
44
|
+
delete(id: string): Promise<void>;
|
|
45
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Servers API
|
|
3
|
+
*/
|
|
4
|
+
export class MCPServersAPI {
|
|
5
|
+
constructor(http) {
|
|
6
|
+
this.http = http;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* List public MCP servers
|
|
10
|
+
*/
|
|
11
|
+
async list(params) {
|
|
12
|
+
return this.http.request('post', '/mcps/list', { data: params });
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Get an MCP server by slug
|
|
16
|
+
*/
|
|
17
|
+
async get(slug) {
|
|
18
|
+
return this.http.request('get', `/mcps/${slug}`);
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* List tools for an MCP server
|
|
22
|
+
*/
|
|
23
|
+
async listTools(slug) {
|
|
24
|
+
return this.http.request('get', `/mcps/${slug}/tools`);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Call a tool on an MCP server
|
|
28
|
+
*/
|
|
29
|
+
async callTool(slug, tool, input) {
|
|
30
|
+
return this.http.request('post', `/mcps/${slug}/tools/${tool}`, { data: input });
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* List user's own MCP servers
|
|
34
|
+
*/
|
|
35
|
+
async listOwned(params) {
|
|
36
|
+
return this.http.request('post', '/mcp-servers/list', { data: params });
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Get a user's MCP server by ID
|
|
40
|
+
*/
|
|
41
|
+
async getOwned(id) {
|
|
42
|
+
return this.http.request('get', `/mcp-servers/${id}`);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Create an MCP server
|
|
46
|
+
*/
|
|
47
|
+
async create(data) {
|
|
48
|
+
return this.http.request('post', '/mcp-servers', { data });
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Update an MCP server
|
|
52
|
+
*/
|
|
53
|
+
async update(id, data) {
|
|
54
|
+
return this.http.request('put', `/mcp-servers/${id}`, { data });
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Delete an MCP server
|
|
58
|
+
*/
|
|
59
|
+
async delete(id) {
|
|
60
|
+
return this.http.request('delete', `/mcp-servers/${id}`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { HttpClient } from '../http/client';
|
|
2
|
+
import { MCPServersAPI } from './mcp-servers';
|
|
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('MCPServersAPI', () => {
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
jest.clearAllMocks();
|
|
15
|
+
});
|
|
16
|
+
const api = () => new MCPServersAPI(new HttpClient({ apiKey: 'test-key' }));
|
|
17
|
+
it('should POST /mcps/list for list()', async () => {
|
|
18
|
+
const page = { items: [{ slug: 'filesystem' }], 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('/mcps/list');
|
|
24
|
+
expect(init.method).toBe('POST');
|
|
25
|
+
});
|
|
26
|
+
it('should GET /mcps/{slug}/tools for listTools()', async () => {
|
|
27
|
+
const tools = [{ name: 'read_file' }];
|
|
28
|
+
mockJsonResponse(tools);
|
|
29
|
+
const result = await api().listTools('filesystem');
|
|
30
|
+
expect(result).toEqual(tools);
|
|
31
|
+
const [url, init] = mockFetch.mock.calls[0];
|
|
32
|
+
expect(url).toContain('/mcps/filesystem/tools');
|
|
33
|
+
expect(init.method).toBe('GET');
|
|
34
|
+
});
|
|
35
|
+
it('should POST /mcps/{slug}/tools/{tool} for callTool()', async () => {
|
|
36
|
+
const input = { path: '/tmp/test.txt' };
|
|
37
|
+
const output = { content: 'hello' };
|
|
38
|
+
mockJsonResponse(output);
|
|
39
|
+
const result = await api().callTool('filesystem', 'read_file', input);
|
|
40
|
+
expect(result).toEqual(output);
|
|
41
|
+
const [url, init] = mockFetch.mock.calls[0];
|
|
42
|
+
expect(url).toContain('/mcps/filesystem/tools/read_file');
|
|
43
|
+
expect(init.method).toBe('POST');
|
|
44
|
+
expect(JSON.parse(init.body)).toEqual(input);
|
|
45
|
+
});
|
|
46
|
+
it('should POST /mcp-servers for create()', async () => {
|
|
47
|
+
const payload = { name: 'My MCP', slug: 'my-mcp' };
|
|
48
|
+
const server = { id: 'mcp-1', ...payload };
|
|
49
|
+
mockJsonResponse(server);
|
|
50
|
+
const result = await api().create(payload);
|
|
51
|
+
expect(result).toEqual(server);
|
|
52
|
+
const [url, init] = mockFetch.mock.calls[0];
|
|
53
|
+
expect(url).toContain('/mcp-servers');
|
|
54
|
+
expect(init.method).toBe('POST');
|
|
55
|
+
});
|
|
56
|
+
it('should PUT /mcp-servers/{id} for update()', async () => {
|
|
57
|
+
const server = { id: 'mcp-1', name: 'Updated MCP' };
|
|
58
|
+
mockJsonResponse(server);
|
|
59
|
+
await api().update('mcp-1', { name: 'Updated MCP' });
|
|
60
|
+
const [url, init] = mockFetch.mock.calls[0];
|
|
61
|
+
expect(url).toContain('/mcp-servers/mcp-1');
|
|
62
|
+
expect(init.method).toBe('PUT');
|
|
63
|
+
});
|
|
64
|
+
it('should GET /mcps/{slug} for get()', async () => {
|
|
65
|
+
const server = { slug: 'filesystem', name: 'Filesystem MCP' };
|
|
66
|
+
mockJsonResponse(server);
|
|
67
|
+
const result = await api().get('filesystem');
|
|
68
|
+
expect(result).toEqual(server);
|
|
69
|
+
const [url, init] = mockFetch.mock.calls[0];
|
|
70
|
+
expect(url).toContain('/mcps/filesystem');
|
|
71
|
+
expect(init.method).toBe('GET');
|
|
72
|
+
});
|
|
73
|
+
it('should POST /mcp-servers/list for listOwned()', async () => {
|
|
74
|
+
const page = { items: [{ id: 'mcp-1' }], next_cursor: null };
|
|
75
|
+
mockJsonResponse(page);
|
|
76
|
+
const result = await api().listOwned({ limit: 5 });
|
|
77
|
+
expect(result).toEqual(page);
|
|
78
|
+
const [url, init] = mockFetch.mock.calls[0];
|
|
79
|
+
expect(url).toContain('/mcp-servers/list');
|
|
80
|
+
expect(init.method).toBe('POST');
|
|
81
|
+
});
|
|
82
|
+
it('should GET /mcp-servers/{id} for getOwned()', async () => {
|
|
83
|
+
const server = { id: 'mcp-1', name: 'My MCP' };
|
|
84
|
+
mockJsonResponse(server);
|
|
85
|
+
const result = await api().getOwned('mcp-1');
|
|
86
|
+
expect(result).toEqual(server);
|
|
87
|
+
const [url, init] = mockFetch.mock.calls[0];
|
|
88
|
+
expect(url).toContain('/mcp-servers/mcp-1');
|
|
89
|
+
expect(init.method).toBe('GET');
|
|
90
|
+
});
|
|
91
|
+
it('should DELETE /mcp-servers/{id} for delete()', async () => {
|
|
92
|
+
mockJsonResponse(null);
|
|
93
|
+
await api().delete('mcp-1');
|
|
94
|
+
const [url, init] = mockFetch.mock.calls[0];
|
|
95
|
+
expect(url).toContain('/mcp-servers/mcp-1');
|
|
96
|
+
expect(init.method).toBe('DELETE');
|
|
97
|
+
});
|
|
98
|
+
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { HttpClient } from '../http/client';
|
|
2
|
+
import { ProjectDTO, CursorListRequest, CursorListResponse } from '../types';
|
|
3
|
+
/**
|
|
4
|
+
* Projects API
|
|
5
|
+
*/
|
|
6
|
+
export declare class ProjectsAPI {
|
|
7
|
+
private readonly http;
|
|
8
|
+
constructor(http: HttpClient);
|
|
9
|
+
/**
|
|
10
|
+
* List projects with cursor-based pagination
|
|
11
|
+
*/
|
|
12
|
+
list(params?: Partial<CursorListRequest>): Promise<CursorListResponse<ProjectDTO>>;
|
|
13
|
+
/**
|
|
14
|
+
* Get a project by ID
|
|
15
|
+
*/
|
|
16
|
+
get(id: string): Promise<ProjectDTO>;
|
|
17
|
+
/**
|
|
18
|
+
* Create a project
|
|
19
|
+
*/
|
|
20
|
+
create(data: Partial<ProjectDTO>): Promise<ProjectDTO>;
|
|
21
|
+
/**
|
|
22
|
+
* Update a project
|
|
23
|
+
*/
|
|
24
|
+
update(id: string, data: Partial<ProjectDTO>): Promise<ProjectDTO>;
|
|
25
|
+
/**
|
|
26
|
+
* Delete a project
|
|
27
|
+
*/
|
|
28
|
+
delete(id: string): Promise<void>;
|
|
29
|
+
}
|
|
@@ -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,61 @@
|
|
|
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
|
+
it('should POST /projects/{id} for update()', async () => {
|
|
53
|
+
const project = { id: 'proj-1', name: 'Renamed' };
|
|
54
|
+
mockJsonResponse(project);
|
|
55
|
+
const result = await api().update('proj-1', { name: 'Renamed' });
|
|
56
|
+
expect(result).toEqual(project);
|
|
57
|
+
const [url, init] = mockFetch.mock.calls[0];
|
|
58
|
+
expect(url).toContain('/projects/proj-1');
|
|
59
|
+
expect(JSON.parse(init.body)).toEqual({ name: 'Renamed' });
|
|
60
|
+
});
|
|
61
|
+
});
|
|
@@ -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
|
+
});
|