on master help sign in
Add accountability and harness, drop mcp, make the glob check parse dockets, narrow jammer's build source, and fix the README's frontend and update instructions
e244079iko
README.md+72−39
@@ -10,9 +10,10 @@ files.
10
10
11| App | Description | Source | Build |
11| App | Description | Source | Build |
12|---|---|---|---|
12|---|---|---|---|
13+| `%accountability` | Watches all 256 galaxies from your ship. Every 10 minutes it checks each one's keys on chain, whether it answers, and which `%kids` OTA it serves, and logs every change. A dashboard at `/apps/accountability` and a public JSON API show the results. It needs `%azimuth` running, as on a livenet ship. | [sigilante/accountability](https://github.com/sigilante/accountability) | `nix build .#accountability` |
13| `%deskjam` | Install and update apps, and upload and download desks, as deskjam files, in your ship's web interface at `/apps/deskjam`. A fork of [tinnus-napbus/deskjam](https://github.com/tinnus-napbus/deskjam). | [iko-soy/deskjam](https://github.com/iko-soy/deskjam) | `nix build .#deskjam` |
14| `%deskjam` | Install and update apps, and upload and download desks, as deskjam files, in your ship's web interface at `/apps/deskjam`. A fork of [tinnus-napbus/deskjam](https://github.com/tinnus-napbus/deskjam). | [iko-soy/deskjam](https://github.com/iko-soy/deskjam) | `nix build .#deskjam` |
15+| `%harness` | Runs AI agents on your ship. Talk to them in its web app at `/apps/harness`, in Tlon, or from an editor, while the ship keeps the conversations, permissions and work. Models come from providers you configure, such as OpenRouter, OpenAI or Anthropic. Its Tlon features need `%groups`. | [mopfel-winrux/urbit-agent-harness](https://github.com/mopfel-winrux/urbit-agent-harness) | `nix build .#harness` |
14−| `%hits` | Ranks apps by how many installs they have among the ships around you, which gossip their installs to their Pals and the ships they chat with in Tlon. There's no central server. Its frontend is a glob: see [Frontends](#frontends). A fork of [urbit/hits](https://github.com/urbit/hits). | [iko-soy/hits](https://github.com/iko-soy/hits) | `nix build .#hits` |
16+| `%hits` | Ranks apps by how many installs they have among the ships around you, which gossip their installs to their Pals and the ships they chat with in Tlon. There's no central server. It works without `%pals`, but then hears only from the ships you chat with. It only counts apps installed from another ship with `|install`, not ones installed from a deskjam file. Its frontend is a glob: see [Frontends](#frontends). A fork of [urbit/hits](https://github.com/urbit/hits). | [iko-soy/hits](https://github.com/iko-soy/hits) | `nix build .#hits` |
15−| `%mcp` | An MCP (Model Context Protocol) server at `/mcp` that lets AI tools use your ship, and a proxy at `/apps/mcp/mcp` that offers its tools together with those of remote MCP and OpenAPI servers. A fork of [gwbtc/urbit-mcp](https://github.com/gwbtc/urbit-mcp). | [tloncorp/mcp](https://github.com/tloncorp/mcp) | `nix build .#mcp` |
16| `%shelf` | A public web page that lists the apps your ship publishes, so anyone can install them on their own ship. It answers at `/shelf`, for a reverse proxy to serve at the root of its own domain, and needs `%landscape`, which fresh ships have. | [iko-soy/shelf](https://github.com/iko-soy/shelf) | `nix build .#shelf` |
17| `%shelf` | A public web page that lists the apps your ship publishes, so anyone can install them on their own ship. It answers at `/shelf`, for a reverse proxy to serve at the root of its own domain, and needs `%landscape`, which fresh ships have. | [iko-soy/shelf](https://github.com/iko-soy/shelf) | `nix build .#shelf` |
17| `%silo` | A file manager for the S3 storage your ship uploads to: add a bucket's credentials, then browse its files and upload new ones. It has no agents of its own, and uses `%storage` from `%landscape`, which fresh ships have. | [arthyn/silo](https://github.com/arthyn/silo) | `nix build .#silo` |
18| `%silo` | A file manager for the S3 storage your ship uploads to: add a bucket's credentials, then browse its files and upload new ones. It has no agents of its own, and uses `%storage` from `%landscape`, which fresh ships have. | [arthyn/silo](https://github.com/arthyn/silo) | `nix build .#silo` |
18
19
@@ -24,12 +25,13 @@ checkout, give the repo's address instead of `.`, such as
24itself, at the version `flake.lock` pins.
25itself, at the version `flake.lock` pins.
25
26
26Each build writes `result/<desk>.deskjam`, such as `result/shelf.deskjam`, in
27Each build writes `result/<desk>.deskjam`, such as `result/shelf.deskjam`, in
27−place of the last build's `result`. For an app with a web frontend, a glob,
28+place of the last build's `result`. For an app whose web frontend is a glob,
28−it also writes the glob's files next to it, as `result/<desk>-glob`, and the
29+such as hits, it also writes the glob's files next to it, as
29−build fails if `flake.nix` doesn't build them, so a deskjam never comes
30+`result/<desk>-glob`, and the build fails if `flake.nix` doesn't build them,
30−without its glob. `nix build .#hits-glob` or `.#silo-glob` builds just a glob.
31+so a deskjam never comes without its glob. `nix build .#hits-glob` or
32+`.#silo-glob` builds just a glob. `nix build .#accountability .#deskjam
31−`nix build .#deskjam .#hits .#mcp .#shelf .#silo` builds all five, into
33+.#harness .#hits .#shelf .#silo` builds all six, into `result` and `result-1`
32−`result`, `result-1`, `result-2`, `result-3` and `result-4`.
34+to `result-5`.
33
35
34You can install these files on your ship with
36You can install these files on your ship with
35[iko-soy/deskjam](https://github.com/iko-soy/deskjam): see
37[iko-soy/deskjam](https://github.com/iko-soy/deskjam): see
@@ -47,7 +49,8 @@ these files. Only install apps you trust: an app's agents, its programs, run
47as your ship, and can read and change anything on it.
49as your ship, and can read and change anything on it.
48
50
49If you build on another machine, such as the server your ship runs on, copy
51If you build on another machine, such as the server your ship runs on, copy
52+the file, and its `<desk>-glob` folder if it has one, to the computer your
50−the file to the computer your browser runs on first, for example with `scp`.
53+browser runs on first, for example with `scp -r`.
51
54
52### Getting deskjam
55### Getting deskjam
53
56
@@ -81,10 +84,7 @@ After that, update deskjam like any other app, as below.
81 click **confirm**.
84 click **confirm**.
825. The status page says whether the app runs.
855. The status page says whether the app runs.
83
86
84−Once it runs, see the app's README for how to set it up. mcp's README
87+Once it runs, see the app's README for how to set it up.
85−describes an operator console at `/apps/mcp/`, which this version of mcp
86−doesn't have. MCP clients send mcp's API key in an `X-Api-Key` header, and
87−`.^(@t %gx /=mcp-proxy=/client-key/noun)` in the dojo prints it.
88
88
89### Updating an app
89### Updating an app
90
90
@@ -102,8 +102,7 @@ Apps installed this way don't update on their own. To update one:
102Uploading the kept copy with **replace it** puts the old version back. If the
102Uploading the kept copy with **replace it** puts the old version back. If the
103update changed the state an agent keeps, that usually fails, and nothing
103update changed the state an agent keeps, that usually fails, and nothing
104changes. But some agents start afresh from state they can't read, and lose it
104changes. But some agents start afresh from state they can't read, and lose it
105−instead: deskjam's and mcp's do. For mcp, that means a new API key, and its
105+instead: deskjam's does.
106−remote servers and OAuth grants are gone.
107
106
108If the app came from another ship with `|install`, kiln, the part of your
107If the app came from another ship with `|install`, kiln, the part of your
109ship that installs and updates apps, keeps it in sync with that ship. That
108ship that installs and updates apps, keeps it in sync with that ship. That
@@ -114,23 +113,40 @@ upload with their latest release.
114
113
115### Frontends
114### Frontends
116
115
117−An app's web frontend, its glob, isn't part of its desk. Landscape's
116+Some apps' web frontends are globs, which aren't part of their desks.
118−`%docket` agent gets it as the app's docket says, and the build puts it next
117+Landscape's `%docket` agent gets an app's glob as its docket says, and the
119−to the deskjam, as `result/<desk>-glob`:
118+build puts the glob's files next to the deskjam, as `result/<desk>-glob`. That
119+folder is what you upload at `/docket/upload`: choose the app's desk and the
120+folder, and click **glob!**. Use a browser other than Safari, which can't
121+upload folders.
120
122
121- silo's docket has `%docket` download its glob from a url, so `silo.deskjam`
123- silo's docket has `%docket` download its glob from a url, so `silo.deskjam`
122− works on any ship. You need `result/silo-glob` only to serve the frontend
124+ works on any ship, with nothing to upload.
123− from your own ship instead, as deskjam's
125+- hits' docket has `%docket` get its glob from `~donmec-sallud` over Ames,
126+ with a placeholder hash. On your ship, click **set** in the glob box on the
127+ confirm page when you install `hits.deskjam`, so your ship serves the glob
128+ instead, and once hits runs, upload `result/hits-glob`. On
129+ `~donmec-sallud`, hits' docket already names your ship, so just upload the
130+ glob. Without its glob, hits runs, but has no frontend.
131+- To serve silo's frontend from your own ship too, click **set** the same way
132+ and upload `result/silo-glob`, as deskjam's
124 [Serving a glob from your ship](https://github.com/iko-soy/deskjam/blob/main/docs/globs.md#serving-a-glob-from-your-ship)
133 [Serving a glob from your ship](https://github.com/iko-soy/deskjam/blob/main/docs/globs.md#serving-a-glob-from-your-ship)
125 says.
134 says.
126−- hits' docket has `%docket` get its glob from `~donmec-sallud` over Ames,
135+
127− with a placeholder hash, so `hits.deskjam` is for that ship. There, install
136+When you update an app whose glob your ship serves, click **set** in the glob
128− it, then open `/docket/upload`, choose the desk `hits` and the folder
129− `result/hits-glob`, and click **glob!**. When you update hits with deskjam,
130− click **set** in the glob box on the confirm page first, or hits loses its
137+box on the confirm page first, which keeps the glob your ship serves now.
131− frontend until you upload it again. Other ships get hits, frontend
138+Then upload the new `result/<desk>-glob`, so the frontend matches the new
132− included, from that ship once it publishes hits:
139+version. If the app was suspended, as after a kernel update, `%docket`
133− `|install ~donmec-sallud %hits`.
140+forgets its glob when the app starts again, even after **set**: upload it
141+again.
142+
143+To let other ships install an app from yours, frontend included, publish it:
144+`:treaty|publish %hits` makes it public and lists it in Landscape, and
145+`|public %hits` is enough for `|install ~your-ship %hits`.
146+
147+The other apps have no glob: accountability, deskjam, harness and shelf
148+serve their pages from their own agents. harness' frontend is in its desk,
149+so `harness.deskjam` already has it.
134
150
135### Kernel updates
151### Kernel updates
136
152
@@ -151,19 +167,36 @@ has the details, including what to do when an upload goes wrong.
151 `.deskjam` file has the files that repo's own build makes. jammer leaves
167 `.deskjam` file has the files that repo's own build makes. jammer leaves
152 out the files a `|commit` would skip too, such as hits' `LICENSE`, which has
168 out the files a `|commit` would skip too, such as hits' `LICENSE`, which has
153 no extension.
169 no extension.
170+- accountability has no flake, and its `desk/` is the whole desk, which
171+ `flake.nix` packs as it is.
154−- mcp's repo builds its desk with `zig build`, which copies some files from
172+- harness' repo builds its desk with `zig build`. It builds `fe/`, its
155− other repos at pinned commits. `flake.nix` copies the same files from the
173+ frontend, into the desk's `web/`, which harness' own agent serves; fetches
156− same commits. After moving mcp's pin, check that its `build.zig` still lists
174+ grubbery and some of Tlon's files with git, at commits it pins; and patches
157− the same files and commits, and change `flake.nix` to match if not: nothing
175+ grubbery's desk under harness' own. `flake.nix` runs the same `zig build`
158− checks this for you.
176+ with npm's packages fetched ahead, and a stand-in `git` that answers from
177+ inputs pinned to the same commits, and fails if the build asks for any
178+ other: when a new version of harness pins other commits, move
179+ `harness-grubbery` or `harness-tlon` to them. The build also fails if the
180+ desk has no `web/index.html`, so it can't come out without its frontend.
159- silo has no flake. Its CI deploys all of urbit's `pkg/base-dev`, then
181- silo has no flake. Its CI deploys all of urbit's `pkg/base-dev`, then
160 landscape's `desk-dev`, then its own `desk/`, and `flake.nix` builds the
182 landscape's `desk-dev`, then its own `desk/`, and `flake.nix` builds the
161− same, with landscape at `master`, as the CI takes it, and urbit at
183+ same, with landscape at `master`, as the CI takes it. The CI's urbit tag
162− `408k-2`, since the CI's tag isn't public. After moving silo's pin, check
184+ isn't public, so `flake.nix` takes `408k-2`, the release of the newest
163− its `.github/helpers/deploy.sh`.
185+ kernel silo lists; when silo lists a newer one, move `silo-urbit` to its
186+ tag. A few of base-dev's libraries import files the desk lacks, so
187+ deskjam's confirm page warns about them, but silo doesn't use them. silo's
188+ old esbuild names two unused variables at random, so `silo-glob` can differ
189+ by a few bytes from one build to the next, and
190+ `nix build --rebuild .#silo-glob` can fail. The page works the same either
191+ way.
164−- A desk whose docket names a glob only builds with its glob, which
192+- A desk whose docket names a glob, however the docket is laid out, only
165− `flake.nix` builds from the app's `ui/`. A glob for a desk whose docket
193+ builds with its glob, which `flake.nix` builds from the app's `ui/`. A glob
166− names none fails the build too.
194+ for a desk whose docket names none fails the build too.
195+- A hash in `flake.nix` pins each frontend's npm packages: each glob's
196+ `npmDepsHash`, and the `hash` of harness' `npmDeps`. When moving an app's
197+ pin changes its `package-lock.json`, its build stops with a hash mismatch,
198+ or `npmDepsHash is out of date`. Set that hash to `pkgs.lib.fakeHash`,
199+ build again, and put the hash the error shows after `got:` in its place.
167
200
168## jammer
201## jammer
169
202
flake.lock+71−54
@@ -1,5 +1,21 @@
1{
1{
2 "nodes": {
2 "nodes": {
3+ "accountability": {
4+ "flake": false,
5+ "locked": {
6+ "lastModified": 1792368005,
7+ "narHash": "sha256-PwbQoFYi6LMMjfZB1PLdc6lbWx4aZhw2J87uJuMV57U=",
8+ "owner": "sigilante",
9+ "repo": "accountability",
10+ "rev": "1099d293caa1aaf27945bfedc8752367a472e6d2",
11+ "type": "github"
12+ },
13+ "original": {
14+ "owner": "sigilante",
15+ "repo": "accountability",
16+ "type": "github"
17+ }
18+ },
3 "deskjam": {
19 "deskjam": {
4 "inputs": {
20 "inputs": {
5 "fang-suite": "fang-suite",
21 "fang-suite": "fang-suite",
@@ -61,6 +77,56 @@
61 "type": "github"
77 "type": "github"
62 }
78 }
63 },
79 },
80+ "harness": {
81+ "flake": false,
82+ "locked": {
83+ "lastModified": 1792513104,
84+ "narHash": "sha256-6U6yLEUWVB0QfcwkiUW1Zio6rbjYVZ/aMQcVTfkxEhY=",
85+ "owner": "mopfel-winrux",
86+ "repo": "urbit-agent-harness",
87+ "rev": "5bde3d6ff4949a67cb94fe28294f29ea65ccfeb8",
88+ "type": "github"
89+ },
90+ "original": {
91+ "owner": "mopfel-winrux",
92+ "repo": "urbit-agent-harness",
93+ "type": "github"
94+ }
95+ },
96+ "harness-grubbery": {
97+ "flake": false,
98+ "locked": {
99+ "lastModified": 1788450026,
100+ "narHash": "sha256-pMiNAJBBzUOAhoX4ZkIsCsS3E8CkUsQHPJLHLDPb2g8=",
101+ "owner": "gwbtc",
102+ "repo": "grubbery",
103+ "rev": "20ef61108f4ffae0c40ccd90e0ee7ae4d475709f",
104+ "type": "github"
105+ },
106+ "original": {
107+ "owner": "gwbtc",
108+ "repo": "grubbery",
109+ "rev": "20ef61108f4ffae0c40ccd90e0ee7ae4d475709f",
110+ "type": "github"
111+ }
112+ },
113+ "harness-tlon": {
114+ "flake": false,
115+ "locked": {
116+ "lastModified": 1787842207,
117+ "narHash": "sha256-Lrwq0LFzRg275k/6gO+yd+qiiy8UR2Qbm12XFuz69pQ=",
118+ "owner": "tloncorp",
119+ "repo": "tlon-apps",
120+ "rev": "938f0c44d693f6f7391cca8107c7b3a40b834a01",
121+ "type": "github"
122+ },
123+ "original": {
124+ "owner": "tloncorp",
125+ "repo": "tlon-apps",
126+ "rev": "938f0c44d693f6f7391cca8107c7b3a40b834a01",
127+ "type": "github"
128+ }
129+ },
64 "hits": {
130 "hits": {
65 "inputs": {
131 "inputs": {
66 "nixpkgs": [
132 "nixpkgs": [
@@ -99,22 +165,6 @@
99 "type": "github"
165 "type": "github"
100 }
166 }
101 },
167 },
102− "mcp": {
103− "flake": false,
104− "locked": {
105− "lastModified": 1792213278,
106− "narHash": "sha256-0sBWeeuxSHPvaXN2bf5dObvVRp375/qAfVAomm8HIrU=",
107− "owner": "tloncorp",
108− "repo": "mcp",
109− "rev": "489751a06e3308d8e176823acf021688b23bd90d",
110− "type": "github"
111− },
112− "original": {
113− "owner": "tloncorp",
114− "repo": "mcp",
115− "type": "github"
116− }
117− },
118 "nixpkgs": {
168 "nixpkgs": {
119 "locked": {
169 "locked": {
120 "lastModified": 1792208399,
170 "lastModified": 1792208399,
@@ -133,17 +183,18 @@
133 },
183 },
134 "root": {
184 "root": {
135 "inputs": {
185 "inputs": {
186+ "accountability": "accountability",
136 "deskjam": "deskjam",
187 "deskjam": "deskjam",
137 "flake-utils": "flake-utils",
188 "flake-utils": "flake-utils",
189+ "harness": "harness",
190+ "harness-grubbery": "harness-grubbery",
191+ "harness-tlon": "harness-tlon",
138 "hits": "hits",
192 "hits": "hits",
139− "mcp": "mcp",
140 "nixpkgs": "nixpkgs",
193 "nixpkgs": "nixpkgs",
141 "shelf": "shelf",
194 "shelf": "shelf",
142 "silo": "silo",
195 "silo": "silo",
143 "silo-landscape": "silo-landscape",
196 "silo-landscape": "silo-landscape",
144− "silo-urbit": "silo-urbit",
197+ "silo-urbit": "silo-urbit"
145− "tlon-apps": "tlon-apps",
146− "urbit": "urbit_3"
147 }
198 }
148 },
199 },
149 "shelf": {
200 "shelf": {
@@ -231,23 +282,6 @@
231 "type": "github"
282 "type": "github"
232 }
283 }
233 },
284 },
234− "tlon-apps": {
235− "flake": false,
236− "locked": {
237− "lastModified": 1756165599,
238− "narHash": "sha256-VhdQTrzBUp1/xk0AkAII3kHKifZ+kl+JbRoe06zoCh8=",
239− "owner": "tloncorp",
240− "repo": "tlon-apps",
241− "rev": "9f0c94771e4773567a2f55a727ffa31b0f6e8e9f",
242− "type": "github"
243− },
244− "original": {
245− "owner": "tloncorp",
246− "repo": "tlon-apps",
247− "rev": "9f0c94771e4773567a2f55a727ffa31b0f6e8e9f",
248− "type": "github"
249− }
250− },
251 "urbit": {
285 "urbit": {
252 "flake": false,
286 "flake": false,
253 "locked": {
287 "locked": {
@@ -281,23 +315,6 @@
281 "repo": "urbit",
315 "repo": "urbit",
282 "type": "github"
316 "type": "github"
283 }
317 }
284− },
285− "urbit_3": {
286− "flake": false,
287− "locked": {
288− "lastModified": 1777378893,
289− "narHash": "sha256-VDEAwDPNB85aPved38aDoNJFqbhJcF7bh7+jFxFAEf4=",
290− "owner": "urbit",
291− "repo": "urbit",
292− "rev": "0f94550b941dfe046d9dff4a541330bd084e8cd1",
293− "type": "github"
294− },
295− "original": {
296− "owner": "urbit",
297− "repo": "urbit",
298− "rev": "0f94550b941dfe046d9dff4a541330bd084e8cd1",
299− "type": "github"
300− }
301 }
318 }
302 },
319 },
303 "root": "root",
320 "root": "root",
flake.nix+154−90
@@ -5,20 +5,6 @@
5 nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
5 nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
6 flake-utils.url = "github:numtide/flake-utils";
6 flake-utils.url = "github:numtide/flake-utils";
7
7
8− mcp = {
9− url = "github:tloncorp/mcp";
10− flake = false;
11− };
12− # Desk dependencies pinned by mcp's build.zig.
13− urbit = {
14− url = "github:urbit/urbit/0f94550b941dfe046d9dff4a541330bd084e8cd1";
15− flake = false;
16− };
17− tlon-apps = {
18− url = "github:tloncorp/tlon-apps/9f0c94771e4773567a2f55a727ffa31b0f6e8e9f";
19− flake = false;
20− };
21−
22 # Builds its full desk itself, with the dependencies mortar.yaml pins.
8 # Builds its full desk itself, with the dependencies mortar.yaml pins.
23 deskjam = {
9 deskjam = {
24 url = "github:iko-soy/deskjam";
10 url = "github:iko-soy/deskjam";
@@ -45,8 +31,8 @@
45 flake = false;
31 flake = false;
46 };
32 };
47 # Desk dependencies, as silo's .github/helpers/deploy.sh takes them. It
33 # Desk dependencies, as silo's .github/helpers/deploy.sh takes them. It
48− # takes urbit at a tag from a repo variable; 408k-2 is the 408 kernel,
34+ # takes urbit at a tag from a repo variable; 408k-2 is the release of the
49− # the oldest silo's sys.kelvin lists.
35+ # 408 kernel, the newest silo's sys.kelvin lists.
50 silo-urbit = {
36 silo-urbit = {
51 url = "github:urbit/urbit/408k-2";
37 url = "github:urbit/urbit/408k-2";
52 flake = false;
38 flake = false;
@@ -55,21 +41,42 @@
55 url = "github:tloncorp/landscape/master";
41 url = "github:tloncorp/landscape/master";
56 flake = false;
42 flake = false;
57 };
43 };
44+
45+ # Its desk/ is the full desk: it vendors its lib/, mar/ and sur/ files.
46+ accountability = {
47+ url = "github:sigilante/accountability";
48+ flake = false;
49+ };
50+
51+ harness = {
52+ url = "github:mopfel-winrux/urbit-agent-harness";
53+ flake = false;
54+ };
55+ # The commits harness' build.zig and scripts/stage-tlon.mjs fetch.
56+ harness-grubbery = {
57+ url = "github:gwbtc/grubbery/20ef61108f4ffae0c40ccd90e0ee7ae4d475709f";
58+ flake = false;
59+ };
60+ harness-tlon = {
61+ url = "github:tloncorp/tlon-apps/938f0c44d693f6f7391cca8107c7b3a40b834a01";
62+ flake = false;
63+ };
58 };
64 };
59
65
60 outputs =
66 outputs =
61 {
67 {
62 nixpkgs,
68 nixpkgs,
63 flake-utils,
69 flake-utils,
64− mcp,
65− urbit,
66− tlon-apps,
67 deskjam,
70 deskjam,
68 shelf,
71 shelf,
69 hits,
72 hits,
70 silo,
73 silo,
71 silo-urbit,
74 silo-urbit,
72 silo-landscape,
75 silo-landscape,
76+ accountability,
77+ harness,
78+ harness-grubbery,
79+ harness-tlon,
73 ...
80 ...
74 }:
81 }:
75 # nixpkgs 26.11 dropped x86_64-darwin, and the apps' own flakes build their
82 # nixpkgs 26.11 dropped x86_64-darwin, and the apps' own flakes build their
@@ -83,9 +90,16 @@
83 jammer = pkgs.rustPlatform.buildRustPackage {
90 jammer = pkgs.rustPlatform.buildRustPackage {
84 pname = cargoToml.package.name;
91 pname = cargoToml.package.name;
85 version = cargoToml.package.version;
92 version = cargoToml.package.version;
93+ # Only what cargo builds and tests, so editing the README doesn't
94+ # rebuild jammer and every deskjam.
86− src = pkgs.lib.cleanSourceWith {
95+ src = pkgs.lib.fileset.toSource {
87− src = ./jammer;
96+ root = ./jammer;
88− filter = path: type: !(type == "directory" && baseNameOf path == "target");
97+ fileset = pkgs.lib.fileset.unions [
98+ ./jammer/Cargo.toml
99+ ./jammer/Cargo.lock
100+ ./jammer/src
101+ ./jammer/tests
102+ ];
89 };
103 };
90 cargoLock.lockFile = ./jammer/Cargo.lock;
104 cargoLock.lockFile = ./jammer/Cargo.lock;
91 meta = {
105 meta = {
@@ -94,9 +108,17 @@
94 };
108 };
95 };
109 };
96
110
111+ # Exits 0 if the docket file it's given names a glob: a glob-ames or
112+ # glob-http term outside the docket's cords, tapes and comments,
113+ # however the docket is laid out.
114+ names-glob = pkgs.writers.writePerlBin "names-glob" { } ''
115+ local $/;
116+ my $docket = <>;
117+ $docket =~ s/'(?:\\.|[^'\\])*'|"(?:\\.|[^"\\])*"|::[^\n]*//g;
118+ exit($docket =~ /(?<![\w-])glob-(?:ames|http)(?![\w-])/ ? 0 : 1);
119+ '';
120+
97− # Pack a desk into $out/<desk>.deskjam. `src` is copied as the base of
121+ # Pack the desk `src` into $out/<desk>.deskjam.
98− # the desk, then each entry of `imports` copies `paths` (relative to
99− # the desk root) from the directory `from`.
100 #
122 #
101 # When the desk's docket names a glob, its frontend (glob-ames or
123 # When the desk's docket names a glob, its frontend (glob-ames or
102 # glob-http), `glob` must be the built frontend, which goes next to the
124 # glob-http), `glob` must be the built frontend, which goes next to the
@@ -107,75 +129,33 @@
107 {
129 {
108 desk,
130 desk,
109 src,
131 src,
110− imports ? [ ],
111 glob ? null,
132 glob ? null,
112 }:
133 }:
113− pkgs.runCommand "${desk}.deskjam" { nativeBuildInputs = [ jammer ]; } ''
134+ pkgs.runCommand "${desk}.deskjam"
114− cp -r --no-preserve=mode ${src} ${desk}
115− ${pkgs.lib.concatMapStrings (
116− { from, paths }:
117− pkgs.lib.concatMapStrings (path: ''
118− install -D -m 644 ${from}/${path} ${desk}/${path}
119− '') paths
120− ) imports}
121− glob=${toString glob}
122− if grep -Eqs '(^|[[:space:]])glob-(ames|http)\+' ${desk}/desk.docket-0; then
123− if [ -z "$glob" ]; then
124− echo "The docket of %${desk} names a glob: give mkDeskjam the built frontend as glob" >&2
125− exit 1
126− fi
127− elif [ -n "$glob" ]; then
128− echo "The docket of %${desk} names no glob, but mkDeskjam got one" >&2
129− exit 1
130− fi
131− mkdir $out
132− jammer jam ${desk} -d ${desk} -o $out/${desk}.deskjam
133− if [ -n "$glob" ]; then
134− cp -r --no-preserve=mode "$glob" $out/${desk}-glob
135− fi
136− '';
137−
138− # Mirrors the dependency list in mcp's build.zig.
139− mcp-desk = mkDeskjam {
140− desk = "mcp";
141− src = "${mcp}/desk";
142− imports = [
143 {
135 {
144− from = "${urbit}/pkg/arvo";
136+ nativeBuildInputs = [
145− paths = [ "lib/pretty-file.hoon" ];
137+ jammer
146− }
138+ names-glob
147− {
148− from = "${tlon-apps}/desk";
149− paths = [ "lib/test-agent.hoon" ];
150− }
151− {
152− from = "${urbit}/pkg/base-dev";
153− paths = [
154− "lib/dbug.hoon"
155− "lib/default-agent.hoon"
156− "lib/server.hoon"
157− "lib/skeleton.hoon"
158− "lib/strand.hoon"
159− "lib/strandio.hoon"
160− "lib/test.hoon"
161− "lib/verb.hoon"
162− "mar/bill.hoon"
163− "mar/hoon.hoon"
164− "mar/json.hoon"
165− "mar/kelvin.hoon"
166− "mar/mime.hoon"
167− "mar/noun.hoon"
168− "mar/ship.hoon"
169− "mar/sole/action.hoon"
170− "mar/sole/effect.hoon"
171− "mar/txt.hoon"
172− "sur/sole.hoon"
173− "sur/spider.hoon"
174− "sur/verb.hoon"
175 ];
139 ];
176 }
140 }
177− ];
141+ ''
142+ cp -r --no-preserve=mode ${src} ${desk}
143+ glob=${pkgs.lib.optionalString (glob != null) "${glob}"}
144+ if [ -f ${desk}/desk.docket-0 ] && names-glob ${desk}/desk.docket-0; then
145+ if [ -z "$glob" ]; then
146+ echo "The docket of %${desk} names a glob: give mkDeskjam the built frontend as glob" >&2
147+ exit 1
148+ fi
149+ elif [ -n "$glob" ]; then
150+ echo "The docket of %${desk} names no glob, but mkDeskjam got one" >&2
151+ exit 1
152+ fi
153+ mkdir $out
154+ jammer jam ${desk} -d ${desk} -o $out/${desk}.deskjam
155+ if [ -n "$glob" ]; then
156+ cp -r --no-preserve=mode "$glob" $out/${desk}-glob
157+ fi
178− };
158+ '';
179
159
180 deskjam-desk = mkDeskjam {
160 deskjam-desk = mkDeskjam {
181 desk = "deskjam";
161 desk = "deskjam";
@@ -235,17 +215,101 @@
235 '';
215 '';
236 glob = silo-glob;
216 glob = silo-glob;
237 };
217 };
218+
219+ accountability-desk = mkDeskjam {
220+ desk = "accountability";
221+ src = "${accountability}/desk";
222+ };
223+
224+ # harness' build.zig fetches grubbery, and its scripts/stage-tlon.mjs
225+ # Tlon's files, with git, at commits they pin. This git answers from
226+ # the inputs pinned to the same commits, and fails on any other, so a
227+ # new pin upstream stops the build until the inputs follow it.
228+ harness-git = pkgs.writeShellScriptBin "git" ''
229+ set -eu
230+ if [ "$1" != -C ]; then
231+ echo "git: unexpected command: $*" >&2
232+ exit 1
233+ fi
234+ repo=$2
235+ shift 2
236+ case $repo in
237+ desk-deps/grubbery) src=${harness-grubbery} rev=${harness-grubbery.rev} ;;
238+ desk-deps/tlon) src=${harness-tlon} rev=${harness-tlon.rev} ;;
239+ *) echo "git: unexpected repo: $repo" >&2; exit 1 ;;
240+ esac
241+ pinned() {
242+ if [ "$1" != "$rev" ]; then
243+ echo "git: $repo is pinned to $rev, but the build wants $1" >&2
244+ exit 1
245+ fi
246+ }
247+ case $1 in
248+ init | remote) ;;
249+ cat-file) pinned "''${3%"^{commit}"}" ;;
250+ checkout) for arg; do :; done; pinned "$arg" ;;
251+ show) pinned "''${2%%:*}"; cat "$src/''${2#*:}" ;;
252+ *) echo "git: unexpected command: $*" >&2; exit 1 ;;
253+ esac
254+ '';
255+
256+ # The desk harness' build.zig assembles: desk/, with fe/ built into
257+ # its web/, over a pruned and patched grubbery desk, with Tlon's files.
258+ # build.zig runs npm itself, which can't work in the sandbox, so fe/ is
259+ # built here first instead.
260+ harness-src = pkgs.stdenv.mkDerivation {
261+ name = "harness-desk";
262+ src = harness;
263+ npmRoot = "fe";
264+ npmDeps = pkgs.fetchNpmDeps {
265+ name = "harness-fe-npm-deps";
266+ src = "${harness}/fe";
267+ hash = "sha256-m5yMZFEUJV4HPrvWRIIyJ7psXy43W+4gZEcrcHHFdu4=";
268+ };
269+ nativeBuildInputs = [
270+ pkgs.zig_0_15
271+ pkgs.nodejs
272+ pkgs.npmHooks.npmConfigHook
273+ harness-git
274+ ];
275+ postPatch = ''
276+ substituteInPlace build.zig \
277+ --replace-fail 'try run(step, &.{ "npm", "ci", "--prefix", "fe", "--no-audit", "--no-fund" });' "" \
278+ --replace-fail 'try run(step, &.{ "npm", "run", "build", "--prefix", "fe" });' ""
279+ '';
280+ buildPhase = ''
281+ runHook preBuild
282+ npm run build --prefix fe
283+ mkdir -p desk-deps
284+ cp -r --no-preserve=mode ${harness-grubbery} desk-deps/grubbery
285+ ZIG_GLOBAL_CACHE_DIR=$TMPDIR/zig zig build
286+ runHook postBuild
287+ '';
288+ installPhase = ''
289+ if [ ! -f zig-out/web/index.html ]; then
290+ echo "harness' desk has no web/index.html: its frontend is missing" >&2
291+ exit 1
292+ fi
293+ cp -r zig-out $out
294+ '';
295+ };
296+
297+ harness-desk = mkDeskjam {
298+ desk = "harness";
299+ src = harness-src;
300+ };
238 in
301 in
239 {
302 {
240 packages = {
303 packages = {
241 inherit jammer;
304 inherit jammer;
242− mcp = mcp-desk;
243 deskjam = deskjam-desk;
305 deskjam = deskjam-desk;
244 shelf = shelf-desk;
306 shelf = shelf-desk;
245 hits = hits-desk;
307 hits = hits-desk;
246 inherit hits-glob;
308 inherit hits-glob;
247 silo = silo-desk;
309 silo = silo-desk;
248 inherit silo-glob;
310 inherit silo-glob;
311+ accountability = accountability-desk;
312+ harness = harness-desk;
249 default = jammer;
313 default = jammer;
250 };
314 };
251
315