@mmmbuto/nexuscli 0.9.4 → 0.9.5

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 CHANGED
@@ -27,19 +27,18 @@ NexusCLI is a lightweight, Termux-first AI cockpit to orchestrate Claude Code, C
27
27
 
28
28
  ---
29
29
 
30
- ## Highlights (v0.9.4)
30
+ ## Highlights (v0.9.5)
31
+
32
+ - **GPT-5.2 Codex**: Added latest frontier agentic coding model as default
33
+ - Updated Codex model catalog to match latest OpenAI CLI
34
+
35
+ ### v0.9.4
31
36
 
32
37
  - **Dark/Light Theme**: Toggle between themes via UserMenu (persisted in localStorage)
33
38
  - **Rate Limiting**: Chat endpoints now protected (10 req/min per user)
34
39
  - **Architecture Docs**: New `docs/ARCHITECTURE.md` with system diagrams
35
40
  - CSS refactored to use CSS variables for theming support
36
41
 
37
- ### v0.9.3
38
-
39
- - Fix: sanitize Termux workspace paths (auto-correct `/data/data/com/termux/...`), preventing Claude spawn ENOENT and stalled GLM-4.6/DeepSeek runs.
40
- - Workspaces API now filters/merges invalid paths so the UI dropdown stays clean.
41
- - Session importer reads `cwd` from Claude session files when available (more accurate workspace mapping).
42
-
43
42
  ## Features
44
43
 
45
44
  - Multi-engine support (Claude, Codex, Gemini)
