@toon-protocol/rig 4.4.0 → 4.6.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,
@@ -40,6 +44,7 @@ import {
40
44
  serializeEventReceipt,
41
45
  serializePushPlan,
42
46
  serializePushResult,
47
+ serializeSecretUpdate,
43
48
  setGitAuthor,
44
49
  setHeadSymref,
45
50
  sha256Hex,
@@ -47,22 +52,24 @@ import {
47
52
  updateRef,
48
53
  writeGitObjects,
49
54
  writeToonConfig
50
- } from "./chunk-RL5XUWR4.js";
55
+ } from "./chunk-DCGV6ALH.js";
51
56
  import {
52
57
  ARWEAVE_GATEWAYS,
53
58
  COMMENT_KIND,
54
59
  DEFAULT_KEYSTORE_PASSWORD,
60
+ LEGACY_COMMENT_KIND,
55
61
  MissingIdentityError,
56
62
  authorizedStatusAuthors,
57
63
  buildComment,
58
64
  buildIssue,
59
65
  buildPatch,
60
- buildRepoAnnouncement,
61
66
  buildStatus,
62
67
  clientConfigPath,
63
68
  clientKeystorePath,
69
+ commentBelongsToThread,
64
70
  defaultWebSocketFactory,
65
71
  fetchRemoteState,
72
+ firstTagValue,
66
73
  isValidEvmPayoutAddress,
67
74
  linkKeystoreInClientConfig,
68
75
  parseMaintainers,
@@ -70,7 +77,7 @@ import {
70
77
  readClientConfigFile,
71
78
  resolveAccountIndex,
72
79
  resolveIdentity
73
- } from "./chunk-GWA3CSUR.js";
80
+ } from "./chunk-JBFBD7HV.js";
74
81
  import {
75
82
  ChannelMapStore,
76
83
  channelStatus,
@@ -99,6 +106,12 @@ function mirrorGatewaysFor(primary) {
99
106
  }
100
107
 
101
108
  // src/cli/render.ts
109
+ function describeAnnouncementDiff(diff) {
110
+ return [
111
+ ...diff.removed.map((tag) => ` - ${tag.join(" ")}`),
112
+ ...diff.added.map((tag) => ` + ${tag.join(" ")}`)
113
+ ];
114
+ }
102
115
  function formatNumber(value) {
103
116
  return String(value).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
104
117
  }
@@ -213,6 +226,14 @@ function repoHashRoute(options) {
213
226
  function rigWebRoute(options) {
214
227
  return `${options.rigWebUrl.replace(/\/+$/, "")}/${repoHashRoute(options)}`;
215
228
  }
229
+ function repoWebUrl(options) {
230
+ return rigWebRoute({
231
+ rigWebUrl: options.env[RIG_WEB_URL_ENV] ?? DEFAULT_RIG_WEB_URL,
232
+ relay: options.relay,
233
+ ownerNpub: hexToNpub(options.ownerPubkey),
234
+ repoId: options.repoId
235
+ });
236
+ }
216
237
  function escapeHtml(value) {
217
238
  return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
218
239
  }
@@ -299,7 +320,7 @@ function loadPaidSession(deps, relayUrl) {
299
320
  });
300
321
  }
301
322
  var defaultLoadStandalone = async (options) => {
302
- const mod = await import("./standalone-mode-R3HH2ALP.js");
323
+ const mod = await import("./standalone-mode-UQSP4677.js");
303
324
  return mod.createStandaloneContext(options);
304
325
  };
305
326
  function identityReport(ctx) {
@@ -558,6 +579,14 @@ async function runPush(args, deps) {
558
579
  `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.`
559
580
  );
560
581
  }
582
+ const announcement = relaysUsed[0] === void 0 ? void 0 : {
583
+ web: repoWebUrl({
584
+ env: deps.env,
585
+ relay: relaysUsed[0],
586
+ ownerPubkey: identity.pubkey,
587
+ repoId
588
+ })
589
+ };
561
590
  let plan;
562
591
  let execute;
563
592
  let pointerPlan;
@@ -576,7 +605,8 @@ async function runPush(args, deps) {
576
605
  feeRates,
577
606
  repoId,
578
607
  refs: refspecs,
579
- force: flags.force
608
+ force: flags.force,
609
+ ...announcement ? { announcement } : {}
580
610
  });
581
611
  plan = serializePushPlan(pushPlan);
582
612
  execute = async () => serializePushResult(
@@ -611,7 +641,8 @@ async function runPush(args, deps) {
611
641
  repoId,
612
642
  refspecs,
613
643
  force: flags.force,
614
- relayUrls: relaysUsed
644
+ relayUrls: relaysUsed,
645
+ ...announcement ? { announcement } : {}
615
646
  });
616
647
  execute = () => client.gitPush({
617
648
  repoPath: repoRoot,
@@ -619,7 +650,8 @@ async function runPush(args, deps) {
619
650
  refspecs,
620
651
  force: flags.force,
621
652
  relayUrls: relaysUsed,
622
- confirm: true
653
+ confirm: true,
654
+ ...announcement ? { announcement } : {}
623
655
  });
624
656
  rigPageSkipDetail = "skipped on the daemon path (refreshes on the next standalone push)";
625
657
  }
@@ -878,6 +910,82 @@ async function resolveRepoContext(flags, deps) {
878
910
  ...repoRoot !== void 0 ? { repoRoot } : {}
879
911
  };
880
912
  }
913
+ async function conformanceFactsFor(options) {
914
+ const { ctx, relayUrl, deps } = options;
915
+ const repoRoot = ctx.repoRoot;
916
+ const rootCommits = deps.rootCommits ?? (repoRoot === void 0 ? void 0 : (rev) => new GitRepoReader(repoRoot).rootCommits(rev));
917
+ let euc = null;
918
+ if (rootCommits !== void 0) {
919
+ try {
920
+ euc = await earliestUniqueCommit({ rootCommits });
921
+ } catch {
922
+ euc = null;
923
+ }
924
+ }
925
+ let web = null;
926
+ try {
927
+ web = repoWebUrl({
928
+ env: deps.env,
929
+ relay: relayUrl,
930
+ ownerPubkey: ctx.owner,
931
+ repoId: ctx.repoId
932
+ });
933
+ } catch {
934
+ web = null;
935
+ }
936
+ return { relays: [relayUrl], web, earliestUniqueCommit: euc };
937
+ }
938
+ async function openAnnouncementRepublish(options) {
939
+ const { deps, flags, what, before } = options;
940
+ const { io } = deps;
941
+ const ctx = await resolveRepoContext(flags, deps);
942
+ if (ctx.relays.length > 1) {
943
+ io.err(singleRelayRefusal(ctx.resolved, "Nothing was published or paid."));
944
+ return { ok: false, exitCode: 1 };
945
+ }
946
+ const relayUrl = ctx.relays[0];
947
+ if (relayUrl === void 0 || !WS_URL_RE.test(relayUrl)) {
948
+ throw new InvalidRelayUrlError(
949
+ relayUrl ?? "",
950
+ "a paid publish needs a ws:// or wss:// relay"
951
+ );
952
+ }
953
+ const load = deps.loadStandalone ?? defaultLoadStandalone;
954
+ const standalone = await load({
955
+ env: deps.env,
956
+ cwd: deps.cwd,
957
+ warn: (line) => io.err(line),
958
+ relayUrl
959
+ });
960
+ const identity = identityReport(standalone);
961
+ if (identity.pubkey.toLowerCase() !== ctx.owner.toLowerCase()) {
962
+ io.err(
963
+ `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.`
964
+ );
965
+ return { ok: false, exitCode: 1, standalone };
966
+ }
967
+ const remote = await fetchRemoteState({
968
+ relayUrls: [relayUrl],
969
+ ownerPubkey: ctx.owner,
970
+ repoId: ctx.repoId,
971
+ ...deps.webSocketFactory ? { webSocketFactory: deps.webSocketFactory } : {}
972
+ });
973
+ if (!remote.announced) {
974
+ io.err(
975
+ `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.`
976
+ );
977
+ return { ok: false, exitCode: 1, standalone };
978
+ }
979
+ return {
980
+ ok: true,
981
+ ctx,
982
+ relayUrl,
983
+ standalone,
984
+ identity,
985
+ remote,
986
+ facts: await conformanceFactsFor({ ctx, relayUrl, deps })
987
+ };
988
+ }
881
989
 
