@toon-protocol/rig 5.2.0 → 5.4.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/README.md +9 -9
- package/dist/{chunk-JX5P2KAA.js → chunk-3KS6S4S5.js} +11 -2
- package/dist/chunk-3KS6S4S5.js.map +1 -0
- package/dist/{chunk-S5R44MMB.js → chunk-MIK7SRWE.js} +70 -41
- package/dist/{chunk-S5R44MMB.js.map → chunk-MIK7SRWE.js.map} +1 -1
- package/dist/{chunk-FX5GPZYU.js → chunk-QSUO7J4H.js} +0 -0
- package/dist/{chunk-Y7TUE7JT.js → chunk-Z3XRZ5E5.js} +982 -436
- package/dist/chunk-Z3XRZ5E5.js.map +1 -0
- package/dist/{ci-serve-CA37KQVC.js → ci-serve-4RJCKEPV.js} +5 -5
- package/dist/cli/rig.js +4 -4
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/{publisher-CQi69Nwg.d.ts → publisher-1vY1WABR.d.ts} +15 -5
- package/dist/standalone/index.d.ts +1 -1
- package/dist/{standalone-mode-NWCCRFCL.js → standalone-mode-LUWNCDPL.js} +6 -4
- package/dist/standalone-mode-LUWNCDPL.js.map +1 -0
- package/package.json +1 -1
- package/dist/chunk-JX5P2KAA.js.map +0 -1
- package/dist/chunk-Y7TUE7JT.js.map +0 -1
- package/dist/standalone-mode-NWCCRFCL.js.map +0 -1
- /package/dist/{chunk-FX5GPZYU.js.map → chunk-QSUO7J4H.js.map} +0 -0
- /package/dist/{ci-serve-CA37KQVC.js.map → ci-serve-4RJCKEPV.js.map} +0 -0
|
@@ -4,12 +4,14 @@ import {
|
|
|
4
4
|
CI_SERVICE_REQUEST_KIND,
|
|
5
5
|
CI_SERVICE_STOP_KIND,
|
|
6
6
|
CI_STATUS_USAGE,
|
|
7
|
+
CI_TRUST_ORDER,
|
|
7
8
|
CI_WORKFLOW_PROGRESS_KIND,
|
|
8
9
|
CI_WORKFLOW_RESULT_KIND,
|
|
9
10
|
GitRepoReader,
|
|
10
11
|
InvalidRelayUrlError,
|
|
11
12
|
MultiUrlRemoteError,
|
|
12
13
|
NotAGitRepositoryError,
|
|
14
|
+
PrMergeError,
|
|
13
15
|
RESERVED_SECRET_NAMES,
|
|
14
16
|
SECRET_NAME_RE,
|
|
15
17
|
UnconfiguredRepoAddressError,
|
|
@@ -26,11 +28,13 @@ import {
|
|
|
26
28
|
earliestUniqueCommit,
|
|
27
29
|
emitCliError,
|
|
28
30
|
encryptSecretUpdate,
|
|
31
|
+
evaluateCiGate,
|
|
29
32
|
executePush,
|
|
30
33
|
generateSecretsKey,
|
|
31
34
|
getGitRemoteUrls,
|
|
32
35
|
hexToNpub,
|
|
33
36
|
initGitRepository,
|
|
37
|
+
isCiTrustLevel,
|
|
34
38
|
isRelayUrl,
|
|
35
39
|
isSafeRefname,
|
|
36
40
|
listGitRemotes,
|
|
@@ -57,13 +61,13 @@ import {
|
|
|
57
61
|
updateRef,
|
|
58
62
|
writeGitObjects,
|
|
59
63
|
writeToonConfig
|
|
60
|
-
} from "./chunk-
|
|
64
|
+
} from "./chunk-MIK7SRWE.js";
|
|
61
65
|
import {
|
|
62
66
|
PREFERRED_GATEWAY,
|
|
63
67
|
mirrorGatewaysFor,
|
|
64
68
|
readGateways,
|
|
65
69
|
shaResolverFor
|
|
66
|
-
} from "./chunk-
|
|
70
|
+
} from "./chunk-QSUO7J4H.js";
|
|
67
71
|
import {
|
|
68
72
|
COMMENT_KIND,
|
|
69
73
|
DEFAULT_KEYSTORE_PASSWORD,
|
|
@@ -92,7 +96,7 @@ import {
|
|
|
92
96
|
resolveIdentity,
|
|
93
97
|
revisionRootId,
|
|
94
98
|
selectRevision
|
|
95
|
-
} from "./chunk-
|
|
99
|
+
} from "./chunk-3KS6S4S5.js";
|
|
96
100
|
import {
|
|
97
101
|
ChannelMapStore,
|
|
98
102
|
channelStatus,
|
|
@@ -321,7 +325,7 @@ function loadPaidSession(deps, relayUrl) {
|
|
|
321
325
|
});
|
|
322
326
|
}
|
|
323
327
|
var defaultLoadStandalone = async (options) => {
|
|
324
|
-
const mod = await import("./standalone-mode-
|
|
328
|
+
const mod = await import("./standalone-mode-LUWNCDPL.js");
|
|
325
329
|
return mod.createStandaloneContext(options);
|
|
326
330
|
};
|
|
327
331
|
function identityReport(ctx) {
|
|
@@ -2306,9 +2310,9 @@ async function runCiTrigger(args, deps) {
|
|
|
2306
2310
|
});
|
|
2307
2311
|
}
|
|
2308
2312
|
async function gitShowBytes(repoRoot, commit, path) {
|
|
2309
|
-
const { execFile } = await import("child_process");
|
|
2313
|
+
const { execFile: execFile2 } = await import("child_process");
|
|
2310
2314
|
return new Promise((resolve2, reject) => {
|
|
2311
|
-
|
|
2315
|
+
execFile2(
|
|
2312
2316
|
"git",
|
|
2313
2317
|
["show", `${commit}:${path}`],
|
|
2314
2318
|
{ cwd: repoRoot, encoding: "buffer", maxBuffer: 16 * 1024 * 1024 },
|
|
@@ -2519,7 +2523,7 @@ async function runCi(args, deps) {
|
|
|
2519
2523
|
case "status":
|
|
2520
2524
|
return runCiStatus(rest, deps);
|
|
2521
2525
|
case "serve":
|
|
2522
|
-
return (await import("./ci-serve-
|
|
2526
|
+
return (await import("./ci-serve-4RJCKEPV.js")).runCiServe(rest, deps);
|
|
2523
2527
|
case "--help":
|
|
2524
2528
|
case "-h":
|
|
2525
2529
|
case "help":
|
|
@@ -2790,18 +2794,258 @@ async function runClone(args, deps) {
|
|
|
2790
2794
|
|
|
2791
2795
|
// src/cli/events.ts
|
|
2792
2796
|
import { readFile } from "fs/promises";
|
|
2793
|
-
import { parseArgs as
|
|
2797
|
+
import { parseArgs as parseArgs10 } from "util";
|
|
2798
|
+
import {
|
|
2799
|
+
PATCH_KIND as PATCH_KIND3,
|
|
2800
|
+
REPOSITORY_ANNOUNCEMENT_KIND as REPOSITORY_ANNOUNCEMENT_KIND3,
|
|
2801
|
+
STATUS_APPLIED_KIND as STATUS_APPLIED_KIND3,
|
|
2802
|
+
STATUS_CLOSED_KIND as STATUS_CLOSED_KIND3,
|
|
2803
|
+
STATUS_DRAFT_KIND as STATUS_DRAFT_KIND2,
|
|
2804
|
+
STATUS_OPEN_KIND as STATUS_OPEN_KIND2
|
|
2805
|
+
} from "@toon-protocol/core/nip34";
|
|
2806
|
+
|
|
2807
|
+
// src/cli/merge.ts
|
|
2808
|
+
import { execFile } from "child_process";
|
|
2809
|
+
import { mkdtemp as mkdtemp2, rm as rm2, writeFile } from "fs/promises";
|
|
2810
|
+
import { tmpdir } from "os";
|
|
2811
|
+
import { join as join4 } from "path";
|
|
2812
|
+
import { parseArgs as parseArgs9 } from "util";
|
|
2813
|
+
import { promisify } from "util";
|
|
2794
2814
|
import {
|
|
2795
2815
|
PATCH_KIND as PATCH_KIND2,
|
|
2796
2816
|
REPOSITORY_ANNOUNCEMENT_KIND as REPOSITORY_ANNOUNCEMENT_KIND2,
|
|
2797
2817
|
STATUS_APPLIED_KIND as STATUS_APPLIED_KIND2,
|
|
2798
|
-
STATUS_CLOSED_KIND as STATUS_CLOSED_KIND2
|
|
2799
|
-
STATUS_DRAFT_KIND as STATUS_DRAFT_KIND2,
|
|
2800
|
-
STATUS_OPEN_KIND as STATUS_OPEN_KIND2
|
|
2818
|
+
STATUS_CLOSED_KIND as STATUS_CLOSED_KIND2
|
|
2801
2819
|
} from "@toon-protocol/core/nip34";
|
|
2802
2820
|
|
|
2803
|
-
// src/cli/
|
|
2821
|
+
// src/cli/fetch.ts
|
|
2804
2822
|
import { parseArgs as parseArgs7 } from "util";
|
|
2823
|
+
var FETCH_USAGE = `Usage: rig fetch [remote] [options]
|
|
2824
|
+
|
|
2825
|
+
Fetch from a TOON remote \u2014 FREE (relay reads + Arweave gateway downloads; no
|
|
2826
|
+
payments, no identity needed). Reads the kind:30618 repository state from the
|
|
2827
|
+
remote's relay, downloads only the git objects this repository is missing
|
|
2828
|
+
(SHA-1 verified), and updates the remote-tracking refs
|
|
2829
|
+
(refs/remotes/<remote>/*; tags land at refs/tags/*), reporting what moved.
|
|
2830
|
+
|
|
2831
|
+
No merge happens: integrate with the git passthrough, e.g.
|
|
2832
|
+
\`rig merge origin/main\`. \`rig fetch\` is the TOON transport and shadows
|
|
2833
|
+
\`git fetch\` \u2014 plain-git fetches remain available via \`git fetch\` directly.
|
|
2834
|
+
|
|
2835
|
+
The repo address (toon.repoid/toon.owner) comes from the config \`rig init\`
|
|
2836
|
+
or \`rig clone\` writes; --repo-id/--owner override.
|
|
2837
|
+
|
|
2838
|
+
Options:
|
|
2839
|
+
--repo-id <id> repository id / NIP-34 d-tag (default: git config)
|
|
2840
|
+
--owner <pubkey> repository owner (npub or 64-char hex; default: git config)
|
|
2841
|
+
--relay <url> ad-hoc relay override \u2014 bypasses the remote's URL
|
|
2842
|
+
--gateway <url> gateway to read objects through FIRST, on its raw-bytes
|
|
2843
|
+
route <url>/raw/<txId>, ahead of the public gateway
|
|
2844
|
+
list \u2014 name a private, local or air-gapped gateway to
|
|
2845
|
+
fetch objects the public ones do not carry
|
|
2846
|
+
(env RIG_ARWEAVE_GATEWAY also sets it)
|
|
2847
|
+
--concurrency <n> parallel gateway downloads (default 8)
|
|
2848
|
+
--json machine-readable result envelope
|
|
2849
|
+
-h, --help show this help`;
|
|
2850
|
+
var WS_URL_RE3 = /^wss?:\/\//i;
|
|
2851
|
+
function localRefFor(refname, remote) {
|
|
2852
|
+
if (refname.startsWith("refs/heads/")) {
|
|
2853
|
+
return `refs/remotes/${remote}/${refname.slice("refs/heads/".length)}`;
|
|
2854
|
+
}
|
|
2855
|
+
if (refname.startsWith("refs/tags/")) return refname;
|
|
2856
|
+
return null;
|
|
2857
|
+
}
|
|
2858
|
+
async function resolveLocalRef(repoRoot, refname) {
|
|
2859
|
+
try {
|
|
2860
|
+
const out = await runGit(repoRoot, [
|
|
2861
|
+
"rev-parse",
|
|
2862
|
+
"--verify",
|
|
2863
|
+
"--quiet",
|
|
2864
|
+
refname
|
|
2865
|
+
]);
|
|
2866
|
+
const sha = out.trim();
|
|
2867
|
+
return /^[0-9a-f]{40}$/.test(sha) ? sha : null;
|
|
2868
|
+
} catch {
|
|
2869
|
+
return null;
|
|
2870
|
+
}
|
|
2871
|
+
}
|
|
2872
|
+
async function runFetch(args, deps) {
|
|
2873
|
+
const { io } = deps;
|
|
2874
|
+
let json = false;
|
|
2875
|
+
let remote = "origin";
|
|
2876
|
+
let relayFlag;
|
|
2877
|
+
let repoIdFlag;
|
|
2878
|
+
let ownerFlag;
|
|
2879
|
+
let gatewayFlag;
|
|
2880
|
+
let concurrency;
|
|
2881
|
+
try {
|
|
2882
|
+
const { values, positionals } = parseArgs7({
|
|
2883
|
+
args,
|
|
2884
|
+
options: {
|
|
2885
|
+
json: { type: "boolean", default: false },
|
|
2886
|
+
relay: { type: "string" },
|
|
2887
|
+
"repo-id": { type: "string" },
|
|
2888
|
+
owner: { type: "string" },
|
|
2889
|
+
gateway: { type: "string" },
|
|
2890
|
+
concurrency: { type: "string" },
|
|
2891
|
+
help: { type: "boolean", short: "h", default: false }
|
|
2892
|
+
},
|
|
2893
|
+
allowPositionals: true
|
|
2894
|
+
});
|
|
2895
|
+
if (values.help) {
|
|
2896
|
+
io.out(FETCH_USAGE);
|
|
2897
|
+
return 0;
|
|
2898
|
+
}
|
|
2899
|
+
json = values.json === true;
|
|
2900
|
+
if (positionals.length > 1) {
|
|
2901
|
+
throw new Error(
|
|
2902
|
+
`expected at most one [remote] argument, got ${positionals.length}`
|
|
2903
|
+
);
|
|
2904
|
+
}
|
|
2905
|
+
if (positionals[0] !== void 0) remote = positionals[0];
|
|
2906
|
+
relayFlag = values.relay;
|
|
2907
|
+
repoIdFlag = values["repo-id"];
|
|
2908
|
+
ownerFlag = values.owner !== void 0 ? ownerToHex(values.owner) : void 0;
|
|
2909
|
+
gatewayFlag = values.gateway;
|
|
2910
|
+
if (values.concurrency !== void 0) {
|
|
2911
|
+
concurrency = Number.parseInt(values.concurrency, 10);
|
|
2912
|
+
if (!Number.isSafeInteger(concurrency) || concurrency < 1) {
|
|
2913
|
+
throw new Error("--concurrency must be a positive integer");
|
|
2914
|
+
}
|
|
2915
|
+
}
|
|
2916
|
+
} catch (err) {
|
|
2917
|
+
io.err(err instanceof Error ? err.message : String(err));
|
|
2918
|
+
io.err(FETCH_USAGE);
|
|
2919
|
+
return 2;
|
|
2920
|
+
}
|
|
2921
|
+
try {
|
|
2922
|
+
const repoRoot = await resolveRepoRoot(deps.cwd);
|
|
2923
|
+
const toonConfig = await readToonConfig(repoRoot);
|
|
2924
|
+
const repoId = repoIdFlag ?? toonConfig.repoId;
|
|
2925
|
+
if (!repoId) throw new UnconfiguredRepoAddressError("repository id");
|
|
2926
|
+
const owner = ownerFlag ?? toonConfig.owner;
|
|
2927
|
+
if (!owner) throw new UnconfiguredRepoAddressError("repository owner");
|
|
2928
|
+
let relayUrl;
|
|
2929
|
+
if (relayFlag !== void 0) {
|
|
2930
|
+
relayUrl = relayFlag;
|
|
2931
|
+
} else {
|
|
2932
|
+
const urls = await getGitRemoteUrls(repoRoot, remote);
|
|
2933
|
+
if (urls.length === 0) throw new UnknownRemoteError(remote);
|
|
2934
|
+
if (urls.length > 1) throw new MultiUrlRemoteError(remote, urls);
|
|
2935
|
+
relayUrl = urls[0];
|
|
2936
|
+
}
|
|
2937
|
+
if (!WS_URL_RE3.test(relayUrl)) {
|
|
2938
|
+
throw new InvalidRelayUrlError(
|
|
2939
|
+
relayUrl,
|
|
2940
|
+
`remote ${JSON.stringify(remote)}`
|
|
2941
|
+
);
|
|
2942
|
+
}
|
|
2943
|
+
const remoteState = await fetchRemoteState({
|
|
2944
|
+
relayUrls: [relayUrl],
|
|
2945
|
+
ownerPubkey: owner,
|
|
2946
|
+
repoId,
|
|
2947
|
+
...deps.webSocketFactory ? { webSocketFactory: deps.webSocketFactory } : {},
|
|
2948
|
+
// The GraphQL fallback for SHAs the map does not cover asks the SAME
|
|
2949
|
+
// permaweb the objects are read from (#183).
|
|
2950
|
+
resolveSha: deps.resolveSha ?? shaResolverFor(gatewayFlag, deps.env)
|
|
2951
|
+
});
|
|
2952
|
+
const planned = [];
|
|
2953
|
+
for (const [refname, sha] of remoteState.refs) {
|
|
2954
|
+
if (!isSafeRefname(refname)) {
|
|
2955
|
+
io.err(
|
|
2956
|
+
`warning: skipping unsafe ref name from relay: ${JSON.stringify(refname)}`
|
|
2957
|
+
);
|
|
2958
|
+
continue;
|
|
2959
|
+
}
|
|
2960
|
+
const localRef = localRefFor(refname, remote);
|
|
2961
|
+
if (localRef === null) {
|
|
2962
|
+
io.err(
|
|
2963
|
+
`warning: skipping ref outside refs/heads and refs/tags: ${refname}`
|
|
2964
|
+
);
|
|
2965
|
+
continue;
|
|
2966
|
+
}
|
|
2967
|
+
planned.push({ refname, localRef, newSha: sha });
|
|
2968
|
+
}
|
|
2969
|
+
const reader = new GitRepoReader(repoRoot);
|
|
2970
|
+
const tips = [...new Set(planned.map((p) => p.newSha))];
|
|
2971
|
+
const presentLocally = new Set(await reader.listAllObjectShas());
|
|
2972
|
+
const collected = await collectRepoObjects({
|
|
2973
|
+
tips,
|
|
2974
|
+
shaToTxId: remoteState.shaToTxId,
|
|
2975
|
+
resolveMissing: (shas) => remoteState.resolveMissing(shas),
|
|
2976
|
+
presentLocally,
|
|
2977
|
+
// `--gateway` / RIG_ARWEAVE_GATEWAY first, public list behind it (#176).
|
|
2978
|
+
gateways: readGateways(gatewayFlag, deps.env),
|
|
2979
|
+
...deps.fetchFn ? { fetchFn: deps.fetchFn } : {},
|
|
2980
|
+
...concurrency !== void 0 ? { concurrency } : {}
|
|
2981
|
+
});
|
|
2982
|
+
if (collected.missing.length > 0) {
|
|
2983
|
+
throw new MissingRemoteObjectsError(
|
|
2984
|
+
collected.missing,
|
|
2985
|
+
`cannot fetch from ${relayUrl}`
|
|
2986
|
+
);
|
|
2987
|
+
}
|
|
2988
|
+
const written = await writeGitObjects(repoRoot, collected.objects.values());
|
|
2989
|
+
const updates = [];
|
|
2990
|
+
for (const { refname, localRef, newSha } of planned) {
|
|
2991
|
+
const oldSha = await resolveLocalRef(repoRoot, localRef);
|
|
2992
|
+
if (oldSha === newSha) {
|
|
2993
|
+
updates.push({ refname, localRef, oldSha, newSha, kind: "up-to-date" });
|
|
2994
|
+
continue;
|
|
2995
|
+
}
|
|
2996
|
+
let kind;
|
|
2997
|
+
if (oldSha === null) {
|
|
2998
|
+
kind = "new";
|
|
2999
|
+
} else {
|
|
3000
|
+
kind = await reader.isAncestor(oldSha, newSha) ? "fast-forward" : "forced";
|
|
3001
|
+
}
|
|
3002
|
+
await updateRef(repoRoot, localRef, newSha);
|
|
3003
|
+
updates.push({ refname, localRef, oldSha, newSha, kind });
|
|
3004
|
+
}
|
|
3005
|
+
const moved = updates.filter((u) => u.kind !== "up-to-date");
|
|
3006
|
+
if (json) {
|
|
3007
|
+
io.emitJson({
|
|
3008
|
+
command: "fetch",
|
|
3009
|
+
repoAddr: { ownerPubkey: owner, repoId },
|
|
3010
|
+
remote,
|
|
3011
|
+
relay: relayUrl,
|
|
3012
|
+
objectsDownloaded: written,
|
|
3013
|
+
updates,
|
|
3014
|
+
executed: true
|
|
3015
|
+
});
|
|
3016
|
+
return 0;
|
|
3017
|
+
}
|
|
3018
|
+
if (moved.length === 0) {
|
|
3019
|
+
io.out("Already up to date.");
|
|
3020
|
+
return 0;
|
|
3021
|
+
}
|
|
3022
|
+
io.out(`From ${relayUrl}`);
|
|
3023
|
+
for (const u of moved) {
|
|
3024
|
+
const short = (refname) => refname.replace(/^refs\/heads\//, "").replace(/^refs\/tags\//, "").replace(/^refs\/remotes\//, "");
|
|
3025
|
+
const src = short(u.refname);
|
|
3026
|
+
const dst = short(u.localRef);
|
|
3027
|
+
if (u.kind === "new") {
|
|
3028
|
+
const label = u.refname.startsWith("refs/tags/") ? "[new tag]" : "[new branch]";
|
|
3029
|
+
io.out(` * ${label.padEnd(17)} ${src.padEnd(14)} -> ${dst}`);
|
|
3030
|
+
} else if (u.kind === "forced") {
|
|
3031
|
+
io.out(
|
|
3032
|
+
` + ${u.oldSha.slice(0, 7)}...${u.newSha.slice(0, 7)} ${src.padEnd(14)} -> ${dst} (forced update)`
|
|
3033
|
+
);
|
|
3034
|
+
} else {
|
|
3035
|
+
io.out(
|
|
3036
|
+
` ${u.oldSha.slice(0, 7)}..${u.newSha.slice(0, 7)} ${src.padEnd(14)} -> ${dst}`
|
|
3037
|
+
);
|
|
3038
|
+
}
|
|
3039
|
+
}
|
|
3040
|
+
io.out(`Downloaded ${written} object(s) (SHA-1 verified).`);
|
|
3041
|
+
return 0;
|
|
3042
|
+
} catch (err) {
|
|
3043
|
+
return emitCliError(io, json, "fetch", err);
|
|
3044
|
+
}
|
|
3045
|
+
}
|
|
3046
|
+
|
|
3047
|
+
// src/cli/tracker.ts
|
|
3048
|
+
import { parseArgs as parseArgs8 } from "util";
|
|
2805
3049
|
import {
|
|
2806
3050
|
ISSUE_KIND,
|
|
2807
3051
|
PATCH_KIND,
|
|
@@ -2887,7 +3131,7 @@ function withTargetAuthor(authorized, targetAuthorPubkey) {
|
|
|
2887
3131
|
}
|
|
2888
3132
|
var RELAY_TIMEOUT_MS = 1e4;
|
|
2889
3133
|
var HEX64_RE2 = /^[0-9a-f]{64}$/;
|
|
2890
|
-
var
|
|
3134
|
+
var WS_URL_RE4 = /^wss?:\/\//i;
|
|
2891
3135
|
function defaultWebSocketFactory2(url) {
|
|
2892
3136
|
const ctor = globalThis.WebSocket;
|
|
2893
3137
|
if (!ctor) {
|
|
@@ -2971,7 +3215,7 @@ async function resolveTrackerContext(flags, deps, needRepoAddr) {
|
|
|
2971
3215
|
repoRoot,
|
|
2972
3216
|
toonRelays: toonConfig.relays
|
|
2973
3217
|
});
|
|
2974
|
-
const wsRelays = resolved.relays.filter((url) =>
|
|
3218
|
+
const wsRelays = resolved.relays.filter((url) => WS_URL_RE4.test(url));
|
|
2975
3219
|
if (wsRelays.length === 0) {
|
|
2976
3220
|
throw new InvalidRelayUrlError(
|
|
2977
3221
|
resolved.relays[0] ?? "",
|
|
@@ -3198,7 +3442,7 @@ async function runList(args, deps, spec) {
|
|
|
3198
3442
|
const { io } = deps;
|
|
3199
3443
|
let flags;
|
|
3200
3444
|
try {
|
|
3201
|
-
const { values, positionals } =
|
|
3445
|
+
const { values, positionals } = parseArgs8({
|
|
3202
3446
|
args,
|
|
3203
3447
|
options: TRACKER_OPTIONS,
|
|
3204
3448
|
allowPositionals: true
|
|
@@ -3263,7 +3507,7 @@ async function runShow(args, deps, spec) {
|
|
|
3263
3507
|
let flags;
|
|
3264
3508
|
let eventId;
|
|
3265
3509
|
try {
|
|
3266
|
-
const { values, positionals } =
|
|
3510
|
+
const { values, positionals } = parseArgs8({
|
|
3267
3511
|
args,
|
|
3268
3512
|
options: TRACKER_OPTIONS,
|
|
3269
3513
|
allowPositionals: true
|
|
@@ -3381,14 +3625,524 @@ function runPrList(args, deps) {
|
|
|
3381
3625
|
jsonKey: "prs"
|
|
3382
3626
|
});
|
|
3383
3627
|
}
|
|
3384
|
-
function runPrShow(args, deps) {
|
|
3385
|
-
return runShow(args, deps, {
|
|
3386
|
-
command: "pr show",
|
|
3387
|
-
kind: PATCH_KIND,
|
|
3388
|
-
usage: PR_SHOW_USAGE,
|
|
3389
|
-
jsonKey: "pr",
|
|
3390
|
-
bodyLabel: "Patch"
|
|
3391
|
-
});
|
|
3628
|
+
function runPrShow(args, deps) {
|
|
3629
|
+
return runShow(args, deps, {
|
|
3630
|
+
command: "pr show",
|
|
3631
|
+
kind: PATCH_KIND,
|
|
3632
|
+
usage: PR_SHOW_USAGE,
|
|
3633
|
+
jsonKey: "pr",
|
|
3634
|
+
bodyLabel: "Patch"
|
|
3635
|
+
});
|
|
3636
|
+
}
|
|
3637
|
+
|
|
3638
|
+
// src/cli/merge.ts
|
|
3639
|
+
var execFileAsync = promisify(execFile);
|
|
3640
|
+
var HEX64_RE3 = /^[0-9a-f]{64}$/;
|
|
3641
|
+
var PR_MERGE_USAGE = `Usage: rig pr merge <patch-id> [--target <branch>] [options]
|
|
3642
|
+
|
|
3643
|
+
Apply the latest authorized revision of a patch with \`git am\` onto the
|
|
3644
|
+
honored tip of the target branch, push the result, and publish an applied
|
|
3645
|
+
status (kind:1631) naming the new commit SHAs \u2014 in that order, so the status
|
|
3646
|
+
never claims a merge that did not happen. A paid command; writes are permanent
|
|
3647
|
+
and non-refundable. Only the owner or a declared maintainer may merge.
|
|
3648
|
+
|
|
3649
|
+
If the patch does not apply, nothing is spent and your working tree is left as
|
|
3650
|
+
it was. If the push succeeds but the status publish fails, the merge is
|
|
3651
|
+
reported as a partial success; retry with \`rig pr status <patch-id> applied\`.
|
|
3652
|
+
|
|
3653
|
+
Options:
|
|
3654
|
+
--target <branch> branch to merge onto (default: the patch's branch-name
|
|
3655
|
+
tag, else the repo's default branch)
|
|
3656
|
+
--require-ci-trust <level>
|
|
3657
|
+
refuse the merge, before paying, unless the patch
|
|
3658
|
+
series' head commit has a concluded green CI run at or
|
|
3659
|
+
above this trust level (same gate as \`rig ci status\`)
|
|
3660
|
+
--closes <issue-id> after a successful merge, publish closed (kind:1632)
|
|
3661
|
+
for this issue; repeatable
|
|
3662
|
+
--yes skip the confirmation prompt
|
|
3663
|
+
--json machine-readable output (without --yes: estimate only)
|
|
3664
|
+
--relay <url> ad-hoc relay override (bypasses git remotes)
|
|
3665
|
+
--remote <name> git remote to use (default: origin)
|
|
3666
|
+
--repo-id <id> repository id (default: git config toon.repoid)
|
|
3667
|
+
--owner <pubkey> repository owner (default: git config toon.owner)`;
|
|
3668
|
+
function parseMergeArgs(args) {
|
|
3669
|
+
const { values, positionals } = parseArgs9({
|
|
3670
|
+
args,
|
|
3671
|
+
options: {
|
|
3672
|
+
target: { type: "string" },
|
|
3673
|
+
yes: { type: "boolean", default: false },
|
|
3674
|
+
json: { type: "boolean", default: false },
|
|
3675
|
+
relay: { type: "string", multiple: true },
|
|
3676
|
+
remote: { type: "string" },
|
|
3677
|
+
"repo-id": { type: "string" },
|
|
3678
|
+
owner: { type: "string" },
|
|
3679
|
+
"require-ci-trust": { type: "string" },
|
|
3680
|
+
closes: { type: "string", multiple: true },
|
|
3681
|
+
standalone: { type: "boolean", default: false },
|
|
3682
|
+
"no-daemon": { type: "boolean", default: false },
|
|
3683
|
+
help: { type: "boolean", short: "h", default: false }
|
|
3684
|
+
},
|
|
3685
|
+
allowPositionals: true
|
|
3686
|
+
});
|
|
3687
|
+
if (values.help) return "help";
|
|
3688
|
+
if (positionals.length !== 1) {
|
|
3689
|
+
throw new Error("expected exactly one argument: <patch-id>");
|
|
3690
|
+
}
|
|
3691
|
+
const patchId = positionals[0];
|
|
3692
|
+
if (!HEX64_RE3.test(patchId)) {
|
|
3693
|
+
throw new Error("<patch-id> must be a 64-character lowercase hex event id");
|
|
3694
|
+
}
|
|
3695
|
+
if (values.owner !== void 0 && !HEX64_RE3.test(values.owner)) {
|
|
3696
|
+
throw new Error("--owner must be a 64-character lowercase hex pubkey");
|
|
3697
|
+
}
|
|
3698
|
+
const flags = {
|
|
3699
|
+
patchId,
|
|
3700
|
+
yes: values.yes === true,
|
|
3701
|
+
json: values.json === true,
|
|
3702
|
+
relay: values.relay ?? [],
|
|
3703
|
+
closes: [...new Set(values.closes ?? [])]
|
|
3704
|
+
};
|
|
3705
|
+
for (const id of flags.closes) {
|
|
3706
|
+
if (!HEX64_RE3.test(id)) {
|
|
3707
|
+
throw new Error("--closes must be a 64-character lowercase hex issue id");
|
|
3708
|
+
}
|
|
3709
|
+
}
|
|
3710
|
+
const level = values["require-ci-trust"];
|
|
3711
|
+
if (level !== void 0) {
|
|
3712
|
+
if (!isCiTrustLevel(level)) {
|
|
3713
|
+
throw new Error(
|
|
3714
|
+
`--require-ci-trust must be one of ${CI_TRUST_ORDER.join(" | ")} (got ${JSON.stringify(level)})`
|
|
3715
|
+
);
|
|
3716
|
+
}
|
|
3717
|
+
flags.requireTrust = level;
|
|
3718
|
+
}
|
|
3719
|
+
if (values.target !== void 0) flags.target = values.target;
|
|
3720
|
+
if (values.remote !== void 0) flags.remote = values.remote;
|
|
3721
|
+
if (values["repo-id"] !== void 0) flags.repoId = values["repo-id"];
|
|
3722
|
+
if (values.owner !== void 0) flags.owner = values.owner;
|
|
3723
|
+
return flags;
|
|
3724
|
+
}
|
|
3725
|
+
async function git(cwd, args, env) {
|
|
3726
|
+
const { stdout } = await execFileAsync("git", args, {
|
|
3727
|
+
cwd,
|
|
3728
|
+
encoding: "utf-8",
|
|
3729
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
3730
|
+
...env ? { env } : {}
|
|
3731
|
+
});
|
|
3732
|
+
return stdout.trim();
|
|
3733
|
+
}
|
|
3734
|
+
async function gitOk(cwd, args) {
|
|
3735
|
+
try {
|
|
3736
|
+
await git(cwd, args);
|
|
3737
|
+
return true;
|
|
3738
|
+
} catch {
|
|
3739
|
+
return false;
|
|
3740
|
+
}
|
|
3741
|
+
}
|
|
3742
|
+
function gitFailure(err) {
|
|
3743
|
+
const e = err;
|
|
3744
|
+
return (e.stderr || e.stdout || e.message || String(err)).trim();
|
|
3745
|
+
}
|
|
3746
|
+
var OverlayReader = class extends GitRepoReader {
|
|
3747
|
+
constructor(repoPath, overlay) {
|
|
3748
|
+
super(repoPath);
|
|
3749
|
+
this.overlay = overlay;
|
|
3750
|
+
}
|
|
3751
|
+
overlay;
|
|
3752
|
+
async listRefs() {
|
|
3753
|
+
const base = await super.listRefs();
|
|
3754
|
+
const refs = base.refs.filter(
|
|
3755
|
+
(r) => r.refname !== this.overlay.refname
|
|
3756
|
+
);
|
|
3757
|
+
refs.push({
|
|
3758
|
+
refname: this.overlay.refname,
|
|
3759
|
+
sha: this.overlay.sha,
|
|
3760
|
+
type: "commit"
|
|
3761
|
+
});
|
|
3762
|
+
return { ...base, refs };
|
|
3763
|
+
}
|
|
3764
|
+
};
|
|
3765
|
+
async function applyInIsolation(repoRoot, tip, patchText, committer) {
|
|
3766
|
+
const scratch = await mkdtemp2(join4(tmpdir(), "rig-merge-"));
|
|
3767
|
+
const worktree = join4(scratch, "wt");
|
|
3768
|
+
try {
|
|
3769
|
+
await git(repoRoot, ["worktree", "add", "--detach", worktree, tip]);
|
|
3770
|
+
const patchFile = join4(scratch, "patch.mbox");
|
|
3771
|
+
await writeFile(patchFile, patchText, "utf-8");
|
|
3772
|
+
const env = { ...process.env };
|
|
3773
|
+
if (committer) {
|
|
3774
|
+
env["GIT_COMMITTER_NAME"] = committer.name;
|
|
3775
|
+
env["GIT_COMMITTER_EMAIL"] = committer.email;
|
|
3776
|
+
}
|
|
3777
|
+
try {
|
|
3778
|
+
await git(worktree, ["am", "--quiet", patchFile], env);
|
|
3779
|
+
} catch (err) {
|
|
3780
|
+
await git(worktree, ["am", "--abort"]).catch(() => void 0);
|
|
3781
|
+
throw new PrMergeError(
|
|
3782
|
+
"apply_conflict",
|
|
3783
|
+
`the patch does not apply cleanly onto ${tip.slice(0, 7)} \u2014 ask the contributor for a revision (\`rig pr create --revises\`). Nothing was pushed, published, or paid, and your working tree is unchanged.
|
|
3784
|
+
` + gitFailure(err)
|
|
3785
|
+
);
|
|
3786
|
+
}
|
|
3787
|
+
const newHead = await git(worktree, ["rev-parse", "HEAD"]);
|
|
3788
|
+
const listed = await git(worktree, [
|
|
3789
|
+
"rev-list",
|
|
3790
|
+
"--reverse",
|
|
3791
|
+
`${tip}..${newHead}`
|
|
3792
|
+
]);
|
|
3793
|
+
const commits = listed === "" ? [] : listed.split("\n");
|
|
3794
|
+
if (commits.length === 0) {
|
|
3795
|
+
throw new PrMergeError(
|
|
3796
|
+
"nothing_to_apply",
|
|
3797
|
+
"the patch applied no commits (already merged?) \u2014 nothing was pushed, published, or paid."
|
|
3798
|
+
);
|
|
3799
|
+
}
|
|
3800
|
+
return { newHead, commits };
|
|
3801
|
+
} finally {
|
|
3802
|
+
await git(repoRoot, ["worktree", "remove", "--force", worktree]).catch(
|
|
3803
|
+
() => void 0
|
|
3804
|
+
);
|
|
3805
|
+
await git(repoRoot, ["worktree", "prune"]).catch(() => void 0);
|
|
3806
|
+
await rm2(scratch, { recursive: true, force: true });
|
|
3807
|
+
}
|
|
3808
|
+
}
|
|
3809
|
+
var BRANCH_PREFIX = "refs/heads/";
|
|
3810
|
+
async function runPrMerge(args, deps) {
|
|
3811
|
+
const { io } = deps;
|
|
3812
|
+
let flags;
|
|
3813
|
+
try {
|
|
3814
|
+
const parsed = parseMergeArgs(args);
|
|
3815
|
+
if (parsed === "help") {
|
|
3816
|
+
io.out(PR_MERGE_USAGE);
|
|
3817
|
+
return 0;
|
|
3818
|
+
}
|
|
3819
|
+
flags = parsed;
|
|
3820
|
+
} catch (err) {
|
|
3821
|
+
io.err(err instanceof Error ? err.message : String(err));
|
|
3822
|
+
io.err(PR_MERGE_USAGE);
|
|
3823
|
+
return 2;
|
|
3824
|
+
}
|
|
3825
|
+
deps = withForcedStandalone(deps);
|
|
3826
|
+
const webSocketFactory = deps.webSocketFactory ?? defaultWebSocketFactory;
|
|
3827
|
+
let stop;
|
|
3828
|
+
try {
|
|
3829
|
+
const repoRoot = await resolveRepoRoot(deps.cwd);
|
|
3830
|
+
const toonConfig = await readToonConfig(repoRoot);
|
|
3831
|
+
const repoId = flags.repoId ?? toonConfig.repoId;
|
|
3832
|
+
if (!repoId) throw new UnconfiguredRepoAddressError("repository id");
|
|
3833
|
+
const resolved = await resolveRelays({
|
|
3834
|
+
relayFlags: flags.relay,
|
|
3835
|
+
remoteName: flags.remote,
|
|
3836
|
+
repoRoot,
|
|
3837
|
+
toonRelays: toonConfig.relays
|
|
3838
|
+
});
|
|
3839
|
+
if (resolved.nudge !== void 0) io.err(resolved.nudge);
|
|
3840
|
+
const relays = resolved.relays;
|
|
3841
|
+
if (relays.length > 1) {
|
|
3842
|
+
io.err(singleRelayRefusal(resolved, "Nothing was pushed or paid."));
|
|
3843
|
+
return 1;
|
|
3844
|
+
}
|
|
3845
|
+
const relay = relays[0];
|
|
3846
|
+
const session = await loadPaidSession(deps, relay);
|
|
3847
|
+
if (session.path !== "standalone") {
|
|
3848
|
+
throw new Error("internal: rig pr merge requires the standalone session");
|
|
3849
|
+
}
|
|
3850
|
+
const ctx = session.ctx;
|
|
3851
|
+
stop = () => ctx.stop();
|
|
3852
|
+
const identity = identityReport(ctx);
|
|
3853
|
+
const owner = flags.owner ?? toonConfig.owner ?? identity.pubkey;
|
|
3854
|
+
const addr = { ownerPubkey: owner, repoId };
|
|
3855
|
+
const aTag = `${REPOSITORY_ANNOUNCEMENT_KIND2}:${owner}:${repoId}`;
|
|
3856
|
+
const lookup = async (id) => (await queryAll(relays, [{ ids: [id] }], webSocketFactory)).get(id);
|
|
3857
|
+
let root = await lookup(flags.patchId);
|
|
3858
|
+
if (root && root.kind === PATCH_KIND2 && isPatchRevision(root)) {
|
|
3859
|
+
const rootId = revisionRootId(root);
|
|
3860
|
+
if (rootId !== void 0) root = await lookup(rootId);
|
|
3861
|
+
}
|
|
3862
|
+
if (!root || root.kind !== PATCH_KIND2) {
|
|
3863
|
+
throw new PrMergeError(
|
|
3864
|
+
"patch_not_found",
|
|
3865
|
+
`patch ${flags.patchId} not found as a kind:1617 patch on ${relay} \u2014 nothing was pushed or paid`
|
|
3866
|
+
);
|
|
3867
|
+
}
|
|
3868
|
+
if (!root.tags.some((t) => t[0] === "a" && t[1] === aTag)) {
|
|
3869
|
+
throw new PrMergeError(
|
|
3870
|
+
"patch_not_found",
|
|
3871
|
+
`patch ${root.id} does not belong to ${aTag} \u2014 nothing was pushed or paid`
|
|
3872
|
+
);
|
|
3873
|
+
}
|
|
3874
|
+
const remote = await ctx.fetchRemote({
|
|
3875
|
+
ownerPubkey: owner,
|
|
3876
|
+
repoId,
|
|
3877
|
+
relayUrls: relays
|
|
3878
|
+
});
|
|
3879
|
+
const authorized = authorizedAuthors(
|
|
3880
|
+
owner,
|
|
3881
|
+
remote.announceEvent?.tags ?? []
|
|
3882
|
+
);
|
|
3883
|
+
const revisionEvents = await queryAll(
|
|
3884
|
+
relays,
|
|
3885
|
+
[{ kinds: [PATCH_KIND2], "#e": [root.id] }],
|
|
3886
|
+
webSocketFactory
|
|
3887
|
+
);
|
|
3888
|
+
const { latest } = selectRevision(
|
|
3889
|
+
root,
|
|
3890
|
+
[...revisionEvents.values()].filter((e) => e.kind === PATCH_KIND2),
|
|
3891
|
+
authorized
|
|
3892
|
+
);
|
|
3893
|
+
if (!authorized.has(identity.pubkey.toLowerCase())) {
|
|
3894
|
+
throw new PrMergeError(
|
|
3895
|
+
"not_authorized",
|
|
3896
|
+
`only the owner or a declared maintainer can merge \u2014 ${identity.pubkey.slice(0, 8)}\u2026 is neither for ${aTag}. Readers would ignore the pushed state. Nothing was pushed or paid.`
|
|
3897
|
+
);
|
|
3898
|
+
}
|
|
3899
|
+
if (!remote.announced) {
|
|
3900
|
+
throw new PrMergeError(
|
|
3901
|
+
"target_not_found",
|
|
3902
|
+
`${aTag} is not announced on ${relay} \u2014 nothing to merge onto`
|
|
3903
|
+
);
|
|
3904
|
+
}
|
|
3905
|
+
const branch = (flags.target ?? firstTagValue(latest.tags, "branch-name") ?? remote.headSymref ?? "main").replace(/^refs\/heads\//, "");
|
|
3906
|
+
const refname = `${BRANCH_PREFIX}${branch}`;
|
|
3907
|
+
const tip = remote.refs.get(refname);
|
|
3908
|
+
if (tip === void 0) {
|
|
3909
|
+
throw new PrMergeError(
|
|
3910
|
+
"target_not_found",
|
|
3911
|
+
`branch ${JSON.stringify(branch)} is not in the honored repo state (${[...remote.refs.keys()].join(", ") || "no refs"}) \u2014 pass --target. Nothing was pushed or paid.`
|
|
3912
|
+
);
|
|
3913
|
+
}
|
|
3914
|
+
if (!await gitOk(repoRoot, ["cat-file", "-e", `${tip}^{commit}`])) {
|
|
3915
|
+
await runFetch(
|
|
3916
|
+
["--relay", relay, "--owner", owner, "--repo-id", repoId],
|
|
3917
|
+
{ ...deps, io: { ...io, out: () => void 0 } }
|
|
3918
|
+
);
|
|
3919
|
+
if (!await gitOk(repoRoot, ["cat-file", "-e", `${tip}^{commit}`])) {
|
|
3920
|
+
throw new PrMergeError(
|
|
3921
|
+
"tip_unavailable",
|
|
3922
|
+
`the honored tip ${tip.slice(0, 7)} of ${branch} could not be fetched \u2014 run \`rig fetch\` and retry. Nothing was pushed or paid.`
|
|
3923
|
+
);
|
|
3924
|
+
}
|
|
3925
|
+
}
|
|
3926
|
+
const reader = new GitRepoReader(repoRoot);
|
|
3927
|
+
const localRefs = (await reader.listRefs()).refs;
|
|
3928
|
+
const localSha = localRefs.find((r) => r.refname === refname)?.sha;
|
|
3929
|
+
if (localSha !== void 0 && localSha !== tip && !await reader.isAncestor(localSha, tip)) {
|
|
3930
|
+
throw new PrMergeError(
|
|
3931
|
+
"local_branch_diverged",
|
|
3932
|
+
`local ${branch} (${localSha.slice(0, 7)}) is not behind the honored tip (${tip.slice(0, 7)}) \u2014 push or reset it first. Nothing was pushed or paid.`
|
|
3933
|
+
);
|
|
3934
|
+
}
|
|
3935
|
+
const hasCommitter = await gitOk(repoRoot, ["config", "user.email"]);
|
|
3936
|
+
const applied = await applyInIsolation(
|
|
3937
|
+
repoRoot,
|
|
3938
|
+
tip,
|
|
3939
|
+
latest.content,
|
|
3940
|
+
hasCommitter ? void 0 : {
|
|
3941
|
+
name: hexToNpub(identity.pubkey),
|
|
3942
|
+
email: `${hexToNpub(identity.pubkey)}@nostr`
|
|
3943
|
+
}
|
|
3944
|
+
);
|
|
3945
|
+
if (flags.requireTrust !== void 0) {
|
|
3946
|
+
const headCommit = (latest.tags.filter((t) => t[0] === "commit").at(-1)?.[1] ?? applied.newHead).toLowerCase();
|
|
3947
|
+
const gate = await evaluateCiGate({
|
|
3948
|
+
relays,
|
|
3949
|
+
owner,
|
|
3950
|
+
repoId,
|
|
3951
|
+
commit: headCommit,
|
|
3952
|
+
webSocketFactory,
|
|
3953
|
+
requireTrust: flags.requireTrust
|
|
3954
|
+
});
|
|
3955
|
+
if (!gate.ok) {
|
|
3956
|
+
throw new PrMergeError(
|
|
3957
|
+
"ci_gate_failed",
|
|
3958
|
+
`no qualifying green CI run (trust >= ${flags.requireTrust}) for ${headCommit.slice(0, 12)}: ${gate.reason ?? "not green"}. Nothing was pushed, published, or paid.`
|
|
3959
|
+
);
|
|
3960
|
+
}
|
|
3961
|
+
}
|
|
3962
|
+
const overlay = new OverlayReader(repoRoot, {
|
|
3963
|
+
refname,
|
|
3964
|
+
sha: applied.newHead
|
|
3965
|
+
});
|
|
3966
|
+
const feeRates = await ctx.publisher.getFeeRates();
|
|
3967
|
+
const pushPlan = await planPush({
|
|
3968
|
+
repoReader: overlay,
|
|
3969
|
+
remoteState: remote,
|
|
3970
|
+
feeRates,
|
|
3971
|
+
repoId,
|
|
3972
|
+
refs: [refname],
|
|
3973
|
+
force: false
|
|
3974
|
+
});
|
|
3975
|
+
const plan = serializePushPlan(pushPlan);
|
|
3976
|
+
const total = BigInt(plan.estimate.totalFee) + feeRates.eventFee * BigInt(1 + flags.closes.length);
|
|
3977
|
+
if (!flags.json) {
|
|
3978
|
+
for (const line of renderPlan(plan)) io.out(line);
|
|
3979
|
+
io.out(
|
|
3980
|
+
` status kind:1631 applied (${applied.commits.length} commit${applied.commits.length === 1 ? "" : "s"}) ${feeRates.eventFee}`
|
|
3981
|
+
);
|
|
3982
|
+
if (flags.closes.length > 0) {
|
|
3983
|
+
io.out(
|
|
3984
|
+
` closes kind:1632 closed (${flags.closes.length} issue${flags.closes.length === 1 ? "" : "s"}) ${feeRates.eventFee * BigInt(flags.closes.length)}`
|
|
3985
|
+
);
|
|
3986
|
+
}
|
|
3987
|
+
io.out(` total with status ${total}`);
|
|
3988
|
+
io.out(renderIdentityLine(identity));
|
|
3989
|
+
}
|
|
3990
|
+
if (!flags.yes) {
|
|
3991
|
+
if (flags.json) {
|
|
3992
|
+
io.emitJson({
|
|
3993
|
+
command: "pr merge",
|
|
3994
|
+
repoAddr: addr,
|
|
3995
|
+
identity,
|
|
3996
|
+
executed: false,
|
|
3997
|
+
patchId: root.id,
|
|
3998
|
+
revisionId: latest.id,
|
|
3999
|
+
target: refname,
|
|
4000
|
+
mergedShas: applied.commits,
|
|
4001
|
+
plan,
|
|
4002
|
+
totalFee: total.toString(),
|
|
4003
|
+
hint: "estimate only \u2014 re-run with --yes to push and publish (permanent, non-refundable)"
|
|
4004
|
+
});
|
|
4005
|
+
return 0;
|
|
4006
|
+
}
|
|
4007
|
+
if (!io.isInteractive) {
|
|
4008
|
+
io.err(
|
|
4009
|
+
"refusing to spend channel funds without confirmation in a non-interactive session \u2014 re-run with --yes (or use --json for an estimate)"
|
|
4010
|
+
);
|
|
4011
|
+
return 1;
|
|
4012
|
+
}
|
|
4013
|
+
const proceed = await io.confirm(
|
|
4014
|
+
`Proceed with paid merge (total ${total} base units)? [y/N] `
|
|
4015
|
+
);
|
|
4016
|
+
if (!proceed) {
|
|
4017
|
+
io.err("aborted \u2014 nothing was pushed or published.");
|
|
4018
|
+
return 1;
|
|
4019
|
+
}
|
|
4020
|
+
}
|
|
4021
|
+
const pushResult = await executePush({
|
|
4022
|
+
plan: pushPlan,
|
|
4023
|
+
publisher: ctx.publisher,
|
|
4024
|
+
remoteState: remote,
|
|
4025
|
+
repoReader: overlay,
|
|
4026
|
+
relayUrls: relays
|
|
4027
|
+
});
|
|
4028
|
+
const result = serializePushResult(pushPlan, pushResult);
|
|
4029
|
+
const localNote = await advanceLocalBranch(
|
|
4030
|
+
repoRoot,
|
|
4031
|
+
reader,
|
|
4032
|
+
refname,
|
|
4033
|
+
applied.newHead,
|
|
4034
|
+
localSha
|
|
4035
|
+
);
|
|
4036
|
+
if (localNote !== void 0) io.err(localNote);
|
|
4037
|
+
let statusEventId;
|
|
4038
|
+
let statusError;
|
|
4039
|
+
try {
|
|
4040
|
+
const receipt = await ctx.publisher.publishEvent(
|
|
4041
|
+
buildStatus(owner, repoId, root.id, STATUS_APPLIED_KIND2, root.pubkey, {
|
|
4042
|
+
commits: applied.commits,
|
|
4043
|
+
revisionEventId: latest.id
|
|
4044
|
+
}),
|
|
4045
|
+
relays
|
|
4046
|
+
);
|
|
4047
|
+
statusEventId = receipt.eventId;
|
|
4048
|
+
} catch (err) {
|
|
4049
|
+
statusError = err instanceof Error ? err.message : String(err);
|
|
4050
|
+
}
|
|
4051
|
+
const closed = [];
|
|
4052
|
+
const closeErrors = [];
|
|
4053
|
+
for (const issueId of flags.closes) {
|
|
4054
|
+
try {
|
|
4055
|
+
await ctx.publisher.publishEvent(
|
|
4056
|
+
buildStatus(owner, repoId, issueId, STATUS_CLOSED_KIND2),
|
|
4057
|
+
relays
|
|
4058
|
+
);
|
|
4059
|
+
closed.push(issueId);
|
|
4060
|
+
} catch (err) {
|
|
4061
|
+
closeErrors.push({
|
|
4062
|
+
issueId,
|
|
4063
|
+
error: err instanceof Error ? err.message : String(err)
|
|
4064
|
+
});
|
|
4065
|
+
}
|
|
4066
|
+
}
|
|
4067
|
+
const closeRetryHints = closeErrors.map(
|
|
4068
|
+
(e) => `rig issue close ${e.issueId}`
|
|
4069
|
+
);
|
|
4070
|
+
const retryHint = `rig pr status ${root.id} applied`;
|
|
4071
|
+
if (flags.json) {
|
|
4072
|
+
io.emitJson({
|
|
4073
|
+
command: "pr merge",
|
|
4074
|
+
repoAddr: addr,
|
|
4075
|
+
identity,
|
|
4076
|
+
executed: true,
|
|
4077
|
+
patchId: root.id,
|
|
4078
|
+
revisionId: latest.id,
|
|
4079
|
+
target: refname,
|
|
4080
|
+
mergedShas: applied.commits,
|
|
4081
|
+
closed,
|
|
4082
|
+
...closeErrors.length > 0 ? { closeErrors } : {},
|
|
4083
|
+
result,
|
|
4084
|
+
...statusEventId !== void 0 ? { statusEventId } : {},
|
|
4085
|
+
...statusError !== void 0 ? {
|
|
4086
|
+
partial: true,
|
|
4087
|
+
error: "status_publish_failed",
|
|
4088
|
+
detail: statusError,
|
|
4089
|
+
hint: `the push succeeded but the applied status was not published \u2014 retry with \`${retryHint}\``
|
|
4090
|
+
} : closeErrors.length > 0 ? {
|
|
4091
|
+
partial: true,
|
|
4092
|
+
error: "close_publish_failed",
|
|
4093
|
+
detail: closeErrors.map((e) => `${e.issueId}: ${e.error}`).join("; "),
|
|
4094
|
+
hint: `the push succeeded but some issues were not closed \u2014 retry with ${closeRetryHints.map((h) => `\`${h}\``).join(", ")}`
|
|
4095
|
+
} : {}
|
|
4096
|
+
});
|
|
4097
|
+
} else {
|
|
4098
|
+
for (const line of renderResult(result)) io.out(line);
|
|
4099
|
+
if (statusEventId !== void 0) {
|
|
4100
|
+
io.out(
|
|
4101
|
+
`Merged ${applied.commits.length} commit(s) onto ${branch}; applied status ${statusEventId}`
|
|
4102
|
+
);
|
|
4103
|
+
}
|
|
4104
|
+
}
|
|
4105
|
+
if (!flags.json && closed.length > 0) {
|
|
4106
|
+
io.out(`Closed ${closed.length} issue(s): ${closed.join(", ")}`);
|
|
4107
|
+
}
|
|
4108
|
+
if (statusError !== void 0) {
|
|
4109
|
+
io.err(
|
|
4110
|
+
`partial success: the push succeeded but publishing the applied status failed: ${statusError}`
|
|
4111
|
+
);
|
|
4112
|
+
io.err(`retry the status with: ${retryHint}`);
|
|
4113
|
+
}
|
|
4114
|
+
if (closeErrors.length > 0) {
|
|
4115
|
+
for (const e of closeErrors) {
|
|
4116
|
+
io.err(
|
|
4117
|
+
`partial success: the push succeeded but closing issue ${e.issueId} failed: ${e.error}`
|
|
4118
|
+
);
|
|
4119
|
+
}
|
|
4120
|
+
for (const h of closeRetryHints) io.err(`retry the close with: ${h}`);
|
|
4121
|
+
}
|
|
4122
|
+
return statusError !== void 0 || closeErrors.length > 0 ? 1 : 0;
|
|
4123
|
+
} catch (err) {
|
|
4124
|
+
return emitCliError(io, flags.json, "pr merge", err);
|
|
4125
|
+
} finally {
|
|
4126
|
+
if (stop) await stop().catch(() => void 0);
|
|
4127
|
+
}
|
|
4128
|
+
}
|
|
4129
|
+
async function advanceLocalBranch(repoRoot, reader, refname, newHead, oldSha) {
|
|
4130
|
+
try {
|
|
4131
|
+
const { head } = await reader.listRefs();
|
|
4132
|
+
if (head === refname) {
|
|
4133
|
+
await git(repoRoot, ["merge", "--ff-only", newHead]);
|
|
4134
|
+
} else {
|
|
4135
|
+
await git(repoRoot, [
|
|
4136
|
+
"update-ref",
|
|
4137
|
+
refname,
|
|
4138
|
+
newHead,
|
|
4139
|
+
oldSha ?? "0".repeat(40)
|
|
4140
|
+
]);
|
|
4141
|
+
}
|
|
4142
|
+
return void 0;
|
|
4143
|
+
} catch (err) {
|
|
4144
|
+
return `warning: the push succeeded but local ${refname} could not be moved to ${newHead.slice(0, 7)}: ${gitFailure(err)} \u2014 run \`rig fetch\``;
|
|
4145
|
+
}
|
|
3392
4146
|
}
|
|
3393
4147
|
|
|
3394
4148
|
// src/cli/events.ts
|
|
@@ -3512,14 +4266,16 @@ Options:
|
|
|
3512
4266
|
${COMMON_FLAGS_USAGE2}`;
|
|
3513
4267
|
var PR_USAGE = `${PR_CREATE_USAGE}
|
|
3514
4268
|
|
|
4269
|
+
${PR_MERGE_USAGE}
|
|
4270
|
+
|
|
3515
4271
|
${PR_STATUS_USAGE}
|
|
3516
4272
|
|
|
3517
4273
|
${PR_LIST_USAGE}
|
|
3518
4274
|
|
|
3519
4275
|
${PR_SHOW_USAGE}`;
|
|
3520
|
-
var
|
|
4276
|
+
var HEX64_RE4 = /^[0-9a-f]{64}$/;
|
|
3521
4277
|
function assertHex64(value, what) {
|
|
3522
|
-
if (!
|
|
4278
|
+
if (!HEX64_RE4.test(value)) {
|
|
3523
4279
|
throw new Error(
|
|
3524
4280
|
`${what} must be a 64-char lowercase hex id (got ${JSON.stringify(value)})`
|
|
3525
4281
|
);
|
|
@@ -3716,7 +4472,7 @@ async function runIssue(args, deps) {
|
|
|
3716
4472
|
let bodyFile;
|
|
3717
4473
|
let labels;
|
|
3718
4474
|
try {
|
|
3719
|
-
const { values } =
|
|
4475
|
+
const { values } = parseArgs10({
|
|
3720
4476
|
args: rest,
|
|
3721
4477
|
options: {
|
|
3722
4478
|
...COMMON_OPTIONS2,
|
|
@@ -3812,7 +4568,7 @@ async function resolveCommentTarget(deps, targetEventId, relayUrl) {
|
|
|
3812
4568
|
const rootEventId = firstTagValue(target.tags, "E");
|
|
3813
4569
|
const rootKind = Number(firstTagValue(target.tags, "K"));
|
|
3814
4570
|
const rootAuthor = firstTagValue(target.tags, "P");
|
|
3815
|
-
if (rootEventId === void 0 || !
|
|
4571
|
+
if (rootEventId === void 0 || !HEX64_RE4.test(rootEventId) || !Number.isSafeInteger(rootKind) || rootAuthor === void 0 || !HEX64_RE4.test(rootAuthor)) {
|
|
3816
4572
|
throw new Error(
|
|
3817
4573
|
`kind:1111 comment ${targetEventId} carries no usable NIP-22 root scope (E/K/P tags) \u2014 cannot derive the thread root to reply into`
|
|
3818
4574
|
);
|
|
@@ -3836,7 +4592,7 @@ async function runComment(args, deps) {
|
|
|
3836
4592
|
let targetEventId;
|
|
3837
4593
|
let body;
|
|
3838
4594
|
try {
|
|
3839
|
-
const { values, positionals } =
|
|
4595
|
+
const { values, positionals } = parseArgs10({
|
|
3840
4596
|
args,
|
|
3841
4597
|
options: {
|
|
3842
4598
|
...COMMON_OPTIONS2,
|
|
@@ -3917,6 +4673,8 @@ async function runPr(args, deps) {
|
|
|
3917
4673
|
switch (sub) {
|
|
3918
4674
|
case "create":
|
|
3919
4675
|
return runPrCreate(rest, deps);
|
|
4676
|
+
case "merge":
|
|
4677
|
+
return runPrMerge(rest, deps);
|
|
3920
4678
|
case "status":
|
|
3921
4679
|
return runStatusCommand(rest, deps, PR_STATUS_SPEC);
|
|
3922
4680
|
// FREE read subcommands (#278): relay queries only, no payment path.
|
|
@@ -3931,7 +4689,7 @@ async function runPr(args, deps) {
|
|
|
3931
4689
|
return 0;
|
|
3932
4690
|
default:
|
|
3933
4691
|
io.err(
|
|
3934
|
-
sub === void 0 ? "missing subcommand: rig pr <create|status|list|show>" : `unknown rig pr subcommand: ${sub}`
|
|
4692
|
+
sub === void 0 ? "missing subcommand: rig pr <create|merge|status|list|show>" : `unknown rig pr subcommand: ${sub}`
|
|
3935
4693
|
);
|
|
3936
4694
|
io.err(PR_USAGE);
|
|
3937
4695
|
return 2;
|
|
@@ -3955,12 +4713,12 @@ async function resolveRevisedRoot(deps, rootId, relayUrl, addr) {
|
|
|
3955
4713
|
`patch ${rootId} not found on ${relayUrl} \u2014 nothing was published or paid`
|
|
3956
4714
|
);
|
|
3957
4715
|
}
|
|
3958
|
-
if (root.kind !==
|
|
4716
|
+
if (root.kind !== PATCH_KIND3) {
|
|
3959
4717
|
throw new Error(
|
|
3960
4718
|
`event ${rootId} has kind ${root.kind}, not a kind:1617 patch \u2014 nothing was published or paid`
|
|
3961
4719
|
);
|
|
3962
4720
|
}
|
|
3963
|
-
const aTag = `${
|
|
4721
|
+
const aTag = `${REPOSITORY_ANNOUNCEMENT_KIND3}:${addr.ownerPubkey}:${addr.repoId}`;
|
|
3964
4722
|
if (!root.tags.some((t) => t[0] === "a" && t[1] === aTag)) {
|
|
3965
4723
|
throw new Error(
|
|
3966
4724
|
`patch ${rootId} belongs to a different repository than ${aTag} \u2014 nothing was published or paid`
|
|
@@ -4011,7 +4769,7 @@ async function runPrCreate(rest, deps) {
|
|
|
4011
4769
|
let bodyFile;
|
|
4012
4770
|
let revises;
|
|
4013
4771
|
try {
|
|
4014
|
-
const { values } =
|
|
4772
|
+
const { values } = parseArgs10({
|
|
4015
4773
|
args: rest,
|
|
4016
4774
|
options: {
|
|
4017
4775
|
...COMMON_OPTIONS2,
|
|
@@ -4145,8 +4903,8 @@ async function runPrCreate(rest, deps) {
|
|
|
4145
4903
|
}
|
|
4146
4904
|
var STATUS_KIND_BY_VALUE = {
|
|
4147
4905
|
open: STATUS_OPEN_KIND2,
|
|
4148
|
-
applied:
|
|
4149
|
-
closed:
|
|
4906
|
+
applied: STATUS_APPLIED_KIND3,
|
|
4907
|
+
closed: STATUS_CLOSED_KIND3,
|
|
4150
4908
|
draft: STATUS_DRAFT_KIND2
|
|
4151
4909
|
};
|
|
4152
4910
|
function isStatusValue(value) {
|
|
@@ -4172,7 +4930,7 @@ async function runStatusCommand(args, deps, spec, fixedStatus) {
|
|
|
4172
4930
|
let targetEventId;
|
|
4173
4931
|
let status;
|
|
4174
4932
|
try {
|
|
4175
|
-
const { values, positionals } =
|
|
4933
|
+
const { values, positionals } = parseArgs10({
|
|
4176
4934
|
args,
|
|
4177
4935
|
options: COMMON_OPTIONS2,
|
|
4178
4936
|
allowPositionals: true
|
|
@@ -4258,8 +5016,8 @@ async function warnIfNotMaintainer(deps, args) {
|
|
|
4258
5016
|
// src/cli/entry.ts
|
|
4259
5017
|
import { mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
4260
5018
|
import { homedir as homedir3 } from "os";
|
|
4261
|
-
import { dirname as dirname4, join as
|
|
4262
|
-
import { parseArgs as
|
|
5019
|
+
import { dirname as dirname4, join as join5 } from "path";
|
|
5020
|
+
import { parseArgs as parseArgs11 } from "util";
|
|
4263
5021
|
var ENTRY_USAGE = `Usage: rig entry [<connector-url> | clear] [options]
|
|
4264
5022
|
|
|
4265
5023
|
Name the TOON connector rig pays. A connector describes itself on GET /ilp
|
|
@@ -4283,8 +5041,8 @@ Options:
|
|
|
4283
5041
|
--json machine-readable envelope
|
|
4284
5042
|
-h, --help show this help`;
|
|
4285
5043
|
function configPathFor2(env) {
|
|
4286
|
-
const dir = env["TOON_CLIENT_HOME"] ??
|
|
4287
|
-
return
|
|
5044
|
+
const dir = env["TOON_CLIENT_HOME"] ?? join5(homedir3(), ".toon-client");
|
|
5045
|
+
return join5(dir, "config.json");
|
|
4288
5046
|
}
|
|
4289
5047
|
function readEntryConfig(configPath) {
|
|
4290
5048
|
try {
|
|
@@ -4329,381 +5087,155 @@ async function runEntry(args, deps) {
|
|
|
4329
5087
|
let relayFlag;
|
|
4330
5088
|
let json = false;
|
|
4331
5089
|
try {
|
|
4332
|
-
const parsed =
|
|
5090
|
+
const parsed = parseArgs11({
|
|
4333
5091
|
args,
|
|
4334
5092
|
allowPositionals: true,
|
|
4335
5093
|
options: {
|
|
4336
|
-
relay: { type: "string" },
|
|
4337
|
-
json: { type: "boolean", default: false },
|
|
4338
|
-
help: { type: "boolean", short: "h", default: false }
|
|
4339
|
-
}
|
|
4340
|
-
});
|
|
4341
|
-
if (parsed.values.help) {
|
|
4342
|
-
io.out(ENTRY_USAGE);
|
|
4343
|
-
return 0;
|
|
4344
|
-
}
|
|
4345
|
-
positionals = parsed.positionals;
|
|
4346
|
-
relayFlag = parsed.values.relay;
|
|
4347
|
-
json = parsed.values.json ?? false;
|
|
4348
|
-
} catch (err) {
|
|
4349
|
-
io.err(err instanceof Error ? err.message : String(err));
|
|
4350
|
-
io.err(ENTRY_USAGE);
|
|
4351
|
-
return 2;
|
|
4352
|
-
}
|
|
4353
|
-
if (positionals.length > 1) {
|
|
4354
|
-
io.err(
|
|
4355
|
-
`rig entry takes at most one argument, got ${String(positionals.length)}: ${positionals.join(" ")}`
|
|
4356
|
-
);
|
|
4357
|
-
io.err(ENTRY_USAGE);
|
|
4358
|
-
return 2;
|
|
4359
|
-
}
|
|
4360
|
-
const target = positionals[0];
|
|
4361
|
-
let mutation;
|
|
4362
|
-
if (target !== void 0) {
|
|
4363
|
-
if (target === "clear") {
|
|
4364
|
-
if (relayFlag !== void 0) {
|
|
4365
|
-
io.err("--relay only applies to a <connector-url> entry");
|
|
4366
|
-
io.err(ENTRY_USAGE);
|
|
4367
|
-
return 2;
|
|
4368
|
-
}
|
|
4369
|
-
mutation = { kind: "clear" };
|
|
4370
|
-
} else if (HTTP_URL.test(target)) {
|
|
4371
|
-
if (relayFlag !== void 0 && !WS_URL.test(relayFlag)) {
|
|
4372
|
-
io.err(`--relay must be a ws(s) URL, got ${JSON.stringify(relayFlag)}`);
|
|
4373
|
-
io.err(ENTRY_USAGE);
|
|
4374
|
-
return 2;
|
|
4375
|
-
}
|
|
4376
|
-
mutation = {
|
|
4377
|
-
kind: "url",
|
|
4378
|
-
connectorUrl: target.replace(/\/+$/, ""),
|
|
4379
|
-
...relayFlag !== void 0 ? { relayUrl: relayFlag } : {}
|
|
4380
|
-
};
|
|
4381
|
-
} else if (WS_URL.test(target)) {
|
|
4382
|
-
io.err(
|
|
4383
|
-
`${JSON.stringify(target)} is a WebSocket URL \u2014 the entry is the connector's http(s) URL (the one whose GET /ilp describes it); pass the relay with --relay`
|
|
4384
|
-
);
|
|
4385
|
-
io.err(ENTRY_USAGE);
|
|
4386
|
-
return 2;
|
|
4387
|
-
} else {
|
|
4388
|
-
io.err(
|
|
4389
|
-
`unknown entry ${JSON.stringify(target)} \u2014 expected a connector http(s):// URL or \`clear\``
|
|
4390
|
-
);
|
|
4391
|
-
io.err(ENTRY_USAGE);
|
|
4392
|
-
return 2;
|
|
4393
|
-
}
|
|
4394
|
-
}
|
|
4395
|
-
try {
|
|
4396
|
-
const configPath = configPathFor2(env);
|
|
4397
|
-
const file = readEntryConfig(configPath);
|
|
4398
|
-
const label = (source) => source === "env" ? "env" : source === "legacy-env" ? "env (TOON_CLIENT_PROXY_URL, pre-4.0 spelling \u2014 set TOON_CONNECTOR)" : source === "config" ? `config (${configPath})` : source === "legacy-config" ? `config proxyUrl (${configPath}, pre-4.0 spelling)` : "not configured";
|
|
4399
|
-
if (mutation === void 0) {
|
|
4400
|
-
const state2 = effectiveEntry(env, file);
|
|
4401
|
-
if (json) {
|
|
4402
|
-
io.emitJson({
|
|
4403
|
-
command: "entry",
|
|
4404
|
-
...state2,
|
|
4405
|
-
configPath
|
|
4406
|
-
});
|
|
4407
|
-
return 0;
|
|
4408
|
-
}
|
|
4409
|
-
io.out(
|
|
4410
|
-
`Connector ${state2.connectorUrl ?? "(none)"} [${label(state2.connectorSource)}]`
|
|
4411
|
-
);
|
|
4412
|
-
io.out(
|
|
4413
|
-
`Relay ${state2.relayUrl ?? "(none)"} [${label(state2.relaySource)}]`
|
|
4414
|
-
);
|
|
4415
|
-
if (!state2.connectorUrl) {
|
|
4416
|
-
io.out(
|
|
4417
|
-
"Set one with `rig entry <connector-url>` (or export TOON_CONNECTOR)."
|
|
4418
|
-
);
|
|
4419
|
-
}
|
|
4420
|
-
return 0;
|
|
4421
|
-
}
|
|
4422
|
-
const warnings = [];
|
|
4423
|
-
const next = { ...file };
|
|
4424
|
-
if (file.proxyUrl !== void 0 || file.btpUrl !== void 0) {
|
|
4425
|
-
delete next.proxyUrl;
|
|
4426
|
-
delete next.btpUrl;
|
|
4427
|
-
warnings.push(
|
|
4428
|
-
"removed the pre-4.0 proxyUrl/btpUrl fields (the connector URL replaces both)"
|
|
4429
|
-
);
|
|
4430
|
-
}
|
|
4431
|
-
if (mutation.kind === "clear") {
|
|
4432
|
-
delete next.connectorUrl;
|
|
4433
|
-
delete next.relayUrl;
|
|
4434
|
-
} else {
|
|
4435
|
-
next.connectorUrl = mutation.connectorUrl;
|
|
4436
|
-
if (mutation.relayUrl !== void 0) next.relayUrl = mutation.relayUrl;
|
|
4437
|
-
}
|
|
4438
|
-
if (env["TOON_CONNECTOR"] || env["TOON_CLIENT_PROXY_URL"]) {
|
|
4439
|
-
warnings.push(
|
|
4440
|
-
"TOON_CONNECTOR / TOON_CLIENT_PROXY_URL is set in this shell and outranks the config file"
|
|
4441
|
-
);
|
|
4442
|
-
}
|
|
4443
|
-
writeEntryConfig(configPath, next);
|
|
4444
|
-
const wrote = {
|
|
4445
|
-
connectorUrl: next.connectorUrl ?? null,
|
|
4446
|
-
relayUrl: next.relayUrl ?? null
|
|
4447
|
-
};
|
|
4448
|
-
const state = effectiveEntry(env, next);
|
|
4449
|
-
if (json) {
|
|
4450
|
-
io.emitJson({
|
|
4451
|
-
command: "entry",
|
|
4452
|
-
...state,
|
|
4453
|
-
wrote,
|
|
4454
|
-
configPath,
|
|
4455
|
-
...warnings.length > 0 ? { warnings } : {}
|
|
4456
|
-
});
|
|
4457
|
-
return 0;
|
|
4458
|
-
}
|
|
4459
|
-
if (mutation.kind === "clear") {
|
|
4460
|
-
io.out(`Cleared the recorded connector in ${configPath}.`);
|
|
4461
|
-
} else {
|
|
4462
|
-
io.out(`Recorded connector ${mutation.connectorUrl} in ${configPath}.`);
|
|
4463
|
-
if (mutation.relayUrl !== void 0)
|
|
4464
|
-
io.out(`Recorded relay ${mutation.relayUrl}.`);
|
|
4465
|
-
}
|
|
4466
|
-
for (const w of warnings) io.out(`warning: ${w}`);
|
|
4467
|
-
io.out(
|
|
4468
|
-
`Effective connector: ${state.connectorUrl ?? "(none)"} [${label(state.connectorSource)}]`
|
|
4469
|
-
);
|
|
4470
|
-
return 0;
|
|
4471
|
-
} catch (err) {
|
|
4472
|
-
return emitCliError(io, json, "entry", err);
|
|
4473
|
-
}
|
|
4474
|
-
}
|
|
4475
|
-
|
|
4476
|
-
// src/cli/fetch.ts
|
|
4477
|
-
import { parseArgs as parseArgs10 } from "util";
|
|
4478
|
-
var FETCH_USAGE = `Usage: rig fetch [remote] [options]
|
|
4479
|
-
|
|
4480
|
-
Fetch from a TOON remote \u2014 FREE (relay reads + Arweave gateway downloads; no
|
|
4481
|
-
payments, no identity needed). Reads the kind:30618 repository state from the
|
|
4482
|
-
remote's relay, downloads only the git objects this repository is missing
|
|
4483
|
-
(SHA-1 verified), and updates the remote-tracking refs
|
|
4484
|
-
(refs/remotes/<remote>/*; tags land at refs/tags/*), reporting what moved.
|
|
4485
|
-
|
|
4486
|
-
No merge happens: integrate with the git passthrough, e.g.
|
|
4487
|
-
\`rig merge origin/main\`. \`rig fetch\` is the TOON transport and shadows
|
|
4488
|
-
\`git fetch\` \u2014 plain-git fetches remain available via \`git fetch\` directly.
|
|
4489
|
-
|
|
4490
|
-
The repo address (toon.repoid/toon.owner) comes from the config \`rig init\`
|
|
4491
|
-
or \`rig clone\` writes; --repo-id/--owner override.
|
|
4492
|
-
|
|
4493
|
-
Options:
|
|
4494
|
-
--repo-id <id> repository id / NIP-34 d-tag (default: git config)
|
|
4495
|
-
--owner <pubkey> repository owner (npub or 64-char hex; default: git config)
|
|
4496
|
-
--relay <url> ad-hoc relay override \u2014 bypasses the remote's URL
|
|
4497
|
-
--gateway <url> gateway to read objects through FIRST, on its raw-bytes
|
|
4498
|
-
route <url>/raw/<txId>, ahead of the public gateway
|
|
4499
|
-
list \u2014 name a private, local or air-gapped gateway to
|
|
4500
|
-
fetch objects the public ones do not carry
|
|
4501
|
-
(env RIG_ARWEAVE_GATEWAY also sets it)
|
|
4502
|
-
--concurrency <n> parallel gateway downloads (default 8)
|
|
4503
|
-
--json machine-readable result envelope
|
|
4504
|
-
-h, --help show this help`;
|
|
4505
|
-
var WS_URL_RE4 = /^wss?:\/\//i;
|
|
4506
|
-
function localRefFor(refname, remote) {
|
|
4507
|
-
if (refname.startsWith("refs/heads/")) {
|
|
4508
|
-
return `refs/remotes/${remote}/${refname.slice("refs/heads/".length)}`;
|
|
4509
|
-
}
|
|
4510
|
-
if (refname.startsWith("refs/tags/")) return refname;
|
|
4511
|
-
return null;
|
|
4512
|
-
}
|
|
4513
|
-
async function resolveLocalRef(repoRoot, refname) {
|
|
4514
|
-
try {
|
|
4515
|
-
const out = await runGit(repoRoot, [
|
|
4516
|
-
"rev-parse",
|
|
4517
|
-
"--verify",
|
|
4518
|
-
"--quiet",
|
|
4519
|
-
refname
|
|
4520
|
-
]);
|
|
4521
|
-
const sha = out.trim();
|
|
4522
|
-
return /^[0-9a-f]{40}$/.test(sha) ? sha : null;
|
|
4523
|
-
} catch {
|
|
4524
|
-
return null;
|
|
4525
|
-
}
|
|
4526
|
-
}
|
|
4527
|
-
async function runFetch(args, deps) {
|
|
4528
|
-
const { io } = deps;
|
|
4529
|
-
let json = false;
|
|
4530
|
-
let remote = "origin";
|
|
4531
|
-
let relayFlag;
|
|
4532
|
-
let repoIdFlag;
|
|
4533
|
-
let ownerFlag;
|
|
4534
|
-
let gatewayFlag;
|
|
4535
|
-
let concurrency;
|
|
4536
|
-
try {
|
|
4537
|
-
const { values, positionals } = parseArgs10({
|
|
4538
|
-
args,
|
|
4539
|
-
options: {
|
|
4540
|
-
json: { type: "boolean", default: false },
|
|
4541
|
-
relay: { type: "string" },
|
|
4542
|
-
"repo-id": { type: "string" },
|
|
4543
|
-
owner: { type: "string" },
|
|
4544
|
-
gateway: { type: "string" },
|
|
4545
|
-
concurrency: { type: "string" },
|
|
4546
|
-
help: { type: "boolean", short: "h", default: false }
|
|
4547
|
-
},
|
|
4548
|
-
allowPositionals: true
|
|
4549
|
-
});
|
|
4550
|
-
if (values.help) {
|
|
4551
|
-
io.out(FETCH_USAGE);
|
|
4552
|
-
return 0;
|
|
4553
|
-
}
|
|
4554
|
-
json = values.json === true;
|
|
4555
|
-
if (positionals.length > 1) {
|
|
4556
|
-
throw new Error(
|
|
4557
|
-
`expected at most one [remote] argument, got ${positionals.length}`
|
|
4558
|
-
);
|
|
4559
|
-
}
|
|
4560
|
-
if (positionals[0] !== void 0) remote = positionals[0];
|
|
4561
|
-
relayFlag = values.relay;
|
|
4562
|
-
repoIdFlag = values["repo-id"];
|
|
4563
|
-
ownerFlag = values.owner !== void 0 ? ownerToHex(values.owner) : void 0;
|
|
4564
|
-
gatewayFlag = values.gateway;
|
|
4565
|
-
if (values.concurrency !== void 0) {
|
|
4566
|
-
concurrency = Number.parseInt(values.concurrency, 10);
|
|
4567
|
-
if (!Number.isSafeInteger(concurrency) || concurrency < 1) {
|
|
4568
|
-
throw new Error("--concurrency must be a positive integer");
|
|
5094
|
+
relay: { type: "string" },
|
|
5095
|
+
json: { type: "boolean", default: false },
|
|
5096
|
+
help: { type: "boolean", short: "h", default: false }
|
|
4569
5097
|
}
|
|
5098
|
+
});
|
|
5099
|
+
if (parsed.values.help) {
|
|
5100
|
+
io.out(ENTRY_USAGE);
|
|
5101
|
+
return 0;
|
|
4570
5102
|
}
|
|
5103
|
+
positionals = parsed.positionals;
|
|
5104
|
+
relayFlag = parsed.values.relay;
|
|
5105
|
+
json = parsed.values.json ?? false;
|
|
4571
5106
|
} catch (err) {
|
|
4572
5107
|
io.err(err instanceof Error ? err.message : String(err));
|
|
4573
|
-
io.err(
|
|
5108
|
+
io.err(ENTRY_USAGE);
|
|
4574
5109
|
return 2;
|
|
4575
5110
|
}
|
|
4576
|
-
|
|
4577
|
-
|
|
4578
|
-
|
|
4579
|
-
|
|
4580
|
-
|
|
4581
|
-
|
|
4582
|
-
|
|
4583
|
-
|
|
4584
|
-
|
|
4585
|
-
|
|
5111
|
+
if (positionals.length > 1) {
|
|
5112
|
+
io.err(
|
|
5113
|
+
`rig entry takes at most one argument, got ${String(positionals.length)}: ${positionals.join(" ")}`
|
|
5114
|
+
);
|
|
5115
|
+
io.err(ENTRY_USAGE);
|
|
5116
|
+
return 2;
|
|
5117
|
+
}
|
|
5118
|
+
const target = positionals[0];
|
|
5119
|
+
let mutation;
|
|
5120
|
+
if (target !== void 0) {
|
|
5121
|
+
if (target === "clear") {
|
|
5122
|
+
if (relayFlag !== void 0) {
|
|
5123
|
+
io.err("--relay only applies to a <connector-url> entry");
|
|
5124
|
+
io.err(ENTRY_USAGE);
|
|
5125
|
+
return 2;
|
|
5126
|
+
}
|
|
5127
|
+
mutation = { kind: "clear" };
|
|
5128
|
+
} else if (HTTP_URL.test(target)) {
|
|
5129
|
+
if (relayFlag !== void 0 && !WS_URL.test(relayFlag)) {
|
|
5130
|
+
io.err(`--relay must be a ws(s) URL, got ${JSON.stringify(relayFlag)}`);
|
|
5131
|
+
io.err(ENTRY_USAGE);
|
|
5132
|
+
return 2;
|
|
5133
|
+
}
|
|
5134
|
+
mutation = {
|
|
5135
|
+
kind: "url",
|
|
5136
|
+
connectorUrl: target.replace(/\/+$/, ""),
|
|
5137
|
+
...relayFlag !== void 0 ? { relayUrl: relayFlag } : {}
|
|
5138
|
+
};
|
|
5139
|
+
} else if (WS_URL.test(target)) {
|
|
5140
|
+
io.err(
|
|
5141
|
+
`${JSON.stringify(target)} is a WebSocket URL \u2014 the entry is the connector's http(s) URL (the one whose GET /ilp describes it); pass the relay with --relay`
|
|
5142
|
+
);
|
|
5143
|
+
io.err(ENTRY_USAGE);
|
|
5144
|
+
return 2;
|
|
4586
5145
|
} else {
|
|
4587
|
-
|
|
4588
|
-
|
|
4589
|
-
if (urls.length > 1) throw new MultiUrlRemoteError(remote, urls);
|
|
4590
|
-
relayUrl = urls[0];
|
|
4591
|
-
}
|
|
4592
|
-
if (!WS_URL_RE4.test(relayUrl)) {
|
|
4593
|
-
throw new InvalidRelayUrlError(
|
|
4594
|
-
relayUrl,
|
|
4595
|
-
`remote ${JSON.stringify(remote)}`
|
|
5146
|
+
io.err(
|
|
5147
|
+
`unknown entry ${JSON.stringify(target)} \u2014 expected a connector http(s):// URL or \`clear\``
|
|
4596
5148
|
);
|
|
5149
|
+
io.err(ENTRY_USAGE);
|
|
5150
|
+
return 2;
|
|
4597
5151
|
}
|
|
4598
|
-
|
|
4599
|
-
|
|
4600
|
-
|
|
4601
|
-
|
|
4602
|
-
|
|
4603
|
-
|
|
4604
|
-
|
|
4605
|
-
|
|
4606
|
-
|
|
4607
|
-
|
|
4608
|
-
|
|
4609
|
-
|
|
4610
|
-
|
|
4611
|
-
|
|
4612
|
-
);
|
|
4613
|
-
continue;
|
|
5152
|
+
}
|
|
5153
|
+
try {
|
|
5154
|
+
const configPath = configPathFor2(env);
|
|
5155
|
+
const file = readEntryConfig(configPath);
|
|
5156
|
+
const label = (source) => source === "env" ? "env" : source === "legacy-env" ? "env (TOON_CLIENT_PROXY_URL, pre-4.0 spelling \u2014 set TOON_CONNECTOR)" : source === "config" ? `config (${configPath})` : source === "legacy-config" ? `config proxyUrl (${configPath}, pre-4.0 spelling)` : "not configured";
|
|
5157
|
+
if (mutation === void 0) {
|
|
5158
|
+
const state2 = effectiveEntry(env, file);
|
|
5159
|
+
if (json) {
|
|
5160
|
+
io.emitJson({
|
|
5161
|
+
command: "entry",
|
|
5162
|
+
...state2,
|
|
5163
|
+
configPath
|
|
5164
|
+
});
|
|
5165
|
+
return 0;
|
|
4614
5166
|
}
|
|
4615
|
-
|
|
4616
|
-
|
|
4617
|
-
|
|
4618
|
-
|
|
5167
|
+
io.out(
|
|
5168
|
+
`Connector ${state2.connectorUrl ?? "(none)"} [${label(state2.connectorSource)}]`
|
|
5169
|
+
);
|
|
5170
|
+
io.out(
|
|
5171
|
+
`Relay ${state2.relayUrl ?? "(none)"} [${label(state2.relaySource)}]`
|
|
5172
|
+
);
|
|
5173
|
+
if (!state2.connectorUrl) {
|
|
5174
|
+
io.out(
|
|
5175
|
+
"Set one with `rig entry <connector-url>` (or export TOON_CONNECTOR)."
|
|
4619
5176
|
);
|
|
4620
|
-
continue;
|
|
4621
5177
|
}
|
|
4622
|
-
|
|
5178
|
+
return 0;
|
|
4623
5179
|
}
|
|
4624
|
-
const
|
|
4625
|
-
const
|
|
4626
|
-
|
|
4627
|
-
|
|
4628
|
-
|
|
4629
|
-
|
|
4630
|
-
|
|
4631
|
-
presentLocally,
|
|
4632
|
-
// `--gateway` / RIG_ARWEAVE_GATEWAY first, public list behind it (#176).
|
|
4633
|
-
gateways: readGateways(gatewayFlag, deps.env),
|
|
4634
|
-
...deps.fetchFn ? { fetchFn: deps.fetchFn } : {},
|
|
4635
|
-
...concurrency !== void 0 ? { concurrency } : {}
|
|
4636
|
-
});
|
|
4637
|
-
if (collected.missing.length > 0) {
|
|
4638
|
-
throw new MissingRemoteObjectsError(
|
|
4639
|
-
collected.missing,
|
|
4640
|
-
`cannot fetch from ${relayUrl}`
|
|
5180
|
+
const warnings = [];
|
|
5181
|
+
const next = { ...file };
|
|
5182
|
+
if (file.proxyUrl !== void 0 || file.btpUrl !== void 0) {
|
|
5183
|
+
delete next.proxyUrl;
|
|
5184
|
+
delete next.btpUrl;
|
|
5185
|
+
warnings.push(
|
|
5186
|
+
"removed the pre-4.0 proxyUrl/btpUrl fields (the connector URL replaces both)"
|
|
4641
5187
|
);
|
|
4642
5188
|
}
|
|
4643
|
-
|
|
4644
|
-
|
|
4645
|
-
|
|
4646
|
-
|
|
4647
|
-
|
|
4648
|
-
|
|
4649
|
-
continue;
|
|
4650
|
-
}
|
|
4651
|
-
let kind;
|
|
4652
|
-
if (oldSha === null) {
|
|
4653
|
-
kind = "new";
|
|
4654
|
-
} else {
|
|
4655
|
-
kind = await reader.isAncestor(oldSha, newSha) ? "fast-forward" : "forced";
|
|
4656
|
-
}
|
|
4657
|
-
await updateRef(repoRoot, localRef, newSha);
|
|
4658
|
-
updates.push({ refname, localRef, oldSha, newSha, kind });
|
|
5189
|
+
if (mutation.kind === "clear") {
|
|
5190
|
+
delete next.connectorUrl;
|
|
5191
|
+
delete next.relayUrl;
|
|
5192
|
+
} else {
|
|
5193
|
+
next.connectorUrl = mutation.connectorUrl;
|
|
5194
|
+
if (mutation.relayUrl !== void 0) next.relayUrl = mutation.relayUrl;
|
|
4659
5195
|
}
|
|
4660
|
-
|
|
5196
|
+
if (env["TOON_CONNECTOR"] || env["TOON_CLIENT_PROXY_URL"]) {
|
|
5197
|
+
warnings.push(
|
|
5198
|
+
"TOON_CONNECTOR / TOON_CLIENT_PROXY_URL is set in this shell and outranks the config file"
|
|
5199
|
+
);
|
|
5200
|
+
}
|
|
5201
|
+
writeEntryConfig(configPath, next);
|
|
5202
|
+
const wrote = {
|
|
5203
|
+
connectorUrl: next.connectorUrl ?? null,
|
|
5204
|
+
relayUrl: next.relayUrl ?? null
|
|
5205
|
+
};
|
|
5206
|
+
const state = effectiveEntry(env, next);
|
|
4661
5207
|
if (json) {
|
|
4662
5208
|
io.emitJson({
|
|
4663
|
-
command: "
|
|
4664
|
-
|
|
4665
|
-
|
|
4666
|
-
|
|
4667
|
-
|
|
4668
|
-
updates,
|
|
4669
|
-
executed: true
|
|
5209
|
+
command: "entry",
|
|
5210
|
+
...state,
|
|
5211
|
+
wrote,
|
|
5212
|
+
configPath,
|
|
5213
|
+
...warnings.length > 0 ? { warnings } : {}
|
|
4670
5214
|
});
|
|
4671
5215
|
return 0;
|
|
4672
5216
|
}
|
|
4673
|
-
if (
|
|
4674
|
-
io.out(
|
|
4675
|
-
|
|
4676
|
-
|
|
4677
|
-
|
|
4678
|
-
|
|
4679
|
-
const short = (refname) => refname.replace(/^refs\/heads\//, "").replace(/^refs\/tags\//, "").replace(/^refs\/remotes\//, "");
|
|
4680
|
-
const src = short(u.refname);
|
|
4681
|
-
const dst = short(u.localRef);
|
|
4682
|
-
if (u.kind === "new") {
|
|
4683
|
-
const label = u.refname.startsWith("refs/tags/") ? "[new tag]" : "[new branch]";
|
|
4684
|
-
io.out(` * ${label.padEnd(17)} ${src.padEnd(14)} -> ${dst}`);
|
|
4685
|
-
} else if (u.kind === "forced") {
|
|
4686
|
-
io.out(
|
|
4687
|
-
` + ${u.oldSha.slice(0, 7)}...${u.newSha.slice(0, 7)} ${src.padEnd(14)} -> ${dst} (forced update)`
|
|
4688
|
-
);
|
|
4689
|
-
} else {
|
|
4690
|
-
io.out(
|
|
4691
|
-
` ${u.oldSha.slice(0, 7)}..${u.newSha.slice(0, 7)} ${src.padEnd(14)} -> ${dst}`
|
|
4692
|
-
);
|
|
4693
|
-
}
|
|
5217
|
+
if (mutation.kind === "clear") {
|
|
5218
|
+
io.out(`Cleared the recorded connector in ${configPath}.`);
|
|
5219
|
+
} else {
|
|
5220
|
+
io.out(`Recorded connector ${mutation.connectorUrl} in ${configPath}.`);
|
|
5221
|
+
if (mutation.relayUrl !== void 0)
|
|
5222
|
+
io.out(`Recorded relay ${mutation.relayUrl}.`);
|
|
4694
5223
|
}
|
|
4695
|
-
io.out(`
|
|
5224
|
+
for (const w of warnings) io.out(`warning: ${w}`);
|
|
5225
|
+
io.out(
|
|
5226
|
+
`Effective connector: ${state.connectorUrl ?? "(none)"} [${label(state.connectorSource)}]`
|
|
5227
|
+
);
|
|
4696
5228
|
return 0;
|
|
4697
5229
|
} catch (err) {
|
|
4698
|
-
return emitCliError(io, json, "
|
|
5230
|
+
return emitCliError(io, json, "entry", err);
|
|
4699
5231
|
}
|
|
4700
5232
|
}
|
|
4701
5233
|
|
|
4702
5234
|
// src/cli/fund.ts
|
|
4703
5235
|
import { readFileSync as readFileSync4 } from "fs";
|
|
4704
5236
|
import { homedir as homedir4 } from "os";
|
|
4705
|
-
import { join as
|
|
4706
|
-
import { parseArgs as
|
|
5237
|
+
import { join as join6 } from "path";
|
|
5238
|
+
import { parseArgs as parseArgs12 } from "util";
|
|
4707
5239
|
var DEVNET_FAUCET_URL = "https://faucet.devnet.toonprotocol.dev";
|
|
4708
5240
|
var CHAINS = ["evm", "solana"];
|
|
4709
5241
|
var CHAIN_ALIASES = {
|
|
@@ -4755,11 +5287,15 @@ Options:
|
|
|
4755
5287
|
--json machine-readable envelope (per-chain results array)
|
|
4756
5288
|
-h, --help show this help`;
|
|
4757
5289
|
var SHARED_DEVNET_SUFFIX = ".devnet.toonprotocol.dev";
|
|
5290
|
+
var SHARED_DEVNET_HOSTS = /* @__PURE__ */ new Set([
|
|
5291
|
+
"toonprotocol.dev",
|
|
5292
|
+
"relay.toonprotocol.dev"
|
|
5293
|
+
]);
|
|
4758
5294
|
function devnetHost(url) {
|
|
4759
5295
|
if (!url) return void 0;
|
|
4760
5296
|
try {
|
|
4761
5297
|
const { hostname } = new URL(url);
|
|
4762
|
-
if (hostname.endsWith(SHARED_DEVNET_SUFFIX) || hostname === SHARED_DEVNET_SUFFIX.slice(1)) {
|
|
5298
|
+
if (hostname.endsWith(SHARED_DEVNET_SUFFIX) || hostname === SHARED_DEVNET_SUFFIX.slice(1) || SHARED_DEVNET_HOSTS.has(hostname)) {
|
|
4763
5299
|
return url;
|
|
4764
5300
|
}
|
|
4765
5301
|
} catch {
|
|
@@ -4841,8 +5377,8 @@ function noFaucetGuidance(network, devnetOrigin) {
|
|
|
4841
5377
|
return `${head}. If you meant the shared devnet, set TOON_CLIENT_NETWORK=devnet and re-run \`rig fund\` \u2014 no faucet URL is needed there. If this is a self-hosted network with its own faucet, set TOON_CLIENT_FAUCET_URL (or the faucetUrl config field). ${external}`;
|
|
4842
5378
|
}
|
|
4843
5379
|
function readFundConfig(env) {
|
|
4844
|
-
const dir = env["TOON_CLIENT_HOME"] ??
|
|
4845
|
-
const configPath =
|
|
5380
|
+
const dir = env["TOON_CLIENT_HOME"] ?? join6(homedir4(), ".toon-client");
|
|
5381
|
+
const configPath = join6(dir, "config.json");
|
|
4846
5382
|
try {
|
|
4847
5383
|
return {
|
|
4848
5384
|
file: JSON.parse(readFileSync4(configPath, "utf8")),
|
|
@@ -4863,7 +5399,7 @@ async function runFund(args, deps) {
|
|
|
4863
5399
|
let addressFlag;
|
|
4864
5400
|
let json = false;
|
|
4865
5401
|
try {
|
|
4866
|
-
const { values, positionals } =
|
|
5402
|
+
const { values, positionals } = parseArgs12({
|
|
4867
5403
|
args,
|
|
4868
5404
|
allowPositionals: true,
|
|
4869
5405
|
options: {
|
|
@@ -4953,7 +5489,8 @@ async function runFund(args, deps) {
|
|
|
4953
5489
|
}
|
|
4954
5490
|
const timeoutEnv = env["TOON_CLIENT_FAUCET_TIMEOUT_MS"];
|
|
4955
5491
|
const timeoutFor = () => {
|
|
4956
|
-
if (timeoutEnv && Number.isFinite(Number(timeoutEnv)))
|
|
5492
|
+
if (timeoutEnv && Number.isFinite(Number(timeoutEnv)))
|
|
5493
|
+
return Number(timeoutEnv);
|
|
4957
5494
|
if (file.faucetTimeoutMs !== void 0) return file.faucetTimeoutMs;
|
|
4958
5495
|
return 9e4;
|
|
4959
5496
|
};
|
|
@@ -4981,10 +5518,15 @@ async function runFund(args, deps) {
|
|
|
4981
5518
|
}
|
|
4982
5519
|
const started = Date.now();
|
|
4983
5520
|
try {
|
|
4984
|
-
const { response } = await client.fundWallet(
|
|
4985
|
-
|
|
4986
|
-
|
|
4987
|
-
|
|
5521
|
+
const { response } = await client.fundWallet(
|
|
5522
|
+
faucetUrl,
|
|
5523
|
+
address,
|
|
5524
|
+
chain,
|
|
5525
|
+
{
|
|
5526
|
+
timeout: timeoutFor(),
|
|
5527
|
+
...deps.fetchImpl ? { fetchImpl: deps.fetchImpl } : {}
|
|
5528
|
+
}
|
|
5529
|
+
);
|
|
4988
5530
|
return {
|
|
4989
5531
|
chain,
|
|
4990
5532
|
funded: true,
|
|
@@ -5030,7 +5572,9 @@ async function runFund(args, deps) {
|
|
|
5030
5572
|
}
|
|
5031
5573
|
}
|
|
5032
5574
|
io.out(renderIdentityLine(identity));
|
|
5033
|
-
io.out(
|
|
5575
|
+
io.out(
|
|
5576
|
+
"Re-check with `rig balance` (a drip can take a few blocks to land)."
|
|
5577
|
+
);
|
|
5034
5578
|
return allFunded ? 0 : 1;
|
|
5035
5579
|
} catch (err) {
|
|
5036
5580
|
return emitCliError(io, json, "fund", err);
|
|
@@ -5089,7 +5633,7 @@ var runGitPassthrough = (argv, options = {}) => {
|
|
|
5089
5633
|
import { existsSync, mkdirSync as mkdirSync4 } from "fs";
|
|
5090
5634
|
import { dirname as dirname5 } from "path";
|
|
5091
5635
|
import { createInterface } from "readline/promises";
|
|
5092
|
-
import { parseArgs as
|
|
5636
|
+
import { parseArgs as parseArgs13 } from "util";
|
|
5093
5637
|
async function loadKeyOps() {
|
|
5094
5638
|
const { generateKeystore, importKeystore } = await import("@toon-protocol/client");
|
|
5095
5639
|
return { generateKeystore, importKeystore, deriveNostrKeyFromMnemonic };
|
|
@@ -5261,7 +5805,7 @@ async function runIdentity(args, deps) {
|
|
|
5261
5805
|
}
|
|
5262
5806
|
}
|
|
5263
5807
|
function parseMintArgs(args, verb) {
|
|
5264
|
-
const { values, positionals } =
|
|
5808
|
+
const { values, positionals } = parseArgs13({
|
|
5265
5809
|
args,
|
|
5266
5810
|
options: {
|
|
5267
5811
|
force: { type: "boolean", default: false },
|
|
@@ -5366,7 +5910,7 @@ async function runIdentityShow(args, deps) {
|
|
|
5366
5910
|
let json = false;
|
|
5367
5911
|
let help = false;
|
|
5368
5912
|
try {
|
|
5369
|
-
const parsed =
|
|
5913
|
+
const parsed = parseArgs13({
|
|
5370
5914
|
args,
|
|
5371
5915
|
options: {
|
|
5372
5916
|
json: { type: "boolean", default: false },
|
|
@@ -5474,15 +6018,15 @@ async function defaultReadSecretLine(prompt, isInteractive) {
|
|
|
5474
6018
|
}
|
|
5475
6019
|
|
|
5476
6020
|
// src/cli/inbox.ts
|
|
5477
|
-
import { parseArgs as
|
|
6021
|
+
import { parseArgs as parseArgs14 } from "util";
|
|
5478
6022
|
import { mkdirSync as mkdirSync5, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "fs";
|
|
5479
|
-
import { join as
|
|
6023
|
+
import { join as join7 } from "path";
|
|
5480
6024
|
import {
|
|
5481
6025
|
ISSUE_KIND as ISSUE_KIND2,
|
|
5482
|
-
PATCH_KIND as
|
|
5483
|
-
REPOSITORY_ANNOUNCEMENT_KIND as
|
|
5484
|
-
STATUS_APPLIED_KIND as
|
|
5485
|
-
STATUS_CLOSED_KIND as
|
|
6026
|
+
PATCH_KIND as PATCH_KIND4,
|
|
6027
|
+
REPOSITORY_ANNOUNCEMENT_KIND as REPOSITORY_ANNOUNCEMENT_KIND4,
|
|
6028
|
+
STATUS_APPLIED_KIND as STATUS_APPLIED_KIND4,
|
|
6029
|
+
STATUS_CLOSED_KIND as STATUS_CLOSED_KIND4,
|
|
5486
6030
|
STATUS_DRAFT_KIND as STATUS_DRAFT_KIND3,
|
|
5487
6031
|
STATUS_OPEN_KIND as STATUS_OPEN_KIND3
|
|
5488
6032
|
} from "@toon-protocol/core/nip34";
|
|
@@ -5599,8 +6143,8 @@ Options:
|
|
|
5599
6143
|
-h, --help show this help`;
|
|
5600
6144
|
var STATUS_KINDS2 = [
|
|
5601
6145
|
STATUS_OPEN_KIND3,
|
|
5602
|
-
|
|
5603
|
-
|
|
6146
|
+
STATUS_APPLIED_KIND4,
|
|
6147
|
+
STATUS_CLOSED_KIND4,
|
|
5604
6148
|
STATUS_DRAFT_KIND3
|
|
5605
6149
|
];
|
|
5606
6150
|
var CI_KINDS = [
|
|
@@ -5611,17 +6155,17 @@ var CI_KINDS = [
|
|
|
5611
6155
|
CI_JOB_RESULT_KIND
|
|
5612
6156
|
];
|
|
5613
6157
|
var INBOX_KINDS = [
|
|
5614
|
-
|
|
6158
|
+
PATCH_KIND4,
|
|
5615
6159
|
ISSUE_KIND2,
|
|
5616
6160
|
COMMENT_KIND,
|
|
5617
6161
|
LEGACY_COMMENT_KIND,
|
|
5618
6162
|
...STATUS_KINDS2,
|
|
5619
6163
|
...CI_KINDS
|
|
5620
6164
|
];
|
|
5621
|
-
var
|
|
6165
|
+
var HEX64_RE5 = /^[0-9a-f]{64}$/;
|
|
5622
6166
|
var INBOX_CURSOR_FILENAME = "inbox-cursors.json";
|
|
5623
6167
|
function cursorPath(env) {
|
|
5624
|
-
return
|
|
6168
|
+
return join7(clientStateDir(env), INBOX_CURSOR_FILENAME);
|
|
5625
6169
|
}
|
|
5626
6170
|
function readCursors(env) {
|
|
5627
6171
|
try {
|
|
@@ -5660,7 +6204,7 @@ function hasTopic(event, topic) {
|
|
|
5660
6204
|
return event.tags.some((t) => t[0] === "t" && t[1] === topic);
|
|
5661
6205
|
}
|
|
5662
6206
|
function typeOf(event) {
|
|
5663
|
-
if (event.kind ===
|
|
6207
|
+
if (event.kind === PATCH_KIND4) {
|
|
5664
6208
|
if (hasTopic(event, "root-revision")) return "revision";
|
|
5665
6209
|
if (hasTopic(event, "root") || tagValue2(event.tags, "e") === void 0) {
|
|
5666
6210
|
return "patch";
|
|
@@ -5696,7 +6240,7 @@ function toInboxEntry(event) {
|
|
|
5696
6240
|
const type = typeOf(event);
|
|
5697
6241
|
if (type === null) return null;
|
|
5698
6242
|
const repo = event.tags.find(
|
|
5699
|
-
(t) => t[0] === "a" && t[1]?.startsWith(`${
|
|
6243
|
+
(t) => t[0] === "a" && t[1]?.startsWith(`${REPOSITORY_ANNOUNCEMENT_KIND4}:`)
|
|
5700
6244
|
)?.[1];
|
|
5701
6245
|
return {
|
|
5702
6246
|
id: event.id,
|
|
@@ -5722,7 +6266,7 @@ async function ownerAnnouncement(relays, owner, repoId, ws) {
|
|
|
5722
6266
|
relays,
|
|
5723
6267
|
[
|
|
5724
6268
|
{
|
|
5725
|
-
kinds: [
|
|
6269
|
+
kinds: [REPOSITORY_ANNOUNCEMENT_KIND4],
|
|
5726
6270
|
authors: [owner],
|
|
5727
6271
|
"#d": [repoId]
|
|
5728
6272
|
}
|
|
@@ -5731,7 +6275,7 @@ async function ownerAnnouncement(relays, owner, repoId, ws) {
|
|
|
5731
6275
|
).catch(() => /* @__PURE__ */ new Map());
|
|
5732
6276
|
return newest(
|
|
5733
6277
|
[...found.values()].filter(
|
|
5734
|
-
(e) => e.kind ===
|
|
6278
|
+
(e) => e.kind === REPOSITORY_ANNOUNCEMENT_KIND4 && e.pubkey.toLowerCase() === owner && tagValue2(e.tags, "d") === repoId
|
|
5735
6279
|
)
|
|
5736
6280
|
);
|
|
5737
6281
|
}
|
|
@@ -5763,7 +6307,7 @@ async function runInbox(args, deps) {
|
|
|
5763
6307
|
let remote;
|
|
5764
6308
|
let relayFlags = [];
|
|
5765
6309
|
try {
|
|
5766
|
-
const { values, positionals } =
|
|
6310
|
+
const { values, positionals } = parseArgs14({
|
|
5767
6311
|
args,
|
|
5768
6312
|
options: INBOX_OPTIONS,
|
|
5769
6313
|
allowPositionals: true
|
|
@@ -5822,14 +6366,14 @@ async function runInbox(args, deps) {
|
|
|
5822
6366
|
const announcements = await queryAll(
|
|
5823
6367
|
configured,
|
|
5824
6368
|
[
|
|
5825
|
-
{ kinds: [
|
|
5826
|
-
{ kinds: [
|
|
6369
|
+
{ kinds: [REPOSITORY_ANNOUNCEMENT_KIND4], authors: [me] },
|
|
6370
|
+
{ kinds: [REPOSITORY_ANNOUNCEMENT_KIND4], "#maintainers": [me] }
|
|
5827
6371
|
],
|
|
5828
6372
|
ws
|
|
5829
6373
|
);
|
|
5830
6374
|
const byRepo = /* @__PURE__ */ new Map();
|
|
5831
6375
|
for (const e of announcements.values()) {
|
|
5832
|
-
if (e.kind !==
|
|
6376
|
+
if (e.kind !== REPOSITORY_ANNOUNCEMENT_KIND4) continue;
|
|
5833
6377
|
const d = tagValue2(e.tags, "d");
|
|
5834
6378
|
if (!d) continue;
|
|
5835
6379
|
const key = `${e.pubkey.toLowerCase()}:${d}`;
|
|
@@ -5843,7 +6387,7 @@ async function runInbox(args, deps) {
|
|
|
5843
6387
|
if (latest === null) continue;
|
|
5844
6388
|
if (owner !== me && !parseMaintainers(latest.tags).includes(me)) continue;
|
|
5845
6389
|
watched.push({
|
|
5846
|
-
address: `${
|
|
6390
|
+
address: `${REPOSITORY_ANNOUNCEMENT_KIND4}:${owner}:${repoId}`,
|
|
5847
6391
|
ownerPubkey: owner,
|
|
5848
6392
|
authorized: authorizedAuthors(owner, latest.tags),
|
|
5849
6393
|
relays: relaysTag(latest.tags)
|
|
@@ -5901,7 +6445,7 @@ async function runInbox(args, deps) {
|
|
|
5901
6445
|
const unknownTargets = /* @__PURE__ */ new Set();
|
|
5902
6446
|
for (const e of statusEvents) {
|
|
5903
6447
|
const target = tagValue2(e.tags, "e");
|
|
5904
|
-
if (!target || !
|
|
6448
|
+
if (!target || !HEX64_RE5.test(target)) continue;
|
|
5905
6449
|
const known = found.get(target);
|
|
5906
6450
|
if (known) targetAuthors.set(target, known.pubkey.toLowerCase());
|
|
5907
6451
|
else unknownTargets.add(target);
|
|
@@ -6035,7 +6579,7 @@ async function runInbox(args, deps) {
|
|
|
6035
6579
|
|
|
6036
6580
|
// src/cli/init.ts
|
|
6037
6581
|
import { basename as basename2 } from "path";
|
|
6038
|
-
import { parseArgs as
|
|
6582
|
+
import { parseArgs as parseArgs15 } from "util";
|
|
6039
6583
|
|
|
6040
6584
|
// src/cli/git-author.ts
|
|
6041
6585
|
var PROFILE_KIND = 0;
|
|
@@ -6153,7 +6697,7 @@ Options:
|
|
|
6153
6697
|
--json machine-readable report
|
|
6154
6698
|
-h, --help show this help`;
|
|
6155
6699
|
function parseInitArgs(args) {
|
|
6156
|
-
const { values, positionals } =
|
|
6700
|
+
const { values, positionals } = parseArgs15({
|
|
6157
6701
|
args,
|
|
6158
6702
|
options: {
|
|
6159
6703
|
"repo-id": { type: "string" },
|
|
@@ -6380,8 +6924,8 @@ async function runInit(args, deps) {
|
|
|
6380
6924
|
}
|
|
6381
6925
|
|
|
6382
6926
|
// src/cli/maintainers.ts
|
|
6383
|
-
import { parseArgs as
|
|
6384
|
-
var
|
|
6927
|
+
import { parseArgs as parseArgs16 } from "util";
|
|
6928
|
+
var HEX64_RE6 = /^[0-9a-f]{64}$/;
|
|
6385
6929
|
var REF_AUTHORITY_NOTICE = "Note: maintainers can move refs (publish honored repo state), not only set status.";
|
|
6386
6930
|
var MAINTAINERS_USAGE = `Usage: rig maintainers <list|add|remove> [<pubkey>] [options]
|
|
6387
6931
|
|
|
@@ -6434,7 +6978,7 @@ async function runList2(args, deps) {
|
|
|
6434
6978
|
const { io } = deps;
|
|
6435
6979
|
let flags;
|
|
6436
6980
|
try {
|
|
6437
|
-
const { values, positionals } =
|
|
6981
|
+
const { values, positionals } = parseArgs16({
|
|
6438
6982
|
args,
|
|
6439
6983
|
options: REPO_COMMAND_OPTIONS,
|
|
6440
6984
|
allowPositionals: true
|
|
@@ -6501,7 +7045,7 @@ async function runMutate(op, args, deps) {
|
|
|
6501
7045
|
let flags;
|
|
6502
7046
|
let pubkey;
|
|
6503
7047
|
try {
|
|
6504
|
-
const { values, positionals } =
|
|
7048
|
+
const { values, positionals } = parseArgs16({
|
|
6505
7049
|
args,
|
|
6506
7050
|
options: REPO_COMMAND_OPTIONS,
|
|
6507
7051
|
allowPositionals: true
|
|
@@ -6515,7 +7059,7 @@ async function runMutate(op, args, deps) {
|
|
|
6515
7059
|
throw new Error(`expected exactly one <pubkey> to ${op}`);
|
|
6516
7060
|
}
|
|
6517
7061
|
pubkey = ownerToHex(positionals[0]).toLowerCase();
|
|
6518
|
-
if (!
|
|
7062
|
+
if (!HEX64_RE6.test(pubkey)) {
|
|
6519
7063
|
throw new Error(
|
|
6520
7064
|
`<pubkey> must resolve to 64-char hex (got ${JSON.stringify(positionals[0])})`
|
|
6521
7065
|
);
|
|
@@ -6636,7 +7180,7 @@ async function runMutate(op, args, deps) {
|
|
|
6636
7180
|
}
|
|
6637
7181
|
|
|
6638
7182
|
// src/cli/refresh.ts
|
|
6639
|
-
import { parseArgs as
|
|
7183
|
+
import { parseArgs as parseArgs17 } from "util";
|
|
6640
7184
|
var REFRESH_USAGE = `Usage: rig refresh [options]
|
|
6641
7185
|
|
|
6642
7186
|
Republish this repo's kind:30617 announcement with no field edit, refreshing
|
|
@@ -6672,7 +7216,7 @@ async function runRefresh(args, deps) {
|
|
|
6672
7216
|
const { io } = deps;
|
|
6673
7217
|
let flags;
|
|
6674
7218
|
try {
|
|
6675
|
-
const { values, positionals } =
|
|
7219
|
+
const { values, positionals } = parseArgs17({
|
|
6676
7220
|
args,
|
|
6677
7221
|
options: REPO_COMMAND_OPTIONS,
|
|
6678
7222
|
allowPositionals: true
|
|
@@ -6795,7 +7339,7 @@ async function runRefresh(args, deps) {
|
|
|
6795
7339
|
|
|
6796
7340
|
// src/cli/name.ts
|
|
6797
7341
|
import { randomUUID } from "crypto";
|
|
6798
|
-
import { parseArgs as
|
|
7342
|
+
import { parseArgs as parseArgs18 } from "util";
|
|
6799
7343
|
var ARIO_DECIMALS = 6;
|
|
6800
7344
|
function renderMario(mario) {
|
|
6801
7345
|
return `${mario.toString()} mARIO (~${formatUnits(mario.toString(), ARIO_DECIMALS)} ARIO)`;
|
|
@@ -7304,7 +7848,7 @@ async function resolveNameContext(deps, network, processId, mode) {
|
|
|
7304
7848
|
return { identity, solanaAddress, network, processId, sdk, nostrSecretKey };
|
|
7305
7849
|
}
|
|
7306
7850
|
function parseNameFlags(args, env) {
|
|
7307
|
-
const { values, positionals } =
|
|
7851
|
+
const { values, positionals } = parseArgs18({
|
|
7308
7852
|
args,
|
|
7309
7853
|
allowPositionals: true,
|
|
7310
7854
|
options: {
|
|
@@ -7863,7 +8407,7 @@ async function runName(args, deps) {
|
|
|
7863
8407
|
}
|
|
7864
8408
|
|
|
7865
8409
|
// src/cli/payout.ts
|
|
7866
|
-
import { parseArgs as
|
|
8410
|
+
import { parseArgs as parseArgs19 } from "util";
|
|
7867
8411
|
import { getAddress } from "viem";
|
|
7868
8412
|
var PAYOUT_USAGE = `Usage: rig payout <set|clear|show> [<address>] [options]
|
|
7869
8413
|
|
|
@@ -7919,7 +8463,7 @@ async function runShow2(args, deps) {
|
|
|
7919
8463
|
const { io } = deps;
|
|
7920
8464
|
let flags;
|
|
7921
8465
|
try {
|
|
7922
|
-
const { values, positionals } =
|
|
8466
|
+
const { values, positionals } = parseArgs19({
|
|
7923
8467
|
args,
|
|
7924
8468
|
options: REPO_COMMAND_OPTIONS,
|
|
7925
8469
|
allowPositionals: true
|
|
@@ -7981,7 +8525,7 @@ async function runMutate2(op, args, deps) {
|
|
|
7981
8525
|
let flags;
|
|
7982
8526
|
let nextPayout = null;
|
|
7983
8527
|
try {
|
|
7984
|
-
const { values, positionals } =
|
|
8528
|
+
const { values, positionals } = parseArgs19({
|
|
7985
8529
|
args,
|
|
7986
8530
|
options: REPO_COMMAND_OPTIONS,
|
|
7987
8531
|
allowPositionals: true
|
|
@@ -8115,7 +8659,7 @@ async function runMutate2(op, args, deps) {
|
|
|
8115
8659
|
}
|
|
8116
8660
|
|
|
8117
8661
|
// src/cli/site.ts
|
|
8118
|
-
import { parseArgs as
|
|
8662
|
+
import { parseArgs as parseArgs20 } from "util";
|
|
8119
8663
|
|
|
8120
8664
|
// src/arweave-manifest.ts
|
|
8121
8665
|
var DEFAULT_INDEX = "index.html";
|
|
@@ -8136,13 +8680,13 @@ function buildArweaveManifest(entries, indexPath = DEFAULT_INDEX, fallbackTxId)
|
|
|
8136
8680
|
// src/cli/site-record.ts
|
|
8137
8681
|
import { mkdirSync as mkdirSync6, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
|
|
8138
8682
|
import { homedir as homedir5 } from "os";
|
|
8139
|
-
import { join as
|
|
8683
|
+
import { join as join8 } from "path";
|
|
8140
8684
|
var SITE_MANIFEST_FILENAME = "site-manifests.json";
|
|
8141
8685
|
function storeDir2(env) {
|
|
8142
|
-
return env["TOON_CLIENT_HOME"] ??
|
|
8686
|
+
return env["TOON_CLIENT_HOME"] ?? join8(homedir5(), ".toon-client");
|
|
8143
8687
|
}
|
|
8144
8688
|
function storePath2(env) {
|
|
8145
|
-
return
|
|
8689
|
+
return join8(storeDir2(env), SITE_MANIFEST_FILENAME);
|
|
8146
8690
|
}
|
|
8147
8691
|
function keyOf(repoId, ref) {
|
|
8148
8692
|
return `${repoId}\0${ref}`;
|
|
@@ -8206,7 +8750,7 @@ Options (publish):
|
|
|
8206
8750
|
estimate (nothing uploaded)
|
|
8207
8751
|
-h, --help show this help`;
|
|
8208
8752
|
function parseSiteArgs(args) {
|
|
8209
|
-
const { values, positionals } =
|
|
8753
|
+
const { values, positionals } = parseArgs20({
|
|
8210
8754
|
args,
|
|
8211
8755
|
options: {
|
|
8212
8756
|
index: { type: "string" },
|
|
@@ -8616,6 +9160,8 @@ Commands rig owns:
|
|
|
8616
9160
|
pr create publish a patch (kind:1617) with real
|
|
8617
9161
|
\`git format-patch\` content (--revises <patch-id>
|
|
8618
9162
|
publishes a revision of an existing patch)
|
|
9163
|
+
pr merge <patch-id> apply the latest revision with git am, push, then
|
|
9164
|
+
publish the applied status (owner/maintainer)
|
|
8619
9165
|
pr status <event-id> <state> set an issue/patch status (kind:1630-1633):
|
|
8620
9166
|
open | applied | closed | draft
|
|
8621
9167
|
inbox everything new since you last read it (free):
|
|
@@ -8804,4 +9350,4 @@ export {
|
|
|
8804
9350
|
rigVersion,
|
|
8805
9351
|
dispatch
|
|
8806
9352
|
};
|
|
8807
|
-
//# sourceMappingURL=chunk-
|
|
9353
|
+
//# sourceMappingURL=chunk-Z3XRZ5E5.js.map
|