@mmmbuto/nexuscrew 0.2.0 → 0.2.2

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.
Files changed (41) hide show
  1. package/README.md +124 -56
  2. package/bin/nexuscrew.js +79 -152
  3. package/frontend/dist/assets/{index-8pw4eMB-.js → index-BsldfYnr.js} +1704 -1716
  4. package/frontend/dist/assets/index-DcQGg3dp.css +1 -0
  5. package/frontend/dist/index.html +2 -2
  6. package/lib/server/routes/hosts.js +3 -2
  7. package/lib/server/routes/launchers.js +12 -0
  8. package/lib/server/routes/models.js +16 -63
  9. package/lib/server/routes/runtime.js +10 -0
  10. package/lib/server/routes/send.js +97 -218
  11. package/lib/server/routes/sessions.js +7 -39
  12. package/lib/server/routes/status.js +16 -18
  13. package/lib/server/routes/tmux.js +88 -0
  14. package/lib/server/server.js +45 -65
  15. package/lib/services/engine-discovery.js +192 -445
  16. package/lib/services/session-store.js +86 -60
  17. package/lib/services/tmux-manager.js +103 -154
  18. package/package.json +3 -7
  19. package/frontend/dist/assets/index-BF0tdvNT.css +0 -1
  20. package/lib/config/manager.js +0 -362
  21. package/lib/config/models.js +0 -408
  22. package/lib/server/db/adapter.js +0 -274
  23. package/lib/server/db/drivers/sql-js.js +0 -75
  24. package/lib/server/db/migrate.js +0 -174
  25. package/lib/server/db/migrations/001_base_schema.sql +0 -70
  26. package/lib/server/middleware/auth.js +0 -134
  27. package/lib/server/middleware/rate-limit.js +0 -63
  28. package/lib/server/models/User.js +0 -128
  29. package/lib/server/routes/auth.js +0 -168
  30. package/lib/server/routes/keys.js +0 -15
  31. package/lib/server/routes/runtimes.js +0 -34
  32. package/lib/server/routes/speech.js +0 -75
  33. package/lib/server/routes/upload.js +0 -134
  34. package/lib/server/routes/wake-lock.js +0 -95
  35. package/lib/server/routes/workspaces.js +0 -101
  36. package/lib/server/services/context-bridge.js +0 -425
  37. package/lib/server/services/runtime-manager.js +0 -462
  38. package/lib/server/services/summary-generator.js +0 -309
  39. package/lib/server/services/workspace-manager.js +0 -79
  40. package/lib/utils/paths.js +0 -107
  41. package/lib/utils/termux.js +0 -145