882
990
  // src/cli/dispatch.ts
883
991
  import { createRequire } from "module";
@@ -2184,10 +2292,10 @@ async function gitShowBytes(repoRoot, commit, path) {
2184
2292
  );
2185
2293
  });
2186
2294
  }
2187
- function assertSecretName(name) {
2295
+ function assertSecretName(name, position) {
2188
2296
  if (!SECRET_NAME_RE.test(name)) {
2189
2297
  throw new Error(
2190
- `invalid secret name ${JSON.stringify(name)} \u2014 must match [A-Z_][A-Z0-9_]*`
2298
+ `invalid secret name in argument ${position} \u2014 NAME must match [A-Z_][A-Z0-9_]* (the argument is not shown)`
2191
2299
  );
2192
2300
  }
2193
2301
  if (RESERVED_SECRET_NAMES.includes(name)) {
@@ -2237,17 +2345,18 @@ async function runCiSecret(args, deps) {
2237
2345
  );
2238
2346
  }
2239
2347
  const seen = /* @__PURE__ */ new Set();
2240
- for (const entry of entries) {
2348
+ for (const [index, entry] of entries.entries()) {
2349
+ const position = index + 1;
2241
2350
  const eq = entry.indexOf("=");
2242
2351
  const name = eq === -1 ? entry : entry.slice(0, eq);
2243
- assertSecretName(name);
2352
+ assertSecretName(name, position);
2244
2353
  if (seen.has(name))
2245
2354
  throw new Error(`secret name ${name} given more than once`);
2246
2355
  seen.add(name);
2247
2356
  if (sub === "remove") {
2248
2357
  if (eq !== -1)
2249
2358
  throw new Error(
2250
- `rig ci secret remove takes bare names (got ${JSON.stringify(entry)})`
2359
+ `rig ci secret remove takes bare names \u2014 argument ${position} is NAME=value`
2251
2360
  );
2252
2361
  remove.push(name);
2253
2362
  } else if (eq === -1) {
@@ -2258,27 +2367,30 @@ async function runCiSecret(args, deps) {
2258
2367
  }
2259
2368
  stdinName = name;
2260
2369
  } else {
2261
- const value = entry.slice(eq + 1);
2262
- if (value === "") throw new Error(`secret ${name} has an empty value`);
2263
- set[name] = value;
2370
+ set[name] = entry.slice(eq + 1);
2371
+ }
2372
+ }
2373
+ if (stdinName !== void 0) {
2374
+ const value = io.isInteractive ? "" : await (deps.readStdin ?? defaultReadStdin)();
2375
+ const trimmed = value.replace(/\r?\n$/, "");
2376
+ if (trimmed === "") {
2377
+ throw new Error(
2378
+ `secret ${stdinName}: no value on stdin (pipe the value, or pass ${stdinName}=<value>)`
2379
+ );
2264
2380
  }
2381
+ set[stdinName] = trimmed;
2265
2382
  }
2383
+ serializeSecretUpdate({
2384
+ author: "0".repeat(64),
2385
+ created_at: 9999999999,
2386
+ set,
2387
+ remove
2388
+ });
2266
2389
  } catch (err) {
2267
2390
  io.err(err instanceof Error ? err.message : String(err));
2268
2391
  io.err(CI_SECRET_USAGE);
2269
2392
  return 2;
2270
2393
  }
2271
- if (stdinName !== void 0) {
2272
- const value = io.isInteractive ? "" : await (deps.readStdin ?? defaultReadStdin)();
2273
- const trimmed = value.replace(/\r?\n$/, "");
2274
- if (trimmed === "") {
2275
- io.err(
2276
- `secret ${stdinName}: no value on stdin (pipe the value, or pass ${stdinName}=<value>)`
2277
- );
2278
- return 2;
2279
- }
2280
- set[stdinName] = trimmed;
2281
- }
2282
2394
  const names = [...Object.keys(set), ...remove];
2283
2395
  return runCiEvent({
2284
2396
  command: sub === "set" ? "ci secret set" : "ci secret remove",
@@ -2373,7 +2485,7 @@ async function runCi(args, deps) {
2373
2485
  case "status":
2374
2486
  return runCiStatus(rest, deps);
2375
2487
  case "serve":
2376
- return (await import("./ci-serve-P4HOLAMP.js")).runCiServe(rest, deps);
2488
+ return (await import("./ci-serve-AYFZOY4O.js")).runCiServe(rest, deps);
2377
2489
  case "--help":
2378
2490
  case "-h":
2379
2491
  case "help":
@@ -2635,7 +2747,6 @@ async function runClone(args, deps) {
2635
2747
  import { readFile } from "fs/promises";
2636
2748
  import { parseArgs as parseArgs8 } from "util";
2637
2749
  import {
2638
- REPOSITORY_ANNOUNCEMENT_KIND as REPOSITORY_ANNOUNCEMENT_KIND2,
2639
2750
  STATUS_APPLIED_KIND as STATUS_APPLIED_KIND2,
2640
2751
  STATUS_CLOSED_KIND as STATUS_CLOSED_KIND2,
2641
2752
  STATUS_DRAFT_KIND as STATUS_DRAFT_KIND2,
@@ -2670,9 +2781,10 @@ Options:
2670
2781
  ${READ_COMMON_FLAGS}`;
2671
2782
  var ISSUE_SHOW_USAGE = `Usage: rig issue show <event-id> [options]
2672
2783
 
2673
- Show one issue (kind:1621): metadata, derived state, body, and its
2674
- kind:1622 comments \u2014 FREE (relay reads only). <event-id> is the 64-char hex
2675
- id \`rig issue create\` printed (also visible in \`rig issue list\`).
2784
+ Show one issue (kind:1621): metadata, derived state, body, and its comments
2785
+ \u2014 NIP-22 kind:1111 merged with legacy kind:1622 in time order \u2014 FREE (relay
2786
+ reads only). <event-id> is the 64-char hex id \`rig issue create\` printed
2787
+ (also visible in \`rig issue list\`).
2676
2788
 
2677
2789
  Options:
2678
2790
  ${READ_COMMON_FLAGS}`;
@@ -2689,7 +2801,8 @@ var PR_SHOW_USAGE = `Usage: rig pr show <event-id> [options]
2689
2801
 
2690
2802
  Show one patch/PR (kind:1617): metadata, derived state, the FULL patch text
2691
2803
  (real \`git format-patch\` output \u2014 pipe it to \`git am\` to apply), and its
2692
- kind:1622 comments \u2014 FREE (relay reads only).
2804
+ comments \u2014 NIP-22 kind:1111 merged with legacy kind:1622 in time order \u2014
2805
+ FREE (relay reads only).
2693
2806
 
2694
2807
  Options:
2695
2808
  ${READ_COMMON_FLAGS}`;
@@ -2705,9 +2818,7 @@ var STATUS_KINDS = [
2705
2818
  STATUS_CLOSED_KIND,
2706
2819
  STATUS_DRAFT_KIND
2707
2820
  ];
2708
- function tagValue(tags, name) {
2709
- return tags.find((t) => t[0] === name)?.[1];
2710
- }
2821
+ var tagValue = firstTagValue;
2711
2822
  function tagValues(tags, name) {
2712
2823
  return tags.filter((t) => t[0] === name && t[1]).map((t) => t[1]);
2713
2824
  }
@@ -2724,6 +2835,9 @@ function deriveStatus(targetEventId, statusEvents, authorized) {
2724
2835
  }
2725
2836
  return winner === null ? "open" : STATUS_BY_KIND[winner.kind];
2726
2837
  }
2838
+ function withTargetAuthor(authorized, targetAuthorPubkey) {
2839
+ return /* @__PURE__ */ new Set([...authorized, targetAuthorPubkey.toLowerCase()]);
2840
+ }
2727
2841
  var RELAY_TIMEOUT_MS = 1e4;
2728
2842
  var HEX64_RE2 = /^[0-9a-f]{64}$/;
2729
2843
  var WS_URL_RE3 = /^wss?:\/\//i;
@@ -2867,7 +2981,7 @@ function parseTrackerItem(event, status) {
2867
2981
  };
2868
2982
  if (event.kind === PATCH_KIND) {
2869
2983
  item.commitShas = tagValues(event.tags, "commit");
2870
- const branch = tagValue(event.tags, "branch");
2984
+ const branch = tagValue(event.tags, "branch-name") ?? tagValue(event.tags, "branch");
2871
2985
  if (branch !== void 0) item.branch = branch;
2872
2986
  const description = tagValue(event.tags, "description");
2873
2987
  if (description !== void 0) item.description = description;
@@ -2912,7 +3026,14 @@ async function fetchItems(ctx, kind) {
2912
3026
  }
2913
3027
  const authorized = await authorizedPromise;
2914
3028
  return items.map(
2915
- (event) => parseTrackerItem(event, deriveStatus(event.id, statuses.values(), authorized))
3029
+ (event) => parseTrackerItem(
3030
+ event,
3031
+ deriveStatus(
3032
+ event.id,
3033
+ statuses.values(),
3034
+ withTargetAuthor(authorized, event.pubkey)
3035
+ )
3036
+ )
2916
3037
  ).sort((a, b) => b.createdAt - a.createdAt);
2917
3038
  }
2918
3039
  async function fetchItem(ctx, eventId, expectedKind) {
@@ -2937,16 +3058,23 @@ async function fetchItem(ctx, eventId, expectedKind) {
2937
3058
  [{ kinds: STATUS_KINDS, "#e": [eventId] }],
2938
3059
  ctx.webSocketFactory
2939
3060
  ),
3061
+ // One thread, two wire shapes (#159): NIP-22 kind:1111 comments hang off
3062
+ // the root's UPPERCASE `E` tag (a nested reply's lowercase `e` names its
3063
+ // parent comment, so `#e` would miss it); legacy kind:1622 comments only
3064
+ // ever carry the lowercase `e`. Both are merged below by `created_at`.
2940
3065
  queryAll(
2941
3066
  ctx.relays,
2942
- [{ kinds: [COMMENT_KIND], "#e": [eventId] }],
3067
+ [
3068
+ { kinds: [COMMENT_KIND], "#E": [eventId] },
3069
+ { kinds: [LEGACY_COMMENT_KIND], "#e": [eventId] }
3070
+ ],
2943
3071
  ctx.webSocketFactory
2944
3072
  )
2945
3073
  ]);
2946
- const comments = [...commentEvents.values()].filter(
2947
- (e) => e.kind === COMMENT_KIND && e.tags.some((t) => t[0] === "e" && t[1] === eventId)
2948
- ).sort((a, b) => a.created_at - b.created_at).map((e) => ({
3074
+ const comments = [...commentEvents.values()].filter((e) => commentBelongsToThread(e, eventId)).sort((a, b) => a.created_at - b.created_at).map((e) => ({
2949
3075
  eventId: e.id,
3076
+ kind: e.kind,
3077
+ replyToEventId: tagValue(e.tags, "e") ?? null,
2950
3078
  authorPubkey: e.pubkey,
2951
3079
  createdAt: e.created_at,
2952
3080
  content: e.content
@@ -2957,7 +3085,11 @@ async function fetchItem(ctx, eventId, expectedKind) {
2957
3085
  return {
2958
3086
  item: parseTrackerItem(
2959
3087
  event,
2960
- deriveStatus(eventId, statusEvents.values(), authorized)
3088
+ deriveStatus(
3089
+ eventId,
3090
+ statusEvents.values(),
3091
+ withTargetAuthor(authorized, event.pubkey)
3092
+ )
2961
3093
  ),
2962
3094
  comments,
2963
3095
  repoATag: repoATag2
@@ -3023,8 +3155,9 @@ async function runList(args, deps, spec) {
3023
3155
  }
3024
3156
  for (const item of items) {
3025
3157
  const labels = item.labels.length > 0 ? ` [${item.labels.join(", ")}]` : "";
3158
+ const branch = item.branch !== void 0 ? ` \u2192 ${item.branch}` : "";
3026
3159
  io.out(
3027
- `${item.status.padEnd(7)} ${item.eventId.slice(0, 8)} ${item.title} (${item.authorPubkey.slice(0, 8)}, ${isoDate(item.createdAt)})${labels}`
3160
+ `${item.status.padEnd(7)} ${item.eventId.slice(0, 8)} ${item.title} (${item.authorPubkey.slice(0, 8)}, ${isoDate(item.createdAt)})${labels}${branch}`
3028
3161
  );
3029
3162
  }
3030
3163
  io.out(
@@ -3100,8 +3233,9 @@ async function runShow(args, deps, spec) {
3100
3233
  io.out("");
3101
3234
  io.out(`Comments (${shown.comments.length}):`);
3102
3235
  for (const comment of shown.comments) {
3236
+ const replyTo = comment.replyToEventId !== null && comment.replyToEventId !== item.eventId ? ` in reply to ${comment.replyToEventId.slice(0, 8)}` : "";
3103
3237
  io.out(
3104
- `--- ${comment.eventId.slice(0, 8)} by ${comment.authorPubkey.slice(0, 8)} on ${isoDate(comment.createdAt)}`
3238
+ `--- ${comment.eventId.slice(0, 8)} by ${comment.authorPubkey.slice(0, 8)} on ${isoDate(comment.createdAt)}${replyTo}`
3105
3239
  );
3106
3240
  for (const line of comment.content.split("\n")) io.out(line);
3107
3241
  }
@@ -3189,18 +3323,27 @@ ${COMMON_FLAGS_USAGE2}
3189
3323
  Related (FREE reads \u2014 no payment):
3190
3324
  rig issue list [--state open|closed|all] list the repo's issues
3191
3325
  rig issue show <event-id> one issue + its comments`;
3192
- var COMMENT_USAGE = `Usage: rig comment <root-event-id> --body <text> [options]
3326
+ var COMMENT_USAGE = `Usage: rig comment <target-event-id> --body <text> [options]
3327
+
3328
+ Comment (NIP-22 kind:1111) on an issue or patch \u2014 a paid publish; writes are
3329
+ permanent and non-refundable. <target-event-id> is the 64-char hex id of
3330
+ either:
3193
3331
 
3194
- Comment (kind:1622) on an issue or patch \u2014 a paid publish; writes are
3195
- permanent and non-refundable. <root-event-id> is the 64-char hex id of the
3196
- kind:1621 issue / kind:1617 patch being commented on.
3332
+ the kind:1621 issue / kind:1617 patch being commented on \u2192 a top-level
3333
+ comment, whose NIP-22 root scope (E/K/P) and parent (e/k/p) both name it;
3334
+ an existing kind:1111 comment \u2192 a REPLY: the comment becomes the lowercase
3335
+ parent (k=1111) while the uppercase root stays the issue/patch it hangs
3336
+ off.
3337
+
3338
+ The target is read from the resolved relay first (a FREE read) so the root's
3339
+ kind and author are taken from the wire rather than guessed. The repo
3340
+ coordinate rides along as an \`a\` tag for subscription filtering.
3341
+
3342
+ Older rig releases read only the legacy kind:1622 dialect and will NOT see
3343
+ comments published by this version.
3197
3344
 
3198
3345
  Options:
3199
3346
  --body <text> comment body (Markdown) [required]
3200
- --parent-author <pubkey> pubkey of the TARGET event's author (p threading
3201
- tag; default: the repo owner)
3202
- --marker <root|reply> e-tag marker (default: root \u2014 commenting directly
3203
- on the issue/patch)
3204
3347
  ${COMMON_FLAGS_USAGE2}`;
3205
3348
  var PR_CREATE_USAGE = `Usage: rig pr create --title <title> (--range <range> | --patch-file <path>) [options]
3206
3349
 
@@ -3223,7 +3366,7 @@ Options:
3223
3366
  --patch-file <path> literal patch text to publish
3224
3367
  --body <text> PR description (Markdown; description tag)
3225
3368
  --body-file <path> read the PR description from a file
3226
- --branch <name> branch name (t tag)
3369
+ --branch <name> branch name (branch-name tag)
3227
3370
  ${COMMON_FLAGS_USAGE2}`;
3228
3371
  var PR_STATUS_USAGE = `Usage: rig pr status <target-event-id> <open|applied|closed|draft> [options]
3229
3372
 
@@ -3328,7 +3471,7 @@ async function runEvent(opts) {
3328
3471
  }
3329
3472
  const owner = flags.owner ?? toonConfig.owner ?? identity.pubkey;
3330
3473
  const addr = { ownerPubkey: owner, repoId };
3331
- const event = await opts.buildEvent(addr);
3474
+ const event = await opts.buildEvent(addr, relaysUsed[0]);
3332
3475
  const action = `kind:${event.kind} ${actionLabel}`;
3333
3476
  if (opts.preConfirm) {
3334
3477
  await opts.preConfirm({ identity, addr, relayUrl: relaysUsed[0] });
@@ -3501,19 +3644,64 @@ async function runIssue(args, deps) {
3501
3644
  })
3502
3645
  });
3503
3646
  }
3647
+ var TARGET_LOOKUP_TIMEOUT_MS = 1e4;
3648
+ async function resolveCommentTarget(deps, targetEventId, relayUrl) {
3649
+ if (relayUrl === void 0) {
3650
+ throw new Error(
3651
+ "no relay resolved \u2014 a comment needs the target event to derive its NIP-22 root; pass --relay <url> or configure a remote"
3652
+ );
3653
+ }
3654
+ const found = await queryRelay(
3655
+ relayUrl,
3656
+ { ids: [targetEventId] },
3657
+ TARGET_LOOKUP_TIMEOUT_MS,
3658
+ deps.webSocketFactory ?? defaultWebSocketFactory
3659
+ );
3660
+ const target = found.find((e) => e.id === targetEventId);
3661
+ if (!target) {
3662
+ throw new Error(
3663
+ `event ${targetEventId} not found on ${relayUrl} \u2014 nothing was published or paid`
3664
+ );
3665
+ }
3666
+ if (target.kind === LEGACY_COMMENT_KIND) {
3667
+ throw new Error(
3668
+ `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`
3669
+ );
3670
+ }
3671
+ if (target.kind === COMMENT_KIND) {
3672
+ const rootEventId = firstTagValue(target.tags, "E");
3673
+ const rootKind = Number(firstTagValue(target.tags, "K"));
3674
+ const rootAuthor = firstTagValue(target.tags, "P");
3675
+ if (rootEventId === void 0 || !HEX64_RE3.test(rootEventId) || !Number.isSafeInteger(rootKind) || rootAuthor === void 0 || !HEX64_RE3.test(rootAuthor)) {
3676
+ throw new Error(
3677
+ `kind:1111 comment ${targetEventId} carries no usable NIP-22 root scope (E/K/P tags) \u2014 cannot derive the thread root to reply into`
3678
+ );
3679
+ }
3680
+ return {
3681
+ root: { eventId: rootEventId, kind: rootKind, authorPubkey: rootAuthor },
3682
+ parent: { eventId: target.id, authorPubkey: target.pubkey }
3683
+ };
3684
+ }
3685
+ return {
3686
+ root: {
3687
+ eventId: target.id,
3688
+ kind: target.kind,
3689
+ authorPubkey: target.pubkey
3690
+ }
3691
+ };
3692
+ }
3504
3693
  async function runComment(args, deps) {
3505
3694
  const { io } = deps;
3506
3695
  let flags;
3507
- let rootEventId;
3696
+ let targetEventId;
3508
3697
  let body;
3509
- let parentAuthor;
3510
- let marker;
3511
3698
  try {
3512
3699
  const { values, positionals } = parseArgs8({
3513
3700
  args,
3514
3701
  options: {
3515
3702
  ...COMMON_OPTIONS2,
3516
3703
  body: { type: "string" },
3704
+ // Retired by #159 — parsed only to explain the migration.
3517
3705
  "parent-author": { type: "string" },
3518
3706
  marker: { type: "string" }
3519
3707
  },
@@ -3524,49 +3712,59 @@ async function runComment(args, deps) {
3524
3712
  io.out(COMMENT_USAGE);
3525
3713
  return 0;
3526
3714
  }
3715
+ if (values.marker !== void 0 || values["parent-author"] !== void 0) {
3716
+ throw new Error(
3717
+ "--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"
3718
+ );
3719
+ }
3527
3720
  if (positionals.length !== 1) {
3528
3721
  throw new Error(
3529
- positionals.length === 0 ? "<root-event-id> is required" : `expected exactly one <root-event-id>, got ${positionals.length} positionals`
3722
+ positionals.length === 0 ? "<target-event-id> is required" : `expected exactly one <target-event-id>, got ${positionals.length} positionals`
3530
3723
  );
3531
3724
  }
3532
- rootEventId = positionals[0];
3533
- assertHex64(rootEventId, "<root-event-id>");
3725
+ targetEventId = positionals[0];
3726
+ assertHex64(targetEventId, "<target-event-id>");
3534
3727
  if (values.body === void 0 || values.body === "") {
3535
3728
  throw new Error("--body is required");
3536
3729
  }
3537
3730
  body = values.body;
3538
- parentAuthor = values["parent-author"];
3539
- if (parentAuthor !== void 0) assertHex64(parentAuthor, "--parent-author");
3540
- const rawMarker = values.marker ?? "root";
3541
- if (rawMarker !== "root" && rawMarker !== "reply") {
3542
- throw new Error(`--marker must be root or reply (got ${JSON.stringify(rawMarker)})`);
3543
- }
3544
- marker = rawMarker;
3545
3731
  } catch (err) {
3546
3732
  io.err(err instanceof Error ? err.message : String(err));
3547
3733
  io.err(COMMENT_USAGE);
3548
3734
  return 2;
3549
3735
  }
3736
+ let threading;
3550
3737
  return runEvent({
3551
3738
  command: "comment",
3552
3739
  flags,
3553
3740
  deps,
3554
- actionLabel: `comment on ${rootEventId.slice(0, 8)}\u2026`,
3555
- buildEvent: async (addr) => buildComment(
3556
- addr.ownerPubkey,
3557
- addr.repoId,
3558
- rootEventId,
3559
- parentAuthor ?? addr.ownerPubkey,
3560
- body,
3561
- marker
3562
- ),
3563
- sendDaemon: (client, addr) => client.gitComment({
3564
- repoAddr: addr,
3565
- rootEventId,
3566
- body,
3567
- ...parentAuthor !== void 0 ? { parentAuthorPubkey: parentAuthor } : {},
3568
- marker
3569
- })
3741
+ actionLabel: `comment on ${targetEventId.slice(0, 8)}\u2026`,
3742
+ buildEvent: async (addr, relayUrl) => {
3743
+ threading = await resolveCommentTarget(deps, targetEventId, relayUrl);
3744
+ return buildComment(
3745
+ addr.ownerPubkey,
3746
+ addr.repoId,
3747
+ threading.root,
3748
+ body,
3749
+ threading.parent
3750
+ );
3751
+ },
3752
+ sendDaemon: (client, addr) => {
3753
+ if (threading === void 0) {
3754
+ throw new Error(
3755
+ "internal: the comment target was not resolved before the publish"
3756
+ );
3757
+ }
3758
+ const { root, parent } = threading;
3759
+ return client.gitComment({
3760
+ repoAddr: addr,
3761
+ body,
3762
+ rootEventId: root.eventId,
3763
+ rootKind: root.kind,
3764
+ rootAuthorPubkey: root.authorPubkey,
3765
+ ...parent !== void 0 ? { parentComment: parent } : {}
3766
+ });
3767
+ }
3570
3768
  });
3571
3769
  }
3572
3770
  var PATCH_FROM_RE = /^From ([0-9a-f]{40}) /gm;
@@ -3771,14 +3969,18 @@ async function runPrStatus(args, deps) {
3771
3969
  flags,
3772
3970
  deps,
3773
3971
  actionLabel: `status ${status} on ${targetEventId.slice(0, 8)}\u2026`,
3774
- buildEvent: async (addr) => {
3775
- const event = buildStatus(targetEventId, STATUS_KIND_BY_VALUE[status]);
3776
- event.tags.push([
3777
- "a",
3778
- `${REPOSITORY_ANNOUNCEMENT_KIND2}:${addr.ownerPubkey}:${addr.repoId}`
3779
- ]);
3780
- return event;
3781
- },
3972
+ buildEvent: async (addr) => (
3973
+ // buildStatus (rig#160) now emits the root-marked `e` tag and the repo
3974
+ // `a` tag itself, so readers can scope a status stream to the
3975
+ // repository without resolving the target first (mirrors the
3976
+ // daemon's gitStatus).
3977
+ buildStatus(
3978
+ addr.ownerPubkey,
3979
+ addr.repoId,
3980
+ targetEventId,
3981
+ STATUS_KIND_BY_VALUE[status]
3982
+ )
3983
+ ),
3782
3984
  // Authority warning (#287): a status event only moves an issue/PR's
3783
3985
  // resolved state for repo-authority-honoring clients if its author is the
3784
3986
  // owner or a declared maintainer. Warn clearly when the active identity is
@@ -3806,11 +4008,11 @@ async function warnIfNotMaintainer(deps, args) {
3806
4008
  );
3807
4009
  if (authorized.has(identity.pubkey.toLowerCase())) return;
3808
4010
  io.err(
3809
- `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.`
4011
+ `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.`
3810
4012
  );
3811
4013
  } catch {
3812
4014
  io.err(
3813
- "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."
4015
+ "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."
3814
4016
  );
3815
4017
  }
3816
4018
  }
@@ -4173,12 +4375,7 @@ async function runFetch(args, deps) {
4173
4375
  }
4174
4376
  const reader = new GitRepoReader(repoRoot);
4175
4377
  const tips = [...new Set(planned.map((p) => p.newSha))];
4176
- const candidates = [.../* @__PURE__ */ new Set([...remoteState.shaToTxId.keys(), ...tips])];
4177
- const { missing: absent } = await reader.statObjects(candidates);
4178
- const absentSet = new Set(absent);
4179
- const presentLocally = new Set(
4180
- candidates.filter((sha) => !absentSet.has(sha))
4181
- );
4378
+ const presentLocally = new Set(await reader.listAllObjectShas());
4182
4379
  const collected = await collectRepoObjects({
4183
4380
  tips,
4184
4381
  shaToTxId: remoteState.shaToTxId,
@@ -5517,44 +5714,15 @@ async function runMutate(op, args, deps) {
5517
5714
  }
5518
5715
  let standaloneCtx;
5519
5716
  try {
5520
- const ctx = await resolveRepoContext(flags, deps);
5521
- if (ctx.relays.length > 1) {
5522
- io.err(singleRelayRefusal(ctx.resolved, "Nothing was published or paid."));
5523
- return 1;
5524
- }
5525
- const relayUrl = ctx.relays[0];
5526
- if (relayUrl === void 0 || !WS_URL_RE.test(relayUrl)) {
5527
- throw new InvalidRelayUrlError(
5528
- relayUrl ?? "",
5529
- "a paid publish needs a ws:// or wss:// relay"
5530
- );
5531
- }
5532
- const load = deps.loadStandalone ?? defaultLoadStandalone;
5533
- standaloneCtx = await load({
5534
- env: deps.env,
5535
- cwd: deps.cwd,
5536
- warn: (line) => io.err(line),
5537
- relayUrl
5717
+ const opened = await openAnnouncementRepublish({
5718
+ deps,
5719
+ flags,
5720
+ what: "change the maintainer set",
5721
+ before: "before managing maintainers"
5538
5722
  });
5539
- const identity = identityReport(standaloneCtx);
5540
- if (identity.pubkey.toLowerCase() !== ctx.owner.toLowerCase()) {
5541
- io.err(
5542
- `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.`
5543
- );
5544
- return 1;
5545
- }
5546
- const remote = await fetchRemoteState({
5547
- relayUrls: [relayUrl],
5548
- ownerPubkey: ctx.owner,
5549
- repoId: ctx.repoId,
5550
- ...deps.webSocketFactory ? { webSocketFactory: deps.webSocketFactory } : {}
5551
- });
5552
- if (!remote.announced) {
5553
- io.err(
5554
- `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.`
5555
- );
5556
- return 1;
5557
- }
5723
+ standaloneCtx = opened.standalone;
5724
+ if (!opened.ok) return opened.exitCode;
5725
+ const { ctx, relayUrl, identity, remote, facts } = opened;
5558
5726
  const current = remote.announceEvent ? parseMaintainers(remote.announceEvent.tags) : [];
5559
5727
  const currentSet = new Set(current);
5560
5728
  if (op === "add" && currentSet.has(pubkey)) {
@@ -5570,21 +5738,23 @@ async function runMutate(op, args, deps) {
5570
5738
  return 0;
5571
5739
  }
5572
5740
  const next = op === "add" ? [...current, pubkey] : current.filter((m) => m !== pubkey);
5573
- const name = remote.name ?? ctx.repoId;
5574
- const description = remote.description ?? "";
5575
- const event = buildRepoAnnouncement(
5576
- ctx.repoId,
5577
- name,
5578
- description,
5579
- next,
5580
- remote.payout
5581
- );
5582
- const fee = (await standaloneCtx.publisher.getFeeRates()).eventFee.toString();
5741
+ const event = amendRepoAnnouncement(remote.announceEvent, {
5742
+ repoId: ctx.repoId,
5743
+ maintainers: next,
5744
+ ...conformanceEdits(remote.announceEvent, facts)
5745
+ });
5746
+ const diff = diffAnnouncementTags(remote.announceEvent, event);
5747
+ const changes = { added: diff.added, removed: diff.removed };
5748
+ const fee = (await opened.standalone.publisher.getFeeRates()).eventFee.toString();
5583
5749
  const action = `kind:30617 maintainers ${op} ${pubkey.slice(0, 8)}\u2026`;
5584
5750
  if (!flags.json) {
5585
5751
  io.out(`Republish ${action}`);
5586
5752
  io.out(`Repo: 30617:${ctx.owner}:${ctx.repoId}`);
5587
5753
  io.out(`Maintainers after: ${next.length === 0 ? "(none)" : next.join(", ")}`);
5754
+ if (!diff.unchanged) {
5755
+ io.out("Tags changed:");
5756
+ for (const line of describeAnnouncementDiff(diff)) io.out(line);
5757
+ }
5588
5758
  io.out(`Fee: ${feeLabel(fee)}. Writes are permanent and non-refundable.`);
5589
5759
  }
5590
5760
  if (!flags.yes) {
@@ -5596,6 +5766,7 @@ async function runMutate(op, args, deps) {
5596
5766
  executed: false,
5597
5767
  feeEstimate: fee,
5598
5768
  maintainers: next,
5769
+ changes,
5599
5770
  hint: "estimate only \u2014 re-run with --yes to publish (permanent, non-refundable)"
5600
5771
  });
5601
5772
  return 0;
@@ -5614,7 +5785,7 @@ async function runMutate(op, args, deps) {
5614
5785
  return 1;
5615
5786
  }
5616
5787
  }
5617
- const receipt = await standaloneCtx.publisher.publishEvent(event, [
5788
+ const receipt = await opened.standalone.publisher.publishEvent(event, [
5618
5789
  relayUrl
5619
5790
  ]);
5620
5791
  const result = serializeEventReceipt(event.kind, receipt);
@@ -5626,6 +5797,7 @@ async function runMutate(op, args, deps) {
5626
5797
  executed: true,
5627
5798
  feeEstimate: fee,
5628
5799
  maintainers: next,
5800
+ changes,
5629
5801
  result
5630
5802
  });
5631
5803
  } else {
@@ -5646,9 +5818,167 @@ async function runMutate(op, args, deps) {
5646
5818
  }
5647
5819
  }
5648
5820
 
5821
+ // src/cli/refresh.ts
5822
+ import { parseArgs as parseArgs15 } from "util";
5823
+ var REFRESH_USAGE = `Usage: rig refresh [options]
5824
+
5825
+ Republish this repo's kind:30617 announcement with no field edit, refreshing
5826
+ its NIP-34 conformance tags (#158) so other clients can find and group it:
5827
+
5828
+ relays the relay this repo publishes to \u2014 where its issues,
5829
+ patches and state live. Added to the relays the
5830
+ announcement already lists; none are removed
5831
+ web the repo's rig-web viewer URL, added alongside any
5832
+ viewer URL another client already announced
5833
+ r <sha> euc the earliest unique commit: the repo's fork identity.
5834
+ Written only if the announcement has none \u2014 once declared it
5835
+ never changes, so forks stay grouped
5836
+
5837
+ Every other tag rides along verbatim, including tags written by other NIP-34
5838
+ clients. rig never writes a \`clone\` tag (it has no git-clonable URL) and never
5839
+ removes one.
5840
+
5841
+ PAID: republishing the kind:30617 costs one event fee (permanent,
5842
+ non-refundable). Nothing is published and nothing is paid when no tag would
5843
+ change. Must run under the repo OWNER's identity \u2014 only the owner's
5844
+ announcement is authoritative.
5845
+
5846
+ Options:
5847
+ --repo-id <id> repository id / NIP-34 d-tag (default: git config)
5848
+ --owner <pubkey> repository owner (npub or hex; default: git config)
5849
+ --remote <name> publish/read via this configured git remote (default: origin)
5850
+ --relay <url> ad-hoc relay override (exactly one)
5851
+ --yes skip the fee confirmation (required when not a TTY)
5852
+ --json machine-readable envelope
5853
+ -h, --help show this help`;
5854
+ async function runRefresh(args, deps) {
5855
+ const { io } = deps;
5856
+ let flags;
5857
+ try {
5858
+ const { values, positionals } = parseArgs15({
5859
+ args,
5860
+ options: REPO_COMMAND_OPTIONS,
5861
+ allowPositionals: true
5862
+ });
5863
+ flags = pickRepoCommandFlags(values);
5864
+ if (flags.help) {
5865
+ io.out(REFRESH_USAGE);
5866
+ return 0;
5867
+ }
5868
+ if (positionals.length > 0) {
5869
+ throw new Error("rig refresh takes no positional arguments");
5870
+ }
5871
+ } catch (err) {
5872
+ io.err(err instanceof Error ? err.message : String(err));
5873
+ io.err(REFRESH_USAGE);
5874
+ return 2;
5875
+ }
5876
+ let standaloneCtx;
5877
+ try {
5878
+ const opened = await openAnnouncementRepublish({
5879
+ deps,
5880
+ flags,
5881
+ what: "refresh the announcement",
5882
+ before: "before refreshing it"
5883
+ });
5884
+ standaloneCtx = opened.standalone;
5885
+ if (!opened.ok) return opened.exitCode;
5886
+ const { ctx, relayUrl, identity, remote, facts } = opened;
5887
+ const event = amendRepoAnnouncement(remote.announceEvent, {
5888
+ repoId: ctx.repoId,
5889
+ ...conformanceEdits(remote.announceEvent, facts)
5890
+ });
5891
+ const diff = diffAnnouncementTags(remote.announceEvent, event);
5892
+ if (diff.unchanged) {
5893
+ const changes2 = { added: diff.added, removed: diff.removed };
5894
+ if (flags.json) {
5895
+ io.emitJson({
5896
+ command: "refresh",
5897
+ repoAddr: { ownerPubkey: ctx.owner, repoId: ctx.repoId },
5898
+ identity,
5899
+ executed: false,
5900
+ feeEstimate: null,
5901
+ changes: changes2,
5902
+ hint: "announcement is already up to date \u2014 nothing published, nothing paid"
5903
+ });
5904
+ return 0;
5905
+ }
5906
+ io.out(
5907
+ `Announcement 30617:${ctx.owner}:${ctx.repoId} is already up to date \u2014 nothing to do (not published, nothing paid).`
5908
+ );
5909
+ return 0;
5910
+ }
5911
+ const fee = (await opened.standalone.publisher.getFeeRates()).eventFee.toString();
5912
+ const changes = { added: diff.added, removed: diff.removed };
5913
+ if (!flags.json) {
5914
+ io.out("Republish kind:30617 announcement (refresh)");
5915
+ io.out(`Repo: 30617:${ctx.owner}:${ctx.repoId}`);
5916
+ io.out("Tags changed:");
5917
+ for (const line of describeAnnouncementDiff(diff)) io.out(line);
5918
+ io.out(`Fee: ${feeLabel(fee)}. Writes are permanent and non-refundable.`);
5919
+ }
5920
+ if (!flags.yes) {
5921
+ if (flags.json) {
5922
+ io.emitJson({
5923
+ command: "refresh",
5924
+ repoAddr: { ownerPubkey: ctx.owner, repoId: ctx.repoId },
5925
+ identity,
5926
+ executed: false,
5927
+ feeEstimate: fee,
5928
+ changes,
5929
+ hint: "estimate only \u2014 re-run with --yes to publish (permanent, non-refundable)"
5930
+ });
5931
+ return 0;
5932
+ }
5933
+ if (!io.isInteractive) {
5934
+ io.err(
5935
+ "refusing to spend channel funds without confirmation in a non-interactive session \u2014 re-run with --yes (or use --json for an estimate)"
5936
+ );
5937
+ return 1;
5938
+ }
5939
+ const proceed = await io.confirm(
5940
+ `Proceed with paid republish (${feeLabel(fee)})? [y/N] `
5941
+ );
5942
+ if (!proceed) {
5943
+ io.err("aborted \u2014 nothing was published.");
5944
+ return 1;
5945
+ }
5946
+ }
5947
+ const receipt = await opened.standalone.publisher.publishEvent(event, [
5948
+ relayUrl
5949
+ ]);
5950
+ const result = serializeEventReceipt(event.kind, receipt);
5951
+ if (flags.json) {
5952
+ io.emitJson({
5953
+ command: "refresh",
5954
+ repoAddr: { ownerPubkey: ctx.owner, repoId: ctx.repoId },
5955
+ identity,
5956
+ executed: true,
5957
+ feeEstimate: fee,
5958
+ changes,
5959
+ result
5960
+ });
5961
+ } else {
5962
+ io.out(
5963
+ `Published kind:30617 refresh: ${result.eventId} paid ${result.feePaid} base units`
5964
+ );
5965
+ }
5966
+ return 0;
5967
+ } catch (err) {
5968
+ return emitCliError(io, flags.json, "refresh", err);
5969
+ } finally {
5970
+ if (standaloneCtx) {
5971
+ try {
5972
+ await standaloneCtx.stop();
5973
+ } catch {
5974
+ }
5975
+ }
5976
+ }
5977
+ }
5978
+
5649
5979
  // src/cli/name.ts
5650
5980
  import { randomUUID } from "crypto";
5651
- import { parseArgs as parseArgs15 } from "util";
5981
+ import { parseArgs as parseArgs16 } from "util";
5652
5982
  var ARIO_DECIMALS = 6;
5653
5983
  function renderMario(mario) {
5654
5984
  return `${mario.toString()} mARIO (~${formatUnits(mario.toString(), ARIO_DECIMALS)} ARIO)`;
@@ -6157,7 +6487,7 @@ async function resolveNameContext(deps, network, processId, mode) {
6157
6487
  return { identity, solanaAddress, network, processId, sdk, nostrSecretKey };
6158
6488
  }
6159
6489
  function parseNameFlags(args, env) {
6160
- const { values, positionals } = parseArgs15({
6490
+ const { values, positionals } = parseArgs16({
6161
6491
  args,
6162
6492
  allowPositionals: true,
6163
6493
  options: {
@@ -6716,7 +7046,7 @@ async function runName(args, deps) {
6716
7046
  }
6717
7047
 
6718
7048
  // src/cli/payout.ts
6719
- import { parseArgs as parseArgs16 } from "util";
7049
+ import { parseArgs as parseArgs17 } from "util";
6720
7050
  import { getAddress } from "viem";
6721
7051
  var PAYOUT_USAGE = `Usage: rig payout <set|clear|show> [<address>] [options]
6722
7052
 
@@ -6772,7 +7102,7 @@ async function runShow2(args, deps) {
6772
7102
  const { io } = deps;
6773
7103
  let flags;
6774
7104
  try {
6775
- const { values, positionals } = parseArgs16({
7105
+ const { values, positionals } = parseArgs17({
6776
7106
  args,
6777
7107
  options: REPO_COMMAND_OPTIONS,
6778
7108
  allowPositionals: true
@@ -6834,7 +7164,7 @@ async function runMutate2(op, args, deps) {
6834
7164
  let flags;
6835
7165
  let nextPayout = null;
6836
7166
  try {
6837
- const { values, positionals } = parseArgs16({
7167
+ const { values, positionals } = parseArgs17({
6838
7168
  args,
6839
7169
  options: REPO_COMMAND_OPTIONS,
6840
7170
  allowPositionals: true
@@ -6865,44 +7195,15 @@ async function runMutate2(op, args, deps) {
6865
7195
  }
6866
7196
  let standaloneCtx;
6867
7197
  try {
6868
- const ctx = await resolveRepoContext(flags, deps);
6869
- if (ctx.relays.length > 1) {
6870
- io.err(singleRelayRefusal(ctx.resolved, "Nothing was published or paid."));
6871
- return 1;
6872
- }
6873
- const relayUrl = ctx.relays[0];
6874
- if (relayUrl === void 0 || !WS_URL_RE.test(relayUrl)) {
6875
- throw new InvalidRelayUrlError(
6876
- relayUrl ?? "",
6877
- "a paid publish needs a ws:// or wss:// relay"
6878
- );
6879
- }
6880
- const load = deps.loadStandalone ?? defaultLoadStandalone;
6881
- standaloneCtx = await load({
6882
- env: deps.env,
6883
- cwd: deps.cwd,
6884
- warn: (line) => io.err(line),
6885
- relayUrl
7198
+ const opened = await openAnnouncementRepublish({
7199
+ deps,
7200
+ flags,
7201
+ what: "change the payout pointer",
7202
+ before: "before managing the payout pointer"
6886
7203
  });
6887
- const identity = identityReport(standaloneCtx);
6888
- if (identity.pubkey.toLowerCase() !== ctx.owner.toLowerCase()) {
6889
- io.err(
6890
- `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.`
6891
- );
6892
- return 1;
6893
- }
6894
- const remote = await fetchRemoteState({
6895
- relayUrls: [relayUrl],
6896
- ownerPubkey: ctx.owner,
6897
- repoId: ctx.repoId,
6898
- ...deps.webSocketFactory ? { webSocketFactory: deps.webSocketFactory } : {}
6899
- });
6900
- if (!remote.announced) {
6901
- io.err(
6902
- `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.`
6903
- );
6904
- return 1;
6905
- }
7204
+ standaloneCtx = opened.standalone;
7205
+ if (!opened.ok) return opened.exitCode;
7206
+ const { ctx, relayUrl, identity, remote, facts } = opened;
6906
7207
  const current = remote.payout;
6907
7208
  if (nextPayout !== null && current !== null && current.chain === nextPayout.chain && current.address === nextPayout.address) {
6908
7209
  io.err(
@@ -6914,16 +7215,14 @@ async function runMutate2(op, args, deps) {
6914
7215
  io.err("rig: no payout pointer is set \u2014 nothing to do (not published).");
6915
7216
  return 0;
6916
7217
  }
6917
- const name = remote.name ?? ctx.repoId;
6918
- const description = remote.description ?? "";
6919
- const event = buildRepoAnnouncement(
6920
- ctx.repoId,
6921
- name,
6922
- description,
6923
- remote.maintainers,
6924
- nextPayout
6925
- );
6926
- const fee = (await standaloneCtx.publisher.getFeeRates()).eventFee.toString();
7218
+ const event = amendRepoAnnouncement(remote.announceEvent, {
7219
+ repoId: ctx.repoId,
7220
+ payout: nextPayout,
7221
+ ...conformanceEdits(remote.announceEvent, facts)
7222
+ });
7223
+ const diff = diffAnnouncementTags(remote.announceEvent, event);
7224
+ const changes = { added: diff.added, removed: diff.removed };
7225
+ const fee = (await opened.standalone.publisher.getFeeRates()).eventFee.toString();
6927
7226
  const action = nextPayout ? `kind:30617 payout set ${nextPayout.chain} ${nextPayout.address}` : "kind:30617 payout clear";
6928
7227
  if (!flags.json) {
6929
7228
  io.out(`Republish ${action}`);
@@ -6931,6 +7230,10 @@ async function runMutate2(op, args, deps) {
6931
7230
  io.out(
6932
7231
  `Payout after: ${nextPayout ? `${nextPayout.chain} ${nextPayout.address}` : "(none)"}`
6933
7232
  );
7233
+ if (!diff.unchanged) {
7234
+ io.out("Tags changed:");
7235
+ for (const line of describeAnnouncementDiff(diff)) io.out(line);
7236
+ }
6934
7237
  io.out(`Fee: ${feeLabel(fee)}. Writes are permanent and non-refundable.`);
6935
7238
  }
6936
7239
  if (!flags.yes) {
@@ -6942,6 +7245,7 @@ async function runMutate2(op, args, deps) {
6942
7245
  executed: false,
6943
7246
  feeEstimate: fee,
6944
7247
  payout: nextPayout,
7248
+ changes,
6945
7249
  hint: "estimate only \u2014 re-run with --yes to publish (permanent, non-refundable)"
6946
7250
  });
6947
7251
  return 0;
@@ -6960,7 +7264,7 @@ async function runMutate2(op, args, deps) {
6960
7264
  return 1;
6961
7265
  }
6962
7266
  }
6963
- const receipt = await standaloneCtx.publisher.publishEvent(event, [
7267
+ const receipt = await opened.standalone.publisher.publishEvent(event, [
6964
7268
  relayUrl
6965
7269
  ]);
6966
7270
  const result = serializeEventReceipt(event.kind, receipt);
@@ -6972,6 +7276,7 @@ async function runMutate2(op, args, deps) {
6972
7276
  executed: true,
6973
7277
  feeEstimate: fee,
6974
7278
  payout: nextPayout,
7279
+ changes,
6975
7280
  result
6976
7281
  });
6977
7282
  } else {
@@ -6993,7 +7298,7 @@ async function runMutate2(op, args, deps) {
6993
7298
  }
6994
7299
 
6995
7300
  // src/cli/site.ts
6996
- import { parseArgs as parseArgs17 } from "util";
7301
+ import { parseArgs as parseArgs18 } from "util";
6997
7302
 
6998
7303
  // src/arweave-manifest.ts
6999
7304
  var DEFAULT_INDEX = "index.html";
@@ -7084,7 +7389,7 @@ Options (publish):
7084
7389
  estimate (nothing uploaded)
7085
7390
  -h, --help show this help`;
7086
7391
  function parseSiteArgs(args) {
7087
- const { values, positionals } = parseArgs17({
7392
+ const { values, positionals } = parseArgs18({
7088
7393
  args,
7089
7394
  options: {
7090
7395
  index: { type: "string" },
@@ -7487,7 +7792,8 @@ Commands rig owns:
7487
7792
  issue list | show <id> read the repo's issues + comments (free)
7488
7793
  pr list | show <id> read the repo's patches; show prints the full
7489
7794
  patch text (free)
7490
- comment <root-event-id> comment (kind:1622) on an issue or patch
7795
+ comment <target-event-id> comment (NIP-22 kind:1111) on an issue or patch,
7796
+ or reply to an existing kind:1111 comment
7491
7797
  pr create publish a patch (kind:1617) with real
7492
7798
  \`git format-patch\` content
7493
7799
  pr status <event-id> <state> set an issue/patch status (kind:1630-1633):
@@ -7496,6 +7802,9 @@ Commands rig owns:
7496
7802
  maintainers add <pubkey> remove republish the kind:30617 to change who may
7497
7803
  maintainers remove <pubkey> author authoritative issue/PR status (owner is
7498
7804
  always an implicit maintainer)
7805
+ refresh republish the kind:30617 with no field edit to
7806
+ refresh its NIP-34 tags (relays, web, the euc fork
7807
+ identity) \u2014 PAID, and free when nothing changes
7499
7808
  payout show show the repo's declared payout pointer (free);
7500
7809
  payout set <address> set/clear republish the kind:30617 \u2014 no pointer
7501
7810
  payout clear means the serving node keeps 100% of
@@ -7614,6 +7923,8 @@ async function dispatch(argv, deps) {
7614
7923
  return runMaintainers(rest, deps);
7615
7924
  case "payout":
7616
7925
  return runPayout(rest, deps);
7926
+ case "refresh":
7927
+ return runRefresh(rest, deps);
7617
7928
  case "channel":
7618
7929
  return runChannel(rest, deps);
7619
7930
  case "channels":
@@ -7665,4 +7976,4 @@ export {
7665
7976
  rigVersion,
7666
7977
  dispatch
7667
7978
  };
7668
- //# sourceMappingURL=chunk-CTRP2UAQ.js.map
7979
+ //# sourceMappingURL=chunk-X232W4TG.js.map