@toon-protocol/rig 2.1.0 → 2.3.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/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { G as GitObjectType, P as Publisher, F as FeeRates, a as PublishReceipt } from './publisher-Cdr1H1hg.js';
2
- export { C as COMMENT_KIND, b as GitObject, c as GitObjectUpload, M as MAX_OBJECT_SIZE, R as REPOSITORY_STATE_KIND, S as StatusKind, U as UnsignedEvent, d as UploadReceipt, e as buildComment, f as buildIssue, g as buildPatch, h as buildRepoAnnouncement, i as buildRepoRefs, j as buildStatus, k as createGitBlob, l as createGitCommit, m as createGitTag, n as createGitTree, o as hashGitObject } from './publisher-Cdr1H1hg.js';
1
+ import { c as GitObjectType, P as Publisher, F as FeeRates, b as PublishReceipt } from './publisher-BtVceNeI.js';
2
+ export { C as COMMENT_KIND, d as GitObject, G as GitObjectUpload, M as MAINTAINERS_TAG, e as MAX_OBJECT_SIZE, R as REPOSITORY_STATE_KIND, S as StatusKind, U as UnsignedEvent, a as UploadReceipt, f as authorizedStatusAuthors, g as buildComment, h as buildIssue, i as buildPatch, j as buildRepoAnnouncement, k as buildRepoRefs, l as buildStatus, m as createGitBlob, n as createGitCommit, o as createGitTag, p as createGitTree, q as hashGitObject, r as parseMaintainers } from './publisher-BtVceNeI.js';
3
3
  import '@toon-protocol/core/nip34';
4
4
 
5
5
  /**
@@ -209,6 +209,18 @@ interface NostrEvent {
209
209
  content: string;
210
210
  sig: string;
211
211
  }
212
+ /** NIP-01 subscription filter (only the fields rig's readers send). */
213
+ interface NostrFilter {
214
+ ids?: string[];
215
+ kinds?: number[];
216
+ authors?: string[];
217
+ '#d'?: string[];
218
+ /** Repo address tag filter, e.g. `30617:<owner>:<repoId>` (#278 tracker). */
219
+ '#a'?: string[];
220
+ /** Event-reference tag filter (#278 tracker: statuses + comments). */
221
+ '#e'?: string[];
222
+ limit?: number;
223
+ }
212
224
  /**
213
225
  * Minimal structural WebSocket type — satisfied by the WHATWG WebSocket
214
226
  * global (Node >= 22 / undici, browsers) and by the `ws` package.
@@ -263,6 +275,12 @@ interface RemoteState {
263
275
  description: string | null;
264
276
  /** Relay URLs advertised in the announcement `relays` tag(s). */
265
277
  relays: string[];
278
+ /**
279
+ * Declared maintainer pubkeys (hex) from the announcement `maintainers`
280
+ * tag (#287). Does NOT include the owner (an implicit maintainer). Empty
281
+ * when unannounced or owner-only.
282
+ */
283
+ maintainers: string[];
266
284
  /**
267
285
  * Resolve SHAs to Arweave txIds: served from the `arweave` tag map when
268
286
  * present, otherwise via the GraphQL Git-SHA resolver. SHAs that resolve
@@ -270,6 +288,12 @@ interface RemoteState {
270
288
  */
271
289
  resolveMissing(shas: string[]): Promise<Map<string, string>>;
272
290
  }
291
+ /**
292
+ * Query one relay: send a REQ, collect EVENTs until EOSE, then CLOSE.
293
+ * Mirrors rig's `queryRelay` (partial results on timeout / early close).
294
+ * Exported for reuse by the network-bootstrap discovery (kind:10032).
295
+ */
296
+ declare function queryRelay(relayUrl: string, filter: NostrFilter, timeoutMs: number, webSocketFactory: WebSocketFactory): Promise<NostrEvent[]>;
273
297
  /**
274
298
  * Fetch the remote repository state from the relay(s).
275
299
  *
@@ -283,6 +307,251 @@ interface RemoteState {
283
307
  */
284
308
  declare function fetchRemoteState(options: FetchRemoteStateOptions): Promise<RemoteState>;
285
309
 
