@toon-protocol/rig 4.5.0 → 4.7.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.
@@ -10,11 +10,15 @@ import {
10
10
  UnconfiguredRepoAddressError,
11
11
  UnknownRemoteError,
12
12
  addGitRemote,
13
+ amendRepoAnnouncement,
13
14
  buildCiManualTrigger,
14
15
  buildCiSecretUpdate,
15
16
  buildCiServiceRequest,
16
17
  buildCiServiceStop,
17
18
  collectRepoObjects,
19
+ conformanceEdits,
20
+ diffAnnouncementTags,
21
+ earliestUniqueCommit,
18
22
  emitCliError,
19
23
  encryptSecretUpdate,
20
24
  executePush,
@@ -48,22 +52,29 @@ import {
48
52
  updateRef,
49
53
  writeGitObjects,
50
54
  writeToonConfig
51
- } from "./chunk-S2BMLXAD.js";
55
+ } from "./chunk-3IVO2MSZ.js";
56
+ import {
57
+ PREFERRED_GATEWAY,
58
+ mirrorGatewaysFor,
59
+ readGateways,
60
+ shaResolverFor
61
+ } from "./chunk-XQ2AWNHQ.js";
52
62
  import {
53
- ARWEAVE_GATEWAYS,
54
63
  COMMENT_KIND,
55
64
  DEFAULT_KEYSTORE_PASSWORD,
65
+ LEGACY_COMMENT_KIND,
56
66
  MissingIdentityError,
57
67
  authorizedStatusAuthors,
58
68
  buildComment,
59
69
  buildIssue,
60
70
  buildPatch,
61
- buildRepoAnnouncement,
62
71
  buildStatus,
63
72
  clientConfigPath,
64
73
  clientKeystorePath,
74
+ commentBelongsToThread,
65
75
  defaultWebSocketFactory,
66
76
  fetchRemoteState,
77
+ firstTagValue,
67
78
  isValidEvmPayoutAddress,
68
79
  linkKeystoreInClientConfig,
69
80
  parseMaintainers,
@@ -71,7 +82,7 @@ import {
71
82
  readClientConfigFile,
72
83
  resolveAccountIndex,
73
84
  resolveIdentity
74
- } from "./chunk-HMED76GU.js";
85
+ } from "./chunk-S3KKQ4IC.js";
75
86
  import {
76
87
  ChannelMapStore,
77
88
  channelStatus,
@@ -84,22 +95,13 @@ import {
84
95
  uploadChargeFor
85
96
  } from "./chunk-U5A7MHBP.js";
86
97
 
87
- // src/gateway-preference.ts
88
- var TESTNET_GATEWAY = /(^|\.)ar-io\.dev$/i;
89
- function isMainnetGateway(url) {
90
- try {
91
- return !TESTNET_GATEWAY.test(new URL(url).hostname);
92
- } catch {
93
- return false;
94
- }
95
- }
96
- var PREFERRED_GATEWAY = ARWEAVE_GATEWAYS.find(isMainnetGateway) ?? "https://arweave.net";
97
- function mirrorGatewaysFor(primary) {
98
- const p = primary.replace(/\/+$/, "");
99
- return ARWEAVE_GATEWAYS.filter((g) => g.replace(/\/+$/, "") !== p);
100
- }
101
-
102
98
  // src/cli/render.ts
99
+ function describeAnnouncementDiff(diff) {
100
+ return [
101
+ ...diff.removed.map((tag) => ` - ${tag.join(" ")}`),
102
+ ...diff.added.map((tag) => ` + ${tag.join(" ")}`)
103
+ ];
104
+ }
103
105
  function formatNumber(value) {
104
106
  return String(value).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
105
107
  }
@@ -214,6 +216,14 @@ function repoHashRoute(options) {
214
216
  function rigWebRoute(options) {
215
217
  return `${options.rigWebUrl.replace(/\/+$/, "")}/${repoHashRoute(options)}`;
216
218
  }
219
+ function repoWebUrl(options) {
220
+ return rigWebRoute({
221
+ rigWebUrl: options.env[RIG_WEB_URL_ENV] ?? DEFAULT_RIG_WEB_URL,
222
+ relay: options.relay,
223
+ ownerNpub: hexToNpub(options.ownerPubkey),
224
+ repoId: options.repoId
225
+ });
226
+ }
217
227
  function escapeHtml(value) {
218
228
  return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
219
229
  }
@@ -300,7 +310,7 @@ function loadPaidSession(deps, relayUrl) {
300
310
  });
301
311
  }
302
312
  var defaultLoadStandalone = async (options) => {
303
- const mod = await import("./standalone-mode-W3ZEKC6M.js");
313
+ const mod = await import("./standalone-mode-FY3XVQQK.js");
304
314
  return mod.createStandaloneContext(options);
305
315
  };
306
316
  function identityReport(ctx) {
@@ -559,6 +569,14 @@ async function runPush(args, deps) {
559
569
  `warning: git config toon.owner (${toonConfig.owner.slice(0, 8)}\u2026) differs from the active identity (${identity.pubkey.slice(0, 8)}\u2026) \u2014 this push publishes under the ACTIVE identity's repo namespace, not the configured owner's. Re-run \`rig init\` to adopt the active identity.`
560
570
  );
561
571
  }
572
+ const announcement = relaysUsed[0] === void 0 ? void 0 : {
573
+ web: repoWebUrl({
574
+ env: deps.env,
575
+ relay: relaysUsed[0],
576
+ ownerPubkey: identity.pubkey,
577
+ repoId
578
+ })
579
+ };
562
580
  let plan;
563
581
  let execute;
564
582
  let pointerPlan;
@@ -577,7 +595,8 @@ async function runPush(args, deps) {
577
595
  feeRates,
578
596
  repoId,
579
597
  refs: refspecs,
580
- force: flags.force
598
+ force: flags.force,
599
+ ...announcement ? { announcement } : {}
581
600
  });
582
601
  plan = serializePushPlan(pushPlan);
583
602
  execute = async () => serializePushResult(
@@ -612,7 +631,8 @@ async function runPush(args, deps) {
612
631
  repoId,
613
632
  refspecs,
614
633
  force: flags.force,
615
- relayUrls: relaysUsed
634
+ relayUrls: relaysUsed,
635
+ ...announcement ? { announcement } : {}
616
636
  });
617
637
  execute = () => client.gitPush({
618
638
  repoPath: repoRoot,
@@ -620,7 +640,8 @@ async function runPush(args, deps) {
620
640
  refspecs,
621
641
  force: flags.force,
622
642
  relayUrls: relaysUsed,
623
- confirm: true
643
+ confirm: true,
644
+ ...announcement ? { announcement } : {}
624
645
  });
625
646
  rigPageSkipDetail = "skipped on the daemon path (refreshes on the next standalone push)";
626
647
  }
@@ -879,6 +900,82 @@ async function resolveRepoContext(flags, deps) {
879
900
  ...repoRoot !== void 0 ? { repoRoot } : {}
880
901
  };
881
902
  }
