@namzu/sandbox 24.0.0 → 26.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.
Files changed (158) hide show
  1. package/CHANGELOG.md +284 -0
  2. package/dist/backends/http-worker-client.d.ts +3 -1
  3. package/dist/backends/http-worker-client.d.ts.map +1 -1
  4. package/dist/backends/http-worker-client.js +15 -4
  5. package/dist/backends/http-worker-client.js.map +1 -1
  6. package/dist/index.d.ts +2 -0
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +1 -0
  9. package/dist/index.js.map +1 -1
  10. package/dist/local-virtual-computer/client.d.ts +21 -0
  11. package/dist/local-virtual-computer/client.d.ts.map +1 -0
  12. package/dist/local-virtual-computer/client.js +503 -0
  13. package/dist/local-virtual-computer/client.js.map +1 -0
  14. package/dist/local-virtual-computer/detached-process.d.ts +15 -0
  15. package/dist/local-virtual-computer/detached-process.d.ts.map +1 -0
  16. package/dist/local-virtual-computer/detached-process.js +111 -0
  17. package/dist/local-virtual-computer/detached-process.js.map +1 -0
  18. package/dist/local-virtual-computer/detached-worker.d.ts +11 -0
  19. package/dist/local-virtual-computer/detached-worker.d.ts.map +1 -0
  20. package/dist/local-virtual-computer/detached-worker.js +97 -0
  21. package/dist/local-virtual-computer/detached-worker.js.map +1 -0
  22. package/dist/local-virtual-computer/dialect.d.ts +26 -0
  23. package/dist/local-virtual-computer/dialect.d.ts.map +1 -0
  24. package/dist/local-virtual-computer/dialect.js +61 -0
  25. package/dist/local-virtual-computer/dialect.js.map +1 -0
  26. package/dist/local-virtual-computer/engine.d.ts +63 -0
  27. package/dist/local-virtual-computer/engine.d.ts.map +1 -0
  28. package/dist/local-virtual-computer/engine.js +125 -0
  29. package/dist/local-virtual-computer/engine.js.map +1 -0
  30. package/dist/local-virtual-computer/guest-stdio.d.ts +15 -0
  31. package/dist/local-virtual-computer/guest-stdio.d.ts.map +1 -0
  32. package/dist/local-virtual-computer/guest-stdio.js +330 -0
  33. package/dist/local-virtual-computer/guest-stdio.js.map +1 -0
  34. package/dist/local-virtual-computer/index.d.ts +36 -0
  35. package/dist/local-virtual-computer/index.d.ts.map +1 -0
  36. package/dist/local-virtual-computer/index.js +341 -0
  37. package/dist/local-virtual-computer/index.js.map +1 -0
  38. package/dist/local-virtual-computer/podman-machine.d.ts +17 -0
  39. package/dist/local-virtual-computer/podman-machine.d.ts.map +1 -0
  40. package/dist/local-virtual-computer/podman-machine.js +181 -0
  41. package/dist/local-virtual-computer/podman-machine.js.map +1 -0
  42. package/dist/local-virtual-computer/setup/contract.d.ts +360 -0
  43. package/dist/local-virtual-computer/setup/contract.d.ts.map +1 -0
  44. package/dist/local-virtual-computer/setup/contract.js +3 -0
  45. package/dist/local-virtual-computer/setup/contract.js.map +1 -0
  46. package/dist/local-virtual-computer/setup/elevated.d.ts +78 -0
  47. package/dist/local-virtual-computer/setup/elevated.d.ts.map +1 -0
  48. package/dist/local-virtual-computer/setup/elevated.js +548 -0
  49. package/dist/local-virtual-computer/setup/elevated.js.map +1 -0
  50. package/dist/local-virtual-computer/setup/host.d.ts +12 -0
  51. package/dist/local-virtual-computer/setup/host.d.ts.map +1 -0
  52. package/dist/local-virtual-computer/setup/host.js +93 -0
  53. package/dist/local-virtual-computer/setup/host.js.map +1 -0
  54. package/dist/local-virtual-computer/setup/idle.d.ts +18 -0
  55. package/dist/local-virtual-computer/setup/idle.d.ts.map +1 -0
  56. package/dist/local-virtual-computer/setup/idle.js +38 -0
  57. package/dist/local-virtual-computer/setup/idle.js.map +1 -0
  58. package/dist/local-virtual-computer/setup/image.d.ts +7 -0
  59. package/dist/local-virtual-computer/setup/image.d.ts.map +1 -0
  60. package/dist/local-virtual-computer/setup/image.js +124 -0
  61. package/dist/local-virtual-computer/setup/image.js.map +1 -0
  62. package/dist/local-virtual-computer/setup/index.d.ts +17 -0
  63. package/dist/local-virtual-computer/setup/index.d.ts.map +1 -0
  64. package/dist/local-virtual-computer/setup/index.js +17 -0
  65. package/dist/local-virtual-computer/setup/index.js.map +1 -0
  66. package/dist/local-virtual-computer/setup/limits.d.ts +30 -0
  67. package/dist/local-virtual-computer/setup/limits.d.ts.map +1 -0
  68. package/dist/local-virtual-computer/setup/limits.js +58 -0
  69. package/dist/local-virtual-computer/setup/limits.js.map +1 -0
  70. package/dist/local-virtual-computer/setup/plan.d.ts +8 -0
  71. package/dist/local-virtual-computer/setup/plan.d.ts.map +1 -0
  72. package/dist/local-virtual-computer/setup/plan.js +131 -0
  73. package/dist/local-virtual-computer/setup/plan.js.map +1 -0
  74. package/dist/local-virtual-computer/setup/readiness.d.ts +11 -0
  75. package/dist/local-virtual-computer/setup/readiness.d.ts.map +1 -0
  76. package/dist/local-virtual-computer/setup/readiness.js +320 -0
  77. package/dist/local-virtual-computer/setup/readiness.js.map +1 -0
  78. package/dist/local-virtual-computer/setup/registry.d.ts +18 -0
  79. package/dist/local-virtual-computer/setup/registry.d.ts.map +1 -0
  80. package/dist/local-virtual-computer/setup/registry.js +50 -0
  81. package/dist/local-virtual-computer/setup/registry.js.map +1 -0
  82. package/dist/local-virtual-computer/setup/restart.d.ts +8 -0
  83. package/dist/local-virtual-computer/setup/restart.d.ts.map +1 -0
  84. package/dist/local-virtual-computer/setup/restart.js +60 -0
  85. package/dist/local-virtual-computer/setup/restart.js.map +1 -0
  86. package/dist/local-virtual-computer/setup/resume.d.ts +14 -0
  87. package/dist/local-virtual-computer/setup/resume.d.ts.map +1 -0
  88. package/dist/local-virtual-computer/setup/resume.js +58 -0
  89. package/dist/local-virtual-computer/setup/resume.js.map +1 -0
  90. package/dist/local-virtual-computer/setup/run.d.ts +8 -0
  91. package/dist/local-virtual-computer/setup/run.d.ts.map +1 -0
  92. package/dist/local-virtual-computer/setup/run.js +200 -0
  93. package/dist/local-virtual-computer/setup/run.js.map +1 -0
  94. package/dist/local-virtual-computer/setup/windows-folder.d.ts +11 -0
  95. package/dist/local-virtual-computer/setup/windows-folder.d.ts.map +1 -0
  96. package/dist/local-virtual-computer/setup/windows-folder.js +14 -0
  97. package/dist/local-virtual-computer/setup/windows-folder.js.map +1 -0
  98. package/dist/local-virtual-computer/setup/wsl-package.d.ts +19 -0
  99. package/dist/local-virtual-computer/setup/wsl-package.d.ts.map +1 -0
  100. package/dist/local-virtual-computer/setup/wsl-package.js +242 -0
  101. package/dist/local-virtual-computer/setup/wsl-package.js.map +1 -0
  102. package/dist/local-virtual-computer/setup/wslc-shared.d.ts +27 -0
  103. package/dist/local-virtual-computer/setup/wslc-shared.d.ts.map +1 -0
  104. package/dist/local-virtual-computer/setup/wslc-shared.js +137 -0
  105. package/dist/local-virtual-computer/setup/wslc-shared.js.map +1 -0
  106. package/dist/local-virtual-computer/ssh-forward.d.ts +19 -0
  107. package/dist/local-virtual-computer/ssh-forward.d.ts.map +1 -0
  108. package/dist/local-virtual-computer/ssh-forward.js +179 -0
  109. package/dist/local-virtual-computer/ssh-forward.js.map +1 -0
  110. package/dist/local-virtual-computer/wslc-engine.d.ts +27 -0
  111. package/dist/local-virtual-computer/wslc-engine.d.ts.map +1 -0
  112. package/dist/local-virtual-computer/wslc-engine.js +98 -0
  113. package/dist/local-virtual-computer/wslc-engine.js.map +1 -0
  114. package/local-computer/Dockerfile +99 -0
  115. package/local-computer/app-catalog.cjs +240 -0
  116. package/local-computer/app-native-host.cjs +144 -0
  117. package/local-computer/browser-home.cjs +249 -0
  118. package/local-computer/build-image.mjs +310 -0
  119. package/local-computer/desktop-worker.cjs +477 -0
  120. package/local-computer/entrypoint.sh +161 -0
  121. package/local-computer/home.html +28 -0
  122. package/local-computer/new-tab/home.css +255 -0
  123. package/local-computer/new-tab/home.js +65 -0
  124. package/local-computer/new-tab/manifest.json +12 -0
  125. package/local-computer/new-tab/wordmark.svg +33 -0
  126. package/local-computer/openbox-rc.xml +41 -0
  127. package/local-computer/prepare-home.cjs +121 -0
  128. package/local-computer/tint2rc +44 -0
  129. package/package.json +13 -4
  130. package/src/backends/http-worker-client.ts +31 -3
  131. package/src/index.ts +13 -0
  132. package/src/local-virtual-computer/client.ts +573 -0
  133. package/src/local-virtual-computer/detached-process.ts +133 -0
  134. package/src/local-virtual-computer/detached-worker.ts +124 -0
  135. package/src/local-virtual-computer/dialect.ts +83 -0
  136. package/src/local-virtual-computer/engine.ts +197 -0
  137. package/src/local-virtual-computer/guest-stdio.ts +332 -0
  138. package/src/local-virtual-computer/index.ts +447 -0
  139. package/src/local-virtual-computer/podman-machine.ts +264 -0
  140. package/src/local-virtual-computer/setup/contract.ts +395 -0
  141. package/src/local-virtual-computer/setup/elevated.ts +655 -0
  142. package/src/local-virtual-computer/setup/host.ts +100 -0
  143. package/src/local-virtual-computer/setup/idle.ts +39 -0
  144. package/src/local-virtual-computer/setup/image.ts +159 -0
  145. package/src/local-virtual-computer/setup/index.ts +16 -0
  146. package/src/local-virtual-computer/setup/limits.ts +68 -0
  147. package/src/local-virtual-computer/setup/plan.ts +144 -0
  148. package/src/local-virtual-computer/setup/readiness.ts +386 -0
  149. package/src/local-virtual-computer/setup/registry.ts +50 -0
  150. package/src/local-virtual-computer/setup/restart.ts +64 -0
  151. package/src/local-virtual-computer/setup/resume.ts +65 -0
  152. package/src/local-virtual-computer/setup/run.ts +225 -0
  153. package/src/local-virtual-computer/setup/windows-folder.ts +13 -0
  154. package/src/local-virtual-computer/setup/wsl-package.ts +242 -0
  155. package/src/local-virtual-computer/setup/wslc-shared.ts +147 -0
  156. package/src/local-virtual-computer/ssh-forward.ts +195 -0
  157. package/src/local-virtual-computer/wslc-engine.ts +124 -0
  158. 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": "24.0.0",
3
+ "version": "26.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": "^49.0.0"
55
+ "@namzu/sdk": "^51.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": "^49.0.0"
66
+ "@namzu/sdk": "^51.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",