replace ptyc with forkpty() via Dart FFI
test / unit + widget + golden + a11y (push) Failing after 3m13s
test / integration_test (xvfb) (push) Has been skipped
test / bundle smoke (xvfb 5s) (push) Has been skipped
test / daemon subprocess + web WASM smoke (push) Has been skipped

NativePty calls forkpty() directly — no helper binary, no socketpair,
no SCM_RIGHTS. The master fd stays in-process. Reader isolate uses
poll() for clean shutdown.

Based on the pty-spike proof-of-concept. Platform-aware: macOS uses
libSystem (DynamicLibrary.process), Linux needs libutil.so.1.
TIOCSWINSZ platform-detected.

PaneRegistry updated to use NativePty. registerPaneCommands no longer
needs a Toolchain parameter. All ptyc references removed from the
daemon layer.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jeroen Schweitzer
2026-04-30 20:30:15 +02:00
co-authored by Claude Opus 4.6
parent cf2ae48201
commit 5aa0eb46e4
12 changed files with 708 additions and 336 deletions
+3 -12
View File
@@ -15,11 +15,10 @@ import '../ipc/envelope.dart';
import '../ipc/schema_v1.dart';
import '../panes/pane.dart';
import '../panes/registry.dart';
import '../../kernel/src/toolchain.dart';
import 'dispatcher.dart';
void registerPaneCommands(DaemonDispatcher d, PaneRegistry registry, {required Toolchain toolchain}) {
d.register('pane.spawn', (req) => _spawn(req, registry, toolchain));
void registerPaneCommands(DaemonDispatcher d, PaneRegistry registry) {
d.register('pane.spawn', (req) => _spawn(req, registry));
d.register('pane.list', (req) => _list(req, registry));
d.register('pane.close', (req) => _close(req, registry));
d.register('pane.write', (req) => _write(req, registry));
@@ -48,14 +47,7 @@ IpcResponse _notFound(String id, String message) => IpcResponse.err(
),
);
Future<IpcResponse> _spawn(IpcRequest req, PaneRegistry registry, Toolchain toolchain) async {
// Wait for toolchain resolution if it hasn't completed yet.
if (!toolchain.resolved) {
await Future.any([
toolchain.waitForResolution(),
Future.delayed(const Duration(seconds: 5)),
]);
}
Future<IpcResponse> _spawn(IpcRequest req, PaneRegistry registry) async {
final args = req.args;
final rawArgv = args['argv'];
if (rawArgv is! List || rawArgv.isEmpty) {
@@ -92,7 +84,6 @@ Future<IpcResponse> _spawn(IpcRequest req, PaneRegistry registry, Toolchain tool
cols: (args['cols'] as num?)?.toInt() ?? 80,
rows: (args['rows'] as num?)?.toInt() ?? 24,
title: args['title'] as String?,
ptycPath: (args['ptyc_path'] as String?) ?? toolchain.ptyc,
);
return IpcResponse.ok(id: req.id, data: pane.toJson());
} catch (e) {