903
+ async function conformanceFactsFor(options) {
904
+ const { ctx, relayUrl, deps } = options;
905
+ const repoRoot = ctx.repoRoot;
906
+ const rootCommits = deps.rootCommits ?? (repoRoot === void 0 ? void 0 : (rev) => new GitRepoReader(repoRoot).rootCommits(rev));
907
+ let euc = null;
908
+ if (rootCommits !== void 0) {
909
+ try {
910
+ euc = await earliestUniqueCommit({ rootCommits });
911
+ } catch {
912
+ euc = null;
913
+ }
914
+ }
915
+ let web = null;
916
+ try {
917
+ web = repoWebUrl({
918
+ env: deps.env,
919
+ relay: relayUrl,
920
+ ownerPubkey: ctx.owner,
921
+ repoId: ctx.repoId
922
+ });
923
+ } catch {
924
+ web = null;
925
+ }
926
+ return { relays: [relayUrl], web, earliestUniqueCommit: euc };
927
+ }
928
+ async function openAnnouncementRepublish(options) {
929
+ const { deps, flags, what, before } = options;
930
+ const { io } = deps;
931
+ const ctx = await resolveRepoContext(flags, deps);
932
+ if (ctx.relays.length > 1) {
933
+ io.err(singleRelayRefusal(ctx.resolved, "Nothing was published or paid."));
934
+ return { ok: false, exitCode: 1 };
935
+ }
936
+ const relayUrl = ctx.relays[0];
937
+ if (relayUrl === void 0 || !WS_URL_RE.test(relayUrl)) {
938
+ throw new InvalidRelayUrlError(
939
+ relayUrl ?? "",
940
+ "a paid publish needs a ws:// or wss:// relay"
941
+ );
942
+ }
943
+ const load = deps.loadStandalone ?? defaultLoadStandalone;
944
+ const standalone = await load({
945
+ env: deps.env,
946
+ cwd: deps.cwd,
947
+ warn: (line) => io.err(line),
948
+ relayUrl
949
+ });
950
+ const identity = identityReport(standalone);
951
+ if (identity.pubkey.toLowerCase() !== ctx.owner.toLowerCase()) {
952
+ io.err(
953
+ `rig: only the repo owner (${ctx.owner.slice(0, 8)}\u2026) can ${what} \u2014 the active identity is ${identity.pubkey.slice(0, 8)}\u2026. A non-owner republish would write your own (ignored) announcement. Nothing was published or paid.`
954
+ );
955
+ return { ok: false, exitCode: 1, standalone };
956
+ }
957
+ const remote = await fetchRemoteState({
958
+ relayUrls: [relayUrl],
959
+ ownerPubkey: ctx.owner,
960
+ repoId: ctx.repoId,
961
+ ...deps.webSocketFactory ? { webSocketFactory: deps.webSocketFactory } : {}
962
+ });
963
+ if (!remote.announced) {
964
+ io.err(
965
+ `rig: 30617:${ctx.owner.slice(0, 8)}\u2026:${ctx.repoId} has no announcement yet \u2014 run \`rig push\` to publish the repo (with its real name/description) ${before}. Nothing was published or paid.`
966
+ );
967
+ return { ok: false, exitCode: 1, standalone };
968
+ }
969
+ return {
970
+ ok: true,
971
+ ctx,
972
+ relayUrl,
973
+ standalone,
974
+ identity,
975
+ remote,
976
+ facts: await conformanceFactsFor({ ctx, relayUrl, deps })
977
+ };
978
+ }
882
979
 
883
980
  // src/cli/dispatch.ts
884
981
  import { createRequire } from "module";
@@ -2378,7 +2475,7 @@ async function runCi(args, deps) {
2378
2475
  case "status":
2379
2476
  return runCiStatus(rest, deps);
2380
2477
  case "serve":
2381
- return (await import("./ci-serve-PYEHIBN7.js")).runCiServe(rest, deps);
2478
+ return (await import("./ci-serve-T33FCQVT.js")).runCiServe(rest, deps);
2382
2479
  case "--help":
2383
2480
  case "-h":
2384
2481
  case "help":
@@ -2418,6 +2515,11 @@ from Arweave gateways. A clone right after a push may report missing
2418
2515
  objects \u2014 retry after propagation.
2419
2516
 
2420
2517
  Options:
2518
+ --gateway <url> gateway to read objects through FIRST, on its raw-bytes
2519
+ route <url>/raw/<txId>, ahead of the public gateway
2520
+ list \u2014 name a private, local or air-gapped gateway to
2521
+ clone a repo the public ones do not carry
2522
+ (env RIG_ARWEAVE_GATEWAY also sets it)
2421
2523
  --concurrency <n> parallel gateway downloads (default 8)
2422
2524
  --json machine-readable result envelope
2423
2525
  -h, --help show this help`;
@@ -2444,6 +2546,7 @@ function parseCloneArgs(args) {
2444
2546
  options: {
2445
2547
  json: { type: "boolean", default: false },
2446
2548
  concurrency: { type: "string" },
2549
+ gateway: { type: "string" },
2447
2550
  help: { type: "boolean", short: "h", default: false }
2448
2551
  },
2449
2552
  allowPositionals: true
@@ -2479,6 +2582,7 @@ function parseCloneArgs(args) {
2479
2582
  repoId,
2480
2583
  dir,
2481
2584
  concurrency,
2585
+ gateway: values.gateway,
2482
2586
  json: values.json === true
2483
2587
  };
2484
2588
  }
@@ -2527,7 +2631,9 @@ async function runClone(args, deps) {
2527
2631
  ownerPubkey: owner,
2528
2632
  repoId,
2529
2633
  ...deps.webSocketFactory ? { webSocketFactory: deps.webSocketFactory } : {},
2530
- ...deps.resolveSha ? { resolveSha: deps.resolveSha } : {}
2634
+ // The GraphQL fallback for SHAs the map does not cover asks the SAME
2635
+ // permaweb the objects are read from (#183).
2636
+ resolveSha: deps.resolveSha ?? shaResolverFor(parsed.gateway, deps.env)
2531
2637
  });
2532
2638
  if (!remoteState.announced && remoteState.refsEvent === null) {
2533
2639
  throw new RepoNotFoundError(relayUrl, owner, repoId);
@@ -2552,6 +2658,8 @@ async function runClone(args, deps) {
2552
2658
  tips,
2553
2659
  shaToTxId: remoteState.shaToTxId,
2554
2660
  resolveMissing: (shas) => remoteState.resolveMissing(shas),
2661
+ // `--gateway` / RIG_ARWEAVE_GATEWAY first, public list behind it (#176).
2662
+ gateways: readGateways(parsed.gateway, deps.env),
2555
2663
  ...deps.fetchFn ? { fetchFn: deps.fetchFn } : {},
2556
2664
  ...parsed.concurrency !== void 0 ? { concurrency: parsed.concurrency } : {}
2557
2665
  });
@@ -2640,7 +2748,6 @@ async function runClone(args, deps) {
2640
2748
  import { readFile } from "fs/promises";
2641
2749
  import { parseArgs as parseArgs8 } from "util";
2642
2750
  import {
2643
- REPOSITORY_ANNOUNCEMENT_KIND as REPOSITORY_ANNOUNCEMENT_KIND2,
2644
2751
  STATUS_APPLIED_KIND as STATUS_APPLIED_KIND2,
2645
2752
  STATUS_CLOSED_KIND as STATUS_CLOSED_KIND2,
2646
2753
  STATUS_DRAFT_KIND as STATUS_DRAFT_KIND2,
@@ -2675,9 +2782,10 @@ Options:
2675
2782
  ${READ_COMMON_FLAGS}`;
2676
2783
  var ISSUE_SHOW_USAGE = `Usage: rig issue show <event-id> [options]
2677
2784
 
2678
- Show one issue (kind:1621): metadata, derived state, body, and its
2679
- kind:1622 comments \u2014 FREE (relay reads only). <event-id> is the 64-char hex
2680
- id \`rig issue create\` printed (also visible in \`rig issue list\`).
2785
+ Show one issue (kind:1621): metadata, derived state, body, and its comments
2786
+ \u2014 NIP-22 kind:1111 merged with legacy kind:1622 in time order \u2014 FREE (relay
2787
+ reads only). <event-id> is the 64-char hex id \`rig issue create\` printed
2788
+ (also visible in \`rig issue list\`).
2681
2789
 
2682
2790
  Options:
2683
2791
  ${READ_COMMON_FLAGS}`;
@@ -2694,7 +2802,8 @@ var PR_SHOW_USAGE = `Usage: rig pr show <event-id> [options]
2694
2802
 
2695
2803
  Show one patch/PR (kind:1617): metadata, derived state, the FULL patch text
2696
2804
  (real \`git format-patch\` output \u2014 pipe it to \`git am\` to apply), and its
2697
- kind:1622 comments \u2014 FREE (relay reads only).
2805
+ comments \u2014 NIP-22 kind:1111 merged with legacy kind:1622 in time order \u2014
2806
+ FREE (relay reads only).
2698
2807
 
2699
2808
  Options:
2700
2809
  ${READ_COMMON_FLAGS}`;
@@ -2710,9 +2819,7 @@ var STATUS_KINDS = [
2710
2819
  STATUS_CLOSED_KIND,
2711
2820
  STATUS_DRAFT_KIND
2712
2821
  ];
2713
- function tagValue(tags, name) {
2714
- return tags.find((t) => t[0] === name)?.[1];
2715
- }
2822
+ var tagValue = firstTagValue;
2716
2823
  function tagValues(tags, name) {
2717
2824
  return tags.filter((t) => t[0] === name && t[1]).map((t) => t[1]);
2718
2825
  }
@@ -2729,6 +2836,9 @@ function deriveStatus(targetEventId, statusEvents, authorized) {
2729
2836
  }
2730
2837
  return winner === null ? "open" : STATUS_BY_KIND[winner.kind];
2731
2838
  }