@@ -1,462 +0,0 @@
1
- const { execFile } = require('child_process');
2
- const { promisify } = require('util');
3
- const { getCliTools, getModelById, getDefaultModelId } = require('../../config/models');
4
-
5
- const execFileAsync = promisify(execFile);
6
-
7
- function isTermux() {
8
- return (
9
- process.env.PREFIX?.includes('com.termux') ||
10
- process.env.TERMUX_VERSION !== undefined
11
- );
12
- }
13
-
14
- function getPlatformId() {
15
- if (isTermux()) return 'termux';
16
- if (process.platform === 'darwin') return 'macos';
17
- if (process.platform === 'linux') return 'linux';
18
- return process.platform;
19
- }
20
-
21
- function npmCommand() {
22
- if (isTermux()) return 'npm';
23
- return 'npm';
24
- }
25
-
26
- function shellJoin(commands) {
27
- return commands.filter(Boolean).join(' && ');
28
- }
29
-
30
- function buildProviderAuth({ providerId, dbKey, envVars = [], displayName, helpUrl, assignOpenAiKey = false }) {
31
- return {
32
- providerId,
33
- dbKey,
34
- envVars,
35
- displayName,
36
- helpUrl,
37
- assignOpenAiKey,
38
- };
39
- }
40
-
41
- function resolveClaudeCustomProfile(model) {
42
- switch (model.id) {
43
- case 'deepseek-reasoner':
44
- case 'deepseek-chat':
45
- return {
46
- env: {
47
- ANTHROPIC_BASE_URL: 'https://api.deepseek.com/anthropic',
48
- ANTHROPIC_MODEL: model.id,
49
- ANTHROPIC_SMALL_FAST_MODEL: 'deepseek-chat',
50
- API_TIMEOUT_MS: '900000',
51
- },
52
- providerAuth: buildProviderAuth({
53
- providerId: 'deepseek',
54
- dbKey: 'deepseek',
55
- envVars: ['DEEPSEEK_API_KEY'],
56
- displayName: 'DeepSeek',
57
- helpUrl: 'https://platform.deepseek.com/api_keys',
58
- }),
59
- };
60
- case 'glm-4.7':
61
- case 'glm-5':
62
- return {
63
- env: {
64
- ANTHROPIC_BASE_URL: 'https://api.z.ai/api/anthropic',
65
- ANTHROPIC_MODEL: model.id === 'glm-5' ? 'GLM-5' : 'GLM-4.7',
66
- ANTHROPIC_SMALL_FAST_MODEL: 'GLM-4.5-Air',
67
- API_TIMEOUT_MS: '3000000',
68
- },
69
- providerAuth: buildProviderAuth({
70
- providerId: 'zai',
71
- dbKey: 'zai',
72
- envVars: ['ZAI_API_KEY', 'ZAI_API_KEY_A', 'ZAI_API_KEY_P'],
73
- displayName: 'Z.ai',
74
- helpUrl: 'https://z.ai',
75
- }),
76
- };
77
- case 'qwen3.5-plus':
78
- case 'qwen3-max-2026-01-23':
79
- case 'kimi-k2.5':
80
- return {
81
- env: {
82
- ANTHROPIC_BASE_URL: 'https://coding-intl.dashscope.aliyuncs.com/apps/anthropic',
83
- ANTHROPIC_MODEL: model.id,
84
- ANTHROPIC_SMALL_FAST_MODEL: 'qwen3.5-plus',
85
- API_TIMEOUT_MS: '3000000',
86
- },
87
- providerAuth: buildProviderAuth({
88
- providerId: 'alibaba',
89
- dbKey: 'alibaba',
90
- envVars: ['ALIBABA_CODE_API_KEY'],
91
- displayName: 'Alibaba Code',
92
- helpUrl: 'https://dashscope.aliyun.com',
93
- }),
94
- };
95
- case 'MiniMax-M2.7':
96
- return {
97
- env: {
98
- ANTHROPIC_BASE_URL: 'https://api.minimax.io/anthropic',
99
- ANTHROPIC_MODEL: 'MiniMax-M2.7',
100
- ANTHROPIC_SMALL_FAST_MODEL: 'MiniMax-M2.7',
101
- API_TIMEOUT_MS: '120000',
102
- },
103
- providerAuth: buildProviderAuth({
104
- providerId: 'minimax',
105
- dbKey: 'minimax',
106
- envVars: ['MINIMAX_API_KEY'],
107
- displayName: 'MiniMax',
108
- helpUrl: 'https://api.minimax.io',
109
- }),
110
- };
111
- default:
112
- return { env: {}, providerAuth: null };
113
- }
114
- }
115
-
116
- function resolveCodexCustomProfile(model) {
117
- if (model.providerId === 'alibaba') {
118
- const providerName = 'alibaba-code';
119
- return {
120
- env: {},
121
- configOverrides: [
122
- `model="${model.id}"`,
123
- `model_provider="${providerName}"`,
124
- `model_providers.${providerName}.name="Alibaba-Code"`,
125
- `model_providers.${providerName}.base_url="https://coding-intl.dashscope.aliyuncs.com/v1"`,
126
- `model_providers.${providerName}.env_key="ALIBABA_CODE_API_KEY"`,
127
- `model_providers.${providerName}.wire_api="chat"`,
128
- ],
129
- providerAuth: buildProviderAuth({
130
- providerId: 'alibaba',
131
- dbKey: 'alibaba',
132
- envVars: ['ALIBABA_CODE_API_KEY'],
133
- displayName: 'Alibaba Code',
134
- helpUrl: 'https://dashscope.aliyun.com',
135
- assignOpenAiKey: true,
136
- }),
137
- };
138
- }
139
-
140
- if (model.providerId === 'zai') {
141
- const providerName = 'zai';
142
- return {
143
- env: {},
144
- configOverrides: [
145
- `model="${model.id}"`,
146
- `model_provider="${providerName}"`,
147
- `model_providers.${providerName}.name="ZAI"`,
148
- `model_providers.${providerName}.base_url="https://api.z.ai/api/coding/paas/v4"`,
149
- `model_providers.${providerName}.env_key="ZAI_API_KEY"`,
150
- `model_providers.${providerName}.wire_api="chat"`,
151
- ],
152
- providerAuth: buildProviderAuth({
153
- providerId: 'zai',
154
- dbKey: 'zai',
155
- envVars: ['ZAI_API_KEY', 'ZAI_API_KEY_A', 'ZAI_API_KEY_P'],
156
- displayName: 'Z.ai',
157
- helpUrl: 'https://z.ai',
158
- assignOpenAiKey: true,
159
- }),
160
- };
161
- }
162
-
163
- if (model.providerId === 'chutes') {
164
- const providerName = 'chutes';
165
- return {
166
- env: {},
167
- configOverrides: [
168
- `model="${model.id}"`,
169
- `model_provider="${providerName}"`,
170
- `model_providers.${providerName}.name="Chutes"`,
171
- `model_providers.${providerName}.base_url="https://llm.chutes.ai/v1"`,
172
- `model_providers.${providerName}.env_key="CHUTES_API_KEY"`,
173
- `model_providers.${providerName}.wire_api="chat"`,
174
- ],
175
- providerAuth: buildProviderAuth({
176
- providerId: 'chutes',
177
- dbKey: 'chutes',
178
- envVars: ['CHUTES_API_KEY'],
179
- displayName: 'Chutes',
180
- helpUrl: 'https://chutes.ai',
181
- assignOpenAiKey: true,
182
- }),
183
- };
184
- }
185
-
186
- return { env: {}, configOverrides: [], providerAuth: null };
187
- }
188
-
189
- function resolveCustomRuntimeProfile(model) {
190
- if (!model?.custom) {
191
- return { env: {}, configOverrides: [], providerAuth: null };
192
- }
193
-
194
- if (model.engine === 'claude') {
195
- return {
196
- configOverrides: [],
197
- ...resolveClaudeCustomProfile(model),
198
- };
199
- }
200
-
201
- if (model.engine === 'codex') {
202
- return resolveCodexCustomProfile(model);
203
- }
204
-
205
- return { env: {}, configOverrides: [], providerAuth: null };
206
- }
207
-
208
- function getRuntimeDefaults(platformId) {
209
- return {
210
- claude: {
211
- native: {
212
- runtimeId: 'claude-native',
213
- command: 'claude',
214
- source: 'npm',
215
- installCommand: `${npmCommand()} install -g @anthropic-ai/claude-code@latest`,
216
- updateCommand: `${npmCommand()} update -g @anthropic-ai/claude-code`,
217
- checkCommand: 'claude --version',
218
- sharedBinary: true,
219
- },
220
- custom: {
221
- runtimeId: 'claude-custom',
222
- command: 'claude',
223
- source: 'shared-cli',
224
- installCommand: `${npmCommand()} install -g @anthropic-ai/claude-code@latest`,
225
- updateCommand: `${npmCommand()} update -g @anthropic-ai/claude-code`,
226
- checkCommand: 'claude --version',
227
- sharedBinary: true,
228
- },
229
- },
230
- codex: {
231
- native: {
232
- runtimeId: 'codex-native',
233
- command: 'codex',
234
- source: 'npm',
235
- installCommand: `${npmCommand()} install -g @openai/codex@latest`,
236
- updateCommand: `${npmCommand()} update -g @openai/codex`,
237
- checkCommand: 'codex --version',
238
- },
239
- custom: {
240
- runtimeId: 'codex-custom',
241
- command: 'codex-lts',
242
- source: platformId === 'termux' ? 'npm' : 'manual',
243
- installCommand: platformId === 'termux'
244
- ? `${npmCommand()} install -g @mmmbuto/codex-cli-termux@0.80.6-lts`
245
- : null,
246
- updateCommand: platformId === 'termux'
247
- ? `${npmCommand()} update -g @mmmbuto/codex-cli-termux`
248
- : null,
249
- checkCommand: 'codex-lts --version',
250
- },
251
- },
252
- gemini: {
253
- native: {
254
- runtimeId: 'gemini-native',
255
- command: 'gemini',
256
- source: 'npm',
257
- installCommand: `${npmCommand()} install -g @google/gemini-cli@latest`,
258
- updateCommand: `${npmCommand()} update -g @google/gemini-cli`,
259
- checkCommand: 'gemini --version',
260
- },
261
- custom: {
262
- runtimeId: 'gemini-custom',
263
- command: 'gemini',
264
- source: 'npm',
265
- installCommand: `${npmCommand()} install -g @google/gemini-cli@latest`,
266
- updateCommand: `${npmCommand()} update -g @google/gemini-cli`,
267
- checkCommand: 'gemini --version',
268
- },
269
- },
270
- qwen: {
271
- native: {
272
- runtimeId: 'qwen-native',
273
- command: 'qwen',
274
- source: platformId === 'termux' ? 'npm' : 'npm',
275
- installCommand: `${npmCommand()} install -g @qwen-code/qwen-code@latest`,
276
- updateCommand: `${npmCommand()} update -g @qwen-code/qwen-code`,
277
- checkCommand: 'qwen --version',
278
- },
279
- custom: {
280
- runtimeId: 'qwen-custom',
281
- command: 'qwen',
282
- source: platformId === 'termux' ? 'npm' : 'npm',
283
- installCommand: `${npmCommand()} install -g @qwen-code/qwen-code@latest`,
284
- updateCommand: `${npmCommand()} update -g @qwen-code/qwen-code`,
285
- checkCommand: 'qwen --version',
286
- },
287
- },
288
- };
289
- }
290
-
291
- class RuntimeManager {
292
- constructor() {
293
- this.platformId = getPlatformId();
294
- }
295
-
296
- getToolCatalog() {
297
- return getCliTools();
298
- }
299
-
300
- getRuntimeDefinitions() {
301
- const defaults = getRuntimeDefaults(this.platformId);
302
- const engines = this.getToolCatalog();
303
-
304
- return Object.fromEntries(
305
- Object.entries(engines).map(([engineId, engine]) => {
306
- const lanes = Object.fromEntries(
307
- Object.entries(engine.lanes || {}).map(([laneId, lane]) => {
308
- const defaultLane = defaults[engineId]?.[laneId] || {};
309
- const command = defaultLane.command;
310
- const runtimeId = lane.runtimeId || defaultLane.runtimeId;
311
- const enabled = true;
312
-
313
- return [laneId, {
314
- engine: engineId,
315
- lane: laneId,
316
- laneLabel: lane.label || laneId,
317
- runtimeId,
318
- command,
319
- enabled,
320
- source: defaultLane.source || 'manual',
321
- installCommand: defaultLane.installCommand || null,
322
- updateCommand: defaultLane.updateCommand || null,
323
- checkCommand: defaultLane.checkCommand || `${command} --version`,
324
- sharedBinary: defaultLane.sharedBinary ?? false,
325
- }];
326
- })
327
- );
328
-
329
- return [engineId, lanes];
330
- })
331
- );
332
- }
333
-
334
- async probeCommand(command) {
335
- if (!command) {
336
- return { available: false, version: null, error: 'command not configured' };
337
- }
338
-
339
- try {
340
- const { stdout, stderr } = await execFileAsync(command, ['--version'], {
341
- timeout: 10000,
342
- env: process.env,
343
- });
344
- return {
345
- available: true,
346
- version: (stdout || stderr || '').trim() || 'unknown',
347
- error: null,
348
- };
349
- } catch (error) {
350
- return {
351
- available: false,
352
- version: null,
353
- error: error.message,
354
- };
355
- }
356
- }
357
-
358
- async getRuntimeInventory() {
359
- const definitions = this.getRuntimeDefinitions();
360
- const tools = this.getToolCatalog();
361
- const inventory = [];
362
-
363
- for (const [engineId, lanes] of Object.entries(definitions)) {
364
- for (const [laneId, runtime] of Object.entries(lanes)) {
365
- const probe = await this.probeCommand(runtime.command);
366
- const laneModels = tools[engineId]?.models?.filter((model) => model.lane === laneId) || [];
367
- inventory.push({
368
- ...runtime,
369
- platform: this.platformId,
370
- status: probe.available ? 'available' : 'missing',
371
- installedVersion: probe.version,
372
- latestVersion: 'upstream',
373
- available: probe.available,
374
- error: probe.error,
375
- models: laneModels.map((model) => ({
376
- id: model.id,
377
- label: model.label,
378
- providerId: model.providerId,
379
- })),
380
- actions: [
381
- runtime.installCommand ? 'install' : null,
382
- runtime.updateCommand ? 'update' : null,
383
- 'check',
384
- ].filter(Boolean),
385
- });
386
- }
387
- }
388
-
389
- return inventory;
390
- }
391
-
392
- async getRuntimeInventoryMap() {
393
- const inventory = await this.getRuntimeInventory();
394
- return Object.fromEntries(inventory.map((item) => [item.runtimeId, item]));
395
- }
396
-
397
- async getRuntimeAwareCliTools() {
398
- const tools = this.getToolCatalog();
399
- const inventoryMap = await this.getRuntimeInventoryMap();
400
-
401
- return Object.fromEntries(Object.entries(tools).map(([engineId, engine]) => {
402
- const models = (engine.models || []).map((model) => {
403
- const runtime = inventoryMap[model.runtimeId];
404
- return {
405
- ...model,
406
- availability: runtime?.status || 'unknown',
407
- runtimeStatus: runtime?.status || 'unknown',
408
- runtimeCommand: runtime?.command || null,
409
- runtimeSource: runtime?.source || null,
410
- available: runtime?.available ?? false,
411
- };
412
- });
413
-
414
- return [engineId, {
415
- ...engine,
416
- models,
417
- }];
418
- }));
419
- }
420
-
421
- resolveModel(modelId) {
422
- return getModelById(modelId) || getModelById(getDefaultModelId());
423
- }
424
-
425
- resolveRuntimeSelection({ engine, lane, runtimeId, modelId }) {
426
- const model = this.resolveModel(modelId);
427
- const resolvedEngine = engine || model?.engine;
428
- const resolvedLane = lane || model?.lane || 'native';
429
- const definitions = this.getRuntimeDefinitions();
430
- const runtime = runtimeId
431
- ? Object.values(definitions[resolvedEngine] || {}).find((entry) => entry.runtimeId === runtimeId)
432
- : definitions[resolvedEngine]?.[resolvedLane];
433
- const customProfile = resolveCustomRuntimeProfile(model);
434
-
435
- return {
436
- engine: resolvedEngine,
437
- lane: resolvedLane,
438
- runtimeId: runtime?.runtimeId || model?.runtimeId || null,
439
- command: runtime?.command || null,
440
- env: customProfile.env || {},
441
- configOverrides: customProfile.configOverrides || [],
442
- providerAuth: customProfile.providerAuth || null,
443
- model,
444
- runtime,
445
- };
446
- }
447
-
448
- resolveAction(runtimeId, action) {
449
- const definitions = this.getRuntimeDefinitions();
450
- for (const lanes of Object.values(definitions)) {
451
- for (const runtime of Object.values(lanes)) {
452
- if (runtime.runtimeId !== runtimeId) continue;
453
- if (action === 'install') return runtime.installCommand;
454
- if (action === 'update') return runtime.updateCommand;
455
- if (action === 'check') return runtime.checkCommand;
456
- }
457
- }
458
- return null;
459
- }
460
- }
461
-
462
- module.exports = RuntimeManager;