vibeshare-live 0.1.1 → 0.2.1
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 +53 -67
- package/dist/chunk-RY2ANOPC.js +1018 -0
- package/dist/cli.d.ts +33 -35
- package/dist/cli.js +393 -239
- package/dist/index.d.ts +106 -47
- package/dist/index.js +53 -22
- package/dist/manager-uhMBJXtu.d.ts +259 -0
- package/dist/mcp.d.ts +27 -38
- package/dist/mcp.js +199 -14616
- package/docs/launch-video-16x9.html +673 -0
- package/docs/launch-video-9x16.html +673 -0
- package/docs/launch-video.html +490 -0
- package/docs/prototype.html +964 -0
- package/docs/spec.md +61 -0
- package/package.json +28 -36
- package/dist/chunk-SBAGPPGC.js +0 -233
- package/dist/share-t_zYXkQB.d.ts +0 -188
package/dist/index.d.ts
CHANGED
|
@@ -1,61 +1,120 @@
|
|
|
1
|
-
|
|
2
|
-
import '
|
|
3
|
-
|
|
1
|
+
import { ConsentStore, ConsentGrant, ConsentLedger } from '@pooriaarab/vibe-core';
|
|
2
|
+
import { S as ShareTransport, a as Share, b as SessionFeed, V as ViewerRegistry, C as CreateShareOptions, c as ShareManager, d as CreatedShare } from './manager-uhMBJXtu.js';
|
|
3
|
+
export { e as ConsentRequiredError, F as FeedEntry, J as JoinRequestStatus, P as PublishOptions, f as SHARE_SCOPE, g as ShareAccess, h as ShareError, i as ShareErrorCode, j as ShareManagerDeps, k as ShareState, l as Viewer, m as ViewerRole } from './manager-uhMBJXtu.js';
|
|
4
|
+
import 'node:events';
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
|
|
6
|
+
interface LocalHttpTransportOptions {
|
|
7
|
+
/** Bind address. Default 127.0.0.1 (loopback-only). Use 0.0.0.0 for LAN. */
|
|
8
|
+
readonly host?: string;
|
|
9
|
+
/** Port. Default 0 = ephemeral. */
|
|
10
|
+
readonly port?: number;
|
|
11
|
+
/**
|
|
12
|
+
* Public base URL to print instead of `http://host:port` — the seam where
|
|
13
|
+
* a future RelayTransport hands out `https://vibeshare.io` URLs. Routing
|
|
14
|
+
* still happens locally.
|
|
15
|
+
*/
|
|
16
|
+
readonly baseUrl?: string;
|
|
17
|
+
/** Bearer token for the loopback control API. Generated when omitted. */
|
|
18
|
+
readonly hostToken?: string;
|
|
19
|
+
/**
|
|
20
|
+
* Called when the control API asks to stop a share (e.g. `vibeshare stop`
|
|
21
|
+
* from another process). When absent, the transport unserves + closes the
|
|
22
|
+
* feed itself.
|
|
23
|
+
*/
|
|
24
|
+
readonly onStopRequested?: (shareId: string) => void;
|
|
25
|
+
}
|
|
26
|
+
declare class LocalHttpTransport implements ShareTransport {
|
|
27
|
+
#private;
|
|
28
|
+
readonly kind = "local-http";
|
|
29
|
+
readonly hostToken: string;
|
|
30
|
+
constructor(opts?: LocalHttpTransportOptions);
|
|
31
|
+
/** Bind the listener. Must be called before serve(). Idempotent. */
|
|
32
|
+
listen(): Promise<void>;
|
|
33
|
+
/** The port actually bound (useful with port 0). */
|
|
34
|
+
get port(): number;
|
|
35
|
+
serve(share: Share, feed: SessionFeed, viewers: ViewerRegistry): Promise<string>;
|
|
36
|
+
unserve(shareId: string): Promise<void>;
|
|
37
|
+
close(): Promise<void>;
|
|
38
|
+
/** Number of shares currently served (diagnostics/tests). */
|
|
39
|
+
get shareCount(): number;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
declare function vibeHome(): string;
|
|
43
|
+
/** Durable ConsentStore backing the vibe-core consent ledger. */
|
|
44
|
+
declare class FileConsentStore implements ConsentStore {
|
|
45
|
+
readonly file: string;
|
|
46
|
+
constructor(file?: string);
|
|
47
|
+
load(): ConsentGrant[];
|
|
48
|
+
save(grants: ConsentGrant[]): void;
|
|
49
|
+
}
|
|
50
|
+
/** A consent ledger backed by the host's `~/.vibeshare/consent.json`. */
|
|
51
|
+
declare function loadLedger(store?: ConsentStore): ConsentLedger;
|
|
7
52
|
|
|
8
53
|
/**
|
|
9
|
-
* The
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* `src/url.test.ts`. A share link is a **capability URL**: the id is the only
|
|
13
|
-
* thing that grants access, so it must be unguessable. The human-facing host is
|
|
14
|
-
* vibeshare's own domain (`vibeshare.stream`); the actual transport is the
|
|
15
|
-
* local/LAN ws relay owned by vibelive, which the URL only *represents*.
|
|
16
|
-
*/
|
|
17
|
-
/**
|
|
18
|
-
* The display origin for vibeshare share links. This is vibeshare's OWN domain —
|
|
19
|
-
* not vibe.live — so a shared link reads as vibeshare even though the bytes flow
|
|
20
|
-
* over a self-hostable e2e relay (see docs/spec.md · "its OWN domain, not
|
|
21
|
-
* vibe.live").
|
|
54
|
+
* The web spectator view: a minimal, self-contained read-only client served
|
|
55
|
+
* straight from the host machine — no install, no build, no external assets
|
|
56
|
+
* (local-first holds for viewers too; the page phones nowhere).
|
|
22
57
|
*/
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
58
|
+
|
|
59
|
+
declare function spectatorPage(share: Share): string;
|
|
60
|
+
|
|
61
|
+
/** A 12-char base64url id (72 bits of entropy) — unguessable, URL-safe. */
|
|
62
|
+
declare function newShareId(): string;
|
|
63
|
+
/** A random hex token (viewer bearer tokens, host control token). */
|
|
64
|
+
declare function newToken(bytes?: number): string;
|
|
26
65
|
/**
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
66
|
+
* Parse an expiry spec into milliseconds.
|
|
67
|
+
* 'stop' | 'never' → null (lasts until explicitly stopped)
|
|
68
|
+
* '<n>m' | '<n>h' | '<n>d' → that duration ('1h', '24h', '7d', …)
|
|
69
|
+
* Anything else throws — callers (CLI) turn this into a usage error.
|
|
30
70
|
*/
|
|
31
|
-
declare function
|
|
71
|
+
declare function parseExpiry(spec: string): number | null;
|
|
72
|
+
/** Hash a passphrase for storage: `scrypt$<saltHex>$<hashHex>`. */
|
|
73
|
+
declare function hashPassphrase(passphrase: string): string;
|
|
74
|
+
/** Verify a candidate against a stored `scrypt$…` hash, timing-safe. */
|
|
75
|
+
declare function verifyPassphrase(passphrase: string, stored: string): boolean;
|
|
76
|
+
|
|
77
|
+
declare const VERSION = "0.1.0";
|
|
78
|
+
|
|
32
79
|
/**
|
|
33
|
-
*
|
|
80
|
+
* vibeshare — npm library surface.
|
|
34
81
|
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
82
|
+
* ```ts
|
|
83
|
+
* import { createShare, grantConsent } from 'vibeshare';
|
|
84
|
+
*
|
|
85
|
+
* grantConsent('share this session from my app'); // once, recorded locally
|
|
86
|
+
* const { url, feed, viewers, revoke } = await createShare({
|
|
87
|
+
* session: 'npm test',
|
|
88
|
+
* access: 'spectate',
|
|
89
|
+
* expiry: '1h',
|
|
90
|
+
* });
|
|
91
|
+
* feed.publish('tests starting…');
|
|
92
|
+
* await revoke();
|
|
93
|
+
* ```
|
|
94
|
+
*
|
|
95
|
+
* Consent is enforced by the vibe-core ledger: no `share:session` grant →
|
|
96
|
+
* `createShare` throws `ConsentRequiredError`. The default transport serves
|
|
97
|
+
* the spectator view + stream from this machine; the relay/p2p seam is the
|
|
98
|
+
* `ShareTransport` interface (see transport.ts).
|
|
38
99
|
*/
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
100
|
+
|
|
101
|
+
interface CreateShareLibraryOptions extends CreateShareOptions {
|
|
102
|
+
/** Serve on a specific consent ledger (default: ~/.vibeshare file ledger). */
|
|
103
|
+
readonly consent?: ConsentLedger;
|
|
104
|
+
/** Transport options for the default local transport (host, port, baseUrl). */
|
|
105
|
+
readonly transport?: LocalHttpTransportOptions;
|
|
106
|
+
/** Bring your own manager (tests, embedders) instead of the shared default. */
|
|
107
|
+
readonly manager?: ShareManager;
|
|
43
108
|
}
|
|
44
109
|
/**
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
* Accepts:
|
|
48
|
-
* - full URLs: `https://vibeshare.stream/s/<id>`, `http://...`, with or without
|
|
49
|
-
* a trailing slash or query string;
|
|
50
|
-
* - bare paths: `/s/<id>`;
|
|
51
|
-
* - any origin host (we key off the `/s/` segment, not the host, so self-hosted
|
|
52
|
-
* / vanity domains round-trip too).
|
|
110
|
+
* Create a share → `{share, url, feed, viewers, revoke}`.
|
|
53
111
|
*
|
|
54
|
-
*
|
|
55
|
-
* Round-trips {@link buildShareUrl}: `parseShareUrl(buildShareUrl(id)).id === id`.
|
|
112
|
+
* @throws ConsentRequiredError when the ledger has no `share:session` grant.
|
|
56
113
|
*/
|
|
57
|
-
declare function
|
|
58
|
-
|
|
59
|
-
|
|
114
|
+
declare function createShare(opts?: CreateShareLibraryOptions): Promise<CreatedShare>;
|
|
115
|
+
/** Grant the `share:session` scope on the host's local consent ledger. */
|
|
116
|
+
declare function grantConsent(note?: string): void;
|
|
117
|
+
/** Revoke the `share:session` scope. */
|
|
118
|
+
declare function revokeConsent(): void;
|
|
60
119
|
|
|
61
|
-
export {
|
|
120
|
+
export { type CreateShareLibraryOptions, CreateShareOptions, CreatedShare, FileConsentStore, LocalHttpTransport, type LocalHttpTransportOptions, SessionFeed, Share, ShareManager, ShareTransport, VERSION, ViewerRegistry, createShare, grantConsent, hashPassphrase, loadLedger, newShareId, newToken, parseExpiry, revokeConsent, spectatorPage, verifyPassphrase, vibeHome };
|
package/dist/index.js
CHANGED
|
@@ -1,32 +1,63 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
2
|
+
ConsentRequiredError,
|
|
3
|
+
FileConsentStore,
|
|
4
|
+
LocalHttpTransport,
|
|
5
|
+
SHARE_SCOPE,
|
|
6
|
+
SessionFeed,
|
|
7
|
+
ShareError,
|
|
8
|
+
ShareManager,
|
|
9
9
|
VERSION,
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
ViewerRegistry,
|
|
11
|
+
hashPassphrase,
|
|
12
|
+
loadLedger,
|
|
13
13
|
newShareId,
|
|
14
|
+
newToken,
|
|
14
15
|
parseExpiry,
|
|
15
|
-
|
|
16
|
-
|
|
16
|
+
spectatorPage,
|
|
17
|
+
verifyPassphrase,
|
|
18
|
+
vibeHome
|
|
19
|
+
} from "./chunk-RY2ANOPC.js";
|
|
20
|
+
|
|
21
|
+
// src/index.ts
|
|
22
|
+
import "@pooriaarab/vibe-core";
|
|
23
|
+
var defaultManager = null;
|
|
24
|
+
function getDefaultManager(transportOpts) {
|
|
25
|
+
defaultManager ??= new ShareManager({
|
|
26
|
+
consent: loadLedger(),
|
|
27
|
+
transport: new LocalHttpTransport(transportOpts)
|
|
28
|
+
});
|
|
29
|
+
return defaultManager;
|
|
30
|
+
}
|
|
31
|
+
async function createShare(opts = {}) {
|
|
32
|
+
const manager = opts.manager ?? (opts.consent ? new ShareManager({ consent: opts.consent, transport: new LocalHttpTransport(opts.transport) }) : getDefaultManager(opts.transport));
|
|
33
|
+
const { consent: _c, transport: _t, manager: _m, ...shareOpts } = opts;
|
|
34
|
+
return manager.createShare(shareOpts);
|
|
35
|
+
}
|
|
36
|
+
function grantConsent(note) {
|
|
37
|
+
loadLedger().grant(SHARE_SCOPE, note ?? "granted via vibeshare library");
|
|
38
|
+
}
|
|
39
|
+
function revokeConsent() {
|
|
40
|
+
loadLedger().revoke(SHARE_SCOPE);
|
|
41
|
+
}
|
|
17
42
|
export {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
43
|
+
ConsentRequiredError,
|
|
44
|
+
FileConsentStore,
|
|
45
|
+
LocalHttpTransport,
|
|
46
|
+
SHARE_SCOPE,
|
|
47
|
+
SessionFeed,
|
|
48
|
+
ShareError,
|
|
49
|
+
ShareManager,
|
|
25
50
|
VERSION,
|
|
26
|
-
|
|
27
|
-
createAccessGate,
|
|
51
|
+
ViewerRegistry,
|
|
28
52
|
createShare,
|
|
53
|
+
grantConsent,
|
|
54
|
+
hashPassphrase,
|
|
55
|
+
loadLedger,
|
|
29
56
|
newShareId,
|
|
57
|
+
newToken,
|
|
30
58
|
parseExpiry,
|
|
31
|
-
|
|
59
|
+
revokeConsent,
|
|
60
|
+
spectatorPage,
|
|
61
|
+
verifyPassphrase,
|
|
62
|
+
vibeHome
|
|
32
63
|
};
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
import { VibeEvent, ConsentLedger } from '@pooriaarab/vibe-core';
|
|
2
|
+
import { EventEmitter } from 'node:events';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* vibeshare — shared types.
|
|
6
|
+
*
|
|
7
|
+
* vibeshare is the URL / identity / access layer for sharing a live agent
|
|
8
|
+
* coding session. It owns the *link and the gate*; the session content is an
|
|
9
|
+
* ordered feed of entries that spectators receive live. vibelive (the
|
|
10
|
+
* multiplayer engine) owns any future collaborator write path — see
|
|
11
|
+
* `transport.ts` for that seam.
|
|
12
|
+
*/
|
|
13
|
+
/** Access policy for a share link. */
|
|
14
|
+
type ShareAccess =
|
|
15
|
+
/** Recipients can only watch. Genuinely read-only: there is no write route. */
|
|
16
|
+
'spectate'
|
|
17
|
+
/** Recipients may request to join; host approval promotes them to collaborator. */
|
|
18
|
+
| 'invite';
|
|
19
|
+
type ShareState = 'live' | 'revoked' | 'expired';
|
|
20
|
+
/** A live (or formerly live) share link. */
|
|
21
|
+
interface Share {
|
|
22
|
+
/** Unguessable capability id — appears in the URL. */
|
|
23
|
+
readonly id: string;
|
|
24
|
+
/** Human label, e.g. the command being shared or the agent name. */
|
|
25
|
+
readonly name: string;
|
|
26
|
+
access: ShareAccess;
|
|
27
|
+
readonly createdAt: string;
|
|
28
|
+
/** ISO timestamp, or null when the share lasts "until I stop". */
|
|
29
|
+
readonly expiresAt: string | null;
|
|
30
|
+
state: ShareState;
|
|
31
|
+
/** scrypt hash (`scrypt$<salt>$<hash>`); never the plaintext passphrase. */
|
|
32
|
+
readonly passphraseHash: string | null;
|
|
33
|
+
}
|
|
34
|
+
type ViewerRole = 'spectator' | 'collaborator';
|
|
35
|
+
type JoinRequestStatus = 'none' | 'pending' | 'approved' | 'denied';
|
|
36
|
+
/** Someone who opened the link and joined as a viewer. */
|
|
37
|
+
interface Viewer {
|
|
38
|
+
readonly id: string;
|
|
39
|
+
name: string;
|
|
40
|
+
role: ViewerRole;
|
|
41
|
+
/** Bearer token issued at join; required for stream/request/leave calls. */
|
|
42
|
+
readonly token: string;
|
|
43
|
+
readonly joinedAt: string;
|
|
44
|
+
joinRequest: JoinRequestStatus;
|
|
45
|
+
}
|
|
46
|
+
/** One entry in the shared session's ordered log. */
|
|
47
|
+
interface FeedEntry {
|
|
48
|
+
/** Monotonic sequence number — also the SSE event id. */
|
|
49
|
+
readonly seq: number;
|
|
50
|
+
readonly ts: number;
|
|
51
|
+
readonly type: 'output' | 'milestone' | 'system';
|
|
52
|
+
/** Which host stream produced an `output` entry. */
|
|
53
|
+
readonly stream?: 'stdout' | 'stderr';
|
|
54
|
+
readonly text: string;
|
|
55
|
+
}
|
|
56
|
+
/** Options for creating a share (CLI, library, and MCP all funnel here). */
|
|
57
|
+
interface CreateShareOptions {
|
|
58
|
+
/** Label for what is being shared (command line, agent name, …). */
|
|
59
|
+
readonly session?: string;
|
|
60
|
+
/** Access policy. Default `'spectate'`. */
|
|
61
|
+
readonly access?: ShareAccess;
|
|
62
|
+
/**
|
|
63
|
+
* How long the link lives: `'1h'`, `'24h'`, `'stop'` (until stopped), or
|
|
64
|
+
* any `<n>m` / `<n>h` / `<n>d`. Default `'stop'`.
|
|
65
|
+
*/
|
|
66
|
+
readonly expiry?: string;
|
|
67
|
+
/** Passphrase second factor. Optional; stored only as a scrypt hash. */
|
|
68
|
+
readonly passphrase?: string;
|
|
69
|
+
/** Override the display name (defaults to `session`). */
|
|
70
|
+
readonly name?: string;
|
|
71
|
+
/**
|
|
72
|
+
* Direct millisecond expiry override (takes precedence over `expiry`).
|
|
73
|
+
* Mainly for tests and embedders that already hold a duration.
|
|
74
|
+
*/
|
|
75
|
+
readonly expiryMs?: number;
|
|
76
|
+
}
|
|
77
|
+
/** vibeshare error with a machine-readable code (mapped to HTTP by transports). */
|
|
78
|
+
declare class ShareError extends Error {
|
|
79
|
+
readonly code: ShareErrorCode;
|
|
80
|
+
constructor(code: ShareErrorCode, message: string);
|
|
81
|
+
}
|
|
82
|
+
type ShareErrorCode = 'not-live' | 'not-found' | 'passphrase-required' | 'passphrase-invalid' | 'invite-disabled' | 'already-pending' | 'not-pending' | 'consent-required' | 'bad-request';
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* SessionFeed — the ordered log of everything a share broadcasts.
|
|
86
|
+
*
|
|
87
|
+
* The host (and only the host) publishes; spectators subscribe. Late joiners
|
|
88
|
+
* get a bounded replay of recent entries, then live entries in order. This is
|
|
89
|
+
* vibeshare's half of vibelive's "ordered-log channel": the log is the source
|
|
90
|
+
* of record, held on the host machine only.
|
|
91
|
+
*/
|
|
92
|
+
|
|
93
|
+
interface PublishOptions {
|
|
94
|
+
readonly type?: FeedEntry['type'];
|
|
95
|
+
readonly stream?: FeedEntry['stream'];
|
|
96
|
+
}
|
|
97
|
+
declare interface SessionFeed {
|
|
98
|
+
on(event: 'entry', listener: (entry: FeedEntry) => void): this;
|
|
99
|
+
on(event: 'close', listener: () => void): this;
|
|
100
|
+
once(event: 'close', listener: () => void): this;
|
|
101
|
+
}
|
|
102
|
+
declare class SessionFeed extends EventEmitter {
|
|
103
|
+
#private;
|
|
104
|
+
constructor(capacity?: number);
|
|
105
|
+
get closed(): boolean;
|
|
106
|
+
/** Append a line to the log and fan it out to subscribers. */
|
|
107
|
+
publish(text: string, opts?: PublishOptions): FeedEntry;
|
|
108
|
+
/** Publish a normalized vibe-core milestone event as a feed line. */
|
|
109
|
+
publishEvent(e: VibeEvent): FeedEntry;
|
|
110
|
+
/** Publish a host-side status line (share opened, viewer joined, …). */
|
|
111
|
+
system(text: string): FeedEntry;
|
|
112
|
+
/** Recent entries for late-joiner replay, oldest first. */
|
|
113
|
+
backlog(): readonly FeedEntry[];
|
|
114
|
+
/** Subscribe to live entries. Returns an unsubscribe function. */
|
|
115
|
+
subscribe(listener: (entry: FeedEntry) => void): () => void;
|
|
116
|
+
/** End the feed: notifies subscribers, refuses further publishes. */
|
|
117
|
+
close(): void;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* ViewerRegistry — who is watching a share, and who may write.
|
|
122
|
+
*
|
|
123
|
+
* Write-arbitration invariant (shared with vibelive §4): the host is the
|
|
124
|
+
* server of record, and `canWrite()` is the single gate any input path must
|
|
125
|
+
* consult. A spectator can never pass it; promotion happens only through a
|
|
126
|
+
* host-approved join request on an `invite`-access share. v0 exposes no
|
|
127
|
+
* remote input route at all — the gate exists so the vibelive handoff has
|
|
128
|
+
* exactly one choke point when it lands.
|
|
129
|
+
*/
|
|
130
|
+
|
|
131
|
+
interface RegistryEvents {
|
|
132
|
+
join: (v: Viewer) => void;
|
|
133
|
+
leave: (v: Viewer) => void;
|
|
134
|
+
request: (v: Viewer) => void;
|
|
135
|
+
approve: (v: Viewer) => void;
|
|
136
|
+
deny: (v: Viewer) => void;
|
|
137
|
+
kick: (v: Viewer) => void;
|
|
138
|
+
}
|
|
139
|
+
declare interface ViewerRegistry {
|
|
140
|
+
on<K extends keyof RegistryEvents>(event: K, listener: RegistryEvents[K]): this;
|
|
141
|
+
}
|
|
142
|
+
declare class ViewerRegistry extends EventEmitter {
|
|
143
|
+
#private;
|
|
144
|
+
constructor(getAccess: () => ShareAccess);
|
|
145
|
+
/** Register a new spectator. Everyone enters read-only — no exceptions. */
|
|
146
|
+
add(name?: string): Viewer;
|
|
147
|
+
get(id: string): Viewer | undefined;
|
|
148
|
+
getByToken(token: string): Viewer | undefined;
|
|
149
|
+
list(): Viewer[];
|
|
150
|
+
count(): number;
|
|
151
|
+
/**
|
|
152
|
+
* A spectator asks to be promoted to collaborator. Only possible on an
|
|
153
|
+
* `invite`-access share; the host still has to approve.
|
|
154
|
+
*/
|
|
155
|
+
requestJoin(id: string): Viewer;
|
|
156
|
+
/** Host approves a pending request → the viewer becomes a collaborator. */
|
|
157
|
+
approve(id: string): Viewer;
|
|
158
|
+
/** Host denies a pending request; the viewer stays a spectator. */
|
|
159
|
+
deny(id: string): Viewer;
|
|
160
|
+
/** Remove a viewer entirely (their streams are closed by the transport). */
|
|
161
|
+
kick(id: string): Viewer;
|
|
162
|
+
/** A viewer leaves on their own. */
|
|
163
|
+
leave(id: string): void;
|
|
164
|
+
/**
|
|
165
|
+
* The write-arbitration gate. The host writes by definition; a remote
|
|
166
|
+
* participant writes only as an approved collaborator. Any future input
|
|
167
|
+
* channel (vibelive cursors, shared terminal input) MUST consult this.
|
|
168
|
+
*/
|
|
169
|
+
canWrite(viewerId: string): boolean;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* ShareTransport — the one seam vibeshare deliberately leaves open.
|
|
174
|
+
*
|
|
175
|
+
* vibeshare owns the link + the gate. How feed bytes reach viewers is a
|
|
176
|
+
* pluggable transport behind this interface:
|
|
177
|
+
*
|
|
178
|
+
* - `LocalHttpTransport` (implemented, the default): serves the spectator
|
|
179
|
+
* web view + an SSE stream directly from the host machine over
|
|
180
|
+
* loopback/LAN. Fully local-first — bytes go straight from host to
|
|
181
|
+
* viewer; nothing is stored on any server.
|
|
182
|
+
*
|
|
183
|
+
* - `RelayTransport` (NOT implemented — lands with vibelive): a dumb,
|
|
184
|
+
* e2e-encrypted relay that forwards opaque blobs so viewers behind NAT
|
|
185
|
+
* can reach the host, returning a public `https://vibeshare.io/s/<id>`
|
|
186
|
+
* URL. The relay never sees plaintext and stores nothing; vibelive owns
|
|
187
|
+
* the encryption and the p2p mesh. Everything in vibeshare (access
|
|
188
|
+
* policy, consent, the viewer registry, write-arbitration) is transport-
|
|
189
|
+
* agnostic and already works — swapping this interface in is the only
|
|
190
|
+
* change a relay requires.
|
|
191
|
+
*
|
|
192
|
+
* The collaborator *input* channel is part of the same vibelive seam: a
|
|
193
|
+
* transport that accepts remote input MUST route it through
|
|
194
|
+
* `ViewerRegistry.canWrite()` before applying anything to the session.
|
|
195
|
+
*/
|
|
196
|
+
|
|
197
|
+
interface ShareTransport {
|
|
198
|
+
/** Transport label for diagnostics, e.g. 'local-http'. */
|
|
199
|
+
readonly kind: string;
|
|
200
|
+
/**
|
|
201
|
+
* Begin serving a share. Returns the URL viewers open. Called once per
|
|
202
|
+
* share; a transport may serve many shares concurrently.
|
|
203
|
+
*/
|
|
204
|
+
serve(share: Share, feed: SessionFeed, viewers: ViewerRegistry): Promise<string>;
|
|
205
|
+
/**
|
|
206
|
+
* Stop serving one share: disconnect its viewers and make its URL answer
|
|
207
|
+
* as gone. Idempotent.
|
|
208
|
+
*/
|
|
209
|
+
unserve(shareId: string): Promise<void>;
|
|
210
|
+
/** Tear the whole transport down (all shares, listeners, sockets). */
|
|
211
|
+
close(): Promise<void>;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* ShareManager — creates, tracks, expires, and revokes shares.
|
|
216
|
+
*
|
|
217
|
+
* The consent ledger from @pooriaarab/vibe-core is the enforcement point for
|
|
218
|
+
* local-first: no grant for the `share:session` scope → no share. Expiry is
|
|
219
|
+
* enforced here with real timers that tear the share down (the transport
|
|
220
|
+
* disconnects viewers and the URL goes 410) — not just a filter on reads.
|
|
221
|
+
*/
|
|
222
|
+
|
|
223
|
+
/** The consent scope the suite reserves for sharing features. */
|
|
224
|
+
declare const SHARE_SCOPE = "share:session";
|
|
225
|
+
declare class ConsentRequiredError extends ShareError {
|
|
226
|
+
constructor();
|
|
227
|
+
}
|
|
228
|
+
interface CreatedShare {
|
|
229
|
+
readonly share: Share;
|
|
230
|
+
/** The URL viewers open. */
|
|
231
|
+
readonly url: string;
|
|
232
|
+
readonly feed: SessionFeed;
|
|
233
|
+
readonly viewers: ViewerRegistry;
|
|
234
|
+
/** End the share now (same as manager.revokeShare(share.id)). */
|
|
235
|
+
revoke(): Promise<void>;
|
|
236
|
+
}
|
|
237
|
+
interface ShareManagerDeps {
|
|
238
|
+
readonly consent: ConsentLedger;
|
|
239
|
+
readonly transport: ShareTransport;
|
|
240
|
+
}
|
|
241
|
+
declare class ShareManager {
|
|
242
|
+
#private;
|
|
243
|
+
constructor(deps: ShareManagerDeps);
|
|
244
|
+
/**
|
|
245
|
+
* Create a share: `createShare({session, access, expiry, passphrase})`
|
|
246
|
+
* → `{share, url, feed, viewers, revoke}`.
|
|
247
|
+
*
|
|
248
|
+
* @throws ConsentRequiredError when the ledger has no `share:session` grant.
|
|
249
|
+
*/
|
|
250
|
+
createShare(opts?: CreateShareOptions): Promise<CreatedShare>;
|
|
251
|
+
/** End a share: disconnect viewers, 410 the URL, close the feed. */
|
|
252
|
+
revokeShare(id: string, reason?: 'revoked' | 'expired'): Promise<void>;
|
|
253
|
+
get(id: string): CreatedShare | undefined;
|
|
254
|
+
list(): CreatedShare[];
|
|
255
|
+
/** Revoke every live share and close the transport. */
|
|
256
|
+
stopAll(): Promise<void>;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
export { type CreateShareOptions as C, type FeedEntry as F, type JoinRequestStatus as J, type PublishOptions as P, type ShareTransport as S, ViewerRegistry as V, type Share as a, SessionFeed as b, ShareManager as c, type CreatedShare as d, ConsentRequiredError as e, SHARE_SCOPE as f, type ShareAccess as g, ShareError as h, type ShareErrorCode as i, type ShareManagerDeps as j, type ShareState as k, type Viewer as l, type ViewerRole as m };
|
package/dist/mcp.d.ts
CHANGED
|
@@ -1,41 +1,30 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import '
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { ConsentLedger } from '@pooriaarab/vibe-core';
|
|
3
|
+
import { c as ShareManager } from './manager-uhMBJXtu.js';
|
|
4
|
+
import 'node:events';
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
interface
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
readonly share: ShareHandle;
|
|
28
|
-
readonly relay: RelayHandle;
|
|
6
|
+
interface JsonRpcRequest {
|
|
7
|
+
jsonrpc?: string;
|
|
8
|
+
id?: string | number | null;
|
|
9
|
+
method?: string;
|
|
10
|
+
params?: Record<string, unknown>;
|
|
11
|
+
}
|
|
12
|
+
interface JsonRpcResponse {
|
|
13
|
+
jsonrpc: '2.0';
|
|
14
|
+
id: string | number | null;
|
|
15
|
+
result?: unknown;
|
|
16
|
+
error?: {
|
|
17
|
+
code: number;
|
|
18
|
+
message: string;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
interface McpServerDeps {
|
|
22
|
+
manager: ShareManager;
|
|
23
|
+
consent: ConsentLedger;
|
|
24
|
+
}
|
|
25
|
+
interface McpServer {
|
|
26
|
+
handleMessage(msg: JsonRpcRequest): Promise<JsonRpcResponse | null>;
|
|
29
27
|
}
|
|
30
|
-
|
|
31
|
-
* Build the vibeshare MCP server (tools registered, not yet connected).
|
|
32
|
-
*
|
|
33
|
-
* `sessions` defaults to a fresh module-level map so independent `createMcpServer`
|
|
34
|
-
* calls don't share state, but a single stdio process keeps one map for its
|
|
35
|
-
* lifetime (so `viewers` sees shares started via `create_share`).
|
|
36
|
-
*/
|
|
37
|
-
declare function createMcpServer(sessions?: Map<string, ActiveShare>): McpServer;
|
|
38
|
-
/** Create the server, wire it to stdio, and run until the client disconnects. */
|
|
39
|
-
declare function runMcpStdio(): Promise<void>;
|
|
28
|
+
declare function createMcpServer(deps: McpServerDeps): McpServer;
|
|
40
29
|
|
|
41
|
-
export {
|
|
30
|
+
export { type McpServer, type McpServerDeps, createMcpServer };
|