@toon-protocol/rig 5.1.0 → 5.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-P5ZKJN6B.js → chunk-FX5GPZYU.js} +0 -0
- package/dist/{chunk-ITC7ESTZ.js → chunk-JX5P2KAA.js} +71 -37
- package/dist/chunk-JX5P2KAA.js.map +1 -0
- package/dist/{chunk-J6GNB54J.js → chunk-S5R44MMB.js} +6 -4
- package/dist/chunk-S5R44MMB.js.map +1 -0
- package/dist/{chunk-ANVDRW5F.js → chunk-Y7TUE7JT.js} +789 -47
- package/dist/chunk-Y7TUE7JT.js.map +1 -0
- package/dist/{ci-serve-MPJ2WPCC.js → ci-serve-CA37KQVC.js} +5 -5
- package/dist/cli/rig.js +10 -4
- package/dist/cli/rig.js.map +1 -1
- package/dist/index.d.ts +48 -3
- package/dist/index.js +11 -3
- package/dist/index.js.map +1 -1
- package/dist/{publisher-BGV0OOUk.d.ts → publisher-CQi69Nwg.d.ts} +8 -18
- package/dist/standalone/index.d.ts +1 -1
- package/dist/{standalone-mode-IETLYVCE.js → standalone-mode-NWCCRFCL.js} +3 -3
- package/package.json +1 -1
- package/dist/chunk-ANVDRW5F.js.map +0 -1
- package/dist/chunk-ITC7ESTZ.js.map +0 -1
- package/dist/chunk-J6GNB54J.js.map +0 -1
- /package/dist/{chunk-P5ZKJN6B.js.map → chunk-FX5GPZYU.js.map} +0 -0
- /package/dist/{ci-serve-MPJ2WPCC.js.map → ci-serve-CA37KQVC.js.map} +0 -0
- /package/dist/{standalone-mode-IETLYVCE.js.map → standalone-mode-NWCCRFCL.js.map} +0 -0
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
CI_ADVERTISEMENT_KIND,
|
|
3
|
+
CI_JOB_RESULT_KIND,
|
|
4
|
+
CI_SERVICE_REQUEST_KIND,
|
|
5
|
+
CI_SERVICE_STOP_KIND,
|
|
3
6
|
CI_STATUS_USAGE,
|
|
7
|
+
CI_WORKFLOW_PROGRESS_KIND,
|
|
8
|
+
CI_WORKFLOW_RESULT_KIND,
|
|
4
9
|
GitRepoReader,
|
|
5
10
|
InvalidRelayUrlError,
|
|
6
11
|
MultiUrlRemoteError,
|
|
@@ -52,13 +57,13 @@ import {
|
|
|
52
57
|
updateRef,
|
|
53
58
|
writeGitObjects,
|
|
54
59
|
writeToonConfig
|
|
55
|
-
} from "./chunk-
|
|
60
|
+
} from "./chunk-S5R44MMB.js";
|
|
56
61
|
import {
|
|
57
62
|
PREFERRED_GATEWAY,
|
|
58
63
|
mirrorGatewaysFor,
|
|
59
64
|
readGateways,
|
|
60
65
|
shaResolverFor
|
|
61
|
-
} from "./chunk-
|
|
66
|
+
} from "./chunk-FX5GPZYU.js";
|
|
62
67
|
import {
|
|
63
68
|
COMMENT_KIND,
|
|
64
69
|
DEFAULT_KEYSTORE_PASSWORD,
|
|
@@ -71,18 +76,23 @@ import {
|
|
|
71
76
|
buildStatus,
|
|
72
77
|
clientConfigPath,
|
|
73
78
|
clientKeystorePath,
|
|
79
|
+
clientStateDir,
|
|
74
80
|
commentBelongsToThread,
|
|
81
|
+
decodeEventPayload,
|
|
75
82
|
defaultWebSocketFactory,
|
|
76
83
|
fetchRemoteState,
|
|
77
84
|
firstTagValue,
|
|
85
|
+
isPatchRevision,
|
|
78
86
|
isValidEvmPayoutAddress,
|
|
79
87
|
linkKeystoreInClientConfig,
|
|
80
88
|
parseMaintainers,
|
|
81
89
|
queryRelay,
|
|
82
90
|
readClientConfigFile,
|
|
83
91
|
resolveAccountIndex,
|
|
84
|
-
resolveIdentity
|
|
85
|
-
|
|
92
|
+
resolveIdentity,
|
|
93
|
+
revisionRootId,
|
|
94
|
+
selectRevision
|
|
95
|
+
} from "./chunk-JX5P2KAA.js";
|
|
86
96
|
import {
|
|
87
97
|
ChannelMapStore,
|
|
88
98
|
channelStatus,
|
|
@@ -311,7 +321,7 @@ function loadPaidSession(deps, relayUrl) {
|
|
|
311
321
|
});
|
|
312
322
|
}
|
|
313
323
|
var defaultLoadStandalone = async (options) => {
|
|
314
|
-
const mod = await import("./standalone-mode-
|
|
324
|
+
const mod = await import("./standalone-mode-NWCCRFCL.js");
|
|
315
325
|
return mod.createStandaloneContext(options);
|
|
316
326
|
};
|
|
317
327
|
function identityReport(ctx) {
|
|
@@ -565,9 +575,23 @@ async function runPush(args, deps) {
|
|
|
565
575
|
} else {
|
|
566
576
|
identity = session.identity;
|
|
567
577
|
}
|
|
568
|
-
|
|
578
|
+
const foreignOwner = toonConfig.owner && toonConfig.owner.toLowerCase() !== identity.pubkey.toLowerCase() ? toonConfig.owner : void 0;
|
|
579
|
+
let maintainerRemote;
|
|
580
|
+
if (session.path === "standalone" && foreignOwner) {
|
|
581
|
+
const ownerState = await session.ctx.fetchRemote({
|
|
582
|
+
ownerPubkey: foreignOwner,
|
|
583
|
+
repoId,
|
|
584
|
+
relayUrls: relaysUsed
|
|
585
|
+
});
|
|
586
|
+
if (ownerState.announced && authorizedAuthors(foreignOwner, ownerState.announceEvent?.tags).has(
|
|
587
|
+
identity.pubkey.toLowerCase()
|
|
588
|
+
)) {
|
|
589
|
+
maintainerRemote = ownerState;
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
if (!maintainerRemote && foreignOwner) {
|
|
569
593
|
io.err(
|
|
570
|
-
`warning: git config toon.owner (${
|
|
594
|
+
`warning: git config toon.owner (${foreignOwner.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.`
|
|
571
595
|
);
|
|
572
596
|
}
|
|
573
597
|
const announcement = relaysUsed[0] === void 0 ? void 0 : {
|
|
@@ -584,7 +608,7 @@ async function runPush(args, deps) {
|
|
|
584
608
|
let rigPageSkipDetail;
|
|
585
609
|
if (session.path === "standalone") {
|
|
586
610
|
const ctx = session.ctx;
|
|
587
|
-
const remoteState = await ctx.fetchRemote({
|
|
611
|
+
const remoteState = maintainerRemote ?? await ctx.fetchRemote({
|
|
588
612
|
ownerPubkey: ctx.ownerPubkey,
|
|
589
613
|
repoId,
|
|
590
614
|
relayUrls: relaysUsed
|
|
@@ -614,6 +638,8 @@ async function runPush(args, deps) {
|
|
|
614
638
|
rigPageSkipDetail = "skipped (--no-rig-page)";
|
|
615
639
|
} else if (relaysUsed[0] === void 0) {
|
|
616
640
|
rigPageSkipDetail = "skipped (no relay resolved for the pointer)";
|
|
641
|
+
} else if (maintainerRemote) {
|
|
642
|
+
rigPageSkipDetail = "skipped (the owner's rig page is the owner's to refresh)";
|
|
617
643
|
} else if (!ctx.publisher.uploadBlob) {
|
|
618
644
|
rigPageSkipDetail = "skipped (the active publisher cannot upload raw blobs)";
|
|
619
645
|
} else {
|
|
@@ -2493,7 +2519,7 @@ async function runCi(args, deps) {
|
|
|
2493
2519
|
case "status":
|
|
2494
2520
|
return runCiStatus(rest, deps);
|
|
2495
2521
|
case "serve":
|
|
2496
|
-
return (await import("./ci-serve-
|
|
2522
|
+
return (await import("./ci-serve-CA37KQVC.js")).runCiServe(rest, deps);
|
|
2497
2523
|
case "--help":
|
|
2498
2524
|
case "-h":
|
|
2499
2525
|
case "help":
|
|
@@ -2766,6 +2792,8 @@ async function runClone(args, deps) {
|
|
|
2766
2792
|
import { readFile } from "fs/promises";
|
|
2767
2793
|
import { parseArgs as parseArgs8 } from "util";
|
|
2768
2794
|
import {
|
|
2795
|
+
PATCH_KIND as PATCH_KIND2,
|
|
2796
|
+
REPOSITORY_ANNOUNCEMENT_KIND as REPOSITORY_ANNOUNCEMENT_KIND2,
|
|
2769
2797
|
STATUS_APPLIED_KIND as STATUS_APPLIED_KIND2,
|
|
2770
2798
|
STATUS_CLOSED_KIND as STATUS_CLOSED_KIND2,
|
|
2771
2799
|
STATUS_DRAFT_KIND as STATUS_DRAFT_KIND2,
|
|
@@ -3031,7 +3059,7 @@ async function fetchItems(ctx, kind) {
|
|
|
3031
3059
|
)
|
|
3032
3060
|
]);
|
|
3033
3061
|
const items = [...itemEvents.values()].filter(
|
|
3034
|
-
(e) => e.kind === kind && e.tags.some((t) => t[0] === "a" && t[1] === aTag)
|
|
3062
|
+
(e) => e.kind === kind && e.tags.some((t) => t[0] === "a" && t[1] === aTag) && !(kind === PATCH_KIND && isPatchRevision(e))
|
|
3035
3063
|
);
|
|
3036
3064
|
const statuses = new Map(aStatusEvents);
|
|
3037
3065
|
if (items.length > 0) {
|
|
@@ -3065,6 +3093,12 @@ async function fetchItem(ctx, eventId, expectedKind) {
|
|
|
3065
3093
|
if (!event) {
|
|
3066
3094
|
throw new Error(`event ${eventId} not found on ${ctx.relays.join(", ")}`);
|
|
3067
3095
|
}
|
|
3096
|
+
if (event.kind === PATCH_KIND && isPatchRevision(event)) {
|
|
3097
|
+
const rootId = revisionRootId(event);
|
|
3098
|
+
if (rootId !== void 0 && rootId !== eventId) {
|
|
3099
|
+
return fetchItem(ctx, rootId, expectedKind);
|
|
3100
|
+
}
|
|
3101
|
+
}
|
|
3068
3102
|
if (event.kind !== expectedKind) {
|
|
3069
3103
|
const hint = event.kind === PATCH_KIND ? " (it is a kind:1617 patch \u2014 use `rig pr show`)" : event.kind === ISSUE_KIND ? " (it is a kind:1621 issue \u2014 use `rig issue show`)" : "";
|
|
3070
3104
|
throw new Error(
|
|
@@ -3105,15 +3139,48 @@ async function fetchItem(ctx, eventId, expectedKind) {
|
|
|
3105
3139
|
parsedAddr.ownerPubkey,
|
|
3106
3140
|
parsedAddr.repoId
|
|
3107
3141
|
) : /* @__PURE__ */ new Set();
|
|
3108
|
-
|
|
3109
|
-
|
|
3142
|
+
const item = parseTrackerItem(
|
|
3143
|
+
event,
|
|
3144
|
+
deriveStatus(
|
|
3145
|
+
eventId,
|
|
3146
|
+
statusEvents.values(),
|
|
3147
|
+
withTargetAuthor(authorized, event.pubkey)
|
|
3148
|
+
)
|
|
3149
|
+
);
|
|
3150
|
+
let revisions;
|
|
3151
|
+
if (event.kind === PATCH_KIND) {
|
|
3152
|
+
const revisionEvents = await queryAll(
|
|
3153
|
+
ctx.relays,
|
|
3154
|
+
[{ kinds: [PATCH_KIND], "#e": [eventId] }],
|
|
3155
|
+
ctx.webSocketFactory
|
|
3156
|
+
);
|
|
3157
|
+
const { latest, earlier } = selectRevision(
|
|
3110
3158
|
event,
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3159
|
+
[...revisionEvents.values()].filter((e) => e.kind === PATCH_KIND),
|
|
3160
|
+
authorized
|
|
3161
|
+
);
|
|
3162
|
+
const describe = (e) => ({
|
|
3163
|
+
eventId: e.id,
|
|
3164
|
+
authorPubkey: e.pubkey,
|
|
3165
|
+
createdAt: e.created_at
|
|
3166
|
+
});
|
|
3167
|
+
revisions = { latest: describe(latest), earlier: earlier.map(describe) };
|
|
3168
|
+
if (latest.id !== event.id) {
|
|
3169
|
+
const shown = parseTrackerItem(latest, item.status);
|
|
3170
|
+
item.content = shown.content;
|
|
3171
|
+
item.title = shown.title;
|
|
3172
|
+
item.commitShas = shown.commitShas ?? [];
|
|
3173
|
+
delete item.branch;
|
|
3174
|
+
delete item.description;
|
|
3175
|
+
if (shown.branch !== void 0) item.branch = shown.branch;
|
|
3176
|
+
if (shown.description !== void 0) {
|
|
3177
|
+
item.description = shown.description;
|
|
3178
|
+
}
|
|
3179
|
+
}
|
|
3180
|
+
}
|
|
3181
|
+
return {
|
|
3182
|
+
item,
|
|
3183
|
+
...revisions ? { revisions } : {},
|
|
3117
3184
|
comments,
|
|
3118
3185
|
repoATag: repoATag2
|
|
3119
3186
|
};
|
|
@@ -3229,6 +3296,10 @@ async function runShow(args, deps, spec) {
|
|
|
3229
3296
|
relays: ctx.relays,
|
|
3230
3297
|
repoATag: shown.repoATag,
|
|
3231
3298
|
[spec.jsonKey]: shown.item,
|
|
3299
|
+
...shown.revisions ? {
|
|
3300
|
+
latestRevision: shown.revisions.latest,
|
|
3301
|
+
earlierRevisions: shown.revisions.earlier
|
|
3302
|
+
} : {},
|
|
3232
3303
|
comments: shown.comments
|
|
3233
3304
|
});
|
|
3234
3305
|
return 0;
|
|
@@ -3245,6 +3316,20 @@ async function runShow(args, deps, spec) {
|
|
|
3245
3316
|
io.out(`Commits: ${item.commitShas.join(", ")}`);
|
|
3246
3317
|
}
|
|
3247
3318
|
if (shown.repoATag !== null) io.out(`Repo: ${shown.repoATag}`);
|
|
3319
|
+
if (shown.revisions) {
|
|
3320
|
+
const { latest, earlier } = shown.revisions;
|
|
3321
|
+
if (earlier.length > 0) {
|
|
3322
|
+
io.out(
|
|
3323
|
+
`Showing: ${latest.eventId} (latest revision, by ${latest.authorPubkey.slice(0, 8)}, ${isoDate(latest.createdAt)})`
|
|
3324
|
+
);
|
|
3325
|
+
io.out("Earlier revisions (oldest first):");
|
|
3326
|
+
for (const r of shown.revisions.earlier) {
|
|
3327
|
+
io.out(
|
|
3328
|
+
` ${r.eventId} (${r.authorPubkey.slice(0, 8)}, ${isoDate(r.createdAt)})`
|
|
3329
|
+
);
|
|
3330
|
+
}
|
|
3331
|
+
}
|
|
3332
|
+
}
|
|
3248
3333
|
if (item.description !== void 0) {
|
|
3249
3334
|
io.out("");
|
|
3250
3335
|
io.out("Body:");
|
|
@@ -3372,7 +3457,7 @@ comments published by this version.
|
|
|
3372
3457
|
Options:
|
|
3373
3458
|
--body <text> comment body (Markdown) [required]
|
|
3374
3459
|
${COMMON_FLAGS_USAGE2}`;
|
|
3375
|
-
var PR_CREATE_USAGE = `Usage: rig pr create --title <title> (--range <range> | --patch-file <path>) [options]
|
|
3460
|
+
var PR_CREATE_USAGE = `Usage: rig pr create --title <title> (--range <range> | --patch-file <path>) [--revises <patch-id>] [options]
|
|
3376
3461
|
|
|
3377
3462
|
Publish a patch (kind:1617) whose content is REAL \`git format-patch\` output \u2014
|
|
3378
3463
|
a paid publish; writes are permanent and non-refundable. --range runs
|
|
@@ -3386,6 +3471,12 @@ The PR body (--body/--body-file) is carried in a dedicated \`description\`
|
|
|
3386
3471
|
tag, not in the event content \u2014 the content stays pure format-patch output so
|
|
3387
3472
|
\`rig pr show \u2026 | git am\` keeps working.
|
|
3388
3473
|
|
|
3474
|
+
--revises publishes the patch as a NIP-34 revision of an existing root patch
|
|
3475
|
+
(\`t root-revision\` plus a NIP-10 reply to it), keeping the original thread,
|
|
3476
|
+
comments and status. Readers honor a revision only from the root's author or a
|
|
3477
|
+
declared maintainer; otherwise rig warns and still publishes. A revision is
|
|
3478
|
+
always published through the embedded (standalone) path.
|
|
3479
|
+
|
|
3389
3480
|
Options:
|
|
3390
3481
|
--title <title> patch/PR title (subject tag) [required]
|
|
3391
3482
|
--range <range> revision range for format-patch: <rev>, <rev>..<rev>,
|
|
@@ -3394,6 +3485,7 @@ Options:
|
|
|
3394
3485
|
--body <text> PR description (Markdown; description tag)
|
|
3395
3486
|
--body-file <path> read the PR description from a file
|
|
3396
3487
|
--branch <name> branch name (branch-name tag)
|
|
3488
|
+
--revises <patch-id> publish as a revision of this root patch (64-hex id)
|
|
3397
3489
|
${COMMON_FLAGS_USAGE2}`;
|
|
3398
3490
|
var PR_STATUS_USAGE = `Usage: rig pr status <target-event-id> <open|applied|closed|draft> [options]
|
|
3399
3491
|
|
|
@@ -3845,6 +3937,69 @@ async function runPr(args, deps) {
|
|
|
3845
3937
|
return 2;
|
|
3846
3938
|
}
|
|
3847
3939
|
}
|
|
3940
|
+
async function resolveRevisedRoot(deps, rootId, relayUrl, addr) {
|
|
3941
|
+
if (relayUrl === void 0) {
|
|
3942
|
+
throw new Error(
|
|
3943
|
+
"--revises needs the root patch from a relay; pass --relay <url> or configure a remote"
|
|
3944
|
+
);
|
|
3945
|
+
}
|
|
3946
|
+
const found = await queryRelay(
|
|
3947
|
+
relayUrl,
|
|
3948
|
+
{ ids: [rootId] },
|
|
3949
|
+
TARGET_LOOKUP_TIMEOUT_MS,
|
|
3950
|
+
deps.webSocketFactory ?? defaultWebSocketFactory
|
|
3951
|
+
);
|
|
3952
|
+
const root = found.find((e) => e.id === rootId);
|
|
3953
|
+
if (!root) {
|
|
3954
|
+
throw new Error(
|
|
3955
|
+
`patch ${rootId} not found on ${relayUrl} \u2014 nothing was published or paid`
|
|
3956
|
+
);
|
|
3957
|
+
}
|
|
3958
|
+
if (root.kind !== PATCH_KIND2) {
|
|
3959
|
+
throw new Error(
|
|
3960
|
+
`event ${rootId} has kind ${root.kind}, not a kind:1617 patch \u2014 nothing was published or paid`
|
|
3961
|
+
);
|
|
3962
|
+
}
|
|
3963
|
+
const aTag = `${REPOSITORY_ANNOUNCEMENT_KIND2}:${addr.ownerPubkey}:${addr.repoId}`;
|
|
3964
|
+
if (!root.tags.some((t) => t[0] === "a" && t[1] === aTag)) {
|
|
3965
|
+
throw new Error(
|
|
3966
|
+
`patch ${rootId} belongs to a different repository than ${aTag} \u2014 nothing was published or paid`
|
|
3967
|
+
);
|
|
3968
|
+
}
|
|
3969
|
+
if (isPatchRevision(root)) {
|
|
3970
|
+
throw new Error(
|
|
3971
|
+
`patch ${rootId} is itself a revision \u2014 pass the ROOT patch id to --revises; nothing was published or paid`
|
|
3972
|
+
);
|
|
3973
|
+
}
|
|
3974
|
+
return { pubkey: root.pubkey };
|
|
3975
|
+
}
|
|
3976
|
+
async function warnIfRevisionIgnored(deps, args, rootAuthor) {
|
|
3977
|
+
const { io } = deps;
|
|
3978
|
+
const { identity, addr, relayUrl } = args;
|
|
3979
|
+
const me = identity.pubkey.toLowerCase();
|
|
3980
|
+
if (me === rootAuthor?.toLowerCase()) return;
|
|
3981
|
+
if (me === addr.ownerPubkey.toLowerCase()) return;
|
|
3982
|
+
let authorized;
|
|
3983
|
+
if (relayUrl !== void 0) {
|
|
3984
|
+
try {
|
|
3985
|
+
const remote = await fetchRemoteState({
|
|
3986
|
+
relayUrls: [relayUrl],
|
|
3987
|
+
ownerPubkey: addr.ownerPubkey,
|
|
3988
|
+
repoId: addr.repoId,
|
|
3989
|
+
...deps.webSocketFactory ? { webSocketFactory: deps.webSocketFactory } : {}
|
|
3990
|
+
});
|
|
3991
|
+
authorized = authorizedAuthors(
|
|
3992
|
+
addr.ownerPubkey,
|
|
3993
|
+
remote.announceEvent?.tags ?? []
|
|
3994
|
+
);
|
|
3995
|
+
} catch {
|
|
3996
|
+
}
|
|
3997
|
+
}
|
|
3998
|
+
if (authorized?.has(me)) return;
|
|
3999
|
+
io.err(
|
|
4000
|
+
authorized === void 0 ? "warning: could not read the repo announcement to verify your maintainer status \u2014 unless you are the root patch's author or a declared maintainer, readers will IGNORE this revision." : `warning: you (${identity.pubkey.slice(0, 8)}\u2026) are neither the root patch's author nor a maintainer of 30617:${addr.ownerPubkey.slice(0, 8)}\u2026:${addr.repoId} \u2014 readers will IGNORE this revision. Publishing anyway (the relay is permissionless).`
|
|
4001
|
+
);
|
|
4002
|
+
}
|
|
3848
4003
|
async function runPrCreate(rest, deps) {
|
|
3849
4004
|
const { io } = deps;
|
|
3850
4005
|
let flags;
|
|
@@ -3854,6 +4009,7 @@ async function runPrCreate(rest, deps) {
|
|
|
3854
4009
|
let branch;
|
|
3855
4010
|
let bodyFlag;
|
|
3856
4011
|
let bodyFile;
|
|
4012
|
+
let revises;
|
|
3857
4013
|
try {
|
|
3858
4014
|
const { values } = parseArgs8({
|
|
3859
4015
|
args: rest,
|
|
@@ -3864,7 +4020,8 @@ async function runPrCreate(rest, deps) {
|
|
|
3864
4020
|
"patch-file": { type: "string" },
|
|
3865
4021
|
body: { type: "string" },
|
|
3866
4022
|
"body-file": { type: "string" },
|
|
3867
|
-
branch: { type: "string" }
|
|
4023
|
+
branch: { type: "string" },
|
|
4024
|
+
revises: { type: "string" }
|
|
3868
4025
|
},
|
|
3869
4026
|
allowPositionals: false
|
|
3870
4027
|
});
|
|
@@ -3888,6 +4045,11 @@ async function runPrCreate(rest, deps) {
|
|
|
3888
4045
|
branch = values.branch;
|
|
3889
4046
|
bodyFlag = values.body;
|
|
3890
4047
|
bodyFile = values["body-file"];
|
|
4048
|
+
if (values.revises !== void 0) {
|
|
4049
|
+
assertHex64(values.revises, "--revises");
|
|
4050
|
+
revises = values.revises;
|
|
4051
|
+
flags = { ...flags, standalone: true };
|
|
4052
|
+
}
|
|
3891
4053
|
} catch (err) {
|
|
3892
4054
|
io.err(err instanceof Error ? err.message : String(err));
|
|
3893
4055
|
io.err(PR_CREATE_USAGE);
|
|
@@ -3912,12 +4074,16 @@ async function runPrCreate(rest, deps) {
|
|
|
3912
4074
|
}
|
|
3913
4075
|
let builtPatchText;
|
|
3914
4076
|
let builtCommits = [];
|
|
4077
|
+
let rootAuthor;
|
|
3915
4078
|
return runEvent({
|
|
3916
4079
|
command: "pr",
|
|
3917
4080
|
flags,
|
|
3918
4081
|
deps,
|
|
3919
4082
|
actionLabel: `patch ${JSON.stringify(title)}`,
|
|
3920
|
-
buildEvent: async (addr) => {
|
|
4083
|
+
buildEvent: async (addr, relayUrl) => {
|
|
4084
|
+
if (revises !== void 0) {
|
|
4085
|
+
rootAuthor = (await resolveRevisedRoot(deps, revises, relayUrl, addr)).pubkey;
|
|
4086
|
+
}
|
|
3921
4087
|
let patchText;
|
|
3922
4088
|
let commits;
|
|
3923
4089
|
if (range !== void 0) {
|
|
@@ -3952,9 +4118,16 @@ async function runPrCreate(rest, deps) {
|
|
|
3952
4118
|
commits,
|
|
3953
4119
|
branch,
|
|
3954
4120
|
patchText,
|
|
3955
|
-
body
|
|
4121
|
+
body,
|
|
4122
|
+
revises !== void 0 ? {
|
|
4123
|
+
rootEventId: revises,
|
|
4124
|
+
...rootAuthor !== void 0 ? { rootAuthorPubkey: rootAuthor } : {}
|
|
4125
|
+
} : void 0
|
|
3956
4126
|
);
|
|
3957
4127
|
},
|
|
4128
|
+
// A revision from anyone but the root's author or a maintainer is ignored
|
|
4129
|
+
// by readers: warn, but publish (the relay is permissionless).
|
|
4130
|
+
preConfirm: revises === void 0 ? void 0 : (args) => warnIfRevisionIgnored(deps, args, rootAuthor),
|
|
3958
4131
|
sendDaemon: (client, addr) => {
|
|
3959
4132
|
if (builtPatchText === void 0) {
|
|
3960
4133
|
throw new Error("internal: patch text not built before delegation");
|
|
@@ -5300,9 +5473,569 @@ async function defaultReadSecretLine(prompt, isInteractive) {
|
|
|
5300
5473
|
}
|
|
5301
5474
|
}
|
|
5302
5475
|
|
|
5476
|
+
// src/cli/inbox.ts
|
|
5477
|
+
import { parseArgs as parseArgs13 } from "util";
|
|
5478
|
+
import { mkdirSync as mkdirSync5, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "fs";
|
|
5479
|
+
import { join as join6 } from "path";
|
|
5480
|
+
import {
|
|
5481
|
+
ISSUE_KIND as ISSUE_KIND2,
|
|
5482
|
+
PATCH_KIND as PATCH_KIND3,
|
|
5483
|
+
REPOSITORY_ANNOUNCEMENT_KIND as REPOSITORY_ANNOUNCEMENT_KIND3,
|
|
5484
|
+
STATUS_APPLIED_KIND as STATUS_APPLIED_KIND3,
|
|
5485
|
+
STATUS_CLOSED_KIND as STATUS_CLOSED_KIND3,
|
|
5486
|
+
STATUS_DRAFT_KIND as STATUS_DRAFT_KIND3,
|
|
5487
|
+
STATUS_OPEN_KIND as STATUS_OPEN_KIND3
|
|
5488
|
+
} from "@toon-protocol/core/nip34";
|
|
5489
|
+
|
|
5490
|
+
// src/cli/relay-follow.ts
|
|
5491
|
+
var WS_OPEN = 1;
|
|
5492
|
+
var CONNECT_TIMEOUT_MS = 1e4;
|
|
5493
|
+
function defaultBackoffMs(attempt) {
|
|
5494
|
+
return Math.min(3e4, 1e3 * 2 ** Math.max(0, attempt - 1));
|
|
5495
|
+
}
|
|
5496
|
+
function connectOnce(opts, onSubscribed) {
|
|
5497
|
+
return new Promise((resolve2) => {
|
|
5498
|
+
let ws;
|
|
5499
|
+
let done = false;
|
|
5500
|
+
let subscribed = false;
|
|
5501
|
+
const subIds = [];
|
|
5502
|
+
let connectTimer;
|
|
5503
|
+
const finish = (reason) => {
|
|
5504
|
+
if (done) return;
|
|
5505
|
+
done = true;
|
|
5506
|
+
clearTimeout(connectTimer);
|
|
5507
|
+
opts.signal.removeEventListener("abort", onAbort);
|
|
5508
|
+
try {
|
|
5509
|
+
if (ws.readyState === WS_OPEN) {
|
|
5510
|
+
for (const id of subIds) ws.send(JSON.stringify(["CLOSE", id]));
|
|
5511
|
+
}
|
|
5512
|
+
ws.close();
|
|
5513
|
+
} catch {
|
|
5514
|
+
}
|
|
5515
|
+
resolve2(reason);
|
|
5516
|
+
};
|
|
5517
|
+
const onAbort = () => finish(null);
|
|
5518
|
+
try {
|
|
5519
|
+
ws = opts.webSocketFactory(opts.url);
|
|
5520
|
+
} catch (err) {
|
|
5521
|
+
resolve2(`failed to connect to ${opts.url}: ${String(err)}`);
|
|
5522
|
+
return;
|
|
5523
|
+
}
|
|
5524
|
+
opts.signal.addEventListener("abort", onAbort);
|
|
5525
|
+
connectTimer = setTimeout(
|
|
5526
|
+
() => finish(`timed out connecting to ${opts.url}`),
|
|
5527
|
+
CONNECT_TIMEOUT_MS
|
|
5528
|
+
);
|
|
5529
|
+
ws.addEventListener("open", () => {
|
|
5530
|
+
clearTimeout(connectTimer);
|
|
5531
|
+
opts.filters().forEach((filter, i) => {
|
|
5532
|
+
const subId = `follow-${Date.now()}-${i}-${Math.random().toString(36).slice(2, 8)}`;
|
|
5533
|
+
subIds.push(subId);
|
|
5534
|
+
ws.send(JSON.stringify(["REQ", subId, filter]));
|
|
5535
|
+
});
|
|
5536
|
+
});
|
|
5537
|
+
ws.addEventListener("message", (msgEvent) => {
|
|
5538
|
+
try {
|
|
5539
|
+
const msg = JSON.parse(String(msgEvent.data));
|
|
5540
|
+
if (msg[0] === "EVENT" && msg[2] !== void 0) {
|
|
5541
|
+
const event = decodeEventPayload(msg[2]);
|
|
5542
|
+
if (event) opts.onEvent(event, opts.url);
|
|
5543
|
+
} else if (msg[0] === "EOSE" && !subscribed && subIds.includes(msg[1])) {
|
|
5544
|
+
subscribed = true;
|
|
5545
|
+
onSubscribed();
|
|
5546
|
+
}
|
|
5547
|
+
} catch {
|
|
5548
|
+
}
|
|
5549
|
+
});
|
|
5550
|
+
ws.addEventListener("error", (event) => {
|
|
5551
|
+
const detail = typeof event === "object" && event !== null && "message" in event ? String(event.message) : "unknown";
|
|
5552
|
+
finish(`websocket error on ${opts.url}: ${detail}`);
|
|
5553
|
+
});
|
|
5554
|
+
ws.addEventListener("close", () => {
|
|
5555
|
+
finish(`connection to ${opts.url} closed`);
|
|
5556
|
+
});
|
|
5557
|
+
});
|
|
5558
|
+
}
|
|
5559
|
+
async function followRelay(opts) {
|
|
5560
|
+
let attempt = 0;
|
|
5561
|
+
while (!opts.signal.aborted) {
|
|
5562
|
+
const reason = await connectOnce(opts, () => {
|
|
5563
|
+
attempt = 0;
|
|
5564
|
+
});
|
|
5565
|
+
if (opts.signal.aborted) return;
|
|
5566
|
+
opts.onError(reason ?? `connection to ${opts.url} ended`);
|
|
5567
|
+
attempt += 1;
|
|
5568
|
+
await new Promise((resolve2) => {
|
|
5569
|
+
const done = () => {
|
|
5570
|
+
clearTimeout(timer);
|
|
5571
|
+
opts.signal.removeEventListener("abort", done);
|
|
5572
|
+
resolve2();
|
|
5573
|
+
};
|
|
5574
|
+
const timer = setTimeout(done, opts.backoffMs(attempt));
|
|
5575
|
+
opts.signal.addEventListener("abort", done);
|
|
5576
|
+
});
|
|
5577
|
+
}
|
|
5578
|
+
}
|
|
5579
|
+
|
|
5580
|
+
// src/cli/inbox.ts
|
|
5581
|
+
var INBOX_USAGE = `Usage: rig inbox [options]
|
|
5582
|
+
|
|
5583
|
+
Print everything new since you last checked (free \u2014 relay reads only):
|
|
5584
|
+
patches and revisions, issues, comments, status changes and CI results for
|
|
5585
|
+
repos you own or maintain, plus any event that tags your pubkey.
|
|
5586
|
+
|
|
5587
|
+
Options:
|
|
5588
|
+
--since <time> read from a unix time, an ISO date, or a relative
|
|
5589
|
+
age (30m, 2h, 7d); does not move the cursor
|
|
5590
|
+
--all read everything, ignoring the cursor; does not move it
|
|
5591
|
+
--pubkey <pubkey> act as this pubkey (npub or hex) instead of the
|
|
5592
|
+
resolved identity
|
|
5593
|
+
--remote <name> read via this configured git remote (default: origin)
|
|
5594
|
+
--relay <url> ad-hoc relay override; repeatable \u2014 reads are merged
|
|
5595
|
+
--json machine-readable array, one object per event
|
|
5596
|
+
--follow emit the backlog, then stay connected and stream new
|
|
5597
|
+
events as JSON lines (one object per line) until
|
|
5598
|
+
interrupted; reconnects when a relay drops
|
|
5599
|
+
-h, --help show this help`;
|
|
5600
|
+
var STATUS_KINDS2 = [
|
|
5601
|
+
STATUS_OPEN_KIND3,
|
|
5602
|
+
STATUS_APPLIED_KIND3,
|
|
5603
|
+
STATUS_CLOSED_KIND3,
|
|
5604
|
+
STATUS_DRAFT_KIND3
|
|
5605
|
+
];
|
|
5606
|
+
var CI_KINDS = [
|
|
5607
|
+
CI_SERVICE_REQUEST_KIND,
|
|
5608
|
+
CI_SERVICE_STOP_KIND,
|
|
5609
|
+
CI_WORKFLOW_RESULT_KIND,
|
|
5610
|
+
CI_WORKFLOW_PROGRESS_KIND,
|
|
5611
|
+
CI_JOB_RESULT_KIND
|
|
5612
|
+
];
|
|
5613
|
+
var INBOX_KINDS = [
|
|
5614
|
+
PATCH_KIND3,
|
|
5615
|
+
ISSUE_KIND2,
|
|
5616
|
+
COMMENT_KIND,
|
|
5617
|
+
LEGACY_COMMENT_KIND,
|
|
5618
|
+
...STATUS_KINDS2,
|
|
5619
|
+
...CI_KINDS
|
|
5620
|
+
];
|
|
5621
|
+
var HEX64_RE4 = /^[0-9a-f]{64}$/;
|
|
5622
|
+
var INBOX_CURSOR_FILENAME = "inbox-cursors.json";
|
|
5623
|
+
function cursorPath(env) {
|
|
5624
|
+
return join6(clientStateDir(env), INBOX_CURSOR_FILENAME);
|
|
5625
|
+
}
|
|
5626
|
+
function readCursors(env) {
|
|
5627
|
+
try {
|
|
5628
|
+
const parsed = JSON.parse(readFileSync5(cursorPath(env), "utf8"));
|
|
5629
|
+
if (parsed && typeof parsed === "object") {
|
|
5630
|
+
return parsed;
|
|
5631
|
+
}
|
|
5632
|
+
} catch {
|
|
5633
|
+
}
|
|
5634
|
+
return {};
|
|
5635
|
+
}
|
|
5636
|
+
function writeCursor(env, pubkey, cursor) {
|
|
5637
|
+
const cursors = readCursors(env);
|
|
5638
|
+
cursors[pubkey] = cursor;
|
|
5639
|
+
mkdirSync5(clientStateDir(env), { recursive: true });
|
|
5640
|
+
writeFileSync4(cursorPath(env), JSON.stringify(cursors, null, 2) + "\n");
|
|
5641
|
+
}
|
|
5642
|
+
function parseSince(value, nowSec) {
|
|
5643
|
+
const trimmed = value.trim();
|
|
5644
|
+
if (/^\d+$/.test(trimmed)) return Number(trimmed);
|
|
5645
|
+
const rel = /^(\d+)([smhdw])$/.exec(trimmed);
|
|
5646
|
+
if (rel) {
|
|
5647
|
+
const unit = { s: 1, m: 60, h: 3600, d: 86400, w: 604800 }[rel[2]];
|
|
5648
|
+
return nowSec - Number(rel[1]) * unit;
|
|
5649
|
+
}
|
|
5650
|
+
const ms = Date.parse(trimmed);
|
|
5651
|
+
if (Number.isNaN(ms)) {
|
|
5652
|
+
throw new Error(
|
|
5653
|
+
`--since must be a unix time, an ISO date, or an age like 2h (got ${JSON.stringify(value)})`
|
|
5654
|
+
);
|
|
5655
|
+
}
|
|
5656
|
+
return Math.floor(ms / 1e3);
|
|
5657
|
+
}
|
|
5658
|
+
var tagValue2 = firstTagValue;
|
|
5659
|
+
function hasTopic(event, topic) {
|
|
5660
|
+
return event.tags.some((t) => t[0] === "t" && t[1] === topic);
|
|
5661
|
+
}
|
|
5662
|
+
function typeOf(event) {
|
|
5663
|
+
if (event.kind === PATCH_KIND3) {
|
|
5664
|
+
if (hasTopic(event, "root-revision")) return "revision";
|
|
5665
|
+
if (hasTopic(event, "root") || tagValue2(event.tags, "e") === void 0) {
|
|
5666
|
+
return "patch";
|
|
5667
|
+
}
|
|
5668
|
+
return null;
|
|
5669
|
+
}
|
|
5670
|
+
if (event.kind === ISSUE_KIND2) return "issue";
|
|
5671
|
+
if (event.kind === COMMENT_KIND || event.kind === LEGACY_COMMENT_KIND) {
|
|
5672
|
+
return "comment";
|
|
5673
|
+
}
|
|
5674
|
+
if (STATUS_KINDS2.includes(event.kind)) return "status";
|
|
5675
|
+
if (CI_KINDS.includes(event.kind)) return "ci";
|
|
5676
|
+
return null;
|
|
5677
|
+
}
|
|
5678
|
+
function targetOf(event, type) {
|
|
5679
|
+
const root = tagValue2(event.tags, "E");
|
|
5680
|
+
const parent = tagValue2(event.tags, "e");
|
|
5681
|
+
switch (type) {
|
|
5682
|
+
case "patch":
|
|
5683
|
+
case "issue":
|
|
5684
|
+
return event.id;
|
|
5685
|
+
case "revision":
|
|
5686
|
+
return parent ?? event.id;
|
|
5687
|
+
case "comment":
|
|
5688
|
+
return root ?? parent ?? event.id;
|
|
5689
|
+
case "status":
|
|
5690
|
+
return parent ?? event.id;
|
|
5691
|
+
case "ci":
|
|
5692
|
+
return root ?? tagValue2(event.tags, "c") ?? event.id;
|
|
5693
|
+
}
|
|
5694
|
+
}
|
|
5695
|
+
function toInboxEntry(event) {
|
|
5696
|
+
const type = typeOf(event);
|
|
5697
|
+
if (type === null) return null;
|
|
5698
|
+
const repo = event.tags.find(
|
|
5699
|
+
(t) => t[0] === "a" && t[1]?.startsWith(`${REPOSITORY_ANNOUNCEMENT_KIND3}:`)
|
|
5700
|
+
)?.[1];
|
|
5701
|
+
return {
|
|
5702
|
+
id: event.id,
|
|
5703
|
+
kind: event.kind,
|
|
5704
|
+
type,
|
|
5705
|
+
repo: repo ?? null,
|
|
5706
|
+
target: targetOf(event, type),
|
|
5707
|
+
author: event.pubkey,
|
|
5708
|
+
created_at: event.created_at
|
|
5709
|
+
};
|
|
5710
|
+
}
|
|
5711
|
+
function newest(events) {
|
|
5712
|
+
let winner = null;
|
|
5713
|
+
for (const e of events) {
|
|
5714
|
+
if (winner === null || e.created_at > winner.created_at || e.created_at === winner.created_at && e.id < winner.id) {
|
|
5715
|
+
winner = e;
|
|
5716
|
+
}
|
|
5717
|
+
}
|
|
5718
|
+
return winner;
|
|
5719
|
+
}
|
|
5720
|
+
async function ownerAnnouncement(relays, owner, repoId, ws) {
|
|
5721
|
+
const found = await queryAll(
|
|
5722
|
+
relays,
|
|
5723
|
+
[
|
|
5724
|
+
{
|
|
5725
|
+
kinds: [REPOSITORY_ANNOUNCEMENT_KIND3],
|
|
5726
|
+
authors: [owner],
|
|
5727
|
+
"#d": [repoId]
|
|
5728
|
+
}
|
|
5729
|
+
],
|
|
5730
|
+
ws
|
|
5731
|
+
).catch(() => /* @__PURE__ */ new Map());
|
|
5732
|
+
return newest(
|
|
5733
|
+
[...found.values()].filter(
|
|
5734
|
+
(e) => e.kind === REPOSITORY_ANNOUNCEMENT_KIND3 && e.pubkey.toLowerCase() === owner && tagValue2(e.tags, "d") === repoId
|
|
5735
|
+
)
|
|
5736
|
+
);
|
|
5737
|
+
}
|
|
5738
|
+
function relaysTag(tags) {
|
|
5739
|
+
return tags.filter((t) => t[0] === "relays").flatMap((t) => t.slice(1)).filter((url) => WS_URL_RE.test(url));
|
|
5740
|
+
}
|
|
5741
|
+
var INBOX_OPTIONS = {
|
|
5742
|
+
json: { type: "boolean", default: false },
|
|
5743
|
+
follow: { type: "boolean", default: false },
|
|
5744
|
+
all: { type: "boolean", default: false },
|
|
5745
|
+
since: { type: "string" },
|
|
5746
|
+
pubkey: { type: "string" },
|
|
5747
|
+
remote: { type: "string" },
|
|
5748
|
+
relay: { type: "string", multiple: true },
|
|
5749
|
+
help: { type: "boolean", short: "h", default: false }
|
|
5750
|
+
};
|
|
5751
|
+
function renderLine(entry) {
|
|
5752
|
+
const when = new Date(entry.created_at * 1e3).toISOString().slice(0, 16).replace("T", " ");
|
|
5753
|
+
const repo = entry.repo ?? "(no repo)";
|
|
5754
|
+
return `${when} ${entry.type.padEnd(8)} ${entry.id.slice(0, 8)} \u2192 ${entry.target.slice(0, 8)} ${repo} (${entry.author.slice(0, 8)})`;
|
|
5755
|
+
}
|
|
5756
|
+
async function runInbox(args, deps) {
|
|
5757
|
+
const { io } = deps;
|
|
5758
|
+
let json = false;
|
|
5759
|
+
let all = false;
|
|
5760
|
+
let follow = false;
|
|
5761
|
+
let sinceArg;
|
|
5762
|
+
let pubkeyArg;
|
|
5763
|
+
let remote;
|
|
5764
|
+
let relayFlags = [];
|
|
5765
|
+
try {
|
|
5766
|
+
const { values, positionals } = parseArgs13({
|
|
5767
|
+
args,
|
|
5768
|
+
options: INBOX_OPTIONS,
|
|
5769
|
+
allowPositionals: true
|
|
5770
|
+
});
|
|
5771
|
+
if (values.help) {
|
|
5772
|
+
io.out(INBOX_USAGE);
|
|
5773
|
+
return 0;
|
|
5774
|
+
}
|
|
5775
|
+
if (positionals.length > 0) {
|
|
5776
|
+
throw new Error("rig inbox takes no positional arguments");
|
|
5777
|
+
}
|
|
5778
|
+
json = values.json === true;
|
|
5779
|
+
all = values.all === true;
|
|
5780
|
+
follow = values.follow === true;
|
|
5781
|
+
sinceArg = values.since;
|
|
5782
|
+
if (values.pubkey !== void 0) pubkeyArg = ownerToHex(values.pubkey);
|
|
5783
|
+
remote = values.remote;
|
|
5784
|
+
relayFlags = values.relay ?? [];
|
|
5785
|
+
if (all && sinceArg !== void 0) {
|
|
5786
|
+
throw new Error("--all and --since are mutually exclusive");
|
|
5787
|
+
}
|
|
5788
|
+
} catch (err) {
|
|
5789
|
+
io.err(err instanceof Error ? err.message : String(err));
|
|
5790
|
+
io.err(INBOX_USAGE);
|
|
5791
|
+
return 2;
|
|
5792
|
+
}
|
|
5793
|
+
try {
|
|
5794
|
+
const nowSec = Math.floor((deps.now ?? Date.now)() / 1e3);
|
|
5795
|
+
const override = sinceArg !== void 0 ? parseSince(sinceArg, nowSec) : null;
|
|
5796
|
+
const me = (pubkeyArg ?? (await (deps.resolveIdentityImpl ?? resolveIdentity)({
|
|
5797
|
+
env: deps.env,
|
|
5798
|
+
cwd: deps.cwd,
|
|
5799
|
+
warn: (line) => io.err(line)
|
|
5800
|
+
})).pubkey).toLowerCase();
|
|
5801
|
+
let repoRoot;
|
|
5802
|
+
let toonRelays = [];
|
|
5803
|
+
try {
|
|
5804
|
+
repoRoot = await resolveRepoRoot(deps.cwd);
|
|
5805
|
+
toonRelays = (await readToonConfig(repoRoot)).relays;
|
|
5806
|
+
} catch {
|
|
5807
|
+
}
|
|
5808
|
+
const resolved = await resolveRelays({
|
|
5809
|
+
relayFlags,
|
|
5810
|
+
remoteName: remote,
|
|
5811
|
+
repoRoot,
|
|
5812
|
+
toonRelays
|
|
5813
|
+
});
|
|
5814
|
+
const configured = resolved.relays.filter((url) => WS_URL_RE.test(url));
|
|
5815
|
+
if (configured.length === 0) {
|
|
5816
|
+
throw new InvalidRelayUrlError(
|
|
5817
|
+
resolved.relays[0] ?? "",
|
|
5818
|
+
"reads need a ws:// or wss:// relay"
|
|
5819
|
+
);
|
|
5820
|
+
}
|
|
5821
|
+
const ws = deps.webSocketFactory ?? defaultWebSocketFactory;
|
|
5822
|
+
const announcements = await queryAll(
|
|
5823
|
+
configured,
|
|
5824
|
+
[
|
|
5825
|
+
{ kinds: [REPOSITORY_ANNOUNCEMENT_KIND3], authors: [me] },
|
|
5826
|
+
{ kinds: [REPOSITORY_ANNOUNCEMENT_KIND3], "#maintainers": [me] }
|
|
5827
|
+
],
|
|
5828
|
+
ws
|
|
5829
|
+
);
|
|
5830
|
+
const byRepo = /* @__PURE__ */ new Map();
|
|
5831
|
+
for (const e of announcements.values()) {
|
|
5832
|
+
if (e.kind !== REPOSITORY_ANNOUNCEMENT_KIND3) continue;
|
|
5833
|
+
const d = tagValue2(e.tags, "d");
|
|
5834
|
+
if (!d) continue;
|
|
5835
|
+
const key = `${e.pubkey.toLowerCase()}:${d}`;
|
|
5836
|
+
byRepo.set(key, [...byRepo.get(key) ?? [], e]);
|
|
5837
|
+
}
|
|
5838
|
+
const watched = [];
|
|
5839
|
+
for (const [key, events2] of byRepo) {
|
|
5840
|
+
const owner = events2[0].pubkey.toLowerCase();
|
|
5841
|
+
const repoId = key.slice(owner.length + 1);
|
|
5842
|
+
const latest = owner === me ? newest(events2) : await ownerAnnouncement(configured, owner, repoId, ws);
|
|
5843
|
+
if (latest === null) continue;
|
|
5844
|
+
if (owner !== me && !parseMaintainers(latest.tags).includes(me)) continue;
|
|
5845
|
+
watched.push({
|
|
5846
|
+
address: `${REPOSITORY_ANNOUNCEMENT_KIND3}:${owner}:${repoId}`,
|
|
5847
|
+
ownerPubkey: owner,
|
|
5848
|
+
authorized: authorizedAuthors(owner, latest.tags),
|
|
5849
|
+
relays: relaysTag(latest.tags)
|
|
5850
|
+
});
|
|
5851
|
+
}
|
|
5852
|
+
const cursor = readCursors(deps.env)[me];
|
|
5853
|
+
const since = all ? void 0 : override !== null ? override : cursor?.at;
|
|
5854
|
+
const base = since !== void 0 ? { since } : {};
|
|
5855
|
+
const events = /* @__PURE__ */ new Map();
|
|
5856
|
+
const collect = (found) => {
|
|
5857
|
+
for (const [id, e] of found) if (!events.has(id)) events.set(id, e);
|
|
5858
|
+
};
|
|
5859
|
+
collect(
|
|
5860
|
+
await queryAll(
|
|
5861
|
+
configured,
|
|
5862
|
+
[{ kinds: INBOX_KINDS, "#p": [me], ...base }],
|
|
5863
|
+
ws
|
|
5864
|
+
)
|
|
5865
|
+
);
|
|
5866
|
+
for (const repo of watched) {
|
|
5867
|
+
const relays = [.../* @__PURE__ */ new Set([...configured, ...repo.relays])];
|
|
5868
|
+
collect(
|
|
5869
|
+
await queryAll(
|
|
5870
|
+
relays,
|
|
5871
|
+
[{ kinds: INBOX_KINDS, "#a": [repo.address], ...base }],
|
|
5872
|
+
ws
|
|
5873
|
+
)
|
|
5874
|
+
);
|
|
5875
|
+
}
|
|
5876
|
+
const authCache = /* @__PURE__ */ new Map();
|
|
5877
|
+
for (const repo of watched) authCache.set(repo.address, repo.authorized);
|
|
5878
|
+
const authorizedFor = async (address) => {
|
|
5879
|
+
const hit = authCache.get(address);
|
|
5880
|
+
if (hit) return hit;
|
|
5881
|
+
const m = /^30617:([0-9a-f]{64}):(.+)$/i.exec(address);
|
|
5882
|
+
let set = /* @__PURE__ */ new Set();
|
|
5883
|
+
if (m) {
|
|
5884
|
+
const owner = m[1].toLowerCase();
|
|
5885
|
+
const latest = await ownerAnnouncement(
|
|
5886
|
+
configured,
|
|
5887
|
+
owner,
|
|
5888
|
+
m[2],
|
|
5889
|
+
ws
|
|
5890
|
+
);
|
|
5891
|
+
set = authorizedAuthors(owner, latest?.tags ?? []);
|
|
5892
|
+
}
|
|
5893
|
+
authCache.set(address, set);
|
|
5894
|
+
return set;
|
|
5895
|
+
};
|
|
5896
|
+
const toEntries = async (found) => {
|
|
5897
|
+
const targetAuthors = /* @__PURE__ */ new Map();
|
|
5898
|
+
const statusEvents = [...found.values()].filter(
|
|
5899
|
+
(e) => STATUS_KINDS2.includes(e.kind)
|
|
5900
|
+
);
|
|
5901
|
+
const unknownTargets = /* @__PURE__ */ new Set();
|
|
5902
|
+
for (const e of statusEvents) {
|
|
5903
|
+
const target = tagValue2(e.tags, "e");
|
|
5904
|
+
if (!target || !HEX64_RE4.test(target)) continue;
|
|
5905
|
+
const known = found.get(target);
|
|
5906
|
+
if (known) targetAuthors.set(target, known.pubkey.toLowerCase());
|
|
5907
|
+
else unknownTargets.add(target);
|
|
5908
|
+
}
|
|
5909
|
+
if (unknownTargets.size > 0) {
|
|
5910
|
+
const lookedUp = await queryAll(
|
|
5911
|
+
configured,
|
|
5912
|
+
[{ ids: [...unknownTargets] }],
|
|
5913
|
+
ws
|
|
5914
|
+
).catch(() => /* @__PURE__ */ new Map());
|
|
5915
|
+
for (const [id, e] of lookedUp)
|
|
5916
|
+
targetAuthors.set(id, e.pubkey.toLowerCase());
|
|
5917
|
+
}
|
|
5918
|
+
const entries = [];
|
|
5919
|
+
for (const event of found.values()) {
|
|
5920
|
+
const entry = toInboxEntry(event);
|
|
5921
|
+
if (entry === null) continue;
|
|
5922
|
+
if (entry.type === "status") {
|
|
5923
|
+
if (entry.repo === null) continue;
|
|
5924
|
+
const repoWide = await authorizedFor(entry.repo);
|
|
5925
|
+
const targetAuthor = targetAuthors.get(entry.target);
|
|
5926
|
+
const authors = targetAuthor === void 0 ? repoWide : withTargetAuthor(repoWide, targetAuthor);
|
|
5927
|
+
if (!authors.has(event.pubkey.toLowerCase())) continue;
|
|
5928
|
+
}
|
|
5929
|
+
entries.push(entry);
|
|
5930
|
+
}
|
|
5931
|
+
return entries;
|
|
5932
|
+
};
|
|
5933
|
+
const entriesOf = await toEntries(events);
|
|
5934
|
+
const isFresh = (e) => all || override !== null || cursor === void 0 || e.created_at > cursor.at || e.created_at === cursor.at && !cursor.seen.includes(e.id);
|
|
5935
|
+
const byAge = (a, b) => a.created_at - b.created_at || (a.id < b.id ? -1 : 1);
|
|
5936
|
+
const fresh = entriesOf.filter(isFresh).sort(byAge);
|
|
5937
|
+
const moveCursor = !all && override === null;
|
|
5938
|
+
let position = cursor;
|
|
5939
|
+
const advance = (emitted) => {
|
|
5940
|
+
if (!moveCursor || emitted.length === 0) return;
|
|
5941
|
+
const at = Math.max(...emitted.map((e) => e.created_at));
|
|
5942
|
+
const seen = emitted.filter((e) => e.created_at === at).map((e) => e.id);
|
|
5943
|
+
if (position?.at === at) seen.push(...position.seen);
|
|
5944
|
+
else if (position !== void 0 && position.at > at) return;
|
|
5945
|
+
position = { at, seen: [...new Set(seen)] };
|
|
5946
|
+
writeCursor(deps.env, me, position);
|
|
5947
|
+
};
|
|
5948
|
+
if (follow) {
|
|
5949
|
+
const line = (entry) => io.emitJsonLine ? io.emitJsonLine(entry) : io.out(JSON.stringify(entry));
|
|
5950
|
+
const emittedIds = /* @__PURE__ */ new Set();
|
|
5951
|
+
let liveSince = position?.at;
|
|
5952
|
+
const emit = (entries) => {
|
|
5953
|
+
for (const entry of entries) {
|
|
5954
|
+
emittedIds.add(entry.id);
|
|
5955
|
+
liveSince = Math.max(liveSince ?? 0, entry.created_at);
|
|
5956
|
+
line(entry);
|
|
5957
|
+
}
|
|
5958
|
+
advance(entries);
|
|
5959
|
+
};
|
|
5960
|
+
emit(fresh);
|
|
5961
|
+
const controller = new AbortController();
|
|
5962
|
+
const stop = () => controller.abort();
|
|
5963
|
+
const external = deps.followSignal;
|
|
5964
|
+
if (external?.aborted) return 0;
|
|
5965
|
+
external?.addEventListener("abort", stop);
|
|
5966
|
+
if (!external) {
|
|
5967
|
+
process.once("SIGINT", stop);
|
|
5968
|
+
process.once("SIGTERM", stop);
|
|
5969
|
+
}
|
|
5970
|
+
let chain = Promise.resolve();
|
|
5971
|
+
const onEvent = (event) => {
|
|
5972
|
+
chain = chain.then(async () => {
|
|
5973
|
+
if (emittedIds.has(event.id)) return;
|
|
5974
|
+
try {
|
|
5975
|
+
const [entry] = await toEntries(/* @__PURE__ */ new Map([[event.id, event]]));
|
|
5976
|
+
if (entry === void 0 || emittedIds.has(entry.id) || !isFresh(entry)) {
|
|
5977
|
+
return;
|
|
5978
|
+
}
|
|
5979
|
+
emit([entry]);
|
|
5980
|
+
} catch (err) {
|
|
5981
|
+
io.err(
|
|
5982
|
+
`rig inbox: could not process live event ${event.id}: ${err instanceof Error ? err.message : String(err)}`
|
|
5983
|
+
);
|
|
5984
|
+
}
|
|
5985
|
+
});
|
|
5986
|
+
};
|
|
5987
|
+
const targets = [];
|
|
5988
|
+
for (const url of configured) {
|
|
5989
|
+
targets.push({ url, filter: { kinds: INBOX_KINDS, "#p": [me] } });
|
|
5990
|
+
}
|
|
5991
|
+
for (const repo of watched) {
|
|
5992
|
+
for (const url of /* @__PURE__ */ new Set([...configured, ...repo.relays])) {
|
|
5993
|
+
targets.push({
|
|
5994
|
+
url,
|
|
5995
|
+
filter: { kinds: INBOX_KINDS, "#a": [repo.address] }
|
|
5996
|
+
});
|
|
5997
|
+
}
|
|
5998
|
+
}
|
|
5999
|
+
const urls = [...new Set(targets.map((t) => t.url))];
|
|
6000
|
+
try {
|
|
6001
|
+
await Promise.all(
|
|
6002
|
+
urls.map(
|
|
6003
|
+
(url) => followRelay({
|
|
6004
|
+
url,
|
|
6005
|
+
filters: () => targets.filter((t) => t.url === url).map((t) => ({ ...t.filter, since: liveSince ?? nowSec })),
|
|
6006
|
+
webSocketFactory: ws,
|
|
6007
|
+
onEvent,
|
|
6008
|
+
onError: (message) => io.err(`rig inbox: ${message}`),
|
|
6009
|
+
signal: controller.signal,
|
|
6010
|
+
backoffMs: deps.followBackoffMs ?? defaultBackoffMs
|
|
6011
|
+
})
|
|
6012
|
+
)
|
|
6013
|
+
);
|
|
6014
|
+
await chain;
|
|
6015
|
+
} finally {
|
|
6016
|
+
external?.removeEventListener("abort", stop);
|
|
6017
|
+
process.off("SIGINT", stop);
|
|
6018
|
+
process.off("SIGTERM", stop);
|
|
6019
|
+
}
|
|
6020
|
+
return 0;
|
|
6021
|
+
}
|
|
6022
|
+
if (json) {
|
|
6023
|
+
io.emitJson(fresh);
|
|
6024
|
+
} else if (fresh.length === 0) {
|
|
6025
|
+
io.out("inbox is empty");
|
|
6026
|
+
} else {
|
|
6027
|
+
for (const entry of fresh) io.out(renderLine(entry));
|
|
6028
|
+
}
|
|
6029
|
+
advance(fresh);
|
|
6030
|
+
return 0;
|
|
6031
|
+
} catch (err) {
|
|
6032
|
+
return emitCliError(io, json, "inbox", err);
|
|
6033
|
+
}
|
|
6034
|
+
}
|
|
6035
|
+
|
|
5303
6036
|
// src/cli/init.ts
|
|
5304
6037
|
import { basename as basename2 } from "path";
|
|
5305
|
-
import { parseArgs as
|
|
6038
|
+
import { parseArgs as parseArgs14 } from "util";
|
|
5306
6039
|
|
|
5307
6040
|
// src/cli/git-author.ts
|
|
5308
6041
|
var PROFILE_KIND = 0;
|
|
@@ -5420,7 +6153,7 @@ Options:
|
|
|
5420
6153
|
--json machine-readable report
|
|
5421
6154
|
-h, --help show this help`;
|
|
5422
6155
|
function parseInitArgs(args) {
|
|
5423
|
-
const { values, positionals } =
|
|
6156
|
+
const { values, positionals } = parseArgs14({
|
|
5424
6157
|
args,
|
|
5425
6158
|
options: {
|
|
5426
6159
|
"repo-id": { type: "string" },
|
|
@@ -5647,8 +6380,8 @@ async function runInit(args, deps) {
|
|
|
5647
6380
|
}
|
|
5648
6381
|
|
|
5649
6382
|
// src/cli/maintainers.ts
|
|
5650
|
-
import { parseArgs as
|
|
5651
|
-
var
|
|
6383
|
+
import { parseArgs as parseArgs15 } from "util";
|
|
6384
|
+
var HEX64_RE5 = /^[0-9a-f]{64}$/;
|
|
5652
6385
|
var REF_AUTHORITY_NOTICE = "Note: maintainers can move refs (publish honored repo state), not only set status.";
|
|
5653
6386
|
var MAINTAINERS_USAGE = `Usage: rig maintainers <list|add|remove> [<pubkey>] [options]
|
|
5654
6387
|
|
|
@@ -5701,7 +6434,7 @@ async function runList2(args, deps) {
|
|
|
5701
6434
|
const { io } = deps;
|
|
5702
6435
|
let flags;
|
|
5703
6436
|
try {
|
|
5704
|
-
const { values, positionals } =
|
|
6437
|
+
const { values, positionals } = parseArgs15({
|
|
5705
6438
|
args,
|
|
5706
6439
|
options: REPO_COMMAND_OPTIONS,
|
|
5707
6440
|
allowPositionals: true
|
|
@@ -5768,7 +6501,7 @@ async function runMutate(op, args, deps) {
|
|
|
5768
6501
|
let flags;
|
|
5769
6502
|
let pubkey;
|
|
5770
6503
|
try {
|
|
5771
|
-
const { values, positionals } =
|
|
6504
|
+
const { values, positionals } = parseArgs15({
|
|
5772
6505
|
args,
|
|
5773
6506
|
options: REPO_COMMAND_OPTIONS,
|
|
5774
6507
|
allowPositionals: true
|
|
@@ -5782,7 +6515,7 @@ async function runMutate(op, args, deps) {
|
|
|
5782
6515
|
throw new Error(`expected exactly one <pubkey> to ${op}`);
|
|
5783
6516
|
}
|
|
5784
6517
|
pubkey = ownerToHex(positionals[0]).toLowerCase();
|
|
5785
|
-
if (!
|
|
6518
|
+
if (!HEX64_RE5.test(pubkey)) {
|
|
5786
6519
|
throw new Error(
|
|
5787
6520
|
`<pubkey> must resolve to 64-char hex (got ${JSON.stringify(positionals[0])})`
|
|
5788
6521
|
);
|
|
@@ -5903,7 +6636,7 @@ async function runMutate(op, args, deps) {
|
|
|
5903
6636
|
}
|
|
5904
6637
|
|
|
5905
6638
|
// src/cli/refresh.ts
|
|
5906
|
-
import { parseArgs as
|
|
6639
|
+
import { parseArgs as parseArgs16 } from "util";
|
|
5907
6640
|
var REFRESH_USAGE = `Usage: rig refresh [options]
|
|
5908
6641
|
|
|
5909
6642
|
Republish this repo's kind:30617 announcement with no field edit, refreshing
|
|
@@ -5939,7 +6672,7 @@ async function runRefresh(args, deps) {
|
|
|
5939
6672
|
const { io } = deps;
|
|
5940
6673
|
let flags;
|
|
5941
6674
|
try {
|
|
5942
|
-
const { values, positionals } =
|
|
6675
|
+
const { values, positionals } = parseArgs16({
|
|
5943
6676
|
args,
|
|
5944
6677
|
options: REPO_COMMAND_OPTIONS,
|
|
5945
6678
|
allowPositionals: true
|
|
@@ -6062,7 +6795,7 @@ async function runRefresh(args, deps) {
|
|
|
6062
6795
|
|
|
6063
6796
|
// src/cli/name.ts
|
|
6064
6797
|
import { randomUUID } from "crypto";
|
|
6065
|
-
import { parseArgs as
|
|
6798
|
+
import { parseArgs as parseArgs17 } from "util";
|
|
6066
6799
|
var ARIO_DECIMALS = 6;
|
|
6067
6800
|
function renderMario(mario) {
|
|
6068
6801
|
return `${mario.toString()} mARIO (~${formatUnits(mario.toString(), ARIO_DECIMALS)} ARIO)`;
|
|
@@ -6571,7 +7304,7 @@ async function resolveNameContext(deps, network, processId, mode) {
|
|
|
6571
7304
|
return { identity, solanaAddress, network, processId, sdk, nostrSecretKey };
|
|
6572
7305
|
}
|
|
6573
7306
|
function parseNameFlags(args, env) {
|
|
6574
|
-
const { values, positionals } =
|
|
7307
|
+
const { values, positionals } = parseArgs17({
|
|
6575
7308
|
args,
|
|
6576
7309
|
allowPositionals: true,
|
|
6577
7310
|
options: {
|
|
@@ -7130,7 +7863,7 @@ async function runName(args, deps) {
|
|
|
7130
7863
|
}
|
|
7131
7864
|
|
|
7132
7865
|
// src/cli/payout.ts
|
|
7133
|
-
import { parseArgs as
|
|
7866
|
+
import { parseArgs as parseArgs18 } from "util";
|
|
7134
7867
|
import { getAddress } from "viem";
|
|
7135
7868
|
var PAYOUT_USAGE = `Usage: rig payout <set|clear|show> [<address>] [options]
|
|
7136
7869
|
|
|
@@ -7186,7 +7919,7 @@ async function runShow2(args, deps) {
|
|
|
7186
7919
|
const { io } = deps;
|
|
7187
7920
|
let flags;
|
|
7188
7921
|
try {
|
|
7189
|
-
const { values, positionals } =
|
|
7922
|
+
const { values, positionals } = parseArgs18({
|
|
7190
7923
|
args,
|
|
7191
7924
|
options: REPO_COMMAND_OPTIONS,
|
|
7192
7925
|
allowPositionals: true
|
|
@@ -7248,7 +7981,7 @@ async function runMutate2(op, args, deps) {
|
|
|
7248
7981
|
let flags;
|
|
7249
7982
|
let nextPayout = null;
|
|
7250
7983
|
try {
|
|
7251
|
-
const { values, positionals } =
|
|
7984
|
+
const { values, positionals } = parseArgs18({
|
|
7252
7985
|
args,
|
|
7253
7986
|
options: REPO_COMMAND_OPTIONS,
|
|
7254
7987
|
allowPositionals: true
|
|
@@ -7382,7 +8115,7 @@ async function runMutate2(op, args, deps) {
|
|
|
7382
8115
|
}
|
|
7383
8116
|
|
|
7384
8117
|
// src/cli/site.ts
|
|
7385
|
-
import { parseArgs as
|
|
8118
|
+
import { parseArgs as parseArgs19 } from "util";
|
|
7386
8119
|
|
|
7387
8120
|
// src/arweave-manifest.ts
|
|
7388
8121
|
var DEFAULT_INDEX = "index.html";
|
|
@@ -7401,22 +8134,22 @@ function buildArweaveManifest(entries, indexPath = DEFAULT_INDEX, fallbackTxId)
|
|
|
7401
8134
|
}
|
|
7402
8135
|
|
|
7403
8136
|
// src/cli/site-record.ts
|
|
7404
|
-
import { mkdirSync as
|
|
8137
|
+
import { mkdirSync as mkdirSync6, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
|
|
7405
8138
|
import { homedir as homedir5 } from "os";
|
|
7406
|
-
import { join as
|
|
8139
|
+
import { join as join7 } from "path";
|
|
7407
8140
|
var SITE_MANIFEST_FILENAME = "site-manifests.json";
|
|
7408
8141
|
function storeDir2(env) {
|
|
7409
|
-
return env["TOON_CLIENT_HOME"] ??
|
|
8142
|
+
return env["TOON_CLIENT_HOME"] ?? join7(homedir5(), ".toon-client");
|
|
7410
8143
|
}
|
|
7411
8144
|
function storePath2(env) {
|
|
7412
|
-
return
|
|
8145
|
+
return join7(storeDir2(env), SITE_MANIFEST_FILENAME);
|
|
7413
8146
|
}
|
|
7414
8147
|
function keyOf(repoId, ref) {
|
|
7415
8148
|
return `${repoId}\0${ref}`;
|
|
7416
8149
|
}
|
|
7417
8150
|
function readFile2(env) {
|
|
7418
8151
|
try {
|
|
7419
|
-
const parsed = JSON.parse(
|
|
8152
|
+
const parsed = JSON.parse(readFileSync6(storePath2(env), "utf8"));
|
|
7420
8153
|
if (parsed && typeof parsed === "object") return parsed;
|
|
7421
8154
|
return {};
|
|
7422
8155
|
} catch {
|
|
@@ -7428,10 +8161,10 @@ function readSiteRecord(env, repoId, ref) {
|
|
|
7428
8161
|
}
|
|
7429
8162
|
function writeSiteRecord(env, record) {
|
|
7430
8163
|
const dir = storeDir2(env);
|
|
7431
|
-
|
|
8164
|
+
mkdirSync6(dir, { recursive: true });
|
|
7432
8165
|
const file = readFile2(env);
|
|
7433
8166
|
file[keyOf(record.repoId, record.ref)] = record;
|
|
7434
|
-
|
|
8167
|
+
writeFileSync5(storePath2(env), `${JSON.stringify(file, null, 2)}
|
|
7435
8168
|
`, "utf8");
|
|
7436
8169
|
}
|
|
7437
8170
|
|
|
@@ -7473,7 +8206,7 @@ Options (publish):
|
|
|
7473
8206
|
estimate (nothing uploaded)
|
|
7474
8207
|
-h, --help show this help`;
|
|
7475
8208
|
function parseSiteArgs(args) {
|
|
7476
|
-
const { values, positionals } =
|
|
8209
|
+
const { values, positionals } = parseArgs19({
|
|
7477
8210
|
args,
|
|
7478
8211
|
options: {
|
|
7479
8212
|
index: { type: "string" },
|
|
@@ -7881,9 +8614,16 @@ Commands rig owns:
|
|
|
7881
8614
|
comment <target-event-id> comment (NIP-22 kind:1111) on an issue or patch,
|
|
7882
8615
|
or reply to an existing kind:1111 comment
|
|
7883
8616
|
pr create publish a patch (kind:1617) with real
|
|
7884
|
-
\`git format-patch\` content
|
|
8617
|
+
\`git format-patch\` content (--revises <patch-id>
|
|
8618
|
+
publishes a revision of an existing patch)
|
|
7885
8619
|
pr status <event-id> <state> set an issue/patch status (kind:1630-1633):
|
|
7886
8620
|
open | applied | closed | draft
|
|
8621
|
+
inbox everything new since you last read it (free):
|
|
8622
|
+
patches, revisions, issues, comments, statuses
|
|
8623
|
+
and CI results for repos you own or maintain,
|
|
8624
|
+
plus events that tag you; --since/--all look
|
|
8625
|
+
back without moving the cursor; --follow keeps
|
|
8626
|
+
streaming new events as JSON lines
|
|
7887
8627
|
maintainers list show the repo's declared maintainers (free); add/
|
|
7888
8628
|
maintainers add <pubkey> remove republish the kind:30617 to change who may
|
|
7889
8629
|
maintainers remove <pubkey> author authoritative issue/PR status (owner is
|
|
@@ -8006,6 +8746,8 @@ async function dispatch(argv, deps) {
|
|
|
8006
8746
|
return runComment(rest, deps);
|
|
8007
8747
|
case "pr":
|
|
8008
8748
|
return runPr(rest, deps);
|
|
8749
|
+
case "inbox":
|
|
8750
|
+
return runInbox(rest, deps);
|
|
8009
8751
|
case "maintainers":
|
|
8010
8752
|
return runMaintainers(rest, deps);
|
|
8011
8753
|
case "payout":
|
|
@@ -8062,4 +8804,4 @@ export {
|
|
|
8062
8804
|
rigVersion,
|
|
8063
8805
|
dispatch
|
|
8064
8806
|
};
|
|
8065
|
-
//# sourceMappingURL=chunk-
|
|
8807
|
+
//# sourceMappingURL=chunk-Y7TUE7JT.js.map
|