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
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 {
|
|
@@ -160,6 +191,13 @@ interface DiscoveryOptions {
|
|
|
160
191
|
* peers that arrive on a shared topic.
|
|
161
192
|
*/
|
|
162
193
|
readonly acceptLeague?: (peerLeague: string) => boolean;
|
|
194
|
+
/**
|
|
195
|
+
* Predicate over an incoming peer's advertised handle. A blocked peer's hello
|
|
196
|
+
* is DROPPED exactly like a wrong-league one — never recorded to peers.json,
|
|
197
|
+
* never notified, never handed to `onLink`/pairing. Default: nothing blocked.
|
|
198
|
+
* The CLI passes one backed by the persisted blocklist (~/.vibedating).
|
|
199
|
+
*/
|
|
200
|
+
readonly isBlocked?: (handle: string) => boolean;
|
|
163
201
|
/** DHT bootstrap nodes; omit for the public DHT. Tests pass a local testnet. */
|
|
164
202
|
readonly bootstrap?: ReadonlyArray<{
|
|
165
203
|
readonly host: string;
|
|
@@ -201,14 +239,74 @@ interface DiscoverySession {
|
|
|
201
239
|
declare function startDiscovery(opts: DiscoveryOptions): Promise<DiscoverySession>;
|
|
202
240
|
|
|
203
241
|
/**
|
|
204
|
-
*
|
|
242
|
+
* Persistent ed25519 identity — binds a handle to a keypair so a peer cannot
|
|
243
|
+
* impersonate it.
|
|
205
244
|
*
|
|
206
|
-
*
|
|
207
|
-
*
|
|
208
|
-
*
|
|
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.
|
|
209
251
|
*
|
|
210
|
-
*
|
|
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.
|
|
211
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;
|
|
212
310
|
|
|
213
311
|
/**
|
|
214
312
|
* A usage league (volume bucket). `max` is inclusive; the top tier is open-ended
|
|
@@ -270,26 +368,34 @@ declare const TOKENS_ENV = "VIBEDATING_TOKENS";
|
|
|
270
368
|
*/
|
|
271
369
|
declare const DEMO_TOTAL_TOKENS = 23400000;
|
|
272
370
|
/**
|
|
273
|
-
*
|
|
274
|
-
*
|
|
275
|
-
*
|
|
276
|
-
*
|
|
277
|
-
*
|
|
278
|
-
* 3. the demo value {@link DEMO_TOTAL_TOKENS} → `verified: false`.
|
|
279
|
-
*
|
|
280
|
-
* The snapshot's `totalTokens` is the only thing that must never leave the
|
|
281
|
-
* 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.
|
|
282
376
|
*/
|
|
283
|
-
|
|
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
|
+
}
|
|
284
383
|
/**
|
|
285
|
-
*
|
|
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`.
|
|
286
393
|
*
|
|
287
|
-
*
|
|
288
|
-
*
|
|
289
|
-
*
|
|
290
|
-
* 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.
|
|
291
397
|
*/
|
|
292
|
-
declare function
|
|
398
|
+
declare function readUsage(harness?: Harness, opts?: HarnessUsageOptions): Promise<LocalUsageSnapshot>;
|
|
293
399
|
/**
|
|
294
400
|
* Parse a self-reported token count: a plain integer (`23400000`), or a suffixed
|
|
295
401
|
* value (`12M`, `1.2B`, `500k`, `500K`). Returns `undefined` for anything that is
|
|
@@ -317,4 +423,4 @@ declare const CANDIDATES: readonly Candidate[];
|
|
|
317
423
|
*/
|
|
318
424
|
declare function matches(myLeague: string, candidates?: readonly Candidate[]): Candidate[];
|
|
319
425
|
|
|
320
|
-
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": {
|
|
@@ -37,7 +37,8 @@
|
|
|
37
37
|
"hyperdht": "^6.33.0",
|
|
38
38
|
"tsup": "^8.0.0",
|
|
39
39
|
"typescript": "^5.4.0",
|
|
40
|
-
"vitest": "^1.6.0"
|
|
40
|
+
"vitest": "^1.6.0",
|
|
41
|
+
"werift": "^0.24.1"
|
|
41
42
|
},
|
|
42
43
|
"homepage": "https://github.com/pooriaarab/vibedating#readme",
|
|
43
44
|
"repository": {
|