@daugasauron/dolly 0.1.4 → 0.1.5

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 (60) hide show
  1. package/README.md +134 -14
  2. package/coi-serviceworker.js +28 -0
  3. package/dist/dolly-build-id.mjs +1 -1
  4. package/dist/dolly-dso-0.wasm +0 -0
  5. package/dist/dolly-image-build-id.mjs +1 -1
  6. package/dist/dolly-process-0.wasm +0 -0
  7. package/dist/dolly-process-abi.mjs +1 -1
  8. package/dist/dolly-process-dso.mjs +382 -53
  9. package/dist/dolly-process-gate-0.wasm +0 -0
  10. package/dist/dolly-process-worker.mjs +164 -35
  11. package/dist/dolly.wasm +0 -0
  12. package/host/dso/abi.mjs +3 -1
  13. package/host/dso/dso.mjs +1 -2
  14. package/host/http/abi.mjs +2 -2
  15. package/host/http/broker.mjs +18 -8
  16. package/host/http/http.mjs +0 -1
  17. package/host/http/policy.mjs +8 -4
  18. package/host/input/abi.mjs +47 -0
  19. package/host/modules.mjs +4 -3
  20. package/host/nena/abi.mjs +1 -1
  21. package/host/nena/nena.mjs +16 -18
  22. package/host/packages/service.mjs +35 -15
  23. package/host/packages/worker.mjs +21 -0
  24. package/host/runtime/abi.mjs +1 -0
  25. package/host/runtime/boot-files.mjs +8 -2
  26. package/host/runtime/grower.mjs +11 -8
  27. package/host/runtime/lease-bridge.mjs +8 -5
  28. package/host/runtime/module.json +1 -1
  29. package/host/runtime/runtime.mjs +37 -18
  30. package/host/snapshot/abi.mjs +7 -9
  31. package/host/snapshot/snapshot.mjs +14 -12
  32. package/host/snapshot/transport.mjs +4 -12
  33. package/host/snapshot/ui.mjs +17 -7
  34. package/host/stack/stack.mjs +1 -2
  35. package/host/threads/abi.mjs +1 -1
  36. package/host/threads/threads.mjs +2 -1
  37. package/package.json +1 -1
  38. package/src/browser.mjs +34 -37
  39. package/src/build-log.mjs +13 -2
  40. package/src/build-worker.mjs +182 -0
  41. package/src/custom-image.mjs +59 -0
  42. package/src/dollyfile-graph.mjs +127 -0
  43. package/src/dollyfile-view.mjs +335 -0
  44. package/src/image-artifact.mjs +248 -0
  45. package/src/image-build.mjs +89 -0
  46. package/src/image-builder.mjs +63 -0
  47. package/src/images.mjs +13 -2
  48. package/src/pack-store.mjs +141 -0
  49. package/src/packaged-snapshot.mjs +263 -91
  50. package/src/page-chords.mjs +4 -2
  51. package/src/process-constants.mjs +4 -2
  52. package/src/process-supervisor.mjs +209 -122
  53. package/src/runtime-worker.mjs +9 -10
  54. package/src/session-store.mjs +5 -7
  55. package/src/sha256.mjs +65 -0
  56. package/src/snapshot-records.mjs +135 -23
  57. package/src/static-asset.mjs +29 -2
  58. package/src/terminal-text.mjs +8 -5
  59. package/src/version.mjs +1 -1
  60. package/src/worker-image.mjs +3 -5
package/README.md CHANGED
@@ -7,21 +7,22 @@ an agent in place of a web frontend (`docs/agent-frontend.md` in the repository)
7
7
 
8
8
  | Package | What it holds |
9
9
  | --- | --- |
