port clide runtime to Windows (ConPTY, AF_UNIX, PATHEXT)

Bring the runtime up on Windows without disturbing the POSIX paths.

PTY: introduce a platform-neutral PtySession contract with a factory
that picks NativePty (posix_openpt/posix_spawn) or the new WindowsPty
(ConPTY via CreatePseudoConsole). The pane registry programs against
the interface; NativePty now implements it.

IPC: the per-workspace AF_UNIX socket lives under %LOCALAPPDATA% and
is hashed from a canonical workspace key (backslash + ASCII-folded
case) so the Dart server and the C client agree despite NTFS case-
insensitivity. The C client grows a Win32 shim (winsock afunix);
chmod is a no-op on Windows where the per-user ACL is the gate.

Toolchain: PATH probing splits on ';' and tries PATHEXT extensions;
the shell defaults to PowerShell (pwsh, then powershell); tmux is
treated as optional since it has no Windows build; dugite falls back
to PATH git for now.

Build: add `make build-windows`, a clide-cli MSVC build wrapped by
ci/build_cli_windows.sh, and a ConPTY smoke-test suite that self-
skips off-platform.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-06-14 17:42:55 +02:00
co-authored by Claude
parent 3a59f4f2e6
commit 03cc0603b0
19 changed files with 1257 additions and 103 deletions
+71
View File
@@ -0,0 +1,71 @@
/// Platform-neutral PTY session contract + factory.
///
/// The pane registry (and anything else that spawns PTY children)
/// programs against [PtySession]; [startPtySession] picks the
/// platform backend — `posix_openpt` + `posix_spawn` on Linux/macOS
/// ([NativePty]), ConPTY on Windows ([WindowsPty]). Both backends
/// share the same lifecycle: spawn → byte stream out → write/resize
/// in → EOF on child exit → close() reaps.
library;
import 'dart:io' show Platform;
import 'dart:typed_data';
import 'native_pty.dart';
import 'windows_pty.dart';
abstract interface class PtySession {
/// OS process id of the spawned child.
int get pid;
/// Byte stream of data produced by the child.
Stream<Uint8List> get output;
bool get isClosed;
/// Write bytes to the child's stdin. Returns the bytes written.
int write(List<int> bytes);
/// Resize the terminal.
void resize({required int cols, required int rows});
/// Signal the child. [signal] is a POSIX signal number; backends
/// without signals (Windows) treat any value as terminate. Null
/// means the backend's default hang-up behaviour.
bool kill([int? signal]);
/// Kill the child and release resources.
Future<void> close();
}
/// Spawn a child under a PTY using the platform backend.
///
/// [environment] must be the complete environment — it goes straight
/// to the child. Merge `Platform.environment` before calling.
PtySession startPtySession({
required String executable,
List<String> arguments = const [],
required int columns,
required int rows,
String? workingDirectory,
Map<String, String> environment = const {},
}) {
if (Platform.isWindows) {
return WindowsPty.start(
executable: executable,
arguments: arguments,
columns: columns,
rows: rows,
workingDirectory: workingDirectory,
environment: environment,
);
}
return NativePty.start(
executable: executable,
arguments: arguments,
columns: columns,
rows: rows,
workingDirectory: workingDirectory,
environment: environment,
);
}