2839
+ function withTargetAuthor(authorized, targetAuthorPubkey) {
2840
+ return /* @__PURE__ */ new Set([...authorized, targetAuthorPubkey.toLowerCase()]);
2841
+ }
2732
2842
  var RELAY_TIMEOUT_MS = 1e4;
2733
2843
  var HEX64_RE2 = /^[0-9a-f]{64}$/;
2734
2844
  var WS_URL_RE3 = /^wss?:\/\//i;
@@ -2872,7 +2982,7 @@ function parseTrackerItem(event, status) {
2872
2982
  };
2873
2983
  if (event.kind === PATCH_KIND) {
2874
2984
  item.commitShas = tagValues(event.tags, "commit");
2875
- const branch = tagValue(event.tags, "branch");
2985
+ const branch = tagValue(event.tags, "branch-name") ?? tagValue(event.tags, "branch");
2876
2986
  if (branch !== void 0) item.branch = branch;
2877
2987
  const description = tagValue(event.tags, "description");
2878
2988
  if (description !== void 0) item.description = description;
@@ -2917,7 +3027,14 @@ async function fetchItems(ctx, kind) {
2917
3027
  }
2918
3028
  const authorized = await authorizedPromise;
2919
3029
  return items.map(
2920
- (event) => parseTrackerItem(event, deriveStatus(event.id, statuses.values(), authorized))
3030
+ (event) => parseTrackerItem(
3031
+ event,
3032
+ deriveStatus(
3033
+ event.id,
3034
+ statuses.values(),
3035
+ withTargetAuthor(authorized, event.pubkey)
3036
+ )
3037
+ )
2921
3038
  ).sort((a, b) => b.createdAt - a.createdAt);
2922
3039
  }
2923
3040
  async function fetchItem(ctx, eventId, expectedKind) {
@@ -2942,16 +3059,23 @@ async function fetchItem(ctx, eventId, expectedKind) {
2942
3059
  [{ kinds: STATUS_KINDS, "#e": [eventId] }],
2943
3060
  ctx.webSocketFactory
2944
3061
  ),
3062
+ // One thread, two wire shapes (#159): NIP-22 kind:1111 comments hang off
3063
+ // the root's UPPERCASE `E` tag (a nested reply's lowercase `e` names its
3064
+ // parent comment, so `#e` would miss it); legacy kind:1622 comments only
3065
+ // ever carry the lowercase `e`. Both are merged below by `created_at`.
2945
3066
  queryAll(
2946
3067
  ctx.relays,
2947
- [{ kinds: [COMMENT_KIND], "#e": [eventId] }],
3068
+ [
3069
+ { kinds: [COMMENT_KIND], "#E": [eventId] },
3070
+ { kinds: [LEGACY_COMMENT_KIND], "#e": [eventId] }
3071
+ ],
2948
3072
  ctx.webSocketFactory
2949
3073
  )
2950
3074
  ]);