10
- | `@daugasauron/dolly` | The runtime: `boot()`, the kernel, the process runtime, and the host modules that are its own machinery (`runtime`, `http`, `threads`, `dso`, `stack`, `nena`, `snapshot`, `packages`). Building images stays with Dolly's site |
11
- | `@daugasauron/dolly-host-NAME` | One device host module each: `display` (Ghostty's terminal and programs' frames), `input`, `gpu`, `audio`, `microphone`, `touch`, `download` (as `dolly-host-save`: npm takes no new name with the word), `upload`, `sockets`, `pty` |
12
- | `@daugasauron/dolly-image-default` | The `default` image alone: a shell, its text tools, the terminal, curl, `amy`, `nena`, `ps`. `amy` lists what is installed and has nothing more to install |
13
- | `@daugasauron/dolly-image-pi-minimal` | The `pi-minimal` image alone: the Pi coding agent with JavaScript (Janis), a shell, POSIX tools, curl, ripgrep and fd, on `display`, `input` and `download` alone. The README's "Pi with npm" serves it with a policy |
10
+ | `@daugasauron/dolly` | The runtime: `boot()`, the kernel, the process runtime, its image builder, and the host modules that are its own machinery (`runtime`, `http`, `threads`, `dso`, `stack`, `nena`, `snapshot`, `packages`) |
11
+ | `@daugasauron/dolly-host-NAME` | One device host module each: `display` (Ghostty's terminal and programs' frames), `input`, `gpu`, `audio`, `microphone`, `touch`, `download` (as `dolly-host-save`: npm takes no new name with the word), `upload`, `html`, `sockets`, `pty` |
12
+ | `@daugasauron/dolly-image-NAME` | An image's directory for `boot()`: `default` (a shell, its text tools, the terminal, curl, `amy`, `nena`, `ps`, `html`), `pi-minimal` (the Pi coding agent with JavaScript, a shell, POSIX tools, curl, ripgrep and fd, on `display`, `input`, `download` and `html` alone: the README's "Pi with npm"), `python-data` (`default` with Python 3.14, pip, the C compiler, NumPy, pandas, Matplotlib and seaborn). Its README gives the install line and the `FROM` row of a recipe built on it. Its `amy-index.txt` lists the packages the image holds, so `amy list` and `amy info` describe them |
13
+ | `@daugasauron/dolly-package-NAME` | A package for an image of your own: `git` (with diff and patch), `cc` (the C and C++ compiler with Make), `python` (3.14 with pip; it brings `libffi` and `cc`), `numpy`, `pandas`, `matplotlib`, `seaborn` and `python-dateutil`. Its directory holds its catalog, metadata, packs and recipes, as an image's does, and nothing to boot; it depends on the packages it installs, so npm installs them with it |
14
+ | `@daugasauron/dolly-build` | The `dolly-build` command: a project with an image of your own, `FROM` `pi-minimal` with the packages it `INSTALL`s, built in a browser into a site a static host serves ("Your own image" below) |
14
15
 
15
16
  ```sh
16
17
  npm install @daugasauron/dolly @daugasauron/dolly-image-default \
17
- @daugasauron/dolly-host-display @daugasauron/dolly-host-input @daugasauron/dolly-host-audio \
18
- @daugasauron/dolly-host-touch @daugasauron/dolly-host-save @daugasauron/dolly-host-upload \
19
- @daugasauron/dolly-host-sockets
18
+ @daugasauron/dolly-host-display @daugasauron/dolly-host-input @daugasauron/dolly-host-save \
19
+ @daugasauron/dolly-host-upload @daugasauron/dolly-host-html
20
20
  ```
21
21
 
22
- Each host module and image depends on the runtime alone, at its own version. An
23
- image does not depend on the host modules it declares: install them with it (its
24
- README names them), so that all lie side by side under `node_modules/@daugasauron/`.
22
+ Each host module and image depends on the runtime alone, at its own version, and
23
+ a package also on the packages it installs. An image or package does not depend on
24
+ the host modules it declares: install them with it (its README names them), so
25
+ that all lie side by side under `node_modules/@daugasauron/`.
25
26
  npm, pnpm and Yarn 1 install them so. Yarn 2 and later need `nodeLinker: node-modules`
26
27
  in `.yarnrc.yml`: Plug'n'Play leaves no files to serve.
27
28
 
@@ -42,16 +43,20 @@ in `.yarnrc.yml`: Plug'n'Play leaves no files to serve.
42
43
  resolves once the image's program starts, to the page's handle on the session
