@nervur-org/dock 0.3.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 (285) hide show
  1. package/GETTING_STARTED.md +128 -0
  2. package/LICENSE +202 -0
  3. package/NOTICE +6 -0
  4. package/README.md +37 -0
  5. package/api/quo-api.md +196 -0
  6. package/api/route.ts +227 -0
  7. package/beings/GLOSSARY.md +118 -0
  8. package/beings/WORLDS.md +567 -0
  9. package/beings/avatar.ts +101 -0
  10. package/beings/carry.ts +159 -0
  11. package/beings/clock.ts +552 -0
  12. package/beings/courier.ts +107 -0
  13. package/beings/desk.ts +128 -0
  14. package/beings/doorbell.ts +129 -0
  15. package/beings/envoy.ts +218 -0
  16. package/beings/index.ts +16 -0
  17. package/beings/join.ts +51 -0
  18. package/beings/link.ts +104 -0
  19. package/beings/look.ts +139 -0
  20. package/beings/porter.ts +43 -0
  21. package/beings/presence.ts +39 -0
  22. package/beings/quo-app-bells.md +280 -0
  23. package/beings/quo-dock.md +1942 -0
  24. package/beings/setup.ts +45 -0
  25. package/beings/side.ts +60 -0
  26. package/beings/user.ts +459 -0
  27. package/beings/way.ts +65 -0
  28. package/cli/bundles.ts +20 -0
  29. package/cli/client.ts +73 -0
  30. package/cli/daemon.ts +581 -0
  31. package/cli/estate/Caddyfile +33 -0
  32. package/cli/estate/quo.service +35 -0
  33. package/cli/estate/systems.quo.plist +44 -0
  34. package/cli/estate.ts +48 -0
  35. package/cli/folder.ts +27 -0
  36. package/cli/oauth.ts +32 -0
  37. package/cli/pilot.ts +38 -0
  38. package/cli/quo-dock.md +1942 -0
  39. package/cli/quo.ts +203 -0
  40. package/cli/stand.ts +160 -0
  41. package/dist/api/route.d.ts +21 -0
  42. package/dist/api/route.js +193 -0
  43. package/dist/beings/avatar.d.ts +32 -0
  44. package/dist/beings/avatar.js +100 -0
  45. package/dist/beings/carry.d.ts +12 -0
  46. package/dist/beings/carry.js +174 -0
  47. package/dist/beings/clock.d.ts +161 -0
  48. package/dist/beings/clock.js +528 -0
  49. package/dist/beings/courier.d.ts +50 -0
  50. package/dist/beings/courier.js +96 -0
  51. package/dist/beings/desk.d.ts +53 -0
  52. package/dist/beings/desk.js +135 -0
  53. package/dist/beings/doorbell.d.ts +78 -0
  54. package/dist/beings/doorbell.js +133 -0
  55. package/dist/beings/envoy.d.ts +54 -0
  56. package/dist/beings/envoy.js +211 -0
  57. package/dist/beings/index.d.ts +14 -0
  58. package/dist/beings/index.js +16 -0
  59. package/dist/beings/join.d.ts +15 -0
  60. package/dist/beings/join.js +42 -0
  61. package/dist/beings/link.d.ts +14 -0
  62. package/dist/beings/link.js +91 -0
  63. package/dist/beings/look.d.ts +30 -0
  64. package/dist/beings/look.js +104 -0
  65. package/dist/beings/porter.d.ts +14 -0
  66. package/dist/beings/porter.js +41 -0
  67. package/dist/beings/presence.d.ts +8 -0
  68. package/dist/beings/presence.js +21 -0
  69. package/dist/beings/setup.d.ts +3 -0
  70. package/dist/beings/setup.js +39 -0
  71. package/dist/beings/side.d.ts +32 -0
  72. package/dist/beings/side.js +37 -0
  73. package/dist/beings/user.d.ts +315 -0
  74. package/dist/beings/user.js +462 -0
  75. package/dist/beings/way.d.ts +19 -0
  76. package/dist/beings/way.js +28 -0
  77. package/dist/cli/bundles.d.ts +5 -0
  78. package/dist/cli/bundles.js +17 -0
  79. package/dist/cli/client.d.ts +15 -0
  80. package/dist/cli/client.js +58 -0
  81. package/dist/cli/daemon.d.ts +50 -0
  82. package/dist/cli/daemon.js +584 -0
  83. package/dist/cli/estate/Caddyfile +33 -0
  84. package/dist/cli/estate/quo.service +35 -0
  85. package/dist/cli/estate/systems.quo.plist +44 -0
  86. package/dist/cli/estate.d.ts +5 -0
  87. package/dist/cli/estate.js +50 -0
  88. package/dist/cli/folder.d.ts +2 -0
  89. package/dist/cli/folder.js +25 -0
  90. package/dist/cli/oauth.d.ts +2 -0
  91. package/dist/cli/oauth.js +32 -0
  92. package/dist/cli/pilot.d.ts +5 -0
  93. package/dist/cli/pilot.js +22 -0
  94. package/dist/cli/quo.d.ts +2 -0
  95. package/dist/cli/quo.js +201 -0
  96. package/dist/cli/stand.d.ts +37 -0
  97. package/dist/cli/stand.js +110 -0
  98. package/dist/dock/agents.d.ts +102 -0
  99. package/dist/dock/agents.js +105 -0
  100. package/dist/dock/apns.d.ts +27 -0
  101. package/dist/dock/apns.js +98 -0
  102. package/dist/dock/console.d.ts +6 -0
  103. package/dist/dock/console.js +20 -0
  104. package/dist/dock/dialers.d.ts +56 -0
  105. package/dist/dock/dialers.js +75 -0
  106. package/dist/dock/faculty.d.ts +24 -0
  107. package/dist/dock/faculty.js +142 -0
  108. package/dist/dock/fcm.d.ts +13 -0
  109. package/dist/dock/fcm.js +77 -0
  110. package/dist/dock/grants.d.ts +41 -0
  111. package/dist/dock/grants.js +99 -0
  112. package/dist/dock/http2.d.ts +4 -0
  113. package/dist/dock/http2.js +68 -0
  114. package/dist/dock/index.d.ts +41 -0
  115. package/dist/dock/index.js +113 -0
  116. package/dist/dock/local.d.ts +13 -0
  117. package/dist/dock/local.js +44 -0
  118. package/dist/dock/location.d.ts +19 -0
  119. package/dist/dock/location.js +11 -0
  120. package/dist/dock/notifications.d.ts +38 -0
  121. package/dist/dock/notifications.js +60 -0
  122. package/dist/dock/push.d.ts +79 -0
  123. package/dist/dock/push.js +132 -0
  124. package/dist/dock/service.d.ts +4 -0
  125. package/dist/dock/service.js +107 -0
  126. package/dist/dock/timer.d.ts +52 -0
  127. package/dist/dock/timer.js +149 -0
  128. package/dist/dock/webpush.d.ts +12 -0
  129. package/dist/dock/webpush.js +144 -0
  130. package/dist/harbor/browser.d.ts +15 -0
  131. package/dist/harbor/browser.js +82 -0
  132. package/dist/harbor/capacitor.d.ts +28 -0
  133. package/dist/harbor/capacitor.js +218 -0
  134. package/dist/harbor/disk.d.ts +27 -0
  135. package/dist/harbor/disk.js +249 -0
  136. package/dist/harbor/edge/alarm.d.ts +10 -0
  137. package/dist/harbor/edge/alarm.js +38 -0
  138. package/dist/harbor/edge/edge.d.ts +54 -0
  139. package/dist/harbor/edge/edge.js +370 -0
  140. package/dist/harbor/edge/exercise.d.ts +7 -0
  141. package/dist/harbor/edge/exercise.js +232 -0
  142. package/dist/harbor/edge/given.d.ts +26 -0
  143. package/dist/harbor/edge/given.js +42 -0
  144. package/dist/harbor/edge/platform.d.ts +39 -0
  145. package/dist/harbor/edge/storage.d.ts +30 -0
  146. package/dist/harbor/edge/storage.js +180 -0
  147. package/dist/harbor/edge/worker.d.ts +17 -0
  148. package/dist/harbor/edge/worker.js +67 -0
  149. package/dist/harbor/files.d.ts +15 -0
  150. package/dist/harbor/files.js +220 -0
  151. package/dist/harbor/http.d.ts +19 -0
  152. package/dist/harbor/http.js +177 -0
  153. package/dist/harbor/idb.d.ts +15 -0
  154. package/dist/harbor/idb.js +0 -0
  155. package/dist/harbor/quo.d.ts +10 -0
  156. package/dist/harbor/quo.js +42 -0
  157. package/dist/harbor/resolve.d.ts +16 -0
  158. package/dist/harbor/resolve.js +17 -0
  159. package/dist/harbor/seal.d.ts +3 -0
  160. package/dist/harbor/seal.js +25 -0
  161. package/dist/harbor/sealed.d.ts +35 -0
  162. package/dist/harbor/sealed.js +162 -0
  163. package/dist/harbor/tauri.d.ts +16 -0
  164. package/dist/harbor/tauri.js +59 -0
  165. package/dist/harbor/ward.d.ts +9 -0
  166. package/dist/harbor/ward.js +15 -0
  167. package/dist/human/app.d.ts +32 -0
  168. package/dist/human/app.js +527 -0
  169. package/dist/human/apps.d.ts +10 -0
  170. package/dist/human/apps.js +29 -0
  171. package/dist/human/dom.d.ts +2 -0
  172. package/dist/human/dom.js +15 -0
  173. package/dist/human/door.d.ts +19 -0
  174. package/dist/human/door.js +92 -0
  175. package/dist/human/guest.d.ts +3 -0
  176. package/dist/human/guest.js +33 -0
  177. package/dist/human/html.d.ts +55 -0
  178. package/dist/human/html.js +451 -0
  179. package/dist/human/local.d.ts +7 -0
  180. package/dist/human/local.js +10 -0
  181. package/dist/human/mark.d.ts +8 -0
  182. package/dist/human/mark.js +47 -0
  183. package/dist/human/move.d.ts +17 -0
  184. package/dist/human/move.js +90 -0
  185. package/dist/human/screen.d.ts +19 -0
  186. package/dist/human/screen.js +119 -0
  187. package/dist/human/style.d.ts +2 -0
  188. package/dist/human/style.js +60 -0
  189. package/dist/human/tab.bundle.txt +4775 -0
  190. package/dist/human/tab.d.ts +15 -0
  191. package/dist/human/tab.js +409 -0
  192. package/dist/human/tree.d.ts +50 -0
  193. package/dist/human/tree.js +157 -0
  194. package/dist/human/wake.d.ts +34 -0
  195. package/dist/human/wake.js +179 -0
  196. package/dist/human/web.d.ts +33 -0
  197. package/dist/human/web.js +184 -0
  198. package/dist/human/worker.bundle.txt +3868 -0
  199. package/dist/human/worker.d.ts +1 -0
  200. package/dist/human/worker.js +81 -0
  201. package/dist/human/worlds.d.ts +24 -0
  202. package/dist/human/worlds.js +99 -0
  203. package/dist/mcp/agent.d.ts +10 -0
  204. package/dist/mcp/agent.js +78 -0
  205. package/dist/mcp/allow.d.ts +20 -0
  206. package/dist/mcp/allow.js +88 -0
  207. package/dist/mcp/http.d.ts +27 -0
  208. package/dist/mcp/http.js +92 -0
  209. package/dist/mcp/oauth.d.ts +91 -0
  210. package/dist/mcp/oauth.js +241 -0
  211. package/dist/mcp/route.d.ts +15 -0
  212. package/dist/mcp/route.js +42 -0
  213. package/dist/mcp/runner.d.ts +45 -0
  214. package/dist/mcp/runner.js +99 -0
  215. package/dist/mcp/server.d.ts +11 -0
  216. package/dist/mcp/server.js +102 -0
  217. package/dock/agents.ts +121 -0
  218. package/dock/apns.ts +138 -0
  219. package/dock/console.ts +23 -0
  220. package/dock/dialers.ts +85 -0
  221. package/dock/faculty.ts +148 -0
  222. package/dock/fcm.ts +103 -0
  223. package/dock/grants.ts +111 -0
  224. package/dock/http2.ts +70 -0
  225. package/dock/index.ts +133 -0
  226. package/dock/local.ts +48 -0
  227. package/dock/location.ts +22 -0
  228. package/dock/notifications.ts +67 -0
  229. package/dock/push.ts +161 -0
  230. package/dock/quo-app-bells.md +280 -0
  231. package/dock/quo-dock.md +1942 -0
  232. package/dock/service.ts +100 -0
  233. package/dock/timer.ts +174 -0
  234. package/dock/webpush.ts +182 -0
  235. package/harbor/browser.ts +97 -0
  236. package/harbor/capacitor.ts +223 -0
  237. package/harbor/disk.ts +237 -0
  238. package/harbor/edge/alarm.ts +48 -0
  239. package/harbor/edge/edge.ts +407 -0
  240. package/harbor/edge/exercise.ts +241 -0
  241. package/harbor/edge/given.ts +106 -0
  242. package/harbor/edge/platform.d.ts +39 -0
  243. package/harbor/edge/storage.ts +179 -0
  244. package/harbor/edge/text.d.ts +15 -0
  245. package/harbor/edge/worker.ts +70 -0
  246. package/harbor/edge/wrangler.toml +30 -0
  247. package/harbor/files.ts +221 -0
  248. package/harbor/http.ts +188 -0
  249. package/harbor/idb.ts +0 -0
  250. package/harbor/quo-harbor.md +850 -0
  251. package/harbor/quo.ts +44 -0
  252. package/harbor/resolve.ts +31 -0
  253. package/harbor/seal.ts +26 -0
  254. package/harbor/sealed.ts +183 -0
  255. package/harbor/tauri.ts +79 -0
  256. package/harbor/ward.ts +28 -0
  257. package/human/app.ts +580 -0
  258. package/human/apps.ts +47 -0
  259. package/human/dom.ts +21 -0
  260. package/human/door.ts +113 -0
  261. package/human/guest.ts +34 -0
  262. package/human/html.ts +490 -0
  263. package/human/local.ts +30 -0
  264. package/human/mark.ts +53 -0
  265. package/human/move.ts +122 -0
  266. package/human/quo-human.md +1042 -0
  267. package/human/screen.ts +136 -0
  268. package/human/style.ts +63 -0
  269. package/human/tab.bundle.txt +4775 -0
  270. package/human/tab.ts +446 -0
  271. package/human/tree.ts +217 -0
  272. package/human/wake.ts +191 -0
  273. package/human/web.ts +259 -0
  274. package/human/worker.bundle.txt +3868 -0
  275. package/human/worker.ts +124 -0
  276. package/human/worlds.ts +127 -0
  277. package/mcp/agent.ts +94 -0
  278. package/mcp/allow.ts +131 -0
  279. package/mcp/http.ts +92 -0
  280. package/mcp/oauth.ts +270 -0
  281. package/mcp/quo-mcp.md +343 -0
  282. package/mcp/route.ts +45 -0
  283. package/mcp/runner.ts +136 -0
  284. package/mcp/server.ts +101 -0
  285. package/package.json +121 -0
