@streamorchard/grapevine 0.3.0 → 0.5.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/PROTOCOL.md +17 -13
- package/README.md +37 -17
- package/fixtures/drops.farming/1/invalid/reward-without-state.json +16 -0
- package/fixtures/drops.farming/1/valid/farming.json +15 -1
- package/package.json +3 -2
- package/schemas/drops.farming/1.json +44 -0
- package/schemas/released.json +5 -0
- package/src/index.js +15 -0
- package/src/known-apps.js +10 -2
- package/types/capabilities.d.ts +13 -0
- package/types/index.d.ts +5 -0
package/PROTOCOL.md
CHANGED
|
@@ -197,18 +197,20 @@ A capability is a named feature one app provides and others use, defined by a JS
|
|
|
197
197
|
|
|
198
198
|
What a drops app is farming, with enough for another app to show it, in a top-bar widget say. Provided by Pear Drops.
|
|
199
199
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
|
203
|
-
|
|
|
204
|
-
| `
|
|
205
|
-
| `
|
|
206
|
-
| `
|
|
207
|
-
| `
|
|
208
|
-
| `drop
|
|
209
|
-
| `drop.
|
|
210
|
-
| `drop.
|
|
211
|
-
| `drop.
|
|
200
|
+
Event `drops.farming.changed`, and `drops.farming.get` answers with the same: the whole state, below. `drops.farming.show` is further down.
|
|
201
|
+
|
|
202
|
+
| Field | |
|
|
203
|
+
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
204
|
+
| `state` | `farming` (its player is watching a stream for drop credit), `waiting`, `paused`, `done`, `signed-out`, or one added later |
|
|
205
|
+
| `account` | The account it farms for, or `null` |
|
|
206
|
+
| `channel` | The channel it's watching while `farming` (with its `avatar`, optional), else `null` |
|
|
207
|
+
| `stream` | Optional, while farming: `{ title?, viewers? }` |
|
|
208
|
+
| `drop` | While farming, `{ campaign, category, progress }`, else `null`. Optional extras below |
|
|
209
|
+
| `drop.image` | The category's picture |
|
|
210
|
+
| `drop.endsAt` | When the drop ends |
|
|
211
|
+
| `drop.rewards` | `{ claimed, total, items? }`. `items` lists every reward in the order they unlock (up to 20): `{ name, image?, required, state, unlocksAt? }`, where `state` is `claimed`, `ready` (unlocked, waiting to be claimed), `locked`, or one added later, and a locked one's `unlocksAt` is when it should unlock at the current rate |
|
|
212
|
+
| `drop.next` | The reward being worked towards: `{ name, image?, required, unlocksAt }`, where `required` is minutes and `unlocksAt` is when it should unlock at the current rate |
|
|
213
|
+
| `drop.doneAt` | When every reward should be earned at the current rate, or `null` |
|
|
212
214
|
|
|
213
215
|
`drops.farming.show {}` asks the drops app to bring its window to the front, for a click on that widget. It replies `{ shown }`.
|
|
214
216
|
|
|
@@ -216,6 +218,8 @@ What a drops app is farming, with enough for another app to show it, in a top-ba
|
|
|
216
218
|
|
|
217
219
|
An app's stream players, by site. Provided by Stream Multiview.
|
|
218
220
|
|
|
221
|
+
Event `players.changed`, and `players.get` answers with the same: the whole state, below.
|
|
222
|
+
|
|
219
223
|
| Field | |
|
|
220
224
|
| ----------- | --------------------------------------------------------------------------------------------------------------- |
|
|
221
225
|
| `platforms` | An object keyed by `Platform`. Each site has `{ signedIn, mode, account, open }` |
|
|
@@ -259,7 +263,7 @@ After the hello, each app tells its user one of:
|
|
|
259
263
|
| **Works, with a feature missing** | A capability one app wants has no major in common. It names the app with the older versions and, if known, the release to update to: "update Pear Drops to 0.1.7 or later" |
|
|
260
264
|
| **Can't connect** | No protocol version in common. Again, it names the app to update |
|
|
261
265
|
|
|
262
|
-
The releases come from
|
|
266
|
+
The releases come from `schemas/released.json`, recorded as each app ships (`npm run released`), through the known-apps list's `firstRelease`: `"<capability>@<major>.<minor>"` and `"protocol@<n>"` mapped to the first release of that app that had them.
|
|
263
267
|
|
|
264
268
|
## Renaming an app
|
|
265
269
|
|
package/README.md
CHANGED
|
@@ -66,7 +66,15 @@ link.publish('drops.farming.changed', {
|
|
|
66
66
|
progress: { done: 48, total: 120, unit: 'minutes' },
|
|
67
67
|
image: 'https://files.kick.com/…/neon-rush.webp',
|
|
68
68
|
endsAt: '2026-10-21T18:00:00Z',
|
|
69
|
-
rewards: {
|
|
69
|
+
rewards: {
|
|
70
|
+
claimed: 1,
|
|
71
|
+
total: 3,
|
|
72
|
+
items: [
|
|
73
|
+
{ name: 'Pear Badge', required: 30, state: 'claimed' },
|
|
74
|
+
{ name: 'Hype Rocket Emote', required: 60, state: 'locked', unlocksAt: '2026-10-18T09:24:00Z' },
|
|
75
|
+
{ name: 'Neon Crown', required: 120, state: 'locked', unlocksAt: '2026-10-18T10:24:00Z' },
|
|
76
|
+
],
|
|
77
|
+
},
|
|
70
78
|
next: { name: 'Hype Rocket Emote', required: 60, unlocksAt: '2026-10-18T09:24:00Z' },
|
|
71
79
|
doneAt: '2026-10-18T10:24:00Z',
|
|
72
80
|
},
|
|
@@ -103,17 +111,18 @@ Editors suggest every event's fields and underline mistakes, in plain JavaScript
|
|
|
103
111
|
|
|
104
112
|
`link.peers()` lists every known app with what its card needs, and `link.on('change', …)` says when to draw it again.
|
|
105
113
|
|
|
106
|
-
| Field |
|
|
107
|
-
| ------------------------------------- |
|
|
108
|
-
| `name`, `summary`, `icon`, `download` | The app's logo (bundled, so it shows even when it isn't installed), one line about it, and where to get it
|
|
109
|
-
| `installed` | Whether its data folder exists (with `appDataDir`)
|
|
110
|
-
| `enabled` | Your switch for it
|
|
111
|
-
| `status` | `off`, `waiting`, `connected`, `pair-again` or `incompatible`
|
|
112
|
-
| `reason` | Why it's waiting: `switched-off` (its own switch is off), `disconnected-there`
|
|
113
|
-
| `keyHeldBy` | For `pair-again`: `us` means show **Pair again** here, `them` means it's pressed in the other app
|
|
114
|
-
| `version`, `report.text` | While connected: its version, and "Works" or what's missing ("update Pear Drops to 0.1.7 or later")
|
|
115
|
-
| `pairedAt`, `keysEncrypted` | When they paired, and whether keys are stored encrypted
|
|
116
|
-
| `formerIds` | Ids the app had before a rename
|
|
114
|
+
| Field | |
|
|
115
|
+
| ------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
|
|
116
|
+
| `name`, `summary`, `icon`, `download` | The app's logo (bundled, so it shows even when it isn't installed), one line about it, and where to get it |
|
|
117
|
+
| `installed` | Whether its data folder exists (with `appDataDir`) |
|
|
118
|
+
| `enabled` | Your switch for it |
|
|
119
|
+
| `status` | `off`, `waiting`, `connected`, `pair-again` or `incompatible` |
|
|
120
|
+
| `reason` | Why it's waiting: `switched-off` (its own switch is off), `disconnected-there` |
|
|
121
|
+
| `keyHeldBy` | For `pair-again`: `us` means show **Pair again** here, `them` means it's pressed in the other app |
|
|
122
|
+
| `version`, `report.text` | While connected: its version, and "Works" or what's missing ("update Pear Drops to 0.1.7 or later") |
|
|
123
|
+
| `pairedAt`, `keysEncrypted` | When they paired, and whether keys are stored encrypted |
|
|
124
|
+
| `formerIds` | Ids the app had before a rename |
|
|
125
|
+
| `shares` | What the two apps share: `{ out, in }`, capabilities this app provides that it uses, and the other way round |
|
|
117
126
|
|
|
118
127
|
Two calls go with buttons on the card:
|
|
119
128
|
|
|
@@ -144,10 +153,10 @@ Example messages for every capability version, good and bad, are in `@streamorch
|
|
|
144
153
|
|
|
145
154
|
## Capabilities
|
|
146
155
|
|
|
147
|
-
| Capability | Provided by | What it says
|
|
148
|
-
| ------------------- | ---------------- |
|
|
149
|
-
| `drops.farming` 1.0 | Pear Drops | Whether it's farming: the channel, stream and drop, with pictures
|
|
150
|
-
| `players` 1.0 | Stream Multiview | Its players by site: signed in or not, and which channels are open
|
|
156
|
+
| Capability | Provided by | What it says |
|
|
157
|
+
| ------------------- | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
158
|
+
| `drops.farming` 1.0 | Pear Drops | Whether it's farming: the channel, stream and drop, with pictures and every reward's state and when it unlocks, enough for a widget. Also `show`, to bring Pear Drops to the front |
|
|
159
|
+
| `players` 1.0 | Stream Multiview | Its players by site: signed in or not, and which channels are open |
|
|
151
160
|
|
|
152
161
|
To add one:
|
|
153
162
|
|
|
@@ -158,7 +167,17 @@ To change one, follow [the version rules](PROTOCOL.md#versions): additions are a
|
|
|
158
167
|
|
|
159
168
|
## Adding an app
|
|
160
169
|
|
|
161
|
-
Add it to `src/known-apps.js
|
|
170
|
+
Add it to `src/known-apps.js` with:
|
|
171
|
+
|
|
172
|
+
- its id and slug
|
|
173
|
+
- a one-line summary
|
|
174
|
+
- a 256×256 logo in `assets/apps/`
|
|
175
|
+
- its download page and its data folder's name
|
|
176
|
+
- the capabilities it `provides` and `uses`
|
|
177
|
+
|
|
178
|
+
Every app that updates Grapevine then shows it on its Connected apps page, with what the two would share. `link.peers()` gives that as `shares`, from this list until they meet and from the hello after.
|
|
179
|
+
|
|
180
|
+
When it ships a release that uses Grapevine, record it: `npm run released -- <its id> <its version>`. That fixes the capability versions it uses (they can't change in place after that) and tells other apps which release to ask for.
|
|
162
181
|
|
|
163
182
|
## Renaming an app
|
|
164
183
|
|
|
@@ -175,6 +194,7 @@ npm run lint # ESLint and Prettier
|
|
|
175
194
|
npm run format # fix what lint can
|
|
176
195
|
npm run types # types/capabilities.d.ts from the schemas
|
|
177
196
|
npm run check # the types are up to date, and tsc finds no type errors
|
|
197
|
+
npm run released -- <app id> <version> # an app shipped a release using Grapevine (see Adding an app)
|
|
178
198
|
```
|
|
179
199
|
|
|
180
200
|
CI runs the tests on Windows, macOS and Linux, and lint, types and coverage on Linux.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"event": "drops.farming.changed",
|
|
3
|
+
"params": {
|
|
4
|
+
"seq": 1,
|
|
5
|
+
"at": "2026-10-18T09:12:04Z",
|
|
6
|
+
"state": "farming",
|
|
7
|
+
"account": null,
|
|
8
|
+
"channel": null,
|
|
9
|
+
"drop": {
|
|
10
|
+
"campaign": "c",
|
|
11
|
+
"category": "c",
|
|
12
|
+
"progress": { "done": 1, "total": 2, "unit": "minutes" },
|
|
13
|
+
"rewards": { "claimed": 0, "total": 1, "items": [{ "name": "Pear Badge", "required": 30 }] }
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -14,7 +14,21 @@
|
|
|
14
14
|
"image": "https://files.kick.com/images/subcategories/1/banner/neon-rush.webp",
|
|
15
15
|
"endsAt": "2026-10-21T18:00:00Z",
|
|
16
16
|
"doneAt": "2026-10-18T10:24:00Z",
|
|
17
|
-
"rewards": {
|
|
17
|
+
"rewards": {
|
|
18
|
+
"claimed": 1,
|
|
19
|
+
"total": 3,
|
|
20
|
+
"items": [
|
|
21
|
+
{ "name": "Pear Badge", "image": "https://files.kick.com/drops/reward/pear-badge.png", "required": 30, "state": "claimed" },
|
|
22
|
+
{
|
|
23
|
+
"name": "Hype Rocket Emote",
|
|
24
|
+
"image": "https://files.kick.com/drops/reward/hype-rocket.png",
|
|
25
|
+
"required": 60,
|
|
26
|
+
"state": "locked",
|
|
27
|
+
"unlocksAt": "2026-10-18T09:24:00Z"
|
|
28
|
+
},
|
|
29
|
+
{ "name": "Neon Crown", "required": 120, "state": "locked", "unlocksAt": "2026-10-18T10:24:00Z" }
|
|
30
|
+
]
|
|
31
|
+
},
|
|
18
32
|
"next": {
|
|
19
33
|
"name": "Hype Rocket Emote",
|
|
20
34
|
"image": "https://files.kick.com/drops/reward/hype-rocket.png",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@streamorchard/grapevine",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Lets desktop apps on the same computer find each other and talk, privately: local sockets, JSON-RPC, pairing and versioned capabilities.",
|
|
5
5
|
"license": "GPL-3.0-only",
|
|
6
6
|
"author": "StreamOrchard",
|
|
@@ -52,7 +52,8 @@
|
|
|
52
52
|
"lint": "eslint . && prettier --check .",
|
|
53
53
|
"format": "prettier --write . && eslint --fix .",
|
|
54
54
|
"types": "node scripts/types.js",
|
|
55
|
-
"check": "node scripts/types.js --check && tsc -p jsconfig.json"
|
|
55
|
+
"check": "node scripts/types.js --check && tsc -p jsconfig.json",
|
|
56
|
+
"released": "node scripts/released.js"
|
|
56
57
|
},
|
|
57
58
|
"devDependencies": {
|
|
58
59
|
"@eslint/js": "^10.0.1",
|
|
@@ -121,6 +121,14 @@
|
|
|
121
121
|
"total": {
|
|
122
122
|
"type": "integer",
|
|
123
123
|
"minimum": 0
|
|
124
|
+
},
|
|
125
|
+
"items": {
|
|
126
|
+
"type": "array",
|
|
127
|
+
"maxItems": 20,
|
|
128
|
+
"items": {
|
|
129
|
+
"$ref": "#Reward"
|
|
130
|
+
},
|
|
131
|
+
"description": "Every reward of the drop, in the order they unlock"
|
|
124
132
|
}
|
|
125
133
|
}
|
|
126
134
|
},
|
|
@@ -190,6 +198,42 @@
|
|
|
190
198
|
"description": "The stream being watched, while farming: its title and viewers"
|
|
191
199
|
}
|
|
192
200
|
}
|
|
201
|
+
},
|
|
202
|
+
"Reward": {
|
|
203
|
+
"type": "object",
|
|
204
|
+
"required": ["name", "required", "state"],
|
|
205
|
+
"properties": {
|
|
206
|
+
"name": {
|
|
207
|
+
"type": "string",
|
|
208
|
+
"maxLength": 200
|
|
209
|
+
},
|
|
210
|
+
"image": {
|
|
211
|
+
"$ref": "common:Image"
|
|
212
|
+
},
|
|
213
|
+
"required": {
|
|
214
|
+
"type": "number",
|
|
215
|
+
"minimum": 0,
|
|
216
|
+
"description": "Minutes of watching it unlocks at"
|
|
217
|
+
},
|
|
218
|
+
"state": {
|
|
219
|
+
"type": "string",
|
|
220
|
+
"minLength": 1,
|
|
221
|
+
"maxLength": 32,
|
|
222
|
+
"x-open": ["claimed", "ready", "locked"],
|
|
223
|
+
"description": "ready: unlocked, waiting to be claimed"
|
|
224
|
+
},
|
|
225
|
+
"unlocksAt": {
|
|
226
|
+
"anyOf": [
|
|
227
|
+
{
|
|
228
|
+
"$ref": "common:Timestamp"
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
"type": "null"
|
|
232
|
+
}
|
|
233
|
+
],
|
|
234
|
+
"description": "For a locked one: when it should unlock, at the current rate"
|
|
235
|
+
}
|
|
236
|
+
}
|
|
193
237
|
}
|
|
194
238
|
}
|
|
195
239
|
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
{
|
|
2
|
+
"description": "Capability versions that have shipped in an app's release, and which release of each app first had them. Written by `npm run released -- <app id> <version>`; never by hand. A version recorded here can't change in place: test/released.test.js fails until the change goes in a new minor.",
|
|
3
|
+
"schemas": {},
|
|
4
|
+
"firstRelease": {}
|
|
5
|
+
}
|
package/src/index.js
CHANGED
|
@@ -415,6 +415,20 @@ class Link extends EventEmitter {
|
|
|
415
415
|
return this.conns.has(this.currentId(appId));
|
|
416
416
|
}
|
|
417
417
|
|
|
418
|
+
/**
|
|
419
|
+
* What this app and another share: `out`, capabilities this app provides that it uses; `in`,
|
|
420
|
+
* ones it provides that this app uses. Agreed in the hello while connected; from the known-apps
|
|
421
|
+
* list otherwise, so a card can say what linking would do before the two have met.
|
|
422
|
+
*/
|
|
423
|
+
sharesWith(app, conn) {
|
|
424
|
+
if (conn) return { out: [...conn.agreed.outgoing.keys()].sort(), in: [...conn.agreed.incoming.keys()].sort() };
|
|
425
|
+
const mine = (list) => (cap) => cap in list;
|
|
426
|
+
return {
|
|
427
|
+
out: (app.uses ?? []).filter(mine(this.mine.provides)).sort(),
|
|
428
|
+
in: (app.provides ?? []).filter(mine(this.mine.uses)).sort(),
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
|
|
418
432
|
/**
|
|
419
433
|
* Every known app, for the Connected apps page:
|
|
420
434
|
* status 'off' (its switch is off) | 'waiting' | 'connected' | 'pair-again' | 'incompatible'.
|
|
@@ -445,6 +459,7 @@ class Link extends EventEmitter {
|
|
|
445
459
|
keyHeldBy: status === 'pair-again' ? (why?.keyHeldBy ?? null) : null,
|
|
446
460
|
version: conn?.peer.version ?? null,
|
|
447
461
|
report,
|
|
462
|
+
shares: this.sharesWith(app, conn),
|
|
448
463
|
pairedAt: paired?.pairedAt ?? null,
|
|
449
464
|
keysEncrypted: !!this.keys.encrypted,
|
|
450
465
|
};
|
package/src/known-apps.js
CHANGED
|
@@ -7,6 +7,8 @@ const fs = require('fs');
|
|
|
7
7
|
const path = require('path');
|
|
8
8
|
|
|
9
9
|
const ICONS = path.join(__dirname, '..', 'assets', 'apps');
|
|
10
|
+
// Which release of each app first had each capability version: written by `npm run released`.
|
|
11
|
+
const RELEASED = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'schemas', 'released.json'), 'utf8')).firstRelease;
|
|
10
12
|
|
|
11
13
|
/**
|
|
12
14
|
* @typedef {object} KnownApp
|
|
@@ -19,6 +21,8 @@ const ICONS = path.join(__dirname, '..', 'assets', 'apps');
|
|
|
19
21
|
* @property {string} homepage
|
|
20
22
|
* @property {string} download its latest release
|
|
21
23
|
* @property {string} dataFolder its folder in the OS's app data folder: no folder means not installed
|
|
24
|
+
* @property {string[]} provides the capabilities it provides (their names), so another app can say what the two share before they've met
|
|
25
|
+
* @property {string[]} uses the capabilities it uses
|
|
22
26
|
* @property {Record<string, string>} firstRelease "capability@major.minor" (or "protocol@N") -> the app version that first had it
|
|
23
27
|
*/
|
|
24
28
|
|
|
@@ -34,7 +38,9 @@ const KNOWN_APPS = [
|
|
|
34
38
|
homepage: 'https://github.com/Stream-Orchard/pear-drops',
|
|
35
39
|
download: 'https://github.com/Stream-Orchard/pear-drops/releases/latest',
|
|
36
40
|
dataFolder: 'Pear Drops',
|
|
37
|
-
|
|
41
|
+
provides: ['drops.farming'],
|
|
42
|
+
uses: ['players'],
|
|
43
|
+
firstRelease: { ...RELEASED['com.streamorchard.peardrops'] },
|
|
38
44
|
},
|
|
39
45
|
{
|
|
40
46
|
id: 'com.glowingpear.streammultiview',
|
|
@@ -46,7 +52,9 @@ const KNOWN_APPS = [
|
|
|
46
52
|
homepage: 'https://github.com/GlowingPear/stream-window-multiview',
|
|
47
53
|
download: 'https://github.com/GlowingPear/stream-window-multiview/releases/latest',
|
|
48
54
|
dataFolder: 'Stream Multiview',
|
|
49
|
-
|
|
55
|
+
provides: ['players'],
|
|
56
|
+
uses: ['drops.farming'],
|
|
57
|
+
firstRelease: { ...RELEASED['com.glowingpear.streammultiview'] },
|
|
50
58
|
},
|
|
51
59
|
];
|
|
52
60
|
|
package/types/capabilities.d.ts
CHANGED
|
@@ -56,6 +56,8 @@ export type DropsFarmingV1State = {
|
|
|
56
56
|
rewards?: {
|
|
57
57
|
claimed: number;
|
|
58
58
|
total: number;
|
|
59
|
+
/** Every reward of the drop, in the order they unlock */
|
|
60
|
+
items?: Array<DropsFarmingV1Reward>;
|
|
59
61
|
};
|
|
60
62
|
/** The reward being worked towards */
|
|
61
63
|
next?: {
|
|
@@ -74,6 +76,17 @@ export type DropsFarmingV1State = {
|
|
|
74
76
|
} | null;
|
|
75
77
|
};
|
|
76
78
|
|
|
79
|
+
export type DropsFarmingV1Reward = {
|
|
80
|
+
name: string;
|
|
81
|
+
image?: Image;
|
|
82
|
+
/** Minutes of watching it unlocks at */
|
|
83
|
+
required: number;
|
|
84
|
+
/** ready: unlocked, waiting to be claimed */
|
|
85
|
+
state: "claimed" | "ready" | "locked" | (string & {});
|
|
86
|
+
/** For a locked one: when it should unlock, at the current rate */
|
|
87
|
+
unlocksAt?: Timestamp | null;
|
|
88
|
+
};
|
|
89
|
+
|
|
77
90
|
// players 1.0: An app's stream players, by site: whether they're signed in, and which channels are open. Provided by Stream Multiview.
|
|
78
91
|
export type PlayersV1State = {
|
|
79
92
|
seq: number;
|
package/types/index.d.ts
CHANGED
|
@@ -64,6 +64,9 @@ export interface KnownApp {
|
|
|
64
64
|
download: string;
|
|
65
65
|
/** Its folder in the OS's app data folder */
|
|
66
66
|
dataFolder: string;
|
|
67
|
+
/** The capabilities it provides and uses (names), so a card can say what linking would do */
|
|
68
|
+
provides: string[];
|
|
69
|
+
uses: string[];
|
|
67
70
|
/** "capability@major.minor" (or "protocol@N") -> the app version that first had it */
|
|
68
71
|
firstRelease: Record<string, string>;
|
|
69
72
|
}
|
|
@@ -119,6 +122,8 @@ export interface Peer {
|
|
|
119
122
|
/** Its version, while connected */
|
|
120
123
|
version: string | null;
|
|
121
124
|
report: Report | null;
|
|
125
|
+
/** What the two apps share: `out`, capabilities this app provides that it uses; `in`, ones it provides that this app uses */
|
|
126
|
+
shares: { out: string[]; in: string[] };
|
|
122
127
|
pairedAt: string | null;
|
|
123
128
|
keysEncrypted: boolean;
|
|
124
129
|
}
|