@toon-protocol/rig 1.0.0 → 2.0.1
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 +98 -18
- package/dist/chunk-CW4HJNMU.js +137 -0
- package/dist/chunk-CW4HJNMU.js.map +1 -0
- package/dist/{chunk-5EXGKIPH.js → chunk-XRRU6YBQ.js} +142 -5
- package/dist/chunk-XRRU6YBQ.js.map +1 -0
- package/dist/cli/rig.js +820 -500
- package/dist/cli/rig.js.map +1 -1
- package/dist/standalone/index.js +6 -8
- package/dist/{standalone-mode-IK2RXILS.js → standalone-mode-Q3FFZUXE.js} +13 -34
- package/dist/standalone-mode-Q3FFZUXE.js.map +1 -0
- package/package.json +2 -10
- package/dist/chunk-5EXGKIPH.js.map +0 -1
- package/dist/chunk-D3HGS44Y.js +0 -144
- package/dist/chunk-D3HGS44Y.js.map +0 -1
- package/dist/standalone-mode-IK2RXILS.js.map +0 -1
package/dist/cli/rig.js
CHANGED
|
@@ -10,15 +10,16 @@ import {
|
|
|
10
10
|
serializePushPlan,
|
|
11
11
|
serializePushResult
|
|
12
12
|
} from "../chunk-LFGDLD6J.js";
|
|
13
|
+
import {
|
|
14
|
+
MissingIdentityError,
|
|
15
|
+
resolveIdentity
|
|
16
|
+
} from "../chunk-CW4HJNMU.js";
|
|
13
17
|
import {
|
|
14
18
|
buildComment,
|
|
15
19
|
buildIssue,
|
|
16
20
|
buildPatch,
|
|
17
21
|
buildStatus
|
|
18
22
|
} from "../chunk-HPSOQP7Q.js";
|
|
19
|
-
import {
|
|
20
|
-
DEFAULT_DAEMON_PORT
|
|
21
|
-
} from "../chunk-D3HGS44Y.js";
|
|
22
23
|
import {
|
|
23
24
|
MAX_OBJECT_SIZE
|
|
24
25
|
} from "../chunk-X2CZPPDM.js";
|
|
@@ -26,9 +27,12 @@ import {
|
|
|
26
27
|
// src/cli/rig.ts
|
|
27
28
|
import { createInterface } from "readline/promises";
|
|
28
29
|
|
|
30
|
+
// src/cli/dispatch.ts
|
|
31
|
+
import { createRequire } from "module";
|
|
32
|
+
|
|
29
33
|
// src/cli/events.ts
|
|
30
34
|
import { readFile } from "fs/promises";
|
|
31
|
-
import { parseArgs as
|
|
35
|
+
import { parseArgs as parseArgs3 } from "util";
|
|
32
36
|
import {
|
|
33
37
|
REPOSITORY_ANNOUNCEMENT_KIND,
|
|
34
38
|
STATUS_APPLIED_KIND,
|
|
@@ -37,179 +41,66 @@ import {
|
|
|
37
41
|
STATUS_OPEN_KIND
|
|
38
42
|
} from "@toon-protocol/core/nip34";
|
|
39
43
|
|
|
40
|
-
// src/cli/
|
|
41
|
-
var
|
|
42
|
-
constructor(
|
|
43
|
-
super(
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
this.
|
|
44
|
+
// src/cli/errors.ts
|
|
45
|
+
var UnconfiguredRepoAddressError = class extends Error {
|
|
46
|
+
constructor(missing) {
|
|
47
|
+
super(
|
|
48
|
+
`no ${missing} configured \u2014 this command addresses the repo as 30617:<ownerPubkey>:<repoId>. Run \`rig init\` once inside the repo (it writes toon.repoid/toon.owner to the local git config), or pass ${missing === "repository id" ? "--repo-id <id>" : "--owner <pubkey>"} explicitly (use --owner for repos you don't own).`
|
|
49
|
+
);
|
|
50
|
+
this.missing = missing;
|
|
51
|
+
this.name = "UnconfiguredRepoAddressError";
|
|
47
52
|
}
|
|
48
|
-
|
|
49
|
-
envelope;
|
|
53
|
+
missing;
|
|
50
54
|
};
|
|
51
|
-
var
|
|
52
|
-
constructor(
|
|
55
|
+
var InvalidRelayUrlError = class extends Error {
|
|
56
|
+
constructor(url, context) {
|
|
53
57
|
super(
|
|
54
|
-
|
|
58
|
+
`${context}: ${JSON.stringify(url)} is not a relay URL \u2014 relays are ws://, wss://, http://, or https://`
|
|
55
59
|
);
|
|
56
|
-
this.
|
|
57
|
-
this.name = "
|
|
60
|
+
this.url = url;
|
|
61
|
+
this.name = "InvalidRelayUrlError";
|
|
58
62
|
}
|
|
59
|
-
|
|
63
|
+
url;
|
|
60
64
|
};
|
|
61
|
-
var
|
|
62
|
-
constructor(
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
gitEstimate(req) {
|
|
69
|
-
return this.post("/git/estimate", req);
|
|
70
|
-
}
|
|
71
|
-
gitPush(req) {
|
|
72
|
-
return this.post("/git/push", req);
|
|
73
|
-
}
|
|
74
|
-
gitIssue(req) {
|
|
75
|
-
return this.post("/git/issue", req);
|
|
76
|
-
}
|
|
77
|
-
gitComment(req) {
|
|
78
|
-
return this.post("/git/comment", req);
|
|
79
|
-
}
|
|
80
|
-
gitPatch(req) {
|
|
81
|
-
return this.post("/git/patch", req);
|
|
82
|
-
}
|
|
83
|
-
gitStatus(req) {
|
|
84
|
-
return this.post("/git/status", req);
|
|
85
|
-
}
|
|
86
|
-
async post(path, body) {
|
|
87
|
-
let res;
|
|
88
|
-
try {
|
|
89
|
-
res = await this.fetchImpl(`${this.baseUrl}${path}`, {
|
|
90
|
-
method: "POST",
|
|
91
|
-
headers: { "content-type": "application/json" },
|
|
92
|
-
body: JSON.stringify(body)
|
|
93
|
-
});
|
|
94
|
-
} catch (err) {
|
|
95
|
-
throw new DaemonUnreachableError(this.baseUrl, err);
|
|
96
|
-
}
|
|
97
|
-
const text = await res.text();
|
|
98
|
-
let parsed;
|
|
99
|
-
try {
|
|
100
|
-
parsed = text === "" ? {} : JSON.parse(text);
|
|
101
|
-
} catch {
|
|
102
|
-
throw new DaemonRouteError(res.status, {
|
|
103
|
-
error: "invalid_response",
|
|
104
|
-
detail: `daemon returned non-JSON (HTTP ${res.status}): ${text.slice(0, 200)}`
|
|
105
|
-
});
|
|
106
|
-
}
|
|
107
|
-
if (!res.ok) {
|
|
108
|
-
const envelope = parsed && typeof parsed === "object" && "error" in parsed ? parsed : { error: "http_error", detail: `HTTP ${res.status}` };
|
|
109
|
-
throw new DaemonRouteError(res.status, envelope);
|
|
110
|
-
}
|
|
111
|
-
return parsed;
|
|
65
|
+
var UnknownRemoteError = class extends Error {
|
|
66
|
+
constructor(remote) {
|
|
67
|
+
super(
|
|
68
|
+
`no remote named ${JSON.stringify(remote)} is configured \u2014 \`rig remote list\` shows configured remotes; add it with \`rig remote add ${remote} <relay-url>\``
|
|
69
|
+
);
|
|
70
|
+
this.remote = remote;
|
|
71
|
+
this.name = "UnknownRemoteError";
|
|
112
72
|
}
|
|
73
|
+
remote;
|
|
113
74
|
};
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
import { existsSync, readFileSync } from "fs";
|
|
117
|
-
import { homedir } from "os";
|
|
118
|
-
import { join } from "path";
|
|
119
|
-
function daemonBaseUrl(env) {
|
|
120
|
-
const raw = env["TOON_CLIENT_HTTP_PORT"];
|
|
121
|
-
const parsed = raw ? Number(raw) : NaN;
|
|
122
|
-
const port = Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_DAEMON_PORT;
|
|
123
|
-
return `http://127.0.0.1:${port}`;
|
|
124
|
-
}
|
|
125
|
-
async function probeDaemon(env, fetchImpl, timeoutMs = 1500) {
|
|
126
|
-
const baseUrl = daemonBaseUrl(env);
|
|
127
|
-
try {
|
|
128
|
-
const res = await fetchImpl(`${baseUrl}/status`, {
|
|
129
|
-
signal: AbortSignal.timeout(timeoutMs)
|
|
130
|
-
});
|
|
131
|
-
if (!res.ok) return { baseUrl, reachable: false };
|
|
132
|
-
const body = await res.json();
|
|
133
|
-
const probe = { baseUrl, reachable: true };
|
|
134
|
-
const pubkey = body?.identity?.nostrPubkey;
|
|
135
|
-
if (typeof pubkey === "string" && pubkey !== "") probe.identity = pubkey;
|
|
136
|
-
if (typeof body?.ready === "boolean") probe.ready = body.ready;
|
|
137
|
-
if (typeof body?.relay?.url === "string" && body.relay.url !== "") {
|
|
138
|
-
probe.relayUrl = body.relay.url;
|
|
139
|
-
}
|
|
140
|
-
if (typeof body?.feePerEvent === "string" && body.feePerEvent !== "") {
|
|
141
|
-
probe.feePerEvent = body.feePerEvent;
|
|
142
|
-
}
|
|
143
|
-
return probe;
|
|
144
|
-
} catch {
|
|
145
|
-
return { baseUrl, reachable: false };
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
function clientConfigPath(env) {
|
|
149
|
-
const dir = env["TOON_CLIENT_HOME"] ?? join(homedir(), ".toon-client");
|
|
150
|
-
return join(dir, "config.json");
|
|
151
|
-
}
|
|
152
|
-
function standaloneAvailability(env) {
|
|
153
|
-
const configPath = clientConfigPath(env);
|
|
154
|
-
if (env["TOON_CLIENT_MNEMONIC"]) {
|
|
155
|
-
return { available: true, source: "env", configPath };
|
|
156
|
-
}
|
|
157
|
-
if (existsSync(configPath)) {
|
|
158
|
-
try {
|
|
159
|
-
const file = JSON.parse(readFileSync(configPath, "utf8"));
|
|
160
|
-
if (typeof file.mnemonic === "string" && file.mnemonic !== "" || typeof file.keystorePath === "string" && file.keystorePath !== "") {
|
|
161
|
-
return { available: true, source: "config", configPath };
|
|
162
|
-
}
|
|
163
|
-
} catch {
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
return { available: false, configPath };
|
|
167
|
-
}
|
|
168
|
-
var NoPublisherError = class extends Error {
|
|
169
|
-
constructor(probe, standalone) {
|
|
75
|
+
var MultiUrlRemoteError = class extends Error {
|
|
76
|
+
constructor(remote, urls) {
|
|
170
77
|
super(
|
|
171
|
-
`
|
|
172
|
-
\u2022 daemon: no toon-clientd control API at ${probe.baseUrl}/status \u2014 start it (\`toon-clientd\`, shipped by @toon-protocol/client-mcp) and re-run, or pass --daemon once it is up
|
|
173
|
-
\u2022 standalone: no identity found \u2014 set TOON_CLIENT_MNEMONIC (BIP-39 seed phrase) or configure ${standalone.configPath} (mnemonic / keystorePath), then re-run (or pass --standalone)`
|
|
78
|
+
`remote ${JSON.stringify(remote)} has ${urls.length} URLs (${urls.join(", ")}) \u2014 rig supports one relay URL per remote. Fix it with \`git remote set-url ${remote} <relay-url>\`. Nothing was uploaded, published, or paid.`
|
|
174
79
|
);
|
|
175
|
-
this.
|
|
80
|
+
this.remote = remote;
|
|
81
|
+
this.urls = urls;
|
|
82
|
+
this.name = "MultiUrlRemoteError";
|
|
176
83
|
}
|
|
84
|
+
remote;
|
|
85
|
+
urls;
|
|
177
86
|
};
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
mode: "standalone",
|
|
186
|
-
probe: { baseUrl: daemonBaseUrl(env), reachable: false }
|
|
187
|
-
};
|
|
87
|
+
var NoOriginConfiguredError = class extends Error {
|
|
88
|
+
constructor(nonRelayOriginUrl) {
|
|
89
|
+
super(
|
|
90
|
+
"no origin configured \u2014 run `rig remote add origin <relay-url>` (or pass --relay <url> for a one-off publish)." + (nonRelayOriginUrl !== void 0 ? `
|
|
91
|
+
The existing "origin" remote (${nonRelayOriginUrl}) is not a relay URL, so rig ignores it \u2014 add the relay under another name (\`rig remote add toon <relay-url>\`) and target it explicitly (\`rig push toon\` / \`--remote toon\`).` : "")
|
|
92
|
+
);
|
|
93
|
+
this.name = "NoOriginConfiguredError";
|
|
188
94
|
}
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
const standalone = standaloneAvailability(env);
|
|
193
|
-
if (standalone.available) return { mode: "standalone", probe };
|
|
194
|
-
throw new NoPublisherError(probe, standalone);
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
// src/cli/errors.ts
|
|
198
|
-
var FUNDING_REMEDIATION = (command) => [
|
|
199
|
-
"Remediation:",
|
|
200
|
-
" \u2022 fund the settlement wallet: run the toon_fund_wallet MCP tool (devnet faucet), or send gas/tokens to the wallet yourself",
|
|
201
|
-
" \u2022 open (or top up) a payment channel: toon_open_channel / toon_channel_deposit",
|
|
202
|
-
` \u2022 then re-run rig ${command}`
|
|
203
|
-
];
|
|
204
|
-
var UnconfiguredRepoAddressError = class extends Error {
|
|
205
|
-
constructor(missing) {
|
|
95
|
+
};
|
|
96
|
+
var NotAGitRepositoryError = class extends Error {
|
|
97
|
+
constructor(cwd) {
|
|
206
98
|
super(
|
|
207
|
-
`
|
|
99
|
+
`not a git repository: ${cwd}
|
|
100
|
+
rig works inside an existing repo \u2014 create one first with \`git init\` (rig never runs it for you), then re-run.`
|
|
208
101
|
);
|
|
209
|
-
this.
|
|
210
|
-
this.name = "UnconfiguredRepoAddressError";
|
|
102
|
+
this.name = "NotAGitRepositoryError";
|
|
211
103
|
}
|
|
212
|
-
missing;
|
|
213
104
|
};
|
|
214
105
|
function nonFastForwardLines(refs) {
|
|
215
106
|
return [
|
|
@@ -231,49 +122,7 @@ function oversizeLines(objects) {
|
|
|
231
122
|
"Large-object support is tracked in toon-client#235."
|
|
232
123
|
];
|
|
233
124
|
}
|
|
234
|
-
function fromDaemonEnvelope(err, command) {
|
|
235
|
-
const { envelope, status } = err;
|
|
236
|
-
const json = { ...envelope, status };
|
|
237
|
-
switch (envelope.error) {
|
|
238
|
-
case "non_fast_forward": {
|
|
239
|
-
const refs = Array.isArray(envelope["refs"]) ? envelope["refs"] : [];
|
|
240
|
-
return { code: "non_fast_forward", lines: nonFastForwardLines(refs), json };
|
|
241
|
-
}
|
|
242
|
-
case "oversize_objects": {
|
|
243
|
-
const objects = Array.isArray(envelope["objects"]) ? envelope["objects"] : [];
|
|
244
|
-
return { code: "oversize_objects", lines: oversizeLines(objects), json };
|
|
245
|
-
}
|
|
246
|
-
case "bootstrapping":
|
|
247
|
-
return {
|
|
248
|
-
code: "bootstrapping",
|
|
249
|
-
lines: [
|
|
250
|
-
`toon-clientd is still bootstrapping: ${envelope.detail ?? "transport/channel coming up"}`,
|
|
251
|
-
"Retry in a few seconds. If it never becomes ready, check the toon-clientd logs."
|
|
252
|
-
],
|
|
253
|
-
json
|
|
254
|
-
};
|
|
255
|
-
case "insufficient_gas":
|
|
256
|
-
return {
|
|
257
|
-
code: "insufficient_gas",
|
|
258
|
-
lines: [
|
|
259
|
-
`Payment failed: ${envelope.detail ?? "the settlement wallet cannot fund the channel"}`,
|
|
260
|
-
...FUNDING_REMEDIATION(command)
|
|
261
|
-
],
|
|
262
|
-
json
|
|
263
|
-
};
|
|
264
|
-
default:
|
|
265
|
-
return {
|
|
266
|
-
code: envelope.error,
|
|
267
|
-
lines: [
|
|
268
|
-
`rig ${command} failed (${envelope.error}, HTTP ${status})` + (envelope.detail ? `: ${envelope.detail}` : ""),
|
|
269
|
-
...envelope.retryable ? ["This error is retryable \u2014 try again shortly."] : []
|
|
270
|
-
],
|
|
271
|
-
json
|
|
272
|
-
};
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
125
|
function describeError(err, command = "push") {
|
|
276
|
-
if (err instanceof DaemonRouteError) return fromDaemonEnvelope(err, command);
|
|
277
126
|
if (err instanceof UnconfiguredRepoAddressError) {
|
|
278
127
|
return {
|
|
279
128
|
code: "unconfigured_repo_address",
|
|
@@ -281,18 +130,51 @@ function describeError(err, command = "push") {
|
|
|
281
130
|
json: { error: "unconfigured_repo_address", detail: err.message }
|
|
282
131
|
};
|
|
283
132
|
}
|
|
284
|
-
if (err instanceof
|
|
133
|
+
if (err instanceof NotAGitRepositoryError) {
|
|
134
|
+
return {
|
|
135
|
+
code: "not_a_git_repository",
|
|
136
|
+
lines: err.message.split("\n"),
|
|
137
|
+
json: { error: "not_a_git_repository", detail: err.message }
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
if (err instanceof InvalidRelayUrlError) {
|
|
285
141
|
return {
|
|
286
|
-
code: "
|
|
287
|
-
lines:
|
|
288
|
-
json: { error: "
|
|
142
|
+
code: "invalid_relay_url",
|
|
143
|
+
lines: err.message.split("\n"),
|
|
144
|
+
json: { error: "invalid_relay_url", detail: err.message, url: err.url }
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
if (err instanceof UnknownRemoteError) {
|
|
148
|
+
return {
|
|
149
|
+
code: "unknown_remote",
|
|
150
|
+
lines: err.message.split("\n"),
|
|
151
|
+
json: { error: "unknown_remote", detail: err.message, remote: err.remote }
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
if (err instanceof MultiUrlRemoteError) {
|
|
155
|
+
return {
|
|
156
|
+
code: "multi_url_remote",
|
|
157
|
+
lines: err.message.split("\n"),
|
|
158
|
+
json: {
|
|
159
|
+
error: "multi_url_remote",
|
|
160
|
+
detail: err.message,
|
|
161
|
+
remote: err.remote,
|
|
162
|
+
urls: err.urls
|
|
163
|
+
}
|
|
289
164
|
};
|
|
290
165
|
}
|
|
291
|
-
if (err instanceof
|
|
166
|
+
if (err instanceof NoOriginConfiguredError) {
|
|
292
167
|
return {
|
|
293
|
-
code: "
|
|
168
|
+
code: "no_origin_configured",
|
|
294
169
|
lines: err.message.split("\n"),
|
|
295
|
-
json: { error: "
|
|
170
|
+
json: { error: "no_origin_configured", detail: err.message }
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
if (err instanceof MissingIdentityError) {
|
|
174
|
+
return {
|
|
175
|
+
code: "missing_identity",
|
|
176
|
+
lines: err.message.split("\n"),
|
|
177
|
+
json: { error: "missing_identity", detail: err.message }
|
|
296
178
|
};
|
|
297
179
|
}
|
|
298
180
|
if (err instanceof NonFastForwardError) {
|
|
@@ -325,15 +207,18 @@ function describeError(err, command = "push") {
|
|
|
325
207
|
if (name === "DaemonIdentityConflictError") {
|
|
326
208
|
return {
|
|
327
209
|
code: "daemon_identity_conflict",
|
|
328
|
-
lines: [
|
|
210
|
+
lines: [
|
|
211
|
+
message,
|
|
212
|
+
"Stop the toon-clientd daemon (or publish through its toon_git_* MCP tools) and re-run."
|
|
213
|
+
],
|
|
329
214
|
json: { error: "daemon_identity_conflict", detail: message }
|
|
330
215
|
};
|
|
331
216
|
}
|
|
332
|
-
if (name === "
|
|
217
|
+
if (name === "MissingUplinkError") {
|
|
333
218
|
return {
|
|
334
|
-
code:
|
|
219
|
+
code: "missing_uplink",
|
|
335
220
|
lines: [message],
|
|
336
|
-
json: { error: "
|
|
221
|
+
json: { error: "missing_uplink", detail: message }
|
|
337
222
|
};
|
|
338
223
|
}
|
|
339
224
|
return {
|
|
@@ -397,6 +282,29 @@ async function readToonConfig(repoPath) {
|
|
|
397
282
|
if (owner.exitCode === 0 && own) config.owner = own;
|
|
398
283
|
return config;
|
|
399
284
|
}
|
|
285
|
+
async function getGitRemoteUrls(repoPath, name) {
|
|
286
|
+
const { stdout } = await git(
|
|
287
|
+
repoPath,
|
|
288
|
+
["config", "--get-all", `remote.${name}.url`],
|
|
289
|
+
[1]
|
|
290
|
+
);
|
|
291
|
+
return stdout.split("\n").map((l) => l.trim()).filter(Boolean);
|
|
292
|
+
}
|
|
293
|
+
async function listGitRemotes(repoPath) {
|
|
294
|
+
const { stdout } = await git(repoPath, ["remote"]);
|
|
295
|
+
const names = stdout.split("\n").map((l) => l.trim()).filter(Boolean);
|
|
296
|
+
const remotes = [];
|
|
297
|
+
for (const name of names) {
|
|
298
|
+
remotes.push({ name, urls: await getGitRemoteUrls(repoPath, name) });
|
|
299
|
+
}
|
|
300
|
+
return remotes;
|
|
301
|
+
}
|
|
302
|
+
async function addGitRemote(repoPath, name, url) {
|
|
303
|
+
await git(repoPath, ["remote", "add", name, url]);
|
|
304
|
+
}
|
|
305
|
+
async function removeGitRemote(repoPath, name) {
|
|
306
|
+
await git(repoPath, ["remote", "remove", name]);
|
|
307
|
+
}
|
|
400
308
|
async function writeToonConfig(repoPath, config) {
|
|
401
309
|
if (config.repoId !== void 0) {
|
|
402
310
|
await git(repoPath, ["config", "toon.repoid", config.repoId]);
|
|
@@ -413,8 +321,7 @@ async function writeToonConfig(repoPath, config) {
|
|
|
413
321
|
}
|
|
414
322
|
|
|
415
323
|
// src/cli/push.ts
|
|
416
|
-
import {
|
|
417
|
-
import { parseArgs } from "util";
|
|
324
|
+
import { parseArgs as parseArgs2 } from "util";
|
|
418
325
|
|
|
419
326
|
// src/cli/render.ts
|
|
420
327
|
function formatNumber(value) {
|
|
@@ -427,10 +334,13 @@ function refLine(update) {
|
|
|
427
334
|
const arrow = `${shortSha(update.remoteSha)} \u2192 ${shortSha(update.localSha)}`;
|
|
428
335
|
return ` ${update.refname} ${arrow} (${update.kind})`;
|
|
429
336
|
}
|
|
430
|
-
function
|
|
337
|
+
function renderIdentityLine(identity) {
|
|
338
|
+
return `Identity: ${identity.pubkey} (from ${identity.sourceLabel})`;
|
|
339
|
+
}
|
|
340
|
+
function renderPlan(plan) {
|
|
431
341
|
const lines = [];
|
|
432
342
|
lines.push(
|
|
433
|
-
`Push plan for repo "${plan.repoId}"
|
|
343
|
+
`Push plan for repo "${plan.repoId}"` + (plan.announceNeeded ? " \u2014 first push, will announce (kind:30617)" : "")
|
|
434
344
|
);
|
|
435
345
|
lines.push("Refs:");
|
|
436
346
|
for (const update of plan.refUpdates) lines.push(refLine(update));
|
|
@@ -453,8 +363,9 @@ function feeLabel(fee) {
|
|
|
453
363
|
}
|
|
454
364
|
function renderEventPlan(opts) {
|
|
455
365
|
return [
|
|
456
|
-
`Publish ${opts.action}
|
|
366
|
+
`Publish ${opts.action}`,
|
|
457
367
|
`Repo: 30617:${opts.addr.ownerPubkey}:${opts.addr.repoId}`,
|
|
368
|
+
renderIdentityLine(opts.identity),
|
|
458
369
|
`Fee: ${feeLabel(opts.fee)}. Writes are permanent and non-refundable.`
|
|
459
370
|
];
|
|
460
371
|
}
|
|
@@ -496,18 +407,248 @@ function renderResult(result) {
|
|
|
496
407
|
return lines;
|
|
497
408
|
}
|
|
498
409
|
|
|
410
|
+
// src/cli/remote.ts
|
|
411
|
+
import { parseArgs } from "util";
|
|
412
|
+
var RELAY_PROTOCOLS = /* @__PURE__ */ new Set(["ws:", "wss:", "http:", "https:"]);
|
|
413
|
+
function isRelayUrl(url) {
|
|
414
|
+
try {
|
|
415
|
+
return RELAY_PROTOCOLS.has(new URL(url).protocol);
|
|
416
|
+
} catch {
|
|
417
|
+
return false;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
function assertRelayUrl(url, context) {
|
|
421
|
+
if (!isRelayUrl(url)) throw new InvalidRelayUrlError(url, context);
|
|
422
|
+
}
|
|
423
|
+
async function resolveRelays(opts) {
|
|
424
|
+
if (opts.relayFlags.length > 0) {
|
|
425
|
+
return { relays: opts.relayFlags, source: "relay-flag" };
|
|
426
|
+
}
|
|
427
|
+
if (opts.remoteName !== void 0) {
|
|
428
|
+
const urls = opts.repoRoot !== void 0 ? await getGitRemoteUrls(opts.repoRoot, opts.remoteName) : [];
|
|
429
|
+
if (urls.length === 0) throw new UnknownRemoteError(opts.remoteName);
|
|
430
|
+
if (urls.length > 1) throw new MultiUrlRemoteError(opts.remoteName, urls);
|
|
431
|
+
const url = urls[0];
|
|
432
|
+
assertRelayUrl(url, `remote ${JSON.stringify(opts.remoteName)}`);
|
|
433
|
+
return { relays: urls, source: "remote", remoteName: opts.remoteName };
|
|
434
|
+
}
|
|
435
|
+
let nonRelayOriginUrl;
|
|
436
|
+
if (opts.repoRoot !== void 0) {
|
|
437
|
+
const urls = await getGitRemoteUrls(opts.repoRoot, "origin");
|
|
438
|
+
if (urls.length === 1 && isRelayUrl(urls[0])) {
|
|
439
|
+
return { relays: urls, source: "remote", remoteName: "origin" };
|
|
440
|
+
}
|
|
441
|
+
if (urls.length > 1 && urls.some(isRelayUrl)) {
|
|
442
|
+
throw new MultiUrlRemoteError("origin", urls);
|
|
443
|
+
}
|
|
444
|
+
if (urls.length > 0) nonRelayOriginUrl = urls[0];
|
|
445
|
+
}
|
|
446
|
+
if (opts.toonRelays.length > 0) {
|
|
447
|
+
return {
|
|
448
|
+
relays: opts.toonRelays,
|
|
449
|
+
source: "toon.relay",
|
|
450
|
+
nudge: `note: git config toon.relay is deprecated (removed in v0.3) \u2014 migrate: rig remote add origin ${opts.toonRelays[0]}`
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
throw new NoOriginConfiguredError(nonRelayOriginUrl);
|
|
454
|
+
}
|
|
455
|
+
function singleRelayRefusal(resolved, nothingHappened) {
|
|
456
|
+
const fix = resolved.source === "relay-flag" ? "pass exactly one --relay <url>" : "trim git config toon.relay to one URL (better: migrate \u2014 `rig remote add origin <relay-url>`)";
|
|
457
|
+
return `rig publishes to a single relay, but ${resolved.relays.length} are configured (${resolved.relays.join(", ")}) \u2014 ${fix}. ${nothingHappened}`;
|
|
458
|
+
}
|
|
459
|
+
var REMOTE_USAGE = `Usage: rig remote <add|remove|list> [args] [options]
|
|
460
|
+
|
|
461
|
+
Manage the relays this repo publishes to. Remotes are stored as REAL git
|
|
462
|
+
remotes (\`git remote -v\` shows them; plain git tooling round-trips). Paid
|
|
463
|
+
commands publish via the "origin" remote by default; \`rig push <remote>\`
|
|
464
|
+
and \`--remote <name>\` target another one. Free \u2014 nothing is published or
|
|
465
|
+
paid.
|
|
466
|
+
|
|
467
|
+
Commands:
|
|
468
|
+
add <name> <relay-url> add a remote pointing at a relay (the URL must be
|
|
469
|
+
ws://, wss://, http://, or https://)
|
|
470
|
+
remove <name> remove a remote
|
|
471
|
+
list list remote names + URLs (the default subcommand)
|
|
472
|
+
|
|
473
|
+
Options:
|
|
474
|
+
--json machine-readable output (list)
|
|
475
|
+
-h, --help show this help`;
|
|
476
|
+
async function runRemote(args, deps) {
|
|
477
|
+
const { io } = deps;
|
|
478
|
+
let sub;
|
|
479
|
+
let rest;
|
|
480
|
+
let json;
|
|
481
|
+
try {
|
|
482
|
+
const { values, positionals } = parseArgs({
|
|
483
|
+
args,
|
|
484
|
+
options: {
|
|
485
|
+
json: { type: "boolean", default: false },
|
|
486
|
+
help: { type: "boolean", short: "h", default: false }
|
|
487
|
+
},
|
|
488
|
+
allowPositionals: true
|
|
489
|
+
});
|
|
490
|
+
if (values.help) {
|
|
491
|
+
io.out(REMOTE_USAGE);
|
|
492
|
+
return 0;
|
|
493
|
+
}
|
|
494
|
+
json = values.json ?? false;
|
|
495
|
+
[sub, ...rest] = positionals;
|
|
496
|
+
} catch (err) {
|
|
497
|
+
io.err(err instanceof Error ? err.message : String(err));
|
|
498
|
+
io.err(REMOTE_USAGE);
|
|
499
|
+
return 2;
|
|
500
|
+
}
|
|
501
|
+
switch (sub) {
|
|
502
|
+
case void 0:
|
|
503
|
+
case "list":
|
|
504
|
+
if (rest.length > 0) {
|
|
505
|
+
io.err(`rig remote list takes no arguments (got ${rest.join(" ")})`);
|
|
506
|
+
io.err(REMOTE_USAGE);
|
|
507
|
+
return 2;
|
|
508
|
+
}
|
|
509
|
+
break;
|
|
510
|
+
case "add": {
|
|
511
|
+
if (rest.length !== 2) {
|
|
512
|
+
io.err("usage: rig remote add <name> <relay-url>");
|
|
513
|
+
io.err(REMOTE_USAGE);
|
|
514
|
+
return 2;
|
|
515
|
+
}
|
|
516
|
+
const url = rest[1];
|
|
517
|
+
if (!isRelayUrl(url)) {
|
|
518
|
+
io.err(
|
|
519
|
+
`cannot add remote: ${JSON.stringify(url)} is not a relay URL \u2014 relays are ws://, wss://, http://, or https://`
|
|
520
|
+
);
|
|
521
|
+
return 2;
|
|
522
|
+
}
|
|
523
|
+
break;
|
|
524
|
+
}
|
|
525
|
+
case "remove":
|
|
526
|
+
if (rest.length !== 1) {
|
|
527
|
+
io.err("usage: rig remote remove <name>");
|
|
528
|
+
io.err(REMOTE_USAGE);
|
|
529
|
+
return 2;
|
|
530
|
+
}
|
|
531
|
+
break;
|
|
532
|
+
default:
|
|
533
|
+
io.err(`unknown rig remote subcommand: ${sub}`);
|
|
534
|
+
io.err(REMOTE_USAGE);
|
|
535
|
+
return 2;
|
|
536
|
+
}
|
|
537
|
+
try {
|
|
538
|
+
let repoRoot;
|
|
539
|
+
try {
|
|
540
|
+
repoRoot = await resolveRepoRoot(deps.cwd);
|
|
541
|
+
} catch {
|
|
542
|
+
throw new NotAGitRepositoryError(deps.cwd);
|
|
543
|
+
}
|
|
544
|
+
switch (sub) {
|
|
545
|
+
case void 0:
|
|
546
|
+
case "list": {
|
|
547
|
+
const remotes = await listGitRemotes(repoRoot);
|
|
548
|
+
if (json) {
|
|
549
|
+
io.out(JSON.stringify({ command: "remote", remotes }, null, 2));
|
|
550
|
+
return 0;
|
|
551
|
+
}
|
|
552
|
+
if (remotes.length === 0) {
|
|
553
|
+
io.out(
|
|
554
|
+
"no remotes configured \u2014 add one: rig remote add origin <relay-url>"
|
|
555
|
+
);
|
|
556
|
+
return 0;
|
|
557
|
+
}
|
|
558
|
+
for (const remote of remotes) {
|
|
559
|
+
for (const url of remote.urls) {
|
|
560
|
+
io.out(
|
|
561
|
+
`${remote.name} ${url}` + (isRelayUrl(url) ? "" : " (not a relay URL \u2014 ignored by rig)")
|
|
562
|
+
);
|
|
563
|
+
}
|
|
564
|
+
if (remote.urls.length > 1) {
|
|
565
|
+
io.err(
|
|
566
|
+
`warning: remote "${remote.name}" has ${remote.urls.length} URLs \u2014 rig supports one relay URL per remote (fix with \`git remote set-url ${remote.name} <relay-url>\`)`
|
|
567
|
+
);
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
return 0;
|
|
571
|
+
}
|
|
572
|
+
case "add": {
|
|
573
|
+
const [name, url] = rest;
|
|
574
|
+
const existing = await getGitRemoteUrls(repoRoot, name);
|
|
575
|
+
if (existing.length > 0) {
|
|
576
|
+
io.err(
|
|
577
|
+
`remote ${JSON.stringify(name)} already exists (${existing.join(", ")}) \u2014 nothing changed. Point it somewhere else with \`git remote set-url ${name} <relay-url>\`, or \`rig remote remove ${name}\` first.`
|
|
578
|
+
);
|
|
579
|
+
return 1;
|
|
580
|
+
}
|
|
581
|
+
await addGitRemote(repoRoot, name, url);
|
|
582
|
+
io.out(`Added remote ${name} \u2192 ${url}`);
|
|
583
|
+
if (name === "origin") {
|
|
584
|
+
io.out(
|
|
585
|
+
"`rig push` and the event commands now publish here by default."
|
|
586
|
+
);
|
|
587
|
+
const toonConfig = await readToonConfig(repoRoot);
|
|
588
|
+
if (toonConfig.relays.length > 0) {
|
|
589
|
+
io.err(
|
|
590
|
+
`note: git config toon.relay (${toonConfig.relays.join(", ")}) is deprecated and now shadowed by the origin remote \u2014 drop it with \`git config --unset-all toon.relay\` (the key is removed in v0.3).`
|
|
591
|
+
);
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
return 0;
|
|
595
|
+
}
|
|
596
|
+
case "remove": {
|
|
597
|
+
const name = rest[0];
|
|
598
|
+
const existing = await getGitRemoteUrls(repoRoot, name);
|
|
599
|
+
if (existing.length === 0) {
|
|
600
|
+
io.err(
|
|
601
|
+
`no remote named ${JSON.stringify(name)} \u2014 \`rig remote list\` shows configured remotes.`
|
|
602
|
+
);
|
|
603
|
+
return 1;
|
|
604
|
+
}
|
|
605
|
+
await removeGitRemote(repoRoot, name);
|
|
606
|
+
io.out(`Removed remote ${name}`);
|
|
607
|
+
return 0;
|
|
608
|
+
}
|
|
609
|
+
/* v8 ignore next 2 -- unreachable: validated above */
|
|
610
|
+
default:
|
|
611
|
+
return 2;
|
|
612
|
+
}
|
|
613
|
+
} catch (err) {
|
|
614
|
+
const described = describeError(err, "remote");
|
|
615
|
+
if (json) {
|
|
616
|
+
io.out(JSON.stringify({ command: "remote", ...described.json }, null, 2));
|
|
617
|
+
} else {
|
|
618
|
+
for (const line of described.lines) io.err(line);
|
|
619
|
+
}
|
|
620
|
+
return 1;
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
|
|
499
624
|
// src/cli/push.ts
|
|
500
|
-
var defaultLoadStandalone = async (
|
|
501
|
-
const mod = await import("../standalone-mode-
|
|
502
|
-
return mod.createStandaloneContext(
|
|
625
|
+
var defaultLoadStandalone = async (options) => {
|
|
626
|
+
const mod = await import("../standalone-mode-Q3FFZUXE.js");
|
|
627
|
+
return mod.createStandaloneContext(options);
|
|
503
628
|
};
|
|
504
|
-
|
|
629
|
+
function identityReport(ctx) {
|
|
630
|
+
return {
|
|
631
|
+
pubkey: ctx.ownerPubkey,
|
|
632
|
+
source: ctx.identitySource,
|
|
633
|
+
sourceLabel: ctx.identitySourceLabel
|
|
634
|
+
};
|
|
635
|
+
}
|
|
636
|
+
var PUSH_USAGE = `Usage: rig push [remote] [refspecs...] [options]
|
|
505
637
|
|
|
506
638
|
Push local git refs to TOON: uploads the object delta to Arweave (paid) and
|
|
507
639
|
publishes the NIP-34 refs event (kind:30618; kind:30617 announce on first
|
|
508
640
|
push). Writes are permanent and non-refundable.
|
|
509
641
|
|
|
510
|
-
|
|
642
|
+
The repo must be set up once with \`rig init\` (writes toon.repoid/toon.owner
|
|
643
|
+
to the local git config); the identity comes from RIG_MNEMONIC (env or a
|
|
644
|
+
project .env), or the ~/.toon-client keystore/config.
|
|
645
|
+
|
|
646
|
+
The relay is a git remote: \`rig push\` publishes via "origin" (set up with
|
|
647
|
+
\`rig remote add origin <relay-url>\`); \`rig push <remote> [refspecs...]\`
|
|
648
|
+
publishes via a named remote. When the first positional matches a configured
|
|
649
|
+
remote name it is the remote; otherwise it is a refspec and the remote
|
|
650
|
+
defaults to origin. Refspecs are branch/tag names or full refnames; default
|
|
651
|
+
is the current branch.
|
|
511
652
|
|
|
512
653
|
Options:
|
|
513
654
|
--force allow non-fast-forward ref updates (overwrites remote history)
|
|
@@ -516,13 +657,12 @@ Options:
|
|
|
516
657
|
--yes skip the fee confirmation (required when not a TTY)
|
|
517
658
|
--json machine-readable plan/receipts; without --yes it is a pure
|
|
518
659
|
estimate (nothing executed)
|
|
519
|
-
--relay <url> relay
|
|
520
|
-
|
|
521
|
-
|
|
660
|
+
--relay <url> ad-hoc relay override (exactly one) \u2014 bypasses the
|
|
661
|
+
configured remotes; every positional is then a refspec.
|
|
662
|
+
The deprecated v0.1 \`git config toon.relay\` still works
|
|
663
|
+
as a fallback, with a migration nudge
|
|
522
664
|
--repo-id <id> repository id / NIP-34 d-tag (default: git config
|
|
523
|
-
toon.repoid
|
|
524
|
-
--daemon force daemon mode (toon-clientd control API)
|
|
525
|
-
--standalone force standalone mode (embedded client from TOON_CLIENT_MNEMONIC)
|
|
665
|
+
toon.repoid \u2014 run \`rig init\` to set it)
|
|
526
666
|
-h, --help show this help`;
|
|
527
667
|
async function selectRefspecs(reader, positionals, all, tags) {
|
|
528
668
|
const { head, refs } = await reader.listRefs();
|
|
@@ -565,7 +705,7 @@ async function selectRefspecs(reader, positionals, all, tags) {
|
|
|
565
705
|
return selected;
|
|
566
706
|
}
|
|
567
707
|
function parsePushArgs(args) {
|
|
568
|
-
const { values, positionals } =
|
|
708
|
+
const { values, positionals } = parseArgs2({
|
|
569
709
|
args,
|
|
570
710
|
options: {
|
|
571
711
|
force: { type: "boolean", default: false },
|
|
@@ -573,8 +713,6 @@ function parsePushArgs(args) {
|
|
|
573
713
|
tags: { type: "boolean", default: false },
|
|
574
714
|
yes: { type: "boolean", default: false },
|
|
575
715
|
json: { type: "boolean", default: false },
|
|
576
|
-
daemon: { type: "boolean", default: false },
|
|
577
|
-
standalone: { type: "boolean", default: false },
|
|
578
716
|
relay: { type: "string", multiple: true },
|
|
579
717
|
"repo-id": { type: "string" },
|
|
580
718
|
help: { type: "boolean", short: "h", default: false }
|
|
@@ -587,8 +725,6 @@ function parsePushArgs(args) {
|
|
|
587
725
|
tags: values.tags ?? false,
|
|
588
726
|
yes: values.yes ?? false,
|
|
589
727
|
json: values.json ?? false,
|
|
590
|
-
daemon: values.daemon ?? false,
|
|
591
|
-
standalone: values.standalone ?? false,
|
|
592
728
|
relay: values.relay ?? [],
|
|
593
729
|
help: values.help ?? false,
|
|
594
730
|
positionals
|
|
@@ -598,7 +734,7 @@ function parsePushArgs(args) {
|
|
|
598
734
|
return flags;
|
|
599
735
|
}
|
|
600
736
|
async function runPush(args, deps) {
|
|
601
|
-
const { io, env
|
|
737
|
+
const { io, env } = deps;
|
|
602
738
|
let flags;
|
|
603
739
|
try {
|
|
604
740
|
flags = parsePushArgs(args);
|
|
@@ -612,91 +748,98 @@ async function runPush(args, deps) {
|
|
|
612
748
|
return 0;
|
|
613
749
|
}
|
|
614
750
|
let standaloneCtx;
|
|
615
|
-
let standalonePlan;
|
|
616
751
|
try {
|
|
617
752
|
const repoRoot = await resolveRepoRoot(deps.cwd);
|
|
618
753
|
const toonConfig = await readToonConfig(repoRoot);
|
|
619
|
-
const repoId = flags.repoId ?? toonConfig.repoId
|
|
754
|
+
const repoId = flags.repoId ?? toonConfig.repoId;
|
|
755
|
+
if (!repoId) throw new UnconfiguredRepoAddressError("repository id");
|
|
620
756
|
const reader = new GitRepoReader(repoRoot);
|
|
757
|
+
let remoteName;
|
|
758
|
+
let refspecArgs = flags.positionals;
|
|
759
|
+
if (flags.relay.length === 0 && refspecArgs.length > 0) {
|
|
760
|
+
const first = refspecArgs[0];
|
|
761
|
+
const remoteNames = new Set(
|
|
762
|
+
(await listGitRemotes(repoRoot)).map((r) => r.name)
|
|
763
|
+
);
|
|
764
|
+
if (remoteNames.has(first)) {
|
|
765
|
+
remoteName = first;
|
|
766
|
+
refspecArgs = refspecArgs.slice(1);
|
|
767
|
+
} else {
|
|
768
|
+
const { refs } = await reader.listRefs();
|
|
769
|
+
const known = new Set(refs.map((r) => r.refname));
|
|
770
|
+
if (!known.has(first) && !known.has(`refs/heads/${first}`) && !known.has(`refs/tags/${first}`)) {
|
|
771
|
+
throw new Error(
|
|
772
|
+
`${JSON.stringify(first)} is neither a configured remote nor a local branch/tag \u2014 add the relay remote (\`rig remote add ${first} <relay-url>\`; \`rig remote list\` shows configured remotes) or fix the refspec`
|
|
773
|
+
);
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
}
|
|
621
777
|
const refspecs = await selectRefspecs(
|
|
622
778
|
reader,
|
|
623
|
-
|
|
779
|
+
refspecArgs,
|
|
624
780
|
flags.all,
|
|
625
781
|
flags.tags
|
|
626
782
|
);
|
|
627
|
-
const
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
fetchImpl
|
|
783
|
+
const resolved = await resolveRelays({
|
|
784
|
+
relayFlags: flags.relay,
|
|
785
|
+
remoteName,
|
|
786
|
+
repoRoot,
|
|
787
|
+
toonRelays: toonConfig.relays
|
|
633
788
|
});
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
repoPath: repoRoot,
|
|
640
|
-
repoId,
|
|
641
|
-
refspecs,
|
|
642
|
-
force: flags.force,
|
|
643
|
-
...explicitRelays ? { relayUrls: explicitRelays } : {}
|
|
644
|
-
};
|
|
645
|
-
if (mode === "daemon") {
|
|
646
|
-
identity = probe.identity;
|
|
647
|
-
relaysUsed ??= probe.relayUrl ? [probe.relayUrl] : void 0;
|
|
648
|
-
plan = await daemonClient.gitEstimate(daemonRequest);
|
|
649
|
-
} else {
|
|
650
|
-
standaloneCtx = await (deps.loadStandalone ?? defaultLoadStandalone)(env);
|
|
651
|
-
identity = standaloneCtx.ownerPubkey;
|
|
652
|
-
relaysUsed ??= standaloneCtx.defaultRelayUrls;
|
|
653
|
-
if (relaysUsed && relaysUsed.length > 1) {
|
|
654
|
-
io.err(
|
|
655
|
-
`standalone mode publishes to a single relay, but ${relaysUsed.length} are configured (${relaysUsed.join(", ")}) \u2014 re-run with exactly one --relay <url> (or trim git config toon.relay). Nothing was uploaded or paid.`
|
|
656
|
-
);
|
|
657
|
-
return 1;
|
|
658
|
-
}
|
|
659
|
-
const remoteState = await standaloneCtx.fetchRemote({
|
|
660
|
-
ownerPubkey: standaloneCtx.ownerPubkey,
|
|
661
|
-
repoId,
|
|
662
|
-
relayUrls: relaysUsed
|
|
663
|
-
});
|
|
664
|
-
const feeRates = await standaloneCtx.publisher.getFeeRates();
|
|
665
|
-
const pushPlan = await planPush({
|
|
666
|
-
repoReader: reader,
|
|
667
|
-
remoteState,
|
|
668
|
-
feeRates,
|
|
669
|
-
repoId,
|
|
670
|
-
refs: refspecs,
|
|
671
|
-
force: flags.force
|
|
672
|
-
});
|
|
673
|
-
plan = serializePushPlan(pushPlan);
|
|
674
|
-
standalonePlan = { pushPlan, remoteState, reader, relaysUsed };
|
|
789
|
+
if (resolved.nudge !== void 0) io.err(resolved.nudge);
|
|
790
|
+
const relaysUsed = resolved.relays;
|
|
791
|
+
if (relaysUsed.length > 1) {
|
|
792
|
+
io.err(singleRelayRefusal(resolved, "Nothing was uploaded or paid."));
|
|
793
|
+
return 1;
|
|
675
794
|
}
|
|
676
|
-
|
|
795
|
+
standaloneCtx = await (deps.loadStandalone ?? defaultLoadStandalone)({
|
|
796
|
+
env,
|
|
797
|
+
cwd: deps.cwd,
|
|
798
|
+
warn: (line) => io.err(line)
|
|
799
|
+
});
|
|
800
|
+
const identity = identityReport(standaloneCtx);
|
|
801
|
+
if (toonConfig.owner && toonConfig.owner !== identity.pubkey) {
|
|
677
802
|
io.err(
|
|
678
|
-
`warning: git config toon.owner (${toonConfig.owner.slice(0, 8)}\u2026) differs from the active
|
|
803
|
+
`warning: git config toon.owner (${toonConfig.owner.slice(0, 8)}\u2026) differs from the active identity (${identity.pubkey.slice(0, 8)}\u2026) \u2014 this push publishes under the ACTIVE identity's repo namespace, not the configured owner's. Re-run \`rig init\` to adopt the active identity.`
|
|
679
804
|
);
|
|
680
805
|
}
|
|
806
|
+
const remoteState = await standaloneCtx.fetchRemote({
|
|
807
|
+
ownerPubkey: standaloneCtx.ownerPubkey,
|
|
808
|
+
repoId,
|
|
809
|
+
relayUrls: relaysUsed
|
|
810
|
+
});
|
|
811
|
+
const feeRates = await standaloneCtx.publisher.getFeeRates();
|
|
812
|
+
const pushPlan = await planPush({
|
|
813
|
+
repoReader: reader,
|
|
814
|
+
remoteState,
|
|
815
|
+
feeRates,
|
|
816
|
+
repoId,
|
|
817
|
+
refs: refspecs,
|
|
818
|
+
force: flags.force
|
|
819
|
+
});
|
|
820
|
+
const plan = serializePushPlan(pushPlan);
|
|
681
821
|
const upToDate = plan.refUpdates.every((u) => u.kind === "up-to-date");
|
|
682
822
|
if (upToDate) {
|
|
683
823
|
if (flags.json) {
|
|
684
|
-
io.out(
|
|
824
|
+
io.out(
|
|
825
|
+
jsonOut({ command: "push", repoId, identity, executed: false, upToDate: true, plan })
|
|
826
|
+
);
|
|
685
827
|
} else {
|
|
686
828
|
io.out("Everything up-to-date \u2014 nothing to push (and nothing paid).");
|
|
687
829
|
}
|
|
688
830
|
return 0;
|
|
689
831
|
}
|
|
690
832
|
if (!flags.json) {
|
|
691
|
-
for (const line of renderPlan(plan
|
|
833
|
+
for (const line of renderPlan(plan)) io.out(line);
|
|
834
|
+
io.out(renderIdentityLine(identity));
|
|
692
835
|
}
|
|
693
836
|
if (!flags.yes) {
|
|
694
837
|
if (flags.json) {
|
|
695
838
|
io.out(
|
|
696
839
|
jsonOut({
|
|
697
840
|
command: "push",
|
|
698
|
-
mode,
|
|
699
841
|
repoId,
|
|
842
|
+
identity,
|
|
700
843
|
executed: false,
|
|
701
844
|
upToDate: false,
|
|
702
845
|
plan,
|
|
@@ -719,41 +862,21 @@ async function runPush(args, deps) {
|
|
|
719
862
|
return 1;
|
|
720
863
|
}
|
|
721
864
|
}
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
const pushResult = await executePush({
|
|
731
|
-
plan: cached.pushPlan,
|
|
732
|
-
publisher: standaloneCtx.publisher,
|
|
733
|
-
remoteState: cached.remoteState,
|
|
734
|
-
repoReader: cached.reader,
|
|
735
|
-
relayUrls: cached.relaysUsed
|
|
736
|
-
});
|
|
737
|
-
result = serializePushResult(cached.pushPlan, pushResult);
|
|
738
|
-
}
|
|
865
|
+
const pushResult = await executePush({
|
|
866
|
+
plan: pushPlan,
|
|
867
|
+
publisher: standaloneCtx.publisher,
|
|
868
|
+
remoteState,
|
|
869
|
+
repoReader: reader,
|
|
870
|
+
relayUrls: relaysUsed
|
|
871
|
+
});
|
|
872
|
+
const result = serializePushResult(pushPlan, pushResult);
|
|
739
873
|
if (flags.json) {
|
|
740
874
|
io.out(
|
|
741
|
-
jsonOut({ command: "push",
|
|
875
|
+
jsonOut({ command: "push", repoId, identity, executed: true, upToDate: false, plan, result })
|
|
742
876
|
);
|
|
743
877
|
} else {
|
|
744
878
|
for (const line of renderResult(result)) io.out(line);
|
|
745
879
|
}
|
|
746
|
-
try {
|
|
747
|
-
await writeToonConfig(repoRoot, {
|
|
748
|
-
repoId,
|
|
749
|
-
...identity ? { owner: identity } : {},
|
|
750
|
-
...relaysUsed ? { relays: relaysUsed } : {}
|
|
751
|
-
});
|
|
752
|
-
} catch (err) {
|
|
753
|
-
io.err(
|
|
754
|
-
`warning: push succeeded but persisting git config failed: ${err instanceof Error ? err.message : String(err)}`
|
|
755
|
-
);
|
|
756
|
-
}
|
|
757
880
|
return 0;
|
|
758
881
|
} catch (err) {
|
|
759
882
|
const described = describeError(err);
|
|
@@ -783,23 +906,24 @@ var defaultReadStdin = async () => {
|
|
|
783
906
|
for await (const chunk of process.stdin) chunks.push(chunk);
|
|
784
907
|
return Buffer.concat(chunks).toString("utf-8");
|
|
785
908
|
};
|
|
786
|
-
var COMMON_FLAGS_USAGE = ` --repo-id <id> repository id / NIP-34 d-tag (default: git config
|
|
909
|
+
var COMMON_FLAGS_USAGE = ` --repo-id <id> repository id / NIP-34 d-tag (default: git config
|
|
910
|
+
toon.repoid \u2014 run \`rig init\` to set it)
|
|
787
911
|
--owner <pubkey> repository owner pubkey, 64-char hex (default: git config
|
|
788
|
-
toon.owner, then the active
|
|
789
|
-
--
|
|
790
|
-
|
|
791
|
-
|
|
912
|
+
toon.owner, then the active identity)
|
|
913
|
+
--remote <name> publish via this configured git remote (default: the
|
|
914
|
+
"origin" remote \u2014 \`rig remote add origin <relay-url>\`)
|
|
915
|
+
--relay <url> ad-hoc relay override (exactly one) \u2014 bypasses the
|
|
916
|
+
configured remotes. The deprecated v0.1 \`git config
|
|
917
|
+
toon.relay\` still works as a fallback, with a nudge
|
|
792
918
|
--yes skip the fee confirmation (required when not a TTY)
|
|
793
919
|
--json machine-readable receipt; without --yes it is a pure
|
|
794
920
|
estimate (nothing published, exit 0)
|
|
795
|
-
--daemon force daemon mode (toon-clientd control API)
|
|
796
|
-
--standalone force standalone mode (embedded client from TOON_CLIENT_MNEMONIC)
|
|
797
921
|
-h, --help show this help`;
|
|
798
922
|
var ISSUE_USAGE = `Usage: rig issue create --title <title> [options]
|
|
799
923
|
|
|
800
924
|
File an issue (kind:1621) against a TOON repo \u2014 a paid publish; writes are
|
|
801
925
|
permanent and non-refundable. The repo address (30617:<owner>:<repoId>) comes
|
|
802
|
-
from the toon.* git config keys \`rig
|
|
926
|
+
from the toon.* git config keys \`rig init\` writes.
|
|
803
927
|
|
|
804
928
|
Body source (exactly one): --body, --body-file, or piped stdin.
|
|
805
929
|
|
|
@@ -822,7 +946,7 @@ Options:
|
|
|
822
946
|
--marker <root|reply> e-tag marker (default: root \u2014 commenting directly
|
|
823
947
|
on the issue/patch)
|
|
824
948
|
${COMMON_FLAGS_USAGE}`;
|
|
825
|
-
var
|
|
949
|
+
var PR_CREATE_USAGE = `Usage: rig pr create --title <title> (--range <range> | --patch-file <path>) [options]
|
|
826
950
|
|
|
827
951
|
Publish a patch (kind:1617) whose content is REAL \`git format-patch\` output \u2014
|
|
828
952
|
a paid publish; writes are permanent and non-refundable. --range runs
|
|
@@ -839,16 +963,20 @@ Options:
|
|
|
839
963
|
--patch-file <path> literal patch text to publish
|
|
840
964
|
--branch <name> branch name (t tag)
|
|
841
965
|
${COMMON_FLAGS_USAGE}`;
|
|
842
|
-
var
|
|
966
|
+
var PR_STATUS_USAGE = `Usage: rig pr status <target-event-id> <open|applied|closed|draft> [options]
|
|
843
967
|
|
|
844
968
|
Set the status of an issue or patch \u2014 a paid publish; writes are permanent
|
|
845
969
|
and non-refundable. Publishes kind:1630 (open), 1631 (applied), 1632
|
|
846
970
|
(closed), or 1633 (draft) against the 64-char hex id of the target event,
|
|
847
971
|
with the repo a-tag attached so readers can scope a status stream to the
|
|
848
|
-
repository.
|
|
972
|
+
repository. (This command was \`rig status\` before v2 \u2014 bare \`rig status\`
|
|
973
|
+
now passes through to \`git status\`.)
|
|
849
974
|
|
|
850
975
|
Options:
|
|
851
976
|
${COMMON_FLAGS_USAGE}`;
|
|
977
|
+
var PR_USAGE = `${PR_CREATE_USAGE}
|
|
978
|
+
|
|
979
|
+
${PR_STATUS_USAGE}`;
|
|
852
980
|
var HEX64_RE = /^[0-9a-f]{64}$/;
|
|
853
981
|
function assertHex64(value, what) {
|
|
854
982
|
if (!HEX64_RE.test(value)) {
|
|
@@ -860,9 +988,8 @@ function assertHex64(value, what) {
|
|
|
860
988
|
var COMMON_OPTIONS = {
|
|
861
989
|
yes: { type: "boolean", default: false },
|
|
862
990
|
json: { type: "boolean", default: false },
|
|
863
|
-
daemon: { type: "boolean", default: false },
|
|
864
|
-
standalone: { type: "boolean", default: false },
|
|
865
991
|
relay: { type: "string", multiple: true },
|
|
992
|
+
remote: { type: "string" },
|
|
866
993
|
"repo-id": { type: "string" },
|
|
867
994
|
owner: { type: "string" },
|
|
868
995
|
help: { type: "boolean", short: "h", default: false }
|
|
@@ -871,11 +998,11 @@ function pickCommon(values) {
|
|
|
871
998
|
const flags = {
|
|
872
999
|
yes: values["yes"] === true,
|
|
873
1000
|
json: values["json"] === true,
|
|
874
|
-
daemon: values["daemon"] === true,
|
|
875
|
-
standalone: values["standalone"] === true,
|
|
876
1001
|
relay: Array.isArray(values["relay"]) ? values["relay"] : [],
|
|
877
1002
|
help: values["help"] === true
|
|
878
1003
|
};
|
|
1004
|
+
const remote = values["remote"];
|
|
1005
|
+
if (typeof remote === "string") flags.remote = remote;
|
|
879
1006
|
const repoId = values["repo-id"];
|
|
880
1007
|
if (typeof repoId === "string") flags.repoId = repoId;
|
|
881
1008
|
const owner = values["owner"];
|
|
@@ -887,54 +1014,43 @@ function pickCommon(values) {
|
|
|
887
1014
|
}
|
|
888
1015
|
async function runEvent(opts) {
|
|
889
1016
|
const { command, flags, deps, actionLabel } = opts;
|
|
890
|
-
const { io, env
|
|
1017
|
+
const { io, env } = deps;
|
|
891
1018
|
let standaloneCtx;
|
|
892
1019
|
try {
|
|
1020
|
+
let repoRoot;
|
|
893
1021
|
let toonConfig = { relays: [] };
|
|
894
1022
|
try {
|
|
895
|
-
|
|
1023
|
+
repoRoot = await resolveRepoRoot(deps.cwd);
|
|
1024
|
+
toonConfig = await readToonConfig(repoRoot);
|
|
896
1025
|
} catch {
|
|
897
1026
|
}
|
|
898
1027
|
const repoId = flags.repoId ?? toonConfig.repoId;
|
|
899
1028
|
if (!repoId) throw new UnconfiguredRepoAddressError("repository id");
|
|
900
|
-
const
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
fetchImpl
|
|
1029
|
+
const resolved = await resolveRelays({
|
|
1030
|
+
relayFlags: flags.relay,
|
|
1031
|
+
remoteName: flags.remote,
|
|
1032
|
+
repoRoot,
|
|
1033
|
+
toonRelays: toonConfig.relays
|
|
906
1034
|
});
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
identity = probe.identity;
|
|
913
|
-
fee = probe.feePerEvent;
|
|
914
|
-
} else {
|
|
915
|
-
standaloneCtx = await (deps.loadStandalone ?? defaultLoadStandalone)(env);
|
|
916
|
-
identity = standaloneCtx.ownerPubkey;
|
|
917
|
-
relaysUsed ??= standaloneCtx.defaultRelayUrls;
|
|
918
|
-
if (relaysUsed.length > 1) {
|
|
919
|
-
io.err(
|
|
920
|
-
`standalone mode publishes to a single relay, but ${relaysUsed.length} are configured (${relaysUsed.join(", ")}) \u2014 re-run with exactly one --relay <url> (or trim git config toon.relay). Nothing was published or paid.`
|
|
921
|
-
);
|
|
922
|
-
return 1;
|
|
923
|
-
}
|
|
924
|
-
fee = (await standaloneCtx.publisher.getFeeRates()).eventFee.toString();
|
|
1035
|
+
if (resolved.nudge !== void 0) io.err(resolved.nudge);
|
|
1036
|
+
const relaysUsed = resolved.relays;
|
|
1037
|
+
if (relaysUsed.length > 1) {
|
|
1038
|
+
io.err(singleRelayRefusal(resolved, "Nothing was published or paid."));
|
|
1039
|
+
return 1;
|
|
925
1040
|
}
|
|
926
|
-
|
|
927
|
-
|
|
1041
|
+
standaloneCtx = await (deps.loadStandalone ?? defaultLoadStandalone)({
|
|
1042
|
+
env,
|
|
1043
|
+
cwd: deps.cwd,
|
|
1044
|
+
warn: (line) => io.err(line)
|
|
1045
|
+
});
|
|
1046
|
+
const identity = identityReport(standaloneCtx);
|
|
1047
|
+
const fee = (await standaloneCtx.publisher.getFeeRates()).eventFee.toString();
|
|
1048
|
+
const owner = flags.owner ?? toonConfig.owner ?? identity.pubkey;
|
|
928
1049
|
const addr = { ownerPubkey: owner, repoId };
|
|
929
1050
|
const event = await opts.buildEvent(addr);
|
|
930
1051
|
const action = `kind:${event.kind} ${actionLabel}`;
|
|
931
1052
|
if (!flags.json) {
|
|
932
|
-
for (const line of renderEventPlan({
|
|
933
|
-
action,
|
|
934
|
-
addr,
|
|
935
|
-
mode,
|
|
936
|
-
...fee !== void 0 ? { fee } : {}
|
|
937
|
-
})) {
|
|
1053
|
+
for (const line of renderEventPlan({ action, addr, identity, fee })) {
|
|
938
1054
|
io.out(line);
|
|
939
1055
|
}
|
|
940
1056
|
}
|
|
@@ -943,11 +1059,11 @@ async function runEvent(opts) {
|
|
|
943
1059
|
io.out(
|
|
944
1060
|
jsonOut2({
|
|
945
1061
|
command,
|
|
946
|
-
mode,
|
|
947
1062
|
repoAddr: addr,
|
|
1063
|
+
identity,
|
|
948
1064
|
kind: event.kind,
|
|
949
1065
|
executed: false,
|
|
950
|
-
feeEstimate: fee
|
|
1066
|
+
feeEstimate: fee,
|
|
951
1067
|
hint: "estimate only \u2014 re-run with --yes to publish (permanent, non-refundable)"
|
|
952
1068
|
})
|
|
953
1069
|
);
|
|
@@ -967,26 +1083,17 @@ async function runEvent(opts) {
|
|
|
967
1083
|
return 1;
|
|
968
1084
|
}
|
|
969
1085
|
}
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
result = await opts.sendDaemon(daemonClient, addr);
|
|
973
|
-
} else {
|
|
974
|
-
if (!standaloneCtx) throw new Error("internal: standalone context missing");
|
|
975
|
-
const receipt = await standaloneCtx.publisher.publishEvent(
|
|
976
|
-
event,
|
|
977
|
-
relaysUsed ?? []
|
|
978
|
-
);
|
|
979
|
-
result = serializeEventReceipt(event.kind, receipt);
|
|
980
|
-
}
|
|
1086
|
+
const receipt = await standaloneCtx.publisher.publishEvent(event, relaysUsed);
|
|
1087
|
+
const result = serializeEventReceipt(event.kind, receipt);
|
|
981
1088
|
if (flags.json) {
|
|
982
1089
|
io.out(
|
|
983
1090
|
jsonOut2({
|
|
984
1091
|
command,
|
|
985
|
-
mode,
|
|
986
1092
|
repoAddr: addr,
|
|
1093
|
+
identity,
|
|
987
1094
|
kind: result.kind,
|
|
988
1095
|
executed: true,
|
|
989
|
-
feeEstimate: fee
|
|
1096
|
+
feeEstimate: fee,
|
|
990
1097
|
result
|
|
991
1098
|
})
|
|
992
1099
|
);
|
|
@@ -1034,7 +1141,7 @@ async function runIssue(args, deps) {
|
|
|
1034
1141
|
let bodyFile;
|
|
1035
1142
|
let labels;
|
|
1036
1143
|
try {
|
|
1037
|
-
const { values } =
|
|
1144
|
+
const { values } = parseArgs3({
|
|
1038
1145
|
args: rest,
|
|
1039
1146
|
options: {
|
|
1040
1147
|
...COMMON_OPTIONS,
|
|
@@ -1093,13 +1200,7 @@ async function runIssue(args, deps) {
|
|
|
1093
1200
|
flags,
|
|
1094
1201
|
deps,
|
|
1095
1202
|
actionLabel: `issue ${JSON.stringify(title)}`,
|
|
1096
|
-
buildEvent: async (addr) => buildIssue(addr.ownerPubkey, addr.repoId, title, body, labels)
|
|
1097
|
-
sendDaemon: (client, addr) => client.gitIssue({
|
|
1098
|
-
repoAddr: addr,
|
|
1099
|
-
title,
|
|
1100
|
-
body,
|
|
1101
|
-
...labels.length > 0 ? { labels } : {}
|
|
1102
|
-
})
|
|
1203
|
+
buildEvent: async (addr) => buildIssue(addr.ownerPubkey, addr.repoId, title, body, labels)
|
|
1103
1204
|
});
|
|
1104
1205
|
}
|
|
1105
1206
|
async function runComment(args, deps) {
|
|
@@ -1110,7 +1211,7 @@ async function runComment(args, deps) {
|
|
|
1110
1211
|
let parentAuthor;
|
|
1111
1212
|
let marker;
|
|
1112
1213
|
try {
|
|
1113
|
-
const { values, positionals } =
|
|
1214
|
+
const { values, positionals } = parseArgs3({
|
|
1114
1215
|
args,
|
|
1115
1216
|
options: {
|
|
1116
1217
|
...COMMON_OPTIONS,
|
|
@@ -1160,14 +1261,7 @@ async function runComment(args, deps) {
|
|
|
1160
1261
|
parentAuthor ?? addr.ownerPubkey,
|
|
1161
1262
|
body,
|
|
1162
1263
|
marker
|
|
1163
|
-
)
|
|
1164
|
-
sendDaemon: (client, addr) => client.gitComment({
|
|
1165
|
-
repoAddr: addr,
|
|
1166
|
-
rootEventId,
|
|
1167
|
-
body,
|
|
1168
|
-
marker,
|
|
1169
|
-
...parentAuthor !== void 0 ? { parentAuthorPubkey: parentAuthor } : {}
|
|
1170
|
-
})
|
|
1264
|
+
)
|
|
1171
1265
|
});
|
|
1172
1266
|
}
|
|
1173
1267
|
var PATCH_FROM_RE = /^From ([0-9a-f]{40}) /gm;
|
|
@@ -1177,24 +1271,33 @@ function extractPatchShas(patchText) {
|
|
|
1177
1271
|
async function runPr(args, deps) {
|
|
1178
1272
|
const { io } = deps;
|
|
1179
1273
|
const [sub, ...rest] = args;
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1274
|
+
switch (sub) {
|
|
1275
|
+
case "create":
|
|
1276
|
+
return runPrCreate(rest, deps);
|
|
1277
|
+
case "status":
|
|
1278
|
+
return runPrStatus(rest, deps);
|
|
1279
|
+
case "--help":
|
|
1280
|
+
case "-h":
|
|
1281
|
+
case "help":
|
|
1282
|
+
io.out(PR_USAGE);
|
|
1283
|
+
return 0;
|
|
1284
|
+
default:
|
|
1285
|
+
io.err(
|
|
1286
|
+
sub === void 0 ? "missing subcommand: rig pr <create|status>" : `unknown rig pr subcommand: ${sub}`
|
|
1287
|
+
);
|
|
1288
|
+
io.err(PR_USAGE);
|
|
1289
|
+
return 2;
|
|
1190
1290
|
}
|
|
1291
|
+
}
|
|
1292
|
+
async function runPrCreate(rest, deps) {
|
|
1293
|
+
const { io } = deps;
|
|
1191
1294
|
let flags;
|
|
1192
1295
|
let title;
|
|
1193
1296
|
let range;
|
|
1194
1297
|
let patchFile;
|
|
1195
1298
|
let branch;
|
|
1196
1299
|
try {
|
|
1197
|
-
const { values } =
|
|
1300
|
+
const { values } = parseArgs3({
|
|
1198
1301
|
args: rest,
|
|
1199
1302
|
options: {
|
|
1200
1303
|
...COMMON_OPTIONS,
|
|
@@ -1207,7 +1310,7 @@ async function runPr(args, deps) {
|
|
|
1207
1310
|
});
|
|
1208
1311
|
flags = pickCommon(values);
|
|
1209
1312
|
if (flags.help) {
|
|
1210
|
-
io.out(
|
|
1313
|
+
io.out(PR_CREATE_USAGE);
|
|
1211
1314
|
return 0;
|
|
1212
1315
|
}
|
|
1213
1316
|
if (values.title === void 0 || values.title === "") {
|
|
@@ -1222,43 +1325,38 @@ async function runPr(args, deps) {
|
|
|
1222
1325
|
branch = values.branch;
|
|
1223
1326
|
} catch (err) {
|
|
1224
1327
|
io.err(err instanceof Error ? err.message : String(err));
|
|
1225
|
-
io.err(
|
|
1328
|
+
io.err(PR_CREATE_USAGE);
|
|
1226
1329
|
return 2;
|
|
1227
1330
|
}
|
|
1228
|
-
let prepared;
|
|
1229
|
-
const prepare = async () => {
|
|
1230
|
-
if (prepared) return prepared;
|
|
1231
|
-
if (range !== void 0) {
|
|
1232
|
-
const reader = new GitRepoReader(await resolveRepoRoot(deps.cwd));
|
|
1233
|
-
const patchText = await reader.formatPatch(range);
|
|
1234
|
-
if (patchText === "") {
|
|
1235
|
-
throw new Error(
|
|
1236
|
-
`range ${JSON.stringify(range)} selects no commits \u2014 nothing to publish`
|
|
1237
|
-
);
|
|
1238
|
-
}
|
|
1239
|
-
const shas = extractPatchShas(patchText);
|
|
1240
|
-
const parents = await reader.commitParents(shas);
|
|
1241
|
-
const commits = shas.flatMap((sha) => {
|
|
1242
|
-
const parentSha = parents.get(sha)?.[0];
|
|
1243
|
-
return parentSha ? [{ sha, parentSha }] : [];
|
|
1244
|
-
});
|
|
1245
|
-
prepared = { patchText, commits };
|
|
1246
|
-
} else {
|
|
1247
|
-
const patchText = await readFile(patchFile, "utf-8");
|
|
1248
|
-
if (patchText.trim() === "") {
|
|
1249
|
-
throw new Error(`--patch-file ${patchFile} is empty \u2014 nothing to publish`);
|
|
1250
|
-
}
|
|
1251
|
-
prepared = { patchText, commits: [] };
|
|
1252
|
-
}
|
|
1253
|
-
return prepared;
|
|
1254
|
-
};
|
|
1255
1331
|
return runEvent({
|
|
1256
1332
|
command: "pr",
|
|
1257
1333
|
flags,
|
|
1258
1334
|
deps,
|
|
1259
1335
|
actionLabel: `patch ${JSON.stringify(title)}`,
|
|
1260
1336
|
buildEvent: async (addr) => {
|
|
1261
|
-
|
|
1337
|
+
let patchText;
|
|
1338
|
+
let commits;
|
|
1339
|
+
if (range !== void 0) {
|
|
1340
|
+
const reader = new GitRepoReader(await resolveRepoRoot(deps.cwd));
|
|
1341
|
+
patchText = await reader.formatPatch(range);
|
|
1342
|
+
if (patchText === "") {
|
|
1343
|
+
throw new Error(
|
|
1344
|
+
`range ${JSON.stringify(range)} selects no commits \u2014 nothing to publish`
|
|
1345
|
+
);
|
|
1346
|
+
}
|
|
1347
|
+
const shas = extractPatchShas(patchText);
|
|
1348
|
+
const parents = await reader.commitParents(shas);
|
|
1349
|
+
commits = shas.flatMap((sha) => {
|
|
1350
|
+
const parentSha = parents.get(sha)?.[0];
|
|
1351
|
+
return parentSha ? [{ sha, parentSha }] : [];
|
|
1352
|
+
});
|
|
1353
|
+
} else {
|
|
1354
|
+
patchText = await readFile(patchFile, "utf-8");
|
|
1355
|
+
if (patchText.trim() === "") {
|
|
1356
|
+
throw new Error(`--patch-file ${patchFile} is empty \u2014 nothing to publish`);
|
|
1357
|
+
}
|
|
1358
|
+
commits = [];
|
|
1359
|
+
}
|
|
1262
1360
|
return buildPatch(
|
|
1263
1361
|
addr.ownerPubkey,
|
|
1264
1362
|
addr.repoId,
|
|
@@ -1267,16 +1365,6 @@ async function runPr(args, deps) {
|
|
|
1267
1365
|
branch,
|
|
1268
1366
|
patchText
|
|
1269
1367
|
);
|
|
1270
|
-
},
|
|
1271
|
-
sendDaemon: async (client, addr) => {
|
|
1272
|
-
const { patchText, commits } = await prepare();
|
|
1273
|
-
return client.gitPatch({
|
|
1274
|
-
repoAddr: addr,
|
|
1275
|
-
title,
|
|
1276
|
-
patchText,
|
|
1277
|
-
...commits.length > 0 ? { commits } : {},
|
|
1278
|
-
...branch !== void 0 ? { branch } : {}
|
|
1279
|
-
});
|
|
1280
1368
|
}
|
|
1281
1369
|
});
|
|
1282
1370
|
}
|
|
@@ -1289,20 +1377,20 @@ var STATUS_KIND_BY_VALUE = {
|
|
|
1289
1377
|
function isStatusValue(value) {
|
|
1290
1378
|
return Object.hasOwn(STATUS_KIND_BY_VALUE, value);
|
|
1291
1379
|
}
|
|
1292
|
-
async function
|
|
1380
|
+
async function runPrStatus(args, deps) {
|
|
1293
1381
|
const { io } = deps;
|
|
1294
1382
|
let flags;
|
|
1295
1383
|
let targetEventId;
|
|
1296
1384
|
let status;
|
|
1297
1385
|
try {
|
|
1298
|
-
const { values, positionals } =
|
|
1386
|
+
const { values, positionals } = parseArgs3({
|
|
1299
1387
|
args,
|
|
1300
1388
|
options: COMMON_OPTIONS,
|
|
1301
1389
|
allowPositionals: true
|
|
1302
1390
|
});
|
|
1303
1391
|
flags = pickCommon(values);
|
|
1304
1392
|
if (flags.help) {
|
|
1305
|
-
io.out(
|
|
1393
|
+
io.out(PR_STATUS_USAGE);
|
|
1306
1394
|
return 0;
|
|
1307
1395
|
}
|
|
1308
1396
|
if (positionals.length !== 2) {
|
|
@@ -1321,11 +1409,11 @@ async function runStatus(args, deps) {
|
|
|
1321
1409
|
status = rawStatus;
|
|
1322
1410
|
} catch (err) {
|
|
1323
1411
|
io.err(err instanceof Error ? err.message : String(err));
|
|
1324
|
-
io.err(
|
|
1412
|
+
io.err(PR_STATUS_USAGE);
|
|
1325
1413
|
return 2;
|
|
1326
1414
|
}
|
|
1327
1415
|
return runEvent({
|
|
1328
|
-
command: "status",
|
|
1416
|
+
command: "pr status",
|
|
1329
1417
|
flags,
|
|
1330
1418
|
deps,
|
|
1331
1419
|
actionLabel: `status ${status} on ${targetEventId.slice(0, 8)}\u2026`,
|
|
@@ -1336,59 +1424,261 @@ async function runStatus(args, deps) {
|
|
|
1336
1424
|
`${REPOSITORY_ANNOUNCEMENT_KIND}:${addr.ownerPubkey}:${addr.repoId}`
|
|
1337
1425
|
]);
|
|
1338
1426
|
return event;
|
|
1427
|
+
}
|
|
1428
|
+
});
|
|
1429
|
+
}
|
|
1430
|
+
|
|
1431
|
+
// src/cli/git-passthrough.ts
|
|
1432
|
+
import { spawn } from "child_process";
|
|
1433
|
+
import { constants as osConstants } from "os";
|
|
1434
|
+
var GIT_NOT_FOUND_EXIT = 127;
|
|
1435
|
+
var RELAYED_SIGNALS = ["SIGINT", "SIGTERM", "SIGHUP"];
|
|
1436
|
+
function signalExitCode(signal) {
|
|
1437
|
+
const num = osConstants.signals[signal];
|
|
1438
|
+
return num === void 0 ? 1 : 128 + num;
|
|
1439
|
+
}
|
|
1440
|
+
var runGitPassthrough = (argv, options = {}) => {
|
|
1441
|
+
const err = options.err ?? ((line) => process.stderr.write(`${line}
|
|
1442
|
+
`));
|
|
1443
|
+
return new Promise((resolve) => {
|
|
1444
|
+
const child = spawn("git", argv, {
|
|
1445
|
+
stdio: "inherit",
|
|
1446
|
+
...options.cwd !== void 0 ? { cwd: options.cwd } : {},
|
|
1447
|
+
env: options.env ?? process.env
|
|
1448
|
+
});
|
|
1449
|
+
const relay = /* @__PURE__ */ new Map();
|
|
1450
|
+
for (const signal of RELAYED_SIGNALS) {
|
|
1451
|
+
const handler = () => {
|
|
1452
|
+
child.kill(signal);
|
|
1453
|
+
};
|
|
1454
|
+
relay.set(signal, handler);
|
|
1455
|
+
process.on(signal, handler);
|
|
1456
|
+
}
|
|
1457
|
+
const restoreSignals = () => {
|
|
1458
|
+
for (const [signal, handler] of relay) process.removeListener(signal, handler);
|
|
1459
|
+
};
|
|
1460
|
+
child.on("error", (spawnErr) => {
|
|
1461
|
+
restoreSignals();
|
|
1462
|
+
if (spawnErr.code === "ENOENT") {
|
|
1463
|
+
err(
|
|
1464
|
+
`rig: git not found \u2014 \`rig ${argv[0] ?? ""}\` is not a rig command, so it is passed through to the system \`git\`, which is not on your PATH. Install git (https://git-scm.com) or fix your PATH.`
|
|
1465
|
+
);
|
|
1466
|
+
resolve(GIT_NOT_FOUND_EXIT);
|
|
1467
|
+
return;
|
|
1468
|
+
}
|
|
1469
|
+
err(`rig: failed to run git: ${spawnErr.message}`);
|
|
1470
|
+
resolve(1);
|
|
1471
|
+
});
|
|
1472
|
+
child.on("close", (code, signal) => {
|
|
1473
|
+
restoreSignals();
|
|
1474
|
+
resolve(signal !== null ? signalExitCode(signal) : code ?? 1);
|
|
1475
|
+
});
|
|
1476
|
+
});
|
|
1477
|
+
};
|
|
1478
|
+
|
|
1479
|
+
// src/cli/init.ts
|
|
1480
|
+
import { basename } from "path";
|
|
1481
|
+
import { parseArgs as parseArgs4 } from "util";
|
|
1482
|
+
var INIT_USAGE = `Usage: rig init [options]
|
|
1483
|
+
|
|
1484
|
+
Set up the current git repository for rig (one-shot, idempotent, free):
|
|
1485
|
+
resolves your identity (RIG_MNEMONIC env \u2192 project .env \u2192 ~/.toon-client
|
|
1486
|
+
keystore/config), then writes toon.repoid and toon.owner to the repo's
|
|
1487
|
+
LOCAL git config. Re-running updates and reports; it never errors on an
|
|
1488
|
+
already-initialized repo. The seed phrase is never written anywhere.
|
|
1489
|
+
|
|
1490
|
+
The follow-up step is adding a relay: \`rig remote add origin <relay-url>\`
|
|
1491
|
+
(a real git remote \u2014 \`git remote -v\` shows it). A deprecated v0.1
|
|
1492
|
+
\`git config toon.relay\` is migrated to the origin remote automatically
|
|
1493
|
+
when no origin exists (the old key stays readable and is removed in v0.3).
|
|
1494
|
+
|
|
1495
|
+
Options:
|
|
1496
|
+
--repo-id <id> repository id / NIP-34 d-tag (default: the existing
|
|
1497
|
+
toon.repoid, then the repo directory basename)
|
|
1498
|
+
--json machine-readable report
|
|
1499
|
+
-h, --help show this help`;
|
|
1500
|
+
function parseInitArgs(args) {
|
|
1501
|
+
const { values, positionals } = parseArgs4({
|
|
1502
|
+
args,
|
|
1503
|
+
options: {
|
|
1504
|
+
"repo-id": { type: "string" },
|
|
1505
|
+
json: { type: "boolean", default: false },
|
|
1506
|
+
help: { type: "boolean", short: "h", default: false }
|
|
1339
1507
|
},
|
|
1340
|
-
|
|
1508
|
+
allowPositionals: false
|
|
1341
1509
|
});
|
|
1510
|
+
if (positionals.length > 0) {
|
|
1511
|
+
throw new Error(`rig init takes no positional arguments`);
|
|
1512
|
+
}
|
|
1513
|
+
const flags = {
|
|
1514
|
+
json: values.json ?? false,
|
|
1515
|
+
help: values.help ?? false
|
|
1516
|
+
};
|
|
1517
|
+
const repoId = values["repo-id"];
|
|
1518
|
+
if (repoId !== void 0) {
|
|
1519
|
+
if (repoId.trim() === "") throw new Error("--repo-id must not be empty");
|
|
1520
|
+
flags.repoId = repoId;
|
|
1521
|
+
}
|
|
1522
|
+
return flags;
|
|
1523
|
+
}
|
|
1524
|
+
async function runInit(args, deps) {
|
|
1525
|
+
const { io, env } = deps;
|
|
1526
|
+
let flags;
|
|
1527
|
+
try {
|
|
1528
|
+
flags = parseInitArgs(args);
|
|
1529
|
+
} catch (err) {
|
|
1530
|
+
io.err(err instanceof Error ? err.message : String(err));
|
|
1531
|
+
io.err(INIT_USAGE);
|
|
1532
|
+
return 2;
|
|
1533
|
+
}
|
|
1534
|
+
if (flags.help) {
|
|
1535
|
+
io.out(INIT_USAGE);
|
|
1536
|
+
return 0;
|
|
1537
|
+
}
|
|
1538
|
+
try {
|
|
1539
|
+
let repoRoot;
|
|
1540
|
+
try {
|
|
1541
|
+
repoRoot = await resolveRepoRoot(deps.cwd);
|
|
1542
|
+
} catch {
|
|
1543
|
+
throw new NotAGitRepositoryError(deps.cwd);
|
|
1544
|
+
}
|
|
1545
|
+
const identity = await (deps.resolveIdentityImpl ?? resolveIdentity)({
|
|
1546
|
+
env,
|
|
1547
|
+
cwd: deps.cwd,
|
|
1548
|
+
warn: (line) => io.err(line)
|
|
1549
|
+
});
|
|
1550
|
+
const existing = await readToonConfig(repoRoot);
|
|
1551
|
+
const repoId = flags.repoId ?? existing.repoId ?? basename(repoRoot);
|
|
1552
|
+
const changed = {
|
|
1553
|
+
repoId: existing.repoId !== repoId,
|
|
1554
|
+
owner: existing.owner !== identity.pubkey
|
|
1555
|
+
};
|
|
1556
|
+
await writeToonConfig(repoRoot, { repoId, owner: identity.pubkey });
|
|
1557
|
+
let remotes = await listGitRemotes(repoRoot);
|
|
1558
|
+
let migratedToonRelay = false;
|
|
1559
|
+
const legacyRelay = existing.relays[0];
|
|
1560
|
+
if (!remotes.some((r) => r.name === "origin") && existing.relays.length === 1 && legacyRelay !== void 0 && isRelayUrl(legacyRelay)) {
|
|
1561
|
+
await addGitRemote(repoRoot, "origin", legacyRelay);
|
|
1562
|
+
migratedToonRelay = true;
|
|
1563
|
+
remotes = await listGitRemotes(repoRoot);
|
|
1564
|
+
}
|
|
1565
|
+
const origin = remotes.find((r) => r.name === "origin");
|
|
1566
|
+
const originRelay = origin !== void 0 && origin.urls.length === 1 && isRelayUrl(origin.urls[0]) ? origin.urls[0] : void 0;
|
|
1567
|
+
if (flags.json) {
|
|
1568
|
+
const output = {
|
|
1569
|
+
command: "init",
|
|
1570
|
+
repoRoot,
|
|
1571
|
+
repoId,
|
|
1572
|
+
owner: identity.pubkey,
|
|
1573
|
+
identity: {
|
|
1574
|
+
source: identity.source,
|
|
1575
|
+
sourceLabel: identity.sourceLabel,
|
|
1576
|
+
pubkey: identity.pubkey
|
|
1577
|
+
},
|
|
1578
|
+
relays: existing.relays,
|
|
1579
|
+
relayConfigured: originRelay !== void 0 || existing.relays.length > 0,
|
|
1580
|
+
remotes,
|
|
1581
|
+
origin: originRelay ?? null,
|
|
1582
|
+
migratedToonRelay,
|
|
1583
|
+
changed
|
|
1584
|
+
};
|
|
1585
|
+
io.out(JSON.stringify(output, null, 2));
|
|
1586
|
+
return 0;
|
|
1587
|
+
}
|
|
1588
|
+
io.out(`Initialized rig for ${repoRoot}`);
|
|
1589
|
+
io.out(renderIdentityLine(identity));
|
|
1590
|
+
io.out(
|
|
1591
|
+
` toon.repoid = ${repoId}` + (changed.repoId ? "" : " (unchanged)") + (existing.repoId && changed.repoId ? ` (was ${existing.repoId})` : "")
|
|
1592
|
+
);
|
|
1593
|
+
io.out(
|
|
1594
|
+
` toon.owner = ${identity.pubkey}` + (changed.owner ? "" : " (unchanged)") + (existing.owner && changed.owner ? ` (was ${existing.owner})` : "")
|
|
1595
|
+
);
|
|
1596
|
+
if (originRelay !== void 0) {
|
|
1597
|
+
io.out(
|
|
1598
|
+
` origin = ${originRelay}` + (migratedToonRelay ? " (migrated from git config toon.relay)" : "")
|
|
1599
|
+
);
|
|
1600
|
+
if (migratedToonRelay) {
|
|
1601
|
+
io.out(
|
|
1602
|
+
"note: toon.relay is deprecated \u2014 it stays readable as a fallback and is removed in v0.3; drop it now with `git config --unset-all toon.relay`."
|
|
1603
|
+
);
|
|
1604
|
+
}
|
|
1605
|
+
io.out('Ready: `rig push` publishes this repo via remote "origin".');
|
|
1606
|
+
} else if (existing.relays.length > 0) {
|
|
1607
|
+
io.out(` toon.relay = ${existing.relays.join(", ")} (deprecated)`);
|
|
1608
|
+
io.out(
|
|
1609
|
+
origin !== void 0 ? `The "origin" remote (${origin.urls.join(", ")}) is not a single relay URL, so toon.relay stays the fallback \u2014 add the relay under another name (\`rig remote add toon <relay-url>\`) and push with \`rig push toon\`.` : existing.relays.length > 1 ? `toon.relay has ${existing.relays.length} values and rig publishes to one relay \u2014 migrate the right one: \`rig remote add origin <relay-url>\`.` : "This value is not a relay URL (ws://, wss://, http://, or https://) \u2014 set a real one: `rig remote add origin <relay-url>`."
|
|
1610
|
+
);
|
|
1611
|
+
} else if (origin !== void 0) {
|
|
1612
|
+
io.out(
|
|
1613
|
+
`No relay configured yet \u2014 "origin" (${origin.urls.join(", ")}) is not a relay URL, so add the relay under another name: \`rig remote add toon <relay-url>\`, then \`rig push toon\`.`
|
|
1614
|
+
);
|
|
1615
|
+
} else {
|
|
1616
|
+
io.out(
|
|
1617
|
+
"No relay configured yet \u2014 add one as the follow-up step: `rig remote add origin <relay-url>` (a real git remote; `git remote -v` shows it). One-off pushes can pass --relay <url>."
|
|
1618
|
+
);
|
|
1619
|
+
}
|
|
1620
|
+
return 0;
|
|
1621
|
+
} catch (err) {
|
|
1622
|
+
const described = describeError(err, "init");
|
|
1623
|
+
if (flags.json) {
|
|
1624
|
+
io.out(JSON.stringify({ command: "init", ...described.json }, null, 2));
|
|
1625
|
+
} else {
|
|
1626
|
+
for (const line of described.lines) io.err(line);
|
|
1627
|
+
}
|
|
1628
|
+
return 1;
|
|
1629
|
+
}
|
|
1342
1630
|
}
|
|
1343
1631
|
|
|
1344
|
-
// src/cli/
|
|
1345
|
-
var USAGE = `rig \u2014
|
|
1632
|
+
// src/cli/dispatch.ts
|
|
1633
|
+
var USAGE = `rig \u2014 git with a TOON remote (pay-to-write Nostr + Arweave)
|
|
1346
1634
|
|
|
1347
1635
|
Usage: rig <command> [options]
|
|
1348
1636
|
|
|
1349
|
-
Commands:
|
|
1350
|
-
|
|
1637
|
+
Commands rig owns:
|
|
1638
|
+
init set up this repo: resolve your identity
|
|
1639
|
+
(RIG_MNEMONIC) and write the toon.* git config
|
|
1640
|
+
remote add <name> <url> add a relay as a REAL git remote ("origin" is
|
|
1641
|
+
remote remove <name> the default publish target); remove/list manage
|
|
1642
|
+
remote list them \u2014 \`git remote -v\` shows the same data
|
|
1643
|
+
push [remote] [refspecs...] plan, price, confirm, and execute a paid push
|
|
1644
|
+
to TOON (defaults to the "origin" remote). rig
|
|
1645
|
+
push is the TOON transport and shadows git push;
|
|
1646
|
+
plain-git pushes remain available by running
|
|
1647
|
+
\`git push\` directly
|
|
1351
1648
|
issue create file an issue (kind:1621) against a repo
|
|
1352
1649
|
comment <root-event-id> comment (kind:1622) on an issue or patch
|
|
1353
1650
|
pr create publish a patch (kind:1617) with real
|
|
1354
1651
|
\`git format-patch\` content
|
|
1355
|
-
status <event-id> <state> set an issue/patch status (kind:1630-1633):
|
|
1652
|
+
pr status <event-id> <state> set an issue/patch status (kind:1630-1633):
|
|
1356
1653
|
open | applied | closed | draft
|
|
1357
1654
|
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
try {
|
|
1374
|
-
const answer = (await rl.question(question)).trim().toLowerCase();
|
|
1375
|
-
return answer === "y" || answer === "yes";
|
|
1376
|
-
} finally {
|
|
1377
|
-
rl.close();
|
|
1378
|
-
}
|
|
1655
|
+
Any other command is passed through to git verbatim: \`rig status\` runs
|
|
1656
|
+
\`git status\`, and \`rig add -p\`, \`rig commit\`, \`rig log --oneline\`,
|
|
1657
|
+
\`rig rebase -i\`, \u2026 behave exactly like git (same output, same exit code).
|
|
1658
|
+
|
|
1659
|
+
Run \`rig <command> --help\` for a rig command's flags. \`rig init\` and
|
|
1660
|
+
\`rig remote\` are free; the other rig commands are paid writes \u2014 permanent
|
|
1661
|
+
and non-refundable; each quotes its fee and asks for confirmation before
|
|
1662
|
+
spending (--yes skips, --json emits machine output).`;
|
|
1663
|
+
function rigVersion() {
|
|
1664
|
+
const require2 = createRequire(import.meta.url);
|
|
1665
|
+
for (const rel of ["../package.json", "../../package.json", "../../../package.json"]) {
|
|
1666
|
+
try {
|
|
1667
|
+
const pkg = require2(rel);
|
|
1668
|
+
if (pkg.name === "@toon-protocol/rig" && pkg.version) return pkg.version;
|
|
1669
|
+
} catch {
|
|
1379
1670
|
}
|
|
1380
|
-
}
|
|
1671
|
+
}
|
|
1672
|
+
return "unknown";
|
|
1381
1673
|
}
|
|
1382
|
-
async function
|
|
1383
|
-
const [command, ...rest] =
|
|
1384
|
-
const io =
|
|
1385
|
-
const deps = {
|
|
1386
|
-
io,
|
|
1387
|
-
env: process.env,
|
|
1388
|
-
cwd: process.cwd(),
|
|
1389
|
-
fetchImpl: fetch
|
|
1390
|
-
};
|
|
1674
|
+
async function dispatch(argv, deps) {
|
|
1675
|
+
const [command, ...rest] = argv;
|
|
1676
|
+
const { io } = deps;
|
|
1391
1677
|
switch (command) {
|
|
1678
|
+
case "init":
|
|
1679
|
+
return runInit(rest, deps);
|
|
1680
|
+
case "remote":
|
|
1681
|
+
return runRemote(rest, deps);
|
|
1392
1682
|
case "push":
|
|
1393
1683
|
return runPush(rest, deps);
|
|
1394
1684
|
case "issue":
|
|
@@ -1397,8 +1687,6 @@ async function main() {
|
|
|
1397
1687
|
return runComment(rest, deps);
|
|
1398
1688
|
case "pr":
|
|
1399
1689
|
return runPr(rest, deps);
|
|
1400
|
-
case "status":
|
|
1401
|
-
return runStatus(rest, deps);
|
|
1402
1690
|
case "help":
|
|
1403
1691
|
case "--help":
|
|
1404
1692
|
case "-h":
|
|
@@ -1406,16 +1694,48 @@ async function main() {
|
|
|
1406
1694
|
io.out("");
|
|
1407
1695
|
io.out(PUSH_USAGE);
|
|
1408
1696
|
return 0;
|
|
1697
|
+
case "--version":
|
|
1698
|
+
io.out(`rig ${rigVersion()}`);
|
|
1699
|
+
return 0;
|
|
1409
1700
|
case void 0:
|
|
1410
1701
|
io.err(USAGE);
|
|
1411
1702
|
return 2;
|
|
1412
1703
|
default:
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1704
|
+
return (deps.runGit ?? runGitPassthrough)(argv, {
|
|
1705
|
+
cwd: deps.cwd,
|
|
1706
|
+
env: deps.env,
|
|
1707
|
+
err: (line) => io.err(line)
|
|
1708
|
+
});
|
|
1416
1709
|
}
|
|
1417
1710
|
}
|
|
1418
|
-
|
|
1711
|
+
|
|
1712
|
+
// src/cli/rig.ts
|
|
1713
|
+
function makeIo() {
|
|
1714
|
+
return {
|
|
1715
|
+
out: (line) => process.stdout.write(`${line}
|
|
1716
|
+
`),
|
|
1717
|
+
err: (line) => process.stderr.write(`${line}
|
|
1718
|
+
`),
|
|
1719
|
+
isInteractive: Boolean(process.stdin.isTTY && process.stdout.isTTY),
|
|
1720
|
+
confirm: async (question) => {
|
|
1721
|
+
const rl = createInterface({
|
|
1722
|
+
input: process.stdin,
|
|
1723
|
+
output: process.stderr
|
|
1724
|
+
});
|
|
1725
|
+
try {
|
|
1726
|
+
const answer = (await rl.question(question)).trim().toLowerCase();
|
|
1727
|
+
return answer === "y" || answer === "yes";
|
|
1728
|
+
} finally {
|
|
1729
|
+
rl.close();
|
|
1730
|
+
}
|
|
1731
|
+
}
|
|
1732
|
+
};
|
|
1733
|
+
}
|
|
1734
|
+
dispatch(process.argv.slice(2), {
|
|
1735
|
+
io: makeIo(),
|
|
1736
|
+
env: process.env,
|
|
1737
|
+
cwd: process.cwd()
|
|
1738
|
+
}).then(
|
|
1419
1739
|
(code) => {
|
|
1420
1740
|
process.exitCode = code;
|
|
1421
1741
|
},
|