@burdenoff/vibe-plugin-session-tmux 2.0.0
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 +53 -0
- package/dist/index.d.ts +123 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +913 -0
- package/dist/index.js.map +1 -0
- package/package.json +72 -0
package/README.md
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# @burdenoff/vibe-plugin-session-tmux
|
|
2
|
+
|
|
3
|
+
Tmux + ttyd session provider plugin for [VibeControls Agent](https://www.npmjs.com/package/@burdenoff/vibe-agent).
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
vibe plugin install @burdenoff/vibe-plugin-session-tmux
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Or install globally alongside the agent:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install -g @burdenoff/vibe-plugin-session-tmux
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Features
|
|
18
|
+
|
|
19
|
+
- **Tmux Sessions** -- Create, manage, and destroy tmux terminal sessions
|
|
20
|
+
- **ttyd Integration** -- Web terminal access via ttyd (auto-started per session)
|
|
21
|
+
- **Session Lifecycle** -- Full lifecycle management with health checks
|
|
22
|
+
- **Command Execution** -- Run commands in existing tmux sessions
|
|
23
|
+
- **Terminal Capture** -- Capture terminal output from running sessions
|
|
24
|
+
- **Port Management** -- Automatic free port allocation for ttyd instances
|
|
25
|
+
|
|
26
|
+
## Provider Interface
|
|
27
|
+
|
|
28
|
+
This plugin registers a `session` provider with the following capabilities:
|
|
29
|
+
|
|
30
|
+
| Method | Description |
|
|
31
|
+
| --- | --- |
|
|
32
|
+
| `create(config)` | Create a new tmux session with optional ttyd terminal |
|
|
33
|
+
| `get(id)` | Get session info by ID |
|
|
34
|
+
| `list()` | List all managed sessions |
|
|
35
|
+
| `terminate(id)` | Kill a tmux session and its ttyd process |
|
|
36
|
+
| `execute(id, command)` | Send a command to a tmux session |
|
|
37
|
+
| `capture(id)` | Capture current terminal output |
|
|
38
|
+
| `startTerminal(id)` | Start a ttyd web terminal for a session |
|
|
39
|
+
| `stopTerminal(id)` | Stop the ttyd process for a session |
|
|
40
|
+
| `healthCheck()` | Check tmux and ttyd health |
|
|
41
|
+
| `getSystemSessions()` | List all system tmux sessions |
|
|
42
|
+
| `getSystemTerminals()` | List all running ttyd processes |
|
|
43
|
+
|
|
44
|
+
## Requirements
|
|
45
|
+
|
|
46
|
+
- VibeControls Agent >= 2.0.0
|
|
47
|
+
- tmux installed on the host system
|
|
48
|
+
- ttyd installed on the host system (optional, for web terminals)
|
|
49
|
+
- Bun runtime >= 1.3.0
|
|
50
|
+
|
|
51
|
+
## License
|
|
52
|
+
|
|
53
|
+
Proprietary -- Copyright Burdenoff Consultancy Services Pvt. Ltd.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @burdenoff/vibe-plugin-session-tmux
|
|
3
|
+
*
|
|
4
|
+
* Tmux + ttyd session provider plugin for VibeControls Agent.
|
|
5
|
+
* Implements the full SessionProvider interface (23 methods) using tmux for
|
|
6
|
+
* terminal session management and ttyd for browser-accessible web terminals.
|
|
7
|
+
*/
|
|
8
|
+
type SessionStatus = "active" | "inactive" | "terminated" | "error";
|
|
9
|
+
interface SessionConfig {
|
|
10
|
+
name: string;
|
|
11
|
+
command?: string;
|
|
12
|
+
workingDirectory?: string;
|
|
13
|
+
environment?: Record<string, string>;
|
|
14
|
+
shell?: string;
|
|
15
|
+
size?: {
|
|
16
|
+
cols: number;
|
|
17
|
+
rows: number;
|
|
18
|
+
};
|
|
19
|
+
projectId?: string;
|
|
20
|
+
}
|
|
21
|
+
interface SessionInfo {
|
|
22
|
+
id: string;
|
|
23
|
+
name: string;
|
|
24
|
+
status: SessionStatus;
|
|
25
|
+
provider: string;
|
|
26
|
+
command?: string;
|
|
27
|
+
workingDirectory?: string;
|
|
28
|
+
pid?: number;
|
|
29
|
+
projectId?: string;
|
|
30
|
+
createdAt: string;
|
|
31
|
+
updatedAt?: string;
|
|
32
|
+
terminal?: TerminalInfo;
|
|
33
|
+
metadata?: Record<string, unknown>;
|
|
34
|
+
}
|
|
35
|
+
interface TerminalInfo {
|
|
36
|
+
url: string;
|
|
37
|
+
port: number;
|
|
38
|
+
pid: number;
|
|
39
|
+
}
|
|
40
|
+
interface HealthCheckResult {
|
|
41
|
+
ok: boolean;
|
|
42
|
+
sessions: number;
|
|
43
|
+
terminals: number;
|
|
44
|
+
message?: string;
|
|
45
|
+
}
|
|
46
|
+
interface SystemSessionInfo {
|
|
47
|
+
id: string;
|
|
48
|
+
name: string;
|
|
49
|
+
windows: number;
|
|
50
|
+
attached: boolean;
|
|
51
|
+
createdAt?: string;
|
|
52
|
+
}
|
|
53
|
+
interface SystemTerminalInfo {
|
|
54
|
+
pid: number;
|
|
55
|
+
port: number;
|
|
56
|
+
sessionId?: string;
|
|
57
|
+
}
|
|
58
|
+
interface SessionProvider {
|
|
59
|
+
readonly name: string;
|
|
60
|
+
create(config: SessionConfig): Promise<SessionInfo>;
|
|
61
|
+
terminate(sessionId: string): Promise<void>;
|
|
62
|
+
getInfo(sessionId: string): Promise<SessionInfo | null>;
|
|
63
|
+
list(): Promise<SessionInfo[]>;
|
|
64
|
+
sendCommand(sessionId: string, command: string): Promise<void>;
|
|
65
|
+
sendKeys(sessionId: string, keys: string): Promise<void>;
|
|
66
|
+
sendInterrupt(sessionId: string): Promise<void>;
|
|
67
|
+
captureOutput(sessionId: string): Promise<string>;
|
|
68
|
+
rename(sessionId: string, newName: string): Promise<void>;
|
|
69
|
+
toggleMouse(sessionId: string): Promise<boolean>;
|
|
70
|
+
getTerminationStatus(sessionId: string): Promise<{
|
|
71
|
+
terminated: boolean;
|
|
72
|
+
exists: boolean;
|
|
73
|
+
}>;
|
|
74
|
+
getTerminalInfo(sessionId: string): Promise<TerminalInfo | null>;
|
|
75
|
+
startTerminal(sessionId: string, port?: number): Promise<TerminalInfo>;
|
|
76
|
+
stopTerminal(sessionId: string): Promise<void>;
|
|
77
|
+
listSystemSessions(): Promise<SystemSessionInfo[]>;
|
|
78
|
+
listSystemTerminals(): Promise<SystemTerminalInfo[]>;
|
|
79
|
+
bulkKillSystemSessions(sessionIds: string[]): Promise<{
|
|
80
|
+
killed: number;
|
|
81
|
+
failed: number;
|
|
82
|
+
}>;
|
|
83
|
+
bulkKillSystemTerminals(pids: number[]): Promise<{
|
|
84
|
+
killed: number;
|
|
85
|
+
failed: number;
|
|
86
|
+
}>;
|
|
87
|
+
healthCheck(): Promise<HealthCheckResult>;
|
|
88
|
+
getSessionsByProject(projectId: string): Promise<SessionInfo[]>;
|
|
89
|
+
cleanup(): Promise<{
|
|
90
|
+
cleaned: number;
|
|
91
|
+
}>;
|
|
92
|
+
}
|
|
93
|
+
interface HostLogger {
|
|
94
|
+
info(message: string, meta?: Record<string, unknown>): void;
|
|
95
|
+
warn(message: string, meta?: Record<string, unknown>): void;
|
|
96
|
+
error(message: string, meta?: Record<string, unknown>): void;
|
|
97
|
+
debug(message: string, meta?: Record<string, unknown>): void;
|
|
98
|
+
}
|
|
99
|
+
interface HostStorage {
|
|
100
|
+
get(key: string): Promise<string | null>;
|
|
101
|
+
set(key: string, value: string): Promise<void>;
|
|
102
|
+
delete(key: string): Promise<void>;
|
|
103
|
+
}
|
|
104
|
+
interface HostServices {
|
|
105
|
+
logger: HostLogger;
|
|
106
|
+
storage: HostStorage;
|
|
107
|
+
}
|
|
108
|
+
interface VibePlugin {
|
|
109
|
+
name: string;
|
|
110
|
+
version: string;
|
|
111
|
+
description: string;
|
|
112
|
+
tags?: Array<"backend" | "frontend" | "cli" | "provider" | "adapter" | "integration">;
|
|
113
|
+
providers: {
|
|
114
|
+
session?: SessionProvider;
|
|
115
|
+
};
|
|
116
|
+
onServerStart?(services: HostServices): Promise<void>;
|
|
117
|
+
onServerStop?(): Promise<void>;
|
|
118
|
+
onCliSetup?(): void;
|
|
119
|
+
}
|
|
120
|
+
declare const vibePlugin: VibePlugin;
|
|
121
|
+
export { vibePlugin };
|
|
122
|
+
export type { SessionProvider, SessionConfig, SessionInfo, SessionStatus, TerminalInfo, HealthCheckResult, SystemSessionInfo, SystemTerminalInfo, VibePlugin, HostServices, };
|
|
123
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,913 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @burdenoff/vibe-plugin-session-tmux
|
|
3
|
+
*
|
|
4
|
+
* Tmux + ttyd session provider plugin for VibeControls Agent.
|
|
5
|
+
* Implements the full SessionProvider interface (23 methods) using tmux for
|
|
6
|
+
* terminal session management and ttyd for browser-accessible web terminals.
|
|
7
|
+
*/
|
|
8
|
+
import { execSync } from "node:child_process";
|
|
9
|
+
import { createServer } from "node:net";
|
|
10
|
+
import { randomBytes } from "node:crypto";
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
// Constants
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
const PROVIDER_NAME = "session-tmux";
|
|
15
|
+
const STORAGE_NAMESPACE = "session-tmux";
|
|
16
|
+
const STORAGE_KEY_SESSIONS = "sessions";
|
|
17
|
+
const TTYD_BASE_PORT = 7681;
|
|
18
|
+
const TTYD_PORT_RANGE = 200;
|
|
19
|
+
const GRACEFUL_KILL_TIMEOUT_MS = 3000;
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
// Helpers
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
/**
|
|
24
|
+
* Generate a short random hex ID (8 chars) for session identifiers.
|
|
25
|
+
*/
|
|
26
|
+
function generateId() {
|
|
27
|
+
return randomBytes(4).toString("hex");
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Execute a tmux command and return its stdout. Throws on non-zero exit.
|
|
31
|
+
*/
|
|
32
|
+
function tmuxExec(args) {
|
|
33
|
+
const result = execSync(`tmux ${args.map(shellEscape).join(" ")}`, {
|
|
34
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
35
|
+
timeout: 10_000,
|
|
36
|
+
});
|
|
37
|
+
return result.toString("utf-8").trimEnd();
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Execute a tmux command, returning true on success, false on failure.
|
|
41
|
+
* Does not throw.
|
|
42
|
+
*/
|
|
43
|
+
function tmuxExecSilent(args) {
|
|
44
|
+
try {
|
|
45
|
+
execSync(`tmux ${args.map(shellEscape).join(" ")}`, {
|
|
46
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
47
|
+
timeout: 10_000,
|
|
48
|
+
});
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Escape a single shell argument.
|
|
57
|
+
*/
|
|
58
|
+
function shellEscape(arg) {
|
|
59
|
+
// If the arg is already safe, return as-is
|
|
60
|
+
if (/^[a-zA-Z0-9_./:@=+-]+$/.test(arg)) {
|
|
61
|
+
return arg;
|
|
62
|
+
}
|
|
63
|
+
// Wrap in single quotes, escaping existing single quotes
|
|
64
|
+
return `'${arg.replace(/'/g, "'\\''")}'`;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Find an available TCP port starting from `start` within the configured range.
|
|
68
|
+
*/
|
|
69
|
+
async function findAvailablePort(start) {
|
|
70
|
+
for (let port = start; port < start + TTYD_PORT_RANGE; port++) {
|
|
71
|
+
const available = await isPortAvailable(port);
|
|
72
|
+
if (available) {
|
|
73
|
+
return port;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
throw new Error(`No available port found in range ${start}-${start + TTYD_PORT_RANGE - 1}`);
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Check whether a TCP port is available by attempting to bind to it.
|
|
80
|
+
*/
|
|
81
|
+
function isPortAvailable(port) {
|
|
82
|
+
return new Promise((resolve) => {
|
|
83
|
+
const server = createServer();
|
|
84
|
+
server.once("error", () => {
|
|
85
|
+
resolve(false);
|
|
86
|
+
});
|
|
87
|
+
server.once("listening", () => {
|
|
88
|
+
server.close(() => resolve(true));
|
|
89
|
+
});
|
|
90
|
+
server.listen(port, "127.0.0.1");
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Send SIGTERM to a process, then SIGKILL after timeout if still alive.
|
|
95
|
+
*/
|
|
96
|
+
async function gracefulKill(pid, timeout = GRACEFUL_KILL_TIMEOUT_MS) {
|
|
97
|
+
try {
|
|
98
|
+
process.kill(pid, "SIGTERM");
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
// Process already dead — nothing to do
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
const deadline = Date.now() + timeout;
|
|
105
|
+
while (Date.now() < deadline) {
|
|
106
|
+
await sleep(200);
|
|
107
|
+
if (!isProcessAlive(pid)) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
// Force kill
|
|
112
|
+
try {
|
|
113
|
+
process.kill(pid, "SIGKILL");
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
// Already gone
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Check if a process is still alive.
|
|
121
|
+
*/
|
|
122
|
+
function isProcessAlive(pid) {
|
|
123
|
+
try {
|
|
124
|
+
process.kill(pid, 0);
|
|
125
|
+
return true;
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Simple async sleep.
|
|
133
|
+
*/
|
|
134
|
+
function sleep(ms) {
|
|
135
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Get the current ISO timestamp.
|
|
139
|
+
*/
|
|
140
|
+
function nowISO() {
|
|
141
|
+
return new Date().toISOString();
|
|
142
|
+
}
|
|
143
|
+
// ---------------------------------------------------------------------------
|
|
144
|
+
// TmuxSessionProvider
|
|
145
|
+
// ---------------------------------------------------------------------------
|
|
146
|
+
class TmuxSessionProvider {
|
|
147
|
+
name = PROVIDER_NAME;
|
|
148
|
+
services;
|
|
149
|
+
log;
|
|
150
|
+
storage;
|
|
151
|
+
/** In-memory map of ttyd child processes keyed by session ID. */
|
|
152
|
+
ttydProcesses = new Map();
|
|
153
|
+
/** In-memory map of ttyd port assignments keyed by session ID. */
|
|
154
|
+
ttydPorts = new Map();
|
|
155
|
+
// -----------------------------------------------------------------------
|
|
156
|
+
// Lifecycle
|
|
157
|
+
// -----------------------------------------------------------------------
|
|
158
|
+
/**
|
|
159
|
+
* Initialise the provider with host services. Called from onServerStart.
|
|
160
|
+
*/
|
|
161
|
+
async init(services) {
|
|
162
|
+
this.services = services;
|
|
163
|
+
this.log = services.logger;
|
|
164
|
+
this.storage = services.storage;
|
|
165
|
+
this.log.info("TmuxSessionProvider initialising", { provider: this.name });
|
|
166
|
+
// Verify tmux is available
|
|
167
|
+
try {
|
|
168
|
+
const version = tmuxExec(["-V"]);
|
|
169
|
+
this.log.info("tmux detected", { version });
|
|
170
|
+
}
|
|
171
|
+
catch {
|
|
172
|
+
this.log.error("tmux is not installed or not in PATH — session provider will not function");
|
|
173
|
+
}
|
|
174
|
+
// Reconcile persisted sessions against actual tmux state
|
|
175
|
+
await this.reconcileSessions();
|
|
176
|
+
this.log.info("TmuxSessionProvider ready");
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Graceful shutdown: stop all ttyd terminals.
|
|
180
|
+
*/
|
|
181
|
+
async shutdown() {
|
|
182
|
+
this.log.info("TmuxSessionProvider shutting down — stopping terminals");
|
|
183
|
+
const stopPromises = [];
|
|
184
|
+
for (const [sessionId] of this.ttydProcesses) {
|
|
185
|
+
stopPromises.push(this.stopTerminal(sessionId));
|
|
186
|
+
}
|
|
187
|
+
await Promise.allSettled(stopPromises);
|
|
188
|
+
this.log.info("TmuxSessionProvider shutdown complete");
|
|
189
|
+
}
|
|
190
|
+
// -----------------------------------------------------------------------
|
|
191
|
+
// SessionProvider — create / terminate / getInfo / list
|
|
192
|
+
// -----------------------------------------------------------------------
|
|
193
|
+
async create(config) {
|
|
194
|
+
const id = generateId();
|
|
195
|
+
const sessionName = `vibe-${id}`;
|
|
196
|
+
const now = nowISO();
|
|
197
|
+
this.log.info("Creating tmux session", {
|
|
198
|
+
id,
|
|
199
|
+
name: config.name,
|
|
200
|
+
sessionName,
|
|
201
|
+
});
|
|
202
|
+
// Build tmux new-session command
|
|
203
|
+
const args = ["new-session", "-d", "-s", sessionName];
|
|
204
|
+
if (config.workingDirectory) {
|
|
205
|
+
args.push("-c", config.workingDirectory);
|
|
206
|
+
}
|
|
207
|
+
if (config.size) {
|
|
208
|
+
args.push("-x", String(config.size.cols), "-y", String(config.size.rows));
|
|
209
|
+
}
|
|
210
|
+
// If a shell is specified, use it as the window command
|
|
211
|
+
if (config.shell) {
|
|
212
|
+
args.push(config.shell);
|
|
213
|
+
}
|
|
214
|
+
try {
|
|
215
|
+
tmuxExec(args);
|
|
216
|
+
}
|
|
217
|
+
catch (err) {
|
|
218
|
+
this.log.error("Failed to create tmux session", {
|
|
219
|
+
id,
|
|
220
|
+
error: String(err),
|
|
221
|
+
});
|
|
222
|
+
throw new Error(`Failed to create tmux session: ${err}`);
|
|
223
|
+
}
|
|
224
|
+
// Apply environment variables
|
|
225
|
+
if (config.environment) {
|
|
226
|
+
for (const [key, value] of Object.entries(config.environment)) {
|
|
227
|
+
tmuxExecSilent([
|
|
228
|
+
"set-environment",
|
|
229
|
+
"-t",
|
|
230
|
+
sessionName,
|
|
231
|
+
key,
|
|
232
|
+
value,
|
|
233
|
+
]);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
// Enable mouse by default
|
|
237
|
+
tmuxExecSilent(["set", "-t", sessionName, "mouse", "on"]);
|
|
238
|
+
// If an initial command is given, send it
|
|
239
|
+
if (config.command) {
|
|
240
|
+
tmuxExecSilent(["send-keys", "-t", sessionName, config.command, "Enter"]);
|
|
241
|
+
}
|
|
242
|
+
// Get the PID of the session's first pane
|
|
243
|
+
const pid = this.getSessionPanePid(sessionName);
|
|
244
|
+
const info = {
|
|
245
|
+
id,
|
|
246
|
+
name: config.name,
|
|
247
|
+
status: "active",
|
|
248
|
+
provider: this.name,
|
|
249
|
+
command: config.command,
|
|
250
|
+
workingDirectory: config.workingDirectory,
|
|
251
|
+
pid: pid ?? undefined,
|
|
252
|
+
projectId: config.projectId,
|
|
253
|
+
createdAt: now,
|
|
254
|
+
updatedAt: now,
|
|
255
|
+
metadata: {
|
|
256
|
+
tmuxSessionName: sessionName,
|
|
257
|
+
shell: config.shell,
|
|
258
|
+
size: config.size,
|
|
259
|
+
},
|
|
260
|
+
};
|
|
261
|
+
await this.saveSession(info);
|
|
262
|
+
this.log.info("Tmux session created", { id, sessionName, pid });
|
|
263
|
+
return info;
|
|
264
|
+
}
|
|
265
|
+
async terminate(sessionId) {
|
|
266
|
+
const session = await this.getInfo(sessionId);
|
|
267
|
+
if (!session) {
|
|
268
|
+
this.log.warn("Terminate called for unknown session", { sessionId });
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
const tmuxName = this.getTmuxName(session);
|
|
272
|
+
this.log.info("Terminating tmux session", {
|
|
273
|
+
sessionId,
|
|
274
|
+
tmuxName,
|
|
275
|
+
});
|
|
276
|
+
// Stop terminal first if running
|
|
277
|
+
if (this.ttydProcesses.has(sessionId)) {
|
|
278
|
+
await this.stopTerminal(sessionId);
|
|
279
|
+
}
|
|
280
|
+
// Kill the tmux session
|
|
281
|
+
tmuxExecSilent(["kill-session", "-t", tmuxName]);
|
|
282
|
+
// Update stored state
|
|
283
|
+
session.status = "terminated";
|
|
284
|
+
session.updatedAt = nowISO();
|
|
285
|
+
session.terminal = undefined;
|
|
286
|
+
await this.saveSession(session);
|
|
287
|
+
this.log.info("Tmux session terminated", { sessionId });
|
|
288
|
+
}
|
|
289
|
+
async getInfo(sessionId) {
|
|
290
|
+
const sessions = await this.loadSessions();
|
|
291
|
+
const session = sessions.find((s) => s.id === sessionId) ?? null;
|
|
292
|
+
if (session) {
|
|
293
|
+
// Refresh live status from tmux
|
|
294
|
+
const tmuxName = this.getTmuxName(session);
|
|
295
|
+
const exists = tmuxExecSilent(["has-session", "-t", tmuxName]);
|
|
296
|
+
if (!exists && session.status === "active") {
|
|
297
|
+
session.status = "inactive";
|
|
298
|
+
session.updatedAt = nowISO();
|
|
299
|
+
await this.saveSession(session);
|
|
300
|
+
}
|
|
301
|
+
else if (exists && session.status === "inactive") {
|
|
302
|
+
session.status = "active";
|
|
303
|
+
session.updatedAt = nowISO();
|
|
304
|
+
await this.saveSession(session);
|
|
305
|
+
}
|
|
306
|
+
// Attach terminal info if running
|
|
307
|
+
const termInfo = this.getRunningTerminalInfo(sessionId);
|
|
308
|
+
if (termInfo) {
|
|
309
|
+
session.terminal = termInfo;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
return session;
|
|
313
|
+
}
|
|
314
|
+
async list() {
|
|
315
|
+
const sessions = await this.loadSessions();
|
|
316
|
+
// Refresh statuses
|
|
317
|
+
for (const session of sessions) {
|
|
318
|
+
if (session.status === "terminated")
|
|
319
|
+
continue;
|
|
320
|
+
const tmuxName = this.getTmuxName(session);
|
|
321
|
+
const exists = tmuxExecSilent(["has-session", "-t", tmuxName]);
|
|
322
|
+
if (!exists && session.status === "active") {
|
|
323
|
+
session.status = "inactive";
|
|
324
|
+
session.updatedAt = nowISO();
|
|
325
|
+
}
|
|
326
|
+
else if (exists && session.status !== "active") {
|
|
327
|
+
session.status = "active";
|
|
328
|
+
session.updatedAt = nowISO();
|
|
329
|
+
}
|
|
330
|
+
// Attach terminal info
|
|
331
|
+
const termInfo = this.getRunningTerminalInfo(session.id);
|
|
332
|
+
if (termInfo) {
|
|
333
|
+
session.terminal = termInfo;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
await this.saveSessions(sessions);
|
|
337
|
+
return sessions;
|
|
338
|
+
}
|
|
339
|
+
// -----------------------------------------------------------------------
|
|
340
|
+
// SessionProvider — command / keys / interrupt / capture
|
|
341
|
+
// -----------------------------------------------------------------------
|
|
342
|
+
async sendCommand(sessionId, command) {
|
|
343
|
+
const session = await this.requireSession(sessionId);
|
|
344
|
+
const tmuxName = this.getTmuxName(session);
|
|
345
|
+
this.log.debug("Sending command", { sessionId, command });
|
|
346
|
+
try {
|
|
347
|
+
tmuxExec(["send-keys", "-t", tmuxName, command, "Enter"]);
|
|
348
|
+
}
|
|
349
|
+
catch (err) {
|
|
350
|
+
this.log.error("Failed to send command", {
|
|
351
|
+
sessionId,
|
|
352
|
+
error: String(err),
|
|
353
|
+
});
|
|
354
|
+
throw new Error(`Failed to send command to session ${sessionId}: ${err}`);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
async sendKeys(sessionId, keys) {
|
|
358
|
+
const session = await this.requireSession(sessionId);
|
|
359
|
+
const tmuxName = this.getTmuxName(session);
|
|
360
|
+
this.log.debug("Sending keys", { sessionId, keys });
|
|
361
|
+
try {
|
|
362
|
+
tmuxExec(["send-keys", "-t", tmuxName, keys]);
|
|
363
|
+
}
|
|
364
|
+
catch (err) {
|
|
365
|
+
this.log.error("Failed to send keys", {
|
|
366
|
+
sessionId,
|
|
367
|
+
error: String(err),
|
|
368
|
+
});
|
|
369
|
+
throw new Error(`Failed to send keys to session ${sessionId}: ${err}`);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
async sendInterrupt(sessionId) {
|
|
373
|
+
const session = await this.requireSession(sessionId);
|
|
374
|
+
const tmuxName = this.getTmuxName(session);
|
|
375
|
+
this.log.debug("Sending interrupt (C-c)", { sessionId });
|
|
376
|
+
try {
|
|
377
|
+
tmuxExec(["send-keys", "-t", tmuxName, "C-c"]);
|
|
378
|
+
}
|
|
379
|
+
catch (err) {
|
|
380
|
+
this.log.error("Failed to send interrupt", {
|
|
381
|
+
sessionId,
|
|
382
|
+
error: String(err),
|
|
383
|
+
});
|
|
384
|
+
throw new Error(`Failed to send interrupt to session ${sessionId}: ${err}`);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
async captureOutput(sessionId) {
|
|
388
|
+
const session = await this.requireSession(sessionId);
|
|
389
|
+
const tmuxName = this.getTmuxName(session);
|
|
390
|
+
this.log.debug("Capturing output", { sessionId });
|
|
391
|
+
try {
|
|
392
|
+
const output = tmuxExec(["capture-pane", "-t", tmuxName, "-p"]);
|
|
393
|
+
return output;
|
|
394
|
+
}
|
|
395
|
+
catch (err) {
|
|
396
|
+
this.log.error("Failed to capture output", {
|
|
397
|
+
sessionId,
|
|
398
|
+
error: String(err),
|
|
399
|
+
});
|
|
400
|
+
throw new Error(`Failed to capture output from session ${sessionId}: ${err}`);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
// -----------------------------------------------------------------------
|
|
404
|
+
// SessionProvider — rename / toggleMouse / getTerminationStatus
|
|
405
|
+
// -----------------------------------------------------------------------
|
|
406
|
+
async rename(sessionId, newName) {
|
|
407
|
+
const session = await this.requireSession(sessionId);
|
|
408
|
+
const tmuxName = this.getTmuxName(session);
|
|
409
|
+
this.log.info("Renaming session", {
|
|
410
|
+
sessionId,
|
|
411
|
+
from: session.name,
|
|
412
|
+
to: newName,
|
|
413
|
+
});
|
|
414
|
+
try {
|
|
415
|
+
// Rename the display name in our records (not the tmux session name,
|
|
416
|
+
// which is vibe-{id} and must stay stable for lookups)
|
|
417
|
+
session.name = newName;
|
|
418
|
+
session.updatedAt = nowISO();
|
|
419
|
+
await this.saveSession(session);
|
|
420
|
+
}
|
|
421
|
+
catch (err) {
|
|
422
|
+
this.log.error("Failed to rename session", {
|
|
423
|
+
sessionId,
|
|
424
|
+
error: String(err),
|
|
425
|
+
});
|
|
426
|
+
throw new Error(`Failed to rename session ${sessionId}: ${err}`);
|
|
427
|
+
}
|
|
428
|
+
// Also rename the tmux session for cosmetic purposes
|
|
429
|
+
const newTmuxName = `vibe-${session.id}`;
|
|
430
|
+
tmuxExecSilent(["rename-session", "-t", tmuxName, newTmuxName]);
|
|
431
|
+
}
|
|
432
|
+
async toggleMouse(sessionId) {
|
|
433
|
+
const session = await this.requireSession(sessionId);
|
|
434
|
+
const tmuxName = this.getTmuxName(session);
|
|
435
|
+
// Read current mouse setting
|
|
436
|
+
let mouseOn = false;
|
|
437
|
+
try {
|
|
438
|
+
const value = tmuxExec([
|
|
439
|
+
"show-options",
|
|
440
|
+
"-t",
|
|
441
|
+
tmuxName,
|
|
442
|
+
"-v",
|
|
443
|
+
"mouse",
|
|
444
|
+
]);
|
|
445
|
+
mouseOn = value.trim() === "on";
|
|
446
|
+
}
|
|
447
|
+
catch {
|
|
448
|
+
// Default to off if we can't read
|
|
449
|
+
mouseOn = false;
|
|
450
|
+
}
|
|
451
|
+
const newState = mouseOn ? "off" : "on";
|
|
452
|
+
tmuxExecSilent(["set", "-t", tmuxName, "mouse", newState]);
|
|
453
|
+
this.log.debug("Toggled mouse", { sessionId, mouse: newState });
|
|
454
|
+
return newState === "on";
|
|
455
|
+
}
|
|
456
|
+
async getTerminationStatus(sessionId) {
|
|
457
|
+
const sessions = await this.loadSessions();
|
|
458
|
+
const session = sessions.find((s) => s.id === sessionId);
|
|
459
|
+
if (!session) {
|
|
460
|
+
return { terminated: true, exists: false };
|
|
461
|
+
}
|
|
462
|
+
const tmuxName = this.getTmuxName(session);
|
|
463
|
+
const exists = tmuxExecSilent(["has-session", "-t", tmuxName]);
|
|
464
|
+
return {
|
|
465
|
+
terminated: session.status === "terminated" || !exists,
|
|
466
|
+
exists,
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
// -----------------------------------------------------------------------
|
|
470
|
+
// SessionProvider — terminal (ttyd) management
|
|
471
|
+
// -----------------------------------------------------------------------
|
|
472
|
+
async getTerminalInfo(sessionId) {
|
|
473
|
+
return this.getRunningTerminalInfo(sessionId);
|
|
474
|
+
}
|
|
475
|
+
async startTerminal(sessionId, port) {
|
|
476
|
+
const session = await this.requireSession(sessionId);
|
|
477
|
+
const tmuxName = this.getTmuxName(session);
|
|
478
|
+
// If already running, return existing info
|
|
479
|
+
const existing = this.getRunningTerminalInfo(sessionId);
|
|
480
|
+
if (existing) {
|
|
481
|
+
this.log.debug("Terminal already running", { sessionId, ...existing });
|
|
482
|
+
return existing;
|
|
483
|
+
}
|
|
484
|
+
// Find available port
|
|
485
|
+
const assignedPort = port ?? (await findAvailablePort(TTYD_BASE_PORT));
|
|
486
|
+
this.log.info("Starting ttyd terminal", {
|
|
487
|
+
sessionId,
|
|
488
|
+
tmuxName,
|
|
489
|
+
port: assignedPort,
|
|
490
|
+
});
|
|
491
|
+
// Spawn ttyd process
|
|
492
|
+
const child = Bun.spawn([
|
|
493
|
+
"ttyd",
|
|
494
|
+
"--writable",
|
|
495
|
+
"--port",
|
|
496
|
+
String(assignedPort),
|
|
497
|
+
"tmux",
|
|
498
|
+
"attach",
|
|
499
|
+
"-t",
|
|
500
|
+
tmuxName,
|
|
501
|
+
], {
|
|
502
|
+
stdout: "ignore",
|
|
503
|
+
stderr: "ignore",
|
|
504
|
+
stdin: "ignore",
|
|
505
|
+
});
|
|
506
|
+
if (!child.pid) {
|
|
507
|
+
throw new Error("Failed to start ttyd — no PID returned");
|
|
508
|
+
}
|
|
509
|
+
// Give ttyd a moment to bind the port
|
|
510
|
+
await sleep(500);
|
|
511
|
+
// Store references
|
|
512
|
+
this.ttydProcesses.set(sessionId, child);
|
|
513
|
+
this.ttydPorts.set(sessionId, assignedPort);
|
|
514
|
+
const terminalInfo = {
|
|
515
|
+
url: `http://localhost:${assignedPort}`,
|
|
516
|
+
port: assignedPort,
|
|
517
|
+
pid: child.pid,
|
|
518
|
+
};
|
|
519
|
+
// Update session record with terminal info
|
|
520
|
+
session.terminal = terminalInfo;
|
|
521
|
+
session.updatedAt = nowISO();
|
|
522
|
+
await this.saveSession(session);
|
|
523
|
+
this.log.info("ttyd terminal started", {
|
|
524
|
+
sessionId,
|
|
525
|
+
port: assignedPort,
|
|
526
|
+
pid: child.pid,
|
|
527
|
+
});
|
|
528
|
+
return terminalInfo;
|
|
529
|
+
}
|
|
530
|
+
async stopTerminal(sessionId) {
|
|
531
|
+
const child = this.ttydProcesses.get(sessionId);
|
|
532
|
+
if (!child) {
|
|
533
|
+
this.log.debug("No ttyd process found for session", { sessionId });
|
|
534
|
+
return;
|
|
535
|
+
}
|
|
536
|
+
this.log.info("Stopping ttyd terminal", {
|
|
537
|
+
sessionId,
|
|
538
|
+
pid: child.pid,
|
|
539
|
+
});
|
|
540
|
+
if (child.pid) {
|
|
541
|
+
await gracefulKill(child.pid);
|
|
542
|
+
}
|
|
543
|
+
this.ttydProcesses.delete(sessionId);
|
|
544
|
+
this.ttydPorts.delete(sessionId);
|
|
545
|
+
// Clear terminal from session record
|
|
546
|
+
const session = await this.getInfo(sessionId);
|
|
547
|
+
if (session) {
|
|
548
|
+
session.terminal = undefined;
|
|
549
|
+
session.updatedAt = nowISO();
|
|
550
|
+
await this.saveSession(session);
|
|
551
|
+
}
|
|
552
|
+
this.log.info("ttyd terminal stopped", { sessionId });
|
|
553
|
+
}
|
|
554
|
+
// -----------------------------------------------------------------------
|
|
555
|
+
// SessionProvider — system-level listing and bulk operations
|
|
556
|
+
// -----------------------------------------------------------------------
|
|
557
|
+
async listSystemSessions() {
|
|
558
|
+
try {
|
|
559
|
+
const raw = tmuxExec([
|
|
560
|
+
"list-sessions",
|
|
561
|
+
"-F",
|
|
562
|
+
"#{session_id}:#{session_name}:#{session_windows}:#{session_attached}:#{session_created}",
|
|
563
|
+
]);
|
|
564
|
+
if (!raw)
|
|
565
|
+
return [];
|
|
566
|
+
return raw.split("\n").map((line) => {
|
|
567
|
+
const [id, name, windows, attached, created] = line.split(":");
|
|
568
|
+
return {
|
|
569
|
+
id: id ?? "",
|
|
570
|
+
name: name ?? "",
|
|
571
|
+
windows: parseInt(windows ?? "0", 10),
|
|
572
|
+
attached: attached === "1",
|
|
573
|
+
createdAt: created
|
|
574
|
+
? new Date(parseInt(created, 10) * 1000).toISOString()
|
|
575
|
+
: undefined,
|
|
576
|
+
};
|
|
577
|
+
});
|
|
578
|
+
}
|
|
579
|
+
catch {
|
|
580
|
+
// tmux list-sessions fails when there are no sessions
|
|
581
|
+
return [];
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
async listSystemTerminals() {
|
|
585
|
+
const terminals = [];
|
|
586
|
+
for (const [sessionId, child] of this.ttydProcesses) {
|
|
587
|
+
const port = this.ttydPorts.get(sessionId);
|
|
588
|
+
if (child.pid && port !== undefined) {
|
|
589
|
+
terminals.push({
|
|
590
|
+
pid: child.pid,
|
|
591
|
+
port,
|
|
592
|
+
sessionId,
|
|
593
|
+
});
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
// Also look for any orphaned ttyd processes via pgrep
|
|
597
|
+
try {
|
|
598
|
+
const raw = execSync("pgrep -a ttyd", {
|
|
599
|
+
encoding: "utf-8",
|
|
600
|
+
timeout: 5000,
|
|
601
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
602
|
+
}).trim();
|
|
603
|
+
if (raw) {
|
|
604
|
+
for (const line of raw.split("\n")) {
|
|
605
|
+
const parts = line.trim().split(/\s+/);
|
|
606
|
+
const pid = parseInt(parts[0] ?? "0", 10);
|
|
607
|
+
if (!pid)
|
|
608
|
+
continue;
|
|
609
|
+
// Skip already-tracked processes
|
|
610
|
+
const alreadyTracked = [...this.ttydProcesses.values()].some((p) => p.pid === pid);
|
|
611
|
+
if (alreadyTracked)
|
|
612
|
+
continue;
|
|
613
|
+
// Try to extract port from command line
|
|
614
|
+
const portIdx = parts.indexOf("--port");
|
|
615
|
+
const port = portIdx !== -1 ? parseInt(parts[portIdx + 1] ?? "0", 10) : 0;
|
|
616
|
+
terminals.push({ pid, port: port || 0 });
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
catch {
|
|
621
|
+
// pgrep returns non-zero when no processes found — that's fine
|
|
622
|
+
}
|
|
623
|
+
return terminals;
|
|
624
|
+
}
|
|
625
|
+
async bulkKillSystemSessions(sessionIds) {
|
|
626
|
+
let killed = 0;
|
|
627
|
+
let failed = 0;
|
|
628
|
+
for (const sid of sessionIds) {
|
|
629
|
+
const success = tmuxExecSilent(["kill-session", "-t", sid]);
|
|
630
|
+
if (success) {
|
|
631
|
+
killed++;
|
|
632
|
+
}
|
|
633
|
+
else {
|
|
634
|
+
failed++;
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
this.log.info("Bulk kill system sessions", { killed, failed });
|
|
638
|
+
return { killed, failed };
|
|
639
|
+
}
|
|
640
|
+
async bulkKillSystemTerminals(pids) {
|
|
641
|
+
let killed = 0;
|
|
642
|
+
let failed = 0;
|
|
643
|
+
const killPromises = pids.map(async (pid) => {
|
|
644
|
+
try {
|
|
645
|
+
await gracefulKill(pid);
|
|
646
|
+
killed++;
|
|
647
|
+
// Remove from tracked processes if present
|
|
648
|
+
for (const [sessionId, child] of this.ttydProcesses) {
|
|
649
|
+
if (child.pid === pid) {
|
|
650
|
+
this.ttydProcesses.delete(sessionId);
|
|
651
|
+
this.ttydPorts.delete(sessionId);
|
|
652
|
+
break;
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
catch {
|
|
657
|
+
failed++;
|
|
658
|
+
}
|
|
659
|
+
});
|
|
660
|
+
await Promise.allSettled(killPromises);
|
|
661
|
+
this.log.info("Bulk kill system terminals", { killed, failed });
|
|
662
|
+
return { killed, failed };
|
|
663
|
+
}
|
|
664
|
+
// -----------------------------------------------------------------------
|
|
665
|
+
// SessionProvider — health / project filter / cleanup
|
|
666
|
+
// -----------------------------------------------------------------------
|
|
667
|
+
async healthCheck() {
|
|
668
|
+
let tmuxOk = false;
|
|
669
|
+
let tmuxVersion = "";
|
|
670
|
+
let sessionCount = 0;
|
|
671
|
+
const terminalCount = this.ttydProcesses.size;
|
|
672
|
+
try {
|
|
673
|
+
tmuxVersion = tmuxExec(["-V"]);
|
|
674
|
+
tmuxOk = true;
|
|
675
|
+
}
|
|
676
|
+
catch {
|
|
677
|
+
return {
|
|
678
|
+
ok: false,
|
|
679
|
+
sessions: 0,
|
|
680
|
+
terminals: terminalCount,
|
|
681
|
+
message: "tmux is not available",
|
|
682
|
+
};
|
|
683
|
+
}
|
|
684
|
+
try {
|
|
685
|
+
const sysSessions = await this.listSystemSessions();
|
|
686
|
+
sessionCount = sysSessions.length;
|
|
687
|
+
}
|
|
688
|
+
catch {
|
|
689
|
+
// Ignore — zero sessions
|
|
690
|
+
}
|
|
691
|
+
// Check ttyd availability
|
|
692
|
+
let ttydOk = false;
|
|
693
|
+
try {
|
|
694
|
+
execSync("which ttyd", {
|
|
695
|
+
encoding: "utf-8",
|
|
696
|
+
timeout: 5000,
|
|
697
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
698
|
+
});
|
|
699
|
+
ttydOk = true;
|
|
700
|
+
}
|
|
701
|
+
catch {
|
|
702
|
+
// ttyd not found
|
|
703
|
+
}
|
|
704
|
+
const messages = [tmuxVersion];
|
|
705
|
+
if (!ttydOk) {
|
|
706
|
+
messages.push("ttyd not found — web terminals unavailable");
|
|
707
|
+
}
|
|
708
|
+
return {
|
|
709
|
+
ok: tmuxOk,
|
|
710
|
+
sessions: sessionCount,
|
|
711
|
+
terminals: terminalCount,
|
|
712
|
+
message: messages.join("; "),
|
|
713
|
+
};
|
|
714
|
+
}
|
|
715
|
+
async getSessionsByProject(projectId) {
|
|
716
|
+
const sessions = await this.list();
|
|
717
|
+
return sessions.filter((s) => s.projectId === projectId);
|
|
718
|
+
}
|
|
719
|
+
async cleanup() {
|
|
720
|
+
this.log.info("Running cleanup");
|
|
721
|
+
const sessions = await this.loadSessions();
|
|
722
|
+
let cleaned = 0;
|
|
723
|
+
const kept = [];
|
|
724
|
+
for (const session of sessions) {
|
|
725
|
+
const tmuxName = this.getTmuxName(session);
|
|
726
|
+
const exists = tmuxExecSilent(["has-session", "-t", tmuxName]);
|
|
727
|
+
if (session.status === "terminated" || !exists) {
|
|
728
|
+
// Stop terminal if somehow still running
|
|
729
|
+
if (this.ttydProcesses.has(session.id)) {
|
|
730
|
+
await this.stopTerminal(session.id);
|
|
731
|
+
}
|
|
732
|
+
// Kill the tmux session if it still exists but was marked terminated
|
|
733
|
+
if (exists && session.status === "terminated") {
|
|
734
|
+
tmuxExecSilent(["kill-session", "-t", tmuxName]);
|
|
735
|
+
}
|
|
736
|
+
cleaned++;
|
|
737
|
+
this.log.debug("Cleaned session", {
|
|
738
|
+
id: session.id,
|
|
739
|
+
name: session.name,
|
|
740
|
+
});
|
|
741
|
+
}
|
|
742
|
+
else {
|
|
743
|
+
kept.push(session);
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
await this.saveSessions(kept);
|
|
747
|
+
this.log.info("Cleanup complete", { cleaned, remaining: kept.length });
|
|
748
|
+
return { cleaned };
|
|
749
|
+
}
|
|
750
|
+
// -----------------------------------------------------------------------
|
|
751
|
+
// Private helpers — storage
|
|
752
|
+
// -----------------------------------------------------------------------
|
|
753
|
+
/**
|
|
754
|
+
* Load all session records from persistent storage.
|
|
755
|
+
*/
|
|
756
|
+
async loadSessions() {
|
|
757
|
+
try {
|
|
758
|
+
const raw = await this.storage.get(`${STORAGE_NAMESPACE}:${STORAGE_KEY_SESSIONS}`);
|
|
759
|
+
if (!raw)
|
|
760
|
+
return [];
|
|
761
|
+
return JSON.parse(raw);
|
|
762
|
+
}
|
|
763
|
+
catch (err) {
|
|
764
|
+
this.log.error("Failed to load sessions from storage", {
|
|
765
|
+
error: String(err),
|
|
766
|
+
});
|
|
767
|
+
return [];
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
/**
|
|
771
|
+
* Save the full session list to persistent storage.
|
|
772
|
+
*/
|
|
773
|
+
async saveSessions(sessions) {
|
|
774
|
+
try {
|
|
775
|
+
await this.storage.set(`${STORAGE_NAMESPACE}:${STORAGE_KEY_SESSIONS}`, JSON.stringify(sessions));
|
|
776
|
+
}
|
|
777
|
+
catch (err) {
|
|
778
|
+
this.log.error("Failed to save sessions to storage", {
|
|
779
|
+
error: String(err),
|
|
780
|
+
});
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
/**
|
|
784
|
+
* Save (upsert) a single session record.
|
|
785
|
+
*/
|
|
786
|
+
async saveSession(session) {
|
|
787
|
+
const sessions = await this.loadSessions();
|
|
788
|
+
const idx = sessions.findIndex((s) => s.id === session.id);
|
|
789
|
+
if (idx >= 0) {
|
|
790
|
+
sessions[idx] = session;
|
|
791
|
+
}
|
|
792
|
+
else {
|
|
793
|
+
sessions.push(session);
|
|
794
|
+
}
|
|
795
|
+
await this.saveSessions(sessions);
|
|
796
|
+
}
|
|
797
|
+
// -----------------------------------------------------------------------
|
|
798
|
+
// Private helpers — tmux utilities
|
|
799
|
+
// -----------------------------------------------------------------------
|
|
800
|
+
/**
|
|
801
|
+
* Extract the tmux session name from a SessionInfo record.
|
|
802
|
+
* The tmux session name is stored in metadata.tmuxSessionName or
|
|
803
|
+
* defaults to `vibe-{id}`.
|
|
804
|
+
*/
|
|
805
|
+
getTmuxName(session) {
|
|
806
|
+
const meta = session.metadata;
|
|
807
|
+
if (meta && typeof meta.tmuxSessionName === "string") {
|
|
808
|
+
return meta.tmuxSessionName;
|
|
809
|
+
}
|
|
810
|
+
return `vibe-${session.id}`;
|
|
811
|
+
}
|
|
812
|
+
/**
|
|
813
|
+
* Get the PID of the first pane in a tmux session.
|
|
814
|
+
*/
|
|
815
|
+
getSessionPanePid(tmuxName) {
|
|
816
|
+
try {
|
|
817
|
+
const raw = tmuxExec([
|
|
818
|
+
"list-panes",
|
|
819
|
+
"-t",
|
|
820
|
+
tmuxName,
|
|
821
|
+
"-F",
|
|
822
|
+
"#{pane_pid}",
|
|
823
|
+
]);
|
|
824
|
+
const pid = parseInt(raw.split("\n")[0] ?? "", 10);
|
|
825
|
+
return isNaN(pid) ? null : pid;
|
|
826
|
+
}
|
|
827
|
+
catch {
|
|
828
|
+
return null;
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
/**
|
|
832
|
+
* Look up a session by ID and throw if not found.
|
|
833
|
+
*/
|
|
834
|
+
async requireSession(sessionId) {
|
|
835
|
+
const session = await this.getInfo(sessionId);
|
|
836
|
+
if (!session) {
|
|
837
|
+
throw new Error(`Session not found: ${sessionId}`);
|
|
838
|
+
}
|
|
839
|
+
if (session.status === "terminated") {
|
|
840
|
+
throw new Error(`Session is terminated: ${sessionId}`);
|
|
841
|
+
}
|
|
842
|
+
return session;
|
|
843
|
+
}
|
|
844
|
+
/**
|
|
845
|
+
* Get TerminalInfo for a session if ttyd is currently running.
|
|
846
|
+
*/
|
|
847
|
+
getRunningTerminalInfo(sessionId) {
|
|
848
|
+
const child = this.ttydProcesses.get(sessionId);
|
|
849
|
+
const port = this.ttydPorts.get(sessionId);
|
|
850
|
+
if (!child || !child.pid || port === undefined) {
|
|
851
|
+
return null;
|
|
852
|
+
}
|
|
853
|
+
// Verify process is still alive
|
|
854
|
+
if (!isProcessAlive(child.pid)) {
|
|
855
|
+
this.ttydProcesses.delete(sessionId);
|
|
856
|
+
this.ttydPorts.delete(sessionId);
|
|
857
|
+
return null;
|
|
858
|
+
}
|
|
859
|
+
return {
|
|
860
|
+
url: `http://localhost:${port}`,
|
|
861
|
+
port,
|
|
862
|
+
pid: child.pid,
|
|
863
|
+
};
|
|
864
|
+
}
|
|
865
|
+
/**
|
|
866
|
+
* Reconcile persisted session records against actual tmux state.
|
|
867
|
+
* Marks sessions as inactive/terminated if their tmux session is gone.
|
|
868
|
+
*/
|
|
869
|
+
async reconcileSessions() {
|
|
870
|
+
const sessions = await this.loadSessions();
|
|
871
|
+
let changed = false;
|
|
872
|
+
for (const session of sessions) {
|
|
873
|
+
if (session.status === "terminated")
|
|
874
|
+
continue;
|
|
875
|
+
const tmuxName = this.getTmuxName(session);
|
|
876
|
+
const exists = tmuxExecSilent(["has-session", "-t", tmuxName]);
|
|
877
|
+
if (!exists && session.status === "active") {
|
|
878
|
+
session.status = "inactive";
|
|
879
|
+
session.updatedAt = nowISO();
|
|
880
|
+
session.terminal = undefined;
|
|
881
|
+
changed = true;
|
|
882
|
+
this.log.info("Reconciled stale session as inactive", {
|
|
883
|
+
id: session.id,
|
|
884
|
+
name: session.name,
|
|
885
|
+
});
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
if (changed) {
|
|
889
|
+
await this.saveSessions(sessions);
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
// ---------------------------------------------------------------------------
|
|
894
|
+
// Plugin export
|
|
895
|
+
// ---------------------------------------------------------------------------
|
|
896
|
+
const provider = new TmuxSessionProvider();
|
|
897
|
+
const vibePlugin = {
|
|
898
|
+
name: "@burdenoff/vibe-plugin-session-tmux",
|
|
899
|
+
version: "1.0.0",
|
|
900
|
+
description: "Tmux + ttyd session provider — manages terminal sessions via tmux and exposes web terminals via ttyd",
|
|
901
|
+
tags: ["backend", "provider"],
|
|
902
|
+
providers: {
|
|
903
|
+
session: provider,
|
|
904
|
+
},
|
|
905
|
+
async onServerStart(services) {
|
|
906
|
+
await provider.init(services);
|
|
907
|
+
},
|
|
908
|
+
async onServerStop() {
|
|
909
|
+
await provider.shutdown();
|
|
910
|
+
},
|
|
911
|
+
};
|
|
912
|
+
export { vibePlugin };
|
|
913
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@burdenoff/vibe-plugin-session-tmux",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"description": "Tmux + ttyd session provider plugin for VibeControls Agent",
|
|
7
|
+
"engines": {
|
|
8
|
+
"bun": ">=1.3.0"
|
|
9
|
+
},
|
|
10
|
+
"author": {
|
|
11
|
+
"name": "Vignesh T.V",
|
|
12
|
+
"email": "vignesh@burdenoff.com",
|
|
13
|
+
"url": "https://github.com/tvvignesh"
|
|
14
|
+
},
|
|
15
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "tsc",
|
|
18
|
+
"dev": "tsc --watch",
|
|
19
|
+
"lint": "eslint ./src",
|
|
20
|
+
"format": "bunx prettier . --write",
|
|
21
|
+
"format:check": "bunx prettier . --check",
|
|
22
|
+
"type:check": "tsc --noEmit",
|
|
23
|
+
"clean": "rimraf dist",
|
|
24
|
+
"prebuild": "bun run clean",
|
|
25
|
+
"prepublishOnly": "bun run build",
|
|
26
|
+
"sanity": "bun run format:check && bun run lint && bun run type:check && bun run build"
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@types/bun": "^1.3.9",
|
|
31
|
+
"@types/node": "^22.0.0",
|
|
32
|
+
"eslint": "^9.30.1",
|
|
33
|
+
"prettier": "^3.6.2",
|
|
34
|
+
"rimraf": "^6.0.1",
|
|
35
|
+
"typescript": "^5.9.3"
|
|
36
|
+
},
|
|
37
|
+
"peerDependencies": {
|
|
38
|
+
"@burdenoff/vibe-agent": ">=2.0.0"
|
|
39
|
+
},
|
|
40
|
+
"peerDependenciesMeta": {
|
|
41
|
+
"@burdenoff/vibe-agent": {
|
|
42
|
+
"optional": true
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
"keywords": [
|
|
46
|
+
"vibecontrols",
|
|
47
|
+
"vibe",
|
|
48
|
+
"vibe-plugin",
|
|
49
|
+
"tmux",
|
|
50
|
+
"ttyd",
|
|
51
|
+
"session",
|
|
52
|
+
"terminal",
|
|
53
|
+
"bun"
|
|
54
|
+
],
|
|
55
|
+
"repository": {
|
|
56
|
+
"type": "git",
|
|
57
|
+
"url": "git+https://github.com/algoshred/vibe-plugin-session-tmux.git"
|
|
58
|
+
},
|
|
59
|
+
"bugs": {
|
|
60
|
+
"url": "https://github.com/algoshred/vibe-plugin-session-tmux/issues"
|
|
61
|
+
},
|
|
62
|
+
"homepage": "https://vibecontrols.com",
|
|
63
|
+
"publishConfig": {
|
|
64
|
+
"access": "public",
|
|
65
|
+
"registry": "https://registry.npmjs.org/"
|
|
66
|
+
},
|
|
67
|
+
"files": [
|
|
68
|
+
"dist/**/*",
|
|
69
|
+
"README.md",
|
|
70
|
+
"LICENSE"
|
|
71
|
+
]
|
|
72
|
+
}
|