@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/harbor/http.ts ADDED
@@ -0,0 +1,188 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // The HTTP door: one listener on loopback that a reverse proxy fronts, and
3
+ // the routes mount on it by path prefix. `/health` is the harbor's own: the
4
+ // wards it hosts, by name and pk, and nothing a stranger can use. Each route
5
+ // lives in its own folder, `human/web.ts`, `harbor/quo.ts`, `mcp/route.ts`,
6
+ // and this file knows none of them: a route is a handler under a prefix.
7
+ //
8
+ // This is the route contract every side is written against, and it stands
9
+ // beside the disk harbor whose door it is. A route is `Request` in and
10
+ // `Response` out, the shapes every runtime speaks, so a side runs on this
11
+ // daemon, on a worker at the edge, in a browser's own service worker, on
12
+ // Deno or on Bun without a line of it changing. A handler answering nothing
13
+ // declines, and the mount tries what comes after it. A folder writing a
14
+ // route takes `Handler`, `readAll` and `Quo` from here and nothing of the
15
+ // daemon that mounts them.
16
+ //
17
+ // `listenHttp` is the one place Node's own objects appear: its door turns an
18
+ // `IncomingMessage` into a `Request` and writes a `Response` back onto a
19
+ // `ServerResponse`, and nothing downstream of it names either. The
20
+ // translation runs that one way only: a `Request` is never dressed up as an
21
+ // `IncomingMessage`, because a route writes headers and there would be
22
+ // nothing to write them onto.
23
+ //
24
+ // The listener half of the socket reach is here too, and for the same reason:
25
+ // holding a socket is the one thing no two runtimes do alike, so it stands
26
+ // with the terrain's own door and never in a route. `ws` is that half on
27
+ // Node; at the edge it is a `WebSocketPair` in the harbor there.
28
+ import { createServer as createHttp, type Server as HttpServer } from 'node:http';
29
+ import { Readable } from 'node:stream';
30
+ import { WebSocketServer } from 'ws';
31
+ import type { IncomingMessage } from 'node:http';
32
+ import { Socket as Held } from '@nervur-org/nervur/harbor';
33
+ import type { Dialer } from '@nervur-org/nervur/harbor';
34
+ import type { DiskHarbor } from './disk.ts';
35
+
36
+ export type Handler = (req: Request, rest: string) => Response | undefined | Promise<Response | undefined>;
37
+ export type Http = { port: number; host: string; mount(prefix: string, handler: Handler): void };
38
+ // What the quo. route holds: the sockets held here and the dialers this
39
+ // daemon runs. `/health` reports them.
40
+ export type Quo = { sockets: Set<Held>; dialers: Dialer[] };
41
+
42
+ // What a request may carry, on the doors that read a body: enough for any ask
43
+ // a ward seals, and not a body chosen by whoever is on the other end.
44
+ // The bytes are a `Uint8Array` and not a `Buffer`: a route runs on every
45
+ // runtime, and node's own type is not one of the shapes they all speak.
46
+ const BODY = 1 << 20;
47
+ export async function readAll(req: Request): Promise<Uint8Array | undefined> {
48
+ if (!req.body) return new Uint8Array(0);
49
+ const chunks: Uint8Array[] = [];
50
+ let size = 0;
51
+ const reader = req.body.getReader();
52
+ for (;;) {
53
+ const { done, value } = await reader.read();
54
+ if (done) break;
55
+ size += value.length;
56
+ if (size > BODY) {
57
+ await reader.cancel().catch(() => {});
58
+ return undefined;
59
+ }
60
+ chunks.push(value);
61
+ }
62
+ const all = new Uint8Array(size);
63
+ let at = 0;
64
+ for (const c of chunks) {
65
+ all.set(c, at);
66
+ at += c.length;
67
+ }
68
+ return all;
69
+ }
70
+
71
+ // The same body as text, for the doors that read one: JSON, a form, or a
72
+ // vendor's signed bytes a being reads as a string.
73
+ export async function readText(req: Request): Promise<string | undefined> {
74
+ const raw = await readAll(req);
75
+ return raw === undefined ? undefined : new TextDecoder().decode(raw);
76
+ }
77
+
78
+ export async function listenHttp(harbor: DiskHarbor, host: string, port: number, quo: Quo): Promise<Http & { server: HttpServer }> {
79
+ const mounts = new Map<string, Handler>();
80
+ const server = createHttp((req, res) => {
81
+ const url = new URL(req.url ?? '/', `http://${req.headers.host ?? 'localhost'}`);
82
+ const answer = async (): Promise<Response> => {
83
+ if (url.pathname === '/health') {
84
+ // the wards it hosts, and the directory: pks and whether each is a
85
+ // socket held here, which is what an operator needs to see a dialer.
86
+ // Open to any origin: a tab reads it, and it holds nothing a stranger can use.
87
+ return Response.json(
88
+ {
89
+ ok: true,
90
+ wards: Object.fromEntries([...harbor.wards].map(([n, h]) => [n, h.pk])),
91
+ reaches: Object.fromEntries([...harbor.reaches].map(([pk, b]) => [pk, b.held ? 'held' : 'reach'])),
92
+ sockets: quo.sockets.size,
93
+ dialers: quo.dialers.map((d) => ({ url: d.url, open: d.socket !== null })),
94
+ // How many saves the disk refused, per ward. The asker whose
95
+ // write was refused was told at the door; this is the operator's
96
+ // half, and a box that says nothing about it looks healthy while
97
+ // one world stops keeping anything.
98
+ faults: Object.fromEntries([...harbor.faults]),
99
+ },
100
+ { headers: { 'access-control-allow-origin': '*' } },
101
+ );
102
+ }
103
+ for (const [prefix, handler] of mounts) {
104
+ if (url.pathname === prefix || url.pathname.startsWith(prefix + '/')) {
105
+ const out = await handler(asRequest(req, url), url.pathname.slice(prefix.length));
106
+ if (out) return out;
107
+ break;
108
+ }
109
+ }
110
+ return Response.json({ error: 'no such route' }, { status: 404 });
111
+ };
112
+ void answer()
113
+ .then((out) => writeResponse(out, res))
114
+ .catch(() => {
115
+ if (!res.headersSent) res.writeHead(500);
116
+ res.end();
117
+ });
118
+ });
119
+ await new Promise<void>((ok, no) => {
120
+ server.once('error', no);
121
+ server.listen(port, host, () => ok());
122
+ });
123
+ hold(server, harbor, quo);
124
+ const address = server.address();
125
+ const bound = typeof address === 'object' && address !== null ? address.port : port;
126
+ return { server, host, port: bound, mount: (prefix, handler) => mounts.set(prefix.replace(/\/$/, ''), handler) };
127
+ }
128
+
129
+ // A dialer's held socket, this terrain's way: an upgrade at the quo route on
130
+ // the daemon's own listener, held as the library's `Socket`. Its pks are bound
131
+ // at announce, each proven at its door, and unbound when the line drops. The
132
+ // route itself reads no socket, and every runtime holds one differently.
133
+ function hold(server: HttpServer, harbor: DiskHarbor, quo: Quo): void {
134
+ const wss = new WebSocketServer({ noServer: true });
135
+ server.on('upgrade', (req: IncomingMessage, socket, head) => {
136
+ const url = new URL(req.url ?? '/', 'http://localhost');
137
+ if (url.pathname.replace(/\/$/, '') !== '/quo') return void socket.destroy(); // the proxy rewrites the route root to /quo/
138
+ wss.handleUpgrade(req, socket, head, (ws) => {
139
+ const s: Held = new Held(
140
+ ws,
141
+ (pk, bytes) => harbor.deliver(pk, bytes),
142
+ (far) => void harbor.bind(far, s, true), // a dialer's claims, each proven at its door: held here, reachable through this socket
143
+ () => {
144
+ harbor.unbind(s);
145
+ quo.sockets.delete(s);
146
+ },
147
+ );
148
+ quo.sockets.add(s);
149
+ s.announce([...harbor.doors.keys()]);
150
+ });
151
+ });
152
+ }
153
+
154
+ // Node's request as a `Request`: the one translation in, at the daemon's own
155
+ // door. A method that carries no body carries none here either, since the
156
+ // web shape refuses one.
157
+ function asRequest(req: import('node:http').IncomingMessage, url: URL): Request {
158
+ const headers = new Headers();
159
+ for (const [k, v] of Object.entries(req.headers)) {
160
+ if (v === undefined) continue;
161
+ if (Array.isArray(v)) for (const one of v) headers.append(k, one);
162
+ else headers.set(k, v);
163
+ }
164
+ const method = req.method ?? 'GET';
165
+ const bodied = method !== 'GET' && method !== 'HEAD';
166
+ return new Request(url, {
167
+ method,
168
+ headers,
169
+ ...(bodied ? { body: Readable.toWeb(req) as ReadableStream<Uint8Array>, duplex: 'half' } : {}),
170
+ } as RequestInit);
171
+ }
172
+
173
+ // And the one translation out.
174
+ async function writeResponse(out: Response, res: import('node:http').ServerResponse): Promise<void> {
175
+ const headers: Record<string, string | string[]> = {};
176
+ out.headers.forEach((v, k) => {
177
+ headers[k] = k === 'set-cookie' ? [...(Array.isArray(headers[k]) ? (headers[k] as string[]) : []), v] : v;
178
+ });
179
+ res.writeHead(out.status, headers);
180
+ if (!out.body) return void res.end();
181
+ const reader = out.body.getReader();
182
+ for (;;) {
183
+ const { done, value } = await reader.read();
184
+ if (done) break;
185
+ res.write(Buffer.from(value));
186
+ }
187
+ res.end();
188
+ }
package/harbor/idb.ts ADDED
Binary file