@inkeep/agents-cli 0.1.0
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/LICENSE.md +51 -0
- package/README.md +512 -0
- package/dist/__tests__/api.test.d.ts +1 -0
- package/dist/__tests__/api.test.js +257 -0
- package/dist/__tests__/cli.test.d.ts +1 -0
- package/dist/__tests__/cli.test.js +153 -0
- package/dist/__tests__/commands/config.test.d.ts +1 -0
- package/dist/__tests__/commands/config.test.js +154 -0
- package/dist/__tests__/commands/init.test.d.ts +1 -0
- package/dist/__tests__/commands/init.test.js +186 -0
- package/dist/__tests__/commands/pull-retry.test.d.ts +1 -0
- package/dist/__tests__/commands/pull-retry.test.js +156 -0
- package/dist/__tests__/commands/pull.test.d.ts +1 -0
- package/dist/__tests__/commands/pull.test.js +54 -0
- package/dist/__tests__/commands/push-spinner.test.d.ts +1 -0
- package/dist/__tests__/commands/push-spinner.test.js +127 -0
- package/dist/__tests__/commands/push.test.d.ts +1 -0
- package/dist/__tests__/commands/push.test.js +265 -0
- package/dist/__tests__/config-validation.test.d.ts +1 -0
- package/dist/__tests__/config-validation.test.js +106 -0
- package/dist/__tests__/package.test.d.ts +1 -0
- package/dist/__tests__/package.test.js +82 -0
- package/dist/__tests__/utils/json-comparator.test.d.ts +1 -0
- package/dist/__tests__/utils/json-comparator.test.js +174 -0
- package/dist/__tests__/utils/port-manager.test.d.ts +1 -0
- package/dist/__tests__/utils/port-manager.test.js +144 -0
- package/dist/__tests__/utils/ts-loader.test.d.ts +1 -0
- package/dist/__tests__/utils/ts-loader.test.js +233 -0
- package/dist/api.d.ts +23 -0
- package/dist/api.js +140 -0
- package/dist/commands/chat-enhanced.d.ts +7 -0
- package/dist/commands/chat-enhanced.js +396 -0
- package/dist/commands/chat.d.ts +5 -0
- package/dist/commands/chat.js +125 -0
- package/dist/commands/config.d.ts +6 -0
- package/dist/commands/config.js +128 -0
- package/dist/commands/init.d.ts +5 -0
- package/dist/commands/init.js +171 -0
- package/dist/commands/list-graphs.d.ts +6 -0
- package/dist/commands/list-graphs.js +131 -0
- package/dist/commands/mcp-list.d.ts +4 -0
- package/dist/commands/mcp-list.js +156 -0
- package/dist/commands/mcp-start-simple.d.ts +5 -0
- package/dist/commands/mcp-start-simple.js +193 -0
- package/dist/commands/mcp-start.d.ts +5 -0
- package/dist/commands/mcp-start.js +217 -0
- package/dist/commands/mcp-status.d.ts +1 -0
- package/dist/commands/mcp-status.js +96 -0
- package/dist/commands/mcp-stop.d.ts +5 -0
- package/dist/commands/mcp-stop.js +160 -0
- package/dist/commands/pull.d.ts +15 -0
- package/dist/commands/pull.js +313 -0
- package/dist/commands/pull.llm-generate.d.ts +10 -0
- package/dist/commands/pull.llm-generate.js +184 -0
- package/dist/commands/push.d.ts +6 -0
- package/dist/commands/push.js +268 -0
- package/dist/config.d.ts +43 -0
- package/dist/config.js +292 -0
- package/dist/exports.d.ts +2 -0
- package/dist/exports.js +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +98 -0
- package/dist/types/config.d.ts +9 -0
- package/dist/types/config.js +3 -0
- package/dist/types/graph.d.ts +10 -0
- package/dist/types/graph.js +1 -0
- package/dist/utils/json-comparator.d.ts +60 -0
- package/dist/utils/json-comparator.js +222 -0
- package/dist/utils/mcp-runner.d.ts +6 -0
- package/dist/utils/mcp-runner.js +147 -0
- package/dist/utils/port-manager.d.ts +43 -0
- package/dist/utils/port-manager.js +92 -0
- package/dist/utils/ts-loader.d.ts +5 -0
- package/dist/utils/ts-loader.js +146 -0
- package/package.json +77 -0
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { createServer } from 'node:net';
|
|
2
|
+
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
3
|
+
import { PortManager } from '../../utils/port-manager.js';
|
|
4
|
+
describe('PortManager', () => {
|
|
5
|
+
let portManager;
|
|
6
|
+
let allocatedPorts = [];
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
portManager = PortManager.getInstance();
|
|
9
|
+
// Clean up any existing allocations
|
|
10
|
+
portManager.releaseAll();
|
|
11
|
+
allocatedPorts = [];
|
|
12
|
+
});
|
|
13
|
+
afterEach(() => {
|
|
14
|
+
// Clean up any ports we allocated during tests
|
|
15
|
+
for (const port of allocatedPorts) {
|
|
16
|
+
portManager.releasePort(port);
|
|
17
|
+
}
|
|
18
|
+
portManager.releaseAll();
|
|
19
|
+
});
|
|
20
|
+
describe('getInstance', () => {
|
|
21
|
+
it('should return a singleton instance', () => {
|
|
22
|
+
const instance1 = PortManager.getInstance();
|
|
23
|
+
const instance2 = PortManager.getInstance();
|
|
24
|
+
expect(instance1).toBe(instance2);
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
describe('allocatePort', () => {
|
|
28
|
+
it('should allocate a port in the valid range', async () => {
|
|
29
|
+
const port = await portManager.allocatePort();
|
|
30
|
+
allocatedPorts.push(port);
|
|
31
|
+
expect(port).toBeGreaterThanOrEqual(3100);
|
|
32
|
+
expect(port).toBeLessThanOrEqual(3200);
|
|
33
|
+
expect(portManager.getAllocatedPorts()).toContain(port);
|
|
34
|
+
});
|
|
35
|
+
it('should allocate preferred port when available', async () => {
|
|
36
|
+
const preferredPort = 3150;
|
|
37
|
+
const port = await portManager.allocatePort(preferredPort);
|
|
38
|
+
allocatedPorts.push(port);
|
|
39
|
+
expect(port).toBe(preferredPort);
|
|
40
|
+
expect(portManager.getAllocatedPorts()).toContain(port);
|
|
41
|
+
});
|
|
42
|
+
it('should allocate different port when preferred port is not available', async () => {
|
|
43
|
+
// Create a server on preferred port to make it unavailable
|
|
44
|
+
const preferredPort = 3151;
|
|
45
|
+
const server = createServer();
|
|
46
|
+
return new Promise((resolve) => {
|
|
47
|
+
server.listen(preferredPort, '127.0.0.1', async () => {
|
|
48
|
+
try {
|
|
49
|
+
const port = await portManager.allocatePort(preferredPort);
|
|
50
|
+
allocatedPorts.push(port);
|
|
51
|
+
expect(port).not.toBe(preferredPort);
|
|
52
|
+
expect(port).toBeGreaterThanOrEqual(3100);
|
|
53
|
+
expect(port).toBeLessThanOrEqual(3200);
|
|
54
|
+
server.close(() => resolve());
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
server.close(() => {
|
|
58
|
+
throw error;
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
it('should not allocate the same port twice', async () => {
|
|
65
|
+
const port1 = await portManager.allocatePort();
|
|
66
|
+
const port2 = await portManager.allocatePort();
|
|
67
|
+
allocatedPorts.push(port1, port2);
|
|
68
|
+
expect(port1).not.toBe(port2);
|
|
69
|
+
expect(portManager.getAllocatedPorts()).toContain(port1);
|
|
70
|
+
expect(portManager.getAllocatedPorts()).toContain(port2);
|
|
71
|
+
});
|
|
72
|
+
it('should throw error when no ports are available', async () => {
|
|
73
|
+
// Allocate all ports in the range (101 ports: 3100-3200 inclusive)
|
|
74
|
+
const allPorts = [];
|
|
75
|
+
for (let i = 3100; i <= 3200; i++) {
|
|
76
|
+
try {
|
|
77
|
+
const port = await portManager.allocatePort();
|
|
78
|
+
allPorts.push(port);
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
break; // Stop when we can't allocate more
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
allocatedPorts.push(...allPorts);
|
|
85
|
+
// Try to allocate one more port
|
|
86
|
+
await expect(portManager.allocatePort()).rejects.toThrow('No available ports in range 3100-3200');
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
describe('releasePort', () => {
|
|
90
|
+
it('should release an allocated port', async () => {
|
|
91
|
+
const port = await portManager.allocatePort();
|
|
92
|
+
expect(portManager.getAllocatedPorts()).toContain(port);
|
|
93
|
+
portManager.releasePort(port);
|
|
94
|
+
expect(portManager.getAllocatedPorts()).not.toContain(port);
|
|
95
|
+
});
|
|
96
|
+
it('should allow releasing a non-allocated port without error', () => {
|
|
97
|
+
expect(() => portManager.releasePort(9999)).not.toThrow();
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
describe('releaseAll', () => {
|
|
101
|
+
it('should release all allocated ports', async () => {
|
|
102
|
+
const port1 = await portManager.allocatePort();
|
|
103
|
+
const port2 = await portManager.allocatePort();
|
|
104
|
+
expect(portManager.getAllocatedPorts()).toHaveLength(2);
|
|
105
|
+
portManager.releaseAll();
|
|
106
|
+
expect(portManager.getAllocatedPorts()).toHaveLength(0);
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
describe('getAllocatedPorts', () => {
|
|
110
|
+
it('should return empty array when no ports allocated', () => {
|
|
111
|
+
expect(portManager.getAllocatedPorts()).toEqual([]);
|
|
112
|
+
});
|
|
113
|
+
it('should return sorted array of allocated ports', async () => {
|
|
114
|
+
const port1 = await portManager.allocatePort(3120);
|
|
115
|
+
const port2 = await portManager.allocatePort(3110);
|
|
116
|
+
allocatedPorts.push(port1, port2);
|
|
117
|
+
const allocated = portManager.getAllocatedPorts();
|
|
118
|
+
expect(allocated).toEqual([3110, 3120]);
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
describe('getStats', () => {
|
|
122
|
+
it('should return correct statistics when no ports allocated', () => {
|
|
123
|
+
const stats = portManager.getStats();
|
|
124
|
+
expect(stats).toEqual({
|
|
125
|
+
allocated: 0,
|
|
126
|
+
available: 101, // 3100-3200 inclusive = 101 ports
|
|
127
|
+
range: { min: 3100, max: 3200 },
|
|
128
|
+
ports: [],
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
it('should return correct statistics with allocated ports', async () => {
|
|
132
|
+
const port1 = await portManager.allocatePort();
|
|
133
|
+
const port2 = await portManager.allocatePort();
|
|
134
|
+
allocatedPorts.push(port1, port2);
|
|
135
|
+
const stats = portManager.getStats();
|
|
136
|
+
expect(stats.allocated).toBe(2);
|
|
137
|
+
expect(stats.available).toBe(99);
|
|
138
|
+
expect(stats.range).toEqual({ min: 3100, max: 3200 });
|
|
139
|
+
expect(stats.ports).toHaveLength(2);
|
|
140
|
+
expect(stats.ports).toContain(port1);
|
|
141
|
+
expect(stats.ports).toContain(port2);
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, rmSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { tmpdir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
5
|
+
import { loadTypeScriptModule } from '../../utils/ts-loader.js';
|
|
6
|
+
describe('TypeScript Loader', () => {
|
|
7
|
+
let testDir;
|
|
8
|
+
let testGraphFile;
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
// Create a unique test directory with process ID to avoid conflicts
|
|
11
|
+
const uniqueId = `${process.pid}-${Date.now()}-${Math.random().toString(36).substring(7)}`;
|
|
12
|
+
testDir = join(tmpdir(), 'ts-loader-test', uniqueId);
|
|
13
|
+
mkdirSync(testDir, { recursive: true });
|
|
14
|
+
testGraphFile = join(testDir, 'test-graph.ts');
|
|
15
|
+
});
|
|
16
|
+
afterEach(async () => {
|
|
17
|
+
// Small delay to ensure file handles are released
|
|
18
|
+
await new Promise(resolve => setTimeout(resolve, 50));
|
|
19
|
+
// Clean up test directory with retries
|
|
20
|
+
if (existsSync(testDir)) {
|
|
21
|
+
rmSync(testDir, { recursive: true, force: true, maxRetries: 3 });
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
describe('loadTypeScriptModule', () => {
|
|
25
|
+
it('should load a simple TypeScript module with exports', async () => {
|
|
26
|
+
// Create a simple test module
|
|
27
|
+
const moduleContent = `
|
|
28
|
+
export const simpleValue = 'test-value';
|
|
29
|
+
export const numberValue = 42;
|
|
30
|
+
export const booleanValue = true;
|
|
31
|
+
|
|
32
|
+
export function testFunction() {
|
|
33
|
+
return 'hello world';
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export const testObject = {
|
|
37
|
+
id: 'test-id',
|
|
38
|
+
name: 'Test Object',
|
|
39
|
+
description: 'A test object for testing',
|
|
40
|
+
port: 3000,
|
|
41
|
+
};
|
|
42
|
+
`;
|
|
43
|
+
writeFileSync(testGraphFile, moduleContent);
|
|
44
|
+
const result = await loadTypeScriptModule(testGraphFile);
|
|
45
|
+
expect(result.simpleValue).toBe('test-value');
|
|
46
|
+
expect(result.numberValue).toBe(42);
|
|
47
|
+
expect(result.booleanValue).toBe(true);
|
|
48
|
+
expect(result.testFunction).toEqual({
|
|
49
|
+
__type: 'function',
|
|
50
|
+
name: 'testFunction',
|
|
51
|
+
});
|
|
52
|
+
expect(result.testObject).toEqual({
|
|
53
|
+
__type: 'object',
|
|
54
|
+
id: 'test-id',
|
|
55
|
+
name: 'Test Object',
|
|
56
|
+
description: 'A test object for testing',
|
|
57
|
+
port: 3000,
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
it('should handle array exports', async () => {
|
|
61
|
+
const moduleContent = `
|
|
62
|
+
export const servers = [
|
|
63
|
+
{
|
|
64
|
+
id: 'server1',
|
|
65
|
+
name: 'Test Server 1',
|
|
66
|
+
port: 3001,
|
|
67
|
+
deployment: 'local'
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
id: 'server2',
|
|
71
|
+
name: 'Test Server 2',
|
|
72
|
+
serverUrl: 'http://localhost:3002',
|
|
73
|
+
deployment: 'remote'
|
|
74
|
+
}
|
|
75
|
+
];
|
|
76
|
+
|
|
77
|
+
export const simpleArray = ['item1', 'item2', 'item3'];
|
|
78
|
+
`;
|
|
79
|
+
writeFileSync(testGraphFile, moduleContent);
|
|
80
|
+
const result = await loadTypeScriptModule(testGraphFile);
|
|
81
|
+
expect(result.servers.__type).toBe('array');
|
|
82
|
+
expect(result.servers.items).toHaveLength(2);
|
|
83
|
+
expect(result.servers.items[0]).toEqual({
|
|
84
|
+
id: 'server1',
|
|
85
|
+
name: 'Test Server 1',
|
|
86
|
+
port: 3001,
|
|
87
|
+
deployment: 'local',
|
|
88
|
+
});
|
|
89
|
+
expect(result.servers.items[1]).toEqual({
|
|
90
|
+
id: 'server2',
|
|
91
|
+
name: 'Test Server 2',
|
|
92
|
+
serverUrl: 'http://localhost:3002',
|
|
93
|
+
deployment: 'remote',
|
|
94
|
+
});
|
|
95
|
+
expect(result.simpleArray.__type).toBe('array');
|
|
96
|
+
expect(result.simpleArray.items).toEqual(['item1', 'item2', 'item3']);
|
|
97
|
+
});
|
|
98
|
+
it('should handle objects with methods', async () => {
|
|
99
|
+
const moduleContent = `
|
|
100
|
+
export const toolWithMethods = {
|
|
101
|
+
id: 'tool-with-methods',
|
|
102
|
+
name: 'Tool With Methods',
|
|
103
|
+
|
|
104
|
+
execute() {
|
|
105
|
+
return 'executed';
|
|
106
|
+
},
|
|
107
|
+
|
|
108
|
+
init() {
|
|
109
|
+
return 'initialized';
|
|
110
|
+
},
|
|
111
|
+
|
|
112
|
+
getServerUrl() {
|
|
113
|
+
return 'http://localhost:3000';
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
export const graphObject = {
|
|
118
|
+
getId() {
|
|
119
|
+
return 'test-graph-id';
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
`;
|
|
123
|
+
writeFileSync(testGraphFile, moduleContent);
|
|
124
|
+
const result = await loadTypeScriptModule(testGraphFile);
|
|
125
|
+
expect(result.toolWithMethods).toEqual({
|
|
126
|
+
__type: 'object',
|
|
127
|
+
id: 'tool-with-methods',
|
|
128
|
+
name: 'Tool With Methods',
|
|
129
|
+
hasExecute: true,
|
|
130
|
+
hasInit: true,
|
|
131
|
+
hasGetServerUrl: true,
|
|
132
|
+
});
|
|
133
|
+
expect(result.graphObject).toEqual({
|
|
134
|
+
__type: 'object',
|
|
135
|
+
graphId: 'test-graph-id',
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
it('should handle module loading errors gracefully', async () => {
|
|
139
|
+
// Create a module with syntax errors
|
|
140
|
+
const moduleContent = `
|
|
141
|
+
export const badSyntax = {
|
|
142
|
+
// Missing closing brace
|
|
143
|
+
id: 'test'
|
|
144
|
+
`;
|
|
145
|
+
writeFileSync(testGraphFile, moduleContent);
|
|
146
|
+
await expect(loadTypeScriptModule(testGraphFile)).rejects.toThrow();
|
|
147
|
+
});
|
|
148
|
+
it('should handle non-existent files', async () => {
|
|
149
|
+
const nonExistentFile = join(testDir, 'non-existent.ts');
|
|
150
|
+
await expect(loadTypeScriptModule(nonExistentFile)).rejects.toThrow();
|
|
151
|
+
});
|
|
152
|
+
it('should handle empty modules', async () => {
|
|
153
|
+
writeFileSync(testGraphFile, '');
|
|
154
|
+
const result = await loadTypeScriptModule(testGraphFile);
|
|
155
|
+
// Empty modules may have a default export object
|
|
156
|
+
expect(result).toEqual(expect.objectContaining({
|
|
157
|
+
default: expect.objectContaining({
|
|
158
|
+
__type: 'object',
|
|
159
|
+
}),
|
|
160
|
+
}));
|
|
161
|
+
});
|
|
162
|
+
it('should handle complex nested objects', async () => {
|
|
163
|
+
const moduleContent = `
|
|
164
|
+
export const complexConfig = {
|
|
165
|
+
servers: [
|
|
166
|
+
{
|
|
167
|
+
id: 'nested-server',
|
|
168
|
+
name: 'Nested Server',
|
|
169
|
+
transport: 'http',
|
|
170
|
+
deployment: 'local'
|
|
171
|
+
}
|
|
172
|
+
],
|
|
173
|
+
metadata: {
|
|
174
|
+
version: '1.0.0',
|
|
175
|
+
author: 'test'
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
`;
|
|
179
|
+
writeFileSync(testGraphFile, moduleContent);
|
|
180
|
+
const result = await loadTypeScriptModule(testGraphFile);
|
|
181
|
+
expect(result.complexConfig.__type).toBe('object');
|
|
182
|
+
// Note: The loader has specific handling for certain property names
|
|
183
|
+
// It may not serialize arbitrary nested structures completely
|
|
184
|
+
});
|
|
185
|
+
it('should use test environment defaults when no environment is set', async () => {
|
|
186
|
+
// Clear environment variables that might affect the test
|
|
187
|
+
const originalEnv = process.env.ENVIRONMENT;
|
|
188
|
+
delete process.env.ENVIRONMENT;
|
|
189
|
+
const moduleContent = `
|
|
190
|
+
export const envValue = process.env.ENVIRONMENT;
|
|
191
|
+
export const dbFileName = process.env.DB_FILE_NAME;
|
|
192
|
+
export const tenantId = process.env.INKEEP_TENANT_ID;
|
|
193
|
+
`;
|
|
194
|
+
writeFileSync(testGraphFile, moduleContent);
|
|
195
|
+
try {
|
|
196
|
+
const result = await loadTypeScriptModule(testGraphFile);
|
|
197
|
+
// The loader should set test defaults
|
|
198
|
+
expect(result.envValue).toBe('test');
|
|
199
|
+
expect(result.dbFileName).toBe(':memory:');
|
|
200
|
+
expect(result.tenantId).toBe('test-tenant');
|
|
201
|
+
}
|
|
202
|
+
finally {
|
|
203
|
+
if (originalEnv !== undefined) {
|
|
204
|
+
process.env.ENVIRONMENT = originalEnv;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
it('should preserve existing environment variables', async () => {
|
|
209
|
+
const originalEnv = process.env.ENVIRONMENT;
|
|
210
|
+
process.env.ENVIRONMENT = 'production';
|
|
211
|
+
process.env.CUSTOM_VAR = 'custom-value';
|
|
212
|
+
const moduleContent = `
|
|
213
|
+
export const envValue = process.env.ENVIRONMENT;
|
|
214
|
+
export const customVar = process.env.CUSTOM_VAR;
|
|
215
|
+
`;
|
|
216
|
+
writeFileSync(testGraphFile, moduleContent);
|
|
217
|
+
try {
|
|
218
|
+
const result = await loadTypeScriptModule(testGraphFile);
|
|
219
|
+
expect(result.envValue).toBe('production');
|
|
220
|
+
expect(result.customVar).toBe('custom-value');
|
|
221
|
+
}
|
|
222
|
+
finally {
|
|
223
|
+
if (originalEnv !== undefined) {
|
|
224
|
+
process.env.ENVIRONMENT = originalEnv;
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
227
|
+
delete process.env.ENVIRONMENT;
|
|
228
|
+
}
|
|
229
|
+
delete process.env.CUSTOM_VAR;
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
});
|
package/dist/api.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
declare abstract class BaseApiClient {
|
|
2
|
+
protected apiUrl: string;
|
|
3
|
+
protected tenantId: string | undefined;
|
|
4
|
+
protected projectId: string;
|
|
5
|
+
protected constructor(apiUrl: string, tenantId: string | undefined, projectId: string);
|
|
6
|
+
protected checkTenantId(): string;
|
|
7
|
+
getTenantId(): string | undefined;
|
|
8
|
+
getProjectId(): string;
|
|
9
|
+
getApiUrl(): string;
|
|
10
|
+
}
|
|
11
|
+
export declare class ManagementApiClient extends BaseApiClient {
|
|
12
|
+
private constructor();
|
|
13
|
+
static create(apiUrl?: string, configPath?: string, tenantIdOverride?: string, projectIdOverride?: string): Promise<ManagementApiClient>;
|
|
14
|
+
listGraphs(): Promise<any[]>;
|
|
15
|
+
getGraph(graphId: string): Promise<any>;
|
|
16
|
+
pushGraph(graphDefinition: any): Promise<any>;
|
|
17
|
+
}
|
|
18
|
+
export declare class ExecutionApiClient extends BaseApiClient {
|
|
19
|
+
private constructor();
|
|
20
|
+
static create(apiUrl?: string, configPath?: string, tenantIdOverride?: string, projectIdOverride?: string): Promise<ExecutionApiClient>;
|
|
21
|
+
chatCompletion(graphId: string, messages: any[], conversationId?: string): Promise<ReadableStream<Uint8Array> | string>;
|
|
22
|
+
}
|
|
23
|
+
export {};
|
package/dist/api.js
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { getManagementApiUrl, getExecutionApiUrl, getProjectId, getTenantId } from './config.js';
|
|
2
|
+
class BaseApiClient {
|
|
3
|
+
apiUrl;
|
|
4
|
+
tenantId;
|
|
5
|
+
projectId;
|
|
6
|
+
constructor(apiUrl, tenantId, projectId) {
|
|
7
|
+
this.apiUrl = apiUrl;
|
|
8
|
+
this.tenantId = tenantId;
|
|
9
|
+
this.projectId = projectId;
|
|
10
|
+
}
|
|
11
|
+
checkTenantId() {
|
|
12
|
+
if (!this.tenantId) {
|
|
13
|
+
throw new Error('No tenant ID configured. Please run: inkeep init');
|
|
14
|
+
}
|
|
15
|
+
return this.tenantId;
|
|
16
|
+
}
|
|
17
|
+
getTenantId() {
|
|
18
|
+
return this.tenantId;
|
|
19
|
+
}
|
|
20
|
+
getProjectId() {
|
|
21
|
+
return this.projectId;
|
|
22
|
+
}
|
|
23
|
+
getApiUrl() {
|
|
24
|
+
return this.apiUrl;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
export class ManagementApiClient extends BaseApiClient {
|
|
28
|
+
constructor(apiUrl, tenantId, projectId) {
|
|
29
|
+
super(apiUrl, tenantId, projectId);
|
|
30
|
+
}
|
|
31
|
+
static async create(apiUrl, configPath, tenantIdOverride, projectIdOverride) {
|
|
32
|
+
const resolvedApiUrl = await getManagementApiUrl(apiUrl, configPath);
|
|
33
|
+
const tenantId = tenantIdOverride || (await getTenantId(configPath));
|
|
34
|
+
const projectId = projectIdOverride || (await getProjectId(configPath));
|
|
35
|
+
return new ManagementApiClient(resolvedApiUrl, tenantId, projectId);
|
|
36
|
+
}
|
|
37
|
+
async listGraphs() {
|
|
38
|
+
const tenantId = this.checkTenantId();
|
|
39
|
+
const projectId = this.getProjectId();
|
|
40
|
+
const response = await fetch(`${this.apiUrl}/tenants/${tenantId}/crud/projects/${projectId}/agent-graphs`, {
|
|
41
|
+
method: 'GET',
|
|
42
|
+
headers: {
|
|
43
|
+
'Content-Type': 'application/json',
|
|
44
|
+
...(process.env.INKEEP_AGENTS_MANAGE_API_SECRET && {
|
|
45
|
+
Authorization: `Bearer ${process.env.INKEEP_AGENTS_MANAGE_API_SECRET}`,
|
|
46
|
+
}),
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
if (!response.ok) {
|
|
50
|
+
throw new Error(`Failed to list graphs: ${response.statusText}`);
|
|
51
|
+
}
|
|
52
|
+
const data = await response.json();
|
|
53
|
+
return data.data || [];
|
|
54
|
+
}
|
|
55
|
+
async getGraph(graphId) {
|
|
56
|
+
// Since there's no dedicated GET endpoint for graphs,
|
|
57
|
+
// we check if the graph exists in the CRUD endpoint
|
|
58
|
+
const graphs = await this.listGraphs();
|
|
59
|
+
const graph = graphs.find((g) => g.id === graphId);
|
|
60
|
+
// If found in CRUD, return it as a valid graph
|
|
61
|
+
// The graph is usable for chat even without a dedicated GET endpoint
|
|
62
|
+
return graph || null;
|
|
63
|
+
}
|
|
64
|
+
async pushGraph(graphDefinition) {
|
|
65
|
+
const tenantId = this.checkTenantId();
|
|
66
|
+
const projectId = this.getProjectId();
|
|
67
|
+
// Ensure the graph has the correct tenant ID
|
|
68
|
+
graphDefinition.tenantId = tenantId;
|
|
69
|
+
const graphId = graphDefinition.id;
|
|
70
|
+
if (!graphId) {
|
|
71
|
+
throw new Error('Graph must have an id property');
|
|
72
|
+
}
|
|
73
|
+
// Try to update first using PUT, if it doesn't exist, it will create it
|
|
74
|
+
const response = await fetch(`${this.apiUrl}/tenants/${tenantId}/crud/projects/${projectId}/graph/${graphId}`, {
|
|
75
|
+
method: 'PUT',
|
|
76
|
+
headers: {
|
|
77
|
+
'Content-Type': 'application/json',
|
|
78
|
+
...(process.env.INKEEP_AGENTS_MANAGE_API_SECRET && {
|
|
79
|
+
Authorization: `Bearer ${process.env.INKEEP_AGENTS_MANAGE_API_SECRET}`,
|
|
80
|
+
}),
|
|
81
|
+
},
|
|
82
|
+
body: JSON.stringify(graphDefinition),
|
|
83
|
+
});
|
|
84
|
+
if (!response.ok) {
|
|
85
|
+
const errorText = await response.text();
|
|
86
|
+
throw new Error(`Failed to push graph: ${response.statusText}\n${errorText}`);
|
|
87
|
+
}
|
|
88
|
+
const data = await response.json();
|
|
89
|
+
return data.data;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
export class ExecutionApiClient extends BaseApiClient {
|
|
93
|
+
constructor(apiUrl, tenantId, projectId) {
|
|
94
|
+
super(apiUrl, tenantId, projectId);
|
|
95
|
+
}
|
|
96
|
+
static async create(apiUrl, configPath, tenantIdOverride, projectIdOverride) {
|
|
97
|
+
const resolvedApiUrl = await getExecutionApiUrl(apiUrl, configPath);
|
|
98
|
+
const tenantId = tenantIdOverride || (await getTenantId(configPath));
|
|
99
|
+
const projectId = projectIdOverride || (await getProjectId(configPath));
|
|
100
|
+
return new ExecutionApiClient(resolvedApiUrl, tenantId, projectId);
|
|
101
|
+
}
|
|
102
|
+
async chatCompletion(graphId, messages, conversationId) {
|
|
103
|
+
const response = await fetch(`${this.apiUrl}/v1/chat/completions`, {
|
|
104
|
+
method: 'POST',
|
|
105
|
+
headers: {
|
|
106
|
+
'Content-Type': 'application/json',
|
|
107
|
+
Accept: 'text/event-stream',
|
|
108
|
+
...(process.env.INKEEP_AGENTS_RUN_BYPASS_SECRET && {
|
|
109
|
+
Authorization: `Bearer ${process.env.INKEEP_AGENTS_RUN_BYPASS_SECRET}`,
|
|
110
|
+
}),
|
|
111
|
+
'x-inkeep-tenant-id': this.tenantId || 'test-tenant-id',
|
|
112
|
+
'x-inkeep-project-id': this.projectId,
|
|
113
|
+
'x-inkeep-graph-id': graphId,
|
|
114
|
+
},
|
|
115
|
+
body: JSON.stringify({
|
|
116
|
+
model: 'gpt-4o-mini', // Required but will be overridden by graph config
|
|
117
|
+
messages,
|
|
118
|
+
conversationId,
|
|
119
|
+
stream: true,
|
|
120
|
+
}),
|
|
121
|
+
});
|
|
122
|
+
if (!response.ok) {
|
|
123
|
+
const errorText = await response.text();
|
|
124
|
+
throw new Error(`Chat request failed: ${response.statusText}\n${errorText}`);
|
|
125
|
+
}
|
|
126
|
+
// Check if response is streaming
|
|
127
|
+
const contentType = response.headers.get('content-type');
|
|
128
|
+
if (contentType?.includes('text/event-stream')) {
|
|
129
|
+
if (!response.body) {
|
|
130
|
+
throw new Error('No response body for streaming request');
|
|
131
|
+
}
|
|
132
|
+
return response.body;
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
// Non-streaming response
|
|
136
|
+
const data = await response.json();
|
|
137
|
+
return data.choices?.[0]?.message?.content || data.result || '';
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|