@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.
- package/GETTING_STARTED.md +128 -0
- package/LICENSE +202 -0
- package/NOTICE +6 -0
- package/README.md +37 -0
- package/api/quo-api.md +196 -0
- package/api/route.ts +227 -0
- package/beings/GLOSSARY.md +118 -0
- package/beings/WORLDS.md +567 -0
- package/beings/avatar.ts +101 -0
- package/beings/carry.ts +159 -0
- package/beings/clock.ts +552 -0
- package/beings/courier.ts +107 -0
- package/beings/desk.ts +128 -0
- package/beings/doorbell.ts +129 -0
- package/beings/envoy.ts +218 -0
- package/beings/index.ts +16 -0
- package/beings/join.ts +51 -0
- package/beings/link.ts +104 -0
- package/beings/look.ts +139 -0
- package/beings/porter.ts +43 -0
- package/beings/presence.ts +39 -0
- package/beings/quo-app-bells.md +280 -0
- package/beings/quo-dock.md +1942 -0
- package/beings/setup.ts +45 -0
- package/beings/side.ts +60 -0
- package/beings/user.ts +459 -0
- package/beings/way.ts +65 -0
- package/cli/bundles.ts +20 -0
- package/cli/client.ts +73 -0
- package/cli/daemon.ts +581 -0
- package/cli/estate/Caddyfile +33 -0
- package/cli/estate/quo.service +35 -0
- package/cli/estate/systems.quo.plist +44 -0
- package/cli/estate.ts +48 -0
- package/cli/folder.ts +27 -0
- package/cli/oauth.ts +32 -0
- package/cli/pilot.ts +38 -0
- package/cli/quo-dock.md +1942 -0
- package/cli/quo.ts +203 -0
- package/cli/stand.ts +160 -0
- package/dist/api/route.d.ts +21 -0
- package/dist/api/route.js +193 -0
- package/dist/beings/avatar.d.ts +32 -0
- package/dist/beings/avatar.js +100 -0
- package/dist/beings/carry.d.ts +12 -0
- package/dist/beings/carry.js +174 -0
- package/dist/beings/clock.d.ts +161 -0
- package/dist/beings/clock.js +528 -0
- package/dist/beings/courier.d.ts +50 -0
- package/dist/beings/courier.js +96 -0
- package/dist/beings/desk.d.ts +53 -0
- package/dist/beings/desk.js +135 -0
- package/dist/beings/doorbell.d.ts +78 -0
- package/dist/beings/doorbell.js +133 -0
- package/dist/beings/envoy.d.ts +54 -0
- package/dist/beings/envoy.js +211 -0
- package/dist/beings/index.d.ts +14 -0
- package/dist/beings/index.js +16 -0
- package/dist/beings/join.d.ts +15 -0
- package/dist/beings/join.js +42 -0
- package/dist/beings/link.d.ts +14 -0
- package/dist/beings/link.js +91 -0
- package/dist/beings/look.d.ts +30 -0
- package/dist/beings/look.js +104 -0
- package/dist/beings/porter.d.ts +14 -0
- package/dist/beings/porter.js +41 -0
- package/dist/beings/presence.d.ts +8 -0
- package/dist/beings/presence.js +21 -0
- package/dist/beings/setup.d.ts +3 -0
- package/dist/beings/setup.js +39 -0
- package/dist/beings/side.d.ts +32 -0
- package/dist/beings/side.js +37 -0
- package/dist/beings/user.d.ts +315 -0
- package/dist/beings/user.js +462 -0
- package/dist/beings/way.d.ts +19 -0
- package/dist/beings/way.js +28 -0
- package/dist/cli/bundles.d.ts +5 -0
- package/dist/cli/bundles.js +17 -0
- package/dist/cli/client.d.ts +15 -0
- package/dist/cli/client.js +58 -0
- package/dist/cli/daemon.d.ts +50 -0
- package/dist/cli/daemon.js +584 -0
- package/dist/cli/estate/Caddyfile +33 -0
- package/dist/cli/estate/quo.service +35 -0
- package/dist/cli/estate/systems.quo.plist +44 -0
- package/dist/cli/estate.d.ts +5 -0
- package/dist/cli/estate.js +50 -0
- package/dist/cli/folder.d.ts +2 -0
- package/dist/cli/folder.js +25 -0
- package/dist/cli/oauth.d.ts +2 -0
- package/dist/cli/oauth.js +32 -0
- package/dist/cli/pilot.d.ts +5 -0
- package/dist/cli/pilot.js +22 -0
- package/dist/cli/quo.d.ts +2 -0
- package/dist/cli/quo.js +201 -0
- package/dist/cli/stand.d.ts +37 -0
- package/dist/cli/stand.js +110 -0
- package/dist/dock/agents.d.ts +102 -0
- package/dist/dock/agents.js +105 -0
- package/dist/dock/apns.d.ts +27 -0
- package/dist/dock/apns.js +98 -0
- package/dist/dock/console.d.ts +6 -0
- package/dist/dock/console.js +20 -0
- package/dist/dock/dialers.d.ts +56 -0
- package/dist/dock/dialers.js +75 -0
- package/dist/dock/faculty.d.ts +24 -0
- package/dist/dock/faculty.js +142 -0
- package/dist/dock/fcm.d.ts +13 -0
- package/dist/dock/fcm.js +77 -0
- package/dist/dock/grants.d.ts +41 -0
- package/dist/dock/grants.js +99 -0
- package/dist/dock/http2.d.ts +4 -0
- package/dist/dock/http2.js +68 -0
- package/dist/dock/index.d.ts +41 -0
- package/dist/dock/index.js +113 -0
- package/dist/dock/local.d.ts +13 -0
- package/dist/dock/local.js +44 -0
- package/dist/dock/location.d.ts +19 -0
- package/dist/dock/location.js +11 -0
- package/dist/dock/notifications.d.ts +38 -0
- package/dist/dock/notifications.js +60 -0
- package/dist/dock/push.d.ts +79 -0
- package/dist/dock/push.js +132 -0
- package/dist/dock/service.d.ts +4 -0
- package/dist/dock/service.js +107 -0
- package/dist/dock/timer.d.ts +52 -0
- package/dist/dock/timer.js +149 -0
- package/dist/dock/webpush.d.ts +12 -0
- package/dist/dock/webpush.js +144 -0
- package/dist/harbor/browser.d.ts +15 -0
- package/dist/harbor/browser.js +82 -0
- package/dist/harbor/capacitor.d.ts +28 -0
- package/dist/harbor/capacitor.js +218 -0
- package/dist/harbor/disk.d.ts +27 -0
- package/dist/harbor/disk.js +249 -0
- package/dist/harbor/edge/alarm.d.ts +10 -0
- package/dist/harbor/edge/alarm.js +38 -0
- package/dist/harbor/edge/edge.d.ts +54 -0
- package/dist/harbor/edge/edge.js +370 -0
- package/dist/harbor/edge/exercise.d.ts +7 -0
- package/dist/harbor/edge/exercise.js +232 -0
- package/dist/harbor/edge/given.d.ts +26 -0
- package/dist/harbor/edge/given.js +42 -0
- package/dist/harbor/edge/platform.d.ts +39 -0
- package/dist/harbor/edge/storage.d.ts +30 -0
- package/dist/harbor/edge/storage.js +180 -0
- package/dist/harbor/edge/worker.d.ts +17 -0
- package/dist/harbor/edge/worker.js +67 -0
- package/dist/harbor/files.d.ts +15 -0
- package/dist/harbor/files.js +220 -0
- package/dist/harbor/http.d.ts +19 -0
- package/dist/harbor/http.js +177 -0
- package/dist/harbor/idb.d.ts +15 -0
- package/dist/harbor/idb.js +0 -0
- package/dist/harbor/quo.d.ts +10 -0
- package/dist/harbor/quo.js +42 -0
- package/dist/harbor/resolve.d.ts +16 -0
- package/dist/harbor/resolve.js +17 -0
- package/dist/harbor/seal.d.ts +3 -0
- package/dist/harbor/seal.js +25 -0
- package/dist/harbor/sealed.d.ts +35 -0
- package/dist/harbor/sealed.js +162 -0
- package/dist/harbor/tauri.d.ts +16 -0
- package/dist/harbor/tauri.js +59 -0
- package/dist/harbor/ward.d.ts +9 -0
- package/dist/harbor/ward.js +15 -0
- package/dist/human/app.d.ts +32 -0
- package/dist/human/app.js +527 -0
- package/dist/human/apps.d.ts +10 -0
- package/dist/human/apps.js +29 -0
- package/dist/human/dom.d.ts +2 -0
- package/dist/human/dom.js +15 -0
- package/dist/human/door.d.ts +19 -0
- package/dist/human/door.js +92 -0
- package/dist/human/guest.d.ts +3 -0
- package/dist/human/guest.js +33 -0
- package/dist/human/html.d.ts +55 -0
- package/dist/human/html.js +451 -0
- package/dist/human/local.d.ts +7 -0
- package/dist/human/local.js +10 -0
- package/dist/human/mark.d.ts +8 -0
- package/dist/human/mark.js +47 -0
- package/dist/human/move.d.ts +17 -0
- package/dist/human/move.js +90 -0
- package/dist/human/screen.d.ts +19 -0
- package/dist/human/screen.js +119 -0
- package/dist/human/style.d.ts +2 -0
- package/dist/human/style.js +60 -0
- package/dist/human/tab.bundle.txt +4775 -0
- package/dist/human/tab.d.ts +15 -0
- package/dist/human/tab.js +409 -0
- package/dist/human/tree.d.ts +50 -0
- package/dist/human/tree.js +157 -0
- package/dist/human/wake.d.ts +34 -0
- package/dist/human/wake.js +179 -0
- package/dist/human/web.d.ts +33 -0
- package/dist/human/web.js +184 -0
- package/dist/human/worker.bundle.txt +3868 -0
- package/dist/human/worker.d.ts +1 -0
- package/dist/human/worker.js +81 -0
- package/dist/human/worlds.d.ts +24 -0
- package/dist/human/worlds.js +99 -0
- package/dist/mcp/agent.d.ts +10 -0
- package/dist/mcp/agent.js +78 -0
- package/dist/mcp/allow.d.ts +20 -0
- package/dist/mcp/allow.js +88 -0
- package/dist/mcp/http.d.ts +27 -0
- package/dist/mcp/http.js +92 -0
- package/dist/mcp/oauth.d.ts +91 -0
- package/dist/mcp/oauth.js +241 -0
- package/dist/mcp/route.d.ts +15 -0
- package/dist/mcp/route.js +42 -0
- package/dist/mcp/runner.d.ts +45 -0
- package/dist/mcp/runner.js +99 -0
- package/dist/mcp/server.d.ts +11 -0
- package/dist/mcp/server.js +102 -0
- package/dock/agents.ts +121 -0
- package/dock/apns.ts +138 -0
- package/dock/console.ts +23 -0
- package/dock/dialers.ts +85 -0
- package/dock/faculty.ts +148 -0
- package/dock/fcm.ts +103 -0
- package/dock/grants.ts +111 -0
- package/dock/http2.ts +70 -0
- package/dock/index.ts +133 -0
- package/dock/local.ts +48 -0
- package/dock/location.ts +22 -0
- package/dock/notifications.ts +67 -0
- package/dock/push.ts +161 -0
- package/dock/quo-app-bells.md +280 -0
- package/dock/quo-dock.md +1942 -0
- package/dock/service.ts +100 -0
- package/dock/timer.ts +174 -0
- package/dock/webpush.ts +182 -0
- package/harbor/browser.ts +97 -0
- package/harbor/capacitor.ts +223 -0
- package/harbor/disk.ts +237 -0
- package/harbor/edge/alarm.ts +48 -0
- package/harbor/edge/edge.ts +407 -0
- package/harbor/edge/exercise.ts +241 -0
- package/harbor/edge/given.ts +106 -0
- package/harbor/edge/platform.d.ts +39 -0
- package/harbor/edge/storage.ts +179 -0
- package/harbor/edge/text.d.ts +15 -0
- package/harbor/edge/worker.ts +70 -0
- package/harbor/edge/wrangler.toml +30 -0
- package/harbor/files.ts +221 -0
- package/harbor/http.ts +188 -0
- package/harbor/idb.ts +0 -0
- package/harbor/quo-harbor.md +850 -0
- package/harbor/quo.ts +44 -0
- package/harbor/resolve.ts +31 -0
- package/harbor/seal.ts +26 -0
- package/harbor/sealed.ts +183 -0
- package/harbor/tauri.ts +79 -0
- package/harbor/ward.ts +28 -0
- package/human/app.ts +580 -0
- package/human/apps.ts +47 -0
- package/human/dom.ts +21 -0
- package/human/door.ts +113 -0
- package/human/guest.ts +34 -0
- package/human/html.ts +490 -0
- package/human/local.ts +30 -0
- package/human/mark.ts +53 -0
- package/human/move.ts +122 -0
- package/human/quo-human.md +1042 -0
- package/human/screen.ts +136 -0
- package/human/style.ts +63 -0
- package/human/tab.bundle.txt +4775 -0
- package/human/tab.ts +446 -0
- package/human/tree.ts +217 -0
- package/human/wake.ts +191 -0
- package/human/web.ts +259 -0
- package/human/worker.bundle.txt +3868 -0
- package/human/worker.ts +124 -0
- package/human/worlds.ts +127 -0
- package/mcp/agent.ts +94 -0
- package/mcp/allow.ts +131 -0
- package/mcp/http.ts +92 -0
- package/mcp/oauth.ts +270 -0
- package/mcp/quo-mcp.md +343 -0
- package/mcp/route.ts +45 -0
- package/mcp/runner.ts +136 -0
- package/mcp/server.ts +101 -0
- 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 {};
|