@namzu/sandbox 24.0.0 → 25.0.0
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/CHANGELOG.md +274 -0
- package/dist/backends/http-worker-client.d.ts +3 -1
- package/dist/backends/http-worker-client.d.ts.map +1 -1
- package/dist/backends/http-worker-client.js +15 -4
- package/dist/backends/http-worker-client.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/local-virtual-computer/client.d.ts +21 -0
- package/dist/local-virtual-computer/client.d.ts.map +1 -0
- package/dist/local-virtual-computer/client.js +503 -0
- package/dist/local-virtual-computer/client.js.map +1 -0
- package/dist/local-virtual-computer/detached-process.d.ts +15 -0
- package/dist/local-virtual-computer/detached-process.d.ts.map +1 -0
- package/dist/local-virtual-computer/detached-process.js +111 -0
- package/dist/local-virtual-computer/detached-process.js.map +1 -0
- package/dist/local-virtual-computer/detached-worker.d.ts +11 -0
- package/dist/local-virtual-computer/detached-worker.d.ts.map +1 -0
- package/dist/local-virtual-computer/detached-worker.js +97 -0
- package/dist/local-virtual-computer/detached-worker.js.map +1 -0
- package/dist/local-virtual-computer/dialect.d.ts +26 -0
- package/dist/local-virtual-computer/dialect.d.ts.map +1 -0
- package/dist/local-virtual-computer/dialect.js +61 -0
- package/dist/local-virtual-computer/dialect.js.map +1 -0
- package/dist/local-virtual-computer/engine.d.ts +63 -0
- package/dist/local-virtual-computer/engine.d.ts.map +1 -0
- package/dist/local-virtual-computer/engine.js +125 -0
- package/dist/local-virtual-computer/engine.js.map +1 -0
- package/dist/local-virtual-computer/guest-stdio.d.ts +15 -0
- package/dist/local-virtual-computer/guest-stdio.d.ts.map +1 -0
- package/dist/local-virtual-computer/guest-stdio.js +330 -0
- package/dist/local-virtual-computer/guest-stdio.js.map +1 -0
- package/dist/local-virtual-computer/index.d.ts +36 -0
- package/dist/local-virtual-computer/index.d.ts.map +1 -0
- package/dist/local-virtual-computer/index.js +341 -0
- package/dist/local-virtual-computer/index.js.map +1 -0
- package/dist/local-virtual-computer/podman-machine.d.ts +17 -0
- package/dist/local-virtual-computer/podman-machine.d.ts.map +1 -0
- package/dist/local-virtual-computer/podman-machine.js +181 -0
- package/dist/local-virtual-computer/podman-machine.js.map +1 -0
- package/dist/local-virtual-computer/setup/contract.d.ts +360 -0
- package/dist/local-virtual-computer/setup/contract.d.ts.map +1 -0
- package/dist/local-virtual-computer/setup/contract.js +3 -0
- package/dist/local-virtual-computer/setup/contract.js.map +1 -0
- package/dist/local-virtual-computer/setup/elevated.d.ts +78 -0
- package/dist/local-virtual-computer/setup/elevated.d.ts.map +1 -0
- package/dist/local-virtual-computer/setup/elevated.js +548 -0
- package/dist/local-virtual-computer/setup/elevated.js.map +1 -0
- package/dist/local-virtual-computer/setup/host.d.ts +12 -0
- package/dist/local-virtual-computer/setup/host.d.ts.map +1 -0
- package/dist/local-virtual-computer/setup/host.js +93 -0
- package/dist/local-virtual-computer/setup/host.js.map +1 -0
- package/dist/local-virtual-computer/setup/idle.d.ts +18 -0
- package/dist/local-virtual-computer/setup/idle.d.ts.map +1 -0
- package/dist/local-virtual-computer/setup/idle.js +38 -0
- package/dist/local-virtual-computer/setup/idle.js.map +1 -0
- package/dist/local-virtual-computer/setup/image.d.ts +7 -0
- package/dist/local-virtual-computer/setup/image.d.ts.map +1 -0
- package/dist/local-virtual-computer/setup/image.js +124 -0
- package/dist/local-virtual-computer/setup/image.js.map +1 -0
- package/dist/local-virtual-computer/setup/index.d.ts +17 -0
- package/dist/local-virtual-computer/setup/index.d.ts.map +1 -0
- package/dist/local-virtual-computer/setup/index.js +17 -0
- package/dist/local-virtual-computer/setup/index.js.map +1 -0
- package/dist/local-virtual-computer/setup/limits.d.ts +30 -0
- package/dist/local-virtual-computer/setup/limits.d.ts.map +1 -0
- package/dist/local-virtual-computer/setup/limits.js +58 -0
- package/dist/local-virtual-computer/setup/limits.js.map +1 -0
- package/dist/local-virtual-computer/setup/plan.d.ts +8 -0
- package/dist/local-virtual-computer/setup/plan.d.ts.map +1 -0
- package/dist/local-virtual-computer/setup/plan.js +131 -0
- package/dist/local-virtual-computer/setup/plan.js.map +1 -0
- package/dist/local-virtual-computer/setup/readiness.d.ts +11 -0
- package/dist/local-virtual-computer/setup/readiness.d.ts.map +1 -0
- package/dist/local-virtual-computer/setup/readiness.js +320 -0
- package/dist/local-virtual-computer/setup/readiness.js.map +1 -0
- package/dist/local-virtual-computer/setup/registry.d.ts +18 -0
- package/dist/local-virtual-computer/setup/registry.d.ts.map +1 -0
- package/dist/local-virtual-computer/setup/registry.js +50 -0
- package/dist/local-virtual-computer/setup/registry.js.map +1 -0
- package/dist/local-virtual-computer/setup/restart.d.ts +8 -0
- package/dist/local-virtual-computer/setup/restart.d.ts.map +1 -0
- package/dist/local-virtual-computer/setup/restart.js +60 -0
- package/dist/local-virtual-computer/setup/restart.js.map +1 -0
- package/dist/local-virtual-computer/setup/resume.d.ts +14 -0
- package/dist/local-virtual-computer/setup/resume.d.ts.map +1 -0
- package/dist/local-virtual-computer/setup/resume.js +58 -0
- package/dist/local-virtual-computer/setup/resume.js.map +1 -0
- package/dist/local-virtual-computer/setup/run.d.ts +8 -0
- package/dist/local-virtual-computer/setup/run.d.ts.map +1 -0
- package/dist/local-virtual-computer/setup/run.js +200 -0
- package/dist/local-virtual-computer/setup/run.js.map +1 -0
- package/dist/local-virtual-computer/setup/windows-folder.d.ts +11 -0
- package/dist/local-virtual-computer/setup/windows-folder.d.ts.map +1 -0
- package/dist/local-virtual-computer/setup/windows-folder.js +14 -0
- package/dist/local-virtual-computer/setup/windows-folder.js.map +1 -0
- package/dist/local-virtual-computer/setup/wsl-package.d.ts +19 -0
- package/dist/local-virtual-computer/setup/wsl-package.d.ts.map +1 -0
- package/dist/local-virtual-computer/setup/wsl-package.js +242 -0
- package/dist/local-virtual-computer/setup/wsl-package.js.map +1 -0
- package/dist/local-virtual-computer/setup/wslc-shared.d.ts +27 -0
- package/dist/local-virtual-computer/setup/wslc-shared.d.ts.map +1 -0
- package/dist/local-virtual-computer/setup/wslc-shared.js +137 -0
- package/dist/local-virtual-computer/setup/wslc-shared.js.map +1 -0
- package/dist/local-virtual-computer/ssh-forward.d.ts +19 -0
- package/dist/local-virtual-computer/ssh-forward.d.ts.map +1 -0
- package/dist/local-virtual-computer/ssh-forward.js +179 -0
- package/dist/local-virtual-computer/ssh-forward.js.map +1 -0
- package/dist/local-virtual-computer/wslc-engine.d.ts +27 -0
- package/dist/local-virtual-computer/wslc-engine.d.ts.map +1 -0
- package/dist/local-virtual-computer/wslc-engine.js +98 -0
- package/dist/local-virtual-computer/wslc-engine.js.map +1 -0
- package/local-computer/Dockerfile +99 -0
- package/local-computer/app-catalog.cjs +240 -0
- package/local-computer/app-native-host.cjs +144 -0
- package/local-computer/browser-home.cjs +249 -0
- package/local-computer/build-image.mjs +310 -0
- package/local-computer/desktop-worker.cjs +477 -0
- package/local-computer/entrypoint.sh +161 -0
- package/local-computer/home.html +28 -0
- package/local-computer/new-tab/home.css +255 -0
- package/local-computer/new-tab/home.js +65 -0
- package/local-computer/new-tab/manifest.json +12 -0
- package/local-computer/new-tab/wordmark.svg +33 -0
- package/local-computer/openbox-rc.xml +41 -0
- package/local-computer/prepare-home.cjs +121 -0
- package/local-computer/tint2rc +44 -0
- package/package.json +13 -4
- package/src/backends/http-worker-client.ts +31 -3
- package/src/index.ts +13 -0
- package/src/local-virtual-computer/client.ts +573 -0
- package/src/local-virtual-computer/detached-process.ts +133 -0
- package/src/local-virtual-computer/detached-worker.ts +124 -0
- package/src/local-virtual-computer/dialect.ts +83 -0
- package/src/local-virtual-computer/engine.ts +197 -0
- package/src/local-virtual-computer/guest-stdio.ts +332 -0
- package/src/local-virtual-computer/index.ts +447 -0
- package/src/local-virtual-computer/podman-machine.ts +264 -0
- package/src/local-virtual-computer/setup/contract.ts +395 -0
- package/src/local-virtual-computer/setup/elevated.ts +655 -0
- package/src/local-virtual-computer/setup/host.ts +100 -0
- package/src/local-virtual-computer/setup/idle.ts +39 -0
- package/src/local-virtual-computer/setup/image.ts +159 -0
- package/src/local-virtual-computer/setup/index.ts +16 -0
- package/src/local-virtual-computer/setup/limits.ts +68 -0
- package/src/local-virtual-computer/setup/plan.ts +144 -0
- package/src/local-virtual-computer/setup/readiness.ts +386 -0
- package/src/local-virtual-computer/setup/registry.ts +50 -0
- package/src/local-virtual-computer/setup/restart.ts +64 -0
- package/src/local-virtual-computer/setup/resume.ts +65 -0
- package/src/local-virtual-computer/setup/run.ts +225 -0
- package/src/local-virtual-computer/setup/windows-folder.ts +13 -0
- package/src/local-virtual-computer/setup/wsl-package.ts +242 -0
- package/src/local-virtual-computer/setup/wslc-shared.ts +147 -0
- package/src/local-virtual-computer/ssh-forward.ts +195 -0
- package/src/local-virtual-computer/wslc-engine.ts +124 -0
- package/worker/server.js +1499 -0
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
* {
|
|
2
|
+
box-sizing: border-box;
|
|
3
|
+
}
|
|
4
|
+
body {
|
|
5
|
+
margin: 0;
|
|
6
|
+
min-height: 100dvh;
|
|
7
|
+
color: #e7eee8;
|
|
8
|
+
background: radial-gradient(ellipse at 20% 0%, #2a4933 0%, transparent 56%),
|
|
9
|
+
#141c17;
|
|
10
|
+
font-family: "DejaVu Sans", sans-serif;
|
|
11
|
+
}
|
|
12
|
+
body::before {
|
|
13
|
+
position: fixed;
|
|
14
|
+
inset: 0;
|
|
15
|
+
background-image: radial-gradient(#a4d5ab 0.7px, transparent 0.8px);
|
|
16
|
+
background-size: 5px 5px;
|
|
17
|
+
mask-image: linear-gradient(145deg, #0004, transparent 65%);
|
|
18
|
+
pointer-events: none;
|
|
19
|
+
content: "";
|
|
20
|
+
}
|
|
21
|
+
header {
|
|
22
|
+
display: flex;
|
|
23
|
+
align-items: center;
|
|
24
|
+
justify-content: space-between;
|
|
25
|
+
padding: 28px 36px;
|
|
26
|
+
}
|
|
27
|
+
.brand {
|
|
28
|
+
display: block;
|
|
29
|
+
width: 126px;
|
|
30
|
+
height: 24px;
|
|
31
|
+
}
|
|
32
|
+
.workspace-label {
|
|
33
|
+
color: #9dac9f;
|
|
34
|
+
font-size: 12px;
|
|
35
|
+
}
|
|
36
|
+
main {
|
|
37
|
+
display: flex;
|
|
38
|
+
align-items: center;
|
|
39
|
+
justify-content: center;
|
|
40
|
+
flex-direction: column;
|
|
41
|
+
min-height: calc(100dvh - 140px);
|
|
42
|
+
padding: 24px 28px 60px;
|
|
43
|
+
animation: arrive 240ms ease-out both;
|
|
44
|
+
}
|
|
45
|
+
#clock {
|
|
46
|
+
font-size: clamp(64px, 11vw, 104px);
|
|
47
|
+
font-weight: 300;
|
|
48
|
+
letter-spacing: -6px;
|
|
49
|
+
font-variant-numeric: tabular-nums;
|
|
50
|
+
}
|
|
51
|
+
#date {
|
|
52
|
+
margin: 4px 0 24px;
|
|
53
|
+
color: #a4b4a6;
|
|
54
|
+
font-size: 14px;
|
|
55
|
+
}
|
|
56
|
+
h1 {
|
|
57
|
+
margin: 0 0 28px;
|
|
58
|
+
font-size: clamp(20px, 3vw, 27px);
|
|
59
|
+
font-weight: 400;
|
|
60
|
+
letter-spacing: -0.5px;
|
|
61
|
+
}
|
|
62
|
+
form {
|
|
63
|
+
display: flex;
|
|
64
|
+
align-items: center;
|
|
65
|
+
width: min(100%, 500px);
|
|
66
|
+
padding: 7px 9px 7px 18px;
|
|
67
|
+
border: 1px solid #62816b66;
|
|
68
|
+
border-radius: 16px;
|
|
69
|
+
background: #17231ccc;
|
|
70
|
+
box-shadow: 0 8px 32px #0002;
|
|
71
|
+
transition: border-color 150ms ease, box-shadow 150ms ease;
|
|
72
|
+
}
|
|
73
|
+
form:focus-within {
|
|
74
|
+
border-color: #7acc84;
|
|
75
|
+
box-shadow: 0 0 0 3px #7acc841a;
|
|
76
|
+
}
|
|
77
|
+
label {
|
|
78
|
+
position: absolute;
|
|
79
|
+
width: 1px;
|
|
80
|
+
height: 1px;
|
|
81
|
+
overflow: hidden;
|
|
82
|
+
clip-path: inset(50%);
|
|
83
|
+
white-space: nowrap;
|
|
84
|
+
}
|
|
85
|
+
input {
|
|
86
|
+
min-width: 0;
|
|
87
|
+
flex: 1;
|
|
88
|
+
height: 40px;
|
|
89
|
+
border: 0;
|
|
90
|
+
outline: none;
|
|
91
|
+
background: transparent;
|
|
92
|
+
color: inherit;
|
|
93
|
+
font: inherit;
|
|
94
|
+
font-size: 14px;
|
|
95
|
+
}
|
|
96
|
+
input::placeholder {
|
|
97
|
+
color: #9bad9f;
|
|
98
|
+
}
|
|
99
|
+
button {
|
|
100
|
+
display: grid;
|
|
101
|
+
place-items: center;
|
|
102
|
+
width: 36px;
|
|
103
|
+
height: 36px;
|
|
104
|
+
border: 0;
|
|
105
|
+
border-radius: 11px;
|
|
106
|
+
background: #84d88c;
|
|
107
|
+
color: #142319;
|
|
108
|
+
cursor: pointer;
|
|
109
|
+
transition: background 150ms ease;
|
|
110
|
+
}
|
|
111
|
+
button:hover {
|
|
112
|
+
background: #9be7a2;
|
|
113
|
+
}
|
|
114
|
+
button:focus-visible {
|
|
115
|
+
outline: 2px solid #c1fac8;
|
|
116
|
+
outline-offset: 3px;
|
|
117
|
+
}
|
|
118
|
+
button svg {
|
|
119
|
+
width: 18px;
|
|
120
|
+
height: 18px;
|
|
121
|
+
}
|
|
122
|
+
.hint {
|
|
123
|
+
margin: 24px 0 0;
|
|
124
|
+
color: #8ea493;
|
|
125
|
+
font-size: 12px;
|
|
126
|
+
text-align: center;
|
|
127
|
+
line-height: 20px;
|
|
128
|
+
}
|
|
129
|
+
.apps {
|
|
130
|
+
display: grid;
|
|
131
|
+
grid-template-columns: repeat(5, minmax(0, 1fr));
|
|
132
|
+
width: min(100%, 600px);
|
|
133
|
+
gap: 14px;
|
|
134
|
+
margin-top: 30px;
|
|
135
|
+
}
|
|
136
|
+
.app {
|
|
137
|
+
display: flex;
|
|
138
|
+
flex-direction: column;
|
|
139
|
+
justify-content: center;
|
|
140
|
+
gap: 9px;
|
|
141
|
+
width: 100%;
|
|
142
|
+
min-height: 94px;
|
|
143
|
+
height: auto;
|
|
144
|
+
padding: 12px 6px;
|
|
145
|
+
color: #e7eee8;
|
|
146
|
+
background: #1c2a2070;
|
|
147
|
+
border: 1px solid #6b877533;
|
|
148
|
+
border-radius: 16px;
|
|
149
|
+
font: inherit;
|
|
150
|
+
font-size: 11px;
|
|
151
|
+
}
|
|
152
|
+
.app:hover {
|
|
153
|
+
background: #314b38;
|
|
154
|
+
}
|
|
155
|
+
.app:disabled {
|
|
156
|
+
opacity: 0.6;
|
|
157
|
+
cursor: wait;
|
|
158
|
+
}
|
|
159
|
+
.app img {
|
|
160
|
+
object-fit: contain;
|
|
161
|
+
}
|
|
162
|
+
.app span {
|
|
163
|
+
max-width: 100%;
|
|
164
|
+
overflow-wrap: anywhere;
|
|
165
|
+
line-height: 16px;
|
|
166
|
+
}
|
|
167
|
+
@media (max-width: 480px) {
|
|
168
|
+
.apps {
|
|
169
|
+
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
170
|
+
gap: 10px;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
@keyframes arrive {
|
|
174
|
+
from {
|
|
175
|
+
opacity: 0;
|
|
176
|
+
transform: translateY(5px);
|
|
177
|
+
}
|
|
178
|
+
to {
|
|
179
|
+
opacity: 1;
|
|
180
|
+
transform: none;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
@media (max-width: 500px) {
|
|
184
|
+
header {
|
|
185
|
+
padding: 22px;
|
|
186
|
+
}
|
|
187
|
+
.workspace-label {
|
|
188
|
+
display: none;
|
|
189
|
+
}
|
|
190
|
+
main {
|
|
191
|
+
padding-inline: 22px;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
@media (max-height: 650px) {
|
|
195
|
+
header {
|
|
196
|
+
padding-block: 8px;
|
|
197
|
+
line-height: 20px;
|
|
198
|
+
}
|
|
199
|
+
.brand {
|
|
200
|
+
width: 105px;
|
|
201
|
+
height: 20px;
|
|
202
|
+
}
|
|
203
|
+
main {
|
|
204
|
+
min-height: calc(100dvh - 36px);
|
|
205
|
+
padding-block: 8px 12px;
|
|
206
|
+
}
|
|
207
|
+
#clock {
|
|
208
|
+
font-size: 52px;
|
|
209
|
+
line-height: 1;
|
|
210
|
+
letter-spacing: -2px;
|
|
211
|
+
}
|
|
212
|
+
#date {
|
|
213
|
+
margin: 2px 0 8px;
|
|
214
|
+
font-size: 12px;
|
|
215
|
+
line-height: 18px;
|
|
216
|
+
}
|
|
217
|
+
h1 {
|
|
218
|
+
margin-bottom: 12px;
|
|
219
|
+
font-size: 20px;
|
|
220
|
+
line-height: 26px;
|
|
221
|
+
}
|
|
222
|
+
form {
|
|
223
|
+
padding-block: 4px;
|
|
224
|
+
}
|
|
225
|
+
input {
|
|
226
|
+
height: 32px;
|
|
227
|
+
}
|
|
228
|
+
.apps {
|
|
229
|
+
width: min(100%, 660px);
|
|
230
|
+
gap: 8px 12px;
|
|
231
|
+
margin-top: 14px;
|
|
232
|
+
}
|
|
233
|
+
.app {
|
|
234
|
+
min-height: 80px;
|
|
235
|
+
gap: 6px;
|
|
236
|
+
padding-block: 6px;
|
|
237
|
+
}
|
|
238
|
+
.app img {
|
|
239
|
+
flex: none;
|
|
240
|
+
width: 44px;
|
|
241
|
+
height: 44px;
|
|
242
|
+
}
|
|
243
|
+
.hint {
|
|
244
|
+
margin-top: 8px;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
@media (prefers-reduced-motion: reduce) {
|
|
248
|
+
main {
|
|
249
|
+
animation: none;
|
|
250
|
+
}
|
|
251
|
+
form,
|
|
252
|
+
button {
|
|
253
|
+
transition: none;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
const clock = document.getElementById('clock');
|
|
2
|
+
const date = document.getElementById('date');
|
|
3
|
+
const timeFormat = new Intl.DateTimeFormat(undefined, { hour: '2-digit', minute: '2-digit', hourCycle: 'h23' });
|
|
4
|
+
const dateFormat = new Intl.DateTimeFormat(undefined, { weekday: 'long', month: 'long', day: 'numeric' });
|
|
5
|
+
function updateClock() {
|
|
6
|
+
const now = new Date();
|
|
7
|
+
clock.textContent = timeFormat.format(now);
|
|
8
|
+
clock.dateTime = now.toISOString();
|
|
9
|
+
date.textContent = dateFormat.format(now);
|
|
10
|
+
}
|
|
11
|
+
updateClock();
|
|
12
|
+
setInterval(updateClock, 1000);
|
|
13
|
+
document.getElementById('search').addEventListener('submit', (event) => {
|
|
14
|
+
const value = document.getElementById('query').value.trim();
|
|
15
|
+
if (!value) { event.preventDefault(); return; }
|
|
16
|
+
const candidate = /^[a-z][a-z\d+.-]*:/i.test(value) ? value : `https://${value}`;
|
|
17
|
+
try {
|
|
18
|
+
const url = new URL(candidate);
|
|
19
|
+
if (['https:', 'http:'].includes(url.protocol) && !value.includes(' ') && (url.hostname.includes('.') || url.hostname === 'localhost')) {
|
|
20
|
+
event.preventDefault();
|
|
21
|
+
location.assign(url.href);
|
|
22
|
+
}
|
|
23
|
+
} catch { /* Ordinary words and phrases go through the web search form. */ }
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const apps = document.getElementById('apps')
|
|
27
|
+
const status = document.getElementById('app-status')
|
|
28
|
+
async function nativeMessage(message) {
|
|
29
|
+
return await chrome.runtime.sendNativeMessage('org.namzu.apps', message)
|
|
30
|
+
}
|
|
31
|
+
async function showApplications() {
|
|
32
|
+
try {
|
|
33
|
+
const result = await nativeMessage({ type: 'list' })
|
|
34
|
+
if (!result?.ok || !Array.isArray(result.apps)) throw new Error('Catalogue unavailable')
|
|
35
|
+
for (const app of result.apps) {
|
|
36
|
+
const button = document.createElement('button')
|
|
37
|
+
button.type = 'button'
|
|
38
|
+
button.className = 'app'
|
|
39
|
+
const icon = document.createElement('img')
|
|
40
|
+
icon.src = app.icon
|
|
41
|
+
icon.alt = ''
|
|
42
|
+
icon.width = 44
|
|
43
|
+
icon.height = 44
|
|
44
|
+
const label = document.createElement('span')
|
|
45
|
+
label.textContent = app.name
|
|
46
|
+
button.append(icon, label)
|
|
47
|
+
button.addEventListener('click', async () => {
|
|
48
|
+
button.disabled = true
|
|
49
|
+
status.textContent = `Opening ${app.name}…`
|
|
50
|
+
try {
|
|
51
|
+
const result = await nativeMessage({ type: 'launch', appId: app.id })
|
|
52
|
+
status.textContent = result?.ok ? `${app.name} started` : `${app.name} could not be opened. Try again.`
|
|
53
|
+
} catch {
|
|
54
|
+
status.textContent = `${app.name} could not be opened. Try again.`
|
|
55
|
+
} finally {
|
|
56
|
+
button.disabled = false
|
|
57
|
+
}
|
|
58
|
+
})
|
|
59
|
+
apps.append(button)
|
|
60
|
+
}
|
|
61
|
+
} catch {
|
|
62
|
+
status.textContent = 'Application launchers are unavailable. The desktop dock is still available.'
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
void showApplications()
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"manifest_version": 3,
|
|
3
|
+
"name": "Namzu workspace",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"description": "A local New Tab page and installed application launchers for this Pal computer.",
|
|
6
|
+
"key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqXoJpqX+TnJeUjITQ6mFH8XMww1XambzeBOR5IJHng5i1GN7sVaKWv2fukjd59blBuW7k3Nu/Yqi+qfp652Zto0nPNCMobZCb/IYJX2xil1JrfOVddSxfJRBK1MMJC6fQpnRB6DZzS+R9yGKH5P4Ivxu070StVDcJ6FBhHZAWM0kyKzqQrYAfCWS1vQeY6Z8oMitNBEQZUKWFmMDBatxJJaXI9bwYJ/IABdf3d+sUiCkFkmXm/LHHNqXSCRCCn0+LPXDgbHjX7MY765+Fk8Ue4rtKkTKISox5xF7wZC/Bm8VkxeRh9xLc8x+jz2nqayU1RNkC52u2QikD4Biafz2KQIDAQAB",
|
|
7
|
+
"permissions": ["nativeMessaging"],
|
|
8
|
+
"chrome_url_overrides": { "newtab": "home.html" },
|
|
9
|
+
"content_security_policy": {
|
|
10
|
+
"extension_pages": "script-src 'self'; object-src 'none'; base-uri 'none'; frame-ancestors 'none'"
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 21 4" width="126" height="24" fill="#8ae592" shape-rendering="crispEdges">
|
|
2
|
+
<title>Namzu</title>
|
|
3
|
+
<!-- Exact CLI/desktop two-row block lettering: each half block is one cell. -->
|
|
4
|
+
<rect x="0" y="0" width="1" height="2" />
|
|
5
|
+
<rect x="1" y="1" width="1" height="1" />
|
|
6
|
+
<rect x="3" y="0" width="1" height="2" />
|
|
7
|
+
<rect x="5" y="1" width="1" height="1" />
|
|
8
|
+
<rect x="6" y="0" width="1" height="1" />
|
|
9
|
+
<rect x="7" y="0" width="1" height="2" />
|
|
10
|
+
<rect x="9" y="0" width="1" height="2" />
|
|
11
|
+
<rect x="10" y="0" width="1" height="1" />
|
|
12
|
+
<rect x="11" y="1" width="1" height="1" />
|
|
13
|
+
<rect x="12" y="0" width="1" height="1" />
|
|
14
|
+
<rect x="13" y="0" width="1" height="2" />
|
|
15
|
+
<rect x="15" y="0" width="1" height="1" />
|
|
16
|
+
<rect x="16" y="0" width="1" height="2" />
|
|
17
|
+
<rect x="18" y="0" width="1" height="2" />
|
|
18
|
+
<rect x="20" y="0" width="1" height="2" />
|
|
19
|
+
<rect x="0" y="2" width="1" height="2" />
|
|
20
|
+
<rect x="2" y="2" width="1" height="1" />
|
|
21
|
+
<rect x="3" y="2" width="1" height="2" />
|
|
22
|
+
<rect x="5" y="2" width="1" height="2" />
|
|
23
|
+
<rect x="6" y="2" width="1" height="1" />
|
|
24
|
+
<rect x="7" y="2" width="1" height="2" />
|
|
25
|
+
<rect x="9" y="2" width="1" height="2" />
|
|
26
|
+
<rect x="11" y="2" width="1" height="1" />
|
|
27
|
+
<rect x="13" y="2" width="1" height="2" />
|
|
28
|
+
<rect x="15" y="2" width="1" height="2" />
|
|
29
|
+
<rect x="16" y="3" width="1" height="1" />
|
|
30
|
+
<rect x="18" y="2" width="1" height="2" />
|
|
31
|
+
<rect x="19" y="3" width="1" height="1" />
|
|
32
|
+
<rect x="20" y="2" width="1" height="2" />
|
|
33
|
+
</svg>
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<openbox_config xmlns="http://openbox.org/3.4/rc">
|
|
3
|
+
<focus>
|
|
4
|
+
<focusNew>yes</focusNew><followMouse>no</followMouse><focusLast>yes</focusLast>
|
|
5
|
+
</focus>
|
|
6
|
+
<placement><policy>Smart</policy><center>yes</center><monitor>Primary</monitor></placement>
|
|
7
|
+
<theme>
|
|
8
|
+
<name>Onyx</name><titleLayout>NLIMC</titleLayout><keepBorder>yes</keepBorder>
|
|
9
|
+
<animateIconify>yes</animateIconify>
|
|
10
|
+
<font place="ActiveWindow"><name>DejaVu Sans</name><size>9</size><weight>normal</weight></font>
|
|
11
|
+
<font place="InactiveWindow"><name>DejaVu Sans</name><size>9</size><weight>normal</weight></font>
|
|
12
|
+
</theme>
|
|
13
|
+
<desktops><number>1</number><firstdesk>1</firstdesk><names><name>Namzu</name></names></desktops>
|
|
14
|
+
<margins><top>16</top><bottom>86</bottom><left>16</left><right>16</right></margins>
|
|
15
|
+
<resize><drawContents>yes</drawContents><popupShow>Nonpixel</popupShow></resize>
|
|
16
|
+
<keyboard>
|
|
17
|
+
<keybind key="A-F4"><action name="Close"/></keybind>
|
|
18
|
+
<keybind key="A-Tab"><action name="NextWindow"><finalactions><action name="Focus"/><action name="Raise"/></finalactions></action></keybind>
|
|
19
|
+
<keybind key="A-S-Tab"><action name="PreviousWindow"><finalactions><action name="Focus"/><action name="Raise"/></finalactions></action></keybind>
|
|
20
|
+
<keybind key="W-d"><action name="ToggleShowDesktop"/></keybind>
|
|
21
|
+
</keyboard>
|
|
22
|
+
<mouse>
|
|
23
|
+
<context name="Frame">
|
|
24
|
+
<mousebind button="A-Left" action="Press"><action name="Focus"/><action name="Raise"/></mousebind>
|
|
25
|
+
<mousebind button="A-Left" action="Drag"><action name="Move"/></mousebind>
|
|
26
|
+
<mousebind button="A-Right" action="Drag"><action name="Resize"/></mousebind>
|
|
27
|
+
</context>
|
|
28
|
+
<context name="Titlebar">
|
|
29
|
+
<mousebind button="Left" action="Press"><action name="Focus"/><action name="Raise"/></mousebind>
|
|
30
|
+
<mousebind button="Left" action="Drag"><action name="Move"/></mousebind>
|
|
31
|
+
<mousebind button="Left" action="DoubleClick"><action name="ToggleMaximize"/></mousebind>
|
|
32
|
+
</context>
|
|
33
|
+
<context name="Client"><mousebind button="Left" action="Press"><action name="Focus"/><action name="Raise"/></mousebind></context>
|
|
34
|
+
<context name="Iconify"><mousebind button="Left" action="Click"><action name="Iconify"/></mousebind></context>
|
|
35
|
+
<context name="Maximize"><mousebind button="Left" action="Click"><action name="ToggleMaximize"/></mousebind></context>
|
|
36
|
+
<context name="Close"><mousebind button="Left" action="Click"><action name="Close"/></mousebind></context>
|
|
37
|
+
</mouse>
|
|
38
|
+
<applications>
|
|
39
|
+
<application class="Chromium"><decor>yes</decor><maximized>no</maximized><fullscreen>no</fullscreen></application>
|
|
40
|
+
</applications>
|
|
41
|
+
</openbox_config>
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
const { copyFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } = require('node:fs')
|
|
2
|
+
const { basename, dirname, extname, join } = require('node:path')
|
|
3
|
+
const { STAGES, applicationsForStage, installedApplication } = require('./app-catalog.cjs')
|
|
4
|
+
const { EXTENSION_ORIGIN, HOST_NAME } = require('./app-native-host.cjs')
|
|
5
|
+
|
|
6
|
+
function findIcon(name, root = '/usr/share') {
|
|
7
|
+
if (name.startsWith('/')) {
|
|
8
|
+
if (name.startsWith(`${root}/`) && ['.png', '.svg'].includes(extname(name)) && existsSync(name))
|
|
9
|
+
return name
|
|
10
|
+
// Debian can name an XPM pixmap and ship a browser-readable theme icon
|
|
11
|
+
// for that same application. Copy that genuine installed asset instead.
|
|
12
|
+
if (name.startsWith(`${root}/`) && extname(name) === '.xpm')
|
|
13
|
+
return findIcon(basename(name, '.xpm'), root)
|
|
14
|
+
return undefined
|
|
15
|
+
}
|
|
16
|
+
if (!/^[a-zA-Z0-9_.-]+$/.test(name)) return undefined
|
|
17
|
+
for (const theme of ['hicolor', 'Adwaita']) {
|
|
18
|
+
for (const size of ['128x128', '96x96', '64x64', '48x48', 'scalable', '256x256', '32x32']) {
|
|
19
|
+
for (const category of ['apps', 'legacy', 'places'])
|
|
20
|
+
for (const extension of ['png', 'svg']) {
|
|
21
|
+
const file = join(root, 'icons', theme, size, category, `${name}.${extension}`)
|
|
22
|
+
if (existsSync(file)) return file
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
for (const extension of ['svg', 'png']) {
|
|
27
|
+
const file = join(root, 'pixmaps', `${name}.${extension}`)
|
|
28
|
+
if (existsSync(file)) return file
|
|
29
|
+
}
|
|
30
|
+
return undefined
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function desktopIcon(source) {
|
|
34
|
+
let entry = false
|
|
35
|
+
for (const line of source.split(/\r?\n/)) {
|
|
36
|
+
if (line.startsWith('[')) entry = line === '[Desktop Entry]'
|
|
37
|
+
if (entry && line.startsWith('Icon=')) return line.slice(5).trim()
|
|
38
|
+
}
|
|
39
|
+
return undefined
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function prepareHome(options = {}) {
|
|
43
|
+
const root = options.shareRoot ?? '/usr/share'
|
|
44
|
+
const destination = options.destination ?? join(__dirname, 'new-tab')
|
|
45
|
+
const stage = options.stage ?? STAGES[STAGES.length - 1]
|
|
46
|
+
const applications = applicationsForStage(stage)
|
|
47
|
+
const applicationExists =
|
|
48
|
+
options.applicationExists ?? ((app) => installedApplication(app.id, undefined, applications))
|
|
49
|
+
const catalogue = []
|
|
50
|
+
mkdirSync(join(destination, 'icons'), { recursive: true })
|
|
51
|
+
for (const app of applications) {
|
|
52
|
+
if (!applicationExists(app)) {
|
|
53
|
+
throw new Error(`Required guest application is missing: ${app.id}`)
|
|
54
|
+
}
|
|
55
|
+
const desktop = app.desktop.map((name) => join(root, 'applications', name)).find(existsSync)
|
|
56
|
+
if (!desktop) throw new Error(`Installed guest application entry is missing: ${app.id}`)
|
|
57
|
+
// A catalogue icon wins over the desktop entry's (the entry may name a plain pixmap).
|
|
58
|
+
const entryIcon = desktopIcon(readFileSync(desktop, 'utf8'))
|
|
59
|
+
const icon =
|
|
60
|
+
(app.icon && findIcon(app.icon, root)) ||
|
|
61
|
+
(entryIcon && findIcon(entryIcon, root)) ||
|
|
62
|
+
(app.fallbackIcon && findIcon(app.fallbackIcon, root))
|
|
63
|
+
if (!icon) throw new Error(`Installed guest application icon is missing: ${app.id}`)
|
|
64
|
+
const path = `icons/${app.id}${extname(icon)}`
|
|
65
|
+
copyFileSync(icon, join(destination, path))
|
|
66
|
+
catalogue.push({ id: app.id, name: app.name, icon: path })
|
|
67
|
+
}
|
|
68
|
+
writeFileSync(join(destination, 'apps.json'), JSON.stringify(catalogue))
|
|
69
|
+
// Runtime readers (the browser launcher, the native host) trust this marker, not a list of their own.
|
|
70
|
+
writeFileSync(options.stageFile ?? join(dirname(destination), 'stage'), `${stage}\n`)
|
|
71
|
+
return catalogue
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function installNativeManifest() {
|
|
75
|
+
const directory = '/etc/chromium/native-messaging-hosts'
|
|
76
|
+
mkdirSync(directory, { recursive: true })
|
|
77
|
+
writeFileSync(
|
|
78
|
+
join(directory, `${HOST_NAME}.json`),
|
|
79
|
+
JSON.stringify({
|
|
80
|
+
name: HOST_NAME,
|
|
81
|
+
description: 'Launch installed applications in this Pal guest',
|
|
82
|
+
path: '/opt/namzu-computer/app-native-host.cjs',
|
|
83
|
+
type: 'stdio',
|
|
84
|
+
allowed_origins: [EXTENSION_ORIGIN],
|
|
85
|
+
}),
|
|
86
|
+
)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Managed policy, so the guest browser never offers to sign in to a browser account. BrowserSignin
|
|
90
|
+
// alone leaves the toolbar's "Sign in to Chromium?" button; with the promotions off it is gone.
|
|
91
|
+
const BROWSER_POLICY = Object.freeze({
|
|
92
|
+
BrowserSignin: 0,
|
|
93
|
+
SyncDisabled: true,
|
|
94
|
+
PromotionsEnabled: false,
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
function installBrowserPolicy(directory = '/etc/chromium/policies/managed') {
|
|
98
|
+
mkdirSync(directory, { recursive: true })
|
|
99
|
+
writeFileSync(join(directory, 'namzu.json'), JSON.stringify(BROWSER_POLICY))
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function stageArgument(argv) {
|
|
103
|
+
const index = argv.indexOf('--stage')
|
|
104
|
+
const stage = index < 0 ? STAGES[STAGES.length - 1] : argv[index + 1]
|
|
105
|
+
if (!STAGES.includes(stage)) throw new Error(`Unknown image stage: ${stage}`)
|
|
106
|
+
return stage
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
module.exports = {
|
|
110
|
+
findIcon,
|
|
111
|
+
desktopIcon,
|
|
112
|
+
prepareHome,
|
|
113
|
+
installBrowserPolicy,
|
|
114
|
+
stageArgument,
|
|
115
|
+
BROWSER_POLICY,
|
|
116
|
+
}
|
|
117
|
+
if (require.main === module) {
|
|
118
|
+
prepareHome({ stage: stageArgument(process.argv.slice(2)) })
|
|
119
|
+
installNativeManifest()
|
|
120
|
+
installBrowserPolicy()
|
|
121
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Genuine guest launchers. The application entries are owned by entrypoint.sh.
|
|
2
|
+
rounded = 18
|
|
3
|
+
border_width = 1
|
|
4
|
+
background_color = #1c2720 100
|
|
5
|
+
border_color = #789882 45
|
|
6
|
+
background_color_hover = #263b2c 100
|
|
7
|
+
border_color_hover = #91bb9a 60
|
|
8
|
+
|
|
9
|
+
panel_items = L
|
|
10
|
+
panel_size = 196 60
|
|
11
|
+
panel_shrink = 1
|
|
12
|
+
panel_position = bottom center horizontal
|
|
13
|
+
panel_margin = 0 16
|
|
14
|
+
panel_padding = 10 7 10
|
|
15
|
+
panel_background_id = 1
|
|
16
|
+
panel_layer = top
|
|
17
|
+
panel_dock = 0
|
|
18
|
+
panel_window_name = Namzu dock
|
|
19
|
+
strut_policy = follow_size
|
|
20
|
+
disable_transparency = 1
|
|
21
|
+
autohide = 0
|
|
22
|
+
mouse_effects = 1
|
|
23
|
+
mouse_hover_icon_asb = 100 0 10
|
|
24
|
+
mouse_pressed_icon_asb = 100 0 -10
|
|
25
|
+
|
|
26
|
+
launcher_background_id = 0
|
|
27
|
+
launcher_icon_background_id = 0
|
|
28
|
+
launcher_padding = 0 0 10
|
|
29
|
+
launcher_icon_size = 42
|
|
30
|
+
launcher_icon_theme = Adwaita
|
|
31
|
+
launcher_icon_theme_override = 1
|
|
32
|
+
launcher_icon_asb = 100 0 0
|
|
33
|
+
launcher_tooltip = 1
|
|
34
|
+
startup_notifications = 0
|
|
35
|
+
launcher_item_app = /home/namzu/.local/share/applications/namzu-browser.desktop
|
|
36
|
+
launcher_item_app = /home/namzu/.local/share/applications/namzu-terminal.desktop
|
|
37
|
+
launcher_item_app = /home/namzu/.local/share/applications/namzu-files.desktop
|
|
38
|
+
|
|
39
|
+
tooltip_show_timeout = 0.3
|
|
40
|
+
tooltip_hide_timeout = 0.1
|
|
41
|
+
tooltip_padding = 8 5
|
|
42
|
+
tooltip_background_id = 1
|
|
43
|
+
tooltip_font = DejaVu Sans 10
|
|
44
|
+
tooltip_font_color = #e7eee8 100
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@namzu/sandbox",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "25.0.0",
|
|
4
4
|
"description": "Pluggable sandbox provider for @namzu/sdk. Process-level isolation (bubblewrap on Linux, Seatbelt on macOS, via @anthropic-ai/sandbox-runtime) for developer dev loops; container-level isolation (HTTP worker + a loopback-bound egress proxy that allows by resolved address and brokers outbound credentials so they never enter the sandbox) for multi-tenant production. Backend selected at construction time; the SDK consumes both through the same SandboxProvider interface.",
|
|
5
5
|
"license": "FSL-1.1-MIT",
|
|
6
6
|
"engines": {
|
|
@@ -22,6 +22,10 @@
|
|
|
22
22
|
".": {
|
|
23
23
|
"types": "./dist/index.d.ts",
|
|
24
24
|
"import": "./dist/index.js"
|
|
25
|
+
},
|
|
26
|
+
"./local-virtual-computer/setup": {
|
|
27
|
+
"types": "./dist/local-virtual-computer/setup/index.d.ts",
|
|
28
|
+
"import": "./dist/local-virtual-computer/setup/index.js"
|
|
25
29
|
}
|
|
26
30
|
},
|
|
27
31
|
"files": [
|
|
@@ -37,6 +41,8 @@
|
|
|
37
41
|
"!src/**/__tests__",
|
|
38
42
|
"!src/**/__fixtures__",
|
|
39
43
|
"!src/**/test-setup.*",
|
|
44
|
+
"local-computer",
|
|
45
|
+
"worker/server.js",
|
|
40
46
|
"LICENSE.md",
|
|
41
47
|
"README.md",
|
|
42
48
|
"CHANGELOG.md"
|
|
@@ -46,7 +52,10 @@
|
|
|
46
52
|
"provenance": true
|
|
47
53
|
},
|
|
48
54
|
"peerDependencies": {
|
|
49
|
-
"@namzu/sdk": "^
|
|
55
|
+
"@namzu/sdk": "^50.0.0"
|
|
56
|
+
},
|
|
57
|
+
"dependencies": {
|
|
58
|
+
"ws": "8.21.3"
|
|
50
59
|
},
|
|
51
60
|
"devDependencies": {
|
|
52
61
|
"@biomejs/biome": "^1.9.4",
|
|
@@ -54,12 +63,12 @@
|
|
|
54
63
|
"typescript": "^5.5.0",
|
|
55
64
|
"vitest": "^4.1.11",
|
|
56
65
|
"yaml": "^2.9.0",
|
|
57
|
-
"@namzu/sdk": "^
|
|
66
|
+
"@namzu/sdk": "^50.0.0"
|
|
58
67
|
},
|
|
59
68
|
"scripts": {
|
|
60
69
|
"build": "tsc --build",
|
|
61
70
|
"dev": "tsc --build --watch",
|
|
62
|
-
"lint": "biome check src/ worker/ agent/ k8s/ egress-proxy/",
|
|
71
|
+
"lint": "biome check src/ worker/ agent/ k8s/ egress-proxy/ local-computer/",
|
|
63
72
|
"lint:fix": "biome check --write src/",
|
|
64
73
|
"format": "biome format --write src/",
|
|
65
74
|
"test": "vitest run --passWithNoTests",
|