@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
@@ -0,0 +1,100 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // The avatar. Someone outside Quo's voice: a human at a screen, a model at
3
+ // an MCP client, a model in an API loop. One class for every side. She holds
4
+ // exactly one standing, `user`, and everything her side asks goes through
5
+ // it; the user being reaches her only to push, and she hands the push to
6
+ // every side she has. A side is in-process with her, holds the object, and
7
+ // calls the four methods below: one of the two ways in, `enter` behind a
8
+ // route's proof or `join` with an invitation, then `tools` and `call`.
9
+ // Nothing of a side is in her cells.
10
+ import { isSilence, isWord, wordOf } from '@nervur-org/nervur';
11
+ import { Carrier } from './carry.js';
12
+ // Her one standing, and the id under which the user being pushes to her.
13
+ // Two ids for one far being, because standings and occupants share one
14
+ // namespace and a record holds one or the other.
15
+ export const USER = 'user';
16
+ export const PUSHER = 'user:push';
17
+ // A carrier who carries for nobody, until a subclass says who: the presence
18
+ // being carries the box's faculties to her user being, and a bare avatar
19
+ // holds one standing and shows nothing of it.
20
+ export class Avatar extends Carrier {
21
+ static cells = { inbox: [] };
22
+ static asks = {
23
+ notify: { description: 'a push from the user being', input: { type: 'object' }, for: (occ) => occ?.id === PUSHER },
24
+ };
25
+ // Her sides' ears. One identity is one avatar for life, and two sessions
26
+ // of that identity are two sides on her at once, so a push goes to every
27
+ // ear present; each side adds its own on open and removes it on close.
28
+ // Dropped if there is none.
29
+ ears = new Set();
30
+ // ---- what the user being may ask her
31
+ notify(args) {
32
+ this.cells.inbox.push(args);
33
+ for (const ear of this.ears)
34
+ ear(args);
35
+ return { notified: true };
36
+ }
37
+ // ---- what her side calls. Not asks: nobody reaches these through a door.
38
+ // The credential exchange's last step. `desk` is the desk's public
39
+ // invitation, `proof` is what the route produced. She knocks the desk as a
40
+ // stranger, gets an invitation the user being minted, and joins with it. A
41
+ // knock on a public invitation births nothing, so she holds no standing at
42
+ // the desk afterwards. If she already holds `user`, this is a reconnect and
43
+ // nothing is minted.
44
+ async enter(desk, proof) {
45
+ if (this.standings[USER])
46
+ return this.tools();
47
+ const inv = await this.knock(desk, 'device', { proof });
48
+ if (isSilence(inv))
49
+ return { error: 'silence' };
50
+ if (isWord(inv))
51
+ return { error: wordOf(inv) };
52
+ if (!inv || typeof inv !== 'object' || Array.isArray(inv) || 'error' in inv)
53
+ return inv ?? { error: 'no invitation' };
54
+ return this.join(inv);
55
+ }
56
+ // Knock with an invitation, hand the far being one back so she can push,
57
+ // and take her as `user`. The only moment a standing is born here. A being
58
+ // who answers the hello with an error object is a being who answered: she
59
+ // is taken all the same, and the way back she did not take is dropped, so
60
+ // any world's public being may admit a guest, hello or not.
61
+ async join(invitation) {
62
+ if (this.standings[USER]) {
63
+ // A standing the far being removed is not one: she drops it and joins
64
+ // afresh, so an identity forgotten at the user being can be let in again.
65
+ const had = await this.tools();
66
+ if (!('error' in had) || had.error !== 'removed')
67
+ return had;
68
+ this.standings.remove(USER);
69
+ this.occupants.remove(PUSHER);
70
+ }
71
+ const mine = await this.invite(PUSHER);
72
+ if (mine === null)
73
+ return { error: 'user is taken' };
74
+ const out = await this.knock(invitation, 'hello', { invitation: mine });
75
+ if (isSilence(out) || isWord(out)) {
76
+ this.occupants.remove(PUSHER);
77
+ return { error: isSilence(out) ? 'silence' : wordOf(out) };
78
+ }
79
+ if (out !== null && typeof out === 'object' && !Array.isArray(out) && 'error' in out)
80
+ this.occupants.remove(PUSHER);
81
+ await this.take(USER, invitation);
82
+ return this.tools();
83
+ }
84
+ // Her describe, as the user being shows it to her. This is the tool list.
85
+ async tools() {
86
+ const st = this.standings[USER];
87
+ if (!st)
88
+ return { error: 'not joined' };
89
+ const bp = await st.ask();
90
+ if (isSilence(bp) || isWord(bp))
91
+ return { error: isSilence(bp) ? 'silence' : wordOf(bp) };
92
+ return bp;
93
+ }
94
+ // One call from the side, one ask on the standing. The three words pass
95
+ // through untouched; the side renders them.
96
+ call(name, args = {}, wanted) {
97
+ const st = this.standings[USER];
98
+ return st ? st.ask(name, args, wanted) : Promise.resolve({ error: 'not joined' });
99
+ }
100
+ }
@@ -0,0 +1,12 @@
1
+ import { Being } from '@nervur-org/nervur';
2
+ import type { Asker, Blueprint, JsonObject, OccupantRecord, Reply } from '@nervur-org/nervur';
3
+ export declare class Carrier extends Being {
4
+ static carries(_occupant: OccupantRecord | undefined, _asker: Asker): boolean;
5
+ static hidden(id: string): boolean;
6
+ static skips(_id: string): boolean;
7
+ static shows(_id: string, _occupant: OccupantRecord | undefined, _asker: Asker): boolean;
8
+ private get carried();
9
+ private get looks();
10
+ blueprint(asker: Asker): Promise<Blueprint>;
11
+ answer(asker: Asker, method?: string, args?: JsonObject): Promise<Reply>;
12
+ }
@@ -0,0 +1,174 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // A carrier: a being who shows the asks of the standings she holds as asks
3
+ // of her own, and forwards. Whoever she carries for sees, in one describe,
4
+ // what she can be asked and what every being she holds can be asked, each
5
+ // under that standing's name; asking one is an ask on that standing, in her
6
+ // name, so the far being sees her and never who is behind her. A user being
7
+ // carries acme for the human's devices; a house being could carry its
8
+ // rooms; nothing here knows what she is.
9
+ //
10
+ // A carried ask is `<id>-<name>`, and the mapping back is kept in her cells
11
+ // and never parsed from the name, so an id with a dash in it is no trouble.
12
+ // The notes say which asks are whose, `standings: { id: { asks, look? } }`,
13
+ // so a screen draws one section per standing and a model side keeps the
14
+ // flat list it needs. Her own asks come first and are never shadowed: a
15
+ // carried name that collides with one of hers is dropped.
16
+ //
17
+ // She asks a standing's describe again when its digest moved, and its
18
+ // `look` once per digest, and keeps both in her cells. So a describe of
19
+ // hers may cost one ask per stale standing, which is the price of a page
20
+ // that is never more than one ask behind.
21
+ import { Being, isSilence, isWord, wordOf } from '@nervur-org/nervur';
22
+ import { sanitise } from './look.js';
23
+ // The names a far being carries, read off her notes: every ask her
24
+ // `standings` groups name, and each group's page. Empty when she carries
25
+ // nothing, which a bare avatar does not.
26
+ function carriedBy(bp) {
27
+ const out = new Set();
28
+ const notes = bp.notes;
29
+ const groups = notes !== null && typeof notes === 'object' && !Array.isArray(notes) ? notes.standings : undefined;
30
+ if (!groups || typeof groups !== 'object' || Array.isArray(groups))
31
+ return out;
32
+ for (const g of Object.values(groups)) {
33
+ if (Array.isArray(g?.asks))
34
+ for (const n of g.asks)
35
+ if (typeof n === 'string')
36
+ out.add(n);
37
+ if (typeof g?.page === 'string')
38
+ out.add(g.page);
39
+ }
40
+ return out;
41
+ }
42
+ export class Carrier extends Being {
43
+ // Who sees what she carries. Nobody, until a subclass says who.
44
+ static carries(_occupant, _asker) {
45
+ return false;
46
+ }
47
+ // Standings that are plumbing: their own asks are never carried, and
48
+ // what they carry is. A way back to a device is one, by the dock's own
49
+ // convention, so a bare avatar's inbox never shows on her and a presence
50
+ // being's faculties do, under `to:<id>-<name>`, which is how home carries
51
+ // her phone. A subclass may name more.
52
+ static hidden(id) {
53
+ return id.startsWith('to:');
54
+ }
55
+ // Standings she never reads for carrying at all: a presence being's
56
+ // standing at her own user being is one, since carrying it would carry
57
+ // the user being's carrying of her, and round. None, until a subclass
58
+ // says which.
59
+ static skips(_id) {
60
+ return false;
61
+ }
62
+ // Which of the standings she carries this asker may see. Every one,
63
+ // until a subclass keeps some for fewer: a user being shows her own door
64
+ // to a device that may mint and to nobody else.
65
+ static shows(_id, _occupant, _asker) {
66
+ return true;
67
+ }
68
+ get carried() {
69
+ return (this.cells.carried ??= {});
70
+ }
71
+ get looks() {
72
+ return (this.cells.looks ??= {});
73
+ }
74
+ // Her describe with her standings carried into it, for an asker who may
75
+ // see them. Every other asker gets her describe alone.
76
+ async blueprint(asker) {
77
+ const C = this.constructor;
78
+ const bp = this.describe(asker);
79
+ if (!C.carries(this.occupant(asker), asker))
80
+ return bp;
81
+ const own = new Set(bp.asks.map((a) => a.name));
82
+ const carried = {};
83
+ const groups = {};
84
+ const occ = this.occupant(asker);
85
+ // By id, and never in the order she happened to take them. Her cells keep
86
+ // standings in the order they were taken, which is the order a lend
87
+ // answered, a device came back or a relation was dropped and taken again:
88
+ // history, and no fact about her. Carried into her blueprint in that
89
+ // order, her shape would come out differently on two runs that made her
90
+ // the same way, and her digest with it. The digest is how everyone
91
+ // holding a standing on her learns she changed, so a shape that follows
92
+ // history says she changed when nothing about her did, and a watcher
93
+ // refreshes or walks away over the order two lends came back in.
94
+ for (const [id, rec] of [...Object.entries(this.cells.standings)].sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))) {
95
+ if (C.skips(id))
96
+ continue;
97
+ const st = this.standings[id];
98
+ if (!st)
99
+ continue;
100
+ if (!rec.blueprint || rec.seen !== rec.digest)
101
+ await st.ask(); // her ward writes the record
102
+ if (!rec.blueprint)
103
+ continue;
104
+ // plumbing carries what it carries and nothing of its own: the names
105
+ // the far being's notes group under her standings, or none
106
+ const through = C.hidden(id) ? carriedBy(rec.blueprint) : null;
107
+ if (through && through.size === 0)
108
+ continue;
109
+ // The mapping is written for every standing she carries, whoever is
110
+ // asking: it is one map in her cells, and the last describe must not
111
+ // decide what the next asker may call. Who sees a standing is decided
112
+ // here and again at answer, by `shows`.
113
+ const shown = C.shows(id, occ, asker);
114
+ // her look is kept here once per digest and never carried as an ask;
115
+ // her page follows every call, so it is carried as one and named in
116
+ // the notes, where every side finds it and none takes it for a form
117
+ const names = [];
118
+ const group = { asks: names };
119
+ for (const a of rec.blueprint.asks) {
120
+ if (a.name === 'look')
121
+ continue;
122
+ if (through && !through.has(a.name))
123
+ continue;
124
+ const name = `${id}-${a.name}`;
125
+ if (own.has(name))
126
+ continue;
127
+ carried[name] = { id, ask: a.name };
128
+ if (!shown)
129
+ continue;
130
+ bp.asks.push({ ...a, name });
131
+ if (a.name === 'page')
132
+ group.page = name;
133
+ else
134
+ names.push(name);
135
+ }
136
+ if (!shown)
137
+ continue;
138
+ if (rec.blueprint.asks.some((a) => a.name === 'look')) {
139
+ const kept = this.looks[id];
140
+ if (!kept || kept.digest !== rec.digest) {
141
+ const l = await st.ask('look');
142
+ this.looks[id] = { digest: rec.digest, look: isSilence(l) || isWord(l) ? {} : sanitise(l) };
143
+ }
144
+ group.look = this.looks[id].look;
145
+ }
146
+ groups[id] = group;
147
+ }
148
+ for (const id of Object.keys(this.looks))
149
+ if (!(id in groups))
150
+ delete this.looks[id];
151
+ this.cells.carried = carried;
152
+ const notes = bp.notes !== null && typeof bp.notes === 'object' && !Array.isArray(bp.notes) ? bp.notes : {};
153
+ return { asks: bp.asks, notes: { ...notes, standings: groups } };
154
+ }
155
+ async answer(asker, method, args = {}) {
156
+ if (method === undefined)
157
+ return this.blueprint(asker);
158
+ const C = this.constructor;
159
+ const to = Object.hasOwn(this.carried, method) ? this.carried[method] : undefined;
160
+ const occ = this.occupant(asker);
161
+ if (to && C.carries(occ, asker) && C.shows(to.id, occ, asker)) {
162
+ const st = this.standings[to.id];
163
+ if (!st)
164
+ return { error: 'unknown ask' };
165
+ const out = await st.ask(to.ask, args);
166
+ if (isSilence(out))
167
+ return out;
168
+ if (isWord(out))
169
+ return { error: wordOf(out) };
170
+ return out;
171
+ }
172
+ return super.answer(asker, method, args);
173
+ }
174
+ }
@@ -0,0 +1,161 @@
1
+ import { Being } from '@nervur-org/nervur';
2
+ import type { Asker, Json, JsonObject, OccupantRecord, Stance } from '@nervur-org/nervur';
3
+ declare const root: (_occ: OccupantRecord | undefined, asker: Asker) => boolean;
4
+ declare const timer: (_occ: OccupantRecord | undefined, asker: Asker) => boolean;
5
+ declare const watching: (occ: OccupantRecord | undefined, asker: Asker) => boolean;
6
+ export type Schedule = {
7
+ id: string;
8
+ ask: string;
9
+ args?: JsonObject;
10
+ note?: Json;
11
+ cron?: string;
12
+ at?: string;
13
+ tz?: string;
14
+ time?: number;
15
+ catchUp?: boolean;
16
+ };
17
+ export type Fire = {
18
+ at: number;
19
+ word: string;
20
+ tries: number;
21
+ };
22
+ export type Cron = {
23
+ minutes: number[];
24
+ hours: number[];
25
+ days: number[];
26
+ months: number[];
27
+ weekdays: number[];
28
+ anyDay: boolean;
29
+ anyWeekday: boolean;
30
+ };
31
+ export declare function parseCron(spec: string): Cron;
32
+ export type Civil = {
33
+ year: number;
34
+ month: number;
35
+ day: number;
36
+ hour: number;
37
+ minute: number;
38
+ };
39
+ export declare function civil(ms: number, tz: string): Civil;
40
+ export declare function fromCivil(tz: string, year: number, month: number, day: number, hour: number, minute: number): number;
41
+ export declare function nextFire(c: Cron, tz: string, after: number): number | null;
42
+ export declare function readSchedule(v: unknown): Schedule | string;
43
+ export declare class Clock extends Being {
44
+ #private;
45
+ static cells: {
46
+ kept: Record<string, Schedule>;
47
+ fires: Record<string, Fire>;
48
+ };
49
+ readonly joined: Promise<string | null>;
50
+ constructor(stance: Stance);
51
+ static asks: {
52
+ tick: {
53
+ description: string;
54
+ input: {
55
+ type: string;
56
+ properties: {
57
+ now: {
58
+ type: string;
59
+ format: string;
60
+ description: string;
61
+ };
62
+ };
63
+ required: string[];
64
+ };
65
+ for: typeof timer;
66
+ };
67
+ list: {
68
+ description: string;
69
+ input: {
70
+ type: string;
71
+ properties: {
72
+ now: {
73
+ type: string;
74
+ format: string;
75
+ description: string;
76
+ };
77
+ };
78
+ required: string[];
79
+ };
80
+ for: typeof watching;
81
+ };
82
+ next: {
83
+ description: string;
84
+ input: {
85
+ type: string;
86
+ properties: {
87
+ now: {
88
+ type: string;
89
+ format: string;
90
+ description: string;
91
+ };
92
+ };
93
+ required: string[];
94
+ };
95
+ for: typeof watching;
96
+ };
97
+ keep: {
98
+ description: string;
99
+ input: {
100
+ type: string;
101
+ properties: {
102
+ id: {
103
+ type: string;
104
+ };
105
+ ask: {
106
+ type: string;
107
+ description: string;
108
+ };
109
+ args: {
110
+ type: string;
111
+ };
112
+ note: {};
113
+ cron: {
114
+ type: string;
115
+ };
116
+ at: {
117
+ type: string;
118
+ };
119
+ tz: {
120
+ type: string;
121
+ };
122
+ time: {
123
+ type: string;
124
+ };
125
+ catchUp: {
126
+ type: string;
127
+ };
128
+ };
129
+ required: string[];
130
+ };
131
+ for: typeof root;
132
+ };
133
+ drop: {
134
+ description: string;
135
+ input: {
136
+ type: string;
137
+ properties: {
138
+ id: {
139
+ type: string;
140
+ };
141
+ };
142
+ required: string[];
143
+ };
144
+ for: typeof root;
145
+ };
146
+ };
147
+ keep(args: JsonObject, _asker?: Asker): Promise<JsonObject>;
148
+ drop(args: JsonObject, _asker?: Asker): Promise<JsonObject>;
149
+ protected told(): Promise<void>;
150
+ schedules(): Schedule[];
151
+ tick(args: JsonObject): Promise<JsonObject>;
152
+ list(args: JsonObject): JsonObject;
153
+ next(args: JsonObject): JsonObject;
154
+ }
155
+ type Owner = {
156
+ ask(method: string, args: JsonObject): Promise<unknown>;
157
+ };
158
+ export declare const CLOCK = "clock";
159
+ export declare function setupClock(hosted: Owner, wakes?: string[]): Promise<void>;
160
+ export declare function placeClock(hosted: Owner, being: string): Promise<void>;
161
+ export {};