vibedate 0.3.1 → 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-3VT4EOBX.js → chunk-43IYNWES.js} +401 -212
- package/dist/{chunk-OVO2CQ7X.js → chunk-ZB56ZBAR.js} +2 -2
- package/dist/cli.js +71 -38
- package/dist/index.d.ts +130 -31
- package/dist/index.js +13 -3
- package/dist/mcp.js +2 -2
- package/package.json +2 -2
|
@@ -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.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
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,
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
leagueTopic,
|
|
18
18
|
leaguesWithin,
|
|
19
19
|
loadBlocklist,
|
|
20
|
+
loadOrCreateIdentity,
|
|
20
21
|
loadPeers,
|
|
21
22
|
loadProfile,
|
|
22
23
|
matches,
|
|
@@ -27,8 +28,9 @@ import {
|
|
|
27
28
|
resolveHandle,
|
|
28
29
|
sameHandle,
|
|
29
30
|
saveHandle,
|
|
31
|
+
signHelloClaims,
|
|
30
32
|
startDiscovery
|
|
31
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-43IYNWES.js";
|
|
32
34
|
|
|
33
35
|
// src/cli.ts
|
|
34
36
|
import readline from "readline";
|
|
@@ -1398,7 +1400,7 @@ async function handle(req, res, opts) {
|
|
|
1398
1400
|
}
|
|
1399
1401
|
|
|
1400
1402
|
// src/cli.ts
|
|
1401
|
-
var VERSION = "0.
|
|
1403
|
+
var VERSION = "0.4.0";
|
|
1402
1404
|
function parsePort(raw) {
|
|
1403
1405
|
const n = Number(raw);
|
|
1404
1406
|
if (!Number.isInteger(n) || n < 1 || n > 65535) return void 0;
|
|
@@ -1489,6 +1491,21 @@ function parseArgs(argv) {
|
|
|
1489
1491
|
function leagueLabel(name) {
|
|
1490
1492
|
return name === "below-1M" ? "below 1M (not yet in a league)" : `${name} League`;
|
|
1491
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
|
+
}
|
|
1492
1509
|
function discoveryScope(myLeague, any) {
|
|
1493
1510
|
const names = any ? allLeagueNames() : leaguesWithin(myLeague, 1);
|
|
1494
1511
|
const ordered = [myLeague, ...names.filter((n) => n !== myLeague)];
|
|
@@ -1510,21 +1527,38 @@ function peerDirection(myLeague, peerLeague, sameBullet = "+") {
|
|
|
1510
1527
|
if (d < 0) return { bullet: "\u2193", qual: " \xB7 lower league" };
|
|
1511
1528
|
return { bullet: sameBullet, qual: "" };
|
|
1512
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)";
|
|
1513
1546
|
async function cmdConnect() {
|
|
1514
1547
|
const harness = process2.env["VIBEDATING_HARNESS"] ?? "claude-code";
|
|
1515
1548
|
const handle2 = resolveHandle();
|
|
1516
1549
|
const snapshot = await readUsage(harness);
|
|
1517
1550
|
const profile = connectProfile(snapshot, handle2);
|
|
1551
|
+
const identity = loadOrCreateIdentity();
|
|
1518
1552
|
const lg = league(snapshot.totalTokens);
|
|
1519
1553
|
process2.stdout.write("\n");
|
|
1520
1554
|
process2.stdout.write(` ${leagueLabel(lg.name)}
|
|
1521
1555
|
`);
|
|
1522
1556
|
process2.stdout.write(` handle: ${profile.handle} \xB7 harness: ${profile.harness}
|
|
1523
1557
|
`);
|
|
1524
|
-
process2.stdout.write(
|
|
1525
|
-
|
|
1526
|
-
`
|
|
1527
|
-
|
|
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
|
+
`);
|
|
1528
1562
|
process2.stdout.write("\n");
|
|
1529
1563
|
process2.stdout.write(" \u2022 raw usage stays local \xB7 only league shared\n\n");
|
|
1530
1564
|
return 0;
|
|
@@ -1573,11 +1607,13 @@ async function cmdMatches(live) {
|
|
|
1573
1607
|
`);
|
|
1574
1608
|
process2.stdout.write(
|
|
1575
1609
|
`${livePeers.length} live peer${livePeers.length === 1 ? "" : "s"} in range (discovered over the DHT):
|
|
1576
|
-
|
|
1577
1610
|
`
|
|
1578
1611
|
);
|
|
1612
|
+
process2.stdout.write(` ${MARKS_LEGEND}
|
|
1613
|
+
|
|
1614
|
+
`);
|
|
1579
1615
|
for (const p of livePeers) {
|
|
1580
|
-
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)}
|
|
1581
1617
|
`);
|
|
1582
1618
|
}
|
|
1583
1619
|
process2.stdout.write("\n");
|
|
@@ -1613,17 +1649,15 @@ async function cmdDiscover(live, any) {
|
|
|
1613
1649
|
if (live && !canShareLive()) grantLiveConsent();
|
|
1614
1650
|
if (!canShareLive()) {
|
|
1615
1651
|
process2.stderr.write(
|
|
1616
|
-
"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"
|
|
1617
1653
|
);
|
|
1618
1654
|
return 1;
|
|
1619
1655
|
}
|
|
1620
|
-
const hello =
|
|
1621
|
-
handle: resolveHandle(),
|
|
1622
|
-
league: profile.league,
|
|
1623
|
-
harness: profile.harness
|
|
1624
|
-
};
|
|
1656
|
+
const hello = buildHello(profile);
|
|
1625
1657
|
process2.stdout.write("\n");
|
|
1626
1658
|
process2.stdout.write(` ${LIVE_NOTICE}
|
|
1659
|
+
`);
|
|
1660
|
+
process2.stdout.write(` ${MARKS_LEGEND}
|
|
1627
1661
|
`);
|
|
1628
1662
|
const { topics, acceptLeague } = discoveryScope(profile.league, any);
|
|
1629
1663
|
const session = await startDiscovery({
|
|
@@ -1634,7 +1668,7 @@ async function cmdDiscover(live, any) {
|
|
|
1634
1668
|
onPeer: (peer, isNew) => {
|
|
1635
1669
|
const { bullet, qual } = peerDirection(profile.league, peer.league);
|
|
1636
1670
|
process2.stdout.write(
|
|
1637
|
-
` ${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" : ""}
|
|
1638
1672
|
`
|
|
1639
1673
|
);
|
|
1640
1674
|
}
|
|
@@ -1677,11 +1711,7 @@ async function cmdOpen(port) {
|
|
|
1677
1711
|
process2.stdout.write(`
|
|
1678
1712
|
${LIVE_NOTICE}
|
|
1679
1713
|
`);
|
|
1680
|
-
const hello =
|
|
1681
|
-
handle: resolveHandle(),
|
|
1682
|
-
league: profile.league,
|
|
1683
|
-
harness: profile.harness
|
|
1684
|
-
};
|
|
1714
|
+
const hello = buildHello(profile);
|
|
1685
1715
|
void startDiscovery({ hello, isBlocked: blockedChecker(), onLink: (link) => live.addLink(link) }).then((s) => {
|
|
1686
1716
|
session = s;
|
|
1687
1717
|
}).catch(() => {
|
|
@@ -1723,13 +1753,11 @@ async function cmdLive(dating, any, to) {
|
|
|
1723
1753
|
process2.stderr.write("Could not enable live discovery. Try `vibedating discover --live`.\n");
|
|
1724
1754
|
return 1;
|
|
1725
1755
|
}
|
|
1726
|
-
const hello =
|
|
1727
|
-
handle: resolveHandle(),
|
|
1728
|
-
league: profile.league,
|
|
1729
|
-
harness: profile.harness
|
|
1730
|
-
};
|
|
1756
|
+
const hello = buildHello(profile);
|
|
1731
1757
|
process2.stdout.write("\n");
|
|
1732
1758
|
process2.stdout.write(` ${LIVE_NOTICE}
|
|
1759
|
+
`);
|
|
1760
|
+
process2.stdout.write(` ${MARKS_LEGEND}
|
|
1733
1761
|
`);
|
|
1734
1762
|
process2.stdout.write(
|
|
1735
1763
|
target !== null ? ` targeted \u2014 auto-opening ${target} when they connect (/quit to stop)
|
|
@@ -1743,7 +1771,7 @@ async function cmdLive(dating, any, to) {
|
|
|
1743
1771
|
}
|
|
1744
1772
|
const { qual } = peerDirection(profile.league, link.hello.league);
|
|
1745
1773
|
process2.stdout.write(
|
|
1746
|
-
` \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)}
|
|
1747
1775
|
`
|
|
1748
1776
|
);
|
|
1749
1777
|
link.onMessage((m) => {
|
|
@@ -1761,7 +1789,7 @@ async function cmdLive(dating, any, to) {
|
|
|
1761
1789
|
if (target !== null && !sameHandle(link.hello.handle, target)) {
|
|
1762
1790
|
const { qual } = peerDirection(profile.league, link.hello.league);
|
|
1763
1791
|
process2.stdout.write(
|
|
1764
|
-
` + ${link.hello.handle} (${link.hello.league}${qual}) \u2014 not your target
|
|
1792
|
+
` + ${link.hello.handle} (${link.hello.league}${qual}) ${usageMark(link.hello)}${idMark(link.hello)} \u2014 not your target
|
|
1765
1793
|
`
|
|
1766
1794
|
);
|
|
1767
1795
|
link.close();
|
|
@@ -1839,13 +1867,11 @@ async function cmdFind(targetArg, any) {
|
|
|
1839
1867
|
process2.stderr.write("Could not enable live discovery. Try `vibedating discover --live`.\n");
|
|
1840
1868
|
return 1;
|
|
1841
1869
|
}
|
|
1842
|
-
const hello =
|
|
1843
|
-
handle: resolveHandle(),
|
|
1844
|
-
league: profile.league,
|
|
1845
|
-
harness: profile.harness
|
|
1846
|
-
};
|
|
1870
|
+
const hello = buildHello(profile);
|
|
1847
1871
|
process2.stdout.write("\n");
|
|
1848
1872
|
process2.stdout.write(` ${LIVE_NOTICE}
|
|
1873
|
+
`);
|
|
1874
|
+
process2.stdout.write(` ${MARKS_LEGEND}
|
|
1849
1875
|
`);
|
|
1850
1876
|
process2.stdout.write(
|
|
1851
1877
|
` looking for ${target} in your league${any ? " (+ every league)" : " + adjacent"}\u2026
|
|
@@ -1862,11 +1888,15 @@ async function cmdFind(targetArg, any) {
|
|
|
1862
1888
|
const { qual } = peerDirection(profile.league, peer.league);
|
|
1863
1889
|
if (sameHandle(peer.handle, target)) {
|
|
1864
1890
|
found = true;
|
|
1865
|
-
process2.stdout.write(
|
|
1866
|
-
`)
|
|
1891
|
+
process2.stdout.write(
|
|
1892
|
+
` \u2605 found ${peer.handle} (${peer.league}${qual} \xB7 ${peer.harness}) ${usageMark(peer)}${idMark(peer)}
|
|
1893
|
+
`
|
|
1894
|
+
);
|
|
1867
1895
|
} else {
|
|
1868
|
-
process2.stdout.write(
|
|
1869
|
-
`)
|
|
1896
|
+
process2.stdout.write(
|
|
1897
|
+
` + ${peer.handle} (${peer.league}${qual}) ${usageMark(peer)}${idMark(peer)} \u2014 not your target
|
|
1898
|
+
`
|
|
1899
|
+
);
|
|
1870
1900
|
}
|
|
1871
1901
|
}
|
|
1872
1902
|
});
|
|
@@ -1965,7 +1995,10 @@ Usage:
|
|
|
1965
1995
|
Privacy:
|
|
1966
1996
|
Raw token usage is read and stored LOCALLY (~/.vibedating). Only the league
|
|
1967
1997
|
bucket is ever shared. Live discovery (off by default) shares ONLY your
|
|
1968
|
-
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).
|
|
1969
2002
|
|
|
1970
2003
|
Matching:
|
|
1971
2004
|
discover/live match your league + adjacent (\xB11) tiers by default, so thin
|
package/dist/index.d.ts
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
|
-
import { VibeEvent, Harness,
|
|
2
|
-
export { Harness, UsageSnapshot, createConsentLedger } from '@pooriaarab/vibe-core';
|
|
1
|
+
import { VibeEvent, UsageSnapshot, UsageSource, Harness, HarnessUsageOptions } from '@pooriaarab/vibe-core';
|
|
2
|
+
export { Harness, UsageSnapshot, UsageSource, createConsentLedger } from '@pooriaarab/vibe-core';
|
|
3
|
+
import { KeyObject } from 'node:crypto';
|
|
3
4
|
|
|
4
5
|
type Frame = {
|
|
5
6
|
t: 'hello';
|
|
6
7
|
handle: string;
|
|
7
8
|
league: string;
|
|
8
9
|
harness: string;
|
|
10
|
+
verified?: boolean;
|
|
11
|
+
pubkey?: string;
|
|
12
|
+
nonce?: string;
|
|
13
|
+
sig?: string;
|
|
9
14
|
} | {
|
|
10
15
|
t: 'msg';
|
|
11
16
|
id: string;
|
|
@@ -57,11 +62,7 @@ interface ReceivedMedia {
|
|
|
57
62
|
|
|
58
63
|
interface PeerLink {
|
|
59
64
|
/** The validated identity of the remote peer (from the hello handshake). */
|
|
60
|
-
readonly hello:
|
|
61
|
-
handle: string;
|
|
62
|
-
league: string;
|
|
63
|
-
harness: string;
|
|
64
|
-
};
|
|
65
|
+
readonly hello: PeerHello;
|
|
65
66
|
/** Send a line of text as a `msg` frame. */
|
|
66
67
|
send(text: string): void;
|
|
67
68
|
/** Read a file from disk and send it as a chunked media transfer. */
|
|
@@ -99,14 +100,36 @@ declare const TOPIC_PREFIX = "vibedate:";
|
|
|
99
100
|
* entire discovery mechanism. Pure.
|
|
100
101
|
*/
|
|
101
102
|
declare function leagueTopic(leagueName: string): Buffer;
|
|
102
|
-
/**
|
|
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
|
+
*/
|
|
103
110
|
interface PeerHello {
|
|
104
111
|
readonly handle: string;
|
|
105
112
|
readonly league: string;
|
|
106
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;
|
|
107
130
|
}
|
|
108
131
|
/** One-line privacy notice printed before joining the swarm. */
|
|
109
|
-
declare const LIVE_NOTICE = "live discovery: sharing only your handle + league + harness (never raw usage) with same-league peers on the public DHT";
|
|
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";
|
|
110
133
|
/**
|
|
111
134
|
* Serialize a hello to the single JSON line sent on connect. Built key-by-key
|
|
112
135
|
* from the allowlist — even if a caller sneaks extra properties onto the
|
|
@@ -124,6 +147,8 @@ declare function parseHandshake(raw: string | Buffer): PeerHello | null;
|
|
|
124
147
|
interface StoredPeer extends PeerHello {
|
|
125
148
|
readonly firstSeenAt: string;
|
|
126
149
|
readonly lastSeenAt: string;
|
|
150
|
+
/** LOCAL metadata: when the last `msg` from this peer arrived (never on the wire). */
|
|
151
|
+
readonly lastMessageAt?: string;
|
|
127
152
|
}
|
|
128
153
|
/** Load persisted live peers, or `[]` if none/corrupt. Local-only data. */
|
|
129
154
|
declare function loadPeers(dir?: string): StoredPeer[];
|
|
@@ -135,6 +160,12 @@ declare function recordPeer(hello: PeerHello, dir?: string, now?: Date): {
|
|
|
135
160
|
peer: StoredPeer;
|
|
136
161
|
isNew: boolean;
|
|
137
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;
|
|
138
169
|
/** Injection point for the match notification (defaults to vibe-core notify). */
|
|
139
170
|
type NotifySink = (event: VibeEvent) => void;
|
|
140
171
|
interface DiscoveryOptions {
|
|
@@ -208,14 +239,74 @@ interface DiscoverySession {
|
|
|
208
239
|
declare function startDiscovery(opts: DiscoveryOptions): Promise<DiscoverySession>;
|
|
209
240
|
|
|
210
241
|
/**
|
|
211
|
-
*
|
|
242
|
+
* Persistent ed25519 identity — binds a handle to a keypair so a peer cannot
|
|
243
|
+
* impersonate it.
|
|
212
244
|
*
|
|
213
|
-
*
|
|
214
|
-
*
|
|
215
|
-
*
|
|
245
|
+
* The keypair lives at `~/.vibedating/identity.json` (mode 0600), generated on
|
|
246
|
+
* first use and reused across runs. A hello that carries a `pubkey` must also
|
|
247
|
+
* carry a valid `sig` over the canonical claims string
|
|
248
|
+
* `handle|league|harness|verified|nonce`; anything else claiming a key is an
|
|
249
|
+
* impersonation attempt and the peer is DROPPED. A hello with no `pubkey` at
|
|
250
|
+
* all is a legacy peer — accepted, but never identity-verified.
|
|
216
251
|
*
|
|
217
|
-
*
|
|
252
|
+
* node:crypto only — no new dependency. The private key never leaves the file;
|
|
253
|
+
* only the raw public key (64 hex) and signatures (128 hex) travel on hellos.
|
|
254
|
+
*/
|
|
255
|
+
|
|
256
|
+
/** A loaded identity: the raw public key (wire form) plus both key objects. */
|
|
257
|
+
interface Identity {
|
|
258
|
+
/** Raw 32-byte ed25519 public key, hex (64 chars) — the wire form. */
|
|
259
|
+
readonly publicKeyHex: string;
|
|
260
|
+
readonly publicKey: KeyObject;
|
|
261
|
+
readonly privateKey: KeyObject;
|
|
262
|
+
/** ISO timestamp of key generation (first connect). */
|
|
263
|
+
readonly createdAt: string;
|
|
264
|
+
}
|
|
265
|
+
/** The hello fields a signature commits to (a PeerHello minus its proof). */
|
|
266
|
+
interface HelloClaims {
|
|
267
|
+
readonly handle: string;
|
|
268
|
+
readonly league: string;
|
|
269
|
+
readonly harness: string;
|
|
270
|
+
readonly verified?: boolean;
|
|
271
|
+
}
|
|
272
|
+
/** The wire proof attached to a hello: pubkey + nonce + signature, all hex. */
|
|
273
|
+
interface IdentityProof {
|
|
274
|
+
readonly pubkey: string;
|
|
275
|
+
readonly nonce: string;
|
|
276
|
+
readonly sig: string;
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Load the persistent keypair, generating + storing it (mode 0600) on first
|
|
280
|
+
* use. A missing or corrupt file is (re)generated — never throws on disk
|
|
281
|
+
* content. Idempotent across runs: same file → same key → same pubkey.
|
|
282
|
+
*/
|
|
283
|
+
declare function loadOrCreateIdentity(dir?: string): Identity;
|
|
284
|
+
/**
|
|
285
|
+
* The canonical string an identity signature commits to:
|
|
286
|
+
* `handle|league|harness|verified|nonce` — verified rendered as `true`/`false`.
|
|
287
|
+
* Pure; both sides compute it byte-identically or the signature cannot verify.
|
|
288
|
+
*/
|
|
289
|
+
declare function canonicalHelloClaims(claims: HelloClaims & {
|
|
290
|
+
nonce: string;
|
|
291
|
+
}): string;
|
|
292
|
+
/**
|
|
293
|
+
* Sign hello claims with the persistent identity. A fresh random 16-byte nonce
|
|
294
|
+
* per call, so two hellos never share a signature. Returns only the wire proof
|
|
295
|
+
* fields — the private key stays put.
|
|
218
296
|
*/
|
|
297
|
+
declare function signHelloClaims(identity: Identity, claims: HelloClaims): IdentityProof;
|
|
298
|
+
/**
|
|
299
|
+
* Verify a claimed proof against hello claims. NEVER throws — any anomaly
|
|
300
|
+
* (bad hex, bad key, bad signature) is simply `false`.
|
|
301
|
+
*/
|
|
302
|
+
declare function verifyHelloClaims(claims: HelloClaims, proof: IdentityProof): boolean;
|
|
303
|
+
/** What an incoming hello's identity material amounts to. */
|
|
304
|
+
type IdentityVerdict = 'verified' | 'legacy' | 'drop';
|
|
305
|
+
/**
|
|
306
|
+
* Classify an incoming hello's identity claim. Pure decision, no IO — the
|
|
307
|
+
* caller (discovery) turns 'drop' into "never recorded, never paired".
|
|
308
|
+
*/
|
|
309
|
+
declare function classifyHelloIdentity(hello: HelloClaims & Partial<IdentityProof>): IdentityVerdict;
|
|
219
310
|
|
|
220
311
|
/**
|
|
221
312
|
* A usage league (volume bucket). `max` is inclusive; the top tier is open-ended
|
|
@@ -277,26 +368,34 @@ declare const TOKENS_ENV = "VIBEDATING_TOKENS";
|
|
|
277
368
|
*/
|
|
278
369
|
declare const DEMO_TOTAL_TOKENS = 23400000;
|
|
279
370
|
/**
|
|
280
|
-
*
|
|
281
|
-
*
|
|
282
|
-
*
|
|
283
|
-
*
|
|
284
|
-
*
|
|
285
|
-
* 3. the demo value {@link DEMO_TOTAL_TOKENS} → `verified: false`.
|
|
286
|
-
*
|
|
287
|
-
* The snapshot's `totalTokens` is the only thing that must never leave the
|
|
288
|
-
* machine; everything downstream consumes only the league bucket.
|
|
371
|
+
* A usage snapshot with honest provenance. `source` says where `totalTokens`
|
|
372
|
+
* came from; `verified` is true ONLY for `source === 'real'` (measured from
|
|
373
|
+
* the harness's own local session logs). The token total is the one thing that
|
|
374
|
+
* must never leave the machine; everything downstream consumes only the league
|
|
375
|
+
* bucket plus the verified flag.
|
|
289
376
|
*/
|
|
290
|
-
|
|
377
|
+
interface LocalUsageSnapshot extends UsageSnapshot {
|
|
378
|
+
/** Where the total came from: measured locally, self-reported, or demo. */
|
|
379
|
+
readonly source: UsageSource;
|
|
380
|
+
/** Short human-facing provenance note from the reader (local display only). */
|
|
381
|
+
readonly detail?: string;
|
|
382
|
+
}
|
|
291
383
|
/**
|
|
292
|
-
*
|
|
384
|
+
* Usage reader, backed by vibe-core's {@link readHarnessUsage}. Resolution order:
|
|
385
|
+
*
|
|
386
|
+
* 1. a self-reported value — explicit `opts.selfReportTokens`, or the
|
|
387
|
+
* {@link TOKENS_ENV} env var (suffix-friendly: `12M`, `1.2B`), or vibe-core's
|
|
388
|
+
* own `VIBE_TOKENS` env → `source: 'self-report'` → `verified: false`.
|
|
389
|
+
* 2. the harness's REAL local session logs (claude-code / codex / gemini / pi /
|
|
390
|
+
* kimi), read from disk by vibe-core → `source: 'real'` → `verified: true`.
|
|
391
|
+
* 3. the demo value {@link DEMO_TOTAL_TOKENS} → `source: 'demo'` →
|
|
392
|
+
* `verified: false`.
|
|
293
393
|
*
|
|
294
|
-
*
|
|
295
|
-
*
|
|
296
|
-
*
|
|
297
|
-
* visible at the call site.
|
|
394
|
+
* The demo arm keeps vibedating's own demo total (10M league, non-empty demo
|
|
395
|
+
* pool) rather than vibe-core's smaller placeholder, so demo behavior is
|
|
396
|
+
* unchanged. Raw usage never leaves the machine.
|
|
298
397
|
*/
|
|
299
|
-
declare function
|
|
398
|
+
declare function readUsage(harness?: Harness, opts?: HarnessUsageOptions): Promise<LocalUsageSnapshot>;
|
|
300
399
|
/**
|
|
301
400
|
* Parse a self-reported token count: a plain integer (`23400000`), or a suffixed
|
|
302
401
|
* value (`12M`, `1.2B`, `500k`, `500K`). Returns `undefined` for anything that is
|
|
@@ -324,4 +423,4 @@ declare const CANDIDATES: readonly Candidate[];
|
|
|
324
423
|
*/
|
|
325
424
|
declare function matches(myLeague: string, candidates?: readonly Candidate[]): Candidate[];
|
|
326
425
|
|
|
327
|
-
export { BELOW_LEAGUE, CANDIDATES, type Candidate, DEMO_TOTAL_TOKENS, type DiscoveryOptions, type DiscoverySession, LEAGUES, LIVE_NOTICE, type League, type PeerHello, type StoredPeer, TOKENS_ENV, TOPIC_PREFIX, allLeagueNames, league, leagueIndex, leagueTopic, leaguesWithin, loadPeers, matches, parseHandshake, parseTokensEnv, readUsage, recordPeer, serializeHandshake, startDiscovery,
|
|
426
|
+
export { BELOW_LEAGUE, CANDIDATES, type Candidate, DEMO_TOTAL_TOKENS, type DiscoveryOptions, type DiscoverySession, type HelloClaims, type Identity, type IdentityProof, type IdentityVerdict, LEAGUES, LIVE_NOTICE, type League, type LocalUsageSnapshot, type PeerHello, type StoredPeer, TOKENS_ENV, TOPIC_PREFIX, allLeagueNames, canonicalHelloClaims, classifyHelloIdentity, league, leagueIndex, leagueTopic, leaguesWithin, loadOrCreateIdentity, loadPeers, matches, parseHandshake, parseTokensEnv, readUsage, recordPeer, recordPeerMessage, serializeHandshake, signHelloClaims, startDiscovery, verifyHelloClaims };
|
package/dist/index.js
CHANGED
|
@@ -7,21 +7,26 @@ import {
|
|
|
7
7
|
TOKENS_ENV,
|
|
8
8
|
TOPIC_PREFIX,
|
|
9
9
|
allLeagueNames,
|
|
10
|
+
canonicalHelloClaims,
|
|
11
|
+
classifyHelloIdentity,
|
|
10
12
|
createConsentLedger,
|
|
11
13
|
league,
|
|
12
14
|
leagueIndex,
|
|
13
15
|
leagueTopic,
|
|
14
16
|
leaguesWithin,
|
|
17
|
+
loadOrCreateIdentity,
|
|
15
18
|
loadPeers,
|
|
16
19
|
matches,
|
|
17
20
|
parseHandshake,
|
|
18
21
|
parseTokensEnv,
|
|
19
22
|
readUsage,
|
|
20
23
|
recordPeer,
|
|
24
|
+
recordPeerMessage,
|
|
21
25
|
serializeHandshake,
|
|
26
|
+
signHelloClaims,
|
|
22
27
|
startDiscovery,
|
|
23
|
-
|
|
24
|
-
} from "./chunk-
|
|
28
|
+
verifyHelloClaims
|
|
29
|
+
} from "./chunk-43IYNWES.js";
|
|
25
30
|
export {
|
|
26
31
|
BELOW_LEAGUE,
|
|
27
32
|
CANDIDATES,
|
|
@@ -31,18 +36,23 @@ export {
|
|
|
31
36
|
TOKENS_ENV,
|
|
32
37
|
TOPIC_PREFIX,
|
|
33
38
|
allLeagueNames,
|
|
39
|
+
canonicalHelloClaims,
|
|
40
|
+
classifyHelloIdentity,
|
|
34
41
|
createConsentLedger,
|
|
35
42
|
league,
|
|
36
43
|
leagueIndex,
|
|
37
44
|
leagueTopic,
|
|
38
45
|
leaguesWithin,
|
|
46
|
+
loadOrCreateIdentity,
|
|
39
47
|
loadPeers,
|
|
40
48
|
matches,
|
|
41
49
|
parseHandshake,
|
|
42
50
|
parseTokensEnv,
|
|
43
51
|
readUsage,
|
|
44
52
|
recordPeer,
|
|
53
|
+
recordPeerMessage,
|
|
45
54
|
serializeHandshake,
|
|
55
|
+
signHelloClaims,
|
|
46
56
|
startDiscovery,
|
|
47
|
-
|
|
57
|
+
verifyHelloClaims
|
|
48
58
|
};
|
package/dist/mcp.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vibedate",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
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",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
32
|
-
"@pooriaarab/vibe-core": "^0.
|
|
32
|
+
"@pooriaarab/vibe-core": "^0.3.0",
|
|
33
33
|
"hyperswarm": "^4.17.0"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|