@rynx-ai/runtime 0.1.11-beta.2 → 0.1.11-beta.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/claude/models.d.ts +0 -5
- package/dist/claude/models.js +1 -7
- package/dist/claude/native-integration.d.ts +35 -7
- package/dist/claude/native-integration.js +204 -32
- package/dist/claude/session-status.d.ts +39 -0
- package/dist/claude/session-status.js +163 -0
- package/dist/codex-app-server/forwarder.d.ts +66 -2
- package/dist/codex-app-server/forwarder.js +329 -11
- package/dist/codex-app-server/mapping.d.ts +2 -0
- package/dist/codex-app-server/mapping.js +97 -23
- package/dist/codex-app-server/mcp-startup.d.ts +13 -0
- package/dist/codex-app-server/mcp-startup.js +63 -0
- package/dist/codex-app-server/protocol.d.ts +8 -5
- package/dist/codex-app-server/ws-channel.js +19 -19
- package/dist/codex-home.js +2 -4
- package/dist/host.d.ts +28 -7
- package/dist/host.js +356 -92
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/models-catalog.d.ts +2 -1
- package/dist/models-catalog.js +82 -3
- package/dist/runner/child.d.ts +22 -6
- package/dist/runner/child.js +237 -30
- package/dist/runner/manager.d.ts +87 -4
- package/dist/runner/manager.js +610 -56
- package/dist/runner/protocol.d.ts +11 -15
- package/dist/runner/startup-policy.d.ts +4 -0
- package/dist/runner/startup-policy.js +5 -0
- package/dist/terminal/claude-tui.d.ts +3 -1
- package/dist/terminal/claude-tui.js +3 -1
- package/dist/terminal/tmux.d.ts +29 -2
- package/dist/terminal/tmux.js +122 -14
- package/package.json +4 -3
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
* (`live.ensure` / `inject` / `live.interrupt`) plus per-thread capabilities and
|
|
7
7
|
* terminal attachment; the reply channels mirror each request's `reqId`.
|
|
8
8
|
*/
|
|
9
|
-
import { AgentRuntimeError, type ResolvedExecutionSnapshot, type RuntimeUserInput, type SessionInteractionResolution, type SessionEvent, type SessionWorkspaceSnapshot } from "@rynx-ai/core";
|
|
9
|
+
import { AgentRuntimeError, type InjectOutcome, type ResolvedExecutionSnapshot, type RuntimeUserInput, type SessionInteractionResolution, type SessionEvent, type SessionWorkspaceSnapshot } from "@rynx-ai/core";
|
|
10
|
+
export type { InjectOutcome } from "@rynx-ai/core";
|
|
10
11
|
import type { ResolveInteractionResult } from "../interactions.js";
|
|
11
12
|
/** A runtime error flattened for the wire; rebuilt parent-side as `AgentRuntimeError`. */
|
|
12
13
|
export interface WireError {
|
|
@@ -22,13 +23,6 @@ export type TerminalRole = "owner" | "read-only";
|
|
|
22
23
|
/** Stable reason for a terminal attach failure. `terminal_not_live` is an
|
|
23
24
|
* expected, retryable absence; `terminal_open_failed` is an infrastructure error. */
|
|
24
25
|
export type TerminalOpenErrorCode = "terminal_not_live" | "terminal_open_failed";
|
|
25
|
-
/** Outcome of an `inject`. `injected` = the app-server / tmux accepted the turn.
|
|
26
|
-
* `notLive` = no live forwarder for this session (caller may use the run path).
|
|
27
|
-
* `notReady` = live but not ready within the park window; `failed` = injection
|
|
28
|
-
* threw. For BOTH notReady and failed the caller reports an error and MUST NOT
|
|
29
|
-
* re-run the turn (re-running would double-write alongside the forwarder).
|
|
30
|
-
* Mirrors reference implementation's "inject failure ⇒ response.failed, never re-run locally". */
|
|
31
|
-
export type InjectOutcome = "injected" | "notLive" | "notReady" | "failed";
|
|
32
26
|
/** Parent → child. Terminal PTY bytes ride `term.input` base64-encoded so the
|
|
33
27
|
* NDJSON line framing stays intact (raw bytes contain newlines). */
|
|
34
28
|
export type ToChild = {
|
|
@@ -70,8 +64,9 @@ export type ToChild = {
|
|
|
70
64
|
}
|
|
71
65
|
/** Eagerly bring up a session's live codex TUI + forwarder (codex-native), so
|
|
72
66
|
* its turns mirror to the bus regardless of whether the web has attached the
|
|
73
|
-
* terminal.
|
|
74
|
-
* the thread
|
|
67
|
+
* terminal. For Codex-lineage fresh sessions, `live.ready` acknowledges that
|
|
68
|
+
* the pane and observer exist; background thread discovery races the
|
|
69
|
+
* executor's bridge wait. */
|
|
75
70
|
| {
|
|
76
71
|
t: "live.ensure";
|
|
77
72
|
reqId: string;
|
|
@@ -160,8 +155,9 @@ export type FromChild = {
|
|
|
160
155
|
execution: ResolvedExecutionSnapshot;
|
|
161
156
|
parentSessionId?: string;
|
|
162
157
|
}
|
|
163
|
-
/** Result of a `live.ensure`: `ok`
|
|
164
|
-
*
|
|
158
|
+
/** Result of a `live.ensure`: for Codex-lineage sessions `ok` means the pane
|
|
159
|
+
* and observer started; thread discovery continues in parallel with
|
|
160
|
+
* injection. Other Providers retain their own readiness gate. */
|
|
165
161
|
| {
|
|
166
162
|
t: "live.ready";
|
|
167
163
|
reqId: string;
|
|
@@ -169,9 +165,9 @@ export type FromChild = {
|
|
|
169
165
|
ok: boolean;
|
|
170
166
|
error?: string;
|
|
171
167
|
}
|
|
172
|
-
/** Result of an `inject`: `outcome` distinguishes
|
|
173
|
-
*
|
|
174
|
-
*
|
|
168
|
+
/** Result of an `inject`: `outcome` distinguishes a new turn, an active-turn
|
|
169
|
+
* steer, and failures so the caller never falls back to a second output path.
|
|
170
|
+
* Echoes the request `reqId`. */
|
|
175
171
|
| {
|
|
176
172
|
t: "injected";
|
|
177
173
|
reqId: string;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { AgentRuntimeId } from "@rynx-ai/core";
|
|
2
|
+
/** Current live providers are native CLI sessions. Keep this explicit so a
|
|
3
|
+
* future non-native provider does not silently inherit their startup budget. */
|
|
4
|
+
export declare function isManagedNativeProvider(provider: AgentRuntimeId | undefined): provider is AgentRuntimeId;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** Current live providers are native CLI sessions. Keep this explicit so a
|
|
2
|
+
* future non-native provider does not silently inherit their startup budget. */
|
|
3
|
+
export function isManagedNativeProvider(provider) {
|
|
4
|
+
return provider === "codex" || provider === "traex" || provider === "claude";
|
|
5
|
+
}
|
|
@@ -18,6 +18,8 @@ export interface ClaudeTuiArgs {
|
|
|
18
18
|
settingSources?: string;
|
|
19
19
|
/** Launch model (`--model`); omit to use claude's default. */
|
|
20
20
|
model?: string;
|
|
21
|
+
/** Native Claude Code effort (`--effort`); omit to use its default. */
|
|
22
|
+
reasoningEffort?: string;
|
|
21
23
|
/** Agent instructions appended to Claude Code's native system prompt. */
|
|
22
24
|
appendSystemPrompt?: string;
|
|
23
25
|
/** Resume a specific prior claude session (`--resume <id>`). */
|
|
@@ -36,4 +38,4 @@ export interface ClaudeTuiArgs {
|
|
|
36
38
|
* Build the `claude` argv for an interactive, co-drivable TUI:
|
|
37
39
|
* `[..extra] [--resume <id>] [--model <m>] [--append-system-prompt <text>] --settings <json>`.
|
|
38
40
|
*/
|
|
39
|
-
export declare function buildClaudeTuiArgs({ settingsJson, settingSources, model, appendSystemPrompt, resume, forkSession, sessionId, additionalDirs, extraArgs, }: ClaudeTuiArgs): string[];
|
|
41
|
+
export declare function buildClaudeTuiArgs({ settingsJson, settingSources, model, reasoningEffort, appendSystemPrompt, resume, forkSession, sessionId, additionalDirs, extraArgs, }: ClaudeTuiArgs): string[];
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Build the `claude` argv for an interactive, co-drivable TUI:
|
|
3
3
|
* `[..extra] [--resume <id>] [--model <m>] [--append-system-prompt <text>] --settings <json>`.
|
|
4
4
|
*/
|
|
5
|
-
export function buildClaudeTuiArgs({ settingsJson, settingSources, model, appendSystemPrompt, resume, forkSession = false, sessionId, additionalDirs = [], extraArgs = [], }) {
|
|
5
|
+
export function buildClaudeTuiArgs({ settingsJson, settingSources, model, reasoningEffort, appendSystemPrompt, resume, forkSession = false, sessionId, additionalDirs = [], extraArgs = [], }) {
|
|
6
6
|
const args = [...extraArgs];
|
|
7
7
|
if (additionalDirs.length > 0)
|
|
8
8
|
args.push("--add-dir", ...additionalDirs);
|
|
@@ -16,6 +16,8 @@ export function buildClaudeTuiArgs({ settingsJson, settingSources, model, append
|
|
|
16
16
|
args.push("--session-id", sessionId);
|
|
17
17
|
if (model)
|
|
18
18
|
args.push("--model", model);
|
|
19
|
+
if (reasoningEffort)
|
|
20
|
+
args.push("--effort", reasoningEffort);
|
|
19
21
|
if (appendSystemPrompt)
|
|
20
22
|
args.push("--append-system-prompt", appendSystemPrompt);
|
|
21
23
|
args.push("--settings", settingsJson);
|
package/dist/terminal/tmux.d.ts
CHANGED
|
@@ -43,6 +43,29 @@ export interface TmuxTerminalOptions {
|
|
|
43
43
|
/** Injectable for tests; defaults to the real `tmux` binary path. */
|
|
44
44
|
tmuxBin?: string;
|
|
45
45
|
}
|
|
46
|
+
/** Resolve the exact tmux executable selected by the current Rynx distribution. */
|
|
47
|
+
export declare function resolveTmuxBin(explicit?: string, env?: NodeJS.ProcessEnv): string;
|
|
48
|
+
/** Deterministic private socket owned by one terminal name. Parent-side
|
|
49
|
+
* shutdown uses the same mapping when the runner child is too wedged to clean
|
|
50
|
+
* up its own tmux server. */
|
|
51
|
+
export declare function tmuxSocketPath(name: string): string;
|
|
52
|
+
/** Read tmux's own output-activity clock for a private terminal window.
|
|
53
|
+
*
|
|
54
|
+
* `#{window_activity}` is an epoch timestamp updated by tmux whenever the pane
|
|
55
|
+
* emits bytes. It is deliberately queried out-of-process rather than inferred
|
|
56
|
+
* from Rynx's forwarded PTY stream: the forwarder/status path is exactly what
|
|
57
|
+
* may have stalled when the idle reaper needs an independent liveness signal.
|
|
58
|
+
* Missing servers, command failures, timeouts, and unparseable output are all
|
|
59
|
+
* treated as unknown (`null`). */
|
|
60
|
+
export declare function tmuxWindowActivityAt(name: string, tmuxBin?: string): Promise<number | null>;
|
|
61
|
+
/** Whether tmux itself reports an attached client for this private terminal.
|
|
62
|
+
* This remains authoritative if a parent-side attachment bookkeeping edge was
|
|
63
|
+
* missed during a transport failure. */
|
|
64
|
+
export declare function tmuxHasAttachedClient(name: string, tmuxBin?: string): Promise<boolean>;
|
|
65
|
+
/** Kill one private tmux server and verify it no longer answers. Missing
|
|
66
|
+
* sockets are already stopped; an existing socket with an unusable tmux
|
|
67
|
+
* command is unproven and returns false. */
|
|
68
|
+
export declare function terminateTmuxServer(name: string, tmuxBin?: string): boolean;
|
|
46
69
|
/** Is a usable `tmux` on PATH? Cheap probe for the capability gate. */
|
|
47
70
|
export declare function isTmuxAvailable(tmuxBin?: string): boolean;
|
|
48
71
|
export declare class TmuxTerminal {
|
|
@@ -102,7 +125,9 @@ export declare class TmuxTerminal {
|
|
|
102
125
|
* there stalls the runner child's event loop (freezing the PTY stream → the
|
|
103
126
|
* terminal appears "stuck"). reference implementation's `_tmux_session_alive` uses an async
|
|
104
127
|
* subprocess + timeout for exactly this reason. */
|
|
105
|
-
|
|
128
|
+
isAliveAsync(): Promise<boolean>;
|
|
129
|
+
/** PID of the process currently owning the pane. */
|
|
130
|
+
panePid(): number | undefined;
|
|
106
131
|
/** Type literal text into the pane (agent injection / co-drive from a
|
|
107
132
|
* non-PTY caller). `-l` sends the text literally rather than as key names. */
|
|
108
133
|
sendKeys(text: string): void;
|
|
@@ -138,7 +163,9 @@ export declare class TmuxTerminal {
|
|
|
138
163
|
paste(text: string, bufferName?: string): void;
|
|
139
164
|
/**
|
|
140
165
|
* Attach a client. `role: "read-only"` passes tmux `-r` so the viewer cannot
|
|
141
|
-
* type
|
|
166
|
+
* type and `ignore-size` so even its initial PTY dimensions cannot resize the
|
|
167
|
+
* owner's pane (defense-in-depth on top of the WS bridge dropping input and
|
|
168
|
+
* resize frames).
|
|
142
169
|
*/
|
|
143
170
|
attach(role: "owner" | "read-only", dims?: {
|
|
144
171
|
cols?: number;
|
package/dist/terminal/tmux.js
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
*/
|
|
17
17
|
import { execFile, execFileSync } from "node:child_process";
|
|
18
18
|
import { createHash } from "node:crypto";
|
|
19
|
-
import { mkdirSync } from "node:fs";
|
|
19
|
+
import { existsSync, mkdirSync, unlinkSync } from "node:fs";
|
|
20
20
|
import { tmpdir } from "node:os";
|
|
21
21
|
import { join } from "node:path";
|
|
22
22
|
const TMUX_TARGET = "main";
|
|
@@ -26,8 +26,106 @@ const DEFAULT_ROWS = 24;
|
|
|
26
26
|
* wheel-scroll (tmux copy-mode, enabled by `mouse on`) reaches as far back as
|
|
27
27
|
* the terminal shows. Mirrors reference implementation's `TerminalEnvSpec.scrollback`. */
|
|
28
28
|
const DEFAULT_SCROLLBACK = 20000;
|
|
29
|
+
/** Resolve the exact tmux executable selected by the current Rynx distribution. */
|
|
30
|
+
export function resolveTmuxBin(explicit, env = process.env) {
|
|
31
|
+
if (explicit !== undefined)
|
|
32
|
+
return explicit;
|
|
33
|
+
if (Object.hasOwn(env, "RYNX_TMUX_BIN")) {
|
|
34
|
+
return env.RYNX_TMUX_BIN?.trim() ?? "";
|
|
35
|
+
}
|
|
36
|
+
return "tmux";
|
|
37
|
+
}
|
|
38
|
+
/** Deterministic private socket owned by one terminal name. Parent-side
|
|
39
|
+
* shutdown uses the same mapping when the runner child is too wedged to clean
|
|
40
|
+
* up its own tmux server. */
|
|
41
|
+
export function tmuxSocketPath(name) {
|
|
42
|
+
const key = createHash("sha256").update(name).digest("hex").slice(0, 16);
|
|
43
|
+
return join(tmpdir(), "rynx-term", `${key}.sock`);
|
|
44
|
+
}
|
|
45
|
+
/** Read tmux's own output-activity clock for a private terminal window.
|
|
46
|
+
*
|
|
47
|
+
* `#{window_activity}` is an epoch timestamp updated by tmux whenever the pane
|
|
48
|
+
* emits bytes. It is deliberately queried out-of-process rather than inferred
|
|
49
|
+
* from Rynx's forwarded PTY stream: the forwarder/status path is exactly what
|
|
50
|
+
* may have stalled when the idle reaper needs an independent liveness signal.
|
|
51
|
+
* Missing servers, command failures, timeouts, and unparseable output are all
|
|
52
|
+
* treated as unknown (`null`). */
|
|
53
|
+
export function tmuxWindowActivityAt(name, tmuxBin = resolveTmuxBin()) {
|
|
54
|
+
const socketPath = tmuxSocketPath(name);
|
|
55
|
+
return new Promise((resolve) => {
|
|
56
|
+
execFile(tmuxBin, ["-S", socketPath, "display-message", "-p", "-t", TMUX_TARGET, "#{window_activity}"], { timeout: 2_000 }, (error, stdout) => {
|
|
57
|
+
if (error) {
|
|
58
|
+
resolve(null);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
const raw = stdout.toString().trim();
|
|
62
|
+
if (!raw) {
|
|
63
|
+
resolve(null);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const value = Number(raw);
|
|
67
|
+
resolve(Number.isFinite(value) ? value : null);
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
/** Whether tmux itself reports an attached client for this private terminal.
|
|
72
|
+
* This remains authoritative if a parent-side attachment bookkeeping edge was
|
|
73
|
+
* missed during a transport failure. */
|
|
74
|
+
export function tmuxHasAttachedClient(name, tmuxBin = resolveTmuxBin()) {
|
|
75
|
+
const socketPath = tmuxSocketPath(name);
|
|
76
|
+
return new Promise((resolve) => {
|
|
77
|
+
execFile(tmuxBin, ["-S", socketPath, "list-clients", "-t", TMUX_TARGET, "-F", "#{client_name}"], { timeout: 2_000 }, (error, stdout) => {
|
|
78
|
+
resolve(!error && stdout.toString().split("\n").some((line) => line.trim().length > 0));
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
/** Kill one private tmux server and verify it no longer answers. Missing
|
|
83
|
+
* sockets are already stopped; an existing socket with an unusable tmux
|
|
84
|
+
* command is unproven and returns false. */
|
|
85
|
+
export function terminateTmuxServer(name, tmuxBin = resolveTmuxBin()) {
|
|
86
|
+
const socketPath = tmuxSocketPath(name);
|
|
87
|
+
if (!existsSync(socketPath))
|
|
88
|
+
return true;
|
|
89
|
+
const base = ["-u", "-S", socketPath];
|
|
90
|
+
try {
|
|
91
|
+
execFileSync(tmuxBin, [...base, "kill-server"], { stdio: "ignore" });
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
// A server may have exited between existsSync and kill-server. Verify below.
|
|
95
|
+
}
|
|
96
|
+
try {
|
|
97
|
+
execFileSync(tmuxBin, [...base, "has-session", "-t", TMUX_TARGET], {
|
|
98
|
+
stdio: "ignore",
|
|
99
|
+
});
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
// Only a real tmux exit status proves the server/session is absent. Spawn
|
|
104
|
+
// failures (missing/unexecutable tmux) leave liveness unproven and must not
|
|
105
|
+
// unlink a socket that could still belong to a live server.
|
|
106
|
+
if (!error ||
|
|
107
|
+
typeof error !== "object" ||
|
|
108
|
+
!("status" in error) ||
|
|
109
|
+
typeof error.status !== "number") {
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
try {
|
|
113
|
+
// tmux custom-socket servers can exit while leaving the filesystem entry
|
|
114
|
+
// behind. Remove that verified-stale socket so process/socket inventories
|
|
115
|
+
// do not accumulate dead terminals forever.
|
|
116
|
+
unlinkSync(socketPath);
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
catch (unlinkError) {
|
|
120
|
+
return Boolean(unlinkError &&
|
|
121
|
+
typeof unlinkError === "object" &&
|
|
122
|
+
"code" in unlinkError &&
|
|
123
|
+
unlinkError.code === "ENOENT");
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
29
127
|
/** Is a usable `tmux` on PATH? Cheap probe for the capability gate. */
|
|
30
|
-
export function isTmuxAvailable(tmuxBin =
|
|
128
|
+
export function isTmuxAvailable(tmuxBin = resolveTmuxBin()) {
|
|
31
129
|
try {
|
|
32
130
|
execFileSync(tmuxBin, ["-V"], { stdio: "ignore" });
|
|
33
131
|
return true;
|
|
@@ -68,7 +166,7 @@ export class TmuxTerminal {
|
|
|
68
166
|
this.env = withUtf8Locale(opts.env ?? process.env);
|
|
69
167
|
this.cols = opts.cols ?? DEFAULT_COLS;
|
|
70
168
|
this.rows = opts.rows ?? DEFAULT_ROWS;
|
|
71
|
-
this.tmuxBin = opts.tmuxBin
|
|
169
|
+
this.tmuxBin = resolveTmuxBin(opts.tmuxBin);
|
|
72
170
|
this.injectedSpawn = opts.ptySpawn;
|
|
73
171
|
const dir = join(tmpdir(), "rynx-term");
|
|
74
172
|
mkdirSync(dir, { recursive: true });
|
|
@@ -76,8 +174,7 @@ export class TmuxTerminal {
|
|
|
76
174
|
// under the unix-domain path limit (~104 chars on macOS) — a full session id
|
|
77
175
|
// like `sess_<uuid>-main` under $TMPDIR would overflow it and fail with
|
|
78
176
|
// "File name too long". The hash stays unique per terminal.
|
|
79
|
-
|
|
80
|
-
this.socketPath = join(dir, `${key}.sock`);
|
|
177
|
+
this.socketPath = tmuxSocketPath(opts.name);
|
|
81
178
|
}
|
|
82
179
|
/** tmux argv prefix targeting this terminal's private server. */
|
|
83
180
|
base() {
|
|
@@ -221,6 +318,19 @@ export class TmuxTerminal {
|
|
|
221
318
|
});
|
|
222
319
|
});
|
|
223
320
|
}
|
|
321
|
+
/** PID of the process currently owning the pane. */
|
|
322
|
+
panePid() {
|
|
323
|
+
if (!this.started)
|
|
324
|
+
return undefined;
|
|
325
|
+
try {
|
|
326
|
+
const value = execFileSync(this.tmuxBin, [...this.base(), "list-panes", "-t", TMUX_TARGET, "-F", "#{pane_pid}"], { stdio: ["ignore", "pipe", "ignore"] }).toString().trim().split(/\s+/)[0];
|
|
327
|
+
const pid = Number(value);
|
|
328
|
+
return Number.isSafeInteger(pid) && pid > 0 ? pid : undefined;
|
|
329
|
+
}
|
|
330
|
+
catch {
|
|
331
|
+
return undefined;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
224
334
|
/** Type literal text into the pane (agent injection / co-drive from a
|
|
225
335
|
* non-PTY caller). `-l` sends the text literally rather than as key names. */
|
|
226
336
|
sendKeys(text) {
|
|
@@ -286,14 +396,17 @@ export class TmuxTerminal {
|
|
|
286
396
|
}
|
|
287
397
|
/**
|
|
288
398
|
* Attach a client. `role: "read-only"` passes tmux `-r` so the viewer cannot
|
|
289
|
-
* type
|
|
399
|
+
* type and `ignore-size` so even its initial PTY dimensions cannot resize the
|
|
400
|
+
* owner's pane (defense-in-depth on top of the WS bridge dropping input and
|
|
401
|
+
* resize frames).
|
|
290
402
|
*/
|
|
291
403
|
async attach(role, dims) {
|
|
292
404
|
this.start();
|
|
293
405
|
const spawn = this.injectedSpawn ?? (await loadPtySpawn());
|
|
294
|
-
const args = [...this.base(), "attach-session"
|
|
406
|
+
const args = [...this.base(), "attach-session"];
|
|
295
407
|
if (role === "read-only")
|
|
296
|
-
args.push("-r");
|
|
408
|
+
args.push("-r", "-f", "ignore-size");
|
|
409
|
+
args.push("-t", TMUX_TARGET);
|
|
297
410
|
const proc = spawn(this.tmuxBin, args, {
|
|
298
411
|
name: "xterm-256color",
|
|
299
412
|
cols: dims?.cols ?? this.cols,
|
|
@@ -353,12 +466,7 @@ export class TmuxTerminal {
|
|
|
353
466
|
}
|
|
354
467
|
/** Kill the tmux server (ends the session and all attaches). */
|
|
355
468
|
kill() {
|
|
356
|
-
|
|
357
|
-
execFileSync(this.tmuxBin, [...this.base(), "kill-server"], { stdio: "ignore" });
|
|
358
|
-
}
|
|
359
|
-
catch {
|
|
360
|
-
// Already gone — nothing to clean up.
|
|
361
|
-
}
|
|
469
|
+
terminateTmuxServer(this.name, this.tmuxBin);
|
|
362
470
|
this.started = false;
|
|
363
471
|
}
|
|
364
472
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rynx-ai/runtime",
|
|
3
|
-
"version": "0.1.11-beta.
|
|
3
|
+
"version": "0.1.11-beta.21",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/rynx-ai/rynx.git",
|
|
@@ -24,9 +24,10 @@
|
|
|
24
24
|
}
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"node-pty": "
|
|
27
|
+
"node-pty": "1.2.0-beta.15",
|
|
28
|
+
"smol-toml": "1.7.1",
|
|
28
29
|
"ws": "^8.21.0",
|
|
29
|
-
"@rynx-ai/core": "0.1.11-beta.
|
|
30
|
+
"@rynx-ai/core": "0.1.11-beta.21"
|
|
30
31
|
},
|
|
31
32
|
"devDependencies": {
|
|
32
33
|
"@types/ws": "^8.18.1"
|