@inkeep/agents-cli 0.1.5 → 0.1.7
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/SUPPLEMENTAL_TERMS.md +40 -0
- package/dist/commands/create.d.ts +12 -0
- package/dist/commands/create.js +869 -0
- package/dist/config.d.ts +4 -4
- package/dist/index.js +5346 -20414
- package/package.json +15 -6
- package/dist/__tests__/api.test.d.ts +0 -1
- package/dist/__tests__/api.test.js +0 -257
- package/dist/__tests__/cli.test.d.ts +0 -1
- package/dist/__tests__/cli.test.js +0 -153
- package/dist/__tests__/commands/config.test.d.ts +0 -1
- package/dist/__tests__/commands/config.test.js +0 -154
- package/dist/__tests__/commands/init.test.d.ts +0 -1
- package/dist/__tests__/commands/init.test.js +0 -186
- package/dist/__tests__/commands/pull.test.d.ts +0 -1
- package/dist/__tests__/commands/pull.test.js +0 -54
- package/dist/__tests__/commands/push-spinner.test.d.ts +0 -1
- package/dist/__tests__/commands/push-spinner.test.js +0 -127
- package/dist/__tests__/commands/push.test.d.ts +0 -1
- package/dist/__tests__/commands/push.test.js +0 -265
- package/dist/__tests__/config-validation.test.d.ts +0 -1
- package/dist/__tests__/config-validation.test.js +0 -98
- package/dist/__tests__/package.test.d.ts +0 -1
- package/dist/__tests__/package.test.js +0 -82
- package/dist/__tests__/utils/json-comparator.test.d.ts +0 -1
- package/dist/__tests__/utils/json-comparator.test.js +0 -174
- package/dist/__tests__/utils/ts-loader.test.d.ts +0 -1
- package/dist/__tests__/utils/ts-loader.test.js +0 -232
- package/dist/api.d.ts +0 -23
- package/dist/api.js +0 -140
- package/dist/commands/chat-enhanced.d.ts +0 -7
- package/dist/commands/chat-enhanced.js +0 -396
- package/dist/commands/chat.d.ts +0 -5
- package/dist/commands/chat.js +0 -125
- package/dist/commands/config.d.ts +0 -6
- package/dist/commands/config.js +0 -128
- package/dist/commands/init.d.ts +0 -5
- package/dist/commands/init.js +0 -171
- package/dist/commands/list-graphs.d.ts +0 -6
- package/dist/commands/list-graphs.js +0 -131
- package/dist/commands/pull.d.ts +0 -15
- package/dist/commands/pull.js +0 -305
- package/dist/commands/pull.llm-generate.d.ts +0 -10
- package/dist/commands/pull.llm-generate.js +0 -184
- package/dist/commands/push.d.ts +0 -6
- package/dist/commands/push.js +0 -268
- package/dist/exports.d.ts +0 -2
- package/dist/exports.js +0 -2
- package/dist/index.js.map +0 -7
- package/dist/types/config.d.ts +0 -9
- package/dist/types/config.js +0 -3
- package/dist/types/graph.d.ts +0 -10
- package/dist/types/graph.js +0 -1
- package/dist/utils/json-comparator.d.ts +0 -60
- package/dist/utils/json-comparator.js +0 -222
- package/dist/utils/mcp-runner.d.ts +0 -6
- package/dist/utils/mcp-runner.js +0 -147
- package/dist/utils/ts-loader.d.ts +0 -5
- package/dist/utils/ts-loader.js +0 -145
|
@@ -1,186 +0,0 @@
|
|
|
1
|
-
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
-
import { initCommand } from '../../commands/init.js';
|
|
3
|
-
// Mock inquirer
|
|
4
|
-
vi.mock('inquirer', () => ({
|
|
5
|
-
default: {
|
|
6
|
-
prompt: vi.fn(),
|
|
7
|
-
},
|
|
8
|
-
}));
|
|
9
|
-
// Mock fs functions
|
|
10
|
-
vi.mock('node:fs', async () => {
|
|
11
|
-
const actual = await vi.importActual('node:fs');
|
|
12
|
-
return {
|
|
13
|
-
...actual,
|
|
14
|
-
existsSync: vi.fn(),
|
|
15
|
-
writeFileSync: vi.fn(),
|
|
16
|
-
readdirSync: vi.fn(),
|
|
17
|
-
};
|
|
18
|
-
});
|
|
19
|
-
describe('Init Command', () => {
|
|
20
|
-
let consoleLogSpy;
|
|
21
|
-
let consoleErrorSpy;
|
|
22
|
-
let processExitSpy;
|
|
23
|
-
beforeEach(() => {
|
|
24
|
-
consoleLogSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
|
|
25
|
-
consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => { });
|
|
26
|
-
processExitSpy = vi.spyOn(process, 'exit').mockImplementation(() => {
|
|
27
|
-
throw new Error('process.exit called');
|
|
28
|
-
});
|
|
29
|
-
vi.clearAllMocks();
|
|
30
|
-
});
|
|
31
|
-
afterEach(() => {
|
|
32
|
-
consoleLogSpy.mockRestore();
|
|
33
|
-
consoleErrorSpy.mockRestore();
|
|
34
|
-
processExitSpy.mockRestore();
|
|
35
|
-
});
|
|
36
|
-
describe('initCommand', () => {
|
|
37
|
-
it('should create a new config file when none exists', async () => {
|
|
38
|
-
const { existsSync, writeFileSync, readdirSync } = await import('node:fs');
|
|
39
|
-
const inquirer = await import('inquirer');
|
|
40
|
-
vi.mocked(existsSync).mockReturnValue(false);
|
|
41
|
-
vi.mocked(readdirSync).mockReturnValue(['package.json']);
|
|
42
|
-
const promptMock = vi.mocked(inquirer.default.prompt);
|
|
43
|
-
promptMock
|
|
44
|
-
.mockResolvedValueOnce({
|
|
45
|
-
confirmedPath: './inkeep.config.ts',
|
|
46
|
-
})
|
|
47
|
-
.mockResolvedValueOnce({
|
|
48
|
-
tenantId: 'test-tenant-123',
|
|
49
|
-
apiUrl: 'http://localhost:3002',
|
|
50
|
-
});
|
|
51
|
-
await initCommand();
|
|
52
|
-
expect(existsSync).toHaveBeenCalledWith(expect.stringContaining('inkeep.config.ts'));
|
|
53
|
-
expect(writeFileSync).toHaveBeenCalledWith(expect.stringContaining('inkeep.config.ts'), expect.stringContaining("tenantId: 'test-tenant-123'"));
|
|
54
|
-
expect(writeFileSync).toHaveBeenCalledWith(expect.stringContaining('inkeep.config.ts'), expect.stringContaining("apiUrl: 'http://localhost:3002'"));
|
|
55
|
-
expect(consoleLogSpy).toHaveBeenCalledWith(expect.any(String), // The checkmark
|
|
56
|
-
expect.stringContaining('Created'));
|
|
57
|
-
});
|
|
58
|
-
it('should prompt for overwrite when config file exists', async () => {
|
|
59
|
-
const { existsSync, writeFileSync, readdirSync } = await import('node:fs');
|
|
60
|
-
const inquirer = await import('inquirer');
|
|
61
|
-
vi.mocked(readdirSync).mockReturnValue(['package.json']);
|
|
62
|
-
vi.mocked(existsSync).mockImplementation((path) => {
|
|
63
|
-
return path.toString().includes('inkeep.config.ts');
|
|
64
|
-
});
|
|
65
|
-
const promptMock = vi.mocked(inquirer.default.prompt);
|
|
66
|
-
promptMock
|
|
67
|
-
.mockResolvedValueOnce({ confirmedPath: './inkeep.config.ts' })
|
|
68
|
-
.mockResolvedValueOnce({ overwrite: true })
|
|
69
|
-
.mockResolvedValueOnce({
|
|
70
|
-
tenantId: 'new-tenant-456',
|
|
71
|
-
apiUrl: 'https://api.example.com',
|
|
72
|
-
});
|
|
73
|
-
await initCommand();
|
|
74
|
-
expect(inquirer.default.prompt).toHaveBeenCalledWith(expect.arrayContaining([
|
|
75
|
-
expect.objectContaining({
|
|
76
|
-
type: 'confirm',
|
|
77
|
-
name: 'overwrite',
|
|
78
|
-
message: expect.stringContaining('already exists'),
|
|
79
|
-
}),
|
|
80
|
-
]));
|
|
81
|
-
expect(writeFileSync).toHaveBeenCalledWith(expect.stringContaining('inkeep.config.ts'), expect.stringContaining("tenantId: 'new-tenant-456'"));
|
|
82
|
-
});
|
|
83
|
-
it('should cancel when user chooses not to overwrite', async () => {
|
|
84
|
-
const { existsSync, writeFileSync, readdirSync } = await import('node:fs');
|
|
85
|
-
const inquirer = await import('inquirer');
|
|
86
|
-
vi.mocked(readdirSync).mockReturnValue(['package.json']);
|
|
87
|
-
vi.mocked(existsSync).mockImplementation((path) => {
|
|
88
|
-
return path.toString().includes('inkeep.config.ts');
|
|
89
|
-
});
|
|
90
|
-
const promptMock = vi.mocked(inquirer.default.prompt);
|
|
91
|
-
promptMock
|
|
92
|
-
.mockResolvedValueOnce({ confirmedPath: './inkeep.config.ts' })
|
|
93
|
-
.mockResolvedValueOnce({ overwrite: false });
|
|
94
|
-
await initCommand();
|
|
95
|
-
expect(writeFileSync).not.toHaveBeenCalled();
|
|
96
|
-
expect(consoleLogSpy).toHaveBeenCalledWith(expect.stringContaining('Init cancelled'));
|
|
97
|
-
});
|
|
98
|
-
it('should validate tenant ID is not empty', async () => {
|
|
99
|
-
const { existsSync, readdirSync } = await import('node:fs');
|
|
100
|
-
const inquirer = await import('inquirer');
|
|
101
|
-
vi.mocked(existsSync).mockReturnValue(false);
|
|
102
|
-
vi.mocked(readdirSync).mockReturnValue(['package.json']);
|
|
103
|
-
let pathCallCount = 0;
|
|
104
|
-
const promptMock = vi.mocked(inquirer.default.prompt);
|
|
105
|
-
promptMock.mockImplementation(async (questions) => {
|
|
106
|
-
pathCallCount++;
|
|
107
|
-
if (pathCallCount === 1) {
|
|
108
|
-
// First call is for path confirmation
|
|
109
|
-
return { confirmedPath: './inkeep.config.ts' };
|
|
110
|
-
}
|
|
111
|
-
else {
|
|
112
|
-
// Second call is for tenant ID and API URL
|
|
113
|
-
const tenantIdQuestion = questions.find((q) => q.name === 'tenantId');
|
|
114
|
-
expect(tenantIdQuestion.validate('')).toBe('Tenant ID is required');
|
|
115
|
-
expect(tenantIdQuestion.validate(' ')).toBe('Tenant ID is required');
|
|
116
|
-
expect(tenantIdQuestion.validate('valid-tenant')).toBe(true);
|
|
117
|
-
return {
|
|
118
|
-
tenantId: 'valid-tenant',
|
|
119
|
-
apiUrl: 'http://localhost:3002',
|
|
120
|
-
};
|
|
121
|
-
}
|
|
122
|
-
});
|
|
123
|
-
await initCommand();
|
|
124
|
-
});
|
|
125
|
-
it('should validate API URL format', async () => {
|
|
126
|
-
const { existsSync, readdirSync } = await import('node:fs');
|
|
127
|
-
const inquirer = await import('inquirer');
|
|
128
|
-
vi.mocked(existsSync).mockReturnValue(false);
|
|
129
|
-
vi.mocked(readdirSync).mockReturnValue(['package.json']);
|
|
130
|
-
let pathCallCount = 0;
|
|
131
|
-
const promptMock = vi.mocked(inquirer.default.prompt);
|
|
132
|
-
promptMock.mockImplementation(async (questions) => {
|
|
133
|
-
pathCallCount++;
|
|
134
|
-
if (pathCallCount === 1) {
|
|
135
|
-
// First call is for path confirmation
|
|
136
|
-
return { confirmedPath: './inkeep.config.ts' };
|
|
137
|
-
}
|
|
138
|
-
else {
|
|
139
|
-
// Second call is for tenant ID and API URL
|
|
140
|
-
const apiUrlQuestion = questions.find((q) => q.name === 'apiUrl');
|
|
141
|
-
expect(apiUrlQuestion.validate('not-a-url')).toBe('Please enter a valid URL');
|
|
142
|
-
expect(apiUrlQuestion.validate('http://localhost:3002')).toBe(true);
|
|
143
|
-
expect(apiUrlQuestion.validate('https://api.example.com')).toBe(true);
|
|
144
|
-
return {
|
|
145
|
-
tenantId: 'test-tenant',
|
|
146
|
-
apiUrl: 'http://localhost:3002',
|
|
147
|
-
};
|
|
148
|
-
}
|
|
149
|
-
});
|
|
150
|
-
await initCommand();
|
|
151
|
-
});
|
|
152
|
-
it('should accept a path parameter', async () => {
|
|
153
|
-
const { existsSync, writeFileSync } = await import('node:fs');
|
|
154
|
-
const inquirer = await import('inquirer');
|
|
155
|
-
vi.mocked(existsSync).mockReturnValue(false);
|
|
156
|
-
const promptMock = vi.mocked(inquirer.default.prompt);
|
|
157
|
-
promptMock.mockResolvedValue({
|
|
158
|
-
tenantId: 'test-tenant',
|
|
159
|
-
apiUrl: 'http://localhost:3002',
|
|
160
|
-
});
|
|
161
|
-
await initCommand({ path: './custom/path' });
|
|
162
|
-
expect(writeFileSync).toHaveBeenCalledWith(expect.stringContaining('custom/path/inkeep.config.ts'), expect.any(String));
|
|
163
|
-
});
|
|
164
|
-
it('should handle write errors gracefully', async () => {
|
|
165
|
-
const { existsSync, writeFileSync, readdirSync } = await import('node:fs');
|
|
166
|
-
const inquirer = await import('inquirer');
|
|
167
|
-
vi.mocked(existsSync).mockReturnValue(false);
|
|
168
|
-
vi.mocked(readdirSync).mockReturnValue(['package.json']);
|
|
169
|
-
const promptMock = vi.mocked(inquirer.default.prompt);
|
|
170
|
-
promptMock
|
|
171
|
-
.mockResolvedValueOnce({
|
|
172
|
-
confirmedPath: './inkeep.config.ts',
|
|
173
|
-
})
|
|
174
|
-
.mockResolvedValueOnce({
|
|
175
|
-
tenantId: 'test-tenant',
|
|
176
|
-
apiUrl: 'http://localhost:3002',
|
|
177
|
-
});
|
|
178
|
-
vi.mocked(writeFileSync).mockImplementation(() => {
|
|
179
|
-
throw new Error('Permission denied');
|
|
180
|
-
});
|
|
181
|
-
await expect(initCommand()).rejects.toThrow('process.exit called');
|
|
182
|
-
expect(consoleErrorSpy).toHaveBeenCalledWith(expect.stringContaining('Failed to create config file'), expect.any(Error));
|
|
183
|
-
expect(processExitSpy).toHaveBeenCalledWith(1);
|
|
184
|
-
});
|
|
185
|
-
});
|
|
186
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
-
import { convertTypeScriptToJson } from '../../commands/pull.js';
|
|
3
|
-
// Mock the convertTypeScriptToJson function
|
|
4
|
-
vi.mock('../../commands/pull.js', async () => {
|
|
5
|
-
const actual = await vi.importActual('../../commands/pull.js');
|
|
6
|
-
return {
|
|
7
|
-
...actual,
|
|
8
|
-
convertTypeScriptToJson: vi.fn(),
|
|
9
|
-
};
|
|
10
|
-
});
|
|
11
|
-
describe('Pull Command', () => {
|
|
12
|
-
beforeEach(() => {
|
|
13
|
-
vi.clearAllMocks();
|
|
14
|
-
});
|
|
15
|
-
describe('convertTypeScriptToJson', () => {
|
|
16
|
-
it('should convert TypeScript file to JSON using tsx spawn', async () => {
|
|
17
|
-
const mockResult = { id: 'test-graph', name: 'Test Graph' };
|
|
18
|
-
convertTypeScriptToJson.mockResolvedValue(mockResult);
|
|
19
|
-
const result = await convertTypeScriptToJson('test-graph.ts');
|
|
20
|
-
expect(convertTypeScriptToJson).toHaveBeenCalledWith('test-graph.ts');
|
|
21
|
-
expect(result).toEqual(mockResult);
|
|
22
|
-
});
|
|
23
|
-
it('should handle tsx spawn errors', async () => {
|
|
24
|
-
convertTypeScriptToJson.mockRejectedValue(new Error('Failed to load TypeScript file: tsx not found'));
|
|
25
|
-
await expect(convertTypeScriptToJson('test-graph.ts')).rejects.toThrow('Failed to load TypeScript file: tsx not found');
|
|
26
|
-
});
|
|
27
|
-
it('should handle tsx exit with non-zero code', async () => {
|
|
28
|
-
convertTypeScriptToJson.mockRejectedValue(new Error('Conversion failed: Error: Module not found'));
|
|
29
|
-
await expect(convertTypeScriptToJson('test-graph.ts')).rejects.toThrow('Conversion failed: Error: Module not found');
|
|
30
|
-
});
|
|
31
|
-
it('should handle missing JSON markers in tsx output', async () => {
|
|
32
|
-
convertTypeScriptToJson.mockRejectedValue(new Error('JSON markers not found in output'));
|
|
33
|
-
await expect(convertTypeScriptToJson('test-graph.ts')).rejects.toThrow('JSON markers not found in output');
|
|
34
|
-
});
|
|
35
|
-
it('should handle invalid JSON in tsx output', async () => {
|
|
36
|
-
convertTypeScriptToJson.mockRejectedValue(new Error('Failed to parse conversion result'));
|
|
37
|
-
await expect(convertTypeScriptToJson('test-graph.ts')).rejects.toThrow('Failed to parse conversion result');
|
|
38
|
-
});
|
|
39
|
-
it('should handle file not found error', async () => {
|
|
40
|
-
convertTypeScriptToJson.mockRejectedValue(new Error('File not found: nonexistent.ts'));
|
|
41
|
-
await expect(convertTypeScriptToJson('nonexistent.ts')).rejects.toThrow('File not found: nonexistent.ts');
|
|
42
|
-
});
|
|
43
|
-
it('should handle non-TypeScript files directly', async () => {
|
|
44
|
-
const mockResult = { id: 'test-graph' };
|
|
45
|
-
convertTypeScriptToJson.mockResolvedValue(mockResult);
|
|
46
|
-
const result = await convertTypeScriptToJson('test-graph.js');
|
|
47
|
-
expect(result).toEqual(mockResult);
|
|
48
|
-
});
|
|
49
|
-
it('should handle modules with no graph exports', async () => {
|
|
50
|
-
convertTypeScriptToJson.mockRejectedValue(new Error('No AgentGraph exported from configuration file'));
|
|
51
|
-
await expect(convertTypeScriptToJson('test-graph.js')).rejects.toThrow('No AgentGraph exported from configuration file');
|
|
52
|
-
});
|
|
53
|
-
});
|
|
54
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
2
|
-
import { spawn } from 'node:child_process';
|
|
3
|
-
import { pushCommand } from '../../commands/push.js';
|
|
4
|
-
import { existsSync } from 'node:fs';
|
|
5
|
-
// Mock dependencies
|
|
6
|
-
vi.mock('node:fs');
|
|
7
|
-
vi.mock('node:child_process');
|
|
8
|
-
vi.mock('@inkeep/agents-core');
|
|
9
|
-
vi.mock('../../config.js', () => ({
|
|
10
|
-
validateConfiguration: vi.fn().mockResolvedValue({
|
|
11
|
-
tenantId: 'test-tenant',
|
|
12
|
-
projectId: 'test-project',
|
|
13
|
-
managementApiUrl: 'http://localhost:3002',
|
|
14
|
-
sources: {
|
|
15
|
-
tenantId: 'config',
|
|
16
|
-
projectId: 'config',
|
|
17
|
-
managementApiUrl: 'config',
|
|
18
|
-
},
|
|
19
|
-
}),
|
|
20
|
-
}));
|
|
21
|
-
// Store the actual ora mock instance
|
|
22
|
-
let oraInstance;
|
|
23
|
-
vi.mock('ora', () => ({
|
|
24
|
-
default: vi.fn(() => {
|
|
25
|
-
oraInstance = {
|
|
26
|
-
start: vi.fn().mockReturnThis(),
|
|
27
|
-
succeed: vi.fn().mockReturnThis(),
|
|
28
|
-
fail: vi.fn().mockReturnThis(),
|
|
29
|
-
warn: vi.fn().mockReturnThis(),
|
|
30
|
-
stop: vi.fn().mockReturnThis(),
|
|
31
|
-
text: '',
|
|
32
|
-
};
|
|
33
|
-
return oraInstance;
|
|
34
|
-
}),
|
|
35
|
-
}));
|
|
36
|
-
describe('Push Command - TypeScript Spinner Fix', () => {
|
|
37
|
-
let mockSpawn;
|
|
38
|
-
let mockExit;
|
|
39
|
-
beforeEach(() => {
|
|
40
|
-
vi.clearAllMocks();
|
|
41
|
-
// Reset ora instance
|
|
42
|
-
oraInstance = null;
|
|
43
|
-
// Ensure TSX_RUNNING is not set
|
|
44
|
-
delete process.env.TSX_RUNNING;
|
|
45
|
-
// Mock file exists
|
|
46
|
-
existsSync.mockReturnValue(true);
|
|
47
|
-
// Mock process.exit
|
|
48
|
-
mockExit = vi.fn();
|
|
49
|
-
vi.spyOn(process, 'exit').mockImplementation(mockExit);
|
|
50
|
-
// Mock console methods
|
|
51
|
-
vi.spyOn(console, 'log').mockImplementation(vi.fn());
|
|
52
|
-
vi.spyOn(console, 'error').mockImplementation(vi.fn());
|
|
53
|
-
// Setup spawn mock
|
|
54
|
-
mockSpawn = vi.fn().mockReturnValue({
|
|
55
|
-
on: vi.fn((event, callback) => {
|
|
56
|
-
if (event === 'exit') {
|
|
57
|
-
// Simulate successful exit
|
|
58
|
-
setTimeout(() => callback(0), 10);
|
|
59
|
-
}
|
|
60
|
-
}),
|
|
61
|
-
});
|
|
62
|
-
spawn.mockImplementation(mockSpawn);
|
|
63
|
-
});
|
|
64
|
-
it('should stop spinner before spawning tsx process for TypeScript files', async () => {
|
|
65
|
-
await pushCommand('/test/path/graph.ts', {});
|
|
66
|
-
// Wait for async operations
|
|
67
|
-
await new Promise(resolve => setTimeout(resolve, 20));
|
|
68
|
-
// Verify spinner was created and stopped
|
|
69
|
-
expect(oraInstance).toBeDefined();
|
|
70
|
-
expect(oraInstance.start).toHaveBeenCalled();
|
|
71
|
-
expect(oraInstance.stop).toHaveBeenCalled();
|
|
72
|
-
// Verify spinner.stop() was called before spawn
|
|
73
|
-
const stopCallOrder = oraInstance.stop.mock.invocationCallOrder[0];
|
|
74
|
-
const spawnCallOrder = mockSpawn.mock.invocationCallOrder[0];
|
|
75
|
-
expect(stopCallOrder).toBeLessThan(spawnCallOrder);
|
|
76
|
-
});
|
|
77
|
-
it('should spawn tsx process with correct arguments for TypeScript files', async () => {
|
|
78
|
-
const options = {
|
|
79
|
-
tenantId: 'custom-tenant',
|
|
80
|
-
managementApiUrl: 'https://api.example.com',
|
|
81
|
-
configFilePath: '/path/to/config.json',
|
|
82
|
-
};
|
|
83
|
-
await pushCommand('/test/path/graph.ts', options);
|
|
84
|
-
// Wait for async operations
|
|
85
|
-
await new Promise(resolve => setTimeout(resolve, 20));
|
|
86
|
-
// Verify spawn was called with correct arguments
|
|
87
|
-
expect(mockSpawn).toHaveBeenCalledWith('npx', expect.arrayContaining([
|
|
88
|
-
'tsx',
|
|
89
|
-
expect.stringContaining('index.js'),
|
|
90
|
-
'push',
|
|
91
|
-
'/test/path/graph.ts',
|
|
92
|
-
'--tenant-id',
|
|
93
|
-
'custom-tenant',
|
|
94
|
-
'--management-api-url',
|
|
95
|
-
'https://api.example.com',
|
|
96
|
-
'--config-file-path',
|
|
97
|
-
'/path/to/config.json',
|
|
98
|
-
]), expect.objectContaining({
|
|
99
|
-
cwd: process.cwd(),
|
|
100
|
-
stdio: 'inherit',
|
|
101
|
-
env: expect.objectContaining({
|
|
102
|
-
TSX_RUNNING: '1',
|
|
103
|
-
}),
|
|
104
|
-
}));
|
|
105
|
-
});
|
|
106
|
-
it('should handle spawn errors correctly without spinner', async () => {
|
|
107
|
-
// Setup spawn to simulate an error
|
|
108
|
-
mockSpawn.mockReturnValue({
|
|
109
|
-
on: vi.fn((event, callback) => {
|
|
110
|
-
if (event === 'error') {
|
|
111
|
-
setTimeout(() => callback(new Error('Spawn failed')), 10);
|
|
112
|
-
}
|
|
113
|
-
}),
|
|
114
|
-
});
|
|
115
|
-
await pushCommand('/test/path/graph.ts', {});
|
|
116
|
-
// Wait for async operations
|
|
117
|
-
await new Promise(resolve => setTimeout(resolve, 20));
|
|
118
|
-
// Verify spinner was stopped before error handling
|
|
119
|
-
expect(oraInstance.stop).toHaveBeenCalled();
|
|
120
|
-
// Verify error was logged without using spinner.fail
|
|
121
|
-
expect(oraInstance.fail).not.toHaveBeenCalled();
|
|
122
|
-
expect(console.error).toHaveBeenCalledWith(expect.stringContaining('Failed to load TypeScript file'));
|
|
123
|
-
expect(console.error).toHaveBeenCalledWith(expect.stringContaining('Error'), 'Spawn failed');
|
|
124
|
-
// Verify process exited with error code
|
|
125
|
-
expect(mockExit).toHaveBeenCalledWith(1);
|
|
126
|
-
});
|
|
127
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,265 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
2
|
-
import { pushCommand } from '../../commands/push.js';
|
|
3
|
-
import * as core from '@inkeep/agents-core';
|
|
4
|
-
import inquirer from 'inquirer';
|
|
5
|
-
import { existsSync } from 'node:fs';
|
|
6
|
-
// Mock all external dependencies
|
|
7
|
-
vi.mock('node:fs');
|
|
8
|
-
vi.mock('@inkeep/agents-core');
|
|
9
|
-
vi.mock('inquirer');
|
|
10
|
-
vi.mock('chalk', () => ({
|
|
11
|
-
default: {
|
|
12
|
-
red: vi.fn((text) => text),
|
|
13
|
-
yellow: vi.fn((text) => text),
|
|
14
|
-
green: vi.fn((text) => text),
|
|
15
|
-
cyan: vi.fn((text) => text),
|
|
16
|
-
gray: vi.fn((text) => text),
|
|
17
|
-
},
|
|
18
|
-
}));
|
|
19
|
-
vi.mock('ora', () => ({
|
|
20
|
-
default: vi.fn(() => ({
|
|
21
|
-
start: vi.fn().mockReturnThis(),
|
|
22
|
-
succeed: vi.fn().mockReturnThis(),
|
|
23
|
-
fail: vi.fn().mockReturnThis(),
|
|
24
|
-
warn: vi.fn().mockReturnThis(),
|
|
25
|
-
stop: vi.fn().mockReturnThis(),
|
|
26
|
-
text: '',
|
|
27
|
-
})),
|
|
28
|
-
}));
|
|
29
|
-
vi.mock('../../api.js', () => ({
|
|
30
|
-
ApiClient: {
|
|
31
|
-
create: vi.fn().mockResolvedValue({}),
|
|
32
|
-
},
|
|
33
|
-
}));
|
|
34
|
-
vi.mock('../../config.js', () => ({
|
|
35
|
-
validateConfiguration: vi.fn().mockResolvedValue({
|
|
36
|
-
tenantId: 'test-tenant',
|
|
37
|
-
projectId: 'test-project',
|
|
38
|
-
apiUrl: 'http://localhost:3002',
|
|
39
|
-
sources: {
|
|
40
|
-
tenantId: 'config',
|
|
41
|
-
projectId: 'config',
|
|
42
|
-
apiUrl: 'config',
|
|
43
|
-
},
|
|
44
|
-
}),
|
|
45
|
-
}));
|
|
46
|
-
describe('Push Command - Project Validation', () => {
|
|
47
|
-
let mockDbClient;
|
|
48
|
-
let mockGetProject;
|
|
49
|
-
let mockCreateProject;
|
|
50
|
-
let mockExit;
|
|
51
|
-
let mockLog;
|
|
52
|
-
beforeEach(() => {
|
|
53
|
-
vi.clearAllMocks();
|
|
54
|
-
// Setup database client mock
|
|
55
|
-
mockDbClient = {};
|
|
56
|
-
mockGetProject = vi.fn();
|
|
57
|
-
mockCreateProject = vi.fn();
|
|
58
|
-
core.createDatabaseClient.mockReturnValue(mockDbClient);
|
|
59
|
-
core.getProject.mockReturnValue(mockGetProject);
|
|
60
|
-
core.createProject.mockReturnValue(mockCreateProject);
|
|
61
|
-
// Mock process.exit to prevent test runner from exiting
|
|
62
|
-
mockExit = vi.fn();
|
|
63
|
-
vi.spyOn(process, 'exit').mockImplementation(mockExit);
|
|
64
|
-
// Mock console methods
|
|
65
|
-
mockLog = vi.fn();
|
|
66
|
-
vi.spyOn(console, 'log').mockImplementation(mockLog);
|
|
67
|
-
vi.spyOn(console, 'error').mockImplementation(vi.fn());
|
|
68
|
-
// Mock file existence check for graph file
|
|
69
|
-
existsSync.mockReturnValue(true);
|
|
70
|
-
// Default environment
|
|
71
|
-
process.env.DB_FILE_NAME = 'test.db';
|
|
72
|
-
});
|
|
73
|
-
it('should validate project exists before pushing graph', async () => {
|
|
74
|
-
// Mock project exists
|
|
75
|
-
mockGetProject.mockResolvedValue({
|
|
76
|
-
id: 'test-project',
|
|
77
|
-
name: 'Test Project',
|
|
78
|
-
tenantId: 'test-tenant',
|
|
79
|
-
});
|
|
80
|
-
// Mock graph file import
|
|
81
|
-
const mockGraph = {
|
|
82
|
-
init: vi.fn().mockResolvedValue(undefined),
|
|
83
|
-
getId: vi.fn().mockReturnValue('test-graph'),
|
|
84
|
-
getName: vi.fn().mockReturnValue('Test Graph'),
|
|
85
|
-
getAgents: vi.fn().mockReturnValue([]),
|
|
86
|
-
getStats: vi.fn().mockReturnValue({
|
|
87
|
-
agentCount: 1,
|
|
88
|
-
toolCount: 0,
|
|
89
|
-
relationCount: 0,
|
|
90
|
-
}),
|
|
91
|
-
getDefaultAgent: vi.fn().mockReturnValue(null),
|
|
92
|
-
setConfig: vi.fn(),
|
|
93
|
-
};
|
|
94
|
-
vi.doMock('/test/path/graph.js', () => ({
|
|
95
|
-
default: mockGraph,
|
|
96
|
-
}));
|
|
97
|
-
// Run in TypeScript mode (skip tsx spawn)
|
|
98
|
-
process.env.TSX_RUNNING = '1';
|
|
99
|
-
await pushCommand('/test/path/graph.js', {});
|
|
100
|
-
// Verify project validation was called
|
|
101
|
-
expect(mockGetProject).toHaveBeenCalledWith({
|
|
102
|
-
scopes: { tenantId: 'test-tenant', projectId: 'test-project' },
|
|
103
|
-
});
|
|
104
|
-
});
|
|
105
|
-
it('should prompt to create project when it does not exist', async () => {
|
|
106
|
-
// Mock project doesn't exist
|
|
107
|
-
mockGetProject.mockResolvedValue(null);
|
|
108
|
-
// Mock user confirms project creation
|
|
109
|
-
inquirer.prompt
|
|
110
|
-
.mockResolvedValueOnce({ shouldCreate: true })
|
|
111
|
-
.mockResolvedValueOnce({
|
|
112
|
-
projectName: 'New Project',
|
|
113
|
-
projectDescription: 'Test description',
|
|
114
|
-
});
|
|
115
|
-
// Mock project creation success
|
|
116
|
-
mockCreateProject.mockResolvedValue({
|
|
117
|
-
id: 'test-project',
|
|
118
|
-
name: 'New Project',
|
|
119
|
-
description: 'Test description',
|
|
120
|
-
tenantId: 'test-tenant',
|
|
121
|
-
});
|
|
122
|
-
// Mock graph file import
|
|
123
|
-
const mockGraph = {
|
|
124
|
-
init: vi.fn().mockResolvedValue(undefined),
|
|
125
|
-
getId: vi.fn().mockReturnValue('test-graph'),
|
|
126
|
-
getName: vi.fn().mockReturnValue('Test Graph'),
|
|
127
|
-
getAgents: vi.fn().mockReturnValue([]),
|
|
128
|
-
getStats: vi.fn().mockReturnValue({
|
|
129
|
-
agentCount: 1,
|
|
130
|
-
toolCount: 0,
|
|
131
|
-
relationCount: 0,
|
|
132
|
-
}),
|
|
133
|
-
getDefaultAgent: vi.fn().mockReturnValue(null),
|
|
134
|
-
setConfig: vi.fn(),
|
|
135
|
-
};
|
|
136
|
-
vi.doMock('/test/path/graph.js', () => ({
|
|
137
|
-
default: mockGraph,
|
|
138
|
-
}));
|
|
139
|
-
process.env.TSX_RUNNING = '1';
|
|
140
|
-
await pushCommand('/test/path/graph.js', {});
|
|
141
|
-
// Verify project creation was prompted
|
|
142
|
-
expect(inquirer.prompt).toHaveBeenCalledWith(expect.arrayContaining([
|
|
143
|
-
expect.objectContaining({
|
|
144
|
-
type: 'confirm',
|
|
145
|
-
name: 'shouldCreate',
|
|
146
|
-
message: expect.stringContaining('does not exist'),
|
|
147
|
-
}),
|
|
148
|
-
]));
|
|
149
|
-
// Verify project was created
|
|
150
|
-
expect(mockCreateProject).toHaveBeenCalledWith({
|
|
151
|
-
id: 'test-project',
|
|
152
|
-
tenantId: 'test-tenant',
|
|
153
|
-
name: 'New Project',
|
|
154
|
-
description: 'Test description',
|
|
155
|
-
});
|
|
156
|
-
});
|
|
157
|
-
it('should exit if user declines to create missing project', async () => {
|
|
158
|
-
// Mock project doesn't exist
|
|
159
|
-
mockGetProject.mockResolvedValue(null);
|
|
160
|
-
// Mock user declines project creation
|
|
161
|
-
inquirer.prompt.mockResolvedValueOnce({ shouldCreate: false });
|
|
162
|
-
// Mock graph file import (needed to prevent errors)
|
|
163
|
-
const mockGraph = {
|
|
164
|
-
init: vi.fn().mockResolvedValue(undefined),
|
|
165
|
-
getId: vi.fn().mockReturnValue('test-graph'),
|
|
166
|
-
getName: vi.fn().mockReturnValue('Test Graph'),
|
|
167
|
-
getAgents: vi.fn().mockReturnValue([]),
|
|
168
|
-
getStats: vi.fn().mockReturnValue({
|
|
169
|
-
agentCount: 1,
|
|
170
|
-
toolCount: 0,
|
|
171
|
-
relationCount: 0,
|
|
172
|
-
}),
|
|
173
|
-
getDefaultAgent: vi.fn().mockReturnValue(null),
|
|
174
|
-
setConfig: vi.fn(),
|
|
175
|
-
};
|
|
176
|
-
vi.doMock('/test/path/graph.js', () => ({
|
|
177
|
-
default: mockGraph,
|
|
178
|
-
}));
|
|
179
|
-
process.env.TSX_RUNNING = '1';
|
|
180
|
-
await pushCommand('/test/path/graph.js', {});
|
|
181
|
-
// Verify push was cancelled
|
|
182
|
-
expect(mockExit).toHaveBeenCalledWith(0);
|
|
183
|
-
expect(mockLog).toHaveBeenCalledWith(expect.stringContaining('Push cancelled'));
|
|
184
|
-
// Verify project was not created
|
|
185
|
-
expect(mockCreateProject).not.toHaveBeenCalled();
|
|
186
|
-
});
|
|
187
|
-
it('should handle project creation errors gracefully', async () => {
|
|
188
|
-
// Mock project doesn't exist
|
|
189
|
-
mockGetProject.mockResolvedValue(null);
|
|
190
|
-
// Mock user confirms project creation
|
|
191
|
-
inquirer.prompt
|
|
192
|
-
.mockResolvedValueOnce({ shouldCreate: true })
|
|
193
|
-
.mockResolvedValueOnce({
|
|
194
|
-
projectName: 'New Project',
|
|
195
|
-
projectDescription: '',
|
|
196
|
-
});
|
|
197
|
-
// Mock project creation failure
|
|
198
|
-
mockCreateProject.mockRejectedValue(new Error('Database error'));
|
|
199
|
-
process.env.TSX_RUNNING = '1';
|
|
200
|
-
await pushCommand('/test/path/graph.js', {});
|
|
201
|
-
// Verify error handling
|
|
202
|
-
expect(mockExit).toHaveBeenCalledWith(1);
|
|
203
|
-
expect(console.error).toHaveBeenCalledWith(expect.stringContaining('Error'), 'Database error');
|
|
204
|
-
});
|
|
205
|
-
it('should use DB_FILE_NAME environment variable for database location', async () => {
|
|
206
|
-
process.env.DB_FILE_NAME = 'custom-location.db';
|
|
207
|
-
mockGetProject.mockResolvedValue({
|
|
208
|
-
id: 'test-project',
|
|
209
|
-
name: 'Test Project',
|
|
210
|
-
tenantId: 'test-tenant',
|
|
211
|
-
});
|
|
212
|
-
const mockGraph = {
|
|
213
|
-
init: vi.fn().mockResolvedValue(undefined),
|
|
214
|
-
getId: vi.fn().mockReturnValue('test-graph'),
|
|
215
|
-
getName: vi.fn().mockReturnValue('Test Graph'),
|
|
216
|
-
getAgents: vi.fn().mockReturnValue([]),
|
|
217
|
-
getStats: vi.fn().mockReturnValue({
|
|
218
|
-
agentCount: 1,
|
|
219
|
-
toolCount: 0,
|
|
220
|
-
relationCount: 0,
|
|
221
|
-
}),
|
|
222
|
-
getDefaultAgent: vi.fn().mockReturnValue(null),
|
|
223
|
-
setConfig: vi.fn(),
|
|
224
|
-
};
|
|
225
|
-
vi.doMock('/test/path/graph.js', () => ({
|
|
226
|
-
default: mockGraph,
|
|
227
|
-
}));
|
|
228
|
-
process.env.TSX_RUNNING = '1';
|
|
229
|
-
await pushCommand('/test/path/graph.js', {});
|
|
230
|
-
// Verify correct database URL was used
|
|
231
|
-
expect(core.createDatabaseClient).toHaveBeenCalledWith({
|
|
232
|
-
url: expect.stringContaining('custom-location.db'),
|
|
233
|
-
});
|
|
234
|
-
});
|
|
235
|
-
it('should default to local.db when DB_FILE_NAME is not set', async () => {
|
|
236
|
-
delete process.env.DB_FILE_NAME;
|
|
237
|
-
mockGetProject.mockResolvedValue({
|
|
238
|
-
id: 'test-project',
|
|
239
|
-
name: 'Test Project',
|
|
240
|
-
tenantId: 'test-tenant',
|
|
241
|
-
});
|
|
242
|
-
const mockGraph = {
|
|
243
|
-
init: vi.fn().mockResolvedValue(undefined),
|
|
244
|
-
getId: vi.fn().mockReturnValue('test-graph'),
|
|
245
|
-
getName: vi.fn().mockReturnValue('Test Graph'),
|
|
246
|
-
getAgents: vi.fn().mockReturnValue([]),
|
|
247
|
-
getStats: vi.fn().mockReturnValue({
|
|
248
|
-
agentCount: 1,
|
|
249
|
-
toolCount: 0,
|
|
250
|
-
relationCount: 0,
|
|
251
|
-
}),
|
|
252
|
-
getDefaultAgent: vi.fn().mockReturnValue(null),
|
|
253
|
-
setConfig: vi.fn(),
|
|
254
|
-
};
|
|
255
|
-
vi.doMock('/test/path/graph.js', () => ({
|
|
256
|
-
default: mockGraph,
|
|
257
|
-
}));
|
|
258
|
-
process.env.TSX_RUNNING = '1';
|
|
259
|
-
await pushCommand('/test/path/graph.js', {});
|
|
260
|
-
// Verify default database URL was used
|
|
261
|
-
expect(core.createDatabaseClient).toHaveBeenCalledWith({
|
|
262
|
-
url: expect.stringContaining('local.db'),
|
|
263
|
-
});
|
|
264
|
-
});
|
|
265
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|