310
+ /**
311
+ * The Git-from-TOON READ pipeline core (#278): download git object bodies
312
+ * from Arweave gateways, verify them against their SHA-1, and walk the
313
+ * object graph to prove a ref closure is complete.
314
+ *
315
+ * This is the CLI counterpart of rig-web's proven browser read path
316
+ * (`web/arweave-client.ts` + `web/git-objects.ts` + the commit walker) — the
317
+ * logic is mirrored, not imported, because the SPA package is not a library.
318
+ *
319
+ * Everything here is FREE (reads only: Arweave gateway GETs + the GraphQL
320
+ * Git-SHA resolver) and pure of git — materializing objects into a real
321
+ * repository lives in ./materialize.ts.
322
+ *
323
+ * INTEGRITY IS NON-NEGOTIABLE: an Arweave upload stores the object BODY
324
+ * (content after the envelope NUL). Re-wrapping the body as each of the four
325
+ * git object types and comparing the envelope SHA-1 against the expected SHA
326
+ * both AUTHENTICATES the bytes and DISCOVERS the object's type in one step —
327
+ * a body that matches under no type is rejected as corrupt/tampered, never
328
+ * written.
329
+ */
330
+
331
+ /** A downloaded git object: SHA-verified body + the type that verified it. */
332
+ interface FetchedObject {
333
+ /** Full 40-hex SHA-1 (verified against the body). */
334
+ sha: string;
335
+ type: GitObjectType;
336
+ /** Raw object body (content only, no envelope header). May be binary. */
337
+ body: Buffer;
338
+ }
339
+ /** WHATWG-fetch seam (injectable for tests). */
340
+ type FetchLike = (url: string, init?: {
341
+ signal?: AbortSignal;
342
+ }) => Promise<{
343
+ ok: boolean;
344
+ arrayBuffer(): Promise<ArrayBuffer>;
345
+ }>;
346
+ interface GatewayFetchOptions {
347
+ /** Ordered gateway base URLs (default: the shared preference list). */
348
+ gateways?: readonly string[];
349
+ /** fetch implementation (default: global fetch). */
350
+ fetchFn?: FetchLike;
351
+ /** Per-request timeout in milliseconds. */
352
+ timeoutMs?: number;
353
+ }
354
+ interface DownloadOptions extends GatewayFetchOptions {
355
+ /** Maximum concurrent gateway downloads (default {@link DEFAULT_CONCURRENCY}). */
356
+ concurrency?: number;
357
+ /** Progress callback, called once per finished object. */
358
+ onObject?: (done: number, total: number) => void;
359
+ }
360
+ /** Result of {@link downloadGitObjects}. */
361
+ interface DownloadResult {
362
+ /** SHA → verified object, for every SHA that could be downloaded. */
363
+ objects: Map<string, FetchedObject>;
364
+ /** SHAs whose txId 404'd/errored on EVERY gateway (propagation lag). */
365
+ unavailable: {
366
+ sha: string;
367
+ txId: string;
368
+ }[];
369
+ }
370
+ /** Default parallel-download cap. */
371
+ declare const DEFAULT_CONCURRENCY = 8;
372
+ /**
373
+ * A downloaded body did not hash to its expected SHA under ANY git object
374
+ * type — corrupt or tampered content. The clone/fetch pipelines treat this
375
+ * as a hard failure: nothing is written.
376
+ */
377
+ declare class ObjectIntegrityError extends Error {
378
+ /** The objects that failed verification. */
379
+ readonly objects: {
380
+ sha: string;
381
+ txId: string;
382
+ }[];
383
+ constructor(
384
+ /** The objects that failed verification. */
385
+ objects: {
386
+ sha: string;
387
+ txId: string;
388
+ }[]);
389
+ }
390
+ /**
391
+ * Verify a downloaded body against its expected SHA-1 by trying the four git
392
+ * envelope types. Returns the verified object, or null when no type matches
393
+ * (corrupt/tampered bytes).
394
+ */
395
+ declare function verifyObjectBody(expectedSha: string, bytes: Uint8Array): FetchedObject | null;
396
+ /**
397
+ * Fetch raw bytes for an Arweave tx id, trying each gateway in preference
398
+ * order. Returns null when every gateway fails (404 / error / timeout).
399
+ */
400
+ declare function fetchTxBytes(txId: string, options?: GatewayFetchOptions): Promise<Uint8Array | null>;
401
+ /**
402
+ * Download + verify a batch of git objects (sha → Arweave txId) with a
403
+ * concurrency cap and per-gateway fallback.
404
+ *
405
+ * Objects that 404 on every gateway are reported in `unavailable` (Arweave
406
+ * propagation lag — the caller decides whether that is fatal). Objects whose
407
+ * bytes fail SHA-1 verification throw {@link ObjectIntegrityError}: corrupt
408
+ * content is NEVER returned.
409
+ */
410
+ declare function downloadGitObjects(entries: Iterable<[sha: string, txId: string]>, options?: DownloadOptions): Promise<DownloadResult>;
411
+ /**
412
+ * SHAs an object references inside the SAME repository:
413
+ * - commit → its tree + parents
414
+ * - tree → entry SHAs, EXCEPT gitlinks (mode 160000: submodule commits
415
+ * live in another repository and are never present — git fsck
416
+ * skips them too)
417
+ * - tag → the tagged object
418
+ * - blob → nothing
419
+ */
420
+ declare function referencedShas(object: FetchedObject): string[];
421
+ /** Result of {@link walkClosure}. */
422
+ interface ClosureResult {
423
+ /** Every SHA reachable from the tips that lives in `objects`. */
424
+ reachable: Set<string>;
425
+ /** Reachable SHAs found NEITHER in `objects` nor in `presentLocally`. */
426
+ missing: string[];
427
+ }
428
+ /**
429
+ * Walk the object graph from the ref tips over the downloaded object set and
430
+ * report which reachable SHAs are missing. `presentLocally` marks SHAs that
431
+ * already exist in the destination repository — the walk does not descend
432
+ * into them (a consistent local repo carries its own closure; the same
433
+ * assumption `git fetch` makes).
434
+ */
435
+ declare function walkClosure(tips: Iterable<string>, objects: ReadonlyMap<string, FetchedObject>, presentLocally?: ReadonlySet<string>): ClosureResult;
436
+
437
+ /**
438
+ * The shared clone/fetch object-collection engine (#278).
439
+ *
440
+ * Given the remote's ref tips and its kind:30618 sha→Arweave-txId map, gather
441
+ * every object the refs need:
442
+ *
443
+ * 1. download the mapped objects the destination repo doesn't already have
444
+ * (parallel, gateway fallback chain, SHA-verified — ./object-fetch.ts);
445
+ * 2. walk the object graph from the tips (./object-fetch.ts walkClosure) —
446
+ * the local repository's objects count as present (git fetch's own
447
+ * assumption: a consistent repo carries its own closure);
448
+ * 3. SHAs the map doesn't cover are resolved through the Arweave GraphQL
449
+ * Git-SHA resolver (RemoteState.resolveMissing) and downloaded, looping
450
+ * until the closure is complete or no progress can be made.
451
+ *
452
+ * The result separates FATAL gaps (reachable objects that could not be
453
+ * obtained — usually Arweave gateway propagation lag, 10–20 min for fresh
454
+ * pushes) from harmless ones (mapped-but-unreachable objects, e.g. history
455
+ * that was force-pushed away). Corrupt objects throw ObjectIntegrityError
456
+ * from the download layer and never surface here.
457
+ */
458
+
459
+ /** A reachable object that could not be obtained. */
460
+ interface MissingObject {
461
+ sha: string;
462
+ /** The txId that failed on every gateway, or null when no txId resolved. */
463
+ txId: string | null;
464
+ }
465
+ interface CollectRepoObjectsOptions extends DownloadOptions {
466
+ /** Ref tip SHAs (commits or annotated tags) the closure must reach. */
467
+ tips: string[];
468
+ /** kind:30618 `arweave` tag map: git SHA → Arweave txId. */
469
+ shaToTxId: ReadonlyMap<string, string>;
470
+ /** GraphQL fallback for SHAs the map doesn't cover (RemoteState.resolveMissing). */
471
+ resolveMissing: (shas: string[]) => Promise<Map<string, string>>;
472
+ /** SHAs already present in the destination repository (fetch delta). */
473
+ presentLocally?: ReadonlySet<string>;
474
+ }
475
+ interface CollectRepoObjectsResult {
476
+ /** Verified objects to write, keyed by SHA. */
477
+ objects: Map<string, FetchedObject>;
478
+ /** Reachable SHAs that could not be obtained — FATAL for clone/fetch. */
479
+ missing: MissingObject[];
480
+ /** Mapped-but-unreachable SHAs that failed to download — warn only. */
481
+ skippedUnavailable: {
482
+ sha: string;
483
+ txId: string;
484
+ }[];
485
+ }
486
+ /** Collect (download + verify + close over) the objects the ref tips need. */
487
+ declare function collectRepoObjects(options: CollectRepoObjectsOptions): Promise<CollectRepoObjectsResult>;
488
+ /**
489
+ * The honest propagation-lag error text: which SHAs are unobtainable and why
490
+ * retrying later is the expected remedy.
491
+ */
492
+ declare function missingObjectsMessage(missing: MissingObject[], context: string): string;
493
+
494
+ /**
495
+ * Materialize downloaded git objects into a REAL repository (#278).
496
+ *
497
+ * Objects are written through git's own plumbing — `git hash-object -w
498
+ * --stdin -t <type>` with the raw body on stdin — so git computes, validates
499
+ * (syntax checks for trees/commits/tags), stores (loose object + zlib), and
500
+ * RETURNS the SHA. The returned SHA is compared against the expected one:
501
+ * a second, independent integrity gate after ./object-fetch.ts's envelope
502
+ * verification. Refs land via `git update-ref`, HEAD via `git symbolic-ref`.
503
+ *
504
+ * Same injection posture as GitRepoReader: child processes use
505
+ * `execFile`/`spawn` with argument ARRAYS (never a shell), and refnames are
506
+ * validated with `git check-ref-format` semantics before use.
507
+ */
508
+
509
+ /** A written object's SHA disagreed with what `git hash-object` computed. */
510
+ declare class ObjectWriteMismatchError extends Error {
511
+ readonly expectedSha: string;
512
+ readonly writtenSha: string;
513
+ constructor(expectedSha: string, writtenSha: string);
514
+ }
515
+ /**
516
+ * Conservative refname validation (superset-safe subset of
517
+ * `git check-ref-format`): must start `refs/`, no component may start with
518
+ * `-` or `.`, no `..`, no control/space/git-special characters, no trailing
519
+ * `/`, `.`, or `.lock`. Rejecting odd-but-legal names is fine — these
520
+ * refnames come from relay events, and a hostile relay must not be able to
521
+ * smuggle options or path traversal into git invocations.
522
+ */
523
+ declare function isSafeRefname(refname: string): boolean;
524
+ /**
525
+ * Write one object via `git hash-object -w --stdin -t <type>` (binary-safe
526
+ * stdin) and verify the SHA git computed matches the expected one.
527
+ */
528
+ declare function writeGitObject(repoPath: string, object: FetchedObject): Promise<void>;
529
+ /** Write a batch of verified objects into the repository (sequential). */
530
+ declare function writeGitObjects(repoPath: string, objects: Iterable<FetchedObject>): Promise<number>;
531
+ /** `git update-ref <refname> <sha>` with refname/SHA validation. */
532
+ declare function updateRef(repoPath: string, refname: string, sha: string): Promise<void>;
533
+ /** Point HEAD at a branch via `git symbolic-ref HEAD <refname>`. */
534
+ declare function setHeadSymref(repoPath: string, refname: string): Promise<void>;
535
+
536
+ /**
537
+ * Minimal bech32 npub encoding/decoding for Nostr pubkeys (BIP-173 / NIP-19),
538
+ * dependency-free. Mirrors rig-web's proven `web/npub.ts` — the CLI must not
539
+ * import from the SPA package, so the ~100 lines live here too (#278: `rig
540
+ * clone` accepts `<owner-npub-or-hex>/<repo-id>` addresses).
541
+ */
542
+ /** Encode a 64-char hex pubkey as an `npub1…` bech32 string. */
543
+ declare function hexToNpub(hexPubkey: string): string;
544
+ /**
545
+ * Decode an `npub1…` bech32 string back to a 64-char hex pubkey.
546
+ * Throws on malformed input (prefix, length, charset, checksum, padding).
547
+ */
548
+ declare function npubToHex(npub: string): string;
549
+ /**
550
+ * Normalize a repo-owner reference to a 64-char hex pubkey: accepts lowercase
551
+ * hex verbatim or an `npub1…` string. Throws a caller-facing error otherwise.
552
+ */
553
+ declare function ownerToHex(owner: string): string;
554
+
286
555
  /**
287
556
  * Push planner/executor — the core of `rig push` (epic #222, ticket #226).
288
557
  *
@@ -665,6 +934,11 @@ interface GitPatchRequest {
665
934
  repoAddr: GitRepoAddr;
666
935
  /** Patch/PR title (`subject` tag). */
