sessioncast-cli 1.0.0 → 1.1.1

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 (48) hide show
  1. package/README.md +65 -40
  2. package/dist/agent/session-handler.d.ts +1 -0
  3. package/dist/agent/session-handler.js +42 -0
  4. package/dist/agent/tmux-executor.d.ts +66 -0
  5. package/dist/agent/tmux-executor.js +368 -0
  6. package/dist/agent/tmux.d.ts +9 -1
  7. package/dist/agent/tmux.js +52 -76
  8. package/dist/agent/websocket.d.ts +2 -8
  9. package/dist/agent/websocket.js +78 -14
  10. package/dist/autopilot/index.d.ts +94 -0
  11. package/dist/autopilot/index.js +322 -0
  12. package/dist/autopilot/mission-analyzer.d.ts +27 -0
  13. package/dist/autopilot/mission-analyzer.js +232 -0
  14. package/dist/autopilot/project-detector.d.ts +12 -0
  15. package/dist/autopilot/project-detector.js +326 -0
  16. package/dist/autopilot/source-scanner.d.ts +26 -0
  17. package/dist/autopilot/source-scanner.js +285 -0
  18. package/dist/autopilot/speckit-generator.d.ts +60 -0
  19. package/dist/autopilot/speckit-generator.js +511 -0
  20. package/dist/autopilot/types.d.ts +110 -0
  21. package/dist/autopilot/types.js +6 -0
  22. package/dist/autopilot/workflow-generator.d.ts +33 -0
  23. package/dist/autopilot/workflow-generator.js +278 -0
  24. package/dist/commands/autopilot.d.ts +30 -0
  25. package/dist/commands/autopilot.js +262 -0
  26. package/dist/commands/login.d.ts +2 -1
  27. package/dist/commands/login.js +199 -8
  28. package/dist/commands/project.d.ts +1 -1
  29. package/dist/commands/project.js +4 -13
  30. package/dist/config.d.ts +20 -0
  31. package/dist/config.js +69 -2
  32. package/dist/index.js +7 -47
  33. package/dist/project/executor.d.ts +8 -53
  34. package/dist/project/executor.js +64 -520
  35. package/dist/project/manager.d.ts +0 -13
  36. package/dist/project/manager.js +0 -107
  37. package/dist/project/relay-client.d.ts +18 -68
  38. package/dist/project/relay-client.js +134 -130
  39. package/dist/project/types.d.ts +5 -0
  40. package/dist/utils/fileUtils.d.ts +28 -0
  41. package/dist/utils/fileUtils.js +159 -0
  42. package/dist/utils/oauthServer.d.ts +18 -0
  43. package/dist/utils/oauthServer.js +244 -0
  44. package/dist/utils/pkce.d.ts +16 -0
  45. package/dist/utils/pkce.js +73 -0
  46. package/package.json +7 -14
  47. package/scripts/postinstall.js +75 -0
  48. package/LICENSE +0 -21
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # SessionCast CLI
2
2
 
