-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathindex.js
133 lines (119 loc) · 4.13 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
// @ts-ignore
let term = new Terminal();
// @ts-ignore
const fitAddon = new FitAddon.FitAddon();
term.loadAddon(fitAddon);
term.open(document.body);
term.focus();
/** @type {number} */
let timeout;
document.body.style.height = `${window.innerHeight}px`;
fitAddon.fit();
addEventListener('resize', () => {
clearTimeout(timeout);
timeout = setTimeout(() => {
document.body.style.height = `${window.innerHeight}px`;
fitAddon.fit();
}, 200);
});
document.body.addEventListener('dragover', (e) => e.preventDefault()); // TODO complete drag-and-drop (import user directory/file)
import { create, fs } from "./lib/kernel.js";
export const webcontainer = await create(new URL('./lib/process.js', import.meta.url).href);
export const filesystem = new fs.OverlayFS();
Object.assign(window, { webcontainer, filesystem });
let root;
try {
// @ts-ignore
const rootEntry = await navigator.storage.getDirectory();
root = new fs.NativeFS(rootEntry);
} catch (err) {
console.warn('No private native file system access', err);
}
if (!root)
root = new fs.MemFS();
try {
if (!await root.access(['etc', 'motd']))
await new Response(`Welcome to bash.js!
This is an exemple of what could be possible with the upcoming WebContainer specification.
Checkout \x1B[4mhttps://github.com/Minigugus/webcontainer-shell/tree/v2\x1B[0m
Type \x1B[1;3mhelp\x1B[0m to get started\n`)
.body
.pipeTo(await root.writeFile(['etc', 'motd'], 0, true));
} catch (err) {
console.warn('could not create default /etc/motd', err);
}
filesystem
.mount([], root)
.mount(['root'], filesystem)
.mount(['mnt'], new fs.EmptyFS())
.mount(['dev'], new fs.NullFS()) // TODO dedicated driver
.mount(['sys'], new fs.NullFS()) // TODO dedicated driver
.mount(['proc'], new fs.NullFS()) // TODO dedicated driver
.mount(['bin'], new fs.HTTPFS(new URL('./command/', import.meta.url).href))
.mount(['tmp'], new fs.MemFS());
document.body.addEventListener('drop', async (e) => {
e.preventDefault();
const dirs = [];
// @ts-ignore
for (const item of e.dataTransfer.items) {
if (item.kind === 'file') {
const entry = await item.getAsFileSystemHandle();
if (entry.kind === 'directory') {
try {
filesystem.mount(['mnt', entry.name], new fs.NativeFS(entry));
dirs.push(` - /mnt/${entry.name}: OK`);
} catch (err) {
dirs.push(` - /mnt/${entry.name}: ERROR: ${(err instanceof Error && err.message) || err}`);
}
}
}
}
alert(`Imported directories:\n${dirs.join('\n')}`)
});
try {
const bash = await webcontainer.run({
entrypoint: 'bash', // will be resolved using the PATH environment variable
cwd: '/',
argv: ['bash'],
env: {
'PATH': '/bin',
'HOST': location.host,
'USER': localStorage.getItem('USER') || 'nobody'
}
}, filesystem); // TODO networking
const decoder = new TextDecoder();
/** @param {ReadableStream<Uint8Array>} stream */
const pipeToTerm = async stream => {
const reader = stream.getReader();
let result;
while (!(result = await reader.read()).done)
term.write(decoder.decode(result.value).replace(/\r?\n/g, '\r\n'));
};
Promise.all([
pipeToTerm(bash.stdout).catch(err => err),
pipeToTerm(bash.stderr).catch(err => err)
])
.then(console.warn, console.error)
.then(() => bash.status)
.then(status => {
disposable.dispose();
term.write('\r\nbash.js exited with status \x1B[1;' + (status ? 31 : 32) + 'm' + status + '\x1B[0;3m\r\nPress any key to restart\r\n');
term.onKey(() => window.location.reload());
});
/** @type {WritableStreamDefaultWriter<Uint8Array>} */
const bashStdin = bash.stdin.getWriter();
const encoder = new TextEncoder();
const disposable = term.onKey(async ({ key }) => {
await bashStdin.ready;
await bashStdin.write(encoder.encode(key));
});
} catch (err) {
let msg = err.message || err;
switch (msg) {
case 'ENOTFOUND':
msg = 'command \x1B[3mbash\x1B[0;1;31m not found';
}
term.write(`\x1B[31mFailed to start bash.js: \x1B[1m${msg}\x1B[0m`);
term.write('\r\n\r\n\x1B[3mPress any key to retry\r\n');
term.onKey(() => window.location.reload());
}