@weaveprotocol/core 0.1.0 → 0.1.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/README.md +107 -1
- package/dist/calls/calls.d.ts +141 -0
- package/dist/calls/calls.d.ts.map +1 -0
- package/dist/calls/calls.js +763 -0
- package/dist/calls/calls.js.map +1 -0
- package/dist/calls/index.d.ts +7 -0
- package/dist/calls/index.d.ts.map +1 -0
- package/dist/calls/index.js +6 -0
- package/dist/calls/index.js.map +1 -0
- package/dist/network/mesh.d.ts +6 -0
- package/dist/network/mesh.d.ts.map +1 -1
- package/dist/network/mesh.js +33 -2
- package/dist/network/mesh.js.map +1 -1
- package/dist/network/multi-signaling.d.ts.map +1 -1
- package/dist/network/multi-signaling.js +13 -0
- package/dist/network/multi-signaling.js.map +1 -1
- package/dist/network/rtc-transport.d.ts +3 -1
- package/dist/network/rtc-transport.d.ts.map +1 -1
- package/dist/network/rtc-transport.js +4 -3
- package/dist/network/rtc-transport.js.map +1 -1
- package/dist/network/signaling.d.ts +8 -1
- package/dist/network/signaling.d.ts.map +1 -1
- package/dist/network/signaling.js +20 -0
- package/dist/network/signaling.js.map +1 -1
- package/dist/node/node.d.ts.map +1 -1
- package/dist/node/node.js +38 -4
- package/dist/node/node.js.map +1 -1
- package/dist/node/space-runtime.d.ts +2 -0
- package/dist/node/space-runtime.d.ts.map +1 -1
- package/dist/node/space-runtime.js +83 -2
- package/dist/node/space-runtime.js.map +1 -1
- package/dist/node/types.d.ts +49 -4
- package/dist/node/types.d.ts.map +1 -1
- package/dist/react/index.d.ts +2 -1
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +2 -1
- package/dist/react/index.js.map +1 -1
- package/dist/react/use-calls.d.ts +21 -0
- package/dist/react/use-calls.d.ts.map +1 -0
- package/dist/react/use-calls.js +38 -0
- package/dist/react/use-calls.js.map +1 -0
- package/dist/react/use-space.d.ts +2 -2
- package/dist/react/use-space.d.ts.map +1 -1
- package/dist/react/use-space.js +9 -4
- package/dist/react/use-space.js.map +1 -1
- package/dist/schemas/index.d.ts +52 -0
- package/dist/schemas/index.d.ts.map +1 -1
- package/dist/schemas/index.js +23 -1
- package/dist/schemas/index.js.map +1 -1
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -142,6 +142,86 @@ Every operation is also described in `NODE_ACTIONS` — a name, a sentence and a
|
|
|
142
142
|
JSON Schema for its input — which is what the CLI, MCP and WebMCP front ends are
|
|
143
143
|
generated from. `runAction(node, 'records_put', { … })` runs one by name.
|
|
144
144
|
|
|
145
|
+
### Live messages, and spaces kept open
|
|
146
|
+
|
|
147
|
+
Some things should reach whoever is connected right now and be kept nowhere:
|
|
148
|
+
typing, presence, setting up a call. `spaces.send` is for those:
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
await node.spaces.send(space.id, { type: 'typing' }); // everyone connected in the space
|
|
152
|
+
await node.spaces.send(space.id, { type: 'nudge' }, anna); // only Anna's devices (her account DID)
|
|
153
|
+
await node.spaces.send(space.id, { type: 'hi' }, annasLaptop); // one device (its session DID)
|
|
154
|
+
|
|
155
|
+
node.subscribe((event) => {
|
|
156
|
+
if (event.type === 'message') event.from; event.peer; event.message; // account, device, what was sent
|
|
157
|
+
});
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
A live message is never signed as a record, never written and never synced;
|
|
161
|
+
a peer who isn't connected misses it. It travels over the space's own peer
|
|
162
|
+
connections, so in a private space only people holding its read key can
|
|
163
|
+
receive one. At most 64 KB each, and each peer gets a burst of 60 and 20 a
|
|
164
|
+
second after that.
|
|
165
|
+
|
|
166
|
+
`event.from` is the **account**, not just a key. When two peers connect in a
|
|
167
|
+
space, each sends the other its note — the delegation from its account to its
|
|
168
|
+
session key, the same one its records carry. The note names the key it was
|
|
169
|
+
made out to, and that has to be the key the connection proved, so a note
|
|
170
|
+
copied off someone's record is no use to anyone else. `spaces.status(id).accounts`
|
|
171
|
+
lists who is connected, by account. A peer that shows no note (a carrier, a
|
|
172
|
+
node serving sockets) is `from: null`.
|
|
173
|
+
|
|
174
|
+
To keep a space syncing, **hold** it, and let go when you're done:
|
|
175
|
+
|
|
176
|
+
```typescript
|
|
177
|
+
const release = await node.spaces.hold(space.id);
|
|
178
|
+
await release();
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Anything that needs a space live holds it: a screen showing it, a call in it.
|
|
182
|
+
It stops syncing once nothing does, so a screen going away never cuts off a
|
|
183
|
+
call. Each hold lets go only of itself, and letting go twice does nothing.
|
|
184
|
+
Reading or writing needs no hold. In React, `useHoldSpace(space)` holds a
|
|
185
|
+
space while a view is on screen, and lets go a few seconds late so that
|
|
186
|
+
clicking away and straight back doesn't rebuild the space's sync.
|
|
187
|
+
|
|
188
|
+
### Calls (`@weaveprotocol/core/calls`)
|
|
189
|
+
|
|
190
|
+
Voice and video between the members of a space, built only from live
|
|
191
|
+
messages and the browser's WebRTC:
|
|
192
|
+
|
|
193
|
+
```typescript
|
|
194
|
+
import { createCalls } from '@weaveprotocol/core/calls';
|
|
195
|
+
|
|
196
|
+
const calls = createCalls(node);
|
|
197
|
+
await calls.start(space.id, { video: true }); // join the call going on in the space, or start one
|
|
198
|
+
await calls.ring(space.id, anna); // start one, and ring Anna's devices
|
|
199
|
+
calls.subscribe(() => draw(calls.getState())); // { current, ringing, around, rejoin }
|
|
200
|
+
await calls.answer(ringing.id); calls.setMuted(true); await calls.shareScreen(); await calls.leave();
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
- **A call belongs to a space, not to a screen.** It keeps its space open for
|
|
204
|
+
as long as it runs, so people can move between spaces and keep talking. One
|
|
205
|
+
call at a time. In React, `<CallsProvider>` above whatever changes as people
|
|
206
|
+
move around, and `useCalls()` below it.
|
|
207
|
+
- **Only members take part.** Someone with no role in the space (a view-only
|
|
208
|
+
reader) is never rung by, counted in or connected to a call.
|
|
209
|
+
- **Group calls don't ring.** A call going on shows in `around` for everyone
|
|
210
|
+
with the space open; only `ring`, aimed at one person, rings. One account
|
|
211
|
+
can ring you three times a minute.
|
|
212
|
+
- **Everyone connects to everyone**, each pair on a WebRTC connection of its
|
|
213
|
+
own, apart from the one sync uses. Good up to about six people with video.
|
|
214
|
+
The setup goes over the space's peer connection, whose handshake proved who
|
|
215
|
+
is at the other end, so a relay can't put itself in the middle of a call.
|
|
216
|
+
- **History, if the space wants it.** Where the space defines `std.call`, a
|
|
217
|
+
ring nobody answered leaves a `missed` record, and the last one out of a
|
|
218
|
+
call writes who was in it. Nothing else about a call is stored.
|
|
219
|
+
- **TURN.** `node.iceServers()` gives the configured ICE servers plus TURN
|
|
220
|
+
servers a relay hands out (see the relay, below), with passwords fresh for
|
|
221
|
+
a while. A call asks for them when it starts.
|
|
222
|
+
|
|
223
|
+
The messages, and the reasoning, are at the top of `src/calls/calls.ts`.
|
|
224
|
+
|
|
145
225
|
## Signing in — the element, and React
|
|
146
226
|
|
|
147
227
|
Getting to a node takes a sign-in flow: where the data lives (a pod or this
|
|
@@ -208,7 +288,7 @@ Everything below the provider asks for what it needs:
|
|
|
208
288
|
| `useRecord(space, key)` / `useLinked(space, key)` | One record; what points at it |
|
|
209
289
|
| `useCollections(space)` / `useProfiles(space)` / `useSpaceStatus(space)` | What a space holds, who is in it, whether it is connected |
|
|
210
290
|
| `useCan(space, action, target)` | Whether this account may create, edit or delete — for hiding a button |
|
|
211
|
-
| `
|
|
291
|
+
| `useHoldSpace(space)` | Keeps a space syncing while a view is on screen |
|
|
212
292
|
| `useLive(space, load, deps)` | Anything else, reloaded as the space changes |
|
|
213
293
|
|
|
214
294
|
An app connected to an account home passes its node instead:
|
|
@@ -677,6 +757,21 @@ it forwards carries the sender's DID as it joined, whatever the message says.
|
|
|
677
757
|
npm run signal # ws://localhost:8787; /health says {"ok":true}
|
|
678
758
|
```
|
|
679
759
|
|
|
760
|
+
It can also hand out **TURN** passwords, for calls between people on networks
|
|
761
|
+
that won't let two browsers connect directly. Run coturn with
|
|
762
|
+
`use-auth-secret`, and give the relay the same secret:
|
|
763
|
+
|
|
764
|
+
```bash
|
|
765
|
+
TURN_SECRET=… TURN_URLS=turn:turn.example.com:3478,turns:turn.example.com:5349 npm run signal
|
|
766
|
+
```
|
|
767
|
+
|
|
768
|
+
Each socket that joins a room gets servers and a password that stops working
|
|
769
|
+
after `TURN_TTL_SECONDS` (4 hours by default). The password is an HMAC of when
|
|
770
|
+
it expires (the "TURN REST API" scheme), so nothing is stored. The relay can't
|
|
771
|
+
tell a Weave peer from anyone else, so set coturn's own quotas (`user-quota`,
|
|
772
|
+
`total-quota`, `max-bps`). TURN only forwards encrypted packets; it can't
|
|
773
|
+
see or hear a call.
|
|
774
|
+
|
|
680
775
|
Only peers already in a room hear about a newcomer, so exactly one side creates
|
|
681
776
|
the offer and the two never collide.
|
|
682
777
|
|
|
@@ -1055,6 +1150,17 @@ tally. The helpers that work this out are pure functions
|
|
|
1055
1150
|
empty space offers a small "define a collection" form; an agent can do the
|
|
1056
1151
|
same over WebMCP.
|
|
1057
1152
|
|
|
1153
|
+
**Calls.** Every space you have a role in has "Start a call" at the top, or
|
|
1154
|
+
"Join call · 3" while one is going on, and People & roles has "Call" beside
|
|
1155
|
+
each member, which rings them. The call sits in a panel in the corner, over
|
|
1156
|
+
whatever page you're on: open other spaces, go back to the list, and you keep
|
|
1157
|
+
talking. The sidebar marks the space a call is in. The panel grows to fill the
|
|
1158
|
+
screen, or moves into a picture-in-picture window where the browser has one,
|
|
1159
|
+
so it stays in view when you switch tabs. Someone ringing you shows as a card
|
|
1160
|
+
on any page. The **Calls** app keeps a log of a space's calls (`std.call`).
|
|
1161
|
+
The calls themselves are `createCalls` (above), made once in `App.tsx`,
|
|
1162
|
+
above everything that changes as you move around.
|
|
1163
|
+
|
|
1058
1164
|
**Agents in the browser (WebMCP).** When `/app` loads, it registers
|
|
1059
1165
|
every node operation as a WebMCP tool on `document.modelContext`
|
|
1060
1166
|
(`example/src/webmcp.ts`, with `@mcp-b/webmcp-polyfill`: Chrome's own WebMCP
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module calls
|
|
3
|
+
* Voice and video calls between the people in a space.
|
|
4
|
+
*
|
|
5
|
+
* Nothing here is new to the protocol. A call is live messages
|
|
6
|
+
* (`node.spaces.send`) in the space it belongs to, and a WebRTC connection of
|
|
7
|
+
* its own between each pair of people in it — the shape Matrix gives group
|
|
8
|
+
* calls (MSC3401, Element Call's full mesh):
|
|
9
|
+
*
|
|
10
|
+
* ```
|
|
11
|
+
* call.here { call, since, camera, muted } to the space, every few seconds while in it
|
|
12
|
+
* call.ring { call } to one account: ring its devices
|
|
13
|
+
* call.answered { call } / call.declined to the caller, and to your own other devices
|
|
14
|
+
* call.cancel { call } the caller gave up ringing
|
|
15
|
+
* call.signal { call, description | candidate } to one device: setting up the connection
|
|
16
|
+
* call.leave { call } to the space: gone now, not in 15 seconds
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* The setup travels over the space's own peer connection, whose handshake
|
|
20
|
+
* proved who is at the other end — so the offer, and the fingerprint of the
|
|
21
|
+
* key the browser will insist on, really come from that person. A relay only
|
|
22
|
+
* ever introduced the two devices; it never sees a call.
|
|
23
|
+
*
|
|
24
|
+
* Only members of the space (anyone holding a role) are rung by, heard in or
|
|
25
|
+
* connected to a call; a view-only reader of a public space is not.
|
|
26
|
+
*
|
|
27
|
+
* A call belongs to the space, not to a screen: it keeps the space open
|
|
28
|
+
* (`spaces.hold`) for as long as it runs, so moving between spaces
|
|
29
|
+
* never interrupts it. One call at a time.
|
|
30
|
+
*
|
|
31
|
+
* Each connection is made with an audio and a video transceiver from the
|
|
32
|
+
* start. Turning the camera on or off, or sharing the screen, swaps the track
|
|
33
|
+
* a sender carries and never renegotiates — so neither side can offer at the
|
|
34
|
+
* same moment as the other, and the side with the lower session DID always
|
|
35
|
+
* makes the offer.
|
|
36
|
+
*/
|
|
37
|
+
import type { P2PNode } from '../node/types.js';
|
|
38
|
+
export interface CallsOptions {
|
|
39
|
+
/** How a WebRTC connection is made. `new RTCPeerConnection` by default. */
|
|
40
|
+
readonly createConnection?: (config: RTCConfiguration) => RTCPeerConnection;
|
|
41
|
+
/** Camera and microphone. `navigator.mediaDevices.getUserMedia` by default. */
|
|
42
|
+
readonly getUserMedia?: (constraints: MediaStreamConstraints) => Promise<MediaStream>;
|
|
43
|
+
/** The screen, for sharing it. `navigator.mediaDevices.getDisplayMedia` by default. */
|
|
44
|
+
readonly getDisplayMedia?: (constraints: DisplayMediaStreamOptions) => Promise<MediaStream>;
|
|
45
|
+
/** How a set of tracks becomes something a `<video>` can show. `new MediaStream` by default. */
|
|
46
|
+
readonly createStream?: (tracks: ReadonlyArray<MediaStreamTrack>) => MediaStream | null;
|
|
47
|
+
/** Where the call you're in is remembered, to offer rejoining after a reload. `sessionStorage` by default; null for nowhere. */
|
|
48
|
+
readonly storage?: Pick<Storage, 'getItem' | 'setItem' | 'removeItem'> | null;
|
|
49
|
+
/** How often you say you're in a call. Default 5 s. */
|
|
50
|
+
readonly heartbeatMs?: number;
|
|
51
|
+
/** How long without hearing that someone is gone. Default 15 s. */
|
|
52
|
+
readonly goneMs?: number;
|
|
53
|
+
/** How long a ring rings before it's a missed call. Default 45 s. */
|
|
54
|
+
readonly ringMs?: number;
|
|
55
|
+
}
|
|
56
|
+
/** Someone else in the call you're in: one device of theirs */
|
|
57
|
+
export interface CallPeer {
|
|
58
|
+
/** Their device's session DID */
|
|
59
|
+
readonly peer: string;
|
|
60
|
+
/** Their account */
|
|
61
|
+
readonly account: string;
|
|
62
|
+
/** What they send: sound, and video when their camera is on. Null until it arrives. */
|
|
63
|
+
readonly stream: MediaStream | null;
|
|
64
|
+
readonly muted: boolean;
|
|
65
|
+
readonly camera: boolean;
|
|
66
|
+
readonly connection: 'connecting' | 'connected' | 'failed';
|
|
67
|
+
}
|
|
68
|
+
/** The call you're in */
|
|
69
|
+
export interface CurrentCall {
|
|
70
|
+
readonly id: string;
|
|
71
|
+
readonly space: string;
|
|
72
|
+
/** When you joined it, ms */
|
|
73
|
+
readonly joinedAt: number;
|
|
74
|
+
/** Your own camera and microphone, for showing yourself */
|
|
75
|
+
readonly local: MediaStream | null;
|
|
76
|
+
readonly muted: boolean;
|
|
77
|
+
readonly camera: boolean;
|
|
78
|
+
readonly sharing: boolean;
|
|
79
|
+
readonly people: ReadonlyArray<CallPeer>;
|
|
80
|
+
/** Who you're ringing, while it rings — and what came of it, for a moment after */
|
|
81
|
+
readonly outgoing: {
|
|
82
|
+
readonly to: string;
|
|
83
|
+
readonly since: number;
|
|
84
|
+
readonly state: 'ringing' | 'declined' | 'missed';
|
|
85
|
+
} | null;
|
|
86
|
+
/** Something went wrong that you should hear about: no microphone, say */
|
|
87
|
+
readonly problem: string | null;
|
|
88
|
+
}
|
|
89
|
+
/** Someone ringing you */
|
|
90
|
+
export interface IncomingCall {
|
|
91
|
+
readonly id: string;
|
|
92
|
+
readonly space: string;
|
|
93
|
+
/** The caller's account */
|
|
94
|
+
readonly from: string;
|
|
95
|
+
readonly since: number;
|
|
96
|
+
}
|
|
97
|
+
/** A call going on in a space you have open, that you're not in */
|
|
98
|
+
export interface CallAround {
|
|
99
|
+
readonly id: string;
|
|
100
|
+
readonly space: string;
|
|
101
|
+
/** Accounts in it, each once */
|
|
102
|
+
readonly people: ReadonlyArray<string>;
|
|
103
|
+
/** When the first of them joined, ms */
|
|
104
|
+
readonly since: number;
|
|
105
|
+
}
|
|
106
|
+
export interface CallsState {
|
|
107
|
+
readonly current: CurrentCall | null;
|
|
108
|
+
readonly ringing: ReadonlyArray<IncomingCall>;
|
|
109
|
+
readonly around: ReadonlyArray<CallAround>;
|
|
110
|
+
/** The call you were in before this page loaded, while it's still going on */
|
|
111
|
+
readonly rejoin: CallAround | null;
|
|
112
|
+
}
|
|
113
|
+
export interface CallOptions {
|
|
114
|
+
/** Start with the camera on. Default false: sound only. */
|
|
115
|
+
readonly video?: boolean;
|
|
116
|
+
}
|
|
117
|
+
export interface Calls {
|
|
118
|
+
/** What's going on now. The same object until something changes. */
|
|
119
|
+
getState(): CallsState;
|
|
120
|
+
subscribe(listener: () => void): () => void;
|
|
121
|
+
/** Joins the call going on in a space, or starts one. Leaves any other call first. */
|
|
122
|
+
start(space: string, options?: CallOptions & {
|
|
123
|
+
readonly call?: string;
|
|
124
|
+
}): Promise<void>;
|
|
125
|
+
/** Starts a call in a space, and rings one person there — all their devices, until one answers */
|
|
126
|
+
ring(space: string, account: string, options?: CallOptions): Promise<void>;
|
|
127
|
+
answer(id: string, options?: CallOptions): Promise<void>;
|
|
128
|
+
decline(id: string): Promise<void>;
|
|
129
|
+
leave(): Promise<void>;
|
|
130
|
+
setMuted(muted: boolean): void;
|
|
131
|
+
setCamera(on: boolean): Promise<void>;
|
|
132
|
+
/** Shows your screen in place of your camera, until you stop it (or the browser does) */
|
|
133
|
+
shareScreen(): Promise<void>;
|
|
134
|
+
stopSharing(): Promise<void>;
|
|
135
|
+
/** Stops offering `rejoin` */
|
|
136
|
+
forgetRejoin(): void;
|
|
137
|
+
/** Leaves any call and stops listening */
|
|
138
|
+
close(): Promise<void>;
|
|
139
|
+
}
|
|
140
|
+
export declare function createCalls(node: P2PNode, options?: CallsOptions): Calls;
|
|
141
|
+
//# sourceMappingURL=calls.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"calls.d.ts","sourceRoot":"","sources":["../../src/calls/calls.ts"],"names":[],"mappings":"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"}
|