@made-by-moonlight/athene-plugin-runtime-tmux 0.9.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.
- package/LICENSE +22 -0
- package/README.md +169 -0
- package/dist/__tests__/index.test.d.ts +2 -0
- package/dist/__tests__/index.test.d.ts.map +1 -0
- package/dist/__tests__/index.test.js +466 -0
- package/dist/__tests__/index.test.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +206 -0
- package/dist/index.js.map +1 -0
- package/package.json +48 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Composio, Inc.
|
|
4
|
+
Copyright (c) 2026 slievr (Athene fork)
|
|
5
|
+
|
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
in the Software without restriction, including without limitation the rights
|
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
furnished to do so, subject to the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
# @agent-orchestrator/plugin-runtime-tmux
|
|
2
|
+
|
|
3
|
+
Runtime plugin for executing agent sessions in tmux.
|
|
4
|
+
|
|
5
|
+
## What This Does
|
|
6
|
+
|
|
7
|
+
Creates isolated tmux sessions for each agent. Each session runs in a separate tmux session with:
|
|
8
|
+
|
|
9
|
+
- Working directory set to workspace path
|
|
10
|
+
- Environment variables from config
|
|
11
|
+
- Agent launch command executed automatically
|
|
12
|
+
|
|
13
|
+
## How It Works
|
|
14
|
+
|
|
15
|
+
### Creating a Session
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
const handle = await runtime.create({
|
|
19
|
+
sessionId: "my-app-3",
|
|
20
|
+
workspacePath: "/Users/dev/.worktrees/my-app/my-app-3",
|
|
21
|
+
launchCommand: "claude -p 'Fix bug in auth module'",
|
|
22
|
+
environment: {
|
|
23
|
+
AO_SESSION_ID: "my-app-3",
|
|
24
|
+
AO_PROJECT_ID: "my-app",
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
**What happens:**
|
|
30
|
+
|
|
31
|
+
1. Validates `sessionId` (only alphanumeric, dash, underscore allowed)
|
|
32
|
+
2. Creates detached tmux session: `tmux new-session -d -s my-app-3 -c /path/to/workspace`
|
|
33
|
+
3. Sets environment variables: `tmux ... -e KEY=VALUE`
|
|
34
|
+
4. Sends launch command: `tmux send-keys -t my-app-3 "claude -p '...'" Enter`
|
|
35
|
+
5. Returns RuntimeHandle with tmux session name
|
|
36
|
+
|
|
37
|
+
### Sending Messages
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
await runtime.sendMessage(handle, "Fix the test failure in auth.test.ts");
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
**What happens:**
|
|
44
|
+
|
|
45
|
+
1. Clears partial input: `tmux send-keys -t my-app-3 C-u`
|
|
46
|
+
2. For short messages (<200 chars, no newlines): sends directly with `-l` flag (literal mode)
|
|
47
|
+
3. For long/multiline messages: writes to temp file → `tmux load-buffer` → `tmux paste-buffer`
|
|
48
|
+
4. Waits 300ms (let tmux process the text)
|
|
49
|
+
5. Sends Enter: `tmux send-keys -t my-app-3 Enter`
|
|
50
|
+
|
|
51
|
+
**Why the complexity?**
|
|
52
|
+
|
|
53
|
+
- `send-keys` without `-l` interprets special strings ("Enter", "Space") as key names
|
|
54
|
+
- Long strings can overflow tmux's command buffer
|
|
55
|
+
- Multiline strings need special handling
|
|
56
|
+
|
|
57
|
+
### Getting Output
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
const output = await runtime.getOutput(handle, 50); // last 50 lines
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Uses `tmux capture-pane -t my-app-3 -p -S -50` to capture terminal buffer.
|
|
64
|
+
|
|
65
|
+
### Checking if Alive
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
const alive = await runtime.isAlive(handle);
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Uses `tmux has-session -t my-app-3` (exit code 0 = exists, 1 = doesn't exist).
|
|
72
|
+
|
|
73
|
+
### Destroying
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
await runtime.destroy(handle);
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Kills tmux session: `tmux kill-session -t my-app-3` (ignores errors if already dead).
|
|
80
|
+
|
|
81
|
+
## Attaching to Sessions
|
|
82
|
+
|
|
83
|
+
For Terminal plugins (iTerm2, web):
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
const attachInfo = await runtime.getAttachInfo(handle);
|
|
87
|
+
// Returns: { type: "tmux", target: "my-app-3", command: "tmux attach -t my-app-3" }
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Security
|
|
91
|
+
|
|
92
|
+
**Session ID validation:**
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
const SAFE_SESSION_ID = /^[a-zA-Z0-9_-]+$/;
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Only allows safe characters. Prevents shell injection via session name (used in tmux commands).
|
|
99
|
+
|
|
100
|
+
## Error Handling
|
|
101
|
+
|
|
102
|
+
- **Session creation fails** → cleans up (kills session) before throwing
|
|
103
|
+
- **Message send fails** → throws (caller should handle)
|
|
104
|
+
- **Session already dead** → `destroy()` silently succeeds (idempotent)
|
|
105
|
+
|
|
106
|
+
## Metrics
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
const metrics = await runtime.getMetrics(handle);
|
|
110
|
+
// Returns: { uptimeMs: 123456 }
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Tracks uptime (stored in RuntimeHandle.data.createdAt).
|
|
114
|
+
|
|
115
|
+
## Testing
|
|
116
|
+
|
|
117
|
+
This plugin is tested indirectly via `packages/core/src/__tests__/tmux.test.ts` (utility functions) and integration tests.
|
|
118
|
+
|
|
119
|
+
To test manually:
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
# Start a test session
|
|
123
|
+
tmux new-session -d -s test-session -c /tmp
|
|
124
|
+
tmux send-keys -t test-session "echo hello" Enter
|
|
125
|
+
|
|
126
|
+
# Capture output
|
|
127
|
+
tmux capture-pane -t test-session -p
|
|
128
|
+
|
|
129
|
+
# Kill session
|
|
130
|
+
tmux kill-session -t test-session
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Common Issues
|
|
134
|
+
|
|
135
|
+
### tmux not installed
|
|
136
|
+
|
|
137
|
+
If tmux is not in PATH, all operations fail. Install via:
|
|
138
|
+
|
|
139
|
+
- macOS: `brew install tmux`
|
|
140
|
+
- Linux: `apt-get install tmux` or `yum install tmux`
|
|
141
|
+
|
|
142
|
+
### Session name conflicts
|
|
143
|
+
|
|
144
|
+
If a session with the same ID already exists, `create()` fails. The orchestrator should ensure unique session IDs.
|
|
145
|
+
|
|
146
|
+
### Detached sessions persist after orchestrator crashes
|
|
147
|
+
|
|
148
|
+
tmux sessions keep running even if the orchestrator dies. Use `tmux list-sessions` to find orphans, `tmux kill-session -t <name>` to clean up.
|
|
149
|
+
|
|
150
|
+
## Limitations
|
|
151
|
+
|
|
152
|
+
- **macOS/Linux only** — tmux is not available natively on Windows. On Windows, use the `runtime-process` plugin (the default there); it provides native PTY support via ConPTY and `node-pty`. WSL is not required.
|
|
153
|
+
- **Terminal buffer size** — `getOutput()` limited by tmux buffer size (default 2000 lines)
|
|
154
|
+
- **No resource limits** — agents can consume unlimited CPU/memory (use docker/k8s runtimes for isolation)
|
|
155
|
+
|
|
156
|
+
## Architecture Notes
|
|
157
|
+
|
|
158
|
+
**Why tmux over raw processes?**
|
|
159
|
+
|
|
160
|
+
- Sessions persist across orchestrator restarts
|
|
161
|
+
- Easy to attach for debugging: `tmux attach -t session-name`
|
|
162
|
+
- Terminal emulation (colors, ANSI codes work)
|
|
163
|
+
- Works well with interactive AI tools (Claude Code, Aider)
|
|
164
|
+
|
|
165
|
+
**Why detached mode?**
|
|
166
|
+
|
|
167
|
+
- Orchestrator doesn't block waiting for agent
|
|
168
|
+
- Multiple agents can run in parallel
|
|
169
|
+
- Humans can attach later without interrupting agent
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/index.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,466 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
2
|
+
import * as childProcess from "node:child_process";
|
|
3
|
+
import * as fs from "node:fs";
|
|
4
|
+
// Mock node:child_process with custom promisify support
|
|
5
|
+
vi.mock("node:child_process", () => {
|
|
6
|
+
const mockExecFile = vi.fn();
|
|
7
|
+
// promisify(execFile) checks for a custom promisify symbol. Set it so
|
|
8
|
+
// await execFileAsync(...) returns { stdout, stderr } properly.
|
|
9
|
+
mockExecFile[Symbol.for("nodejs.util.promisify.custom")] = vi.fn();
|
|
10
|
+
return { execFile: mockExecFile };
|
|
11
|
+
});
|
|
12
|
+
// Mock node:crypto for deterministic UUIDs
|
|
13
|
+
vi.mock("node:crypto", () => ({
|
|
14
|
+
randomUUID: () => "test-uuid-1234",
|
|
15
|
+
}));
|
|
16
|
+
// Mock node:fs for writeFileSync / unlinkSync
|
|
17
|
+
vi.mock("node:fs", () => ({
|
|
18
|
+
writeFileSync: vi.fn(),
|
|
19
|
+
unlinkSync: vi.fn(),
|
|
20
|
+
}));
|
|
21
|
+
// Get reference to the promisify-custom mock — this is what the plugin actually calls
|
|
22
|
+
const mockExecFileCustom = childProcess.execFile[Symbol.for("nodejs.util.promisify.custom")];
|
|
23
|
+
const expectedTmuxOptions = { timeout: 5_000 };
|
|
24
|
+
/** Queue a successful tmux command with the given stdout. */
|
|
25
|
+
function mockTmuxSuccess(stdout = "") {
|
|
26
|
+
mockExecFileCustom.mockResolvedValueOnce({ stdout: stdout + "\n", stderr: "" });
|
|
27
|
+
}
|
|
28
|
+
/** Queue a failed tmux command. */
|
|
29
|
+
function mockTmuxError(message) {
|
|
30
|
+
mockExecFileCustom.mockRejectedValueOnce(new Error(message));
|
|
31
|
+
}
|
|
32
|
+
/** Create a RuntimeHandle for testing. */
|
|
33
|
+
function makeHandle(id, createdAt) {
|
|
34
|
+
return {
|
|
35
|
+
id,
|
|
36
|
+
runtimeName: "tmux",
|
|
37
|
+
data: {
|
|
38
|
+
createdAt: createdAt ?? 1000,
|
|
39
|
+
workspacePath: "/tmp/workspace",
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
// Import after mocks are set up
|
|
44
|
+
import tmuxPlugin, { manifest, create } from "../index.js";
|
|
45
|
+
beforeEach(() => {
|
|
46
|
+
vi.clearAllMocks();
|
|
47
|
+
});
|
|
48
|
+
describe("manifest", () => {
|
|
49
|
+
it("has name 'tmux' and slot 'runtime'", () => {
|
|
50
|
+
expect(manifest.name).toBe("tmux");
|
|
51
|
+
expect(manifest.slot).toBe("runtime");
|
|
52
|
+
expect(manifest.version).toBe("0.1.0");
|
|
53
|
+
expect(manifest.description).toBe("Runtime plugin: tmux sessions");
|
|
54
|
+
});
|
|
55
|
+
it("default export includes manifest and create", () => {
|
|
56
|
+
expect(tmuxPlugin.manifest).toBe(manifest);
|
|
57
|
+
expect(tmuxPlugin.create).toBe(create);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
describe("create()", () => {
|
|
61
|
+
it("returns a Runtime with name 'tmux'", () => {
|
|
62
|
+
const runtime = create();
|
|
63
|
+
expect(runtime.name).toBe("tmux");
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
describe("runtime.create()", () => {
|
|
67
|
+
it("calls new-session with correct args", async () => {
|
|
68
|
+
const runtime = create();
|
|
69
|
+
// 1: new-session (with launch command as initial), 2: set-option status off
|
|
70
|
+
mockTmuxSuccess();
|
|
71
|
+
mockTmuxSuccess();
|
|
72
|
+
const handle = await runtime.create({
|
|
73
|
+
sessionId: "test-session",
|
|
74
|
+
workspacePath: "/tmp/workspace",
|
|
75
|
+
launchCommand: "echo hello",
|
|
76
|
+
environment: {},
|
|
77
|
+
});
|
|
78
|
+
expect(handle.id).toBe("test-session");
|
|
79
|
+
expect(handle.runtimeName).toBe("tmux");
|
|
80
|
+
expect(handle.data.workspacePath).toBe("/tmp/workspace");
|
|
81
|
+
// First call: new-session — launch command has the keep-alive shell tail
|
|
82
|
+
// appended so the tmux session survives agent exit (issue #1756).
|
|
83
|
+
expect(mockExecFileCustom).toHaveBeenCalledWith("tmux", [
|
|
84
|
+
"new-session",
|
|
85
|
+
"-d",
|
|
86
|
+
"-s",
|
|
87
|
+
"test-session",
|
|
88
|
+
"-c",
|
|
89
|
+
"/tmp/workspace",
|
|
90
|
+
'echo hello\nexec "${SHELL:-/bin/bash}" -i',
|
|
91
|
+
], expectedTmuxOptions);
|
|
92
|
+
});
|
|
93
|
+
it("disables the tmux status bar immediately after new-session", async () => {
|
|
94
|
+
const runtime = create();
|
|
95
|
+
// 1: new-session, 2: set-option status off
|
|
96
|
+
mockTmuxSuccess();
|
|
97
|
+
mockTmuxSuccess();
|
|
98
|
+
await runtime.create({
|
|
99
|
+
sessionId: "status-bar-off",
|
|
100
|
+
workspacePath: "/tmp/ws",
|
|
101
|
+
launchCommand: "echo hi",
|
|
102
|
+
environment: {},
|
|
103
|
+
});
|
|
104
|
+
// Second call must be set-option ... status off, scoped to the session
|
|
105
|
+
expect(mockExecFileCustom).toHaveBeenNthCalledWith(2, "tmux", ["set-option", "-t", "status-bar-off", "status", "off"], expectedTmuxOptions);
|
|
106
|
+
});
|
|
107
|
+
it("includes -e KEY=VALUE flags for environment variables", async () => {
|
|
108
|
+
const runtime = create();
|
|
109
|
+
mockTmuxSuccess();
|
|
110
|
+
mockTmuxSuccess();
|
|
111
|
+
await runtime.create({
|
|
112
|
+
sessionId: "env-session",
|
|
113
|
+
workspacePath: "/tmp/ws",
|
|
114
|
+
launchCommand: "bash",
|
|
115
|
+
environment: { AO_SESSION: "env-session", FOO: "bar" },
|
|
116
|
+
});
|
|
117
|
+
// First call: new-session with env args
|
|
118
|
+
const firstCallArgs = mockExecFileCustom.mock.calls[0];
|
|
119
|
+
const args = firstCallArgs[1];
|
|
120
|
+
expect(args).toContain("-e");
|
|
121
|
+
expect(args).toContain("AO_SESSION=env-session");
|
|
122
|
+
expect(args).toContain("FOO=bar");
|
|
123
|
+
expect(args.at(-1)).toBe('bash\nexec "${SHELL:-/bin/bash}" -i');
|
|
124
|
+
});
|
|
125
|
+
it("starts the launch command as the initial tmux pane command", async () => {
|
|
126
|
+
const runtime = create();
|
|
127
|
+
mockTmuxSuccess();
|
|
128
|
+
mockTmuxSuccess();
|
|
129
|
+
await runtime.create({
|
|
130
|
+
sessionId: "launch-test",
|
|
131
|
+
workspacePath: "/tmp/ws",
|
|
132
|
+
launchCommand: "claude --session abc",
|
|
133
|
+
environment: {},
|
|
134
|
+
});
|
|
135
|
+
// First call: new-session passes the launch command as the pane's initial
|
|
136
|
+
// command, with the keep-alive shell tail appended.
|
|
137
|
+
expect(mockExecFileCustom).toHaveBeenCalledWith("tmux", [
|
|
138
|
+
"new-session",
|
|
139
|
+
"-d",
|
|
140
|
+
"-s",
|
|
141
|
+
"launch-test",
|
|
142
|
+
"-c",
|
|
143
|
+
"/tmp/ws",
|
|
144
|
+
'claude --session abc\nexec "${SHELL:-/bin/bash}" -i',
|
|
145
|
+
], expectedTmuxOptions);
|
|
146
|
+
});
|
|
147
|
+
it("appends an interactive shell tail so the tmux pane survives agent exit (regression for #1756)", async () => {
|
|
148
|
+
const runtime = create();
|
|
149
|
+
mockTmuxSuccess();
|
|
150
|
+
mockTmuxSuccess();
|
|
151
|
+
await runtime.create({
|
|
152
|
+
sessionId: "keep-alive",
|
|
153
|
+
workspacePath: "/tmp/ws",
|
|
154
|
+
launchCommand: "claude --session abc",
|
|
155
|
+
environment: {},
|
|
156
|
+
});
|
|
157
|
+
const finalArg = mockExecFileCustom.mock.calls[0][1].at(-1);
|
|
158
|
+
expect(finalArg).toContain("claude --session abc");
|
|
159
|
+
expect(finalArg).toMatch(/exec "\$\{SHELL:-\/bin\/bash\}" -i\s*$/);
|
|
160
|
+
});
|
|
161
|
+
it("keeps the keep-alive tail in the temp script for long launch commands", async () => {
|
|
162
|
+
const runtime = create();
|
|
163
|
+
const longCommand = "x".repeat(250);
|
|
164
|
+
// 1: new-session (with bash invocation as initial command), 2: set-option
|
|
165
|
+
mockTmuxSuccess();
|
|
166
|
+
mockTmuxSuccess();
|
|
167
|
+
await runtime.create({
|
|
168
|
+
sessionId: "launch-long",
|
|
169
|
+
workspacePath: "/tmp/ws",
|
|
170
|
+
launchCommand: longCommand,
|
|
171
|
+
environment: {},
|
|
172
|
+
});
|
|
173
|
+
expect(fs.writeFileSync).toHaveBeenCalledWith(expect.stringContaining("ao-launch-test-uuid-1234.sh"), expect.stringContaining(longCommand), { encoding: "utf-8", mode: 0o700 });
|
|
174
|
+
// The script body includes the interactive shell tail too — without it
|
|
175
|
+
// long-command sessions would still nuke tmux on agent exit (#1756).
|
|
176
|
+
const writeCall = fs.writeFileSync.mock
|
|
177
|
+
.calls[0];
|
|
178
|
+
expect(writeCall[1]).toMatch(/exec "\$\{SHELL:-\/bin\/bash\}" -i/);
|
|
179
|
+
expect(mockExecFileCustom).toHaveBeenNthCalledWith(1, "tmux", [
|
|
180
|
+
"new-session",
|
|
181
|
+
"-d",
|
|
182
|
+
"-s",
|
|
183
|
+
"launch-long",
|
|
184
|
+
"-c",
|
|
185
|
+
"/tmp/ws",
|
|
186
|
+
expect.stringContaining("bash "),
|
|
187
|
+
], expectedTmuxOptions);
|
|
188
|
+
});
|
|
189
|
+
it("surfaces tmux new-session failures", async () => {
|
|
190
|
+
const runtime = create();
|
|
191
|
+
// new-session itself fails — no further tmux calls happen
|
|
192
|
+
mockTmuxError("new-session failed");
|
|
193
|
+
await expect(runtime.create({
|
|
194
|
+
sessionId: "fail-session",
|
|
195
|
+
workspacePath: "/tmp/ws",
|
|
196
|
+
launchCommand: "bad-command",
|
|
197
|
+
environment: {},
|
|
198
|
+
})).rejects.toThrow("new-session failed");
|
|
199
|
+
expect(mockExecFileCustom).toHaveBeenCalledTimes(1);
|
|
200
|
+
});
|
|
201
|
+
it("cleans up session if set-option fails", async () => {
|
|
202
|
+
const runtime = create();
|
|
203
|
+
// 1: new-session succeeds
|
|
204
|
+
mockTmuxSuccess();
|
|
205
|
+
// 2: set-option fails (e.g. tmux command timeout on a slow host)
|
|
206
|
+
mockTmuxError("set-option timed out");
|
|
207
|
+
// 3: kill-session (cleanup attempt)
|
|
208
|
+
mockTmuxSuccess();
|
|
209
|
+
await expect(runtime.create({
|
|
210
|
+
sessionId: "setopt-fail",
|
|
211
|
+
workspacePath: "/tmp/ws",
|
|
212
|
+
launchCommand: "echo hi",
|
|
213
|
+
environment: {},
|
|
214
|
+
})).rejects.toThrow('Failed to configure or launch session "setopt-fail"');
|
|
215
|
+
// kill-session must run so we don't leave an orphaned tmux session
|
|
216
|
+
expect(mockExecFileCustom).toHaveBeenCalledWith("tmux", ["kill-session", "-t", "setopt-fail"], expectedTmuxOptions);
|
|
217
|
+
});
|
|
218
|
+
it("rejects invalid session IDs with special characters", async () => {
|
|
219
|
+
const runtime = create();
|
|
220
|
+
await expect(runtime.create({
|
|
221
|
+
sessionId: "bad session!",
|
|
222
|
+
workspacePath: "/tmp/ws",
|
|
223
|
+
launchCommand: "echo",
|
|
224
|
+
environment: {},
|
|
225
|
+
})).rejects.toThrow('Invalid session ID "bad session!"');
|
|
226
|
+
});
|
|
227
|
+
it("rejects session IDs with dots", async () => {
|
|
228
|
+
const runtime = create();
|
|
229
|
+
await expect(runtime.create({
|
|
230
|
+
sessionId: "bad.session",
|
|
231
|
+
workspacePath: "/tmp/ws",
|
|
232
|
+
launchCommand: "echo",
|
|
233
|
+
environment: {},
|
|
234
|
+
})).rejects.toThrow("Invalid session ID");
|
|
235
|
+
});
|
|
236
|
+
it("accepts valid session IDs with hyphens and underscores", async () => {
|
|
237
|
+
const runtime = create();
|
|
238
|
+
mockTmuxSuccess();
|
|
239
|
+
mockTmuxSuccess();
|
|
240
|
+
const handle = await runtime.create({
|
|
241
|
+
sessionId: "valid-session_123",
|
|
242
|
+
workspacePath: "/tmp/ws",
|
|
243
|
+
launchCommand: "echo",
|
|
244
|
+
environment: {},
|
|
245
|
+
});
|
|
246
|
+
expect(handle.id).toBe("valid-session_123");
|
|
247
|
+
});
|
|
248
|
+
it("handles no environment (undefined)", async () => {
|
|
249
|
+
const runtime = create();
|
|
250
|
+
mockTmuxSuccess();
|
|
251
|
+
mockTmuxSuccess();
|
|
252
|
+
await runtime.create({
|
|
253
|
+
sessionId: "no-env",
|
|
254
|
+
workspacePath: "/tmp/ws",
|
|
255
|
+
launchCommand: "echo hi",
|
|
256
|
+
});
|
|
257
|
+
// First call should not contain -e flags
|
|
258
|
+
const firstCallArgs = mockExecFileCustom.mock.calls[0][1];
|
|
259
|
+
expect(firstCallArgs).toEqual([
|
|
260
|
+
"new-session",
|
|
261
|
+
"-d",
|
|
262
|
+
"-s",
|
|
263
|
+
"no-env",
|
|
264
|
+
"-c",
|
|
265
|
+
"/tmp/ws",
|
|
266
|
+
'echo hi\nexec "${SHELL:-/bin/bash}" -i',
|
|
267
|
+
]);
|
|
268
|
+
});
|
|
269
|
+
});
|
|
270
|
+
describe("runtime.destroy()", () => {
|
|
271
|
+
it("calls kill-session with the handle id", async () => {
|
|
272
|
+
const runtime = create();
|
|
273
|
+
const handle = makeHandle("destroy-test");
|
|
274
|
+
mockTmuxSuccess();
|
|
275
|
+
await runtime.destroy(handle);
|
|
276
|
+
expect(mockExecFileCustom).toHaveBeenCalledWith("tmux", ["kill-session", "-t", "destroy-test"], expectedTmuxOptions);
|
|
277
|
+
});
|
|
278
|
+
it("does not throw if session is already gone", async () => {
|
|
279
|
+
const runtime = create();
|
|
280
|
+
const handle = makeHandle("already-dead");
|
|
281
|
+
mockTmuxError("session not found: already-dead");
|
|
282
|
+
// Should not throw
|
|
283
|
+
await expect(runtime.destroy(handle)).resolves.toBeUndefined();
|
|
284
|
+
});
|
|
285
|
+
});
|
|
286
|
+
describe("runtime.sendMessage()", () => {
|
|
287
|
+
it("sends short text with send-keys -l (literal) + Enter", async () => {
|
|
288
|
+
const runtime = create();
|
|
289
|
+
const handle = makeHandle("msg-short");
|
|
290
|
+
// 1: send-keys C-u (clear), 2: send-keys -l text, 3: send-keys Enter
|
|
291
|
+
mockTmuxSuccess();
|
|
292
|
+
mockTmuxSuccess();
|
|
293
|
+
mockTmuxSuccess();
|
|
294
|
+
await runtime.sendMessage(handle, "hello world");
|
|
295
|
+
expect(mockExecFileCustom).toHaveBeenCalledTimes(3);
|
|
296
|
+
// Call 0: Clear partial input
|
|
297
|
+
expect(mockExecFileCustom).toHaveBeenNthCalledWith(1, "tmux", ["send-keys", "-t", "msg-short", "C-u"], expectedTmuxOptions);
|
|
298
|
+
// Call 1: Literal text
|
|
299
|
+
expect(mockExecFileCustom).toHaveBeenNthCalledWith(2, "tmux", ["send-keys", "-t", "msg-short", "-l", "hello world"], expectedTmuxOptions);
|
|
300
|
+
// Call 2: Enter
|
|
301
|
+
expect(mockExecFileCustom).toHaveBeenNthCalledWith(3, "tmux", ["send-keys", "-t", "msg-short", "Enter"], expectedTmuxOptions);
|
|
302
|
+
});
|
|
303
|
+
it("uses load-buffer + paste-buffer for long text (> 200 chars)", async () => {
|
|
304
|
+
const runtime = create();
|
|
305
|
+
const handle = makeHandle("msg-long");
|
|
306
|
+
const longText = "x".repeat(250);
|
|
307
|
+
// 1: C-u, 2: load-buffer, 3: paste-buffer, 4: unlinkSync (sync), 5: delete-buffer, 6: Enter
|
|
308
|
+
mockTmuxSuccess(); // C-u
|
|
309
|
+
mockTmuxSuccess(); // load-buffer
|
|
310
|
+
mockTmuxSuccess(); // paste-buffer
|
|
311
|
+
mockTmuxSuccess(); // delete-buffer (finally block)
|
|
312
|
+
mockTmuxSuccess(); // Enter
|
|
313
|
+
await runtime.sendMessage(handle, longText);
|
|
314
|
+
expect(mockExecFileCustom).toHaveBeenCalledTimes(5);
|
|
315
|
+
// Call 0: clear
|
|
316
|
+
expect(mockExecFileCustom).toHaveBeenNthCalledWith(1, "tmux", ["send-keys", "-t", "msg-long", "C-u"], expectedTmuxOptions);
|
|
317
|
+
// Call 1: load-buffer with named buffer
|
|
318
|
+
expect(mockExecFileCustom).toHaveBeenNthCalledWith(2, "tmux", [
|
|
319
|
+
"load-buffer",
|
|
320
|
+
"-b",
|
|
321
|
+
"ao-test-uuid-1234",
|
|
322
|
+
expect.stringContaining("ao-send-test-uuid-1234.txt"),
|
|
323
|
+
], expectedTmuxOptions);
|
|
324
|
+
// Call 2: paste-buffer
|
|
325
|
+
expect(mockExecFileCustom).toHaveBeenNthCalledWith(3, "tmux", ["paste-buffer", "-b", "ao-test-uuid-1234", "-t", "msg-long", "-d"], expectedTmuxOptions);
|
|
326
|
+
// Verify writeFileSync was called with the message
|
|
327
|
+
expect(fs.writeFileSync).toHaveBeenCalledWith(expect.stringContaining("ao-send-test-uuid-1234.txt"), longText, { encoding: "utf-8", mode: 0o600 });
|
|
328
|
+
// Verify unlinkSync was called for cleanup
|
|
329
|
+
expect(fs.unlinkSync).toHaveBeenCalledWith(expect.stringContaining("ao-send-test-uuid-1234.txt"));
|
|
330
|
+
});
|
|
331
|
+
it("uses load-buffer for multiline text", async () => {
|
|
332
|
+
const runtime = create();
|
|
333
|
+
const handle = makeHandle("msg-multi");
|
|
334
|
+
mockTmuxSuccess(); // C-u
|
|
335
|
+
mockTmuxSuccess(); // load-buffer
|
|
336
|
+
mockTmuxSuccess(); // paste-buffer
|
|
337
|
+
mockTmuxSuccess(); // delete-buffer (finally)
|
|
338
|
+
mockTmuxSuccess(); // Enter
|
|
339
|
+
await runtime.sendMessage(handle, "line1\nline2\nline3");
|
|
340
|
+
// Should use buffer path, not send-keys -l
|
|
341
|
+
expect(mockExecFileCustom).toHaveBeenNthCalledWith(2, "tmux", [
|
|
342
|
+
"load-buffer",
|
|
343
|
+
"-b",
|
|
344
|
+
"ao-test-uuid-1234",
|
|
345
|
+
expect.stringContaining("ao-send-test-uuid-1234.txt"),
|
|
346
|
+
], expectedTmuxOptions);
|
|
347
|
+
expect(fs.writeFileSync).toHaveBeenCalledWith(expect.stringContaining("ao-send-test-uuid-1234.txt"), "line1\nline2\nline3", { encoding: "utf-8", mode: 0o600 });
|
|
348
|
+
});
|
|
349
|
+
it("cleans up buffer and temp file on paste failure", async () => {
|
|
350
|
+
const runtime = create();
|
|
351
|
+
const handle = makeHandle("msg-fail");
|
|
352
|
+
const longText = "y".repeat(250);
|
|
353
|
+
mockTmuxSuccess(); // C-u
|
|
354
|
+
mockTmuxSuccess(); // load-buffer succeeds
|
|
355
|
+
mockTmuxError("paste-buffer failed"); // paste-buffer fails
|
|
356
|
+
// finally block:
|
|
357
|
+
// unlinkSync is sync (mocked)
|
|
358
|
+
mockTmuxSuccess(); // delete-buffer in finally
|
|
359
|
+
// After finally, the error propagates — no Enter call
|
|
360
|
+
await expect(runtime.sendMessage(handle, longText)).rejects.toThrow("paste-buffer failed");
|
|
361
|
+
// unlinkSync should still be called for temp file cleanup
|
|
362
|
+
expect(fs.unlinkSync).toHaveBeenCalledWith(expect.stringContaining("ao-send-test-uuid-1234.txt"));
|
|
363
|
+
// delete-buffer should be called in finally block
|
|
364
|
+
expect(mockExecFileCustom).toHaveBeenCalledWith("tmux", ["delete-buffer", "-b", "ao-test-uuid-1234"], expectedTmuxOptions);
|
|
365
|
+
});
|
|
366
|
+
});
|
|
367
|
+
describe("runtime.getOutput()", () => {
|
|
368
|
+
it("calls capture-pane with correct args and default lines", async () => {
|
|
369
|
+
const runtime = create();
|
|
370
|
+
const handle = makeHandle("output-test");
|
|
371
|
+
mockTmuxSuccess("some output\nfrom tmux");
|
|
372
|
+
const output = await runtime.getOutput(handle);
|
|
373
|
+
expect(output).toBe("some output\nfrom tmux");
|
|
374
|
+
expect(mockExecFileCustom).toHaveBeenCalledWith("tmux", ["capture-pane", "-t", "output-test", "-p", "-S", "-50"], expectedTmuxOptions);
|
|
375
|
+
});
|
|
376
|
+
it("passes custom line count", async () => {
|
|
377
|
+
const runtime = create();
|
|
378
|
+
const handle = makeHandle("output-custom");
|
|
379
|
+
mockTmuxSuccess("output");
|
|
380
|
+
await runtime.getOutput(handle, 100);
|
|
381
|
+
expect(mockExecFileCustom).toHaveBeenCalledWith("tmux", ["capture-pane", "-t", "output-custom", "-p", "-S", "-100"], expectedTmuxOptions);
|
|
382
|
+
});
|
|
383
|
+
it("returns empty string on error", async () => {
|
|
384
|
+
const runtime = create();
|
|
385
|
+
const handle = makeHandle("output-err");
|
|
386
|
+
mockTmuxError("session not found");
|
|
387
|
+
const output = await runtime.getOutput(handle);
|
|
388
|
+
expect(output).toBe("");
|
|
389
|
+
});
|
|
390
|
+
});
|
|
391
|
+
describe("runtime.isAlive()", () => {
|
|
392
|
+
it("returns true when has-session succeeds", async () => {
|
|
393
|
+
const runtime = create();
|
|
394
|
+
const handle = makeHandle("alive-test");
|
|
395
|
+
mockTmuxSuccess();
|
|
396
|
+
const alive = await runtime.isAlive(handle);
|
|
397
|
+
expect(alive).toBe(true);
|
|
398
|
+
expect(mockExecFileCustom).toHaveBeenCalledWith("tmux", ["has-session", "-t", "alive-test"], expectedTmuxOptions);
|
|
399
|
+
});
|
|
400
|
+
it("returns false when has-session fails", async () => {
|
|
401
|
+
const runtime = create();
|
|
402
|
+
const handle = makeHandle("dead-test");
|
|
403
|
+
mockTmuxError("session not found");
|
|
404
|
+
const alive = await runtime.isAlive(handle);
|
|
405
|
+
expect(alive).toBe(false);
|
|
406
|
+
});
|
|
407
|
+
});
|
|
408
|
+
describe("runtime.getMetrics()", () => {
|
|
409
|
+
it("returns uptimeMs based on createdAt", async () => {
|
|
410
|
+
const runtime = create();
|
|
411
|
+
const now = Date.now();
|
|
412
|
+
const handle = makeHandle("metrics-test", now - 5000);
|
|
413
|
+
const metrics = await runtime.getMetrics(handle);
|
|
414
|
+
// uptimeMs should be approximately 5000ms (allow some wiggle room)
|
|
415
|
+
expect(metrics.uptimeMs).toBeGreaterThanOrEqual(5000);
|
|
416
|
+
expect(metrics.uptimeMs).toBeLessThan(6000);
|
|
417
|
+
});
|
|
418
|
+
it("handles missing createdAt by using Date.now()", async () => {
|
|
419
|
+
const runtime = create();
|
|
420
|
+
const handle = {
|
|
421
|
+
id: "metrics-no-created",
|
|
422
|
+
runtimeName: "tmux",
|
|
423
|
+
data: {},
|
|
424
|
+
};
|
|
425
|
+
const metrics = await runtime.getMetrics(handle);
|
|
426
|
+
// uptimeMs should be very close to 0 since createdAt defaults to Date.now()
|
|
427
|
+
expect(metrics.uptimeMs).toBeGreaterThanOrEqual(0);
|
|
428
|
+
expect(metrics.uptimeMs).toBeLessThan(1000);
|
|
429
|
+
});
|
|
430
|
+
});
|
|
431
|
+
describe("runtime.getAttachInfo()", () => {
|
|
432
|
+
it("returns tmux type and attach command", async () => {
|
|
433
|
+
const runtime = create();
|
|
434
|
+
const handle = makeHandle("attach-test");
|
|
435
|
+
const info = await runtime.getAttachInfo(handle);
|
|
436
|
+
expect(info).toEqual({
|
|
437
|
+
type: "tmux",
|
|
438
|
+
target: "attach-test",
|
|
439
|
+
command: "tmux attach -t attach-test",
|
|
440
|
+
});
|
|
441
|
+
});
|
|
442
|
+
});
|
|
443
|
+
describe("runtime.preflight()", () => {
|
|
444
|
+
it("resolves when `tmux -V` succeeds", async () => {
|
|
445
|
+
mockTmuxSuccess("tmux 3.4");
|
|
446
|
+
const runtime = create();
|
|
447
|
+
await expect(runtime.preflight({})).resolves.toBeUndefined();
|
|
448
|
+
expect(mockExecFileCustom).toHaveBeenCalledWith("tmux", ["-V"], expectedTmuxOptions);
|
|
449
|
+
});
|
|
450
|
+
it("throws with platform-specific install hint when tmux is missing", async () => {
|
|
451
|
+
mockTmuxError("ENOENT");
|
|
452
|
+
const runtime = create();
|
|
453
|
+
const err = (await runtime.preflight({}).catch((e) => e));
|
|
454
|
+
expect(err).toBeInstanceOf(Error);
|
|
455
|
+
expect(err.message).toContain("tmux is not installed");
|
|
456
|
+
expect(err.message).toContain("Install it:");
|
|
457
|
+
// Hint must include something runnable for the host platform.
|
|
458
|
+
if (process.platform === "darwin")
|
|
459
|
+
expect(err.message).toContain("brew install tmux");
|
|
460
|
+
else if (process.platform === "win32")
|
|
461
|
+
expect(err.message).toContain("WSL");
|
|
462
|
+
else
|
|
463
|
+
expect(err.message).toMatch(/apt install tmux|dnf install tmux/);
|
|
464
|
+
});
|
|
465
|
+
});
|
|
466
|
+
//# sourceMappingURL=index.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.test.js","sourceRoot":"","sources":["../../src/__tests__/index.test.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type Runtime } from "@made-by-moonlight/athene-core";
|
|
2
|
+
export declare const manifest: {
|
|
3
|
+
name: string;
|
|
4
|
+
slot: "runtime";
|
|
5
|
+
description: string;
|
|
6
|
+
version: string;
|
|
7
|
+
};
|
|
8
|
+
export declare function create(): Runtime;
|
|
9
|
+
declare const _default: {
|
|
10
|
+
manifest: {
|
|
11
|
+
name: string;
|
|
12
|
+
slot: "runtime";
|
|
13
|
+
description: string;
|
|
14
|
+
version: string;
|
|
15
|
+
};
|
|
16
|
+
create: typeof create;
|
|
17
|
+
};
|
|
18
|
+
export default _default;
|
|
19
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAOA,OAAO,EAEL,KAAK,OAAO,EAMb,MAAM,gCAAgC,CAAC;AAKxC,eAAO,MAAM,QAAQ;;;;;CAKpB,CAAC;AA4CF,wBAAgB,MAAM,IAAI,OAAO,CAwKhC;;;;;;;;;;AAED,wBAAoE"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { promisify } from "node:util";
|
|
3
|
+
import { setTimeout as sleep } from "node:timers/promises";
|
|
4
|
+
import { randomUUID } from "node:crypto";
|
|
5
|
+
import { writeFileSync, unlinkSync } from "node:fs";
|
|
6
|
+
import { tmpdir } from "node:os";
|
|
7
|
+
import { join } from "node:path";
|
|
8
|
+
import { shellEscape, } from "@made-by-moonlight/athene-core";
|
|
9
|
+
const execFileAsync = promisify(execFile);
|
|
10
|
+
const TMUX_COMMAND_TIMEOUT_MS = 5_000;
|
|
11
|
+
export const manifest = {
|
|
12
|
+
name: "tmux",
|
|
13
|
+
slot: "runtime",
|
|
14
|
+
description: "Runtime plugin: tmux sessions",
|
|
15
|
+
version: "0.1.0",
|
|
16
|
+
};
|
|
17
|
+
/** Only allow safe characters in session IDs */
|
|
18
|
+
const SAFE_SESSION_ID = /^[a-zA-Z0-9_-]+$/;
|
|
19
|
+
function assertValidSessionId(id) {
|
|
20
|
+
if (!SAFE_SESSION_ID.test(id)) {
|
|
21
|
+
throw new Error(`Invalid session ID "${id}": must match ${SAFE_SESSION_ID}`);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Shell snippet appended after the agent launch command so the tmux pane
|
|
26
|
+
* (and therefore the tmux session) survives agent exit. Without this, the
|
|
27
|
+
* pane closes when the agent process exits, the only window goes away, and
|
|
28
|
+
* the whole tmux session dies — leaving the dashboard with a phantom
|
|
29
|
+
* "runtime lost" state and the user with no way to do anything in that
|
|
30
|
+
* workspace (issue #1756).
|
|
31
|
+
*
|
|
32
|
+
* `exec` replaces the wrapping sh/bash with the user's interactive shell,
|
|
33
|
+
* so the lifecycle manager still detects agent termination via
|
|
34
|
+
* `agent.isProcessRunning` and transitions the session correctly.
|
|
35
|
+
*/
|
|
36
|
+
const KEEP_ALIVE_SHELL = `exec "\${SHELL:-/bin/bash}" -i`;
|
|
37
|
+
function withKeepAliveShell(command) {
|
|
38
|
+
return `${command.replace(/\n+$/, "")}\n${KEEP_ALIVE_SHELL}`;
|
|
39
|
+
}
|
|
40
|
+
function writeLaunchScript(command) {
|
|
41
|
+
const scriptPath = join(tmpdir(), `ao-launch-${randomUUID()}.sh`);
|
|
42
|
+
const content = `#!/usr/bin/env bash\nrm -- "$0" 2>/dev/null || true\n${withKeepAliveShell(command)}\n`;
|
|
43
|
+
writeFileSync(scriptPath, content, { encoding: "utf-8", mode: 0o700 });
|
|
44
|
+
return `bash ${shellEscape(scriptPath)}`;
|
|
45
|
+
}
|
|
46
|
+
/** Run a tmux command and return stdout */
|
|
47
|
+
async function tmux(...args) {
|
|
48
|
+
const { stdout } = await execFileAsync("tmux", args, {
|
|
49
|
+
timeout: TMUX_COMMAND_TIMEOUT_MS,
|
|
50
|
+
});
|
|
51
|
+
return stdout.trimEnd();
|
|
52
|
+
}
|
|
53
|
+
export function create() {
|
|
54
|
+
return {
|
|
55
|
+
name: "tmux",
|
|
56
|
+
async create(config) {
|
|
57
|
+
assertValidSessionId(config.sessionId);
|
|
58
|
+
const sessionName = config.sessionId;
|
|
59
|
+
// Build environment flags: -e KEY=VALUE for each env var
|
|
60
|
+
const envArgs = [];
|
|
61
|
+
for (const [key, value] of Object.entries(config.environment ?? {})) {
|
|
62
|
+
envArgs.push("-e", `${key}=${value}`);
|
|
63
|
+
}
|
|
64
|
+
// Re-export PATH inside the launch script. macOS zsh runs path_helper
|
|
65
|
+
// during shell startup which resets PATH, wiping entries set via tmux -e.
|
|
66
|
+
// Including the export in the launched shell command avoids terminal
|
|
67
|
+
// buffer issues with long PATH values (1000+ chars).
|
|
68
|
+
const pathValue = config.environment?.["PATH"];
|
|
69
|
+
let launchCommand = config.launchCommand;
|
|
70
|
+
if (pathValue) {
|
|
71
|
+
// Use printf with JSON-escaped value to avoid shell injection if
|
|
72
|
+
// PATH contains single quotes or other shell metacharacters.
|
|
73
|
+
launchCommand = `export PATH=$(printf '%s' ${JSON.stringify(pathValue)})\n${launchCommand}`;
|
|
74
|
+
}
|
|
75
|
+
// Start the launch command as the pane's initial command instead of
|
|
76
|
+
// typing into a live shell. A dashboard attach can trigger terminal
|
|
77
|
+
// device responses; if those race with tmux send-keys, they become
|
|
78
|
+
// literal shell input and corrupt the launch path. The keep-alive
|
|
79
|
+
// tail is appended in both code paths — see KEEP_ALIVE_SHELL.
|
|
80
|
+
const shellCommand = launchCommand.length > 200
|
|
81
|
+
? writeLaunchScript(launchCommand)
|
|
82
|
+
: withKeepAliveShell(launchCommand);
|
|
83
|
+
await tmux("new-session", "-d", "-s", sessionName, "-c", config.workspacePath, ...envArgs, shellCommand);
|
|
84
|
+
// Hide the tmux status bar — sessions are embedded in the web terminal,
|
|
85
|
+
// and the green bar at the bottom is visual noise (and racy with the
|
|
86
|
+
// web layer's own set-option call, which only fires on WebSocket connect).
|
|
87
|
+
// Kill the session if this fails so we don't leave an orphaned tmux process.
|
|
88
|
+
try {
|
|
89
|
+
await tmux("set-option", "-t", sessionName, "status", "off");
|
|
90
|
+
}
|
|
91
|
+
catch (err) {
|
|
92
|
+
try {
|
|
93
|
+
await tmux("kill-session", "-t", sessionName);
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
// Best-effort cleanup
|
|
97
|
+
}
|
|
98
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
99
|
+
throw new Error(`Failed to configure or launch session "${sessionName}": ${msg}`, {
|
|
100
|
+
cause: err,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
return {
|
|
104
|
+
id: sessionName,
|
|
105
|
+
runtimeName: "tmux",
|
|
106
|
+
data: {
|
|
107
|
+
createdAt: Date.now(),
|
|
108
|
+
workspacePath: config.workspacePath,
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
},
|
|
112
|
+
async destroy(handle) {
|
|
113
|
+
try {
|
|
114
|
+
await tmux("kill-session", "-t", handle.id);
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
// Session may already be dead — that's fine
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
async sendMessage(handle, message) {
|
|
121
|
+
// Clear any partial input
|
|
122
|
+
await tmux("send-keys", "-t", handle.id, "C-u");
|
|
123
|
+
// For long or multiline messages, use load-buffer + paste-buffer
|
|
124
|
+
// Use randomUUID to avoid temp file collisions on concurrent sends
|
|
125
|
+
if (message.includes("\n") || message.length > 200) {
|
|
126
|
+
const bufferName = `ao-${randomUUID()}`;
|
|
127
|
+
const tmpPath = join(tmpdir(), `ao-send-${randomUUID()}.txt`);
|
|
128
|
+
writeFileSync(tmpPath, message, { encoding: "utf-8", mode: 0o600 });
|
|
129
|
+
try {
|
|
130
|
+
await tmux("load-buffer", "-b", bufferName, tmpPath);
|
|
131
|
+
await tmux("paste-buffer", "-b", bufferName, "-t", handle.id, "-d");
|
|
132
|
+
}
|
|
133
|
+
finally {
|
|
134
|
+
// Clean up temp file and tmux buffer (in case paste-buffer failed
|
|
135
|
+
// and the -d flag didn't delete it)
|
|
136
|
+
try {
|
|
137
|
+
unlinkSync(tmpPath);
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
// ignore cleanup errors
|
|
141
|
+
}
|
|
142
|
+
try {
|
|
143
|
+
await tmux("delete-buffer", "-b", bufferName);
|
|
144
|
+
}
|
|
145
|
+
catch {
|
|
146
|
+
// Buffer may already be deleted by -d flag — that's fine
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
// Use -l (literal) so text like "Enter" or "Space" isn't interpreted
|
|
152
|
+
// as tmux key names
|
|
153
|
+
await tmux("send-keys", "-t", handle.id, "-l", message);
|
|
154
|
+
}
|
|
155
|
+
// Small delay to let tmux process the pasted text before pressing Enter.
|
|
156
|
+
// Without this, Enter can arrive before the text is fully rendered.
|
|
157
|
+
await sleep(300);
|
|
158
|
+
await tmux("send-keys", "-t", handle.id, "Enter");
|
|
159
|
+
},
|
|
160
|
+
async getOutput(handle, lines = 50) {
|
|
161
|
+
try {
|
|
162
|
+
return await tmux("capture-pane", "-t", handle.id, "-p", "-S", `-${lines}`);
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
return "";
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
async isAlive(handle) {
|
|
169
|
+
try {
|
|
170
|
+
await tmux("has-session", "-t", handle.id);
|
|
171
|
+
return true;
|
|
172
|
+
}
|
|
173
|
+
catch {
|
|
174
|
+
return false;
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
async getMetrics(handle) {
|
|
178
|
+
const createdAt = handle.data.createdAt ?? Date.now();
|
|
179
|
+
return {
|
|
180
|
+
uptimeMs: Date.now() - createdAt,
|
|
181
|
+
};
|
|
182
|
+
},
|
|
183
|
+
async getAttachInfo(handle) {
|
|
184
|
+
return {
|
|
185
|
+
type: "tmux",
|
|
186
|
+
target: handle.id,
|
|
187
|
+
command: `tmux attach -t ${handle.id}`,
|
|
188
|
+
};
|
|
189
|
+
},
|
|
190
|
+
async preflight() {
|
|
191
|
+
try {
|
|
192
|
+
await execFileAsync("tmux", ["-V"], { timeout: TMUX_COMMAND_TIMEOUT_MS });
|
|
193
|
+
}
|
|
194
|
+
catch {
|
|
195
|
+
const hint = process.platform === "darwin"
|
|
196
|
+
? "brew install tmux"
|
|
197
|
+
: process.platform === "win32"
|
|
198
|
+
? "tmux is not available on Windows. Use WSL: wsl --install, then: sudo apt install tmux"
|
|
199
|
+
: "sudo apt install tmux (Debian/Ubuntu) or sudo dnf install tmux (Fedora)";
|
|
200
|
+
throw new Error(`tmux is not installed. Install it: ${hint}`);
|
|
201
|
+
}
|
|
202
|
+
},
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
export default { manifest, create };
|
|
206
|
+
//# 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,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@made-by-moonlight/athene-plugin-runtime-tmux",
|
|
3
|
+
"version": "0.9.1",
|
|
4
|
+
"description": "Runtime plugin: tmux sessions",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "dist/index.js",
|
|
8
|
+
"types": "dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "https://github.com/slievr/Athene.git",
|
|
21
|
+
"directory": "packages/plugins/runtime-tmux"
|
|
22
|
+
},
|
|
23
|
+
"homepage": "https://github.com/slievr/Athene",
|
|
24
|
+
"bugs": {
|
|
25
|
+
"url": "https://github.com/slievr/Athene/issues"
|
|
26
|
+
},
|
|
27
|
+
"engines": {
|
|
28
|
+
"node": ">=20.0.0"
|
|
29
|
+
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@made-by-moonlight/athene-core": "0.9.1"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@types/node": "^25.2.3",
|
|
35
|
+
"typescript": "^5.7.0",
|
|
36
|
+
"vitest": "^3.0.0"
|
|
37
|
+
},
|
|
38
|
+
"publishConfig": {
|
|
39
|
+
"access": "public"
|
|
40
|
+
},
|
|
41
|
+
"scripts": {
|
|
42
|
+
"build": "tsc",
|
|
43
|
+
"typecheck": "tsc --noEmit",
|
|
44
|
+
"test": "vitest run",
|
|
45
|
+
"test:watch": "vitest",
|
|
46
|
+
"clean": "rm -rf dist"
|
|
47
|
+
}
|
|
48
|
+
}
|