2951
- const comments = [...commentEvents.values()].filter(
2952
- (e) => e.kind === COMMENT_KIND && e.tags.some((t) => t[0] === "e" && t[1] === eventId)
2953
- ).sort((a, b) => a.created_at - b.created_at).map((e) => ({
3075
+ const comments = [...commentEvents.values()].filter((e) => commentBelongsToThread(e, eventId)).sort((a, b) => a.created_at - b.created_at).map((e) => ({
2954
3076
  eventId: e.id,
3077
+ kind: e.kind,
3078
+ replyToEventId: tagValue(e.tags, "e") ?? null,
2955
3079
  authorPubkey: e.pubkey,
2956
3080
  createdAt: e.created_at,
2957
3081
  content: e.content
@@ -2962,7 +3086,11 @@ async function fetchItem(ctx, eventId, expectedKind) {
2962
3086
  return {
2963
3087
  item: parseTrackerItem(
2964
3088
  event,
2965
- deriveStatus(eventId, statusEvents.values(), authorized)
3089
+ deriveStatus(
3090
+ eventId,
3091
+ statusEvents.values(),
3092
+ withTargetAuthor(authorized, event.pubkey)
3093
+ )
2966
3094
  ),
2967
3095
  comments,
2968
3096
  repoATag: repoATag2
@@ -3028,8 +3156,9 @@ async function runList(args, deps, spec) {
3028
3156
  }
3029
3157
  for (const item of items) {
3030
3158
  const labels = item.labels.length > 0 ? ` [${item.labels.join(", ")}]` : "";
3159
+ const branch = item.branch !== void 0 ? ` \u2192 ${item.branch}` : "";
3031
3160
  io.out(
3032
- `${item.status.padEnd(7)} ${item.eventId.slice(0, 8)} ${item.title} (${item.authorPubkey.slice(0, 8)}, ${isoDate(item.createdAt)})${labels}`
3161
+ `${item.status.padEnd(7)} ${item.eventId.slice(0, 8)} ${item.title} (${item.authorPubkey.slice(0, 8)}, ${isoDate(item.createdAt)})${labels}${branch}`
3033
3162
  );
3034
3163
  }
3035
3164
  io.out(
@@ -3105,8 +3234,9 @@ async function runShow(args, deps, spec) {
3105
3234
  io.out("");
3106
3235
  io.out(`Comments (${shown.comments.length}):`);
3107
3236
  for (const comment of shown.comments) {
3237
+ const replyTo = comment.replyToEventId !== null && comment.replyToEventId !== item.eventId ? ` in reply to ${comment.replyToEventId.slice(0, 8)}` : "";
3108
3238
  io.out(
3109
- `--- ${comment.eventId.slice(0, 8)} by ${comment.authorPubkey.slice(0, 8)} on ${isoDate(comment.createdAt)}`
3239
+ `--- ${comment.eventId.slice(0, 8)} by ${comment.authorPubkey.slice(0, 8)} on ${isoDate(comment.createdAt)}${replyTo}`
3110
3240
  );
3111
3241
  for (const line of comment.content.split("\n")) io.out(line);
3112
3242
  }
@@ -3194,18 +3324,27 @@ ${COMMON_FLAGS_USAGE2}
3194
3324
  Related (FREE reads \u2014 no payment):
3195
3325
  rig issue list [--state open|closed|all] list the repo's issues
3196
3326
  rig issue show <event-id> one issue + its comments`;
3197
- var COMMENT_USAGE = `Usage: rig comment <root-event-id> --body <text> [options]
3327
+ var COMMENT_USAGE = `Usage: rig comment <target-event-id> --body <text> [options]
3328
+
3329
+ Comment (NIP-22 kind:1111) on an issue or patch \u2014 a paid publish; writes are
3330
+ permanent and non-refundable. <target-event-id> is the 64-char hex id of
3331
+ either:
3332
+
3333
+ the kind:1621 issue / kind:1617 patch being commented on \u2192 a top-level
3334
+ comment, whose NIP-22 root scope (E/K/P) and parent (e/k/p) both name it;
3335
+ an existing kind:1111 comment \u2192 a REPLY: the comment becomes the lowercase
3336
+ parent (k=1111) while the uppercase root stays the issue/patch it hangs
3337
+ off.
3198
3338
 
3199
- Comment (kind:1622) on an issue or patch \u2014 a paid publish; writes are
3200
- permanent and non-refundable. <root-event-id> is the 64-char hex id of the
3201
- kind:1621 issue / kind:1617 patch being commented on.
3339
+ The target is read from the resolved relay first (a FREE read) so the root's
3340
+ kind and author are taken from the wire rather than guessed. The repo
3341
+ coordinate rides along as an \`a\` tag for subscription filtering.
3342
+
3343
+ Older rig releases read only the legacy kind:1622 dialect and will NOT see
3344
+ comments published by this version.
3202
3345
 
3203
3346
  Options:
3204
3347
  --body <text> comment body (Markdown) [required]
3205
- --parent-author <pubkey> pubkey of the TARGET event's author (p threading
3206
- tag; default: the repo owner)
3207
- --marker <root|reply> e-tag marker (default: root \u2014 commenting directly
3208
- on the issue/patch)
3209
3348
  ${COMMON_FLAGS_USAGE2}`;
3210
3349
  var PR_CREATE_USAGE = `Usage: rig pr create --title <title> (--range <range> | --patch-file <path>) [options]
3211
3350
 
@@ -3228,7 +3367,7 @@ Options:
3228
3367
  --patch-file <path> literal patch text to publish
3229
3368
  --body <text> PR description (Markdown; description tag)
3230
3369
  --body-file <path> read the PR description from a file
3231
- --branch <name> branch name (t tag)
3370
+ --branch <name> branch name (branch-name tag)
3232
3371
  ${COMMON_FLAGS_USAGE2}`;
3233
3372
  var PR_STATUS_USAGE = `Usage: rig pr status <target-event-id> <open|applied|closed|draft> [options]
3234
3373
 
@@ -3333,7 +3472,7 @@ async function runEvent(opts) {
3333
3472
  }
3334
3473
  const owner = flags.owner ?? toonConfig.owner ?? identity.pubkey;
3335
3474
  const addr = { ownerPubkey: owner, repoId };
3336
- const event = await opts.buildEvent(addr);
3475
+ const event = await opts.buildEvent(addr, relaysUsed[0]);
3337
3476
  const action = `kind:${event.kind} ${actionLabel}`;
3338
3477
  if (opts.preConfirm) {
3339
3478
  await opts.preConfirm({ identity, addr, relayUrl: relaysUsed[0] });
@@ -3506,19 +3645,64 @@ async function runIssue(args, deps) {
3506
3645
  })
3507
3646
  });
3508
3647
  }
3648
+ var TARGET_LOOKUP_TIMEOUT_MS = 1e4;
3649
+ async function resolveCommentTarget(deps, targetEventId, relayUrl) {
3650
+ if (relayUrl === void 0) {
3651
+ throw new Error(
3652
+ "no relay resolved \u2014 a comment needs the target event to derive its NIP-22 root; pass --relay <url> or configure a remote"
3653
+ );
3654
+ }
3655
+ const found = await queryRelay(
3656
+ relayUrl,
3657
+ { ids: [targetEventId] },
3658
+ TARGET_LOOKUP_TIMEOUT_MS,
3659
+ deps.webSocketFactory ?? defaultWebSocketFactory
3660
+ );
3661
+ const target = found.find((e) => e.id === targetEventId);
3662
+ if (!target) {
3663
+ throw new Error(
3664
+ `event ${targetEventId} not found on ${relayUrl} \u2014 nothing was published or paid`
3665
+ );
3666
+ }
3667
+ if (target.kind === LEGACY_COMMENT_KIND) {
3668
+ throw new Error(
3669
+ `event ${targetEventId} is a legacy kind:1622 comment \u2014 replies to it are not representable in NIP-22; comment on the issue or patch instead`
3670
+ );
3671
+ }
3672
+ if (target.kind === COMMENT_KIND) {
3673
+ const rootEventId = firstTagValue(target.tags, "E");
3674
+ const rootKind = Number(firstTagValue(target.tags, "K"));
3675
+ const rootAuthor = firstTagValue(target.tags, "P");
3676
+ if (rootEventId === void 0 || !HEX64_RE3.test(rootEventId) || !Number.isSafeInteger(rootKind) || rootAuthor === void 0 || !HEX64_RE3.test(rootAuthor)) {
3677
+ throw new Error(
3678
+ `kind:1111 comment ${targetEventId} carries no usable NIP-22 root scope (E/K/P tags) \u2014 cannot derive the thread root to reply into`
3679
+ );
3680
+ }
3681
+ return {
3682
+ root: { eventId: rootEventId, kind: rootKind, authorPubkey: rootAuthor },
3683
+ parent: { eventId: target.id, authorPubkey: target.pubkey }
3684
+ };
3685
+ }
3686
+ return {
3687
+ root: {
3688
+ eventId: target.id,
3689
+ kind: target.kind,
3690
+ authorPubkey: target.pubkey
3691
+ }
3692
+ };
3693
+ }
3509
3694
  async function runComment(args, deps) {
3510
3695
  const { io } = deps;
3511
3696
  let flags;
3512
- let rootEventId;
3697
+ let targetEventId;
3513
3698
  let body;
3514
- let parentAuthor;
3515
- let marker;
3516
3699
  try {
3517
3700
  const { values, positionals } = parseArgs8({
3518
3701
  args,
3519
3702
  options: {
3520
3703
  ...COMMON_OPTIONS2,
3521
3704
  body: { type: "string" },
3705
+ // Retired by #159 — parsed only to explain the migration.
3522
3706
  "parent-author": { type: "string" },
3523
3707
  marker: { type: "string" }
3524
3708
  },
@@ -3529,49 +3713,59 @@ async function runComment(args, deps) {
3529
3713
  io.out(COMMENT_USAGE);
3530
3714
  return 0;
3531
3715
  }
3716
+ if (values.marker !== void 0 || values["parent-author"] !== void 0) {
3717
+ throw new Error(
3718
+ "--marker and --parent-author were removed with the move to NIP-22 kind:1111 (#159): pass the comment you are replying to as <target-event-id> instead \u2014 its author and the thread root are read from the relay"
3719
+ );
3720
+ }
3532
3721
  if (positionals.length !== 1) {
3533
3722
  throw new Error(
3534
- positionals.length === 0 ? "<root-event-id> is required" : `expected exactly one <root-event-id>, got ${positionals.length} positionals`
3723
+ positionals.length === 0 ? "<target-event-id> is required" : `expected exactly one <target-event-id>, got ${positionals.length} positionals`
3535
3724
  );
3536
3725
  }
3537
- rootEventId = positionals[0];
3538
- assertHex64(rootEventId, "<root-event-id>");
3726
+ targetEventId = positionals[0];
3727
+ assertHex64(targetEventId, "<target-event-id>");
3539
3728
  if (values.body === void 0 || values.body === "") {
3540
3729
  throw new Error("--body is required");
3541
3730
  }
3542
3731
  body = values.body;
3543
- parentAuthor = values["parent-author"];
3544
- if (parentAuthor !== void 0) assertHex64(parentAuthor, "--parent-author");
3545
- const rawMarker = values.marker ?? "root";
3546
- if (rawMarker !== "root" && rawMarker !== "reply") {
3547
- throw new Error(`--marker must be root or reply (got ${JSON.stringify(rawMarker)})`);
3548
- }
3549
- marker = rawMarker;
3550
3732
  } catch (err) {
3551
3733
  io.err(err instanceof Error ? err.message : String(err));
3552
3734
  io.err(COMMENT_USAGE);
3553
3735
  return 2;
3554
3736
  }
3737
+ let threading;
3555
3738
  return runEvent({
3556
3739
  command: "comment",
3557
3740
  flags,
3558
3741
  deps,
3559
- actionLabel: `comment on ${rootEventId.slice(0, 8)}\u2026`,
3560
- buildEvent: async (addr) => buildComment(
3561
- addr.ownerPubkey,
3562
- addr.repoId,
3563
- rootEventId,
3564
- parentAuthor ?? addr.ownerPubkey,
3565
- body,
3566
- marker
3567
- ),
3568
- sendDaemon: (client, addr) => client.gitComment({
3569
- repoAddr: addr,
3570
- rootEventId,
3571
- body,
3572
- ...parentAuthor !== void 0 ? { parentAuthorPubkey: parentAuthor } : {},
3573
- marker
3574
- })
3742
+ actionLabel: `comment on ${targetEventId.slice(0, 8)}\u2026`,
3743
+ buildEvent: async (addr, relayUrl) => {
3744
+ threading = await resolveCommentTarget(deps, targetEventId, relayUrl);
3745
+ return buildComment(
3746
+ addr.ownerPubkey,
3747
+ addr.repoId,
3748
+ threading.root,
3749
+ body,
3750
+ threading.parent
3751
+ );
3752
+ },
3753
+ sendDaemon: (client, addr) => {
3754
+ if (threading === void 0) {
3755
+ throw new Error(
3756
+ "internal: the comment target was not resolved before the publish"
3757
+ );
3758
+ }
3759
+ const { root, parent } = threading;
3760
+ return client.gitComment({
3761
+ repoAddr: addr,
3762
+ body,
3763
+ rootEventId: root.eventId,
3764
+ rootKind: root.kind,
3765
+ rootAuthorPubkey: root.authorPubkey,
3766
+ ...parent !== void 0 ? { parentComment: parent } : {}
3767
+ });
3768
+ }
3575
3769
  });
3576
3770
  }
3577
3771
  var PATCH_FROM_RE = /^From ([0-9a-f]{40}) /gm;
@@ -3776,14 +3970,18 @@ async function runPrStatus(args, deps) {
3776
3970
  flags,
3777
3971
  deps,
3778
3972
  actionLabel: `status ${status} on ${targetEventId.slice(0, 8)}\u2026`,
3779
- buildEvent: async (addr) => {
3780
- const event = buildStatus(targetEventId, STATUS_KIND_BY_VALUE[status]);
3781
- event.tags.push([
3782
- "a",
3783
- `${REPOSITORY_ANNOUNCEMENT_KIND2}:${addr.ownerPubkey}:${addr.repoId}`
3784
- ]);
3785
- return event;
3786
- },
3973
+ buildEvent: async (addr) => (
3974
+ // buildStatus (rig#160) now emits the root-marked `e` tag and the repo
3975
+ // `a` tag itself, so readers can scope a status stream to the
3976
+ // repository without resolving the target first (mirrors the
3977
+ // daemon's gitStatus).
3978
+ buildStatus(
3979
+ addr.ownerPubkey,
3980
+ addr.repoId,
3981
+ targetEventId,
3982
+ STATUS_KIND_BY_VALUE[status]
3983
+ )
3984
+ ),
3787
3985
  // Authority warning (#287): a status event only moves an issue/PR's
3788
3986
  // resolved state for repo-authority-honoring clients if its author is the
3789
3987
  // owner or a declared maintainer. Warn clearly when the active identity is
@@ -3811,11 +4009,11 @@ async function warnIfNotMaintainer(deps, args) {
3811
4009
  );
3812
4010
  if (authorized.has(identity.pubkey.toLowerCase())) return;
3813
4011
  io.err(
3814
- `warning: you (${identity.pubkey.slice(0, 8)}\u2026) are not a maintainer of 30617:${addr.ownerPubkey.slice(0, 8)}\u2026:${addr.repoId} \u2014 clients honoring repo authority (rig, rig-web) will IGNORE this status when resolving the issue/PR's state. Publishing anyway (the relay is permissionless); ask the owner to \`rig maintainers add\` you if this should stick.`
4012
+ `warning: you (${identity.pubkey.slice(0, 8)}\u2026) are not a maintainer of 30617:${addr.ownerPubkey.slice(0, 8)}\u2026:${addr.repoId} \u2014 clients honoring repo authority (rig, rig-web) will IGNORE this status when resolving the issue/PR's state, UNLESS you are also the target event's own author (rig#160: an issue/PR author may move their own item\u2019s status even without maintainer standing \u2014 this check cannot tell from here, since it does not know the target's author without a second relay read). Publishing anyway (the relay is permissionless); ask the owner to \`rig maintainers add\` you if this should stick for OTHER items too.`
3815
4013
  );
3816
4014
  } catch {
3817
4015
  io.err(
3818
- "warning: could not read the repo announcement to verify your maintainer status \u2014 if you are not the owner or a declared maintainer, authority-honoring clients will ignore this status."
4016
+ "warning: could not read the repo announcement to verify your maintainer status \u2014 if you are not the owner, a declared maintainer, or the target event's own author, authority-honoring clients will ignore this status."
3819
4017
  );
3820
4018
  }
3821
4019
  }
@@ -4059,6 +4257,11 @@ Options:
4059
4257
  --repo-id <id> repository id / NIP-34 d-tag (default: git config)
4060
4258
  --owner <pubkey> repository owner (npub or 64-char hex; default: git config)
4061
4259
  --relay <url> ad-hoc relay override \u2014 bypasses the remote's URL
4260
+ --gateway <url> gateway to read objects through FIRST, on its raw-bytes
4261
+ route <url>/raw/<txId>, ahead of the public gateway
4262
+ list \u2014 name a private, local or air-gapped gateway to
4263
+ fetch objects the public ones do not carry
4264
+ (env RIG_ARWEAVE_GATEWAY also sets it)
4062
4265
  --concurrency <n> parallel gateway downloads (default 8)
4063
4266
  --json machine-readable result envelope
4064
4267
  -h, --help show this help`;
@@ -4091,6 +4294,7 @@ async function runFetch(args, deps) {
4091
4294
  let relayFlag;
4092
4295
  let repoIdFlag;
4093
4296
  let ownerFlag;
4297
+ let gatewayFlag;
4094
4298
  let concurrency;
4095
4299
  try {
4096
4300
  const { values, positionals } = parseArgs10({
@@ -4100,6 +4304,7 @@ async function runFetch(args, deps) {
4100
4304
  relay: { type: "string" },
4101
4305
  "repo-id": { type: "string" },
4102
4306
  owner: { type: "string" },
4307
+ gateway: { type: "string" },
4103
4308
  concurrency: { type: "string" },
4104
4309
  help: { type: "boolean", short: "h", default: false }
4105
4310
  },
@@ -4119,6 +4324,7 @@ async function runFetch(args, deps) {
4119
4324
  relayFlag = values.relay;
4120
4325
  repoIdFlag = values["repo-id"];
4121
4326
  ownerFlag = values.owner !== void 0 ? ownerToHex(values.owner) : void 0;
4327
+ gatewayFlag = values.gateway;
4122
4328
  if (values.concurrency !== void 0) {
4123
4329
  concurrency = Number.parseInt(values.concurrency, 10);
4124
4330
  if (!Number.isSafeInteger(concurrency) || concurrency < 1) {
@@ -4157,7 +4363,9 @@ async function runFetch(args, deps) {
4157
4363
  ownerPubkey: owner,
4158
4364
  repoId,
4159
4365
  ...deps.webSocketFactory ? { webSocketFactory: deps.webSocketFactory } : {},
4160
- ...deps.resolveSha ? { resolveSha: deps.resolveSha } : {}
4366
+ // The GraphQL fallback for SHAs the map does not cover asks the SAME
4367
+ // permaweb the objects are read from (#183).
4368
+ resolveSha: deps.resolveSha ?? shaResolverFor(gatewayFlag, deps.env)
4161
4369
  });
4162
4370
  const planned = [];
4163
4371
  for (const [refname, sha] of remoteState.refs) {
@@ -4178,17 +4386,14 @@ async function runFetch(args, deps) {
4178
4386
  }
4179
4387
  const reader = new GitRepoReader(repoRoot);
4180
4388
  const tips = [...new Set(planned.map((p) => p.newSha))];
4181
- const candidates = [.../* @__PURE__ */ new Set([...remoteState.shaToTxId.keys(), ...tips])];
4182
- const { missing: absent } = await reader.statObjects(candidates);
4183
- const absentSet = new Set(absent);
4184
- const presentLocally = new Set(
4185
- candidates.filter((sha) => !absentSet.has(sha))
4186
- );
4389
+ const presentLocally = new Set(await reader.listAllObjectShas());
4187
4390
  const collected = await collectRepoObjects({
4188
4391
  tips,
4189
4392
  shaToTxId: remoteState.shaToTxId,
4190
4393
  resolveMissing: (shas) => remoteState.resolveMissing(shas),
4191
4394
  presentLocally,
4395
+ // `--gateway` / RIG_ARWEAVE_GATEWAY first, public list behind it (#176).
4396
+ gateways: readGateways(gatewayFlag, deps.env),
4192
4397
  ...deps.fetchFn ? { fetchFn: deps.fetchFn } : {},
4193
4398
  ...concurrency !== void 0 ? { concurrency } : {}
4194
4399
  });
@@ -5522,44 +5727,15 @@ async function runMutate(op, args, deps) {
5522
5727
  }
5523
5728
  let standaloneCtx;
5524
5729
  try {
5525
- const ctx = await resolveRepoContext(flags, deps);
5526
- if (ctx.relays.length > 1) {
5527
- io.err(singleRelayRefusal(ctx.resolved, "Nothing was published or paid."));
5528
- return 1;
5529
- }
5530
- const relayUrl = ctx.relays[0];
5531
- if (relayUrl === void 0 || !WS_URL_RE.test(relayUrl)) {
5532
- throw new InvalidRelayUrlError(
5533
- relayUrl ?? "",
5534
- "a paid publish needs a ws:// or wss:// relay"
5535
- );
5536
- }
5537
- const load = deps.loadStandalone ?? defaultLoadStandalone;
5538
- standaloneCtx = await load({
5539
- env: deps.env,
5540
- cwd: deps.cwd,
5541
- warn: (line) => io.err(line),
5542
- relayUrl
5730
+ const opened = await openAnnouncementRepublish({
5731
+ deps,
5732
+ flags,
5733
+ what: "change the maintainer set",
5734
+ before: "before managing maintainers"
5543
5735
  });
5544
- const identity = identityReport(standaloneCtx);
5545
- if (identity.pubkey.toLowerCase() !== ctx.owner.toLowerCase()) {
5546
- io.err(
5547
- `rig: only the repo owner (${ctx.owner.slice(0, 8)}\u2026) can change the maintainer set \u2014 the active identity is ${identity.pubkey.slice(0, 8)}\u2026. A non-owner republish would write your own (ignored) announcement. Nothing was published or paid.`
5548
- );
5549
- return 1;
5550
- }
5551
- const remote = await fetchRemoteState({
5552
- relayUrls: [relayUrl],
5553
- ownerPubkey: ctx.owner,
5554
- repoId: ctx.repoId,
5555
- ...deps.webSocketFactory ? { webSocketFactory: deps.webSocketFactory } : {}
5556
- });
5557
- if (!remote.announced) {
5558
- io.err(
5559
- `rig: 30617:${ctx.owner.slice(0, 8)}\u2026:${ctx.repoId} has no announcement yet \u2014 run \`rig push\` to publish the repo (with its real name/description) before managing maintainers. Nothing was published or paid.`
5560
- );
5561
- return 1;
5562
- }
5736
+ standaloneCtx = opened.standalone;
5737
+ if (!opened.ok) return opened.exitCode;
5738
+ const { ctx, relayUrl, identity, remote, facts } = opened;
5563
5739
  const current = remote.announceEvent ? parseMaintainers(remote.announceEvent.tags) : [];
5564
5740
  const currentSet = new Set(current);
5565
5741
  if (op === "add" && currentSet.has(pubkey)) {
@@ -5575,21 +5751,23 @@ async function runMutate(op, args, deps) {
5575
5751
  return 0;
5576
5752
  }
5577
5753
  const next = op === "add" ? [...current, pubkey] : current.filter((m) => m !== pubkey);
5578
- const name = remote.name ?? ctx.repoId;
5579
- const description = remote.description ?? "";
5580
- const event = buildRepoAnnouncement(
5581
- ctx.repoId,
5582
- name,
5583
- description,
5584
- next,
5585
- remote.payout
5586
- );
5587
- const fee = (await standaloneCtx.publisher.getFeeRates()).eventFee.toString();
5754
+ const event = amendRepoAnnouncement(remote.announceEvent, {
5755
+ repoId: ctx.repoId,
5756
+ maintainers: next,
5757
+ ...conformanceEdits(remote.announceEvent, facts)
5758
+ });
5759
+ const diff = diffAnnouncementTags(remote.announceEvent, event);
5760
+ const changes = { added: diff.added, removed: diff.removed };
5761
+ const fee = (await opened.standalone.publisher.getFeeRates()).eventFee.toString();
5588
5762
  const action = `kind:30617 maintainers ${op} ${pubkey.slice(0, 8)}\u2026`;
5589
5763
  if (!flags.json) {
5590
5764
  io.out(`Republish ${action}`);
5591
5765
  io.out(`Repo: 30617:${ctx.owner}:${ctx.repoId}`);
5592
5766
  io.out(`Maintainers after: ${next.length === 0 ? "(none)" : next.join(", ")}`);
5767
+ if (!diff.unchanged) {
5768
+ io.out("Tags changed:");
5769
+ for (const line of describeAnnouncementDiff(diff)) io.out(line);
5770
+ }
5593
5771
  io.out(`Fee: ${feeLabel(fee)}. Writes are permanent and non-refundable.`);
5594
5772
  }
5595
5773
  if (!flags.yes) {
@@ -5601,6 +5779,7 @@ async function runMutate(op, args, deps) {
5601
5779
  executed: false,
5602
5780
  feeEstimate: fee,
5603
5781
  maintainers: next,
5782
+ changes,
5604
5783
  hint: "estimate only \u2014 re-run with --yes to publish (permanent, non-refundable)"
5605
5784
  });
5606
5785
  return 0;
@@ -5619,7 +5798,7 @@ async function runMutate(op, args, deps) {
5619
5798
  return 1;
5620
5799
  }
5621
5800
  }
5622
- const receipt = await standaloneCtx.publisher.publishEvent(event, [
5801
+ const receipt = await opened.standalone.publisher.publishEvent(event, [
5623
5802
  relayUrl
5624
5803
  ]);
5625
5804
  const result = serializeEventReceipt(event.kind, receipt);
@@ -5631,6 +5810,7 @@ async function runMutate(op, args, deps) {
5631
5810
  executed: true,
5632
5811
  feeEstimate: fee,
5633
5812
  maintainers: next,
5813
+ changes,
5634
5814
  result
5635
5815
  });
5636
5816
  } else {
@@ -5651,9 +5831,167 @@ async function runMutate(op, args, deps) {
5651
5831
  }
5652
5832
  }
5653
5833
 
5834
+ // src/cli/refresh.ts
5835
+ import { parseArgs as parseArgs15 } from "util";
5836
+ var REFRESH_USAGE = `Usage: rig refresh [options]
5837
+
5838
+ Republish this repo's kind:30617 announcement with no field edit, refreshing
5839
+ its NIP-34 conformance tags (#158) so other clients can find and group it:
5840
+
5841
+ relays the relay this repo publishes to \u2014 where its issues,
5842
+ patches and state live. Added to the relays the
5843
+ announcement already lists; none are removed
5844
+ web the repo's rig-web viewer URL, added alongside any
5845
+ viewer URL another client already announced
5846
+ r <sha> euc the earliest unique commit: the repo's fork identity.
5847
+ Written only if the announcement has none \u2014 once declared it
5848
+ never changes, so forks stay grouped
5849
+
5850
+ Every other tag rides along verbatim, including tags written by other NIP-34
5851
+ clients. rig never writes a \`clone\` tag (it has no git-clonable URL) and never
5852
+ removes one.
5853
+
5854
+ PAID: republishing the kind:30617 costs one event fee (permanent,
5855
+ non-refundable). Nothing is published and nothing is paid when no tag would
5856
+ change. Must run under the repo OWNER's identity \u2014 only the owner's
5857
+ announcement is authoritative.
5858
+
5859
+ Options:
5860
+ --repo-id <id> repository id / NIP-34 d-tag (default: git config)
5861
+ --owner <pubkey> repository owner (npub or hex; default: git config)
5862
+ --remote <name> publish/read via this configured git remote (default: origin)
5863
+ --relay <url> ad-hoc relay override (exactly one)
5864
+ --yes skip the fee confirmation (required when not a TTY)
5865
+ --json machine-readable envelope
5866
+ -h, --help show this help`;
5867
+ async function runRefresh(args, deps) {
5868
+ const { io } = deps;
5869
+ let flags;
5870
+ try {
5871
+ const { values, positionals } = parseArgs15({
5872
+ args,
5873
+ options: REPO_COMMAND_OPTIONS,
5874
+ allowPositionals: true
5875
+ });
5876
+ flags = pickRepoCommandFlags(values);
5877
+ if (flags.help) {
5878
+ io.out(REFRESH_USAGE);
5879
+ return 0;
5880
+ }
5881
+ if (positionals.length > 0) {
5882
+ throw new Error("rig refresh takes no positional arguments");
5883
+ }
5884
+ } catch (err) {
5885
+ io.err(err instanceof Error ? err.message : String(err));
5886
+ io.err(REFRESH_USAGE);
5887
+ return 2;
5888
+ }
5889
+ let standaloneCtx;
5890
+ try {
5891
+ const opened = await openAnnouncementRepublish({
5892
+ deps,
5893
+ flags,
5894
+ what: "refresh the announcement",
5895
+ before: "before refreshing it"
5896
+ });
5897
+ standaloneCtx = opened.standalone;
5898
+ if (!opened.ok) return opened.exitCode;
5899
+ const { ctx, relayUrl, identity, remote, facts } = opened;
5900
+ const event = amendRepoAnnouncement(remote.announceEvent, {
5901
+ repoId: ctx.repoId,
5902
+ ...conformanceEdits(remote.announceEvent, facts)
5903
+ });
5904
+ const diff = diffAnnouncementTags(remote.announceEvent, event);
5905
+ if (diff.unchanged) {
5906
+ const changes2 = { added: diff.added, removed: diff.removed };
5907
+ if (flags.json) {
5908
+ io.emitJson({
5909
+ command: "refresh",
5910
+ repoAddr: { ownerPubkey: ctx.owner, repoId: ctx.repoId },
5911
+ identity,
5912
+ executed: false,
5913
+ feeEstimate: null,
5914
+ changes: changes2,
5915
+ hint: "announcement is already up to date \u2014 nothing published, nothing paid"
5916
+ });
5917
+ return 0;
5918
+ }
5919
+ io.out(
5920
+ `Announcement 30617:${ctx.owner}:${ctx.repoId} is already up to date \u2014 nothing to do (not published, nothing paid).`
5921
+ );
5922
+ return 0;
5923
+ }
5924
+ const fee = (await opened.standalone.publisher.getFeeRates()).eventFee.toString();
5925
+ const changes = { added: diff.added, removed: diff.removed };
5926
+ if (!flags.json) {
5927
+ io.out("Republish kind:30617 announcement (refresh)");
5928
+ io.out(`Repo: 30617:${ctx.owner}:${ctx.repoId}`);
5929
+ io.out("Tags changed:");
5930
+ for (const line of describeAnnouncementDiff(diff)) io.out(line);
5931
+ io.out(`Fee: ${feeLabel(fee)}. Writes are permanent and non-refundable.`);
5932
+ }
5933
+ if (!flags.yes) {
5934
+ if (flags.json) {
5935
+ io.emitJson({
5936
+ command: "refresh",
5937
+ repoAddr: { ownerPubkey: ctx.owner, repoId: ctx.repoId },
5938
+ identity,
5939
+ executed: false,
5940
+ feeEstimate: fee,
5941
+ changes,
5942
+ hint: "estimate only \u2014 re-run with --yes to publish (permanent, non-refundable)"
5943
+ });
5944
+ return 0;
5945
+ }
5946
+ if (!io.isInteractive) {
5947
+ io.err(
5948
+ "refusing to spend channel funds without confirmation in a non-interactive session \u2014 re-run with --yes (or use --json for an estimate)"
5949
+ );
5950
+ return 1;
5951
+ }
5952
+ const proceed = await io.confirm(
5953
+ `Proceed with paid republish (${feeLabel(fee)})? [y/N] `
5954
+ );
5955
+ if (!proceed) {
5956
+ io.err("aborted \u2014 nothing was published.");
5957
+ return 1;
5958
+ }
5959
+ }
5960
+ const receipt = await opened.standalone.publisher.publishEvent(event, [
5961
+ relayUrl
5962
+ ]);
5963
+ const result = serializeEventReceipt(event.kind, receipt);
5964
+ if (flags.json) {
5965
+ io.emitJson({
5966
+ command: "refresh",
5967
+ repoAddr: { ownerPubkey: ctx.owner, repoId: ctx.repoId },
5968
+ identity,
5969
+ executed: true,
5970
+ feeEstimate: fee,
5971
+ changes,
5972
+ result
5973
+ });
5974
+ } else {
5975
+ io.out(
5976
+ `Published kind:30617 refresh: ${result.eventId} paid ${result.feePaid} base units`
5977
+ );
5978
+ }
5979
+ return 0;
5980
+ } catch (err) {
5981
+ return emitCliError(io, flags.json, "refresh", err);
5982
+ } finally {
5983
+ if (standaloneCtx) {
5984
+ try {
5985
+ await standaloneCtx.stop();
5986
+ } catch {
5987
+ }
5988
+ }
5989
+ }
5990
+ }
5991
+
5654
5992
  // src/cli/name.ts
5655
5993
  import { randomUUID } from "crypto";
5656
- import { parseArgs as parseArgs15 } from "util";
5994
+ import { parseArgs as parseArgs16 } from "util";
5657
5995
  var ARIO_DECIMALS = 6;
5658
5996
  function renderMario(mario) {
5659
5997
  return `${mario.toString()} mARIO (~${formatUnits(mario.toString(), ARIO_DECIMALS)} ARIO)`;
@@ -6162,7 +6500,7 @@ async function resolveNameContext(deps, network, processId, mode) {
6162
6500
  return { identity, solanaAddress, network, processId, sdk, nostrSecretKey };
6163
6501
  }
6164
6502
  function parseNameFlags(args, env) {
6165
- const { values, positionals } = parseArgs15({
6503
+ const { values, positionals } = parseArgs16({
6166
6504
  args,
6167
6505
  allowPositionals: true,
6168
6506
  options: {
@@ -6721,7 +7059,7 @@ async function runName(args, deps) {
6721
7059
  }
6722
7060
 
6723
7061
  // src/cli/payout.ts
6724
- import { parseArgs as parseArgs16 } from "util";
7062
+ import { parseArgs as parseArgs17 } from "util";
6725
7063
  import { getAddress } from "viem";
6726
7064
  var PAYOUT_USAGE = `Usage: rig payout <set|clear|show> [<address>] [options]
6727
7065
 
@@ -6777,7 +7115,7 @@ async function runShow2(args, deps) {
6777
7115
  const { io } = deps;
6778
7116
  let flags;
6779
7117
  try {
6780
- const { values, positionals } = parseArgs16({
7118
+ const { values, positionals } = parseArgs17({
6781
7119
  args,
6782
7120
  options: REPO_COMMAND_OPTIONS,
6783
7121
  allowPositionals: true
@@ -6839,7 +7177,7 @@ async function runMutate2(op, args, deps) {
6839
7177
  let flags;
6840
7178
  let nextPayout = null;
6841
7179
  try {
6842
- const { values, positionals } = parseArgs16({
7180
+ const { values, positionals } = parseArgs17({
6843
7181
  args,
6844
7182
  options: REPO_COMMAND_OPTIONS,
6845
7183
  allowPositionals: true
@@ -6870,44 +7208,15 @@ async function runMutate2(op, args, deps) {
6870
7208
  }
6871
7209
  let standaloneCtx;
6872
7210
  try {
6873
- const ctx = await resolveRepoContext(flags, deps);
6874
- if (ctx.relays.length > 1) {
6875
- io.err(singleRelayRefusal(ctx.resolved, "Nothing was published or paid."));
6876
- return 1;
6877
- }
6878
- const relayUrl = ctx.relays[0];
6879
- if (relayUrl === void 0 || !WS_URL_RE.test(relayUrl)) {
6880
- throw new InvalidRelayUrlError(
6881
- relayUrl ?? "",
6882
- "a paid publish needs a ws:// or wss:// relay"
6883
- );
6884
- }
6885
- const load = deps.loadStandalone ?? defaultLoadStandalone;
6886
- standaloneCtx = await load({
6887
- env: deps.env,
6888
- cwd: deps.cwd,
6889
- warn: (line) => io.err(line),
6890
- relayUrl
7211
+ const opened = await openAnnouncementRepublish({
7212
+ deps,
7213
+ flags,
7214
+ what: "change the payout pointer",
7215
+ before: "before managing the payout pointer"
6891
7216
  });
6892
- const identity = identityReport(standaloneCtx);
6893
- if (identity.pubkey.toLowerCase() !== ctx.owner.toLowerCase()) {
6894
- io.err(
6895
- `rig: only the repo owner (${ctx.owner.slice(0, 8)}\u2026) can change the payout pointer \u2014 the active identity is ${identity.pubkey.slice(0, 8)}\u2026. A non-owner republish would write your own (ignored) announcement. Nothing was published or paid.`
6896
- );
6897
- return 1;
6898
- }
6899
- const remote = await fetchRemoteState({
6900
- relayUrls: [relayUrl],
6901
- ownerPubkey: ctx.owner,
6902
- repoId: ctx.repoId,
6903
- ...deps.webSocketFactory ? { webSocketFactory: deps.webSocketFactory } : {}
6904
- });
6905
- if (!remote.announced) {
6906
- io.err(
6907
- `rig: 30617:${ctx.owner.slice(0, 8)}\u2026:${ctx.repoId} has no announcement yet \u2014 run \`rig push\` to publish the repo (with its real name/description) before managing the payout pointer. Nothing was published or paid.`
6908
- );
6909
- return 1;
6910
- }
7217
+ standaloneCtx = opened.standalone;
7218
+ if (!opened.ok) return opened.exitCode;
7219
+ const { ctx, relayUrl, identity, remote, facts } = opened;
6911
7220
  const current = remote.payout;
6912
7221
  if (nextPayout !== null && current !== null && current.chain === nextPayout.chain && current.address === nextPayout.address) {
6913
7222
  io.err(
@@ -6919,16 +7228,14 @@ async function runMutate2(op, args, deps) {
6919
7228
  io.err("rig: no payout pointer is set \u2014 nothing to do (not published).");
6920
7229
  return 0;
6921
7230
  }
6922
- const name = remote.name ?? ctx.repoId;
6923
- const description = remote.description ?? "";
6924
- const event = buildRepoAnnouncement(
6925
- ctx.repoId,
6926
- name,
6927
- description,
6928
- remote.maintainers,
6929
- nextPayout
6930
- );
6931
- const fee = (await standaloneCtx.publisher.getFeeRates()).eventFee.toString();
7231
+ const event = amendRepoAnnouncement(remote.announceEvent, {
7232
+ repoId: ctx.repoId,
7233
+ payout: nextPayout,
7234
+ ...conformanceEdits(remote.announceEvent, facts)
7235
+ });
7236
+ const diff = diffAnnouncementTags(remote.announceEvent, event);
7237
+ const changes = { added: diff.added, removed: diff.removed };
7238
+ const fee = (await opened.standalone.publisher.getFeeRates()).eventFee.toString();
6932
7239
  const action = nextPayout ? `kind:30617 payout set ${nextPayout.chain} ${nextPayout.address}` : "kind:30617 payout clear";
6933
7240
  if (!flags.json) {
6934
7241
  io.out(`Republish ${action}`);
@@ -6936,6 +7243,10 @@ async function runMutate2(op, args, deps) {
6936
7243
  io.out(
6937
7244
  `Payout after: ${nextPayout ? `${nextPayout.chain} ${nextPayout.address}` : "(none)"}`
6938
7245
  );
7246
+ if (!diff.unchanged) {
7247
+ io.out("Tags changed:");
7248
+ for (const line of describeAnnouncementDiff(diff)) io.out(line);
7249
+ }
6939
7250
  io.out(`Fee: ${feeLabel(fee)}. Writes are permanent and non-refundable.`);
6940
7251
  }
6941
7252
  if (!flags.yes) {
@@ -6947,6 +7258,7 @@ async function runMutate2(op, args, deps) {
6947
7258
  executed: false,
6948
7259
  feeEstimate: fee,
6949
7260
  payout: nextPayout,
7261
+ changes,
6950
7262
  hint: "estimate only \u2014 re-run with --yes to publish (permanent, non-refundable)"
6951
7263
  });
6952
7264
  return 0;
@@ -6965,7 +7277,7 @@ async function runMutate2(op, args, deps) {
6965
7277
  return 1;
6966
7278
  }
6967
7279
  }
6968
- const receipt = await standaloneCtx.publisher.publishEvent(event, [
7280
+ const receipt = await opened.standalone.publisher.publishEvent(event, [
6969
7281
  relayUrl
6970
7282
  ]);
6971
7283
  const result = serializeEventReceipt(event.kind, receipt);
@@ -6977,6 +7289,7 @@ async function runMutate2(op, args, deps) {
6977
7289
  executed: true,
6978
7290
  feeEstimate: fee,
6979
7291
  payout: nextPayout,
7292
+ changes,
6980
7293
  result
6981
7294
  });
6982
7295
  } else {
@@ -6998,7 +7311,7 @@ async function runMutate2(op, args, deps) {
6998
7311
  }
6999
7312
 
7000
7313
  // src/cli/site.ts
7001
- import { parseArgs as parseArgs17 } from "util";
7314
+ import { parseArgs as parseArgs18 } from "util";
7002
7315
 
7003
7316
  // src/arweave-manifest.ts
7004
7317
  var DEFAULT_INDEX = "index.html";
@@ -7089,7 +7402,7 @@ Options (publish):
7089
7402
  estimate (nothing uploaded)
7090
7403
  -h, --help show this help`;
7091
7404
  function parseSiteArgs(args) {
7092
- const { values, positionals } = parseArgs17({
7405
+ const { values, positionals } = parseArgs18({
7093
7406
  args,
7094
7407
  options: {
7095
7408
  index: { type: "string" },
@@ -7492,7 +7805,8 @@ Commands rig owns:
7492
7805
  issue list | show <id> read the repo's issues + comments (free)
7493
7806
  pr list | show <id> read the repo's patches; show prints the full
7494
7807
  patch text (free)
7495
- comment <root-event-id> comment (kind:1622) on an issue or patch
7808
+ comment <target-event-id> comment (NIP-22 kind:1111) on an issue or patch,
7809
+ or reply to an existing kind:1111 comment
7496
7810
  pr create publish a patch (kind:1617) with real
7497
7811
  \`git format-patch\` content
7498
7812
  pr status <event-id> <state> set an issue/patch status (kind:1630-1633):
@@ -7501,6 +7815,9 @@ Commands rig owns:
7501
7815
  maintainers add <pubkey> remove republish the kind:30617 to change who may
7502
7816
  maintainers remove <pubkey> author authoritative issue/PR status (owner is
7503
7817
  always an implicit maintainer)
7818
+ refresh republish the kind:30617 with no field edit to
7819
+ refresh its NIP-34 tags (relays, web, the euc fork
7820
+ identity) \u2014 PAID, and free when nothing changes
7504
7821
  payout show show the repo's declared payout pointer (free);
7505
7822
  payout set <address> set/clear republish the kind:30617 \u2014 no pointer
7506
7823
  payout clear means the serving node keeps 100% of
@@ -7619,6 +7936,8 @@ async function dispatch(argv, deps) {
7619
7936
  return runMaintainers(rest, deps);
7620
7937
  case "payout":
7621
7938
  return runPayout(rest, deps);
7939
+ case "refresh":
7940
+ return runRefresh(rest, deps);
7622
7941
  case "channel":
7623
7942
  return runChannel(rest, deps);
7624
7943
  case "channels":
@@ -7661,7 +7980,6 @@ async function dispatch(argv, deps) {
7661
7980
  }
7662
7981
 
7663
7982
  export {
7664
- PREFERRED_GATEWAY,
7665
7983
  renderIdentityLine,
7666
7984
  withForcedStandalone,
7667
7985
  loadPaidSession,
@@ -7670,4 +7988,4 @@ export {
7670
7988
  rigVersion,
7671
7989
  dispatch
7672
7990
  };
7673
- //# sourceMappingURL=chunk-UZZ2JKEC.js.map
7991
+ //# sourceMappingURL=chunk-J6Y3U645.js.map