vibeshare-live 0.1.0 → 0.2.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/docs/spec.md ADDED
@@ -0,0 +1,61 @@
1
+ # vibeshare — spec
2
+
3
+ Status: DRAFT (Opus-authored) · 2026-07-25 · depends on `@vibe/core` + vibelive
4
+ Identity: vibeshare.stream (available $4/yr — its OWN domain, not vibe.live) · CLI + npm + MCP
5
+ Prior art: traces.com (but open source, CLI-first) · Happy (slopus/happy) transport
6
+
7
+ ## What it is
8
+ Share your **live** agent coding session by URL. The distribution layer on top of
9
+ **vibelive** (the multiplayer engine): a link that lets people **spectate read-only**
10
+ or be **invited into the live multiplayer session**.
11
+
12
+ ## Relationship to vibelive
13
+ - **vibelive** = the engine (transport, presence, cursors, chat, write-arbitration).
14
+ - **vibeshare** = the URL/identity/access layer: create a shareable link, set access
15
+ policy, let viewers request to join → hands off into vibelive.
16
+ - A spectator is a vibelive participant with read-only + no cursor-write; "join"
17
+ promotes them to a full participant. vibeshare owns the *link + gate*, vibelive
18
+ owns the *session*.
19
+
20
+ ## The flow
21
+ 1. `vibeshare` in your terminal → generates `vibeshare.io/s/<id>` (its own domain).
22
+ 2. Access policy: **spectate** (read-only) ↔ **invite** (can join to collaborate);
23
+ expiry (1h / 24h / until I stop); optional passphrase.
24
+ 3. Recipients open the URL → read-only live view of your terminal (agent output
25
+ streams via vibelive's ordered-log channel). "Request to join" → host approves →
26
+ promoted into the live session.
27
+
28
+ ## Local-first / transport (inherits vibelive §1-2)
29
+ Session streamed **peer-to-peer / via a dumb e2e-encrypted relay** — the relay
30
+ forwards opaque blobs, **nothing readable stored on a server**, relay self-hostable.
31
+ Badge: "● p2p · e2e · nothing stored on a server". `share:session` consent grant
32
+ required (§4). Spectator-heavy sharing = pub/sub fan-out tier (scales to the vibelive
33
+ 1,000 spectator target).
34
+
35
+ ## Access / safety
36
+ - Link is a capability URL (unguessable id); passphrase optional second factor.
37
+ - Read-only spectators genuinely can't write (enforced server-of-record = host, not
38
+ UI-only) — same write-arbitration invariant as vibelive §4.
39
+ - Revoke link / kick viewer / lock to invite-only, live.
40
+ - Expiry auto-tears down the relay subscription.
41
+
42
+ ## Surfaces
43
+ - **CLI:** `vibeshare [--spectate|--invite] [--expire 1h] [--pass]` → prints URL ·
44
+ `vibeshare stop` · `vibeshare viewers` (see/kick/approve).
45
+ - **npm:** `createShare({session, access, expiry})` → `{url, revoke}`.
46
+ - **MCP:** `vibeshare.create`, `vibeshare.viewers` — agent can offer "share this
47
+ session?"
48
+ - **Web spectator view:** the URL target — a minimal read-only vibelive client
49
+ (self-contained, no install for viewers).
50
+
51
+ ## Cross-harness
52
+ Wraps whatever agent vibelive is hosting (Claude Code / Codex / Cursor / Gemini /
53
+ Grok / pi / Kimi / …). Harness-agnostic via vibelive.
54
+
55
+ ## Open questions
56
+ - Web spectator client hosting: served from the self-hostable relay, or a static
57
+ page + relay data channel? Prefer static page (any host / IPFS) + e2e relay data,
58
+ so "open source, self-host everything" holds.
59
+ - Public/discoverable shares (a directory of live sessions) vs link-only — v0 =
60
+ link-only (privacy default); discovery opt-in later.
61
+ - Recording a shared session to replay (→ vibemovie handoff) — v2.
package/package.json CHANGED
@@ -1,38 +1,56 @@
1
1
  {
2
2
  "name": "vibeshare-live",
3
- "version": "0.1.0",
4
- "description": "Share your live agent coding session by URL — spectate read-only or invite into the vibelive multiplayer engine. Open source, CLI-first, e2e p2p. Local-first.",
5
- "type": "module",
3
+ "version": "0.2.0",
4
+ "description": "Share your live agent coding session by URL — spectate read-only or invite into the session. Local-first: runs on your machine, nothing readable leaves without consent. Part of the Vibe Suite.",
6
5
  "license": "MIT",
7
- "author": "Pooria Arab",
8
- "bin": { "vibeshare": "./dist/cli.js" },
6
+ "type": "module",
7
+ "engines": {
8
+ "node": ">=18"
9
+ },
10
+ "bin": {
11
+ "vibeshare": "./dist/cli.js",
12
+ "vibeshare-mcp": "./dist/mcp.js"
13
+ },
9
14
  "main": "./dist/index.js",
10
15
  "types": "./dist/index.d.ts",
11
- "exports": { ".": { "types": "./dist/index.d.ts", "import": "./dist/index.js" } },
12
- "files": ["dist"],
16
+ "exports": {
17
+ ".": {
18
+ "types": "./dist/index.d.ts",
19
+ "import": "./dist/index.js"
20
+ },
21
+ "./mcp": {
22
+ "types": "./dist/mcp.d.ts",
23
+ "import": "./dist/mcp.js"
24
+ }
25
+ },
26
+ "files": [
27
+ "dist",
28
+ "docs",
29
+ "README.md"
30
+ ],
13
31
  "scripts": {
14
- "build": "tsup src/index.ts src/cli.ts src/mcp.ts --format esm --dts --clean",
32
+ "build": "tsup",
15
33
  "typecheck": "tsc --noEmit",
16
34
  "test": "vitest run",
17
35
  "test:watch": "vitest"
18
36
  },
19
37
  "dependencies": {
20
- "@pooriaarab/vibe-core": "^0.1.0",
21
- "vibelive-cli": "^0.1.0",
22
- "@modelcontextprotocol/sdk": "^1.0.0",
23
- "ws": "^8.16.0"
38
+ "@pooriaarab/vibe-core": "^0.2.0",
39
+ "vibelive": "^0.2.0"
24
40
  },
25
41
  "devDependencies": {
26
- "@types/node": "^20.11.0",
27
- "@types/ws": "^8.5.0",
28
- "tsup": "^8.0.0",
29
- "typescript": "^5.4.0",
30
- "vitest": "^1.6.0"
42
+ "@types/node": "^22.10.0",
43
+ "tsup": "^8.3.0",
44
+ "typescript": "^5.6.0",
45
+ "vitest": "^3.0.0"
31
46
  },
32
- "homepage": "https://github.com/pooriaarab/vibeshare#readme",
33
- "repository": { "type": "git", "url": "git+https://github.com/pooriaarab/vibeshare.git" },
34
- "bugs": { "url": "https://github.com/pooriaarab/vibeshare/issues" },
35
- "keywords": ["vibe-suite", "vibeshare", "claude-code", "codex", "multiplayer", "cli", "mcp", "share", "local-first"],
36
- "publishConfig": { "access": "public" },
37
- "engines": { "node": ">=18" }
47
+ "keywords": [
48
+ "vibe-suite",
49
+ "pair-programming",
50
+ "live-share",
51
+ "agentic-coding",
52
+ "cli",
53
+ "mcp",
54
+ "local-first"
55
+ ]
38
56
  }