@@ -8614,4 +8614,4 @@ ${J}`);const nn={id:`user-${Date.now()}`,role:"user",content:De,created_at:Date.
8614
8614
  🇪🇸 Tu interfaz para tu CLI de IAs.
8615
8615
  🇨🇳 你的 AI CLI 界面。
8616
8616
  🇯🇵 あなたの AI CLI インターフェース。
8617
- 🇷🇺 Твой интерфейс для CLI ИИ.`,subtitle:""},kSe="Nuova Chat",gSe="Workspace",vSe="Invia",MSe={inputPlaceholder:"Scrivi un messaggio..."},xSe={searchPlaceholder:"Cerca chat..."},wSe={welcome:mSe,newChat:kSe,workspace:gSe,send:vSe,chat:MSe,sidebar:xSe},bSe={title:"🇬🇧 Your interface for your AIs CLI",subtitle:""},_Se="New Chat",SSe="Workspace",LSe="Send",NSe={inputPlaceholder:"Type a message..."},CSe={searchPlaceholder:"Search chats..."},ISe={welcome:bSe,newChat:_Se,workspace:SSe,send:LSe,chat:NSe,sidebar:CSe},ASe={title:"🇪🇸 Tu interfaz para tu CLI de IAs",subtitle:""},ESe="Nueva Chat",TSe="Espacio de trabajo",zSe="Enviar",$Se={inputPlaceholder:"Escribe un mensaje..."},RSe={searchPlaceholder:"Buscar chats..."},OSe={welcome:ASe,newChat:ESe,workspace:TSe,send:zSe,chat:$Se,sidebar:RSe},DSe={title:"🇷🇺 Твой интерфейс для CLI ИИ",subtitle:""},PSe="Новый чат",qSe="Рабочее пространство",FSe="Отправить",BSe={inputPlaceholder:"Введите сообщение..."},HSe={searchPlaceholder:"Поиск чатов..."},jSe={welcome:DSe,newChat:PSe,workspace:qSe,send:FSe,chat:BSe,sidebar:HSe},VSe={title:"🇯🇵 あなたの AI CLI インターフェース",subtitle:""},USe="新しいチャット",GSe="ワークスペース",WSe="送信",KSe={inputPlaceholder:"メッセージを入力..."},ZSe={searchPlaceholder:"チャットを検索..."},XSe={welcome:VSe,newChat:USe,workspace:GSe,send:WSe,chat:KSe,sidebar:ZSe},YSe={title:"🇨🇳 你的 AI CLI 界面",subtitle:""},QSe="新对话",JSe="工作区",eLe="发送",tLe={inputPlaceholder:"输入消息..."},nLe={searchPlaceholder:"搜索对话..."},aLe={welcome:YSe,newChat:QSe,workspace:JSe,send:eLe,chat:tLe,sidebar:nLe};Tt.use(OH).use(IU).init({resources:{it:{translation:wSe},en:{translation:ISe},es:{translation:OSe},ru:{translation:jSe},ja:{translation:XSe},zh:{translation:aLe}},fallbackLng:"it",debug:!1,interpolation:{escapeValue:!1},detection:{order:["localStorage","navigator"],caches:["localStorage"]}});ZA.createRoot(document.getElementById("root")).render(_.jsx(aj.StrictMode,{children:_.jsx(C_e,{})}));
8617
+ 🇷🇺 Твой интерфейс для CLI ИИ.`,subtitle:""},kSe="Nuova Chat",gSe="Workspace",vSe="Invia",MSe={inputPlaceholder:"Scrivi un messaggio..."},xSe={searchPlaceholder:"Cerca chat..."},wSe={welcome:mSe,newChat:kSe,workspace:gSe,send:vSe,chat:MSe,sidebar:xSe},bSe={title:"🇬🇧 Your interface for your AIs CLI",subtitle:""},_Se="New Chat",SSe="Workspace",LSe="Send",NSe={inputPlaceholder:"Type a message..."},CSe={searchPlaceholder:"Search chats..."},ISe={welcome:bSe,newChat:_Se,workspace:SSe,send:LSe,chat:NSe,sidebar:CSe},ASe={title:"🇪🇸 Tu interfaz para tu CLI de IAs",subtitle:""},ESe="Nueva Chat",TSe="Espacio de trabajo",zSe="Enviar",$Se={inputPlaceholder:"Escribe un mensaje..."},RSe={searchPlaceholder:"Buscar chats..."},OSe={welcome:ASe,newChat:ESe,workspace:TSe,send:zSe,chat:$Se,sidebar:RSe},DSe={title:"🇷🇺 Твой интерфейс для CLI ИИ",subtitle:""},PSe="Новый чат",qSe="Рабочее пространство",FSe="Отправить",BSe={inputPlaceholder:"Введите сообщение..."},HSe={searchPlaceholder:"Поиск чатов..."},jSe={welcome:DSe,newChat:PSe,workspace:qSe,send:FSe,chat:BSe,sidebar:HSe},VSe={title:"🇯🇵 あなたの AI CLI インターフェース",subtitle:""},USe="新しいチャット",GSe="ワークスペース",WSe="送信",KSe={inputPlaceholder:"メッセージを入力..."},ZSe={searchPlaceholder:"チャットを検索..."},XSe={welcome:VSe,newChat:USe,workspace:GSe,send:WSe,chat:KSe,sidebar:ZSe},YSe={title:"🇨🇳 你的 AI CLI 界面",subtitle:""},QSe="新对话",JSe="工作区",eLe="发送",tLe={inputPlaceholder:"输入消息..."},nLe={searchPlaceholder:"搜索对话..."},aLe={welcome:YSe,newChat:QSe,workspace:JSe,send:eLe,chat:tLe,sidebar:nLe};Tt.use(OH).use(IU).init({resources:{it:{translation:wSe},en:{translation:ISe},es:{translation:OSe},ru:{translation:jSe},ja:{translation:XSe},zh:{translation:aLe}},fallbackLng:"en",debug:!1,interpolation:{escapeValue:!1},detection:{order:["localStorage","navigator"],caches:["localStorage"]}});ZA.createRoot(document.getElementById("root")).render(_.jsx(aj.StrictMode,{children:_.jsx(C_e,{})}));
@@ -59,7 +59,7 @@
59
59
 
60
60
  <!-- Prevent Scaling on iOS -->
61
61
  <meta name="format-detection" content="telephone=no" />
62
- <script type="module" crossorigin src="/assets/index-CxYyVrH5.js"></script>
62
+ <script type="module" crossorigin src="/assets/index-CvOYN8ds.js"></script>
63
63
  <link rel="stylesheet" crossorigin href="/assets/index-BAfxoAUN.css">
64
64
  </head>
65
65
  <body>
@@ -1,5 +1,5 @@
1
1
  // NexusCLI Service Worker
2
- const CACHE_VERSION = 'nexuscli-v1766695496989';
2
+ const CACHE_VERSION = 'nexuscli-v1766701518522';
3
3
  const STATIC_CACHE = `${CACHE_VERSION}-static`;
4
4
  const DYNAMIC_CACHE = `${CACHE_VERSION}-dynamic`;
5
5
 
@@ -80,11 +80,21 @@ function getCliTools() {
80
80
  enabled: true,
81
81
  endpoint: '/api/v1/codex',
82
82
  models: [
83
+ {
84
+ id: 'gpt-5.2-codex',
85
+ name: 'gpt-5.2-codex',
86
+ label: 'GPT-5.2 Codex',
87
+ description: '🤖 Latest frontier agentic coding model',
88
+ category: 'codex',
89
+ reasoningEfforts: ['low', 'medium', 'high', 'xhigh'],
90
+ defaultReasoning: 'xhigh',
91
+ default: true
92
+ },
83
93
  {
84
94
  id: 'gpt-5.2',
85
95
  name: 'gpt-5.2',
86
96
  label: 'GPT-5.2',
87
- description: '🧠 Next Gen Reasoning',
97
+ description: '🧠 Latest frontier model',
88
98
  category: 'codex',
89
99
  reasoningEfforts: ['low', 'medium', 'high', 'xhigh'],
90
100
  defaultReasoning: 'xhigh'
@@ -93,11 +103,10 @@ function getCliTools() {
93
103
  id: 'gpt-5.1-codex-max',
94
104
  name: 'gpt-5.1-codex-max',
95
105
  label: 'GPT-5.1 Codex Max',
96
- description: '💎 Extra High reasoning (best)',
106
+ description: '💎 Deep and fast reasoning',
97
107
  category: 'codex',
98
108
  reasoningEfforts: ['low', 'medium', 'high', 'xhigh'],
99
- defaultReasoning: 'xhigh',
100
- default: true
109
+ defaultReasoning: 'xhigh'
101
110
  },
102
111
  {
103
112
  id: 'gpt-5.1-codex',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mmmbuto/nexuscli",
3
- "version": "0.9.4",
3
+ "version": "0.9.5",
4
4
  "description": "NexusCLI - TRI CLI Control Plane (Claude/Codex/Gemini)",
5
5
  "main": "lib/server/server.js",
6
6
  "bin": {
@@ -1,164 +0,0 @@
1
- const pty = require('./pty-adapter');
2
- const OutputParser = require('./output-parser');
3
-
4
- /**
5
- * CLI Wrapper - Generic CLI tool execution (adapted from NexusChat claude-wrapper)
6
- * Spawns PTY processes for any CLI tool (bash, git, docker, python, etc.)
7
- * Streams output via SSE
8
- */
9
- class CliWrapper {
10
- constructor(options = {}) {
11
- this.workspaceDir = options.workspaceDir || process.cwd();
12
- this.activeJobs = new Map();
13
- }
14
-
15
- /**
16
- * Execute command via PTY
17
- * @param {Object} params - { jobId, tool, command, workingDir, timeout, onStatus }
18
- * @returns {Promise<Object>} - { exitCode, stdout, stderr, duration }
19
- */
20
- async execute({ jobId, tool = 'bash', command, workingDir, timeout = 30000, onStatus }) {
21
- return new Promise((resolve, reject) => {
22
- const parser = new OutputParser();
23
- const startTime = Date.now();
24
-
25
- // Determine shell command
26
- let shellCmd = '/bin/bash';
27
- let args = ['-c', command];
28
-
29
- // Tool-specific handling
30
- if (tool === 'bash') {
31
- shellCmd = '/bin/bash';
32
- args = ['-c', command];
33
- } else if (tool === 'python') {
34
- shellCmd = '/usr/bin/python3';
35
- args = ['-c', command];
36
- } else if (tool === 'node') {
37
- shellCmd = '/usr/bin/node';
38
- args = ['-e', command];
39
- } else {
40
- // Generic: assume tool is in PATH
41
- shellCmd = tool;
42
- args = command.split(' ');
43
- }
44
-
45
- console.log(`[CliWrapper] Executing: ${tool} - ${command.substring(0, 50)}...`);
46
- console.log(`[CliWrapper] Working dir: ${workingDir || this.workspaceDir}`);
47
-
48
- // Spawn PTY process
49
- let ptyProcess;
50
- try {
51
- ptyProcess = pty.spawn(shellCmd, args, {
52
- name: 'xterm-color',
53
- cols: 80,
54
- rows: 30,
55
- cwd: workingDir || this.workspaceDir,
56
- env: process.env,
57
- });
58
- } catch (err) {
59
- return reject(err);
60
- }
61
-
62
- this.activeJobs.set(jobId, ptyProcess);
63
-
64
- let stdout = '';
65
- let stderr = '';
66
-
67
- // Handle timeout
68
- const timer = setTimeout(() => {
69
- console.log(`[CliWrapper] Job ${jobId} timeout`);
70
- ptyProcess.kill('SIGTERM');
71
-
72
- if (onStatus) {
73
- onStatus({
74
- type: 'error',
75
- error: 'Command timeout exceeded',
76
- timeout
77
- });
78
- }
79
- }, timeout);
80
-
81
- // Handle process errors
82
- if (typeof ptyProcess.on === 'function') {
83
- ptyProcess.on('error', (err) => {
84
- console.error('[CliWrapper] Spawn error:', err);
85
- reject(err);
86
- });
87
- } else if (typeof ptyProcess.onError === 'function') {
88
- ptyProcess.onError((err) => {
89
- console.error('[CliWrapper] Spawn error:', err);
90
- reject(err);
91
- });
92
- }
93
-
94
- // Handle output
95
- ptyProcess.onData((data) => {
96
- stdout += data;
97
-
98
- // Parse output and emit status events
99
- if (onStatus) {
100
- try {
101
- const events = parser.parse(data);
102
- events.forEach(event => {
103
- onStatus(event);
104
- });
105
- } catch (parseError) {
106
- console.error('[CliWrapper] Parser error:', parseError);
107
- }
108
- }
109
- });
110
-
111
- // Handle exit
112
- ptyProcess.onExit(({ exitCode }) => {
113
- clearTimeout(timer);
114
- this.activeJobs.delete(jobId);
115
-
116
- const duration = Date.now() - startTime;
117
-
118
- // Clean ANSI escape codes
119
- const cleanStdout = stdout
120
- .replace(/\x1B\[[0-9;]*[a-zA-Z]/g, '') // ANSI codes
121
- .replace(/\x1B\[\?[0-9;]*[a-zA-Z]/g, '') // Private modes
122
- .trim();
123
-
124
- console.log(`[CliWrapper] Job ${jobId} exit: ${exitCode} (${duration}ms)`);
125
-
126
- if (exitCode !== 0) {
127
- // Extract stderr (if any)
128
- stderr = cleanStdout; // In PTY mode, stderr is mixed with stdout
129
- }
130
-
131
- resolve({
132
- exitCode,
133
- stdout: cleanStdout,
134
- stderr,
135
- duration
136
- });
137
- });
138
- });
139
- }
140
-
141
- /**
142
- * Kill running job
143
- * @param {string} jobId - Job ID
144
- */
145
- kill(jobId) {
146
- const ptyProcess = this.activeJobs.get(jobId);
147
- if (ptyProcess) {
148
- ptyProcess.kill('SIGTERM');
149
- this.activeJobs.delete(jobId);
150
- console.log(`[CliWrapper] Killed job ${jobId}`);
151
- return true;
152
- }
153
- return false;
154
- }
155
-
156
- /**
157
- * Get active job count
158
- */
159
- getActiveJobCount() {
160
- return this.activeJobs.size;
161
- }
162
- }
163
-
164
- module.exports = CliWrapper;
@@ -1,132 +0,0 @@
1
- /**
2
- * OutputParser - Parse CLI stdout/stderr into structured events
3
- * Adapted from NexusChat output-parser.js
4
- */
5
- class OutputParser {
6
- constructor() {
7
- this.state = 'idle';
8
- this.buffer = '';
9
- this.lineBuffer = '';
10
- }
11
-
12
- /**
13
- * Regex patterns for detecting CLI output markers
14
- */
15
- static PATTERNS = {
16
- // Tool execution patterns (generic)
17
- toolExecution: /(?:Running|Executing)\s+(\w+)(?:\s+(?:command|tool))?:\s*(.+)/i,
18
-
19
- // Common CLI patterns
20
- errorPattern: /(?:Error|ERROR|Failed|FAILED|Exception):\s*(.+)/i,
21
- warningPattern: /(?:Warning|WARNING|warn):\s*(.+)/i,
22
-
23
- // ANSI escape codes
24
- ansiCodes: /\x1B\[[0-9;]*[a-zA-Z]/g,
25
- ansiPrivate: /\x1B\[\?[0-9;]*[a-zA-Z]/g,
26
- };
27
-
28
- /**
29
- * Parse chunk of stdout and return array of events
30
- * @param {string} chunk - Raw stdout chunk from PTY
31
- * @returns {Array} Array of event objects
32
- */
33
- parse(chunk) {
34
- const events = [];
35
-
36
- // Add to buffer
37
- this.buffer += chunk;
38
- this.lineBuffer += chunk;
39
-
40
- // Process line by line
41
- const lines = this.lineBuffer.split('\n');
42
- this.lineBuffer = lines.pop(); // Keep incomplete line
43
-
44
- for (const line of lines) {
45
- const lineEvents = this.parseLine(line);
46
- events.push(...lineEvents);
47
- }
48
-
49
- // Also emit raw output chunks
50
- const cleanChunk = this.cleanAnsi(chunk);
51
- if (cleanChunk && cleanChunk.trim()) {
52
- events.push({
53
- type: 'output_chunk',
54
- stream: 'stdout',
55
- text: cleanChunk,
56
- isIncremental: true,
57
- });
58
- }
59
-
60
- return events;
61
- }
62
-
63
- /**
64
- * Parse single line
65
- * @param {string} line - Single line of output
66
- * @returns {Array} Events from this line
67
- */
68
- parseLine(line) {
69
- const events = [];
70
- const cleanLine = this.cleanAnsi(line);
71
-
72
- // Check for tool execution
73
- const toolMatch = cleanLine.match(OutputParser.PATTERNS.toolExecution);
74
- if (toolMatch) {
75
- const [, tool, command] = toolMatch;
76
- events.push({
77
- type: 'status',
78
- category: 'tool',
79
- tool,
80
- message: `${tool}: ${command.substring(0, 60)}${command.length > 60 ? '...' : ''}`,
81
- icon: '🔧',
82
- timestamp: new Date().toISOString(),
83
- });
84
- }
85
-
86
- // Check for errors
87
- if (OutputParser.PATTERNS.errorPattern.test(cleanLine)) {
88
- const [, errorMsg] = cleanLine.match(OutputParser.PATTERNS.errorPattern);
89
- events.push({
90
- type: 'status',
91
- category: 'warning',
92
- message: `Error: ${errorMsg}`,
93
- icon: '⚠️',
94
- timestamp: new Date().toISOString(),
95
- });
96
- }
97
-
98
- // Check for warnings
99
- if (OutputParser.PATTERNS.warningPattern.test(cleanLine)) {
100
- const [, warnMsg] = cleanLine.match(OutputParser.PATTERNS.warningPattern);
101
- events.push({
102
- type: 'status',
103
- category: 'warning',
104
- message: `Warning: ${warnMsg}`,
105
- icon: '⚠️',
106
- timestamp: new Date().toISOString(),
107
- });
108
- }
109
-
110
- return events;
111
- }
112
-
113
- /**
114
- * Clean ANSI escape codes from text
115
- */
116
- cleanAnsi(text) {
117
- return text
118
- .replace(OutputParser.PATTERNS.ansiCodes, '')
119
- .replace(OutputParser.PATTERNS.ansiPrivate, '');
120
- }
121
-
122
- /**
123
- * Reset parser state
124
- */
125
- reset() {
126
- this.state = 'idle';
127
- this.buffer = '';
128
- this.lineBuffer = '';
129
- }
130
- }
131
-
132
- module.exports = OutputParser;
@@ -1,81 +0,0 @@
1
- /**
2
- * PTY Adapter for NexusCLI (Termux)
3
- * Provides node-pty-like interface using child_process.spawn
4
- * Termux-only: no native node-pty compilation needed
5
- *
6
- * @version 0.5.0 - Added stdin support for interrupt (ESC key)
7
- */
8
-
9
- const { spawn: cpSpawn } = require('child_process');
10
-
11
- /**
12
- * Spawn a process with node-pty-like interface
13
- * @param {string} command - Command to spawn
14
- * @param {string[]} args - Arguments
15
- * @param {Object} options - Spawn options (cwd, env)
16
- * @returns {Object} PTY-like interface with onData, onExit, kill
17
- */
18
- function spawn(command, args, options = {}) {
19
- const proc = cpSpawn(command, args, {
20
- cwd: options.cwd,
21
- env: options.env,
22
- shell: false,
23
- stdio: ['pipe', 'pipe', 'pipe'], // stdin enabled for interrupt support
24
- });
25
-
26
- const dataHandlers = [];
27
- const exitHandlers = [];
28
- const errorHandlers = [];
29
-
30
- proc.stdout.on('data', (buf) => {
31
- const data = buf.toString();
32
- console.log('[PTY-Adapter] stdout:', data.substring(0, 200));
33
- dataHandlers.forEach((fn) => fn(data));
34
- });
35
-
36
- proc.stderr.on('data', (buf) => {
37
- const data = buf.toString();
38
- console.log('[PTY-Adapter] stderr:', data.substring(0, 200));
39
- dataHandlers.forEach((fn) => fn(data));
40
- });
41
-
42
- proc.on('close', (code) => {
43
- exitHandlers.forEach((fn) => fn({ exitCode: code ?? 0 }));
44
- });
45
-
46
- proc.on('error', (err) => {
47
- console.error('[PTY-Adapter] Error:', err.message);
48
- errorHandlers.forEach((fn) => fn(err));
49
- });
50
-
51
- return {
52
- onData: (fn) => dataHandlers.push(fn),
53
- onExit: (fn) => exitHandlers.push(fn),
54
- onError: (fn) => errorHandlers.push(fn),
55
- write: (data) => proc.stdin?.writable && proc.stdin.write(data),
56
- /**
57
- * Send ESC key (0x1B) to interrupt CLI
58
- * Used to gracefully stop Claude/Gemini CLI execution
59
- * @returns {boolean} true if ESC was sent successfully
60
- */
61
- sendEsc: () => {
62
- if (proc.stdin?.writable) {
63
- proc.stdin.write('\x1B');
64
- return true;
65
- }
66
- return false;
67
- },
68
- kill: (signal = 'SIGTERM') => proc.kill(signal),
69
- pid: proc.pid,
70
- };
71
- }
72
-
73
- /**
74
- * Check if native PTY is available
75
- * Always returns false on Termux (we use spawn adapter)
76
- */
77
- function isPtyAvailable() {
78
- return false;
79
- }
80
-
81
- module.exports = { spawn, isPtyAvailable };