buninu-linux 0.7.1 → 0.7.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +78 -15
- package/build-uki.sh +1 -5
- package/initramfs/buninu/CHANGELOG.md +13 -0
- package/initramfs/buninu/README.md +1 -0
- package/initramfs/buninu/apps/bunmsh/CHANGELOG.md +15 -0
- package/initramfs/buninu/apps/bunmsh/PORTING.md +6 -2
- package/initramfs/buninu/apps/bunmsh/help/README.md +3 -1
- package/initramfs/buninu/apps/bunmsh/help/ln.md +3 -1
- package/initramfs/buninu/apps/bunmsh/package.json +1 -1
- package/initramfs/buninu/apps/bunmsh/src/shell.js +173 -84
- package/initramfs/buninu/apps/bunmsh/test/reference.test.js +95 -1
- package/initramfs/buninu/apps/bunmsh/test/shell1.test.js +91 -0
- package/initramfs/buninu/apps/jsgotty/LICENSES/MIT-microsoft-terminal-conpty.txt +21 -0
- package/initramfs/buninu/apps/jsgotty/LICENSES/MIT-ws.txt +20 -0
- package/initramfs/buninu/apps/jsgotty/LICENSES/NOTICE-microsoft-terminal-conpty.md +535 -0
- package/initramfs/buninu/apps/jsgotty/NOTICE +100 -0
- package/initramfs/buninu/apps/jsgotty/conpty/OpenConsole.exe +0 -0
- package/initramfs/buninu/apps/jsgotty/conpty/conpty.dll +0 -0
- package/initramfs/buninu/apps/jsgotty/gotty.js +24 -0
- package/initramfs/buninu/apps/jsgotty/package-lock.json +2 -2
- package/initramfs/buninu/apps/jsgotty/package.json +6 -2
- package/initramfs/buninu/apps/jsgotty/windows-conpty.js +135 -0
- package/initramfs/buninu/package.json +2 -2
- package/initramfs/usr/share/doc/buninu-linux/bunterm.md +2 -2
- package/package.json +1 -1
- package/scripts/pack-initramfs.sh +23 -0
|
@@ -115,6 +115,26 @@ function assetFilePath(key) {
|
|
|
115
115
|
return assets ? assets.assetDiskPath(key) : path.join(__dirname, key);
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
+
function windowsConptyDllPath() {
|
|
119
|
+
const dll = assetFilePath("conpty/conpty.dll");
|
|
120
|
+
if (fs.existsSync(dll)) return dll;
|
|
121
|
+
if (!assets) return null;
|
|
122
|
+
try {
|
|
123
|
+
const dllBytes = assets.readAssetBytesSync("conpty/conpty.dll");
|
|
124
|
+
const hostBytes = assets.readAssetBytesSync("conpty/OpenConsole.exe");
|
|
125
|
+
const version = crypto.createHash("sha256").update(dllBytes).digest("hex").slice(0, 16);
|
|
126
|
+
const dir = path.join(os.tmpdir(), `jsgotty-conpty-${version}`);
|
|
127
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
128
|
+
for (const [name, bytes] of [["conpty.dll", dllBytes], ["OpenConsole.exe", hostBytes]]) {
|
|
129
|
+
const dest = path.join(dir, name);
|
|
130
|
+
if (!fs.existsSync(dest)) fs.writeFileSync(dest, bytes);
|
|
131
|
+
}
|
|
132
|
+
return path.join(dir, "conpty.dll");
|
|
133
|
+
} catch (error) {
|
|
134
|
+
throw new Error(`Could not prepare bundled ConPTY: ${error.message}`);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
118
138
|
// URL-relative path -> asset key, or null when it escapes `static/`.
|
|
119
139
|
// Normalizing against "/" is what makes "../../etc/passwd" unreachable.
|
|
120
140
|
function staticAssetKey(relativePath) {
|
|
@@ -1624,6 +1644,10 @@ function createPtyBackend(options) {
|
|
|
1624
1644
|
if (globalThis.Bun?.semver?.satisfies?.(
|
|
1625
1645
|
globalThis.Bun?.version, ">=1.3.14"
|
|
1626
1646
|
)) {
|
|
1647
|
+
const conptyDll = windowsConptyDllPath();
|
|
1648
|
+
if (conptyDll) {
|
|
1649
|
+
return new (require("./windows-conpty.js"))(options, conptyDll);
|
|
1650
|
+
}
|
|
1627
1651
|
return new BunPtyBackend(options);
|
|
1628
1652
|
}
|
|
1629
1653
|
return new NodePtyBackend(options);
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jsgotty",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.14",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "jsgotty",
|
|
9
|
-
"version": "1.1.
|
|
9
|
+
"version": "1.1.14",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"node-addon-api": "^7.1.1",
|
|
12
12
|
"ws": "^8.18.3"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jsgotty",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.14",
|
|
4
4
|
"description": "Share your terminal as a web application: A JavaScript rewrite of GoTTY",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"bin": {
|
|
@@ -8,9 +8,13 @@
|
|
|
8
8
|
},
|
|
9
9
|
"type": "commonjs",
|
|
10
10
|
"assets": [
|
|
11
|
+
"conpty",
|
|
11
12
|
"static",
|
|
12
13
|
"README.md",
|
|
13
|
-
"README.en.md"
|
|
14
|
+
"README.en.md",
|
|
15
|
+
"LICENSE",
|
|
16
|
+
"NOTICE",
|
|
17
|
+
"LICENSES"
|
|
14
18
|
],
|
|
15
19
|
"dependencies": {
|
|
16
20
|
"node-addon-api": "^7.1.1",
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
// Windows PTY adapter for the out-of-band Microsoft Console package.
|
|
2
|
+
// Keep the same interface as BunPtyBackend in gotty.js.
|
|
3
|
+
const { dlopen, ptr } = require("bun:ffi");
|
|
4
|
+
const path = require("path");
|
|
5
|
+
|
|
6
|
+
const kernel = dlopen("kernel32.dll", {
|
|
7
|
+
CreatePipe: { args: ["ptr", "ptr", "ptr", "u32"], returns: "i32" },
|
|
8
|
+
InitializeProcThreadAttributeList: { args: ["ptr", "u32", "u32", "ptr"], returns: "i32" },
|
|
9
|
+
UpdateProcThreadAttribute: { args: ["ptr", "u32", "usize", "ptr", "usize", "ptr", "ptr"], returns: "i32" },
|
|
10
|
+
DeleteProcThreadAttributeList: { args: ["ptr"], returns: "void" },
|
|
11
|
+
CreateProcessW: { args: ["ptr", "ptr", "ptr", "ptr", "i32", "u32", "ptr", "ptr", "ptr", "ptr"], returns: "i32" },
|
|
12
|
+
PeekNamedPipe: { args: ["ptr", "ptr", "u32", "ptr", "ptr", "ptr"], returns: "i32" },
|
|
13
|
+
ReadFile: { args: ["ptr", "ptr", "u32", "ptr", "ptr"], returns: "i32" },
|
|
14
|
+
WriteFile: { args: ["ptr", "ptr", "u32", "ptr", "ptr"], returns: "i32" },
|
|
15
|
+
GetExitCodeProcess: { args: ["ptr", "ptr"], returns: "i32" },
|
|
16
|
+
TerminateProcess: { args: ["ptr", "u32"], returns: "i32" },
|
|
17
|
+
CloseHandle: { args: ["ptr"], returns: "i32" },
|
|
18
|
+
GetLastError: { args: [], returns: "u32" },
|
|
19
|
+
}).symbols;
|
|
20
|
+
|
|
21
|
+
const conptySymbols = {
|
|
22
|
+
ConptyCreatePseudoConsole: { args: ["u32", "ptr", "ptr", "u32", "ptr"], returns: "i32" },
|
|
23
|
+
ConptyResizePseudoConsole: { args: ["ptr", "u32"], returns: "i32" },
|
|
24
|
+
ConptyClosePseudoConsole: { args: ["ptr"], returns: "void" },
|
|
25
|
+
};
|
|
26
|
+
const wide = (value) => Buffer.from(`${value}\0`, "utf16le");
|
|
27
|
+
const handle = (buffer) => Number(buffer.readBigUInt64LE(0));
|
|
28
|
+
const size = (cols, rows) => ((Math.max(1, rows) & 0xffff) << 16) | (Math.max(1, cols) & 0xffff);
|
|
29
|
+
const winError = (operation) => new Error(`${operation} failed: ${kernel.GetLastError()}`);
|
|
30
|
+
// Windows CRT argv escaping: double backslashes before quotes and at the end.
|
|
31
|
+
function quoteArg(value) {
|
|
32
|
+
let result = '"', slashes = 0;
|
|
33
|
+
for (const char of String(value)) {
|
|
34
|
+
if (char === "\\") { slashes++; continue; }
|
|
35
|
+
if (char === '"') result += "\\".repeat(slashes * 2 + 1) + '"';
|
|
36
|
+
else result += "\\".repeat(slashes) + char;
|
|
37
|
+
slashes = 0;
|
|
38
|
+
}
|
|
39
|
+
return result + "\\".repeat(slashes * 2) + '"';
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
class WindowsConptyBackend {
|
|
43
|
+
constructor(options, dllPath) {
|
|
44
|
+
this.options = options;
|
|
45
|
+
this.dataHandlers = [];
|
|
46
|
+
this.exitHandlers = [];
|
|
47
|
+
this.closed = false;
|
|
48
|
+
this.conpty = dlopen(path.resolve(dllPath), conptySymbols).symbols;
|
|
49
|
+
const inputRead = Buffer.alloc(8), inputWrite = Buffer.alloc(8);
|
|
50
|
+
const outputRead = Buffer.alloc(8), outputWrite = Buffer.alloc(8);
|
|
51
|
+
if (!kernel.CreatePipe(inputRead, inputWrite, 0, 0)) throw winError("CreatePipe input");
|
|
52
|
+
if (!kernel.CreatePipe(outputRead, outputWrite, 0, 0)) throw winError("CreatePipe output");
|
|
53
|
+
this.input = handle(inputWrite);
|
|
54
|
+
this.output = handle(outputRead);
|
|
55
|
+
const pseudo = Buffer.alloc(8);
|
|
56
|
+
const hr = this.conpty.ConptyCreatePseudoConsole(
|
|
57
|
+
size(options.width || 80, options.height || 24), handle(inputRead), handle(outputWrite), 0, pseudo);
|
|
58
|
+
kernel.CloseHandle(handle(inputRead));
|
|
59
|
+
kernel.CloseHandle(handle(outputWrite));
|
|
60
|
+
if (hr !== 0) throw new Error(`ConptyCreatePseudoConsole failed: ${hr}`);
|
|
61
|
+
this.pseudo = handle(pseudo);
|
|
62
|
+
|
|
63
|
+
const listSize = Buffer.alloc(8);
|
|
64
|
+
kernel.InitializeProcThreadAttributeList(0, 1, 0, listSize);
|
|
65
|
+
const list = Buffer.alloc(Number(listSize.readBigUInt64LE(0)));
|
|
66
|
+
if (!kernel.InitializeProcThreadAttributeList(list, 1, 0, listSize)) throw winError("InitializeProcThreadAttributeList");
|
|
67
|
+
if (!kernel.UpdateProcThreadAttribute(list, 0, 0x00020016, this.pseudo, 8, 0, 0)) throw winError("UpdateProcThreadAttribute");
|
|
68
|
+
const startup = Buffer.alloc(112);
|
|
69
|
+
startup.writeUInt32LE(112, 0);
|
|
70
|
+
startup.writeUInt32LE(0x100, 60); // STARTF_USESTDHANDLES
|
|
71
|
+
startup.writeBigUInt64LE(BigInt(ptr(list)), 104);
|
|
72
|
+
const processInfo = Buffer.alloc(24);
|
|
73
|
+
const env = { ...process.env, TERM: "xterm-256color", ...(options.headerEnv || {}) };
|
|
74
|
+
const envBlock = wide(Object.entries(env).filter(([key]) => !key.startsWith("="))
|
|
75
|
+
.map(([key, value]) => `${key}=${value}`).join("\0") + "\0");
|
|
76
|
+
const executable = Bun.which(options.command) || options.command;
|
|
77
|
+
const command = wide([executable, ...options.argv].map(quoteArg).join(" "));
|
|
78
|
+
const ok = kernel.CreateProcessW(wide(executable), command, 0, 0, 0,
|
|
79
|
+
0x00080000 | 0x00000400, envBlock, wide(process.cwd()), startup, processInfo);
|
|
80
|
+
kernel.DeleteProcThreadAttributeList(list);
|
|
81
|
+
if (!ok) throw winError("CreateProcessW");
|
|
82
|
+
this.process = handle(processInfo);
|
|
83
|
+
this.pid = processInfo.readUInt32LE(16);
|
|
84
|
+
kernel.CloseHandle(Number(processInfo.readBigUInt64LE(8)));
|
|
85
|
+
this.pollTimer = setInterval(() => this.poll(), 5);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
onData(handler) { this.dataHandlers.push(handler); }
|
|
89
|
+
onExit(handler) { this.exitHandlers.push(handler); }
|
|
90
|
+
write(buffer) { this.writeBinary(buffer); }
|
|
91
|
+
writeBinary(buffer) {
|
|
92
|
+
const data = Buffer.from(buffer);
|
|
93
|
+
const written = Buffer.alloc(4);
|
|
94
|
+
for (let offset = 0; offset < data.length;) {
|
|
95
|
+
const chunk = data.subarray(offset);
|
|
96
|
+
if (!kernel.WriteFile(this.input, chunk, chunk.length, written, 0)) throw winError("WriteFile");
|
|
97
|
+
const count = written.readUInt32LE(0);
|
|
98
|
+
if (!count) throw new Error("WriteFile wrote no data");
|
|
99
|
+
offset += count;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
resize(cols, rows) {
|
|
103
|
+
const hr = this.conpty.ConptyResizePseudoConsole(this.pseudo, size(cols, rows));
|
|
104
|
+
if (hr !== 0) throw new Error(`ConptyResizePseudoConsole failed: ${hr}`);
|
|
105
|
+
}
|
|
106
|
+
kill() { if (!this.closed) kernel.TerminateProcess(this.process, 1); }
|
|
107
|
+
close() { this.kill(); }
|
|
108
|
+
|
|
109
|
+
poll() {
|
|
110
|
+
if (this.closed) return;
|
|
111
|
+
const available = Buffer.alloc(4), readCount = Buffer.alloc(4);
|
|
112
|
+
if (kernel.PeekNamedPipe(this.output, 0, 0, 0, available, 0)) {
|
|
113
|
+
for (let bytes = available.readUInt32LE(0); bytes > 0; bytes = available.readUInt32LE(0)) {
|
|
114
|
+
const data = Buffer.alloc(Math.min(bytes, 65536));
|
|
115
|
+
if (!kernel.ReadFile(this.output, data, data.length, readCount, 0)) break;
|
|
116
|
+
const count = readCount.readUInt32LE(0);
|
|
117
|
+
if (!count) break;
|
|
118
|
+
for (const handler of this.dataHandlers) handler(data.subarray(0, count));
|
|
119
|
+
if (!kernel.PeekNamedPipe(this.output, 0, 0, 0, available, 0)) break;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
const exitCode = Buffer.alloc(4);
|
|
123
|
+
if (kernel.GetExitCodeProcess(this.process, exitCode) && exitCode.readUInt32LE(0) !== 259) {
|
|
124
|
+
this.closed = true;
|
|
125
|
+
clearInterval(this.pollTimer);
|
|
126
|
+
this.conpty.ConptyClosePseudoConsole(this.pseudo);
|
|
127
|
+
kernel.CloseHandle(this.input);
|
|
128
|
+
kernel.CloseHandle(this.output);
|
|
129
|
+
kernel.CloseHandle(this.process);
|
|
130
|
+
for (const handler of this.exitHandlers) handler();
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
module.exports = WindowsConptyBackend;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "buninu",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.18",
|
|
4
4
|
"description": "Buninu is a portable, self-bootstrapping Unix-like userspace built on Bun that runs across operating systems",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./bin/init.js",
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"type": "git",
|
|
11
11
|
"url": "git+https://github.com/jjtseng93/buninu.git"
|
|
12
12
|
},
|
|
13
|
-
"homepage": "https://
|
|
13
|
+
"homepage": "https://buninu.org",
|
|
14
14
|
"bugs": {
|
|
15
15
|
"url": "https://github.com/jjtseng93/buninu/issues"
|
|
16
16
|
},
|
|
@@ -6,9 +6,9 @@ xterm-compatible terminal with anti-aliased text, CJK, colour emoji, seamless
|
|
|
6
6
|
box drawing and kitty graphics protocol images — no X11, Wayland or GPU.
|
|
7
7
|
|
|
8
8
|
```sh
|
|
9
|
-
bunterm # run /bin/sh on the console you are on
|
|
10
9
|
bunterm 2 # start a detached bunterm on /dev/tty2
|
|
11
|
-
bunterm /dev/
|
|
10
|
+
bunterm /dev/tty2 # the full virtual-console spelling also works
|
|
11
|
+
bunterm # take over the console you are on (on tty1 that hides the bun-repl)
|
|
12
12
|
bunterm -s 20 # short for --font-size 20
|
|
13
13
|
bunterm -e bun /buninu/apps/jsmdcui/src/index.js --demo
|
|
14
14
|
bunterm --no-mouse # keyboard only
|
package/package.json
CHANGED
|
@@ -27,6 +27,29 @@ cp -a --reflink=auto "$native_dir/." "$staging/"
|
|
|
27
27
|
cp README.md "$staging/usr/share/doc/buninu-linux/README.md"
|
|
28
28
|
cp -a LICENSE NOTICE.md LICENSES "$staging/usr/share/licenses/"
|
|
29
29
|
|
|
30
|
+
# os-release(5): the canonical file lives in /usr/lib and /etc/os-release links
|
|
31
|
+
# to it. build-uki.sh embeds the same file as the UKI's .osrel section.
|
|
32
|
+
version="$(sed -n 's/^ *"version": *"\([^"]*\)".*/\1/p' package.json | head -n 1)"
|
|
33
|
+
test -n "$version" || { echo "error: no version in package.json" >&2; exit 1; }
|
|
34
|
+
printf '%s\n' \
|
|
35
|
+
'NAME="Buninu Linux"' \
|
|
36
|
+
'ID=buninu-linux' \
|
|
37
|
+
"VERSION=\"$version\"" \
|
|
38
|
+
"VERSION_ID=$version" \
|
|
39
|
+
"PRETTY_NAME=\"Buninu Linux $version\"" \
|
|
40
|
+
'HOME_URL="https://buninu.org"' \
|
|
41
|
+
'BUG_REPORT_URL="https://github.com/jjtseng93/buninu-linux/issues"' \
|
|
42
|
+
'LOGO=buninu-linux' \
|
|
43
|
+
'DEFAULT_HOSTNAME=buninu' > build/os-release
|
|
44
|
+
mkdir -p "$staging/usr/lib" "$staging/etc"
|
|
45
|
+
cp build/os-release "$staging/usr/lib/os-release"
|
|
46
|
+
ln -sfn ../usr/lib/os-release "$staging/etc/os-release"
|
|
47
|
+
# LOGO names an icon-theme icon; point the standard lookup paths at the Buninu
|
|
48
|
+
# icon that /buninu already ships instead of packing a second copy.
|
|
49
|
+
mkdir -p "$staging/usr/share/icons/hicolor/512x512/apps" "$staging/usr/share/pixmaps"
|
|
50
|
+
ln -sfn ../../../../../../buninu/icon.png "$staging/usr/share/icons/hicolor/512x512/apps/buninu-linux.png"
|
|
51
|
+
ln -sfn ../../../buninu/icon.png "$staging/usr/share/pixmaps/buninu-linux.png"
|
|
52
|
+
|
|
30
53
|
# fakeroot lets cpio record the character devices without real root. Nothing
|
|
31
54
|
# has mounted devtmpfs when the kernel execs Bun as PID 1, so every device
|
|
32
55
|
# Bun's startup needs has to be in the archive itself:
|