667
936
  title: string;
937
+ /**
938
+ * PR body/cover text (`description` tag). Kept OUT of the event content so
939
+ * `git am` still consumes the patch text verbatim (#280).
940
+ */
941
+ description?: string;
668
942
  /** Literal patch text. Mutually exclusive with `repoPath`+`range`. */
669
943
  patchText?: string;
670
944
  /** Local repository to run format-patch in. Requires `range`. */
@@ -739,4 +1013,4 @@ declare function serializeEventReceipt(kind: number, receipt: PublishReceipt): G
739
1013
  /** Serialize a PushResult onto the wire (bigints → strings, Maps → records). */
740
1014
  declare function serializePushResult(plan: PushPlan, result: PushResult): GitPushResponse;
741
1015
 
742
- export { type ExecutePushOptions, FeeRates, type FetchRemoteStateOptions, type GitCommentRequest, GitError, type GitErrorEnvelope, type GitEstimateRequest, type GitEstimateResponse, type GitEventResponse, type GitFeeEstimate, type GitIssueRequest, GitObjectType, type GitPatchRequest, type GitPlannedObject, type GitPublishReceipt, type GitPushRequest, type GitPushResponse, type GitRef, type GitRefUpdate, type GitRepoAddr, GitRepoReader, type GitStatusRequest, type GitStatusValue, type GitUploadStep, NonFastForwardError, type NostrEvent, type ObjectStat, type ObjectWithPath, type OversizeObject, OversizeObjectsError, type PlanPushOptions, type PlannedObject, PublishReceipt, Publisher, type PushFeeEstimate, type PushPlan, type PushResult, type ReadGitObject, type ReadObjectsResult, type RefUpdate, type RefUpdateKind, type RejectedRefUpdate, type RemoteState, type RepoRefs, type StatObjectsResult, type UploadStepResult, type WebSocketFactory, type WebSocketLike, executePush, fetchRemoteState, planPush, serializeEventReceipt, serializeFeeEstimate, serializePushPlan, serializePushResult };
1016
+ export { type ClosureResult, type CollectRepoObjectsOptions, type CollectRepoObjectsResult, DEFAULT_CONCURRENCY, type DownloadOptions, type DownloadResult, type ExecutePushOptions, FeeRates, type FetchLike, type FetchRemoteStateOptions, type FetchedObject, type GatewayFetchOptions, type GitCommentRequest, GitError, type GitErrorEnvelope, type GitEstimateRequest, type GitEstimateResponse, type GitEventResponse, type GitFeeEstimate, type GitIssueRequest, GitObjectType, type GitPatchRequest, type GitPlannedObject, type GitPublishReceipt, type GitPushRequest, type GitPushResponse, type GitRef, type GitRefUpdate, type GitRepoAddr, GitRepoReader, type GitStatusRequest, type GitStatusValue, type GitUploadStep, type MissingObject, NonFastForwardError, type NostrEvent, type NostrFilter, ObjectIntegrityError, type ObjectStat, type ObjectWithPath, ObjectWriteMismatchError, type OversizeObject, OversizeObjectsError, type PlanPushOptions, type PlannedObject, PublishReceipt, Publisher, type PushFeeEstimate, type PushPlan, type PushResult, type ReadGitObject, type ReadObjectsResult, type RefUpdate, type RefUpdateKind, type RejectedRefUpdate, type RemoteState, type RepoRefs, type StatObjectsResult, type UploadStepResult, type WebSocketFactory, type WebSocketLike, collectRepoObjects, downloadGitObjects, executePush, fetchRemoteState, fetchTxBytes, hexToNpub, isSafeRefname, missingObjectsMessage, npubToHex, ownerToHex, planPush, queryRelay, referencedShas, serializeEventReceipt, serializeFeeEstimate, serializePushPlan, serializePushResult, setHeadSymref, updateRef, verifyObjectBody, walkClosure, writeGitObject, writeGitObjects };
package/dist/index.js CHANGED
@@ -1,28 +1,48 @@
1
1
  import {
2
+ DEFAULT_CONCURRENCY,
2
3
  GitError,
3
4
  GitRepoReader,
4
5
  NonFastForwardError,
6
+ ObjectIntegrityError,
7
+ ObjectWriteMismatchError,
5
8
  OversizeObjectsError,
9
+ collectRepoObjects,
10
+ downloadGitObjects,
6
11
  executePush,
12
+ fetchTxBytes,
13
+ hexToNpub,
14
+ isSafeRefname,
15
+ missingObjectsMessage,
16
+ npubToHex,
17
+ ownerToHex,
7
18
  planPush,
19
+ referencedShas,
8
20
  serializeEventReceipt,
9
21
  serializeFeeEstimate,
10
22
  serializePushPlan,
11
- serializePushResult
12
- } from "./chunk-LFGDLD6J.js";
13
- import {
14
- fetchRemoteState
15
- } from "./chunk-PLKZAUTG.js";
23
+ serializePushResult,
24
+ setHeadSymref,
25
+ updateRef,
26
+ verifyObjectBody,
27
+ walkClosure,
28
+ writeGitObject,
29
+ writeGitObjects
30
+ } from "./chunk-W2SDL2PE.js";
16
31
  import {
17
32
  COMMENT_KIND,
33
+ MAINTAINERS_TAG,
18
34
  REPOSITORY_STATE_KIND,
35
+ authorizedStatusAuthors,
19
36
  buildComment,
20
37
  buildIssue,
21
38
  buildPatch,
22
39
  buildRepoAnnouncement,
23
40
  buildRepoRefs,
24
- buildStatus
25
- } from "./chunk-HPSOQP7Q.js";
41
+ buildStatus,
42
+ fetchRemoteState,
43
+ parseMaintainers,
44
+ queryRelay
45
+ } from "./chunk-3HRFDH7H.js";
26
46
  import {
27
47
  MAX_OBJECT_SIZE,
28
48
  createGitBlob,
@@ -33,29 +53,51 @@ import {
33
53
  } from "./chunk-X2CZPPDM.js";
34
54
  export {
35
55
  COMMENT_KIND,
56
+ DEFAULT_CONCURRENCY,
36
57
  GitError,
37
58
  GitRepoReader,
59
+ MAINTAINERS_TAG,
38
60
  MAX_OBJECT_SIZE,
39
61
  NonFastForwardError,
62
+ ObjectIntegrityError,
63
+ ObjectWriteMismatchError,
40
64
  OversizeObjectsError,
41
65
  REPOSITORY_STATE_KIND,
66
+ authorizedStatusAuthors,
42
67
  buildComment,
43
68
  buildIssue,
44
69
  buildPatch,
45
70
  buildRepoAnnouncement,
46
71
  buildRepoRefs,
47
72
  buildStatus,
73
+ collectRepoObjects,
48
74
  createGitBlob,
49
75
  createGitCommit,
50
76
  createGitTag,
51
77
  createGitTree,
78
+ downloadGitObjects,
52
79
  executePush,
53
80
  fetchRemoteState,
81
+ fetchTxBytes,
54
82
  hashGitObject,
83
+ hexToNpub,
84
+ isSafeRefname,
85
+ missingObjectsMessage,
86
+ npubToHex,
87
+ ownerToHex,
88
+ parseMaintainers,
55
89
  planPush,
90
+ queryRelay,
91
+ referencedShas,
56
92
  serializeEventReceipt,
57
93
  serializeFeeEstimate,
58
94
  serializePushPlan,
59
- serializePushResult
95
+ serializePushResult,
96
+ setHeadSymref,
97
+ updateRef,
98
+ verifyObjectBody,
99
+ walkClosure,
100
+ writeGitObject,
101
+ writeGitObjects
60
102
  };
61
103
  //# sourceMappingURL=index.js.map
@@ -104,14 +104,41 @@ interface UnsignedEvent {
104
104
  tags: string[][];
105
105
  created_at: number;
106
106
  }
107
+ /**
108
+ * NIP-34 tag naming the repo's declared maintainers: one multi-valued tag
109
+ * `["maintainers", "<hex-pubkey>", "<hex-pubkey>", …]` on the kind:30617
110
+ * announcement (mirrors the spec's multi-valued `relays` tag). The repo
111
+ * OWNER — the announcement event's own pubkey — is ALWAYS an implicit
112
+ * maintainer and need not be listed. Consumers derive an issue/PR's status
113
+ * ONLY from kind:1630-1633 events signed by owner ∪ maintainers (#287): the
114
+ * relay is permissionless, so this is the CONSUMER-side authority filter.
115
+ */
116
+ declare const MAINTAINERS_TAG = "maintainers";
117
+ /**
118
+ * Collect the declared maintainer pubkeys (lowercased hex) from a kind:30617
119
+ * event's tags. Tolerant of repeated `maintainers` tags and non-hex noise —
120
+ * only 64-char hex values survive. Does NOT include the owner (implicit).
121
+ */
122
+ declare function parseMaintainers(tags: string[][]): string[];
123
+ /**
124
+ * The set of pubkeys whose kind:1630-1633 status events are authoritative for
125
+ * a repo: the owner (always) ∪ the declared maintainers (from the 30617's
126
+ * `maintainers` tag). All values are lowercased hex.
127
+ */
128
+ declare function authorizedStatusAuthors(ownerPubkey: string, repoAnnouncementTags: string[][]): Set<string>;
107
129
  /**
108
130
  * Build a kind:30617 repository announcement event.
109
131
  *
110
132
  * @param repoId - Repository identifier (d tag)
111
133
  * @param name - Human-readable repository name
112
134
  * @param description - Repository description
135
+ * @param maintainers - Optional declared maintainer pubkeys (hex). Emitted as
136
+ * a single `["maintainers", …]` tag when non-empty; duplicate and non-64-hex
137
+ * values are dropped. The owner (the signer) is an implicit maintainer and
138
+ * need not be listed — if passed it is emitted, which is harmless since the
139
+ * owner is authorized regardless. See {@link MAINTAINERS_TAG}.
113
140
  */
114
- declare function buildRepoAnnouncement(repoId: string, name: string, description: string): UnsignedEvent;
141
+ declare function buildRepoAnnouncement(repoId: string, name: string, description: string, maintainers?: string[]): UnsignedEvent;
115
142
  /**
116
143
  * Build a kind:30618 repository refs/state event.
117
144
  *
@@ -144,6 +171,13 @@ declare function buildComment(repoOwnerPubkey: string, repoId: string, issueOrPr
144
171
  /**
145
172
  * Build a kind:1617 patch event.
146
173
  *
174
+ * The PR body/description travels in a dedicated `description` tag, NEVER in
175
+ * `content` (#280): `content` is real `git format-patch` output that readers
176
+ * pipe straight into `git am`, and git's patch-format detection hard-fails on
177
+ * any leading prose (verified: "Patch format detection failed."). The tag
178
+ * route keeps `git am` consumption intact while `rig pr show` and the
179
+ * rig-web/views `parsePR` renderers surface the description.
180
+ *
147
181
  * @param repoOwnerPubkey - Pubkey of the repository owner
148
182
  * @param repoId - Repository identifier
149
183
  * @param title - Patch/PR title (subject tag)
@@ -151,11 +185,13 @@ declare function buildComment(repoOwnerPubkey: string, repoId: string, issueOrPr
151
185
  * @param branchTag - Branch name for the t tag
152
186
  * @param content - Real `git format-patch` text (NIP-34 patch body); defaults
153
187
  * to '' for callers that only reference commits by tag
188
+ * @param description - PR body/cover text (`description` tag) — kept out of
189
+ * `content` so `git am` still applies it
154
190
  */
155
191
  declare function buildPatch(repoOwnerPubkey: string, repoId: string, title: string, commits: {
156
192
  sha: string;
157
193
  parentSha: string;
158
- }[], branchTag?: string, content?: string): UnsignedEvent;
194
+ }[], branchTag?: string, content?: string, description?: string): UnsignedEvent;
159
195
  /** Status kinds: 1630 open, 1631 applied/merged, 1632 closed, 1633 draft. */
