@mmmbuto/nexuscli 0.9.5 → 0.9.7-termux
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 +28 -5
- package/lib/cli/engines.js +51 -2
- package/lib/config/manager.js +5 -0
- package/lib/config/models.js +28 -0
- package/lib/server/lib/cli-wrapper.js +167 -0
- package/lib/server/lib/output-parser.js +132 -0
- package/lib/server/lib/pty-adapter.js +81 -0
- package/lib/server/middleware/rate-limit.js +1 -1
- package/lib/server/models/Message.js +1 -1
- package/lib/server/routes/models.js +2 -0
- package/lib/server/routes/qwen.js +240 -0
- package/lib/server/routes/sessions.js +13 -1
- package/lib/server/server.js +7 -4
- package/lib/server/services/cli-loader.js +83 -3
- package/lib/server/services/context-bridge.js +4 -2
- package/lib/server/services/qwen-output-parser.js +289 -0
- package/lib/server/services/qwen-wrapper.js +251 -0
- package/lib/server/services/session-importer.js +35 -2
- package/lib/server/services/session-manager.js +32 -5
- package/lib/server/tests/history-sync.test.js +11 -2
- package/lib/server/tests/integration-session-sync.test.js +40 -8
- package/lib/server/tests/integration.test.js +33 -16
- package/lib/server/tests/performance.test.js +16 -9
- package/lib/server/tests/services.test.js +17 -10
- package/package.json +2 -2
|
@@ -4,6 +4,7 @@ const os = require('os');
|
|
|
4
4
|
|
|
5
5
|
describe('HistorySync', () => {
|
|
6
6
|
let tmpDir;
|
|
7
|
+
let testHome;
|
|
7
8
|
let historyPath;
|
|
8
9
|
let HistorySync;
|
|
9
10
|
let initDb;
|
|
@@ -26,7 +27,8 @@ describe('HistorySync', () => {
|
|
|
26
27
|
beforeEach(async () => {
|
|
27
28
|
// Fresh temp DB per test
|
|
28
29
|
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'nexus-db-'));
|
|
29
|
-
|
|
30
|
+
testHome = fs.mkdtempSync(path.join(os.tmpdir(), 'nexuscli-home-'));
|
|
31
|
+
process.env.HOME = testHome;
|
|
30
32
|
|
|
31
33
|
jest.resetModules();
|
|
32
34
|
({ initDb, prepare, getDb } = require('../db'));
|
|
@@ -83,7 +85,14 @@ describe('HistorySync', () => {
|
|
|
83
85
|
test('getWorkspaceSessions filters by workspace path', async () => {
|
|
84
86
|
await historySync.sync(true);
|
|
85
87
|
|
|
86
|
-
const
|
|
88
|
+
const grouped = await historySync.getWorkspaceSessions('/test/path');
|
|
89
|
+
const sessions = [
|
|
90
|
+
...grouped.today,
|
|
91
|
+
...grouped.yesterday,
|
|
92
|
+
...grouped.last7days,
|
|
93
|
+
...grouped.last30days,
|
|
94
|
+
...grouped.older
|
|
95
|
+
];
|
|
87
96
|
expect(sessions).toHaveLength(1);
|
|
88
97
|
expect(sessions[0].id).toBe('test-1');
|
|
89
98
|
});
|
|
@@ -9,16 +9,34 @@
|
|
|
9
9
|
|
|
10
10
|
const fs = require('fs');
|
|
11
11
|
const path = require('path');
|
|
12
|
-
const
|
|
13
|
-
|
|
12
|
+
const os = require('os');
|
|
13
|
+
|
|
14
|
+
let HistorySync;
|
|
15
|
+
let initDb;
|
|
16
|
+
let getDb;
|
|
17
|
+
let prepare;
|
|
14
18
|
|
|
15
19
|
describe('Session Sync Integration', () => {
|
|
16
20
|
let historySync;
|
|
17
21
|
let testHistoryPath;
|
|
18
22
|
|
|
19
23
|
beforeAll(async () => {
|
|
24
|
+
const testHome = fs.mkdtempSync(path.join(os.tmpdir(), 'nexuscli-home-'));
|
|
25
|
+
process.env.HOME = testHome;
|
|
26
|
+
|
|
27
|
+
jest.resetModules();
|
|
28
|
+
HistorySync = require('../services/history-sync');
|
|
29
|
+
({ initDb, getDb, prepare } = require('../db'));
|
|
30
|
+
|
|
20
31
|
// Setup in-memory test database
|
|
21
|
-
await initDb(
|
|
32
|
+
await initDb();
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
afterAll(() => {
|
|
36
|
+
const db = getDb && getDb();
|
|
37
|
+
if (db && typeof db.close === 'function') {
|
|
38
|
+
db.close();
|
|
39
|
+
}
|
|
22
40
|
});
|
|
23
41
|
|
|
24
42
|
beforeEach(() => {
|
|
@@ -110,11 +128,25 @@ describe('Session Sync Integration', () => {
|
|
|
110
128
|
const workspace1Sessions = await historySync.getWorkspaceSessions('/test/workspace1');
|
|
111
129
|
const workspace2Sessions = await historySync.getWorkspaceSessions('/test/workspace2');
|
|
112
130
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
131
|
+
const ws1 = [
|
|
132
|
+
...workspace1Sessions.today,
|
|
133
|
+
...workspace1Sessions.yesterday,
|
|
134
|
+
...workspace1Sessions.last7days,
|
|
135
|
+
...workspace1Sessions.last30days,
|
|
136
|
+
...workspace1Sessions.older
|
|
137
|
+
];
|
|
138
|
+
expect(ws1).toHaveLength(1);
|
|
139
|
+
expect(ws1[0].id).toBe('session-001');
|
|
140
|
+
|
|
141
|
+
const ws2 = [
|
|
142
|
+
...workspace2Sessions.today,
|
|
143
|
+
...workspace2Sessions.yesterday,
|
|
144
|
+
...workspace2Sessions.last7days,
|
|
145
|
+
...workspace2Sessions.last30days,
|
|
146
|
+
...workspace2Sessions.older
|
|
147
|
+
];
|
|
148
|
+
expect(ws2).toHaveLength(1);
|
|
149
|
+
expect(ws2[0].id).toBe('session-002');
|
|
118
150
|
});
|
|
119
151
|
|
|
120
152
|
test('conversation titles are generated from first message', async () => {
|
|
@@ -3,38 +3,56 @@
|
|
|
3
3
|
* Phase 7 - End-to-end flow validation
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
const
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
const os = require('os');
|
|
9
|
+
|
|
10
|
+
const TEST_HOME = fs.mkdtempSync(path.join(os.tmpdir(), 'nexuscli-home-'));
|
|
11
|
+
process.env.HOME = TEST_HOME;
|
|
12
|
+
|
|
13
|
+
let initDb;
|
|
14
|
+
let getDb;
|
|
15
|
+
let prepare;
|
|
16
|
+
|
|
17
|
+
beforeAll(async () => {
|
|
18
|
+
jest.resetModules();
|
|
19
|
+
({ initDb, getDb, prepare } = require('../db'));
|
|
20
|
+
await initDb(); // run migrations for session tables
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
afterAll(() => {
|
|
24
|
+
const db = getDb && getDb();
|
|
25
|
+
if (db && typeof db.close === 'function') {
|
|
26
|
+
db.close();
|
|
27
|
+
}
|
|
28
|
+
});
|
|
7
29
|
|
|
8
30
|
describe('Database Integration', () => {
|
|
9
31
|
test('should initialize database successfully', async () => {
|
|
10
|
-
const db =
|
|
32
|
+
const db = getDb();
|
|
11
33
|
expect(db).toBeDefined();
|
|
12
34
|
expect(typeof db.exec).toBe('function');
|
|
13
35
|
});
|
|
14
36
|
|
|
15
37
|
test('should have sessions table', async () => {
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
expect(result.length).toBeGreaterThan(0);
|
|
38
|
+
const row = prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='sessions'").get();
|
|
39
|
+
expect(row).not.toBeNull();
|
|
19
40
|
});
|
|
20
41
|
|
|
21
42
|
test('should have session_summaries table', async () => {
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
expect(result.length).toBeGreaterThan(0);
|
|
43
|
+
const row = prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='session_summaries'").get();
|
|
44
|
+
expect(row).not.toBeNull();
|
|
25
45
|
});
|
|
26
46
|
|
|
27
47
|
test('should have workspace_memory table', async () => {
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
expect(result.length).toBeGreaterThan(0);
|
|
48
|
+
const row = prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='workspace_memory'").get();
|
|
49
|
+
expect(row).not.toBeNull();
|
|
31
50
|
});
|
|
32
51
|
|
|
33
52
|
test('should query sessions successfully', async () => {
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
expect(
|
|
37
|
-
expect(result[0].columns).toContain('count');
|
|
53
|
+
const row = prepare('SELECT COUNT(*) as count FROM sessions').get();
|
|
54
|
+
expect(row).toBeDefined();
|
|
55
|
+
expect(Object.prototype.hasOwnProperty.call(row, 'count')).toBe(true);
|
|
38
56
|
});
|
|
39
57
|
});
|
|
40
58
|
|
|
@@ -47,7 +65,6 @@ describe('Service Integration', () => {
|
|
|
47
65
|
const loader = new CliLoader();
|
|
48
66
|
|
|
49
67
|
expect(manager.claudePath).toBe(loader.claudePath);
|
|
50
|
-
expect(manager.historyPath).toBe(loader.historyPath);
|
|
51
68
|
});
|
|
52
69
|
|
|
53
70
|
test('All services should initialize without errors', () => {
|
|
@@ -3,6 +3,13 @@
|
|
|
3
3
|
* Phase 7 - Performance benchmarks
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
const os = require('os');
|
|
9
|
+
|
|
10
|
+
const TEST_HOME = fs.mkdtempSync(path.join(os.tmpdir(), 'nexuscli-home-'));
|
|
11
|
+
process.env.HOME = TEST_HOME;
|
|
12
|
+
|
|
6
13
|
const WorkspaceManager = require('../services/workspace-manager');
|
|
7
14
|
const SummaryGenerator = require('../services/summary-generator');
|
|
8
15
|
|
|
@@ -45,7 +52,9 @@ describe('Performance Benchmarks', () => {
|
|
|
45
52
|
|
|
46
53
|
test('Workspace validation should be fast', async () => {
|
|
47
54
|
const manager = new WorkspaceManager();
|
|
48
|
-
const
|
|
55
|
+
const baseDir = path.join(process.env.HOME || os.tmpdir(), '.nexuscli-test');
|
|
56
|
+
fs.mkdirSync(baseDir, { recursive: true });
|
|
57
|
+
const testPath = fs.mkdtempSync(path.join(baseDir, 'ws-'));
|
|
49
58
|
|
|
50
59
|
const start = Date.now();
|
|
51
60
|
const validated = await manager.validateWorkspace(testPath);
|
|
@@ -97,18 +106,16 @@ describe('Memory Efficiency', () => {
|
|
|
97
106
|
});
|
|
98
107
|
|
|
99
108
|
describe('Ultra-Light Compliance', () => {
|
|
100
|
-
test('should
|
|
101
|
-
const
|
|
102
|
-
const chatCode = fs.readFileSync('routes/chat.js', 'utf8');
|
|
109
|
+
test('should persist both user and assistant messages (code verification)', () => {
|
|
110
|
+
const chatCode = fs.readFileSync(path.join(__dirname, '..', 'routes', 'chat.js'), 'utf8');
|
|
103
111
|
|
|
104
|
-
// Verify
|
|
105
|
-
expect(chatCode).toContain('
|
|
106
|
-
expect(chatCode).toContain('
|
|
112
|
+
// Verify both user and assistant messages are persisted
|
|
113
|
+
expect(chatCode).toContain('[Chat] Saved user message');
|
|
114
|
+
expect(chatCode).toContain('[Chat] Saved assistant message');
|
|
107
115
|
});
|
|
108
116
|
|
|
109
117
|
test('should use cache for history.jsonl reads', () => {
|
|
110
|
-
const
|
|
111
|
-
const managerCode = fs.readFileSync('services/workspace-manager.js', 'utf8');
|
|
118
|
+
const managerCode = fs.readFileSync(path.join(__dirname, '..', 'services', 'workspace-manager.js'), 'utf8');
|
|
112
119
|
|
|
113
120
|
// Verify cache implementation exists
|
|
114
121
|
expect(managerCode).toContain('historyCache');
|
|
@@ -3,6 +3,13 @@
|
|
|
3
3
|
* Phase 7 - Testing & Deployment
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
const os = require('os');
|
|
9
|
+
|
|
10
|
+
const TEST_HOME = fs.mkdtempSync(path.join(os.tmpdir(), 'nexuscli-home-'));
|
|
11
|
+
process.env.HOME = TEST_HOME;
|
|
12
|
+
|
|
6
13
|
const WorkspaceManager = require('../services/workspace-manager');
|
|
7
14
|
const CliLoader = require('../services/cli-loader');
|
|
8
15
|
const SummaryGenerator = require('../services/summary-generator');
|
|
@@ -15,8 +22,10 @@ describe('WorkspaceManager', () => {
|
|
|
15
22
|
});
|
|
16
23
|
|
|
17
24
|
test('should validate workspace path', async () => {
|
|
18
|
-
// Test with allowed path
|
|
19
|
-
const
|
|
25
|
+
// Test with allowed path (Termux-first)
|
|
26
|
+
const baseDir = path.join(process.env.HOME || os.tmpdir(), '.nexuscli-test');
|
|
27
|
+
fs.mkdirSync(baseDir, { recursive: true });
|
|
28
|
+
const validPath = fs.mkdtempSync(path.join(baseDir, 'ws-'));
|
|
20
29
|
const result = await manager.validateWorkspace(validPath);
|
|
21
30
|
expect(result).toBe(validPath);
|
|
22
31
|
});
|
|
@@ -28,7 +37,8 @@ describe('WorkspaceManager', () => {
|
|
|
28
37
|
});
|
|
29
38
|
|
|
30
39
|
test('should detect non-existent workspace', async () => {
|
|
31
|
-
const
|
|
40
|
+
const baseDir = path.join(process.env.HOME || os.tmpdir(), '.nexuscli-test');
|
|
41
|
+
const nonExistent = path.join(baseDir, 'nonexistent-workspace-12345');
|
|
32
42
|
await expect(manager.validateWorkspace(nonExistent)).rejects.toThrow('does not exist');
|
|
33
43
|
});
|
|
34
44
|
|
|
@@ -41,7 +51,7 @@ describe('WorkspaceManager', () => {
|
|
|
41
51
|
|
|
42
52
|
test('should extract title from messages', () => {
|
|
43
53
|
const messages = [
|
|
44
|
-
{ display: 'Implement user authentication feature' },
|
|
54
|
+
{ role: 'user', display: 'Implement user authentication feature' },
|
|
45
55
|
{ display: 'Follow up on previous discussion' }
|
|
46
56
|
];
|
|
47
57
|
const title = manager.extractTitle(messages);
|
|
@@ -62,11 +72,6 @@ describe('CliLoader', () => {
|
|
|
62
72
|
expect(loader.claudePath).toBe(expectedPath);
|
|
63
73
|
});
|
|
64
74
|
|
|
65
|
-
test('should initialize with correct historyPath', () => {
|
|
66
|
-
const expectedPath = require('path').join(process.env.HOME, '.claude', 'history.jsonl');
|
|
67
|
-
expect(loader.historyPath).toBe(expectedPath);
|
|
68
|
-
});
|
|
69
|
-
|
|
70
75
|
test('should have loadMessagesFromCLI method', () => {
|
|
71
76
|
expect(typeof loader.loadMessagesFromCLI).toBe('function');
|
|
72
77
|
});
|
|
@@ -147,7 +152,9 @@ describe('SummaryGenerator', () => {
|
|
|
147
152
|
describe('Integration - Service Interactions', () => {
|
|
148
153
|
test('WorkspaceManager should use consistent path resolution', async () => {
|
|
149
154
|
const manager = new WorkspaceManager();
|
|
150
|
-
const
|
|
155
|
+
const baseDir = path.join(process.env.HOME || os.tmpdir(), '.nexuscli-test');
|
|
156
|
+
fs.mkdirSync(baseDir, { recursive: true });
|
|
157
|
+
const testPath = fs.mkdtempSync(path.join(baseDir, 'ws-'));
|
|
151
158
|
const validated = await manager.validateWorkspace(testPath);
|
|
152
159
|
expect(validated).toBe(testPath);
|
|
153
160
|
});
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mmmbuto/nexuscli",
|
|
3
|
-
"version": "0.9.
|
|
4
|
-
"description": "NexusCLI - TRI CLI Control Plane (Claude/Codex/Gemini)",
|
|
3
|
+
"version": "0.9.7-termux",
|
|
4
|
+
"description": "NexusCLI - TRI CLI Control Plane (Claude/Codex/Gemini/Qwen)",
|
|
5
5
|
"main": "lib/server/server.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"nexuscli": "bin/nexuscli.js"
|