@ebowwa/terminal 0.3.8 → 0.3.9

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/dist/index.js CHANGED
@@ -443,7 +443,7 @@ var init_error = __esm(() => {
443
443
  };
444
444
  });
445
445
 
446
- // ../codespaces-types/runtime/ssh.js
446
+ // node_modules/@ebowwa/codespaces-types/runtime/ssh.js
447
447
  import { z } from "zod";
448
448
  var SSHOptionsSchema, SSHCommandSchema, SCPOptionsSchema, FilesListOptionsSchema, FilePreviewOptionsSchema;
449
449
  var init_ssh = __esm(() => {
@@ -513,7 +513,7 @@ var init_client = __esm(() => {
513
513
  // src/tmux.ts
514
514
  init_pool();
515
515
 
516
- // ../ssh/flags.js
516
+ // node_modules/@ebowwa/ssh/flags.js
517
517
  function sshConfig(key, value) {
518
518
  return { type: "config", key, value };
519
519
  }
@@ -2176,9 +2176,29 @@ async function validateSSHKeyForServer(host, keyPath, hetznerKeyId) {
2176
2176
  }
2177
2177
  // src/pty.ts
2178
2178
  var activeSessions = new Map;
2179
+ var activeReplSessions = new Map;
2179
2180
  function generateSessionId() {
2180
2181
  return `pty-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
2181
2182
  }
2183
+ function stripAnsi(str) {
2184
+ return str.replace(/\x1B\[[0-9;]*m/g, "").replace(/\x1B\][0-9;]*?(?:\x07|\x1B\\)/g, "").replace(/\x1B[\][0-9;]*[A-Za-z]/g, "").replace(/\x1B[()][AB012]/g, "").replace(/\x1B[78]/g, "").replace(/\r\n|\r/g, `
2185
+ `).trim();
2186
+ }
2187
+ function detectPrompt(output) {
2188
+ const promptPatterns = [
2189
+ /[$#]\s*$/,
2190
+ /~\s*$/,
2191
+ /]\$\s*$/,
2192
+ />\s*$/
2193
+ ];
2194
+ const cleanOutput = stripAnsi(output);
2195
+ for (const pattern of promptPatterns) {
2196
+ if (pattern.test(cleanOutput)) {
2197
+ return true;
2198
+ }
2199
+ }
2200
+ return false;
2201
+ }
2182
2202
  async function createPTYSession(host, user = "root", options = {}) {
2183
2203
  const { rows = 24, cols = 80, port = 22, keyPath } = options;
2184
2204
  const sessionId = generateSessionId();
@@ -2203,8 +2223,7 @@ async function createPTYSession(host, user = "root", options = {}) {
2203
2223
  }
2204
2224
  sshArgs.push("-t", "-t");
2205
2225
  sshArgs.push(`${user}@${host}`);
2206
- sshArgs.push("TERM=xterm-256color");
2207
- sshArgs.push("script", "-q", "/dev/null", "/bin/bash");
2226
+ sshArgs.push("TERM=xterm-256color script -q -c 'bash --norc --noprofile' /dev/null");
2208
2227
  const proc = Bun.spawn(sshArgs, {
2209
2228
  stdin: "pipe",
2210
2229
  stdout: "pipe",
@@ -2256,9 +2275,9 @@ async function writeToPTY(sessionId, data) {
2256
2275
  return false;
2257
2276
  }
2258
2277
  try {
2259
- const writer = session.stdin.getWriter();
2260
- await writer.write(new TextEncoder().encode(data));
2261
- writer.releaseLock();
2278
+ const stdin = session.stdin;
2279
+ await stdin.write(new TextEncoder().encode(data));
2280
+ await stdin.flush();
2262
2281
  return true;
2263
2282
  } catch {
2264
2283
  return false;
@@ -2331,6 +2350,141 @@ function getPTYSession(sessionId) {
2331
2350
  function getActivePTYSessions() {
2332
2351
  return Array.from(activeSessions.values());
2333
2352
  }
2353
+ async function createSSHReplSession(options) {
2354
+ const {
2355
+ host,
2356
+ user = "root",
2357
+ shell = "/bin/bash",
2358
+ initCommands = [],
2359
+ timeout = 30000,
2360
+ onConnect
2361
+ } = options;
2362
+ const sessionId = generateSessionId();
2363
+ const startTime = Date.now();
2364
+ const { sessionId: ptyId, initialOutput } = await createPTYSession(host, user, {
2365
+ port: options.port,
2366
+ keyPath: options.keyPath
2367
+ });
2368
+ const session = {
2369
+ id: sessionId,
2370
+ ptyId,
2371
+ host,
2372
+ user,
2373
+ cwd: "~",
2374
+ env: {},
2375
+ lastExitCode: 0,
2376
+ lastCommand: "",
2377
+ createdAt: startTime,
2378
+ connected: true
2379
+ };
2380
+ activeReplSessions.set(sessionId, session);
2381
+ for (const cmd of initCommands) {
2382
+ await execPTY(sessionId, cmd, { timeout });
2383
+ }
2384
+ onConnect?.(session);
2385
+ return session;
2386
+ }
2387
+ async function execPTY(sessionId, command, options = {}) {
2388
+ const replSession = activeReplSessions.get(sessionId);
2389
+ if (!replSession) {
2390
+ throw new Error(`REPL session not found: ${sessionId}`);
2391
+ }
2392
+ const session = activeSessions.get(replSession.ptyId);
2393
+ if (!session) {
2394
+ throw new Error(`PTY session not found: ${replSession.ptyId}`);
2395
+ }
2396
+ const startTime = Date.now();
2397
+ const timeout = options.timeout || 30000;
2398
+ const cmdWithNewline = command.endsWith(`
2399
+ `) ? command : `${command}
2400
+ `;
2401
+ const written = await writeToPTY(replSession.ptyId, cmdWithNewline);
2402
+ if (!written) {
2403
+ throw new Error("Failed to write to PTY");
2404
+ }
2405
+ let output = "";
2406
+ let stderrOutput = "";
2407
+ const deadline = startTime + timeout;
2408
+ while (Date.now() < deadline) {
2409
+ const chunk = await readFromPTY(replSession.ptyId, 200);
2410
+ if (chunk === null) {
2411
+ break;
2412
+ }
2413
+ output += chunk;
2414
+ if (detectPrompt(output)) {
2415
+ break;
2416
+ }
2417
+ }
2418
+ const exitCodeResult = await writeToPTY(replSession.ptyId, `echo "$?"
2419
+ `);
2420
+ await new Promise((resolve) => setTimeout(resolve, 100));
2421
+ const exitChunk = await readFromPTY(replSession.ptyId, 200);
2422
+ const exitCodeMatch = (exitChunk || "").trim().match(/^\d+$/);
2423
+ const exitCode = exitCodeMatch ? parseInt(exitCodeMatch[0], 10) : 0;
2424
+ if (command.startsWith("cd ") || command === "cd") {
2425
+ const cwdMatch = output.match(/^(.+?)\n/);
2426
+ if (cwdMatch) {
2427
+ replSession.cwd = cwdMatch[1].trim();
2428
+ }
2429
+ }
2430
+ const clean = stripAnsi(output).replace(/\r?\n/g, `
2431
+ `).trim();
2432
+ replSession.lastCommand = command;
2433
+ replSession.lastExitCode = exitCode;
2434
+ return {
2435
+ stdout: clean,
2436
+ stderr: stderrOutput,
2437
+ exitCode,
2438
+ cwd: replSession.cwd,
2439
+ duration: Date.now() - startTime,
2440
+ raw: output,
2441
+ clean
2442
+ };
2443
+ }
2444
+ async function execPTYBatch(sessionId, commands, options = {}) {
2445
+ const results = [];
2446
+ const errors = [];
2447
+ const startTime = Date.now();
2448
+ for (const command of commands) {
2449
+ try {
2450
+ const result = await execPTY(sessionId, command, options);
2451
+ results.push(result);
2452
+ if (result.exitCode !== 0 && options.stopOnError) {
2453
+ errors.push({ command, error: new Error(`Command failed with exit code ${result.exitCode}`) });
2454
+ break;
2455
+ }
2456
+ } catch (error) {
2457
+ const err = error instanceof Error ? error : new Error(String(error));
2458
+ errors.push({ command, error: err });
2459
+ if (options.stopOnError) {
2460
+ break;
2461
+ }
2462
+ }
2463
+ }
2464
+ return {
2465
+ results,
2466
+ errors,
2467
+ totalDuration: Date.now() - startTime
2468
+ };
2469
+ }
2470
+ function getSSHReplSession(sessionId) {
2471
+ return activeReplSessions.get(sessionId);
2472
+ }
2473
+ function getActiveSSHReplSessions() {
2474
+ return Array.from(activeReplSessions.values());
2475
+ }
2476
+ async function closeSSHReplSession(sessionId) {
2477
+ const session = activeReplSessions.get(sessionId);
2478
+ if (!session) {
2479
+ return false;
2480
+ }
2481
+ const result = await closePTYSession(session.ptyId);
2482
+ if (result) {
2483
+ session.connected = false;
2484
+ activeReplSessions.delete(sessionId);
2485
+ }
2486
+ return result;
2487
+ }
2334
2488
 
2335
2489
  // src/index.ts
2336
2490
  init_pool();
@@ -2338,7 +2492,7 @@ init_pool();
2338
2492
  // src/sessions.ts
2339
2493
  import path2 from "path";
2340
2494
 
2341
- // ../codespaces-types/compile/index.js
2495
+ // node_modules/@ebowwa/codespaces-types/compile/index.js
2342
2496
  var WebSocketCloseCode = {
2343
2497
  NORMAL_CLOSURE: 1000,
2344
2498
  ENDPOINT_GOING_AWAY: 1001,
@@ -3754,6 +3908,7 @@ export {
3754
3908
  getSessionCount,
3755
3909
  getSession,
3756
3910
  getSecurityEvents,
3911
+ getSSHReplSession,
3757
3912
  getSSHPool,
3758
3913
  getSSHFingerprint,
3759
3914
  getPaneHistory,
@@ -3767,6 +3922,7 @@ export {
3767
3922
  getDetailedLocalSessionInfo,
3768
3923
  getAllSessions,
3769
3924
  getAllSessionInfo,
3925
+ getActiveSSHReplSessions,
3770
3926
  getActiveSSHConnections,
3771
3927
  getActivePTYSessions,
3772
3928
  generateSessionName,
@@ -3776,14 +3932,18 @@ export {
3776
3932
  execViaTmux,
3777
3933
  execSSHParallel,
3778
3934
  execSSH,
3935
+ execPTYBatch,
3936
+ execPTY,
3779
3937
  ensureTmux,
3780
3938
  ensureCorrectSSHKey,
3781
3939
  detectNetworkError,
3782
3940
  detachWebSocket,
3941
+ createSSHReplSession,
3783
3942
  createPTYSession,
3784
3943
  createOrAttachTmuxSession,
3785
3944
  createLocalTmuxSSHSession,
3786
3945
  closeSession,
3946
+ closeSSHReplSession,
3787
3947
  closePTYSession,
3788
3948
  closeGlobalSSHPool,
3789
3949
  clearSecurityEvents,
package/dist/mcp/index.js CHANGED
@@ -444,7 +444,7 @@ var init_error = __esm(() => {
444
444
  };
445
445
  });
446
446
 
447
- // ../codespaces-types/runtime/ssh.js
447
+ // node_modules/@ebowwa/codespaces-types/runtime/ssh.js
448
448
  import { z } from "zod";
449
449
  var SSHOptionsSchema, SSHCommandSchema, SCPOptionsSchema, FilesListOptionsSchema, FilePreviewOptionsSchema;
450
450
  var init_ssh = __esm(() => {
@@ -514,7 +514,7 @@ var init_client = __esm(() => {
514
514
  // src/tmux.ts
515
515
  init_pool();
516
516
 
517
- // ../ssh/flags.js
517
+ // node_modules/@ebowwa/ssh/flags.js
518
518
  function sshConfig(key, value) {
519
519
  return { type: "config", key, value };
520
520
  }
@@ -1871,6 +1871,7 @@ import { promisify } from "util";
1871
1871
  var execAsync = promisify(__require("child_process").exec);
1872
1872
  // src/pty.ts
1873
1873
  var activeSessions = new Map;
1874
+ var activeReplSessions = new Map;
1874
1875
 
1875
1876
  // src/index.ts
1876
1877
  init_pool();
@@ -1878,7 +1879,7 @@ init_pool();
1878
1879
  // src/sessions.ts
1879
1880
  import path2 from "path";
1880
1881
 
1881
- // ../codespaces-types/compile/index.js
1882
+ // node_modules/@ebowwa/codespaces-types/compile/index.js
1882
1883
  var WebSocketCloseCode = {
1883
1884
  NORMAL_CLOSURE: 1000,
1884
1885
  ENDPOINT_GOING_AWAY: 1001,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ebowwa/terminal",
3
- "version": "0.3.8",
3
+ "version": "0.3.9",
4
4
  "description": "Terminal session management with tmux integration, SSH client, WebSocket support, and MCP interface",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",