fix(ui): resolve M2 blocking bugs with full POSIX fork/exec PTY spawn and tmux environment isolation
This commit is contained in:
+273
-273
@@ -16,129 +16,6 @@
|
|||||||
"test"
|
"test"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "shelf_web_socket",
|
|
||||||
"version": "1.0.4",
|
|
||||||
"dependencies": [
|
|
||||||
"shelf",
|
|
||||||
"stream_channel",
|
|
||||||
"web_socket_channel"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "shelf",
|
|
||||||
"version": "1.4.2",
|
|
||||||
"dependencies": [
|
|
||||||
"async",
|
|
||||||
"collection",
|
|
||||||
"http_parser",
|
|
||||||
"path",
|
|
||||||
"stack_trace",
|
|
||||||
"stream_channel"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "http_parser",
|
|
||||||
"version": "4.1.2",
|
|
||||||
"dependencies": [
|
|
||||||
"collection",
|
|
||||||
"source_span",
|
|
||||||
"string_scanner",
|
|
||||||
"typed_data"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "collection",
|
|
||||||
"version": "1.19.1",
|
|
||||||
"dependencies": []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "typed_data",
|
|
||||||
"version": "1.4.0",
|
|
||||||
"dependencies": [
|
|
||||||
"collection"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "stream_channel",
|
|
||||||
"version": "2.1.4",
|
|
||||||
"dependencies": [
|
|
||||||
"async"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "stack_trace",
|
|
||||||
"version": "1.12.1",
|
|
||||||
"dependencies": [
|
|
||||||
"path"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "path",
|
|
||||||
"version": "1.9.1",
|
|
||||||
"dependencies": []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "string_scanner",
|
|
||||||
"version": "1.4.1",
|
|
||||||
"dependencies": [
|
|
||||||
"source_span"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "ffi",
|
|
||||||
"version": "2.2.0",
|
|
||||||
"dependencies": []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "source_span",
|
|
||||||
"version": "1.10.2",
|
|
||||||
"dependencies": [
|
|
||||||
"collection",
|
|
||||||
"path",
|
|
||||||
"term_glyph"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "term_glyph",
|
|
||||||
"version": "1.2.2",
|
|
||||||
"dependencies": []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "web_socket_channel",
|
|
||||||
"version": "2.4.5",
|
|
||||||
"dependencies": [
|
|
||||||
"async",
|
|
||||||
"crypto",
|
|
||||||
"stream_channel",
|
|
||||||
"web"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "web",
|
|
||||||
"version": "0.5.1",
|
|
||||||
"dependencies": []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "crypto",
|
|
||||||
"version": "3.0.7",
|
|
||||||
"dependencies": [
|
|
||||||
"typed_data"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "async",
|
|
||||||
"version": "2.13.1",
|
|
||||||
"dependencies": [
|
|
||||||
"collection",
|
|
||||||
"meta"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "meta",
|
|
||||||
"version": "1.19.0",
|
|
||||||
"dependencies": []
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "test",
|
"name": "test",
|
||||||
"version": "1.31.2",
|
"version": "1.31.2",
|
||||||
@@ -170,6 +47,70 @@
|
|||||||
"yaml"
|
"yaml"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "meta",
|
||||||
|
"version": "1.19.0",
|
||||||
|
"dependencies": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "shelf_web_socket",
|
||||||
|
"version": "1.0.4",
|
||||||
|
"dependencies": [
|
||||||
|
"shelf",
|
||||||
|
"stream_channel",
|
||||||
|
"web_socket_channel"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "shelf",
|
||||||
|
"version": "1.4.2",
|
||||||
|
"dependencies": [
|
||||||
|
"async",
|
||||||
|
"collection",
|
||||||
|
"http_parser",
|
||||||
|
"path",
|
||||||
|
"stack_trace",
|
||||||
|
"stream_channel"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ffi",
|
||||||
|
"version": "2.2.0",
|
||||||
|
"dependencies": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "yaml",
|
||||||
|
"version": "3.1.3",
|
||||||
|
"dependencies": [
|
||||||
|
"collection",
|
||||||
|
"source_span",
|
||||||
|
"string_scanner"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "webkit_inspection_protocol",
|
||||||
|
"version": "1.2.1",
|
||||||
|
"dependencies": [
|
||||||
|
"logging"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "web_socket_channel",
|
||||||
|
"version": "2.4.5",
|
||||||
|
"dependencies": [
|
||||||
|
"async",
|
||||||
|
"crypto",
|
||||||
|
"stream_channel",
|
||||||
|
"web"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "typed_data",
|
||||||
|
"version": "1.4.0",
|
||||||
|
"dependencies": [
|
||||||
|
"collection"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "test_core",
|
"name": "test_core",
|
||||||
"version": "0.6.19",
|
"version": "0.6.19",
|
||||||
@@ -212,6 +153,101 @@
|
|||||||
"term_glyph"
|
"term_glyph"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "stream_channel",
|
||||||
|
"version": "2.1.4",
|
||||||
|
"dependencies": [
|
||||||
|
"async"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "stack_trace",
|
||||||
|
"version": "1.12.1",
|
||||||
|
"dependencies": [
|
||||||
|
"path"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "source_span",
|
||||||
|
"version": "1.10.2",
|
||||||
|
"dependencies": [
|
||||||
|
"collection",
|
||||||
|
"path",
|
||||||
|
"term_glyph"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "shelf_static",
|
||||||
|
"version": "1.1.3",
|
||||||
|
"dependencies": [
|
||||||
|
"convert",
|
||||||
|
"http_parser",
|
||||||
|
"mime",
|
||||||
|
"path",
|
||||||
|
"shelf"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "shelf_packages_handler",
|
||||||
|
"version": "3.0.2",
|
||||||
|
"dependencies": [
|
||||||
|
"path",
|
||||||
|
"shelf",
|
||||||
|
"shelf_static"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "pool",
|
||||||
|
"version": "1.5.2",
|
||||||
|
"dependencies": [
|
||||||
|
"async",
|
||||||
|
"stack_trace"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "path",
|
||||||
|
"version": "1.9.1",
|
||||||
|
"dependencies": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "package_config",
|
||||||
|
"version": "3.0.0",
|
||||||
|
"dependencies": [
|
||||||
|
"meta"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "node_preamble",
|
||||||
|
"version": "2.0.2",
|
||||||
|
"dependencies": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "matcher",
|
||||||
|
"version": "0.12.20",
|
||||||
|
"dependencies": [
|
||||||
|
"async",
|
||||||
|
"meta",
|
||||||
|
"stack_trace",
|
||||||
|
"term_glyph",
|
||||||
|
"test_api"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "io",
|
||||||
|
"version": "1.0.5",
|
||||||
|
"dependencies": [
|
||||||
|
"meta",
|
||||||
|
"path",
|
||||||
|
"string_scanner"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "http_multi_server",
|
||||||
|
"version": "3.2.2",
|
||||||
|
"dependencies": [
|
||||||
|
"async"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "coverage",
|
"name": "coverage",
|
||||||
"version": "1.15.1",
|
"version": "1.15.1",
|
||||||
@@ -230,22 +266,99 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "yaml",
|
"name": "collection",
|
||||||
"version": "3.1.3",
|
"version": "1.19.1",
|
||||||
|
"dependencies": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "boolean_selector",
|
||||||
|
"version": "2.1.2",
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
"collection",
|
|
||||||
"source_span",
|
"source_span",
|
||||||
"string_scanner"
|
"string_scanner"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "cli_config",
|
"name": "async",
|
||||||
"version": "0.2.0",
|
"version": "2.13.1",
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
"args",
|
"collection",
|
||||||
|
"meta"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "analyzer",
|
||||||
|
"version": "14.1.0",
|
||||||
|
"dependencies": [
|
||||||
|
"_fe_analyzer_shared",
|
||||||
|
"collection",
|
||||||
|
"convert",
|
||||||
|
"crypto",
|
||||||
|
"glob",
|
||||||
|
"meta",
|
||||||
|
"package_config",
|
||||||
|
"path",
|
||||||
|
"pub_semver",
|
||||||
|
"source_span",
|
||||||
|
"watcher",
|
||||||
"yaml"
|
"yaml"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "http_parser",
|
||||||
|
"version": "4.1.2",
|
||||||
|
"dependencies": [
|
||||||
|
"collection",
|
||||||
|
"source_span",
|
||||||
|
"string_scanner",
|
||||||
|
"typed_data"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "string_scanner",
|
||||||
|
"version": "1.4.1",
|
||||||
|
"dependencies": [
|
||||||
|
"source_span"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "logging",
|
||||||
|
"version": "1.3.0",
|
||||||
|
"dependencies": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "web",
|
||||||
|
"version": "0.5.1",
|
||||||
|
"dependencies": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "crypto",
|
||||||
|
"version": "3.0.7",
|
||||||
|
"dependencies": [
|
||||||
|
"typed_data"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "vm_service",
|
||||||
|
"version": "15.2.0",
|
||||||
|
"dependencies": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "source_maps",
|
||||||
|
"version": "0.10.13",
|
||||||
|
"dependencies": [
|
||||||
|
"source_span"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "source_map_stack_trace",
|
||||||
|
"version": "2.1.2",
|
||||||
|
"dependencies": [
|
||||||
|
"path",
|
||||||
|
"source_maps",
|
||||||
|
"stack_trace"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "glob",
|
"name": "glob",
|
||||||
"version": "2.1.3",
|
"version": "2.1.3",
|
||||||
@@ -266,146 +379,18 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "shelf_packages_handler",
|
"name": "args",
|
||||||
"version": "3.0.2",
|
"version": "2.7.0",
|
||||||
"dependencies": [
|
|
||||||
"path",
|
|
||||||
"shelf",
|
|
||||||
"shelf_static"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "pool",
|
|
||||||
"version": "1.5.2",
|
|
||||||
"dependencies": [
|
|
||||||
"async",
|
|
||||||
"stack_trace"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "node_preamble",
|
|
||||||
"version": "2.0.2",
|
|
||||||
"dependencies": []
|
"dependencies": []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "boolean_selector",
|
"name": "term_glyph",
|
||||||
"version": "2.1.2",
|
"version": "1.2.2",
|
||||||
"dependencies": [
|
"dependencies": []
|
||||||
"source_span",
|
|
||||||
"string_scanner"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "source_map_stack_trace",
|
"name": "mime",
|
||||||
"version": "2.1.2",
|
"version": "2.0.0",
|
||||||
"dependencies": [
|
|
||||||
"path",
|
|
||||||
"source_maps",
|
|
||||||
"stack_trace"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "source_maps",
|
|
||||||
"version": "0.10.13",
|
|
||||||
"dependencies": [
|
|
||||||
"source_span"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "file",
|
|
||||||
"version": "7.0.1",
|
|
||||||
"dependencies": [
|
|
||||||
"meta",
|
|
||||||
"path"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "webkit_inspection_protocol",
|
|
||||||
"version": "1.2.1",
|
|
||||||
"dependencies": [
|
|
||||||
"logging"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "shelf_static",
|
|
||||||
"version": "1.1.3",
|
|
||||||
"dependencies": [
|
|
||||||
"convert",
|
|
||||||
"http_parser",
|
|
||||||
"mime",
|
|
||||||
"path",
|
|
||||||
"shelf"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "io",
|
|
||||||
"version": "1.0.5",
|
|
||||||
"dependencies": [
|
|
||||||
"meta",
|
|
||||||
"path",
|
|
||||||
"string_scanner"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "http_multi_server",
|
|
||||||
"version": "3.2.2",
|
|
||||||
"dependencies": [
|
|
||||||
"async"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "package_config",
|
|
||||||
"version": "3.0.0",
|
|
||||||
"dependencies": [
|
|
||||||
"meta"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "matcher",
|
|
||||||
"version": "0.12.20",
|
|
||||||
"dependencies": [
|
|
||||||
"async",
|
|
||||||
"meta",
|
|
||||||
"stack_trace",
|
|
||||||
"term_glyph",
|
|
||||||
"test_api"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "analyzer",
|
|
||||||
"version": "14.1.0",
|
|
||||||
"dependencies": [
|
|
||||||
"_fe_analyzer_shared",
|
|
||||||
"collection",
|
|
||||||
"convert",
|
|
||||||
"crypto",
|
|
||||||
"glob",
|
|
||||||
"meta",
|
|
||||||
"package_config",
|
|
||||||
"path",
|
|
||||||
"pub_semver",
|
|
||||||
"source_span",
|
|
||||||
"watcher",
|
|
||||||
"yaml"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "_fe_analyzer_shared",
|
|
||||||
"version": "105.0.0",
|
|
||||||
"dependencies": [
|
|
||||||
"meta"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "pub_semver",
|
|
||||||
"version": "2.2.0",
|
|
||||||
"dependencies": [
|
|
||||||
"collection"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "args",
|
|
||||||
"version": "2.7.0",
|
|
||||||
"dependencies": []
|
"dependencies": []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -416,9 +401,12 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "logging",
|
"name": "cli_config",
|
||||||
"version": "1.3.0",
|
"version": "0.2.0",
|
||||||
"dependencies": []
|
"dependencies": [
|
||||||
|
"args",
|
||||||
|
"yaml"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "watcher",
|
"name": "watcher",
|
||||||
@@ -429,14 +417,26 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "mime",
|
"name": "pub_semver",
|
||||||
"version": "2.0.0",
|
"version": "2.2.0",
|
||||||
"dependencies": []
|
"dependencies": [
|
||||||
|
"collection"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "vm_service",
|
"name": "_fe_analyzer_shared",
|
||||||
"version": "15.2.0",
|
"version": "105.0.0",
|
||||||
"dependencies": []
|
"dependencies": [
|
||||||
|
"meta"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "file",
|
||||||
|
"version": "7.0.1",
|
||||||
|
"dependencies": [
|
||||||
|
"meta",
|
||||||
|
"path"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"configVersion": 1
|
"configVersion": 1
|
||||||
|
|||||||
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -20,6 +20,30 @@ typedef _ptsname_dart = ffi.Pointer<ffi.Char> Function(int fd);
|
|||||||
typedef _ioctl_c = ffi.Int32 Function(ffi.Int32 fd, ffi.UnsignedLong request, ffi.Pointer<ffi.Void> argp);
|
typedef _ioctl_c = ffi.Int32 Function(ffi.Int32 fd, ffi.UnsignedLong request, ffi.Pointer<ffi.Void> argp);
|
||||||
typedef _ioctl_dart = int Function(int fd, int request, ffi.Pointer<ffi.Void> argp);
|
typedef _ioctl_dart = int Function(int fd, int request, ffi.Pointer<ffi.Void> argp);
|
||||||
|
|
||||||
|
typedef _fork_c = ffi.Int32 Function();
|
||||||
|
typedef _fork_dart = int Function();
|
||||||
|
|
||||||
|
typedef _setsid_c = ffi.Int32 Function();
|
||||||
|
typedef _setsid_dart = int Function();
|
||||||
|
|
||||||
|
typedef _open_c = ffi.Int32 Function(ffi.Pointer<ffi.Char> pathname, ffi.Int32 flags);
|
||||||
|
typedef _open_dart = int Function(ffi.Pointer<ffi.Char> pathname, int flags);
|
||||||
|
|
||||||
|
typedef _close_c = ffi.Int32 Function(ffi.Int32 fd);
|
||||||
|
typedef _close_dart = int Function(int fd);
|
||||||
|
|
||||||
|
typedef _dup2_c = ffi.Int32 Function(ffi.Int32 oldfd, ffi.Int32 newfd);
|
||||||
|
typedef _dup2_dart = int Function(int oldfd, int newfd);
|
||||||
|
|
||||||
|
typedef _execvp_c = ffi.Int32 Function(ffi.Pointer<ffi.Char> file, ffi.Pointer<ffi.Pointer<ffi.Char>> argv);
|
||||||
|
typedef _execvp_dart = int Function(ffi.Pointer<ffi.Char> file, ffi.Pointer<ffi.Pointer<ffi.Char>> argv);
|
||||||
|
|
||||||
|
typedef _read_c = ffi.IntPtr Function(ffi.Int32 fd, ffi.Pointer<ffi.Void> buf, ffi.IntPtr count);
|
||||||
|
typedef _read_dart = int Function(int fd, ffi.Pointer<ffi.Void> buf, int count);
|
||||||
|
|
||||||
|
typedef _write_c = ffi.IntPtr Function(ffi.Int32 fd, ffi.Pointer<ffi.Void> buf, ffi.IntPtr count);
|
||||||
|
typedef _write_dart = int Function(int fd, ffi.Pointer<ffi.Void> buf, int count);
|
||||||
|
|
||||||
base class Winsize extends ffi.Struct {
|
base class Winsize extends ffi.Struct {
|
||||||
@ffi.Uint16()
|
@ffi.Uint16()
|
||||||
external int ws_row;
|
external int ws_row;
|
||||||
@@ -34,22 +58,16 @@ base class Winsize extends ffi.Struct {
|
|||||||
class PtySession {
|
class PtySession {
|
||||||
final int masterFd;
|
final int masterFd;
|
||||||
final String slaveName;
|
final String slaveName;
|
||||||
late final File _masterFile;
|
final int childPid;
|
||||||
late final RandomAccessFile _masterRaf;
|
|
||||||
|
|
||||||
final _stdoutController = StreamController<List<int>>();
|
final _stdoutController = StreamController<List<int>>();
|
||||||
late final StreamSubscription _readSub;
|
|
||||||
late final Process _process;
|
|
||||||
bool _isClosed = false;
|
bool _isClosed = false;
|
||||||
|
|
||||||
PtySession._({
|
PtySession._({
|
||||||
required this.masterFd,
|
required this.masterFd,
|
||||||
required this.slaveName,
|
required this.slaveName,
|
||||||
required RandomAccessFile raf,
|
required this.childPid,
|
||||||
required File file,
|
|
||||||
}) {
|
}) {
|
||||||
_masterFile = file;
|
|
||||||
_masterRaf = raf;
|
|
||||||
_startReading();
|
_startReading();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,72 +83,137 @@ class PtySession {
|
|||||||
final grantpt = libc.lookupFunction<_grantpt_c, _grantpt_dart>('grantpt');
|
final grantpt = libc.lookupFunction<_grantpt_c, _grantpt_dart>('grantpt');
|
||||||
final unlockpt = libc.lookupFunction<_unlockpt_c, _unlockpt_dart>('unlockpt');
|
final unlockpt = libc.lookupFunction<_unlockpt_c, _unlockpt_dart>('unlockpt');
|
||||||
final ptsname = libc.lookupFunction<_ptsname_c, _ptsname_dart>('ptsname');
|
final ptsname = libc.lookupFunction<_ptsname_c, _ptsname_dart>('ptsname');
|
||||||
|
final fork = libc.lookupFunction<_fork_c, _fork_dart>('fork');
|
||||||
|
final setsid = libc.lookupFunction<_setsid_c, _setsid_dart>('setsid');
|
||||||
|
final open = libc.lookupFunction<_open_c, _open_dart>('open');
|
||||||
|
final close = libc.lookupFunction<_close_c, _close_dart>('close');
|
||||||
|
final dup2 = libc.lookupFunction<_dup2_c, _dup2_dart>('dup2');
|
||||||
|
final execvp = libc.lookupFunction<_execvp_c, _execvp_dart>('execvp');
|
||||||
|
final ioctl = libc.lookupFunction<_ioctl_c, _ioctl_dart>('ioctl');
|
||||||
|
|
||||||
// O_RDWR = 2, O_NOCTTY = 0x00000400
|
// 1. Open master PTY (O_RDWR = 2, O_NOCTTY = 0x00000400)
|
||||||
final fd = posixOpenpt(2 | 0x00000400);
|
final masterFd = posixOpenpt(2 | 0x00000400);
|
||||||
if (fd < 0) {
|
if (masterFd < 0) {
|
||||||
throw OSError('Failed to open pseudo-terminal master');
|
throw OSError('Failed to open pseudo-terminal master');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (grantpt(fd) != 0) {
|
if (grantpt(masterFd) != 0) {
|
||||||
|
close(masterFd);
|
||||||
throw OSError('Failed to grant pseudo-terminal slave permissions');
|
throw OSError('Failed to grant pseudo-terminal slave permissions');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (unlockpt(fd) != 0) {
|
if (unlockpt(masterFd) != 0) {
|
||||||
|
close(masterFd);
|
||||||
throw OSError('Failed to unlock pseudo-terminal slave descriptor');
|
throw OSError('Failed to unlock pseudo-terminal slave descriptor');
|
||||||
}
|
}
|
||||||
|
|
||||||
final slavePtr = ptsname(fd);
|
final slavePtr = ptsname(masterFd);
|
||||||
if (slavePtr == ffi.Pointer.fromAddress(0)) {
|
if (slavePtr == ffi.Pointer.fromAddress(0)) {
|
||||||
|
close(masterFd);
|
||||||
throw OSError('Failed to get pseudo-terminal slave device name');
|
throw OSError('Failed to get pseudo-terminal slave device name');
|
||||||
}
|
}
|
||||||
final slaveName = slavePtr.cast<Utf8>().toDartString();
|
final slaveName = slavePtr.cast<Utf8>().toDartString();
|
||||||
|
|
||||||
final masterFile = File('/proc/self/fd/');
|
// 2. Resolve environment, isolating nested TMUX contexts (§6.7)
|
||||||
final raf = masterFile.openSync(mode: FileMode.writeOnlyAppend);
|
final env = Map<String, String>.from(environment ?? Platform.environment);
|
||||||
|
env.remove('TMUX');
|
||||||
|
env.remove('TMUX_PANE');
|
||||||
|
|
||||||
final process = await Process.start(
|
// 3. Fork child process
|
||||||
executable,
|
final pid = fork();
|
||||||
arguments,
|
if (pid < 0) {
|
||||||
workingDirectory: workingDirectory,
|
close(masterFd);
|
||||||
environment: environment,
|
throw OSError('Failed to fork child process');
|
||||||
runInShell: false,
|
}
|
||||||
mode: ProcessStartMode.normal,
|
|
||||||
);
|
|
||||||
|
|
||||||
final session = PtySession._(
|
if (pid == 0) {
|
||||||
masterFd: fd,
|
// --- CHILD PROCESS BRANCH ---
|
||||||
|
setsid();
|
||||||
|
|
||||||
|
final pathPtr = slaveName.toNativeUtf8();
|
||||||
|
final slaveFd = open(pathPtr.cast<ffi.Char>(), 2);
|
||||||
|
malloc.free(pathPtr);
|
||||||
|
if (slaveFd < 0) {
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TIOCSCTTY = 0x540E
|
||||||
|
ioctl(slaveFd, 0x540E, ffi.Pointer.fromAddress(0));
|
||||||
|
|
||||||
|
dup2(slaveFd, 0);
|
||||||
|
dup2(slaveFd, 1);
|
||||||
|
dup2(slaveFd, 2);
|
||||||
|
|
||||||
|
close(masterFd);
|
||||||
|
if (slaveFd > 2) {
|
||||||
|
close(slaveFd);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (workingDirectory != null) {
|
||||||
|
Directory.current = workingDirectory;
|
||||||
|
}
|
||||||
|
|
||||||
|
final exePtr = executable.toNativeUtf8();
|
||||||
|
final argsCount = arguments.length;
|
||||||
|
final argv = malloc<ffi.Pointer<ffi.Char>>(argsCount + 2);
|
||||||
|
argv[0] = exePtr.cast<ffi.Char>();
|
||||||
|
|
||||||
|
for (var i = 0; i < argsCount; i++) {
|
||||||
|
argv[i + 1] = arguments[i].toNativeUtf8().cast<ffi.Char>();
|
||||||
|
}
|
||||||
|
argv[argsCount + 1] = ffi.Pointer.fromAddress(0);
|
||||||
|
|
||||||
|
execvp(exePtr.cast<ffi.Char>(), argv);
|
||||||
|
exit(-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- PARENT PROCESS BRANCH ---
|
||||||
|
return PtySession._(
|
||||||
|
masterFd: masterFd,
|
||||||
slaveName: slaveName,
|
slaveName: slaveName,
|
||||||
raf: raf,
|
childPid: pid,
|
||||||
file: masterFile,
|
|
||||||
);
|
);
|
||||||
session._process = process;
|
|
||||||
return session;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _startReading() {
|
void _startReading() {
|
||||||
final readStream = _masterFile.openRead();
|
final libc = ffi.DynamicLibrary.open('libc.so.6');
|
||||||
_readSub = readStream.listen(
|
final cRead = libc.lookupFunction<_read_c, _read_dart>('read');
|
||||||
(data) {
|
final buffer = malloc<ffi.Uint8>(1024);
|
||||||
if (!_isClosed) _stdoutController.add(data);
|
|
||||||
},
|
Timer.periodic(const Duration(milliseconds: 10), (timer) {
|
||||||
onError: (err) {
|
if (_isClosed) {
|
||||||
if (!_isClosed) _stdoutController.addError(err);
|
timer.cancel();
|
||||||
},
|
malloc.free(buffer);
|
||||||
onDone: () {
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final bytesRead = cRead(masterFd, buffer.cast<ffi.Void>(), 1024);
|
||||||
|
if (bytesRead > 0) {
|
||||||
|
final bytes = List<int>.generate(bytesRead, (i) => buffer[i]);
|
||||||
|
_stdoutController.add(bytes);
|
||||||
|
} else if (bytesRead < 0) {
|
||||||
|
final errno = ffi.DynamicLibrary.open('libc.so.6').lookup<ffi.Int32>('errno').value;
|
||||||
|
if (errno != 4 && errno != 11) {
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
close();
|
close();
|
||||||
},
|
}
|
||||||
cancelOnError: false,
|
});
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void write(List<int> data) {
|
void write(List<int> data) {
|
||||||
if (_isClosed) return;
|
if (_isClosed) return;
|
||||||
try {
|
final libc = ffi.DynamicLibrary.open('libc.so.6');
|
||||||
_masterRaf.writeFromSync(data);
|
final cWrite = libc.lookupFunction<_write_c, _write_dart>('write');
|
||||||
_masterRaf.flushSync();
|
|
||||||
} catch (_) {
|
final buffer = malloc<ffi.Uint8>(data.length);
|
||||||
|
for (var i = 0; i < data.length; i++) {
|
||||||
|
buffer[i] = data[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cWrite(masterFd, buffer.cast<ffi.Void>(), data.length);
|
||||||
|
malloc.free(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void writeString(String str) {
|
void writeString(String str) {
|
||||||
@@ -153,9 +236,16 @@ class PtySession {
|
|||||||
void close() {
|
void close() {
|
||||||
if (_isClosed) return;
|
if (_isClosed) return;
|
||||||
_isClosed = true;
|
_isClosed = true;
|
||||||
_readSub.cancel();
|
|
||||||
_stdoutController.close();
|
_stdoutController.close();
|
||||||
_masterRaf.closeSync();
|
|
||||||
_process.kill(ProcessSignal.sigterm);
|
final libc = ffi.DynamicLibrary.open('libc.so.6');
|
||||||
|
final close = libc.lookupFunction<_close_c, _close_dart>('close');
|
||||||
|
close(masterFd);
|
||||||
|
|
||||||
|
final kill = libc.lookupFunction<
|
||||||
|
ffi.Int32 Function(ffi.Int32 pid, ffi.Int32 sig),
|
||||||
|
int Function(int pid, int sig)
|
||||||
|
>('kill');
|
||||||
|
kill(childPid, 15);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:io';
|
||||||
|
import 'package:mam_pty/mam_pty.dart';
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
test('PtySession runtime execution resolves process output', () async {
|
||||||
|
final pty = await PtySession.start('echo', ['hello-pty-ok']);
|
||||||
|
|
||||||
|
final completer = Completer<String>();
|
||||||
|
final buffer = StringBuffer();
|
||||||
|
|
||||||
|
final sub = pty.stdout.listen((data) {
|
||||||
|
buffer.write(utf8.decode(data));
|
||||||
|
}, onDone: () {
|
||||||
|
completer.complete(buffer.toString());
|
||||||
|
});
|
||||||
|
|
||||||
|
final result = await completer.future.timeout(const Duration(seconds: 3));
|
||||||
|
sub.cancel();
|
||||||
|
pty.close();
|
||||||
|
|
||||||
|
expect(result, contains('hello-pty-ok'));
|
||||||
|
print('PTY Runtime stdout verified: ' + result.trim());
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"configVersion": 2,
|
||||||
|
"packages": [
|
||||||
|
{
|
||||||
|
"name": "multi_agent_mux_ui_root",
|
||||||
|
"rootUri": "../",
|
||||||
|
"packageUri": "lib/",
|
||||||
|
"languageVersion": "3.0"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"generator": "pub",
|
||||||
|
"generatorVersion": "3.12.1",
|
||||||
|
"flutterRoot": "file:///home/godopu16/puenv/flutter",
|
||||||
|
"flutterVersion": "3.44.1",
|
||||||
|
"pubCache": "file:///home/godopu16/.pub-cache"
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"roots": [
|
||||||
|
"multi_agent_mux_ui_root"
|
||||||
|
],
|
||||||
|
"packages": [
|
||||||
|
{
|
||||||
|
"name": "multi_agent_mux_ui_root",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"dependencies": [],
|
||||||
|
"devDependencies": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"configVersion": 1
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
# Generated by pub
|
||||||
|
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||||
|
packages: {}
|
||||||
|
sdks:
|
||||||
|
dart: ">=3.0.0 <4.0.0"
|
||||||
Reference in New Issue
Block a user