@rigxyz/tapd 0.1.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.
@@ -0,0 +1,542 @@
1
+ import { Ignore } from 'ignore';
2
+ import { GetBindingResponse, PrepareUploadRequest, PrepareUploadResponse, CompleteUploadRequest, CompleteUploadResponse, DownloadUrlResponse, SubmitChangeEvent, SubmitChangesResponse, ChangesSinceResponse, CreateInviteRequest, CreateInviteResponse, BindingInvite, ManifestSnapshotResponse, CreateBindingRequest, CreateBindingResponse, BindingDevice, CapabilityOp, Role, ChangeEvent } from '@tap/core';
3
+
4
+ /**
5
+ * .tapignore loader.
6
+ *
7
+ * Layers:
8
+ * 1. Built-in defaults — common secrets, dependencies, OS noise, and
9
+ * bulky generated artifacts (per design doc § "Large File Policy").
10
+ * A user CAN override these via `!pattern` in their .tapignore.
11
+ * 2. Project .tapignore.
12
+ * 3. Always-excluded paths — Tap's own local metadata. These cannot be
13
+ * re-enabled by `!pattern`; the relay would reject the upload anyway
14
+ * (validateRigPath rejects reserved paths).
15
+ */
16
+
17
+ declare const BUILTIN_IGNORE_LINES: ReadonlyArray<string>;
18
+ declare function loadIgnore(rootDir: string): Ignore;
19
+ /**
20
+ * Hard-exclude check that runs in addition to .tapignore. Tap's own
21
+ * local metadata is invisible to sync no matter what the user puts in
22
+ * their .tapignore — including `!pattern` overrides.
23
+ */
24
+ declare function isAlwaysExcluded(posixPath: string): boolean;
25
+
26
+ /**
27
+ * Recursive directory walker for a rig. Yields:
28
+ * - `files`: candidates for upload (size + posix path relative to root)
29
+ * - `emptyDirs`: paths to mkdir markers (dirs with no tracked children)
30
+ * - `warnings`: paths that won't sync (oversized, symlink, invalid path)
31
+ *
32
+ * Rules enforced:
33
+ * - `.tapignore` (loadIgnore)
34
+ * - Tap's own reserved metadata (isAlwaysExcluded)
35
+ * - MAX_FILE_BYTES (100 MB) — daemon refuses upload above this
36
+ * - Symlinks (lstat-detected) — rejected; warning emitted
37
+ * - validateRigPath — defends against control chars / weird segments
38
+ * before the relay sees them
39
+ *
40
+ * Path strings are POSIX (forward slashes) even on Windows so they
41
+ * match what the relay stores and what the manifest returns.
42
+ */
43
+
44
+ type WalkFile = {
45
+ path: string;
46
+ size: number;
47
+ };
48
+ type WalkWarning = {
49
+ path: string;
50
+ reason: "too_large" | "symlink" | "invalid_path" | "unreadable";
51
+ };
52
+ type WalkResult = {
53
+ files: WalkFile[];
54
+ emptyDirs: string[];
55
+ /**
56
+ * Every directory visited during the walk, including non-empty ones.
57
+ * Used by `scanAndPush` to detect "previously-tracked dir no longer
58
+ * on disk" cases that emptyDirs alone wouldn't catch — e.g. a
59
+ * directory that was empty-and-tracked, then gained files (now
60
+ * non-empty and on disk: should NOT be deleted) vs one that was
61
+ * `rm -rf`d (gone from disk: should be rmdir'd).
62
+ */
63
+ directoriesScanned: string[];
64
+ warnings: WalkWarning[];
65
+ };
66
+ declare function walk(rootDir: string, opts: {
67
+ ignore: Ignore;
68
+ }): WalkResult;
69
+
70
+ /**
71
+ * Thin fetch wrapper around the Phase 1 relay routes.
72
+ *
73
+ * Module-level `createBinding` is unauthenticated (Phase 3 will Clerk-gate
74
+ * it). Everything else binds to a (baseUrl, bindingId, token) triple via
75
+ * the `RelayClient` class, since every binding-scoped route needs the
76
+ * same three values on every call.
77
+ *
78
+ * `RelayError` carries the route + status + parsed body so caller logic
79
+ * can map specific 400s ("object_not_uploaded", "case_collision_with",
80
+ * etc.) to user-facing warnings instead of "something went wrong".
81
+ */
82
+
83
+ type FetchLike$1 = typeof fetch;
84
+ declare class RelayError extends Error {
85
+ readonly route: string;
86
+ readonly status: number;
87
+ readonly body: unknown;
88
+ constructor(route: string, status: number, body: unknown);
89
+ }
90
+ /**
91
+ * Create a new binding. User-authenticated via the X-Tap-User-Id header
92
+ * (Phase 3 placeholder; Clerk JWT later). The response carries the
93
+ * owner's first capability token, returned exactly once.
94
+ */
95
+ declare function createBinding(opts: {
96
+ baseUrl: string;
97
+ request: CreateBindingRequest;
98
+ userId: string;
99
+ email?: string;
100
+ fetch?: FetchLike$1;
101
+ }): Promise<CreateBindingResponse>;
102
+ type RelayClientOptions = {
103
+ baseUrl: string;
104
+ bindingId: string;
105
+ token: string;
106
+ fetch?: FetchLike$1;
107
+ };
108
+ /**
109
+ * Binding-scoped relay client. One instance per active binding; cheap to
110
+ * construct, holds no resources beyond the closed-over fetch impl.
111
+ */
112
+ declare class RelayClient {
113
+ private readonly baseUrl;
114
+ private readonly bindingId;
115
+ private readonly token;
116
+ private readonly fetch;
117
+ constructor(opts: RelayClientOptions);
118
+ private authHeaders;
119
+ private bindingPath;
120
+ getBinding(): Promise<GetBindingResponse>;
121
+ prepareUpload(body: PrepareUploadRequest): Promise<PrepareUploadResponse>;
122
+ completeUpload(body: CompleteUploadRequest): Promise<CompleteUploadResponse>;
123
+ downloadUrl(hash: string): Promise<DownloadUrlResponse>;
124
+ /** PUT bytes to a presigned URL. Throws on non-2xx. */
125
+ putObjectBytes(uploadUrl: string, bytes: Uint8Array | Buffer): Promise<void>;
126
+ /** GET bytes from a presigned URL. Throws on non-2xx. */
127
+ getObjectBytes(downloadUrl: string): Promise<Buffer>;
128
+ submitChanges(events: ReadonlyArray<SubmitChangeEvent>): Promise<SubmitChangesResponse>;
129
+ listChanges(opts?: {
130
+ after?: string;
131
+ limit?: number;
132
+ }): Promise<ChangesSinceResponse>;
133
+ /**
134
+ * Mint a new invite (owner-only). Returns the secret + a ready-to-share
135
+ * accept URL, exactly once. Caller must surface to the user immediately
136
+ * and rely on `listInvites()` later if they want to refer back without
137
+ * the secret.
138
+ */
139
+ mintInvite(body: CreateInviteRequest): Promise<CreateInviteResponse>;
140
+ listInvites(): Promise<{
141
+ invites: BindingInvite[];
142
+ }>;
143
+ revokeInvite(inviteId: string): Promise<{
144
+ revoked: boolean;
145
+ alreadyRevoked: boolean;
146
+ }>;
147
+ getManifest(opts?: {
148
+ after?: string;
149
+ limit?: number;
150
+ }): Promise<ManifestSnapshotResponse>;
151
+ }
152
+
153
+ /**
154
+ * Local sync metadata — one SQLite file per binding, at
155
+ * `<rig>/.rig/tap/state.local.db`. Machine-local; never synced.
156
+ *
157
+ * Tracks:
158
+ * - per-path: last_applied_change_id, last_seen_hash, local_dirty,
159
+ * last_scan_at
160
+ * - cursor: the change_event id we've consumed up to (so a daemon
161
+ * restart resumes from the right place)
162
+ *
163
+ * Schema is small and applied inline on open — no migrations file.
164
+ */
165
+ type LocalPathState = {
166
+ path: string;
167
+ lastAppliedChangeId: string | null;
168
+ lastSeenHash: string | null;
169
+ localDirty: boolean;
170
+ lastScanAt: string | null;
171
+ };
172
+ type StateDb = {
173
+ /** Cursor we've applied up to, or "chg_0" if nothing yet. */
174
+ getCursor(): string;
175
+ setCursor(cursor: string): void;
176
+ upsertPath(state: LocalPathState): void;
177
+ getPath(path: string): LocalPathState | null;
178
+ deletePath(path: string): void;
179
+ /** Enumerate every tracked path. Cheap — state DBs are small. */
180
+ listPaths(): string[];
181
+ setMeta(key: string, value: string): void;
182
+ getMeta(key: string): string | null;
183
+ close(): void;
184
+ };
185
+ declare function openStateDb(dbPath: string): StateDb;
186
+
187
+ /**
188
+ * .rig/tap-binding.local.json — machine-local per-binding pointer.
189
+ *
190
+ * Holds the bindingId, the device's capability token, and where to find
191
+ * the relay. NEVER synced (validated by isAlwaysExcluded in tapignore.ts;
192
+ * the relay also rejects the path via validateRigPath).
193
+ *
194
+ * Tokens live here in plaintext for now. A future revision can move
195
+ * them to the OS keychain; the file structure is designed to make that
196
+ * swap localized.
197
+ */
198
+ declare const BINDING_CONFIG_PATH = ".rig/tap-binding.local.json";
199
+ type BindingConfig = {
200
+ bindingId: string;
201
+ relayUrl: string;
202
+ deviceId: string;
203
+ /** Capability-token secret. Returned once by the relay, never re-derivable. */
204
+ token: string;
205
+ };
206
+ declare function bindingConfigFile(rootDir: string): string;
207
+ declare function readBindingConfig(rootDir: string): BindingConfig | null;
208
+ declare function writeBindingConfig(rootDir: string, config: BindingConfig): void;
209
+ /**
210
+ * Thrown when an operation requires a bound workspace but
211
+ * `.rig/tap-binding.local.json` is missing. All three callers (`invite`,
212
+ * `status`, `uninit`) raise the same class so bin.ts maps it to a
213
+ * single `not_initialized` JSON error code for rig.
214
+ */
215
+ declare class NotInitializedError extends Error {
216
+ readonly rootDir: string;
217
+ constructor(rootDir: string);
218
+ }
219
+
220
+ /**
221
+ * Streaming sha256 of a file path, returned in the relay's wire format
222
+ * ("sha256:" + 64 hex). Used by walker → uploader and by watcher → uploader.
223
+ */
224
+ declare function hashFile(absPath: string): Promise<string>;
225
+
226
+ /**
227
+ * `tapd init` — bootstrap a fresh binding from the current working
228
+ * directory.
229
+ *
230
+ * Steps (mapped to design doc § "Init Flow"):
231
+ * 1. Refuse if .rig/tap-binding.local.json already exists.
232
+ * 2. Walk the rig, applying .tapignore + reserved-path rules.
233
+ * 3. Hash every tracked file (de-duplicated by hash so identical
234
+ * content uploads once).
235
+ * 4. For each unique hash: prepare-upload → PUT bytes → complete-upload.
236
+ * 5. Submit one batched POST /changes with one event per file +
237
+ * one mkdir per empty-dir marker.
238
+ * 6. Write .rig/tap-binding.local.json (mode 0600).
239
+ * 7. Populate .rig/tap/state.local.db with last_applied_change_id
240
+ * per path, and the resulting cursor.
241
+ *
242
+ * Returns a summary with the binding, the owner token (so the caller
243
+ * CLI can print it once), upload + change counts, and any walker
244
+ * warnings (oversized files / symlinks / invalid paths). Caller is
245
+ * responsible for surfacing the warnings to the user.
246
+ */
247
+
248
+ type InitOptions = {
249
+ rootDir: string;
250
+ relayUrl: string;
251
+ ownerUserId: string;
252
+ bindingName: string;
253
+ deviceLabel?: string;
254
+ /** Test seam: override fetch (used to drive the in-process relay). */
255
+ fetch?: typeof fetch;
256
+ };
257
+ type InitResult = {
258
+ binding: CreateBindingResponse["binding"];
259
+ device: CreateBindingResponse["device"];
260
+ /** Owner secret. Returned exactly once. The caller should display it. */
261
+ ownerSecret: string;
262
+ uploadedHashes: number;
263
+ reusedHashes: number;
264
+ submittedEvents: number;
265
+ warnings: WalkResult["warnings"];
266
+ /** Cursor written to state.local.db after the initial submit. */
267
+ cursor: string;
268
+ };
269
+ declare class AlreadyInitializedError extends Error {
270
+ readonly path: string;
271
+ constructor(path: string);
272
+ }
273
+ declare function init(opts: InitOptions): Promise<InitResult>;
274
+
275
+ /**
276
+ * `tapd join` — accept an invite URL and set up a fresh local binding.
277
+ *
278
+ * Unlike `tapd init`, join does NOT touch the rig contents:
279
+ * 1. Refuse if .rig/tap-binding.local.json already exists.
280
+ * 2. Parse the invite URL → (baseUrl, secret).
281
+ * 3. POST /v1/invites/:secret/accept with the user's identity header,
282
+ * receive {bindingId, device, token}.
283
+ * 4. Write .rig/tap-binding.local.json (mode 0600) IMMEDIATELY — same
284
+ * "secret-returned-once survives downstream failure" discipline as
285
+ * `tapd init`.
286
+ * 5. Open .rig/tap/state.local.db with cursor `chg_0`.
287
+ *
288
+ * After join, `tapd start` will:
289
+ * - applyOnce: pull the remote manifest + change log
290
+ * - scanAndPush: push any pre-existing local divergence
291
+ *
292
+ * That covers both the bootstrap-from-empty-dir case and the
293
+ * "user already has some files locally" merge case.
294
+ */
295
+
296
+ type JoinOptions = {
297
+ rootDir: string;
298
+ inviteUrl: string;
299
+ userId: string;
300
+ email?: string;
301
+ deviceLabel?: string;
302
+ /** Test seam: override fetch. */
303
+ fetch?: typeof fetch;
304
+ };
305
+ type JoinResult = {
306
+ bindingId: string;
307
+ device: BindingDevice;
308
+ /** Capability secret. Returned exactly once. */
309
+ tokenSecret: string;
310
+ /** Whether the invite granted a member role (vs pure-capability). */
311
+ becameMember: boolean;
312
+ };
313
+ declare class AlreadyJoinedError extends Error {
314
+ readonly path: string;
315
+ constructor(path: string);
316
+ }
317
+ declare class InvalidInviteUrlError extends Error {
318
+ readonly url: string;
319
+ constructor(url: string);
320
+ }
321
+ /**
322
+ * Parse `https://relay.example/v1/invites/<secret>/accept` into
323
+ * (baseUrl, secret). Trailing slash + missing /accept both accepted.
324
+ */
325
+ declare function parseInviteUrl(url: string): {
326
+ baseUrl: string;
327
+ secret: string;
328
+ };
329
+ declare function join(opts: JoinOptions): Promise<JoinResult>;
330
+
331
+ /**
332
+ * `tapd invite` — owner-side helpers for the invite lifecycle.
333
+ *
334
+ * Reads .rig/tap-binding.local.json for the relay URL + token, builds
335
+ * a RelayClient, and calls the matching endpoint. Designed to be a
336
+ * thin wrapper that the CLI dispatcher (bin.ts) can call without
337
+ * thinking about plumbing.
338
+ */
339
+
340
+ type MintInviteOptions = {
341
+ rootDir: string;
342
+ ops: ReadonlyArray<CapabilityOp>;
343
+ role?: Role | null;
344
+ pathGlobs?: ReadonlyArray<string>;
345
+ ttlSeconds?: number;
346
+ maxUses?: number;
347
+ emailConstraint?: string;
348
+ label?: string;
349
+ fetch?: typeof fetch;
350
+ };
351
+ declare function mint(opts: MintInviteOptions): Promise<CreateInviteResponse>;
352
+ declare function list(opts: {
353
+ rootDir: string;
354
+ fetch?: typeof fetch;
355
+ }): Promise<BindingInvite[]>;
356
+ declare function revoke(opts: {
357
+ rootDir: string;
358
+ inviteId: string;
359
+ fetch?: typeof fetch;
360
+ }): Promise<{
361
+ revoked: boolean;
362
+ alreadyRevoked: boolean;
363
+ }>;
364
+
365
+ /**
366
+ * Apply remote change events to the local filesystem.
367
+ *
368
+ * Phase 2 keeps this simple: last-write-wins, no conflict materialization
369
+ * (Phase 4 adds it). Echo suppression is handled via the state DB:
370
+ * if the remote event's hash matches our last_seen_hash for the path,
371
+ * we know it's our own write coming back through the poll loop and skip
372
+ * the download.
373
+ *
374
+ * Materialization is atomic: download to `<file>.tap-tmp.<rand>` then
375
+ * `rename` into place. Atomic on POSIX; on Windows, `fs.rename` falls
376
+ * back to MoveFileEx-style replace.
377
+ */
378
+
379
+ /** State-DB meta key — running count of conflicts surfaced by `tapd status`. */
380
+ declare const STATE_KEY_CONFLICT_COUNT = "conflict_count";
381
+ /**
382
+ * Sidecar path for a conflict materialization. Original extension is
383
+ * preserved at the end so editor highlighting still works.
384
+ *
385
+ * decisions/pricing.md → decisions/pricing.conflict-from.dev_xyz.chg_456.md
386
+ *
387
+ * Device label resolution lands when GET /v1/bindings/:id/devices does
388
+ * (Phase 5); until then we use the raw deviceId, which is at least an
389
+ * audit-stable reference.
390
+ */
391
+ declare function conflictSidecarPath(path: string, ev: {
392
+ id: string;
393
+ actorDeviceId: string | null;
394
+ }): string;
395
+ type ApplyEventError = {
396
+ eventId: string;
397
+ path: string;
398
+ op: string;
399
+ reason: string;
400
+ };
401
+ type ApplyResult = {
402
+ applied: number;
403
+ echoSkipped: number;
404
+ errored: number;
405
+ events: ReadonlyArray<ChangeEvent>;
406
+ errors: ReadonlyArray<ApplyEventError>;
407
+ };
408
+ /**
409
+ * One iteration of the apply loop: poll `GET /changes?after=<cursor>`,
410
+ * materialize each event, update the cursor.
411
+ *
412
+ * Per-event errors (hash mismatch, file/directory collision, disk full,
413
+ * etc.) are caught, logged, and recorded to the state DB — the cursor
414
+ * still advances past the failed event so the daemon doesn't loop
415
+ * forever on the same broken row. Phase 4 conflict materialization
416
+ * will replace the "skip + log" behavior for the file/dir collision
417
+ * case; for now it's the safe default.
418
+ */
419
+ declare function applyOnce(opts: {
420
+ rootDir: string;
421
+ client: RelayClient;
422
+ stateDb: StateDb;
423
+ limit?: number;
424
+ }): Promise<ApplyResult>;
425
+
426
+ /**
427
+ * Local → remote sync direction: compute the diff between the file
428
+ * system and the state DB, upload any new bytes, submit one batched
429
+ * `POST /changes`.
430
+ *
431
+ * Phase 2 calls this on startup (initial reconcile) and on each
432
+ * watcher event (debounced).
433
+ */
434
+
435
+ type ScanAndPushResult = {
436
+ /** Bytes uploaded this cycle. */
437
+ uploaded: number;
438
+ /** Hashes that were already on the relay (no PUT needed). */
439
+ reused: number;
440
+ /** Change events submitted (writes + deletes + mkdir + rmdir). */
441
+ submitted: ReadonlyArray<ChangeEvent>;
442
+ /** Warnings from the walker (symlinks / oversize / invalid) +
443
+ * mid-scan mutations detected before upload. */
444
+ warnings: ReadonlyArray<{
445
+ path: string;
446
+ reason: string;
447
+ }>;
448
+ };
449
+ declare function scanAndPush(opts: {
450
+ rootDir: string;
451
+ client: RelayClient;
452
+ stateDb: StateDb;
453
+ ignore: Ignore;
454
+ }): Promise<ScanAndPushResult>;
455
+
456
+ /**
457
+ * SSE consumer for the relay's /stream endpoint.
458
+ *
459
+ * Phase 4c — push delivery so applyOnce fires within milliseconds of a
460
+ * remote change instead of waiting on the poll interval. The poll loop
461
+ * remains the source of truth and keeps running underneath as a
462
+ * fallback (slower cadence is fine when SSE is healthy).
463
+ *
464
+ * Reconnect semantics:
465
+ * - Server-side disconnect (stream end) or client-side fetch error
466
+ * → backoff 1s, 2s, 4s, 8s, 16s, capped at 30s; reset on success.
467
+ * - Heartbeat watchdog: if no event/heartbeat lands within
468
+ * `heartbeatTimeoutMs` (default 45s), abort + reconnect.
469
+ * - `stop()` aborts the in-flight fetch + cancels pending reconnects.
470
+ *
471
+ * The subscriber NEVER advances the cursor — it just nudges applyOnce.
472
+ * Cursor management stays in the apply loop, which reads /changes as
473
+ * the durable source of truth.
474
+ */
475
+
476
+ type FetchLike = typeof fetch;
477
+ type SseSubscriberOptions = {
478
+ baseUrl: string;
479
+ bindingId: string;
480
+ token: string;
481
+ onChange: (change: ChangeEvent) => void | Promise<void>;
482
+ onSubscribed?: (info: {
483
+ bindingId: string;
484
+ pathGlobs: string[];
485
+ }) => void;
486
+ onError?: (err: Error) => void;
487
+ fetch?: FetchLike;
488
+ /** Watchdog window. Default 45s — matches a server heartbeat of 15s × 3. */
489
+ heartbeatTimeoutMs?: number;
490
+ /** Backoff for the Nth reconnect attempt (0-indexed). */
491
+ reconnectDelayMs?: (attempt: number) => number;
492
+ };
493
+ type SseSubscriber = {
494
+ stop(): Promise<void>;
495
+ };
496
+ declare function subscribe(opts: SseSubscriberOptions): SseSubscriber;
497
+
498
+ /**
499
+ * `tapd start` — the daemon main loop.
500
+ *
501
+ * Three concurrent jobs (Phase 4):
502
+ * - SSE subscriber: opens /v1/bindings/:id/stream and triggers
503
+ * applyOnce on every push. Fast path — latency is network +
504
+ * download time, not pollSeconds.
505
+ * - poll loop: every `pollSeconds`, runs applyOnce as a fallback
506
+ * for SSE outages (and for multi-relay deployments where the SSE
507
+ * bus is single-process; see relay's event-bus.ts).
508
+ * - watcher: chokidar emits add / change / unlink / addDir / unlinkDir.
509
+ * Debounced rescan calls `scanAndPush` so file diffs flow upstream.
510
+ *
511
+ * All three share the state DB and the same RelayClient. Echo
512
+ * suppression in `applyEvent` prevents a self-write from being applied
513
+ * back over the same bytes.
514
+ */
515
+
516
+ type StartOptions = {
517
+ rootDir: string;
518
+ pollSeconds?: number;
519
+ /** Quiescence window before the watcher triggers a rescan. */
520
+ debounceMs?: number;
521
+ /** Test seam — override fetch (passed to RelayClient + SSE subscriber). */
522
+ fetch?: typeof fetch;
523
+ /** Test seam — disable the chokidar watcher (poll loop only). */
524
+ noWatch?: boolean;
525
+ /** Test seam — disable SSE (poll-only mode). */
526
+ noSse?: boolean;
527
+ /** SSE watchdog window in ms. Default 45s. */
528
+ sseHeartbeatTimeoutMs?: number;
529
+ };
530
+ type DaemonHandle = {
531
+ /** Cleanly shut down the watcher + poll loop + close the state DB. */
532
+ stop(): Promise<void>;
533
+ /** Force one apply cycle now. Useful for tests + `tapd status`. */
534
+ applyNow(): Promise<ApplyResult>;
535
+ /** Force one upstream scan+push now. Useful for tests + `tapd status`. */
536
+ scanNow(): Promise<ScanAndPushResult>;
537
+ /** Underlying state DB. Caller MUST NOT close it — stop() does that. */
538
+ stateDb: StateDb;
539
+ };
540
+ declare function start(opts: StartOptions): Promise<DaemonHandle>;
541
+
542
+ export { AlreadyInitializedError, AlreadyJoinedError, type ApplyResult, BINDING_CONFIG_PATH, BUILTIN_IGNORE_LINES, type BindingConfig, type DaemonHandle, type InitOptions, type InitResult, InvalidInviteUrlError, type JoinOptions, type JoinResult, type LocalPathState, type MintInviteOptions, NotInitializedError, RelayClient, type RelayClientOptions, RelayError, STATE_KEY_CONFLICT_COUNT, type ScanAndPushResult, type SseSubscriber, type SseSubscriberOptions, type StartOptions, type StateDb, type WalkFile, type WalkResult, type WalkWarning, applyOnce, bindingConfigFile, conflictSidecarPath, createBinding, hashFile, init, isAlwaysExcluded, join, list as listInvites, loadIgnore, mint as mintInvite, openStateDb, parseInviteUrl, readBindingConfig, revoke as revokeInvite, scanAndPush, subscribe as sseSubscribe, start, walk, writeBindingConfig };
package/dist/index.js ADDED
@@ -0,0 +1,64 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ AlreadyInitializedError,
4
+ AlreadyJoinedError,
5
+ BINDING_CONFIG_PATH,
6
+ BUILTIN_IGNORE_LINES,
7
+ InvalidInviteUrlError,
8
+ NotInitializedError,
9
+ RelayClient,
10
+ RelayError,
11
+ STATE_KEY_CONFLICT_COUNT,
12
+ applyOnce,
13
+ bindingConfigFile,
14
+ conflictSidecarPath,
15
+ createBinding,
16
+ hashFile,
17
+ init,
18
+ isAlwaysExcluded,
19
+ join,
20
+ list,
21
+ loadIgnore,
22
+ mint,
23
+ openStateDb,
24
+ parseInviteUrl,
25
+ readBindingConfig,
26
+ revoke,
27
+ scanAndPush,
28
+ start,
29
+ subscribe,
30
+ walk,
31
+ writeBindingConfig
32
+ } from "./chunk-RQC73B5Y.js";
33
+ export {
34
+ AlreadyInitializedError,
35
+ AlreadyJoinedError,
36
+ BINDING_CONFIG_PATH,
37
+ BUILTIN_IGNORE_LINES,
38
+ InvalidInviteUrlError,
39
+ NotInitializedError,
40
+ RelayClient,
41
+ RelayError,
42
+ STATE_KEY_CONFLICT_COUNT,
43
+ applyOnce,
44
+ bindingConfigFile,
45
+ conflictSidecarPath,
46
+ createBinding,
47
+ hashFile,
48
+ init,
49
+ isAlwaysExcluded,
50
+ join,
51
+ list as listInvites,
52
+ loadIgnore,
53
+ mint as mintInvite,
54
+ openStateDb,
55
+ parseInviteUrl,
56
+ readBindingConfig,
57
+ revoke as revokeInvite,
58
+ scanAndPush,
59
+ subscribe as sseSubscribe,
60
+ start,
61
+ walk,
62
+ writeBindingConfig
63
+ };
64
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "@rigxyz/tapd",
3
+ "version": "0.1.0",
4
+ "description": "Local daemon for the hosted Tap relay — watches files, hashes content, uploads/downloads objects, applies remote changes. Pairs with @rigxyz/cli for shared-rig sync.",
5
+ "license": "MIT",
6
+ "author": "Dylan Bourgeois <dtsbourg@gmail.com>",
7
+ "homepage": "https://github.com/dtsbourg/tap#readme",
8
+ "bugs": "https://github.com/dtsbourg/tap/issues",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/dtsbourg/tap.git",
12
+ "directory": "packages/tapd"
13
+ },
14
+ "keywords": ["tap", "tapd", "rig", "sync", "daemon"],
15
+ "type": "module",
16
+ "bin": {
17
+ "tapd": "./dist/bin.js"
18
+ },
19
+ "files": ["dist", "README.md"],
20
+ "publishConfig": {
21
+ "access": "public"
22
+ },
23
+ "scripts": {
24
+ "build": "tsup",
25
+ "dev": "tsup --watch",
26
+ "typecheck": "tsc --noEmit",
27
+ "test": "vitest run",
28
+ "test:watch": "vitest"
29
+ },
30
+ "dependencies": {
31
+ "better-sqlite3": "^11.5.0",
32
+ "chokidar": "^4.0.1",
33
+ "ignore": "^5.3.0",
34
+ "kleur": "^4.1.5"
35
+ },
36
+ "devDependencies": {
37
+ "@tap/core": "workspace:*",
38
+ "@tap/relay": "workspace:*",
39
+ "@types/better-sqlite3": "^7.6.12",
40
+ "@types/pg": "^8.20.0",
41
+ "pg": "^8.21.0",
42
+ "tsup": "^8.3.5",
43
+ "typescript": "^5.7.2",
44
+ "vitest": "^2.1.9"
45
+ }
46
+ }