vibedate 0.3.0 → 0.4.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/README.md +16 -10
- package/dist/{chunk-VBLFAOV3.js → chunk-43IYNWES.js} +413 -105
- package/dist/{chunk-DMA7MSN7.js → chunk-ZB56ZBAR.js} +2 -2
- package/dist/cli.d.ts +5 -1
- package/dist/cli.js +310 -40
- package/dist/index.d.ts +137 -31
- package/dist/index.js +13 -3
- package/dist/mcp.js +2 -2
- package/package.json +4 -3
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
CANDIDATES,
|
|
3
3
|
loadProfile,
|
|
4
4
|
matches
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-43IYNWES.js";
|
|
6
6
|
|
|
7
7
|
// src/mcp.ts
|
|
8
8
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
@@ -29,7 +29,7 @@ async function runMcp() {
|
|
|
29
29
|
`handle: ${p.handle}`,
|
|
30
30
|
`harness: ${p.harness}`,
|
|
31
31
|
`league: ${p.league} League`,
|
|
32
|
-
`verified: ${p.verified ? "true (
|
|
32
|
+
`verified: ${p.verified ? "true (real local usage)" : "false (self-reported or demo)"}`,
|
|
33
33
|
"privacy: raw token usage is local-only; only the league bucket is shared."
|
|
34
34
|
];
|
|
35
35
|
return { content: [textBlock(lines.join("\n"))] };
|
package/dist/cli.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/** Recognized top-level commands, plus the synthetic help/version. */
|
|
3
|
-
type Command = 'connect' | 'matches' | 'discover' | 'open' | 'live' | 'mcp' | 'help' | 'version' | null;
|
|
3
|
+
type Command = 'connect' | 'matches' | 'discover' | 'open' | 'live' | 'find' | 'handle' | 'block' | 'unblock' | 'blocklist' | 'mcp' | 'help' | 'version' | null;
|
|
4
4
|
interface ParsedArgs {
|
|
5
5
|
readonly command: Command;
|
|
6
6
|
/** Port for `open --port`; undefined means "let the OS pick". */
|
|
@@ -11,6 +11,10 @@ interface ParsedArgs {
|
|
|
11
11
|
readonly dating: boolean;
|
|
12
12
|
/** `discover --any` / `live --any`: match every league (not just ±1). Default false. */
|
|
13
13
|
readonly any: boolean;
|
|
14
|
+
/** Positional argument for `handle`/`find` (e.g. `@name`). */
|
|
15
|
+
readonly arg: string | undefined;
|
|
16
|
+
/** `live --to <@handle>`: targeted match — auto-open that specific peer. */
|
|
17
|
+
readonly to: string | undefined;
|
|
14
18
|
}
|
|
15
19
|
/**
|
|
16
20
|
* Parse argv (the slice AFTER the program name) into a command + options.
|
package/dist/cli.js
CHANGED
|
@@ -1,26 +1,36 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
runMcp
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-ZB56ZBAR.js";
|
|
5
5
|
import {
|
|
6
6
|
CANDIDATES,
|
|
7
7
|
LIVE_NOTICE,
|
|
8
8
|
TOPIC_PREFIX,
|
|
9
|
+
addBlock,
|
|
9
10
|
allLeagueNames,
|
|
10
11
|
canShareLive,
|
|
11
12
|
connectProfile,
|
|
12
13
|
grantLiveConsent,
|
|
14
|
+
isBlocked,
|
|
13
15
|
league,
|
|
14
16
|
leagueIndex,
|
|
15
17
|
leagueTopic,
|
|
16
18
|
leaguesWithin,
|
|
19
|
+
loadBlocklist,
|
|
20
|
+
loadOrCreateIdentity,
|
|
17
21
|
loadPeers,
|
|
18
22
|
loadProfile,
|
|
19
23
|
matches,
|
|
24
|
+
normalizeHandle,
|
|
20
25
|
parseFrame,
|
|
21
26
|
readUsage,
|
|
27
|
+
removeBlock,
|
|
28
|
+
resolveHandle,
|
|
29
|
+
sameHandle,
|
|
30
|
+
saveHandle,
|
|
31
|
+
signHelloClaims,
|
|
22
32
|
startDiscovery
|
|
23
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-43IYNWES.js";
|
|
24
34
|
|
|
25
35
|
// src/cli.ts
|
|
26
36
|
import readline from "readline";
|
|
@@ -1390,21 +1400,45 @@ async function handle(req, res, opts) {
|
|
|
1390
1400
|
}
|
|
1391
1401
|
|
|
1392
1402
|
// src/cli.ts
|
|
1393
|
-
var VERSION = "0.
|
|
1403
|
+
var VERSION = "0.4.0";
|
|
1394
1404
|
function parsePort(raw) {
|
|
1395
1405
|
const n = Number(raw);
|
|
1396
1406
|
if (!Number.isInteger(n) || n < 1 || n > 65535) return void 0;
|
|
1397
1407
|
return n;
|
|
1398
1408
|
}
|
|
1399
1409
|
function parseArgs(argv) {
|
|
1400
|
-
let out = {
|
|
1410
|
+
let out = {
|
|
1411
|
+
command: null,
|
|
1412
|
+
port: void 0,
|
|
1413
|
+
live: false,
|
|
1414
|
+
dating: false,
|
|
1415
|
+
any: false,
|
|
1416
|
+
arg: void 0,
|
|
1417
|
+
to: void 0
|
|
1418
|
+
};
|
|
1401
1419
|
for (let i = 0; i < argv.length; i++) {
|
|
1402
1420
|
const a = argv[i];
|
|
1403
1421
|
if (a === "--version" || a === "-v") {
|
|
1404
|
-
return {
|
|
1422
|
+
return {
|
|
1423
|
+
command: "version",
|
|
1424
|
+
port: void 0,
|
|
1425
|
+
live: false,
|
|
1426
|
+
dating: false,
|
|
1427
|
+
any: false,
|
|
1428
|
+
arg: void 0,
|
|
1429
|
+
to: void 0
|
|
1430
|
+
};
|
|
1405
1431
|
}
|
|
1406
1432
|
if (a === "--help" || a === "-h") {
|
|
1407
|
-
return {
|
|
1433
|
+
return {
|
|
1434
|
+
command: "help",
|
|
1435
|
+
port: void 0,
|
|
1436
|
+
live: false,
|
|
1437
|
+
dating: false,
|
|
1438
|
+
any: false,
|
|
1439
|
+
arg: void 0,
|
|
1440
|
+
to: void 0
|
|
1441
|
+
};
|
|
1408
1442
|
}
|
|
1409
1443
|
if (a === "--live") {
|
|
1410
1444
|
out = { ...out, live: true };
|
|
@@ -1432,10 +1466,24 @@ function parseArgs(argv) {
|
|
|
1432
1466
|
if (p !== void 0) out = { ...out, port: p };
|
|
1433
1467
|
continue;
|
|
1434
1468
|
}
|
|
1469
|
+
if (a === "--to") {
|
|
1470
|
+
const next = argv[i + 1];
|
|
1471
|
+
if (next !== void 0) {
|
|
1472
|
+
out = { ...out, to: next };
|
|
1473
|
+
i++;
|
|
1474
|
+
}
|
|
1475
|
+
continue;
|
|
1476
|
+
}
|
|
1477
|
+
if (a.startsWith("--to=")) {
|
|
1478
|
+
out = { ...out, to: a.slice("--to=".length) };
|
|
1479
|
+
continue;
|
|
1480
|
+
}
|
|
1435
1481
|
if (a.startsWith("-")) continue;
|
|
1436
|
-
const known = a === "connect" || a === "matches" || a === "discover" || a === "open" || a === "live" || a === "mcp" || a === "help" ? a : null;
|
|
1482
|
+
const known = a === "connect" || a === "matches" || a === "discover" || a === "open" || a === "live" || a === "find" || a === "handle" || a === "block" || a === "unblock" || a === "blocklist" || a === "mcp" || a === "help" ? a : null;
|
|
1437
1483
|
if (known !== null && out.command === null) {
|
|
1438
1484
|
out = { ...out, command: known };
|
|
1485
|
+
} else if (out.arg === void 0) {
|
|
1486
|
+
out = { ...out, arg: a };
|
|
1439
1487
|
}
|
|
1440
1488
|
}
|
|
1441
1489
|
return out;
|
|
@@ -1443,6 +1491,21 @@ function parseArgs(argv) {
|
|
|
1443
1491
|
function leagueLabel(name) {
|
|
1444
1492
|
return name === "below-1M" ? "below 1M (not yet in a league)" : `${name} League`;
|
|
1445
1493
|
}
|
|
1494
|
+
function formatTokens(n) {
|
|
1495
|
+
const trim = (v) => String(Math.round(v * 10) / 10);
|
|
1496
|
+
const abs = Math.abs(n);
|
|
1497
|
+
if (abs >= 1e9) return `${trim(n / 1e9)}B`;
|
|
1498
|
+
if (abs >= 1e6) return `${trim(n / 1e6)}M`;
|
|
1499
|
+
if (abs >= 1e3) return `${trim(n / 1e3)}k`;
|
|
1500
|
+
return String(n);
|
|
1501
|
+
}
|
|
1502
|
+
function verificationText(snapshot) {
|
|
1503
|
+
if (snapshot.source === "real") {
|
|
1504
|
+
return `verified: real usage \u2014 ${formatTokens(snapshot.totalTokens)} tokens from ${snapshot.harness} logs`;
|
|
1505
|
+
}
|
|
1506
|
+
if (snapshot.source === "self-report") return "self-reported (unverified)";
|
|
1507
|
+
return "demo (unverified)";
|
|
1508
|
+
}
|
|
1446
1509
|
function discoveryScope(myLeague, any) {
|
|
1447
1510
|
const names = any ? allLeagueNames() : leaguesWithin(myLeague, 1);
|
|
1448
1511
|
const ordered = [myLeague, ...names.filter((n) => n !== myLeague)];
|
|
@@ -1455,31 +1518,73 @@ function discoveryScope(myLeague, any) {
|
|
|
1455
1518
|
acceptLeague: (peerLeague) => accepted.has(peerLeague)
|
|
1456
1519
|
};
|
|
1457
1520
|
}
|
|
1521
|
+
function blockedChecker() {
|
|
1522
|
+
return (handle2) => isBlocked(handle2);
|
|
1523
|
+
}
|
|
1458
1524
|
function peerDirection(myLeague, peerLeague, sameBullet = "+") {
|
|
1459
1525
|
const d = leagueIndex(peerLeague) - leagueIndex(myLeague);
|
|
1460
1526
|
if (d > 0) return { bullet: "\u2191", qual: " \xB7 higher league" };
|
|
1461
1527
|
if (d < 0) return { bullet: "\u2193", qual: " \xB7 lower league" };
|
|
1462
1528
|
return { bullet: sameBullet, qual: "" };
|
|
1463
1529
|
}
|
|
1530
|
+
function buildHello(profile) {
|
|
1531
|
+
const claims = {
|
|
1532
|
+
handle: resolveHandle(),
|
|
1533
|
+
league: profile.league,
|
|
1534
|
+
harness: profile.harness,
|
|
1535
|
+
verified: profile.verified
|
|
1536
|
+
};
|
|
1537
|
+
return { ...claims, ...signHelloClaims(loadOrCreateIdentity(), claims) };
|
|
1538
|
+
}
|
|
1539
|
+
function usageMark(peer) {
|
|
1540
|
+
return peer.verified === true ? "\u2713" : "~";
|
|
1541
|
+
}
|
|
1542
|
+
function idMark(peer) {
|
|
1543
|
+
return peer.identityVerified === true ? " \u{1F511}" : "";
|
|
1544
|
+
}
|
|
1545
|
+
var MARKS_LEGEND = "marks: \u2713 usage verified (real local logs) \xB7 ~ unverified (self-report/demo/legacy) \xB7 \u{1F511} identity-verified (signed hello)";
|
|
1464
1546
|
async function cmdConnect() {
|
|
1465
1547
|
const harness = process2.env["VIBEDATING_HARNESS"] ?? "claude-code";
|
|
1466
|
-
const handle2 =
|
|
1548
|
+
const handle2 = resolveHandle();
|
|
1467
1549
|
const snapshot = await readUsage(harness);
|
|
1468
1550
|
const profile = connectProfile(snapshot, handle2);
|
|
1551
|
+
const identity = loadOrCreateIdentity();
|
|
1469
1552
|
const lg = league(snapshot.totalTokens);
|
|
1470
1553
|
process2.stdout.write("\n");
|
|
1471
1554
|
process2.stdout.write(` ${leagueLabel(lg.name)}
|
|
1472
1555
|
`);
|
|
1473
1556
|
process2.stdout.write(` handle: ${profile.handle} \xB7 harness: ${profile.harness}
|
|
1474
1557
|
`);
|
|
1475
|
-
process2.stdout.write(
|
|
1476
|
-
|
|
1477
|
-
`
|
|
1478
|
-
|
|
1558
|
+
process2.stdout.write(` verification: ${verificationText(snapshot)}
|
|
1559
|
+
`);
|
|
1560
|
+
process2.stdout.write(` identity: ed25519 ${identity.publicKeyHex.slice(0, 12)}\u2026 \u2014 signs your hello (\u{1F511})
|
|
1561
|
+
`);
|
|
1479
1562
|
process2.stdout.write("\n");
|
|
1480
1563
|
process2.stdout.write(" \u2022 raw usage stays local \xB7 only league shared\n\n");
|
|
1481
1564
|
return 0;
|
|
1482
1565
|
}
|
|
1566
|
+
async function cmdHandle(arg) {
|
|
1567
|
+
if (arg === void 0 || arg.trim() === "") {
|
|
1568
|
+
const handle2 = resolveHandle();
|
|
1569
|
+
process2.stdout.write(`${handle2}
|
|
1570
|
+
`);
|
|
1571
|
+
const env = process2.env["VIBEDATING_HANDLE"];
|
|
1572
|
+
if (env !== void 0 && env.trim() !== "" && normalizeHandle(env) !== null) {
|
|
1573
|
+
process2.stdout.write(" (env VIBEDATING_HANDLE overrides the persisted handle for this run)\n");
|
|
1574
|
+
}
|
|
1575
|
+
return 0;
|
|
1576
|
+
}
|
|
1577
|
+
try {
|
|
1578
|
+
const canonical = saveHandle(arg);
|
|
1579
|
+
process2.stdout.write(` handle set \u2192 ${canonical} (saved to ~/.vibedating/handle.json)
|
|
1580
|
+
`);
|
|
1581
|
+
return 0;
|
|
1582
|
+
} catch (err) {
|
|
1583
|
+
process2.stderr.write(`${err instanceof Error ? err.message : String(err)}
|
|
1584
|
+
`);
|
|
1585
|
+
return 1;
|
|
1586
|
+
}
|
|
1587
|
+
}
|
|
1483
1588
|
async function cmdMatches(live) {
|
|
1484
1589
|
const profile = loadProfile();
|
|
1485
1590
|
if (!profile) {
|
|
@@ -1502,11 +1607,13 @@ async function cmdMatches(live) {
|
|
|
1502
1607
|
`);
|
|
1503
1608
|
process2.stdout.write(
|
|
1504
1609
|
`${livePeers.length} live peer${livePeers.length === 1 ? "" : "s"} in range (discovered over the DHT):
|
|
1505
|
-
|
|
1506
1610
|
`
|
|
1507
1611
|
);
|
|
1612
|
+
process2.stdout.write(` ${MARKS_LEGEND}
|
|
1613
|
+
|
|
1614
|
+
`);
|
|
1508
1615
|
for (const p of livePeers) {
|
|
1509
|
-
process2.stdout.write(` ${p.handle.padEnd(28)} ${p.league} \xB7 ${p.harness}
|
|
1616
|
+
process2.stdout.write(` ${p.handle.padEnd(28)} ${p.league} \xB7 ${p.harness} ${usageMark(p)}${idMark(p)}
|
|
1510
1617
|
`);
|
|
1511
1618
|
}
|
|
1512
1619
|
process2.stdout.write("\n");
|
|
@@ -1542,27 +1649,26 @@ async function cmdDiscover(live, any) {
|
|
|
1542
1649
|
if (live && !canShareLive()) grantLiveConsent();
|
|
1543
1650
|
if (!canShareLive()) {
|
|
1544
1651
|
process2.stderr.write(
|
|
1545
|
-
"Live discovery is off. It shares ONLY your handle + league + harness (never raw usage)
|
|
1652
|
+
"Live discovery is off. It shares ONLY your handle + league + harness + verified flag + identity pubkey\n(never raw usage) with same-league peers on the public DHT. Opt in: `vibedating discover --live`\n"
|
|
1546
1653
|
);
|
|
1547
1654
|
return 1;
|
|
1548
1655
|
}
|
|
1549
|
-
const hello =
|
|
1550
|
-
handle: profile.handle,
|
|
1551
|
-
league: profile.league,
|
|
1552
|
-
harness: profile.harness
|
|
1553
|
-
};
|
|
1656
|
+
const hello = buildHello(profile);
|
|
1554
1657
|
process2.stdout.write("\n");
|
|
1555
1658
|
process2.stdout.write(` ${LIVE_NOTICE}
|
|
1659
|
+
`);
|
|
1660
|
+
process2.stdout.write(` ${MARKS_LEGEND}
|
|
1556
1661
|
`);
|
|
1557
1662
|
const { topics, acceptLeague } = discoveryScope(profile.league, any);
|
|
1558
1663
|
const session = await startDiscovery({
|
|
1559
1664
|
hello,
|
|
1560
1665
|
topics,
|
|
1561
1666
|
acceptLeague,
|
|
1667
|
+
isBlocked: blockedChecker(),
|
|
1562
1668
|
onPeer: (peer, isNew) => {
|
|
1563
1669
|
const { bullet, qual } = peerDirection(profile.league, peer.league);
|
|
1564
1670
|
process2.stdout.write(
|
|
1565
|
-
` ${bullet} ${peer.handle} (${peer.league}${qual} \xB7 ${peer.harness})${isNew ? " \u2190 new match" : ""}
|
|
1671
|
+
` ${bullet} ${peer.handle} (${peer.league}${qual} \xB7 ${peer.harness}) ${usageMark(peer)}${idMark(peer)}${isNew ? " \u2190 new match" : ""}
|
|
1566
1672
|
`
|
|
1567
1673
|
);
|
|
1568
1674
|
}
|
|
@@ -1605,12 +1711,8 @@ async function cmdOpen(port) {
|
|
|
1605
1711
|
process2.stdout.write(`
|
|
1606
1712
|
${LIVE_NOTICE}
|
|
1607
1713
|
`);
|
|
1608
|
-
const hello =
|
|
1609
|
-
|
|
1610
|
-
league: profile.league,
|
|
1611
|
-
harness: profile.harness
|
|
1612
|
-
};
|
|
1613
|
-
void startDiscovery({ hello, onLink: (link) => live.addLink(link) }).then((s) => {
|
|
1714
|
+
const hello = buildHello(profile);
|
|
1715
|
+
void startDiscovery({ hello, isBlocked: blockedChecker(), onLink: (link) => live.addLink(link) }).then((s) => {
|
|
1614
1716
|
session = s;
|
|
1615
1717
|
}).catch(() => {
|
|
1616
1718
|
});
|
|
@@ -1634,27 +1736,32 @@ async function cmdOpen(port) {
|
|
|
1634
1736
|
await new Promise((resolve) => started.server.close(() => resolve()));
|
|
1635
1737
|
return 0;
|
|
1636
1738
|
}
|
|
1637
|
-
async function cmdLive(dating, any) {
|
|
1739
|
+
async function cmdLive(dating, any, to) {
|
|
1638
1740
|
const profile = loadProfile();
|
|
1639
1741
|
if (!profile) {
|
|
1640
1742
|
process2.stderr.write("Not connected yet. Run `vibedating connect` first.\n");
|
|
1641
1743
|
return 1;
|
|
1642
1744
|
}
|
|
1745
|
+
const target = to !== void 0 ? normalizeHandle(to) : null;
|
|
1746
|
+
if (to !== void 0 && target === null) {
|
|
1747
|
+
process2.stderr.write(`invalid target handle: ${to}
|
|
1748
|
+
`);
|
|
1749
|
+
return 1;
|
|
1750
|
+
}
|
|
1643
1751
|
if (!canShareLive()) grantLiveConsent();
|
|
1644
1752
|
if (!canShareLive()) {
|
|
1645
1753
|
process2.stderr.write("Could not enable live discovery. Try `vibedating discover --live`.\n");
|
|
1646
1754
|
return 1;
|
|
1647
1755
|
}
|
|
1648
|
-
const hello =
|
|
1649
|
-
handle: profile.handle,
|
|
1650
|
-
league: profile.league,
|
|
1651
|
-
harness: profile.harness
|
|
1652
|
-
};
|
|
1756
|
+
const hello = buildHello(profile);
|
|
1653
1757
|
process2.stdout.write("\n");
|
|
1654
1758
|
process2.stdout.write(` ${LIVE_NOTICE}
|
|
1759
|
+
`);
|
|
1760
|
+
process2.stdout.write(` ${MARKS_LEGEND}
|
|
1655
1761
|
`);
|
|
1656
1762
|
process2.stdout.write(
|
|
1657
|
-
|
|
1763
|
+
target !== null ? ` targeted \u2014 auto-opening ${target} when they connect (/quit to stop)
|
|
1764
|
+
` : dating ? " dating mode \u2014 /open <handle> to pick a peer\n" : " omegle mode \u2014 /next to roll a new peer\n"
|
|
1658
1765
|
);
|
|
1659
1766
|
const pairing = createPairing();
|
|
1660
1767
|
pairing.onMatch((link) => {
|
|
@@ -1664,7 +1771,7 @@ async function cmdLive(dating, any) {
|
|
|
1664
1771
|
}
|
|
1665
1772
|
const { qual } = peerDirection(profile.league, link.hello.league);
|
|
1666
1773
|
process2.stdout.write(
|
|
1667
|
-
` \xB7 matched ${link.hello.handle} (${link.hello.league}${qual} \xB7 ${link.hello.harness})
|
|
1774
|
+
` \xB7 matched ${link.hello.handle} (${link.hello.league}${qual} \xB7 ${link.hello.harness}) ${usageMark(link.hello)}${idMark(link.hello)}
|
|
1668
1775
|
`
|
|
1669
1776
|
);
|
|
1670
1777
|
link.onMessage((m) => {
|
|
@@ -1677,7 +1784,23 @@ async function cmdLive(dating, any) {
|
|
|
1677
1784
|
hello,
|
|
1678
1785
|
topics,
|
|
1679
1786
|
acceptLeague,
|
|
1680
|
-
|
|
1787
|
+
isBlocked: blockedChecker(),
|
|
1788
|
+
onLink: (link) => {
|
|
1789
|
+
if (target !== null && !sameHandle(link.hello.handle, target)) {
|
|
1790
|
+
const { qual } = peerDirection(profile.league, link.hello.league);
|
|
1791
|
+
process2.stdout.write(
|
|
1792
|
+
` + ${link.hello.handle} (${link.hello.league}${qual}) ${usageMark(link.hello)}${idMark(link.hello)} \u2014 not your target
|
|
1793
|
+
`
|
|
1794
|
+
);
|
|
1795
|
+
link.close();
|
|
1796
|
+
return;
|
|
1797
|
+
}
|
|
1798
|
+
if (target !== null) {
|
|
1799
|
+
process2.stdout.write(` \u2605 found ${link.hello.handle} \u2014 auto-opening
|
|
1800
|
+
`);
|
|
1801
|
+
}
|
|
1802
|
+
pairing.add(link);
|
|
1803
|
+
}
|
|
1681
1804
|
});
|
|
1682
1805
|
process2.stdout.write(
|
|
1683
1806
|
` topic: ${TOPIC_PREFIX}${profile.league} \u2192 ${session.topic.toString("hex").slice(0, 12)}\u2026${topics.length > 1 ? ` (+${topics.length - 1} more)` : ""}
|
|
@@ -1723,13 +1846,146 @@ async function cmdLive(dating, any) {
|
|
|
1723
1846
|
process2.stdout.write("\n");
|
|
1724
1847
|
return 0;
|
|
1725
1848
|
}
|
|
1849
|
+
async function cmdFind(targetArg, any) {
|
|
1850
|
+
if (targetArg === void 0 || targetArg.trim() === "") {
|
|
1851
|
+
process2.stderr.write("usage: vibedating find <@handle> [--any]\n");
|
|
1852
|
+
return 1;
|
|
1853
|
+
}
|
|
1854
|
+
const target = normalizeHandle(targetArg);
|
|
1855
|
+
if (target === null) {
|
|
1856
|
+
process2.stderr.write(`invalid handle: ${targetArg}
|
|
1857
|
+
`);
|
|
1858
|
+
return 1;
|
|
1859
|
+
}
|
|
1860
|
+
const profile = loadProfile();
|
|
1861
|
+
if (!profile) {
|
|
1862
|
+
process2.stderr.write("Not connected yet. Run `vibedating connect` first.\n");
|
|
1863
|
+
return 1;
|
|
1864
|
+
}
|
|
1865
|
+
if (!canShareLive()) grantLiveConsent();
|
|
1866
|
+
if (!canShareLive()) {
|
|
1867
|
+
process2.stderr.write("Could not enable live discovery. Try `vibedating discover --live`.\n");
|
|
1868
|
+
return 1;
|
|
1869
|
+
}
|
|
1870
|
+
const hello = buildHello(profile);
|
|
1871
|
+
process2.stdout.write("\n");
|
|
1872
|
+
process2.stdout.write(` ${LIVE_NOTICE}
|
|
1873
|
+
`);
|
|
1874
|
+
process2.stdout.write(` ${MARKS_LEGEND}
|
|
1875
|
+
`);
|
|
1876
|
+
process2.stdout.write(
|
|
1877
|
+
` looking for ${target} in your league${any ? " (+ every league)" : " + adjacent"}\u2026
|
|
1878
|
+
`
|
|
1879
|
+
);
|
|
1880
|
+
let found = false;
|
|
1881
|
+
const { topics, acceptLeague } = discoveryScope(profile.league, any);
|
|
1882
|
+
const session = await startDiscovery({
|
|
1883
|
+
hello,
|
|
1884
|
+
topics,
|
|
1885
|
+
acceptLeague,
|
|
1886
|
+
isBlocked: blockedChecker(),
|
|
1887
|
+
onPeer: (peer) => {
|
|
1888
|
+
const { qual } = peerDirection(profile.league, peer.league);
|
|
1889
|
+
if (sameHandle(peer.handle, target)) {
|
|
1890
|
+
found = true;
|
|
1891
|
+
process2.stdout.write(
|
|
1892
|
+
` \u2605 found ${peer.handle} (${peer.league}${qual} \xB7 ${peer.harness}) ${usageMark(peer)}${idMark(peer)}
|
|
1893
|
+
`
|
|
1894
|
+
);
|
|
1895
|
+
} else {
|
|
1896
|
+
process2.stdout.write(
|
|
1897
|
+
` + ${peer.handle} (${peer.league}${qual}) ${usageMark(peer)}${idMark(peer)} \u2014 not your target
|
|
1898
|
+
`
|
|
1899
|
+
);
|
|
1900
|
+
}
|
|
1901
|
+
}
|
|
1902
|
+
});
|
|
1903
|
+
process2.stdout.write(
|
|
1904
|
+
` topic: ${TOPIC_PREFIX}${profile.league} \u2192 ${session.topic.toString("hex").slice(0, 12)}\u2026${topics.length > 1 ? ` (+${topics.length - 1} more)` : ""}
|
|
1905
|
+
`
|
|
1906
|
+
);
|
|
1907
|
+
process2.stdout.write(" (Ctrl+C to stop)\n\n");
|
|
1908
|
+
await new Promise((resolve) => {
|
|
1909
|
+
process2.once("SIGINT", () => resolve());
|
|
1910
|
+
process2.once("SIGTERM", () => resolve());
|
|
1911
|
+
});
|
|
1912
|
+
process2.stdout.write("\n leaving the swarm\u2026\n");
|
|
1913
|
+
await session.close();
|
|
1914
|
+
if (!found) {
|
|
1915
|
+
process2.stdout.write(` \u2717 ${target} was not found this session
|
|
1916
|
+
|
|
1917
|
+
`);
|
|
1918
|
+
} else {
|
|
1919
|
+
process2.stdout.write("\n");
|
|
1920
|
+
}
|
|
1921
|
+
return 0;
|
|
1922
|
+
}
|
|
1923
|
+
async function cmdBlock(arg) {
|
|
1924
|
+
if (arg === void 0 || arg.trim() === "") {
|
|
1925
|
+
process2.stderr.write("usage: vibedating block <@handle>\n");
|
|
1926
|
+
return 1;
|
|
1927
|
+
}
|
|
1928
|
+
const canonical = normalizeHandle(arg);
|
|
1929
|
+
if (canonical === null) {
|
|
1930
|
+
process2.stderr.write(`invalid handle: ${arg}
|
|
1931
|
+
`);
|
|
1932
|
+
return 1;
|
|
1933
|
+
}
|
|
1934
|
+
const { blocked, changed } = addBlock(canonical);
|
|
1935
|
+
process2.stdout.write(
|
|
1936
|
+
changed ? ` blocked ${canonical} (saved to ~/.vibedating/blocklist.json)
|
|
1937
|
+
` : ` ${canonical} is already blocked
|
|
1938
|
+
`
|
|
1939
|
+
);
|
|
1940
|
+
process2.stdout.write(` ${blocked.length} handle${blocked.length === 1 ? "" : "s"} blocked
|
|
1941
|
+
`);
|
|
1942
|
+
return 0;
|
|
1943
|
+
}
|
|
1944
|
+
async function cmdUnblock(arg) {
|
|
1945
|
+
if (arg === void 0 || arg.trim() === "") {
|
|
1946
|
+
process2.stderr.write("usage: vibedating unblock <@handle>\n");
|
|
1947
|
+
return 1;
|
|
1948
|
+
}
|
|
1949
|
+
const canonical = normalizeHandle(arg);
|
|
1950
|
+
if (canonical === null) {
|
|
1951
|
+
process2.stderr.write(`invalid handle: ${arg}
|
|
1952
|
+
`);
|
|
1953
|
+
return 1;
|
|
1954
|
+
}
|
|
1955
|
+
const { blocked, changed } = removeBlock(canonical);
|
|
1956
|
+
process2.stdout.write(
|
|
1957
|
+
changed ? ` unblocked ${canonical}
|
|
1958
|
+
` : ` ${canonical} was not blocked
|
|
1959
|
+
`
|
|
1960
|
+
);
|
|
1961
|
+
process2.stdout.write(` ${blocked.length} handle${blocked.length === 1 ? "" : "s"} blocked
|
|
1962
|
+
`);
|
|
1963
|
+
return 0;
|
|
1964
|
+
}
|
|
1965
|
+
async function cmdBlocklist() {
|
|
1966
|
+
const blocked = loadBlocklist();
|
|
1967
|
+
if (blocked.length === 0) {
|
|
1968
|
+
process2.stdout.write(" (blocklist is empty)\n");
|
|
1969
|
+
return 0;
|
|
1970
|
+
}
|
|
1971
|
+
process2.stdout.write(` ${blocked.length} blocked handle${blocked.length === 1 ? "" : "s"}:
|
|
1972
|
+
`);
|
|
1973
|
+
for (const h of blocked) process2.stdout.write(` ${h}
|
|
1974
|
+
`);
|
|
1975
|
+
return 0;
|
|
1976
|
+
}
|
|
1726
1977
|
var HELP = `vibedating ${VERSION} \u2014 dating by tokens (local-first)
|
|
1727
1978
|
|
|
1728
1979
|
Usage:
|
|
1729
1980
|
vibedating connect Read your usage, compute + print your league
|
|
1730
1981
|
vibedating matches [--live] List candidates in your league (live peers if any)
|
|
1731
1982
|
vibedating discover [--live] [--any] Find live peers over the DHT (your league + adjacent; --any = everyone)
|
|
1732
|
-
vibedating live [--dating] [--any]
|
|
1983
|
+
vibedating live [--dating] [--any] [--to @handle] Live chat (your league + adjacent; --any = everyone; /next or --dating pick; --to targets one peer)
|
|
1984
|
+
vibedating find <@handle> [--any] Search the DHT for one specific handle (\u2605 highlights a match)
|
|
1985
|
+
vibedating handle [@name] Print or set your handle (persisted; a leading '@' is optional)
|
|
1986
|
+
vibedating block <@handle> Block a handle \u2014 their hello is dropped (never recorded/paired)
|
|
1987
|
+
vibedating unblock <@handle> Remove a handle from the blocklist
|
|
1988
|
+
vibedating blocklist List blocked handles
|
|
1733
1989
|
vibedating open [--port N] Serve the local web app (default: random port)
|
|
1734
1990
|
+ live A/V video with connected same-league peers
|
|
1735
1991
|
vibedating mcp Run the stdio MCP server (profile, matches)
|
|
@@ -1739,16 +1995,20 @@ Usage:
|
|
|
1739
1995
|
Privacy:
|
|
1740
1996
|
Raw token usage is read and stored LOCALLY (~/.vibedating). Only the league
|
|
1741
1997
|
bucket is ever shared. Live discovery (off by default) shares ONLY your
|
|
1742
|
-
handle + league + harness
|
|
1998
|
+
handle + league + harness + verified flag + identity pubkey (an ed25519 key
|
|
1999
|
+
generated on first connect, stored 0600 in ~/.vibedating/identity.json) with
|
|
2000
|
+
same-league peers \u2014 opt in with --live. Peers are marked: \u2713 usage verified
|
|
2001
|
+
(real local logs) \xB7 ~ unverified \xB7 \u{1F511} identity-verified (signed hello).
|
|
1743
2002
|
|
|
1744
2003
|
Matching:
|
|
1745
2004
|
discover/live match your league + adjacent (\xB11) tiers by default, so thin
|
|
1746
2005
|
leagues and cross-league friends still connect. --any matches every league.
|
|
2006
|
+
find / live --to look for one specific handle instead of the first random peer.
|
|
1747
2007
|
|
|
1748
2008
|
Env:
|
|
1749
2009
|
VIBEDATING_TOKENS=<n> Self-report a token count (e.g. 23400000 or 12M)
|
|
1750
2010
|
VIBEDATING_HARNESS=<h> Harness id (claude-code, codex, \u2026)
|
|
1751
|
-
VIBEDATING_HANDLE=<@id> Display handle
|
|
2011
|
+
VIBEDATING_HANDLE=<@id> Display handle (one-off override; not persisted)
|
|
1752
2012
|
`;
|
|
1753
2013
|
async function main(argv) {
|
|
1754
2014
|
const parsed = parseArgs(argv);
|
|
@@ -1763,6 +2023,14 @@ async function main(argv) {
|
|
|
1763
2023
|
return 0;
|
|
1764
2024
|
case "connect":
|
|
1765
2025
|
return cmdConnect();
|
|
2026
|
+
case "handle":
|
|
2027
|
+
return cmdHandle(parsed.arg);
|
|
2028
|
+
case "block":
|
|
2029
|
+
return cmdBlock(parsed.arg);
|
|
2030
|
+
case "unblock":
|
|
2031
|
+
return cmdUnblock(parsed.arg);
|
|
2032
|
+
case "blocklist":
|
|
2033
|
+
return cmdBlocklist();
|
|
1766
2034
|
case "matches":
|
|
1767
2035
|
return cmdMatches(parsed.live);
|
|
1768
2036
|
case "discover":
|
|
@@ -1770,7 +2038,9 @@ async function main(argv) {
|
|
|
1770
2038
|
case "open":
|
|
1771
2039
|
return cmdOpen(parsed.port);
|
|
1772
2040
|
case "live":
|
|
1773
|
-
return cmdLive(parsed.dating, parsed.any);
|
|
2041
|
+
return cmdLive(parsed.dating, parsed.any, parsed.to);
|
|
2042
|
+
case "find":
|
|
2043
|
+
return cmdFind(parsed.arg, parsed.any);
|
|
1774
2044
|
case "mcp":
|
|
1775
2045
|
await runMcp();
|
|
1776
2046
|
return 0;
|