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 || !Platform.isWindows) {
return fallback.start(request);
}
// The system and user variables as the registry holds them now, not as
// they were when the node started (see `live_environment`), with the
// profile over them: the environment the session gets.
final live = liveProcessEnvironment();
final env = mergeEnvironment([
live,
baseEnvironmentFor?.call(live) ?? baseEnvironment,
]);
// Bash and the winpty.dll beside it, resolved per session from that
// environment's `PATH` (both cached until it changes): a Git install added,
// moved or removed while the node runs is followed, and the DLL always
// matches the bash it drives. Without them this session uses pipes.
final bash = resolveWindowsBash(environment: env);
final dll = bash == null ? null : _winptyDllFor(bash);
if (bash == null || dll == null || _failedDlls.contains(dll)) {
return _fallBack(request, env);
}
if (allowCommand != null && !allowCommand!(request)) {
throw ProcessException(
request.command ?? defaultShell,
request.args,
'Command not permitted by node policy',
);
}
try {
return _spawn(request, spec, env, bash, dll);
} on _ChildSpawnError catch (e) {
// This request's child could not start; winpty itself is fine, so it
// stays in use for later sessions.
onWarning?.call('winpty could not start this session, using pipes: $e');
return _fallBack(request, env);
} on Object catch (e) {
// Stop using this winpty for the rest of the process and fall back so the
// session still works (with env-var geometry) instead of being rejected.
_failedDlls.add(dll);
onWarning?.call(
'winpty PTY backend unavailable, using pipe fallback: $e',
);
return _fallBack(request, env);
}
}