tabminal 2.0.9 → 2.0.11
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 +2 -2
- package/package.json +1 -1
- package/src/server.mjs +16 -0
- package/src/terminal-manager.mjs +21 -10
package/README.md
CHANGED
|
@@ -40,7 +40,7 @@ As a long-time terminal user who frequently needs to step away from my computer
|
|
|
40
40
|
|
|
41
41
|
### 🧠 AI-Native Intelligence
|
|
42
42
|
Powered by **modern AI models** (via OpenRouter or OpenAI), `t> Tabminal` understands your context.
|
|
43
|
-
*(Defaults to **Gemini 3 Flash** for OpenRouter or **GPT-5.
|
|
43
|
+
*(Defaults to **Gemini 3 Flash** for OpenRouter or **GPT-5.4** for OpenAI if not configured)*
|
|
44
44
|
* **Context-Aware Chat**: Type `# how do I...` to ask questions. The AI knows your **CWD**, **Environment**, and **Recent History**.
|
|
45
45
|
* **Auto-Fix**: Command failed? `t> Tabminal` automatically analyzes the exit code and error output to suggest fixes. No copy-pasting required.
|
|
46
46
|
* **Web Search**: Enable Google Search integration to let the AI fetch real-time answers from the web.
|
|
@@ -129,7 +129,7 @@ You can configure `t> Tabminal` via command-line arguments, environment variable
|
|
|
129
129
|
| `-k`, `--openrouter-key` | `TABMINAL_OPENROUTER_KEY` | OpenRouter API Key (Mutually exclusive with OpenAI) | `null` |
|
|
130
130
|
| `-o`, `--openai-key` | `TABMINAL_OPENAI_KEY` | OpenAI API Key (Mutually exclusive with OpenRouter) | `null` |
|
|
131
131
|
| `-u`, `--openai-api` | `TABMINAL_OPENAI_API` | OpenAI Base API URL (Optional) | `null` |
|
|
132
|
-
| `-m`, `--model` | `TABMINAL_MODEL` | AI Model ID | `gpt-5.
|
|
132
|
+
| `-m`, `--model` | `TABMINAL_MODEL` | AI Model ID | `gpt-5.4` (OpenAI) / `gemini-3-flash-preview` (OpenRouter) |
|
|
133
133
|
| `-f`, `--cloudflare-key` | `TABMINAL_CLOUDFLARE_KEY` | Cloudflare Tunnel Token | `null` |
|
|
134
134
|
| `-g`, `--google-key` | `TABMINAL_GOOGLE_KEY` | Google Search API Key | `null` |
|
|
135
135
|
| `-c`, `--google-cx` | `TABMINAL_GOOGLE_CX` | Google Search Engine ID (CX) | `null` |
|
package/package.json
CHANGED
package/src/server.mjs
CHANGED
|
@@ -275,6 +275,14 @@ app.use(router.allowedMethods());
|
|
|
275
275
|
|
|
276
276
|
const httpServer = createServer(app.callback());
|
|
277
277
|
const wss = new WebSocketServer({ noServer: true, verifyClient });
|
|
278
|
+
const httpConnections = new Set();
|
|
279
|
+
|
|
280
|
+
httpServer.on('connection', (socket) => {
|
|
281
|
+
httpConnections.add(socket);
|
|
282
|
+
socket.on('close', () => {
|
|
283
|
+
httpConnections.delete(socket);
|
|
284
|
+
});
|
|
285
|
+
});
|
|
278
286
|
|
|
279
287
|
httpServer.on('upgrade', (request, socket, head) => {
|
|
280
288
|
const url = new URL(request.url, `http://${request.headers.host}`);
|
|
@@ -370,6 +378,9 @@ function shutdown(signal) {
|
|
|
370
378
|
isShuttingDown = true;
|
|
371
379
|
console.log(`Shutting down (${signal})...`);
|
|
372
380
|
clearInterval(heartbeatInterval);
|
|
381
|
+
for (const socket of wss.clients) {
|
|
382
|
+
socket.terminate();
|
|
383
|
+
}
|
|
373
384
|
wss.close();
|
|
374
385
|
terminalManager.dispose();
|
|
375
386
|
|
|
@@ -382,6 +393,11 @@ function shutdown(signal) {
|
|
|
382
393
|
clearTimeout(forceExitTimer);
|
|
383
394
|
process.exit(0);
|
|
384
395
|
});
|
|
396
|
+
httpServer.closeIdleConnections?.();
|
|
397
|
+
httpServer.closeAllConnections?.();
|
|
398
|
+
for (const socket of httpConnections) {
|
|
399
|
+
socket.destroy();
|
|
400
|
+
}
|
|
385
401
|
}
|
|
386
402
|
|
|
387
403
|
process.on('SIGINT', () => shutdown('SIGINT'));
|
package/src/terminal-manager.mjs
CHANGED
|
@@ -9,13 +9,12 @@ import * as persistence from './persistence.mjs';
|
|
|
9
9
|
import { config } from './config.mjs';
|
|
10
10
|
|
|
11
11
|
function resolveShell() {
|
|
12
|
-
if (process.platform === 'win32') {
|
|
13
|
-
return process.env.COMSPEC || 'cmd.exe';
|
|
14
|
-
}
|
|
15
|
-
// config.shell has already handled process.env.SHELL and TABMINAL_SHELL via config.mjs
|
|
16
12
|
if (config.shell) {
|
|
17
13
|
return config.shell;
|
|
18
14
|
}
|
|
15
|
+
if (process.platform === 'win32') {
|
|
16
|
+
return process.env.COMSPEC || 'cmd.exe';
|
|
17
|
+
}
|
|
19
18
|
// Try to use Homebrew installed bash if available (newer version)
|
|
20
19
|
if (fs.existsSync('/opt/homebrew/bin/bash')) {
|
|
21
20
|
return '/opt/homebrew/bin/bash';
|
|
@@ -60,7 +59,12 @@ export class TerminalManager {
|
|
|
60
59
|
|
|
61
60
|
// Inject shell tools
|
|
62
61
|
const shellToolsPath = path.join(process.cwd(), 'shell');
|
|
63
|
-
|
|
62
|
+
const pathDelimiter = path.delimiter;
|
|
63
|
+
const pathKey = Object.keys(env).find((key) => key.toLowerCase() === 'path') || 'PATH';
|
|
64
|
+
const existingPath = env[pathKey];
|
|
65
|
+
env[pathKey] = existingPath
|
|
66
|
+
? `${shellToolsPath}${pathDelimiter}${existingPath}`
|
|
67
|
+
: shellToolsPath;
|
|
64
68
|
|
|
65
69
|
let args = [];
|
|
66
70
|
let initFilePath = null;
|
|
@@ -151,14 +155,17 @@ precmd_functions+=(_tabminal_zsh_apply_prompt_marker)
|
|
|
151
155
|
|
|
152
156
|
let ptyProcess;
|
|
153
157
|
try {
|
|
154
|
-
|
|
158
|
+
const ptyOptions = {
|
|
155
159
|
name: 'xterm-256color',
|
|
156
160
|
cols: cols,
|
|
157
161
|
rows: rows,
|
|
158
162
|
cwd: initialCwd,
|
|
159
|
-
env: env
|
|
160
|
-
|
|
161
|
-
|
|
163
|
+
env: env
|
|
164
|
+
};
|
|
165
|
+
if (process.platform !== 'win32') {
|
|
166
|
+
ptyOptions.encoding = 'utf8';
|
|
167
|
+
}
|
|
168
|
+
ptyProcess = pty.spawn(shell, args, ptyOptions);
|
|
162
169
|
} catch (err) {
|
|
163
170
|
const spawnInfo = {
|
|
164
171
|
shell,
|
|
@@ -295,7 +302,11 @@ precmd_functions+=(_tabminal_zsh_apply_prompt_marker)
|
|
|
295
302
|
this.disposing = true;
|
|
296
303
|
for (const session of this.sessions.values()) {
|
|
297
304
|
try {
|
|
298
|
-
|
|
305
|
+
if (process.platform === 'win32') {
|
|
306
|
+
session.pty.kill();
|
|
307
|
+
} else {
|
|
308
|
+
session.pty.kill('SIGHUP');
|
|
309
|
+
}
|
|
299
310
|
} catch {
|
|
300
311
|
// ignore
|
|
301
312
|
}
|