start method
Starts a session for request, returning a live ShellSession.
May throw if the command is invalid or the node is at capacity; the node
runtime translates failures into a node.session.rejected message.
Implementation
@override
Future<ShellSession> start(ShellRequest request) async {
final spec = request.pty;
if (spec == null || !_scriptUsable) {
return fallback.start(request);
}
if (allowCommand != null && !allowCommand!(request)) {
throw ProcessException(
request.command ?? defaultShell,
request.args,
'Command not permitted by node policy',
);
}
Directory? ttyDir;
try {
final script = _scriptPath();
if (script == null) throw StateError('script(1) not found');
// A unique per-session temp file the wrapper records its controlling-tty
// path into (via `tty`), so [ScriptPtyShellSession.resize] can target the
// child PTS with `stty` — `script` over pipes leaves the node no pty fd.
ttyDir = Directory.systemTemp.createTempSync('omnyshell-pts-');
final ttyFile = '${ttyDir.path}/tty';
final (executable, args) = _scriptInvocation(request, spec, ttyFile);
final process = await Process.start(
script,
args,
workingDirectory: request.cwd ?? workingDirectory,
environment: {
...baseEnvironment,
..._environment(spec),
...request.env,
},
includeParentEnvironment: true,
);
return ScriptPtyShellSession(process, ttyFile: ttyFile, ttyDir: ttyDir);
} on Object catch (e) {
// Disable the `script` path for the rest of the process and fall back so
// the session still works (with env-var geometry) instead of being
// rejected.
try {
ttyDir?.deleteSync(recursive: true);
} on Object {
// Best-effort cleanup of the unused temp dir.
}
_scriptUsable = false;
onWarning?.call(
'script PTY backend unavailable, using pipe fallback: $e',
);
return fallback.start(request);
}
}