@inferencesh/sdk 0.6.14 → 0.6.19
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 +10 -0
- package/dist/agent/provider.d.ts +2 -1
- package/dist/agent/provider.js +5 -5
- package/dist/agent/types.d.ts +3 -0
- package/dist/api/apps.d.ts +4 -0
- package/dist/api/apps.js +6 -0
- package/dist/api/index.d.ts +3 -3
- package/dist/http/index.d.ts +3 -3
- package/dist/index.d.ts +9 -9
- package/dist/types.d.ts +131 -19
- package/dist/types.js +26 -9
- package/package.json +8 -5
- package/dist/agent/actions.test.d.ts +0 -1
- package/dist/agent/actions.test.js +0 -820
- package/dist/agent/api.test.d.ts +0 -1
- package/dist/agent/api.test.js +0 -246
- package/dist/agent/reducer.test.d.ts +0 -1
- package/dist/agent/reducer.test.js +0 -131
- package/dist/agent/types.test.d.ts +0 -1
- package/dist/agent/types.test.js +0 -75
- package/dist/api/agents.test.d.ts +0 -1
- package/dist/api/agents.test.js +0 -1014
- package/dist/api/api-keys.test.d.ts +0 -1
- package/dist/api/api-keys.test.js +0 -44
- package/dist/api/apps.test.d.ts +0 -1
- package/dist/api/apps.test.js +0 -135
- package/dist/api/chats.test.d.ts +0 -1
- package/dist/api/chats.test.js +0 -85
- package/dist/api/engines.test.d.ts +0 -1
- package/dist/api/engines.test.js +0 -133
- package/dist/api/files.test.d.ts +0 -1
- package/dist/api/files.test.js +0 -256
- package/dist/api/flow-runs.test.d.ts +0 -1
- package/dist/api/flow-runs.test.js +0 -97
- package/dist/api/flows.test.d.ts +0 -1
- package/dist/api/flows.test.js +0 -118
- package/dist/api/integrations.test.d.ts +0 -1
- package/dist/api/integrations.test.js +0 -109
- package/dist/api/knowledge.test.d.ts +0 -1
- package/dist/api/knowledge.test.js +0 -238
- package/dist/api/mcp-servers.test.d.ts +0 -1
- package/dist/api/mcp-servers.test.js +0 -98
- package/dist/api/projects.test.d.ts +0 -1
- package/dist/api/projects.test.js +0 -61
- package/dist/api/search.test.d.ts +0 -1
- package/dist/api/search.test.js +0 -50
- package/dist/api/secrets.test.d.ts +0 -1
- package/dist/api/secrets.test.js +0 -61
- package/dist/api/sessions.test.d.ts +0 -1
- package/dist/api/sessions.test.js +0 -61
- package/dist/api/tasks.test.d.ts +0 -1
- package/dist/api/tasks.test.js +0 -381
- package/dist/api/teams.test.d.ts +0 -1
- package/dist/api/teams.test.js +0 -139
- package/dist/client.test.d.ts +0 -1
- package/dist/client.test.js +0 -446
- package/dist/http/client.test.d.ts +0 -1
- package/dist/http/client.test.js +0 -435
- package/dist/http/errors.test.d.ts +0 -1
- package/dist/http/errors.test.js +0 -66
- package/dist/http/poll.test.d.ts +0 -1
- package/dist/http/poll.test.js +0 -146
- package/dist/http/streamable.test.d.ts +0 -4
- package/dist/http/streamable.test.js +0 -510
- package/dist/integration.test.d.ts +0 -9
- package/dist/integration.test.js +0 -107
- package/dist/proxy/express.test.d.ts +0 -1
- package/dist/proxy/express.test.js +0 -106
- package/dist/proxy/hono.test.d.ts +0 -1
- package/dist/proxy/hono.test.js +0 -90
- package/dist/proxy/index.test.d.ts +0 -1
- package/dist/proxy/index.test.js +0 -173
- package/dist/proxy/nextjs.test.d.ts +0 -1
- package/dist/proxy/nextjs.test.js +0 -125
- package/dist/proxy/remix.test.d.ts +0 -1
- package/dist/proxy/remix.test.js +0 -66
- package/dist/proxy/svelte.test.d.ts +0 -1
- package/dist/proxy/svelte.test.js +0 -69
- package/dist/sessions.integration.test.d.ts +0 -13
- package/dist/sessions.integration.test.js +0 -310
- package/dist/stream.test.d.ts +0 -1
- package/dist/stream.test.js +0 -363
- package/dist/streamable.integration.test.d.ts +0 -11
- package/dist/streamable.integration.test.js +0 -150
- package/dist/tool-builder.test.d.ts +0 -1
- package/dist/tool-builder.test.js +0 -407
- package/dist/utils.test.d.ts +0 -1
- package/dist/utils.test.js +0 -43
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Integration tests for streamable HTTP client
|
|
3
|
-
*
|
|
4
|
-
* Tests NDJSON streaming against the real API.
|
|
5
|
-
* Run with: INFERENCE_API_KEY=xxx npm run test:integration
|
|
6
|
-
*
|
|
7
|
-
* Note: Format negotiation tests may be skipped if server doesn't support NDJSON yet.
|
|
8
|
-
*
|
|
9
|
-
* @jest-environment node
|
|
10
|
-
*/
|
|
11
|
-
import { streamable, StreamableManager } from './http/streamable';
|
|
12
|
-
import { Inference, TaskStatusCompleted, TaskStatusFailed, TaskStatusCancelled } from './index';
|
|
13
|
-
const isTerminalStatus = (status) => status === TaskStatusCompleted || status === TaskStatusFailed || status === TaskStatusCancelled;
|
|
14
|
-
const API_KEY = process.env.INFERENCE_API_KEY;
|
|
15
|
-
const BASE_URL = process.env.INFERENCE_BASE_URL || 'https://api.inference.sh';
|
|
16
|
-
// Simple test app that completes quickly
|
|
17
|
-
const TEST_APP = 'infsh/text-templating@53bk0yzk';
|
|
18
|
-
const describeIfApiKey = API_KEY ? describe : describe.skip;
|
|
19
|
-
describeIfApiKey('Streamable Integration Tests', () => {
|
|
20
|
-
let client;
|
|
21
|
-
beforeAll(() => {
|
|
22
|
-
client = new Inference({
|
|
23
|
-
apiKey: API_KEY,
|
|
24
|
-
baseUrl: BASE_URL,
|
|
25
|
-
});
|
|
26
|
-
});
|
|
27
|
-
describe('Format Negotiation', () => {
|
|
28
|
-
it('should negotiate format based on Accept header', async () => {
|
|
29
|
-
// Create a task using the SDK
|
|
30
|
-
const task = await client.run({ app: TEST_APP, input: { template: 'Format {1}!', strings: ['Test'] } }, { wait: false });
|
|
31
|
-
// Test NDJSON request
|
|
32
|
-
const ndjsonRes = await fetch(`${BASE_URL}/tasks/${task.id}/stream`, {
|
|
33
|
-
headers: {
|
|
34
|
-
'Authorization': `Bearer ${API_KEY}`,
|
|
35
|
-
'Accept': 'application/x-ndjson',
|
|
36
|
-
},
|
|
37
|
-
});
|
|
38
|
-
expect(ndjsonRes.ok).toBe(true);
|
|
39
|
-
const ndjsonContentType = ndjsonRes.headers.get('content-type');
|
|
40
|
-
// Test SSE request
|
|
41
|
-
const sseRes = await fetch(`${BASE_URL}/tasks/${task.id}/stream`, {
|
|
42
|
-
headers: {
|
|
43
|
-
'Authorization': `Bearer ${API_KEY}`,
|
|
44
|
-
'Accept': 'text/event-stream',
|
|
45
|
-
},
|
|
46
|
-
});
|
|
47
|
-
expect(sseRes.ok).toBe(true);
|
|
48
|
-
const sseContentType = sseRes.headers.get('content-type');
|
|
49
|
-
// Log what we got for debugging
|
|
50
|
-
console.log('NDJSON request Content-Type:', ndjsonContentType);
|
|
51
|
-
console.log('SSE request Content-Type:', sseContentType);
|
|
52
|
-
// At minimum, SSE should work
|
|
53
|
-
expect(sseContentType).toContain('text/event-stream');
|
|
54
|
-
// If NDJSON is supported, it should return NDJSON content-type
|
|
55
|
-
// Otherwise it falls back to SSE (server not yet updated)
|
|
56
|
-
const supportsNDJSON = ndjsonContentType?.includes('application/x-ndjson');
|
|
57
|
-
if (supportsNDJSON) {
|
|
58
|
-
console.log('✓ Server supports NDJSON format negotiation');
|
|
59
|
-
}
|
|
60
|
-
else {
|
|
61
|
-
console.log('⚠ Server does not yet support NDJSON, falling back to SSE');
|
|
62
|
-
}
|
|
63
|
-
// Clean up - consume the streams
|
|
64
|
-
for (const res of [ndjsonRes, sseRes]) {
|
|
65
|
-
const reader = res.body?.getReader();
|
|
66
|
-
if (reader) {
|
|
67
|
-
while (true) {
|
|
68
|
-
const { done } = await reader.read();
|
|
69
|
-
if (done)
|
|
70
|
-
break;
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
}, 60000);
|
|
75
|
-
});
|
|
76
|
-
describe('streamable() with NDJSON server', () => {
|
|
77
|
-
// These tests require the server to support NDJSON
|
|
78
|
-
// Skip if server doesn't support it yet
|
|
79
|
-
it('should stream task updates when server supports NDJSON', async () => {
|
|
80
|
-
// Create a task using the SDK (fire and forget)
|
|
81
|
-
const task = await client.run({ app: TEST_APP, input: { template: 'Hello {1}!', strings: ['Streamable'] } }, { wait: false });
|
|
82
|
-
expect(task.id).toBeDefined();
|
|
83
|
-
// Stream updates using streamable - will request NDJSON format
|
|
84
|
-
const updates = [];
|
|
85
|
-
try {
|
|
86
|
-
for await (const update of streamable(`${BASE_URL}/tasks/${task.id}/stream`, {
|
|
87
|
-
headers: { 'Authorization': `Bearer ${API_KEY}` },
|
|
88
|
-
})) {
|
|
89
|
-
updates.push(update);
|
|
90
|
-
// Stop when task reaches terminal status
|
|
91
|
-
if (isTerminalStatus(update.status))
|
|
92
|
-
break;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
catch (err) {
|
|
96
|
-
// If server doesn't support NDJSON, streamable will fail to parse SSE
|
|
97
|
-
console.log('⚠ Skipping - server returned non-NDJSON:', err.message);
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
100
|
-
expect(updates.length).toBeGreaterThan(0);
|
|
101
|
-
// Last update should be terminal
|
|
102
|
-
const lastUpdate = updates[updates.length - 1];
|
|
103
|
-
expect(isTerminalStatus(lastUpdate.status)).toBe(true);
|
|
104
|
-
}, 60000);
|
|
105
|
-
});
|
|
106
|
-
describe('StreamableManager with NDJSON server', () => {
|
|
107
|
-
it('should receive updates via callbacks when server supports NDJSON', async () => {
|
|
108
|
-
// Create a task using the SDK
|
|
109
|
-
const task = await client.run({ app: TEST_APP, input: { template: 'Manager {1}!', strings: ['Test'] } }, { wait: false });
|
|
110
|
-
const updates = [];
|
|
111
|
-
let started = false;
|
|
112
|
-
let ended = false;
|
|
113
|
-
let error = null;
|
|
114
|
-
const manager = new StreamableManager({
|
|
115
|
-
url: `${BASE_URL}/tasks/${task.id}/stream`,
|
|
116
|
-
headers: { 'Authorization': `Bearer ${API_KEY}` },
|
|
117
|
-
onStart: () => { started = true; },
|
|
118
|
-
onEnd: () => { ended = true; },
|
|
119
|
-
onData: (data) => {
|
|
120
|
-
updates.push(data);
|
|
121
|
-
// Stop when terminal
|
|
122
|
-
if (isTerminalStatus(data.status)) {
|
|
123
|
-
manager.stop();
|
|
124
|
-
}
|
|
125
|
-
},
|
|
126
|
-
onError: (err) => {
|
|
127
|
-
error = err;
|
|
128
|
-
},
|
|
129
|
-
});
|
|
130
|
-
await manager.start();
|
|
131
|
-
// If server doesn't support NDJSON, we'll get a parse error
|
|
132
|
-
if (error) {
|
|
133
|
-
console.log('⚠ Skipping StreamableManager test - server returned non-NDJSON');
|
|
134
|
-
return;
|
|
135
|
-
}
|
|
136
|
-
expect(started).toBe(true);
|
|
137
|
-
expect(ended).toBe(true);
|
|
138
|
-
expect(updates.length).toBeGreaterThan(0);
|
|
139
|
-
}, 60000);
|
|
140
|
-
});
|
|
141
|
-
});
|
|
142
|
-
// Ensure Jest doesn't complain when API key is not set
|
|
143
|
-
describe('Streamable Integration Setup', () => {
|
|
144
|
-
it('checks for API key', () => {
|
|
145
|
-
if (!API_KEY) {
|
|
146
|
-
console.log('⚠️ Skipping streamable integration tests - INFERENCE_API_KEY not set');
|
|
147
|
-
}
|
|
148
|
-
expect(true).toBe(true);
|
|
149
|
-
});
|
|
150
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,407 +0,0 @@
|
|
|
1
|
-
import { tool, appTool, agentTool, webhookTool, httpTool, callTool, mcpTool, internalTools, string, number, integer, boolean, enumOf, object, array, optional, } from './tool-builder';
|
|
2
|
-
import { ToolTypeClient, ToolTypeApp, ToolTypeAgent, ToolTypeHook, ToolTypeHTTP, ToolTypeMCP, IntegrationProviderGoogle, IntegrationProviderGoogleSA, } from './types';
|
|
3
|
-
describe('Schema Helpers', () => {
|
|
4
|
-
describe('string', () => {
|
|
5
|
-
it('creates string schema without description', () => {
|
|
6
|
-
const schema = string();
|
|
7
|
-
expect(schema).toEqual({ type: 'string' });
|
|
8
|
-
});
|
|
9
|
-
it('creates string schema with description', () => {
|
|
10
|
-
const schema = string('User name');
|
|
11
|
-
expect(schema).toEqual({ type: 'string', description: 'User name' });
|
|
12
|
-
});
|
|
13
|
-
});
|
|
14
|
-
describe('number', () => {
|
|
15
|
-
it('creates number schema', () => {
|
|
16
|
-
expect(number()).toEqual({ type: 'number' });
|
|
17
|
-
expect(number('Temperature')).toEqual({ type: 'number', description: 'Temperature' });
|
|
18
|
-
});
|
|
19
|
-
});
|
|
20
|
-
describe('integer', () => {
|
|
21
|
-
it('creates integer schema', () => {
|
|
22
|
-
expect(integer()).toEqual({ type: 'integer' });
|
|
23
|
-
expect(integer('Age')).toEqual({ type: 'integer', description: 'Age' });
|
|
24
|
-
});
|
|
25
|
-
});
|
|
26
|
-
describe('boolean', () => {
|
|
27
|
-
it('creates boolean schema', () => {
|
|
28
|
-
expect(boolean()).toEqual({ type: 'boolean' });
|
|
29
|
-
expect(boolean('Is active')).toEqual({ type: 'boolean', description: 'Is active' });
|
|
30
|
-
});
|
|
31
|
-
});
|
|
32
|
-
describe('enumOf', () => {
|
|
33
|
-
it('creates enum schema', () => {
|
|
34
|
-
const schema = enumOf(['low', 'medium', 'high']);
|
|
35
|
-
expect(schema).toEqual({ type: 'string', enum: ['low', 'medium', 'high'] });
|
|
36
|
-
});
|
|
37
|
-
it('creates enum schema with description', () => {
|
|
38
|
-
const schema = enumOf(['a', 'b'], 'Priority level');
|
|
39
|
-
expect(schema).toEqual({ type: 'string', enum: ['a', 'b'], description: 'Priority level' });
|
|
40
|
-
});
|
|
41
|
-
});
|
|
42
|
-
describe('object', () => {
|
|
43
|
-
it('creates object schema with nested properties', () => {
|
|
44
|
-
const schema = object({
|
|
45
|
-
name: string('Name'),
|
|
46
|
-
age: integer('Age'),
|
|
47
|
-
});
|
|
48
|
-
expect(schema).toEqual({
|
|
49
|
-
type: 'object',
|
|
50
|
-
properties: {
|
|
51
|
-
name: { type: 'string', description: 'Name' },
|
|
52
|
-
age: { type: 'integer', description: 'Age' },
|
|
53
|
-
},
|
|
54
|
-
});
|
|
55
|
-
});
|
|
56
|
-
it('creates object schema with description', () => {
|
|
57
|
-
const schema = object({ x: number() }, 'Coordinates');
|
|
58
|
-
expect(schema.description).toBe('Coordinates');
|
|
59
|
-
});
|
|
60
|
-
});
|
|
61
|
-
describe('array', () => {
|
|
62
|
-
it('creates array schema', () => {
|
|
63
|
-
const schema = array(string('Tag'));
|
|
64
|
-
expect(schema).toEqual({
|
|
65
|
-
type: 'array',
|
|
66
|
-
items: { type: 'string', description: 'Tag' },
|
|
67
|
-
});
|
|
68
|
-
});
|
|
69
|
-
it('creates array schema with description', () => {
|
|
70
|
-
const schema = array(integer(), 'List of IDs');
|
|
71
|
-
expect(schema).toEqual({
|
|
72
|
-
type: 'array',
|
|
73
|
-
items: { type: 'integer' },
|
|
74
|
-
description: 'List of IDs',
|
|
75
|
-
});
|
|
76
|
-
});
|
|
77
|
-
});
|
|
78
|
-
describe('optional', () => {
|
|
79
|
-
it('marks schema as optional', () => {
|
|
80
|
-
const schema = optional(string('Optional field'));
|
|
81
|
-
expect(schema).toEqual({ type: 'string', description: 'Optional field', optional: true });
|
|
82
|
-
});
|
|
83
|
-
it('preserves original schema properties', () => {
|
|
84
|
-
const schema = optional(enumOf(['a', 'b'], 'Choice'));
|
|
85
|
-
expect(schema.enum).toEqual(['a', 'b']);
|
|
86
|
-
expect(schema.optional).toBe(true);
|
|
87
|
-
});
|
|
88
|
-
});
|
|
89
|
-
});
|
|
90
|
-
describe('ClientToolBuilder (tool)', () => {
|
|
91
|
-
it('creates minimal tool', () => {
|
|
92
|
-
const t = tool('my_tool').build();
|
|
93
|
-
expect(t).toEqual({
|
|
94
|
-
name: 'my_tool',
|
|
95
|
-
display_name: 'my_tool',
|
|
96
|
-
description: '',
|
|
97
|
-
type: ToolTypeClient,
|
|
98
|
-
require_approval: undefined,
|
|
99
|
-
client: {
|
|
100
|
-
input_schema: { type: 'object', properties: {}, required: [] },
|
|
101
|
-
},
|
|
102
|
-
});
|
|
103
|
-
});
|
|
104
|
-
it('creates tool with description', () => {
|
|
105
|
-
const t = tool('greet').describe('Says hello').build();
|
|
106
|
-
expect(t.description).toBe('Says hello');
|
|
107
|
-
});
|
|
108
|
-
it('creates tool with display name', () => {
|
|
109
|
-
const t = tool('get_data').display('Get Data').build();
|
|
110
|
-
expect(t.display_name).toBe('Get Data');
|
|
111
|
-
});
|
|
112
|
-
it('creates tool with displayName alias', () => {
|
|
113
|
-
const t = tool('get_data').displayName('Get Data').build();
|
|
114
|
-
expect(t.display_name).toBe('Get Data');
|
|
115
|
-
});
|
|
116
|
-
it('returns schema and handler from handler()', async () => {
|
|
117
|
-
const clientTool = tool('greet')
|
|
118
|
-
.describe('Say hi')
|
|
119
|
-
.param('name', string('Name'))
|
|
120
|
-
.handler(async (args) => `Hello, ${args.name}`);
|
|
121
|
-
expect(clientTool.schema.name).toBe('greet');
|
|
122
|
-
expect(await clientTool.handler({ name: 'Ada' })).toBe('Hello, Ada');
|
|
123
|
-
});
|
|
124
|
-
it('creates tool with parameters', () => {
|
|
125
|
-
const t = tool('add')
|
|
126
|
-
.param('a', number('First number'))
|
|
127
|
-
.param('b', number('Second number'))
|
|
128
|
-
.build();
|
|
129
|
-
expect(t.client?.input_schema).toEqual({
|
|
130
|
-
type: 'object',
|
|
131
|
-
properties: {
|
|
132
|
-
a: { type: 'number', description: 'First number' },
|
|
133
|
-
b: { type: 'number', description: 'Second number' },
|
|
134
|
-
},
|
|
135
|
-
required: ['a', 'b'],
|
|
136
|
-
});
|
|
137
|
-
});
|
|
138
|
-
it('handles optional parameters correctly', () => {
|
|
139
|
-
const t = tool('search')
|
|
140
|
-
.param('query', string('Search query'))
|
|
141
|
-
.param('limit', optional(integer('Max results')))
|
|
142
|
-
.build();
|
|
143
|
-
expect(t.client?.input_schema.required).toEqual(['query']);
|
|
144
|
-
expect(t.client?.input_schema.properties).toHaveProperty('limit');
|
|
145
|
-
});
|
|
146
|
-
it('creates tool with require approval', () => {
|
|
147
|
-
const t = tool('dangerous').requireApproval().build();
|
|
148
|
-
expect(t.require_approval).toBe(true);
|
|
149
|
-
});
|
|
150
|
-
it('creates tool with nested object parameters', () => {
|
|
151
|
-
const t = tool('create_user')
|
|
152
|
-
.param('user', object({
|
|
153
|
-
name: string('Name'),
|
|
154
|
-
email: string('Email'),
|
|
155
|
-
}, 'User data'))
|
|
156
|
-
.build();
|
|
157
|
-
const schema = t.client?.input_schema;
|
|
158
|
-
expect(schema?.properties?.user).toMatchObject({
|
|
159
|
-
type: 'object',
|
|
160
|
-
properties: {
|
|
161
|
-
name: { type: 'string', description: 'Name' },
|
|
162
|
-
email: { type: 'string', description: 'Email' },
|
|
163
|
-
},
|
|
164
|
-
required: ['name', 'email'],
|
|
165
|
-
});
|
|
166
|
-
});
|
|
167
|
-
it('propagates required into nested objects with optional fields', () => {
|
|
168
|
-
const t = tool('update_user')
|
|
169
|
-
.param('user', object({
|
|
170
|
-
name: string('Name'),
|
|
171
|
-
bio: optional(string('Bio')),
|
|
172
|
-
}))
|
|
173
|
-
.build();
|
|
174
|
-
const userProp = t.client?.input_schema.properties?.user;
|
|
175
|
-
expect(userProp.required).toEqual(['name']);
|
|
176
|
-
});
|
|
177
|
-
it('creates tool with array parameters', () => {
|
|
178
|
-
const t = tool('process_items')
|
|
179
|
-
.param('items', array(string('Item'), 'List of items'))
|
|
180
|
-
.build();
|
|
181
|
-
expect(t.client?.input_schema.properties?.items).toMatchObject({
|
|
182
|
-
type: 'array',
|
|
183
|
-
items: { type: 'string', description: 'Item' },
|
|
184
|
-
});
|
|
185
|
-
});
|
|
186
|
-
});
|
|
187
|
-
describe('AppToolBuilder (appTool)', () => {
|
|
188
|
-
it('creates app tool with reference', () => {
|
|
189
|
-
const t = appTool('generate', 'infsh/flux@v1.0')
|
|
190
|
-
.describe('Generate image')
|
|
191
|
-
.build();
|
|
192
|
-
expect(t.type).toBe(ToolTypeApp);
|
|
193
|
-
expect(t.app).toEqual({ ref: 'infsh/flux@v1.0' });
|
|
194
|
-
expect(t.description).toBe('Generate image');
|
|
195
|
-
});
|
|
196
|
-
it('handles app reference with latest version', () => {
|
|
197
|
-
const t = appTool('browse', 'my-org/browser@latest').build();
|
|
198
|
-
expect(t.app).toEqual({ ref: 'my-org/browser@latest' });
|
|
199
|
-
});
|
|
200
|
-
it('includes parameters', () => {
|
|
201
|
-
const t = appTool('fetch', 'infsh/fetch@v1')
|
|
202
|
-
.param('url', string('URL to fetch'))
|
|
203
|
-
.build();
|
|
204
|
-
expect(t.name).toBe('fetch');
|
|
205
|
-
// Note: App tools don't use client input_schema, params are for documentation
|
|
206
|
-
});
|
|
207
|
-
it('supports function selection for multi-function apps', () => {
|
|
208
|
-
const t = appTool('upscale', 'infsh/sdxl@v1')
|
|
209
|
-
.function('upscale')
|
|
210
|
-
.describe('Upscale an image')
|
|
211
|
-
.build();
|
|
212
|
-
expect(t.app?.function).toBe('upscale');
|
|
213
|
-
});
|
|
214
|
-
it('supports session enabled for stateful apps', () => {
|
|
215
|
-
const t = appTool('browser', 'infsh/browser-use@v1')
|
|
216
|
-
.sessionEnabled()
|
|
217
|
-
.describe('Control a browser')
|
|
218
|
-
.build();
|
|
219
|
-
expect(t.app?.session_enabled).toBe(true);
|
|
220
|
-
});
|
|
221
|
-
it('supports full configuration with function and session', () => {
|
|
222
|
-
const t = appTool('chat', 'infsh/chatbot@v1')
|
|
223
|
-
.describe('Chat with context')
|
|
224
|
-
.function('stream')
|
|
225
|
-
.sessionEnabled()
|
|
226
|
-
.setup({ api_key: 'secret' })
|
|
227
|
-
.input({ temperature: 0.7 })
|
|
228
|
-
.build();
|
|
229
|
-
expect(t.app).toEqual({
|
|
230
|
-
ref: 'infsh/chatbot@v1',
|
|
231
|
-
function: 'stream',
|
|
232
|
-
session_enabled: true,
|
|
233
|
-
setup: { api_key: 'secret' },
|
|
234
|
-
input: { temperature: 0.7 },
|
|
235
|
-
});
|
|
236
|
-
});
|
|
237
|
-
});
|
|
238
|
-
describe('AgentToolBuilder (agentTool)', () => {
|
|
239
|
-
it('creates agent tool with reference', () => {
|
|
240
|
-
const t = agentTool('research', 'acme/researcher@v2')
|
|
241
|
-
.describe('Research a topic')
|
|
242
|
-
.build();
|
|
243
|
-
expect(t.type).toBe(ToolTypeAgent);
|
|
244
|
-
expect(t.agent).toEqual({ ref: 'acme/researcher@v2' });
|
|
245
|
-
});
|
|
246
|
-
it('supports display name and approval', () => {
|
|
247
|
-
const t = agentTool('coder', 'infsh/code-agent@latest')
|
|
248
|
-
.display('Code Assistant')
|
|
249
|
-
.requireApproval()
|
|
250
|
-
.build();
|
|
251
|
-
expect(t.display_name).toBe('Code Assistant');
|
|
252
|
-
expect(t.require_approval).toBe(true);
|
|
253
|
-
});
|
|
254
|
-
});
|
|
255
|
-
describe('HTTPToolBuilder (httpTool)', () => {
|
|
256
|
-
it('includes custom method and headers in the built tool', () => {
|
|
257
|
-
const t = httpTool('fetch', 'https://api.example.com/data')
|
|
258
|
-
.method('GET')
|
|
259
|
-
.header('X-Custom', '1')
|
|
260
|
-
.build();
|
|
261
|
-
expect(t.http?.method).toBe('GET');
|
|
262
|
-
expect(t.http?.headers).toEqual({ 'X-Custom': '1' });
|
|
263
|
-
});
|
|
264
|
-
it('omits method when POST is the default', () => {
|
|
265
|
-
const t = httpTool('post', 'https://api.example.com').build();
|
|
266
|
-
expect(t.http?.method).toBeUndefined();
|
|
267
|
-
});
|
|
268
|
-
it('should attach integration auth with provider and integration id', () => {
|
|
269
|
-
const t = httpTool('gmail_send', 'https://api.example.com/send')
|
|
270
|
-
.auth({ integration: IntegrationProviderGoogle, integrationId: 'int-123' })
|
|
271
|
-
.build();
|
|
272
|
-
expect(t.type).toBe(ToolTypeHTTP);
|
|
273
|
-
expect(t.http?.auth).toEqual({
|
|
274
|
-
type: 'integration',
|
|
275
|
-
provider: IntegrationProviderGoogle,
|
|
276
|
-
integration_id: 'int-123',
|
|
277
|
-
});
|
|
278
|
-
});
|
|
279
|
-
it('should attach integration auth for google-sa service account provider', () => {
|
|
280
|
-
const t = httpTool('calendar_read', 'https://api.example.com/calendar')
|
|
281
|
-
.auth({ integration: IntegrationProviderGoogleSA, integrationId: 'sa-int-1' })
|
|
282
|
-
.build();
|
|
283
|
-
expect(t.http?.auth).toEqual({
|
|
284
|
-
type: 'integration',
|
|
285
|
-
provider: IntegrationProviderGoogleSA,
|
|
286
|
-
integration_id: 'sa-int-1',
|
|
287
|
-
});
|
|
288
|
-
});
|
|
289
|
-
it('should attach api key auth with default header', () => {
|
|
290
|
-
const t = httpTool('fetch', 'https://api.example.com').auth({ apiKey: 'KEY' }).build();
|
|
291
|
-
expect(t.http?.auth).toEqual({
|
|
292
|
-
type: 'api_key',
|
|
293
|
-
secret: 'KEY',
|
|
294
|
-
header: 'X-API-Key',
|
|
295
|
-
});
|
|
296
|
-
});
|
|
297
|
-
it('should attach bearer auth', () => {
|
|
298
|
-
const t = httpTool('fetch', 'https://api.example.com')
|
|
299
|
-
.auth({ bearer: 'token-abc' })
|
|
300
|
-
.build();
|
|
301
|
-
expect(t.http?.auth).toEqual({
|
|
302
|
-
type: 'bearer',
|
|
303
|
-
secret: 'token-abc',
|
|
304
|
-
});
|
|
305
|
-
});
|
|
306
|
-
it('callTool should be an alias for httpTool', () => {
|
|
307
|
-
const t = callTool('ping', 'https://api.example.com/ping').method('GET').build();
|
|
308
|
-
expect(t.type).toBe(ToolTypeHTTP);
|
|
309
|
-
expect(t.http?.method).toBe('GET');
|
|
310
|
-
});
|
|
311
|
-
});
|
|
312
|
-
describe('MCPToolBuilder (mcpTool)', () => {
|
|
313
|
-
it('creates MCP tool with integration and tool name', () => {
|
|
314
|
-
const t = mcpTool('search_docs', 'int-mcp-1', 'search').describe('Search docs').build();
|
|
315
|
-
expect(t.type).toBe(ToolTypeMCP);
|
|
316
|
-
expect(t.mcp).toEqual({ integration_id: 'int-mcp-1', tool_name: 'search' });
|
|
317
|
-
expect(t.description).toBe('Search docs');
|
|
318
|
-
});
|
|
319
|
-
});
|
|
320
|
-
describe('WebhookToolBuilder (webhookTool)', () => {
|
|
321
|
-
it('creates webhook tool with URL', () => {
|
|
322
|
-
const t = webhookTool('notify', 'https://api.example.com/webhook')
|
|
323
|
-
.describe('Send notification')
|
|
324
|
-
.build();
|
|
325
|
-
expect(t.type).toBe(ToolTypeHook);
|
|
326
|
-
expect(t.hook?.url).toBe('https://api.example.com/webhook');
|
|
327
|
-
expect(t.description).toBe('Send notification');
|
|
328
|
-
});
|
|
329
|
-
it('includes secret key', () => {
|
|
330
|
-
const t = webhookTool('slack', 'https://hooks.slack.com/services/xxx')
|
|
331
|
-
.secret('SLACK_SECRET')
|
|
332
|
-
.build();
|
|
333
|
-
expect(t.hook?.secret).toBe('SLACK_SECRET');
|
|
334
|
-
});
|
|
335
|
-
it('generates input schema for parameters', () => {
|
|
336
|
-
const t = webhookTool('send', 'https://api.example.com')
|
|
337
|
-
.param('message', string('Message'))
|
|
338
|
-
.param('priority', optional(enumOf(['low', 'high'])))
|
|
339
|
-
.build();
|
|
340
|
-
expect(t.hook?.input_schema).toEqual({
|
|
341
|
-
type: 'object',
|
|
342
|
-
properties: {
|
|
343
|
-
message: { type: 'string', description: 'Message' },
|
|
344
|
-
priority: { type: 'string', enum: ['low', 'high'] },
|
|
345
|
-
},
|
|
346
|
-
required: ['message'],
|
|
347
|
-
});
|
|
348
|
-
});
|
|
349
|
-
});
|
|
350
|
-
describe('InternalToolsBuilder (internalTools)', () => {
|
|
351
|
-
it('creates empty config by default', () => {
|
|
352
|
-
const config = internalTools().build();
|
|
353
|
-
expect(config).toEqual({});
|
|
354
|
-
});
|
|
355
|
-
it('enables plan tools', () => {
|
|
356
|
-
const config = internalTools().plan().build();
|
|
357
|
-
expect(config).toEqual({ plan: true });
|
|
358
|
-
});
|
|
359
|
-
it('enables memory tools', () => {
|
|
360
|
-
const config = internalTools().memory().build();
|
|
361
|
-
expect(config).toEqual({ memory: true });
|
|
362
|
-
});
|
|
363
|
-
it('enables widget tools', () => {
|
|
364
|
-
const config = internalTools().widget().build();
|
|
365
|
-
expect(config).toEqual({ widget: true });
|
|
366
|
-
});
|
|
367
|
-
it('enables finish tool', () => {
|
|
368
|
-
const config = internalTools().finish().build();
|
|
369
|
-
expect(config).toEqual({ finish: true });
|
|
370
|
-
});
|
|
371
|
-
it('chains multiple tool enables', () => {
|
|
372
|
-
const config = internalTools().plan().memory().widget().build();
|
|
373
|
-
expect(config).toEqual({ plan: true, memory: true, widget: true });
|
|
374
|
-
});
|
|
375
|
-
it('enables all tools with all()', () => {
|
|
376
|
-
const config = internalTools().all().build();
|
|
377
|
-
expect(config).toEqual({ plan: true, memory: true, widget: true, finish: true });
|
|
378
|
-
});
|
|
379
|
-
it('disables all tools with none()', () => {
|
|
380
|
-
const config = internalTools().none().build();
|
|
381
|
-
expect(config).toEqual({ plan: false, memory: false, widget: false, finish: false });
|
|
382
|
-
});
|
|
383
|
-
it('allows explicit disable', () => {
|
|
384
|
-
const config = internalTools().plan(false).memory(true).build();
|
|
385
|
-
expect(config).toEqual({ plan: false, memory: true });
|
|
386
|
-
});
|
|
387
|
-
});
|
|
388
|
-
describe('Fluent API chaining', () => {
|
|
389
|
-
it('supports full fluent chain', () => {
|
|
390
|
-
const t = tool('complex')
|
|
391
|
-
.display('Complex Tool')
|
|
392
|
-
.describe('A complex tool with many params')
|
|
393
|
-
.param('name', string('Name'))
|
|
394
|
-
.param('count', integer('Count'))
|
|
395
|
-
.param('options', optional(object({
|
|
396
|
-
verbose: boolean('Verbose mode'),
|
|
397
|
-
tags: array(string('Tag')),
|
|
398
|
-
})))
|
|
399
|
-
.requireApproval()
|
|
400
|
-
.build();
|
|
401
|
-
expect(t.name).toBe('complex');
|
|
402
|
-
expect(t.display_name).toBe('Complex Tool');
|
|
403
|
-
expect(t.description).toBe('A complex tool with many params');
|
|
404
|
-
expect(t.require_approval).toBe(true);
|
|
405
|
-
expect(t.client?.input_schema.required).toEqual(['name', 'count']);
|
|
406
|
-
});
|
|
407
|
-
});
|
package/dist/utils.test.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/utils.test.js
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import { TaskStatusCancelled, TaskStatusCompleted, TaskStatusFailed, TaskStatusRunning, TaskStatusUnknown, } from './types';
|
|
2
|
-
import { isTerminalStatus, parseStatus } from './utils';
|
|
3
|
-
describe('parseStatus', () => {
|
|
4
|
-
it('should return TaskStatusUnknown for null and undefined', () => {
|
|
5
|
-
expect(parseStatus(null)).toBe(TaskStatusUnknown);
|
|
6
|
-
expect(parseStatus(undefined)).toBe(TaskStatusUnknown);
|
|
7
|
-
});
|
|
8
|
-
it('should pass through numeric status values', () => {
|
|
9
|
-
expect(parseStatus(TaskStatusRunning)).toBe(TaskStatusRunning);
|
|
10
|
-
expect(parseStatus(TaskStatusCompleted)).toBe(TaskStatusCompleted);
|
|
11
|
-
});
|
|
12
|
-
it('should map lowercase string status names to TaskStatus', () => {
|
|
13
|
-
expect(parseStatus('running')).toBe(TaskStatusRunning);
|
|
14
|
-
expect(parseStatus('completed')).toBe(TaskStatusCompleted);
|
|
15
|
-
expect(parseStatus('failed')).toBe(TaskStatusFailed);
|
|
16
|
-
expect(parseStatus('cancelled')).toBe(TaskStatusCancelled);
|
|
17
|
-
});
|
|
18
|
-
it('should be case-insensitive for string statuses', () => {
|
|
19
|
-
expect(parseStatus('COMPLETED')).toBe(TaskStatusCompleted);
|
|
20
|
-
expect(parseStatus('Running')).toBe(TaskStatusRunning);
|
|
21
|
-
});
|
|
22
|
-
it('should return TaskStatusUnknown for unrecognized strings', () => {
|
|
23
|
-
expect(parseStatus('not_a_real_status')).toBe(TaskStatusUnknown);
|
|
24
|
-
});
|
|
25
|
-
it('should return TaskStatusUnknown for non-string non-number values', () => {
|
|
26
|
-
expect(parseStatus(true)).toBe(TaskStatusUnknown);
|
|
27
|
-
});
|
|
28
|
-
});
|
|
29
|
-
describe('isTerminalStatus', () => {
|
|
30
|
-
it('should be true for completed, failed, and cancelled (int or string)', () => {
|
|
31
|
-
expect(isTerminalStatus(TaskStatusCompleted)).toBe(true);
|
|
32
|
-
expect(isTerminalStatus(TaskStatusFailed)).toBe(true);
|
|
33
|
-
expect(isTerminalStatus(TaskStatusCancelled)).toBe(true);
|
|
34
|
-
expect(isTerminalStatus('completed')).toBe(true);
|
|
35
|
-
expect(isTerminalStatus('failed')).toBe(true);
|
|
36
|
-
expect(isTerminalStatus('cancelled')).toBe(true);
|
|
37
|
-
});
|
|
38
|
-
it('should be false for non-terminal statuses', () => {
|
|
39
|
-
expect(isTerminalStatus(TaskStatusRunning)).toBe(false);
|
|
40
|
-
expect(isTerminalStatus('running')).toBe(false);
|
|
41
|
-
expect(isTerminalStatus(null)).toBe(false);
|
|
42
|
-
});
|
|
43
|
-
});
|