@@ -1,233 +0,0 @@
1
- var __defProp = Object.defineProperty;
2
- var __export = (target, all) => {
3
- for (var name in all)
4
- __defProp(target, name, { get: all[name], enumerable: true });
5
- };
6
-
7
- // src/access.ts
8
- var DEFAULT_ACCESS = "spectate";
9
- function createAccessGate(options) {
10
- const access = options.access ?? DEFAULT_ACCESS;
11
- const arbiter = options.arbiter;
12
- const passphrase = options.passphrase;
13
- const roles = /* @__PURE__ */ new Map();
14
- const hasPassphrase = typeof passphrase === "string" && passphrase.length > 0;
15
- return {
16
- access,
17
- hasPassphrase,
18
- admit({ id }, input) {
19
- if (hasPassphrase && input !== passphrase) return false;
20
- if (!roles.has(id)) roles.set(id, "spectator");
21
- return true;
22
- },
23
- remove(id) {
24
- roles.delete(id);
25
- arbiter.leave(id);
26
- },
27
- has: (id) => roles.has(id),
28
- role: (id) => roles.get(id),
29
- promote(id) {
30
- if (access !== "invite") return false;
31
- if (!roles.has(id)) return false;
32
- roles.set(id, "participant");
33
- return true;
34
- },
35
- requestControl(id) {
36
- const role = roles.get(id);
37
- if (role === void 0) return { ok: false, reason: "unknown" };
38
- if (role === "participant") {
39
- const state = arbiter.requestControl(id);
40
- return { ok: true, state };
41
- }
42
- return { ok: false, reason: access === "invite" ? "not-promoted" : "spectator" };
43
- }
44
- };
45
- }
46
-
47
- // src/url.ts
48
- var SHARE_ORIGIN = "https://vibeshare.stream";
49
- var SHARE_PATH_PREFIX = "/s/";
50
- function newShareId() {
51
- const uuid = globalThis.crypto?.randomUUID?.();
52
- if (uuid) return uuid;
53
- const bytes = new Uint8Array(16);
54
- globalThis.crypto.getRandomValues(bytes);
55
- const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
56
- return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
57
- }
58
- function buildShareUrl(id, origin = SHARE_ORIGIN) {
59
- return `${origin}${SHARE_PATH_PREFIX}${id}`;
60
- }
61
- var ShareUrlParseError = class extends Error {
62
- constructor(message) {
63
- super(message);
64
- this.name = "ShareUrlParseError";
65
- }
66
- };
67
- function parseShareUrl(url) {
68
- if (typeof url !== "string" || url.length === 0) {
69
- throw new ShareUrlParseError("share url must be a non-empty string");
70
- }
71
- const idx = url.indexOf(SHARE_PATH_PREFIX);
72
- if (idx < 0) {
73
- throw new ShareUrlParseError(`not a vibeshare share url (missing "${SHARE_PATH_PREFIX}"): ${url}`);
74
- }
75
- const tail = url.slice(idx + SHARE_PATH_PREFIX.length);
76
- const end = tail.search(/[/?#]/);
77
- const id = end < 0 ? tail : tail.slice(0, end);
78
- if (id.length === 0) {
79
- throw new ShareUrlParseError(`share url has an empty id: ${url}`);
80
- }
81
- return { id };
82
- }
83
-
84
- // src/share.ts
85
- import { SHARE_SESSION_SCOPE as VIBELIVE_SHARE_SCOPE } from "vibelive-cli";
86
- var SHARE_SESSION_SCOPE = VIBELIVE_SHARE_SCOPE;
87
- function parseExpiry(spec) {
88
- if (spec === void 0) return null;
89
- if (typeof spec === "number") {
90
- if (!Number.isFinite(spec) || spec <= 0) {
91
- throw new RangeError(`expiry duration must be a positive finite number (ms), got: ${spec}`);
92
- }
93
- return Math.floor(spec);
94
- }
95
- switch (spec) {
96
- case "1h":
97
- return 60 * 60 * 1e3;
98
- case "24h":
99
- return 24 * 60 * 60 * 1e3;
100
- default: {
101
- const exhaustive = spec;
102
- throw new RangeError(`unknown expiry preset: ${String(exhaustive)}`);
103
- }
104
- }
105
- }
106
- var ConsentError = class extends Error {
107
- constructor() {
108
- super(`consent for "${SHARE_SESSION_SCOPE}" is required to share a session \u2014 grant it first`);
109
- this.name = "ConsentError";
110
- }
111
- };
112
- var HOST_PARTICIPANT_NAME = "host";
113
- function isLocalHostParticipant(p) {
114
- return p.ws === void 0;
115
- }
116
- function participantName(p, fallback) {
117
- return p.name && p.name.length > 0 ? p.name : fallback;
118
- }
119
- function createShare(options) {
120
- const relay = options.session;
121
- const consent = options.consent ?? relay.consent;
122
- if (!consent.allows(SHARE_SESSION_SCOPE)) {
123
- throw new ConsentError();
124
- }
125
- const id = newShareId();
126
- const url = buildShareUrl(id);
127
- const gate = createAccessGate({
128
- arbiter: relay.arbiter,
129
- access: options.access,
130
- passphrase: options.passphrase
131
- });
132
- const firstSeen = /* @__PURE__ */ new Map();
133
- const noteSeen = (pid) => {
134
- let t = firstSeen.get(pid);
135
- if (t === void 0) {
136
- t = Date.now();
137
- firstSeen.set(pid, t);
138
- }
139
- return t;
140
- };
141
- let revoked = false;
142
- let revokeReason;
143
- let expiryTimer;
144
- const fireRevoke = (reason) => {
145
- if (revoked) return Promise.resolve();
146
- revoked = true;
147
- revokeReason = reason;
148
- if (expiryTimer !== void 0) {
149
- clearTimeout(expiryTimer);
150
- expiryTimer = void 0;
151
- }
152
- try {
153
- options.onRevoke?.(reason);
154
- } catch {
155
- }
156
- return relay.close().catch(() => {
157
- });
158
- };
159
- const expiryMs = parseExpiry(options.expiry);
160
- if (expiryMs !== null) {
161
- expiryTimer = setTimeout(() => {
162
- void fireRevoke("expired");
163
- }, expiryMs);
164
- expiryTimer.unref?.();
165
- }
166
- const toViewer = (p) => {
167
- const pid = p.id;
168
- if (!gate.has(pid)) {
169
- gate.admit({ id: pid, name: participantName(p, pid) });
170
- }
171
- const role = gate.role(pid) ?? "spectator";
172
- return {
173
- id: pid,
174
- name: participantName(p, pid),
175
- role,
176
- joinedAt: noteSeen(pid)
177
- };
178
- };
179
- return {
180
- id,
181
- url,
182
- access: gate.access,
183
- gate,
184
- relayUrl: relay.url,
185
- get revoked() {
186
- return revoked;
187
- },
188
- viewers() {
189
- const connected = [];
190
- const pending = [];
191
- for (const p of relay.participants) {
192
- if (isLocalHostParticipant(p)) continue;
193
- const v = toViewer(p);
194
- if (gate.access === "invite" && v.role !== "participant") {
195
- pending.push(v);
196
- } else {
197
- connected.push(v);
198
- }
199
- }
200
- return { viewers: connected, pending };
201
- },
202
- approve(viewerId) {
203
- return gate.promote(viewerId);
204
- },
205
- removeViewer(viewerId) {
206
- gate.remove(viewerId);
207
- },
208
- revoke() {
209
- return fireRevoke(revokeReason === "expired" ? "expired" : "manual");
210
- }
211
- };
212
- }
213
-
214
- // src/version.ts
215
- var VERSION = "0.1.0";
216
-
217
- export {
218
- __export,
219
- DEFAULT_ACCESS,
220
- createAccessGate,
221
- SHARE_ORIGIN,
222
- SHARE_PATH_PREFIX,
223
- newShareId,
224
- buildShareUrl,
225
- ShareUrlParseError,
226
- parseShareUrl,
227
- SHARE_SESSION_SCOPE,
228
- parseExpiry,
229
- ConsentError,
230
- HOST_PARTICIPANT_NAME,
231
- createShare,
232
- VERSION
233
- };
@@ -1,188 +0,0 @@
1
- import { ControlState, WriteArbiter, RelayHandle } from 'vibelive-cli';
2
- import { ConsentLedger } from '@pooriaarab/vibe-core';
3
-
4
- /**
5
- * The access gate — the correctness-critical piece of vibeshare.
6
- *
7
- * Background (docs/spec.md · "Access / safety"): a shared link carries an access
8
- * policy. **Spectators** are read-only: they see the live agent output stream but
9
- * can never drive the wrapped agent. **Invite** viewers may be promoted to a full
10
- * vibelive participant (request control → drive).
11
- *
12
- * The invariant vibeshare adds on top of vibelive:
13
- *
14
- * > A spectator NEVER obtains the write token.
15
- *
16
- * We do NOT enforce this in the UI only. We enforce it by never letting a
17
- * spectator reach the write-arbitration state machine: the gate swallows their
18
- * `requestControl` and returns `null` (denied) without touching the arbiter, so a
19
- * spectator is never the driver and never enters the FIFO queue. The same
20
- * `WriteArbiter` vibelive uses to guarantee "never two concurrent writers" is the
21
- * witness we assert against (see `src/access.test.ts`): after a spectator request,
22
- * `arbiter.isDriver(spectatorId) === false` and the queue does not contain them.
23
- *
24
- * This module is pure logic over an injected arbiter — no IO, no network — so the
25
- * invariant is fully unit-testable.
26
- */
27
-
28
- /** Who a share link lets its holders do. */
29
- type AccessMode = 'spectate' | 'invite';
30
- /** Default access when a share is created without an explicit policy. */
31
- declare const DEFAULT_ACCESS: AccessMode;
32
- /** The role a particular viewer currently holds inside a share. */
33
- type ViewerRole = 'spectator' | 'participant';
34
- /** Denial reasons surfaced to callers / tests. */
35
- type DenialReason = 'spectator' | 'not-promoted' | 'unknown';
36
- /** Result of a (possibly-denied) control request. */
37
- type ControlRequestResult = {
38
- readonly ok: true;
39
- readonly state: ControlState;
40
- } | {
41
- readonly ok: false;
42
- readonly reason: DenialReason;
43
- };
44
- interface AccessGateOptions {
45
- /** The real vibelive write-arbiter the gate mediates. */
46
- readonly arbiter: WriteArbiter;
47
- /** The share's access policy. */
48
- readonly access?: AccessMode;
49
- /**
50
- * Optional passphrase. When set, `verifyPassphrase` must pass before a viewer is
51
- * admitted at all (a second factor on top of the capability URL).
52
- */
53
- readonly passphrase?: string;
54
- }
55
- /**
56
- * The access gate. Owns the per-viewer role bookkeeping and mediates every write
57
- * request through the injected vibelive {@link WriteArbiter}. Stateless w.r.t. the
58
- * network — the CLI/MCP wire viewers in and call `requestControl`/`approve`.
59
- */
60
- interface AccessGate {
61
- /** This share's access policy. */
62
- readonly access: AccessMode;
63
- /** True iff a passphrase is required to join this share. */
64
- readonly hasPassphrase: boolean;
65
- /** Admit a new viewer (recorded at the share's default role). Idempotent. */
66
- admit(viewer: {
67
- readonly id: string;
68
- readonly name: string;
69
- }, passphrase?: string): boolean;
70
- /** Remove a viewer (e.g. on disconnect / kick). Releases the token if they hold it. */
71
- remove(viewerId: string): void;
72
- /** True iff this viewer id is known to the gate. */
73
- has(viewerId: string): boolean;
74
- /** This viewer's current role, or `undefined` if unknown. */
75
- role(viewerId: string): ViewerRole | undefined;
76
- /**
77
- * Promote an invite viewer into a full participant (eligible to drive).
78
- * Only meaningful under `access: 'invite'`. Returns false if the viewer is
79
- * unknown or the share is spectate-only.
80
- */
81
- promote(viewerId: string): boolean;
82
- /**
83
- * Request the write token on behalf of a viewer.
84
- *
85
- * - Spectators (any share) → denied (`reason: 'spectator'`), arbiter untouched.
86
- * - Invite viewers not yet promoted → denied (`reason: 'not-promoted'`).
87
- * - Promoted invite viewers → forwards to the arbiter and returns its snapshot.
88
- *
89
- * This is the single chokepoint that upholds "a spectator never obtains the
90
- * write token" against the real vibelive arbiter.
91
- */
92
- requestControl(viewerId: string): ControlRequestResult;
93
- }
94
- declare function createAccessGate(options: AccessGateOptions): AccessGate;
95
-
96
- /**
97
- * The share orchestrator — the public library surface.
98
- *
99
- * vibeshare is the URL/access layer ON TOP of vibelive's engine: it does not
100
- * reimplement transport. {@link createShare} takes an already-running vibelive
101
- * relay (the host's local/LAN ws server) and mints a capability URL + access gate
102
- * + expiry around it. Spectators connect to the relay as ordinary vibelive
103
- * participants (they get the ordered output fan-out) but the access gate ensures
104
- * they can never drive the wrapped agent.
105
- *
106
- * Consent: vibeshare gates fanning session output off the host machine behind the
107
- * `share:session` scope from `@pooriaarab/vibe-core`. `createShare` refuses to
108
- * mint a share unless that grant is present (the CLI grants it; a bare library
109
- * caller must too), which is the same local-first enforcement vibelive uses.
110
- */
111
-
112
- /** The consent scope that gates sharing a session off the host machine. */
113
- declare const SHARE_SESSION_SCOPE = "share:session";
114
- /** Expiry specification: a preset, or a raw duration in milliseconds. */
115
- type ExpirySpec = '1h' | '24h' | number;
116
- /**
117
- * Parse an expiry spec into a duration in milliseconds, or `null` for "never".
118
- *
119
- * Pure and total — unit-tested directly. `undefined` → never expires.
120
- */
121
- declare function parseExpiry(spec: ExpirySpec | undefined): number | null;
122
- /** A viewer of a share, derived from the live vibelive roster + the access gate. */
123
- interface Viewer {
124
- readonly id: string;
125
- readonly name: string;
126
- /** Current role — spectators can't drive; participants can. */
127
- readonly role: ViewerRole;
128
- readonly joinedAt: number;
129
- }
130
- /** A snapshot of a share's audience. */
131
- interface ViewerRoster {
132
- readonly viewers: readonly Viewer[];
133
- /** Invite viewers awaiting host promotion (join requests). */
134
- readonly pending: readonly Viewer[];
135
- }
136
- /** Why a share tore down. Surfaced via {@link ShareOptions.onRevoke}. */
137
- type RevokeReason = 'manual' | 'expired';
138
- interface ShareOptions {
139
- /** The running vibelive relay whose session is being shared. */
140
- readonly session: RelayHandle;
141
- /** Access policy for link holders. Defaults to spectate (read-only). */
142
- readonly access?: AccessMode;
143
- /** Optional expiry (auto-revokes the share after this elapses). */
144
- readonly expiry?: ExpirySpec;
145
- /** Optional passphrase — a second factor on top of the capability URL. */
146
- readonly passphrase?: string;
147
- /**
148
- * Optional explicit consent ledger. Defaults to the relay's own ledger, which
149
- * vibelive seeds with `share:session` granted. If you pass a different ledger it
150
- * must hold the grant or `createShare` throws.
151
- */
152
- readonly consent?: ConsentLedger;
153
- /** Called exactly once when the share tears down (manual revoke or expiry). */
154
- readonly onRevoke?: (reason: RevokeReason) => void;
155
- }
156
- interface ShareHandle {
157
- /** The unguessable capability id backing this share. */
158
- readonly id: string;
159
- /** The human-facing share URL (`https://vibeshare.stream/s/<id>`). */
160
- readonly url: string;
161
- /** This share's access policy. */
162
- readonly access: AccessMode;
163
- /** True once revoked (manually or by expiry). */
164
- readonly revoked: boolean;
165
- /** The underlying access gate (exposed for tests + advanced callers). */
166
- readonly gate: AccessGate;
167
- /** The local/LAN relay URL viewers actually connect over. */
168
- readonly relayUrl: string;
169
- /** Live audience snapshot: connected viewers + pending join requests. */
170
- viewers(): ViewerRoster;
171
- /**
172
- * Promote a pending invite viewer into a participant (lets them request control).
173
- * Invite shares only. Returns false if unknown / spectate share.
174
- */
175
- approve(viewerId: string): boolean;
176
- /** Remove a viewer (disconnect handling / kick). Releases the token if held. */
177
- removeViewer(viewerId: string): void;
178
- /** Tear down the share: clear the expiry timer and close the vibelive relay. */
179
- revoke(): Promise<void>;
180
- }
181
- /** Error thrown when consent for `share:session` has not been granted. */
182
- declare class ConsentError extends Error {
183
- constructor();
184
- }
185
- declare const HOST_PARTICIPANT_NAME = "host";
186
- declare function createShare(options: ShareOptions): ShareHandle;
187
-
188
- export { type AccessMode as A, ConsentError as C, DEFAULT_ACCESS as D, type ExpirySpec as E, HOST_PARTICIPANT_NAME as H, type RevokeReason as R, SHARE_SESSION_SCOPE as S, type Viewer as V, type AccessGate as a, type AccessGateOptions as b, type ControlRequestResult as c, type DenialReason as d, type ShareHandle as e, type ShareOptions as f, type ViewerRole as g, type ViewerRoster as h, createAccessGate as i, createShare as j, parseExpiry as p };