@nervur-org/dock 0.4.1 → 0.6.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/README.md +17 -15
- package/api/route.ts +4 -72
- package/beings/avatar.ts +11 -7
- package/beings/carry.ts +129 -116
- package/beings/clock.ts +11 -7
- package/beings/desk.ts +7 -6
- package/beings/doorbell.ts +36 -7
- package/beings/index.ts +17 -4
- package/beings/join.ts +7 -4
- package/beings/link.ts +1 -1
- package/beings/porter.ts +5 -1
- package/beings/presence.ts +15 -5
- package/beings/setup.ts +27 -21
- package/beings/user.ts +26 -17
- package/beings/way.ts +1 -1
- package/{dock → box}/agents.ts +37 -44
- package/{dock → box}/apns.ts +4 -4
- package/box/box.ts +31 -0
- package/box/contract.ts +41 -0
- package/box/dialers.ts +88 -0
- package/box/dock.ts +148 -0
- package/box/envoy.ts +96 -0
- package/box/faculty.ts +111 -0
- package/{dock → box}/fcm.ts +1 -1
- package/box/grants.ts +90 -0
- package/box/http.ts +93 -0
- package/{dock → box}/http2.ts +7 -7
- package/box/index.ts +201 -0
- package/box/join.ts +78 -0
- package/box/local.ts +37 -0
- package/{dock → box}/location.ts +15 -3
- package/{dock → box}/notifications.ts +17 -5
- package/box/push.ts +85 -0
- package/box/ring.ts +85 -0
- package/{dock → box}/service.ts +2 -2
- package/box/timer.ts +150 -0
- package/box/tree.ts +22 -0
- package/{dock → box}/webpush.ts +2 -2
- package/cli/client.ts +1 -1
- package/cli/daemon.ts +101 -103
- package/cli/estate.ts +2 -3
- package/cli/nervur.ts +44 -25
- package/cli/stand.ts +1 -1
- package/dist/api/route.d.ts +1 -2
- package/dist/api/route.js +4 -76
- package/dist/beings/avatar.d.ts +4 -3
- package/dist/beings/avatar.js +9 -6
- package/dist/beings/carry.d.ts +10 -12
- package/dist/beings/carry.js +119 -127
- package/dist/beings/clock.js +10 -7
- package/dist/beings/desk.js +8 -7
- package/dist/beings/doorbell.d.ts +3 -1
- package/dist/beings/doorbell.js +37 -4
- package/dist/beings/index.d.ts +24 -4
- package/dist/beings/index.js +15 -4
- package/dist/beings/join.d.ts +1 -0
- package/dist/beings/join.js +6 -4
- package/dist/beings/link.d.ts +0 -1
- package/dist/beings/link.js +1 -1
- package/dist/beings/porter.js +5 -1
- package/dist/beings/presence.d.ts +1 -0
- package/dist/beings/presence.js +12 -3
- package/dist/beings/setup.d.ts +5 -2
- package/dist/beings/setup.js +16 -15
- package/dist/beings/user.d.ts +4 -3
- package/dist/beings/user.js +18 -11
- package/dist/{dock → box}/agents.d.ts +7 -4
- package/dist/{dock → box}/agents.js +30 -37
- package/dist/{dock → box}/apns.d.ts +1 -1
- package/dist/{dock → box}/apns.js +3 -3
- package/dist/box/box.d.ts +9 -0
- package/dist/box/box.js +18 -0
- package/dist/box/contract.d.ts +6 -0
- package/dist/box/contract.js +35 -0
- package/dist/{dock → box}/dialers.d.ts +7 -4
- package/dist/box/dialers.js +76 -0
- package/dist/box/dock.d.ts +182 -0
- package/dist/box/dock.js +128 -0
- package/dist/box/envoy.d.ts +44 -0
- package/dist/box/envoy.js +75 -0
- package/dist/box/faculty.d.ts +68 -0
- package/dist/box/faculty.js +97 -0
- package/dist/{dock → box}/fcm.d.ts +1 -1
- package/dist/{dock → box}/grants.d.ts +4 -3
- package/dist/box/grants.js +78 -0
- package/dist/box/http.d.ts +26 -0
- package/dist/box/http.js +89 -0
- package/dist/{dock → box}/http2.d.ts +1 -1
- package/dist/{dock → box}/http2.js +6 -6
- package/dist/box/index.d.ts +46 -0
- package/dist/box/index.js +179 -0
- package/dist/box/join.d.ts +19 -0
- package/dist/box/join.js +67 -0
- package/dist/{dock → box}/local.d.ts +3 -2
- package/dist/box/local.js +27 -0
- package/dist/{dock → box}/location.d.ts +3 -0
- package/dist/box/location.js +21 -0
- package/dist/{dock → box}/notifications.d.ts +3 -0
- package/dist/{dock → box}/notifications.js +14 -4
- package/dist/{dock → box}/push.d.ts +12 -18
- package/dist/box/push.js +83 -0
- package/dist/box/ring.d.ts +18 -0
- package/dist/box/ring.js +55 -0
- package/dist/{dock → box}/service.d.ts +1 -1
- package/dist/{dock → box}/service.js +1 -1
- package/dist/{dock → box}/timer.d.ts +2 -0
- package/dist/box/timer.js +127 -0
- package/dist/box/tree.d.ts +5 -0
- package/dist/box/tree.js +14 -0
- package/dist/{dock → box}/webpush.d.ts +1 -1
- package/dist/cli/client.d.ts +1 -0
- package/dist/cli/daemon.d.ts +2 -8
- package/dist/cli/daemon.js +102 -107
- package/dist/cli/estate.js +2 -3
- package/dist/cli/nervur.js +44 -24
- package/dist/cli/stand.d.ts +0 -14
- package/dist/cli/stand.js +1 -1
- package/dist/harbor/browser.d.ts +8 -5
- package/dist/harbor/browser.js +20 -21
- package/dist/harbor/capacitor.d.ts +3 -3
- package/dist/harbor/capacitor.js +9 -8
- package/dist/harbor/disk.d.ts +9 -9
- package/dist/harbor/disk.js +56 -51
- package/dist/harbor/edge/alarm.d.ts +4 -2
- package/dist/harbor/edge/alarm.js +9 -19
- package/dist/harbor/edge/edge.d.ts +7 -6
- package/dist/harbor/edge/edge.js +58 -53
- package/dist/harbor/edge/exercise.js +4 -4
- package/dist/harbor/edge/given.d.ts +0 -1
- package/dist/harbor/edge/given.js +0 -29
- package/dist/harbor/edge/storage.d.ts +0 -2
- package/dist/harbor/edge/storage.js +2 -2
- package/dist/harbor/http.js +4 -2
- package/dist/harbor/sealed.d.ts +0 -5
- package/dist/harbor/sealed.js +0 -39
- package/dist/harbor/tauri.d.ts +2 -2
- package/dist/harbor/tauri.js +13 -10
- package/dist/human/app.d.ts +1 -1
- package/dist/human/app.js +10 -12
- package/dist/human/door.d.ts +0 -1
- package/dist/human/door.js +1 -1
- package/dist/human/html.js +1 -1
- package/dist/human/move.js +1 -1
- package/dist/human/tab.bundle.txt +3629 -2824
- package/dist/human/wake.d.ts +5 -2
- package/dist/human/wake.js +23 -14
- package/dist/human/web.d.ts +0 -1
- package/dist/human/web.js +3 -3
- package/dist/human/worker.bundle.txt +2945 -2118
- package/dist/human/worlds.js +3 -3
- package/dist/mcp/agent.d.ts +0 -1
- package/dist/mcp/agent.js +1 -1
- package/dist/mcp/allow.js +3 -2
- package/dist/mcp/http.d.ts +0 -1
- package/dist/mcp/http.js +1 -1
- package/dist/mcp/oauth.d.ts +3 -4
- package/dist/mcp/oauth.js +4 -4
- package/dist/mcp/route.d.ts +1 -1
- package/dist/mcp/runner.d.ts +0 -1
- package/dist/mcp/runner.js +1 -1
- package/dist/mcp/server.d.ts +0 -1
- package/dist/mcp/server.js +1 -1
- package/harbor/browser.ts +22 -24
- package/harbor/capacitor.ts +9 -8
- package/harbor/disk.ts +53 -48
- package/harbor/edge/alarm.ts +15 -12
- package/harbor/edge/edge.ts +61 -55
- package/harbor/edge/exercise.ts +4 -4
- package/harbor/edge/given.ts +3 -33
- package/harbor/edge/storage.ts +2 -2
- package/harbor/http.ts +5 -3
- package/harbor/sealed.ts +0 -35
- package/harbor/tauri.ts +13 -10
- package/human/app.ts +12 -13
- package/human/door.ts +1 -1
- package/human/html.ts +1 -1
- package/human/local.ts +1 -1
- package/human/move.ts +1 -1
- package/human/tab.bundle.txt +3629 -2824
- package/human/wake.ts +36 -16
- package/human/web.ts +4 -4
- package/human/worker.bundle.txt +2945 -2118
- package/human/worlds.ts +3 -3
- package/mcp/agent.ts +1 -1
- package/mcp/allow.ts +2 -1
- package/mcp/http.ts +1 -1
- package/mcp/oauth.ts +4 -4
- package/mcp/route.ts +1 -1
- package/mcp/runner.ts +1 -1
- package/mcp/server.ts +1 -1
- package/package.json +10 -18
- package/GETTING_STARTED.md +0 -138
- package/GLOSSARY.md +0 -118
- package/WORLDS.md +0 -569
- package/beings/envoy.ts +0 -218
- package/dist/beings/envoy.d.ts +0 -54
- package/dist/beings/envoy.js +0 -211
- package/dist/dock/console.d.ts +0 -6
- package/dist/dock/console.js +0 -20
- package/dist/dock/dialers.js +0 -75
- package/dist/dock/faculty.d.ts +0 -24
- package/dist/dock/faculty.js +0 -142
- package/dist/dock/grants.js +0 -99
- package/dist/dock/index.d.ts +0 -41
- package/dist/dock/index.js +0 -113
- package/dist/dock/local.js +0 -44
- package/dist/dock/location.js +0 -11
- package/dist/dock/push.js +0 -132
- package/dist/dock/timer.js +0 -149
- package/dock/console.ts +0 -23
- package/dock/dialers.ts +0 -85
- package/dock/faculty.ts +0 -148
- package/dock/grants.ts +0 -111
- package/dock/index.ts +0 -133
- package/dock/local.ts +0 -48
- package/dock/push.ts +0 -161
- package/dock/timer.ts +0 -174
- package/quo-api.md +0 -196
- package/quo-app-bells.md +0 -280
- package/quo-dock.md +0 -1945
- package/quo-harbor.md +0 -874
- package/quo-human.md +0 -1042
- package/quo-mcp.md +0 -343
- /package/dist/{dock → box}/fcm.js +0 -0
- /package/dist/{dock → box}/webpush.js +0 -0
package/cli/nervur.ts
CHANGED
|
@@ -10,14 +10,15 @@
|
|
|
10
10
|
// device holds for the daemon is either: the unit, the unix user, the home
|
|
11
11
|
// and the environment file all say nervur.
|
|
12
12
|
//
|
|
13
|
-
// nervur init [--dir D] [--ward W] [--user U] [--class C] [--default] [--show] [--domain DOM] mint a seed, boot a ward over a disk store, write both; C is the class of the ward's home being, User unless said; --default marks it the default ward; --show shows it at the web route; --domain writes the four routes under DOM
|
|
13
|
+
// nervur init [--dir D] [--ward W] [--user U] [--class C] [--default] [--show] [--domain DOM] mint a seed, boot a ward over a disk store, write both; C is the class of the ward's home being, beings/User unless said; --default marks it the default ward; --show shows it at the web route; --domain writes the four routes under DOM
|
|
14
14
|
// nervur default [--dir D] <ward> mark one ward the default, the one a caller means when naming none; the one ward hosted is the default unmarked
|
|
15
15
|
// nervur show|hide [--dir D] <ward> show a ward at the web route, listed at its root and its public being's page painted for a stranger; hidden at init, and a hidden ward's page is the plain door
|
|
16
|
-
// nervur domain [--dir D] <domain>
|
|
17
|
-
// nervur
|
|
16
|
+
// nervur domain [--dir D] <domain> say the four routes by the convention, mcp., web., quo. and api. under the domain over https, a cell of the Dock
|
|
17
|
+
// nervur apps [--dir D] <json> say the apps this box's links open, { apple, android }, a cell of the Dock; {} forgets them
|
|
18
|
+
// nervur serve [--dir D] [--http PORT] [--host ADDR] the daemon: harbor from disk, the two sockets, the HTTP door on ADDR, 127.0.0.1 unless said, a socket held to every route the dialers faculty keeps
|
|
18
19
|
// nervur census [--dir D] [--ward W] [--via S] the empty ask: pk and every being
|
|
19
20
|
// nervur boot [--dir D] [--ward W] [--via S] <json> { key, class, public? }
|
|
20
|
-
// nervur invite [--dir D] [--ward W] [--via S] <json> { being, id }; with routes
|
|
21
|
+
// nervur invite [--dir D] [--ward W] [--via S] <json> { being, id }; with the box's routes naming web., the answer carries the link
|
|
21
22
|
// nervur knock [--dir D] [--ward W] [--via S] <json> { being | { boot, key }, id, invitation, method?, args?, wanted? }
|
|
22
23
|
// nervur remove [--dir D] [--ward W] [--via S] <json> { being, id }: a relation out of a being; on the ward pk, an owner
|
|
23
24
|
// nervur unboot [--dir D] [--ward W] [--via S] <json> { being }: a being out of the ward, with every relation she holds
|
|
@@ -29,7 +30,9 @@
|
|
|
29
30
|
// nervur estate <dir> --domain D an estate folder of the shape every estate has: a droplet's harbor directory, unit and Caddyfile, a Mac's launchd agent, on this dock
|
|
30
31
|
//
|
|
31
32
|
// D defaults to $NERVUR_DIR, then ~/.nervur. W defaults to the default ward, and
|
|
32
|
-
// init names a new ward main when none is given.
|
|
33
|
+
// init names a new ward main when none is given. serve takes the HTTP port
|
|
34
|
+
// as --http or $NERVUR_HTTP and the address it binds as --host or
|
|
35
|
+
// $NERVUR_HOST, 127.0.0.1 when neither says one. init takes
|
|
33
36
|
// --user NAME for the user being, defaulting to the device's user. run
|
|
34
37
|
// takes the model's base URL as --url or $NERVUR_MODEL_URL, its name as
|
|
35
38
|
// --model or $NERVUR_MODEL, a bearer from $NERVUR_MODEL_KEY, read here and never
|
|
@@ -42,7 +45,8 @@ import { join } from 'node:path';
|
|
|
42
45
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
43
46
|
import { isInvitation } from 'nervur';
|
|
44
47
|
import { DiskHarbor } from '../harbor/disk.ts';
|
|
45
|
-
import { serve
|
|
48
|
+
import { serve } from './daemon.ts';
|
|
49
|
+
import { BOX, BOX_SEED, type Routes } from '../box/index.ts';
|
|
46
50
|
import { ask, askBeing, side, reach, type Run } from './client.ts';
|
|
47
51
|
import { link } from '../beings/link.ts';
|
|
48
52
|
import { pilotSide } from './pilot.ts';
|
|
@@ -57,7 +61,7 @@ function parse(argv: string[]) {
|
|
|
57
61
|
const a = argv[i] ?? '';
|
|
58
62
|
// a flag takes the word after it, unless that word is a flag or there is
|
|
59
63
|
// none: --default stands alone
|
|
60
|
-
if (a.startsWith('--')) flags[a.slice(2)] = argv[i + 1] === undefined || argv[i + 1]
|
|
64
|
+
if (a.startsWith('--')) flags[a.slice(2)] = argv[i + 1] === undefined || argv[i + 1].startsWith('--') ? '' : argv[++i];
|
|
61
65
|
else rest.push(a);
|
|
62
66
|
}
|
|
63
67
|
const [cmd, json, more, third] = rest;
|
|
@@ -69,8 +73,10 @@ const print = (v: unknown) => console.log(JSON.stringify(v, null, 2));
|
|
|
69
73
|
async function main(argv: string[]): Promise<number> {
|
|
70
74
|
const { cmd, json, more, third, flags, dir, via } = parse(argv);
|
|
71
75
|
if (cmd === 'init') {
|
|
72
|
-
const made = await DiskHarbor.init(dir, flags.ward, flags.user ?? userInfo().username, flags.class, '
|
|
73
|
-
|
|
76
|
+
const made = await DiskHarbor.init(dir, flags.ward, flags.user ?? userInfo().username, flags.class, 'show' in flags);
|
|
77
|
+
// the box's own cells, said to the Dock with no daemon over the folder
|
|
78
|
+
if ('default' in flags) await DiskHarbor.say(dir, 'default', { ward: made.name });
|
|
79
|
+
print(flags.domain ? { ...made, ...(await DiskHarbor.say(dir, 'domain', { domain: flags.domain })) } : made);
|
|
74
80
|
return 0;
|
|
75
81
|
}
|
|
76
82
|
if (cmd === 'default') {
|
|
@@ -78,8 +84,15 @@ async function main(argv: string[]): Promise<number> {
|
|
|
78
84
|
console.error('nervur default <ward>');
|
|
79
85
|
return 2;
|
|
80
86
|
}
|
|
81
|
-
await DiskHarbor.
|
|
82
|
-
|
|
87
|
+
print(await DiskHarbor.say(dir, 'default', { ward: json }));
|
|
88
|
+
return 0;
|
|
89
|
+
}
|
|
90
|
+
if (cmd === 'apps') {
|
|
91
|
+
if (!json) {
|
|
92
|
+
console.error('nervur apps <json> { apple: [...], android: [{ package, fingerprints }] }; {} forgets them');
|
|
93
|
+
return 2;
|
|
94
|
+
}
|
|
95
|
+
print(await DiskHarbor.say(dir, 'apps', JSON.parse(json) as Record<string, unknown>));
|
|
83
96
|
return 0;
|
|
84
97
|
}
|
|
85
98
|
if (cmd === 'show' || cmd === 'hide') {
|
|
@@ -96,13 +109,12 @@ async function main(argv: string[]): Promise<number> {
|
|
|
96
109
|
console.error('nervur domain <domain>');
|
|
97
110
|
return 2;
|
|
98
111
|
}
|
|
99
|
-
print(
|
|
112
|
+
print(await DiskHarbor.say(dir, 'domain', { domain: json }));
|
|
100
113
|
return 0;
|
|
101
114
|
}
|
|
102
115
|
// Every command that reaches a ward is on one: the one named, or the
|
|
103
|
-
// default,
|
|
104
|
-
|
|
105
|
-
const wardOf = async () => flags.ward ?? (await DiskHarbor.defaultOf(dir));
|
|
116
|
+
// default, which the daemon resolves for a request naming none.
|
|
117
|
+
const wardOf = () => flags.ward;
|
|
106
118
|
if (cmd === 'side' || cmd === 'run') {
|
|
107
119
|
if (!flags.as) {
|
|
108
120
|
console.error(`nervur ${cmd} needs --as NAME`);
|
|
@@ -124,11 +136,11 @@ async function main(argv: string[]): Promise<number> {
|
|
|
124
136
|
}
|
|
125
137
|
run = { url, model, ...(key ? { key } : {}), ...(turns !== undefined ? { turns } : {}) };
|
|
126
138
|
}
|
|
127
|
-
await side(dir, flags.as,
|
|
139
|
+
await side(dir, flags.as, wardOf(), run); // stdio to the daemon, until one end closes
|
|
128
140
|
return -1;
|
|
129
141
|
}
|
|
130
142
|
if (cmd === 'pilot') {
|
|
131
|
-
const ward =
|
|
143
|
+
const ward = wardOf();
|
|
132
144
|
await ask(dir, undefined, {}, ward); // no daemon is a clear word now, not at the first tool call
|
|
133
145
|
const serving = await pilotSide(dir, ward, new StdioServerTransport(), via);
|
|
134
146
|
process.stdin.once('end', () => void serving.close().then(() => process.exit(0)));
|
|
@@ -136,7 +148,11 @@ async function main(argv: string[]): Promise<number> {
|
|
|
136
148
|
}
|
|
137
149
|
if (cmd === 'serve') {
|
|
138
150
|
const port = flags.http ?? process.env.NERVUR_HTTP;
|
|
139
|
-
|
|
151
|
+
// Loopback unless an address is said. A daemon reachable from the
|
|
152
|
+
// network is the operator's own word and never a default, so the flag
|
|
153
|
+
// exists and is never filled in for him.
|
|
154
|
+
const host = flags.host || process.env.NERVUR_HOST;
|
|
155
|
+
const s = await serve(dir, port !== undefined ? { http: { port: Number(port), ...(host ? { host } : {}) } } : {});
|
|
140
156
|
print({ dir: s.harbor.dir, sock: s.sock, side: s.side, http: s.http ? `http://${s.http.host}:${s.http.port}` : null, dial: s.quo.dialers.map((d) => d.url), wards: Object.fromEntries([...s.harbor.wards].map(([n, h]) => [n, h.pk])) });
|
|
141
157
|
const stop = () => void s.close().then(() => process.exit(0));
|
|
142
158
|
process.once('SIGINT', stop);
|
|
@@ -160,7 +176,7 @@ async function main(argv: string[]): Promise<number> {
|
|
|
160
176
|
console.error('nervur ask needs <being> [method] [json]');
|
|
161
177
|
return 2;
|
|
162
178
|
}
|
|
163
|
-
const out = await askBeing(dir, json, more, third ? (JSON.parse(third) as Record<string, unknown>) : {},
|
|
179
|
+
const out = await askBeing(dir, json, more, third ? (JSON.parse(third) as Record<string, unknown>) : {}, wardOf());
|
|
164
180
|
print('error' in out ? { error: out.error } : out.result);
|
|
165
181
|
const failed = 'error' in out || (typeof out.result === 'object' && out.result !== null && 'error' in out.result);
|
|
166
182
|
return failed ? 1 : 0;
|
|
@@ -176,25 +192,28 @@ async function main(argv: string[]): Promise<number> {
|
|
|
176
192
|
}
|
|
177
193
|
if (cmd === 'census' || (cmd !== undefined && OWNER_ASKS.has(cmd))) {
|
|
178
194
|
const args = json ? (JSON.parse(json) as Record<string, unknown>) : {};
|
|
179
|
-
const ward =
|
|
195
|
+
const ward = wardOf();
|
|
180
196
|
let out = await ask(dir, cmd === 'census' ? undefined : cmd, args, ward, via);
|
|
181
|
-
// an invitation minted here is handed on as a link when the
|
|
197
|
+
// an invitation minted here is handed on as a link when the box's
|
|
182
198
|
// routes name a web. origin: the world's page with the invitation in
|
|
183
|
-
// the fragment, one string to send to the person it is for
|
|
199
|
+
// the fragment, one string to send to the person it is for. The routes
|
|
200
|
+
// are the Dock's cells, asked of her through the daemon.
|
|
184
201
|
const minted = cmd === 'invite' && !via && 'result' in out && isInvitation(out.result) ? out.result : null;
|
|
185
|
-
const
|
|
202
|
+
const box = minted ? await askBeing(dir, BOX, 'box', {}, BOX_SEED) : null;
|
|
203
|
+
const routes = box && 'result' in box ? ((box.result as { routes?: Routes | null }).routes ?? null) : null;
|
|
204
|
+
const on = ('ward' in out ? out.ward : undefined) ?? ward ?? '';
|
|
186
205
|
// the ward answers null to an invite on an id already held, standing or
|
|
187
206
|
// occupant, and null on a terminal is nothing to read: it is a refusal
|
|
188
207
|
// here, in words, and an exit code a script can tell apart
|
|
189
208
|
if (cmd === 'invite' && 'result' in out && out.result === null) out = { result: { error: 'id taken' } };
|
|
190
|
-
if (minted && routes) print({ ...minted, link: link(`${routes.web}/${encodeURIComponent(
|
|
209
|
+
if (minted && routes) print({ ...minted, link: link(`${routes.web}/${encodeURIComponent(on)}`, minted) });
|
|
191
210
|
else print('error' in out ? { error: out.error } : out.result);
|
|
192
211
|
// an error object the ward answered is an ordinary answer, and still a
|
|
193
212
|
// non-zero exit, so a script can tell a boot that happened from one that did not.
|
|
194
213
|
const failed = 'error' in out || (typeof out.result === 'object' && out.result !== null && 'error' in out.result);
|
|
195
214
|
return failed ? 1 : 0;
|
|
196
215
|
}
|
|
197
|
-
console.error('usage: nervur init|default|show|hide|domain|serve|census|boot|public|invite|knock|remove|unboot|ask|side|run|pilot|reach|estate [--dir D] [--ward W] [--via S] [--as NAME] [--url URL --model NAME] [json]');
|
|
216
|
+
console.error('usage: nervur init|default|show|hide|domain|apps|serve|census|boot|public|invite|knock|remove|unboot|ask|side|run|pilot|reach|estate [--dir D] [--ward W] [--via S] [--as NAME] [--http PORT] [--host ADDR] [--url URL --model NAME] [json]');
|
|
198
217
|
return 2;
|
|
199
218
|
}
|
|
200
219
|
|
package/cli/stand.ts
CHANGED
|
@@ -89,7 +89,7 @@ const ward = (h: Wards, name: string, why: string) => h.wards.get(name) ?? fail(
|
|
|
89
89
|
// for exactly one reason once the reader has held the id to a word the ward
|
|
90
90
|
// does not reserve, which is that the id is taken, and an id taken is this
|
|
91
91
|
// wire, placed the last time the script ran.
|
|
92
|
-
|
|
92
|
+
async function standUp(harbor: Harbor | Wards, decl: Stand): Promise<{ booted: string[]; wired: string[]; already: string[] }> {
|
|
93
93
|
const h = harbor as Wards;
|
|
94
94
|
const booted: string[] = [];
|
|
95
95
|
for (const b of decl.boot ?? []) {
|
package/dist/api/route.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Handler } from '../harbor/http.ts';
|
|
2
2
|
import type { Hosted } from 'nervur/harbor';
|
|
3
3
|
import type { Avatar } from '../beings/avatar.ts';
|
|
4
|
-
import type { Grants } from '../
|
|
4
|
+
import type { Bearers as Grants } from '../box/grants.ts';
|
|
5
5
|
import { type Invitation } from 'nervur';
|
|
6
6
|
export type Held = {
|
|
7
7
|
wards: ReadonlyMap<string, Hosted>;
|
|
@@ -16,6 +16,5 @@ export type Options = {
|
|
|
16
16
|
join: (identity: string, invitation: Invitation, ward: string) => Promise<{
|
|
17
17
|
error?: string;
|
|
18
18
|
}>;
|
|
19
|
-
web?: string;
|
|
20
19
|
};
|
|
21
20
|
export declare function apiRoute(harbor: Held, o: Options): Handler;
|
package/dist/api/route.js
CHANGED
|
@@ -1,35 +1,12 @@
|
|
|
1
1
|
import { readText } from '../harbor/http.js';
|
|
2
2
|
import { invitationOf } from '../beings/link.js';
|
|
3
|
-
import {
|
|
4
|
-
import { CSS } from '../human/style.js';
|
|
3
|
+
import { isIdentity } from '../beings/join.js';
|
|
5
4
|
import { isInvitation, isSilence, isWord, wordOf } from 'nervur';
|
|
6
5
|
const open = { 'access-control-allow-origin': '*', 'access-control-allow-methods': 'GET, POST, OPTIONS', 'access-control-allow-headers': 'content-type, authorization' };
|
|
7
6
|
const word = (s) => (typeof s === 'string' && /^[\w.-]{1,40}$/.test(s) ? s : null);
|
|
8
7
|
export function apiRoute(harbor, o) {
|
|
9
8
|
return async (req, rest) => {
|
|
10
9
|
const json = (status, body) => Response.json(body, { status, headers: open });
|
|
11
|
-
// The one page this road serves, and it wears the one stylesheet every
|
|
12
|
-
// screen wears. No cross-origin header on it: a page is for the browser
|
|
13
|
-
// that was sent here and never for a script somewhere else. Its policy
|
|
14
|
-
// is the allow page's, and for the same reason: a page that runs no
|
|
15
|
-
// script of its own should be allowed none.
|
|
16
|
-
const page = (status, body) => new Response(`<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>nervur</title>\n<style>${CSS}</style>\n</head><body>${body}</body></html>`, {
|
|
17
|
-
status,
|
|
18
|
-
headers: { 'content-type': 'text/html; charset=utf-8', 'cache-control': 'no-store', 'content-security-policy': POLICY, 'referrer-policy': 'no-referrer' },
|
|
19
|
-
});
|
|
20
|
-
// Where this world lives, as the daemon was told and not as the request
|
|
21
|
-
// said: a Host header is the caller's to write, and a page that printed
|
|
22
|
-
// one would print whatever a caller wanted a human to read. Falling back
|
|
23
|
-
// to it is for a box configured with no web address at all, where the
|
|
24
|
-
// request is the only thing that knows.
|
|
25
|
-
const hostOf = (r) => {
|
|
26
|
-
try {
|
|
27
|
-
return o.web === undefined ? (r.headers.get('host') ?? '') : new URL(o.web).host;
|
|
28
|
-
}
|
|
29
|
-
catch {
|
|
30
|
-
return r.headers.get('host') ?? '';
|
|
31
|
-
}
|
|
32
|
-
};
|
|
33
10
|
if (req.method === 'OPTIONS')
|
|
34
11
|
return new Response(null, { status: 204, headers: open });
|
|
35
12
|
if (req.method !== 'GET' && req.method !== 'POST')
|
|
@@ -64,8 +41,8 @@ export function apiRoute(harbor, o) {
|
|
|
64
41
|
return json(400, { error: 'that invitation is for a world that does not live here' });
|
|
65
42
|
const [ward, hosted] = world;
|
|
66
43
|
const identity = word(b.identity);
|
|
67
|
-
if (
|
|
68
|
-
return json(400, { error: 'an identity is one word, and not
|
|
44
|
+
if (!isIdentity(hosted, identity))
|
|
45
|
+
return json(400, { error: 'an identity is one word, and not a being of the ward' });
|
|
69
46
|
// Nobody at a page vouches for a program, so a trade is a knock that
|
|
70
47
|
// spends: an identity already in refreshes, or is removed at the user
|
|
71
48
|
// being and invited again, and never re-enters on a spent invitation.
|
|
@@ -81,57 +58,8 @@ export function apiRoute(harbor, o) {
|
|
|
81
58
|
// device's, and its door answers for it.
|
|
82
59
|
const [, named = '', ask = '', ...more] = rest.split('/');
|
|
83
60
|
const ward = named === '' ? harbor.here() : named;
|
|
84
|
-
// The delivery: a vendor's webhook to an envoy, `/api/<ward>/<being>/deliver`.
|
|
85
|
-
// Not a stranger's ask and not an occupant's: the owner asking her
|
|
86
|
-
// `deliver` with the raw bytes and the headers, and nothing else. The
|
|
87
|
-
// body is read as a string and never parsed here, since the vendor
|
|
88
|
-
// signed the bytes and a reparsed body fails her check; no bearer is
|
|
89
|
-
// read, since the rung is not the caller's to prove. She answers `{ ok }`
|
|
90
|
-
// or her error object, and the route carries it.
|
|
91
|
-
// A GET on the same path is the same ask with an empty body: where an
|
|
92
|
-
// OAuth redirect lands, the code in the query. The query crosses inside
|
|
93
|
-
// the headers under `:query`, a name no header on the wire can have, so
|
|
94
|
-
// her one ask in stays one.
|
|
95
|
-
if (more.length === 1 && more[0] === 'deliver') {
|
|
96
|
-
if (req.method !== 'POST' && req.method !== 'GET')
|
|
97
|
-
return json(405, { error: 'POST, or GET for a redirect' });
|
|
98
|
-
const hosted = harbor.wards.get(ward);
|
|
99
|
-
if (!hosted || !/^[\w-]+$/.test(ask))
|
|
100
|
-
return json(404, { error: 'no such world' });
|
|
101
|
-
const raw = req.method === 'GET' ? '' : await readText(req);
|
|
102
|
-
if (raw === undefined)
|
|
103
|
-
return json(413, { error: 'too big' });
|
|
104
|
-
const headers = {};
|
|
105
|
-
req.headers.forEach((v, k) => (headers[k] = v));
|
|
106
|
-
const query = new URL(req.url).search.slice(1);
|
|
107
|
-
if (query)
|
|
108
|
-
headers[':query'] = query;
|
|
109
|
-
const out = await hosted.ask('ask', { being: ask, method: 'deliver', args: { body: raw, headers } });
|
|
110
|
-
await hosted.save();
|
|
111
|
-
// A GET here is a browser and a browser is a human: a vendor a world
|
|
112
|
-
// sent her to has opened this URL in her tab, so what she is looking
|
|
113
|
-
// at is whatever the envoy answered a machine. She gets a page
|
|
114
|
-
// instead, and only she does; a vendor asking for JSON is answered in
|
|
115
|
-
// JSON as it always was, and so is a GET that says nothing about what
|
|
116
|
-
// it wants.
|
|
117
|
-
const wants = req.method === 'GET' && (req.headers.get('accept') ?? '').includes('text/html');
|
|
118
|
-
if (wants) {
|
|
119
|
-
const ok = out !== null && typeof out === 'object' && !Array.isArray(out) && typeof out.error !== 'string';
|
|
120
|
-
return page(ok ? 200 : 400, landed({ world: ward, host: hostOf(req), ...(o.web ? { at: `${o.web.replace(/\/$/, '')}/${encodeURIComponent(ward)}` } : {}), ok }));
|
|
121
|
-
}
|
|
122
|
-
if (isSilence(out) || out === null || typeof out !== 'object' || Array.isArray(out))
|
|
123
|
-
return json(503, { error: 'silence' });
|
|
124
|
-
const said = out.error;
|
|
125
|
-
if (said === 'no such being' || said === 'unknown ask')
|
|
126
|
-
return json(404, { error: 'no such envoy' });
|
|
127
|
-
if (said === 'silence' || said === 'threw' || said === 'late')
|
|
128
|
-
return json(503, { error: 'silence' });
|
|
129
|
-
if (typeof said === 'string')
|
|
130
|
-
return json(400, out);
|
|
131
|
-
return json(200, out);
|
|
132
|
-
}
|
|
133
61
|
if (!/^[\w.-]+$/.test(ward) || more.length > 0 || (ask !== '' && !/^[\w-]+$/.test(ask)))
|
|
134
|
-
return json(404, { error: 'GET /api for the device’s ward, GET /api/<ward> for the describe, GET or POST /api/<ward>/<ask> for one ask
|
|
62
|
+
return json(404, { error: 'GET /api for the device’s ward, GET /api/<ward> for the describe, GET or POST /api/<ward>/<ask> for one ask' });
|
|
135
63
|
const hosted = harbor.wards.get(ward);
|
|
136
64
|
if (!hosted)
|
|
137
65
|
return json(404, { error: 'no such world' });
|
package/dist/beings/avatar.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
1
|
+
import { Being } from 'nervur';
|
|
2
|
+
import type { Answer, Asker, Blueprint, Invitation, JsonObject, OccupantRecord, Reply, Wanted } from 'nervur';
|
|
3
3
|
export declare const USER = "user";
|
|
4
4
|
export declare const PUSHER = "user:push";
|
|
5
|
-
export declare class Avatar extends
|
|
5
|
+
export declare class Avatar extends Being {
|
|
6
6
|
static cells: {
|
|
7
7
|
inbox: JsonObject[];
|
|
8
8
|
};
|
|
@@ -15,6 +15,7 @@ export declare class Avatar extends Carrier {
|
|
|
15
15
|
for: (occ: OccupantRecord | undefined) => boolean;
|
|
16
16
|
};
|
|
17
17
|
};
|
|
18
|
+
answer(asker: Asker, method?: string, args?: JsonObject): Promise<Reply>;
|
|
18
19
|
readonly ears: Set<(object: JsonObject) => void>;
|
|
19
20
|
notify(args: JsonObject): {
|
|
20
21
|
notified: boolean;
|
package/dist/beings/avatar.js
CHANGED
|
@@ -7,21 +7,24 @@
|
|
|
7
7
|
// calls the four methods below: one of the two ways in, `enter` behind a
|
|
8
8
|
// route's proof or `join` with an invitation, then `tools` and `call`.
|
|
9
9
|
// Nothing of a side is in her cells.
|
|
10
|
-
import { isSilence, isWord, wordOf } from 'nervur';
|
|
11
|
-
import {
|
|
10
|
+
import { Being, isSilence, isWord, wordOf } from 'nervur';
|
|
11
|
+
import { carry } from './carry.js';
|
|
12
12
|
// Her one standing, and the id under which the user being pushes to her.
|
|
13
13
|
// Two ids for one far being, because standings and occupants share one
|
|
14
14
|
// namespace and a record holds one or the other.
|
|
15
15
|
export const USER = 'user';
|
|
16
16
|
export const PUSHER = 'user:push';
|
|
17
|
-
//
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
export class Avatar extends
|
|
17
|
+
// She carries for nobody, until a subclass says who: the presence being
|
|
18
|
+
// carries the box's faculties to her user being, and a bare avatar holds
|
|
19
|
+
// one standing and shows nothing of it.
|
|
20
|
+
export class Avatar extends Being {
|
|
21
21
|
static cells = { inbox: [] };
|
|
22
22
|
static asks = {
|
|
23
23
|
notify: { description: 'a push from the user being', input: { type: 'object' }, for: (occ) => occ?.id === PUSHER },
|
|
24
24
|
};
|
|
25
|
+
answer(asker, method, args = {}) {
|
|
26
|
+
return carry(this, asker, method, args, (a, m, g) => super.answer(a, m, g));
|
|
27
|
+
}
|
|
25
28
|
// Her sides' ears. One identity is one avatar for life, and two sessions
|
|
26
29
|
// of that identity are two sides on her at once, so a push goes to every
|
|
27
30
|
// ear present; each side adds its own on open and removes it on close.
|
package/dist/beings/carry.d.ts
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
import { Being } from 'nervur';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
answer(asker: Asker, method?: string, args?: JsonObject): Promise<Reply>;
|
|
12
|
-
}
|
|
1
|
+
import type { Asker, Being, Blueprint, JsonObject, OccupantRecord, Reply } from 'nervur';
|
|
2
|
+
export type Carrying = {
|
|
3
|
+
carries(occupant: OccupantRecord | undefined, asker: Asker): boolean;
|
|
4
|
+
hidden(id: string): boolean;
|
|
5
|
+
skips(id: string): boolean;
|
|
6
|
+
shows(id: string, occupant: OccupantRecord | undefined, asker: Asker): boolean;
|
|
7
|
+
};
|
|
8
|
+
export declare const CARRIES: Carrying;
|
|
9
|
+
export declare function blueprint(self: Being, asker: Asker): Promise<Blueprint>;
|
|
10
|
+
export declare function carry(self: Being, asker: Asker, method: string | undefined, args: JsonObject, own: (asker: Asker, method: string, args: JsonObject) => Promise<Reply>): Promise<Reply>;
|