@toon-protocol/rig 2.8.0 → 2.10.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 +35 -2
- package/dist/{chunk-XGFBDUQX.js → chunk-GZUDRNOK.js} +22 -2
- package/dist/chunk-GZUDRNOK.js.map +1 -0
- package/dist/{chunk-LTQPBBHA.js → chunk-RALCZLP6.js} +14 -3
- package/dist/chunk-RALCZLP6.js.map +1 -0
- package/dist/cli/rig.js +906 -96
- package/dist/cli/rig.js.map +1 -1
- package/dist/{network-bootstrap-HTNM7BBX.js → network-bootstrap-GRBEPZMI.js} +4 -2
- package/dist/{standalone-mode-FPFEEL72.js → standalone-mode-6KIBWGLP.js} +3 -3
- package/package.json +5 -2
- package/dist/chunk-LTQPBBHA.js.map +0 -1
- package/dist/chunk-XGFBDUQX.js.map +0 -1
- /package/dist/{network-bootstrap-HTNM7BBX.js.map → network-bootstrap-GRBEPZMI.js.map} +0 -0
- /package/dist/{standalone-mode-FPFEEL72.js.map → standalone-mode-6KIBWGLP.js.map} +0 -0
package/dist/cli/rig.js
CHANGED
|
@@ -26,8 +26,9 @@ import {
|
|
|
26
26
|
clientKeystorePath,
|
|
27
27
|
linkKeystoreInClientConfig,
|
|
28
28
|
readClientConfigFile,
|
|
29
|
+
resolveAccountIndex,
|
|
29
30
|
resolveIdentity
|
|
30
|
-
} from "../chunk-
|
|
31
|
+
} from "../chunk-GZUDRNOK.js";
|
|
31
32
|
import {
|
|
32
33
|
ChannelMapStore,
|
|
33
34
|
channelStatus,
|
|
@@ -312,9 +313,7 @@ function nonFastForwardLines(refs) {
|
|
|
312
313
|
function oversizeLines(objects) {
|
|
313
314
|
return [
|
|
314
315
|
`Push rejected: ${objects.length} object(s) exceed the ${MAX_OBJECT_SIZE}-byte (95KB) upload limit:`,
|
|
315
|
-
...objects.map(
|
|
316
|
-
(o) => ` ${o.path ?? o.sha} ${o.type}, ${o.size} bytes`
|
|
317
|
-
),
|
|
316
|
+
...objects.map((o) => ` ${o.path ?? o.sha} ${o.type}, ${o.size} bytes`),
|
|
318
317
|
"Objects over 95KB are a hard error in v1 \u2014 split or remove the file(s) from history to push.",
|
|
319
318
|
"Large-object support is tracked in toon-client#235."
|
|
320
319
|
];
|
|
@@ -345,7 +344,11 @@ function describeError(err, command = "push") {
|
|
|
345
344
|
return {
|
|
346
345
|
code: "unknown_remote",
|
|
347
346
|
lines: err.message.split("\n"),
|
|
348
|
-
json: {
|
|
347
|
+
json: {
|
|
348
|
+
error: "unknown_remote",
|
|
349
|
+
detail: err.message,
|
|
350
|
+
remote: err.remote
|
|
351
|
+
}
|
|
349
352
|
};
|
|
350
353
|
}
|
|
351
354
|
if (err instanceof MultiUrlRemoteError) {
|
|
@@ -406,6 +409,13 @@ function describeError(err, command = "push") {
|
|
|
406
409
|
json: { error: "arns_sdk_unavailable", detail: err.message }
|
|
407
410
|
};
|
|
408
411
|
}
|
|
412
|
+
if (err instanceof Error && err.name === "ArnsSdkIncompatibleError") {
|
|
413
|
+
return {
|
|
414
|
+
code: "arns_sdk_incompatible",
|
|
415
|
+
lines: err.message.split("\n"),
|
|
416
|
+
json: { error: "arns_sdk_incompatible", detail: err.message }
|
|
417
|
+
};
|
|
418
|
+
}
|
|
409
419
|
if (err instanceof DaemonRouteError) {
|
|
410
420
|
const envelope = err.envelope;
|
|
411
421
|
if (envelope.error === "non_fast_forward" && Array.isArray(envelope["refs"])) {
|
|
@@ -532,8 +542,97 @@ function emitCliError(io2, json, command, err) {
|
|
|
532
542
|
}
|
|
533
543
|
|
|
534
544
|
// src/cli/push.ts
|
|
545
|
+
import { createHash } from "crypto";
|
|
535
546
|
import { parseArgs as parseArgs2 } from "util";
|
|
536
547
|
|
|
548
|
+
// src/rig-pointer.ts
|
|
549
|
+
var DEFAULT_RIG_WEB_BUNDLE = {
|
|
550
|
+
manifestTx: "Iy8sHYVnoevSpwDC2lqUoIg8BWdes9NmMbgmPd81UpU",
|
|
551
|
+
entryJs: "assets/index-BuKzYubR.js",
|
|
552
|
+
entryCss: "assets/index-H5D6cmfa.css"
|
|
553
|
+
};
|
|
554
|
+
var RIG_WEB_TX_ENV = "RIG_WEB_TX";
|
|
555
|
+
var RIG_WEB_ENTRY_JS_ENV = "RIG_WEB_ENTRY_JS";
|
|
556
|
+
var RIG_WEB_ENTRY_CSS_ENV = "RIG_WEB_ENTRY_CSS";
|
|
557
|
+
var DEFAULT_RIG_WEB_URL = "https://toon-protocol.github.io/toon-client";
|
|
558
|
+
var RIG_WEB_URL_ENV = "RIG_WEB_URL";
|
|
559
|
+
function repoHashRoute(options) {
|
|
560
|
+
const { relay, ownerNpub, repoId } = options;
|
|
561
|
+
return `#/${encodeURIComponent(ownerNpub)}/${encodeURIComponent(repoId)}?relay=${encodeURIComponent(relay)}`;
|
|
562
|
+
}
|
|
563
|
+
function rigWebRoute(options) {
|
|
564
|
+
return `${options.rigWebUrl.replace(/\/+$/, "")}/${repoHashRoute(options)}`;
|
|
565
|
+
}
|
|
566
|
+
function escapeHtml(value) {
|
|
567
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
568
|
+
}
|
|
569
|
+
function generateRigPointerHtml(options) {
|
|
570
|
+
const gateway = options.gateway.replace(/\/+$/, "");
|
|
571
|
+
const base = `${gateway}/${options.bundle.manifestTx}`;
|
|
572
|
+
const cssHref = `${base}/${options.bundle.entryCss}`;
|
|
573
|
+
const jsSrc = `${base}/${options.bundle.entryJs}`;
|
|
574
|
+
const fallback = rigWebRoute(options);
|
|
575
|
+
const safeFallback = escapeHtml(fallback);
|
|
576
|
+
const title = escapeHtml(`${options.repoId} \u2014 Rig`);
|
|
577
|
+
const config = JSON.stringify({
|
|
578
|
+
relay: options.relay,
|
|
579
|
+
owner: options.ownerNpub,
|
|
580
|
+
repo: options.repoId
|
|
581
|
+
}).replace(/</g, "\\u003c");
|
|
582
|
+
const hashRoute = JSON.stringify(repoHashRoute(options)).replace(
|
|
583
|
+
/</g,
|
|
584
|
+
"\\u003c"
|
|
585
|
+
);
|
|
586
|
+
return `<!DOCTYPE html>
|
|
587
|
+
<html lang="en"><head>
|
|
588
|
+
<meta charset="utf-8">
|
|
589
|
+
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
590
|
+
<meta name="description" content="Decentralized Git on Nostr & TOON Protocol">
|
|
591
|
+
<title>${title}</title>
|
|
592
|
+
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>⚒</text></svg>">
|
|
593
|
+
<script>window.__RIG_CONFIG__=${config};if(!location.hash||location.hash==="#")location.replace(${hashRoute})</script>
|
|
594
|
+
<link rel="stylesheet" href="${escapeHtml(cssHref)}">
|
|
595
|
+
<script type="module" crossorigin src="${escapeHtml(jsSrc)}"></script>
|
|
596
|
+
</head><body>
|
|
597
|
+
<div id="app"></div>
|
|
598
|
+
<noscript><p>JavaScript is off \u2014 open <a href="${safeFallback}">${title}</a> in the hosted Rig.</p></noscript>
|
|
599
|
+
<p hidden data-fallback>If nothing loads, open <a href="${safeFallback}">${title}</a> in the hosted Rig.</p>
|
|
600
|
+
<script>setTimeout(function(){var p=document.querySelector("[data-fallback]");var app=document.getElementById("app");if(p&&(!app||!app.childElementCount))p.hidden=false},8000)</script>
|
|
601
|
+
</body></html>
|
|
602
|
+
`;
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
// src/cli/rig-pointer-record.ts
|
|
606
|
+
import { mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
607
|
+
import { homedir } from "os";
|
|
608
|
+
import { join } from "path";
|
|
609
|
+
var RIG_POINTER_FILENAME = "rig-pointers.json";
|
|
610
|
+
function storeDir(env) {
|
|
611
|
+
return env["TOON_CLIENT_HOME"] ?? join(homedir(), ".toon-client");
|
|
612
|
+
}
|
|
613
|
+
function storePath(env) {
|
|
614
|
+
return join(storeDir(env), RIG_POINTER_FILENAME);
|
|
615
|
+
}
|
|
616
|
+
function readAll(env) {
|
|
617
|
+
try {
|
|
618
|
+
const parsed = JSON.parse(readFileSync(storePath(env), "utf8"));
|
|
619
|
+
if (parsed && typeof parsed === "object") return parsed;
|
|
620
|
+
return {};
|
|
621
|
+
} catch {
|
|
622
|
+
return {};
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
function readRigPointerRecord(env, repoId) {
|
|
626
|
+
return readAll(env)[repoId];
|
|
627
|
+
}
|
|
628
|
+
function writeRigPointerRecord(env, record) {
|
|
629
|
+
const all = readAll(env);
|
|
630
|
+
all[record.repoId] = record;
|
|
631
|
+
mkdirSync(storeDir(env), { recursive: true });
|
|
632
|
+
writeFileSync(storePath(env), `${JSON.stringify(all, null, 2)}
|
|
633
|
+
`);
|
|
634
|
+
}
|
|
635
|
+
|
|
537
636
|
// src/cli/render.ts
|
|
538
637
|
function formatNumber(value) {
|
|
539
638
|
return String(value).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
|
@@ -974,7 +1073,7 @@ function loadPaidSession(deps, relayUrl) {
|
|
|
974
1073
|
});
|
|
975
1074
|
}
|
|
976
1075
|
var defaultLoadStandalone = async (options) => {
|
|
977
|
-
const mod = await import("../standalone-mode-
|
|
1076
|
+
const mod = await import("../standalone-mode-6KIBWGLP.js");
|
|
978
1077
|
return mod.createStandaloneContext(options);
|
|
979
1078
|
};
|
|
980
1079
|
function identityReport(ctx) {
|
|
@@ -1001,10 +1100,18 @@ remote name it is the remote; otherwise it is a refspec and the remote
|
|
|
1001
1100
|
defaults to origin. Refspecs are branch/tag names or full refnames; default
|
|
1002
1101
|
is the current branch.
|
|
1003
1102
|
|
|
1103
|
+
Every push also keeps the repo's RIG PAGE current: a permanent Arweave
|
|
1104
|
+
page that RENDERS this repo in place \u2014 it boots the FULL Rig (rig-web)
|
|
1105
|
+
from its Arweave deployment with this repo pinned, everything served from
|
|
1106
|
+
Arweave + the relay. The pointer is content-addressed, so it is paid for
|
|
1107
|
+
once and reused for free until the relay or Rig deployment changes.
|
|
1108
|
+
--no-rig-page skips it.
|
|
1109
|
+
|
|
1004
1110
|
Options:
|
|
1005
1111
|
--force allow non-fast-forward ref updates (overwrites remote history)
|
|
1006
1112
|
--all push all local branches
|
|
1007
1113
|
--tags push all local tags
|
|
1114
|
+
--no-rig-page skip the Rig-page pointer upload for this push
|
|
1008
1115
|
--yes skip the fee confirmation (required when not a TTY)
|
|
1009
1116
|
--json machine-readable plan/receipts; without --yes it is a pure
|
|
1010
1117
|
estimate (nothing executed)
|
|
@@ -1077,6 +1184,7 @@ function parsePushArgs(args) {
|
|
|
1077
1184
|
force: { type: "boolean", default: false },
|
|
1078
1185
|
all: { type: "boolean", default: false },
|
|
1079
1186
|
tags: { type: "boolean", default: false },
|
|
1187
|
+
"no-rig-page": { type: "boolean", default: false },
|
|
1080
1188
|
yes: { type: "boolean", default: false },
|
|
1081
1189
|
json: { type: "boolean", default: false },
|
|
1082
1190
|
relay: { type: "string", multiple: true },
|
|
@@ -1089,6 +1197,7 @@ function parsePushArgs(args) {
|
|
|
1089
1197
|
force: values.force ?? false,
|
|
1090
1198
|
all: values.all ?? false,
|
|
1091
1199
|
tags: values.tags ?? false,
|
|
1200
|
+
rigPage: !(values["no-rig-page"] ?? false),
|
|
1092
1201
|
yes: values.yes ?? false,
|
|
1093
1202
|
json: values.json ?? false,
|
|
1094
1203
|
relay: values.relay ?? [],
|
|
@@ -1099,6 +1208,46 @@ function parsePushArgs(args) {
|
|
|
1099
1208
|
if (repoId !== void 0) flags.repoId = repoId;
|
|
1100
1209
|
return flags;
|
|
1101
1210
|
}
|
|
1211
|
+
function pointerGateway(env) {
|
|
1212
|
+
return (env["RIG_ARWEAVE_GATEWAY"] ?? "https://arweave.net").replace(
|
|
1213
|
+
/\/+$/,
|
|
1214
|
+
""
|
|
1215
|
+
);
|
|
1216
|
+
}
|
|
1217
|
+
function planRigPointer(args) {
|
|
1218
|
+
const html = generateRigPointerHtml({
|
|
1219
|
+
bundle: {
|
|
1220
|
+
manifestTx: args.env[RIG_WEB_TX_ENV] ?? DEFAULT_RIG_WEB_BUNDLE.manifestTx,
|
|
1221
|
+
entryJs: args.env[RIG_WEB_ENTRY_JS_ENV] ?? DEFAULT_RIG_WEB_BUNDLE.entryJs,
|
|
1222
|
+
entryCss: args.env[RIG_WEB_ENTRY_CSS_ENV] ?? DEFAULT_RIG_WEB_BUNDLE.entryCss
|
|
1223
|
+
},
|
|
1224
|
+
gateway: pointerGateway(args.env),
|
|
1225
|
+
rigWebUrl: args.env[RIG_WEB_URL_ENV] ?? DEFAULT_RIG_WEB_URL,
|
|
1226
|
+
relay: args.relay,
|
|
1227
|
+
ownerNpub: hexToNpub(args.ownerPubkey),
|
|
1228
|
+
repoId: args.repoId
|
|
1229
|
+
});
|
|
1230
|
+
const contentHash = createHash("sha256").update(html).digest("hex");
|
|
1231
|
+
const bytes = Buffer.byteLength(html);
|
|
1232
|
+
const record = readRigPointerRecord(args.env, args.repoId);
|
|
1233
|
+
if (record && record.contentHash === contentHash && record.owner === args.ownerPubkey) {
|
|
1234
|
+
return {
|
|
1235
|
+
action: "unchanged",
|
|
1236
|
+
html,
|
|
1237
|
+
contentHash,
|
|
1238
|
+
bytes,
|
|
1239
|
+
fee: 0n,
|
|
1240
|
+
recordedTxId: record.pointerTxId
|
|
1241
|
+
};
|
|
1242
|
+
}
|
|
1243
|
+
return {
|
|
1244
|
+
action: "upload",
|
|
1245
|
+
html,
|
|
1246
|
+
contentHash,
|
|
1247
|
+
bytes,
|
|
1248
|
+
fee: BigInt(bytes) * args.uploadFeePerByte
|
|
1249
|
+
};
|
|
1250
|
+
}
|
|
1102
1251
|
async function runPush(args, deps) {
|
|
1103
1252
|
const { io: io2 } = deps;
|
|
1104
1253
|
let flags;
|
|
@@ -1175,6 +1324,8 @@ async function runPush(args, deps) {
|
|
|
1175
1324
|
}
|
|
1176
1325
|
let plan;
|
|
1177
1326
|
let execute;
|
|
1327
|
+
let pointerPlan;
|
|
1328
|
+
let rigPageSkipDetail;
|
|
1178
1329
|
if (session.path === "standalone") {
|
|
1179
1330
|
const ctx = session.ctx;
|
|
1180
1331
|
const remoteState = await ctx.fetchRemote({
|
|
@@ -1202,6 +1353,21 @@ async function runPush(args, deps) {
|
|
|
1202
1353
|
relayUrls: relaysUsed
|
|
1203
1354
|
})
|
|
1204
1355
|
);
|
|
1356
|
+
if (!flags.rigPage) {
|
|
1357
|
+
rigPageSkipDetail = "skipped (--no-rig-page)";
|
|
1358
|
+
} else if (relaysUsed[0] === void 0) {
|
|
1359
|
+
rigPageSkipDetail = "skipped (no relay resolved for the pointer)";
|
|
1360
|
+
} else if (!ctx.publisher.uploadBlob) {
|
|
1361
|
+
rigPageSkipDetail = "skipped (the active publisher cannot upload raw blobs)";
|
|
1362
|
+
} else {
|
|
1363
|
+
pointerPlan = planRigPointer({
|
|
1364
|
+
env: deps.env,
|
|
1365
|
+
ownerPubkey: ctx.ownerPubkey,
|
|
1366
|
+
repoId,
|
|
1367
|
+
relay: relaysUsed[0],
|
|
1368
|
+
uploadFeePerByte: feeRates.uploadFeePerByte
|
|
1369
|
+
});
|
|
1370
|
+
}
|
|
1205
1371
|
} else {
|
|
1206
1372
|
const client = session.client;
|
|
1207
1373
|
plan = await client.gitEstimate({
|
|
@@ -1219,9 +1385,100 @@ async function runPush(args, deps) {
|
|
|
1219
1385
|
relayUrls: relaysUsed,
|
|
1220
1386
|
confirm: true
|
|
1221
1387
|
});
|
|
1388
|
+
rigPageSkipDetail = "skipped on the daemon path (refreshes on the next standalone push)";
|
|
1222
1389
|
}
|
|
1390
|
+
const gateway = pointerGateway(deps.env);
|
|
1391
|
+
const publishPointer = async (pointer) => {
|
|
1392
|
+
try {
|
|
1393
|
+
const ctx = standaloneCtx;
|
|
1394
|
+
const receipt = await ctx.publisher.uploadBlob({
|
|
1395
|
+
body: Buffer.from(pointer.html, "utf8"),
|
|
1396
|
+
contentType: "text/html",
|
|
1397
|
+
repoId
|
|
1398
|
+
});
|
|
1399
|
+
writeRigPointerRecord(deps.env, {
|
|
1400
|
+
repoId,
|
|
1401
|
+
owner: identity.pubkey,
|
|
1402
|
+
pointerTxId: receipt.txId,
|
|
1403
|
+
contentHash: pointer.contentHash,
|
|
1404
|
+
updatedAt: Date.now()
|
|
1405
|
+
});
|
|
1406
|
+
return {
|
|
1407
|
+
status: "published",
|
|
1408
|
+
txId: receipt.txId,
|
|
1409
|
+
url: `${gateway}/${receipt.txId}`,
|
|
1410
|
+
feePaid: receipt.feePaid.toString()
|
|
1411
|
+
};
|
|
1412
|
+
} catch (err) {
|
|
1413
|
+
return {
|
|
1414
|
+
status: "skipped",
|
|
1415
|
+
detail: `pointer upload failed (push itself succeeded; the next push retries): ${err instanceof Error ? err.message : String(err)}`
|
|
1416
|
+
};
|
|
1417
|
+
}
|
|
1418
|
+
};
|
|
1419
|
+
const printRigPage = (rigPage2) => {
|
|
1420
|
+
if (rigPage2.url) {
|
|
1421
|
+
io2.out(
|
|
1422
|
+
`Rig page: ${rigPage2.url}` + (rigPage2.status === "published" ? ` paid ${rigPage2.feePaid}` : "") + " (renders this repo from Arweave)"
|
|
1423
|
+
);
|
|
1424
|
+
} else if (rigPage2.detail) {
|
|
1425
|
+
io2.err(`rig page: ${rigPage2.detail}`);
|
|
1426
|
+
}
|
|
1427
|
+
};
|
|
1223
1428
|
const upToDate = plan.refUpdates.every((u) => u.kind === "up-to-date");
|
|
1224
1429
|
if (upToDate) {
|
|
1430
|
+
if (pointerPlan?.action !== "upload") {
|
|
1431
|
+
if (flags.json) {
|
|
1432
|
+
io2.emitJson({
|
|
1433
|
+
command: "push",
|
|
1434
|
+
repoId,
|
|
1435
|
+
path,
|
|
1436
|
+
identity,
|
|
1437
|
+
executed: false,
|
|
1438
|
+
upToDate: true,
|
|
1439
|
+
plan
|
|
1440
|
+
});
|
|
1441
|
+
} else {
|
|
1442
|
+
io2.out("Everything up-to-date \u2014 nothing to push (and nothing paid).");
|
|
1443
|
+
}
|
|
1444
|
+
return 0;
|
|
1445
|
+
}
|
|
1446
|
+
if (!flags.json) {
|
|
1447
|
+
io2.out(
|
|
1448
|
+
"Everything up-to-date \u2014 refs unchanged, but the Rig page is stale (new Rig build, relay, or gateway):"
|
|
1449
|
+
);
|
|
1450
|
+
io2.out(` rig page ${pointerPlan.bytes} bytes ${pointerPlan.fee}`);
|
|
1451
|
+
io2.out(renderIdentityLine(identity));
|
|
1452
|
+
}
|
|
1453
|
+
if (!flags.yes) {
|
|
1454
|
+
if (flags.json) {
|
|
1455
|
+
io2.emitJson({
|
|
1456
|
+
command: "push",
|
|
1457
|
+
repoId,
|
|
1458
|
+
path,
|
|
1459
|
+
identity,
|
|
1460
|
+
executed: false,
|
|
1461
|
+
upToDate: true,
|
|
1462
|
+
plan,
|
|
1463
|
+
hint: "refs up-to-date; the Rig page is stale \u2014 re-run with --yes to refresh it (or --no-rig-page to skip)"
|
|
1464
|
+
});
|
|
1465
|
+
return 0;
|
|
1466
|
+
}
|
|
1467
|
+
if (!io2.isInteractive) {
|
|
1468
|
+
io2.err(
|
|
1469
|
+
"refusing to spend channel funds without confirmation in a non-interactive session \u2014 re-run with --yes (or --no-rig-page to skip the refresh)"
|
|
1470
|
+
);
|
|
1471
|
+
return 1;
|
|
1472
|
+
}
|
|
1473
|
+
const proceed = await io2.confirm(
|
|
1474
|
+
`Refresh the Rig page (total ${pointerPlan.fee} base units)? [y/N] `
|
|
1475
|
+
);
|
|
1476
|
+
if (!proceed) {
|
|
1477
|
+
io2.err("aborted \u2014 nothing was uploaded or published.");
|
|
1478
|
+
return 1;
|
|
1479
|
+
}
|
|
1480
|
+
}
|
|
1481
|
+
const rigPage2 = await publishPointer(pointerPlan);
|
|
1225
1482
|
if (flags.json) {
|
|
1226
1483
|
io2.emitJson({
|
|
1227
1484
|
command: "push",
|
|
@@ -1230,15 +1487,23 @@ async function runPush(args, deps) {
|
|
|
1230
1487
|
identity,
|
|
1231
1488
|
executed: false,
|
|
1232
1489
|
upToDate: true,
|
|
1233
|
-
plan
|
|
1490
|
+
plan,
|
|
1491
|
+
rigPage: rigPage2
|
|
1234
1492
|
});
|
|
1235
1493
|
} else {
|
|
1236
|
-
|
|
1494
|
+
printRigPage(rigPage2);
|
|
1237
1495
|
}
|
|
1238
1496
|
return 0;
|
|
1239
1497
|
}
|
|
1498
|
+
const confirmTotal = BigInt(plan.estimate.totalFee) + (pointerPlan?.fee ?? 0n);
|
|
1240
1499
|
if (!flags.json) {
|
|
1241
1500
|
for (const line of renderPlan(plan)) io2.out(line);
|
|
1501
|
+
if (pointerPlan?.action === "upload") {
|
|
1502
|
+
io2.out(
|
|
1503
|
+
` rig page ${pointerPlan.bytes} bytes ${pointerPlan.fee} (repo page \u2014 paid once, reused until relay/rig-web changes)`
|
|
1504
|
+
);
|
|
1505
|
+
io2.out(` total with rig page ${confirmTotal}`);
|
|
1506
|
+
}
|
|
1242
1507
|
io2.out(renderIdentityLine(identity));
|
|
1243
1508
|
}
|
|
1244
1509
|
if (!flags.yes) {
|
|
@@ -1262,7 +1527,7 @@ async function runPush(args, deps) {
|
|
|
1262
1527
|
return 1;
|
|
1263
1528
|
}
|
|
1264
1529
|
const proceed = await io2.confirm(
|
|
1265
|
-
`Proceed with paid push (total ${
|
|
1530
|
+
`Proceed with paid push (total ${confirmTotal} base units)? [y/N] `
|
|
1266
1531
|
);
|
|
1267
1532
|
if (!proceed) {
|
|
1268
1533
|
io2.err("aborted \u2014 nothing was uploaded or published.");
|
|
@@ -1270,6 +1535,18 @@ async function runPush(args, deps) {
|
|
|
1270
1535
|
}
|
|
1271
1536
|
}
|
|
1272
1537
|
const result = await execute();
|
|
1538
|
+
let rigPage;
|
|
1539
|
+
if (!pointerPlan) {
|
|
1540
|
+
rigPage = { status: "skipped", ...rigPageSkipDetail ? { detail: rigPageSkipDetail } : {} };
|
|
1541
|
+
} else if (pointerPlan.action === "unchanged") {
|
|
1542
|
+
rigPage = {
|
|
1543
|
+
status: "unchanged",
|
|
1544
|
+
txId: pointerPlan.recordedTxId,
|
|
1545
|
+
url: `${gateway}/${pointerPlan.recordedTxId}`
|
|
1546
|
+
};
|
|
1547
|
+
} else {
|
|
1548
|
+
rigPage = await publishPointer(pointerPlan);
|
|
1549
|
+
}
|
|
1273
1550
|
if (flags.json) {
|
|
1274
1551
|
io2.emitJson({
|
|
1275
1552
|
command: "push",
|
|
@@ -1279,10 +1556,12 @@ async function runPush(args, deps) {
|
|
|
1279
1556
|
executed: true,
|
|
1280
1557
|
upToDate: false,
|
|
1281
1558
|
plan,
|
|
1282
|
-
result
|
|
1559
|
+
result,
|
|
1560
|
+
rigPage
|
|
1283
1561
|
});
|
|
1284
1562
|
} else {
|
|
1285
1563
|
for (const line of renderResult(result)) io2.out(line);
|
|
1564
|
+
printRigPage(rigPage);
|
|
1286
1565
|
}
|
|
1287
1566
|
return 0;
|
|
1288
1567
|
} catch (err) {
|
|
@@ -1896,7 +2175,7 @@ async function stopQuietly(ctx) {
|
|
|
1896
2175
|
|
|
1897
2176
|
// src/cli/clone.ts
|
|
1898
2177
|
import { mkdtemp, mkdir, readdir, rename, rm, rmdir } from "fs/promises";
|
|
1899
|
-
import { basename, dirname, join, resolve } from "path";
|
|
2178
|
+
import { basename, dirname, join as join2, resolve } from "path";
|
|
1900
2179
|
import { parseArgs as parseArgs5 } from "util";
|
|
1901
2180
|
var CLONE_USAGE = `Usage: rig clone <relay-url> <owner>/<repo-id> [dir] [options]
|
|
1902
2181
|
|
|
@@ -2067,7 +2346,7 @@ async function runClone(args, deps) {
|
|
|
2067
2346
|
}
|
|
2068
2347
|
const parent = dirname(dest);
|
|
2069
2348
|
await mkdir(parent, { recursive: true });
|
|
2070
|
-
tempDir = await mkdtemp(
|
|
2349
|
+
tempDir = await mkdtemp(join2(parent, `.${basename(dest)}.rig-clone-`));
|
|
2071
2350
|
await runGit(tempDir, [
|
|
2072
2351
|
"init",
|
|
2073
2352
|
"--quiet",
|
|
@@ -3533,9 +3812,9 @@ async function runFetch(args, deps) {
|
|
|
3533
3812
|
}
|
|
3534
3813
|
|
|
3535
3814
|
// src/cli/fund.ts
|
|
3536
|
-
import { readFileSync } from "fs";
|
|
3537
|
-
import { homedir } from "os";
|
|
3538
|
-
import { join as
|
|
3815
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
3816
|
+
import { homedir as homedir2 } from "os";
|
|
3817
|
+
import { join as join3 } from "path";
|
|
3539
3818
|
import { parseArgs as parseArgs9 } from "util";
|
|
3540
3819
|
var DEVNET_FAUCET_URL = "https://faucet.devnet.toonprotocol.dev";
|
|
3541
3820
|
var CHAINS = ["evm", "solana", "mina"];
|
|
@@ -3625,11 +3904,11 @@ function noFaucetGuidance(network, devnetOrigin) {
|
|
|
3625
3904
|
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}`;
|
|
3626
3905
|
}
|
|
3627
3906
|
function readFundConfig(env) {
|
|
3628
|
-
const dir = env["TOON_CLIENT_HOME"] ??
|
|
3629
|
-
const configPath =
|
|
3907
|
+
const dir = env["TOON_CLIENT_HOME"] ?? join3(homedir2(), ".toon-client");
|
|
3908
|
+
const configPath = join3(dir, "config.json");
|
|
3630
3909
|
try {
|
|
3631
3910
|
return {
|
|
3632
|
-
file: JSON.parse(
|
|
3911
|
+
file: JSON.parse(readFileSync2(configPath, "utf8")),
|
|
3633
3912
|
configPath
|
|
3634
3913
|
};
|
|
3635
3914
|
} catch (err) {
|
|
@@ -3862,7 +4141,7 @@ var runGitPassthrough = (argv2, options = {}) => {
|
|
|
3862
4141
|
};
|
|
3863
4142
|
|
|
3864
4143
|
// src/cli/identity-cmd.ts
|
|
3865
|
-
import { existsSync, mkdirSync } from "fs";
|
|
4144
|
+
import { existsSync, mkdirSync as mkdirSync2 } from "fs";
|
|
3866
4145
|
import { dirname as dirname2 } from "path";
|
|
3867
4146
|
import { createInterface } from "readline/promises";
|
|
3868
4147
|
import { parseArgs as parseArgs10 } from "util";
|
|
@@ -3914,12 +4193,12 @@ async function prepareKeystoreTarget(input) {
|
|
|
3914
4193
|
const envPassword = input.env["TOON_CLIENT_KEYSTORE_PASSWORD"]?.trim() || void 0;
|
|
3915
4194
|
const autoPassword = envPassword === void 0;
|
|
3916
4195
|
const password = envPassword ?? DEFAULT_KEYSTORE_PASSWORD;
|
|
3917
|
-
|
|
4196
|
+
mkdirSync2(dirname2(keystorePath), { recursive: true });
|
|
3918
4197
|
return { keystorePath, password, autoPassword, existing, keyOps };
|
|
3919
4198
|
}
|
|
3920
4199
|
function readAccountIndex(env) {
|
|
3921
4200
|
const file = readClientConfigFile(clientConfigPath(env));
|
|
3922
|
-
return
|
|
4201
|
+
return resolveAccountIndex(env, "keystore", file);
|
|
3923
4202
|
}
|
|
3924
4203
|
function finishMint(input, target, mnemonic) {
|
|
3925
4204
|
const configPath = linkKeystoreInClientConfig(
|
|
@@ -4412,7 +4691,7 @@ async function resolveProfileRelay(flagRelay, originRelay, toonRelays) {
|
|
|
4412
4691
|
].filter((u) => u !== void 0 && isWebSocketRelay2(u));
|
|
4413
4692
|
if (candidates[0] !== void 0) return candidates[0];
|
|
4414
4693
|
try {
|
|
4415
|
-
const { loadGenesisSeed } = await import("../network-bootstrap-
|
|
4694
|
+
const { loadGenesisSeed } = await import("../network-bootstrap-GRBEPZMI.js");
|
|
4416
4695
|
const seed = loadGenesisSeed();
|
|
4417
4696
|
if (seed?.relayUrl && isWebSocketRelay2(seed.relayUrl)) return seed.relayUrl;
|
|
4418
4697
|
} catch {
|
|
@@ -4921,67 +5200,411 @@ async function runMutate(op, args, deps) {
|
|
|
4921
5200
|
}
|
|
4922
5201
|
|
|
4923
5202
|
// src/cli/name.ts
|
|
5203
|
+
import { randomUUID } from "crypto";
|
|
4924
5204
|
import { parseArgs as parseArgs13 } from "util";
|
|
4925
5205
|
var ARIO_DECIMALS = 6;
|
|
4926
5206
|
function renderMario(mario) {
|
|
4927
5207
|
return `${mario.toString()} mARIO (~${formatUnits(mario.toString(), ARIO_DECIMALS)} ARIO)`;
|
|
4928
5208
|
}
|
|
4929
5209
|
var MARIO_PAYMENT_NOTE = "Payment is mARIO from this identity's Solana wallet, charged on-chain by the ar.io registry program \u2014 NOT through TOON ILP payment channels. Fund this wallet with `rig fund --chain solana` (or send $ARIO to the Solana address above).";
|
|
5210
|
+
var DVM_PAYMENT_NOTE = "Brokered buy (--via): the DVM pays the mARIO name price from ITS Solana wallet via the kind:5095 job; this wallet needs only dust SOL to spawn the ANT it owns. On the paid path the job fee is enforced by the connector payment proxy in front of the DVM; when --via targets the DVM backend directly the job payment is STUBBED (dev/e2e only). The ArNS purchase itself is real either way, and the name is owned by YOUR ANT from inception.";
|
|
5211
|
+
var MIN_ARIO_SDK_VERSION = "4.0.3";
|
|
4930
5212
|
var ArnsSdkUnavailableError = class extends Error {
|
|
4931
|
-
constructor(cause) {
|
|
5213
|
+
constructor(cause, module = "@ar.io/sdk") {
|
|
4932
5214
|
super(
|
|
4933
|
-
|
|
5215
|
+
`\`rig name\` needs the optional \`${module}\` dependency, which is not installed. It is intentionally NOT a base \`rig\` dependency (keeps the install lean). Add it to use ArNS naming:
|
|
5216
|
+
npm i @ar.io/sdk@^${MIN_ARIO_SDK_VERSION} # or: pnpm add @ar.io/sdk@^${MIN_ARIO_SDK_VERSION}
|
|
5217
|
+
(\`@solana/kit\` installs alongside it) then re-run your \`rig name\` command.` + (cause instanceof Error ? `
|
|
4934
5218
|
(underlying error: ${cause.message})` : "")
|
|
4935
5219
|
);
|
|
4936
5220
|
this.name = "ArnsSdkUnavailableError";
|
|
4937
5221
|
}
|
|
4938
5222
|
};
|
|
4939
|
-
var
|
|
5223
|
+
var ArnsSdkIncompatibleError = class extends Error {
|
|
5224
|
+
constructor(detail) {
|
|
5225
|
+
super(
|
|
5226
|
+
`the installed @ar.io/sdk is incompatible with \`rig name\`: ${detail}.
|
|
5227
|
+
\`rig name\` requires @ar.io/sdk >= ${MIN_ARIO_SDK_VERSION} (the Solana-native release driven via a \`@solana/kit\` rpc + signer). Upgrade with:
|
|
5228
|
+
npm i @ar.io/sdk@latest # or: pnpm add @ar.io/sdk@latest`
|
|
5229
|
+
);
|
|
5230
|
+
this.name = "ArnsSdkIncompatibleError";
|
|
5231
|
+
}
|
|
5232
|
+
};
|
|
5233
|
+
var SOLANA_MAINNET_RPC_URL = "https://api.mainnet-beta.solana.com";
|
|
5234
|
+
var SOLANA_DEVNET_RPC_URL = "https://api.devnet.solana.com";
|
|
4940
5235
|
var defaultLoadArns = async (options) => {
|
|
4941
|
-
const
|
|
5236
|
+
const sdkSpecifier = "@ar.io/sdk";
|
|
4942
5237
|
let mod;
|
|
4943
5238
|
try {
|
|
4944
|
-
mod = await import(
|
|
5239
|
+
mod = await import(sdkSpecifier);
|
|
4945
5240
|
} catch (err) {
|
|
4946
5241
|
throw new ArnsSdkUnavailableError(err);
|
|
4947
5242
|
}
|
|
4948
|
-
|
|
4949
|
-
|
|
4950
|
-
|
|
4951
|
-
);
|
|
4952
|
-
}
|
|
4953
|
-
|
|
4954
|
-
|
|
4955
|
-
|
|
4956
|
-
|
|
4957
|
-
|
|
4958
|
-
|
|
5243
|
+
const kitSpecifier = "@solana/kit";
|
|
5244
|
+
let kit;
|
|
5245
|
+
try {
|
|
5246
|
+
kit = await import(kitSpecifier);
|
|
5247
|
+
} catch (err) {
|
|
5248
|
+
throw new ArnsSdkUnavailableError(err, "@solana/kit");
|
|
5249
|
+
}
|
|
5250
|
+
const arioInit = mod.ARIO?.init;
|
|
5251
|
+
const antInit = mod.ANT?.init;
|
|
5252
|
+
if (!arioInit || !antInit) {
|
|
5253
|
+
throw new ArnsSdkIncompatibleError("it exposes no ARIO.init / ANT.init");
|
|
5254
|
+
}
|
|
5255
|
+
const {
|
|
5256
|
+
createSolanaRpc,
|
|
5257
|
+
createSolanaRpcSubscriptions,
|
|
5258
|
+
createKeyPairSignerFromBytes
|
|
5259
|
+
} = kit;
|
|
5260
|
+
if (!createSolanaRpc || !createSolanaRpcSubscriptions || !createKeyPairSignerFromBytes) {
|
|
5261
|
+
throw new ArnsSdkIncompatibleError(
|
|
5262
|
+
"the resolved @solana/kit exposes no createSolanaRpc / createSolanaRpcSubscriptions / createKeyPairSignerFromBytes"
|
|
4959
5263
|
);
|
|
4960
5264
|
}
|
|
4961
|
-
|
|
4962
|
-
|
|
4963
|
-
|
|
4964
|
-
|
|
4965
|
-
|
|
5265
|
+
const rpcUrl = options.network === "devnet" ? mod.DEVNET_RPC_URL ?? SOLANA_DEVNET_RPC_URL : mod.DEFAULT_SOLANA_RPC_URL ?? mod.MAINNET_RPC_URL ?? SOLANA_MAINNET_RPC_URL;
|
|
5266
|
+
const devnetIds = options.network === "devnet" ? mod.DEVNET_PROGRAM_IDS : void 0;
|
|
5267
|
+
if (options.network === "devnet" && devnetIds === void 0) {
|
|
5268
|
+
throw new ArnsSdkIncompatibleError(
|
|
5269
|
+
"it exposes no DEVNET_PROGRAM_IDS \u2014 devnet targeting needs the SDK's staging program ids"
|
|
4966
5270
|
);
|
|
4967
5271
|
}
|
|
4968
|
-
const
|
|
4969
|
-
const
|
|
4970
|
-
|
|
5272
|
+
const antProgramId = devnetIds?.ant;
|
|
5273
|
+
const programOverrides = {
|
|
5274
|
+
...devnetIds?.core !== void 0 ? { coreProgramId: devnetIds.core } : {},
|
|
5275
|
+
...devnetIds?.gar !== void 0 ? { garProgramId: devnetIds.gar } : {},
|
|
5276
|
+
...devnetIds?.arns !== void 0 ? { arnsProgramId: devnetIds.arns } : {},
|
|
5277
|
+
...antProgramId !== void 0 ? { antProgramId } : {},
|
|
5278
|
+
// --process-id / RIG_ARIO_PROCESS_ID: explicit ArNS registry program
|
|
5279
|
+
// override (wins over the cluster default).
|
|
5280
|
+
...options.processId !== void 0 ? { arnsProgramId: options.processId } : {}
|
|
5281
|
+
};
|
|
5282
|
+
const rpc = createSolanaRpc(rpcUrl);
|
|
5283
|
+
const writeExtras = options.mode === "write" ? {
|
|
5284
|
+
signer: await createKeyPairSignerFromBytes(options.solanaSecretKey),
|
|
5285
|
+
rpcSubscriptions: createSolanaRpcSubscriptions(
|
|
5286
|
+
rpcUrl.replace(/^http/, "ws")
|
|
5287
|
+
)
|
|
5288
|
+
} : {};
|
|
5289
|
+
const ario = arioInit({ rpc, ...writeExtras, ...programOverrides });
|
|
4971
5290
|
return {
|
|
4972
5291
|
getTokenCost: async (args) => BigInt(String(await ario.getTokenCost(args))),
|
|
4973
|
-
buyRecord: async (args) =>
|
|
4974
|
-
|
|
5292
|
+
buyRecord: async (args) => {
|
|
5293
|
+
if (typeof ario.buyRecord !== "function") {
|
|
5294
|
+
throw new ArnsSdkIncompatibleError(
|
|
5295
|
+
"the signed ARIO client exposes no buyRecord"
|
|
5296
|
+
);
|
|
5297
|
+
}
|
|
5298
|
+
return await ario.buyRecord(args);
|
|
5299
|
+
},
|
|
5300
|
+
spawnAnt: async (args) => {
|
|
5301
|
+
const spawn2 = mod.ANT?.spawn;
|
|
5302
|
+
if (typeof spawn2 !== "function") {
|
|
5303
|
+
throw new ArnsSdkIncompatibleError("it exposes no ANT.spawn");
|
|
5304
|
+
}
|
|
5305
|
+
if (options.mode !== "write") {
|
|
5306
|
+
throw new ArnsSdkIncompatibleError(
|
|
5307
|
+
"ANT.spawn requires the write-mode SDK (a signer) \u2014 this client was built signerless"
|
|
5308
|
+
);
|
|
5309
|
+
}
|
|
5310
|
+
const result = await spawn2({
|
|
5311
|
+
rpc,
|
|
5312
|
+
...writeExtras,
|
|
5313
|
+
state: { name: args.name },
|
|
5314
|
+
...antProgramId !== void 0 ? { antProgramId } : {}
|
|
5315
|
+
});
|
|
5316
|
+
return { processId: result.processId, signature: result.signature };
|
|
5317
|
+
},
|
|
5318
|
+
buildSetRecordTransaction: async (args) => {
|
|
5319
|
+
if (options.mode !== "write") {
|
|
5320
|
+
throw new ArnsSdkIncompatibleError(
|
|
5321
|
+
"buildSetRecordTransaction requires the write-mode SDK (a client partial-signature) \u2014 this client was built signerless"
|
|
5322
|
+
);
|
|
5323
|
+
}
|
|
5324
|
+
const contractsSpecifier = "@ar.io/solana-contracts/ant";
|
|
5325
|
+
let contracts;
|
|
5326
|
+
try {
|
|
5327
|
+
contracts = await import(contractsSpecifier);
|
|
5328
|
+
} catch (err) {
|
|
5329
|
+
throw new ArnsSdkUnavailableError(err, "@ar.io/solana-contracts");
|
|
5330
|
+
}
|
|
5331
|
+
const getSetRecordInstructionAsync = contracts.getSetRecordInstructionAsync;
|
|
5332
|
+
const getAntRecordPDA = mod.getAntRecordPDA;
|
|
5333
|
+
const {
|
|
5334
|
+
createKeyPairFromBytes,
|
|
5335
|
+
createNoopSigner,
|
|
5336
|
+
address: toAddr,
|
|
5337
|
+
blockhash: toBlockhash,
|
|
5338
|
+
createTransactionMessage,
|
|
5339
|
+
setTransactionMessageFeePayer,
|
|
5340
|
+
setTransactionMessageLifetimeUsingBlockhash,
|
|
5341
|
+
appendTransactionMessageInstructions,
|
|
5342
|
+
compileTransaction,
|
|
5343
|
+
partiallySignTransaction,
|
|
5344
|
+
getBase64EncodedWireTransaction
|
|
5345
|
+
} = kit;
|
|
5346
|
+
if (!getSetRecordInstructionAsync || !getAntRecordPDA || !createKeyPairFromBytes || !createNoopSigner || !toAddr || !toBlockhash || !createTransactionMessage || !setTransactionMessageFeePayer || !setTransactionMessageLifetimeUsingBlockhash || !appendTransactionMessageInstructions || !compileTransaction || !partiallySignTransaction || !getBase64EncodedWireTransaction) {
|
|
5347
|
+
throw new ArnsSdkIncompatibleError(
|
|
5348
|
+
"the installed @ar.io/sdk / @ar.io/solana-contracts / @solana/kit do not expose the record-set transaction builder surface"
|
|
5349
|
+
);
|
|
5350
|
+
}
|
|
5351
|
+
const asset = toAddr(args.antProcessId);
|
|
5352
|
+
const antProgram = antProgramId !== void 0 ? toAddr(antProgramId) : void 0;
|
|
5353
|
+
const [recordPda] = await getAntRecordPDA(
|
|
5354
|
+
asset,
|
|
5355
|
+
args.undername,
|
|
5356
|
+
antProgram
|
|
5357
|
+
);
|
|
5358
|
+
const ix = await getSetRecordInstructionAsync(
|
|
5359
|
+
{
|
|
5360
|
+
asset,
|
|
5361
|
+
record: recordPda,
|
|
5362
|
+
caller: createNoopSigner(toAddr(options.solanaPublicKey)),
|
|
5363
|
+
undername: args.undername,
|
|
5364
|
+
target: args.transactionId,
|
|
5365
|
+
targetProtocol: 0,
|
|
5366
|
+
ttlSeconds: args.ttlSeconds,
|
|
5367
|
+
priority: null,
|
|
5368
|
+
recordOwner: null
|
|
5369
|
+
},
|
|
5370
|
+
antProgram !== void 0 ? { programAddress: antProgram } : void 0
|
|
5371
|
+
);
|
|
5372
|
+
let message = createTransactionMessage({ version: 0 });
|
|
5373
|
+
message = setTransactionMessageFeePayer(toAddr(args.feePayer), message);
|
|
5374
|
+
message = setTransactionMessageLifetimeUsingBlockhash(
|
|
5375
|
+
{
|
|
5376
|
+
blockhash: toBlockhash(args.recentBlockhash),
|
|
5377
|
+
lastValidBlockHeight: 0n
|
|
5378
|
+
},
|
|
5379
|
+
message
|
|
5380
|
+
);
|
|
5381
|
+
message = appendTransactionMessageInstructions([ix], message);
|
|
5382
|
+
const keyPair = await createKeyPairFromBytes(options.solanaSecretKey);
|
|
5383
|
+
const signed = await partiallySignTransaction(
|
|
5384
|
+
[keyPair],
|
|
5385
|
+
compileTransaction(message)
|
|
5386
|
+
);
|
|
5387
|
+
return getBase64EncodedWireTransaction(signed);
|
|
5388
|
+
},
|
|
5389
|
+
getArNSRecord: async (args) => {
|
|
5390
|
+
let raw;
|
|
5391
|
+
try {
|
|
5392
|
+
raw = await ario.getArNSRecord(args);
|
|
5393
|
+
} catch (err) {
|
|
5394
|
+
if (err instanceof Error && /not found/i.test(err.message)) return null;
|
|
5395
|
+
throw err;
|
|
5396
|
+
}
|
|
5397
|
+
return {
|
|
5398
|
+
processId: raw.processId ?? null,
|
|
5399
|
+
type: raw.type ?? null,
|
|
5400
|
+
...raw.startTimestamp !== void 0 ? { startTimestamp: raw.startTimestamp * 1e3 } : {},
|
|
5401
|
+
...raw.endTimestamp !== void 0 ? { endTimestamp: raw.endTimestamp * 1e3 } : {},
|
|
5402
|
+
...raw.undernameLimit !== void 0 ? { undernameLimit: raw.undernameLimit } : {}
|
|
5403
|
+
};
|
|
5404
|
+
},
|
|
4975
5405
|
ant: async (pid) => {
|
|
4976
|
-
const ant = antInit({
|
|
5406
|
+
const ant = await antInit({
|
|
5407
|
+
processId: pid,
|
|
5408
|
+
rpc,
|
|
5409
|
+
...writeExtras,
|
|
5410
|
+
...antProgramId !== void 0 ? { antProgramId } : {}
|
|
5411
|
+
});
|
|
4977
5412
|
return {
|
|
4978
|
-
getRecords: () =>
|
|
4979
|
-
|
|
4980
|
-
|
|
5413
|
+
getRecords: async () => {
|
|
5414
|
+
const records = await ant.getRecords();
|
|
5415
|
+
const targets = {};
|
|
5416
|
+
for (const [undername, record] of Object.entries(records)) {
|
|
5417
|
+
targets[undername] = {
|
|
5418
|
+
transactionId: record.transactionId,
|
|
5419
|
+
ttlSeconds: record.ttlSeconds
|
|
5420
|
+
};
|
|
5421
|
+
}
|
|
5422
|
+
return targets;
|
|
5423
|
+
},
|
|
5424
|
+
setBaseNameRecord: async (a) => {
|
|
5425
|
+
if (typeof ant.setBaseNameRecord !== "function") {
|
|
5426
|
+
throw new ArnsSdkIncompatibleError(
|
|
5427
|
+
"the signed ANT client exposes no setBaseNameRecord"
|
|
5428
|
+
);
|
|
5429
|
+
}
|
|
5430
|
+
return ant.setBaseNameRecord(a);
|
|
5431
|
+
},
|
|
5432
|
+
setUndernameRecord: async (a) => {
|
|
5433
|
+
if (typeof ant.setUndernameRecord !== "function") {
|
|
5434
|
+
throw new ArnsSdkIncompatibleError(
|
|
5435
|
+
"the signed ANT client exposes no setUndernameRecord"
|
|
5436
|
+
);
|
|
5437
|
+
}
|
|
5438
|
+
return ant.setUndernameRecord(a);
|
|
5439
|
+
}
|
|
4981
5440
|
};
|
|
4982
5441
|
}
|
|
4983
5442
|
};
|
|
4984
5443
|
};
|
|
5444
|
+
var ARNS_BUY_JOB_KIND = 5095;
|
|
5445
|
+
var DvmBuyJobError = class extends Error {
|
|
5446
|
+
constructor(message) {
|
|
5447
|
+
super(message);
|
|
5448
|
+
this.name = "DvmBuyJobError";
|
|
5449
|
+
}
|
|
5450
|
+
};
|
|
5451
|
+
var defaultSubmitDvmBuyJob = async (request) => {
|
|
5452
|
+
const { finalizeEvent } = await import("nostr-tools/pure");
|
|
5453
|
+
const event = finalizeEvent(
|
|
5454
|
+
{
|
|
5455
|
+
kind: ARNS_BUY_JOB_KIND,
|
|
5456
|
+
created_at: Math.floor(Date.now() / 1e3),
|
|
5457
|
+
content: "",
|
|
5458
|
+
tags: [
|
|
5459
|
+
["param", "name", request.name],
|
|
5460
|
+
["param", "type", request.type],
|
|
5461
|
+
...request.years !== null ? [["param", "years", String(request.years)]] : [],
|
|
5462
|
+
["param", "processId", request.processId]
|
|
5463
|
+
]
|
|
5464
|
+
},
|
|
5465
|
+
request.nostrSecretKey
|
|
5466
|
+
);
|
|
5467
|
+
const url = `${request.viaUrl.replace(/\/+$/, "")}/store`;
|
|
5468
|
+
let response;
|
|
5469
|
+
try {
|
|
5470
|
+
response = await fetch(url, {
|
|
5471
|
+
method: "POST",
|
|
5472
|
+
headers: { "content-type": "application/json" },
|
|
5473
|
+
body: JSON.stringify({ event })
|
|
5474
|
+
});
|
|
5475
|
+
} catch (err) {
|
|
5476
|
+
throw new DvmBuyJobError(
|
|
5477
|
+
`could not reach the DVM at ${url}: ${err instanceof Error ? err.message : String(err)}`
|
|
5478
|
+
);
|
|
5479
|
+
}
|
|
5480
|
+
let body;
|
|
5481
|
+
try {
|
|
5482
|
+
body = await response.json();
|
|
5483
|
+
} catch {
|
|
5484
|
+
throw new DvmBuyJobError(
|
|
5485
|
+
`the DVM at ${url} returned a non-JSON response (HTTP ${response.status})`
|
|
5486
|
+
);
|
|
5487
|
+
}
|
|
5488
|
+
if (!response.ok || body.accept !== true) {
|
|
5489
|
+
throw new DvmBuyJobError(
|
|
5490
|
+
`the DVM rejected the kind:${ARNS_BUY_JOB_KIND} buy job (HTTP ${response.status}${body.code ? `, ${body.code}` : ""})${body.message ? `: ${body.message}` : ""}`
|
|
5491
|
+
);
|
|
5492
|
+
}
|
|
5493
|
+
const registryTxId = body.result?.registryTxId;
|
|
5494
|
+
if (typeof registryTxId !== "string" || registryTxId.length === 0) {
|
|
5495
|
+
throw new DvmBuyJobError(
|
|
5496
|
+
`the DVM accepted the job but returned no registryTxId \u2014 response: ` + JSON.stringify(body.result ?? null)
|
|
5497
|
+
);
|
|
5498
|
+
}
|
|
5499
|
+
return {
|
|
5500
|
+
registryTxId,
|
|
5501
|
+
quotedMario: typeof body.result?.quotedMario === "string" ? body.result.quotedMario : null,
|
|
5502
|
+
syncAttributesTxId: typeof body.result?.syncAttributesTxId === "string" ? body.result.syncAttributesTxId : null
|
|
5503
|
+
};
|
|
5504
|
+
};
|
|
5505
|
+
var GAS_STATION_JOB_KIND = 5096;
|
|
5506
|
+
var GasStationJobError = class extends Error {
|
|
5507
|
+
constructor(reason, message) {
|
|
5508
|
+
super(message);
|
|
5509
|
+
this.reason = reason;
|
|
5510
|
+
this.name = "GasStationJobError";
|
|
5511
|
+
}
|
|
5512
|
+
reason;
|
|
5513
|
+
};
|
|
5514
|
+
async function postGasStationJob(viaUrl, params, nostrSecretKey) {
|
|
5515
|
+
const { finalizeEvent } = await import("nostr-tools/pure");
|
|
5516
|
+
const event = finalizeEvent(
|
|
5517
|
+
{
|
|
5518
|
+
kind: GAS_STATION_JOB_KIND,
|
|
5519
|
+
created_at: Math.floor(Date.now() / 1e3),
|
|
5520
|
+
content: "",
|
|
5521
|
+
tags: params.map(([k, v]) => ["param", k, v])
|
|
5522
|
+
},
|
|
5523
|
+
nostrSecretKey
|
|
5524
|
+
);
|
|
5525
|
+
const url = `${viaUrl.replace(/\/+$/, "")}/store`;
|
|
5526
|
+
let response;
|
|
5527
|
+
try {
|
|
5528
|
+
response = await fetch(url, {
|
|
5529
|
+
method: "POST",
|
|
5530
|
+
headers: { "content-type": "application/json" },
|
|
5531
|
+
body: JSON.stringify({ event })
|
|
5532
|
+
});
|
|
5533
|
+
} catch (err) {
|
|
5534
|
+
throw new GasStationJobError(
|
|
5535
|
+
"unreachable",
|
|
5536
|
+
`could not reach the DVM at ${url}: ${err instanceof Error ? err.message : String(err)}`
|
|
5537
|
+
);
|
|
5538
|
+
}
|
|
5539
|
+
let body;
|
|
5540
|
+
try {
|
|
5541
|
+
body = await response.json();
|
|
5542
|
+
} catch {
|
|
5543
|
+
throw new GasStationJobError(
|
|
5544
|
+
"bad_response",
|
|
5545
|
+
`the DVM at ${url} returned a non-JSON response (HTTP ${response.status})`
|
|
5546
|
+
);
|
|
5547
|
+
}
|
|
5548
|
+
if (!response.ok || body.accept !== true || !body.result) {
|
|
5549
|
+
throw new GasStationJobError(
|
|
5550
|
+
String(body.code ?? "rejected"),
|
|
5551
|
+
`the DVM rejected the kind:${GAS_STATION_JOB_KIND} job (HTTP ${response.status}${body.code ? `, ${body.code}` : ""})${body.message ? `: ${body.message}` : ""}`
|
|
5552
|
+
);
|
|
5553
|
+
}
|
|
5554
|
+
const result = body.result;
|
|
5555
|
+
if (result["status"] === "failed") {
|
|
5556
|
+
throw new GasStationJobError(
|
|
5557
|
+
String(result["reason"] ?? "failed"),
|
|
5558
|
+
`gas-station job failed: ${String(result["reason"] ?? "unknown")} \u2014 ${String(result["detail"] ?? "")}`
|
|
5559
|
+
);
|
|
5560
|
+
}
|
|
5561
|
+
return result;
|
|
5562
|
+
}
|
|
5563
|
+
var defaultGasStationClient = {
|
|
5564
|
+
quote: async (args) => {
|
|
5565
|
+
const result = await postGasStationJob(
|
|
5566
|
+
args.viaUrl,
|
|
5567
|
+
[
|
|
5568
|
+
["phase", "quote"],
|
|
5569
|
+
...args.transaction !== void 0 ? [["transaction", args.transaction]] : []
|
|
5570
|
+
],
|
|
5571
|
+
args.nostrSecretKey
|
|
5572
|
+
);
|
|
5573
|
+
const { quoteId, feePayer, maxLamports, recentBlockhash, expiresAt } = result;
|
|
5574
|
+
if (typeof quoteId !== "string" || typeof feePayer !== "string" || typeof maxLamports !== "string" || typeof recentBlockhash !== "string" || typeof expiresAt !== "number") {
|
|
5575
|
+
throw new GasStationJobError(
|
|
5576
|
+
"bad_response",
|
|
5577
|
+
`the DVM quote is missing fields: ${JSON.stringify(result)}`
|
|
5578
|
+
);
|
|
5579
|
+
}
|
|
5580
|
+
return { quoteId, feePayer, maxLamports, recentBlockhash, expiresAt };
|
|
5581
|
+
},
|
|
5582
|
+
execute: async (args) => {
|
|
5583
|
+
const result = await postGasStationJob(
|
|
5584
|
+
args.viaUrl,
|
|
5585
|
+
[
|
|
5586
|
+
["phase", "execute"],
|
|
5587
|
+
["transaction", args.transaction],
|
|
5588
|
+
["quoteId", args.quoteId],
|
|
5589
|
+
["idempotencyKey", args.idempotencyKey]
|
|
5590
|
+
],
|
|
5591
|
+
args.nostrSecretKey
|
|
5592
|
+
);
|
|
5593
|
+
const signature = result["signature"];
|
|
5594
|
+
if (typeof signature !== "string" || signature.length === 0) {
|
|
5595
|
+
throw new GasStationJobError(
|
|
5596
|
+
"bad_response",
|
|
5597
|
+
`the DVM execute result carries no signature: ${JSON.stringify(result)}`
|
|
5598
|
+
);
|
|
5599
|
+
}
|
|
5600
|
+
return {
|
|
5601
|
+
signature,
|
|
5602
|
+
slot: typeof result["slot"] === "string" ? result["slot"] : null,
|
|
5603
|
+
feeLamportsActual: typeof result["feeLamportsActual"] === "string" ? result["feeLamportsActual"] : null,
|
|
5604
|
+
...result["replayed"] === true ? { replayed: true } : {}
|
|
5605
|
+
};
|
|
5606
|
+
}
|
|
5607
|
+
};
|
|
4985
5608
|
var NAME_USAGE = `Usage: rig name <buy|set|status> <name> [options]
|
|
4986
5609
|
|
|
4987
5610
|
ArNS naming for Arweave-served artifacts. A name registered on the ar.io
|
|
@@ -4994,14 +5617,21 @@ mnemonic that pays for pushes (derived at m/44'/501'/0'/0'). Fund it with
|
|
|
4994
5617
|
registry program \u2014 NOT through TOON ILP payment channels.
|
|
4995
5618
|
|
|
4996
5619
|
Commands:
|
|
4997
|
-
name buy <name> [--years n | --permabuy]
|
|
5620
|
+
name buy <name> [--years n | --permabuy] [--via <dvm-url>]
|
|
4998
5621
|
quote (mARIO) \u2192 confirm \u2192 register. The spawned ANT is
|
|
4999
5622
|
owned by this identity's Solana key. Default: 1-year
|
|
5000
5623
|
lease. PAID \u2014 spends mARIO from the Solana wallet.
|
|
5001
|
-
|
|
5624
|
+
With --via: BROKERED \u2014 this identity spawns and owns
|
|
5625
|
+
the ANT (dust SOL only), then a store DVM executes the
|
|
5626
|
+
kind:5095 buy job and pays the mARIO from ITS wallet;
|
|
5627
|
+
the name is owned by your ANT from inception.
|
|
5628
|
+
name set <name> <txId> [--undername <sub>] [--ttl <seconds>] [--via <dvm-url>]
|
|
5002
5629
|
point the name (or an undername) at an Arweave txId
|
|
5003
5630
|
(typically a deployed path manifest). Signs an ANT
|
|
5004
|
-
record update with the Solana key.
|
|
5631
|
+
record update with the Solana key. With --via: the
|
|
5632
|
+
gas-station path (kind:5096) \u2014 you author and
|
|
5633
|
+
partial-sign, the DVM co-signs as fee payer and pays
|
|
5634
|
+
the lamports; needs ZERO SOL in this wallet.
|
|
5005
5635
|
name status <name> FREE: registry record (lease/permabuy, expiry), ANT
|
|
5006
5636
|
process id, current target txId(s), TTL, undernames.
|
|
5007
5637
|
|
|
@@ -5012,11 +5642,18 @@ Options:
|
|
|
5012
5642
|
--undername <sub> target the undername <sub>_<name> instead of the base
|
|
5013
5643
|
name (set)
|
|
5014
5644
|
--ttl <seconds> record TTL in seconds (set; default 3600)
|
|
5015
|
-
--network <net>
|
|
5016
|
-
|
|
5645
|
+
--network <net> cluster: mainnet | devnet (default mainnet; or
|
|
5646
|
+
RIG_ARIO_NETWORK). ar.io has no Solana-testnet
|
|
5647
|
+
deployment. Non-mainnet gateway resolution is
|
|
5017
5648
|
unverified.
|
|
5018
|
-
--process-id <id> explicit registry
|
|
5019
|
-
RIG_ARIO_PROCESS_ID)
|
|
5649
|
+
--process-id <id> explicit ArNS registry program id \u2014 a Solana program
|
|
5650
|
+
address (overrides --network; or RIG_ARIO_PROCESS_ID)
|
|
5651
|
+
--via <dvm-url> buy only: broker the purchase through a store DVM's
|
|
5652
|
+
kind:5095 job endpoint (or RIG_ARNS_DVM_URL). The DVM
|
|
5653
|
+
pays the mARIO; you own the name via your spawned ANT.
|
|
5654
|
+
Direct backend targeting stubs the job payment
|
|
5655
|
+
(dev/e2e) \u2014 the paid path runs the same job through
|
|
5656
|
+
the connector payment proxy.
|
|
5020
5657
|
--yes skip the confirmation (required when not a TTY) for
|
|
5021
5658
|
buy/set
|
|
5022
5659
|
--json machine-readable envelope; for buy/set without --yes it
|
|
@@ -5024,8 +5661,8 @@ Options:
|
|
|
5024
5661
|
-h, --help show this help`;
|
|
5025
5662
|
var DEFAULT_TTL_SECONDS = 3600;
|
|
5026
5663
|
var DEFAULT_LEASE_YEARS = 1;
|
|
5027
|
-
var NETWORKS = ["mainnet", "devnet"
|
|
5028
|
-
async function resolveNameContext(deps, network, processId) {
|
|
5664
|
+
var NETWORKS = ["mainnet", "devnet"];
|
|
5665
|
+
async function resolveNameContext(deps, network, processId, mode) {
|
|
5029
5666
|
const { io: io2, env } = deps;
|
|
5030
5667
|
const resolved = await resolveIdentity({
|
|
5031
5668
|
env,
|
|
@@ -5049,12 +5686,17 @@ async function resolveNameContext(deps, network, processId) {
|
|
|
5049
5686
|
);
|
|
5050
5687
|
}
|
|
5051
5688
|
const sdk = await (deps.loadArns ?? defaultLoadArns)({
|
|
5689
|
+
mode,
|
|
5052
5690
|
solanaSecretKey: derived.solana.secretKey,
|
|
5053
5691
|
solanaPublicKey: solanaAddress,
|
|
5054
5692
|
network,
|
|
5055
5693
|
...processId !== void 0 ? { processId } : {}
|
|
5056
5694
|
});
|
|
5057
|
-
|
|
5695
|
+
const nostrSecretKey = client.deriveNostrKeyFromMnemonic(
|
|
5696
|
+
resolved.mnemonic,
|
|
5697
|
+
resolved.accountIndex
|
|
5698
|
+
).secretKey;
|
|
5699
|
+
return { identity, solanaAddress, network, processId, sdk, nostrSecretKey };
|
|
5058
5700
|
}
|
|
5059
5701
|
function parseNameFlags(args, env) {
|
|
5060
5702
|
const { values, positionals } = parseArgs13({
|
|
@@ -5067,19 +5709,31 @@ function parseNameFlags(args, env) {
|
|
|
5067
5709
|
ttl: { type: "string" },
|
|
5068
5710
|
network: { type: "string" },
|
|
5069
5711
|
"process-id": { type: "string" },
|
|
5712
|
+
via: { type: "string" },
|
|
5070
5713
|
yes: { type: "boolean", default: false },
|
|
5071
5714
|
json: { type: "boolean", default: false },
|
|
5072
5715
|
help: { type: "boolean", short: "h", default: false }
|
|
5073
5716
|
}
|
|
5074
5717
|
});
|
|
5075
5718
|
if (values.help) {
|
|
5076
|
-
return {
|
|
5719
|
+
return {
|
|
5720
|
+
positionals: ["--help"],
|
|
5721
|
+
flags: {
|
|
5722
|
+
permabuy: false,
|
|
5723
|
+
ttl: 0,
|
|
5724
|
+
network: "mainnet",
|
|
5725
|
+
yes: false,
|
|
5726
|
+
json: false
|
|
5727
|
+
}
|
|
5728
|
+
};
|
|
5077
5729
|
}
|
|
5078
5730
|
let years;
|
|
5079
5731
|
if (values.years !== void 0) {
|
|
5080
5732
|
years = Number(values.years);
|
|
5081
5733
|
if (!Number.isInteger(years) || years <= 0) {
|
|
5082
|
-
throw new Error(
|
|
5734
|
+
throw new Error(
|
|
5735
|
+
`--years must be a positive integer, got ${JSON.stringify(values.years)}`
|
|
5736
|
+
);
|
|
5083
5737
|
}
|
|
5084
5738
|
}
|
|
5085
5739
|
if (years !== void 0 && values.permabuy) {
|
|
@@ -5089,14 +5743,28 @@ function parseNameFlags(args, env) {
|
|
|
5089
5743
|
if (values.ttl !== void 0) {
|
|
5090
5744
|
ttl = Number(values.ttl);
|
|
5091
5745
|
if (!Number.isInteger(ttl) || ttl <= 0) {
|
|
5092
|
-
throw new Error(
|
|
5746
|
+
throw new Error(
|
|
5747
|
+
`--ttl must be a positive integer number of seconds, got ${JSON.stringify(values.ttl)}`
|
|
5748
|
+
);
|
|
5093
5749
|
}
|
|
5094
5750
|
}
|
|
5095
5751
|
const networkRaw = values.network ?? env["RIG_ARIO_NETWORK"] ?? "mainnet";
|
|
5096
5752
|
if (!NETWORKS.includes(networkRaw)) {
|
|
5097
|
-
throw new Error(
|
|
5753
|
+
throw new Error(
|
|
5754
|
+
`--network must be one of ${NETWORKS.join(" | ")}, got ${JSON.stringify(networkRaw)}`
|
|
5755
|
+
);
|
|
5098
5756
|
}
|
|
5099
5757
|
const processId = values["process-id"] ?? env["RIG_ARIO_PROCESS_ID"];
|
|
5758
|
+
const viaRaw = values.via ?? env["RIG_ARNS_DVM_URL"];
|
|
5759
|
+
let via;
|
|
5760
|
+
if (viaRaw !== void 0) {
|
|
5761
|
+
if (!/^https?:\/\/.+/.test(viaRaw)) {
|
|
5762
|
+
throw new Error(
|
|
5763
|
+
`--via must be an http(s) DVM endpoint URL, got ${JSON.stringify(viaRaw)}`
|
|
5764
|
+
);
|
|
5765
|
+
}
|
|
5766
|
+
via = viaRaw;
|
|
5767
|
+
}
|
|
5100
5768
|
return {
|
|
5101
5769
|
positionals,
|
|
5102
5770
|
flags: {
|
|
@@ -5106,6 +5774,7 @@ function parseNameFlags(args, env) {
|
|
|
5106
5774
|
ttl,
|
|
5107
5775
|
network: networkRaw,
|
|
5108
5776
|
...processId !== void 0 ? { processId } : {},
|
|
5777
|
+
...via !== void 0 ? { via } : {},
|
|
5109
5778
|
yes: values.yes ?? false,
|
|
5110
5779
|
json: values.json ?? false
|
|
5111
5780
|
}
|
|
@@ -5115,7 +5784,12 @@ async function runBuy(name, flags, deps) {
|
|
|
5115
5784
|
const { io: io2 } = deps;
|
|
5116
5785
|
const type = flags.permabuy ? "permabuy" : "lease";
|
|
5117
5786
|
const years = type === "lease" ? flags.years ?? DEFAULT_LEASE_YEARS : null;
|
|
5118
|
-
const ctx = await resolveNameContext(
|
|
5787
|
+
const ctx = await resolveNameContext(
|
|
5788
|
+
deps,
|
|
5789
|
+
flags.network,
|
|
5790
|
+
flags.processId,
|
|
5791
|
+
"read"
|
|
5792
|
+
);
|
|
5119
5793
|
const mario = await ctx.sdk.getTokenCost({
|
|
5120
5794
|
intent: "Buy-Name",
|
|
5121
5795
|
name,
|
|
@@ -5126,6 +5800,8 @@ async function runBuy(name, flags, deps) {
|
|
|
5126
5800
|
mARIO: mario.toString(),
|
|
5127
5801
|
ARIO: formatUnits(mario.toString(), ARIO_DECIMALS)
|
|
5128
5802
|
};
|
|
5803
|
+
const via = flags.via ?? null;
|
|
5804
|
+
const paymentNote = via === null ? MARIO_PAYMENT_NOTE : DVM_PAYMENT_NOTE;
|
|
5129
5805
|
const buildJson = (executed, extra = {}) => ({
|
|
5130
5806
|
command: "name",
|
|
5131
5807
|
action: "buy",
|
|
@@ -5134,46 +5810,100 @@ async function runBuy(name, flags, deps) {
|
|
|
5134
5810
|
years,
|
|
5135
5811
|
network: ctx.network,
|
|
5136
5812
|
processId: ctx.processId ?? null,
|
|
5813
|
+
via,
|
|
5137
5814
|
identity: ctx.identity,
|
|
5138
5815
|
solanaAddress: ctx.solanaAddress,
|
|
5139
5816
|
quote,
|
|
5140
|
-
payment:
|
|
5817
|
+
payment: paymentNote,
|
|
5141
5818
|
executed,
|
|
5142
5819
|
...extra
|
|
5143
5820
|
});
|
|
5144
5821
|
if (!flags.json) {
|
|
5145
5822
|
io2.out(
|
|
5146
|
-
`Buy ArNS name "${name}" \u2014 ${type}` + (years !== null ? ` (${years} year${years === 1 ? "" : "s"})` : "") + ` on ${ctx.network}`
|
|
5823
|
+
`Buy ArNS name "${name}" \u2014 ${type}` + (years !== null ? ` (${years} year${years === 1 ? "" : "s"})` : "") + ` on ${ctx.network}` + (via !== null ? ` \u2014 brokered via DVM ${via}` : "")
|
|
5824
|
+
);
|
|
5825
|
+
io2.out(
|
|
5826
|
+
via === null ? ` Cost: ${renderMario(mario)}` : ` Name cost (paid by the DVM): ${renderMario(mario)}`
|
|
5147
5827
|
);
|
|
5148
|
-
io2.out(` Cost: ${renderMario(mario)}`);
|
|
5149
5828
|
io2.out(` Solana wallet: ${ctx.solanaAddress}`);
|
|
5150
|
-
io2.out(` ${
|
|
5829
|
+
io2.out(` ${paymentNote}`);
|
|
5151
5830
|
io2.out(renderIdentityLine(ctx.identity));
|
|
5152
5831
|
}
|
|
5153
5832
|
if (!flags.yes) {
|
|
5154
5833
|
if (flags.json) {
|
|
5155
5834
|
io2.emitJson(
|
|
5156
5835
|
buildJson(false, {
|
|
5157
|
-
hint: "estimate only \u2014 re-run with --yes to buy (spends mARIO on Solana, non-refundable)"
|
|
5836
|
+
hint: via === null ? "estimate only \u2014 re-run with --yes to buy (spends mARIO on Solana, non-refundable)" : "estimate only \u2014 re-run with --yes to spawn your ANT and submit the kind:5095 buy job to the DVM"
|
|
5158
5837
|
})
|
|
5159
5838
|
);
|
|
5160
5839
|
return 0;
|
|
5161
5840
|
}
|
|
5162
5841
|
if (!io2.isInteractive) {
|
|
5163
5842
|
io2.err(
|
|
5164
|
-
"refusing to spend mARIO without confirmation in a non-interactive session \u2014 re-run with --yes (or use --json for a pure estimate)"
|
|
5843
|
+
via === null ? "refusing to spend mARIO without confirmation in a non-interactive session \u2014 re-run with --yes (or use --json for a pure estimate)" : "refusing to spawn an ANT and submit a paid DVM job without confirmation in a non-interactive session \u2014 re-run with --yes (or use --json for a pure estimate)"
|
|
5165
5844
|
);
|
|
5166
5845
|
return 1;
|
|
5167
5846
|
}
|
|
5168
5847
|
const proceed = await io2.confirm(
|
|
5169
|
-
`Proceed \u2014 buy "${name}" for ${renderMario(mario)} from your Solana wallet? [y/N] `
|
|
5848
|
+
via === null ? `Proceed \u2014 buy "${name}" for ${renderMario(mario)} from your Solana wallet? [y/N] ` : `Proceed \u2014 spawn your ANT and have the DVM at ${via} buy "${name}" for ${renderMario(mario)} from ITS wallet? [y/N] `
|
|
5170
5849
|
);
|
|
5171
5850
|
if (!proceed) {
|
|
5172
5851
|
io2.err("aborted \u2014 nothing was bought or paid.");
|
|
5173
5852
|
return 1;
|
|
5174
5853
|
}
|
|
5175
5854
|
}
|
|
5176
|
-
const
|
|
5855
|
+
const signed = await resolveNameContext(
|
|
5856
|
+
deps,
|
|
5857
|
+
flags.network,
|
|
5858
|
+
flags.processId,
|
|
5859
|
+
"write"
|
|
5860
|
+
);
|
|
5861
|
+
if (via !== null) {
|
|
5862
|
+
const spawn2 = await signed.sdk.spawnAnt({ name });
|
|
5863
|
+
if (!flags.json) {
|
|
5864
|
+
io2.out(`Spawned ANT ${spawn2.processId} (tx ${spawn2.signature})`);
|
|
5865
|
+
}
|
|
5866
|
+
const submit = deps.submitDvmBuyJob ?? defaultSubmitDvmBuyJob;
|
|
5867
|
+
const receipt2 = await submit({
|
|
5868
|
+
viaUrl: via,
|
|
5869
|
+
name,
|
|
5870
|
+
type,
|
|
5871
|
+
years,
|
|
5872
|
+
processId: spawn2.processId,
|
|
5873
|
+
nostrSecretKey: signed.nostrSecretKey
|
|
5874
|
+
});
|
|
5875
|
+
const result2 = {
|
|
5876
|
+
registryTxId: receipt2.registryTxId,
|
|
5877
|
+
antProcessId: spawn2.processId,
|
|
5878
|
+
syncAttributesTxId: receipt2.syncAttributesTxId,
|
|
5879
|
+
dvmQuotedMario: receipt2.quotedMario
|
|
5880
|
+
};
|
|
5881
|
+
if (flags.json) {
|
|
5882
|
+
io2.emitJson(
|
|
5883
|
+
buildJson(true, {
|
|
5884
|
+
spawn: { processId: spawn2.processId, signature: spawn2.signature },
|
|
5885
|
+
result: result2
|
|
5886
|
+
})
|
|
5887
|
+
);
|
|
5888
|
+
} else {
|
|
5889
|
+
io2.out(
|
|
5890
|
+
`Registered "${name}" via the DVM \u2014 registry tx ${result2.registryTxId}`
|
|
5891
|
+
);
|
|
5892
|
+
io2.out(` ANT process (owned by YOU): ${spawn2.processId}`);
|
|
5893
|
+
if (receipt2.syncAttributesTxId) {
|
|
5894
|
+
io2.out(` Trait sync: ${receipt2.syncAttributesTxId}`);
|
|
5895
|
+
} else {
|
|
5896
|
+
io2.out(
|
|
5897
|
+
" Trait sync: not run by the DVM (holder-gated on this deployment) \u2014 as the ANT holder you can reconcile with syncAttributes later; resolution works regardless."
|
|
5898
|
+
);
|
|
5899
|
+
}
|
|
5900
|
+
io2.out(
|
|
5901
|
+
`Point it at content with \`rig name set ${name} <txId>\`, then it resolves at https://${name}.<gateway>/`
|
|
5902
|
+
);
|
|
5903
|
+
}
|
|
5904
|
+
return 0;
|
|
5905
|
+
}
|
|
5906
|
+
const receipt = await signed.sdk.buyRecord({
|
|
5177
5907
|
name,
|
|
5178
5908
|
type,
|
|
5179
5909
|
...years !== null ? { years } : {}
|
|
@@ -5195,7 +5925,12 @@ async function runBuy(name, flags, deps) {
|
|
|
5195
5925
|
}
|
|
5196
5926
|
async function runSet(name, txId, flags, deps) {
|
|
5197
5927
|
const { io: io2 } = deps;
|
|
5198
|
-
const ctx = await resolveNameContext(
|
|
5928
|
+
const ctx = await resolveNameContext(
|
|
5929
|
+
deps,
|
|
5930
|
+
flags.network,
|
|
5931
|
+
flags.processId,
|
|
5932
|
+
"read"
|
|
5933
|
+
);
|
|
5199
5934
|
const record = await ctx.sdk.getArNSRecord({ name });
|
|
5200
5935
|
if (!record || !record.processId) {
|
|
5201
5936
|
throw new Error(
|
|
@@ -5204,6 +5939,7 @@ async function runSet(name, txId, flags, deps) {
|
|
|
5204
5939
|
}
|
|
5205
5940
|
const antProcessId = record.processId;
|
|
5206
5941
|
const undername = flags.undername ?? null;
|
|
5942
|
+
const via = flags.via ?? null;
|
|
5207
5943
|
const buildJson = (executed, extra = {}) => ({
|
|
5208
5944
|
command: "name",
|
|
5209
5945
|
action: "set",
|
|
@@ -5213,6 +5949,7 @@ async function runSet(name, txId, flags, deps) {
|
|
|
5213
5949
|
ttl: flags.ttl,
|
|
5214
5950
|
network: ctx.network,
|
|
5215
5951
|
processId: ctx.processId ?? null,
|
|
5952
|
+
via,
|
|
5216
5953
|
identity: ctx.identity,
|
|
5217
5954
|
solanaAddress: ctx.solanaAddress,
|
|
5218
5955
|
antProcessId,
|
|
@@ -5221,16 +5958,23 @@ async function runSet(name, txId, flags, deps) {
|
|
|
5221
5958
|
});
|
|
5222
5959
|
const targetLabel = undername ? `undername "${undername}_${name}"` : `base name "${name}"`;
|
|
5223
5960
|
if (!flags.json) {
|
|
5224
|
-
io2.out(
|
|
5961
|
+
io2.out(
|
|
5962
|
+
`Set ${targetLabel} \u2192 txId ${txId} (ttl ${flags.ttl}s) on ${ctx.network}` + (via !== null ? ` \u2014 gas paid by the DVM at ${via} (kind:5096)` : "")
|
|
5963
|
+
);
|
|
5225
5964
|
io2.out(` ANT process: ${antProcessId}`);
|
|
5226
5965
|
io2.out(` Signed by the identity's Solana key (${ctx.solanaAddress}).`);
|
|
5966
|
+
if (via !== null) {
|
|
5967
|
+
io2.out(
|
|
5968
|
+
" Gas station: you author + partial-sign the record update; the DVM co-signs as fee payer and pays the lamports (toon-meta#163). Direct backend targeting stubs the job payment (dev/e2e)."
|
|
5969
|
+
);
|
|
5970
|
+
}
|
|
5227
5971
|
io2.out(renderIdentityLine(ctx.identity));
|
|
5228
5972
|
}
|
|
5229
5973
|
if (!flags.yes) {
|
|
5230
5974
|
if (flags.json) {
|
|
5231
5975
|
io2.emitJson(
|
|
5232
5976
|
buildJson(false, {
|
|
5233
|
-
hint: "estimate only \u2014 re-run with --yes to write the ANT record"
|
|
5977
|
+
hint: via === null ? "estimate only \u2014 re-run with --yes to write the ANT record" : "estimate only \u2014 re-run with --yes to quote the gas-station and write the ANT record (DVM pays the gas)"
|
|
5234
5978
|
})
|
|
5235
5979
|
);
|
|
5236
5980
|
return 0;
|
|
@@ -5242,28 +5986,90 @@ async function runSet(name, txId, flags, deps) {
|
|
|
5242
5986
|
return 1;
|
|
5243
5987
|
}
|
|
5244
5988
|
const proceed = await io2.confirm(
|
|
5245
|
-
`Proceed \u2014 point ${targetLabel} at ${txId}? [y/N]
|
|
5989
|
+
`Proceed \u2014 point ${targetLabel} at ${txId}?` + (via !== null ? " (the DVM pays the gas)" : "") + " [y/N] "
|
|
5246
5990
|
);
|
|
5247
5991
|
if (!proceed) {
|
|
5248
5992
|
io2.err("aborted \u2014 the ANT record was not changed.");
|
|
5249
5993
|
return 1;
|
|
5250
5994
|
}
|
|
5251
5995
|
}
|
|
5252
|
-
const
|
|
5253
|
-
|
|
5996
|
+
const signed = await resolveNameContext(
|
|
5997
|
+
deps,
|
|
5998
|
+
flags.network,
|
|
5999
|
+
flags.processId,
|
|
6000
|
+
"write"
|
|
6001
|
+
);
|
|
6002
|
+
if (via !== null) {
|
|
6003
|
+
const gas = deps.gasStation ?? defaultGasStationClient;
|
|
6004
|
+
const quote = await gas.quote({
|
|
6005
|
+
viaUrl: via,
|
|
6006
|
+
nostrSecretKey: signed.nostrSecretKey
|
|
6007
|
+
});
|
|
6008
|
+
const wireTx = await signed.sdk.buildSetRecordTransaction({
|
|
6009
|
+
antProcessId,
|
|
6010
|
+
undername: undername ?? "@",
|
|
6011
|
+
transactionId: txId,
|
|
6012
|
+
ttlSeconds: flags.ttl,
|
|
6013
|
+
feePayer: quote.feePayer,
|
|
6014
|
+
recentBlockhash: quote.recentBlockhash
|
|
6015
|
+
});
|
|
6016
|
+
const executed = await gas.execute({
|
|
6017
|
+
viaUrl: via,
|
|
6018
|
+
transaction: wireTx,
|
|
6019
|
+
quoteId: quote.quoteId,
|
|
6020
|
+
idempotencyKey: randomUUID(),
|
|
6021
|
+
nostrSecretKey: signed.nostrSecretKey
|
|
6022
|
+
});
|
|
6023
|
+
const gasStation = {
|
|
6024
|
+
quoteId: quote.quoteId,
|
|
6025
|
+
feePayer: quote.feePayer,
|
|
6026
|
+
maxLamports: quote.maxLamports,
|
|
6027
|
+
expiresAt: quote.expiresAt,
|
|
6028
|
+
feeLamportsActual: executed.feeLamportsActual
|
|
6029
|
+
};
|
|
6030
|
+
const result2 = { messageId: executed.signature };
|
|
6031
|
+
if (flags.json) {
|
|
6032
|
+
io2.emitJson(buildJson(true, { gasStation, result: result2 }));
|
|
6033
|
+
} else {
|
|
6034
|
+
io2.out(
|
|
6035
|
+
`Updated \u2014 record-set tx ${executed.signature} (gas paid by ${quote.feePayer})`
|
|
6036
|
+
);
|
|
6037
|
+
const host = undername ? `${undername}_${name}` : name;
|
|
6038
|
+
io2.out(
|
|
6039
|
+
` Resolves at https://${host}.<gateway>/ (allow for gateway cache/TTL).`
|
|
6040
|
+
);
|
|
6041
|
+
}
|
|
6042
|
+
return 0;
|
|
6043
|
+
}
|
|
6044
|
+
const ant = await signed.sdk.ant(antProcessId);
|
|
6045
|
+
const receipt = undername ? await ant.setUndernameRecord({
|
|
6046
|
+
undername,
|
|
6047
|
+
transactionId: txId,
|
|
6048
|
+
ttlSeconds: flags.ttl
|
|
6049
|
+
}) : await ant.setBaseNameRecord({
|
|
6050
|
+
transactionId: txId,
|
|
6051
|
+
ttlSeconds: flags.ttl
|
|
6052
|
+
});
|
|
5254
6053
|
const result = { messageId: receipt.id };
|
|
5255
6054
|
if (flags.json) {
|
|
5256
6055
|
io2.emitJson(buildJson(true, { result }));
|
|
5257
6056
|
} else {
|
|
5258
6057
|
io2.out(`Updated \u2014 ANT message ${result.messageId}`);
|
|
5259
6058
|
const host = undername ? `${undername}_${name}` : name;
|
|
5260
|
-
io2.out(
|
|
6059
|
+
io2.out(
|
|
6060
|
+
` Resolves at https://${host}.<gateway>/ (allow for gateway cache/TTL).`
|
|
6061
|
+
);
|
|
5261
6062
|
}
|
|
5262
6063
|
return 0;
|
|
5263
6064
|
}
|
|
5264
6065
|
async function runStatus(name, flags, deps) {
|
|
5265
6066
|
const { io: io2 } = deps;
|
|
5266
|
-
const ctx = await resolveNameContext(
|
|
6067
|
+
const ctx = await resolveNameContext(
|
|
6068
|
+
deps,
|
|
6069
|
+
flags.network,
|
|
6070
|
+
flags.processId,
|
|
6071
|
+
"read"
|
|
6072
|
+
);
|
|
5267
6073
|
const record = await ctx.sdk.getArNSRecord({ name });
|
|
5268
6074
|
let targets = null;
|
|
5269
6075
|
if (record?.processId) {
|
|
@@ -5311,7 +6117,9 @@ async function runStatus(name, flags, deps) {
|
|
|
5311
6117
|
io2.out(" Records:");
|
|
5312
6118
|
for (const [under, target] of Object.entries(targets)) {
|
|
5313
6119
|
const label = under === "@" ? "(base)" : `${under}_${name}`;
|
|
5314
|
-
io2.out(
|
|
6120
|
+
io2.out(
|
|
6121
|
+
` ${label} \u2192 ${target.transactionId} (ttl ${target.ttlSeconds}s)`
|
|
6122
|
+
);
|
|
5315
6123
|
}
|
|
5316
6124
|
} else {
|
|
5317
6125
|
io2.out(" Records: none set \u2014 point one with `rig name set`.");
|
|
@@ -5361,7 +6169,9 @@ async function runName(args, deps) {
|
|
|
5361
6169
|
if (sub === "status") return await runStatus(name, flags, deps);
|
|
5362
6170
|
const txId = positionals[1];
|
|
5363
6171
|
if (txId === void 0) {
|
|
5364
|
-
io2.err(
|
|
6172
|
+
io2.err(
|
|
6173
|
+
"`rig name set` needs a <txId> (the Arweave transaction to point at)"
|
|
6174
|
+
);
|
|
5365
6175
|
io2.err(NAME_USAGE);
|
|
5366
6176
|
return 2;
|
|
5367
6177
|
}
|
|
@@ -5391,22 +6201,22 @@ function buildArweaveManifest(entries, indexPath = DEFAULT_INDEX, fallbackTxId)
|
|
|
5391
6201
|
}
|
|
5392
6202
|
|
|
5393
6203
|
// src/cli/site-record.ts
|
|
5394
|
-
import { mkdirSync as
|
|
5395
|
-
import { homedir as
|
|
5396
|
-
import { join as
|
|
6204
|
+
import { mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
6205
|
+
import { homedir as homedir3 } from "os";
|
|
6206
|
+
import { join as join4 } from "path";
|
|
5397
6207
|
var SITE_MANIFEST_FILENAME = "site-manifests.json";
|
|
5398
|
-
function
|
|
5399
|
-
return env["TOON_CLIENT_HOME"] ??
|
|
6208
|
+
function storeDir2(env) {
|
|
6209
|
+
return env["TOON_CLIENT_HOME"] ?? join4(homedir3(), ".toon-client");
|
|
5400
6210
|
}
|
|
5401
|
-
function
|
|
5402
|
-
return
|
|
6211
|
+
function storePath2(env) {
|
|
6212
|
+
return join4(storeDir2(env), SITE_MANIFEST_FILENAME);
|
|
5403
6213
|
}
|
|
5404
6214
|
function keyOf(repoId, ref) {
|
|
5405
6215
|
return `${repoId}\0${ref}`;
|
|
5406
6216
|
}
|
|
5407
6217
|
function readFile2(env) {
|
|
5408
6218
|
try {
|
|
5409
|
-
const parsed = JSON.parse(
|
|
6219
|
+
const parsed = JSON.parse(readFileSync3(storePath2(env), "utf8"));
|
|
5410
6220
|
if (parsed && typeof parsed === "object") return parsed;
|
|
5411
6221
|
return {};
|
|
5412
6222
|
} catch {
|
|
@@ -5417,11 +6227,11 @@ function readSiteRecord(env, repoId, ref) {
|
|
|
5417
6227
|
return readFile2(env)[keyOf(repoId, ref)];
|
|
5418
6228
|
}
|
|
5419
6229
|
function writeSiteRecord(env, record) {
|
|
5420
|
-
const dir =
|
|
5421
|
-
|
|
6230
|
+
const dir = storeDir2(env);
|
|
6231
|
+
mkdirSync3(dir, { recursive: true });
|
|
5422
6232
|
const file = readFile2(env);
|
|
5423
6233
|
file[keyOf(record.repoId, record.ref)] = record;
|
|
5424
|
-
|
|
6234
|
+
writeFileSync2(storePath2(env), `${JSON.stringify(file, null, 2)}
|
|
5425
6235
|
`, "utf8");
|
|
5426
6236
|
}
|
|
5427
6237
|
|