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
+4 -4
View File
@@ -1,6 +1,6 @@
/// [PaneRegistry] — backend-side state for all live panes.
///
/// Owns the [NativePty] per pane, generates `p_N` ids, and forwards
/// Owns the [PtySession] per pane, generates `p_N` ids, and forwards
/// pty output + lifecycle changes as IPC events via a [DaemonEventSink].
/// Pane commands (pane.spawn / list / write / resize / close) resolve
/// against this registry; extension UIs subscribe to the emitted events.
@@ -12,7 +12,7 @@ import 'dart:io' show Platform;
import 'dart:typed_data';
import '../ipc/envelope.dart';
import '../pty/native_pty.dart';
import '../pty/pty_session.dart';
import 'event_sink.dart';
import 'pane.dart';
@@ -21,7 +21,7 @@ class PaneRegistry {
final DaemonEventSink events;
final Map<String, Pane> _panes = {};
final Map<String, NativePty> _sessions = {};
final Map<String, PtySession> _sessions = {};
final Map<String, StreamSubscription<Uint8List>> _subs = {};
int _nextId = 1;
@@ -55,7 +55,7 @@ class PaneRegistry {
...?env,
};
final session = NativePty.start(executable: executable, arguments: arguments, columns: cols, rows: rows, workingDirectory: cwd, environment: fullEnv);
final session = startPtySession(executable: executable, arguments: arguments, columns: cols, rows: rows, workingDirectory: cwd, environment: fullEnv);
final pane = Pane(id: id, kind: kind, pid: session.pid, argv: argv, cwd: cwd, title: title);
_panes[id] = pane;
_sessions[id] = session;