@threadedstack/wmd-web 0.0.0-stage → 0.1.13
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 +2601 -2
- package/RELEASE.json +123 -0
- package/export-page/coi-sw.js +20 -0
- package/export-page/index.html +17 -0
- package/export-page/run.css +10 -0
- package/export-page/run.js +236 -0
- package/guest/arm64/Image +4 -0
- package/guest/arm64/Image-4k +4 -0
- package/guest/arm64/initramfs.cpio.gz +0 -0
- package/guest/arm64/vm.wasm +0 -0
- package/package.json +36 -4
- package/page/sw.js +381 -0
- package/relay/control.ts +139 -0
- package/relay/engine.ts +787 -0
- package/relay/hosted.ts +660 -0
- package/relay/keys.ts +53 -0
- package/relay/local-tls.ts +121 -0
- package/relay/lock.ts +81 -0
- package/relay/logfile.ts +138 -0
- package/relay/main.ts +642 -0
- package/relay/policy.ts +241 -0
- package/relay/relay.ts +266 -0
- package/relay/token.ts +116 -0
- package/relay/v1.ts +107 -0
- package/relay/wisp.ts +615 -0
- package/src/base.js +325 -0
- package/src/boot.js +2037 -0
- package/src/calib-worker.js +40 -0
- package/src/fs/9p/codec.js +303 -0
- package/src/fs/9p/server.js +521 -0
- package/src/fs/errno.js +43 -0
- package/src/fs/graph.js +380 -0
- package/src/fs/overlay.js +1018 -0
- package/src/fs/pack-write.js +165 -0
- package/src/fs/pack.js +427 -0
- package/src/fs/seed.js +22 -0
- package/src/fs/svc-snapshot.js +65 -0
- package/src/fs/upper-pack.js +112 -0
- package/src/host/base-cache.js +195 -0
- package/src/host/base-file.js +328 -0
- package/src/host/base-io.js +93 -0
- package/src/host/boot-events.js +209 -0
- package/src/host/chan-service.js +300 -0
- package/src/host/chan-services.js +689 -0
- package/src/host/chan.js +655 -0
- package/src/host/ckpt-file.js +395 -0
- package/src/host/ckpt-io.js +207 -0
- package/src/host/ckpt-point.js +160 -0
- package/src/host/ckpt-save.js +64 -0
- package/src/host/ckpt.js +434 -0
- package/src/host/clock.js +111 -0
- package/src/host/ctl.js +778 -0
- package/src/host/errno.js +18 -0
- package/src/host/fs.js +167 -0
- package/src/host/gcnudge.js +83 -0
- package/src/host/i64shim.js +81 -0
- package/src/host/jit-cache.js +341 -0
- package/src/host/jit-hub-core.js +208 -0
- package/src/host/jit-hub.js +93 -0
- package/src/host/jit-snap.js +226 -0
- package/src/host/jit.js +1903 -0
- package/src/host/layout.js +100 -0
- package/src/host/monitor.js +110 -0
- package/src/host/p9mount.js +27 -0
- package/src/host/ram-mem.js +125 -0
- package/src/host/stdio.js +143 -0
- package/src/host/threads.js +109 -0
- package/src/host/wasi.js +335 -0
- package/src/host/wmd-imports.js +71 -0
- package/src/host/yamux.js +387 -0
- package/src/image-files.js +154 -0
- package/src/net/dhcp-dns.js +126 -0
- package/src/net/net-worker.js +172 -0
- package/src/net/packet.js +227 -0
- package/src/net/peer.js +230 -0
- package/src/net/proto.js +189 -0
- package/src/net/relay-client.js +226 -0
- package/src/net/stack.js +867 -0
- package/src/net/switch.js +160 -0
- package/src/net/wisp-client.js +570 -0
- package/src/net/wisp-transport.js +196 -0
- package/src/net/wisp.js +511 -0
- package/src/registry/client.js +176 -0
- package/src/registry/converter.js +68 -0
- package/src/registry/hub.js +221 -0
- package/src/registry/oci.js +76 -0
- package/src/registry/pack-worker.js +70 -0
- package/src/registry/pull.js +258 -0
- package/src/registry/ref.js +110 -0
- package/src/registry/wasi-mem.js +355 -0
- package/src/worker.js +315 -0
- package/wmdpack.wasm +0 -0
package/RELEASE.json
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "wmd-web",
|
|
3
|
+
"version": "0.3.1",
|
|
4
|
+
"commit": "23bf0595198cec6c2027017049b2caa39753efc2",
|
|
5
|
+
"previous": "wmd-web-v0.1.0",
|
|
6
|
+
"decisions": [
|
|
7
|
+
"D-486",
|
|
8
|
+
"D-488",
|
|
9
|
+
"D-494",
|
|
10
|
+
"D-498",
|
|
11
|
+
"D-505",
|
|
12
|
+
"D-506",
|
|
13
|
+
"D-507",
|
|
14
|
+
"D-509",
|
|
15
|
+
"D-511",
|
|
16
|
+
"D-516",
|
|
17
|
+
"D-518",
|
|
18
|
+
"D-531",
|
|
19
|
+
"D-533",
|
|
20
|
+
"D-539",
|
|
21
|
+
"D-542"
|
|
22
|
+
],
|
|
23
|
+
"vmSources": "f638fbdada4ccae26c12334b851e12244c1985d8",
|
|
24
|
+
"guest": {
|
|
25
|
+
"arch": "arm64",
|
|
26
|
+
"files": {
|
|
27
|
+
"vm.wasm": {
|
|
28
|
+
"sha256": "f7affc474658fdfdc50e96c29a224eed1520ea69b426d6427fbc058eb141091a",
|
|
29
|
+
"bytes": 1548267
|
|
30
|
+
},
|
|
31
|
+
"Image": {
|
|
32
|
+
"sha256": "a6b3209cf69d5bc1dacf0f3ed67f92c535dbb4108d67b47091c2aef87c6d7b04",
|
|
33
|
+
"bytes": 39047176
|
|
34
|
+
},
|
|
35
|
+
"Image-4k": {
|
|
36
|
+
"sha256": "8ff6e338a0b42d9f774b35423e0e7f9d7f779c635446e8de2a3beb83d3518f8a",
|
|
37
|
+
"bytes": 38828040
|
|
38
|
+
},
|
|
39
|
+
"initramfs.cpio.gz": {
|
|
40
|
+
"sha256": "38101c6b9bd0f8ec8f1b0aea99046d80d36669052794fa8e0368b6a1743285c1",
|
|
41
|
+
"bytes": 4667433
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
"files": {
|
|
46
|
+
"README.md": "e26f6e48262e55f34f7bb2c3e5b4a91163aac356e3f07b5f82202b6ca16e10a3",
|
|
47
|
+
"export-page/coi-sw.js": "0300e670b413cb2e0aae35843fc583546bbf6687df971ab6df41cfa3b0d494d7",
|
|
48
|
+
"export-page/index.html": "f5450c89e63601bbbbffef421f03491b231d1f143070012c1c5315d4b0059f43",
|
|
49
|
+
"export-page/run.css": "b57adf1e76553549c1f89ff157b7973409dedc21889cd3932948cd05b51adc6f",
|
|
50
|
+
"export-page/run.js": "717636a632a40469106ddf1383e198eaac5d83651b5866d0699ec04b82462b7f",
|
|
51
|
+
"guest/arm64/Image": "a6b3209cf69d5bc1dacf0f3ed67f92c535dbb4108d67b47091c2aef87c6d7b04",
|
|
52
|
+
"guest/arm64/Image-4k": "8ff6e338a0b42d9f774b35423e0e7f9d7f779c635446e8de2a3beb83d3518f8a",
|
|
53
|
+
"guest/arm64/initramfs.cpio.gz": "38101c6b9bd0f8ec8f1b0aea99046d80d36669052794fa8e0368b6a1743285c1",
|
|
54
|
+
"guest/arm64/vm.wasm": "f7affc474658fdfdc50e96c29a224eed1520ea69b426d6427fbc058eb141091a",
|
|
55
|
+
"package.json": "5ec889bd829e3e0a021e042e59eaf4f832280ac2135ce8ef160582173aa9dce3",
|
|
56
|
+
"src/base.js": "982f8e46dbb727ec542a494ebdb884ccd19a31ff4e091425b2d1fc01b0d4754a",
|
|
57
|
+
"src/boot.js": "114f9cf0561bca17b042c8db2e72e6984f7a55fd8d19700730895935f226cc77",
|
|
58
|
+
"src/calib-worker.js": "3d610591a1b3b58f59c3a2ab457f43c90727d95973cd43055b3b73b76180611d",
|
|
59
|
+
"src/fs/9p/codec.js": "69711eb4ebc84eff39256138169fe18f5a5066d6cd9e4ff2daf61c65380d7ef7",
|
|
60
|
+
"src/fs/9p/server.js": "da0ba00ebb5522161bb0ef7cd94bfc9616e342bf865633dfef0ba5bc67627d7a",
|
|
61
|
+
"src/fs/errno.js": "b1f18bcd7b565be7c6c80084d87333e58fbaba2e71d81e1c8573ac62754b2530",
|
|
62
|
+
"src/fs/graph.js": "d24a247c7a39668078913e763e0a55520cf6fd74807bb5cd93e3f49f9b90b45b",
|
|
63
|
+
"src/fs/overlay.js": "9f24336b8686c40fcab1c8e6bfa68776b9067fb1a816c051de484603ede9981e",
|
|
64
|
+
"src/fs/pack-write.js": "3f1bc479bcac980ab568b24825eb813881858b14b7bb80ded618f7bd560cde90",
|
|
65
|
+
"src/fs/pack.js": "680ca17a4228f0dfaf1607a1f821755e9a36bd72cb28824a6d21b0906b8e7300",
|
|
66
|
+
"src/fs/seed.js": "1cd2765e5d28e961bc4e81b41898f768a63e24feaf44e0aa42bd23e5cb910a74",
|
|
67
|
+
"src/fs/svc-snapshot.js": "eae82a6e136f9430680197c617601028da35cbe8786df39d6cab28280a2f9a7c",
|
|
68
|
+
"src/fs/upper-pack.js": "737a7e2bb5aac0ff774645db5789d699adfff263ca6f979e6687a9183a47f9dc",
|
|
69
|
+
"src/host/base-cache.js": "e0a9ea65e4eb9e30e7f78b28d9e5653b395f8eaa6c5e4c75b92f123c8d6a76a9",
|
|
70
|
+
"src/host/base-file.js": "edb47c1eeb6b1cec627fa5894512185336b15d3f5db96a7151a8a57b34b4a2e8",
|
|
71
|
+
"src/host/base-io.js": "cdd049875dce9f1417f28b27efa2c6f494cbe2a49851249150d0c1ec871a8b59",
|
|
72
|
+
"src/host/boot-events.js": "a6567c889fa1e95ad7e917107a2593f03d14cc921827616368b3417de96faad2",
|
|
73
|
+
"src/host/chan-service.js": "f131746c6c03167b5968dfd430d797c4717c7c332563de475e62c6ed84acecbe",
|
|
74
|
+
"src/host/chan-services.js": "64f5cb8d05379618011f5e840830f196013f3134ac0523fed1d7a6969b4d322e",
|
|
75
|
+
"src/host/chan.js": "43104dcdd4d17d13f55a71906b398bdb52d9e96c6fca239310f2ee1ccbb4d4ad",
|
|
76
|
+
"src/host/ckpt-file.js": "da7938fa640a062e77bdfb59caad5b3b1fcbf6150e3220de3be23f253f4c4497",
|
|
77
|
+
"src/host/ckpt-io.js": "3fbe74f0254c597f10d088c85e8ba72725502d86ae4dd8c6d2bf0821a93c873f",
|
|
78
|
+
"src/host/ckpt-point.js": "3cc9ec778f131fbfd17a788709f01f096719f3ef81490a7be36a7fc1a422aef4",
|
|
79
|
+
"src/host/ckpt-save.js": "e222571033ae74a4439596c5e90e03b2aa9a5516665dd290f8dd243712294523",
|
|
80
|
+
"src/host/ckpt.js": "17c3e3f39c7e4ab9b51a4d121d1017d943257f95357330d8f21140011562d0db",
|
|
81
|
+
"src/host/clock.js": "aba6af3a8b1a985ae340892222e4b482d33afe20f5c8b3a4c7403ce87d1e247c",
|
|
82
|
+
"src/host/ctl.js": "a2293b8cb53463323999e0d74277eeab6ebc580625db81b324ecbe2e07ddd98d",
|
|
83
|
+
"src/host/errno.js": "26021d85962084be2c43c98fb3accafc2a8f11fbf3c8a5b8d484bd6a088ae4db",
|
|
84
|
+
"src/host/fs.js": "d6753f74db35b7b01baa8709fc7fdcdfc8d638dfe42f454b0a833b22111e8f4a",
|
|
85
|
+
"src/host/gcnudge.js": "4a6569cb73ae24a11fc4ac8c467380bd9efba0bb5fa9867b797bc4f153ab15ab",
|
|
86
|
+
"src/host/i64shim.js": "6c7cbe226c1596acc2ef084b1c7f817b220d43a17fb927d288246257619d5301",
|
|
87
|
+
"src/host/jit-cache.js": "c31311029de8ee20c0ba6f6b09042f8b91ab08e07134e5a6b1a4de3a1d492c58",
|
|
88
|
+
"src/host/jit-hub-core.js": "f8d39dd0e4cbd42568bf56a8a7105f321d33bcf2d5faf8756d45a1ed692bd344",
|
|
89
|
+
"src/host/jit-hub.js": "cf3fc9f9d4e3fb50c41d6de5e2523f380d3c6c1feaf61c15d90cde89aec2f4c3",
|
|
90
|
+
"src/host/jit-snap.js": "3932f36047c9edeb7f59282d1c37fef19c8f5a9fe4bd0192fb93aa64030fe51d",
|
|
91
|
+
"src/host/jit.js": "cb39f90df63752a7d4077f6972731cf4b1d8026cb4ca594e32f9203be6fe1edb",
|
|
92
|
+
"src/host/layout.js": "d5bc5aa9292630b204438df5d6278fda6daa2a65acc061e1c57e0272b32fecc8",
|
|
93
|
+
"src/host/monitor.js": "ae1741ba35668bcc6192642246494433b175668933921634c699af5a0a69acc0",
|
|
94
|
+
"src/host/p9mount.js": "43246601fc1e27d5294742e4482ed166a50c665cb5f526c3646a47dd27e06150",
|
|
95
|
+
"src/host/ram-mem.js": "bf4036d5d12be1d5d2f5223e526b0b8e225bc702ae7e60a2a59fdfe9fbbb49e3",
|
|
96
|
+
"src/host/stdio.js": "43973675578eb1d324c2c6d7078e51a6a1f121190ada5ef14a5551364b8c7f5e",
|
|
97
|
+
"src/host/threads.js": "952381940e7f43114867e9a9714528f2f20c8064b6b0f2018ab075128dc601be",
|
|
98
|
+
"src/host/wasi.js": "fbafa239b8fb4083f7e8c910b1adab38901e8e7cffd247284a9abb2847eaf4bd",
|
|
99
|
+
"src/host/wmd-imports.js": "094f994758c239b75177781c0613decbbd926090b0e8974163ce7caf8a99234d",
|
|
100
|
+
"src/host/yamux.js": "f4044f14f7e836224bdb4b1e53e59c565ee478b83b2e428fffb9f7a048768f79",
|
|
101
|
+
"src/image-files.js": "539856f3991b9f89202dd04ec3816ba8bce7787034ab810534c5170a205ffde7",
|
|
102
|
+
"src/net/dhcp-dns.js": "fdfa7c7d5cbbb4a8774b7dee195bf2df22a0c3fe8f20ac9694b24485fa8f25d5",
|
|
103
|
+
"src/net/net-worker.js": "0a2ce998f69807e386ded7ff1bee53687a1d59ed76d7ef46e2eb6f958b14ad38",
|
|
104
|
+
"src/net/packet.js": "d658f26fa84324a88d7dbc26af9c4a9c10e977c3060f16f7db431496eacd1127",
|
|
105
|
+
"src/net/peer.js": "d83cf85639f6197fe26c877eb79f8c9afc017bf2362dc2b22a8b6bd0f45f3f19",
|
|
106
|
+
"src/net/proto.js": "abfc8cd6f4331cbc98fcb1b0bba0795ff3042cc7db10476673ead1723249eee0",
|
|
107
|
+
"src/net/relay-client.js": "4101de76dc0251db46839dc786cce677e4020795adf022bdce14bcf7edf8f864",
|
|
108
|
+
"src/net/stack.js": "cd451cce7130f08e59cc19c961d2a5fca8416e577a40df1419ab6d5046018891",
|
|
109
|
+
"src/net/switch.js": "bb047fb1ceeb10a84b5475916ead916362accf0e6f42a86561653dfad0c6c50d",
|
|
110
|
+
"src/net/wisp-client.js": "51207a1fab6f0d2dc4cada17ecdc202db2ffa95f16c318459be79c0abb919faf",
|
|
111
|
+
"src/net/wisp-transport.js": "2b71201f3c686078c867e8995bf5c1cd6d47a0d415f476f3a33fbd5729a55f74",
|
|
112
|
+
"src/net/wisp.js": "f5740bd349cca6ca5bbb03ef392a0b2ce9f23c0ec8e07abe3286329fa209118d",
|
|
113
|
+
"src/registry/client.js": "fd38550e9afd82c5cb498513d245c33357a71697b3b2e6dd78538934bcc9d879",
|
|
114
|
+
"src/registry/converter.js": "1fb8294fd5275f6772c27130cc9ba70bd46fafaf440884d42448036618229f37",
|
|
115
|
+
"src/registry/hub.js": "22323e0f799a7078843514c9f87bbcdaa4173fd991ad4e059377befa773762d4",
|
|
116
|
+
"src/registry/oci.js": "6a51ea16b34a65c0e732730f3e07d7ad16c50f6943a486ec05ca6aa3e98676a1",
|
|
117
|
+
"src/registry/pack-worker.js": "fd6798851cb7815dacfcb67fc78c41654209c5504d8fd343aae524e99de1c741",
|
|
118
|
+
"src/registry/pull.js": "66edf4602aca8d41cc68393d01a2da01c4667de7c493af5ae400e8f249d4e3ae",
|
|
119
|
+
"src/registry/ref.js": "24038e0fbcb6ac143cb8e8f7b9fac4dd0803d0891961786dc88dba01a4402e7b",
|
|
120
|
+
"src/registry/wasi-mem.js": "a7f684343010892460db27ee8c1e38e47e797c93dc4ebe4c30185dca6ca56ee2",
|
|
121
|
+
"src/worker.js": "dda8e216ff774fc6d31ccb55c265f1c560243f872b57bc0affe3075b94bd5b4f"
|
|
122
|
+
}
|
|
123
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// wmd image export: adds the cross-origin isolation headers (COOP, COEP, CORP) to this folder's
|
|
2
|
+
// own responses, for static hosts that cannot send headers (GitHub Pages). index.html registers
|
|
3
|
+
// it only when the page is not already cross-origin isolated. Other origins are not touched.
|
|
4
|
+
self.addEventListener('install', () => self.skipWaiting())
|
|
5
|
+
self.addEventListener('activate', e => e.waitUntil(self.clients.claim()))
|
|
6
|
+
self.addEventListener('fetch', e => {
|
|
7
|
+
const r = e.request
|
|
8
|
+
if (r.cache === 'only-if-cached' && r.mode !== 'same-origin') return
|
|
9
|
+
if (new URL(r.url).origin !== self.location.origin) return
|
|
10
|
+
e.respondWith(
|
|
11
|
+
fetch(r).then(res => {
|
|
12
|
+
if (res.status === 0) return res
|
|
13
|
+
const h = new Headers(res.headers)
|
|
14
|
+
h.set('Cross-Origin-Opener-Policy', 'same-origin')
|
|
15
|
+
h.set('Cross-Origin-Embedder-Policy', 'require-corp')
|
|
16
|
+
h.set('Cross-Origin-Resource-Policy', 'same-origin')
|
|
17
|
+
return new Response(res.body, { status: res.status, statusText: res.statusText, headers: h })
|
|
18
|
+
}),
|
|
19
|
+
)
|
|
20
|
+
})
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<title>wmd image</title>
|
|
7
|
+
<link rel="stylesheet" href="./run.css">
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<header><h1 id="title">wmd image</h1><span id="status">loading</span></header>
|
|
11
|
+
<main>
|
|
12
|
+
<section><h2>Output</h2><pre id="main"></pre></section>
|
|
13
|
+
<details><summary>Boot log</summary><pre id="log"></pre><pre id="out"></pre><pre id="err"></pre></details>
|
|
14
|
+
</main>
|
|
15
|
+
<script type="module" src="./run.js"></script>
|
|
16
|
+
</body>
|
|
17
|
+
</html>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
:root { color-scheme: light dark; --bg: #fff; --fg: #111; --pane: #f4f4f4; --bad: #b00020; }
|
|
2
|
+
@media (prefers-color-scheme: dark) { :root { --bg: #111; --fg: #ddd; --pane: #1c1c1c; --bad: #ff6b6b; } }
|
|
3
|
+
body { margin: 0; background: var(--bg); color: var(--fg); font: 14px system-ui, sans-serif; }
|
|
4
|
+
header { padding: 8px 16px; display: flex; gap: 16px; align-items: baseline; flex-wrap: wrap; }
|
|
5
|
+
h1 { font-size: 18px; margin: 0; overflow-wrap: anywhere; }
|
|
6
|
+
h2 { font-size: 13px; margin: 8px 0 4px; }
|
|
7
|
+
main { padding: 0 16px 16px; }
|
|
8
|
+
pre { background: var(--pane); padding: 8px; margin: 0 0 8px; max-height: 60vh; overflow: auto; white-space: pre-wrap; word-break: break-all; font: 12px ui-monospace, monospace; }
|
|
9
|
+
summary { cursor: pointer; margin: 8px 0; }
|
|
10
|
+
.error { color: var(--bad); }
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
// wmd image export --format web (2.7): the runner page. It boots one exported image from this
|
|
2
|
+
// folder alone, with no service of wmd's: page/b7.js's boot path without the registry proxy.
|
|
3
|
+
//
|
|
4
|
+
// wmd-web.json what was exported; the guest files' sha256 (checked here before boot)
|
|
5
|
+
// idx/..., blobs/... the image indexes and packs (getImage with pull: 'never', fetchPacks)
|
|
6
|
+
// guest/arm64/... vm.wasm, the kernel and the init, handed to bootWmd as checked bytes
|
|
7
|
+
//
|
|
8
|
+
// ?ref=REF the exported image to run (default: the first in wmd-web.json)
|
|
9
|
+
// ?cmd=STRING run `/bin/sh -c STRING` instead of the image's entrypoint + cmd
|
|
10
|
+
// ?argv=JSON run this argv instead
|
|
11
|
+
// ?auto=0 do not start
|
|
12
|
+
// cpus=N (1-8) ram=MiB (128-1536, boot.js's MAX_BROWSER_RAM_MIB)
|
|
13
|
+
//
|
|
14
|
+
// Every URL resolves against the page, so the folder works at a site root or under a sub-path.
|
|
15
|
+
// A host without the cross-origin isolation headers gets them from coi-sw.js (one reload).
|
|
16
|
+
// State for tests: window.wmdExport {status, crossOriginIsolated, ref, index, guest, argv, user,
|
|
17
|
+
// mainOut, mainExit, code, reason, error}.
|
|
18
|
+
|
|
19
|
+
import { MAX_BROWSER_RAM_MIB, bootWmd, memoryMaxPages, p9MountOptions } from './wmd-web/boot.js'
|
|
20
|
+
import { MAIN_PROC_ID, containerBundle, fillRunRequest } from './wmd-web/host/ctl.js'
|
|
21
|
+
import { openImage, resolveUser } from './wmd-web/image-files.js'
|
|
22
|
+
import { fetchPacks, getImage } from './wmd-web/registry/client.js'
|
|
23
|
+
import { parseRef } from './wmd-web/registry/ref.js'
|
|
24
|
+
|
|
25
|
+
const ENGINE_APPEND = [
|
|
26
|
+
'console=ttyAMA0', 'earlycon=pl011,0x9000000', 'rdinit=/sbin/init', 'loglevel=8',
|
|
27
|
+
'norandmaps', 'print-fatal-signals=1', 'debug.exception-trace=1',
|
|
28
|
+
'wmd.init', 'wmd.p9tag=wmdroot', 'wmd.control=vport', 'wmd.exit',
|
|
29
|
+
'initcall_blacklist=virtio_net_driver_init', // no eth0: no network in the browser here
|
|
30
|
+
].join(' ')
|
|
31
|
+
const P9_PORT = 564
|
|
32
|
+
const CONTROL_PORT = 8
|
|
33
|
+
const KEEP = 4 << 20
|
|
34
|
+
const COI_RELOAD = 'wmd-export-coi-reload'
|
|
35
|
+
|
|
36
|
+
const q = new URLSearchParams(location.search)
|
|
37
|
+
const state = (window.wmdExport = {
|
|
38
|
+
status: 'idle', crossOriginIsolated: globalThis.crossOriginIsolated === true, ref: null, index: null, guest: null,
|
|
39
|
+
argv: null, user: null, mainOut: '', mainExit: null, stdout: '', stderr: '', code: null, reason: null, error: null,
|
|
40
|
+
})
|
|
41
|
+
const $ = id => document.getElementById(id)
|
|
42
|
+
const clip = (s, n) => (s.length > n ? s.slice(-n) : s)
|
|
43
|
+
const log = s => ($('log').textContent += `${s}\n`)
|
|
44
|
+
const status = s => ($('status').textContent = s)
|
|
45
|
+
const here = p => new URL(p, document.baseURI)
|
|
46
|
+
const hex = buf => [...new Uint8Array(buf)].map(b => b.toString(16).padStart(2, '0')).join('')
|
|
47
|
+
|
|
48
|
+
/** Cross-origin isolation, through coi-sw.js when the host does not send the headers. false: the page reloads. */
|
|
49
|
+
async function isolate() {
|
|
50
|
+
if (globalThis.crossOriginIsolated) {
|
|
51
|
+
try {
|
|
52
|
+
sessionStorage.removeItem(COI_RELOAD)
|
|
53
|
+
} catch {}
|
|
54
|
+
return true
|
|
55
|
+
}
|
|
56
|
+
let reloaded = false
|
|
57
|
+
try {
|
|
58
|
+
reloaded = sessionStorage.getItem(COI_RELOAD) === '1'
|
|
59
|
+
} catch {}
|
|
60
|
+
if (!('serviceWorker' in navigator)) throw new Error('this page needs cross-origin isolation: the host must send COOP/COEP headers (see README.txt), and this browser has no service workers to add them')
|
|
61
|
+
if (reloaded) throw new Error('this page is not cross-origin isolated even through coi-sw.js; send the COOP/COEP/CORP headers from the host (see README.txt)')
|
|
62
|
+
status('adding cross-origin isolation headers (coi-sw.js)...')
|
|
63
|
+
const reg = await navigator.serviceWorker.register(here('coi-sw.js'), { scope: here('./').pathname })
|
|
64
|
+
if (!navigator.serviceWorker.controller && !reg.active) {
|
|
65
|
+
await new Promise((res, rej) => {
|
|
66
|
+
const t = setTimeout(() => rej(new Error('coi-sw.js did not start within 30 s; send the COOP/COEP/CORP headers from the host (see README.txt)')), 30_000)
|
|
67
|
+
const w = reg.installing || reg.waiting
|
|
68
|
+
if (!w) return clearTimeout(t), rej(new Error('coi-sw.js registered but has no worker; send the headers from the host (see README.txt)'))
|
|
69
|
+
w.addEventListener('statechange', () => {
|
|
70
|
+
if (w.state === 'activated') clearTimeout(t), res()
|
|
71
|
+
else if (w.state === 'redundant') clearTimeout(t), rej(new Error('coi-sw.js failed to install; send the COOP/COEP/CORP headers from the host (see README.txt)'))
|
|
72
|
+
})
|
|
73
|
+
})
|
|
74
|
+
}
|
|
75
|
+
try {
|
|
76
|
+
sessionStorage.setItem(COI_RELOAD, '1')
|
|
77
|
+
} catch {}
|
|
78
|
+
location.reload()
|
|
79
|
+
return false
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** A whole-number query parameter in [min, max], or def when absent. */
|
|
83
|
+
function int(name, def, min, max) {
|
|
84
|
+
const v = q.get(name)
|
|
85
|
+
if (v === null || v === '') return def
|
|
86
|
+
const n = Number(v)
|
|
87
|
+
if (!Number.isInteger(n) || n < min || n > max) throw new Error(`?${name}= must be a whole number from ${min} to ${max}`)
|
|
88
|
+
return n
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async function fetchBytes(url) {
|
|
92
|
+
let res
|
|
93
|
+
try {
|
|
94
|
+
res = await fetch(url, { cache: 'no-store' })
|
|
95
|
+
} catch (e) {
|
|
96
|
+
throw new Error(`${url.pathname}: unreachable (${e.message})`)
|
|
97
|
+
}
|
|
98
|
+
if (!res.ok) throw new Error(`${url.pathname}: HTTP ${res.status}`)
|
|
99
|
+
return new Uint8Array(await res.arrayBuffer())
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** A guest file, checked against wmd-web.json's size and sha256. */
|
|
103
|
+
async function guestFile(guest, name) {
|
|
104
|
+
const f = guest.files?.[name]
|
|
105
|
+
if (!f) throw new Error(`wmd-web.json lists no guest file ${name}`)
|
|
106
|
+
const b = await fetchBytes(here(f.path))
|
|
107
|
+
if (b.length !== f.size) throw new Error(`${f.path}: ${b.length} bytes, wmd-web.json says ${f.size}; this folder is incomplete or mixed from two exports`)
|
|
108
|
+
const got = hex(await crypto.subtle.digest('SHA-256', b))
|
|
109
|
+
if (got !== f.sha256) throw new Error(`${f.path}: sha256 ${got.slice(0, 12)}, wmd-web.json says ${f.sha256.slice(0, 12)}; refused (a file from another export?)`)
|
|
110
|
+
return b
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
async function main() {
|
|
114
|
+
if (!(await isolate())) return
|
|
115
|
+
state.crossOriginIsolated = true
|
|
116
|
+
const base = here('.').pathname.replace(/\/$/, '')
|
|
117
|
+
|
|
118
|
+
state.status = 'manifest'
|
|
119
|
+
const mres = await fetch(here('wmd-web.json'), { cache: 'no-store' })
|
|
120
|
+
if (!mres.ok) throw new Error(`wmd-web.json: HTTP ${mres.status}; is this an exported folder?`)
|
|
121
|
+
const man = await mres.json()
|
|
122
|
+
if (man.schema !== 'wmd-web-export/1') throw new Error(`wmd-web.json: schema ${JSON.stringify(man.schema)}, this page reads wmd-web-export/1`)
|
|
123
|
+
if (!man.images?.length) throw new Error('wmd-web.json lists no images')
|
|
124
|
+
let img = man.images[0]
|
|
125
|
+
if (q.get('ref')) {
|
|
126
|
+
const want = parseRef(q.get('ref')).ref
|
|
127
|
+
img = man.images.find(i => i.ref === want)
|
|
128
|
+
if (!img) throw new Error(`${want} is not in this folder; it has: ${man.images.map(i => i.ref).join(', ')}`)
|
|
129
|
+
}
|
|
130
|
+
state.ref = img.ref
|
|
131
|
+
$('title').textContent = img.ref
|
|
132
|
+
log(`wmd ${man.wmd?.version || '?'} (${(man.wmd?.commit || 'unknown commit').slice(0, 12)}): ${img.ref} ${img.platform}`)
|
|
133
|
+
|
|
134
|
+
state.status = 'files'
|
|
135
|
+
status(`getting ${img.ref}...`)
|
|
136
|
+
const kernelName = img.kernel || 'Image'
|
|
137
|
+
const [index, vmBytes, kernel, initrd] = await Promise.all([
|
|
138
|
+
getImage(img.ref, { platform: img.platform, pull: 'never', base }),
|
|
139
|
+
guestFile(man.guest, 'vm.wasm'),
|
|
140
|
+
guestFile(man.guest, kernelName),
|
|
141
|
+
guestFile(man.guest, 'initramfs.cpio.gz'),
|
|
142
|
+
])
|
|
143
|
+
state.index = index
|
|
144
|
+
state.guest = { verified: true, kernel: kernelName }
|
|
145
|
+
const got = await fetchPacks(index, { base })
|
|
146
|
+
log(`packs: ${got.packs.length}, ${(got.bytes / 1e6).toFixed(1)} MB, checked`)
|
|
147
|
+
|
|
148
|
+
const cfg = index.config || {}
|
|
149
|
+
const env = cfg.Env || ['PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin']
|
|
150
|
+
const cwd = cfg.WorkingDir || '/'
|
|
151
|
+
let argv
|
|
152
|
+
if (q.get('argv')) {
|
|
153
|
+
try {
|
|
154
|
+
argv = JSON.parse(q.get('argv'))
|
|
155
|
+
} catch {
|
|
156
|
+
argv = null
|
|
157
|
+
}
|
|
158
|
+
if (!Array.isArray(argv) || !argv.every(a => typeof a === 'string')) throw new Error('?argv= must be a JSON array of strings, e.g. ["python3","-c","print(1)"]')
|
|
159
|
+
}
|
|
160
|
+
else if (q.get('cmd')) argv = ['/bin/sh', '-c', q.get('cmd')]
|
|
161
|
+
else argv = [...(cfg.Entrypoint || []), ...(cfg.Cmd || [])]
|
|
162
|
+
if (!argv.length) throw new Error('the image has no entrypoint or cmd; pass ?cmd=')
|
|
163
|
+
let user
|
|
164
|
+
if (cfg.User) {
|
|
165
|
+
const files = await openImage(got.packs)
|
|
166
|
+
const u = resolveUser(cfg.User, files.accounts())
|
|
167
|
+
user = { uid: u.uid, gid: u.gid }
|
|
168
|
+
}
|
|
169
|
+
state.argv = argv
|
|
170
|
+
state.user = user || null
|
|
171
|
+
const { files } = containerBundle({ args: argv, env, cwd, user })
|
|
172
|
+
const cpus = int('cpus', 2, 1, 8)
|
|
173
|
+
const ram = int('ram', 512, 128, MAX_BROWSER_RAM_MIB)
|
|
174
|
+
const args = [
|
|
175
|
+
'--machine', '--arch', 'a64', '--board', 'arm64', '--mode', 'jit',
|
|
176
|
+
'--cpus', String(cpus), '--ram', String(ram),
|
|
177
|
+
'--kernel', '/g/guest/Image', '--initrd', '/g/guest/initramfs.cpio.gz', '--append', ENGINE_APPEND,
|
|
178
|
+
'--p9', String(P9_PORT), '--p9-tag', 'wmdroot', '--control', String(CONTROL_PORT),
|
|
179
|
+
]
|
|
180
|
+
state.status = 'booting'
|
|
181
|
+
status(`booting ${img.ref}...`)
|
|
182
|
+
const vm = await bootWmd({
|
|
183
|
+
vmUrl: vmBytes,
|
|
184
|
+
maxPages: memoryMaxPages(args, 512),
|
|
185
|
+
files: { 'guest/Image': kernel, 'guest/initramfs.cpio.gz': initrd },
|
|
186
|
+
args,
|
|
187
|
+
onStdout: s => {
|
|
188
|
+
state.stdout = clip(state.stdout + s, KEEP)
|
|
189
|
+
$('out').textContent = clip($('out').textContent + s, 32 << 10)
|
|
190
|
+
},
|
|
191
|
+
onStderr: s => {
|
|
192
|
+
state.stderr = clip(state.stderr + s, KEEP)
|
|
193
|
+
$('err').textContent = clip($('err').textContent + s, 32 << 10)
|
|
194
|
+
},
|
|
195
|
+
channels: {
|
|
196
|
+
services: [
|
|
197
|
+
{ port: P9_PORT, kind: '9p', layers: got.packs, at: 'rootfs', files },
|
|
198
|
+
{ port: CONTROL_PORT, kind: 'control', yamux: true },
|
|
199
|
+
],
|
|
200
|
+
collect: [{ port: P9_PORT, path: 'bundle/exitcode' }],
|
|
201
|
+
},
|
|
202
|
+
})
|
|
203
|
+
state.status = 'running'
|
|
204
|
+
status(`running ${img.ref}`)
|
|
205
|
+
const ended = vm.exited.then(r => {
|
|
206
|
+
state.code = r.code
|
|
207
|
+
state.reason = r.reason
|
|
208
|
+
return r
|
|
209
|
+
})
|
|
210
|
+
const ctl = vm.control
|
|
211
|
+
await Promise.race([ctl.waitSession(300_000), ended.then(r => Promise.reject(new Error(`the machine ended before the container started: ${r.code} ${r.reason}`)))])
|
|
212
|
+
const dec = new TextDecoder()
|
|
213
|
+
const show = b => {
|
|
214
|
+
state.mainOut += dec.decode(b, { stream: true })
|
|
215
|
+
$('main').textContent = clip(state.mainOut, 256 << 10)
|
|
216
|
+
}
|
|
217
|
+
const proc = ctl.run(fillRunRequest({ id: MAIN_PROC_ID, args: argv, env, cwd }, { p9opts: p9MountOptions() }), { onStdout: show, onStderr: show })
|
|
218
|
+
state.mainExit = await Promise.race([proc.exited, ended.then(r => ({ code: null, error: `the machine ended first: ${r.code} ${r.reason}` }))])
|
|
219
|
+
const r = await Promise.race([ended, new Promise(res => setTimeout(() => res(null), 60_000))])
|
|
220
|
+
if (!r) {
|
|
221
|
+
vm.stop()
|
|
222
|
+
await ended
|
|
223
|
+
}
|
|
224
|
+
state.status = 'exited'
|
|
225
|
+
status(`exited ${state.mainExit?.code ?? state.mainExit?.error}`)
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (q.get('auto') !== '0') {
|
|
229
|
+
main().catch(err => {
|
|
230
|
+
state.error = `${err.name || 'Error'}: ${err.message}`
|
|
231
|
+
state.status = 'error'
|
|
232
|
+
status(`error: ${err.message}`)
|
|
233
|
+
$('status').className = 'error'
|
|
234
|
+
state.stderr += `\n[page] ${state.error}\n${err.stack || ''}`
|
|
235
|
+
})
|
|
236
|
+
}
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,38 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@threadedstack/wmd-web",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.13",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"homepage": "https://github.com/threadedstack/wmd-releases",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/threadedstack/wmd-releases.git"
|
|
9
|
+
},
|
|
10
|
+
"description": "Browser host for wmd's guest vm.wasm (Web Workers + shared memory + WASI preview1)",
|
|
11
|
+
"wmd": {
|
|
12
|
+
"tag": "v0.1.13",
|
|
13
|
+
"commit": "23bf0595198cec6c2027017049b2caa39753efc2",
|
|
14
|
+
"web": "0.3.1"
|
|
15
|
+
},
|
|
16
|
+
"type": "module",
|
|
17
|
+
"main": "./src/boot.js",
|
|
18
|
+
"exports": {
|
|
19
|
+
".": "./src/boot.js",
|
|
20
|
+
"./src/*": "./src/*",
|
|
21
|
+
"./relay/*": "./relay/*",
|
|
22
|
+
"./page/sw.js": "./page/sw.js",
|
|
23
|
+
"./export-page/*": "./export-page/*",
|
|
24
|
+
"./guest/*": "./guest/*",
|
|
25
|
+
"./wmdpack.wasm": "./wmdpack.wasm",
|
|
26
|
+
"./RELEASE.json": "./RELEASE.json",
|
|
27
|
+
"./package.json": "./package.json"
|
|
28
|
+
},
|
|
29
|
+
"files": [
|
|
30
|
+
"src",
|
|
31
|
+
"relay",
|
|
32
|
+
"page",
|
|
33
|
+
"export-page",
|
|
34
|
+
"guest",
|
|
35
|
+
"wmdpack.wasm",
|
|
36
|
+
"RELEASE.json"
|
|
37
|
+
]
|
|
38
|
+
}
|