vibedate 0.8.3 → 0.8.6
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-XUWNLMOY.js → chunk-NSSNOQJG.js} +31 -4
- package/dist/{chunk-3FJDVN72.js → chunk-ZU5UPYSF.js} +247 -49
- package/dist/cli.js +70 -24
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/mcp-bin.d.ts +1 -0
- package/dist/mcp-bin.js +14 -0
- package/dist/mcp.d.ts +1 -31
- package/dist/mcp.js +2 -2
- package/dist/{room-DpZhNvuu.d.ts → room-DgdlQcRD.d.ts} +14 -2
- package/package.json +8 -5
|
@@ -29,9 +29,10 @@ import {
|
|
|
29
29
|
signHelloClaims,
|
|
30
30
|
startDiscovery,
|
|
31
31
|
startRoom
|
|
32
|
-
} from "./chunk-
|
|
32
|
+
} from "./chunk-ZU5UPYSF.js";
|
|
33
33
|
|
|
34
34
|
// src/mcp.ts
|
|
35
|
+
import { createRequire } from "module";
|
|
35
36
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
36
37
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
37
38
|
|
|
@@ -14890,6 +14891,7 @@ function uninstallDaemonService(opts) {
|
|
|
14890
14891
|
|
|
14891
14892
|
// src/pairing.ts
|
|
14892
14893
|
var MAX_BUFFERED = 100;
|
|
14894
|
+
var MAX_QUEUE = 100;
|
|
14893
14895
|
function createPairing() {
|
|
14894
14896
|
const queue = [];
|
|
14895
14897
|
const matchCbs = /* @__PURE__ */ new Set();
|
|
@@ -14923,7 +14925,14 @@ function createPairing() {
|
|
|
14923
14925
|
link.onClose(() => {
|
|
14924
14926
|
buffers.delete(link);
|
|
14925
14927
|
if (current === link) {
|
|
14926
|
-
|
|
14928
|
+
let nextUp;
|
|
14929
|
+
while (queue.length > 0) {
|
|
14930
|
+
const cand = queue.shift();
|
|
14931
|
+
if (!cand.closed) {
|
|
14932
|
+
nextUp = cand;
|
|
14933
|
+
break;
|
|
14934
|
+
}
|
|
14935
|
+
}
|
|
14927
14936
|
setCurrent(nextUp);
|
|
14928
14937
|
} else {
|
|
14929
14938
|
const idx = queue.indexOf(link);
|
|
@@ -14980,6 +14989,10 @@ function createPairing() {
|
|
|
14980
14989
|
setCurrent(link);
|
|
14981
14990
|
} else {
|
|
14982
14991
|
queue.push(link);
|
|
14992
|
+
if (queue.length > MAX_QUEUE) {
|
|
14993
|
+
const oldest = queue.shift();
|
|
14994
|
+
oldest.close();
|
|
14995
|
+
}
|
|
14983
14996
|
}
|
|
14984
14997
|
},
|
|
14985
14998
|
next() {
|
|
@@ -14987,7 +15000,14 @@ function createPairing() {
|
|
|
14987
15000
|
current.close();
|
|
14988
15001
|
current = void 0;
|
|
14989
15002
|
}
|
|
14990
|
-
|
|
15003
|
+
let nextUp;
|
|
15004
|
+
while (queue.length > 0) {
|
|
15005
|
+
const cand = queue.shift();
|
|
15006
|
+
if (!cand.closed) {
|
|
15007
|
+
nextUp = cand;
|
|
15008
|
+
break;
|
|
15009
|
+
}
|
|
15010
|
+
}
|
|
14991
15011
|
setCurrent(nextUp);
|
|
14992
15012
|
return current;
|
|
14993
15013
|
},
|
|
@@ -15027,7 +15047,14 @@ function textBlock(text) {
|
|
|
15027
15047
|
function jsonResult(value) {
|
|
15028
15048
|
return { content: [textBlock(JSON.stringify(value, null, 2))] };
|
|
15029
15049
|
}
|
|
15030
|
-
var VERSION =
|
|
15050
|
+
var VERSION = (() => {
|
|
15051
|
+
try {
|
|
15052
|
+
const require2 = createRequire(import.meta.url);
|
|
15053
|
+
return require2("../package.json").version ?? "0.0.0";
|
|
15054
|
+
} catch {
|
|
15055
|
+
return "0.0.0";
|
|
15056
|
+
}
|
|
15057
|
+
})();
|
|
15031
15058
|
function discoveryScope(myLeague, any2) {
|
|
15032
15059
|
const names = any2 ? allLeagueNames() : leaguesWithin(myLeague, 1);
|
|
15033
15060
|
const ordered = [myLeague, ...names.filter((n) => n !== myLeague)];
|
|
@@ -18,13 +18,18 @@ import { makeEvent, notify as vibeCoreNotify } from "@pooriaarab/vibe-core";
|
|
|
18
18
|
var MAX_TEXT_LEN = 4e3;
|
|
19
19
|
var MAX_ID_LEN = 64;
|
|
20
20
|
var MAX_B64_CHUNK_LEN = 16 * 1024;
|
|
21
|
+
var MAX_BINARY_CHUNK_BYTES = 64 * 1024;
|
|
21
22
|
var MAX_MEDIA_SIZE = 25 * 1024 * 1024;
|
|
22
23
|
var MAX_MIME_LEN = 128;
|
|
23
24
|
var MAX_NAME_LEN = 256;
|
|
24
25
|
var MAX_SDP_LEN = 64 * 1024;
|
|
25
26
|
var MAX_CANDIDATE_LEN = 4 * 1024;
|
|
26
27
|
var MAX_FRAME_LEN = Math.max(MAX_B64_CHUNK_LEN, 2 * MAX_SDP_LEN) + 2048;
|
|
28
|
+
var BIN_MEDIA_CHUNK_TAG = 1;
|
|
27
29
|
function serializeFrame(f) {
|
|
30
|
+
if (f.t === "media-chunk" && "data" in f) {
|
|
31
|
+
throw new Error("binary media-chunk must use serializeBinaryMediaChunk, not serializeFrame");
|
|
32
|
+
}
|
|
28
33
|
return JSON.stringify(f);
|
|
29
34
|
}
|
|
30
35
|
function parseFrame(raw) {
|
|
@@ -123,6 +128,107 @@ function parseFrame(raw) {
|
|
|
123
128
|
return null;
|
|
124
129
|
}
|
|
125
130
|
}
|
|
131
|
+
function serializeBinaryMediaChunk(parts) {
|
|
132
|
+
const { id, seq, data } = parts;
|
|
133
|
+
if (typeof id !== "string" || id.length === 0 || id.length > MAX_ID_LEN) {
|
|
134
|
+
throw new Error(`invalid media chunk id length: ${typeof id === "string" ? id.length : "?"}`);
|
|
135
|
+
}
|
|
136
|
+
if (typeof seq !== "number" || !Number.isFinite(seq) || !Number.isInteger(seq) || seq < 0) {
|
|
137
|
+
throw new Error(`invalid media chunk seq: ${String(seq)}`);
|
|
138
|
+
}
|
|
139
|
+
if (!Buffer.isBuffer(data) || data.length === 0 || data.length > MAX_BINARY_CHUNK_BYTES) {
|
|
140
|
+
throw new Error(
|
|
141
|
+
`invalid media chunk payload length: ${Buffer.isBuffer(data) ? data.length : "n/a"}`
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
const hdr = Buffer.from(JSON.stringify({ id, seq }), "utf8");
|
|
145
|
+
if (hdr.length > 65535) throw new Error(`media chunk header too large: ${hdr.length}`);
|
|
146
|
+
const out = Buffer.allocUnsafe(1 + 2 + 4 + hdr.length + data.length);
|
|
147
|
+
let o = 0;
|
|
148
|
+
out[o++] = BIN_MEDIA_CHUNK_TAG;
|
|
149
|
+
out.writeUInt16BE(hdr.length, o);
|
|
150
|
+
o += 2;
|
|
151
|
+
out.writeUInt32BE(data.length, o);
|
|
152
|
+
o += 4;
|
|
153
|
+
hdr.copy(out, o);
|
|
154
|
+
o += hdr.length;
|
|
155
|
+
data.copy(out, o);
|
|
156
|
+
return out;
|
|
157
|
+
}
|
|
158
|
+
function tryParseBinaryMediaChunk(buf) {
|
|
159
|
+
if (buf.length === 0) return { frame: null, bytesConsumed: 0 };
|
|
160
|
+
if (buf[0] !== BIN_MEDIA_CHUNK_TAG) return { frame: null, bytesConsumed: 0 };
|
|
161
|
+
if (buf.length < 7) return { frame: null, bytesConsumed: 0 };
|
|
162
|
+
const hdrLen = buf.readUInt16BE(1);
|
|
163
|
+
const payloadLen = buf.readUInt32BE(3);
|
|
164
|
+
const MAX_HDR_LEN = 512;
|
|
165
|
+
if (hdrLen === 0 || hdrLen > MAX_HDR_LEN || payloadLen === 0 || payloadLen > MAX_BINARY_CHUNK_BYTES) {
|
|
166
|
+
return { frame: null, bytesConsumed: 1 };
|
|
167
|
+
}
|
|
168
|
+
const total = 7 + hdrLen + payloadLen;
|
|
169
|
+
if (buf.length < total) return { frame: null, bytesConsumed: 0 };
|
|
170
|
+
const hdrBuf = buf.subarray(7, 7 + hdrLen);
|
|
171
|
+
let hdrObj;
|
|
172
|
+
try {
|
|
173
|
+
hdrObj = JSON.parse(hdrBuf.toString("utf8"));
|
|
174
|
+
} catch {
|
|
175
|
+
return { frame: null, bytesConsumed: 1 };
|
|
176
|
+
}
|
|
177
|
+
if (typeof hdrObj !== "object" || hdrObj === null || Array.isArray(hdrObj)) {
|
|
178
|
+
return { frame: null, bytesConsumed: 1 };
|
|
179
|
+
}
|
|
180
|
+
const r = hdrObj;
|
|
181
|
+
const id = r["id"];
|
|
182
|
+
const seq = r["seq"];
|
|
183
|
+
if (typeof id !== "string" || id.length === 0 || id.length > MAX_ID_LEN) {
|
|
184
|
+
return { frame: null, bytesConsumed: 1 };
|
|
185
|
+
}
|
|
186
|
+
if (typeof seq !== "number" || !Number.isFinite(seq) || !Number.isInteger(seq) || seq < 0) {
|
|
187
|
+
return { frame: null, bytesConsumed: 1 };
|
|
188
|
+
}
|
|
189
|
+
const data = Buffer.from(buf.subarray(7 + hdrLen, 7 + hdrLen + payloadLen));
|
|
190
|
+
return {
|
|
191
|
+
frame: { t: "media-chunk", id, seq, data },
|
|
192
|
+
bytesConsumed: total
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
function pullFramesFromBuffer(buf) {
|
|
196
|
+
const frames = [];
|
|
197
|
+
let offset = 0;
|
|
198
|
+
while (offset < buf.length) {
|
|
199
|
+
const first = buf[offset];
|
|
200
|
+
if (first === BIN_MEDIA_CHUNK_TAG) {
|
|
201
|
+
const slice = offset === 0 ? buf : buf.subarray(offset);
|
|
202
|
+
const { frame: frame2, bytesConsumed } = tryParseBinaryMediaChunk(slice);
|
|
203
|
+
if (bytesConsumed === 0) {
|
|
204
|
+
break;
|
|
205
|
+
}
|
|
206
|
+
if (frame2 !== null) frames.push(frame2);
|
|
207
|
+
offset += bytesConsumed;
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
const nlRel = buf.indexOf(10, offset);
|
|
211
|
+
if (nlRel < 0) {
|
|
212
|
+
break;
|
|
213
|
+
}
|
|
214
|
+
const lineBuf = buf.subarray(offset, nlRel);
|
|
215
|
+
offset = nlRel + 1;
|
|
216
|
+
if (lineBuf.length === 0) continue;
|
|
217
|
+
let onlyWs = true;
|
|
218
|
+
for (let i = 0; i < lineBuf.length; i++) {
|
|
219
|
+
const c = lineBuf[i];
|
|
220
|
+
if (c !== 32 && c !== 9 && c !== 13) {
|
|
221
|
+
onlyWs = false;
|
|
222
|
+
break;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
if (onlyWs) continue;
|
|
226
|
+
const frame = parseFrame(lineBuf);
|
|
227
|
+
if (frame !== null) frames.push(frame);
|
|
228
|
+
}
|
|
229
|
+
const rest = offset === 0 ? buf : buf.subarray(offset);
|
|
230
|
+
return { frames, rest: rest.length === 0 ? Buffer.alloc(0) : Buffer.from(rest) };
|
|
231
|
+
}
|
|
126
232
|
|
|
127
233
|
// src/identity.ts
|
|
128
234
|
import {
|
|
@@ -137,14 +243,20 @@ import { chmodSync, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writ
|
|
|
137
243
|
import path2 from "path";
|
|
138
244
|
|
|
139
245
|
// src/state.ts
|
|
140
|
-
import { mkdirSync, readFileSync, rmSync, writeFileSync } from "fs";
|
|
246
|
+
import { mkdirSync, readFileSync, rmSync, renameSync, writeFileSync } from "fs";
|
|
141
247
|
import os from "os";
|
|
142
248
|
import path from "path";
|
|
143
249
|
import { createConsentLedger } from "@pooriaarab/vibe-core";
|
|
250
|
+
function writeJsonAtomic(filePath, obj) {
|
|
251
|
+
const tmpPath = `${filePath}.tmp`;
|
|
252
|
+
writeFileSync(tmpPath, JSON.stringify(obj, null, 2) + "\n", "utf8");
|
|
253
|
+
renameSync(tmpPath, filePath);
|
|
254
|
+
}
|
|
144
255
|
var CONSENT_SCOPE = "share:league";
|
|
145
256
|
var LIVE_CONSENT_SCOPE = "share:live";
|
|
146
257
|
function defaultStateDir() {
|
|
147
|
-
|
|
258
|
+
const home = process.env["VIBEDATE_HOME"];
|
|
259
|
+
return home && home.length > 0 ? home : path.join(os.homedir(), ".vibedating");
|
|
148
260
|
}
|
|
149
261
|
var FileConsentStore = class {
|
|
150
262
|
constructor(file) {
|
|
@@ -162,7 +274,7 @@ var FileConsentStore = class {
|
|
|
162
274
|
}
|
|
163
275
|
save(grants) {
|
|
164
276
|
mkdirSync(path.dirname(this.file), { recursive: true });
|
|
165
|
-
|
|
277
|
+
writeJsonAtomic(this.file, { grants });
|
|
166
278
|
}
|
|
167
279
|
};
|
|
168
280
|
function createLedger(dir = defaultStateDir()) {
|
|
@@ -184,7 +296,7 @@ function connectProfile(snapshot, handle, dir = defaultStateDir()) {
|
|
|
184
296
|
connectedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
185
297
|
};
|
|
186
298
|
mkdirSync(dir, { recursive: true });
|
|
187
|
-
|
|
299
|
+
writeJsonAtomic(profilePath(dir), state);
|
|
188
300
|
return state;
|
|
189
301
|
}
|
|
190
302
|
function loadProfile(dir = defaultStateDir()) {
|
|
@@ -245,14 +357,10 @@ function saveHandle(handle, dir = defaultStateDir()) {
|
|
|
245
357
|
);
|
|
246
358
|
}
|
|
247
359
|
mkdirSync(dir, { recursive: true });
|
|
248
|
-
|
|
360
|
+
writeJsonAtomic(handleFilePath(dir), { handle: canonical });
|
|
249
361
|
const existing = loadProfile(dir);
|
|
250
362
|
if (existing !== null) {
|
|
251
|
-
|
|
252
|
-
profilePath(dir),
|
|
253
|
-
JSON.stringify({ ...existing, handle: canonical }, null, 2) + "\n",
|
|
254
|
-
"utf8"
|
|
255
|
-
);
|
|
363
|
+
writeJsonAtomic(profilePath(dir), { ...existing, handle: canonical });
|
|
256
364
|
}
|
|
257
365
|
return canonical;
|
|
258
366
|
}
|
|
@@ -270,7 +378,7 @@ function blocklistPath(dir) {
|
|
|
270
378
|
}
|
|
271
379
|
function persistBlocklist(blocked, dir) {
|
|
272
380
|
mkdirSync(dir, { recursive: true });
|
|
273
|
-
|
|
381
|
+
writeJsonAtomic(blocklistPath(dir), { blocked });
|
|
274
382
|
}
|
|
275
383
|
function loadBlocklist(dir = defaultStateDir()) {
|
|
276
384
|
try {
|
|
@@ -442,7 +550,7 @@ import { readFile } from "fs/promises";
|
|
|
442
550
|
import { writeFileSync as writeFileSync3 } from "fs";
|
|
443
551
|
import os2 from "os";
|
|
444
552
|
import path3 from "path";
|
|
445
|
-
var DEFAULT_CHUNK_BYTES =
|
|
553
|
+
var DEFAULT_CHUNK_BYTES = MAX_BINARY_CHUNK_BYTES;
|
|
446
554
|
var MIME_BY_EXT = {
|
|
447
555
|
".png": "image/png",
|
|
448
556
|
".jpg": "image/jpeg",
|
|
@@ -455,11 +563,32 @@ var MIME_BY_EXT = {
|
|
|
455
563
|
function inferMime(name) {
|
|
456
564
|
return MIME_BY_EXT[path3.extname(name).toLowerCase()] ?? "application/octet-stream";
|
|
457
565
|
}
|
|
458
|
-
function
|
|
459
|
-
return new Promise((resolve) => {
|
|
460
|
-
const ok = socket.write(
|
|
566
|
+
function writeBytes(socket, data) {
|
|
567
|
+
return new Promise((resolve, reject) => {
|
|
568
|
+
const ok = socket.write(data);
|
|
461
569
|
if (ok) resolve();
|
|
462
|
-
else
|
|
570
|
+
else {
|
|
571
|
+
const onDrain = () => {
|
|
572
|
+
cleanup();
|
|
573
|
+
resolve();
|
|
574
|
+
};
|
|
575
|
+
const onClose = () => {
|
|
576
|
+
cleanup();
|
|
577
|
+
reject(new Error("Socket closed before drain"));
|
|
578
|
+
};
|
|
579
|
+
const onError = (err) => {
|
|
580
|
+
cleanup();
|
|
581
|
+
reject(err);
|
|
582
|
+
};
|
|
583
|
+
const cleanup = () => {
|
|
584
|
+
socket.removeListener("drain", onDrain);
|
|
585
|
+
socket.removeListener("close", onClose);
|
|
586
|
+
socket.removeListener("error", onError);
|
|
587
|
+
};
|
|
588
|
+
socket.once("drain", onDrain);
|
|
589
|
+
socket.once("close", onClose);
|
|
590
|
+
socket.once("error", onError);
|
|
591
|
+
}
|
|
463
592
|
});
|
|
464
593
|
}
|
|
465
594
|
async function sendMedia(opts) {
|
|
@@ -471,15 +600,23 @@ async function sendMedia(opts) {
|
|
|
471
600
|
}
|
|
472
601
|
if (mime.length > MAX_MIME_LEN) throw new Error(`mime too long: ${mime.length} > ${MAX_MIME_LEN}`);
|
|
473
602
|
if (name.length > MAX_NAME_LEN) throw new Error(`name too long: ${name.length} > ${MAX_NAME_LEN}`);
|
|
474
|
-
await
|
|
475
|
-
const chunkBytes =
|
|
603
|
+
await writeBytes(socket, serializeFrame({ t: "media-start", id, mime, size, name }) + "\n");
|
|
604
|
+
const chunkBytes = Math.min(
|
|
605
|
+
opts.chunkBytes ?? DEFAULT_CHUNK_BYTES,
|
|
606
|
+
opts.legacyJson ? Math.floor(16 * 1024 * 3 / 4) : MAX_BINARY_CHUNK_BYTES
|
|
607
|
+
);
|
|
476
608
|
let seq = 0;
|
|
477
609
|
for (let off = 0; off < size; off += chunkBytes) {
|
|
478
|
-
const
|
|
479
|
-
|
|
610
|
+
const slice = data.subarray(off, off + chunkBytes);
|
|
611
|
+
if (opts.legacyJson) {
|
|
612
|
+
const b64 = slice.toString("base64");
|
|
613
|
+
await writeBytes(socket, serializeFrame({ t: "media-chunk", id, seq, b64 }) + "\n");
|
|
614
|
+
} else {
|
|
615
|
+
await writeBytes(socket, serializeBinaryMediaChunk({ id, seq, data: slice }));
|
|
616
|
+
}
|
|
480
617
|
seq++;
|
|
481
618
|
}
|
|
482
|
-
await
|
|
619
|
+
await writeBytes(socket, serializeFrame({ t: "media-end", id }) + "\n");
|
|
483
620
|
return { id, size };
|
|
484
621
|
}
|
|
485
622
|
async function sendMediaFile(opts) {
|
|
@@ -492,7 +629,8 @@ async function sendMediaFile(opts) {
|
|
|
492
629
|
mime,
|
|
493
630
|
name,
|
|
494
631
|
id: opts.id,
|
|
495
|
-
chunkBytes: opts.chunkBytes
|
|
632
|
+
chunkBytes: opts.chunkBytes,
|
|
633
|
+
legacyJson: opts.legacyJson
|
|
496
634
|
});
|
|
497
635
|
}
|
|
498
636
|
function safeName(id, name) {
|
|
@@ -533,9 +671,20 @@ var MediaReceiver = class {
|
|
|
533
671
|
return;
|
|
534
672
|
}
|
|
535
673
|
let bytes;
|
|
536
|
-
|
|
537
|
-
bytes =
|
|
538
|
-
|
|
674
|
+
if ("data" in frame && Buffer.isBuffer(frame.data)) {
|
|
675
|
+
bytes = frame.data;
|
|
676
|
+
if (bytes.length === 0 || bytes.length > MAX_BINARY_CHUNK_BYTES) {
|
|
677
|
+
this.abort(frame.id);
|
|
678
|
+
return;
|
|
679
|
+
}
|
|
680
|
+
} else if ("b64" in frame && typeof frame.b64 === "string") {
|
|
681
|
+
try {
|
|
682
|
+
bytes = Buffer.from(frame.b64, "base64");
|
|
683
|
+
} catch {
|
|
684
|
+
this.abort(frame.id);
|
|
685
|
+
return;
|
|
686
|
+
}
|
|
687
|
+
} else {
|
|
539
688
|
this.abort(frame.id);
|
|
540
689
|
return;
|
|
541
690
|
}
|
|
@@ -558,7 +707,9 @@ var MediaReceiver = class {
|
|
|
558
707
|
const filePath = path3.join(this.opts.tmpDir ?? os2.tmpdir(), safeName(frame.id, tx.name));
|
|
559
708
|
try {
|
|
560
709
|
writeFileSync3(filePath, buf);
|
|
561
|
-
} catch {
|
|
710
|
+
} catch (err) {
|
|
711
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
712
|
+
this.onMedia({ mime: tx.mime, name: tx.name, path: filePath, size: tx.received, error });
|
|
562
713
|
return;
|
|
563
714
|
}
|
|
564
715
|
this.onMedia({ mime: tx.mime, name: tx.name, path: filePath, size: tx.received });
|
|
@@ -576,7 +727,7 @@ function createPeerLink(socket, hello, initialBuffer = "", linkOpts = {}) {
|
|
|
576
727
|
const mediaCbs = /* @__PURE__ */ new Set();
|
|
577
728
|
const signalCbs = /* @__PURE__ */ new Set();
|
|
578
729
|
const closeCbs = /* @__PURE__ */ new Set();
|
|
579
|
-
let buf = initialBuffer;
|
|
730
|
+
let buf = typeof initialBuffer === "string" ? Buffer.from(initialBuffer, "utf8") : Buffer.from(initialBuffer);
|
|
580
731
|
let closed = false;
|
|
581
732
|
let mediaReceiver;
|
|
582
733
|
const ensureMediaReceiver = () => {
|
|
@@ -624,19 +775,15 @@ function createPeerLink(socket, hello, initialBuffer = "", linkOpts = {}) {
|
|
|
624
775
|
}
|
|
625
776
|
};
|
|
626
777
|
const pump = () => {
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
if (line.trim() === "") continue;
|
|
632
|
-
const frame = parseFrame(line);
|
|
633
|
-
if (frame === null) continue;
|
|
634
|
-
dispatch(frame);
|
|
635
|
-
}
|
|
778
|
+
if (buf.length === 0) return;
|
|
779
|
+
const { frames, rest } = pullFramesFromBuffer(buf);
|
|
780
|
+
buf = rest;
|
|
781
|
+
for (const frame of frames) dispatch(frame);
|
|
636
782
|
};
|
|
637
783
|
pump();
|
|
638
784
|
socket.on("data", (chunk) => {
|
|
639
|
-
|
|
785
|
+
const bytes = typeof chunk === "string" ? Buffer.from(chunk, "utf8") : chunk;
|
|
786
|
+
buf = buf.length === 0 ? Buffer.from(bytes) : Buffer.concat([buf, bytes]);
|
|
640
787
|
pump();
|
|
641
788
|
});
|
|
642
789
|
socket.on("end", () => {
|
|
@@ -659,6 +806,9 @@ function createPeerLink(socket, hello, initialBuffer = "", linkOpts = {}) {
|
|
|
659
806
|
});
|
|
660
807
|
return {
|
|
661
808
|
hello,
|
|
809
|
+
get closed() {
|
|
810
|
+
return closed;
|
|
811
|
+
},
|
|
662
812
|
send(text) {
|
|
663
813
|
if (closed) return;
|
|
664
814
|
const frame = { t: "msg", id: randomUUID2(), text, at: Date.now() };
|
|
@@ -846,7 +996,8 @@ async function startDiscovery(opts) {
|
|
|
846
996
|
const topics = opts.topics ? [...opts.topics] : opts.topic !== void 0 ? [opts.topic] : [leagueTopic(hello.league)];
|
|
847
997
|
const acceptLeague = opts.acceptLeague ?? ((l) => l === hello.league);
|
|
848
998
|
const { default: Hyperswarm } = await import("hyperswarm");
|
|
849
|
-
const
|
|
999
|
+
const bootstrap = opts.bootstrap ?? parseBootstrapEnv(process.env["VIBEDATE_BOOTSTRAP"]);
|
|
1000
|
+
const swarm = new Hyperswarm(bootstrap === void 0 ? {} : { bootstrap });
|
|
850
1001
|
await swarm.dht.fullyBootstrapped();
|
|
851
1002
|
const peers = /* @__PURE__ */ new Map();
|
|
852
1003
|
swarm.on("connection", (socket, info) => {
|
|
@@ -863,15 +1014,18 @@ async function startDiscovery(opts) {
|
|
|
863
1014
|
...hello.sig !== void 0 ? { sig: hello.sig } : {}
|
|
864
1015
|
}) + "\n"
|
|
865
1016
|
);
|
|
866
|
-
let buf =
|
|
1017
|
+
let buf = Buffer.alloc(0);
|
|
867
1018
|
let handedOff = false;
|
|
868
1019
|
const onData = (chunk) => {
|
|
869
1020
|
if (handedOff) return;
|
|
870
|
-
buf
|
|
1021
|
+
buf = buf.length === 0 ? Buffer.from(chunk) : Buffer.concat([buf, chunk]);
|
|
871
1022
|
let nl;
|
|
872
|
-
while ((nl = buf.indexOf(
|
|
873
|
-
|
|
874
|
-
|
|
1023
|
+
while ((nl = buf.indexOf(
|
|
1024
|
+
10
|
|
1025
|
+
/* \n */
|
|
1026
|
+
)) >= 0) {
|
|
1027
|
+
const line = buf.subarray(0, nl).toString("utf8");
|
|
1028
|
+
buf = buf.subarray(nl + 1);
|
|
875
1029
|
if (line.trim() === "") continue;
|
|
876
1030
|
const frame = parseFrame(line);
|
|
877
1031
|
if (frame === null) continue;
|
|
@@ -917,7 +1071,7 @@ async function startDiscovery(opts) {
|
|
|
917
1071
|
});
|
|
918
1072
|
onLink(link);
|
|
919
1073
|
}
|
|
920
|
-
buf =
|
|
1074
|
+
buf = Buffer.alloc(0);
|
|
921
1075
|
return;
|
|
922
1076
|
}
|
|
923
1077
|
};
|
|
@@ -958,6 +1112,21 @@ async function startDiscovery(opts) {
|
|
|
958
1112
|
}
|
|
959
1113
|
};
|
|
960
1114
|
}
|
|
1115
|
+
function parseBootstrapEnv(raw) {
|
|
1116
|
+
if (raw === void 0 || raw.trim() === "") return void 0;
|
|
1117
|
+
const nodes = [];
|
|
1118
|
+
for (const entry of raw.split(",")) {
|
|
1119
|
+
const trimmed = entry.trim();
|
|
1120
|
+
if (trimmed === "") continue;
|
|
1121
|
+
const idx = trimmed.lastIndexOf(":");
|
|
1122
|
+
if (idx <= 0) continue;
|
|
1123
|
+
const host = trimmed.slice(0, idx);
|
|
1124
|
+
const port = Number(trimmed.slice(idx + 1));
|
|
1125
|
+
if (!Number.isInteger(port) || port <= 0 || port > 65535) continue;
|
|
1126
|
+
nodes.push({ host, port });
|
|
1127
|
+
}
|
|
1128
|
+
return nodes.length > 0 ? nodes : void 0;
|
|
1129
|
+
}
|
|
961
1130
|
|
|
962
1131
|
// src/relay.ts
|
|
963
1132
|
import { createHash as createHash2 } from "crypto";
|
|
@@ -1067,6 +1236,14 @@ async function createNostrRelayLink(opts) {
|
|
|
1067
1236
|
return;
|
|
1068
1237
|
}
|
|
1069
1238
|
peerNostrPubkey = event.pubkey;
|
|
1239
|
+
try {
|
|
1240
|
+
const parsed = JSON.parse(plaintext);
|
|
1241
|
+
if (parsed && typeof parsed === "object" && "__vibe_rtc" in parsed) {
|
|
1242
|
+
for (const cb of signalCbs) cb(parsed.__vibe_rtc);
|
|
1243
|
+
return;
|
|
1244
|
+
}
|
|
1245
|
+
} catch {
|
|
1246
|
+
}
|
|
1070
1247
|
const msg = { id: event.id, text: plaintext, at: event.created_at * 1e3 };
|
|
1071
1248
|
for (const cb of messageCbs) cb(msg);
|
|
1072
1249
|
return;
|
|
@@ -1075,6 +1252,9 @@ async function createNostrRelayLink(opts) {
|
|
|
1075
1252
|
publishPresence();
|
|
1076
1253
|
return {
|
|
1077
1254
|
hello,
|
|
1255
|
+
get closed() {
|
|
1256
|
+
return closed;
|
|
1257
|
+
},
|
|
1078
1258
|
send(text) {
|
|
1079
1259
|
if (closed) return;
|
|
1080
1260
|
if (peerNostrPubkey === void 0) {
|
|
@@ -1088,9 +1268,14 @@ async function createNostrRelayLink(opts) {
|
|
|
1088
1268
|
async sendMedia() {
|
|
1089
1269
|
return { id: "", size: 0 };
|
|
1090
1270
|
},
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1271
|
+
sendSignal(frame) {
|
|
1272
|
+
if (closed) return;
|
|
1273
|
+
const payload = JSON.stringify({ __vibe_rtc: frame });
|
|
1274
|
+
if (peerNostrPubkey === void 0) {
|
|
1275
|
+
pending.push(payload);
|
|
1276
|
+
return;
|
|
1277
|
+
}
|
|
1278
|
+
sendEncrypted(payload, peerNostrPubkey);
|
|
1094
1279
|
},
|
|
1095
1280
|
onMessage(cb) {
|
|
1096
1281
|
messageCbs.add(cb);
|
|
@@ -1163,6 +1348,7 @@ var ROOM_TOPIC_PREFIX = "vibedate-room:";
|
|
|
1163
1348
|
function roomTopic(name) {
|
|
1164
1349
|
return createHash3("sha256").update(`${ROOM_TOPIC_PREFIX}${name}`, "utf8").digest();
|
|
1165
1350
|
}
|
|
1351
|
+
var MAX_ROOM = 32;
|
|
1166
1352
|
async function startRoom(opts) {
|
|
1167
1353
|
const topic = roomTopic(opts.room);
|
|
1168
1354
|
const entries = /* @__PURE__ */ new Map();
|
|
@@ -1186,7 +1372,19 @@ async function startRoom(opts) {
|
|
|
1186
1372
|
...opts.stateDir === void 0 ? {} : { stateDir: opts.stateDir },
|
|
1187
1373
|
...opts.notify === void 0 ? {} : { notify: opts.notify },
|
|
1188
1374
|
onLink: (link) => {
|
|
1375
|
+
const isSelf = link.hello.pubkey !== void 0 && opts.hello.pubkey !== void 0 ? link.hello.pubkey === opts.hello.pubkey : link.hello.handle === opts.hello.handle;
|
|
1376
|
+
if (isSelf) {
|
|
1377
|
+
link.close();
|
|
1378
|
+
return;
|
|
1379
|
+
}
|
|
1189
1380
|
const handle = link.hello.handle;
|
|
1381
|
+
if (!entries.has(handle) && entries.size >= MAX_ROOM) {
|
|
1382
|
+
link.send(`Room is full (${MAX_ROOM} members max). Try again later.`);
|
|
1383
|
+
link.close();
|
|
1384
|
+
return;
|
|
1385
|
+
}
|
|
1386
|
+
const cur = entries.get(handle);
|
|
1387
|
+
if (cur) cur.link.close();
|
|
1190
1388
|
entries.set(handle, { hello: link.hello, link });
|
|
1191
1389
|
memberHellos.set(handle, link.hello);
|
|
1192
1390
|
fireRoster();
|
|
@@ -1197,8 +1395,8 @@ async function startRoom(opts) {
|
|
|
1197
1395
|
for (const cb of signalCbs) cb(handle, frame);
|
|
1198
1396
|
});
|
|
1199
1397
|
link.onClose(() => {
|
|
1200
|
-
const
|
|
1201
|
-
if (
|
|
1398
|
+
const cur2 = entries.get(handle);
|
|
1399
|
+
if (cur2 && cur2.link === link) {
|
|
1202
1400
|
entries.delete(handle);
|
|
1203
1401
|
memberHellos.delete(handle);
|
|
1204
1402
|
fireRoster();
|
package/dist/cli.js
CHANGED
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
stopDaemon,
|
|
11
11
|
uninstallDaemonService,
|
|
12
12
|
writeDaemonState
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-NSSNOQJG.js";
|
|
14
14
|
import {
|
|
15
15
|
CANDIDATES,
|
|
16
16
|
LIVE_NOTICE,
|
|
@@ -46,7 +46,7 @@ import {
|
|
|
46
46
|
signHelloClaims,
|
|
47
47
|
startDiscovery,
|
|
48
48
|
startRoom
|
|
49
|
-
} from "./chunk-
|
|
49
|
+
} from "./chunk-ZU5UPYSF.js";
|
|
50
50
|
|
|
51
51
|
// src/cli.ts
|
|
52
52
|
import readline from "readline";
|
|
@@ -1389,9 +1389,8 @@ var webAppHtml = `<!DOCTYPE html>
|
|
|
1389
1389
|
var idleIdx = 0;
|
|
1390
1390
|
|
|
1391
1391
|
function rtcConfig(){
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
return { iceServers: [{ urls: "stun:stun.l.google.com:19302" }] };
|
|
1392
|
+
var extra = (window.__VIBE_ICE__ && window.__VIBE_ICE__.iceServers) || [];
|
|
1393
|
+
return { iceServers: [{ urls: "stun:stun.l.google.com:19302" }].concat(extra) };
|
|
1395
1394
|
}
|
|
1396
1395
|
function sleep(ms){ return new Promise(function(r){ setTimeout(r, ms); }); }
|
|
1397
1396
|
|
|
@@ -2228,10 +2227,11 @@ function createLiveBridge() {
|
|
|
2228
2227
|
link.onMedia((m) => {
|
|
2229
2228
|
const mb = boxes.get(handle2);
|
|
2230
2229
|
if (!mb) {
|
|
2231
|
-
fs.unlink(m.path, () => {
|
|
2230
|
+
if (!m.error) fs.unlink(m.path, () => {
|
|
2232
2231
|
});
|
|
2233
2232
|
return;
|
|
2234
2233
|
}
|
|
2234
|
+
if (m.error) return;
|
|
2235
2235
|
mb.media.push({ ...m, name: sanitizePeerText(m.name), mime: sanitizePeerText(m.mime) });
|
|
2236
2236
|
if (mb.media.length > MAX_QUEUED_MESSAGES) {
|
|
2237
2237
|
const dropped = mb.media.splice(0, mb.media.length - MAX_QUEUED_MESSAGES);
|
|
@@ -2459,7 +2459,18 @@ async function handle(req, res, opts) {
|
|
|
2459
2459
|
const url = new URL(req.url ?? "/", "http://localhost");
|
|
2460
2460
|
const pathname = url.pathname;
|
|
2461
2461
|
if (req.method === "GET" && pathname === "/") {
|
|
2462
|
-
|
|
2462
|
+
const turnUrl = process.env["VIBEDATE_TURN_URL"];
|
|
2463
|
+
let html = webAppHtml;
|
|
2464
|
+
if (turnUrl) {
|
|
2465
|
+
const turnUser = process.env["VIBEDATE_TURN_USER"];
|
|
2466
|
+
const turnCred = process.env["VIBEDATE_TURN_CRED"];
|
|
2467
|
+
const server = { urls: turnUrl };
|
|
2468
|
+
if (turnUser) server.username = turnUser;
|
|
2469
|
+
if (turnCred) server.credential = turnCred;
|
|
2470
|
+
const script = `<script>window.__VIBE_ICE__={iceServers:${JSON.stringify([server]).replace(/</g, "\\u003c")}};</script>`;
|
|
2471
|
+
html = html.replace("<script>", script + "\n<script>");
|
|
2472
|
+
}
|
|
2473
|
+
send(res, 200, "text/html; charset=utf-8", html);
|
|
2463
2474
|
return;
|
|
2464
2475
|
}
|
|
2465
2476
|
if (req.method === "GET" && pathname === "/api/state") {
|
|
@@ -2784,7 +2795,7 @@ async function handle(req, res, opts) {
|
|
|
2784
2795
|
}
|
|
2785
2796
|
|
|
2786
2797
|
// src/cli.ts
|
|
2787
|
-
var VERSION = "0.8.
|
|
2798
|
+
var VERSION = "0.8.6";
|
|
2788
2799
|
function parsePort(raw) {
|
|
2789
2800
|
const n = Number(raw);
|
|
2790
2801
|
if (!Number.isInteger(n) || n < 1 || n > 65535) return void 0;
|
|
@@ -3136,7 +3147,7 @@ async function cmdDiscover(live, any, viaRelay) {
|
|
|
3136
3147
|
);
|
|
3137
3148
|
return 0;
|
|
3138
3149
|
}
|
|
3139
|
-
async function cmdOpen(port, any, room) {
|
|
3150
|
+
async function cmdOpen(port, any, room, viaRelay, to) {
|
|
3140
3151
|
const profile = loadProfile();
|
|
3141
3152
|
let live;
|
|
3142
3153
|
let roomBridge;
|
|
@@ -3167,17 +3178,45 @@ async function cmdOpen(port, any, room) {
|
|
|
3167
3178
|
}).catch(() => {
|
|
3168
3179
|
});
|
|
3169
3180
|
} else if (live) {
|
|
3170
|
-
|
|
3171
|
-
|
|
3172
|
-
|
|
3173
|
-
|
|
3174
|
-
|
|
3175
|
-
|
|
3176
|
-
|
|
3177
|
-
|
|
3178
|
-
|
|
3179
|
-
|
|
3180
|
-
|
|
3181
|
+
if (viaRelay && to !== void 0) {
|
|
3182
|
+
const target = normalizeHandle(to);
|
|
3183
|
+
const peer = target ? loadPeers().find((p) => sameHandle(p.handle, target) && typeof p.pubkey === "string") : void 0;
|
|
3184
|
+
if (peer && peer.pubkey) {
|
|
3185
|
+
const peerPubkey = peer.pubkey;
|
|
3186
|
+
void (async () => {
|
|
3187
|
+
try {
|
|
3188
|
+
const myNostr = await loadOrCreateNostrKey();
|
|
3189
|
+
const transport = await createNostrPoolTransport();
|
|
3190
|
+
const identity = loadOrCreateIdentity();
|
|
3191
|
+
const link = await createNostrRelayLink({
|
|
3192
|
+
myNostr,
|
|
3193
|
+
myEd25519Hex: identity.publicKeyHex,
|
|
3194
|
+
peerEd25519Hex: peerPubkey,
|
|
3195
|
+
hello: peer,
|
|
3196
|
+
transport
|
|
3197
|
+
});
|
|
3198
|
+
live.addLink(link);
|
|
3199
|
+
} catch {
|
|
3200
|
+
}
|
|
3201
|
+
})();
|
|
3202
|
+
} else {
|
|
3203
|
+
process2.stderr.write(`
|
|
3204
|
+
warning: --via-relay peer ${to} not found or lacks identity pubkey.
|
|
3205
|
+
`);
|
|
3206
|
+
}
|
|
3207
|
+
} else {
|
|
3208
|
+
const { topics, acceptLeague } = discoveryScope(profile.league, any);
|
|
3209
|
+
void startDiscovery({
|
|
3210
|
+
hello,
|
|
3211
|
+
topics,
|
|
3212
|
+
acceptLeague,
|
|
3213
|
+
isBlocked: blockedChecker(),
|
|
3214
|
+
onLink: (link) => live.addLink(link)
|
|
3215
|
+
}).then((s) => {
|
|
3216
|
+
session = s;
|
|
3217
|
+
}).catch(() => {
|
|
3218
|
+
});
|
|
3219
|
+
}
|
|
3181
3220
|
}
|
|
3182
3221
|
}
|
|
3183
3222
|
process2.stdout.write(`
|
|
@@ -3390,10 +3429,17 @@ async function cmdLive(dating, any, to, keepAlive, viaRelay) {
|
|
|
3390
3429
|
`);
|
|
3391
3430
|
}
|
|
3392
3431
|
link.onMedia((m) => {
|
|
3393
|
-
|
|
3394
|
-
|
|
3432
|
+
if (m.error) {
|
|
3433
|
+
process2.stdout.write(
|
|
3434
|
+
` \u{1F4CE} <${sanitizePeerText(link.hello.handle)}> sent ${sanitizePeerText(m.name)} \u2014 FAILED to save: ${m.error.message}
|
|
3395
3435
|
`
|
|
3396
|
-
|
|
3436
|
+
);
|
|
3437
|
+
} else {
|
|
3438
|
+
process2.stdout.write(
|
|
3439
|
+
` \u{1F4CE} <${sanitizePeerText(link.hello.handle)}> sent ${sanitizePeerText(m.name)} \u2014 saved to ${m.path}
|
|
3440
|
+
`
|
|
3441
|
+
);
|
|
3442
|
+
}
|
|
3397
3443
|
});
|
|
3398
3444
|
pairing.add(link);
|
|
3399
3445
|
}
|
|
@@ -3884,7 +3930,7 @@ async function main(argv) {
|
|
|
3884
3930
|
case "discover":
|
|
3885
3931
|
return cmdDiscover(parsed.live, parsed.any, parsed.viaRelay);
|
|
3886
3932
|
case "open":
|
|
3887
|
-
return cmdOpen(parsed.port, parsed.any, parsed.room);
|
|
3933
|
+
return cmdOpen(parsed.port, parsed.any, parsed.room, parsed.viaRelay, parsed.to);
|
|
3888
3934
|
case "live":
|
|
3889
3935
|
return cmdLive(parsed.dating, parsed.any, parsed.to, parsed.keepAlive, parsed.viaRelay);
|
|
3890
3936
|
case "find":
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { UsageSnapshot, UsageSource, Harness, HarnessUsageOptions } from '@pooriaarab/vibe-core';
|
|
2
2
|
export { Harness, UsageSnapshot, UsageSource, createConsentLedger } from '@pooriaarab/vibe-core';
|
|
3
|
-
import {
|
|
4
|
-
export {
|
|
3
|
+
import { a as PeerHello, P as PeerLink } from './room-DgdlQcRD.js';
|
|
4
|
+
export { c as DiscoveryOptions, D as DiscoverySession, L as LIVE_NOTICE, d as ROOM_TOPIC_PREFIX, e as RoomMember, b as RoomMessage, f as RoomOptions, R 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-DgdlQcRD.js';
|
|
5
5
|
import { KeyObject } from 'node:crypto';
|
|
6
6
|
|
|
7
7
|
/**
|
package/dist/index.js
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
package/dist/mcp-bin.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
runMcp
|
|
4
|
+
} from "./chunk-NSSNOQJG.js";
|
|
5
|
+
import "./chunk-ZU5UPYSF.js";
|
|
6
|
+
|
|
7
|
+
// src/mcp-bin.ts
|
|
8
|
+
runMcp().catch((err) => {
|
|
9
|
+
process.stderr.write(
|
|
10
|
+
`vibedate-mcp: ${err instanceof Error ? err.stack ?? err.message : String(err)}
|
|
11
|
+
`
|
|
12
|
+
);
|
|
13
|
+
process.exit(1);
|
|
14
|
+
});
|
package/dist/mcp.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
-
import {
|
|
2
|
+
import { P as PeerLink, a as PeerHello, R as RoomSession, D as DiscoverySession, b as RoomMessage } from './room-DgdlQcRD.js';
|
|
3
3
|
import '@pooriaarab/vibe-core';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -86,36 +86,6 @@ interface LivePairing {
|
|
|
86
86
|
queued(): PeerLink[];
|
|
87
87
|
}
|
|
88
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
89
|
/** Injectable hooks so tests can drive live/room without the real DHT. */
|
|
120
90
|
interface McpLiveHooks {
|
|
121
91
|
/** Build (or inject) the pairing object used by live_* tools. */
|
package/dist/mcp.js
CHANGED
|
@@ -24,11 +24,20 @@ type Frame = {
|
|
|
24
24
|
mime: string;
|
|
25
25
|
size: number;
|
|
26
26
|
name: string;
|
|
27
|
-
}
|
|
27
|
+
}
|
|
28
|
+
/** Legacy JSON media-chunk (base64). Prefer binary wire for new sends. */
|
|
29
|
+
| {
|
|
28
30
|
t: 'media-chunk';
|
|
29
31
|
id: string;
|
|
30
32
|
seq: number;
|
|
31
33
|
b64: string;
|
|
34
|
+
}
|
|
35
|
+
/** Binary-path media-chunk: raw bytes, no base64. Produced by the binary framer. */
|
|
36
|
+
| {
|
|
37
|
+
t: 'media-chunk';
|
|
38
|
+
id: string;
|
|
39
|
+
seq: number;
|
|
40
|
+
data: Buffer;
|
|
32
41
|
} | {
|
|
33
42
|
t: 'media-end';
|
|
34
43
|
id: string;
|
|
@@ -56,11 +65,14 @@ interface ReceivedMedia {
|
|
|
56
65
|
/** Temp file path holding the reassembled bytes. */
|
|
57
66
|
readonly path: string;
|
|
58
67
|
readonly size: number;
|
|
68
|
+
readonly error?: Error;
|
|
59
69
|
}
|
|
60
70
|
|
|
61
71
|
interface PeerLink {
|
|
62
72
|
/** The validated identity of the remote peer (from the hello handshake). */
|
|
63
73
|
readonly hello: PeerHello;
|
|
74
|
+
/** Whether the link has been closed. */
|
|
75
|
+
readonly closed: boolean;
|
|
64
76
|
/** Send a line of text as a `msg` frame. */
|
|
65
77
|
send(text: string): void;
|
|
66
78
|
/** Read a file from disk and send it as a chunked media transfer. */
|
|
@@ -317,4 +329,4 @@ interface RoomSession {
|
|
|
317
329
|
*/
|
|
318
330
|
declare function startRoom(opts: RoomOptions): Promise<RoomSession>;
|
|
319
331
|
|
|
320
|
-
export { type
|
|
332
|
+
export { type DiscoverySession as D, LIVE_NOTICE as L, type PeerLink as P, type RoomSession as R, type StoredPeer as S, TOPIC_PREFIX as T, type PeerHello as a, type RoomMessage as b, type DiscoveryOptions as c, ROOM_TOPIC_PREFIX as d, type RoomMember as e, type RoomOptions as f, loadPeers as g, recordPeerMessage as h, roomTopic as i, startDiscovery as j, startRoom as k, leagueTopic as l, parseHandshake as p, recordPeer as r, serializeHandshake as s };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vibedate",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.6",
|
|
4
4
|
"description": "Dating by tokens — matched by usage league across agentic CLIs. Raw usage stays local; only your league is shared. CLI + local web app + MCP. Local-first.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"bin": {
|
|
9
9
|
"vibedate": "./dist/cli.js",
|
|
10
10
|
"vibedating": "./dist/cli.js",
|
|
11
|
-
"vibedate-mcp": "./dist/mcp.js"
|
|
11
|
+
"vibedate-mcp": "./dist/mcp-bin.js"
|
|
12
12
|
},
|
|
13
13
|
"main": "./dist/index.js",
|
|
14
14
|
"types": "./dist/index.d.ts",
|
|
@@ -22,10 +22,12 @@
|
|
|
22
22
|
"dist"
|
|
23
23
|
],
|
|
24
24
|
"scripts": {
|
|
25
|
-
"build": "tsup src/index.ts src/cli.ts src/mcp.ts --format esm --dts --clean",
|
|
25
|
+
"build": "tsup src/index.ts src/cli.ts src/mcp.ts src/mcp-bin.ts --format esm --dts --clean",
|
|
26
26
|
"typecheck": "tsc --noEmit",
|
|
27
|
-
"test": "vitest run",
|
|
28
|
-
"test:watch": "vitest"
|
|
27
|
+
"test": "vitest run --exclude '**/*.harness.test.ts'",
|
|
28
|
+
"test:watch": "vitest",
|
|
29
|
+
"bench": "tsx bench/run.ts",
|
|
30
|
+
"test:harness": "npm run build && vitest run harness.test"
|
|
29
31
|
},
|
|
30
32
|
"dependencies": {
|
|
31
33
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
@@ -37,6 +39,7 @@
|
|
|
37
39
|
"@types/node": "^20.11.0",
|
|
38
40
|
"hyperdht": "^6.33.0",
|
|
39
41
|
"tsup": "^8.0.0",
|
|
42
|
+
"tsx": "^4.23.1",
|
|
40
43
|
"typescript": "^5.4.0",
|
|
41
44
|
"vitest": "^1.6.0",
|
|
42
45
|
"werift": "^0.24.1"
|