@nervur-org/dock 0.3.1 → 0.4.1
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 +23 -13
- package/{beings/GLOSSARY.md → GLOSSARY.md} +3 -3
- package/NOTICE +1 -1
- package/README.md +20 -16
- package/{beings/WORLDS.md → WORLDS.md} +6 -4
- package/api/route.ts +3 -3
- package/beings/avatar.ts +2 -2
- package/beings/carry.ts +2 -2
- package/beings/clock.ts +2 -2
- package/beings/courier.ts +2 -2
- package/beings/desk.ts +2 -2
- package/beings/doorbell.ts +2 -2
- package/beings/envoy.ts +2 -2
- package/beings/join.ts +2 -2
- package/beings/link.ts +5 -5
- package/beings/look.ts +1 -1
- package/beings/porter.ts +2 -2
- package/beings/presence.ts +1 -1
- package/beings/setup.ts +3 -3
- package/beings/side.ts +2 -2
- package/beings/user.ts +2 -2
- package/beings/way.ts +1 -1
- package/cli/client.ts +6 -6
- package/cli/daemon.ts +7 -7
- package/cli/estate/{quo.service → nervur.service} +12 -12
- package/cli/estate/org.nervur.plist +44 -0
- package/cli/estate.ts +6 -6
- package/cli/{quo.ts → nervur.ts} +43 -37
- package/cli/pilot.ts +3 -3
- package/cli/stand.ts +3 -3
- package/dist/api/route.d.ts +2 -2
- package/dist/api/route.js +2 -2
- package/dist/beings/avatar.d.ts +1 -1
- package/dist/beings/avatar.js +1 -1
- package/dist/beings/carry.d.ts +2 -2
- package/dist/beings/carry.js +1 -1
- package/dist/beings/clock.d.ts +2 -2
- package/dist/beings/clock.js +1 -1
- package/dist/beings/courier.d.ts +2 -2
- package/dist/beings/courier.js +1 -1
- package/dist/beings/desk.d.ts +3 -3
- package/dist/beings/desk.js +1 -1
- package/dist/beings/doorbell.d.ts +3 -3
- package/dist/beings/doorbell.js +1 -1
- package/dist/beings/envoy.d.ts +2 -2
- package/dist/beings/envoy.js +1 -1
- package/dist/beings/join.d.ts +2 -2
- package/dist/beings/link.d.ts +2 -2
- package/dist/beings/link.js +3 -3
- package/dist/beings/look.d.ts +1 -1
- package/dist/beings/porter.d.ts +2 -2
- package/dist/beings/porter.js +1 -1
- package/dist/beings/presence.d.ts +1 -1
- package/dist/beings/setup.d.ts +1 -1
- package/dist/beings/side.d.ts +1 -1
- package/dist/beings/side.js +1 -1
- package/dist/beings/user.d.ts +3 -3
- package/dist/beings/user.js +1 -1
- package/dist/beings/way.d.ts +1 -1
- package/dist/cli/client.js +4 -4
- package/dist/cli/daemon.d.ts +1 -1
- package/dist/cli/daemon.js +6 -6
- package/dist/cli/estate/{quo.service → nervur.service} +12 -12
- package/dist/cli/estate/org.nervur.plist +44 -0
- package/dist/cli/estate.js +6 -6
- package/dist/cli/{quo.js → nervur.js} +43 -37
- package/dist/cli/stand.d.ts +2 -2
- package/dist/dock/agents.d.ts +1 -1
- package/dist/dock/agents.js +1 -1
- package/dist/dock/apns.js +1 -1
- package/dist/dock/console.d.ts +1 -1
- package/dist/dock/dialers.d.ts +2 -2
- package/dist/dock/dialers.js +2 -2
- package/dist/dock/faculty.d.ts +2 -2
- package/dist/dock/faculty.js +1 -1
- package/dist/dock/grants.d.ts +1 -1
- package/dist/dock/grants.js +1 -1
- package/dist/dock/http2.js +1 -1
- package/dist/dock/index.d.ts +1 -1
- package/dist/dock/index.js +1 -1
- package/dist/dock/local.d.ts +2 -2
- package/dist/dock/location.d.ts +1 -1
- package/dist/dock/notifications.d.ts +1 -1
- package/dist/dock/notifications.js +1 -1
- package/dist/dock/push.d.ts +1 -1
- package/dist/dock/push.js +3 -3
- package/dist/dock/service.js +1 -1
- package/dist/dock/timer.d.ts +1 -1
- package/dist/harbor/browser.d.ts +2 -2
- package/dist/harbor/browser.js +4 -4
- package/dist/harbor/capacitor.d.ts +1 -1
- package/dist/harbor/capacitor.js +1 -1
- package/dist/harbor/disk.d.ts +4 -4
- package/dist/harbor/disk.js +5 -5
- package/dist/harbor/edge/edge.d.ts +2 -2
- package/dist/harbor/edge/edge.js +5 -5
- package/dist/harbor/edge/exercise.js +8 -8
- package/dist/harbor/edge/given.d.ts +1 -1
- package/dist/harbor/edge/platform.d.ts +1 -1
- package/dist/harbor/edge/storage.d.ts +1 -1
- package/dist/harbor/edge/storage.js +2 -2
- package/dist/harbor/edge/worker.js +2 -2
- package/dist/harbor/files.d.ts +1 -1
- package/dist/harbor/files.js +2 -2
- package/dist/harbor/http.d.ts +2 -2
- package/dist/harbor/http.js +1 -1
- package/dist/harbor/idb.d.ts +1 -1
- package/dist/harbor/idb.js +0 -0
- package/dist/harbor/quo.js +1 -1
- package/dist/harbor/seal.js +1 -1
- package/dist/harbor/sealed.d.ts +1 -1
- package/dist/harbor/sealed.js +4 -4
- package/dist/harbor/tauri.d.ts +1 -1
- package/dist/harbor/tauri.js +1 -1
- package/dist/harbor/ward.d.ts +1 -1
- package/dist/human/app.d.ts +2 -2
- package/dist/human/door.js +4 -4
- package/dist/human/guest.js +1 -1
- package/dist/human/html.d.ts +1 -1
- package/dist/human/html.js +1 -1
- package/dist/human/local.d.ts +1 -1
- package/dist/human/mark.js +2 -2
- package/dist/human/move.d.ts +2 -2
- package/dist/human/move.js +1 -1
- package/dist/human/screen.d.ts +1 -1
- package/dist/human/screen.js +3 -3
- package/dist/human/tab.bundle.txt +32 -32
- package/dist/human/tab.js +7 -7
- package/dist/human/tree.d.ts +1 -1
- package/dist/human/wake.d.ts +2 -2
- package/dist/human/wake.js +1 -1
- package/dist/human/web.d.ts +1 -1
- package/dist/human/web.js +4 -4
- package/dist/human/worker.bundle.txt +27 -27
- package/dist/human/worker.js +4 -4
- package/dist/human/worlds.d.ts +2 -2
- package/dist/human/worlds.js +4 -4
- package/dist/mcp/agent.d.ts +1 -1
- package/dist/mcp/allow.d.ts +1 -1
- package/dist/mcp/allow.js +2 -2
- package/dist/mcp/oauth.js +1 -1
- package/dist/mcp/runner.d.ts +1 -1
- package/dist/mcp/server.d.ts +2 -2
- package/dist/mcp/server.js +2 -2
- package/dock/agents.ts +2 -2
- package/dock/apns.ts +1 -1
- package/dock/console.ts +1 -1
- package/dock/dialers.ts +3 -3
- package/dock/faculty.ts +2 -2
- package/dock/grants.ts +2 -2
- package/dock/http2.ts +1 -1
- package/dock/index.ts +3 -3
- package/dock/local.ts +1 -1
- package/dock/location.ts +1 -1
- package/dock/notifications.ts +2 -2
- package/dock/push.ts +4 -4
- package/dock/service.ts +1 -1
- package/dock/timer.ts +1 -1
- package/harbor/browser.ts +5 -5
- package/harbor/capacitor.ts +2 -2
- package/harbor/disk.ts +8 -8
- package/harbor/edge/edge.ts +7 -7
- package/harbor/edge/exercise.ts +11 -11
- package/harbor/edge/given.ts +1 -1
- package/harbor/edge/platform.d.ts +1 -1
- package/harbor/edge/storage.ts +2 -2
- package/harbor/edge/worker.ts +2 -2
- package/harbor/edge/wrangler.toml +3 -3
- package/harbor/files.ts +2 -2
- package/harbor/http.ts +2 -2
- package/harbor/idb.ts +0 -0
- package/harbor/quo.ts +1 -1
- package/harbor/resolve.ts +1 -1
- package/harbor/seal.ts +1 -1
- package/harbor/sealed.ts +5 -5
- package/harbor/tauri.ts +2 -2
- package/harbor/ward.ts +1 -1
- package/human/app.ts +2 -2
- package/human/apps.ts +1 -1
- package/human/door.ts +4 -4
- package/human/guest.ts +2 -2
- package/human/html.ts +2 -2
- package/human/local.ts +1 -1
- package/human/mark.ts +2 -2
- package/human/move.ts +3 -3
- package/human/screen.ts +5 -5
- package/human/tab.bundle.txt +32 -32
- package/human/tab.ts +10 -10
- package/human/tree.ts +1 -1
- package/human/wake.ts +3 -3
- package/human/web.ts +5 -5
- package/human/worker.bundle.txt +27 -27
- package/human/worker.ts +6 -6
- package/human/worlds.ts +5 -5
- package/mcp/agent.ts +1 -1
- package/mcp/allow.ts +5 -5
- package/mcp/oauth.ts +1 -1
- package/mcp/runner.ts +1 -1
- package/mcp/server.ts +3 -3
- package/package.json +15 -7
- package/{api/quo-api.md → quo-api.md} +1 -1
- package/{beings/quo-dock.md → quo-dock.md} +75 -72
- package/{harbor/quo-harbor.md → quo-harbor.md} +54 -30
- package/{human/quo-human.md → quo-human.md} +7 -7
- package/{mcp/quo-mcp.md → quo-mcp.md} +10 -10
- package/cli/estate/systems.quo.plist +0 -44
- package/cli/quo-dock.md +0 -1942
- package/dist/cli/estate/systems.quo.plist +0 -44
- package/dock/quo-app-bells.md +0 -280
- package/dock/quo-dock.md +0 -1942
- /package/dist/cli/{quo.d.ts → nervur.d.ts} +0 -0
- /package/{beings/quo-app-bells.md → quo-app-bells.md} +0 -0
package/GETTING_STARTED.md
CHANGED
|
@@ -8,27 +8,37 @@ This is the shortest road from nothing to each of the three, for a
|
|
|
8
8
|
stranger with a terminal. `SPEC.md` is the truth behind every sentence
|
|
9
9
|
here and assumes nothing; `quo-dock.md` is the dock, the part a box runs.
|
|
10
10
|
|
|
11
|
+
Quo is the protocol and Nervur is this kit of it. Every name you install
|
|
12
|
+
and every command you type is Nervur's; `quo` stays on the wire, as the
|
|
13
|
+
`quo.` route a ward's door answers at.
|
|
14
|
+
|
|
11
15
|
Two packages, and you start with the one that fits what you have:
|
|
12
16
|
|
|
13
|
-
-
|
|
17
|
+
- `nervur`, the library. A harbor, a ward and a being in one
|
|
14
18
|
process, no wire, no files. For a program that wants Quo inside it.
|
|
15
|
-
- `@nervur-org/dock`, the dock. A daemon and one command, `
|
|
19
|
+
- `@nervur-org/dock`, the dock. A daemon and one command, `nervur`, that
|
|
16
20
|
stand a box up: a person's world with a page, a model's side, an api, a
|
|
17
21
|
door on the wire. For a box that receives people and models.
|
|
18
22
|
|
|
23
|
+
The command is in the dock and not in the library, so `npm install nervur`
|
|
24
|
+
gives you something to import and no command, and the command arrives with
|
|
25
|
+
`npm install -g @nervur-org/dock`. That is how a kit of this shape is
|
|
26
|
+
always named: one unscoped headline name for the library, the parts under
|
|
27
|
+
the scope, and the CLI in the package that owns it.
|
|
28
|
+
|
|
19
29
|
## A being, in one process
|
|
20
30
|
|
|
21
31
|
```bash
|
|
22
|
-
npm install
|
|
32
|
+
npm install nervur
|
|
23
33
|
```
|
|
24
34
|
|
|
25
35
|
A being is a class with `asks`, the methods anyone may reach, each with
|
|
26
36
|
the JSON schema of its input. Everything else on the class is hers alone.
|
|
27
37
|
|
|
28
38
|
```js
|
|
29
|
-
import { Being } from '
|
|
30
|
-
import { Ward } from '
|
|
31
|
-
import { MemoryHarbor } from '
|
|
39
|
+
import { Being } from 'nervur';
|
|
40
|
+
import { Ward } from 'nervur/ward';
|
|
41
|
+
import { MemoryHarbor } from 'nervur/harbor';
|
|
32
42
|
|
|
33
43
|
class Shop extends Being {
|
|
34
44
|
static asks = { price: { input: { type: 'object', properties: { item: { type: 'string' } } } } };
|
|
@@ -78,11 +88,11 @@ ward, never held.
|
|
|
78
88
|
|
|
79
89
|
```bash
|
|
80
90
|
npm install @nervur-org/dock
|
|
81
|
-
npx
|
|
82
|
-
npx
|
|
91
|
+
npx nervur init --dir ~/.nervur --ward acme --user ana --domain acme.com --default --show
|
|
92
|
+
npx nervur serve --dir ~/.nervur --http 8787
|
|
83
93
|
```
|
|
84
94
|
|
|
85
|
-
`init` mints the ward `acme`, boots
|
|
95
|
+
`init` mints the ward `acme`, boots ana's user being, her doorbell and
|
|
86
96
|
the desk in it, marks it the default ward and shows it at the web route,
|
|
87
97
|
and writes `routes.json`, the four routes of a box, `mcp.`, `web.`, `quo.`
|
|
88
98
|
and `api.` under the domain, for a proxy to map onto the one loopback
|
|
@@ -97,7 +107,7 @@ at one loopback address:
|
|
|
97
107
|
command is its client. From a second terminal, a phone:
|
|
98
108
|
|
|
99
109
|
```bash
|
|
100
|
-
npx
|
|
110
|
+
npx nervur invite --dir ~/.nervur '{"being":"ana","id":"phone"}'
|
|
101
111
|
```
|
|
102
112
|
|
|
103
113
|
The answer carries `link`, the ward's page with the invitation in its
|
|
@@ -108,7 +118,7 @@ JSON at `api.`, and what another box gets is the door at `quo.`; a being
|
|
|
108
118
|
answers each the same, because each is an ask at her door.
|
|
109
119
|
|
|
110
120
|
The box's own doings are rows on the faculties of its dock ward, placed by
|
|
111
|
-
the root with `
|
|
121
|
+
the root with `nervur ask --ward dock`: a socket held to another box, an
|
|
112
122
|
agent run for a ward, a schedule on the clock. A second box owns this one
|
|
113
123
|
across the wire with an invitation on the ward's own pk, knocked with
|
|
114
124
|
from there, and every owner command with `--via` from then on. Each of
|
|
@@ -119,8 +129,8 @@ somebody with nothing else to read.
|
|
|
119
129
|
## Your own beings on a box
|
|
120
130
|
|
|
121
131
|
A box holds the dock's classes and yours. `classes/index.ts` beside the
|
|
122
|
-
wards exports each of yours by name, and `
|
|
123
|
-
"class":"Shop"}'` boots one; `
|
|
132
|
+
wards exports each of yours by name, and `nervur boot '{"key":"shop",
|
|
133
|
+
"class":"Shop"}'` boots one; `nervur init --class Shop` makes the ward's home
|
|
124
134
|
being one of yours, an organisation's ward with the org as its being. A
|
|
125
135
|
being's asks may name who may reach them, `for`, over the record of the
|
|
126
136
|
occupant asking, and her `cells` are what she keeps between boots: the
|
|
@@ -50,14 +50,14 @@ are where the rest are built.
|
|
|
50
50
|
faculties that box lends. Never a home, and never migrated.
|
|
51
51
|
- **estate**: all of one owner's harbors, with what is theirs alone, in a
|
|
52
52
|
tree of the owner's own and never published.
|
|
53
|
-
- **daemon**: `
|
|
53
|
+
- **daemon**: `nervur serve`, the one process on a device that holds a harbor.
|
|
54
54
|
- **route**: a hostname a reverse proxy sends to one process: `web.`,
|
|
55
55
|
`quo.`, `mcp.`, `api.`.
|
|
56
56
|
- **user being**: the org being of an org of one: a human's own, holding
|
|
57
57
|
all of her standings. The dock's `User` class, which also serves a
|
|
58
58
|
membership the desk makes in another org's ward.
|
|
59
59
|
- **front desk**: the being every fresh client meets; hands out invitations
|
|
60
|
-
minted by the ward's being, and is what `
|
|
60
|
+
minted by the ward's being, and is what `nervur init` marks public in a
|
|
61
61
|
fresh ward. Any ward may mark one being of any class public instead.
|
|
62
62
|
- **avatar**: a being that is someone's voice from outside Quo, one per
|
|
63
63
|
client identity.
|
|
@@ -106,7 +106,7 @@ are where the rest are built.
|
|
|
106
106
|
- **look**: how a being is presented, as values; one optional ask.
|
|
107
107
|
- **page**: a being's page as a tree of values in the screen's grammar; one
|
|
108
108
|
optional ask, read by a screen alone.
|
|
109
|
-
- **owner pilot**: `
|
|
109
|
+
- **owner pilot**: `nervur pilot`, the model side over a ward's owner asks,
|
|
110
110
|
reaching the daemon's socket; with `--via`, a ward elsewhere through a
|
|
111
111
|
standing. Under cron, the estate's own agent.
|
|
112
112
|
|
package/NOTICE
CHANGED
package/README.md
CHANGED
|
@@ -1,35 +1,39 @@
|
|
|
1
1
|
# The dock
|
|
2
2
|
|
|
3
3
|
What every estate on Quo needs and nobody writes twice: a daemon and the
|
|
4
|
-
`
|
|
4
|
+
`nervur` command, the front desk, the user being and the avatar, harbors on
|
|
5
5
|
disk, in a tab and on the edge, the model sides and the screen, the
|
|
6
6
|
credential exchange. An estate is all of one owner's harbors, and it is
|
|
7
7
|
built on the dock, on Quo.
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
10
|
npm install @nervur-org/dock
|
|
11
|
-
npx
|
|
12
|
-
npx
|
|
13
|
-
npx
|
|
11
|
+
npx nervur estate my-estate --domain example.com
|
|
12
|
+
npx nervur init --dir my-estate/droplet
|
|
13
|
+
npx nervur serve --dir my-estate/droplet
|
|
14
14
|
```
|
|
15
15
|
|
|
16
|
-
`
|
|
17
|
-
droplet's
|
|
16
|
+
`nervur estate` writes an estate folder: one folder per harbor, the first a
|
|
17
|
+
droplet's harbor directory with its routes under your domain, its unit and
|
|
18
18
|
its Caddyfile, and a package file that depends on the dock and nothing
|
|
19
19
|
else. Add your beings to `droplet/classes/`, and your placement to the
|
|
20
20
|
routes and agents files.
|
|
21
21
|
|
|
22
|
-
`
|
|
23
|
-
documents; read it first. It knows nothing the spec of Quo does not say,
|
|
22
|
+
[`quo-dock.md`](quo-dock.md) is the truth of this package and the trunk of
|
|
23
|
+
its documents; read it first. It knows nothing the spec of Quo does not say,
|
|
24
24
|
and adds no word to harbor, ward or being. Beside it are the sides,
|
|
25
|
-
`human
|
|
26
|
-
`
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
25
|
+
[`quo-human.md`](quo-human.md), [`quo-mcp.md`](quo-mcp.md),
|
|
26
|
+
[`quo-api.md`](quo-api.md) and [`quo-harbor.md`](quo-harbor.md), and the
|
|
27
|
+
bells, [`quo-app-bells.md`](quo-app-bells.md);
|
|
28
|
+
[`WORLDS.md`](WORLDS.md) is what home, presence and membership mean, and
|
|
29
|
+
[`GLOSSARY.md`](GLOSSARY.md) is every word above the spec. Each stands once,
|
|
30
|
+
at the root of the package, the way `nervur` and `@nervur-org/ui` carry
|
|
31
|
+
theirs.
|
|
32
|
+
|
|
33
|
+
## Versions
|
|
34
|
+
|
|
35
|
+
No compatibility promise before 1.0.0. The package ships an emitted `dist/`
|
|
36
|
+
with its command, and the source beside it.
|
|
33
37
|
|
|
34
38
|
## License
|
|
35
39
|
|
|
@@ -55,9 +55,11 @@ That is the whole of it, and this is what it leaves open:
|
|
|
55
55
|
- **Quo has no home.** Three boxes rooted by one party, each holding
|
|
56
56
|
standings on the other two, is Quo, and so is one box with a hundred
|
|
57
57
|
wards. Nothing says which ward, if any, is the one that matters.
|
|
58
|
-
- **Quo has no route.**
|
|
59
|
-
|
|
60
|
-
|
|
58
|
+
- **Quo has no route.** It pins the two shapes a reach may take and the
|
|
59
|
+
frames they carry, because two harbors meet on those, and it says nothing
|
|
60
|
+
about where a ward lives: which hostname answers, how a harbor learns a
|
|
61
|
+
foreign pk, and whether anyone can be dialed at all are the device's. A
|
|
62
|
+
hostname, a screen and a tool list are outside the protocol entirely.
|
|
61
63
|
- **Quo has four rungs and no more.** A stranger hears the public being as
|
|
62
64
|
`{}`. An occupant hears what the gate shows that occupant. An owner
|
|
63
65
|
reaches in. A root holds the ask pointer. Guest, member, subscriber,
|
|
@@ -356,7 +358,7 @@ An org's box hears the world on three routes, and a route is a hostname the
|
|
|
356
358
|
reverse proxy sends to one process. `web.` speaks a being's describe as
|
|
357
359
|
forms and a page, for a human at a screen. `mcp.` speaks it as tools, for a
|
|
358
360
|
model at its client. `api.` speaks it as plain HTTP and JSON, for a
|
|
359
|
-
program, a script or a tab on another origin. A fourth voice, the `
|
|
361
|
+
program, a script or a tab on another origin. A fourth voice, the `nervur`
|
|
360
362
|
command on the device itself, speaks it as lines, and reaches the daemon's
|
|
361
363
|
socket rather than a hostname.
|
|
362
364
|
|
package/api/route.ts
CHANGED
|
@@ -34,13 +34,13 @@
|
|
|
34
34
|
// shapes, are the route's own 404 and never a being's.
|
|
35
35
|
import type { Handler } from '../harbor/http.ts';
|
|
36
36
|
import { readText } from '../harbor/http.ts';
|
|
37
|
-
import type { Hosted } from '
|
|
37
|
+
import type { Hosted } from 'nervur/harbor';
|
|
38
38
|
import type { Avatar } from '../beings/avatar.ts';
|
|
39
39
|
import { invitationOf } from '../beings/link.ts';
|
|
40
40
|
import { landed, POLICY } from '../human/door.ts';
|
|
41
41
|
import { CSS } from '../human/style.ts';
|
|
42
42
|
import type { Grants } from '../dock/grants.ts';
|
|
43
|
-
import { isInvitation, isSilence, isWord, wordOf, type Invitation, type JsonObject } from '
|
|
43
|
+
import { isInvitation, isSilence, isWord, wordOf, type Invitation, type JsonObject } from 'nervur';
|
|
44
44
|
|
|
45
45
|
const open = { 'access-control-allow-origin': '*', 'access-control-allow-methods': 'GET, POST, OPTIONS', 'access-control-allow-headers': 'content-type, authorization' };
|
|
46
46
|
|
|
@@ -75,7 +75,7 @@ export function apiRoute(harbor: Held, o: Options): Handler {
|
|
|
75
75
|
// is the allow page's, and for the same reason: a page that runs no
|
|
76
76
|
// script of its own should be allowed none.
|
|
77
77
|
const page = (status: number, body: string) =>
|
|
78
|
-
new Response(`<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>
|
|
78
|
+
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>`, {
|
|
79
79
|
status,
|
|
80
80
|
headers: { 'content-type': 'text/html; charset=utf-8', 'cache-control': 'no-store', 'content-security-policy': POLICY, 'referrer-policy': 'no-referrer' },
|
|
81
81
|
});
|
package/beings/avatar.ts
CHANGED
|
@@ -7,8 +7,8 @@
|
|
|
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 '
|
|
11
|
-
import type { Answer, Blueprint, Invitation, JsonObject, OccupantRecord, Wanted } from '
|
|
10
|
+
import { isSilence, isWord, wordOf } from 'nervur';
|
|
11
|
+
import type { Answer, Blueprint, Invitation, JsonObject, OccupantRecord, Wanted } from 'nervur';
|
|
12
12
|
import { Carrier } from './carry.ts';
|
|
13
13
|
|
|
14
14
|
// Her one standing, and the id under which the user being pushes to her.
|
package/beings/carry.ts
CHANGED
|
@@ -18,8 +18,8 @@
|
|
|
18
18
|
// `look` once per digest, and keeps both in her cells. So a describe of
|
|
19
19
|
// hers may cost one ask per stale standing, which is the price of a page
|
|
20
20
|
// that is never more than one ask behind.
|
|
21
|
-
import { Being, isSilence, isWord, wordOf } from '
|
|
22
|
-
import type { Asker, Blueprint, JsonObject, OccupantRecord, Reply } from '
|
|
21
|
+
import { Being, isSilence, isWord, wordOf } from 'nervur';
|
|
22
|
+
import type { Asker, Blueprint, JsonObject, OccupantRecord, Reply } from 'nervur';
|
|
23
23
|
import { sanitise, type Look } from './look.ts';
|
|
24
24
|
|
|
25
25
|
type Carried = Record<string, { id: string; ask: string }>;
|
package/beings/clock.ts
CHANGED
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
// and what an occupant registered on the service placement. Nothing is read
|
|
30
30
|
// from a file and nothing is set on the class, so a schedule outlives the
|
|
31
31
|
// process the way every cell does, and is gone when it is dropped.
|
|
32
|
-
import { Being, isSilence, isWord, wordOf, OWNER } from '
|
|
33
|
-
import type { Asker, Invitation, Json, JsonObject, OccupantRecord, Stance } from '
|
|
32
|
+
import { Being, isSilence, isWord, wordOf, OWNER } from 'nervur';
|
|
33
|
+
import type { Asker, Invitation, Json, JsonObject, OccupantRecord, Stance } from 'nervur';
|
|
34
34
|
import { TIMER, join } from '../dock/index.ts';
|
|
35
35
|
|
|
36
36
|
// The root and nobody else. `OWNER` is one of the ward's own words, refused at
|
package/beings/courier.ts
CHANGED
|
@@ -26,8 +26,8 @@
|
|
|
26
26
|
// the shell holding this device when it is asked for, and what arrives goes
|
|
27
27
|
// back to the shell, since a partition written into another ward's cells is
|
|
28
28
|
// a copy nobody asked for.
|
|
29
|
-
import { Being, isSilence, isWord, wordOf } from '
|
|
30
|
-
import type { Answer, Invitation, JsonObject, OccupantRecord } from '
|
|
29
|
+
import { Being, isSilence, isWord, wordOf } from 'nervur';
|
|
30
|
+
import type { Answer, Invitation, JsonObject, OccupantRecord } from 'nervur';
|
|
31
31
|
|
|
32
32
|
// The id each end knows the other by for the length of one move.
|
|
33
33
|
export const MOVER = 'mover';
|
package/beings/desk.ts
CHANGED
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
// judges nothing here: she does not know what an address is, whether one was
|
|
17
17
|
// sent to before, or what a link says. She keeps the ways in named in one
|
|
18
18
|
// place, so a side that renders her renders every way into this world.
|
|
19
|
-
import { Being, isSilence, isWord, wordOf } from '
|
|
20
|
-
import type { Asker, Blueprint, JsonObject, Reply, Stance } from '
|
|
19
|
+
import { Being, isSilence, isWord, wordOf } from 'nervur';
|
|
20
|
+
import type { Asker, Blueprint, JsonObject, Reply, Stance } from 'nervur';
|
|
21
21
|
import { join } from '../dock/faculty.ts';
|
|
22
22
|
import { LOCAL } from '../dock/local.ts';
|
|
23
23
|
import { WAY, kindOf, verified } from './way.ts';
|
package/beings/doorbell.ts
CHANGED
|
@@ -21,8 +21,8 @@
|
|
|
21
21
|
// in. The device hands its banner key to a world itself, beside the
|
|
22
22
|
// invitation to this doorbell, so a bell that held both halves of the
|
|
23
23
|
// relation is a bell that could read a banner, and this one cannot.
|
|
24
|
-
import { Being, isSilence, isWord, wordOf } from '
|
|
25
|
-
import type { Asker, JsonObject, OccupantRecord } from '
|
|
24
|
+
import { Being, isSilence, isWord, wordOf } from 'nervur';
|
|
25
|
+
import type { Asker, JsonObject, OccupantRecord } from 'nervur';
|
|
26
26
|
|
|
27
27
|
const DEVICE = 'device';
|
|
28
28
|
// Her standing at the box's push faculty, under an id of her own. The ward
|
package/beings/envoy.ts
CHANGED
|
@@ -18,8 +18,8 @@
|
|
|
18
18
|
// bytes checked before anything is parsed, the id kept bounded so a retry
|
|
19
19
|
// lands once, `{ ok }` answered before the wake goes out, a wake that meets
|
|
20
20
|
// silence written down and never retried.
|
|
21
|
-
import { Being, isSilence, isWord, wordOf } from '
|
|
22
|
-
import type { Asker, AskSpec, JsonObject, OccupantRecord } from '
|
|
21
|
+
import { Being, isSilence, isWord, wordOf } from 'nervur';
|
|
22
|
+
import type { Asker, AskSpec, JsonObject, OccupantRecord } from 'nervur';
|
|
23
23
|
|
|
24
24
|
// The two gates. The owner is the ward's ask pointer, `{ id: 'OWNER' }`,
|
|
25
25
|
// which is how the daemon asks `deliver` and how the root places a
|
package/beings/join.ts
CHANGED
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
// A way in that is the box's own, a nonce on the `local` faculty, is not
|
|
8
8
|
// here: it is a process the device saw on its own socket, which only a
|
|
9
9
|
// terrain with processes has, and the daemon keeps it.
|
|
10
|
-
import type { Hosted } from '
|
|
11
|
-
import type { Invitation } from '
|
|
10
|
+
import type { Hosted } from 'nervur/harbor';
|
|
11
|
+
import type { Invitation } from 'nervur';
|
|
12
12
|
import type { Avatar } from './avatar.ts';
|
|
13
13
|
|
|
14
14
|
// Her avatar in a ward, found or booted under the identity's key. An
|
package/beings/link.ts
CHANGED
|
@@ -2,15 +2,15 @@
|
|
|
2
2
|
// A link: a world's page with an invitation in the fragment. The page is the
|
|
3
3
|
// hint, where the world lives; the fragment is the invitation, which never
|
|
4
4
|
// leaves the browser: a query string reaches the server, its logs and every
|
|
5
|
-
// referer, and an invitation is a capability. One reserved key, `
|
|
5
|
+
// referer, and an invitation is a capability. One reserved key, `nervur`, the
|
|
6
6
|
// one word no page uses for anything else, and the value is the invitation
|
|
7
7
|
// compact: `ward.heir.secret`, three hex strings, or `ward` alone for a
|
|
8
8
|
// world's public being. The dock's human module reads it, strips it before
|
|
9
9
|
// any page code runs, and joins. One other key is reserved, `proof`, for the
|
|
10
10
|
// way in a world sent out itself, and it is read the same way.
|
|
11
|
-
import type { Invitation } from '
|
|
11
|
+
import type { Invitation } from 'nervur';
|
|
12
12
|
|
|
13
|
-
export const KEY = '
|
|
13
|
+
export const KEY = 'nervur';
|
|
14
14
|
// The other reserved key: a proof, for a way in the world sent out itself.
|
|
15
15
|
// A proof is not a capability into a ward, it is a word one door will trade
|
|
16
16
|
// once for an invitation, and it rides in the fragment for the same reason:
|
|
@@ -29,7 +29,7 @@ export function format(inv: Invitation): string {
|
|
|
29
29
|
// The invitation in a fragment, or null. Anything else in the fragment is
|
|
30
30
|
// the page's own and is left alone.
|
|
31
31
|
export function parse(hash: string): Invitation | null {
|
|
32
|
-
const m =
|
|
32
|
+
const m = new RegExp(`(?:^#|&)${KEY}=([^&]+)`).exec(hash.startsWith('#') ? hash : `#${hash}`);
|
|
33
33
|
if (!m) return null;
|
|
34
34
|
const v = decodeURIComponent(m[1]);
|
|
35
35
|
const full = FULL.exec(v);
|
|
@@ -70,7 +70,7 @@ export function proofLink(page: string, kind: string, token: string): string {
|
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
// The invitation as a human hands it over: the compact form, the whole link
|
|
73
|
-
// it came in, or the JSON value `
|
|
73
|
+
// it came in, or the JSON value `nervur invite` printed. Wherever a person
|
|
74
74
|
// types or pastes an invitation, this is what reads it, so the three forms
|
|
75
75
|
// are one form everywhere.
|
|
76
76
|
export function invitationOf(text: string): Invitation | null {
|
package/beings/look.ts
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
// is a list of origins a side reads to decide whether to vouch for an
|
|
13
13
|
// address the being's own page named, and nothing in the dock ever fetches
|
|
14
14
|
// one.
|
|
15
|
-
import type { Blueprint, Json } from '
|
|
15
|
+
import type { Blueprint, Json } from 'nervur';
|
|
16
16
|
|
|
17
17
|
// What a side may do with one ask. `title` is a human label instead of the
|
|
18
18
|
// name; `readOnly` says the ask changes nothing, so a screen may run it on
|
package/beings/porter.ts
CHANGED
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
// twice is two doorbells and no harm. A stranger who keeps knocking is the
|
|
14
14
|
// harbor's, never hers and never the door's, and the count below is only so
|
|
15
15
|
// that a loop in this session stops somewhere a human can see.
|
|
16
|
-
import { Being, isSilence, isWord, wordOf } from '
|
|
17
|
-
import type { JsonObject } from '
|
|
16
|
+
import { Being, isSilence, isWord, wordOf } from 'nervur';
|
|
17
|
+
import type { JsonObject } from 'nervur';
|
|
18
18
|
|
|
19
19
|
const ROOM = 64;
|
|
20
20
|
|
package/beings/presence.ts
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
// to her, and birth is synchronous while taking a standing is not, so what
|
|
14
14
|
// she holds is settled after her constructor returns; the terrain waits on
|
|
15
15
|
// `settled` before it says the box is up.
|
|
16
|
-
import type { Asker, OccupantRecord, Stance } from '
|
|
16
|
+
import type { Asker, OccupantRecord, Stance } from 'nervur';
|
|
17
17
|
import { Avatar, PUSHER, USER } from './avatar.ts';
|
|
18
18
|
import { FACULTIES, join } from '../dock/index.ts';
|
|
19
19
|
|
package/beings/setup.ts
CHANGED
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
// `user:<name>`, placed by the root's knock, and she holding her own ward's
|
|
5
5
|
// door as an owner under `home`. She names the desk `desk`,
|
|
6
6
|
// and that id is the only thing that makes it the desk. A disk harbor does
|
|
7
|
-
// this at `
|
|
7
|
+
// this at `nervur init`, the edge at its root route's init; the memory
|
|
8
8
|
// harbor's test world does the same by hand.
|
|
9
|
-
import type { Invitation } from '
|
|
10
|
-
import type { Hosted } from '
|
|
9
|
+
import type { Invitation } from 'nervur';
|
|
10
|
+
import type { Hosted } from 'nervur/harbor';
|
|
11
11
|
import { DESK, HOME } from './user.ts';
|
|
12
12
|
|
|
13
13
|
// The root boots and steps back: it invites an owner on the ward's own pk
|
package/beings/side.ts
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
// her one standing, and a push as a notification. Sides in this repository:
|
|
6
6
|
// a screen, an MCP server, a runner, an event-driven agent. None knows
|
|
7
7
|
// another exists, and none of this file names a platform.
|
|
8
|
-
import { isSilence, isUnreached, isWord, wordOf } from '
|
|
9
|
-
import type { Answer, Blueprint, Json, JsonObject, Wanted, WordName } from '
|
|
8
|
+
import { isSilence, isUnreached, isWord, wordOf } from 'nervur';
|
|
9
|
+
import type { Answer, Blueprint, Json, JsonObject, Wanted, WordName } from 'nervur';
|
|
10
10
|
import type { Avatar } from './avatar.ts';
|
|
11
11
|
|
|
12
12
|
// A side, running: it holds the avatar until closed.
|
package/beings/user.ts
CHANGED
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
// client identity the desk established. A device she let mint brings the
|
|
8
8
|
// next one in, so a human adds a device from a device already in and only
|
|
9
9
|
// the first one has to come from the root, on the box.
|
|
10
|
-
import { isInvitation, isSilence, isWord, wordOf } from '
|
|
11
|
-
import type { Asker, Blueprint, Invitation, JsonObject, OccupantRecord, Stance } from '
|
|
10
|
+
import { isInvitation, isSilence, isWord, wordOf } from 'nervur';
|
|
11
|
+
import type { Asker, Blueprint, Invitation, JsonObject, OccupantRecord, Stance } from 'nervur';
|
|
12
12
|
import { Carrier } from './carry.ts';
|
|
13
13
|
import { PUSH, banner, isToken, join, sealBanner } from '../dock/index.ts';
|
|
14
14
|
|
package/beings/way.ts
CHANGED
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
// `send` beside it, and the desk shows the form for it because her standing
|
|
25
25
|
// says the being can be asked it. She judges neither: she does not know what
|
|
26
26
|
// an address is, whether one was sent to before, or what a link says.
|
|
27
|
-
import type { AskSpec, Json } from '
|
|
27
|
+
import type { AskSpec, Json } from 'nervur';
|
|
28
28
|
|
|
29
29
|
export type Proof = { kind: string; [more: string]: Json };
|
|
30
30
|
// Who the proof is for, and how far the device it admits may go. `make` is
|
package/cli/client.ts
CHANGED
|
@@ -14,7 +14,7 @@ export function ask(dir: string, method: string | undefined, args: Record<string
|
|
|
14
14
|
return line(dir, { ...(ward === undefined ? {} : { ward }), method, args, ...(via === undefined ? {} : { via }) });
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
// `
|
|
17
|
+
// `nervur ask`: the root asking a being of its own ward, as the device. The six
|
|
18
18
|
// owner asks make and place beings; this is the other half of the owner role,
|
|
19
19
|
// reaching into one, and without it a being's owner-gated ask had no caller
|
|
20
20
|
// but the daemon itself.
|
|
@@ -22,7 +22,7 @@ export function askBeing(dir: string, being: string, method?: string, args: Reco
|
|
|
22
22
|
return line(dir, { ...(ward === undefined ? {} : { ward }), being, method, args });
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
// `
|
|
25
|
+
// `nervur reach`: a hint for the harbor's directory, which is the harbor's and
|
|
26
26
|
// not a ward's, so it crosses the root socket as its own line.
|
|
27
27
|
export function reach(dir: string, pk: string, url: string): Promise<Answer> {
|
|
28
28
|
return line(dir, { reach: { pk, url } });
|
|
@@ -32,7 +32,7 @@ function line(dir: string, body: Record<string, unknown>): Promise<Answer> {
|
|
|
32
32
|
return new Promise((ok, no) => {
|
|
33
33
|
const c = connect(sockPath(dir));
|
|
34
34
|
const id = Math.random().toString(36).slice(2);
|
|
35
|
-
c.once('error', (e: NodeJS.ErrnoException) => no(e.code === 'ENOENT' || e.code === 'ECONNREFUSED' ? new Error(`no daemon at ${dir}: run \`
|
|
35
|
+
c.once('error', (e: NodeJS.ErrnoException) => no(e.code === 'ENOENT' || e.code === 'ECONNREFUSED' ? new Error(`no daemon at ${dir}: run \`nervur serve\``) : e));
|
|
36
36
|
c.once('connect', () => {
|
|
37
37
|
c.write(JSON.stringify({ id, ...body }) + '\n');
|
|
38
38
|
createInterface({ input: c }).once('line', (reply) => {
|
|
@@ -44,19 +44,19 @@ function line(dir: string, body: Record<string, unknown>): Promise<Answer> {
|
|
|
44
44
|
});
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
// `
|
|
47
|
+
// `nervur side`: a pipe. Hello to the daemon's side socket, then stdin to the
|
|
48
48
|
// socket and the socket to stdout, line for line, until either end closes.
|
|
49
49
|
// The MCP server runs in the daemon, beside the avatar; this process knows
|
|
50
50
|
// nothing of MCP but its newlines.
|
|
51
51
|
//
|
|
52
|
-
// `
|
|
52
|
+
// `nervur run` is the same pipe with a model named in the hello: the daemon
|
|
53
53
|
// runs the runner beside the avatar, each line typed is a turn, and each
|
|
54
54
|
// final text comes back as a line.
|
|
55
55
|
export type Run = { url: string; model: string; key?: string; turns?: number };
|
|
56
56
|
export function side(dir: string, as: string, ward: string, run?: Run): Promise<void> {
|
|
57
57
|
return new Promise((ok, no) => {
|
|
58
58
|
const c = connect(sidePath(dir));
|
|
59
|
-
c.once('error', (e: NodeJS.ErrnoException) => no(e.code === 'ENOENT' || e.code === 'ECONNREFUSED' ? new Error(`no daemon at ${dir}: run \`
|
|
59
|
+
c.once('error', (e: NodeJS.ErrnoException) => no(e.code === 'ENOENT' || e.code === 'ECONNREFUSED' ? new Error(`no daemon at ${dir}: run \`nervur serve\``) : e));
|
|
60
60
|
c.once('connect', () => {
|
|
61
61
|
c.write(JSON.stringify(run ? { as, ward, run } : { as, ward }) + '\n');
|
|
62
62
|
const lines = createInterface({ input: c });
|
package/cli/daemon.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
-
// The daemon: `
|
|
2
|
+
// The daemon: `nervur serve`. One process holds the harbor and the ask pointer
|
|
3
3
|
// of every ward in it. Two local doors, never mixed, each a unix socket only
|
|
4
4
|
// the device's user can read:
|
|
5
5
|
//
|
|
6
|
-
//
|
|
6
|
+
// nervur.sock the root. One line of JSON in, one line out.
|
|
7
7
|
// -> { id, ward?, method?, args?, via? }
|
|
8
8
|
// <- { id, result } | { id, error }
|
|
9
9
|
// With `being`, the line is the owner's `ask`, forwarded like
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
// push faculty with one ring per token kind this device can ring.
|
|
60
60
|
//
|
|
61
61
|
// The default ward is the one a caller means when naming none: the one ward
|
|
62
|
-
// hosted, or the one marked in its own folder by `
|
|
62
|
+
// hosted, or the one marked in its own folder by `nervur default`. A harbor is
|
|
63
63
|
// not a being and has no self to be, so the one thing it can do is point at
|
|
64
64
|
// one of its wards. Its agents boot there and its door answers for it when a
|
|
65
65
|
// caller names no ward. Several wards and no mark is refused here, by name,
|
|
@@ -86,11 +86,11 @@ import { createHash, randomBytes } from 'node:crypto';
|
|
|
86
86
|
import { tmpdir } from 'node:os';
|
|
87
87
|
import { join, resolve } from 'node:path';
|
|
88
88
|
import { createInterface } from 'node:readline';
|
|
89
|
-
import { isSilence, isWord, wordOf, type Invitation, type JsonObject } from '
|
|
89
|
+
import { isSilence, isWord, wordOf, type Invitation, type JsonObject } from 'nervur';
|
|
90
90
|
import type { JSONRPCMessage } from '@modelcontextprotocol/sdk/types.js';
|
|
91
91
|
import type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';
|
|
92
92
|
import { DiskHarbor, defaultWard, type Hosted } from '../harbor/disk.ts';
|
|
93
|
-
import type { Harbor } from '
|
|
93
|
+
import type { Harbor } from 'nervur/harbor';
|
|
94
94
|
import type { Avatar, User } from '../beings/index.ts';
|
|
95
95
|
// an identity in a world: the three asks any terrain's routes make at a ward
|
|
96
96
|
export { avatarOf, letIn, find } from '../beings/join.ts';
|
|
@@ -128,9 +128,9 @@ export type Options = { http?: { port: number; host?: string }; routes?: Routes;
|
|
|
128
128
|
const socketPath = (dir: string, name: string): string => {
|
|
129
129
|
const inside = join(resolve(dir), `${name}.sock`);
|
|
130
130
|
if (inside.length < 100) return inside;
|
|
131
|
-
return join(tmpdir(), `
|
|
131
|
+
return join(tmpdir(), `nervur-${createHash('sha256').update(resolve(dir)).digest('hex').slice(0, 16)}-${name}.sock`);
|
|
132
132
|
};
|
|
133
|
-
export const sockPath = (dir: string) => socketPath(dir, '
|
|
133
|
+
export const sockPath = (dir: string) => socketPath(dir, 'nervur');
|
|
134
134
|
export const sidePath = (dir: string) => socketPath(dir, 'side');
|
|
135
135
|
|
|
136
136
|
// Admit a local client identity into a ward: find or boot her avatar, and
|
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
# The daemon on a droplet, as one user, forever. Install with:
|
|
2
|
-
# sudo cp droplet/
|
|
3
|
-
# sudo systemctl enable --now
|
|
4
|
-
# The unit runs as the `
|
|
2
|
+
# sudo cp droplet/nervur.service /etc/systemd/system/nervur.service
|
|
3
|
+
# sudo systemctl enable --now nervur
|
|
4
|
+
# The unit runs as the `nervur` user, whose home holds the estate folder and
|
|
5
5
|
# the harbor folder, so the seed, the lease and the sockets are that user's
|
|
6
6
|
# alone. The HTTP door is loopback only; Caddy fronts it. See Caddyfile
|
|
7
7
|
# beside this file.
|
|
8
8
|
[Unit]
|
|
9
|
-
Description=
|
|
9
|
+
Description=nervur: one harbor on this device
|
|
10
10
|
After=network.target
|
|
11
11
|
|
|
12
12
|
[Service]
|
|
13
|
-
User=
|
|
14
|
-
Group=
|
|
15
|
-
Environment=
|
|
16
|
-
Environment=
|
|
13
|
+
User=nervur
|
|
14
|
+
Group=nervur
|
|
15
|
+
Environment=NERVUR_DIR=/home/nervur/.nervur
|
|
16
|
+
Environment=NERVUR_HTTP=8787
|
|
17
17
|
# CLAUDE_CODE_OAUTH_TOKEN, when an agent folder runs `claude -p`: the agent
|
|
18
18
|
# reads its credential from the device, never from cells.
|
|
19
|
-
EnvironmentFile=-/etc/
|
|
20
|
-
WorkingDirectory=/home/
|
|
21
|
-
ExecStart=/usr/bin/node /home/
|
|
19
|
+
EnvironmentFile=-/etc/nervur/env
|
|
20
|
+
WorkingDirectory=/home/nervur/ESTATE
|
|
21
|
+
ExecStart=/usr/bin/node /home/nervur/ESTATE/node_modules/@nervur-org/dock/dist/cli/nervur.js serve
|
|
22
22
|
Restart=always
|
|
23
23
|
RestartSec=2
|
|
24
24
|
KillSignal=SIGTERM
|
|
@@ -28,7 +28,7 @@ ProtectSystem=strict
|
|
|
28
28
|
# Agents run in their folders and Claude Code keeps its own state in the
|
|
29
29
|
# user's home; both must be writable for a run to happen, and a harbor
|
|
30
30
|
# with no agent has neither.
|
|
31
|
-
ReadWritePaths=/home/
|
|
31
|
+
ReadWritePaths=/home/nervur/.nervur -/home/nervur/agents -/home/nervur/.claude -/home/nervur/.claude.json /tmp
|
|
32
32
|
PrivateTmp=false
|
|
33
33
|
|
|
34
34
|
[Install]
|