3
- Node.js agent and CLI for [SessionCast](https://sessioncast.io) - a real-time terminal sharing platform.
3
+ Node.js agent and CLI for SessionCast - a real-time terminal streaming platform.
4
4
 
5
5
  ## Features
6
6
 
@@ -9,7 +9,7 @@ Node.js agent and CLI for [SessionCast](https://sessioncast.io) - a real-time te
9
9
  - **Real-time screen capture**: Streams terminal output with gzip compression
10
10
  - **Circuit breaker**: Prevents reconnection storms with exponential backoff
11
11
  - **Interactive control**: Supports keyboard input, resize, and session management
12
- - **File viewer**: Cmd+Click on file paths to view files in browser
12
+ - **API integration**: External command execution and LLM service support
13
13
 
14
14
  ### CLI Commands
15
15
  - `sessioncast login <api-key>` - Authenticate with API key
@@ -23,36 +23,27 @@ Node.js agent and CLI for [SessionCast](https://sessioncast.io) - a real-time te
23
23
  ## Installation
24
24
 
25
25
  ```bash
26
- npm install -g sessioncast-cli
27
- ```
28
-
29
- ## Quick Start
26
+ # Clone the repository
27
+ git clone git@github.com:your-org/sessioncast-cli.git
28
+ cd sessioncast-cli
30
29
 
31
- 1. **Get your agent token** from [app.sessioncast.io](https://app.sessioncast.io)
30
+ # Install dependencies
31
+ npm install
32
32
 
33
- 2. **Create config file** `~/.sessioncast.yml`:
33
+ # Build
34
+ npm run build
34
35
 
35
- ```yaml
36
- machineId: my-machine
37
- relay: wss://relay.sessioncast.io/ws
38
- token: agt_your_agent_token_here
36
+ # Link globally (optional)
37
+ npm link
39
38
  ```
40
39
 
41
- 3. **Start the agent**:
42
-
43
- ```bash
44
- sessioncast agent
45
- ```
46
-
47
- 4. **View your sessions** at [app.sessioncast.io](https://app.sessioncast.io)
48
-
49
40
  ## Configuration
50
41
 
51
42
  Create `~/.sessioncast.yml` or `~/.tmux-remote.yml`:
52
43
 
53
44
  ```yaml
54
45
  machineId: my-machine
55
- relay: wss://relay.sessioncast.io/ws
46
+ relay: wss://your-relay.sessioncast.io/ws
56
47
  token: agt_your_agent_token_here
57
48
 
58
49
  # Optional: API configuration
@@ -68,6 +59,9 @@ api:
68
59
 
69
60
  llm:
70
61
  enabled: false
62
+ provider: ollama
63
+ model: codellama
64
+ endpoint: http://localhost:11434
71
65
  ```
72
66
 
73
67
  ### Environment Variables
@@ -102,15 +96,56 @@ sessioncast send my-machine/dev "Enter"
102
96
  ```
103
97
  ┌─────────────┐ WebSocket ┌─────────────┐ WebSocket ┌─────────────┐
104
98
  │ Agent │ ◄─────────────────► │ Relay │ ◄────────────────► │ Viewer │
105
- │ (Node.js) │ screen/keys │ (Server) │ screen/keys │ (Web) │
99
+ │ (Node.js) │ screen/keys │ (Spring) │ screen/keys │ (Web) │
106
100
  └─────────────┘ └─────────────┘ └─────────────┘
107
-
108
- │ tmux
109
-
110
- ┌─────────────┐
111
- │ tmux │
112
- │ sessions │
113
- └─────────────┘
101
+
102
+ │ tmux │ DynamoDB
103
+
104
+ ┌─────────────┐ ┌─────────────┐
105
+ │ tmux │ │ Sessions │
106
+ │ sessions │ │ metadata │
107
+ └─────────────┘ └─────────────┘
108
+ ```
109
+
110
+ ## Development
111
+
112
+ ```bash
113
+ # Install dependencies
114
+ npm install
115
+
116
+ # Build TypeScript
117
+ npm run build
118
+
119
+ # Watch mode (if configured)
120
+ npm run dev
121
+ ```
122
+
123
+ ## Project Structure
124
+
125
+ ```
126
+ sessioncast-cli/
127
+ ├── src/
128
+ │ ├── agent/
129
+ │ │ ├── api-client.ts # API WebSocket client
130
+ │ │ ├── exec-service.ts # Command execution service
131
+ │ │ ├── llm-service.ts # LLM integration (Ollama/OpenAI)
132
+ │ │ ├── runner.ts # Agent runner with auto-discovery
133
+ │ │ ├── session-handler.ts # tmux session handler
134
+ │ │ ├── tmux.ts # tmux utilities
135
+ │ │ ├── types.ts # Type definitions
136
+ │ │ └── websocket.ts # WebSocket client with circuit breaker
137
+ │ ├── commands/
138
+ │ │ ├── agent.ts # Agent command
139
+ │ │ ├── agents.ts # List agents command
140
+ │ │ ├── login.ts # Login command
141
+ │ │ ├── sendkeys.ts # Send keys command
142
+ │ │ └── sessions.ts # List sessions command
143
+ │ ├── api.ts # API client
144
+ │ ├── config.ts # Configuration management
145
+ │ └── index.ts # CLI entry point
146
+ ├── package.json
147
+ ├── tsconfig.json
148
+ └── README.md
114
149
  ```
115
150
 
116
151
  ## Circuit Breaker
@@ -122,16 +157,6 @@ The agent implements a circuit breaker pattern to prevent reconnection storms:
122
157
  - **Max delay**: 30 seconds (with exponential backoff + jitter)
123
158
  - **Circuit breaker duration**: 2 minutes cooldown after max attempts
124
159
 
125
- ## Requirements
126
-
127
- - Node.js >= 18
128
- - tmux installed on the host machine
129
-
130
160
  ## License
131
161
 
132
- MIT License - see [LICENSE](LICENSE) for details.
133
-
134
- ## Support
135
-
136
- - Homepage: https://sessioncast.io
137
- - Email: devload@sessioncast.io
162
+ Private - All rights reserved.
@@ -19,6 +19,7 @@ export declare class TmuxSessionHandler {
19
19
  start(): void;
20
20
  private connectAndRun;
21
21
  private handleKeys;
22
+ private handleFileViewRequest;
22
23
  private startScreenCapture;
23
24
  private stopScreenCapture;
24
25
  stop(): void;
@@ -36,6 +36,28 @@ Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.TmuxSessionHandler = void 0;
37
37
  const websocket_1 = require("./websocket");
38
38
  const tmux = __importStar(require("./tmux"));
39
+ const fs = __importStar(require("fs"));
40
+ const path = __importStar(require("path"));
41
+ const os = __importStar(require("os"));
42
+ function expandPath(filePath) {
43
+ if (filePath.startsWith('~/')) {
44
+ return path.join(os.homedir(), filePath.slice(2));
45
+ }
46
+ return filePath;
47
+ }
48
+ function getLanguage(filePath) {
49
+ const ext = path.extname(filePath).toLowerCase();
50
+ const langMap = {
51
+ '.js': 'javascript', '.ts': 'typescript', '.tsx': 'typescript',
52
+ '.jsx': 'javascript', '.json': 'json', '.md': 'markdown',
53
+ '.py': 'python', '.java': 'java', '.sh': 'shell',
54
+ '.css': 'css', '.html': 'html', '.yaml': 'yaml', '.yml': 'yaml',
55
+ '.kt': 'kotlin', '.swift': 'swift', '.go': 'go', '.rs': 'rust',
56
+ '.c': 'c', '.cpp': 'cpp', '.h': 'c', '.hpp': 'cpp',
57
+ '.rb': 'ruby', '.php': 'php', '.sql': 'sql', '.xml': 'xml',
58
+ };
59
+ return langMap[ext] || 'text';
60
+ }
39
61
  // Capture intervals
40
62
  const CAPTURE_INTERVAL_ACTIVE_MS = 50;
41
63
  const CAPTURE_INTERVAL_IDLE_MS = 200;
@@ -102,6 +124,10 @@ class TmuxSessionHandler {
102
124
  tmux.killSession(this.tmuxSession);
103
125
  this.stop();
104
126
  });
127
+ this.wsClient.on('requestFileView', (filePath) => {
128
+ console.log(`[${this.tmuxSession}] File view request: ${filePath}`);
129
+ this.handleFileViewRequest(filePath);
130
+ });
105
131
  this.wsClient.on('error', (error) => {
106
132
  console.error(`[${this.tmuxSession}] WebSocket error:`, error.message);
107
133
  });
@@ -110,6 +136,22 @@ class TmuxSessionHandler {
110
136
  handleKeys(keys) {
111
137
  tmux.sendKeys(this.tmuxSession, keys, false);
112
138
  }
139
+ handleFileViewRequest(filePath) {
140
+ if (!this.wsClient)
141
+ return;
142
+ const realPath = expandPath(filePath);
143
+ const language = getLanguage(filePath);
144
+ try {
145
+ const content = fs.readFileSync(realPath, 'utf-8');
146
+ console.log(`[${this.tmuxSession}] File read: ${content.length} bytes`);
147
+ this.wsClient.sendFileView(filePath, content, language);
148
+ }
149
+ catch (err) {
150
+ const errorMessage = err instanceof Error ? err.message : 'Unknown error';
151
+ console.error(`[${this.tmuxSession}] File read error: ${errorMessage}`);
152
+ this.wsClient.sendFileView(filePath, `Error: ${errorMessage}`, 'text', errorMessage);
153
+ }
154
+ }
113
155
  startScreenCapture() {
114
156
  if (this.captureTimer)
115
157
  return;
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Interface for tmux operations.
3
+ * Implementations handle platform-specific differences (Unix vs Windows/itmux).
4
+ */
5
+ export interface TmuxExecutor {
6
+ listSessions(): string[];
7
+ capturePane(session: string): string | null;
8
+ sendKeys(session: string, keys: string): boolean;
9
+ sendSpecialKey(session: string, key: string): boolean;
10
+ resizeWindow(session: string, cols: number, rows: number): boolean;
11
+ killSession(session: string): boolean;
12
+ createSession(session: string, workingDir?: string): boolean;
13
+ isAvailable(): boolean;
14
+ getVersion(): string | null;
15
+ }
16
+ /**
17
+ * Unix/Linux/macOS implementation of TmuxExecutor.
18
+ */
19
+ export declare class UnixTmuxExecutor implements TmuxExecutor {
20
+ listSessions(): string[];
21
+ capturePane(session: string): string | null;
22
+ sendKeys(session: string, keys: string): boolean;
23
+ sendSpecialKey(session: string, key: string): boolean;
24
+ resizeWindow(session: string, cols: number, rows: number): boolean;
25
+ killSession(session: string): boolean;
26
+ createSession(session: string, workingDir?: string): boolean;
27
+ isAvailable(): boolean;
28
+ getVersion(): string | null;
29
+ private escapeForShell;
30
+ }
31
+ /**
32
+ * Windows implementation of TmuxExecutor using itmux (Cygwin + tmux bundle).
33
+ */
34
+ export declare class WindowsTmuxExecutor implements TmuxExecutor {
35
+ private itmuxPath;
36
+ private bashPath;
37
+ constructor(itmuxPath: string);
38
+ private executeCommand;
39
+ listSessions(): string[];
40
+ capturePane(session: string): string | null;
41
+ sendKeys(session: string, keys: string): boolean;
42
+ sendSpecialKey(session: string, key: string): boolean;
43
+ resizeWindow(session: string, cols: number, rows: number): boolean;
44
+ killSession(session: string): boolean;
45
+ createSession(session: string, workingDir?: string): boolean;
46
+ isAvailable(): boolean;
47
+ getVersion(): string | null;
48
+ private escapeSession;
49
+ private windowsToCygwinPath;
50
+ /**
51
+ * Find itmux installation path on Windows.
52
+ */
53
+ static findItmuxPath(): string | null;
54
+ }
55
+ /**
56
+ * Get the appropriate TmuxExecutor for the current platform.
57
+ */
58
+ export declare function createTmuxExecutor(): TmuxExecutor;
59
+ /**
60
+ * Check if the current platform is Windows.
61
+ */
62
+ export declare function isWindows(): boolean;
63
+ /**
64
+ * Get platform name for logging.
65
+ */
66
+ export declare function getPlatformName(): string;
@@ -0,0 +1,368 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.WindowsTmuxExecutor = exports.UnixTmuxExecutor = void 0;
37
+ exports.createTmuxExecutor = createTmuxExecutor;
38
+ exports.isWindows = isWindows;
39
+ exports.getPlatformName = getPlatformName;
40
+ const child_process_1 = require("child_process");
41
+ const os = __importStar(require("os"));
42
+ const path = __importStar(require("path"));
43
+ const fs = __importStar(require("fs"));
44
+ /**
45
+ * Unix/Linux/macOS implementation of TmuxExecutor.
46
+ */
47
+ class UnixTmuxExecutor {
48
+ listSessions() {
49
+ try {
50
+ const output = (0, child_process_1.execSync)('tmux ls -F "#{session_name}"', {
51
+ encoding: 'utf-8',
52
+ stdio: ['pipe', 'pipe', 'pipe']
53
+ });
54
+ return output
55
+ .trim()
56
+ .split('\n')
57
+ .filter(s => s.length > 0);
58
+ }
59
+ catch {
60
+ return [];
61
+ }
62
+ }
63
+ capturePane(session) {
64
+ try {
65
+ const output = (0, child_process_1.execSync)(`tmux capture-pane -t "${session}" -p -e -N`, {
66
+ encoding: 'utf-8',
67
+ stdio: ['pipe', 'pipe', 'pipe'],
68
+ maxBuffer: 10 * 1024 * 1024
69
+ });
70
+ return output.replace(/\n/g, '\r\n');
71
+ }
72
+ catch {
73
+ return null;
74
+ }
75
+ }
76
+ sendKeys(session, keys) {
77
+ try {
78
+ const escaped = this.escapeForShell(keys);
79
+ (0, child_process_1.execSync)(`tmux send-keys -t "${session}" -l "${escaped}"`, { stdio: 'pipe' });
80
+ return true;
81
+ }
82
+ catch {
83
+ return false;
84
+ }
85
+ }
86
+ sendSpecialKey(session, key) {
87
+ try {
88
+ (0, child_process_1.execSync)(`tmux send-keys -t "${session}" ${key}`, { stdio: 'pipe' });
89
+ return true;
90
+ }
91
+ catch {
92
+ return false;
93
+ }
94
+ }
95
+ resizeWindow(session, cols, rows) {
96
+ try {
97
+ (0, child_process_1.execSync)(`tmux resize-window -t "${session}" -x ${cols} -y ${rows}`, { stdio: 'pipe' });
98
+ return true;
99
+ }
100
+ catch {
101
+ return false;
102
+ }
103
+ }
104
+ killSession(session) {
105
+ try {
106
+ (0, child_process_1.execSync)(`tmux kill-session -t "${session}"`, { stdio: 'pipe' });
107
+ return true;
108
+ }
109
+ catch {
110
+ return false;
111
+ }
112
+ }
113
+ createSession(session, workingDir) {
114
+ try {
115
+ const sanitized = session.replace(/[^a-zA-Z0-9_-]/g, '_');
116
+ if (!sanitized)
117
+ return false;
118
+ let cmd = `tmux new-session -d -s "${sanitized}"`;
119
+ if (workingDir) {
120
+ cmd += ` -c "${workingDir}"`;
121
+ }
122
+ (0, child_process_1.execSync)(cmd, { stdio: 'pipe' });
123
+ return true;
124
+ }
125
+ catch {
126
+ return false;
127
+ }
128
+ }
129
+ isAvailable() {
130
+ try {
131
+ (0, child_process_1.execSync)('which tmux', { stdio: 'pipe' });
132
+ return true;
133
+ }
134
+ catch {
135
+ return false;
136
+ }
137
+ }
138
+ getVersion() {
139
+ try {
140
+ return (0, child_process_1.execSync)('tmux -V', { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
141
+ }
142
+ catch {
143
+ return null;
144
+ }
145
+ }
146
+ escapeForShell(str) {
147
+ return str.replace(/"/g, '\\"').replace(/\$/g, '\\$').replace(/`/g, '\\`');
148
+ }
149
+ }
150
+ exports.UnixTmuxExecutor = UnixTmuxExecutor;
151
+ /**
152
+ * Windows implementation of TmuxExecutor using itmux (Cygwin + tmux bundle).
153
+ */
154
+ class WindowsTmuxExecutor {
155
+ constructor(itmuxPath) {
156
+ this.itmuxPath = itmuxPath;
157
+ this.bashPath = path.join(itmuxPath, 'bin', 'bash.exe');
158
+ if (!fs.existsSync(this.bashPath)) {
159
+ throw new Error(`itmux bash not found at: ${this.bashPath}`);
160
+ }
161
+ console.log(`[Windows] Using itmux at: ${itmuxPath}`);
162
+ }
163
+ executeCommand(command) {
164
+ try {
165
+ const result = (0, child_process_1.spawnSync)(this.bashPath, ['-l', '-c', command], {
166
+ encoding: 'utf-8',
167
+ cwd: this.itmuxPath,
168
+ env: {
169
+ ...process.env,
170
+ CYGWIN: 'nodosfilewarning',
171
+ HOME: `/home/${os.userInfo().username}`,
172
+ TERM: 'xterm-256color'
173
+ },
174
+ maxBuffer: 10 * 1024 * 1024
175
+ });
176
+ if (result.stderr) {
177
+ // Log but don't fail on stderr (tmux often outputs to stderr)
178
+ console.debug(`[itmux stderr] ${result.stderr}`);
179
+ }
180
+ return result.stdout?.trim() || '';
181
+ }
182
+ catch (error) {
183
+ console.error(`[itmux] Command failed: ${command}`, error);
184
+ return '';
185
+ }
186
+ }
187
+ listSessions() {
188
+ try {
189
+ const output = this.executeCommand("tmux ls -F '#{session_name}' 2>/dev/null || true");
190
+ if (!output)
191
+ return [];
192
+ return output
193
+ .split('\n')
194
+ .map(s => s.trim())
195
+ .filter(s => s.length > 0 && !s.startsWith('no server') && !s.includes('error'));
196
+ }
197
+ catch {
198
+ return [];
199
+ }
200
+ }
201
+ capturePane(session) {
202
+ try {
203
+ const escaped = this.escapeSession(session);
204
+ const output = this.executeCommand(`tmux capture-pane -t '${escaped}' -p -e -N`);
205
+ if (!output)
206
+ return null;
207
+ return output.replace(/\n/g, '\r\n');
208
+ }
209
+ catch {
210
+ return null;
211
+ }
212
+ }
213
+ sendKeys(session, keys) {
214
+ try {
215
+ const escapedSession = this.escapeSession(session);
216
+ const escapedKeys = keys.replace(/'/g, "'\\''");
217
+ this.executeCommand(`tmux send-keys -t '${escapedSession}' -l '${escapedKeys}'`);
218
+ return true;
219
+ }
220
+ catch {
221
+ return false;
222
+ }
223
+ }
224
+ sendSpecialKey(session, key) {
225
+ try {
226
+ const escaped = this.escapeSession(session);
227
+ this.executeCommand(`tmux send-keys -t '${escaped}' ${key}`);
228
+ return true;
229
+ }
230
+ catch {
231
+ return false;
232
+ }
233
+ }
234
+ resizeWindow(session, cols, rows) {
235
+ try {
236
+ const escaped = this.escapeSession(session);
237
+ this.executeCommand(`tmux resize-window -t '${escaped}' -x ${cols} -y ${rows}`);
238
+ return true;
239
+ }
240
+ catch {
241
+ return false;
242
+ }
243
+ }
244
+ killSession(session) {
245
+ try {
246
+ const escaped = this.escapeSession(session);
247
+ this.executeCommand(`tmux kill-session -t '${escaped}'`);
248
+ return true;
249
+ }
250
+ catch {
251
+ return false;
252
+ }
253
+ }
254
+ createSession(session, workingDir) {
255
+ try {
256
+ const sanitized = session.replace(/[^a-zA-Z0-9_-]/g, '_');
257
+ if (!sanitized)
258
+ return false;
259
+ let cmd;
260
+ if (workingDir) {
261
+ const cygwinPath = this.windowsToCygwinPath(workingDir);
262
+ cmd = `tmux new-session -d -s '${sanitized}' -c '${cygwinPath}'`;
263
+ }
264
+ else {
265
+ cmd = `tmux new-session -d -s '${sanitized}'`;
266
+ }
267
+ this.executeCommand(cmd);
268
+ return true;
269
+ }
270
+ catch {
271
+ return false;
272
+ }
273
+ }
274
+ isAvailable() {
275
+ try {
276
+ const version = this.getVersion();
277
+ return version !== null && version.includes('tmux');
278
+ }
279
+ catch {
280
+ return false;
281
+ }
282
+ }
283
+ getVersion() {
284
+ try {
285
+ const output = this.executeCommand('tmux -V');
286
+ return output || null;
287
+ }
288
+ catch {
289
+ return null;
290
+ }
291
+ }
292
+ escapeSession(session) {
293
+ return session.replace(/'/g, "'\\''");
294
+ }
295
+ windowsToCygwinPath(windowsPath) {
296
+ if (!windowsPath)
297
+ return windowsPath;
298
+ // Handle UNC paths
299
+ if (windowsPath.startsWith('\\\\')) {
300
+ return windowsPath;
301
+ }
302
+ // Handle drive letters (C:\...)
303
+ if (windowsPath.length >= 2 && windowsPath[1] === ':') {
304
+ const drive = windowsPath[0].toLowerCase();
305
+ const rest = windowsPath.substring(2).replace(/\\/g, '/');
306
+ return `/cygdrive/${drive}${rest}`;
307
+ }
308
+ return windowsPath.replace(/\\/g, '/');
309
+ }
310
+ /**
311
+ * Find itmux installation path on Windows.
312
+ */
313
+ static findItmuxPath() {
314
+ // 1. Check environment variable
315
+ const envPath = process.env.ITMUX_HOME;
316
+ if (envPath && fs.existsSync(path.join(envPath, 'bin', 'bash.exe'))) {
317
+ return envPath;
318
+ }
319
+ // 2. Check common locations
320
+ const locations = [
321
+ path.join(os.homedir(), 'itmux'),
322
+ 'C:\\itmux',
323
+ 'D:\\itmux',
324
+ path.join(process.cwd(), 'itmux'),
325
+ path.join(process.env.LOCALAPPDATA || '', 'itmux'),
326
+ path.join(process.env.ProgramFiles || '', 'itmux'),
327
+ ];
328
+ for (const loc of locations) {
329
+ if (loc && fs.existsSync(path.join(loc, 'bin', 'bash.exe'))) {
330
+ return loc;
331
+ }
332
+ }
333
+ return null;
334
+ }
335
+ }
336
+ exports.WindowsTmuxExecutor = WindowsTmuxExecutor;
337
+ /**
338
+ * Get the appropriate TmuxExecutor for the current platform.
339
+ */
340
+ function createTmuxExecutor() {
341
+ const isWindows = os.platform() === 'win32';
342
+ if (isWindows) {
343
+ const itmuxPath = WindowsTmuxExecutor.findItmuxPath();
344
+ if (!itmuxPath) {
345
+ throw new Error('itmux not found. Please install itmux from https://github.com/itefixnet/itmux\n' +
346
+ 'Set ITMUX_HOME environment variable or place itmux in a standard location.');
347
+ }
348
+ return new WindowsTmuxExecutor(itmuxPath);
349
+ }
350
+ return new UnixTmuxExecutor();
351
+ }
352
+ /**
353
+ * Check if the current platform is Windows.
354
+ */
355
+ function isWindows() {
356
+ return os.platform() === 'win32';
357
+ }
358
+ /**
359
+ * Get platform name for logging.
360
+ */
361
+ function getPlatformName() {
362
+ const platform = os.platform();
363
+ if (platform === 'win32')
364
+ return 'Windows';
365
+ if (platform === 'darwin')
366
+ return 'macOS';
367
+ return 'Linux/Unix';
368
+ }
@@ -22,8 +22,16 @@ export declare function resizeWindow(sessionName: string, cols: number, rows: nu
22
22
  /**
23
23
  * Create new tmux session
24
24
  */
25
- export declare function createSession(sessionName: string): boolean;
25
+ export declare function createSession(sessionName: string, workingDir?: string): boolean;
26
26
  /**
27
27
  * Kill tmux session
28
28
  */
29
29
  export declare function killSession(sessionName: string): boolean;
30
+ /**
31
+ * Check if tmux is available
32
+ */
33
+ export declare function isAvailable(): boolean;
34
+ /**
35
+ * Get tmux version
36
+ */
37
+ export declare function getVersion(): string | null;