43
44
  (`hostModules`, `visibleTerminalText()`, `waitForInteractiveTerminal(pattern)`,
44
45
  then `submit(command)` at the shell's prompt, and what each module adds), and
45
- shows its own failure in the element.
46
+ shows its own failure in the element. What Dolly shows, its modules' buttons,
47
+ dialogs and indicators among them, stays inside `element`, and its styles
48
+ select only there; keys typed outside it, into the page's own controls, are
49
+ the page's.
46
50
 
47
51
  | Option | Meaning |
48
52
  | --- | --- |
49
- | `images` | URL of an image directory: `dist/dolly-images.mjs`, the images' metadata and packs, their recipes. `@daugasauron/dolly-image-default`'s `index.mjs` exports its own. A page has one image directory |
53
+ | `images` | URL of an image directory: `dist/dolly-images.json`, the images' metadata and packs, their recipes, all data. `@daugasauron/dolly-image-default`'s `index.mjs` exports its own. A page has one image directory. The packs an image reads stay in the origin's private storage, so it starts again without downloading them (`src/pack-store.mjs`) |
50
54
  | `image` | The image to run, by name |
51
55
  | `modules` | URLs of the `module.json` of each host module the page has. The registry loads these and no other; an image's `REQUIRES HOST` must be among them |
52
- | `site` | Optional: the URL of a site that serves Dolly's own pages (`session/`, `sessions/`, `docs/`), as Dolly's site does. Given, a saved session names itself in the tab's URL and modules link to those pages |
56
+ | `site` | Optional: the URL of a site that serves Dolly's own pages (`session/`, `sessions/`, `docs/`), as Dolly's site does. Given, a saved session names itself in the tab's URL as the site's session route and modules link to those pages |
53
57
  | `http` | Optional `{ policy, relays }` for the HTTP broker, the page's one network edge (docs/http.md). Without a policy a program may fetch any HTTPS URL |
54
58
  | `files` | Optional `{ "/absolute/path": text or Uint8Array }`, written over the image before its program starts: Pi's instructions and skills (`docs/agent-frontend.md`), a start script. Programs can read them; they are not for secrets |
59
+ | `session` | Optional: the name of a session saved in this browser, opened as it was saved in place of a fresh start (`files` are not written over it). It needs `snapshot@0`, whose Save (Ctrl+Shift+S) keeps a session in the browser's storage for the page's origin and, on a page without a `site`, names it in the tab's address as `?session=NAME` for the page to give back here |
55
60
 
56
61
  The page, not the image, chooses the modules and the policy, before anything of
57
62
  the image runs. A program reaches nothing of the browser but through them.
@@ -73,6 +78,49 @@ const dolly = await boot(element, { images, image, modules: [...modules, gpu] })
73
78
  `nena enable gpu` then gives a program the GPU, or, where the browser has no
74
79
  WebGPU adapter, a device that fails to open with `ENODEV`.
75
80
 
81
+ ## Colours
82
+
83
+ The terminal's colours and the popups' are the page's, not the image's. Set
84
+ these on the element (or above it) in your style sheet to match your page;
85
+ unset, they are Dolly's:
86
+
87
+ ```css
88
+ #dolly {
89
+ --dolly-background: #262626; /* the terminal's and the popups' */
90
+ --dolly-color: #e8e3d7; /* their text */
91
+ --dolly-accent: #f2d45c; /* the cursor and the selection; the popups' headings, buttons, focus */
92
+ --dolly-display-palette-1: #e8e3d7; /* -0 to -15: the 16 colours programs name */
93
+ }
94
+ ```
95
+
96
+ Unset, of the 16 colours programs name, black (`-0`) is the background, yellow
97
+ and bright yellow (`-3`, `-11`) the accent, bright black (`-8`) `#77736c` and
98
+ the others the text's colour, so a program's red shows as its text does. A
99
+ value may be any CSS colour; one that is not counts as unset. The terminal
100
+ takes them at boot and whenever they change, by any rule or media query; a
101
+ popup as it opens.
102
+
103
+ ## Popups
104
+
105
+ `html@0` shows a program's HTML document in a popup over the terminal, in
106
+ boot()'s element: a report, or a form whose submitted fields go back to the
107
+ program (Pi's `ask_user` and `show_html`). The popup takes the document's text,
108
+ headings, lists, tables and forms and nothing else of it; its look is the
109
+ page's: the colours above and these, unset Dolly's:
110
+
111
+ ```css
112
+ #dolly {
113
+ --dolly-html-font: ui-monospace, monospace;
114
+ --dolly-html-color-scheme: dark; /* the form controls' own colours */
115
+ --dolly-html-font-file: none; /* or url(…): the font of the first family above */
116
+ }
117
+ ```
118
+
119
+ The popup loads nothing itself: the page fetches the font file, once, and hands
120
+ the popup its bytes; without one, name fonts the system has. The page reads the
121
+ properties each time a popup opens. Dolly's site sets
122
+ `"Dolly IosevkaTerm SemiBold", monospace` and its font file.
123
+
76
124
  ## Serving
77
125
 
78
126
  - Serve the installed packages as they lie: the runtime finds its own files
@@ -81,9 +129,81 @@ WebGPU adapter, a device that fails to open with `ENODEV`.
81
129
  - Send `Cross-Origin-Opener-Policy: same-origin` and
82
130
  `Cross-Origin-Embedder-Policy: require-corp` with the page: Dolly needs shared
83
131
  memory, which a browser gives only to a cross-origin isolated page.
84
- - Serve `.wasm` as `application/wasm` and `.mjs` as `text/javascript`.
132
+ - Serve `.wasm` as `application/wasm`, `.mjs` as `text/javascript` and `.json`
133
+ as `application/json`: a browser imports a host module's manifest and an image
134
+ directory's catalog and metadata only with that type.
85
135
  - No bundler is needed: these are ES modules, Workers and Wasm files.
86
136
 
137
+ ## Your own image
138
+
139
+ `dolly-build` builds a Dollyfile (`docs/dollyfile.md` in the repository)
140
+ `FROM` the installed `pi-minimal`, with the packages whose directories are
141
+ installed beside it, into a site: a directory that any static host serves as it
142
+ lies. The build runs in a headless browser, with the installed runtime's own
143
+ builder, as the catalog's images are built; the command serves the packages to
144
+ it on 127.0.0.1 and writes what it hands back. Nothing of it, nor of the site,
145
+ the page or the image, fetches from the network: after `npm install`, the
146
+ packages hold every file. The build's browser reaches that server and the URLs
147
+ of the recipe's own `SOURCE` rows alone; its own requests (its vendor's updates,
148
+ clock and autofill) go to a proxy of the command's that refuses them.
149
+
150
+ ```sh
151
+ npm install @daugasauron/dolly-build @daugasauron/dolly-package-git
152
+ npx dolly-build init my-pi git
153
+ npx dolly-build Dollyfile-my-pi my-pi
154
+ ```
155
+
156
+ - `dolly-build init NAME [PACKAGE…]` writes a project where the packages are
157
+ installed, and refuses to write over a file: `Dollyfile-NAME` (the host modules
158
+ of `pi-minimal` and of each package, `FROM` `pi-minimal` and `INSTALL` of each
159
+ package at the installed pins, rows for Pi's instructions, a skill, an extension,
160
+ a tool and the script that starts Pi, and `pi-minimal`'s `ENTRY`) and `public/`,
161
+ the page's files: `index.html` (the policy, the popups' font, and the files of
162
+ `workspace/` and `theme.json` for `boot()`'s `files`, listed by `workspace.json`),
163
+ `theme.json` (the sandbox's colours: Pi's theme, written over the image's at
164
+ boot, whose background, text, accent and muted the page sets as the terminal's
165
+ and the popups') and `workspace/README.md`; and, unless the project has one, a
166
+ `.gitignore` for `node_modules/` and the site. It names the host module
167
+ package to install when a package needs one the page lacks (`python`'s `sockets`).
168
+ The repository README's "Pi with npm" says what each file is for.
169
+ - The recipe is the one place that declares the sandbox. Add rows before `ENTRY`:
170
+ `FILE` with a body (indented four spaces) for a file, `INSTALL` of a package
171
+ whose directory is installed (every package it needs comes with it, so `python`
172
+ brings `cc`), `SLOP` to run the image's tools at build time. `FROM` carries the
173
+ base's files, environment and exports but not its host modules or `ENTRY`, so the
174
+ recipe repeats those; a package's `REQUIRES HOST` rows must be among the recipe's,
175
+ and the build names a missing one. A recipe is at most 128 KiB; `/workspace` is
176
+ never part of an image (`boot()`'s `files` writes the project at each boot).
177
+ - `dolly-build Dollyfile-NAME OUT` writes the site `OUT`, replacing the one it
178
+ wrote there before: the files of `public/`, `workspace.json` (the files under
179
+ `public/workspace/`), the runtime (`dolly/`), the device host modules the recipe
180
+ declares (`dolly-host-NAME/`), `coi-serviceworker.js` and `image/`, the image's
181
+ directory as an image package lays it out (its catalog, metadata and packs, the
182
+ recipes it was built from, its `amy-index.txt` and an `index.mjs`). The page
183
+ imports `./dolly/src/browser.mjs` and `./image/index.mjs`. An image this recipe
184
+ built on this runtime is kept, so a change to `public/` alone takes seconds.
185
+ `--browser chrome` (the default: your Chrome), `chromium` or `firefox`
186
+ (Playwright's, after `npx playwright-core install chromium` or `firefox`).
187
+ Both commands run where the packages are installed: the directory whose
188
+ `node_modules/@daugasauron/` holds them (npm, pnpm and Yarn 1 alike). A row
189
+ that names an image no installed package holds at that pin fails naming the row.
190
+ - Serve `OUT` with any static file server, `python3 -m http.server -d OUT` on
191
+ your machine. Where the host sends the isolation headers ("Serving" above) the
192
+ page is isolated; where it does not, `init`'s page registers
193
+ `coi-serviceworker.js`, which adds them to the site's own responses, and loads
194
+ again under it.
195
+ - A recipe may start `FROM` another installed image instead, `python-data` say: its
196
+ package's README gives the `FROM` row, and the recipe repeats the image's `REQUIRES HOST`
197
+ rows (a FILE of the project's own script and a SLOP that runs it at build time, for
198
+ example). `init` writes Pi's project only; the page in `public/index.html` is then one
199
+ as in "A page" above that imports `./dolly/src/browser.mjs` and `./image/index.mjs`,
200
+ with the lines of `init`'s page that register `coi-serviceworker.js` for a host
201
+ without the headers.
202
+ - An image is built for the installed versions: after the packages update, the
203
+ rows' pins are others (`init` in an empty directory writes the new ones), and it
204
+ is built again. Packages of two versions in one `node_modules` fail the build,
205
+ naming both.
206
+
87
207
  ## Host modules
88
208
 
89
209
  A host module is a directory with a `module.json`; its provider receives what it
@@ -0,0 +1,28 @@
1
+ // GitHub Pages cannot configure COOP/COEP response headers. This worker adds
2
+ // them only to Dolly's same-origin static responses so shared wasm64 memory is
3
+ // available there. Cross-origin requests, including the explicit HTTP broker,
4
+ // pass through unchanged.
5
+ self.addEventListener("install", () => self.skipWaiting());
6
+ self.addEventListener("activate", (event) => event.waitUntil(self.clients.claim()));
7
+ self.addEventListener("fetch", (event) => {
8
+ const target = new URL(event.request.url);
9
+ if (target.origin !== self.location.origin) return;
10
+ event.respondWith((async () => {
11
+ const response = await fetch(event.request);
12
+ if (response.type === "opaque" || response.type === "opaqueredirect") return response;
13
+ const headers = new Headers(response.headers);
14
+ // Fetch exposes a decoded body while origin transport headers can still
15
+ // describe its compressed representation. A reconstructed Response must
16
+ // let the browser derive framing for the decoded stream.
17
+ headers.delete("Content-Encoding");
18
+ headers.delete("Content-Length");
19
+ headers.set("Cross-Origin-Opener-Policy", "same-origin");
20
+ headers.set("Cross-Origin-Embedder-Policy", "require-corp");
21
+ headers.set("Cross-Origin-Resource-Policy", "same-origin");
22
+ return new Response(response.body, {
23
+ status: response.status,
24
+ statusText: response.statusText,
25
+ headers,
26
+ });
27
+ })());
28
+ });
@@ -1 +1 @@
1
- export const DOLLY_BUILD_ID = "sha256:3642f6397575530c5410de65927a733ad526c2d55d239715800baac568572ec1";
1
+ export const DOLLY_BUILD_ID = "sha256:4dab85fcfe184d0632f281d1ff0a7839f9ac8176e6f1656819881e8ca38c3c27";
Binary file
@@ -1 +1 @@
1
- export const DOLLY_IMAGE_BUILD_ID = "sha256:45d43ca59eeecb6128c23b546cd9c0d41be3cd7f67ce0c5ff78dfba4b94ef721";
1
+ export const DOLLY_IMAGE_BUILD_ID = "sha256:b9e16e76c04b07f3e0a20b6a399a1b736c1c8d9e103554634abb910a34d6c341";
Binary file
@@ -1 +1 @@
1
- export const DOLLY_PROCESS_ABI_DIGEST = "8002d2ac9e15e7d366e3eae77fdb1c61ac7650239792dc0c1ca64b7a1c3f4730";
1
+ export const DOLLY_PROCESS_ABI_DIGEST = "36d9bdf963bcc094b53c3b7e44f244aeb2e9213d99a729a90fd01b2f9a368c5f";