package/human/local.ts ADDED
@@ -0,0 +1,30 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // A being in the tab's own ward, as a subject the screen renders: a world's
3
+ // code running on the device, booted from the bundle that world serves on
4
+ // its origin. Her cells are in the tab's store and migrate with the ward;
5
+ // her asks are the forms; her page is computed from her cells at every
6
+ // digest, the same painter and the same side as a far being's, so one
7
+ // grammar has two sources and no second verb. A push never reaches her:
8
+ // nothing outside the tab holds her.
9
+ //
10
+ // The tab holds the ward's ask pointer, so the tab is the root owner, and
11
+ // the subject is the owner's `ask` and nothing else: no standing and no
12
+ // relation, since the local ward is the human's and she is on it. The ward
13
+ // names the asker, bounds the wait, judges her answer and saves before the
14
+ // pointer returns, so this file holds an id nobody minted no longer.
15
+ import type { Answer, Blueprint, JsonObject } from '@nervur-org/nervur';
16
+ import type { Subject } from '../beings/side.ts';
17
+
18
+ // The ward as its root holds it: the ask pointer, and nothing else of it.
19
+ type Owner = { ask(method?: string, args?: JsonObject): Promise<unknown> };
20
+
21
+ export function local(ward: Owner, key: string): Subject {
22
+ // A named ask names it; the empty ask says nothing at all, which is how
23
+ // the owner's ask says the empty ask too.
24
+ const one = (method?: string, args: JsonObject = {}) => ward.ask('ask', method === undefined ? { being: key } : { being: key, method, args });
25
+ return {
26
+ tools: async () => (await one()) as Blueprint,
27
+ call: (name: string, args: JsonObject = {}) => one(name, args) as Promise<Answer>,
28
+ ears: new Set(),
29
+ };
30
+ }
package/human/mark.ts ADDED
@@ -0,0 +1,53 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // The mark: a ring with a gap and a point at the centre, one being's voice
3
+ // reaching another's door. It is the one mark every Quo app wears, on the
4
+ // door page, on the desk app's dock icon and on the phone's home screen,
5
+ // and it is drawn once here so those can never drift apart.
6
+ //
7
+ // The glyph is strokes on nothing, coloured by whoever draws it: the door
8
+ // page inherits `currentColor` and the app icons are given the two colours
9
+ // below. `app/icon/build.ts` is what makes the icons of it.
10
+ export const RING = '<circle cx="32" cy="32" r="22" stroke-dasharray="110 30" transform="rotate(-60 32 32)"/><circle cx="32" cy="32" r="5" fill="currentColor" stroke="none"/>';
11
+
12
+ // The mark as a page uses it: strokes in the colour of the text around it.
13
+ export const MARK_SVG = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" fill="none" stroke="currentColor" stroke-width="4" stroke-linecap="round">${RING}</svg>`;
14
+
15
+ // And as a data URI, since a look carries a logo that way and no request
16
+ // for an image ever leaves a page.
17
+ export const MARK = 'data:image/svg+xml;base64,' + btoa(MARK_SVG);
18
+
19
+ // The app icon's two colours, the dock's own accent and paper: an icon is
20
+ // the product's palette or it is somebody else's product.
21
+ export const ICON_GROUND = '#3b6ef5';
22
+ export const ICON_MARK = '#fbfaf7';
23
+
24
+ // The mark alone on nothing, at the size a layer wants it. Android draws an
25
+ // icon in two layers and crops both to whatever shape the phone likes, a
26
+ // circle among them, so its foreground is the mark small enough to survive
27
+ // the crop, roughly the middle half, and its background is the ground.
28
+ export function markLayer(fraction = 0.5): string {
29
+ const scale = fraction;
30
+ const shift = (64 - 64 * scale) / 2;
31
+ return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><g transform="translate(${shift} ${shift}) scale(${scale})" fill="none" stroke="${ICON_MARK}" stroke-width="4" stroke-linecap="round" color="${ICON_MARK}">${RING}</g></svg>`;
32
+ }
33
+
34
+ export function groundLayer(): string {
35
+ return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><rect width="64" height="64" fill="${ICON_GROUND}"/></svg>`;
36
+ }
37
+
38
+ // The mark as an app icon, on its ground. `radius` is the corner in the
39
+ // viewBox's own units: 0 for a full square, which is what iOS and Android
40
+ // want since the platform masks it, and a rounded one for macOS, which
41
+ // masks nothing and where every icon in the dock is a rounded square with
42
+ // air around it.
43
+ export function iconSVG(radius = 0): string {
44
+ const inset = radius === 0 ? 0 : 5;
45
+ const size = 64 - inset * 2;
46
+ const glyph = radius === 0 ? '' : ` transform="translate(${inset} ${inset}) scale(${size / 64})"`;
47
+ return [
48
+ '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">',
49
+ `<rect x="${inset}" y="${inset}" width="${size}" height="${size}" rx="${radius}" fill="${ICON_GROUND}"/>`,
50
+ `<g${glyph} fill="none" stroke="${ICON_MARK}" stroke-width="4" stroke-linecap="round" color="${ICON_MARK}">${RING}</g>`,
51
+ '</svg>',
52
+ ].join('');
53
+ }
package/human/move.ts ADDED
@@ -0,0 +1,122 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Moving a world from one device to another, as both shells do it. A device
3
+ // holds one local ward per world, on its own seed, and everything it is in
4
+ // that world is in there: the avatars, their standings, what each was told.
5
+ // So a human moving a world to a new device moves that ward, whole, and
6
+ // nothing else happens: the far world is never asked, never told and never
7
+ // notices, because a pk is permanent and every standing still points at the
8
+ // same ward.
9
+ //
10
+ // Two halves, and the being between them is `beings/courier.ts`. The device
11
+ // giving offers: a courier in a ward of her own, an invitation minted on
12
+ // her, and beside it the reach note of the rendezvous this device dials,
13
+ // since the other one may dial nothing yet and a device reaches a pk only
14
+ // through a listener it holds a socket to. Both travel as one link, and a
15
+ // human carries them by eye or by camera. The device taking dials that
16
+ // note, knocks, puts the ward on its harbor, and says so; the copy on the
17
+ // device giving goes at that word and not before.
18
+ //
19
+ // The ward that travels is not the courier's, on either side, because a
20
+ // ward cannot carry itself.
21
+ import type { Harbor } from '@nervur-org/nervur/harbor';
22
+ import type { Invitation, JsonObject } from '@nervur-org/nervur';
23
+ import { arithmetic } from '@nervur-org/nervur/ward';
24
+ import { Courier, MOVER } from '../beings/courier.ts';
25
+ import { link } from '../beings/link.ts';
26
+ import { localName, type World } from './worlds.ts';
27
+
28
+ const { hex, unhex } = arithmetic;
29
+
30
+ // The ward a device's courier lives in, minted when a move starts and gone
31
+ // when it ends: nothing of a move outlives it.
32
+ export const COURIER = 'courier';
33
+ const KEY = 'courier';
34
+
35
+ async function courier(harbor: Harbor): Promise<Courier> {
36
+ const ward = harbor.wards.get(COURIER) ?? (await harbor.create(COURIER, 'me'));
37
+ if (!ward.being(KEY)) {
38
+ const out = (await ward.ask('boot', { key: KEY, class: 'Courier' })) as { error?: string };
39
+ if (out.error) throw new Error(`no courier on this device: ${out.error}`);
40
+ }
41
+ return ward.being(KEY) as Courier;
42
+ }
43
+
44
+ const away = async (harbor: Harbor) => {
45
+ const c = harbor.wards.get(COURIER)?.being(KEY) as Courier | undefined;
46
+ if (c) {
47
+ c.giving = undefined;
48
+ c.gone = undefined;
49
+ }
50
+ await harbor.drop(COURIER);
51
+ };
52
+
53
+ export type Giving = {
54
+ // The one thing the human carries to the other device: the invitation and
55
+ // the rendezvous to reach this one through, written as a link. Single-use,
56
+ // so one offer moves one ward.
57
+ offer: string;
58
+ invitation: Invitation;
59
+ // Whether the ward has gone, once the other device says it landed.
60
+ moved: Promise<string>;
61
+ // Stop offering: the courier and her ward are gone, and a knock arriving
62
+ // after this reaches nobody. The world stays here unless it was taken.
63
+ close(): Promise<void>;
64
+ };
65
+
66
+ // This device, offering one world to whichever device takes the offer.
67
+ // `at` is the rendezvous this device dials, which is the world's own `quo.`
68
+ // route: the other device has no reach to anything yet and this is what it
69
+ // dials to answer.
70
+ export async function giveWorld(harbor: Harbor, world: World, at: string): Promise<Giving> {
71
+ const name = localName(world.pk);
72
+ if (!harbor.wards.has(name)) throw new Error('this device does not hold that world');
73
+ const c = await courier(harbor);
74
+ const invitation = await c.invite(MOVER);
75
+ if (!invitation) {
76
+ await away(harbor);
77
+ throw new Error('this device is already offering a world');
78
+ }
79
+ let landed: (pk: string) => void = () => {};
80
+ const moved = new Promise<string>((ok) => (landed = ok));
81
+ c.giving = async () => {
82
+ const ward = harbor.wards.get(name);
83
+ if (!ward) return { error: 'this device no longer holds that world' };
84
+ await ward.save();
85
+ const kept = await harbor.store.load(name);
86
+ if (!kept) return { error: 'this device no longer holds that world' };
87
+ // The partition crosses as the values it is: a ward's partition is
88
+ // I-JSON already, since that is all a ward ever writes into one.
89
+ return { world: world.pk, at: world.at, name: world.name, seed: hex(kept.seed), partition: kept.partition as JsonObject, record: kept.record as unknown as JsonObject };
90
+ };
91
+ c.gone = async () => {
92
+ const gone = await harbor.drop(name);
93
+ if (!gone) return { error: 'this device no longer holds that world' };
94
+ landed(world.pk);
95
+ return { deleted: world.pk };
96
+ };
97
+ await harbor.wards.get(COURIER)!.save();
98
+ return { offer: link(at, invitation), invitation, moved, close: () => away(harbor) };
99
+ }
100
+
101
+ // This device, taking the world the other one offers. The harbor must
102
+ // already be dialing the reach note in the offer: dialing is the shell's,
103
+ // since only a shell knows which harbor its terrain gave it.
104
+ export async function takeWorld(harbor: Harbor, invitation: Invitation): Promise<{ took: string; world: World } | { error: string }> {
105
+ const c = await courier(harbor);
106
+ try {
107
+ const moving = await c.fetch(invitation);
108
+ if ('error' in moving) return moving;
109
+ const name = localName(moving.world);
110
+ if (harbor.wards.has(name)) return { error: 'this device is already in that world' };
111
+ const hosted = await harbor.adopt(name, { seed: unhex(moving.seed), partition: moving.partition, record: moving.record as unknown as { pk: string; code: string; user: string } });
112
+ // it is here: the other device may let go, and if that word never
113
+ // arrives the human is told, since two copies is what the next move
114
+ // has to be told about
115
+ const let_go = await c.landed();
116
+ return 'error' in let_go ? { error: `it is here, but the other device still has it: ${let_go.error}` } : { took: hosted.pk, world: { pk: moving.world, at: moving.at, name: moving.name } };
117
+ } catch (e) {
118
+ return { error: e instanceof Error ? e.message : String(e) };
119
+ } finally {
120
+ await away(harbor);
121
+ }
122
+ }