@daugasauron/dolly 0.1.4-rc.3 → 0.1.5-rc.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/README.md +135 -14
- package/coi-serviceworker.js +28 -0
- package/dist/dolly-build-id.mjs +1 -1
- package/dist/dolly-dso-0.wasm +0 -0
- package/dist/dolly-image-build-id.mjs +1 -1
- package/dist/dolly-process-0.wasm +0 -0
- package/dist/dolly-process-abi.mjs +1 -1
- package/dist/dolly-process-dso.mjs +382 -53
- package/dist/dolly-process-gate-0.wasm +0 -0
- package/dist/dolly-process-worker.mjs +164 -35
- package/dist/dolly.wasm +0 -0
- package/host/dso/abi.mjs +3 -1
- package/host/dso/dso.mjs +1 -2
- package/host/http/abi.mjs +2 -2
- package/host/http/broker.mjs +18 -8
- package/host/http/http.mjs +0 -1
- package/host/http/policy.mjs +8 -4
- package/host/input/abi.mjs +47 -0
- package/host/modules.mjs +4 -3
- package/host/nena/abi.mjs +1 -1
- package/host/nena/nena.mjs +16 -18
- package/host/packages/service.mjs +35 -15
- package/host/packages/worker.mjs +21 -0
- package/host/runtime/abi.mjs +1 -0
- package/host/runtime/boot-files.mjs +8 -2
- package/host/runtime/grower.mjs +11 -8
- package/host/runtime/lease-bridge.mjs +8 -5
- package/host/runtime/module.json +1 -1
- package/host/runtime/runtime.mjs +37 -18
- package/host/snapshot/abi.mjs +7 -9
- package/host/snapshot/snapshot.mjs +14 -12
- package/host/snapshot/transport.mjs +4 -12
- package/host/snapshot/ui.mjs +17 -7
- package/host/stack/stack.mjs +1 -2
- package/host/threads/abi.mjs +1 -1
- package/host/threads/threads.mjs +2 -1
- package/package.json +1 -1
- package/src/browser.mjs +39 -40
- package/src/build-log.mjs +13 -2
- package/src/build-worker.mjs +182 -0
- package/src/custom-image.mjs +59 -0
- package/src/dollyfile-graph.mjs +127 -0
- package/src/dollyfile-view.mjs +335 -0
- package/src/image-artifact.mjs +248 -0
- package/src/image-build.mjs +89 -0
- package/src/image-builder.mjs +63 -0
- package/src/images.mjs +13 -2
- package/src/pack-store.mjs +141 -0
- package/src/packaged-snapshot.mjs +263 -91
- package/src/page-chords.mjs +4 -2
- package/src/process-constants.mjs +4 -2
- package/src/process-supervisor.mjs +209 -122
- package/src/runtime-worker.mjs +14 -12
- package/src/session-store.mjs +5 -7
- package/src/sha256.mjs +65 -0
- package/src/snapshot-records.mjs +135 -23
- package/src/static-asset.mjs +29 -2
- package/src/terminal-text.mjs +8 -5
- package/src/version.mjs +1 -1
- 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`)
|
|
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-
|
|
13
|
-
| `@daugasauron/dolly-
|
|
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-
|
|
18
|
-
@daugasauron/dolly-host-
|
|
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
|
|
23
|
-
|
|
24
|
-
|
|
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,15 +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.
|
|
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 |
|
|
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 |
|
|
54
60
|
|
|
55
61
|
The page, not the image, chooses the modules and the policy, before anything of
|
|
56
62
|
the image runs. A program reaches nothing of the browser but through them.
|
|
@@ -72,6 +78,49 @@ const dolly = await boot(element, { images, image, modules: [...modules, gpu] })
|
|
|
72
78
|
`nena enable gpu` then gives a program the GPU, or, where the browser has no
|
|
73
79
|
WebGPU adapter, a device that fails to open with `ENODEV`.
|
|
74
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
|
+
|
|
75
124
|
## Serving
|
|
76
125
|
|
|
77
126
|
- Serve the installed packages as they lie: the runtime finds its own files
|
|
@@ -80,9 +129,81 @@ WebGPU adapter, a device that fails to open with `ENODEV`.
|
|
|
80
129
|
- Send `Cross-Origin-Opener-Policy: same-origin` and
|
|
81
130
|
`Cross-Origin-Embedder-Policy: require-corp` with the page: Dolly needs shared
|
|
82
131
|
memory, which a browser gives only to a cross-origin isolated page.
|
|
83
|
-
- Serve `.wasm` as `application/wasm
|
|
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.
|
|
84
135
|
- No bundler is needed: these are ES modules, Workers and Wasm files.
|
|
85
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
|
+
|
|
86
207
|
## Host modules
|
|
87
208
|
|
|
88
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
|
+
});
|
package/dist/dolly-build-id.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const DOLLY_BUILD_ID = "sha256:
|
|
1
|
+
export const DOLLY_BUILD_ID = "sha256:4dab85fcfe184d0632f281d1ff0a7839f9ac8176e6f1656819881e8ca38c3c27";
|
package/dist/dolly-dso-0.wasm
CHANGED
|
Binary file
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export const DOLLY_IMAGE_BUILD_ID = "sha256:
|
|
1
|
+
export const DOLLY_IMAGE_BUILD_ID = "sha256:b9e16e76c04b07f3e0a20b6a399a1b736c1c8d9e103554634abb910a34d6c341";
|
|
Binary file
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export const DOLLY_PROCESS_ABI_DIGEST = "
|
|
1
|
+
export const DOLLY_PROCESS_ABI_DIGEST = "36d9bdf963bcc094b53c3b7e44f244aeb2e9213d99a729a90fd01b2f9a368c5f";
|