vibedate 0.7.2 → 0.8.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/dist/chunk-JBADPOR5.js +15748 -0
- package/dist/{chunk-HDHJLZZG.js → chunk-S7MO5OSO.js} +7 -0
- package/dist/cli.js +14 -450
- package/dist/index.d.ts +4 -319
- package/dist/index.js +1 -1
- package/dist/mcp.d.ts +181 -1
- package/dist/mcp.js +8 -2
- package/dist/room-BStsMaeT.d.ts +320 -0
- package/package.json +1 -1
- package/dist/chunk-ZD6JBWEW.js +0 -63
package/dist/index.d.ts
CHANGED
|
@@ -1,243 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { UsageSnapshot, UsageSource, Harness, HarnessUsageOptions } from '@pooriaarab/vibe-core';
|
|
2
2
|
export { Harness, UsageSnapshot, UsageSource, createConsentLedger } from '@pooriaarab/vibe-core';
|
|
3
|
+
import { P as PeerHello, a as PeerLink } from './room-BStsMaeT.js';
|
|
4
|
+
export { D as DiscoveryOptions, b as DiscoverySession, L as LIVE_NOTICE, R as ROOM_TOPIC_PREFIX, c as RoomMember, d as RoomMessage, e as RoomOptions, f as RoomSession, S as StoredPeer, T as TOPIC_PREFIX, l as leagueTopic, g as loadPeers, p as parseHandshake, r as recordPeer, h as recordPeerMessage, i as roomTopic, s as serializeHandshake, j as startDiscovery, k as startRoom } from './room-BStsMaeT.js';
|
|
3
5
|
import { KeyObject } from 'node:crypto';
|
|
4
6
|
|
|
5
|
-
type Frame = {
|
|
6
|
-
t: 'hello';
|
|
7
|
-
handle: string;
|
|
8
|
-
league: string;
|
|
9
|
-
harness: string;
|
|
10
|
-
verified?: boolean;
|
|
11
|
-
pubkey?: string;
|
|
12
|
-
nonce?: string;
|
|
13
|
-
sig?: string;
|
|
14
|
-
} | {
|
|
15
|
-
t: 'msg';
|
|
16
|
-
id: string;
|
|
17
|
-
text: string;
|
|
18
|
-
at: number;
|
|
19
|
-
} | {
|
|
20
|
-
t: 'typing';
|
|
21
|
-
} | {
|
|
22
|
-
t: 'bye';
|
|
23
|
-
} | {
|
|
24
|
-
t: 'media-start';
|
|
25
|
-
id: string;
|
|
26
|
-
mime: string;
|
|
27
|
-
size: number;
|
|
28
|
-
name: string;
|
|
29
|
-
} | {
|
|
30
|
-
t: 'media-chunk';
|
|
31
|
-
id: string;
|
|
32
|
-
seq: number;
|
|
33
|
-
b64: string;
|
|
34
|
-
} | {
|
|
35
|
-
t: 'media-end';
|
|
36
|
-
id: string;
|
|
37
|
-
} | {
|
|
38
|
-
t: 'rtc-offer';
|
|
39
|
-
sdp: string;
|
|
40
|
-
} | {
|
|
41
|
-
t: 'rtc-answer';
|
|
42
|
-
sdp: string;
|
|
43
|
-
} | {
|
|
44
|
-
t: 'rtc-ice';
|
|
45
|
-
candidate: string;
|
|
46
|
-
};
|
|
47
|
-
/** Convenience union of the three WebRTC signaling frame types (offer / answer
|
|
48
|
-
* / ice). Live A/V runs in the BROWSER via a native RTCPeerConnection; these
|
|
49
|
-
* frames only RELAY signaling over the P2P socket — no media bytes, no native
|
|
50
|
-
* WebRTC dependency in the CLI. */
|
|
51
|
-
type RtcFrame = Extract<Frame, {
|
|
52
|
-
t: `rtc-${string}`;
|
|
53
|
-
}>;
|
|
54
|
-
|
|
55
|
-
interface ReceivedMedia {
|
|
56
|
-
readonly mime: string;
|
|
57
|
-
readonly name: string;
|
|
58
|
-
/** Temp file path holding the reassembled bytes. */
|
|
59
|
-
readonly path: string;
|
|
60
|
-
readonly size: number;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
interface PeerLink {
|
|
64
|
-
/** The validated identity of the remote peer (from the hello handshake). */
|
|
65
|
-
readonly hello: PeerHello;
|
|
66
|
-
/** Send a line of text as a `msg` frame. */
|
|
67
|
-
send(text: string): void;
|
|
68
|
-
/** Read a file from disk and send it as a chunked media transfer. */
|
|
69
|
-
sendMedia(filePath: string, opts?: {
|
|
70
|
-
mime?: string;
|
|
71
|
-
name?: string;
|
|
72
|
-
}): Promise<{
|
|
73
|
-
id: string;
|
|
74
|
-
size: number;
|
|
75
|
-
}>;
|
|
76
|
-
/** Relay one `rtc-*` signaling frame (offer / answer / ice) to the peer.
|
|
77
|
-
* Live media never touches this socket — only SDP / ICE strings do. */
|
|
78
|
-
sendSignal(frame: RtcFrame): void;
|
|
79
|
-
/** Register a callback for each incoming `msg` frame. */
|
|
80
|
-
onMessage(cb: (m: {
|
|
81
|
-
id: string;
|
|
82
|
-
text: string;
|
|
83
|
-
at: number;
|
|
84
|
-
}) => void): void;
|
|
85
|
-
/** Register a callback fired for each fully-reassembled incoming media file. */
|
|
86
|
-
onMedia(cb: (m: ReceivedMedia) => void): void;
|
|
87
|
-
/** Register a callback fired for each incoming `rtc-*` signaling frame. */
|
|
88
|
-
onSignal(cb: (f: RtcFrame) => void): void;
|
|
89
|
-
/** Register a callback fired once when the peer closes the link. */
|
|
90
|
-
onClose(cb: () => void): void;
|
|
91
|
-
/** Omegle "next": write a `bye` frame, then end the socket. */
|
|
92
|
-
close(): void;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/** Namespace prefix so vibedating topics never collide with other DHT traffic. */
|
|
96
|
-
declare const TOPIC_PREFIX = "vibedate:";
|
|
97
|
-
/**
|
|
98
|
-
* Derive the 32-byte DHT topic for a league bucket. Deterministic: everyone in
|
|
99
|
-
* the same league anywhere in the world hashes to the same topic, which is the
|
|
100
|
-
* entire discovery mechanism. Pure.
|
|
101
|
-
*/
|
|
102
|
-
declare function leagueTopic(leagueName: string): Buffer;
|
|
103
|
-
/**
|
|
104
|
-
* The fields that ever leave the machine over a peer connection: handle, league,
|
|
105
|
-
* harness, and (optionally) the self-asserted usage-verification flag plus the
|
|
106
|
-
* identity proof (pubkey/nonce/sig). NEVER raw usage — no token totals, no logs.
|
|
107
|
-
* `verified`/`pubkey` are undefined for legacy peers that predate them; both
|
|
108
|
-
* `undefined` and `false` display as unverified (~).
|
|
109
|
-
*/
|
|
110
|
-
interface PeerHello {
|
|
111
|
-
readonly handle: string;
|
|
112
|
-
readonly league: string;
|
|
113
|
-
readonly harness: string;
|
|
114
|
-
/**
|
|
115
|
-
* Self-asserted: the sender's usage came from real local logs (see readUsage).
|
|
116
|
-
* Bound to the sender's key by the identity signature when `pubkey` is present.
|
|
117
|
-
*/
|
|
118
|
-
readonly verified?: boolean;
|
|
119
|
-
/** Raw ed25519 public key (64 hex) — the persistent identity this hello signs. */
|
|
120
|
-
readonly pubkey?: string;
|
|
121
|
-
/** Random per-hello nonce (hex) covered by the signature. */
|
|
122
|
-
readonly nonce?: string;
|
|
123
|
-
/** ed25519 signature (128 hex) over `handle|league|harness|verified|nonce`. */
|
|
124
|
-
readonly sig?: string;
|
|
125
|
-
/**
|
|
126
|
-
* LOCAL-DERIVED, never on the wire: true when this hello's signature verified
|
|
127
|
-
* against its pubkey (see classifyHelloIdentity). Marked 🔑 in the UI.
|
|
128
|
-
*/
|
|
129
|
-
readonly identityVerified?: boolean;
|
|
130
|
-
}
|
|
131
|
-
/** One-line privacy notice printed before joining the swarm. */
|
|
132
|
-
declare const LIVE_NOTICE = "live discovery: sharing only your handle + league + harness + verified flag + identity pubkey (never raw usage) with same-league peers on the public DHT";
|
|
133
|
-
/**
|
|
134
|
-
* Serialize a hello to the single JSON line sent on connect. Built key-by-key
|
|
135
|
-
* from the allowlist — even if a caller sneaks extra properties onto the
|
|
136
|
-
* object, they cannot leak into the wire format.
|
|
137
|
-
*/
|
|
138
|
-
declare function serializeHandshake(hello: PeerHello): string;
|
|
139
|
-
/**
|
|
140
|
-
* Parse one incoming handshake line. Returns `null` for anything malformed
|
|
141
|
-
* (bad JSON, non-object, missing/oversized handle or league). The result is
|
|
142
|
-
* constructed from an allowlist of keys, so any extra fields a peer sends —
|
|
143
|
-
* in particular any raw-usage field — are ignored and never retained.
|
|
144
|
-
*/
|
|
145
|
-
declare function parseHandshake(raw: string | Buffer): PeerHello | null;
|
|
146
|
-
/** A peer we've shaken hands with, persisted locally. */
|
|
147
|
-
interface StoredPeer extends PeerHello {
|
|
148
|
-
readonly firstSeenAt: string;
|
|
149
|
-
readonly lastSeenAt: string;
|
|
150
|
-
/** LOCAL metadata: when the last `msg` from this peer arrived (never on the wire). */
|
|
151
|
-
readonly lastMessageAt?: string;
|
|
152
|
-
}
|
|
153
|
-
/** Load persisted live peers, or `[]` if none/corrupt. Local-only data. */
|
|
154
|
-
declare function loadPeers(dir?: string): StoredPeer[];
|
|
155
|
-
/**
|
|
156
|
-
* Record a successfully handshaken peer, keyed by handle (a peer may reconnect
|
|
157
|
-
* from a different key). Returns whether this handle is NEW (first time seen).
|
|
158
|
-
*/
|
|
159
|
-
declare function recordPeer(hello: PeerHello, dir?: string, now?: Date): {
|
|
160
|
-
peer: StoredPeer;
|
|
161
|
-
isNew: boolean;
|
|
162
|
-
};
|
|
163
|
-
/**
|
|
164
|
-
* Stamp `lastMessageAt` on a stored peer (a `msg` just arrived from them).
|
|
165
|
-
* Local metadata only; never on the wire. Returns false when the handle isn't
|
|
166
|
-
* a known peer. Never throws — best-effort bookkeeping.
|
|
167
|
-
*/
|
|
168
|
-
declare function recordPeerMessage(handle: string, dir?: string, now?: Date): boolean;
|
|
169
|
-
/** Injection point for the match notification (defaults to vibe-core notify). */
|
|
170
|
-
type NotifySink = (event: VibeEvent) => void;
|
|
171
|
-
interface DiscoveryOptions {
|
|
172
|
-
/** What we broadcast. Must already be consent-gated by the caller. */
|
|
173
|
-
readonly hello: PeerHello;
|
|
174
|
-
/**
|
|
175
|
-
* Override the joined topic (tests pass a random one on an isolated DHT).
|
|
176
|
-
* Defaults to {@link leagueTopic}`(hello.league)`. Ignored when {@link topics}
|
|
177
|
-
* is set.
|
|
178
|
-
*/
|
|
179
|
-
readonly topic?: Buffer;
|
|
180
|
-
/**
|
|
181
|
-
* ALL topics to join on the one swarm (e.g. your league + adjacent leagues),
|
|
182
|
-
* so thin pools and cross-league friends still connect. Every topic is
|
|
183
|
-
* joined, refreshed, and left on close. Defaults to `[topic]` — i.e. a single
|
|
184
|
-
* own-league topic (the legacy behavior).
|
|
185
|
-
*/
|
|
186
|
-
readonly topics?: readonly Buffer[];
|
|
187
|
-
/**
|
|
188
|
-
* Predicate over an incoming peer's advertised league. Defaults to EXACT
|
|
189
|
-
* match against `hello.league` — the same privacy invariant as before. Widen
|
|
190
|
-
* it (e.g. ±1 adjacency via {@link leaguesWithin}) to accept cross-league
|
|
191
|
-
* peers that arrive on a shared topic.
|
|
192
|
-
*/
|
|
193
|
-
readonly acceptLeague?: (peerLeague: string) => boolean;
|
|
194
|
-
/**
|
|
195
|
-
* Predicate over an incoming peer's advertised handle. A blocked peer's hello
|
|
196
|
-
* is DROPPED exactly like a wrong-league one — never recorded to peers.json,
|
|
197
|
-
* never notified, never handed to `onLink`/pairing. Default: nothing blocked.
|
|
198
|
-
* The CLI passes one backed by the persisted blocklist (~/.vibedating).
|
|
199
|
-
*/
|
|
200
|
-
readonly isBlocked?: (handle: string) => boolean;
|
|
201
|
-
/** DHT bootstrap nodes; omit for the public DHT. Tests pass a local testnet. */
|
|
202
|
-
readonly bootstrap?: ReadonlyArray<{
|
|
203
|
-
readonly host: string;
|
|
204
|
-
readonly port: number;
|
|
205
|
-
}>;
|
|
206
|
-
/** Where peers.json lives. Defaults to ~/.vibedating. */
|
|
207
|
-
readonly stateDir?: string;
|
|
208
|
-
/** Called after each accepted handshake; `isNew` = first time this handle is seen. */
|
|
209
|
-
readonly onPeer?: (peer: PeerHello, isNew: boolean) => void;
|
|
210
|
-
/**
|
|
211
|
-
* Called once per connection with a live {@link PeerLink} over the same socket
|
|
212
|
-
* (the hello was frame #1; subsequent frames flow to the link). Omit for the
|
|
213
|
-
* plain `discover` behavior (no live chat). Existing discovery behavior is
|
|
214
|
-
* unchanged when this is absent.
|
|
215
|
-
*/
|
|
216
|
-
readonly onLink?: (link: PeerLink) => void;
|
|
217
|
-
/** Match-notification sink (tests capture with a fake). Best-effort. */
|
|
218
|
-
readonly notify?: NotifySink;
|
|
219
|
-
}
|
|
220
|
-
interface DiscoverySession {
|
|
221
|
-
/** The primary (first) joined topic. See {@link topics} for the full set. */
|
|
222
|
-
readonly topic: Buffer;
|
|
223
|
-
/** Every topic this session joined (primary first). */
|
|
224
|
-
readonly topics: readonly Buffer[];
|
|
225
|
-
/** What we broadcast on every connection. */
|
|
226
|
-
readonly hello: PeerHello;
|
|
227
|
-
/** Live peer set, keyed by the remote's public key (hex). */
|
|
228
|
-
readonly peers: ReadonlyMap<string, PeerHello>;
|
|
229
|
-
/** Resolves when the first DHT announce/lookup round for every topic completes. */
|
|
230
|
-
readonly ready: Promise<unknown>;
|
|
231
|
-
/** Leave every topic and destroy the node. Idempotent. */
|
|
232
|
-
close(): Promise<void>;
|
|
233
|
-
}
|
|
234
|
-
/**
|
|
235
|
-
* Join the swarm on the league topic and handshake with every peer that
|
|
236
|
-
* connects. CONSENT GATE LIVES WITH THE CALLER — never call this without the
|
|
237
|
-
* `share:live` grant (or an explicit `--live` opt-in in the same breath).
|
|
238
|
-
*/
|
|
239
|
-
declare function startDiscovery(opts: DiscoveryOptions): Promise<DiscoverySession>;
|
|
240
|
-
|
|
241
7
|
/**
|
|
242
8
|
* A Nostr event, reduced to the fields this module touches. Structurally
|
|
243
9
|
* compatible with `nostr-tools`'s `Event`, so a finalized/verified event can be
|
|
@@ -351,87 +117,6 @@ declare function createNostrRelayLink(opts: CreateNostrRelayLinkOptions): Promis
|
|
|
351
117
|
*/
|
|
352
118
|
declare function createNostrPoolTransport(urls?: readonly string[]): Promise<RelayTransport>;
|
|
353
119
|
|
|
354
|
-
/** Namespace prefix so room topics never collide with league topics (or anything
|
|
355
|
-
* else on the DHT). Mirrors {@link p2p.TOPIC_PREFIX} for the 1:1 path. */
|
|
356
|
-
declare const ROOM_TOPIC_PREFIX = "vibedate-room:";
|
|
357
|
-
/**
|
|
358
|
-
* Derive the 32-byte DHT topic for a named room. Deterministic: everyone who
|
|
359
|
-
* joins the same room name anywhere in the world hashes to the same topic,
|
|
360
|
-
* which is the entire discovery mechanism. Pure (mirrors {@link p2p.leagueTopic}).
|
|
361
|
-
*/
|
|
362
|
-
declare function roomTopic(name: string): Buffer;
|
|
363
|
-
/** A room member = a connected, handshaken peer. Same shape as a live peer. */
|
|
364
|
-
type RoomMember = PeerHello;
|
|
365
|
-
/** One group chat message: a `msg` frame's payload tagged with the sender. */
|
|
366
|
-
interface RoomMessage {
|
|
367
|
-
/** Sender's handle (from the validated hello — UNTRUSTED display data). */
|
|
368
|
-
readonly from: string;
|
|
369
|
-
readonly id: string;
|
|
370
|
-
readonly text: string;
|
|
371
|
-
readonly at: number;
|
|
372
|
-
}
|
|
373
|
-
interface RoomOptions {
|
|
374
|
-
/** What we broadcast. Must already be consent-gated by the caller. */
|
|
375
|
-
readonly hello: PeerHello;
|
|
376
|
-
/** Room name → its own DHT topic (see {@link roomTopic}). */
|
|
377
|
-
readonly room: string;
|
|
378
|
-
/** DHT bootstrap nodes; omit for the public DHT. Tests pass a local testnet. */
|
|
379
|
-
readonly bootstrap?: ReadonlyArray<{
|
|
380
|
-
readonly host: string;
|
|
381
|
-
readonly port: number;
|
|
382
|
-
}>;
|
|
383
|
-
/** Where peers.json lives. Defaults to ~/.vibedating. */
|
|
384
|
-
readonly stateDir?: string;
|
|
385
|
-
/** Predicate over a blocked handle — a blocked peer's hello is dropped exactly
|
|
386
|
-
* like in 1:1 discovery. Default: nothing blocked. */
|
|
387
|
-
readonly isBlocked?: (handle: string) => boolean;
|
|
388
|
-
/** Match-notification sink (tests capture with a fake). Best-effort. */
|
|
389
|
-
readonly notify?: NotifySink;
|
|
390
|
-
}
|
|
391
|
-
interface RoomSession {
|
|
392
|
-
/** The room name. */
|
|
393
|
-
readonly room: string;
|
|
394
|
-
/** The joined DHT topic (see {@link roomTopic}). */
|
|
395
|
-
readonly topic: Buffer;
|
|
396
|
-
/** What we broadcast. */
|
|
397
|
-
readonly hello: PeerHello;
|
|
398
|
-
/** Live member set, keyed by handle (excludes self). Mirrors the live view
|
|
399
|
-
* semantics of {@link p2p.DiscoverySession.peers}. */
|
|
400
|
-
readonly members: ReadonlyMap<string, RoomMember>;
|
|
401
|
-
/** Resolves when the first DHT announce/lookup round completes. */
|
|
402
|
-
readonly ready: Promise<unknown>;
|
|
403
|
-
/**
|
|
404
|
-
* Broadcast a text message to ALL room members (fan-out: one `msg` frame per
|
|
405
|
-
* member's {@link PeerLink}). Returns the handles the message was sent to.
|
|
406
|
-
* Best-effort: a member whose link has just closed is skipped silently.
|
|
407
|
-
*/
|
|
408
|
-
broadcast(text: string): readonly string[];
|
|
409
|
-
/** Register a callback fired for each incoming group message (with sender). */
|
|
410
|
-
onMessage(cb: (m: RoomMessage) => void): void;
|
|
411
|
-
/** Register a callback fired whenever the member roster changes (join/leave). */
|
|
412
|
-
onRoster(cb: (members: readonly RoomMember[]) => void): void;
|
|
413
|
-
/** Register a callback fired for each incoming `rtc-*` signaling frame,
|
|
414
|
-
* tagged with the sender's handle (full-mesh video signaling). */
|
|
415
|
-
onSignal(cb: (from: string, frame: RtcFrame) => void): void;
|
|
416
|
-
/** Relay one `rtc-*` signaling frame to one member (by handle). */
|
|
417
|
-
sendSignal(handle: string, frame: RtcFrame): void;
|
|
418
|
-
/** The underlying {@link PeerLink} to a member (by handle), or undefined. */
|
|
419
|
-
linkFor(handle: string): PeerLink | undefined;
|
|
420
|
-
/** Leave the room and destroy the node. Idempotent. */
|
|
421
|
-
close(): Promise<void>;
|
|
422
|
-
}
|
|
423
|
-
/**
|
|
424
|
-
* Join (or create) a named room on the DHT and discover ALL members. CONSENT
|
|
425
|
-
* GATE LIVES WITH THE CALLER — never call this without the `share:live` grant
|
|
426
|
-
* (or an explicit opt-in in the same breath), exactly like
|
|
427
|
-
* {@link p2p.startDiscovery}.
|
|
428
|
-
*
|
|
429
|
-
* Resolves once the first DHT announce/lookup round completes; the returned
|
|
430
|
-
* session's {@link RoomSession.members} map + {@link RoomSession.onRoster}
|
|
431
|
-
* callback track every member that joins or leaves thereafter.
|
|
432
|
-
*/
|
|
433
|
-
declare function startRoom(opts: RoomOptions): Promise<RoomSession>;
|
|
434
|
-
|
|
435
120
|
/**
|
|
436
121
|
* Persistent ed25519 identity — binds a handle to a keypair so a peer cannot
|
|
437
122
|
* impersonate it.
|
|
@@ -639,4 +324,4 @@ declare const CANDIDATES: readonly Candidate[];
|
|
|
639
324
|
*/
|
|
640
325
|
declare function matches(myLeague: string, candidates?: readonly Candidate[]): Candidate[];
|
|
641
326
|
|
|
642
|
-
export { BELOW_LEAGUE, CANDIDATES, type Candidate, type CreateNostrRelayLinkOptions, DEFAULT_RELAYS, DEMO_TOTAL_TOKENS, type
|
|
327
|
+
export { BELOW_LEAGUE, CANDIDATES, type Candidate, type CreateNostrRelayLinkOptions, DEFAULT_RELAYS, DEMO_TOTAL_TOKENS, type HelloClaims, type Identity, type IdentityProof, type IdentityVerdict, LEAGUES, type League, type LocalUsageSnapshot, type NostrKey, type NostrRelayLink, PeerHello, type RelayEvent, type RelayFilter, type RelayTransport, TOKENS_ENV, VIBEDATE_MESSAGE_KIND, VIBEDATE_PRESENCE_KIND, allLeagueNames, canonicalHelloClaims, classifyHelloIdentity, conversationTag, createNostrPoolTransport, createNostrRelayLink, league, leagueIndex, leaguesWithin, loadOrCreateIdentity, loadOrCreateNostrKey, matches, parseTokensEnv, readUsage, signHelloClaims, verifyHelloClaims };
|
package/dist/index.js
CHANGED
package/dist/mcp.d.ts
CHANGED
|
@@ -1,7 +1,187 @@
|
|
|
1
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import { a as PeerLink, P as PeerHello, f as RoomSession, b as DiscoverySession, d as RoomMessage } from './room-BStsMaeT.js';
|
|
3
|
+
import '@pooriaarab/vibe-core';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Pairing policy for the live layer — pure, over an injected set of links.
|
|
7
|
+
*
|
|
8
|
+
* Two modes share one policy object:
|
|
9
|
+
*
|
|
10
|
+
* - Omegle: links arrive (`add`) and are auto-paired one at a time; `next()`
|
|
11
|
+
* closes the current match and rolls to the next waiting link (or idles).
|
|
12
|
+
* - Dating: `open(handle)` picks the specific waiting link whose peer
|
|
13
|
+
* advertised that handle, closing any current match first.
|
|
14
|
+
*
|
|
15
|
+
* This module knows nothing about hyperswarm, the DHT, or the wire — it only
|
|
16
|
+
* shuffles {@link PeerLink}s handed to it. That keeps it trivially unit-testable
|
|
17
|
+
* with fakes and keeps the policy out of the transport.
|
|
18
|
+
*
|
|
19
|
+
* Remote hang-ups are handled too: when the matched peer "next"s us, the link's
|
|
20
|
+
* onClose fires and we auto-pair the next waiting link (omegle stays live).
|
|
21
|
+
*
|
|
22
|
+
* MESSAGE ROUTING (the correctness bit): `onMessage` is bound ONCE per link in
|
|
23
|
+
* {@link LivePairing.add} — never re-bound on match. A message from the current
|
|
24
|
+
* peer is delivered live; a message from a NON-current (queued) peer is BUFFERED
|
|
25
|
+
* (never dropped) and a queued-count notice fires, then flushed when that peer
|
|
26
|
+
* becomes current. Binding once per link means there is exactly one handler per
|
|
27
|
+
* link: no accumulation (no duplicate delivery) and no silent drops.
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
/** A chat message received from a peer. Mirrors PeerLink.onMessage's payload. */
|
|
31
|
+
interface IncomingMessage {
|
|
32
|
+
readonly id: string;
|
|
33
|
+
readonly text: string;
|
|
34
|
+
readonly at: number;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* A drained message tagged with its sender's handle — for PULL-based consumers
|
|
38
|
+
* (the MCP `live_poll` tool) that read every inbound message since the last
|
|
39
|
+
* poll, current or queued, without registering a push callback.
|
|
40
|
+
*/
|
|
41
|
+
interface PairingMessage {
|
|
42
|
+
readonly from: string;
|
|
43
|
+
readonly id: string;
|
|
44
|
+
readonly text: string;
|
|
45
|
+
readonly at: number;
|
|
46
|
+
/** True if this arrived from a NON-current (queued) peer at receive time. */
|
|
47
|
+
readonly queued: boolean;
|
|
48
|
+
}
|
|
49
|
+
interface LivePairing {
|
|
50
|
+
/** Number of unmatched links waiting in the queue. */
|
|
51
|
+
readonly available: number;
|
|
52
|
+
/** The currently-matched link, or `undefined` when idle. */
|
|
53
|
+
current(): PeerLink | undefined;
|
|
54
|
+
/**
|
|
55
|
+
* Omegle "next": close the current match and advance to the next waiting link
|
|
56
|
+
* (auto-pair), or go idle if the queue is empty. Returns the new current link.
|
|
57
|
+
*/
|
|
58
|
+
next(): PeerLink | undefined;
|
|
59
|
+
/**
|
|
60
|
+
* Dating pick: match the available link whose `hello.handle === handle`.
|
|
61
|
+
* Closes the current match first. Returns the matched link, or `undefined`.
|
|
62
|
+
*/
|
|
63
|
+
open(handle: string): PeerLink | undefined;
|
|
64
|
+
/** Add a newly-arrived link (fed from discovery's `onLink`). Auto-pairs if idle. */
|
|
65
|
+
add(link: PeerLink): void;
|
|
66
|
+
/** Register a callback fired whenever the current match changes (incl. → idle). */
|
|
67
|
+
onMatch(cb: (link: PeerLink | undefined) => void): void;
|
|
68
|
+
/**
|
|
69
|
+
* Register a callback fired for each message from the CURRENT peer — including
|
|
70
|
+
* buffered messages flushed when a peer becomes current. `from` is the sender's
|
|
71
|
+
* handle. Registered on the pairing (not per-link), so callers NEVER bind
|
|
72
|
+
* `link.onMessage` themselves.
|
|
73
|
+
*/
|
|
74
|
+
onMessage(cb: (from: string, m: IncomingMessage) => void): void;
|
|
75
|
+
/**
|
|
76
|
+
* Register a callback fired when a NON-current (queued) peer sends a message —
|
|
77
|
+
* it is buffered, not dropped. `queued` is that peer's current buffered count.
|
|
78
|
+
*/
|
|
79
|
+
onQueued(cb: (from: string, queued: number) => void): void;
|
|
80
|
+
/**
|
|
81
|
+
* Pull every inbound message received since the last drain (current + queued),
|
|
82
|
+
* tagged with sender; empties the buffer. For PULL consumers (MCP live_poll).
|
|
83
|
+
*/
|
|
84
|
+
drain(): PairingMessage[];
|
|
85
|
+
/** Snapshot of the currently-queued (non-current) links. */
|
|
86
|
+
queued(): PeerLink[];
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* vibedating MCP server (stdio) — full agent-native tool surface.
|
|
91
|
+
*
|
|
92
|
+
* An agent drives vibedate ENTIRELY via tool calls — no interactive terminal.
|
|
93
|
+
* This is the fix for agents whose interactive `live` sessions time out.
|
|
94
|
+
*
|
|
95
|
+
* Tools (every response is structured JSON with `{ ok, ... }` / `{ ok:false, error }`):
|
|
96
|
+
*
|
|
97
|
+
* READ/STATE
|
|
98
|
+
* get_profile · connect · matches · handle_get · handle_set · blocklist
|
|
99
|
+
* block · unblock · daemon_status
|
|
100
|
+
*
|
|
101
|
+
* DISCOVERY
|
|
102
|
+
* discover · who · find
|
|
103
|
+
*
|
|
104
|
+
* LIVE CHAT (stateful in-process session — poll, don't block a TTY)
|
|
105
|
+
* live_start · live_peers · live_send · live_poll · live_open · live_next · live_stop
|
|
106
|
+
*
|
|
107
|
+
* ROOMS
|
|
108
|
+
* room_join · room_send · room_poll · room_who · room_leave
|
|
109
|
+
*
|
|
110
|
+
* MEDIA
|
|
111
|
+
* media_send
|
|
112
|
+
*
|
|
113
|
+
* Legacy aliases `profile` / `matches` remain registered for back-compat.
|
|
114
|
+
*
|
|
115
|
+
* AEGIS: peer text is UNTRUSTED display data — sanitized before return, never
|
|
116
|
+
* executed, never fed to a shell.
|
|
117
|
+
*/
|
|
118
|
+
|
|
119
|
+
/** Injectable hooks so tests can drive live/room without the real DHT. */
|
|
120
|
+
interface McpLiveHooks {
|
|
121
|
+
/** Build (or inject) the pairing object used by live_* tools. */
|
|
122
|
+
createPairing?: () => LivePairing;
|
|
123
|
+
/**
|
|
124
|
+
* Optional override for live_start's discovery join. When provided, the
|
|
125
|
+
* real DHT is never touched — the hook receives the pairing and is free to
|
|
126
|
+
* inject fake links. Return a close handle (or nothing).
|
|
127
|
+
*/
|
|
128
|
+
startLive?: (args: {
|
|
129
|
+
pairing: LivePairing;
|
|
130
|
+
hello: PeerHello;
|
|
131
|
+
any: boolean;
|
|
132
|
+
to: string | null;
|
|
133
|
+
}) => Promise<{
|
|
134
|
+
close: () => Promise<void>;
|
|
135
|
+
} | void> | {
|
|
136
|
+
close: () => Promise<void>;
|
|
137
|
+
} | void;
|
|
138
|
+
/** Optional override for room_join. */
|
|
139
|
+
startRoom?: (args: {
|
|
140
|
+
hello: PeerHello;
|
|
141
|
+
room: string;
|
|
142
|
+
}) => Promise<RoomSession> | RoomSession;
|
|
143
|
+
}
|
|
144
|
+
interface McpSessionState {
|
|
145
|
+
discovery: DiscoverySession | null;
|
|
146
|
+
/** Target handle sought by `find` (canonical), or null when idle. */
|
|
147
|
+
findTarget: string | null;
|
|
148
|
+
findSeen: boolean;
|
|
149
|
+
live: {
|
|
150
|
+
pairing: LivePairing;
|
|
151
|
+
session: DiscoverySession | {
|
|
152
|
+
close: () => Promise<void>;
|
|
153
|
+
} | null;
|
|
154
|
+
any: boolean;
|
|
155
|
+
to: string | null;
|
|
156
|
+
} | null;
|
|
157
|
+
room: {
|
|
158
|
+
session: RoomSession;
|
|
159
|
+
/** Draining buffer of inbound room messages since last room_poll. */
|
|
160
|
+
buffer: RoomMessage[];
|
|
161
|
+
name: string;
|
|
162
|
+
} | null;
|
|
163
|
+
}
|
|
164
|
+
/** Construct a fresh in-process session bag (also used by tests to reset). */
|
|
165
|
+
declare function createSessionState(): McpSessionState;
|
|
166
|
+
/**
|
|
167
|
+
* Create the vibedating MCP server with every agent-native tool registered.
|
|
168
|
+
* Pure registration — no transport attached. Callers (or {@link runMcp}) attach
|
|
169
|
+
* a transport. Tests drive this with InMemoryTransport.
|
|
170
|
+
*
|
|
171
|
+
* Pass a session bag to share/reset state across calls; omit to use a private
|
|
172
|
+
* fresh bag for this server instance.
|
|
173
|
+
*/
|
|
174
|
+
declare function createMcpServer(opts?: {
|
|
175
|
+
readonly session?: McpSessionState;
|
|
176
|
+
readonly hooks?: McpLiveHooks;
|
|
177
|
+
readonly version?: string;
|
|
178
|
+
}): McpServer;
|
|
1
179
|
/**
|
|
2
180
|
* Start the stdio MCP server. Resolves once connected to the transport; the
|
|
3
181
|
* transport then keeps the process alive for the host agent to call tools.
|
|
4
182
|
*/
|
|
5
183
|
declare function runMcp(): Promise<void>;
|
|
184
|
+
/** Full set of tool names the server exposes — kept in sync for tests. */
|
|
185
|
+
declare const MCP_TOOL_NAMES: readonly ["get_profile", "profile", "connect", "matches", "handle_get", "handle_set", "blocklist", "block", "unblock", "daemon_status", "discover", "who", "find", "live_start", "live_peers", "live_send", "live_poll", "live_open", "live_next", "live_stop", "room_join", "room_send", "room_poll", "room_who", "room_leave", "media_send"];
|
|
6
186
|
|
|
7
|
-
export { runMcp };
|
|
187
|
+
export { MCP_TOOL_NAMES, type McpLiveHooks, type McpSessionState, createMcpServer, createSessionState, runMcp };
|
package/dist/mcp.js
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
|
+
MCP_TOOL_NAMES,
|
|
3
|
+
createMcpServer,
|
|
4
|
+
createSessionState,
|
|
2
5
|
runMcp
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import "./chunk-
|
|
6
|
+
} from "./chunk-JBADPOR5.js";
|
|
7
|
+
import "./chunk-S7MO5OSO.js";
|
|
5
8
|
export {
|
|
9
|
+
MCP_TOOL_NAMES,
|
|
10
|
+
createMcpServer,
|
|
11
|
+
createSessionState,
|
|
6
12
|
runMcp
|
|
7
13
|
};
|