@toon-protocol/rig 2.6.3 → 2.6.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-6SQ7723I.js → chunk-2DBPLBXW.js} +59 -3
- package/dist/chunk-2DBPLBXW.js.map +1 -0
- package/dist/{chunk-SW7ZHMGS.js → chunk-57UY3OGX.js} +67 -1
- package/dist/chunk-57UY3OGX.js.map +1 -0
- package/dist/{chunk-NIRC5LFS.js → chunk-PG5PUKDR.js} +41 -2
- package/dist/chunk-PG5PUKDR.js.map +1 -0
- package/dist/cli/rig.js +918 -5
- package/dist/cli/rig.js.map +1 -1
- package/dist/index.d.ts +13 -2
- package/dist/index.js +1 -1
- package/dist/{publisher-BtVceNeI.d.ts → publisher-BvDfjvHK.d.ts} +30 -1
- package/dist/standalone/index.d.ts +16 -1
- package/dist/standalone/index.js +2 -2
- package/dist/{standalone-mode-WEMJBHME.js → standalone-mode-W4DULOEC.js} +9 -5
- package/dist/standalone-mode-W4DULOEC.js.map +1 -0
- package/package.json +5 -2
- package/dist/chunk-6SQ7723I.js.map +0 -1
- package/dist/chunk-NIRC5LFS.js.map +0 -1
- package/dist/chunk-SW7ZHMGS.js.map +0 -1
- package/dist/standalone-mode-WEMJBHME.js.map +0 -1
package/dist/cli/rig.js
CHANGED
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
setHeadSymref,
|
|
19
19
|
updateRef,
|
|
20
20
|
writeGitObjects
|
|
21
|
-
} from "../chunk-
|
|
21
|
+
} from "../chunk-PG5PUKDR.js";
|
|
22
22
|
import {
|
|
23
23
|
DEFAULT_KEYSTORE_PASSWORD,
|
|
24
24
|
MissingIdentityError,
|
|
@@ -32,8 +32,9 @@ import {
|
|
|
32
32
|
ChannelMapStore,
|
|
33
33
|
channelStatus,
|
|
34
34
|
defaultDaemonPort,
|
|
35
|
-
resolveChannelPaths
|
|
36
|
-
|
|
35
|
+
resolveChannelPaths,
|
|
36
|
+
resolveConflictingPath
|
|
37
|
+
} from "../chunk-57UY3OGX.js";
|
|
37
38
|
import {
|
|
38
39
|
MAX_OBJECT_SIZE
|
|
39
40
|
} from "../chunk-B5ISARMU.js";
|
|
@@ -398,6 +399,13 @@ function describeError(err, command = "push") {
|
|
|
398
399
|
json: { error: "git_error", detail: err.message }
|
|
399
400
|
};
|
|
400
401
|
}
|
|
402
|
+
if (err instanceof Error && err.name === "ArnsSdkUnavailableError") {
|
|
403
|
+
return {
|
|
404
|
+
code: "arns_sdk_unavailable",
|
|
405
|
+
lines: err.message.split("\n"),
|
|
406
|
+
json: { error: "arns_sdk_unavailable", detail: err.message }
|
|
407
|
+
};
|
|
408
|
+
}
|
|
401
409
|
if (err instanceof DaemonRouteError) {
|
|
402
410
|
const envelope = err.envelope;
|
|
403
411
|
if (envelope.error === "non_fast_forward" && Array.isArray(envelope["refs"])) {
|
|
@@ -966,7 +974,7 @@ function loadPaidSession(deps, relayUrl) {
|
|
|
966
974
|
});
|
|
967
975
|
}
|
|
968
976
|
var defaultLoadStandalone = async (options) => {
|
|
969
|
-
const mod = await import("../standalone-mode-
|
|
977
|
+
const mod = await import("../standalone-mode-W4DULOEC.js");
|
|
970
978
|
return mod.createStandaloneContext(options);
|
|
971
979
|
};
|
|
972
980
|
function identityReport(ctx) {
|
|
@@ -4912,6 +4920,896 @@ async function runMutate(op, args, deps) {
|
|
|
4912
4920
|
}
|
|
4913
4921
|
}
|
|
4914
4922
|
|
|
4923
|
+
// src/cli/name.ts
|
|
4924
|
+
import { parseArgs as parseArgs13 } from "util";
|
|
4925
|
+
var ARIO_DECIMALS = 6;
|
|
4926
|
+
function renderMario(mario) {
|
|
4927
|
+
return `${mario.toString()} mARIO (~${formatUnits(mario.toString(), ARIO_DECIMALS)} ARIO)`;
|
|
4928
|
+
}
|
|
4929
|
+
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).";
|
|
4930
|
+
var ArnsSdkUnavailableError = class extends Error {
|
|
4931
|
+
constructor(cause) {
|
|
4932
|
+
super(
|
|
4933
|
+
"`rig name` needs the optional `@ar.io/sdk` dependency, which is not installed. It is intentionally NOT a base `rig` dependency (keeps the install lean). Add it to use ArNS naming:\n npm i @ar.io/sdk # or: pnpm add @ar.io/sdk\nthen re-run your `rig name` command." + (cause instanceof Error ? `
|
|
4934
|
+
(underlying error: ${cause.message})` : "")
|
|
4935
|
+
);
|
|
4936
|
+
this.name = "ArnsSdkUnavailableError";
|
|
4937
|
+
}
|
|
4938
|
+
};
|
|
4939
|
+
var ARIO_MAINNET_PROCESS_ID = "qNvAoz0TgcH7DMg8BCVn8jF32QH5L6T29VjHxhHqqGE";
|
|
4940
|
+
var defaultLoadArns = async (options) => {
|
|
4941
|
+
const specifier = "@ar.io/sdk";
|
|
4942
|
+
let mod;
|
|
4943
|
+
try {
|
|
4944
|
+
mod = await import(specifier);
|
|
4945
|
+
} catch (err) {
|
|
4946
|
+
throw new ArnsSdkUnavailableError(err);
|
|
4947
|
+
}
|
|
4948
|
+
if (!mod.ARIO?.init || !mod.ANT?.init) {
|
|
4949
|
+
throw new ArnsSdkUnavailableError(
|
|
4950
|
+
new Error("the installed @ar.io/sdk does not expose ARIO.init / ANT.init")
|
|
4951
|
+
);
|
|
4952
|
+
}
|
|
4953
|
+
const processId = options.processId ?? (options.network === "devnet" ? mod.ARIO_DEVNET_PROCESS_ID ?? "" : options.network === "testnet" ? mod.ARIO_TESTNET_PROCESS_ID ?? "" : mod.ARIO_MAINNET_PROCESS_ID ?? ARIO_MAINNET_PROCESS_ID);
|
|
4954
|
+
if (!processId) {
|
|
4955
|
+
throw new ArnsSdkUnavailableError(
|
|
4956
|
+
new Error(
|
|
4957
|
+
`the installed @ar.io/sdk exposes no ${options.network} registry process id \u2014 pass --process-id explicitly`
|
|
4958
|
+
)
|
|
4959
|
+
);
|
|
4960
|
+
}
|
|
4961
|
+
if (!mod.SolanaSigner) {
|
|
4962
|
+
throw new ArnsSdkUnavailableError(
|
|
4963
|
+
new Error(
|
|
4964
|
+
"the installed @ar.io/sdk exposes no SolanaSigner \u2014 this build predates ar.io's Solana migration; upgrade @ar.io/sdk"
|
|
4965
|
+
)
|
|
4966
|
+
);
|
|
4967
|
+
}
|
|
4968
|
+
const signer = new mod.SolanaSigner(options.solanaSecretKey);
|
|
4969
|
+
const ario = mod.ARIO.init({ process: processId, signer });
|
|
4970
|
+
const antInit = mod.ANT.init;
|
|
4971
|
+
return {
|
|
4972
|
+
getTokenCost: async (args) => BigInt(String(await ario.getTokenCost(args))),
|
|
4973
|
+
buyRecord: async (args) => await ario.buyRecord(args),
|
|
4974
|
+
getArNSRecord: async (args) => await ario.getArNSRecord(args),
|
|
4975
|
+
ant: async (pid) => {
|
|
4976
|
+
const ant = antInit({ processId: pid, signer });
|
|
4977
|
+
return {
|
|
4978
|
+
getRecords: () => ant.getRecords(),
|
|
4979
|
+
setBaseNameRecord: (a) => ant.setBaseNameRecord(a),
|
|
4980
|
+
setUndernameRecord: (a) => ant.setUndernameRecord(a)
|
|
4981
|
+
};
|
|
4982
|
+
}
|
|
4983
|
+
};
|
|
4984
|
+
};
|
|
4985
|
+
var NAME_USAGE = `Usage: rig name <buy|set|status> <name> [options]
|
|
4986
|
+
|
|
4987
|
+
ArNS naming for Arweave-served artifacts. A name registered on the ar.io
|
|
4988
|
+
registry resolves at every ar.io gateway (https://<name>.<gateway>/), pointing
|
|
4989
|
+
at whatever Arweave txId its ANT record targets (10 undernames included).
|
|
4990
|
+
|
|
4991
|
+
Names are OWNED and PAID FOR by this rig identity's Solana key \u2014 the same
|
|
4992
|
+
mnemonic that pays for pushes (derived at m/44'/501'/0'/0'). Fund it with
|
|
4993
|
+
\`rig fund --chain solana\`. Purchases spend mARIO on Solana via the ar.io
|
|
4994
|
+
registry program \u2014 NOT through TOON ILP payment channels.
|
|
4995
|
+
|
|
4996
|
+
Commands:
|
|
4997
|
+
name buy <name> [--years n | --permabuy]
|
|
4998
|
+
quote (mARIO) \u2192 confirm \u2192 register. The spawned ANT is
|
|
4999
|
+
owned by this identity's Solana key. Default: 1-year
|
|
5000
|
+
lease. PAID \u2014 spends mARIO from the Solana wallet.
|
|
5001
|
+
name set <name> <txId> [--undername <sub>] [--ttl <seconds>]
|
|
5002
|
+
point the name (or an undername) at an Arweave txId
|
|
5003
|
+
(typically a deployed path manifest). Signs an ANT
|
|
5004
|
+
record update with the Solana key.
|
|
5005
|
+
name status <name> FREE: registry record (lease/permabuy, expiry), ANT
|
|
5006
|
+
process id, current target txId(s), TTL, undernames.
|
|
5007
|
+
|
|
5008
|
+
Options:
|
|
5009
|
+
--years <n> lease length in years (buy; default 1; mutually
|
|
5010
|
+
exclusive with --permabuy)
|
|
5011
|
+
--permabuy permanent registration instead of a lease (buy)
|
|
5012
|
+
--undername <sub> target the undername <sub>_<name> instead of the base
|
|
5013
|
+
name (set)
|
|
5014
|
+
--ttl <seconds> record TTL in seconds (set; default 3600)
|
|
5015
|
+
--network <net> registry: mainnet | devnet | testnet (default mainnet;
|
|
5016
|
+
or RIG_ARIO_NETWORK). Non-mainnet gateway resolution is
|
|
5017
|
+
unverified.
|
|
5018
|
+
--process-id <id> explicit registry process id (overrides --network; or
|
|
5019
|
+
RIG_ARIO_PROCESS_ID)
|
|
5020
|
+
--yes skip the confirmation (required when not a TTY) for
|
|
5021
|
+
buy/set
|
|
5022
|
+
--json machine-readable envelope; for buy/set without --yes it
|
|
5023
|
+
is a pure estimate (nothing is bought or written)
|
|
5024
|
+
-h, --help show this help`;
|
|
5025
|
+
var DEFAULT_TTL_SECONDS = 3600;
|
|
5026
|
+
var DEFAULT_LEASE_YEARS = 1;
|
|
5027
|
+
var NETWORKS = ["mainnet", "devnet", "testnet"];
|
|
5028
|
+
async function resolveNameContext(deps, network, processId) {
|
|
5029
|
+
const { io: io2, env } = deps;
|
|
5030
|
+
const resolved = await resolveIdentity({
|
|
5031
|
+
env,
|
|
5032
|
+
cwd: deps.cwd,
|
|
5033
|
+
warn: (line) => io2.err(line)
|
|
5034
|
+
});
|
|
5035
|
+
const identity = {
|
|
5036
|
+
pubkey: resolved.pubkey,
|
|
5037
|
+
source: resolved.source,
|
|
5038
|
+
sourceLabel: resolved.sourceLabel
|
|
5039
|
+
};
|
|
5040
|
+
const client = await import("@toon-protocol/client");
|
|
5041
|
+
const derived = await client.deriveFullIdentity(
|
|
5042
|
+
resolved.mnemonic,
|
|
5043
|
+
resolved.accountIndex
|
|
5044
|
+
);
|
|
5045
|
+
const solanaAddress = derived.solana.publicKey;
|
|
5046
|
+
if (!solanaAddress) {
|
|
5047
|
+
throw new Error(
|
|
5048
|
+
"no Solana key could be derived for this identity \u2014 ArNS names are owned by the Solana key (m/44'/501'/0'/0'). Ensure the optional Solana derivation deps are installed."
|
|
5049
|
+
);
|
|
5050
|
+
}
|
|
5051
|
+
const sdk = await (deps.loadArns ?? defaultLoadArns)({
|
|
5052
|
+
solanaSecretKey: derived.solana.secretKey,
|
|
5053
|
+
solanaPublicKey: solanaAddress,
|
|
5054
|
+
network,
|
|
5055
|
+
...processId !== void 0 ? { processId } : {}
|
|
5056
|
+
});
|
|
5057
|
+
return { identity, solanaAddress, network, processId, sdk };
|
|
5058
|
+
}
|
|
5059
|
+
function parseNameFlags(args, env) {
|
|
5060
|
+
const { values, positionals } = parseArgs13({
|
|
5061
|
+
args,
|
|
5062
|
+
allowPositionals: true,
|
|
5063
|
+
options: {
|
|
5064
|
+
years: { type: "string" },
|
|
5065
|
+
permabuy: { type: "boolean", default: false },
|
|
5066
|
+
undername: { type: "string" },
|
|
5067
|
+
ttl: { type: "string" },
|
|
5068
|
+
network: { type: "string" },
|
|
5069
|
+
"process-id": { type: "string" },
|
|
5070
|
+
yes: { type: "boolean", default: false },
|
|
5071
|
+
json: { type: "boolean", default: false },
|
|
5072
|
+
help: { type: "boolean", short: "h", default: false }
|
|
5073
|
+
}
|
|
5074
|
+
});
|
|
5075
|
+
if (values.help) {
|
|
5076
|
+
return { positionals: ["--help"], flags: { permabuy: false, ttl: 0, network: "mainnet", yes: false, json: false } };
|
|
5077
|
+
}
|
|
5078
|
+
let years;
|
|
5079
|
+
if (values.years !== void 0) {
|
|
5080
|
+
years = Number(values.years);
|
|
5081
|
+
if (!Number.isInteger(years) || years <= 0) {
|
|
5082
|
+
throw new Error(`--years must be a positive integer, got ${JSON.stringify(values.years)}`);
|
|
5083
|
+
}
|
|
5084
|
+
}
|
|
5085
|
+
if (years !== void 0 && values.permabuy) {
|
|
5086
|
+
throw new Error("--years and --permabuy are mutually exclusive");
|
|
5087
|
+
}
|
|
5088
|
+
let ttl = DEFAULT_TTL_SECONDS;
|
|
5089
|
+
if (values.ttl !== void 0) {
|
|
5090
|
+
ttl = Number(values.ttl);
|
|
5091
|
+
if (!Number.isInteger(ttl) || ttl <= 0) {
|
|
5092
|
+
throw new Error(`--ttl must be a positive integer number of seconds, got ${JSON.stringify(values.ttl)}`);
|
|
5093
|
+
}
|
|
5094
|
+
}
|
|
5095
|
+
const networkRaw = values.network ?? env["RIG_ARIO_NETWORK"] ?? "mainnet";
|
|
5096
|
+
if (!NETWORKS.includes(networkRaw)) {
|
|
5097
|
+
throw new Error(`--network must be one of ${NETWORKS.join(" | ")}, got ${JSON.stringify(networkRaw)}`);
|
|
5098
|
+
}
|
|
5099
|
+
const processId = values["process-id"] ?? env["RIG_ARIO_PROCESS_ID"];
|
|
5100
|
+
return {
|
|
5101
|
+
positionals,
|
|
5102
|
+
flags: {
|
|
5103
|
+
...years !== void 0 ? { years } : {},
|
|
5104
|
+
permabuy: values.permabuy ?? false,
|
|
5105
|
+
...values.undername !== void 0 ? { undername: values.undername } : {},
|
|
5106
|
+
ttl,
|
|
5107
|
+
network: networkRaw,
|
|
5108
|
+
...processId !== void 0 ? { processId } : {},
|
|
5109
|
+
yes: values.yes ?? false,
|
|
5110
|
+
json: values.json ?? false
|
|
5111
|
+
}
|
|
5112
|
+
};
|
|
5113
|
+
}
|
|
5114
|
+
async function runBuy(name, flags, deps) {
|
|
5115
|
+
const { io: io2 } = deps;
|
|
5116
|
+
const type = flags.permabuy ? "permabuy" : "lease";
|
|
5117
|
+
const years = type === "lease" ? flags.years ?? DEFAULT_LEASE_YEARS : null;
|
|
5118
|
+
const ctx = await resolveNameContext(deps, flags.network, flags.processId);
|
|
5119
|
+
const mario = await ctx.sdk.getTokenCost({
|
|
5120
|
+
intent: "Buy-Name",
|
|
5121
|
+
name,
|
|
5122
|
+
type,
|
|
5123
|
+
...years !== null ? { years } : {}
|
|
5124
|
+
});
|
|
5125
|
+
const quote = {
|
|
5126
|
+
mARIO: mario.toString(),
|
|
5127
|
+
ARIO: formatUnits(mario.toString(), ARIO_DECIMALS)
|
|
5128
|
+
};
|
|
5129
|
+
const buildJson = (executed, extra = {}) => ({
|
|
5130
|
+
command: "name",
|
|
5131
|
+
action: "buy",
|
|
5132
|
+
name,
|
|
5133
|
+
type,
|
|
5134
|
+
years,
|
|
5135
|
+
network: ctx.network,
|
|
5136
|
+
processId: ctx.processId ?? null,
|
|
5137
|
+
identity: ctx.identity,
|
|
5138
|
+
solanaAddress: ctx.solanaAddress,
|
|
5139
|
+
quote,
|
|
5140
|
+
payment: MARIO_PAYMENT_NOTE,
|
|
5141
|
+
executed,
|
|
5142
|
+
...extra
|
|
5143
|
+
});
|
|
5144
|
+
if (!flags.json) {
|
|
5145
|
+
io2.out(
|
|
5146
|
+
`Buy ArNS name "${name}" \u2014 ${type}` + (years !== null ? ` (${years} year${years === 1 ? "" : "s"})` : "") + ` on ${ctx.network}`
|
|
5147
|
+
);
|
|
5148
|
+
io2.out(` Cost: ${renderMario(mario)}`);
|
|
5149
|
+
io2.out(` Solana wallet: ${ctx.solanaAddress}`);
|
|
5150
|
+
io2.out(` ${MARIO_PAYMENT_NOTE}`);
|
|
5151
|
+
io2.out(renderIdentityLine(ctx.identity));
|
|
5152
|
+
}
|
|
5153
|
+
if (!flags.yes) {
|
|
5154
|
+
if (flags.json) {
|
|
5155
|
+
io2.emitJson(
|
|
5156
|
+
buildJson(false, {
|
|
5157
|
+
hint: "estimate only \u2014 re-run with --yes to buy (spends mARIO on Solana, non-refundable)"
|
|
5158
|
+
})
|
|
5159
|
+
);
|
|
5160
|
+
return 0;
|
|
5161
|
+
}
|
|
5162
|
+
if (!io2.isInteractive) {
|
|
5163
|
+
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)"
|
|
5165
|
+
);
|
|
5166
|
+
return 1;
|
|
5167
|
+
}
|
|
5168
|
+
const proceed = await io2.confirm(
|
|
5169
|
+
`Proceed \u2014 buy "${name}" for ${renderMario(mario)} from your Solana wallet? [y/N] `
|
|
5170
|
+
);
|
|
5171
|
+
if (!proceed) {
|
|
5172
|
+
io2.err("aborted \u2014 nothing was bought or paid.");
|
|
5173
|
+
return 1;
|
|
5174
|
+
}
|
|
5175
|
+
}
|
|
5176
|
+
const receipt = await ctx.sdk.buyRecord({
|
|
5177
|
+
name,
|
|
5178
|
+
type,
|
|
5179
|
+
...years !== null ? { years } : {}
|
|
5180
|
+
});
|
|
5181
|
+
const result = {
|
|
5182
|
+
registryTxId: receipt.id,
|
|
5183
|
+
antProcessId: receipt.processId ?? null
|
|
5184
|
+
};
|
|
5185
|
+
if (flags.json) {
|
|
5186
|
+
io2.emitJson(buildJson(true, { result }));
|
|
5187
|
+
} else {
|
|
5188
|
+
io2.out(`Registered "${name}" \u2014 registry tx ${result.registryTxId}`);
|
|
5189
|
+
if (result.antProcessId) io2.out(` ANT process: ${result.antProcessId}`);
|
|
5190
|
+
io2.out(
|
|
5191
|
+
`Point it at content with \`rig name set ${name} <txId>\`, then it resolves at https://${name}.<gateway>/`
|
|
5192
|
+
);
|
|
5193
|
+
}
|
|
5194
|
+
return 0;
|
|
5195
|
+
}
|
|
5196
|
+
async function runSet(name, txId, flags, deps) {
|
|
5197
|
+
const { io: io2 } = deps;
|
|
5198
|
+
const ctx = await resolveNameContext(deps, flags.network, flags.processId);
|
|
5199
|
+
const record = await ctx.sdk.getArNSRecord({ name });
|
|
5200
|
+
if (!record || !record.processId) {
|
|
5201
|
+
throw new Error(
|
|
5202
|
+
`no ArNS record for "${name}" on ${ctx.network} \u2014 buy it first with \`rig name buy ${name}\` (or check \`rig name status ${name}\`)`
|
|
5203
|
+
);
|
|
5204
|
+
}
|
|
5205
|
+
const antProcessId = record.processId;
|
|
5206
|
+
const undername = flags.undername ?? null;
|
|
5207
|
+
const buildJson = (executed, extra = {}) => ({
|
|
5208
|
+
command: "name",
|
|
5209
|
+
action: "set",
|
|
5210
|
+
name,
|
|
5211
|
+
undername,
|
|
5212
|
+
txId,
|
|
5213
|
+
ttl: flags.ttl,
|
|
5214
|
+
network: ctx.network,
|
|
5215
|
+
processId: ctx.processId ?? null,
|
|
5216
|
+
identity: ctx.identity,
|
|
5217
|
+
solanaAddress: ctx.solanaAddress,
|
|
5218
|
+
antProcessId,
|
|
5219
|
+
executed,
|
|
5220
|
+
...extra
|
|
5221
|
+
});
|
|
5222
|
+
const targetLabel = undername ? `undername "${undername}_${name}"` : `base name "${name}"`;
|
|
5223
|
+
if (!flags.json) {
|
|
5224
|
+
io2.out(`Set ${targetLabel} \u2192 txId ${txId} (ttl ${flags.ttl}s) on ${ctx.network}`);
|
|
5225
|
+
io2.out(` ANT process: ${antProcessId}`);
|
|
5226
|
+
io2.out(` Signed by the identity's Solana key (${ctx.solanaAddress}).`);
|
|
5227
|
+
io2.out(renderIdentityLine(ctx.identity));
|
|
5228
|
+
}
|
|
5229
|
+
if (!flags.yes) {
|
|
5230
|
+
if (flags.json) {
|
|
5231
|
+
io2.emitJson(
|
|
5232
|
+
buildJson(false, {
|
|
5233
|
+
hint: "estimate only \u2014 re-run with --yes to write the ANT record"
|
|
5234
|
+
})
|
|
5235
|
+
);
|
|
5236
|
+
return 0;
|
|
5237
|
+
}
|
|
5238
|
+
if (!io2.isInteractive) {
|
|
5239
|
+
io2.err(
|
|
5240
|
+
"refusing to write an ANT record without confirmation in a non-interactive session \u2014 re-run with --yes (or --json to preview)"
|
|
5241
|
+
);
|
|
5242
|
+
return 1;
|
|
5243
|
+
}
|
|
5244
|
+
const proceed = await io2.confirm(
|
|
5245
|
+
`Proceed \u2014 point ${targetLabel} at ${txId}? [y/N] `
|
|
5246
|
+
);
|
|
5247
|
+
if (!proceed) {
|
|
5248
|
+
io2.err("aborted \u2014 the ANT record was not changed.");
|
|
5249
|
+
return 1;
|
|
5250
|
+
}
|
|
5251
|
+
}
|
|
5252
|
+
const ant = await ctx.sdk.ant(antProcessId);
|
|
5253
|
+
const receipt = undername ? await ant.setUndernameRecord({ undername, transactionId: txId, ttlSeconds: flags.ttl }) : await ant.setBaseNameRecord({ transactionId: txId, ttlSeconds: flags.ttl });
|
|
5254
|
+
const result = { messageId: receipt.id };
|
|
5255
|
+
if (flags.json) {
|
|
5256
|
+
io2.emitJson(buildJson(true, { result }));
|
|
5257
|
+
} else {
|
|
5258
|
+
io2.out(`Updated \u2014 ANT message ${result.messageId}`);
|
|
5259
|
+
const host = undername ? `${undername}_${name}` : name;
|
|
5260
|
+
io2.out(` Resolves at https://${host}.<gateway>/ (allow for gateway cache/TTL).`);
|
|
5261
|
+
}
|
|
5262
|
+
return 0;
|
|
5263
|
+
}
|
|
5264
|
+
async function runStatus(name, flags, deps) {
|
|
5265
|
+
const { io: io2 } = deps;
|
|
5266
|
+
const ctx = await resolveNameContext(deps, flags.network, flags.processId);
|
|
5267
|
+
const record = await ctx.sdk.getArNSRecord({ name });
|
|
5268
|
+
let targets = null;
|
|
5269
|
+
if (record?.processId) {
|
|
5270
|
+
const ant = await ctx.sdk.ant(record.processId);
|
|
5271
|
+
targets = await ant.getRecords();
|
|
5272
|
+
}
|
|
5273
|
+
if (flags.json) {
|
|
5274
|
+
io2.emitJson({
|
|
5275
|
+
command: "name",
|
|
5276
|
+
action: "status",
|
|
5277
|
+
name,
|
|
5278
|
+
network: ctx.network,
|
|
5279
|
+
processId: ctx.processId ?? null,
|
|
5280
|
+
registered: record !== null,
|
|
5281
|
+
identity: ctx.identity,
|
|
5282
|
+
solanaAddress: ctx.solanaAddress,
|
|
5283
|
+
record: record ? {
|
|
5284
|
+
antProcessId: record.processId,
|
|
5285
|
+
type: record.type,
|
|
5286
|
+
startTimestamp: record.startTimestamp ?? null,
|
|
5287
|
+
endTimestamp: record.endTimestamp ?? null,
|
|
5288
|
+
undernameLimit: record.undernameLimit ?? null
|
|
5289
|
+
} : null,
|
|
5290
|
+
targets
|
|
5291
|
+
});
|
|
5292
|
+
return 0;
|
|
5293
|
+
}
|
|
5294
|
+
io2.out(`ArNS name "${name}" on ${ctx.network}:`);
|
|
5295
|
+
if (!record) {
|
|
5296
|
+
io2.out(" not registered \u2014 available to `rig name buy`.");
|
|
5297
|
+
io2.out(renderIdentityLine(ctx.identity));
|
|
5298
|
+
return 0;
|
|
5299
|
+
}
|
|
5300
|
+
io2.out(` ANT process: ${record.processId ?? "(unknown)"}`);
|
|
5301
|
+
io2.out(` Type: ${record.type ?? "(unknown)"}`);
|
|
5302
|
+
if (record.type === "permabuy") {
|
|
5303
|
+
io2.out(" Expiry: never (permabuy)");
|
|
5304
|
+
} else if (record.endTimestamp !== void 0) {
|
|
5305
|
+
io2.out(` Expiry: ${new Date(record.endTimestamp).toISOString()}`);
|
|
5306
|
+
}
|
|
5307
|
+
if (record.undernameLimit !== void 0) {
|
|
5308
|
+
io2.out(` Undernames included: ${record.undernameLimit}`);
|
|
5309
|
+
}
|
|
5310
|
+
if (targets && Object.keys(targets).length > 0) {
|
|
5311
|
+
io2.out(" Records:");
|
|
5312
|
+
for (const [under, target] of Object.entries(targets)) {
|
|
5313
|
+
const label = under === "@" ? "(base)" : `${under}_${name}`;
|
|
5314
|
+
io2.out(` ${label} \u2192 ${target.transactionId} (ttl ${target.ttlSeconds}s)`);
|
|
5315
|
+
}
|
|
5316
|
+
} else {
|
|
5317
|
+
io2.out(" Records: none set \u2014 point one with `rig name set`.");
|
|
5318
|
+
}
|
|
5319
|
+
io2.out(renderIdentityLine(ctx.identity));
|
|
5320
|
+
return 0;
|
|
5321
|
+
}
|
|
5322
|
+
async function runName(args, deps) {
|
|
5323
|
+
const { io: io2 } = deps;
|
|
5324
|
+
const sub = args[0];
|
|
5325
|
+
if (sub === void 0) {
|
|
5326
|
+
io2.err(NAME_USAGE);
|
|
5327
|
+
return 2;
|
|
5328
|
+
}
|
|
5329
|
+
if (sub === "--help" || sub === "-h" || sub === "help") {
|
|
5330
|
+
io2.out(NAME_USAGE);
|
|
5331
|
+
return 0;
|
|
5332
|
+
}
|
|
5333
|
+
if (!["buy", "set", "status"].includes(sub)) {
|
|
5334
|
+
io2.err(`unknown \`rig name\` subcommand: ${JSON.stringify(sub)}`);
|
|
5335
|
+
io2.err(NAME_USAGE);
|
|
5336
|
+
return 2;
|
|
5337
|
+
}
|
|
5338
|
+
let positionals;
|
|
5339
|
+
let flags;
|
|
5340
|
+
try {
|
|
5341
|
+
const parsed = parseNameFlags(args.slice(1), deps.env);
|
|
5342
|
+
positionals = parsed.positionals;
|
|
5343
|
+
flags = parsed.flags;
|
|
5344
|
+
} catch (err) {
|
|
5345
|
+
io2.err(err instanceof Error ? err.message : String(err));
|
|
5346
|
+
io2.err(NAME_USAGE);
|
|
5347
|
+
return 2;
|
|
5348
|
+
}
|
|
5349
|
+
if (positionals[0] === "--help") {
|
|
5350
|
+
io2.out(NAME_USAGE);
|
|
5351
|
+
return 0;
|
|
5352
|
+
}
|
|
5353
|
+
const name = positionals[0];
|
|
5354
|
+
if (name === void 0) {
|
|
5355
|
+
io2.err(`\`rig name ${sub}\` needs a <name>`);
|
|
5356
|
+
io2.err(NAME_USAGE);
|
|
5357
|
+
return 2;
|
|
5358
|
+
}
|
|
5359
|
+
try {
|
|
5360
|
+
if (sub === "buy") return await runBuy(name, flags, deps);
|
|
5361
|
+
if (sub === "status") return await runStatus(name, flags, deps);
|
|
5362
|
+
const txId = positionals[1];
|
|
5363
|
+
if (txId === void 0) {
|
|
5364
|
+
io2.err("`rig name set` needs a <txId> (the Arweave transaction to point at)");
|
|
5365
|
+
io2.err(NAME_USAGE);
|
|
5366
|
+
return 2;
|
|
5367
|
+
}
|
|
5368
|
+
return await runSet(name, txId, flags, deps);
|
|
5369
|
+
} catch (err) {
|
|
5370
|
+
return emitCliError(io2, flags.json, "name", err);
|
|
5371
|
+
}
|
|
5372
|
+
}
|
|
5373
|
+
|
|
5374
|
+
// src/cli/site.ts
|
|
5375
|
+
import { parseArgs as parseArgs14 } from "util";
|
|
5376
|
+
|
|
5377
|
+
// src/arweave-manifest.ts
|
|
5378
|
+
var DEFAULT_INDEX = "index.html";
|
|
5379
|
+
function buildArweaveManifest(entries, indexPath = DEFAULT_INDEX, fallbackTxId) {
|
|
5380
|
+
const paths = {};
|
|
5381
|
+
for (const { path, txId } of entries) {
|
|
5382
|
+
paths[path] = { id: txId };
|
|
5383
|
+
}
|
|
5384
|
+
return {
|
|
5385
|
+
manifest: "arweave/paths",
|
|
5386
|
+
version: "0.2.0",
|
|
5387
|
+
index: { path: indexPath },
|
|
5388
|
+
...fallbackTxId ? { fallback: { id: fallbackTxId } } : {},
|
|
5389
|
+
paths
|
|
5390
|
+
};
|
|
5391
|
+
}
|
|
5392
|
+
|
|
5393
|
+
// src/cli/site-record.ts
|
|
5394
|
+
import { mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
5395
|
+
import { homedir as homedir2 } from "os";
|
|
5396
|
+
import { join as join3 } from "path";
|
|
5397
|
+
var SITE_MANIFEST_FILENAME = "site-manifests.json";
|
|
5398
|
+
function storeDir(env) {
|
|
5399
|
+
return env["TOON_CLIENT_HOME"] ?? join3(homedir2(), ".toon-client");
|
|
5400
|
+
}
|
|
5401
|
+
function storePath(env) {
|
|
5402
|
+
return join3(storeDir(env), SITE_MANIFEST_FILENAME);
|
|
5403
|
+
}
|
|
5404
|
+
function keyOf(repoId, ref) {
|
|
5405
|
+
return `${repoId}\0${ref}`;
|
|
5406
|
+
}
|
|
5407
|
+
function readFile2(env) {
|
|
5408
|
+
try {
|
|
5409
|
+
const parsed = JSON.parse(readFileSync2(storePath(env), "utf8"));
|
|
5410
|
+
if (parsed && typeof parsed === "object") return parsed;
|
|
5411
|
+
return {};
|
|
5412
|
+
} catch {
|
|
5413
|
+
return {};
|
|
5414
|
+
}
|
|
5415
|
+
}
|
|
5416
|
+
function readSiteRecord(env, repoId, ref) {
|
|
5417
|
+
return readFile2(env)[keyOf(repoId, ref)];
|
|
5418
|
+
}
|
|
5419
|
+
function writeSiteRecord(env, record) {
|
|
5420
|
+
const dir = storeDir(env);
|
|
5421
|
+
mkdirSync2(dir, { recursive: true });
|
|
5422
|
+
const file = readFile2(env);
|
|
5423
|
+
file[keyOf(record.repoId, record.ref)] = record;
|
|
5424
|
+
writeFileSync(storePath(env), `${JSON.stringify(file, null, 2)}
|
|
5425
|
+
`, "utf8");
|
|
5426
|
+
}
|
|
5427
|
+
|
|
5428
|
+
// src/cli/site.ts
|
|
5429
|
+
var MANIFEST_CONTENT_TYPE = "application/x.arweave-manifest+json";
|
|
5430
|
+
var DEFAULT_GATEWAY = "https://arweave.net";
|
|
5431
|
+
var SITE_USAGE = `Usage: rig site <publish|url> [ref] [options]
|
|
5432
|
+
|
|
5433
|
+
Serve a pushed repo as a permanent, path-routed website. \`rig push\` already
|
|
5434
|
+
stored the file bytes on Arweave; \`rig site publish\` builds the ar.io path
|
|
5435
|
+
manifest (paths \u2192 Arweave txids, index \u2192 index.html) and uploads it as ONE
|
|
5436
|
+
paid store write, printing the site URL. \`rig site url\` re-prints the URL of
|
|
5437
|
+
the last publish for free.
|
|
5438
|
+
|
|
5439
|
+
The repo must be set up with \`rig init\`; the relay is the "origin" remote
|
|
5440
|
+
(\`rig push\` reads the same). \`ref\` defaults to the current branch.
|
|
5441
|
+
|
|
5442
|
+
Commands:
|
|
5443
|
+
publish [ref] build + upload the path manifest (paid); prints the URL and
|
|
5444
|
+
a \`rig name set\` suggestion (#367)
|
|
5445
|
+
url [ref] print the last-published site URL for a ref (free)
|
|
5446
|
+
|
|
5447
|
+
Options (publish):
|
|
5448
|
+
--index <path> path served at / (default: index.html)
|
|
5449
|
+
--spa single-page app: serve index for any unknown path
|
|
5450
|
+
(sets the manifest \`fallback\` to the index txid)
|
|
5451
|
+
--fallback <path> explicit fallback path for unknown routes (overrides
|
|
5452
|
+
--spa); must be one of the site's files
|
|
5453
|
+
--force-reupload re-pay to re-upload the ref's blobs with #368 per-file
|
|
5454
|
+
Content-Type before building the manifest \u2014 for repos
|
|
5455
|
+
pushed before mime-typing existed (else they serve as
|
|
5456
|
+
application/octet-stream)
|
|
5457
|
+
--gateway <url> gateway base for the printed URL (default: ${DEFAULT_GATEWAY};
|
|
5458
|
+
env RIG_ARWEAVE_GATEWAY also sets it)
|
|
5459
|
+
--relay <url> ad-hoc relay override (bypasses configured remotes)
|
|
5460
|
+
--repo-id <id> repository id (default: git config toon.repoid)
|
|
5461
|
+
--yes skip the fee confirmation (required when not a TTY)
|
|
5462
|
+
--json machine-readable envelope; without --yes it is a pure
|
|
5463
|
+
estimate (nothing uploaded)
|
|
5464
|
+
-h, --help show this help`;
|
|
5465
|
+
function parseSiteArgs(args) {
|
|
5466
|
+
const { values, positionals } = parseArgs14({
|
|
5467
|
+
args,
|
|
5468
|
+
options: {
|
|
5469
|
+
index: { type: "string" },
|
|
5470
|
+
spa: { type: "boolean", default: false },
|
|
5471
|
+
fallback: { type: "string" },
|
|
5472
|
+
"force-reupload": { type: "boolean", default: false },
|
|
5473
|
+
gateway: { type: "string" },
|
|
5474
|
+
relay: { type: "string", multiple: true },
|
|
5475
|
+
"repo-id": { type: "string" },
|
|
5476
|
+
yes: { type: "boolean", default: false },
|
|
5477
|
+
json: { type: "boolean", default: false },
|
|
5478
|
+
help: { type: "boolean", short: "h", default: false }
|
|
5479
|
+
},
|
|
5480
|
+
allowPositionals: true
|
|
5481
|
+
});
|
|
5482
|
+
const flags = {
|
|
5483
|
+
index: values.index ?? "index.html",
|
|
5484
|
+
spa: values.spa ?? false,
|
|
5485
|
+
forceReupload: values["force-reupload"] ?? false,
|
|
5486
|
+
relay: values.relay ?? [],
|
|
5487
|
+
yes: values.yes ?? false,
|
|
5488
|
+
json: values.json ?? false,
|
|
5489
|
+
help: values.help ?? false,
|
|
5490
|
+
positionals
|
|
5491
|
+
};
|
|
5492
|
+
if (values.fallback !== void 0) flags.fallback = values.fallback;
|
|
5493
|
+
if (values.gateway !== void 0) flags.gateway = values.gateway;
|
|
5494
|
+
if (values["repo-id"] !== void 0) flags.repoId = values["repo-id"];
|
|
5495
|
+
return flags;
|
|
5496
|
+
}
|
|
5497
|
+
function normalizeGateway(gateway) {
|
|
5498
|
+
return gateway.replace(/\/+$/, "");
|
|
5499
|
+
}
|
|
5500
|
+
function siteUrl(gateway, manifestTxId) {
|
|
5501
|
+
return `${normalizeGateway(gateway)}/${manifestTxId}/`;
|
|
5502
|
+
}
|
|
5503
|
+
var PLACEHOLDER_TX = "A".repeat(43);
|
|
5504
|
+
async function runSite(args, deps) {
|
|
5505
|
+
const { io: io2 } = deps;
|
|
5506
|
+
const [sub, ...rest] = args;
|
|
5507
|
+
if (sub === "publish") return runSitePublish(rest, deps);
|
|
5508
|
+
if (sub === "url") return runSiteUrl(rest, deps);
|
|
5509
|
+
if (sub === void 0 || sub === "-h" || sub === "--help" || sub === "help") {
|
|
5510
|
+
io2.out(SITE_USAGE);
|
|
5511
|
+
return sub === void 0 ? 2 : 0;
|
|
5512
|
+
}
|
|
5513
|
+
io2.err(`unknown site subcommand ${JSON.stringify(sub)}`);
|
|
5514
|
+
io2.err(SITE_USAGE);
|
|
5515
|
+
return 2;
|
|
5516
|
+
}
|
|
5517
|
+
async function runSitePublish(args, deps) {
|
|
5518
|
+
const { io: io2 } = deps;
|
|
5519
|
+
let flags;
|
|
5520
|
+
try {
|
|
5521
|
+
flags = parseSiteArgs(args);
|
|
5522
|
+
} catch (err) {
|
|
5523
|
+
io2.err(err instanceof Error ? err.message : String(err));
|
|
5524
|
+
io2.err(SITE_USAGE);
|
|
5525
|
+
return 2;
|
|
5526
|
+
}
|
|
5527
|
+
if (flags.help) {
|
|
5528
|
+
io2.out(SITE_USAGE);
|
|
5529
|
+
return 0;
|
|
5530
|
+
}
|
|
5531
|
+
let ctx;
|
|
5532
|
+
try {
|
|
5533
|
+
const repoRoot = await resolveRepoRoot(deps.cwd);
|
|
5534
|
+
const toonConfig = await readToonConfig(repoRoot);
|
|
5535
|
+
const repoId = flags.repoId ?? toonConfig.repoId;
|
|
5536
|
+
if (!repoId) throw new UnconfiguredRepoAddressError("repository id");
|
|
5537
|
+
const reader = new GitRepoReader(repoRoot);
|
|
5538
|
+
const selected = await selectRefspecs(reader, flags.positionals, false, false);
|
|
5539
|
+
if (selected.length !== 1) {
|
|
5540
|
+
throw new Error(
|
|
5541
|
+
`rig site publish builds ONE ref into a site \u2014 got ${selected.length} (${selected.join(", ")}); pass a single branch or tag`
|
|
5542
|
+
);
|
|
5543
|
+
}
|
|
5544
|
+
const ref = selected[0];
|
|
5545
|
+
const resolved = await resolveRelays({
|
|
5546
|
+
relayFlags: flags.relay,
|
|
5547
|
+
remoteName: void 0,
|
|
5548
|
+
repoRoot,
|
|
5549
|
+
toonRelays: toonConfig.relays
|
|
5550
|
+
});
|
|
5551
|
+
if (resolved.nudge !== void 0) io2.err(resolved.nudge);
|
|
5552
|
+
const relaysUsed = resolved.relays;
|
|
5553
|
+
if (relaysUsed.length > 1) {
|
|
5554
|
+
io2.err(singleRelayRefusal(resolved, "Nothing was uploaded or paid."));
|
|
5555
|
+
return 1;
|
|
5556
|
+
}
|
|
5557
|
+
ctx = await (deps.loadStandalone ?? defaultLoadStandalone)({
|
|
5558
|
+
env: deps.env,
|
|
5559
|
+
cwd: deps.cwd,
|
|
5560
|
+
warn: (line) => io2.err(line),
|
|
5561
|
+
...relaysUsed[0] !== void 0 ? { relayUrl: relaysUsed[0] } : {}
|
|
5562
|
+
});
|
|
5563
|
+
const identity = identityReport(ctx);
|
|
5564
|
+
if (!ctx.publisher.uploadBlob) {
|
|
5565
|
+
throw new Error(
|
|
5566
|
+
"the active publisher cannot upload a raw manifest blob (uploadBlob unavailable) \u2014 `rig site` requires the standalone publisher"
|
|
5567
|
+
);
|
|
5568
|
+
}
|
|
5569
|
+
const blobs = await reader.listBlobs(ref);
|
|
5570
|
+
if (blobs.length === 0) {
|
|
5571
|
+
throw new Error(`ref ${JSON.stringify(ref)} has no files to serve`);
|
|
5572
|
+
}
|
|
5573
|
+
const shaToPaths = /* @__PURE__ */ new Map();
|
|
5574
|
+
for (const { path, sha } of blobs) {
|
|
5575
|
+
const paths = shaToPaths.get(sha) ?? [];
|
|
5576
|
+
paths.push(path);
|
|
5577
|
+
shaToPaths.set(sha, paths);
|
|
5578
|
+
}
|
|
5579
|
+
const uniqueShas = [...shaToPaths.keys()];
|
|
5580
|
+
const remoteState = await ctx.fetchRemote({
|
|
5581
|
+
ownerPubkey: ctx.ownerPubkey,
|
|
5582
|
+
repoId,
|
|
5583
|
+
relayUrls: relaysUsed
|
|
5584
|
+
});
|
|
5585
|
+
const known = new Map(remoteState.shaToTxId);
|
|
5586
|
+
const gaps = uniqueShas.filter((sha) => !known.has(sha));
|
|
5587
|
+
if (gaps.length > 0) {
|
|
5588
|
+
const found = await remoteState.resolveMissing(gaps);
|
|
5589
|
+
for (const [sha, txId] of found) known.set(sha, txId);
|
|
5590
|
+
}
|
|
5591
|
+
const fallbackPath = flags.fallback ?? (flags.spa ? flags.index : void 0);
|
|
5592
|
+
const pathSet = new Set(blobs.map((b) => b.path));
|
|
5593
|
+
if (fallbackPath !== void 0 && !pathSet.has(fallbackPath)) {
|
|
5594
|
+
throw new Error(
|
|
5595
|
+
`fallback path ${JSON.stringify(fallbackPath)} is not one of the site's files \u2014 pick an existing path (or the --index file for --spa)`
|
|
5596
|
+
);
|
|
5597
|
+
}
|
|
5598
|
+
if (!pathSet.has(flags.index)) {
|
|
5599
|
+
io2.err(
|
|
5600
|
+
`warning: no ${JSON.stringify(flags.index)} in this ref \u2014 the site will 404 at / until you add one (or pass --index <path>)`
|
|
5601
|
+
);
|
|
5602
|
+
}
|
|
5603
|
+
if (!flags.forceReupload) {
|
|
5604
|
+
const unpublished = uniqueShas.filter((sha) => !known.has(sha));
|
|
5605
|
+
if (unpublished.length > 0) {
|
|
5606
|
+
const paths = unpublished.flatMap((sha) => shaToPaths.get(sha) ?? []).slice(0, 10);
|
|
5607
|
+
throw new Error(
|
|
5608
|
+
`${unpublished.length} file(s) in ${ref} are not on Arweave yet (e.g. ${paths.join(", ")}) \u2014 run \`rig push\` first, or \`rig site publish --force-reupload\` to upload them now (paid).`
|
|
5609
|
+
);
|
|
5610
|
+
}
|
|
5611
|
+
}
|
|
5612
|
+
const { uploadFeePerByte } = await ctx.publisher.getFeeRates();
|
|
5613
|
+
const previewEntries = blobs.map((b) => ({
|
|
5614
|
+
path: b.path,
|
|
5615
|
+
txId: known.get(b.sha) ?? PLACEHOLDER_TX
|
|
5616
|
+
}));
|
|
5617
|
+
const previewManifest = buildArweaveManifest(
|
|
5618
|
+
previewEntries,
|
|
5619
|
+
flags.index,
|
|
5620
|
+
fallbackPath ? PLACEHOLDER_TX : void 0
|
|
5621
|
+
);
|
|
5622
|
+
const manifestBytes = Buffer.byteLength(JSON.stringify(previewManifest));
|
|
5623
|
+
let reuploadBytes = 0;
|
|
5624
|
+
if (flags.forceReupload) {
|
|
5625
|
+
const { objects } = await reader.statObjects(uniqueShas);
|
|
5626
|
+
reuploadBytes = objects.reduce((sum, o) => sum + o.size, 0);
|
|
5627
|
+
}
|
|
5628
|
+
const manifestFee = BigInt(manifestBytes) * uploadFeePerByte;
|
|
5629
|
+
const reuploadFee = BigInt(reuploadBytes) * uploadFeePerByte;
|
|
5630
|
+
const totalFee = manifestFee + reuploadFee;
|
|
5631
|
+
const gateway = flags.gateway ?? deps.env["RIG_ARWEAVE_GATEWAY"] ?? DEFAULT_GATEWAY;
|
|
5632
|
+
const baseJson = () => ({
|
|
5633
|
+
command: "site publish",
|
|
5634
|
+
repoId,
|
|
5635
|
+
ref,
|
|
5636
|
+
identity,
|
|
5637
|
+
forceReupload: flags.forceReupload,
|
|
5638
|
+
fileCount: blobs.length,
|
|
5639
|
+
index: flags.index,
|
|
5640
|
+
...fallbackPath ? { fallback: fallbackPath } : {},
|
|
5641
|
+
estimate: {
|
|
5642
|
+
manifestBytes,
|
|
5643
|
+
reuploadBytes,
|
|
5644
|
+
totalFee: totalFee.toString()
|
|
5645
|
+
}
|
|
5646
|
+
});
|
|
5647
|
+
if (!flags.json) {
|
|
5648
|
+
io2.out(`Site publish plan for ${ref} (repo ${repoId}):`);
|
|
5649
|
+
io2.out(` files: ${blobs.length}`);
|
|
5650
|
+
io2.out(` index: ${flags.index}${pathSet.has(flags.index) ? "" : " (missing!)"}`);
|
|
5651
|
+
if (fallbackPath) io2.out(` fallback: ${fallbackPath}`);
|
|
5652
|
+
if (flags.forceReupload) {
|
|
5653
|
+
io2.out(` re-upload ${uniqueShas.length} blob(s), ${reuploadBytes} bytes (paid)`);
|
|
5654
|
+
}
|
|
5655
|
+
io2.out(` manifest: ${manifestBytes} bytes`);
|
|
5656
|
+
io2.out(` total fee: ${totalFee} base units`);
|
|
5657
|
+
io2.out(renderIdentityLine(identity));
|
|
5658
|
+
}
|
|
5659
|
+
if (!flags.yes) {
|
|
5660
|
+
if (flags.json) {
|
|
5661
|
+
io2.emitJson({
|
|
5662
|
+
...baseJson(),
|
|
5663
|
+
executed: false,
|
|
5664
|
+
hint: "estimate only \u2014 re-run with --yes to upload the manifest (permanent, non-refundable)"
|
|
5665
|
+
});
|
|
5666
|
+
return 0;
|
|
5667
|
+
}
|
|
5668
|
+
if (!io2.isInteractive) {
|
|
5669
|
+
io2.err(
|
|
5670
|
+
"refusing to spend channel funds without confirmation in a non-interactive session \u2014 re-run with --yes (or use --json for an estimate)"
|
|
5671
|
+
);
|
|
5672
|
+
return 1;
|
|
5673
|
+
}
|
|
5674
|
+
const proceed = await io2.confirm(
|
|
5675
|
+
`Proceed with paid site publish (total ${totalFee} base units)? [y/N] `
|
|
5676
|
+
);
|
|
5677
|
+
if (!proceed) {
|
|
5678
|
+
io2.err("aborted \u2014 nothing was uploaded.");
|
|
5679
|
+
return 1;
|
|
5680
|
+
}
|
|
5681
|
+
}
|
|
5682
|
+
if (flags.forceReupload) {
|
|
5683
|
+
const { objects, missing } = await reader.readObjects(uniqueShas);
|
|
5684
|
+
if (missing.length > 0) {
|
|
5685
|
+
throw new Error(
|
|
5686
|
+
`objects vanished from the local repository: ${missing.join(", ")}`
|
|
5687
|
+
);
|
|
5688
|
+
}
|
|
5689
|
+
const bodyBySha = new Map(objects.map((o) => [o.sha, o.body]));
|
|
5690
|
+
for (const sha of uniqueShas) {
|
|
5691
|
+
const body = bodyBySha.get(sha);
|
|
5692
|
+
if (!body) throw new Error(`internal: no body for ${sha}`);
|
|
5693
|
+
const uploadPath = resolveConflictingPath(shaToPaths.get(sha) ?? []);
|
|
5694
|
+
const receipt2 = await ctx.publisher.uploadGitObject({
|
|
5695
|
+
sha,
|
|
5696
|
+
type: "blob",
|
|
5697
|
+
body,
|
|
5698
|
+
repoId,
|
|
5699
|
+
...uploadPath ? { path: uploadPath } : {}
|
|
5700
|
+
});
|
|
5701
|
+
known.set(sha, receipt2.txId);
|
|
5702
|
+
}
|
|
5703
|
+
}
|
|
5704
|
+
const entries = blobs.map((b) => {
|
|
5705
|
+
const txId = known.get(b.sha);
|
|
5706
|
+
if (!txId) {
|
|
5707
|
+
throw new Error(`internal: no Arweave txid for ${b.path} (${b.sha})`);
|
|
5708
|
+
}
|
|
5709
|
+
return { path: b.path, txId };
|
|
5710
|
+
});
|
|
5711
|
+
const fallbackTxId = fallbackPath !== void 0 ? entries.find((e) => e.path === fallbackPath)?.txId : void 0;
|
|
5712
|
+
const manifest = buildArweaveManifest(entries, flags.index, fallbackTxId);
|
|
5713
|
+
const manifestBody = Buffer.from(JSON.stringify(manifest), "utf8");
|
|
5714
|
+
const receipt = await ctx.publisher.uploadBlob({
|
|
5715
|
+
body: manifestBody,
|
|
5716
|
+
contentType: MANIFEST_CONTENT_TYPE,
|
|
5717
|
+
repoId
|
|
5718
|
+
});
|
|
5719
|
+
const url = siteUrl(gateway, receipt.txId);
|
|
5720
|
+
writeSiteRecord(deps.env, {
|
|
5721
|
+
repoId,
|
|
5722
|
+
ref,
|
|
5723
|
+
owner: ctx.ownerPubkey,
|
|
5724
|
+
manifestTxId: receipt.txId,
|
|
5725
|
+
gateway: normalizeGateway(gateway),
|
|
5726
|
+
updatedAt: Date.now()
|
|
5727
|
+
});
|
|
5728
|
+
const nameHint = `Point a name at it (see #367): rig name set <name> ${receipt.txId}`;
|
|
5729
|
+
if (flags.json) {
|
|
5730
|
+
io2.emitJson({
|
|
5731
|
+
...baseJson(),
|
|
5732
|
+
executed: true,
|
|
5733
|
+
manifest: { txId: receipt.txId, url },
|
|
5734
|
+
nameHint
|
|
5735
|
+
});
|
|
5736
|
+
} else {
|
|
5737
|
+
io2.out(`Site published: ${url}`);
|
|
5738
|
+
io2.out(nameHint);
|
|
5739
|
+
}
|
|
5740
|
+
return 0;
|
|
5741
|
+
} catch (err) {
|
|
5742
|
+
return emitCliError(io2, flags.json, "site publish", err);
|
|
5743
|
+
} finally {
|
|
5744
|
+
if (ctx) {
|
|
5745
|
+
try {
|
|
5746
|
+
await ctx.stop();
|
|
5747
|
+
} catch {
|
|
5748
|
+
}
|
|
5749
|
+
}
|
|
5750
|
+
}
|
|
5751
|
+
}
|
|
5752
|
+
async function runSiteUrl(args, deps) {
|
|
5753
|
+
const { io: io2 } = deps;
|
|
5754
|
+
let flags;
|
|
5755
|
+
try {
|
|
5756
|
+
flags = parseSiteArgs(args);
|
|
5757
|
+
} catch (err) {
|
|
5758
|
+
io2.err(err instanceof Error ? err.message : String(err));
|
|
5759
|
+
io2.err(SITE_USAGE);
|
|
5760
|
+
return 2;
|
|
5761
|
+
}
|
|
5762
|
+
if (flags.help) {
|
|
5763
|
+
io2.out(SITE_USAGE);
|
|
5764
|
+
return 0;
|
|
5765
|
+
}
|
|
5766
|
+
try {
|
|
5767
|
+
const repoRoot = await resolveRepoRoot(deps.cwd);
|
|
5768
|
+
const toonConfig = await readToonConfig(repoRoot);
|
|
5769
|
+
const repoId = flags.repoId ?? toonConfig.repoId;
|
|
5770
|
+
if (!repoId) throw new UnconfiguredRepoAddressError("repository id");
|
|
5771
|
+
const reader = new GitRepoReader(repoRoot);
|
|
5772
|
+
const selected = await selectRefspecs(reader, flags.positionals, false, false);
|
|
5773
|
+
if (selected.length !== 1) {
|
|
5774
|
+
throw new Error(
|
|
5775
|
+
`rig site url takes ONE ref \u2014 got ${selected.length} (${selected.join(", ")})`
|
|
5776
|
+
);
|
|
5777
|
+
}
|
|
5778
|
+
const ref = selected[0];
|
|
5779
|
+
const record = readSiteRecord(deps.env, repoId, ref);
|
|
5780
|
+
if (!record) {
|
|
5781
|
+
const detail = `no site published for ${ref} (repo ${repoId}) yet \u2014 run \`rig site publish\` first (the manifest txid is only known after a paid publish)`;
|
|
5782
|
+
if (flags.json) {
|
|
5783
|
+
io2.emitJson({
|
|
5784
|
+
command: "site url",
|
|
5785
|
+
repoId,
|
|
5786
|
+
ref,
|
|
5787
|
+
found: false
|
|
5788
|
+
});
|
|
5789
|
+
}
|
|
5790
|
+
io2.err(detail);
|
|
5791
|
+
return 1;
|
|
5792
|
+
}
|
|
5793
|
+
const url = siteUrl(record.gateway, record.manifestTxId);
|
|
5794
|
+
if (flags.json) {
|
|
5795
|
+
io2.emitJson({
|
|
5796
|
+
command: "site url",
|
|
5797
|
+
repoId,
|
|
5798
|
+
ref,
|
|
5799
|
+
found: true,
|
|
5800
|
+
manifestTxId: record.manifestTxId,
|
|
5801
|
+
url,
|
|
5802
|
+
updatedAt: record.updatedAt
|
|
5803
|
+
});
|
|
5804
|
+
} else {
|
|
5805
|
+
io2.out(url);
|
|
5806
|
+
}
|
|
5807
|
+
return 0;
|
|
5808
|
+
} catch (err) {
|
|
5809
|
+
return emitCliError(io2, flags.json, "site url", err);
|
|
5810
|
+
}
|
|
5811
|
+
}
|
|
5812
|
+
|
|
4915
5813
|
// src/cli/dispatch.ts
|
|
4916
5814
|
var USAGE = `rig \u2014 git with a TOON remote (pay-to-write Nostr + Arweave)
|
|
4917
5815
|
|
|
@@ -4942,6 +5840,10 @@ Commands rig owns:
|
|
|
4942
5840
|
push is the TOON transport and shadows git push;
|
|
4943
5841
|
plain-git pushes remain available by running
|
|
4944
5842
|
\`git push\` directly
|
|
5843
|
+
site publish [ref] deploy a pushed repo as a permaweb site: build the
|
|
5844
|
+
ar.io path manifest (paths \u2192 Arweave txids) and
|
|
5845
|
+
upload it as one paid store write; prints the URL
|
|
5846
|
+
site url [ref] print the last-published site URL for a ref (free)
|
|
4945
5847
|
issue create file an issue (kind:1621) against a repo
|
|
4946
5848
|
issue list | show <id> read the repo's issues + comments (free)
|
|
4947
5849
|
pr list | show <id> read the repo's patches; show prints the full
|
|
@@ -4960,6 +5862,11 @@ Commands rig owns:
|
|
|
4960
5862
|
address(es) to fund externally
|
|
4961
5863
|
balance wallet balances + payment-channel holdings
|
|
4962
5864
|
(free \u2014 chain reads and local state only)
|
|
5865
|
+
name buy <name> ArNS naming (#367): buy points a human name at an
|
|
5866
|
+
name set <name> <txId> Arweave txId, owned + paid by this identity's
|
|
5867
|
+
name status <name> Solana key. buy spends mARIO on Solana via the
|
|
5868
|
+
ar.io registry (NOT ILP); status is free. Needs
|
|
5869
|
+
the optional \`@ar.io/sdk\` dependency
|
|
4963
5870
|
channel list show the payment channels paid commands hold
|
|
4964
5871
|
(free \u2014 reads local state)
|
|
4965
5872
|
channel open explicitly open (or resume) the channel for a
|
|
@@ -5014,6 +5921,8 @@ async function dispatch(argv2, deps) {
|
|
|
5014
5921
|
return runFetch(rest, deps);
|
|
5015
5922
|
case "push":
|
|
5016
5923
|
return runPush(rest, deps);
|
|
5924
|
+
case "site":
|
|
5925
|
+
return runSite(rest, deps);
|
|
5017
5926
|
case "issue":
|
|
5018
5927
|
return runIssue(rest, deps);
|
|
5019
5928
|
case "comment":
|
|
@@ -5028,6 +5937,8 @@ async function dispatch(argv2, deps) {
|
|
|
5028
5937
|
return runFund(rest, deps);
|
|
5029
5938
|
case "balance":
|
|
5030
5939
|
return runBalance(rest, deps);
|
|
5940
|
+
case "name":
|
|
5941
|
+
return runName(rest, deps);
|
|
5031
5942
|
case "help":
|
|
5032
5943
|
case "--help":
|
|
5033
5944
|
case "-h":
|
|
@@ -5097,13 +6008,15 @@ var RIG_OWNED_VERBS = /* @__PURE__ */ new Set([
|
|
|
5097
6008
|
"clone",
|
|
5098
6009
|
"fetch",
|
|
5099
6010
|
"push",
|
|
6011
|
+
"site",
|
|
5100
6012
|
"issue",
|
|
5101
6013
|
"comment",
|
|
5102
6014
|
"pr",
|
|
5103
6015
|
"maintainers",
|
|
5104
6016
|
"channel",
|
|
5105
6017
|
"fund",
|
|
5106
|
-
"balance"
|
|
6018
|
+
"balance",
|
|
6019
|
+
"name"
|
|
5107
6020
|
]);
|
|
5108
6021
|
function isJsonInvocation(argv2) {
|
|
5109
6022
|
const [verb, ...rest] = argv2;
|