160
196
  type StatusKind = typeof STATUS_OPEN_KIND | typeof STATUS_APPLIED_KIND | typeof STATUS_CLOSED_KIND | typeof STATUS_DRAFT_KIND;
161
197
  /**
@@ -251,4 +287,4 @@ interface Publisher {
251
287
  publishEvent(event: UnsignedEvent, relayUrls: string[]): Promise<PublishReceipt>;
252
288
  }
253
289
 
254
- export { COMMENT_KIND as C, type FeeRates as F, type GitObjectType as G, MAX_OBJECT_SIZE as M, type Publisher as P, REPOSITORY_STATE_KIND as R, type StatusKind as S, type UnsignedEvent as U, type PublishReceipt as a, type GitObject as b, type GitObjectUpload as c, type UploadReceipt as d, buildComment as e, buildIssue as f, buildPatch as g, buildRepoAnnouncement as h, buildRepoRefs as i, buildStatus as j, createGitBlob as k, createGitCommit as l, createGitTag as m, createGitTree as n, hashGitObject as o };
290
+ export { COMMENT_KIND as C, type FeeRates as F, type GitObjectUpload as G, MAINTAINERS_TAG as M, type Publisher as P, REPOSITORY_STATE_KIND as R, type StatusKind as S, type UnsignedEvent as U, type UploadReceipt as a, type PublishReceipt as b, type GitObjectType as c, type GitObject as d, MAX_OBJECT_SIZE as e, authorizedStatusAuthors as f, buildComment as g, buildIssue as h, buildPatch as i, buildRepoAnnouncement as j, buildRepoRefs as k, buildStatus as l, createGitBlob as m, createGitCommit as n, createGitTag as o, createGitTree as p, hashGitObject as q, parseMaintainers as r };
@@ -1,5 +1,5 @@
1
1
  import { ToonClientConfig, SignedBalanceProof, PublishEventResult } from '@toon-protocol/client';
2
- import { U as UnsignedEvent, P as Publisher, F as FeeRates, c as GitObjectUpload, d as UploadReceipt, a as PublishReceipt } from '../publisher-Cdr1H1hg.js';
2
+ import { U as UnsignedEvent, P as Publisher, F as FeeRates, G as GitObjectUpload, a as UploadReceipt, b as PublishReceipt } from '../publisher-BtVceNeI.js';
3
3
  import '@toon-protocol/core/nip34';
4
4
 
5
5
  /**
@@ -583,6 +583,12 @@ declare class StandalonePublisher implements Publisher {
583
583
  * 1. Daemon detection — probe the toon-clientd loopback control API
584
584
  * (`GET /status`) and REFUSE when it reports the SAME identity. A daemon
585
585
  * on a different identity holds different channels and is harmless.
586
+ * Since #279 the CLI's paid WRITE commands never get here with a
587
+ * same-identity daemon up — they delegate to its `/git/*` routes first
588
+ * (`cli/daemon-session.ts`), which achieves the same one-writer goal by
589
+ * handing the watermark to the daemon. This guard remains the backstop
590
+ * for the probe→publish race window and for operations with no daemon
591
+ * route (channel close/settle, explicit open).
586
592
  * 2. Advisory lockfile — an exclusive per-pubkey lockfile under the shared
587
593
  * `~/.toon-client` state dir so two STANDALONE processes can't race each
588
594
  * other (the daemon check only covers the daemon). Stale locks (dead pid)
@@ -1,24 +1,24 @@
1
1
  import {
2
- DEFAULT_DAEMON_PORT,
3
- DaemonIdentityConflictError,
4
- NonceLock,
5
- StandaloneLockError,
6
2
  StandalonePublishError,
7
3
  StandalonePublisher,
8
- checkDaemonIdentity,
9
- defaultDaemonPort,
10
- defaultLockDir,
11
4
  deriveRouteDestinations,
12
5
  extractArweaveTxId
13
- } from "../chunk-3EKP7PMM.js";
6
+ } from "../chunk-OIJNRACA.js";
14
7
  import {
15
8
  ChannelMapCorruptError,
16
9
  ChannelMapStore,
10
+ DEFAULT_DAEMON_PORT,
11
+ DaemonIdentityConflictError,
12
+ NonceLock,
17
13
  RIG_CHANNEL_MAP_FILENAME,
14
+ StandaloneLockError,
18
15
  channelStatus,
16
+ checkDaemonIdentity,
17
+ defaultDaemonPort,
18
+ defaultLockDir,
19
19
  recordKey,
20
20
  resolveChannelPaths
21
- } from "../chunk-O6TXHKWG.js";
21
+ } from "../chunk-SW7ZHMGS.js";
22
22
  import "../chunk-X2CZPPDM.js";
23
23
  export {
24
24
  ChannelMapCorruptError,