@parity/dotns-cli 0.5.3 → 0.5.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.
Files changed (2) hide show
  1. package/dist/cli.js +2005 -2040
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -247567,7 +247567,7 @@ __export(exports_storeManagement, {
247567
247567
  listStoreCids: () => listStoreCids,
247568
247568
  getStoreValue: () => getStoreValue,
247569
247569
  getStoreInfo: () => getStoreInfo,
247570
- ensureStoreAuthorizations: () => ensureStoreAuthorizations2,
247570
+ ensureStoreAuthorizations: () => ensureStoreAuthorizations,
247571
247571
  deleteStoreValue: () => deleteStoreValue,
247572
247572
  checkStoreAuth: () => checkStoreAuth,
247573
247573
  cacheCidToStore: () => cacheCidToStore,
@@ -247784,7 +247784,7 @@ async function unauthorizeDotnsController(clientWrapper, substrateAddress, signe
247784
247784
  console.log(source_default.gray(" tx: ") + source_default.blue(tx));
247785
247785
  console.log(source_default.gray(" target: ") + source_default.white(targetAddress));
247786
247786
  }
247787
- async function ensureStoreAuthorizations2(clientWrapper, substrateAddress, signer, evmAddress) {
247787
+ async function ensureStoreAuthorizations(clientWrapper, substrateAddress, signer, evmAddress) {
247788
247788
  const storeAddress = await resolveStoreAddress(clientWrapper, substrateAddress, evmAddress);
247789
247789
  const spinner = ora("Checking Store authorizations").start();
247790
247790
  const [controllerAuthorized, registryAuthorized] = await Promise.all([
@@ -248059,12 +248059,9 @@ function decryptKeystorePayload(ks, password) {
248059
248059
  }
248060
248060
  }
248061
248061
 
248062
- // src/cli/commands/auth.ts
248063
- var DEFAULT_ACCOUNT_POINTER_FILE = ".default";
248064
- var KEYSTORE_FILE_EXTENSION = ".json";
248065
- var MNEMONIC_PROMPT = "mnemonic";
248066
- var KEY_URI_PROMPT = "key-uri";
248067
- var AUTH_TYPE_UNKNOWN = "unknown";
248062
+ // src/cli/commands/jsonHelpers.ts
248063
+ init_source();
248064
+ init_formatting();
248068
248065
  function getMergedOptions(command, fallback) {
248069
248066
  const mergedOptions = { ...fallback ?? {} };
248070
248067
  let currentCommand = command?.parent;
@@ -248072,9 +248069,8 @@ function getMergedOptions(command, fallback) {
248072
248069
  if (typeof currentCommand.opts === "function") {
248073
248070
  const parentOptions = currentCommand.opts();
248074
248071
  for (const key in parentOptions) {
248075
- const optionKey = key;
248076
- if (!(optionKey in mergedOptions) && parentOptions[optionKey] !== undefined) {
248077
- mergedOptions[optionKey] = parentOptions[optionKey];
248072
+ if (!(key in mergedOptions) && parentOptions[key] !== undefined) {
248073
+ mergedOptions[key] = parentOptions[key];
248078
248074
  }
248079
248075
  }
248080
248076
  }
@@ -248082,6 +248078,91 @@ function getMergedOptions(command, fallback) {
248082
248078
  }
248083
248079
  return mergedOptions;
248084
248080
  }
248081
+ function getJsonFlag(command) {
248082
+ if (command && typeof command.optsWithGlobals === "function") {
248083
+ const options = command.optsWithGlobals();
248084
+ if (typeof options?.json === "boolean")
248085
+ return options.json;
248086
+ }
248087
+ const localOptions = command && typeof command.opts === "function" ? command.opts() : undefined;
248088
+ if (typeof localOptions?.json === "boolean")
248089
+ return localOptions.json;
248090
+ return false;
248091
+ }
248092
+ function emitJsonResult(jsonOutput, result) {
248093
+ if (jsonOutput) {
248094
+ console.log(JSON.stringify(result));
248095
+ return true;
248096
+ }
248097
+ return false;
248098
+ }
248099
+ function handleCommandError(jsonOutput, error2) {
248100
+ const message = formatErrorMessage(error2);
248101
+ if (jsonOutput) {
248102
+ console.error(JSON.stringify({ error: message }));
248103
+ } else {
248104
+ console.error(source_default.red(`
248105
+ ✗ Error: ${message}
248106
+ `));
248107
+ }
248108
+ process.exit(1);
248109
+ }
248110
+ async function withCapturedConsole(callback) {
248111
+ const MAX_CAPTURED_ENTRIES = 400;
248112
+ const captured = [];
248113
+ const pushCaptured = (value) => {
248114
+ captured.push(value);
248115
+ if (captured.length > MAX_CAPTURED_ENTRIES) {
248116
+ captured.splice(0, captured.length - MAX_CAPTURED_ENTRIES);
248117
+ }
248118
+ };
248119
+ const capture = (...args) => {
248120
+ pushCaptured(args.map(String).join(" "));
248121
+ };
248122
+ const captureWrite = (chunk) => {
248123
+ pushCaptured(String(chunk));
248124
+ return true;
248125
+ };
248126
+ const saved = {
248127
+ log: console.log,
248128
+ error: console.error,
248129
+ warn: console.warn,
248130
+ info: console.info,
248131
+ stdoutWrite: process.stdout.write.bind(process.stdout),
248132
+ stderrWrite: process.stderr.write.bind(process.stderr)
248133
+ };
248134
+ console.log = capture;
248135
+ console.error = capture;
248136
+ console.warn = capture;
248137
+ console.info = capture;
248138
+ process.stdout.write = captureWrite;
248139
+ process.stderr.write = captureWrite;
248140
+ try {
248141
+ return await callback();
248142
+ } catch (error2) {
248143
+ saved.error(`[captured output before failure]
248144
+ ` + captured.join(`
248145
+ `));
248146
+ throw error2;
248147
+ } finally {
248148
+ console.log = saved.log;
248149
+ console.error = saved.error;
248150
+ console.warn = saved.warn;
248151
+ console.info = saved.info;
248152
+ process.stdout.write = saved.stdoutWrite;
248153
+ process.stderr.write = saved.stderrWrite;
248154
+ }
248155
+ }
248156
+ function maybeQuiet(jsonOutput, callback) {
248157
+ return jsonOutput ? withCapturedConsole(callback) : callback();
248158
+ }
248159
+
248160
+ // src/cli/commands/auth.ts
248161
+ var DEFAULT_ACCOUNT_POINTER_FILE = ".default";
248162
+ var KEYSTORE_FILE_EXTENSION = ".json";
248163
+ var MNEMONIC_PROMPT = "mnemonic";
248164
+ var KEY_URI_PROMPT = "key-uri";
248165
+ var AUTH_TYPE_UNKNOWN = "unknown";
248085
248166
  function resolvePasswordForCreate(options) {
248086
248167
  const fromCli = String(options.password ?? "").trim();
248087
248168
  if (fromCli)
@@ -265069,1712 +265150,1860 @@ async function prepareContext(options2) {
265069
265150
 
265070
265151
  // src/cli/commands/bulletin.ts
265071
265152
  init_constants();
265072
-
265073
- // src/cli/commands/lookup.ts
265074
- init_source();
265075
- init_esm10();
265076
- init_ws_provider();
265077
- init_dist();
265078
- init_polkadotClient();
265079
-
265080
- // src/commands/lookup.ts
265081
- init_source();
265082
- init_ora();
265083
- init__esm();
265084
- init_constants();
265085
-
265086
- // src/utils/validation.ts
265087
- init__esm();
265088
- function countTrailingDigits(label) {
265089
- let count2 = 0;
265090
- for (let characterIndex = label.length - 1;characterIndex >= 0; characterIndex--) {
265091
- const asciiCode = label.charCodeAt(characterIndex);
265092
- if (asciiCode >= 48 && asciiCode <= 57) {
265093
- count2++;
265094
- } else {
265095
- break;
265096
- }
265097
- }
265098
- return count2;
265153
+ init_reporter();
265154
+ function cleanupHeliaAndExit(code8) {
265155
+ Promise.resolve().then(() => (init_heliaClient(), exports_heliaClient)).then(({ destroySharedHeliaClient: destroySharedHeliaClient2 }) => destroySharedHeliaClient2()).catch(() => {}).finally(() => process.exit(code8));
265156
+ setTimeout(() => process.exit(code8), 500);
265157
+ return;
265099
265158
  }
265100
- function stripTrailingDigits(label) {
265101
- return label.replace(/\d+$/, "");
265159
+ var PROFILE_SAMPLE_INTERVAL_MS = 2000;
265160
+ function addReporterOption(command) {
265161
+ return command.option("--reporter <mode>", "Progress reporter: auto, interactive, stream, or quiet", "auto");
265102
265162
  }
265103
- function validateDomainLabel(label) {
265104
- if (!/^[a-z0-9-]{3,}$/.test(label)) {
265105
- throw new Error("Invalid domain label: must contain only lowercase letters, digits, and hyphens, with minimum length of 3 characters");
265106
- }
265107
- if (label.startsWith("-") || label.endsWith("-")) {
265108
- throw new Error("Invalid domain label: cannot start or end with hyphen");
265109
- }
265110
- const trailingDigitCount = countTrailingDigits(label);
265111
- if (trailingDigitCount > 2) {
265112
- throw new Error(`Invalid domain label: maximum 2 trailing digits allowed, found ${trailingDigitCount}`);
265113
- }
265163
+ function createProfileFingerprint(input) {
265164
+ return createHash2("sha256").update(input).digest("hex").slice(0, 16);
265114
265165
  }
265115
- function validateGovernanceLabel(label) {
265116
- validateDomainLabel(label);
265117
- const baseName = stripTrailingDigits(label);
265118
- if (baseName.length > 5) {
265119
- throw new Error(`Invalid governance label: base name must be 5 characters or fewer (got ${baseName.length})`);
265120
- }
265166
+ function buildDefaultProfileOutputPath(sourcePath, fingerprint) {
265167
+ const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
265168
+ const basename = path9.basename(sourcePath).replace(/[^a-zA-Z0-9._-]/g, "_");
265169
+ return path9.join(os11.homedir(), ".dotns", "upload-profiles", `${timestamp}-${basename}-${fingerprint}.json`);
265121
265170
  }
265122
- var isValidSubstrateAddress = (address, ss58Format = 42) => {
265123
- try {
265124
- if (isHex(address))
265125
- return false;
265126
- const decoded = decodeAddress2(address);
265127
- const checksummed = encodeAddress2(decoded, ss58Format);
265128
- return address === checksummed;
265129
- } catch {
265130
- return false;
265131
- }
265132
- };
265133
-
265134
- // src/commands/lookup.ts
265135
- init_contractInteractions();
265136
- init_formatting();
265137
- async function performDomainLookup(label, originSubstrateAddress, clientWrapper) {
265138
- const fullyQualifiedDomainName = `${label}.dot`;
265139
- const namehashNode = namehash(fullyQualifiedDomainName);
265140
- const result = {
265141
- domain: fullyQualifiedDomainName,
265142
- node: namehashNode,
265143
- exists: false,
265144
- owner: zeroAddress,
265145
- resolver: zeroAddress,
265146
- store: null,
265147
- resolvedAddress: null,
265148
- ownerBalance: null,
265149
- baseNameReservation: null
265171
+ function createUploadProfiler(options2) {
265172
+ const startedAtMs = Date.now();
265173
+ const startedAtIso = new Date(startedAtMs).toISOString();
265174
+ let latestSchedulerState = {
265175
+ timestampMs: startedAtMs,
265176
+ window: Math.max(1, options2.initialConcurrency),
265177
+ inFlightBytes: 0,
265178
+ inFlightChunks: 0,
265179
+ completedChunks: 0,
265180
+ retries: 0
265150
265181
  };
265151
- const spinner = ora("Reading registry").start();
265152
- result.exists = Boolean(await performContractCall(clientWrapper, originSubstrateAddress, CONTRACTS.DOTNS_REGISTRY, DOTNS_REGISTRY_ABI, "recordExists", [namehashNode]));
265153
- result.owner = getAddress(await performContractCall(clientWrapper, originSubstrateAddress, CONTRACTS.DOTNS_REGISTRY, DOTNS_REGISTRY_ABI, "owner", [namehashNode]));
265154
- result.resolver = getAddress(await performContractCall(clientWrapper, originSubstrateAddress, CONTRACTS.DOTNS_REGISTRY, DOTNS_REGISTRY_ABI, "resolver", [namehashNode]));
265155
- spinner.succeed("Registry read");
265156
- console.log(`
265157
- DotNS Domain Lookup`);
265158
- console.log(source_default.gray(" domain: ") + source_default.cyan(fullyQualifiedDomainName));
265159
- console.log(source_default.gray(" node: ") + source_default.white(namehashNode));
265160
- console.log();
265161
- console.log("▶ Registry (DotnsRegistry)");
265162
- console.log(source_default.gray(" registry: ") + source_default.white(CONTRACTS.DOTNS_REGISTRY));
265163
- console.log(source_default.gray(" exists: ") + source_default.white(String(result.exists)));
265164
- console.log(source_default.gray(" owner: ") + source_default.white(result.owner));
265165
- console.log(source_default.gray(" resolver: ") + source_default.white(result.resolver));
265166
- console.log();
265167
- if (!result.exists || result.owner === zeroAddress) {
265168
- console.log("▶ Status");
265169
- console.log(source_default.gray(" status: ") + source_default.yellow("not registered (no record)"));
265170
- console.log();
265171
- const baseName2 = stripTrailingDigits(label);
265172
- if (baseName2 !== label) {
265173
- result.baseNameReservation = await lookupBaseNameReservation(clientWrapper, originSubstrateAddress, baseName2);
265174
- displayBaseNameReservation(result.baseNameReservation);
265175
- }
265176
- return result;
265177
- }
265178
- console.log("▶ User Store (StoreFactory)");
265179
- console.log(source_default.gray(" factory: ") + source_default.white(CONTRACTS.STORE_FACTORY));
265180
- const storeAddress = getAddress(await performContractCall(clientWrapper, originSubstrateAddress, CONTRACTS.STORE_FACTORY, STORE_FACTORY_ABI, "getDeployedStore", [result.owner]));
265181
- result.store = storeAddress === zeroAddress ? null : storeAddress;
265182
- console.log(source_default.gray(" store: ") + source_default.white(storeAddress));
265183
- console.log(source_default.gray(" status: ") + source_default.white(storeAddress === zeroAddress ? "no store deployed" : "store exists"));
265184
- console.log();
265185
- console.log("▶ Forward Resolution (DotnsResolver)");
265186
- console.log(source_default.gray(" expectedResolver: ") + source_default.white(CONTRACTS.DOTNS_RESOLVER));
265187
- if (checksumAddress(result.resolver) !== checksumAddress(CONTRACTS.DOTNS_RESOLVER)) {
265188
- console.log(source_default.gray(" note: ") + source_default.yellow("registry resolver is not DotnsResolver, skipping address lookup"));
265189
- } else {
265190
- try {
265191
- const resolvedAddress = getAddress(await performContractCall(clientWrapper, originSubstrateAddress, result.resolver, DOTNS_RESOLVER_ABI, "addressOf", [namehashNode]));
265192
- result.resolvedAddress = resolvedAddress === zeroAddress ? null : resolvedAddress;
265193
- console.log(source_default.gray(" resolvedAddress: ") + source_default.white(resolvedAddress === zeroAddress ? "(not set)" : resolvedAddress));
265194
- } catch {
265195
- console.log(source_default.gray(" resolvedAddress: ") + source_default.yellow("lookup failed"));
265196
- }
265197
- }
265198
- console.log();
265199
- console.log("▶ Owner Balance");
265200
- try {
265201
- const ownerSubstrateAddress = await clientWrapper.getSubstrateAddress(result.owner);
265202
- const accountInfo = await clientWrapper.client.query.System.Account.getValue(ownerSubstrateAddress);
265203
- const freeBalance = accountInfo.data.free;
265204
- result.ownerBalance = {
265205
- substrate: ownerSubstrateAddress,
265206
- free: formatNativeBalance(freeBalance)
265207
- };
265208
- console.log(source_default.gray(" substrate: ") + source_default.white(ownerSubstrateAddress));
265209
- console.log(source_default.gray(" free: ") + source_default.white(formatNativeBalance(freeBalance) + " PAS"));
265210
- } catch {
265211
- console.log(source_default.gray(" balance: ") + source_default.yellow("unavailable"));
265212
- }
265213
- console.log();
265214
- const baseName = stripTrailingDigits(label);
265215
- if (baseName !== label) {
265216
- result.baseNameReservation = await lookupBaseNameReservation(clientWrapper, originSubstrateAddress, baseName);
265217
- displayBaseNameReservation(result.baseNameReservation);
265218
- }
265219
- console.log("▶ Lookup completed");
265220
- return result;
265221
- }
265222
- async function lookupBaseNameReservation(clientWrapper, originSubstrateAddress, baseName) {
265223
- try {
265224
- const reservationInfo = await performContractCall(clientWrapper, originSubstrateAddress, CONTRACTS.DOTNS_RULES, POP_RULES_ABI, "isBaseNameReserved", [baseName]);
265225
- const [isReserved, reservationOwner, expirationTimestamp] = reservationInfo;
265226
- return {
265227
- baseName,
265228
- isReserved,
265229
- reservedBy: getAddress(reservationOwner),
265230
- expires: expirationTimestamp > 0n ? new Date(Number(expirationTimestamp) * 1000).toISOString() : null
265231
- };
265232
- } catch {
265233
- return {
265234
- expires: null,
265235
- baseName,
265236
- isReserved: false,
265237
- reservedBy: zeroAddress
265182
+ const samples = [];
265183
+ const waves = [];
265184
+ const captureSample = () => {
265185
+ const usage = process.memoryUsage();
265186
+ samples.push({
265187
+ timestampMs: Date.now(),
265188
+ heapUsed: usage.heapUsed,
265189
+ rss: usage.rss,
265190
+ arrayBuffers: usage.arrayBuffers,
265191
+ external: usage.external,
265192
+ inFlightBytes: latestSchedulerState.inFlightBytes,
265193
+ inFlightChunks: latestSchedulerState.inFlightChunks,
265194
+ window: latestSchedulerState.window,
265195
+ completed: latestSchedulerState.completedChunks,
265196
+ retries: latestSchedulerState.retries
265197
+ });
265198
+ };
265199
+ captureSample();
265200
+ const timer4 = setInterval(captureSample, PROFILE_SAMPLE_INTERVAL_MS);
265201
+ timer4.unref?.();
265202
+ const summarizeAndWrite = async (finalCid, overrideOutputPath) => {
265203
+ clearInterval(timer4);
265204
+ captureSample();
265205
+ const finishedAtMs = Date.now();
265206
+ const finishedAtIso = new Date(finishedAtMs).toISOString();
265207
+ const totalUploadTimeMs = Math.max(1, finishedAtMs - startedAtMs);
265208
+ const throughputBytesPerSecond = options2.sourceSizeBytes / totalUploadTimeMs * 1000;
265209
+ const peakHeapUsed = Math.max(...samples.map((sample) => sample.heapUsed));
265210
+ const peakRss = Math.max(...samples.map((sample) => sample.rss));
265211
+ const peakArrayBuffers = Math.max(...samples.map((sample) => sample.arrayBuffers));
265212
+ const peakExternal = Math.max(...samples.map((sample) => sample.external));
265213
+ const maxWindowReached = Math.max(...samples.map((sample) => sample.window));
265214
+ const report = {
265215
+ meta: {
265216
+ sourcePath: options2.sourcePath,
265217
+ sourceSizeBytes: options2.sourceSizeBytes,
265218
+ chunkSizeBytes: options2.chunkSizeBytes,
265219
+ rpc: options2.rpc,
265220
+ startedAtIso,
265221
+ finishedAtIso,
265222
+ heapLimitBytes: v8.getHeapStatistics().heap_size_limit,
265223
+ initialConcurrency: options2.initialConcurrency,
265224
+ maxConcurrency: options2.maxConcurrency
265225
+ },
265226
+ samples,
265227
+ waves,
265228
+ summary: {
265229
+ totalUploadTimeMs,
265230
+ totalUploadTimeSeconds: totalUploadTimeMs / 1000,
265231
+ elapsedMs: totalUploadTimeMs,
265232
+ throughputBytesPerSecond,
265233
+ peakHeapUsed,
265234
+ peakRss,
265235
+ peakArrayBuffers,
265236
+ peakExternal,
265237
+ retryCount: latestSchedulerState.retries,
265238
+ maxWindowReached,
265239
+ finalCid
265240
+ }
265238
265241
  };
265239
- }
265240
- }
265241
- function displayBaseNameReservation(reservation) {
265242
- console.log("▶ Base Name Reservation (PopRules)");
265243
- console.log(source_default.gray(" oracle: ") + source_default.white(CONTRACTS.DOTNS_RULES));
265244
- console.log(source_default.gray(" baseName: ") + source_default.cyan(reservation.baseName));
265245
- console.log(source_default.gray(" isReserved: ") + source_default.white(String(reservation.isReserved)));
265246
- console.log(source_default.gray(" owner: ") + source_default.white(reservation.reservedBy));
265247
- if (reservation.expires) {
265248
- console.log(source_default.gray(" expires: ") + source_default.white(reservation.expires));
265249
- }
265250
- console.log();
265251
- }
265252
- async function performOwnerOfLookup(name10, substrateAddress, clientWrapper) {
265253
- if (!name10 || name10.trim().length === 0) {
265254
- throw new Error("--owner-of requires a <label>");
265255
- }
265256
- const label = name10.trim();
265257
- console.log(source_default.bold(`
265258
- \uD83D\uDD0E Ownership lookup
265259
- `));
265260
- console.log(source_default.gray(" Label: ") + source_default.cyan(label));
265261
- console.log(source_default.gray(" Domain: ") + source_default.cyan(label + ".dot"));
265262
- const tokenId = computeDomainTokenId(label);
265263
- let actualOwner;
265264
- let isRegistered;
265265
- try {
265266
- actualOwner = await withTimeout(performContractCall(clientWrapper, substrateAddress, CONTRACTS.DOTNS_REGISTRAR, DOTNS_REGISTRAR_ABI, "ownerOf", [tokenId]), 30000, "ownerOf");
265267
- isRegistered = actualOwner !== zeroAddress;
265268
- } catch (error2) {
265269
- const errorMessage = formatErrorMessage(error2);
265270
- if (errorMessage.includes("Contract reverted") || errorMessage.includes("does not exist")) {
265271
- actualOwner = zeroAddress;
265272
- isRegistered = false;
265273
- } else {
265274
- throw error2;
265275
- }
265276
- }
265277
- const ownerSubstrateAddress = isRegistered ? await clientWrapper.getSubstrateAddress(actualOwner) : "(none)";
265278
- console.log(source_default.gray(`
265279
- Registered: `) + source_default.white(String(isRegistered)));
265280
- console.log(source_default.gray(" Owner (EVM): ") + source_default.white(isRegistered ? actualOwner : "(none)"));
265281
- console.log(source_default.gray(" Owner (Substrate): ") + source_default.white(ownerSubstrateAddress));
265242
+ const outputPath = overrideOutputPath ?? options2.outputPath ?? buildDefaultProfileOutputPath(options2.sourcePath, createProfileFingerprint(`${options2.sourcePath}:${options2.sourceSizeBytes}:${options2.chunkSizeBytes}:${startedAtIso}`));
265243
+ const resolvedOutputPath = path9.resolve(outputPath);
265244
+ await filesystem2.mkdir(path9.dirname(resolvedOutputPath), { recursive: true });
265245
+ await filesystem2.writeFile(resolvedOutputPath, JSON.stringify(report, null, 2), "utf8");
265246
+ return { report, outputPath: resolvedOutputPath };
265247
+ };
265282
265248
  return {
265283
- label,
265284
- domain: `${label}.dot`,
265285
- registered: isRegistered,
265286
- ownerEvm: isRegistered ? actualOwner : zeroAddress,
265287
- ownerSubstrate: ownerSubstrateAddress
265249
+ onSchedulerState: (state3) => {
265250
+ latestSchedulerState = state3;
265251
+ captureSample();
265252
+ },
265253
+ onWave: (wave) => {
265254
+ waves.push(wave);
265255
+ captureSample();
265256
+ },
265257
+ finalize: summarizeAndWrite
265288
265258
  };
265289
265259
  }
265290
-
265291
- // src/commands/register.ts
265292
- init_source();
265293
- init_ora();
265294
- init__esm();
265295
- init_formatting();
265296
- import crypto16 from "crypto";
265297
-
265298
- // src/types/types.ts
265299
- var ProofOfPersonhoodStatus;
265300
- ((ProofOfPersonhoodStatus2) => {
265301
- ProofOfPersonhoodStatus2[ProofOfPersonhoodStatus2["NoStatus"] = 0] = "NoStatus";
265302
- ProofOfPersonhoodStatus2[ProofOfPersonhoodStatus2["ProofOfPersonhoodLite"] = 1] = "ProofOfPersonhoodLite";
265303
- ProofOfPersonhoodStatus2[ProofOfPersonhoodStatus2["ProofOfPersonhoodFull"] = 2] = "ProofOfPersonhoodFull";
265304
- ProofOfPersonhoodStatus2[ProofOfPersonhoodStatus2["Reserved"] = 3] = "Reserved";
265305
- })(ProofOfPersonhoodStatus ||= {});
265306
-
265307
- // src/commands/register.ts
265308
- init_constants();
265309
- init_contractInteractions();
265310
- init_formatting();
265311
- function redactSecret(secret) {
265312
- if (secret.length <= 10)
265313
- return "0x" + "*".repeat(secret.length - 2);
265314
- const prefix = secret.slice(0, 6);
265315
- const suffix = secret.slice(-4);
265316
- const redacted = "*".repeat(secret.length - 10);
265317
- return `${prefix}${redacted}${suffix}`;
265318
- }
265319
- function convertToProofOfPersonhoodStatus(value3) {
265320
- if (typeof value3 === "number")
265321
- return value3;
265322
- if (typeof value3 === "bigint")
265323
- return Number(value3);
265324
- throw new Error(`Unexpected ProofOfPersonhoodStatus type: ${typeof value3}`);
265325
- }
265326
- async function classifyDomainName(clientWrapper, originSubstrateAddress, label) {
265327
- const spinner = ora("Classifying name via PopRules").start();
265328
- try {
265329
- const classificationResult = await withTimeout(performContractCall(clientWrapper, originSubstrateAddress, CONTRACTS.DOTNS_RULES, POP_RULES_ABI, "classifyName", [label]), 30000, "classifyName");
265330
- const requiredStatus = convertToProofOfPersonhoodStatus(classificationResult[0]);
265331
- const message2 = classificationResult[1];
265332
- spinner.succeed("Name classification");
265333
- console.log(source_default.gray(" required: ") + source_default.white(ProofOfPersonhoodStatus[requiredStatus]));
265334
- console.log(source_default.gray(" message: ") + source_default.white(message2));
265335
- return { requiredStatus, message: message2 };
265336
- } catch (error2) {
265337
- spinner.fail("Name classification failed");
265338
- throw error2;
265260
+ async function withBulletinHumanOutput(reporterMode, callback) {
265261
+ if (reporterMode === "quiet") {
265262
+ return withCapturedConsole(callback);
265339
265263
  }
265264
+ return withConsoleToStderr(callback);
265340
265265
  }
265341
- async function ensureDomainNotRegistered(clientWrapper, originSubstrateAddress, label) {
265342
- const spinner = ora(`Checking availability of ${source_default.cyan(label + ".dot")}`).start();
265343
- const tokenId = computeDomainTokenId(label);
265344
- try {
265345
- const owner = await withTimeout(performContractCall(clientWrapper, originSubstrateAddress, CONTRACTS.DOTNS_REGISTRAR, DOTNS_REGISTRAR_ABI, "ownerOf", [tokenId]), 30000, "Availability check");
265346
- if (owner !== zeroAddress) {
265347
- spinner.fail(`Name ${source_default.cyan(label + ".dot")} already owned by ${source_default.yellow(owner)}`);
265348
- throw new Error(`Domain already owned by ${owner}`);
265349
- }
265350
- } catch (error2) {
265351
- const errorMessage = formatErrorMessage(error2);
265352
- if (errorMessage.includes("already owned"))
265353
- throw error2;
265354
- }
265355
- spinner.succeed(`Name ${source_default.cyan(label + ".dot")} is available`);
265266
+ async function resolveTargetAddress(positionalAddress, mergedOptions, reporterMode) {
265267
+ if (positionalAddress)
265268
+ return positionalAddress;
265269
+ const context = await withBulletinHumanOutput(reporterMode, () => prepareContext({ ...mergedOptions, useBulletin: true }));
265270
+ return context.substrateAddress;
265356
265271
  }
265357
- async function generateCommitment(clientWrapper, originSubstrateAddress, label, ownerAddress, includeReverse) {
265358
- const spinner = ora("Generating commitment hash").start();
265359
- try {
265360
- validateDomainLabel(label);
265361
- const secret = `0x${crypto16.randomBytes(32).toString("hex")}`;
265362
- const registration = {
265363
- label,
265364
- owner: ownerAddress,
265365
- secret,
265366
- reserved: includeReverse
265367
- };
265368
- const commitment = await withTimeout(performContractCall(clientWrapper, originSubstrateAddress, CONTRACTS.DOTNS_REGISTRAR_CONTROLLER, DOTNS_REGISTRAR_CONTROLLER_ABI, "makeCommitment", [registration]), 30000, "Commitment generation");
265369
- spinner.succeed("Commitment hash generated");
265370
- console.log(source_default.gray(" commitment: ") + source_default.blue(commitment));
265371
- console.log(source_default.gray(" hash: ") + source_default.dim("label + owner + secret + reserved"));
265372
- console.log(source_default.gray(" secret: ") + source_default.yellow(redactSecret(secret)));
265373
- console.log(source_default.gray(" reserved: ") + (includeReverse ? source_default.green("true") : source_default.white("false")));
265374
- return { commitment, registration };
265375
- } catch (error2) {
265376
- spinner.fail("Failed to create commitment");
265377
- throw error2;
265378
- }
265272
+ function writeBulletinJson(payload) {
265273
+ process.stdout.write(`${JSON.stringify(payload)}
265274
+ `);
265379
265275
  }
265380
- async function submitCommitment(clientWrapper, substrateAddress, signer, commitment) {
265381
- const spinner = ora("Submitting commitment to controller").start();
265382
- const transactionHash = await submitContractTransaction(clientWrapper, CONTRACTS.DOTNS_REGISTRAR_CONTROLLER, 0n, DOTNS_REGISTRAR_CONTROLLER_ABI, "commit", [commitment], substrateAddress, signer, spinner, "Commitment");
265383
- console.log(source_default.gray(" tx: ") + source_default.blue(transactionHash));
265384
- console.log(source_default.gray(" committed: ") + source_default.white(new Date().toISOString()));
265276
+ function writeBulletinJsonError(error2) {
265277
+ writeBulletinJson({ error: formatErrorMessage(error2) });
265278
+ process.exit(1);
265385
265279
  }
265386
- async function waitForMinimumCommitmentAge(clientWrapper, originSubstrateAddress, commitment, commitmentBuffer) {
265387
- const buffer2 = commitmentBuffer ?? DEFAULT_COMMITMENT_BUFFER_SECONDS;
265388
- const checkSpinner = ora("Reading minimum commitment age").start();
265389
- const [minimumAge, initialCommitTimestamp] = await Promise.all([
265390
- withTimeout(performContractCall(clientWrapper, originSubstrateAddress, CONTRACTS.DOTNS_REGISTRAR_CONTROLLER, DOTNS_REGISTRAR_CONTROLLER_ABI, "minCommitmentAge", []), 30000, "minCommitmentAge"),
265391
- withTimeout(performContractCall(clientWrapper, originSubstrateAddress, CONTRACTS.DOTNS_REGISTRAR_CONTROLLER, DOTNS_REGISTRAR_CONTROLLER_ABI, "commitments", [commitment]), 30000, "commitments")
265392
- ]);
265393
- const minimumAgeSeconds = typeof minimumAge === "bigint" ? Number(minimumAge) : minimumAge;
265394
- const initialCommitTime = typeof initialCommitTimestamp === "bigint" ? Number(initialCommitTimestamp) : initialCommitTimestamp;
265395
- if (initialCommitTime === 0) {
265396
- checkSpinner.fail("Commitment not found on-chain");
265397
- throw new Error("Commitment not found on-chain. It may not have been included in a block yet.");
265398
- }
265399
- const waitSeconds = minimumAgeSeconds + buffer2;
265400
- checkSpinner.succeed(`Minimum commitment age: ${source_default.yellow(waitSeconds.toString() + "s")}`);
265401
- const waitSpinner = ora(`Waiting for commitment age (${waitSeconds}s)`).start();
265402
- let elapsedSeconds = 0;
265403
- const intervalId = setInterval(() => {
265404
- elapsedSeconds++;
265405
- const remainingSeconds = waitSeconds - elapsedSeconds;
265406
- waitSpinner.text = `Waiting for commitment age (${source_default.yellow(remainingSeconds + "s")} remaining)`;
265407
- }, 1000);
265408
- await new Promise((resolve) => setTimeout(resolve, waitSeconds * 1000));
265409
- clearInterval(intervalId);
265410
- waitSpinner.text = "Verifying commitment age on-chain";
265411
- const pollDeadline = Date.now() + COMMITMENT_POLL_TIMEOUT_MS;
265412
- while (Date.now() < pollDeadline) {
265413
- const polledTimestamp = await performContractCall(clientWrapper, originSubstrateAddress, CONTRACTS.DOTNS_REGISTRAR_CONTROLLER, DOTNS_REGISTRAR_CONTROLLER_ABI, "commitments", [commitment]);
265414
- const polledCommitTime = typeof polledTimestamp === "bigint" ? Number(polledTimestamp) : polledTimestamp;
265415
- const nowSeconds = Math.floor(Date.now() / 1000);
265416
- if (polledCommitTime > 0 && nowSeconds - polledCommitTime >= minimumAgeSeconds) {
265417
- waitSpinner.succeed("Commitment age requirement met (verified on-chain)");
265280
+ function createPhaseHandler(reporter) {
265281
+ const tasks = new Map;
265282
+ return (event) => {
265283
+ const key = event.phase;
265284
+ const activeTask = tasks.get(key);
265285
+ if (event.state === "start") {
265286
+ activeTask?.stop();
265287
+ tasks.set(key, reporter.task(event.message));
265418
265288
  return;
265419
265289
  }
265420
- waitSpinner.text = `Commitment still too new, polling (${Math.ceil((pollDeadline - Date.now()) / 1000)}s remaining)`;
265421
- await new Promise((resolve) => setTimeout(resolve, COMMITMENT_POLL_INTERVAL_MS));
265422
- }
265423
- waitSpinner.fail("Commitment age verification timed out");
265424
- throw new Error(`Commitment still too new after ${waitSeconds + COMMITMENT_POLL_TIMEOUT_MS / 1000}s. The chain's block timestamps may be advancing slower than expected. Try increasing --commitment-buffer or DOTNS_COMMITMENT_BUFFER.`);
265425
- }
265426
- async function getUserProofOfPersonhoodStatus(clientWrapper, originSubstrateAddress, ownerAddress) {
265427
- const userStatusRaw = await withTimeout(performContractCall(clientWrapper, originSubstrateAddress, CONTRACTS.DOTNS_RULES, POP_RULES_ABI, "userPopStatus", [ownerAddress]), 30000, "userPopStatus");
265428
- return convertToProofOfPersonhoodStatus(userStatusRaw);
265429
- }
265430
- async function getPriceAndValidateEligibility(clientWrapper, originSubstrateAddress, label, ownerAddress) {
265431
- const spinner = ora("Pricing via PopRules.price").start();
265432
- try {
265433
- validateDomainLabel(label);
265434
- const baseName = stripTrailingDigits(label);
265435
- const reservationInfo = await withTimeout(performContractCall(clientWrapper, originSubstrateAddress, CONTRACTS.DOTNS_RULES, POP_RULES_ABI, "isBaseNameReserved", [baseName]), 30000, "isBaseNameReserved");
265436
- const [isReserved, reservationOwner] = reservationInfo;
265437
- if (isReserved && checksumAddress(reservationOwner) !== checksumAddress(ownerAddress)) {
265438
- spinner.fail("Eligibility failed");
265439
- throw new Error("Base name reserved for original Lite registrant");
265440
- }
265441
- const classificationResult = await withTimeout(performContractCall(clientWrapper, originSubstrateAddress, CONTRACTS.DOTNS_RULES, POP_RULES_ABI, "priceWithoutCheck", [label, ownerAddress]), 30000, "priceWithoutCheck");
265442
- const requiredStatus = convertToProofOfPersonhoodStatus(classificationResult.status);
265443
- const message2 = classificationResult.message;
265444
- const userStatus = await getUserProofOfPersonhoodStatus(clientWrapper, originSubstrateAddress, ownerAddress);
265445
- if (requiredStatus === 3 /* Reserved */) {
265446
- spinner.fail("Eligibility failed");
265447
- throw new Error(message2);
265448
- }
265449
- if (requiredStatus === 2 /* ProofOfPersonhoodFull */) {
265450
- if (userStatus !== 2 /* ProofOfPersonhoodFull */) {
265451
- spinner.fail("Eligibility failed");
265452
- throw new Error("Requires Full Personhood verification");
265290
+ if (event.state === "update") {
265291
+ if (activeTask) {
265292
+ activeTask.update(event.message);
265293
+ } else {
265294
+ tasks.set(key, reporter.task(event.message));
265453
265295
  }
265454
- } else if (requiredStatus === 1 /* ProofOfPersonhoodLite */) {
265455
- if (userStatus !== 1 /* ProofOfPersonhoodLite */ && userStatus !== 2 /* ProofOfPersonhoodFull */) {
265456
- spinner.fail("Eligibility failed");
265457
- throw new Error("Requires Personhood Lite verification");
265296
+ return;
265297
+ }
265298
+ if (event.state === "success") {
265299
+ if (activeTask) {
265300
+ activeTask.succeed(event.message);
265301
+ tasks.delete(key);
265302
+ } else {
265303
+ reporter.success(event.message);
265458
265304
  }
265459
- } else {
265460
- const trailingDigitCount = countTrailingDigits(label);
265461
- if (trailingDigitCount === 0 || userStatus === 1 /* ProofOfPersonhoodLite */) {
265462
- spinner.fail("Eligibility failed");
265463
- throw new Error(`Personhood Lite cannot register base names
265464
- This means another user already owns the name without digits`);
265305
+ return;
265306
+ }
265307
+ if (event.state === "warning") {
265308
+ if (activeTask) {
265309
+ activeTask.warn(event.message);
265310
+ tasks.delete(key);
265311
+ } else {
265312
+ reporter.warn(event.message);
265465
265313
  }
265314
+ return;
265466
265315
  }
265467
- spinner.succeed("Eligibility and price");
265468
- console.log(source_default.gray(" required: ") + source_default.white(ProofOfPersonhoodStatus[requiredStatus]));
265469
- console.log(source_default.gray(" user: ") + source_default.white(ProofOfPersonhoodStatus[userStatus]));
265470
- console.log(source_default.gray(" message: ") + source_default.white(message2));
265471
- console.log(source_default.gray(" price: ") + source_default.green(`${classificationResult.priceWei > 0n ? formatWeiAsEther(classificationResult.priceWei) : 0n} PAS`));
265472
- return {
265473
- priceWei: classificationResult.price ?? classificationResult.priceWei,
265474
- requiredStatus,
265475
- userStatus,
265476
- message: message2,
265477
- status: requiredStatus,
265478
- price: classificationResult.price ?? classificationResult.priceWei
265479
- };
265480
- } catch (error2) {
265481
- if (!spinner.isSpinning)
265482
- throw error2;
265483
- spinner.fail("Pricing failed");
265484
- throw error2;
265485
- }
265486
- }
265487
- async function finalizeRegularRegistration(clientWrapper, substrateAddress, signer, registration, priceWei) {
265488
- const spinner = ora(`Registering ${source_default.cyan(registration.label + ".dot")}`).start();
265489
- try {
265490
- const bufferedPaymentWei = priceWei * 110n / 100n;
265491
- const bufferedPaymentNative = convertWeiToNative(bufferedPaymentWei);
265492
- console.log(source_default.gray(" oracle: ") + source_default.green(formatWeiAsEther(priceWei) + " PAS"));
265493
- console.log(source_default.gray(" paying: ") + source_default.green(formatWeiAsEther(bufferedPaymentWei) + " PAS"));
265494
- const transactionHash = await submitContractTransaction(clientWrapper, CONTRACTS.DOTNS_REGISTRAR_CONTROLLER, bufferedPaymentNative, DOTNS_REGISTRAR_CONTROLLER_ABI, "register", [registration], substrateAddress, signer, spinner, "Registration");
265495
- console.log(source_default.gray(" tx: ") + source_default.blue(transactionHash));
265496
- console.log(source_default.gray(" paid: ") + source_default.green(formatWeiAsEther(bufferedPaymentWei) + " PAS"));
265497
- } catch (error2) {
265498
- spinner.fail("Registration failed");
265499
- throw error2;
265500
- }
265501
- }
265502
- async function finalizeGovernanceRegistration(clientWrapper, substrateAddress, signer, registration) {
265503
- const spinner = ora(`Registering governance name ${source_default.cyan(registration.label + ".dot")}`).start();
265504
- try {
265505
- const transactionHash = await submitContractTransaction(clientWrapper, CONTRACTS.DOTNS_REGISTRAR_CONTROLLER, 0n, DOTNS_REGISTRAR_CONTROLLER_ABI, "registerReserved", [registration], substrateAddress, signer, spinner, "Governance Registration");
265506
- console.log(source_default.gray(" tx: ") + source_default.blue(transactionHash));
265507
- } catch (error2) {
265508
- spinner.fail("Governance registration failed");
265509
- throw error2;
265510
- }
265511
- }
265512
- async function registerSubnode(clientWrapper, substrateAddress, signer, sublabel, parentLabel, ownerAddress) {
265513
- const fullName = `${sublabel}.${parentLabel}.dot`;
265514
- const spinner = ora(`Registering subname ${source_default.cyan(fullName)}`).start();
265515
- try {
265516
- const parentNode = namehash(`${parentLabel}.dot`);
265517
- const subnodeRecord = {
265518
- parentNode,
265519
- subLabel: sublabel,
265520
- parentLabel,
265521
- owner: ownerAddress
265522
- };
265523
- const transactionHash = await submitContractTransaction(clientWrapper, CONTRACTS.DOTNS_REGISTRY, 0n, DOTNS_REGISTRY_ABI, "setSubnodeOwner", [subnodeRecord], substrateAddress, signer, spinner, "Subname registration");
265524
- console.log(source_default.gray(" tx: ") + source_default.blue(transactionHash));
265525
- return transactionHash;
265526
- } catch (error2) {
265527
- spinner.fail("Subname registration failed");
265528
- throw error2;
265529
- }
265530
- }
265531
- async function verifyDomainOwnership(clientWrapper, originSubstrateAddress, label, expectedOwner) {
265532
- const spinner = ora("Verifying minted ownership").start();
265533
- try {
265534
- const tokenId = computeDomainTokenId(label);
265535
- const actualOwner = await withTimeout(performContractCall(clientWrapper, originSubstrateAddress, CONTRACTS.DOTNS_REGISTRAR, DOTNS_REGISTRAR_ABI, "ownerOf", [tokenId]), 30000, "ownerOf");
265536
- if (checksumAddress(actualOwner) !== checksumAddress(expectedOwner)) {
265537
- spinner.fail("Ownership verification failed");
265538
- console.log(source_default.gray(" expected: ") + source_default.white(expectedOwner));
265539
- console.log(source_default.gray(" actual: ") + source_default.white(actualOwner));
265540
- throw new Error(`Owner mismatch for ${label}.dot`);
265316
+ if (activeTask) {
265317
+ activeTask.fail(event.message);
265318
+ tasks.delete(key);
265319
+ return;
265541
265320
  }
265542
- spinner.succeed("Ownership verified");
265543
- console.log(source_default.gray(" owner: ") + source_default.white(actualOwner));
265544
- return actualOwner;
265545
- } catch (error2) {
265546
- spinner.fail("Ownership verification failed");
265547
- throw error2;
265548
- }
265549
- }
265550
- async function displayDeployedStore(clientWrapper, originSubstrateAddress, ownerAddress) {
265551
- const spinner = ora("Reading deployed Store address").start();
265552
- try {
265553
- const storeAddress = await withTimeout(performContractCall(clientWrapper, originSubstrateAddress, CONTRACTS.STORE_FACTORY, STORE_FACTORY_ABI, "getDeployedStore", [ownerAddress]), 30000, "getDeployedStore");
265554
- spinner.succeed("Store");
265555
- console.log(source_default.gray(" store: ") + (storeAddress === zeroAddress ? source_default.yellow("not deployed") : source_default.cyan(storeAddress)));
265556
- } catch (error2) {
265557
- spinner.fail("Failed to read Store address");
265558
- throw error2;
265559
- }
265321
+ reporter.fail(event.message);
265322
+ };
265560
265323
  }
265561
- async function ensureStoreAuthorizations(clientWrapper, substrateAddress, signer, evmAddress) {
265562
- const storeAddress = getAddress(await withTimeout(performContractCall(clientWrapper, substrateAddress, CONTRACTS.STORE_FACTORY, STORE_FACTORY_ABI, "getDeployedStore", [evmAddress]), 30000, "getDeployedStore"));
265563
- if (storeAddress === zeroAddress)
265564
- return;
265565
- const spinner = ora("Checking Store authorizations").start();
265566
- const [controllerAuthorized, registryAuthorized] = await Promise.all([
265567
- Boolean(await performContractCall(clientWrapper, substrateAddress, storeAddress, STORE_ABI, "isAuthorized", [CONTRACTS.DOTNS_REGISTRAR_CONTROLLER])),
265568
- Boolean(await performContractCall(clientWrapper, substrateAddress, storeAddress, STORE_ABI, "isAuthorized", [CONTRACTS.DOTNS_REGISTRY]))
265569
- ]);
265570
- if (controllerAuthorized && registryAuthorized) {
265571
- spinner.succeed("Store authorizations verified");
265572
- console.log(source_default.gray(" controller: ") + source_default.white(CONTRACTS.DOTNS_REGISTRAR_CONTROLLER));
265573
- console.log(source_default.gray(" registry: ") + source_default.white(CONTRACTS.DOTNS_REGISTRY));
265574
- return;
265575
- }
265576
- spinner.warn("Store authorizations need update");
265577
- if (!controllerAuthorized) {
265578
- const controllerSpinner = ora("Authorizing registrar controller as Store writer").start();
265579
- const tx = await submitContractTransaction(clientWrapper, storeAddress, 0n, STORE_ABI, "authorizeStore", [CONTRACTS.DOTNS_REGISTRAR_CONTROLLER], substrateAddress, signer, controllerSpinner, "Authorize controller");
265580
- console.log(source_default.gray(" tx: ") + source_default.blue(tx));
265581
- console.log(source_default.gray(" controller: ") + source_default.white(CONTRACTS.DOTNS_REGISTRAR_CONTROLLER));
265582
- }
265583
- if (!registryAuthorized) {
265584
- const registrySpinner = ora("Authorizing registry as Store writer").start();
265585
- const tx = await submitContractTransaction(clientWrapper, storeAddress, 0n, STORE_ABI, "authorizeStore", [CONTRACTS.DOTNS_REGISTRY], substrateAddress, signer, registrySpinner, "Authorize registry");
265586
- console.log(source_default.gray(" tx: ") + source_default.blue(tx));
265587
- console.log(source_default.gray(" registry: ") + source_default.white(CONTRACTS.DOTNS_REGISTRY));
265588
- }
265324
+ function createRetryHandler(reporter) {
265325
+ return ({ label, retry, totalAttempts, delayMs, errorMessage }) => {
265326
+ reporter.warn(`${label} attempt ${retry + 1}/${totalAttempts} failed: ${errorMessage}`);
265327
+ reporter.detail(`retrying in ${(delayMs / 1000).toFixed(delayMs >= 1000 ? 1 : 0)}s`);
265328
+ };
265589
265329
  }
265590
- async function setUserProofOfPersonhoodStatus(clientWrapper, substrateAddress, signer, ownerAddress, label, status) {
265591
- const displayName = label ? source_default.cyan(label) : "account";
265592
- const checkSpinner = ora(`Checking current PoP status for ${displayName}`).start();
265593
- try {
265594
- const currentStatus = await getUserProofOfPersonhoodStatus(clientWrapper, substrateAddress, ownerAddress);
265595
- checkSpinner.succeed("Current PoP status");
265596
- console.log(source_default.gray(" current: ") + source_default.white(ProofOfPersonhoodStatus[currentStatus]));
265597
- console.log(source_default.gray(" desired: ") + source_default.white(ProofOfPersonhoodStatus[status]));
265598
- if (currentStatus === status) {
265599
- console.log(source_default.yellow(` ↳ Status already set to ${ProofOfPersonhoodStatus[status]}, skipping update`));
265330
+ function createChunkedUploadMonitor(reporter, phaseHandler, fileSize, chunkSizeBytes, totalChunks) {
265331
+ const heartbeatIntervalMs = 15000;
265332
+ let latestState = null;
265333
+ let heartbeatTimer;
265334
+ const startedAtMs = Date.now();
265335
+ const stopHeartbeat = () => {
265336
+ if (heartbeatTimer) {
265337
+ clearInterval(heartbeatTimer);
265338
+ heartbeatTimer = undefined;
265339
+ }
265340
+ };
265341
+ const emitHeartbeat = () => {
265342
+ if (!latestState || latestState.inFlightChunks === 0) {
265343
+ stopHeartbeat();
265600
265344
  return;
265601
265345
  }
265602
- const updateSpinner = ora(`Setting PoP status for ${displayName} to ${source_default.yellow(ProofOfPersonhoodStatus[status])}`).start();
265603
- const transactionHash = await submitContractTransaction(clientWrapper, CONTRACTS.DOTNS_RULES, 0n, POP_RULES_ABI, "setUserPopStatus", [status], substrateAddress, signer, updateSpinner, "PoP status update");
265604
- console.log(source_default.gray(" tx: ") + source_default.blue(transactionHash));
265605
- } catch (error2) {
265606
- if (checkSpinner.isSpinning)
265607
- checkSpinner.fail("Failed to check PoP status");
265608
- throw error2;
265609
- }
265610
- }
265611
-
265612
- // src/cli/commands/lookup.ts
265613
- init_env();
265614
- init_formatting();
265615
-
265616
- // src/cli/transfer.ts
265617
- init_source();
265618
- init_ora();
265619
- init__esm();
265620
- init_constants();
265621
- init_formatting();
265622
- init_contractInteractions();
265623
- function toChecksummed(a2) {
265624
- return checksumAddress(a2);
265625
- }
265626
- function isLabelLike(input) {
265627
- return /^[a-z0-9-]{3,}$/.test(input);
265628
- }
265629
- function asDotLabel(input) {
265630
- const raw = input.trim().toLowerCase();
265631
- if (raw.endsWith(".dot"))
265632
- return raw.slice(0, -4);
265633
- return raw;
265634
- }
265635
- async function ownerOfLabel(clientWrapper, originSubstrateAddress, label) {
265636
- const tokenId = computeDomainTokenId(label);
265637
- return await performContractCall(clientWrapper, originSubstrateAddress, CONTRACTS.DOTNS_REGISTRAR, DOTNS_REGISTRAR_ABI, "ownerOf", [tokenId]);
265638
- }
265639
- async function resolveTransferRecipient(clientWrapper, originSubstrateAddress, recipientIdentifier) {
265640
- const input = recipientIdentifier.trim();
265641
- if (isAddress(input))
265642
- return toChecksummed(input);
265643
- const label = asDotLabel(input);
265644
- if (isLabelLike(label)) {
265645
- const spinner2 = ora(`Resolving ${source_default.cyan(label + ".dot")} to owner address`).start();
265646
- const ownerAddress = await ownerOfLabel(clientWrapper, originSubstrateAddress, label);
265647
- if (ownerAddress === zeroAddress) {
265648
- spinner2.fail(`No owner found for ${source_default.cyan(label + ".dot")} (unregistered)`);
265649
- throw new Error(`Domain ${label}.dot has no owner`);
265650
- }
265651
- spinner2.succeed(`${source_default.cyan(label + ".dot")} → ${source_default.white(toChecksummed(ownerAddress))}`);
265652
- return toChecksummed(ownerAddress);
265653
- }
265654
- const spinner = ora(`Resolving recipient address`).start();
265655
- const evmAddress = await clientWrapper.getEvmAddress(input);
265656
- spinner.succeed(`${source_default.white(input)} → ${source_default.white(toChecksummed(evmAddress))}`);
265657
- return toChecksummed(evmAddress);
265658
- }
265659
- async function transferDomain(clientWrapper, originSubstrateAddress, signer, fromAddress, toAddress, label) {
265660
- const spinner = ora().start();
265661
- const normLabel = asDotLabel(label);
265662
- spinner.text = `Validating ${source_default.cyan(normLabel + ".dot")}`;
265663
- validateDomainLabel(normLabel);
265664
- spinner.succeed(`Valid: ${source_default.cyan(normLabel + ".dot")}`);
265665
- spinner.start(`Checking owner of ${source_default.cyan(normLabel + ".dot")}`);
265666
- const tokenId = computeDomainTokenId(normLabel);
265667
- const currentOwner = await ownerOfLabel(clientWrapper, originSubstrateAddress, normLabel);
265668
- const currentOwnerC = toChecksummed(currentOwner);
265669
- spinner.succeed(`Owner: ${source_default.cyan(normLabel + ".dot")} → ${source_default.white(currentOwnerC)}`);
265670
- const fromC = toChecksummed(fromAddress);
265671
- const toC = toChecksummed(toAddress);
265672
- if (currentOwner === zeroAddress) {
265673
- spinner.fail(`${source_default.cyan(normLabel + ".dot")} is not registered`);
265674
- throw new Error(`Cannot transfer: ${normLabel}.dot is not registered`);
265675
- }
265676
- if (currentOwnerC !== fromC) {
265677
- spinner.fail(`Not authorized to transfer ${source_default.cyan(normLabel + ".dot")}`);
265678
- console.log(source_default.gray(" expected owner: ") + source_default.white(fromC));
265679
- console.log(source_default.gray(" on-chain owner: ") + source_default.white(currentOwnerC));
265680
- throw new Error(`Cannot transfer: ${normLabel}.dot owned by ${currentOwnerC}`);
265681
- }
265682
- try {
265683
- const syncSpinner = ora(`Syncing label ${source_default.cyan(normLabel)} with registrar`).start();
265684
- await submitContractTransaction(clientWrapper, CONTRACTS.DOTNS_REGISTRAR, 0n, DOTNS_REGISTRAR_ABI, "syncLabel", [tokenId, normLabel], originSubstrateAddress, signer, syncSpinner, "Label sync");
265685
- syncSpinner.succeed("Label synced");
265686
- } catch (error2) {
265687
- const errorMessage = formatErrorMessage(error2);
265688
- if (!errorMessage.includes("LabelAlreadySet")) {
265689
- console.log(source_default.yellow(" Label sync skipped: " + errorMessage.split(`
265690
- `)[0]));
265691
- }
265692
- }
265693
- spinner.start(`Submitting transfer ${source_default.cyan(normLabel + ".dot")} → ${source_default.green(toC)}`);
265694
- const transactionHash = await submitContractTransaction(clientWrapper, CONTRACTS.DOTNS_REGISTRAR, 0n, DOTNS_REGISTRAR_ABI, "transferFrom", [fromC, toC, tokenId], originSubstrateAddress, signer, spinner, "Transfer");
265695
- spinner.succeed(`Transfer finalized`);
265696
- console.log(source_default.gray(" tx: ") + source_default.blue(transactionHash));
265697
- console.log(source_default.gray(" from: ") + source_default.yellow(fromC));
265698
- console.log(source_default.gray(" to: ") + source_default.green(toC));
265699
- console.log(source_default.gray(" name: ") + source_default.cyan(normLabel + ".dot"));
265700
- }
265701
-
265702
- // src/cli/commands/register.ts
265703
- init_source();
265704
- init__esm();
265705
-
265706
- // src/cli/labels.ts
265707
- function generateRandomLabel(status) {
265708
- const alphabet4 = "abcdefghijklmnopqrstuvwxyz";
265709
- const alphanumeric = "abcdefghijklmnopqrstuvwxyz0123456789";
265710
- const randomInteger = (maxExclusive) => Math.floor(Math.random() * maxExclusive);
265711
- const randomCharacters = (characterSet, count2) => {
265712
- let result = "";
265713
- for (let i2 = 0;i2 < count2; i2++)
265714
- result += characterSet[randomInteger(characterSet.length)];
265715
- return result;
265346
+ reporter.detail(`heartbeat | ${latestState.completedChunks}/${totalChunks} chunks | window=${latestState.window} | in-flight=${latestState.inFlightChunks} | bytes=${formatBytes(latestState.inFlightBytes)} | retries=${latestState.retries} | elapsed=${formatDuration((Date.now() - startedAtMs) / 1000)}`);
265716
265347
  };
265717
- const twoDigits = () => String(randomInteger(99) + 1).padStart(2, "0");
265718
- const baseEndingWithLetter = (length5) => {
265719
- if (length5 <= 0)
265720
- throw new Error("Invalid base length");
265721
- if (length5 === 1)
265722
- return randomCharacters(alphabet4, 1);
265723
- return randomCharacters(alphanumeric, length5 - 1) + randomCharacters(alphabet4, 1);
265348
+ return {
265349
+ onSchedulerState(state3) {
265350
+ latestState = state3;
265351
+ if (reporter.mode !== "stream") {
265352
+ return;
265353
+ }
265354
+ if (state3.inFlightChunks > 0 && !heartbeatTimer) {
265355
+ heartbeatTimer = setInterval(emitHeartbeat, heartbeatIntervalMs);
265356
+ heartbeatTimer.unref?.();
265357
+ } else if (state3.inFlightChunks === 0) {
265358
+ stopHeartbeat();
265359
+ }
265360
+ },
265361
+ onWave(wave) {
265362
+ const completedChunks = latestState?.completedChunks ?? 0;
265363
+ const bytesUploaded = Math.min(fileSize, completedChunks * chunkSizeBytes);
265364
+ const throughputBytesPerSecond = wave.durationMs > 0 ? wave.succeeded * chunkSizeBytes * 1000 / wave.durationMs : 0;
265365
+ const message2 = `wave #${wave.wave} complete | ${completedChunks}/${totalChunks} chunks | ${formatBytes(bytesUploaded)}/${formatBytes(fileSize)} | ${(wave.durationMs / 1000).toFixed(1)}s | window=${wave.window} | retries=${wave.retries} | ${formatBytes(throughputBytesPerSecond)}/s`;
265366
+ if (reporter.mode === "interactive") {
265367
+ phaseHandler({ phase: "upload", state: "update", message: message2 });
265368
+ } else {
265369
+ reporter.line(message2);
265370
+ }
265371
+ },
265372
+ stop() {
265373
+ stopHeartbeat();
265374
+ }
265724
265375
  };
265725
- if (status === 1 /* ProofOfPersonhoodLite */) {
265726
- const baseLength = 6 + randomInteger(3);
265727
- return baseEndingWithLetter(baseLength) + twoDigits();
265728
- }
265729
- if (status === 0 /* NoStatus */) {
265730
- const baseLength = 9 + randomInteger(6);
265731
- return baseEndingWithLetter(baseLength) + twoDigits();
265732
- }
265733
- if (status === 2 /* ProofOfPersonhoodFull */) {
265734
- const baseLength = randomInteger(2) === 0 ? 6 + randomInteger(3) : 9 + randomInteger(6);
265735
- return baseEndingWithLetter(baseLength);
265736
- }
265737
- throw new Error("Cannot auto-generate Reserved names");
265738
- }
265739
- function parseProofOfPersonhoodStatus(statusString) {
265740
- const normalized = statusString.toLowerCase();
265741
- if (normalized === "none" || normalized === "nostatus")
265742
- return 0 /* NoStatus */;
265743
- if (normalized === "lite" || normalized === "poplite")
265744
- return 1 /* ProofOfPersonhoodLite */;
265745
- if (normalized === "full" || normalized === "popfull")
265746
- return 2 /* ProofOfPersonhoodFull */;
265747
- throw new Error("Invalid status: use none, lite, or full");
265748
- }
265749
-
265750
- // src/cli/commands/register.ts
265751
- function resolveTransferDestination(options2) {
265752
- const legacy = options2.transferTo;
265753
- const modern = options2.to;
265754
- const destination = legacy ?? modern;
265755
- if (options2.transfer === true && !destination) {
265756
- throw new Error("Missing transfer destination: use --to <evm|ss58|label>");
265757
- }
265758
- if (options2.transfer === false && legacy) {
265759
- return legacy;
265760
- }
265761
- return destination;
265762
- }
265763
- function classifyTransferDestination(destination) {
265764
- if (isAddress(destination))
265765
- return "evm";
265766
- if (isValidSubstrateAddress(destination))
265767
- return "substrate";
265768
- return "label";
265769
- }
265770
- function isValidTransferDestination(destination) {
265771
- const kind = classifyTransferDestination(destination);
265772
- if (kind === "evm" || kind === "substrate")
265773
- return true;
265774
- const domainLabelPattern = /^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/;
265775
- return domainLabelPattern.test(destination) && destination.length >= 3 && destination.length <= 63;
265776
- }
265777
- async function executeRegistration(options2 = {}) {
265778
- if (options2.mnemonic && options2.keyUri) {
265779
- throw new Error("Cannot specify both --mnemonic and --key-uri");
265780
- }
265781
- if (options2.parent) {
265782
- return executeSubnameRegistration(options2);
265783
- }
265784
- const transferDestination = resolveTransferDestination(options2);
265785
- if (transferDestination && !isValidTransferDestination(transferDestination)) {
265786
- throw new Error("Invalid transfer destination: must be valid EVM address, Substrate address, or domain label");
265787
- }
265788
- const context = await prepareAssetHubContext(options2);
265789
- const { clientWrapper, substrateAddress, signer, evmAddress } = context;
265790
- const statusWasProvided = options2.__statusProvided === true;
265791
- const popStatusConfig = statusWasProvided ? { mode: "set", status: parseProofOfPersonhoodStatus(options2.status ?? "none") } : { mode: "unchanged" };
265792
- const label = options2.name ?? generateRandomLabel(popStatusConfig.mode === "set" ? popStatusConfig.status : 0 /* NoStatus */);
265793
- console.log(source_default.gray(" Mode: ") + source_default.yellow(options2.governance ? "Governance registration" : "Regular registration"));
265794
- console.log(source_default.gray(" Label: ") + source_default.cyan(label));
265795
- console.log(source_default.gray(" Domain: ") + source_default.cyan(label + ".dot"));
265796
- console.log(source_default.gray(" Transfer: ") + (transferDestination ? source_default.green("post-mint") : source_default.gray("none")));
265797
- await step("Ensuring account mapped", async () => clientWrapper.ensureAccountMapped(substrateAddress, signer));
265798
- if (options2.governance) {
265799
- await executeGovernanceRegistration(clientWrapper, substrateAddress, signer, evmAddress, label, transferDestination, options2.commitmentBuffer);
265800
- } else {
265801
- await executeRegularRegistration(clientWrapper, substrateAddress, signer, evmAddress, label, popStatusConfig, options2.reverse ?? false, transferDestination, options2.commitmentBuffer);
265802
- }
265803
- console.log(`
265804
- ${source_default.bold.green("═══════════════════════════════════════")}`);
265805
- console.log(`${source_default.bold.green(" ✓ Operation Complete ")}`);
265806
- console.log(`${source_default.bold.green("═══════════════════════════════════════")}
265807
- `);
265808
- console.log(source_default.gray(" Domain: ") + source_default.cyan(label + ".dot"));
265809
265376
  }
265810
- async function executeSubnameRegistration(options2) {
265811
- if (!options2.name) {
265812
- throw new Error("Missing subname: use --name <sublabel>");
265813
- }
265814
- if (!options2.parent) {
265815
- throw new Error("Missing parent: use --parent <parentlabel>");
265816
- }
265817
- const context = await prepareAssetHubContext(options2);
265818
- const { clientWrapper, substrateAddress, signer, evmAddress } = context;
265819
- const sublabel = options2.name;
265820
- const parentLabel = options2.parent;
265821
- const ownerAddress = options2.owner ?? evmAddress;
265822
- const fullName = `${sublabel}.${parentLabel}.dot`;
265823
- console.log(source_default.bold(`
265824
- ▶ Subname Registration
265825
- `));
265826
- console.log(source_default.gray(" Subname: ") + source_default.cyan(fullName));
265827
- console.log(source_default.gray(" Parent: ") + source_default.white(`${parentLabel}.dot`));
265828
- console.log(source_default.gray(" Owner: ") + source_default.white(ownerAddress));
265829
- await step("Ensuring account mapped", async () => clientWrapper.ensureAccountMapped(substrateAddress, signer));
265830
- await registerSubnode(clientWrapper, substrateAddress, signer, sublabel, parentLabel, ownerAddress);
265831
- console.log(`
265832
- ${source_default.bold.green("═══════════════════════════════════════")}`);
265833
- console.log(`${source_default.bold.green(" ✓ Subname Registered ")}`);
265834
- console.log(`${source_default.bold.green("═══════════════════════════════════════")}
265835
- `);
265836
- console.log(source_default.gray(" Domain: ") + source_default.cyan(fullName));
265837
- }
265838
- async function executeGovernanceRegistration(clientWrapper, substrateAddress, signer, evmAddress, label, transferDestination, commitmentBuffer) {
265839
- console.log(source_default.bold(`
265840
- \uD83C\uDFDB Governance registration (commit-reveal)
265841
- `));
265842
- validateGovernanceLabel(label);
265843
- const classification = await step("Classifying name", async () => classifyDomainName(clientWrapper, substrateAddress, label));
265844
- if (classification.requiredStatus !== 3 /* Reserved */) {
265845
- throw new Error(`Governance name must classify as Reserved; got ${ProofOfPersonhoodStatus[classification.requiredStatus]}`);
265846
- }
265847
- await step("Checking availability", async () => ensureDomainNotRegistered(clientWrapper, substrateAddress, label));
265848
- const { commitment, registration } = await step("Generating commitment", async () => generateCommitment(clientWrapper, substrateAddress, label, evmAddress, true));
265849
- await step("Submitting commitment", async () => submitCommitment(clientWrapper, substrateAddress, signer, commitment));
265850
- await step("Waiting commitment age", async () => waitForMinimumCommitmentAge(clientWrapper, substrateAddress, commitment, commitmentBuffer));
265851
- await step("Finalizing registration", async () => finalizeGovernanceRegistration(clientWrapper, substrateAddress, signer, registration));
265852
- await step("Verifying ownership", async () => verifyDomainOwnership(clientWrapper, substrateAddress, label, evmAddress));
265853
- await step("Displaying store", async () => displayDeployedStore(clientWrapper, substrateAddress, evmAddress));
265854
- await step("Ensuring store authorizations", async () => ensureStoreAuthorizations(clientWrapper, substrateAddress, signer, evmAddress));
265855
- if (transferDestination) {
265856
- const recipient = await step("Resolving recipient", async () => resolveTransferRecipient(clientWrapper, substrateAddress, transferDestination));
265857
- await step("Transferring domain", async () => transferDomain(clientWrapper, substrateAddress, signer, evmAddress, recipient, label));
265858
- await step("Verifying ownership", async () => verifyDomainOwnership(clientWrapper, substrateAddress, label, recipient));
265859
- }
265860
- }
265861
- async function executeRegularRegistration(clientWrapper, substrateAddress, signer, evmAddress, label, popStatusConfig, enableReverseRecord, transferDestination, commitmentBuffer) {
265862
- console.log(source_default.bold(`
265863
- \uD83E\uDDFE Regular registration (commit-reveal)
265864
- `));
265865
- validateDomainLabel(label);
265866
- const classification = await step("Classifying name", async () => classifyDomainName(clientWrapper, substrateAddress, label));
265867
- if (popStatusConfig.mode === "set") {
265868
- await step("Setting PoP status", async () => setUserProofOfPersonhoodStatus(clientWrapper, substrateAddress, signer, evmAddress, label, popStatusConfig.status));
265869
- }
265870
- await step("Checking availability", async () => ensureDomainNotRegistered(clientWrapper, substrateAddress, label));
265871
- const { commitment, registration } = await step("Generating commitment", async () => generateCommitment(clientWrapper, substrateAddress, label, evmAddress, enableReverseRecord));
265872
- await step("Submitting commitment", async () => submitCommitment(clientWrapper, substrateAddress, signer, commitment));
265873
- await step("Waiting commitment age", async () => waitForMinimumCommitmentAge(clientWrapper, substrateAddress, commitment, commitmentBuffer));
265874
- const pricing = await step("Pricing and eligibility", async () => getPriceAndValidateEligibility(clientWrapper, substrateAddress, label, evmAddress));
265875
- await step("Finalizing registration", async () => finalizeRegularRegistration(clientWrapper, substrateAddress, signer, registration, pricing.priceWei));
265876
- await step("Verifying ownership", async () => verifyDomainOwnership(clientWrapper, substrateAddress, label, evmAddress));
265877
- await step("Displaying store", async () => displayDeployedStore(clientWrapper, substrateAddress, evmAddress));
265878
- await step("Ensuring store authorizations", async () => ensureStoreAuthorizations(clientWrapper, substrateAddress, signer, evmAddress));
265879
- if (transferDestination) {
265880
- const recipient = await step("Resolving recipient", async () => resolveTransferRecipient(clientWrapper, substrateAddress, transferDestination));
265881
- await step("Transferring domain", async () => transferDomain(clientWrapper, substrateAddress, signer, evmAddress, recipient, label));
265882
- await step("Verifying ownership", async () => verifyDomainOwnership(clientWrapper, substrateAddress, label, recipient));
265883
- }
265884
- }
265885
-
265886
- // src/cli/commands/lookup.ts
265887
- function createClientWrapper(rpc) {
265888
- const client = createClient3(getWsProvider(rpc)).getTypedApi(paseo_default);
265889
- return new ReviveClientWrapper(client);
265890
- }
265891
- function getJsonFlag(command) {
265892
- if (command && typeof command.optsWithGlobals === "function") {
265893
- const options2 = command.optsWithGlobals();
265894
- if (typeof options2?.json === "boolean")
265895
- return options2.json;
265896
- }
265897
- const localOptions = command && typeof command.opts === "function" ? command.opts() : undefined;
265898
- if (typeof localOptions?.json === "boolean")
265899
- return localOptions.json;
265900
- return process.argv.includes("--json");
265901
- }
265902
- function hasAnyAuthHint(opts) {
265903
- return opts.mnemonic != null && String(opts.mnemonic).length > 0 || opts.keyUri != null && String(opts.keyUri).length > 0 || opts.keystorePath != null && String(opts.keystorePath).length > 0 || opts.account != null && String(opts.account).length > 0 || opts.password != null && String(opts.password).length > 0;
265904
- }
265905
- function withReadOnlyPasswordFallback(opts) {
265906
- const passwordEnv = process.env.DOTNS_KEYSTORE_PASSWORD;
265907
- if ((opts.password == null || String(opts.password).length === 0) && passwordEnv && passwordEnv.length > 0) {
265908
- return { ...opts, password: passwordEnv };
265909
- }
265910
- return opts;
265911
- }
265912
- async function prepareReadOnlyContext(options2) {
265913
- const rpc = resolveRpc(options2.rpc);
265914
- const clientWrapper = await step(`Connecting RPC ${rpc}`, async () => createClientWrapper(rpc));
265915
- const auth = await step("Resolving read-only account", async () => {
265916
- if (hasAnyAuthHint(options2)) {
265917
- const merged = withReadOnlyPasswordFallback(options2);
265918
- const resolved2 = await resolveAuthSource(merged);
265919
- return {
265920
- source: resolved2.source,
265921
- isKeyUri: resolved2.isKeyUri,
265922
- resolvedFrom: resolved2.resolvedFrom,
265923
- account: resolved2.account
265924
- };
265925
- }
265926
- const resolved = await resolveAuthSourceReadOnly();
265927
- return {
265928
- source: resolved.source,
265929
- isKeyUri: resolved.isKeyUri,
265930
- resolvedFrom: resolved.resolvedFrom,
265931
- account: resolved.account
265932
- };
265933
- });
265934
- const keypair3 = await step("Loading keypair", async () => createAccountFromSource(auth.source, auth.isKeyUri));
265935
- const evmAddress = await step("Resolving EVM address", async () => clientWrapper.getEvmAddress(keypair3.address));
265936
- console.log(source_default.gray(`
265937
- RPC: `) + source_default.white(rpc));
265938
- console.log(source_default.gray(" Account: ") + source_default.white(keypair3.address));
265939
- return { clientWrapper, account: { address: keypair3.address }, rpc, evmAddress };
265940
- }
265941
- async function resolveRecipientByKind(clientWrapper, substrateAddress, destination) {
265942
- const kind = classifyTransferDestination(destination);
265943
- switch (kind) {
265944
- case "evm":
265945
- return destination;
265946
- case "substrate":
265947
- return clientWrapper.getEvmAddress(destination);
265948
- case "label":
265949
- return resolveTransferRecipient(clientWrapper, substrateAddress, destination);
265950
- }
265951
- }
265952
- function attachLookupCommands(root) {
265953
- const lookupCommand = root.command("lookup").description("Lookup domain information");
265954
- addAuthOptions(lookupCommand);
265955
- const lookupNameCommand = lookupCommand.command("name [label]").description("Lookup comprehensive domain information").option("-n, --name <label>", "Domain label to lookup (alternative to positional argument)").option("--json", "Output result as JSON (suppresses all other output)", false);
265956
- addAuthOptions(lookupNameCommand).action(async (positionalLabel, options2, cmd) => {
265377
+ function attachBulletinCommands(root) {
265378
+ const bulletinCommand = addReporterOption(root.command("bulletin").description("Bulletin storage utilities")).option("--json", "Write machine-readable JSON to stdout", false);
265379
+ addAuthOptions(bulletinCommand);
265380
+ const authorizeCommand = addReporterOption(bulletinCommand.command("authorize [address]").description("Authorize an account for Bulletin TransactionStorage").option("--bulletin-rpc <wsUrl>", "Bulletin WebSocket RPC endpoint", DEFAULT_BULLETIN_RPC).option("--transactions <count>", "Number of transactions to authorize", String(DEFAULT_AUTHORIZATION_TRANSACTIONS)).option("--bytes <count>", "Number of bytes to authorize", String(DEFAULT_AUTHORIZATION_BYTES)).option("--force", "Force re-authorization even if account appears already authorized", false).option("--json", "Write machine-readable JSON to stdout", false));
265381
+ addAuthOptions(authorizeCommand).action(async (positionalAddress, options2, command) => {
265957
265382
  try {
265958
- const merged = {
265959
- ...options2 ?? {},
265960
- ...getAuthOptions(cmd),
265961
- __positionalLabel: positionalLabel
265962
- };
265963
- const label = merged.name || merged.__positionalLabel;
265964
- const jsonOutput = getJsonFlag(cmd);
265965
- if (!label) {
265966
- if (jsonOutput) {
265967
- console.error(JSON.stringify({ error: "Domain label is required" }));
265968
- process.exit(1);
265969
- }
265970
- console.error(source_default.red(`
265971
- Error: Domain label is required
265972
- `));
265973
- console.log(source_default.gray("Usage: dotns lookup name <label>"));
265974
- console.log(source_default.gray(" or: dotns lookup name --name <label>"));
265975
- console.log(source_default.gray(` or: dotns lookup --name <label>
265976
- `));
265977
- process.exit(1);
265383
+ const mergedOptions = getMergedOptions(command, options2);
265384
+ const jsonOutput = getJsonFlag(command);
265385
+ const reporterMode = resolveReporterMode(mergedOptions.reporter);
265386
+ const reporter = createCliReporter(mergedOptions.reporter);
265387
+ const onPhase = createPhaseHandler(reporter);
265388
+ const bulletinRpc = String(mergedOptions.bulletinRpc || DEFAULT_BULLETIN_RPC);
265389
+ const transactions = Number(mergedOptions.transactions || DEFAULT_AUTHORIZATION_TRANSACTIONS);
265390
+ const bytes = BigInt(mergedOptions.bytes || DEFAULT_AUTHORIZATION_BYTES);
265391
+ const force = Boolean(options2.force);
265392
+ const signerKeyUri = String(mergedOptions.keyUri || DEFAULT_SUDO_KEY_URI);
265393
+ const targetAddress = await resolveTargetAddress(positionalAddress, mergedOptions, reporterMode);
265394
+ const signerContext = await withBulletinHumanOutput(reporterMode, () => prepareContext({ keyUri: signerKeyUri, useBulletin: true }));
265395
+ if (!jsonOutput) {
265396
+ console.log(source_default.blue(`
265397
+ ▶ Bulletin Authorize`));
265398
+ console.log(source_default.gray(" target: ") + source_default.cyan(targetAddress));
265399
+ console.log(source_default.gray(" rpc: ") + source_default.white(bulletinRpc));
265400
+ console.log(source_default.gray(" transactions: ") + source_default.white(transactions.toLocaleString()));
265401
+ console.log(source_default.gray(" bytes: ") + source_default.white(formatBytes(bytes)));
265402
+ console.log(source_default.gray(" signer: ") + source_default.yellow(signerKeyUri));
265978
265403
  }
265979
- const { clientWrapper, account } = await maybeQuiet(jsonOutput, () => prepareReadOnlyContext(merged));
265980
- if (!jsonOutput)
265981
- console.log(source_default.bold(`
265982
- ▶ Domain Lookup
265983
- `));
265984
- const result = await maybeQuiet(jsonOutput, () => performDomainLookup(label, account.address, clientWrapper));
265404
+ await withBulletinHumanOutput(reporterMode, () => authorizeAccount({
265405
+ rpc: bulletinRpc,
265406
+ signer: signerContext.signer,
265407
+ targetAddress,
265408
+ transactions,
265409
+ bytes,
265410
+ force,
265411
+ onPhase
265412
+ }));
265985
265413
  if (jsonOutput) {
265986
- console.log(JSON.stringify(result));
265414
+ const authStatus = await checkAuthorization(bulletinRpc, targetAddress);
265415
+ const expiresAt = expirationToISOString(authStatus.currentBlock, authStatus.expiration);
265416
+ writeBulletinJson({
265417
+ ok: true,
265418
+ target: targetAddress,
265419
+ rpc: bulletinRpc,
265420
+ transactions,
265421
+ bytes: bytes.toString(),
265422
+ expiresAt
265423
+ });
265987
265424
  } else {
265988
265425
  console.log(source_default.green(`
265989
- ✓ Complete
265426
+ Authorization Complete`));
265427
+ console.log(source_default.gray(` The account can now upload to Bulletin.
265990
265428
  `));
265991
265429
  }
265992
265430
  process.exit(0);
265993
265431
  } catch (error2) {
265994
265432
  const errorMessage = formatErrorMessage(error2);
265995
- const jsonOutput = getJsonFlag(cmd);
265433
+ const jsonOutput = getJsonFlag(command);
265996
265434
  if (jsonOutput) {
265997
- console.error(JSON.stringify({ error: errorMessage }));
265998
- process.exit(1);
265435
+ writeBulletinJsonError(errorMessage);
265999
265436
  }
266000
- console.error(source_default.red(`
266001
- ✗ Error: ${errorMessage}
266002
- `));
266003
- process.exit(1);
266004
- }
266005
- });
266006
- lookupCommand.option("-n, --name <label>", "Domain label to lookup").option("--json", "Output result as JSON (suppresses all other output)", false).action(async (options2, cmd) => {
266007
- const subcommand = cmd.args?.[0];
266008
- if (["name", "owner-of", "oo", "transfer"].includes(subcommand))
266009
- return;
266010
- if (options2.name) {
266011
- try {
266012
- const merged = { ...options2 ?? {}, ...getAuthOptions(cmd) };
266013
- const jsonOutput = getJsonFlag(cmd);
266014
- const { clientWrapper, account } = await maybeQuiet(jsonOutput, () => prepareReadOnlyContext(merged));
266015
- if (!jsonOutput)
266016
- console.log(source_default.bold(`
266017
- ▶ Domain Lookup
266018
- `));
266019
- const result = await maybeQuiet(jsonOutput, () => performDomainLookup(merged.name, account.address, clientWrapper));
266020
- if (jsonOutput) {
266021
- console.log(JSON.stringify(result));
266022
- } else {
266023
- console.log(source_default.green(`
266024
- ✓ Complete
265437
+ if (errorMessage.includes("AlreadyAuthorized")) {
265438
+ console.log(source_default.yellow(`
265439
+ ⚠ Account is already authorized
266025
265440
  `));
266026
- }
266027
265441
  process.exit(0);
266028
- } catch (error2) {
266029
- const errorMessage = formatErrorMessage(error2);
266030
- const jsonOutput = getJsonFlag(cmd);
266031
- if (jsonOutput) {
266032
- console.error(JSON.stringify({ error: errorMessage }));
266033
- process.exit(1);
266034
- }
265442
+ }
265443
+ if (errorMessage.includes("BadOrigin")) {
266035
265444
  console.error(source_default.red(`
266036
- Error: ${errorMessage}
265445
+ Authorization failed — insufficient privileges`));
265446
+ console.error(source_default.yellow(" The signer does not have Authorizer privileges on this chain."));
265447
+ console.error(source_default.gray(` Override with --key-uri if needed.
265448
+ `));
265449
+ process.exit(1);
265450
+ }
265451
+ if (errorMessage.includes("not applied")) {
265452
+ console.error(source_default.red(`
265453
+ ✗ ${errorMessage}`));
265454
+ console.error(source_default.gray(` Override with --key-uri if needed.
266037
265455
  `));
266038
265456
  process.exit(1);
266039
265457
  }
265458
+ console.error(source_default.red(`
265459
+ ✗ Error: ${errorMessage}
265460
+ `));
265461
+ process.exit(1);
266040
265462
  }
266041
265463
  });
266042
- const ownerOfCommand = lookupCommand.command("owner-of <label>").description("Show whether a name is registered and who owns it").alias("oo").option("--json", "Output result as JSON (suppresses all other output)", false);
266043
- addAuthOptions(ownerOfCommand).action(async (label, options2, cmd) => {
265464
+ const uploadCommand = addReporterOption(bulletinCommand.command("upload <path>").description("Upload a file or directory to Bulletin and print the resulting CID").option("--bulletin-rpc <wsUrl>", "Bulletin WebSocket RPC endpoint", DEFAULT_BULLETIN_RPC).option("--chunk-size <bytes>", "Chunk size for large uploads (clamped to 256 KB–2 MB)", String(DEFAULT_CHUNK_SIZE_BYTES)).option("--max-retries <n>", "Retry transient upload failures (default: 5, capped at 20)", String(DEFAULT_UPLOAD_MAX_RETRIES)).option("--force-chunked", "Force chunked upload (DAG-PB)", false).option("--concurrency <n>", "Adaptive scheduler max window (default: 16, max: 64)", "16").option("--print-contenthash", "Also print 0x-prefixed IPFS contenthash for the CID", false).option("--resume", "Resume a previously interrupted upload", false).option("--profile-upload", "Enable upload profiling and write a JSON report", false).option("--profile-output <path>", "Path to write upload profiling JSON report").option("--no-history", "Do not save upload to history", true).option("--cache", "Write the CID to the user's on-chain Store after upload", false).option("--json", "Write machine-readable JSON to stdout", false));
265465
+ addAuthOptions(uploadCommand).action(async (inputPath, options2, command) => {
266044
265466
  try {
266045
- const merged = { ...options2 ?? {}, ...getAuthOptions(cmd) };
266046
- const jsonOutput = getJsonFlag(cmd);
266047
- const { clientWrapper, account } = await maybeQuiet(jsonOutput, () => prepareReadOnlyContext(merged));
266048
- if (!jsonOutput)
266049
- console.log(source_default.bold(`
266050
- Ownership Lookup
266051
- `));
266052
- const result = await maybeQuiet(jsonOutput, () => performOwnerOfLookup(label, account.address, clientWrapper));
266053
- if (jsonOutput) {
266054
- console.log(JSON.stringify(result));
266055
- } else {
266056
- console.log(source_default.green(`
266057
- ✓ Complete
266058
- `));
266059
- }
266060
- process.exit(0);
266061
- } catch (error2) {
266062
- const errorMessage = formatErrorMessage(error2);
266063
- const jsonOutput = getJsonFlag(cmd);
266064
- if (jsonOutput) {
266065
- console.error(JSON.stringify({ error: errorMessage }));
266066
- process.exit(1);
265467
+ const mergedOptions = getMergedOptions(command, options2);
265468
+ const jsonOutput = getJsonFlag(command);
265469
+ const reporterMode = resolveReporterMode(mergedOptions.reporter);
265470
+ const reporter = createCliReporter(mergedOptions.reporter);
265471
+ const onPhase = createPhaseHandler(reporter);
265472
+ const onRetry = createRetryHandler(reporter);
265473
+ if (mergedOptions.mnemonic && mergedOptions.keyUri) {
265474
+ throw new Error("Cannot specify both --mnemonic and --key-uri");
266067
265475
  }
266068
- console.error(source_default.red(`
266069
- Error: ${errorMessage}
266070
- `));
266071
- process.exit(1);
266072
- }
266073
- });
266074
- const transferCommand = lookupCommand.command("transfer [label]").description("Transfer domain ownership to another address or label").requiredOption("-d, --destination <destination>", "Transfer destination (EVM address, SS58 address, or domain label)").option("--json", "Output result as JSON (suppresses all other output)", false);
266075
- addAuthOptions(transferCommand).action(async (positionalLabel, options2, cmd) => {
266076
- try {
266077
- const merged = { ...options2 ?? {}, ...getAuthOptions(cmd) };
266078
- const jsonOutput = getJsonFlag(cmd);
266079
- const label = positionalLabel || merged.name || cmd.parent?.opts()?.name;
266080
- if (!label) {
266081
- throw new Error("Domain label is required: dotns lookup transfer <label> -d <destination>");
265476
+ await cleanupStaleManifests();
265477
+ const validatedPath = await withBulletinHumanOutput(reporterMode, () => validateAndReadPath(inputPath, onPhase));
265478
+ const { bytes, isDirectory, resolvedPath, fileSize, fileMtimeMs } = validatedPath;
265479
+ const bulletinRpc = String(mergedOptions.bulletinRpc || DEFAULT_BULLETIN_RPC);
265480
+ const chunkSizeBytes = clampChunkSizeBytes(Number(mergedOptions.chunkSize || DEFAULT_CHUNK_SIZE_BYTES));
265481
+ const maxRetries = normalizeUploadMaxRetries(mergedOptions.maxRetries);
265482
+ const concurrency = Math.max(1, Math.min(64, Math.floor(Number(mergedOptions.concurrency || 16))));
265483
+ const resume = Boolean(mergedOptions.resume);
265484
+ const profileUpload = Boolean(mergedOptions.profileUpload);
265485
+ const shouldUseChunkedUpload = !isDirectory;
265486
+ const effectiveFileSize = isDirectory ? 0 : fileSize ?? bytes.length;
265487
+ let resumedBlocks;
265488
+ if (resume && shouldUseChunkedUpload) {
265489
+ const resolvedFileMtimeMs = fileMtimeMs ?? (await filesystem2.stat(resolvedPath)).mtimeMs;
265490
+ const manifestLoadResult = await loadManifestForResume({
265491
+ inputPath: resolvedPath,
265492
+ fileSize: effectiveFileSize,
265493
+ fileMtimeMs: resolvedFileMtimeMs,
265494
+ chunkSize: chunkSizeBytes
265495
+ });
265496
+ if (manifestLoadResult.manifest && manifestLoadResult.manifest.completedBlocks.length > 0) {
265497
+ resumedBlocks = completedBlocksFromManifest(manifestLoadResult.manifest);
265498
+ reporter.warn(`resuming: ${manifestLoadResult.manifest.completedBlocks.length} blocks already uploaded`);
265499
+ } else if (manifestLoadResult.staleManifest) {
265500
+ reporter.warn("resume notice: file fingerprint changed, starting a fresh upload manifest");
265501
+ await deleteManifest(manifestLoadResult.staleManifest);
265502
+ }
266082
265503
  }
266083
- const destination = merged.destination;
266084
- if (!isValidTransferDestination(destination)) {
266085
- throw new Error("Invalid transfer destination: must be a valid EVM address, Substrate address, or domain label");
265504
+ const context = await withBulletinHumanOutput(reporterMode, () => prepareContext({ ...mergedOptions, useBulletin: true }));
265505
+ const authInfo = await withBulletinHumanOutput(reporterMode, () => ensureAccountAuthorized(bulletinRpc, context.substrateAddress));
265506
+ const profileOutputOverride = mergedOptions.profileOutput ? String(mergedOptions.profileOutput) : undefined;
265507
+ const profiler = profileUpload ? createUploadProfiler({
265508
+ sourcePath: resolvedPath,
265509
+ sourceSizeBytes: effectiveFileSize,
265510
+ chunkSizeBytes: shouldUseChunkedUpload ? chunkSizeBytes : Math.max(1, effectiveFileSize),
265511
+ rpc: bulletinRpc,
265512
+ initialConcurrency: shouldUseChunkedUpload ? 1 : 1,
265513
+ maxConcurrency: shouldUseChunkedUpload ? concurrency : 1,
265514
+ outputPath: profileOutputOverride,
265515
+ jsonOutput
265516
+ }) : undefined;
265517
+ const totalChunks = shouldUseChunkedUpload ? Math.ceil(effectiveFileSize / chunkSizeBytes) : undefined;
265518
+ const chunkedMonitor = shouldUseChunkedUpload && totalChunks ? createChunkedUploadMonitor(reporter, onPhase, effectiveFileSize, chunkSizeBytes, totalChunks) : null;
265519
+ const performUpload = async () => {
265520
+ if (isDirectory) {
265521
+ const result2 = await storeDirectory(bulletinRpc, context.signer, resolvedPath, {
265522
+ concurrency,
265523
+ accountAddress: context.substrateAddress,
265524
+ maxRetries,
265525
+ onPhase,
265526
+ onRetry,
265527
+ waitForFinalization: false
265528
+ });
265529
+ return { cid: result2.cid, size: 0 };
265530
+ }
265531
+ if (shouldUseChunkedUpload) {
265532
+ const result2 = await uploadChunkedBlocks(bulletinRpc, context.signer, resolvedPath, chunkSizeBytes, effectiveFileSize, context.substrateAddress, {
265533
+ completedBlocks: resumedBlocks,
265534
+ concurrency,
265535
+ maxRetries,
265536
+ onPhase,
265537
+ onRetry,
265538
+ onSchedulerState: (state3) => {
265539
+ profiler?.onSchedulerState(state3);
265540
+ chunkedMonitor?.onSchedulerState(state3);
265541
+ },
265542
+ onWave: (wave) => {
265543
+ profiler?.onWave(wave);
265544
+ chunkedMonitor?.onWave(wave);
265545
+ }
265546
+ });
265547
+ return { cid: result2, size: effectiveFileSize };
265548
+ }
265549
+ const result = await uploadSingleBlock(bulletinRpc, context.signer, bytes, {
265550
+ maxRetries,
265551
+ onPhase,
265552
+ onRetry
265553
+ });
265554
+ return { cid: result, size: bytes.length };
265555
+ };
265556
+ let cid;
265557
+ let uploadSize;
265558
+ let profileReportPath;
265559
+ let profileReport;
265560
+ const uploadStartedAtMs = Date.now();
265561
+ const uploadStartedAtIso = new Date(uploadStartedAtMs).toISOString();
265562
+ const pathBasename = resolvedPath.split("/").pop() ?? resolvedPath;
265563
+ if (authInfo?.expiration && authInfo.currentBlock) {
265564
+ reporter.detail(`auth: valid (expires ${formatExpirationDisplay(authInfo.currentBlock, authInfo.expiration)})`);
266086
265565
  }
266087
- const context = await maybeQuiet(jsonOutput, () => prepareAssetHubContext(merged));
266088
- const { clientWrapper, substrateAddress, signer, evmAddress } = context;
266089
265566
  if (!jsonOutput) {
266090
- console.log(source_default.bold(`
266091
- ▶ Transfer
266092
- `));
266093
- console.log(source_default.gray(" domain: ") + source_default.cyan(`${label}.dot`));
266094
- console.log(source_default.gray(" to: ") + source_default.white(destination));
265567
+ if (isDirectory) {
265568
+ console.log(source_default.blue(`
265569
+ ▶ Uploading directory: ${pathBasename}`));
265570
+ console.log(source_default.gray(" path: ") + source_default.white(resolvedPath));
265571
+ console.log(source_default.gray(" rpc: ") + source_default.white(bulletinRpc));
265572
+ console.log(source_default.gray(" concurrency: ") + source_default.white(`${concurrency}x parallel waves`));
265573
+ } else if (shouldUseChunkedUpload) {
265574
+ console.log(source_default.blue(`
265575
+ ▶ Uploading file: ${pathBasename} (${formatBytes(effectiveFileSize)})`));
265576
+ console.log(source_default.gray(" path: ") + source_default.white(resolvedPath));
265577
+ console.log(source_default.gray(" rpc: ") + source_default.white(bulletinRpc));
265578
+ console.log(source_default.gray(" mode: ") + source_default.white(`chunked (${totalChunks} × ${formatBytes(chunkSizeBytes)}, adaptive window 1..${concurrency})`));
265579
+ } else {
265580
+ console.log(source_default.blue(`
265581
+ ▶ Uploading file: ${pathBasename} (${formatBytes(bytes.length)})`));
265582
+ console.log(source_default.gray(" path: ") + source_default.white(resolvedPath));
265583
+ console.log(source_default.gray(" rpc: ") + source_default.white(bulletinRpc));
265584
+ console.log(source_default.gray(" mode: ") + source_default.white("single block"));
265585
+ }
266095
265586
  }
266096
- await maybeQuiet(jsonOutput, () => step("Ensuring account mapped", async () => clientWrapper.ensureAccountMapped(substrateAddress, signer)));
266097
- const recipient = await maybeQuiet(jsonOutput, () => step("Resolving recipient", async () => resolveRecipientByKind(clientWrapper, substrateAddress, destination)));
266098
- await maybeQuiet(jsonOutput, () => step("Transferring domain", async () => transferDomain(clientWrapper, substrateAddress, signer, evmAddress, recipient, label)));
266099
- await maybeQuiet(jsonOutput, () => step("Verifying ownership", async () => verifyDomainOwnership(clientWrapper, substrateAddress, label, recipient)));
265587
+ try {
265588
+ const uploadResult = await withBulletinHumanOutput(reporterMode, performUpload);
265589
+ cid = uploadResult.cid;
265590
+ uploadSize = uploadResult.size;
265591
+ } finally {
265592
+ chunkedMonitor?.stop();
265593
+ }
265594
+ const uploadFinishedAtMs = Date.now();
265595
+ const uploadFinishedAtIso = new Date(uploadFinishedAtMs).toISOString();
265596
+ const totalUploadTimeMs = Math.max(1, uploadFinishedAtMs - uploadStartedAtMs);
265597
+ const totalUploadTimeSeconds = totalUploadTimeMs / 1000;
265598
+ if (profiler) {
265599
+ const finalizedProfile = await profiler.finalize(cid, profileOutputOverride);
265600
+ profileReport = finalizedProfile.report;
265601
+ profileReportPath = finalizedProfile.outputPath;
265602
+ }
265603
+ onPhase({
265604
+ phase: "verify",
265605
+ state: "start",
265606
+ message: "Verifying content on Bulletin P2P..."
265607
+ });
265608
+ let verified = false;
265609
+ try {
265610
+ const p2pResult = await verifyCidViaP2P(cid);
265611
+ if (p2pResult.resolvable) {
265612
+ onPhase({ phase: "verify", state: "success", message: "Content verified via P2P" });
265613
+ verified = true;
265614
+ }
265615
+ } catch {}
265616
+ if (!verified) {
265617
+ onPhase({
265618
+ phase: "verify",
265619
+ state: "update",
265620
+ message: "P2P unavailable, checking IPFS gateways..."
265621
+ });
265622
+ const gatewayResults = await verifyCidWithMultipleGateways(cid);
265623
+ const resolvable = Array.from(gatewayResults.values()).some((r2) => r2.resolvable);
265624
+ if (resolvable) {
265625
+ onPhase({
265626
+ phase: "verify",
265627
+ state: "success",
265628
+ message: "Content verified via IPFS gateway"
265629
+ });
265630
+ } else {
265631
+ onPhase({
265632
+ phase: "verify",
265633
+ state: "warning",
265634
+ message: "Content not yet resolvable — it may still be propagating"
265635
+ });
265636
+ }
265637
+ }
265638
+ const contenthash = generateContenthash(cid);
265639
+ const previewUrl = getPreviewUrl({
265640
+ cid,
265641
+ path: resolvedPath,
265642
+ type: isDirectory ? "directory" : "file",
265643
+ size: uploadSize,
265644
+ timestamp: ""
265645
+ });
266100
265646
  if (jsonOutput) {
266101
- console.log(JSON.stringify({
266102
- label,
266103
- domain: `${label}.dot`,
266104
- destination,
266105
- recipient,
266106
- transferred: true
266107
- }));
265647
+ const authExpiresAt = expirationToISOString(authInfo?.currentBlock, authInfo?.expiration);
265648
+ writeBulletinJson({
265649
+ cid,
265650
+ contenthash: `0x${contenthash}`,
265651
+ preview: previewUrl,
265652
+ path: resolvedPath,
265653
+ type: isDirectory ? "directory" : "file",
265654
+ size: uploadSize,
265655
+ authorizationExpiresAt: authExpiresAt,
265656
+ uploadStartedAtIso,
265657
+ uploadFinishedAtIso,
265658
+ totalUploadTimeMs,
265659
+ totalUploadTimeSeconds
265660
+ });
266108
265661
  } else {
265662
+ console.log(source_default.gray(`
265663
+ cid: `) + source_default.cyan(cid));
265664
+ console.log(source_default.gray(" preview: ") + source_default.blue(previewUrl));
265665
+ console.log(source_default.gray(" total time: ") + source_default.white(formatDuration(totalUploadTimeSeconds)));
265666
+ if (mergedOptions.printContenthash) {
265667
+ console.log(source_default.gray(" contenthash: ") + source_default.white(`0x${contenthash}`));
265668
+ }
265669
+ if (profileReportPath && profileReport) {
265670
+ console.log(source_default.gray(" profile: ") + source_default.white(profileReportPath));
265671
+ console.log(source_default.gray(" throughput: ") + source_default.white(`${formatBytes(profileReport.summary.throughputBytesPerSecond)}/s`));
265672
+ console.log(source_default.gray(" peak heap: ") + source_default.white(formatBytes(profileReport.summary.peakHeapUsed)));
265673
+ }
266109
265674
  console.log(source_default.green(`
266110
- ✓ Complete
265675
+ Upload Complete
266111
265676
  `));
266112
265677
  }
266113
- process.exit(0);
266114
- } catch (error2) {
266115
- const errorMessage = formatErrorMessage(error2);
266116
- const jsonOutput = getJsonFlag(cmd);
266117
- if (jsonOutput) {
266118
- console.error(JSON.stringify({ error: errorMessage }));
266119
- process.exit(1);
266120
- }
266121
- console.error(source_default.red(`
266122
- Error: ${errorMessage}
265678
+ if (options2.cache) {
265679
+ onPhase({
265680
+ phase: "cache",
265681
+ state: "start",
265682
+ message: "Saving CID to on-chain Store..."
265683
+ });
265684
+ try {
265685
+ const { cacheCidToStore: cacheCidToStore2 } = await Promise.resolve().then(() => (init_storeManagement(), exports_storeManagement));
265686
+ const { createClient: createClient4 } = await Promise.resolve().then(() => (init_esm10(), exports_esm2));
265687
+ const { getWsProvider: getWsProvider2 } = await Promise.resolve().then(() => (init_ws_provider(), exports_ws_provider));
265688
+ const { paseo } = await Promise.resolve().then(() => (init_dist(), exports_dist));
265689
+ const { ReviveClientWrapper: ReviveClientWrapper2 } = await Promise.resolve().then(() => (init_polkadotClient(), exports_polkadotClient));
265690
+ const { resolveRpc: resolveRpc2 } = await Promise.resolve().then(() => (init_env(), exports_env));
265691
+ const rpc = resolveRpc2(process.env.DOTNS_RPC);
265692
+ const typedApi = createClient4(getWsProvider2(rpc)).getTypedApi(paseo);
265693
+ const clientWrapper = new ReviveClientWrapper2(typedApi);
265694
+ const evmAddress = await clientWrapper.getEvmAddress(context.substrateAddress);
265695
+ await cacheCidToStore2({
265696
+ cid,
265697
+ clientWrapper,
265698
+ signer: context.signer,
265699
+ substrateAddress: context.substrateAddress,
265700
+ evmAddress
265701
+ });
265702
+ onPhase({ phase: "cache", state: "success", message: "CID cached to Store" });
265703
+ } catch (cacheError) {
265704
+ const msg = formatErrorMessage(cacheError);
265705
+ let reason;
265706
+ if (/insufficient|balance/i.test(msg)) {
265707
+ reason = "insufficient PAS balance on Asset Hub — fund the account and retry with --cache";
265708
+ } else if (/no store deployed|store not deployed/i.test(msg)) {
265709
+ reason = "no Store deployed — register a domain first or deploy a Store manually";
265710
+ } else if (/not authorized|unauthorized/i.test(msg)) {
265711
+ reason = "Store not authorised for writes — run dotns store ensure-auth";
265712
+ } else {
265713
+ reason = msg;
265714
+ }
265715
+ onPhase({
265716
+ phase: "cache",
265717
+ state: "warning",
265718
+ message: `Store caching skipped: ${reason}`
265719
+ });
265720
+ }
265721
+ }
265722
+ if (mergedOptions.history !== false) {
265723
+ await addUploadRecord({
265724
+ cid,
265725
+ path: resolvedPath,
265726
+ type: isDirectory ? "directory" : "file",
265727
+ size: uploadSize
265728
+ });
265729
+ }
265730
+ process.exit(0);
265731
+ } catch (error2) {
265732
+ const errorMessage = formatErrorMessage(error2);
265733
+ const jsonOutput = getJsonFlag(command);
265734
+ if (jsonOutput) {
265735
+ writeBulletinJsonError(errorMessage);
265736
+ }
265737
+ console.error(source_default.red(`
265738
+ ✗ Error: ${errorMessage}
266123
265739
  `));
266124
265740
  process.exit(1);
266125
265741
  }
266126
265742
  });
266127
- }
266128
-
266129
- // src/cli/commands/bulletin.ts
266130
- init_reporter();
266131
- function cleanupHeliaAndExit(code8) {
266132
- Promise.resolve().then(() => (init_heliaClient(), exports_heliaClient)).then(({ destroySharedHeliaClient: destroySharedHeliaClient2 }) => destroySharedHeliaClient2()).catch(() => {}).finally(() => process.exit(code8));
266133
- setTimeout(() => process.exit(code8), 500);
266134
- return;
266135
- }
266136
- function getMergedOptions2(command, fallback) {
266137
- const mergedOptions = { ...fallback ?? {} };
266138
- let currentCommand = command?.parent;
266139
- while (currentCommand) {
266140
- if (typeof currentCommand.opts === "function") {
266141
- const parentOptions = currentCommand.opts();
266142
- for (const key in parentOptions) {
266143
- if (!(key in mergedOptions) && parentOptions[key] !== undefined) {
266144
- mergedOptions[key] = parentOptions[key];
265743
+ const statusCommand = addReporterOption(bulletinCommand.command("status [address]").description("Check authorization status for an account on Bulletin").option("--bulletin-rpc <wsUrl>", "Bulletin WebSocket RPC endpoint", DEFAULT_BULLETIN_RPC).option("--json", "Write machine-readable JSON to stdout", false));
265744
+ addAuthOptions(statusCommand).action(async (positionalAddress, options2, command) => {
265745
+ try {
265746
+ const mergedOptions = getMergedOptions(command, options2);
265747
+ const jsonOutput = getJsonFlag(command);
265748
+ const reporterMode = resolveReporterMode(mergedOptions.reporter);
265749
+ const reporter = createCliReporter(mergedOptions.reporter);
265750
+ const bulletinRpc = String(mergedOptions.bulletinRpc || DEFAULT_BULLETIN_RPC);
265751
+ const targetAddress = await resolveTargetAddress(positionalAddress, mergedOptions, reporterMode);
265752
+ const statusTask = reporter.task("Checking authorization");
265753
+ const authStatus = await checkAuthorization(bulletinRpc, targetAddress);
265754
+ statusTask.succeed("Authorization status read");
265755
+ if (jsonOutput) {
265756
+ writeBulletinJson({
265757
+ address: targetAddress,
265758
+ rpc: bulletinRpc,
265759
+ authorized: authStatus.authorized,
265760
+ expired: authStatus.expired ?? false,
265761
+ transactions: authStatus.transactions ?? 0,
265762
+ bytes: (authStatus.bytes ?? BigInt(0)).toString(),
265763
+ expiresAt: expirationToISOString(authStatus.currentBlock, authStatus.expiration)
265764
+ });
265765
+ } else {
265766
+ console.log(source_default.blue(`
265767
+ ▶ Bulletin Authorization Status`));
265768
+ console.log(source_default.gray(" account: ") + source_default.cyan(targetAddress));
265769
+ console.log(source_default.gray(" rpc: ") + source_default.white(bulletinRpc));
265770
+ if (!authStatus.authorized) {
265771
+ console.log(source_default.gray(" status: ") + source_default.red("not authorized"));
265772
+ console.log(source_default.gray(`
265773
+ Authorize with: dotns bulletin authorize ` + targetAddress + `
265774
+ `));
265775
+ } else {
265776
+ const isExpired = authStatus.expired;
265777
+ const dateDisplay = formatExpirationDisplay(authStatus.currentBlock, authStatus.expiration);
265778
+ console.log(source_default.gray(" status: ") + (isExpired ? source_default.red("expired") : source_default.green("authorized")));
265779
+ console.log(source_default.gray(isExpired ? " expired: " : " expires: ") + (isExpired ? source_default.red(dateDisplay) : source_default.white(dateDisplay)));
265780
+ console.log(source_default.gray(" transactions: ") + source_default.white((authStatus.transactions ?? 0).toLocaleString()));
265781
+ console.log(source_default.gray(" bytes: ") + source_default.white(formatBytes(authStatus.bytes ?? BigInt(0))));
265782
+ if (isExpired) {
265783
+ console.log(source_default.gray(`
265784
+ Re-authorize with: dotns bulletin authorize ` + targetAddress + `
265785
+ `));
265786
+ } else {
265787
+ console.log();
265788
+ }
266145
265789
  }
266146
265790
  }
266147
- }
266148
- currentCommand = currentCommand.parent;
266149
- }
266150
- return mergedOptions;
266151
- }
266152
- var PROFILE_SAMPLE_INTERVAL_MS = 2000;
266153
- function addReporterOption(command) {
266154
- return command.option("--reporter <mode>", "Progress reporter: auto, interactive, stream, or quiet", "auto");
266155
- }
266156
- function createProfileFingerprint(input) {
266157
- return createHash2("sha256").update(input).digest("hex").slice(0, 16);
266158
- }
266159
- function buildDefaultProfileOutputPath(sourcePath, fingerprint) {
266160
- const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
266161
- const basename = path9.basename(sourcePath).replace(/[^a-zA-Z0-9._-]/g, "_");
266162
- return path9.join(os11.homedir(), ".dotns", "upload-profiles", `${timestamp}-${basename}-${fingerprint}.json`);
266163
- }
266164
- function createUploadProfiler(options2) {
266165
- const startedAtMs = Date.now();
266166
- const startedAtIso = new Date(startedAtMs).toISOString();
266167
- let latestSchedulerState = {
266168
- timestampMs: startedAtMs,
266169
- window: Math.max(1, options2.initialConcurrency),
266170
- inFlightBytes: 0,
266171
- inFlightChunks: 0,
266172
- completedChunks: 0,
266173
- retries: 0
266174
- };
266175
- const samples = [];
266176
- const waves = [];
266177
- const captureSample = () => {
266178
- const usage = process.memoryUsage();
266179
- samples.push({
266180
- timestampMs: Date.now(),
266181
- heapUsed: usage.heapUsed,
266182
- rss: usage.rss,
266183
- arrayBuffers: usage.arrayBuffers,
266184
- external: usage.external,
266185
- inFlightBytes: latestSchedulerState.inFlightBytes,
266186
- inFlightChunks: latestSchedulerState.inFlightChunks,
266187
- window: latestSchedulerState.window,
266188
- completed: latestSchedulerState.completedChunks,
266189
- retries: latestSchedulerState.retries
266190
- });
266191
- };
266192
- captureSample();
266193
- const timer4 = setInterval(captureSample, PROFILE_SAMPLE_INTERVAL_MS);
266194
- timer4.unref?.();
266195
- const summarizeAndWrite = async (finalCid, overrideOutputPath) => {
266196
- clearInterval(timer4);
266197
- captureSample();
266198
- const finishedAtMs = Date.now();
266199
- const finishedAtIso = new Date(finishedAtMs).toISOString();
266200
- const totalUploadTimeMs = Math.max(1, finishedAtMs - startedAtMs);
266201
- const throughputBytesPerSecond = options2.sourceSizeBytes / totalUploadTimeMs * 1000;
266202
- const peakHeapUsed = Math.max(...samples.map((sample) => sample.heapUsed));
266203
- const peakRss = Math.max(...samples.map((sample) => sample.rss));
266204
- const peakArrayBuffers = Math.max(...samples.map((sample) => sample.arrayBuffers));
266205
- const peakExternal = Math.max(...samples.map((sample) => sample.external));
266206
- const maxWindowReached = Math.max(...samples.map((sample) => sample.window));
266207
- const report = {
266208
- meta: {
266209
- sourcePath: options2.sourcePath,
266210
- sourceSizeBytes: options2.sourceSizeBytes,
266211
- chunkSizeBytes: options2.chunkSizeBytes,
266212
- rpc: options2.rpc,
266213
- startedAtIso,
266214
- finishedAtIso,
266215
- heapLimitBytes: v8.getHeapStatistics().heap_size_limit,
266216
- initialConcurrency: options2.initialConcurrency,
266217
- maxConcurrency: options2.maxConcurrency
266218
- },
266219
- samples,
266220
- waves,
266221
- summary: {
266222
- totalUploadTimeMs,
266223
- totalUploadTimeSeconds: totalUploadTimeMs / 1000,
266224
- elapsedMs: totalUploadTimeMs,
266225
- throughputBytesPerSecond,
266226
- peakHeapUsed,
266227
- peakRss,
266228
- peakArrayBuffers,
266229
- peakExternal,
266230
- retryCount: latestSchedulerState.retries,
266231
- maxWindowReached,
266232
- finalCid
265791
+ process.exit(0);
265792
+ } catch (error2) {
265793
+ const errorMessage = formatErrorMessage(error2);
265794
+ const jsonOutput = getJsonFlag(command);
265795
+ if (jsonOutput) {
265796
+ writeBulletinJsonError(errorMessage);
265797
+ } else {
265798
+ console.error(source_default.red(`
265799
+ ✗ Error: ${errorMessage}
265800
+ `));
265801
+ process.exit(1);
266233
265802
  }
266234
- };
266235
- const outputPath = overrideOutputPath ?? options2.outputPath ?? buildDefaultProfileOutputPath(options2.sourcePath, createProfileFingerprint(`${options2.sourcePath}:${options2.sourceSizeBytes}:${options2.chunkSizeBytes}:${startedAtIso}`));
266236
- const resolvedOutputPath = path9.resolve(outputPath);
266237
- await filesystem2.mkdir(path9.dirname(resolvedOutputPath), { recursive: true });
266238
- await filesystem2.writeFile(resolvedOutputPath, JSON.stringify(report, null, 2), "utf8");
266239
- return { report, outputPath: resolvedOutputPath };
266240
- };
266241
- return {
266242
- onSchedulerState: (state3) => {
266243
- latestSchedulerState = state3;
266244
- captureSample();
266245
- },
266246
- onWave: (wave) => {
266247
- waves.push(wave);
266248
- captureSample();
266249
- },
266250
- finalize: summarizeAndWrite
266251
- };
266252
- }
266253
- async function withCapturedConsole(callback) {
266254
- const MAX_CAPTURED_ENTRIES = 400;
266255
- const captured = [];
266256
- const pushCaptured = (value3) => {
266257
- captured.push(value3);
266258
- if (captured.length > MAX_CAPTURED_ENTRIES) {
266259
- captured.splice(0, captured.length - MAX_CAPTURED_ENTRIES);
266260
265803
  }
266261
- };
266262
- const capture = (...args) => {
266263
- pushCaptured(args.map(String).join(" "));
266264
- };
266265
- const captureWrite = (chunk) => {
266266
- pushCaptured(String(chunk));
266267
- return true;
266268
- };
266269
- const saved = {
266270
- log: console.log,
266271
- error: console.error,
266272
- warn: console.warn,
266273
- info: console.info,
266274
- stdoutWrite: process.stdout.write.bind(process.stdout),
266275
- stderrWrite: process.stderr.write.bind(process.stderr)
266276
- };
266277
- console.log = capture;
266278
- console.error = capture;
266279
- console.warn = capture;
266280
- console.info = capture;
266281
- process.stdout.write = captureWrite;
266282
- process.stderr.write = captureWrite;
266283
- try {
266284
- return await callback();
266285
- } catch (error2) {
266286
- saved.error(`[captured output before failure]
266287
- ` + captured.join(`
266288
- `));
266289
- throw error2;
266290
- } finally {
266291
- console.log = saved.log;
266292
- console.error = saved.error;
266293
- console.warn = saved.warn;
266294
- console.info = saved.info;
266295
- process.stdout.write = saved.stdoutWrite;
266296
- process.stderr.write = saved.stderrWrite;
266297
- }
266298
- }
266299
- function maybeQuiet(jsonOutput, callback) {
266300
- return jsonOutput ? withCapturedConsole(callback) : callback();
266301
- }
266302
- async function withBulletinHumanOutput(reporterMode, callback) {
266303
- if (reporterMode === "quiet") {
266304
- return withCapturedConsole(callback);
266305
- }
266306
- return withConsoleToStderr(callback);
266307
- }
266308
- async function resolveTargetAddress(positionalAddress, mergedOptions, reporterMode) {
266309
- if (positionalAddress)
266310
- return positionalAddress;
266311
- const context = await withBulletinHumanOutput(reporterMode, () => prepareContext({ ...mergedOptions, useBulletin: true }));
266312
- return context.substrateAddress;
266313
- }
266314
- function writeBulletinJson(payload) {
266315
- process.stdout.write(`${JSON.stringify(payload)}
266316
- `);
266317
- }
266318
- function writeBulletinJsonError(error2) {
266319
- writeBulletinJson({ error: formatErrorMessage(error2) });
266320
- process.exit(1);
266321
- }
266322
- function createPhaseHandler(reporter) {
266323
- const tasks = new Map;
266324
- return (event) => {
266325
- const key = event.phase;
266326
- const activeTask = tasks.get(key);
266327
- if (event.state === "start") {
266328
- activeTask?.stop();
266329
- tasks.set(key, reporter.task(event.message));
266330
- return;
266331
- }
266332
- if (event.state === "update") {
266333
- if (activeTask) {
266334
- activeTask.update(event.message);
266335
- } else {
266336
- tasks.set(key, reporter.task(event.message));
266337
- }
266338
- return;
266339
- }
266340
- if (event.state === "success") {
266341
- if (activeTask) {
266342
- activeTask.succeed(event.message);
266343
- tasks.delete(key);
266344
- } else {
266345
- reporter.success(event.message);
266346
- }
266347
- return;
266348
- }
266349
- if (event.state === "warning") {
266350
- if (activeTask) {
266351
- activeTask.warn(event.message);
266352
- tasks.delete(key);
266353
- } else {
266354
- reporter.warn(event.message);
266355
- }
266356
- return;
266357
- }
266358
- if (activeTask) {
266359
- activeTask.fail(event.message);
266360
- tasks.delete(key);
266361
- return;
266362
- }
266363
- reporter.fail(event.message);
266364
- };
266365
- }
266366
- function createRetryHandler(reporter) {
266367
- return ({ label, retry, totalAttempts, delayMs, errorMessage }) => {
266368
- reporter.warn(`${label} attempt ${retry + 1}/${totalAttempts} failed: ${errorMessage}`);
266369
- reporter.detail(`retrying in ${(delayMs / 1000).toFixed(delayMs >= 1000 ? 1 : 0)}s`);
266370
- };
266371
- }
266372
- function createChunkedUploadMonitor(reporter, phaseHandler, fileSize, chunkSizeBytes, totalChunks) {
266373
- const heartbeatIntervalMs = 15000;
266374
- let latestState = null;
266375
- let heartbeatTimer;
266376
- const startedAtMs = Date.now();
266377
- const stopHeartbeat = () => {
266378
- if (heartbeatTimer) {
266379
- clearInterval(heartbeatTimer);
266380
- heartbeatTimer = undefined;
266381
- }
266382
- };
266383
- const emitHeartbeat = () => {
266384
- if (!latestState || latestState.inFlightChunks === 0) {
266385
- stopHeartbeat();
266386
- return;
266387
- }
266388
- reporter.detail(`heartbeat | ${latestState.completedChunks}/${totalChunks} chunks | window=${latestState.window} | in-flight=${latestState.inFlightChunks} | bytes=${formatBytes(latestState.inFlightBytes)} | retries=${latestState.retries} | elapsed=${formatDuration((Date.now() - startedAtMs) / 1000)}`);
266389
- };
266390
- return {
266391
- onSchedulerState(state3) {
266392
- latestState = state3;
266393
- if (reporter.mode !== "stream") {
266394
- return;
266395
- }
266396
- if (state3.inFlightChunks > 0 && !heartbeatTimer) {
266397
- heartbeatTimer = setInterval(emitHeartbeat, heartbeatIntervalMs);
266398
- heartbeatTimer.unref?.();
266399
- } else if (state3.inFlightChunks === 0) {
266400
- stopHeartbeat();
266401
- }
266402
- },
266403
- onWave(wave) {
266404
- const completedChunks = latestState?.completedChunks ?? 0;
266405
- const bytesUploaded = Math.min(fileSize, completedChunks * chunkSizeBytes);
266406
- const throughputBytesPerSecond = wave.durationMs > 0 ? wave.succeeded * chunkSizeBytes * 1000 / wave.durationMs : 0;
266407
- const message2 = `wave #${wave.wave} complete | ${completedChunks}/${totalChunks} chunks | ${formatBytes(bytesUploaded)}/${formatBytes(fileSize)} | ${(wave.durationMs / 1000).toFixed(1)}s | window=${wave.window} | retries=${wave.retries} | ${formatBytes(throughputBytesPerSecond)}/s`;
266408
- if (reporter.mode === "interactive") {
266409
- phaseHandler({ phase: "upload", state: "update", message: message2 });
266410
- } else {
266411
- reporter.line(message2);
266412
- }
266413
- },
266414
- stop() {
266415
- stopHeartbeat();
266416
- }
266417
- };
266418
- }
266419
- function attachBulletinCommands(root) {
266420
- const bulletinCommand = addReporterOption(root.command("bulletin").description("Bulletin storage utilities")).option("--json", "Write machine-readable JSON to stdout", false);
266421
- addAuthOptions(bulletinCommand);
266422
- const authorizeCommand = addReporterOption(bulletinCommand.command("authorize [address]").description("Authorize an account for Bulletin TransactionStorage").option("--bulletin-rpc <wsUrl>", "Bulletin WebSocket RPC endpoint", DEFAULT_BULLETIN_RPC).option("--transactions <count>", "Number of transactions to authorize", String(DEFAULT_AUTHORIZATION_TRANSACTIONS)).option("--bytes <count>", "Number of bytes to authorize", String(DEFAULT_AUTHORIZATION_BYTES)).option("--force", "Force re-authorization even if account appears already authorized", false).option("--json", "Write machine-readable JSON to stdout", false));
266423
- addAuthOptions(authorizeCommand).action(async (positionalAddress, options2, command) => {
265804
+ });
265805
+ const historyCommand = addReporterOption(bulletinCommand.command("history").alias("list").description("List all uploaded CIDs").option("--json", "Write machine-readable JSON to stdout", false));
265806
+ historyCommand.action(async (_options, command) => {
266424
265807
  try {
266425
- const mergedOptions = getMergedOptions2(command, options2);
266426
- const jsonOutput = getJsonFlag(command);
266427
- const reporterMode = resolveReporterMode(mergedOptions.reporter);
266428
- const reporter = createCliReporter(mergedOptions.reporter);
266429
- const onPhase = createPhaseHandler(reporter);
266430
- const bulletinRpc = String(mergedOptions.bulletinRpc || DEFAULT_BULLETIN_RPC);
266431
- const transactions = Number(mergedOptions.transactions || DEFAULT_AUTHORIZATION_TRANSACTIONS);
266432
- const bytes = BigInt(mergedOptions.bytes || DEFAULT_AUTHORIZATION_BYTES);
266433
- const force = Boolean(options2.force);
266434
- const signerKeyUri = String(mergedOptions.keyUri || DEFAULT_SUDO_KEY_URI);
266435
- const targetAddress = await resolveTargetAddress(positionalAddress, mergedOptions, reporterMode);
266436
- const signerContext = await withBulletinHumanOutput(reporterMode, () => prepareContext({ keyUri: signerKeyUri, useBulletin: true }));
266437
- if (!jsonOutput) {
266438
- console.log(source_default.blue(`
266439
- ▶ Bulletin Authorize`));
266440
- console.log(source_default.gray(" target: ") + source_default.cyan(targetAddress));
266441
- console.log(source_default.gray(" rpc: ") + source_default.white(bulletinRpc));
266442
- console.log(source_default.gray(" transactions: ") + source_default.white(transactions.toLocaleString()));
266443
- console.log(source_default.gray(" bytes: ") + source_default.white(formatBytes(bytes)));
266444
- console.log(source_default.gray(" signer: ") + source_default.yellow(signerKeyUri));
266445
- }
266446
- await withBulletinHumanOutput(reporterMode, () => authorizeAccount({
266447
- rpc: bulletinRpc,
266448
- signer: signerContext.signer,
266449
- targetAddress,
266450
- transactions,
266451
- bytes,
266452
- force,
266453
- onPhase
266454
- }));
266455
- if (jsonOutput) {
266456
- const authStatus = await checkAuthorization(bulletinRpc, targetAddress);
266457
- const expiresAt = expirationToISOString(authStatus.currentBlock, authStatus.expiration);
266458
- writeBulletinJson({
266459
- ok: true,
266460
- target: targetAddress,
266461
- rpc: bulletinRpc,
266462
- transactions,
266463
- bytes: bytes.toString(),
266464
- expiresAt
266465
- });
266466
- } else {
266467
- console.log(source_default.green(`
266468
- ✓ Authorization Complete`));
266469
- console.log(source_default.gray(` The account can now upload to Bulletin.
266470
- `));
266471
- }
266472
- process.exit(0);
266473
- } catch (error2) {
266474
- const errorMessage = formatErrorMessage(error2);
266475
265808
  const jsonOutput = getJsonFlag(command);
265809
+ const history = await readHistory();
266476
265810
  if (jsonOutput) {
266477
- writeBulletinJsonError(errorMessage);
265811
+ writeBulletinJson(history);
265812
+ process.exit(0);
266478
265813
  }
266479
- if (errorMessage.includes("AlreadyAuthorized")) {
265814
+ if (history.length === 0) {
266480
265815
  console.log(source_default.yellow(`
266481
- Account is already authorized
265816
+ No uploads in history.
265817
+ `));
265818
+ console.log(source_default.gray(` Upload files with: dotns bulletin upload <path>
266482
265819
  `));
266483
265820
  process.exit(0);
266484
265821
  }
266485
- if (errorMessage.includes("BadOrigin")) {
266486
- console.error(source_default.red(`
266487
- ✗ Authorization failed — insufficient privileges`));
266488
- console.error(source_default.yellow(" The signer does not have Authorizer privileges on this chain."));
266489
- console.error(source_default.gray(` Override with --key-uri if needed.
265822
+ console.log(source_default.blue(`
265823
+ ▶ Upload History
266490
265824
  `));
266491
- process.exit(1);
266492
- }
266493
- if (errorMessage.includes("not applied")) {
266494
- console.error(source_default.red(`
266495
- ✗ ${errorMessage}`));
266496
- console.error(source_default.gray(` Override with --key-uri if needed.
265825
+ console.log(source_default.gray(` ${history.length} upload(s) found
266497
265826
  `));
266498
- process.exit(1);
265827
+ history.forEach((record2, index) => {
265828
+ const num = (index + 1).toString().padStart(2, " ");
265829
+ console.log(source_default.yellow(` ${num}.`) + source_default.white(` ${formatRecordTimestamp(record2)}`));
265830
+ console.log(source_default.gray(" cid: ") + source_default.cyan(record2.cid));
265831
+ console.log(source_default.gray(" path: ") + source_default.white(record2.path));
265832
+ console.log(source_default.gray(" type: ") + source_default.white(record2.type));
265833
+ if (record2.size > 0) {
265834
+ console.log(source_default.gray(" size: ") + source_default.white(formatBytes(record2.size)));
265835
+ }
265836
+ console.log(source_default.gray(" preview: ") + source_default.blue(getPreviewUrl(record2)));
265837
+ console.log();
265838
+ });
265839
+ process.exit(0);
265840
+ } catch (error2) {
265841
+ const jsonOutput = getJsonFlag(command);
265842
+ if (jsonOutput) {
265843
+ writeBulletinJsonError(error2);
266499
265844
  }
266500
265845
  console.error(source_default.red(`
266501
- ✗ Error: ${errorMessage}
265846
+ ✗ Error: ${formatErrorMessage(error2)}
266502
265847
  `));
266503
265848
  process.exit(1);
266504
265849
  }
266505
265850
  });
266506
- const uploadCommand = addReporterOption(bulletinCommand.command("upload <path>").description("Upload a file or directory to Bulletin and print the resulting CID").option("--bulletin-rpc <wsUrl>", "Bulletin WebSocket RPC endpoint", DEFAULT_BULLETIN_RPC).option("--chunk-size <bytes>", "Chunk size for large uploads (clamped to 256 KB–2 MB)", String(DEFAULT_CHUNK_SIZE_BYTES)).option("--max-retries <n>", "Retry transient upload failures (default: 5, capped at 20)", String(DEFAULT_UPLOAD_MAX_RETRIES)).option("--force-chunked", "Force chunked upload (DAG-PB)", false).option("--concurrency <n>", "Adaptive scheduler max window (default: 16, max: 64)", "16").option("--print-contenthash", "Also print 0x-prefixed IPFS contenthash for the CID", false).option("--resume", "Resume a previously interrupted upload", false).option("--profile-upload", "Enable upload profiling and write a JSON report", false).option("--profile-output <path>", "Path to write upload profiling JSON report").option("--no-history", "Do not save upload to history", true).option("--cache", "Write the CID to the user's on-chain Store after upload", false).option("--json", "Write machine-readable JSON to stdout", false));
266507
- addAuthOptions(uploadCommand).action(async (inputPath, options2, command) => {
265851
+ bulletinCommand.command("history:remove <cid>").description("Remove an upload from history by CID").action(async (cid) => {
266508
265852
  try {
266509
- const mergedOptions = getMergedOptions2(command, options2);
266510
- const jsonOutput = getJsonFlag(command);
266511
- const reporterMode = resolveReporterMode(mergedOptions.reporter);
266512
- const reporter = createCliReporter(mergedOptions.reporter);
266513
- const onPhase = createPhaseHandler(reporter);
266514
- const onRetry = createRetryHandler(reporter);
266515
- if (mergedOptions.mnemonic && mergedOptions.keyUri) {
266516
- throw new Error("Cannot specify both --mnemonic and --key-uri");
265853
+ const removed = await removeUploadRecord(cid);
265854
+ if (removed) {
265855
+ console.log(source_default.green(`
265856
+ Removed ${cid} from history
265857
+ `));
265858
+ } else {
265859
+ console.log(source_default.yellow(`
265860
+ CID not found in history: ${cid}
265861
+ `));
266517
265862
  }
266518
- await cleanupStaleManifests();
266519
- const validatedPath = await withBulletinHumanOutput(reporterMode, () => validateAndReadPath(inputPath, onPhase));
266520
- const { bytes, isDirectory, resolvedPath, fileSize, fileMtimeMs } = validatedPath;
266521
- const bulletinRpc = String(mergedOptions.bulletinRpc || DEFAULT_BULLETIN_RPC);
266522
- const chunkSizeBytes = clampChunkSizeBytes(Number(mergedOptions.chunkSize || DEFAULT_CHUNK_SIZE_BYTES));
266523
- const maxRetries = normalizeUploadMaxRetries(mergedOptions.maxRetries);
266524
- const concurrency = Math.max(1, Math.min(64, Math.floor(Number(mergedOptions.concurrency || 16))));
266525
- const resume = Boolean(mergedOptions.resume);
266526
- const profileUpload = Boolean(mergedOptions.profileUpload);
266527
- const shouldUseChunkedUpload = !isDirectory;
266528
- const effectiveFileSize = isDirectory ? 0 : fileSize ?? bytes.length;
266529
- let resumedBlocks;
266530
- if (resume && shouldUseChunkedUpload) {
266531
- const resolvedFileMtimeMs = fileMtimeMs ?? (await filesystem2.stat(resolvedPath)).mtimeMs;
266532
- const manifestLoadResult = await loadManifestForResume({
266533
- inputPath: resolvedPath,
266534
- fileSize: effectiveFileSize,
266535
- fileMtimeMs: resolvedFileMtimeMs,
266536
- chunkSize: chunkSizeBytes
266537
- });
266538
- if (manifestLoadResult.manifest && manifestLoadResult.manifest.completedBlocks.length > 0) {
266539
- resumedBlocks = completedBlocksFromManifest(manifestLoadResult.manifest);
266540
- reporter.warn(`resuming: ${manifestLoadResult.manifest.completedBlocks.length} blocks already uploaded`);
266541
- } else if (manifestLoadResult.staleManifest) {
266542
- reporter.warn("resume notice: file fingerprint changed, starting a fresh upload manifest");
266543
- await deleteManifest(manifestLoadResult.staleManifest);
266544
- }
265863
+ process.exit(0);
265864
+ } catch (error2) {
265865
+ console.error(source_default.red(`
265866
+ Error: ${formatErrorMessage(error2)}
265867
+ `));
265868
+ process.exit(1);
265869
+ }
265870
+ });
265871
+ addReporterOption(bulletinCommand.command("history:clear").description("Clear all upload history")).action(async () => {
265872
+ try {
265873
+ const count2 = await clearHistory();
265874
+ const historyPath = getHistoryPath();
265875
+ console.log(source_default.green(`
265876
+ Cleared ${count2} upload(s) from history`));
265877
+ console.log(source_default.gray(` ${historyPath}
265878
+ `));
265879
+ process.exit(0);
265880
+ } catch (error2) {
265881
+ console.error(source_default.red(`
265882
+ ✗ Error: ${formatErrorMessage(error2)}
265883
+ `));
265884
+ process.exit(1);
265885
+ }
265886
+ });
265887
+ addReporterOption(bulletinCommand.command("verify <cid>").description("Verify a CID is resolvable via IPFS gateways").option("--json", "Write machine-readable JSON to stdout", false)).action(async (cid, options2, command) => {
265888
+ const mergedOptions = getMergedOptions(command, options2);
265889
+ const jsonOutput = getJsonFlag(command) || Boolean(options2.json);
265890
+ const reporter = createCliReporter(mergedOptions.reporter);
265891
+ const p2pTask = reporter.task("Connecting to Bulletin P2P");
265892
+ try {
265893
+ if (!jsonOutput) {
265894
+ console.log(source_default.blue(`
265895
+ ▶ Verifying CID`));
265896
+ console.log(source_default.gray(" cid: ") + source_default.cyan(cid));
266545
265897
  }
266546
- const context = await withBulletinHumanOutput(reporterMode, () => prepareContext({ ...mergedOptions, useBulletin: true }));
266547
- const authInfo = await withBulletinHumanOutput(reporterMode, () => ensureAccountAuthorized(bulletinRpc, context.substrateAddress));
266548
- const profileOutputOverride = mergedOptions.profileOutput ? String(mergedOptions.profileOutput) : undefined;
266549
- const profiler = profileUpload ? createUploadProfiler({
266550
- sourcePath: resolvedPath,
266551
- sourceSizeBytes: effectiveFileSize,
266552
- chunkSizeBytes: shouldUseChunkedUpload ? chunkSizeBytes : Math.max(1, effectiveFileSize),
266553
- rpc: bulletinRpc,
266554
- initialConcurrency: shouldUseChunkedUpload ? 1 : 1,
266555
- maxConcurrency: shouldUseChunkedUpload ? concurrency : 1,
266556
- outputPath: profileOutputOverride,
266557
- jsonOutput
266558
- }) : undefined;
266559
- const totalChunks = shouldUseChunkedUpload ? Math.ceil(effectiveFileSize / chunkSizeBytes) : undefined;
266560
- const chunkedMonitor = shouldUseChunkedUpload && totalChunks ? createChunkedUploadMonitor(reporter, onPhase, effectiveFileSize, chunkSizeBytes, totalChunks) : null;
266561
- const performUpload = async () => {
266562
- if (isDirectory) {
266563
- const result2 = await storeDirectory(bulletinRpc, context.signer, resolvedPath, {
266564
- concurrency,
266565
- accountAddress: context.substrateAddress,
266566
- maxRetries,
266567
- onPhase,
266568
- onRetry,
266569
- waitForFinalization: false
266570
- });
266571
- return { cid: result2.cid, size: 0 };
266572
- }
266573
- if (shouldUseChunkedUpload) {
266574
- const result2 = await uploadChunkedBlocks(bulletinRpc, context.signer, resolvedPath, chunkSizeBytes, effectiveFileSize, context.substrateAddress, {
266575
- completedBlocks: resumedBlocks,
266576
- concurrency,
266577
- maxRetries,
266578
- onPhase,
266579
- onRetry,
266580
- onSchedulerState: (state3) => {
266581
- profiler?.onSchedulerState(state3);
266582
- chunkedMonitor?.onSchedulerState(state3);
266583
- },
266584
- onWave: (wave) => {
266585
- profiler?.onWave(wave);
266586
- chunkedMonitor?.onWave(wave);
266587
- }
265898
+ const p2pResult = await verifyCidViaP2P(cid);
265899
+ if (p2pResult.resolvable) {
265900
+ p2pTask.succeed("CID verified via P2P (bitswap)");
265901
+ if (jsonOutput) {
265902
+ writeBulletinJson({
265903
+ cid,
265904
+ resolvable: true,
265905
+ method: "p2p",
265906
+ gateways: [{ gateway: "p2p/bitswap", resolvable: true }]
266588
265907
  });
266589
- return { cid: result2, size: effectiveFileSize };
266590
- }
266591
- const result = await uploadSingleBlock(bulletinRpc, context.signer, bytes, {
266592
- maxRetries,
266593
- onPhase,
266594
- onRetry
266595
- });
266596
- return { cid: result, size: bytes.length };
266597
- };
266598
- let cid;
266599
- let uploadSize;
266600
- let profileReportPath;
266601
- let profileReport;
266602
- const uploadStartedAtMs = Date.now();
266603
- const uploadStartedAtIso = new Date(uploadStartedAtMs).toISOString();
266604
- const pathBasename = resolvedPath.split("/").pop() ?? resolvedPath;
266605
- if (authInfo?.expiration && authInfo.currentBlock) {
266606
- reporter.detail(`auth: valid (expires ${formatExpirationDisplay(authInfo.currentBlock, authInfo.expiration)})`);
266607
- }
266608
- if (!jsonOutput) {
266609
- if (isDirectory) {
266610
- console.log(source_default.blue(`
266611
- ▶ Uploading directory: ${pathBasename}`));
266612
- console.log(source_default.gray(" path: ") + source_default.white(resolvedPath));
266613
- console.log(source_default.gray(" rpc: ") + source_default.white(bulletinRpc));
266614
- console.log(source_default.gray(" concurrency: ") + source_default.white(`${concurrency}x parallel waves`));
266615
- } else if (shouldUseChunkedUpload) {
266616
- console.log(source_default.blue(`
266617
- ▶ Uploading file: ${pathBasename} (${formatBytes(effectiveFileSize)})`));
266618
- console.log(source_default.gray(" path: ") + source_default.white(resolvedPath));
266619
- console.log(source_default.gray(" rpc: ") + source_default.white(bulletinRpc));
266620
- console.log(source_default.gray(" mode: ") + source_default.white(`chunked (${totalChunks} × ${formatBytes(chunkSizeBytes)}, adaptive window 1..${concurrency})`));
266621
265908
  } else {
266622
- console.log(source_default.blue(`
266623
- ▶ Uploading file: ${pathBasename} (${formatBytes(bytes.length)})`));
266624
- console.log(source_default.gray(" path: ") + source_default.white(resolvedPath));
266625
- console.log(source_default.gray(" rpc: ") + source_default.white(bulletinRpc));
266626
- console.log(source_default.gray(" mode: ") + source_default.white("single block"));
265909
+ console.log(source_default.gray(" ✓ ") + source_default.white("p2p/bitswap"));
265910
+ console.log();
266627
265911
  }
265912
+ cleanupHeliaAndExit(0);
266628
265913
  }
266629
- try {
266630
- const uploadResult = await withBulletinHumanOutput(reporterMode, performUpload);
266631
- cid = uploadResult.cid;
266632
- uploadSize = uploadResult.size;
266633
- } finally {
266634
- chunkedMonitor?.stop();
266635
- }
266636
- const uploadFinishedAtMs = Date.now();
266637
- const uploadFinishedAtIso = new Date(uploadFinishedAtMs).toISOString();
266638
- const totalUploadTimeMs = Math.max(1, uploadFinishedAtMs - uploadStartedAtMs);
266639
- const totalUploadTimeSeconds = totalUploadTimeMs / 1000;
266640
- if (profiler) {
266641
- const finalizedProfile = await profiler.finalize(cid, profileOutputOverride);
266642
- profileReport = finalizedProfile.report;
266643
- profileReportPath = finalizedProfile.outputPath;
266644
- }
266645
- onPhase({
266646
- phase: "verify",
266647
- state: "start",
266648
- message: "Verifying content on Bulletin P2P..."
266649
- });
266650
- let verified = false;
266651
- try {
266652
- const p2pResult = await verifyCidViaP2P(cid);
266653
- if (p2pResult.resolvable) {
266654
- onPhase({ phase: "verify", state: "success", message: "Content verified via P2P" });
266655
- verified = true;
266656
- }
266657
- } catch {}
266658
- if (!verified) {
266659
- onPhase({
266660
- phase: "verify",
266661
- state: "update",
266662
- message: "P2P unavailable, checking IPFS gateways..."
266663
- });
266664
- const gatewayResults = await verifyCidWithMultipleGateways(cid);
266665
- const resolvable = Array.from(gatewayResults.values()).some((r2) => r2.resolvable);
266666
- if (resolvable) {
266667
- onPhase({
266668
- phase: "verify",
266669
- state: "success",
266670
- message: "Content verified via IPFS gateway"
266671
- });
265914
+ p2pTask.warn("P2P verification failed, falling back to gateways");
265915
+ const gatewayTask = reporter.task("Checking IPFS gateways");
265916
+ const results = await verifyCidWithMultipleGateways(cid);
265917
+ const resolvableGateways = [];
265918
+ const failedGateways = [];
265919
+ for (const [gateway, result] of results) {
265920
+ if (result.resolvable) {
265921
+ resolvableGateways.push(gateway);
266672
265922
  } else {
266673
- onPhase({
266674
- phase: "verify",
266675
- state: "warning",
266676
- message: "Content not yet resolvable — it may still be propagating"
266677
- });
265923
+ failedGateways.push(gateway);
266678
265924
  }
266679
265925
  }
266680
- const contenthash = generateContenthash(cid);
266681
- const previewUrl = getPreviewUrl({
266682
- cid,
266683
- path: resolvedPath,
266684
- type: isDirectory ? "directory" : "file",
266685
- size: uploadSize,
266686
- timestamp: ""
266687
- });
266688
265926
  if (jsonOutput) {
266689
- const authExpiresAt = expirationToISOString(authInfo?.currentBlock, authInfo?.expiration);
265927
+ const entries = Array.from(results.entries()).map(([gateway, result]) => ({
265928
+ gateway,
265929
+ resolvable: result.resolvable,
265930
+ statusCode: result.statusCode,
265931
+ errorMessage: result.errorMessage
265932
+ }));
266690
265933
  writeBulletinJson({
266691
265934
  cid,
266692
- contenthash: `0x${contenthash}`,
266693
- preview: previewUrl,
266694
- path: resolvedPath,
266695
- type: isDirectory ? "directory" : "file",
266696
- size: uploadSize,
266697
- authorizationExpiresAt: authExpiresAt,
266698
- uploadStartedAtIso,
266699
- uploadFinishedAtIso,
266700
- totalUploadTimeMs,
266701
- totalUploadTimeSeconds
265935
+ resolvable: resolvableGateways.length > 0,
265936
+ method: resolvableGateways.length > 0 ? "gateway" : "none",
265937
+ gateways: entries
266702
265938
  });
265939
+ cleanupHeliaAndExit(resolvableGateways.length > 0 ? 0 : 1);
265940
+ }
265941
+ if (resolvableGateways.length > 0) {
265942
+ gatewayTask.succeed(`CID resolvable on ${resolvableGateways.length} gateway(s)`);
265943
+ for (const gw of resolvableGateways) {
265944
+ console.log(source_default.gray(" ✓ ") + source_default.white(gw));
265945
+ }
266703
265946
  } else {
266704
- console.log(source_default.gray(`
266705
- cid: `) + source_default.cyan(cid));
266706
- console.log(source_default.gray(" preview: ") + source_default.blue(previewUrl));
266707
- console.log(source_default.gray(" total time: ") + source_default.white(formatDuration(totalUploadTimeSeconds)));
266708
- if (mergedOptions.printContenthash) {
266709
- console.log(source_default.gray(" contenthash: ") + source_default.white(`0x${contenthash}`));
266710
- }
266711
- if (profileReportPath && profileReport) {
266712
- console.log(source_default.gray(" profile: ") + source_default.white(profileReportPath));
266713
- console.log(source_default.gray(" throughput: ") + source_default.white(`${formatBytes(profileReport.summary.throughputBytesPerSecond)}/s`));
266714
- console.log(source_default.gray(" peak heap: ") + source_default.white(formatBytes(profileReport.summary.peakHeapUsed)));
265947
+ gatewayTask.fail("CID not resolvable on any gateway");
265948
+ }
265949
+ if (failedGateways.length > 0 && resolvableGateways.length > 0) {
265950
+ for (const gw of failedGateways) {
265951
+ console.log(source_default.gray(" ✗ ") + source_default.dim(gw));
266715
265952
  }
266716
- console.log(source_default.green(`
266717
- ✓ Upload Complete
265953
+ }
265954
+ console.log();
265955
+ cleanupHeliaAndExit(resolvableGateways.length > 0 ? 0 : 1);
265956
+ } catch (error2) {
265957
+ p2pTask.fail("Verification failed");
265958
+ const errorMessage = formatErrorMessage(error2);
265959
+ if (jsonOutput) {
265960
+ writeBulletinJsonError(errorMessage);
265961
+ } else {
265962
+ console.error(source_default.red(`
265963
+ ✗ Error: ${errorMessage}
266718
265964
  `));
265965
+ cleanupHeliaAndExit(1);
266719
265966
  }
266720
- if (options2.cache) {
266721
- onPhase({
266722
- phase: "cache",
266723
- state: "start",
266724
- message: "Saving CID to on-chain Store..."
266725
- });
266726
- try {
266727
- const { cacheCidToStore: cacheCidToStore2 } = await Promise.resolve().then(() => (init_storeManagement(), exports_storeManagement));
266728
- const { createClient: createClient4 } = await Promise.resolve().then(() => (init_esm10(), exports_esm2));
266729
- const { getWsProvider: getWsProvider2 } = await Promise.resolve().then(() => (init_ws_provider(), exports_ws_provider));
266730
- const { paseo } = await Promise.resolve().then(() => (init_dist(), exports_dist));
266731
- const { ReviveClientWrapper: ReviveClientWrapper2 } = await Promise.resolve().then(() => (init_polkadotClient(), exports_polkadotClient));
266732
- const { resolveRpc: resolveRpc2 } = await Promise.resolve().then(() => (init_env(), exports_env));
266733
- const rpc = resolveRpc2(process.env.DOTNS_RPC);
266734
- const typedApi = createClient4(getWsProvider2(rpc)).getTypedApi(paseo);
266735
- const clientWrapper = new ReviveClientWrapper2(typedApi);
266736
- const evmAddress = await clientWrapper.getEvmAddress(context.substrateAddress);
266737
- await cacheCidToStore2({
266738
- cid,
266739
- clientWrapper,
266740
- signer: context.signer,
266741
- substrateAddress: context.substrateAddress,
266742
- evmAddress
266743
- });
266744
- onPhase({ phase: "cache", state: "success", message: "CID cached to Store" });
266745
- } catch (cacheError) {
266746
- const msg = formatErrorMessage(cacheError);
266747
- let reason;
266748
- if (/insufficient|balance/i.test(msg)) {
266749
- reason = "insufficient PAS balance on Asset Hub — fund the account and retry with --cache";
266750
- } else if (/no store deployed|store not deployed/i.test(msg)) {
266751
- reason = "no Store deployed — register a domain first or deploy a Store manually";
266752
- } else if (/not authorized|unauthorized/i.test(msg)) {
266753
- reason = "Store not authorised for writes run dotns store ensure-auth";
266754
- } else {
266755
- reason = msg;
266756
- }
266757
- onPhase({
266758
- phase: "cache",
266759
- state: "warning",
266760
- message: `Store caching skipped: ${reason}`
266761
- });
265967
+ }
265968
+ });
265969
+ }
265970
+
265971
+ // src/cli/commands/content.ts
265972
+ init_source();
265973
+
265974
+ // src/commands/contentHash.ts
265975
+ init_source();
265976
+ init__esm();
265977
+ init_constants();
265978
+ init_contractInteractions();
265979
+
265980
+ // src/commands/resolverAuth.ts
265981
+ init__esm();
265982
+ init_constants();
265983
+ init_contractInteractions();
265984
+ async function getResolverNodeInfo(clientWrapper, originSubstrateAddress, namehashNode) {
265985
+ const exists2 = await performContractCall(clientWrapper, originSubstrateAddress, CONTRACTS.DOTNS_REGISTRY, DOTNS_REGISTRY_ABI, "recordExists", [namehashNode]);
265986
+ const owner = await performContractCall(clientWrapper, originSubstrateAddress, CONTRACTS.DOTNS_REGISTRY, DOTNS_REGISTRY_ABI, "owner", [namehashNode]);
265987
+ const caller = await clientWrapper.getEvmAddress(originSubstrateAddress);
265988
+ return { exists: exists2 && owner !== zeroAddress, owner, caller };
265989
+ }
265990
+ async function requireResolverAuthorization(clientWrapper, originSubstrateAddress, owner, caller) {
265991
+ const isOwner = checksumAddress(owner) === checksumAddress(caller);
265992
+ const isApproved = await performContractCall(clientWrapper, originSubstrateAddress, CONTRACTS.DOTNS_CONTENT_RESOLVER, DOTNS_CONTENT_RESOLVER_ABI, "isApprovedForAll", [owner, caller]);
265993
+ if (!isOwner && !isApproved) {
265994
+ throw new Error(`You do not own this domain. Owner is ${owner}, but you are ${caller}`);
265995
+ }
265996
+ }
265997
+
265998
+ // src/commands/contentHash.ts
265999
+ function decodeContenthashToCid(contenthash) {
266000
+ if (contenthash === "0x" || contenthash === "0x0" || contenthash.length < 6) {
266001
+ return "No CID set";
266002
+ }
266003
+ const cid = decodeIpfsContenthash(contenthash);
266004
+ return cid ?? `Unable to decode: ${contenthash}`;
266005
+ }
266006
+ function encodeCidToContenthash(cidString) {
266007
+ const encoded = encodeIpfsContenthash(cidString);
266008
+ return `0x${encoded}`;
266009
+ }
266010
+ async function viewDomainContentHash(clientWrapper, originSubstrateAddress, label, spinner) {
266011
+ const namehashNode = namehash(`${label}.dot`);
266012
+ spinner.start("Querying registry");
266013
+ const recordExists = await performContractCall(clientWrapper, originSubstrateAddress, CONTRACTS.DOTNS_REGISTRY, DOTNS_REGISTRY_ABI, "recordExists", [namehashNode]);
266014
+ const ownerAddress = await performContractCall(clientWrapper, originSubstrateAddress, CONTRACTS.DOTNS_REGISTRY, DOTNS_REGISTRY_ABI, "owner", [namehashNode]);
266015
+ spinner.succeed("Registry read");
266016
+ console.log(source_default.gray(" registry: ") + source_default.white(CONTRACTS.DOTNS_REGISTRY));
266017
+ console.log(source_default.gray(" domain: ") + source_default.cyan(`${label}.dot`));
266018
+ console.log(source_default.gray(" node: ") + source_default.white(namehashNode));
266019
+ console.log(source_default.gray(" exists: ") + source_default.white(String(recordExists)));
266020
+ console.log(source_default.gray(" owner: ") + source_default.white(ownerAddress));
266021
+ console.log();
266022
+ if (!recordExists || ownerAddress === zeroAddress) {
266023
+ console.log(source_default.yellow(" status: Domain not registered"));
266024
+ return { domain: `${label}.dot`, contenthash: null, cid: null };
266025
+ }
266026
+ const contentHashBytes = await performContractCall(clientWrapper, originSubstrateAddress, CONTRACTS.DOTNS_CONTENT_RESOLVER, DOTNS_CONTENT_RESOLVER_ABI, "contenthash", [namehashNode]);
266027
+ const decodedCid = decodeContenthashToCid(contentHashBytes);
266028
+ console.log(source_default.gray(" resolver: ") + source_default.white(CONTRACTS.DOTNS_CONTENT_RESOLVER));
266029
+ console.log(source_default.gray(" contenthash: ") + source_default.white(contentHashBytes));
266030
+ console.log(source_default.gray(" cid: ") + source_default.cyan(decodedCid));
266031
+ const hasContent = decodedCid !== "No CID set";
266032
+ const cidResolved = hasContent && !decodedCid.startsWith("Unable to decode:") ? decodedCid : null;
266033
+ return {
266034
+ domain: `${label}.dot`,
266035
+ contenthash: hasContent ? contentHashBytes : null,
266036
+ cid: cidResolved
266037
+ };
266038
+ }
266039
+ async function setDomainContentHash(clientWrapper, originSubstrateAddress, signer, label, contentId, spinner) {
266040
+ const namehashNode = namehash(`${label}.dot`);
266041
+ console.log(source_default.gray(" domain: ") + source_default.cyan(`${label}.dot`));
266042
+ console.log(source_default.gray(" node: ") + source_default.white(namehashNode));
266043
+ console.log();
266044
+ const { exists: exists2, owner, caller } = await getResolverNodeInfo(clientWrapper, originSubstrateAddress, namehashNode);
266045
+ console.log(source_default.gray(" exists: ") + source_default.white(String(exists2)));
266046
+ console.log(source_default.gray(" owner: ") + source_default.white(owner));
266047
+ console.log(source_default.gray(" caller: ") + source_default.white(caller));
266048
+ console.log();
266049
+ if (!exists2) {
266050
+ throw new Error(`Domain ${label}.dot is not registered`);
266051
+ }
266052
+ await requireResolverAuthorization(clientWrapper, originSubstrateAddress, owner, caller);
266053
+ console.log(source_default.gray(" status: ") + source_default.green("Ownership verified"));
266054
+ console.log();
266055
+ try {
266056
+ const currentContentHash = await performContractCall(clientWrapper, originSubstrateAddress, CONTRACTS.DOTNS_CONTENT_RESOLVER, DOTNS_CONTENT_RESOLVER_ABI, "contenthash", [namehashNode]);
266057
+ const currentCid = decodeContenthashToCid(currentContentHash);
266058
+ console.log(source_default.gray(" current: ") + source_default.cyan(currentCid));
266059
+ } catch {
266060
+ console.log(source_default.gray(" current: ") + source_default.yellow("Not set or error reading"));
266061
+ }
266062
+ console.log();
266063
+ const contentBytes = encodeCidToContenthash(contentId);
266064
+ console.log(source_default.gray(" resolver: ") + source_default.white(CONTRACTS.DOTNS_CONTENT_RESOLVER));
266065
+ console.log(source_default.gray(" node: ") + source_default.white(namehashNode));
266066
+ console.log(source_default.gray(" cid: ") + source_default.cyan(contentId));
266067
+ console.log(source_default.gray(" bytes: ") + source_default.white(contentBytes));
266068
+ spinner.start("Submitting setContenthash");
266069
+ const transactionHash = await submitContractTransaction(clientWrapper, CONTRACTS.DOTNS_CONTENT_RESOLVER, 0n, DOTNS_CONTENT_RESOLVER_ABI, "setContenthash", [namehashNode, contentBytes], originSubstrateAddress, signer, spinner, "setContenthash");
266070
+ console.log(source_default.gray(" tx: ") + source_default.blue(transactionHash));
266071
+ const updatedContentHash = await performContractCall(clientWrapper, originSubstrateAddress, CONTRACTS.DOTNS_CONTENT_RESOLVER, DOTNS_CONTENT_RESOLVER_ABI, "contenthash", [namehashNode]);
266072
+ const updatedCid = decodeContenthashToCid(updatedContentHash);
266073
+ console.log();
266074
+ console.log(source_default.gray(" new: ") + source_default.cyan(updatedCid));
266075
+ return {
266076
+ ok: true,
266077
+ domain: `${label}.dot`,
266078
+ cid: contentId,
266079
+ contenthash: contentBytes,
266080
+ txHash: transactionHash
266081
+ };
266082
+ }
266083
+
266084
+ // src/cli/commands/lookup.ts
266085
+ init_source();
266086
+ init_esm10();
266087
+ init_ws_provider();
266088
+ init_dist();
266089
+ init_polkadotClient();
266090
+
266091
+ // src/commands/lookup.ts
266092
+ init_source();
266093
+ init_ora();
266094
+ init__esm();
266095
+ init_constants();
266096
+
266097
+ // src/utils/validation.ts
266098
+ init__esm();
266099
+ function countTrailingDigits(label) {
266100
+ let count2 = 0;
266101
+ for (let characterIndex = label.length - 1;characterIndex >= 0; characterIndex--) {
266102
+ const asciiCode = label.charCodeAt(characterIndex);
266103
+ if (asciiCode >= 48 && asciiCode <= 57) {
266104
+ count2++;
266105
+ } else {
266106
+ break;
266107
+ }
266108
+ }
266109
+ return count2;
266110
+ }
266111
+ function stripTrailingDigits(label) {
266112
+ return label.replace(/\d+$/, "");
266113
+ }
266114
+ function validateDomainLabel(label) {
266115
+ if (!/^[a-z0-9-]{3,}$/.test(label)) {
266116
+ throw new Error("Invalid domain label: must contain only lowercase letters, digits, and hyphens, with minimum length of 3 characters");
266117
+ }
266118
+ if (label.startsWith("-") || label.endsWith("-")) {
266119
+ throw new Error("Invalid domain label: cannot start or end with hyphen");
266120
+ }
266121
+ const trailingDigitCount = countTrailingDigits(label);
266122
+ if (trailingDigitCount > 2) {
266123
+ throw new Error(`Invalid domain label: maximum 2 trailing digits allowed, found ${trailingDigitCount}`);
266124
+ }
266125
+ }
266126
+ function validateGovernanceLabel(label) {
266127
+ validateDomainLabel(label);
266128
+ const baseName = stripTrailingDigits(label);
266129
+ if (baseName.length > 5) {
266130
+ throw new Error(`Invalid governance label: base name must be 5 characters or fewer (got ${baseName.length})`);
266131
+ }
266132
+ }
266133
+ var isValidSubstrateAddress = (address, ss58Format = 42) => {
266134
+ try {
266135
+ if (isHex(address))
266136
+ return false;
266137
+ const decoded = decodeAddress2(address);
266138
+ const checksummed = encodeAddress2(decoded, ss58Format);
266139
+ return address === checksummed;
266140
+ } catch {
266141
+ return false;
266142
+ }
266143
+ };
266144
+
266145
+ // src/commands/lookup.ts
266146
+ init_contractInteractions();
266147
+ init_formatting();
266148
+ async function performDomainLookup(label, originSubstrateAddress, clientWrapper) {
266149
+ const fullyQualifiedDomainName = `${label}.dot`;
266150
+ const namehashNode = namehash(fullyQualifiedDomainName);
266151
+ const result = {
266152
+ domain: fullyQualifiedDomainName,
266153
+ node: namehashNode,
266154
+ exists: false,
266155
+ owner: zeroAddress,
266156
+ resolver: zeroAddress,
266157
+ store: null,
266158
+ resolvedAddress: null,
266159
+ ownerBalance: null,
266160
+ baseNameReservation: null
266161
+ };
266162
+ const spinner = ora("Reading registry").start();
266163
+ result.exists = Boolean(await performContractCall(clientWrapper, originSubstrateAddress, CONTRACTS.DOTNS_REGISTRY, DOTNS_REGISTRY_ABI, "recordExists", [namehashNode]));
266164
+ result.owner = getAddress(await performContractCall(clientWrapper, originSubstrateAddress, CONTRACTS.DOTNS_REGISTRY, DOTNS_REGISTRY_ABI, "owner", [namehashNode]));
266165
+ result.resolver = getAddress(await performContractCall(clientWrapper, originSubstrateAddress, CONTRACTS.DOTNS_REGISTRY, DOTNS_REGISTRY_ABI, "resolver", [namehashNode]));
266166
+ spinner.succeed("Registry read");
266167
+ console.log(`
266168
+ ▶ DotNS Domain Lookup`);
266169
+ console.log(source_default.gray(" domain: ") + source_default.cyan(fullyQualifiedDomainName));
266170
+ console.log(source_default.gray(" node: ") + source_default.white(namehashNode));
266171
+ console.log();
266172
+ console.log("▶ Registry (DotnsRegistry)");
266173
+ console.log(source_default.gray(" registry: ") + source_default.white(CONTRACTS.DOTNS_REGISTRY));
266174
+ console.log(source_default.gray(" exists: ") + source_default.white(String(result.exists)));
266175
+ console.log(source_default.gray(" owner: ") + source_default.white(result.owner));
266176
+ console.log(source_default.gray(" resolver: ") + source_default.white(result.resolver));
266177
+ console.log();
266178
+ if (!result.exists || result.owner === zeroAddress) {
266179
+ console.log("▶ Status");
266180
+ console.log(source_default.gray(" status: ") + source_default.yellow("not registered (no record)"));
266181
+ console.log();
266182
+ const baseName2 = stripTrailingDigits(label);
266183
+ if (baseName2 !== label) {
266184
+ result.baseNameReservation = await lookupBaseNameReservation(clientWrapper, originSubstrateAddress, baseName2);
266185
+ displayBaseNameReservation(result.baseNameReservation);
266186
+ }
266187
+ return result;
266188
+ }
266189
+ console.log("▶ User Store (StoreFactory)");
266190
+ console.log(source_default.gray(" factory: ") + source_default.white(CONTRACTS.STORE_FACTORY));
266191
+ const storeAddress = getAddress(await performContractCall(clientWrapper, originSubstrateAddress, CONTRACTS.STORE_FACTORY, STORE_FACTORY_ABI, "getDeployedStore", [result.owner]));
266192
+ result.store = storeAddress === zeroAddress ? null : storeAddress;
266193
+ console.log(source_default.gray(" store: ") + source_default.white(storeAddress));
266194
+ console.log(source_default.gray(" status: ") + source_default.white(storeAddress === zeroAddress ? "no store deployed" : "store exists"));
266195
+ console.log();
266196
+ console.log("▶ Forward Resolution (DotnsResolver)");
266197
+ console.log(source_default.gray(" expectedResolver: ") + source_default.white(CONTRACTS.DOTNS_RESOLVER));
266198
+ if (checksumAddress(result.resolver) !== checksumAddress(CONTRACTS.DOTNS_RESOLVER)) {
266199
+ console.log(source_default.gray(" note: ") + source_default.yellow("registry resolver is not DotnsResolver, skipping address lookup"));
266200
+ } else {
266201
+ try {
266202
+ const resolvedAddress = getAddress(await performContractCall(clientWrapper, originSubstrateAddress, result.resolver, DOTNS_RESOLVER_ABI, "addressOf", [namehashNode]));
266203
+ result.resolvedAddress = resolvedAddress === zeroAddress ? null : resolvedAddress;
266204
+ console.log(source_default.gray(" resolvedAddress: ") + source_default.white(resolvedAddress === zeroAddress ? "(not set)" : resolvedAddress));
266205
+ } catch {
266206
+ console.log(source_default.gray(" resolvedAddress: ") + source_default.yellow("lookup failed"));
266207
+ }
266208
+ }
266209
+ console.log();
266210
+ console.log("▶ Owner Balance");
266211
+ try {
266212
+ const ownerSubstrateAddress = await clientWrapper.getSubstrateAddress(result.owner);
266213
+ const accountInfo = await clientWrapper.client.query.System.Account.getValue(ownerSubstrateAddress);
266214
+ const freeBalance = accountInfo.data.free;
266215
+ result.ownerBalance = {
266216
+ substrate: ownerSubstrateAddress,
266217
+ free: formatNativeBalance(freeBalance)
266218
+ };
266219
+ console.log(source_default.gray(" substrate: ") + source_default.white(ownerSubstrateAddress));
266220
+ console.log(source_default.gray(" free: ") + source_default.white(formatNativeBalance(freeBalance) + " PAS"));
266221
+ } catch {
266222
+ console.log(source_default.gray(" balance: ") + source_default.yellow("unavailable"));
266223
+ }
266224
+ console.log();
266225
+ const baseName = stripTrailingDigits(label);
266226
+ if (baseName !== label) {
266227
+ result.baseNameReservation = await lookupBaseNameReservation(clientWrapper, originSubstrateAddress, baseName);
266228
+ displayBaseNameReservation(result.baseNameReservation);
266229
+ }
266230
+ console.log("▶ Lookup completed");
266231
+ return result;
266232
+ }
266233
+ async function lookupBaseNameReservation(clientWrapper, originSubstrateAddress, baseName) {
266234
+ try {
266235
+ const reservationInfo = await performContractCall(clientWrapper, originSubstrateAddress, CONTRACTS.DOTNS_RULES, POP_RULES_ABI, "isBaseNameReserved", [baseName]);
266236
+ const [isReserved, reservationOwner, expirationTimestamp] = reservationInfo;
266237
+ return {
266238
+ baseName,
266239
+ isReserved,
266240
+ reservedBy: getAddress(reservationOwner),
266241
+ expires: expirationTimestamp > 0n ? new Date(Number(expirationTimestamp) * 1000).toISOString() : null
266242
+ };
266243
+ } catch {
266244
+ return {
266245
+ expires: null,
266246
+ baseName,
266247
+ isReserved: false,
266248
+ reservedBy: zeroAddress
266249
+ };
266250
+ }
266251
+ }
266252
+ function displayBaseNameReservation(reservation) {
266253
+ console.log("▶ Base Name Reservation (PopRules)");
266254
+ console.log(source_default.gray(" oracle: ") + source_default.white(CONTRACTS.DOTNS_RULES));
266255
+ console.log(source_default.gray(" baseName: ") + source_default.cyan(reservation.baseName));
266256
+ console.log(source_default.gray(" isReserved: ") + source_default.white(String(reservation.isReserved)));
266257
+ console.log(source_default.gray(" owner: ") + source_default.white(reservation.reservedBy));
266258
+ if (reservation.expires) {
266259
+ console.log(source_default.gray(" expires: ") + source_default.white(reservation.expires));
266260
+ }
266261
+ console.log();
266262
+ }
266263
+ async function performOwnerOfLookup(name10, substrateAddress, clientWrapper) {
266264
+ if (!name10 || name10.trim().length === 0) {
266265
+ throw new Error("--owner-of requires a <label>");
266266
+ }
266267
+ const label = name10.trim();
266268
+ console.log(source_default.bold(`
266269
+ \uD83D\uDD0E Ownership lookup
266270
+ `));
266271
+ console.log(source_default.gray(" Label: ") + source_default.cyan(label));
266272
+ console.log(source_default.gray(" Domain: ") + source_default.cyan(label + ".dot"));
266273
+ const tokenId = computeDomainTokenId(label);
266274
+ let actualOwner;
266275
+ let isRegistered;
266276
+ try {
266277
+ actualOwner = await withTimeout(performContractCall(clientWrapper, substrateAddress, CONTRACTS.DOTNS_REGISTRAR, DOTNS_REGISTRAR_ABI, "ownerOf", [tokenId]), 30000, "ownerOf");
266278
+ isRegistered = actualOwner !== zeroAddress;
266279
+ } catch (error2) {
266280
+ const errorMessage = formatErrorMessage(error2);
266281
+ if (errorMessage.includes("Contract reverted") || errorMessage.includes("does not exist")) {
266282
+ actualOwner = zeroAddress;
266283
+ isRegistered = false;
266284
+ } else {
266285
+ throw error2;
266286
+ }
266287
+ }
266288
+ const ownerSubstrateAddress = isRegistered ? await clientWrapper.getSubstrateAddress(actualOwner) : "(none)";
266289
+ console.log(source_default.gray(`
266290
+ Registered: `) + source_default.white(String(isRegistered)));
266291
+ console.log(source_default.gray(" Owner (EVM): ") + source_default.white(isRegistered ? actualOwner : "(none)"));
266292
+ console.log(source_default.gray(" Owner (Substrate): ") + source_default.white(ownerSubstrateAddress));
266293
+ return {
266294
+ label,
266295
+ domain: `${label}.dot`,
266296
+ registered: isRegistered,
266297
+ ownerEvm: isRegistered ? actualOwner : zeroAddress,
266298
+ ownerSubstrate: ownerSubstrateAddress
266299
+ };
266300
+ }
266301
+
266302
+ // src/commands/register.ts
266303
+ init_source();
266304
+ init_ora();
266305
+ init__esm();
266306
+ init_formatting();
266307
+ import crypto16 from "crypto";
266308
+
266309
+ // src/types/types.ts
266310
+ var ProofOfPersonhoodStatus;
266311
+ ((ProofOfPersonhoodStatus2) => {
266312
+ ProofOfPersonhoodStatus2[ProofOfPersonhoodStatus2["NoStatus"] = 0] = "NoStatus";
266313
+ ProofOfPersonhoodStatus2[ProofOfPersonhoodStatus2["ProofOfPersonhoodLite"] = 1] = "ProofOfPersonhoodLite";
266314
+ ProofOfPersonhoodStatus2[ProofOfPersonhoodStatus2["ProofOfPersonhoodFull"] = 2] = "ProofOfPersonhoodFull";
266315
+ ProofOfPersonhoodStatus2[ProofOfPersonhoodStatus2["Reserved"] = 3] = "Reserved";
266316
+ })(ProofOfPersonhoodStatus ||= {});
266317
+
266318
+ // src/commands/register.ts
266319
+ init_constants();
266320
+ init_contractInteractions();
266321
+ init_formatting();
266322
+ function redactSecret(secret) {
266323
+ if (secret.length <= 10)
266324
+ return "0x" + "*".repeat(secret.length - 2);
266325
+ const prefix = secret.slice(0, 6);
266326
+ const suffix = secret.slice(-4);
266327
+ const redacted = "*".repeat(secret.length - 10);
266328
+ return `${prefix}${redacted}${suffix}`;
266329
+ }
266330
+ function convertToProofOfPersonhoodStatus(value3) {
266331
+ if (typeof value3 === "number")
266332
+ return value3;
266333
+ if (typeof value3 === "bigint")
266334
+ return Number(value3);
266335
+ throw new Error(`Unexpected ProofOfPersonhoodStatus type: ${typeof value3}`);
266336
+ }
266337
+ async function classifyDomainName(clientWrapper, originSubstrateAddress, label) {
266338
+ const spinner = ora("Classifying name via PopRules").start();
266339
+ try {
266340
+ const classificationResult = await withTimeout(performContractCall(clientWrapper, originSubstrateAddress, CONTRACTS.DOTNS_RULES, POP_RULES_ABI, "classifyName", [label]), 30000, "classifyName");
266341
+ const requiredStatus = convertToProofOfPersonhoodStatus(classificationResult[0]);
266342
+ const message2 = classificationResult[1];
266343
+ spinner.succeed("Name classification");
266344
+ console.log(source_default.gray(" required: ") + source_default.white(ProofOfPersonhoodStatus[requiredStatus]));
266345
+ console.log(source_default.gray(" message: ") + source_default.white(message2));
266346
+ return { requiredStatus, message: message2 };
266347
+ } catch (error2) {
266348
+ spinner.fail("Name classification failed");
266349
+ throw error2;
266350
+ }
266351
+ }
266352
+ async function ensureDomainNotRegistered(clientWrapper, originSubstrateAddress, label) {
266353
+ const spinner = ora(`Checking availability of ${source_default.cyan(label + ".dot")}`).start();
266354
+ const tokenId = computeDomainTokenId(label);
266355
+ try {
266356
+ const owner = await withTimeout(performContractCall(clientWrapper, originSubstrateAddress, CONTRACTS.DOTNS_REGISTRAR, DOTNS_REGISTRAR_ABI, "ownerOf", [tokenId]), 30000, "Availability check");
266357
+ if (owner !== zeroAddress) {
266358
+ spinner.fail(`Name ${source_default.cyan(label + ".dot")} already owned by ${source_default.yellow(owner)}`);
266359
+ throw new Error(`Domain already owned by ${owner}`);
266360
+ }
266361
+ } catch (error2) {
266362
+ const errorMessage = formatErrorMessage(error2);
266363
+ if (errorMessage.includes("already owned"))
266364
+ throw error2;
266365
+ }
266366
+ spinner.succeed(`Name ${source_default.cyan(label + ".dot")} is available`);
266367
+ }
266368
+ async function generateCommitment(clientWrapper, originSubstrateAddress, label, ownerAddress, includeReverse) {
266369
+ const spinner = ora("Generating commitment hash").start();
266370
+ try {
266371
+ validateDomainLabel(label);
266372
+ const secret = `0x${crypto16.randomBytes(32).toString("hex")}`;
266373
+ const registration = {
266374
+ label,
266375
+ owner: ownerAddress,
266376
+ secret,
266377
+ reserved: includeReverse
266378
+ };
266379
+ const commitment = await withTimeout(performContractCall(clientWrapper, originSubstrateAddress, CONTRACTS.DOTNS_REGISTRAR_CONTROLLER, DOTNS_REGISTRAR_CONTROLLER_ABI, "makeCommitment", [registration]), 30000, "Commitment generation");
266380
+ spinner.succeed("Commitment hash generated");
266381
+ console.log(source_default.gray(" commitment: ") + source_default.blue(commitment));
266382
+ console.log(source_default.gray(" hash: ") + source_default.dim("label + owner + secret + reserved"));
266383
+ console.log(source_default.gray(" secret: ") + source_default.yellow(redactSecret(secret)));
266384
+ console.log(source_default.gray(" reserved: ") + (includeReverse ? source_default.green("true") : source_default.white("false")));
266385
+ return { commitment, registration };
266386
+ } catch (error2) {
266387
+ spinner.fail("Failed to create commitment");
266388
+ throw error2;
266389
+ }
266390
+ }
266391
+ async function submitCommitment(clientWrapper, substrateAddress, signer, commitment) {
266392
+ const spinner = ora("Submitting commitment to controller").start();
266393
+ const transactionHash = await submitContractTransaction(clientWrapper, CONTRACTS.DOTNS_REGISTRAR_CONTROLLER, 0n, DOTNS_REGISTRAR_CONTROLLER_ABI, "commit", [commitment], substrateAddress, signer, spinner, "Commitment");
266394
+ console.log(source_default.gray(" tx: ") + source_default.blue(transactionHash));
266395
+ console.log(source_default.gray(" committed: ") + source_default.white(new Date().toISOString()));
266396
+ }
266397
+ async function waitForMinimumCommitmentAge(clientWrapper, originSubstrateAddress, commitment, commitmentBuffer) {
266398
+ const buffer2 = commitmentBuffer ?? DEFAULT_COMMITMENT_BUFFER_SECONDS;
266399
+ const checkSpinner = ora("Reading minimum commitment age").start();
266400
+ const [minimumAge, initialCommitTimestamp] = await Promise.all([
266401
+ withTimeout(performContractCall(clientWrapper, originSubstrateAddress, CONTRACTS.DOTNS_REGISTRAR_CONTROLLER, DOTNS_REGISTRAR_CONTROLLER_ABI, "minCommitmentAge", []), 30000, "minCommitmentAge"),
266402
+ withTimeout(performContractCall(clientWrapper, originSubstrateAddress, CONTRACTS.DOTNS_REGISTRAR_CONTROLLER, DOTNS_REGISTRAR_CONTROLLER_ABI, "commitments", [commitment]), 30000, "commitments")
266403
+ ]);
266404
+ const minimumAgeSeconds = typeof minimumAge === "bigint" ? Number(minimumAge) : minimumAge;
266405
+ const initialCommitTime = typeof initialCommitTimestamp === "bigint" ? Number(initialCommitTimestamp) : initialCommitTimestamp;
266406
+ if (initialCommitTime === 0) {
266407
+ checkSpinner.fail("Commitment not found on-chain");
266408
+ throw new Error("Commitment not found on-chain. It may not have been included in a block yet.");
266409
+ }
266410
+ const waitSeconds = minimumAgeSeconds + buffer2;
266411
+ checkSpinner.succeed(`Minimum commitment age: ${source_default.yellow(waitSeconds.toString() + "s")}`);
266412
+ const waitSpinner = ora(`Waiting for commitment age (${waitSeconds}s)`).start();
266413
+ let elapsedSeconds = 0;
266414
+ const intervalId = setInterval(() => {
266415
+ elapsedSeconds++;
266416
+ const remainingSeconds = waitSeconds - elapsedSeconds;
266417
+ waitSpinner.text = `Waiting for commitment age (${source_default.yellow(remainingSeconds + "s")} remaining)`;
266418
+ }, 1000);
266419
+ await new Promise((resolve) => setTimeout(resolve, waitSeconds * 1000));
266420
+ clearInterval(intervalId);
266421
+ waitSpinner.text = "Verifying commitment age on-chain";
266422
+ const pollDeadline = Date.now() + COMMITMENT_POLL_TIMEOUT_MS;
266423
+ while (Date.now() < pollDeadline) {
266424
+ const polledTimestamp = await performContractCall(clientWrapper, originSubstrateAddress, CONTRACTS.DOTNS_REGISTRAR_CONTROLLER, DOTNS_REGISTRAR_CONTROLLER_ABI, "commitments", [commitment]);
266425
+ const polledCommitTime = typeof polledTimestamp === "bigint" ? Number(polledTimestamp) : polledTimestamp;
266426
+ const nowSeconds = Math.floor(Date.now() / 1000);
266427
+ if (polledCommitTime > 0 && nowSeconds - polledCommitTime >= minimumAgeSeconds) {
266428
+ waitSpinner.succeed("Commitment age requirement met (verified on-chain)");
266429
+ return;
266430
+ }
266431
+ waitSpinner.text = `Commitment still too new, polling (${Math.ceil((pollDeadline - Date.now()) / 1000)}s remaining)`;
266432
+ await new Promise((resolve) => setTimeout(resolve, COMMITMENT_POLL_INTERVAL_MS));
266433
+ }
266434
+ waitSpinner.fail("Commitment age verification timed out");
266435
+ throw new Error(`Commitment still too new after ${waitSeconds + COMMITMENT_POLL_TIMEOUT_MS / 1000}s. The chain's block timestamps may be advancing slower than expected. Try increasing --commitment-buffer or DOTNS_COMMITMENT_BUFFER.`);
266436
+ }
266437
+ async function getUserProofOfPersonhoodStatus(clientWrapper, originSubstrateAddress, ownerAddress) {
266438
+ const userStatusRaw = await withTimeout(performContractCall(clientWrapper, originSubstrateAddress, CONTRACTS.DOTNS_RULES, POP_RULES_ABI, "userPopStatus", [ownerAddress]), 30000, "userPopStatus");
266439
+ return convertToProofOfPersonhoodStatus(userStatusRaw);
266440
+ }
266441
+ async function getPriceAndValidateEligibility(clientWrapper, originSubstrateAddress, label, ownerAddress) {
266442
+ const spinner = ora("Pricing via PopRules.price").start();
266443
+ try {
266444
+ validateDomainLabel(label);
266445
+ const baseName = stripTrailingDigits(label);
266446
+ const reservationInfo = await withTimeout(performContractCall(clientWrapper, originSubstrateAddress, CONTRACTS.DOTNS_RULES, POP_RULES_ABI, "isBaseNameReserved", [baseName]), 30000, "isBaseNameReserved");
266447
+ const [isReserved, reservationOwner] = reservationInfo;
266448
+ if (isReserved && checksumAddress(reservationOwner) !== checksumAddress(ownerAddress)) {
266449
+ spinner.fail("Eligibility failed");
266450
+ throw new Error("Base name reserved for original Lite registrant");
266451
+ }
266452
+ const classificationResult = await withTimeout(performContractCall(clientWrapper, originSubstrateAddress, CONTRACTS.DOTNS_RULES, POP_RULES_ABI, "priceWithoutCheck", [label, ownerAddress]), 30000, "priceWithoutCheck");
266453
+ const requiredStatus = convertToProofOfPersonhoodStatus(classificationResult.status);
266454
+ const message2 = classificationResult.message;
266455
+ const userStatus = await getUserProofOfPersonhoodStatus(clientWrapper, originSubstrateAddress, ownerAddress);
266456
+ if (requiredStatus === 3 /* Reserved */) {
266457
+ spinner.fail("Eligibility failed");
266458
+ throw new Error(message2);
266459
+ }
266460
+ if (requiredStatus === 2 /* ProofOfPersonhoodFull */) {
266461
+ if (userStatus !== 2 /* ProofOfPersonhoodFull */) {
266462
+ spinner.fail("Eligibility failed");
266463
+ throw new Error("Requires Full Personhood verification");
266464
+ }
266465
+ } else if (requiredStatus === 1 /* ProofOfPersonhoodLite */) {
266466
+ if (userStatus !== 1 /* ProofOfPersonhoodLite */ && userStatus !== 2 /* ProofOfPersonhoodFull */) {
266467
+ spinner.fail("Eligibility failed");
266468
+ throw new Error("Requires Personhood Lite verification");
266469
+ }
266470
+ } else {
266471
+ const trailingDigitCount = countTrailingDigits(label);
266472
+ if (trailingDigitCount === 0 || userStatus === 1 /* ProofOfPersonhoodLite */) {
266473
+ spinner.fail("Eligibility failed");
266474
+ throw new Error(`Personhood Lite cannot register base names
266475
+ This means another user already owns the name without digits`);
266476
+ }
266477
+ }
266478
+ spinner.succeed("Eligibility and price");
266479
+ console.log(source_default.gray(" required: ") + source_default.white(ProofOfPersonhoodStatus[requiredStatus]));
266480
+ console.log(source_default.gray(" user: ") + source_default.white(ProofOfPersonhoodStatus[userStatus]));
266481
+ console.log(source_default.gray(" message: ") + source_default.white(message2));
266482
+ console.log(source_default.gray(" price: ") + source_default.green(`${classificationResult.priceWei > 0n ? formatWeiAsEther(classificationResult.priceWei) : 0n} PAS`));
266483
+ return {
266484
+ priceWei: classificationResult.price ?? classificationResult.priceWei,
266485
+ requiredStatus,
266486
+ userStatus,
266487
+ message: message2,
266488
+ status: requiredStatus,
266489
+ price: classificationResult.price ?? classificationResult.priceWei
266490
+ };
266491
+ } catch (error2) {
266492
+ if (!spinner.isSpinning)
266493
+ throw error2;
266494
+ spinner.fail("Pricing failed");
266495
+ throw error2;
266496
+ }
266497
+ }
266498
+ async function finalizeRegularRegistration(clientWrapper, substrateAddress, signer, registration, priceWei) {
266499
+ const spinner = ora(`Registering ${source_default.cyan(registration.label + ".dot")}`).start();
266500
+ try {
266501
+ const bufferedPaymentWei = priceWei * 110n / 100n;
266502
+ const bufferedPaymentNative = convertWeiToNative(bufferedPaymentWei);
266503
+ console.log(source_default.gray(" oracle: ") + source_default.green(formatWeiAsEther(priceWei) + " PAS"));
266504
+ console.log(source_default.gray(" paying: ") + source_default.green(formatWeiAsEther(bufferedPaymentWei) + " PAS"));
266505
+ const transactionHash = await submitContractTransaction(clientWrapper, CONTRACTS.DOTNS_REGISTRAR_CONTROLLER, bufferedPaymentNative, DOTNS_REGISTRAR_CONTROLLER_ABI, "register", [registration], substrateAddress, signer, spinner, "Registration");
266506
+ console.log(source_default.gray(" tx: ") + source_default.blue(transactionHash));
266507
+ console.log(source_default.gray(" paid: ") + source_default.green(formatWeiAsEther(bufferedPaymentWei) + " PAS"));
266508
+ } catch (error2) {
266509
+ spinner.fail("Registration failed");
266510
+ throw error2;
266511
+ }
266512
+ }
266513
+ async function finalizeGovernanceRegistration(clientWrapper, substrateAddress, signer, registration) {
266514
+ const spinner = ora(`Registering governance name ${source_default.cyan(registration.label + ".dot")}`).start();
266515
+ try {
266516
+ const transactionHash = await submitContractTransaction(clientWrapper, CONTRACTS.DOTNS_REGISTRAR_CONTROLLER, 0n, DOTNS_REGISTRAR_CONTROLLER_ABI, "registerReserved", [registration], substrateAddress, signer, spinner, "Governance Registration");
266517
+ console.log(source_default.gray(" tx: ") + source_default.blue(transactionHash));
266518
+ } catch (error2) {
266519
+ spinner.fail("Governance registration failed");
266520
+ throw error2;
266521
+ }
266522
+ }
266523
+ async function registerSubnode(clientWrapper, substrateAddress, signer, sublabel, parentLabel, ownerAddress) {
266524
+ const fullName = `${sublabel}.${parentLabel}.dot`;
266525
+ const spinner = ora(`Registering subname ${source_default.cyan(fullName)}`).start();
266526
+ try {
266527
+ const parentNode = namehash(`${parentLabel}.dot`);
266528
+ const subnodeRecord = {
266529
+ parentNode,
266530
+ subLabel: sublabel,
266531
+ parentLabel,
266532
+ owner: ownerAddress
266533
+ };
266534
+ const transactionHash = await submitContractTransaction(clientWrapper, CONTRACTS.DOTNS_REGISTRY, 0n, DOTNS_REGISTRY_ABI, "setSubnodeOwner", [subnodeRecord], substrateAddress, signer, spinner, "Subname registration");
266535
+ console.log(source_default.gray(" tx: ") + source_default.blue(transactionHash));
266536
+ return transactionHash;
266537
+ } catch (error2) {
266538
+ spinner.fail("Subname registration failed");
266539
+ throw error2;
266540
+ }
266541
+ }
266542
+ async function verifyDomainOwnership(clientWrapper, originSubstrateAddress, label, expectedOwner) {
266543
+ const spinner = ora("Verifying minted ownership").start();
266544
+ try {
266545
+ const tokenId = computeDomainTokenId(label);
266546
+ const actualOwner = await withTimeout(performContractCall(clientWrapper, originSubstrateAddress, CONTRACTS.DOTNS_REGISTRAR, DOTNS_REGISTRAR_ABI, "ownerOf", [tokenId]), 30000, "ownerOf");
266547
+ if (checksumAddress(actualOwner) !== checksumAddress(expectedOwner)) {
266548
+ spinner.fail("Ownership verification failed");
266549
+ console.log(source_default.gray(" expected: ") + source_default.white(expectedOwner));
266550
+ console.log(source_default.gray(" actual: ") + source_default.white(actualOwner));
266551
+ throw new Error(`Owner mismatch for ${label}.dot`);
266552
+ }
266553
+ spinner.succeed("Ownership verified");
266554
+ console.log(source_default.gray(" owner: ") + source_default.white(actualOwner));
266555
+ return actualOwner;
266556
+ } catch (error2) {
266557
+ spinner.fail("Ownership verification failed");
266558
+ throw error2;
266559
+ }
266560
+ }
266561
+ async function displayDeployedStore(clientWrapper, originSubstrateAddress, ownerAddress) {
266562
+ const spinner = ora("Reading deployed Store address").start();
266563
+ try {
266564
+ const storeAddress = await withTimeout(performContractCall(clientWrapper, originSubstrateAddress, CONTRACTS.STORE_FACTORY, STORE_FACTORY_ABI, "getDeployedStore", [ownerAddress]), 30000, "getDeployedStore");
266565
+ spinner.succeed("Store");
266566
+ console.log(source_default.gray(" store: ") + (storeAddress === zeroAddress ? source_default.yellow("not deployed") : source_default.cyan(storeAddress)));
266567
+ } catch (error2) {
266568
+ spinner.fail("Failed to read Store address");
266569
+ throw error2;
266570
+ }
266571
+ }
266572
+ async function ensureStoreAuthorizations2(clientWrapper, substrateAddress, signer, evmAddress) {
266573
+ const storeAddress = getAddress(await withTimeout(performContractCall(clientWrapper, substrateAddress, CONTRACTS.STORE_FACTORY, STORE_FACTORY_ABI, "getDeployedStore", [evmAddress]), 30000, "getDeployedStore"));
266574
+ if (storeAddress === zeroAddress)
266575
+ return;
266576
+ const spinner = ora("Checking Store authorizations").start();
266577
+ const [controllerAuthorized, registryAuthorized] = await Promise.all([
266578
+ Boolean(await performContractCall(clientWrapper, substrateAddress, storeAddress, STORE_ABI, "isAuthorized", [CONTRACTS.DOTNS_REGISTRAR_CONTROLLER])),
266579
+ Boolean(await performContractCall(clientWrapper, substrateAddress, storeAddress, STORE_ABI, "isAuthorized", [CONTRACTS.DOTNS_REGISTRY]))
266580
+ ]);
266581
+ if (controllerAuthorized && registryAuthorized) {
266582
+ spinner.succeed("Store authorizations verified");
266583
+ console.log(source_default.gray(" controller: ") + source_default.white(CONTRACTS.DOTNS_REGISTRAR_CONTROLLER));
266584
+ console.log(source_default.gray(" registry: ") + source_default.white(CONTRACTS.DOTNS_REGISTRY));
266585
+ return;
266586
+ }
266587
+ spinner.warn("Store authorizations need update");
266588
+ if (!controllerAuthorized) {
266589
+ const controllerSpinner = ora("Authorizing registrar controller as Store writer").start();
266590
+ const tx = await submitContractTransaction(clientWrapper, storeAddress, 0n, STORE_ABI, "authorizeStore", [CONTRACTS.DOTNS_REGISTRAR_CONTROLLER], substrateAddress, signer, controllerSpinner, "Authorize controller");
266591
+ console.log(source_default.gray(" tx: ") + source_default.blue(tx));
266592
+ console.log(source_default.gray(" controller: ") + source_default.white(CONTRACTS.DOTNS_REGISTRAR_CONTROLLER));
266593
+ }
266594
+ if (!registryAuthorized) {
266595
+ const registrySpinner = ora("Authorizing registry as Store writer").start();
266596
+ const tx = await submitContractTransaction(clientWrapper, storeAddress, 0n, STORE_ABI, "authorizeStore", [CONTRACTS.DOTNS_REGISTRY], substrateAddress, signer, registrySpinner, "Authorize registry");
266597
+ console.log(source_default.gray(" tx: ") + source_default.blue(tx));
266598
+ console.log(source_default.gray(" registry: ") + source_default.white(CONTRACTS.DOTNS_REGISTRY));
266599
+ }
266600
+ }
266601
+ async function setUserProofOfPersonhoodStatus(clientWrapper, substrateAddress, signer, ownerAddress, label, status) {
266602
+ const displayName = label ? source_default.cyan(label) : "account";
266603
+ const checkSpinner = ora(`Checking current PoP status for ${displayName}`).start();
266604
+ try {
266605
+ const currentStatus = await getUserProofOfPersonhoodStatus(clientWrapper, substrateAddress, ownerAddress);
266606
+ checkSpinner.succeed("Current PoP status");
266607
+ console.log(source_default.gray(" current: ") + source_default.white(ProofOfPersonhoodStatus[currentStatus]));
266608
+ console.log(source_default.gray(" desired: ") + source_default.white(ProofOfPersonhoodStatus[status]));
266609
+ if (currentStatus === status) {
266610
+ console.log(source_default.yellow(` ↳ Status already set to ${ProofOfPersonhoodStatus[status]}, skipping update`));
266611
+ return;
266612
+ }
266613
+ const updateSpinner = ora(`Setting PoP status for ${displayName} to ${source_default.yellow(ProofOfPersonhoodStatus[status])}`).start();
266614
+ const transactionHash = await submitContractTransaction(clientWrapper, CONTRACTS.DOTNS_RULES, 0n, POP_RULES_ABI, "setUserPopStatus", [status], substrateAddress, signer, updateSpinner, "PoP status update");
266615
+ console.log(source_default.gray(" tx: ") + source_default.blue(transactionHash));
266616
+ } catch (error2) {
266617
+ if (checkSpinner.isSpinning)
266618
+ checkSpinner.fail("Failed to check PoP status");
266619
+ throw error2;
266620
+ }
266621
+ }
266622
+
266623
+ // src/cli/commands/lookup.ts
266624
+ init_env();
266625
+ init_formatting();
266626
+
266627
+ // src/cli/transfer.ts
266628
+ init_source();
266629
+ init_ora();
266630
+ init__esm();
266631
+ init_constants();
266632
+ init_formatting();
266633
+ init_contractInteractions();
266634
+ function toChecksummed(a2) {
266635
+ return checksumAddress(a2);
266636
+ }
266637
+ function isLabelLike(input) {
266638
+ return /^[a-z0-9-]{3,}$/.test(input);
266639
+ }
266640
+ function asDotLabel(input) {
266641
+ const raw = input.trim().toLowerCase();
266642
+ if (raw.endsWith(".dot"))
266643
+ return raw.slice(0, -4);
266644
+ return raw;
266645
+ }
266646
+ async function ownerOfLabel(clientWrapper, originSubstrateAddress, label) {
266647
+ const tokenId = computeDomainTokenId(label);
266648
+ return await performContractCall(clientWrapper, originSubstrateAddress, CONTRACTS.DOTNS_REGISTRAR, DOTNS_REGISTRAR_ABI, "ownerOf", [tokenId]);
266649
+ }
266650
+ async function resolveTransferRecipient(clientWrapper, originSubstrateAddress, recipientIdentifier) {
266651
+ const input = recipientIdentifier.trim();
266652
+ if (isAddress(input))
266653
+ return toChecksummed(input);
266654
+ const label = asDotLabel(input);
266655
+ if (isLabelLike(label)) {
266656
+ const spinner2 = ora(`Resolving ${source_default.cyan(label + ".dot")} to owner address`).start();
266657
+ const ownerAddress = await ownerOfLabel(clientWrapper, originSubstrateAddress, label);
266658
+ if (ownerAddress === zeroAddress) {
266659
+ spinner2.fail(`No owner found for ${source_default.cyan(label + ".dot")} (unregistered)`);
266660
+ throw new Error(`Domain ${label}.dot has no owner`);
266661
+ }
266662
+ spinner2.succeed(`${source_default.cyan(label + ".dot")} → ${source_default.white(toChecksummed(ownerAddress))}`);
266663
+ return toChecksummed(ownerAddress);
266664
+ }
266665
+ const spinner = ora(`Resolving recipient address`).start();
266666
+ const evmAddress = await clientWrapper.getEvmAddress(input);
266667
+ spinner.succeed(`${source_default.white(input)} → ${source_default.white(toChecksummed(evmAddress))}`);
266668
+ return toChecksummed(evmAddress);
266669
+ }
266670
+ async function transferDomain(clientWrapper, originSubstrateAddress, signer, fromAddress, toAddress, label) {
266671
+ const spinner = ora().start();
266672
+ const normLabel = asDotLabel(label);
266673
+ spinner.text = `Validating ${source_default.cyan(normLabel + ".dot")}`;
266674
+ validateDomainLabel(normLabel);
266675
+ spinner.succeed(`Valid: ${source_default.cyan(normLabel + ".dot")}`);
266676
+ spinner.start(`Checking owner of ${source_default.cyan(normLabel + ".dot")}`);
266677
+ const tokenId = computeDomainTokenId(normLabel);
266678
+ const currentOwner = await ownerOfLabel(clientWrapper, originSubstrateAddress, normLabel);
266679
+ const currentOwnerC = toChecksummed(currentOwner);
266680
+ spinner.succeed(`Owner: ${source_default.cyan(normLabel + ".dot")} → ${source_default.white(currentOwnerC)}`);
266681
+ const fromC = toChecksummed(fromAddress);
266682
+ const toC = toChecksummed(toAddress);
266683
+ if (currentOwner === zeroAddress) {
266684
+ spinner.fail(`${source_default.cyan(normLabel + ".dot")} is not registered`);
266685
+ throw new Error(`Cannot transfer: ${normLabel}.dot is not registered`);
266686
+ }
266687
+ if (currentOwnerC !== fromC) {
266688
+ spinner.fail(`Not authorized to transfer ${source_default.cyan(normLabel + ".dot")}`);
266689
+ console.log(source_default.gray(" expected owner: ") + source_default.white(fromC));
266690
+ console.log(source_default.gray(" on-chain owner: ") + source_default.white(currentOwnerC));
266691
+ throw new Error(`Cannot transfer: ${normLabel}.dot owned by ${currentOwnerC}`);
266692
+ }
266693
+ try {
266694
+ const syncSpinner = ora(`Syncing label ${source_default.cyan(normLabel)} with registrar`).start();
266695
+ await submitContractTransaction(clientWrapper, CONTRACTS.DOTNS_REGISTRAR, 0n, DOTNS_REGISTRAR_ABI, "syncLabel", [tokenId, normLabel], originSubstrateAddress, signer, syncSpinner, "Label sync");
266696
+ syncSpinner.succeed("Label synced");
266697
+ } catch (error2) {
266698
+ const errorMessage = formatErrorMessage(error2);
266699
+ if (!errorMessage.includes("LabelAlreadySet")) {
266700
+ console.log(source_default.yellow(" Label sync skipped: " + errorMessage.split(`
266701
+ `)[0]));
266702
+ }
266703
+ }
266704
+ spinner.start(`Submitting transfer ${source_default.cyan(normLabel + ".dot")} → ${source_default.green(toC)}`);
266705
+ const transactionHash = await submitContractTransaction(clientWrapper, CONTRACTS.DOTNS_REGISTRAR, 0n, DOTNS_REGISTRAR_ABI, "transferFrom", [fromC, toC, tokenId], originSubstrateAddress, signer, spinner, "Transfer");
266706
+ spinner.succeed(`Transfer finalized`);
266707
+ console.log(source_default.gray(" tx: ") + source_default.blue(transactionHash));
266708
+ console.log(source_default.gray(" from: ") + source_default.yellow(fromC));
266709
+ console.log(source_default.gray(" to: ") + source_default.green(toC));
266710
+ console.log(source_default.gray(" name: ") + source_default.cyan(normLabel + ".dot"));
266711
+ }
266712
+
266713
+ // src/cli/commands/register.ts
266714
+ init_source();
266715
+ init__esm();
266716
+
266717
+ // src/cli/labels.ts
266718
+ function generateRandomLabel(status) {
266719
+ const alphabet4 = "abcdefghijklmnopqrstuvwxyz";
266720
+ const alphanumeric = "abcdefghijklmnopqrstuvwxyz0123456789";
266721
+ const randomInteger = (maxExclusive) => Math.floor(Math.random() * maxExclusive);
266722
+ const randomCharacters = (characterSet, count2) => {
266723
+ let result = "";
266724
+ for (let i2 = 0;i2 < count2; i2++)
266725
+ result += characterSet[randomInteger(characterSet.length)];
266726
+ return result;
266727
+ };
266728
+ const twoDigits = () => String(randomInteger(99) + 1).padStart(2, "0");
266729
+ const baseEndingWithLetter = (length5) => {
266730
+ if (length5 <= 0)
266731
+ throw new Error("Invalid base length");
266732
+ if (length5 === 1)
266733
+ return randomCharacters(alphabet4, 1);
266734
+ return randomCharacters(alphanumeric, length5 - 1) + randomCharacters(alphabet4, 1);
266735
+ };
266736
+ if (status === 1 /* ProofOfPersonhoodLite */) {
266737
+ const baseLength = 6 + randomInteger(3);
266738
+ return baseEndingWithLetter(baseLength) + twoDigits();
266739
+ }
266740
+ if (status === 0 /* NoStatus */) {
266741
+ const baseLength = 9 + randomInteger(6);
266742
+ return baseEndingWithLetter(baseLength) + twoDigits();
266743
+ }
266744
+ if (status === 2 /* ProofOfPersonhoodFull */) {
266745
+ const baseLength = randomInteger(2) === 0 ? 6 + randomInteger(3) : 9 + randomInteger(6);
266746
+ return baseEndingWithLetter(baseLength);
266747
+ }
266748
+ throw new Error("Cannot auto-generate Reserved names");
266749
+ }
266750
+ function parseProofOfPersonhoodStatus(statusString) {
266751
+ const normalized = statusString.toLowerCase();
266752
+ if (normalized === "none" || normalized === "nostatus")
266753
+ return 0 /* NoStatus */;
266754
+ if (normalized === "lite" || normalized === "poplite")
266755
+ return 1 /* ProofOfPersonhoodLite */;
266756
+ if (normalized === "full" || normalized === "popfull")
266757
+ return 2 /* ProofOfPersonhoodFull */;
266758
+ throw new Error("Invalid status: use none, lite, or full");
266759
+ }
266760
+
266761
+ // src/cli/commands/register.ts
266762
+ function resolveTransferDestination(options2) {
266763
+ const legacy = options2.transferTo;
266764
+ const modern = options2.to;
266765
+ const destination = legacy ?? modern;
266766
+ if (options2.transfer === true && !destination) {
266767
+ throw new Error("Missing transfer destination: use --to <evm|ss58|label>");
266768
+ }
266769
+ if (options2.transfer === false && legacy) {
266770
+ return legacy;
266771
+ }
266772
+ return destination;
266773
+ }
266774
+ function classifyTransferDestination(destination) {
266775
+ if (isAddress(destination))
266776
+ return "evm";
266777
+ if (isValidSubstrateAddress(destination))
266778
+ return "substrate";
266779
+ return "label";
266780
+ }
266781
+ function isValidTransferDestination(destination) {
266782
+ const kind = classifyTransferDestination(destination);
266783
+ if (kind === "evm" || kind === "substrate")
266784
+ return true;
266785
+ const domainLabelPattern = /^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/;
266786
+ return domainLabelPattern.test(destination) && destination.length >= 3 && destination.length <= 63;
266787
+ }
266788
+ async function executeRegistration(options2 = {}) {
266789
+ if (options2.mnemonic && options2.keyUri) {
266790
+ throw new Error("Cannot specify both --mnemonic and --key-uri");
266791
+ }
266792
+ if (options2.parent) {
266793
+ return executeSubnameRegistration(options2);
266794
+ }
266795
+ const transferDestination = resolveTransferDestination(options2);
266796
+ if (transferDestination && !isValidTransferDestination(transferDestination)) {
266797
+ throw new Error("Invalid transfer destination: must be valid EVM address, Substrate address, or domain label");
266798
+ }
266799
+ const context = await prepareAssetHubContext(options2);
266800
+ const { clientWrapper, substrateAddress, signer, evmAddress } = context;
266801
+ const statusWasProvided = options2.__statusProvided === true;
266802
+ const popStatusConfig = statusWasProvided ? { mode: "set", status: parseProofOfPersonhoodStatus(options2.status ?? "none") } : { mode: "unchanged" };
266803
+ const label = options2.name ?? generateRandomLabel(popStatusConfig.mode === "set" ? popStatusConfig.status : 0 /* NoStatus */);
266804
+ console.log(source_default.gray(" Mode: ") + source_default.yellow(options2.governance ? "Governance registration" : "Regular registration"));
266805
+ console.log(source_default.gray(" Label: ") + source_default.cyan(label));
266806
+ console.log(source_default.gray(" Domain: ") + source_default.cyan(label + ".dot"));
266807
+ console.log(source_default.gray(" Transfer: ") + (transferDestination ? source_default.green("post-mint") : source_default.gray("none")));
266808
+ await step("Ensuring account mapped", async () => clientWrapper.ensureAccountMapped(substrateAddress, signer));
266809
+ if (options2.governance) {
266810
+ await executeGovernanceRegistration(clientWrapper, substrateAddress, signer, evmAddress, label, transferDestination, options2.commitmentBuffer);
266811
+ } else {
266812
+ await executeRegularRegistration(clientWrapper, substrateAddress, signer, evmAddress, label, popStatusConfig, options2.reverse ?? false, transferDestination, options2.commitmentBuffer);
266813
+ }
266814
+ console.log(`
266815
+ ${source_default.bold.green("═══════════════════════════════════════")}`);
266816
+ console.log(`${source_default.bold.green(" ✓ Operation Complete ")}`);
266817
+ console.log(`${source_default.bold.green("═══════════════════════════════════════")}
266818
+ `);
266819
+ console.log(source_default.gray(" Domain: ") + source_default.cyan(label + ".dot"));
266820
+ return { ok: true, label, domain: `${label}.dot`, owner: evmAddress };
266821
+ }
266822
+ async function executeSubnameRegistration(options2) {
266823
+ if (!options2.name) {
266824
+ throw new Error("Missing subname: use --name <sublabel>");
266825
+ }
266826
+ if (!options2.parent) {
266827
+ throw new Error("Missing parent: use --parent <parentlabel>");
266828
+ }
266829
+ const context = await prepareAssetHubContext(options2);
266830
+ const { clientWrapper, substrateAddress, signer, evmAddress } = context;
266831
+ const sublabel = options2.name;
266832
+ const parentLabel = options2.parent;
266833
+ const ownerAddress = options2.owner ?? evmAddress;
266834
+ const fullName = `${sublabel}.${parentLabel}.dot`;
266835
+ console.log(source_default.bold(`
266836
+ ▶ Subname Registration
266837
+ `));
266838
+ console.log(source_default.gray(" Subname: ") + source_default.cyan(fullName));
266839
+ console.log(source_default.gray(" Parent: ") + source_default.white(`${parentLabel}.dot`));
266840
+ console.log(source_default.gray(" Owner: ") + source_default.white(ownerAddress));
266841
+ await step("Ensuring account mapped", async () => clientWrapper.ensureAccountMapped(substrateAddress, signer));
266842
+ await registerSubnode(clientWrapper, substrateAddress, signer, sublabel, parentLabel, ownerAddress);
266843
+ console.log(`
266844
+ ${source_default.bold.green("═══════════════════════════════════════")}`);
266845
+ console.log(`${source_default.bold.green(" ✓ Subname Registered ")}`);
266846
+ console.log(`${source_default.bold.green("═══════════════════════════════════════")}
266847
+ `);
266848
+ console.log(source_default.gray(" Domain: ") + source_default.cyan(fullName));
266849
+ return {
266850
+ ok: true,
266851
+ label: sublabel,
266852
+ parent: parentLabel,
266853
+ domain: fullName,
266854
+ owner: ownerAddress
266855
+ };
266856
+ }
266857
+ async function executeGovernanceRegistration(clientWrapper, substrateAddress, signer, evmAddress, label, transferDestination, commitmentBuffer) {
266858
+ console.log(source_default.bold(`
266859
+ \uD83C\uDFDB Governance registration (commit-reveal)
266860
+ `));
266861
+ validateGovernanceLabel(label);
266862
+ const classification = await step("Classifying name", async () => classifyDomainName(clientWrapper, substrateAddress, label));
266863
+ if (classification.requiredStatus !== 3 /* Reserved */) {
266864
+ throw new Error(`Governance name must classify as Reserved; got ${ProofOfPersonhoodStatus[classification.requiredStatus]}`);
266865
+ }
266866
+ await step("Checking availability", async () => ensureDomainNotRegistered(clientWrapper, substrateAddress, label));
266867
+ const { commitment, registration } = await step("Generating commitment", async () => generateCommitment(clientWrapper, substrateAddress, label, evmAddress, true));
266868
+ await step("Submitting commitment", async () => submitCommitment(clientWrapper, substrateAddress, signer, commitment));
266869
+ await step("Waiting commitment age", async () => waitForMinimumCommitmentAge(clientWrapper, substrateAddress, commitment, commitmentBuffer));
266870
+ await step("Finalizing registration", async () => finalizeGovernanceRegistration(clientWrapper, substrateAddress, signer, registration));
266871
+ await step("Verifying ownership", async () => verifyDomainOwnership(clientWrapper, substrateAddress, label, evmAddress));
266872
+ await step("Displaying store", async () => displayDeployedStore(clientWrapper, substrateAddress, evmAddress));
266873
+ await step("Ensuring store authorizations", async () => ensureStoreAuthorizations2(clientWrapper, substrateAddress, signer, evmAddress));
266874
+ if (transferDestination) {
266875
+ const recipient = await step("Resolving recipient", async () => resolveTransferRecipient(clientWrapper, substrateAddress, transferDestination));
266876
+ await step("Transferring domain", async () => transferDomain(clientWrapper, substrateAddress, signer, evmAddress, recipient, label));
266877
+ await step("Verifying ownership", async () => verifyDomainOwnership(clientWrapper, substrateAddress, label, recipient));
266878
+ }
266879
+ }
266880
+ async function executeRegularRegistration(clientWrapper, substrateAddress, signer, evmAddress, label, popStatusConfig, enableReverseRecord, transferDestination, commitmentBuffer) {
266881
+ console.log(source_default.bold(`
266882
+ \uD83E\uDDFE Regular registration (commit-reveal)
266883
+ `));
266884
+ validateDomainLabel(label);
266885
+ const classification = await step("Classifying name", async () => classifyDomainName(clientWrapper, substrateAddress, label));
266886
+ if (popStatusConfig.mode === "set") {
266887
+ await step("Setting PoP status", async () => setUserProofOfPersonhoodStatus(clientWrapper, substrateAddress, signer, evmAddress, label, popStatusConfig.status));
266888
+ }
266889
+ await step("Checking availability", async () => ensureDomainNotRegistered(clientWrapper, substrateAddress, label));
266890
+ const { commitment, registration } = await step("Generating commitment", async () => generateCommitment(clientWrapper, substrateAddress, label, evmAddress, enableReverseRecord));
266891
+ await step("Submitting commitment", async () => submitCommitment(clientWrapper, substrateAddress, signer, commitment));
266892
+ await step("Waiting commitment age", async () => waitForMinimumCommitmentAge(clientWrapper, substrateAddress, commitment, commitmentBuffer));
266893
+ const pricing = await step("Pricing and eligibility", async () => getPriceAndValidateEligibility(clientWrapper, substrateAddress, label, evmAddress));
266894
+ await step("Finalizing registration", async () => finalizeRegularRegistration(clientWrapper, substrateAddress, signer, registration, pricing.priceWei));
266895
+ await step("Verifying ownership", async () => verifyDomainOwnership(clientWrapper, substrateAddress, label, evmAddress));
266896
+ await step("Displaying store", async () => displayDeployedStore(clientWrapper, substrateAddress, evmAddress));
266897
+ await step("Ensuring store authorizations", async () => ensureStoreAuthorizations2(clientWrapper, substrateAddress, signer, evmAddress));
266898
+ if (transferDestination) {
266899
+ const recipient = await step("Resolving recipient", async () => resolveTransferRecipient(clientWrapper, substrateAddress, transferDestination));
266900
+ await step("Transferring domain", async () => transferDomain(clientWrapper, substrateAddress, signer, evmAddress, recipient, label));
266901
+ await step("Verifying ownership", async () => verifyDomainOwnership(clientWrapper, substrateAddress, label, recipient));
266902
+ }
266903
+ }
266904
+
266905
+ // src/cli/commands/lookup.ts
266906
+ function createClientWrapper(rpc) {
266907
+ const client = createClient3(getWsProvider(rpc)).getTypedApi(paseo_default);
266908
+ return new ReviveClientWrapper(client);
266909
+ }
266910
+ function hasAnyAuthHint(opts) {
266911
+ return opts.mnemonic != null && String(opts.mnemonic).length > 0 || opts.keyUri != null && String(opts.keyUri).length > 0 || opts.keystorePath != null && String(opts.keystorePath).length > 0 || opts.account != null && String(opts.account).length > 0 || opts.password != null && String(opts.password).length > 0;
266912
+ }
266913
+ function withReadOnlyPasswordFallback(opts) {
266914
+ const passwordEnv = process.env.DOTNS_KEYSTORE_PASSWORD;
266915
+ if ((opts.password == null || String(opts.password).length === 0) && passwordEnv && passwordEnv.length > 0) {
266916
+ return { ...opts, password: passwordEnv };
266917
+ }
266918
+ return opts;
266919
+ }
266920
+ async function prepareReadOnlyContext(options2) {
266921
+ const rpc = resolveRpc(options2.rpc);
266922
+ const clientWrapper = await step(`Connecting RPC ${rpc}`, async () => createClientWrapper(rpc));
266923
+ const auth = await step("Resolving read-only account", async () => {
266924
+ if (hasAnyAuthHint(options2)) {
266925
+ const merged = withReadOnlyPasswordFallback(options2);
266926
+ const resolved2 = await resolveAuthSource(merged);
266927
+ return {
266928
+ source: resolved2.source,
266929
+ isKeyUri: resolved2.isKeyUri,
266930
+ resolvedFrom: resolved2.resolvedFrom,
266931
+ account: resolved2.account
266932
+ };
266933
+ }
266934
+ const resolved = await resolveAuthSourceReadOnly();
266935
+ return {
266936
+ source: resolved.source,
266937
+ isKeyUri: resolved.isKeyUri,
266938
+ resolvedFrom: resolved.resolvedFrom,
266939
+ account: resolved.account
266940
+ };
266941
+ });
266942
+ const keypair3 = await step("Loading keypair", async () => createAccountFromSource(auth.source, auth.isKeyUri));
266943
+ const evmAddress = await step("Resolving EVM address", async () => clientWrapper.getEvmAddress(keypair3.address));
266944
+ console.log(source_default.gray(`
266945
+ RPC: `) + source_default.white(rpc));
266946
+ console.log(source_default.gray(" Account: ") + source_default.white(keypair3.address));
266947
+ return { clientWrapper, account: { address: keypair3.address }, rpc, evmAddress };
266948
+ }
266949
+ async function resolveRecipientByKind(clientWrapper, substrateAddress, destination) {
266950
+ const kind = classifyTransferDestination(destination);
266951
+ switch (kind) {
266952
+ case "evm":
266953
+ return destination;
266954
+ case "substrate":
266955
+ return clientWrapper.getEvmAddress(destination);
266956
+ case "label":
266957
+ return resolveTransferRecipient(clientWrapper, substrateAddress, destination);
266958
+ }
266959
+ }
266960
+ function attachLookupCommands(root) {
266961
+ const lookupCommand = root.command("lookup").description("Lookup domain information");
266962
+ addAuthOptions(lookupCommand);
266963
+ const lookupNameCommand = lookupCommand.command("name [label]").description("Lookup comprehensive domain information").option("-n, --name <label>", "Domain label to lookup (alternative to positional argument)").option("--json", "Output result as JSON (suppresses all other output)", false);
266964
+ addAuthOptions(lookupNameCommand).action(async (positionalLabel, options2, cmd) => {
266965
+ try {
266966
+ const merged = {
266967
+ ...options2 ?? {},
266968
+ ...getAuthOptions(cmd),
266969
+ __positionalLabel: positionalLabel
266970
+ };
266971
+ const label = merged.name || merged.__positionalLabel;
266972
+ const jsonOutput = getJsonFlag(cmd);
266973
+ if (!label) {
266974
+ if (jsonOutput) {
266975
+ console.error(JSON.stringify({ error: "Domain label is required" }));
266976
+ process.exit(1);
266762
266977
  }
266978
+ console.error(source_default.red(`
266979
+ Error: Domain label is required
266980
+ `));
266981
+ console.log(source_default.gray("Usage: dotns lookup name <label>"));
266982
+ console.log(source_default.gray(" or: dotns lookup name --name <label>"));
266983
+ console.log(source_default.gray(` or: dotns lookup --name <label>
266984
+ `));
266985
+ process.exit(1);
266763
266986
  }
266764
- if (mergedOptions.history !== false) {
266765
- await addUploadRecord({
266766
- cid,
266767
- path: resolvedPath,
266768
- type: isDirectory ? "directory" : "file",
266769
- size: uploadSize
266770
- });
266987
+ const { clientWrapper, account } = await maybeQuiet(jsonOutput, () => prepareReadOnlyContext(merged));
266988
+ if (!jsonOutput)
266989
+ console.log(source_default.bold(`
266990
+ Domain Lookup
266991
+ `));
266992
+ const result = await maybeQuiet(jsonOutput, () => performDomainLookup(label, account.address, clientWrapper));
266993
+ if (jsonOutput) {
266994
+ console.log(JSON.stringify(result));
266995
+ } else {
266996
+ console.log(source_default.green(`
266997
+ ✓ Complete
266998
+ `));
266771
266999
  }
266772
267000
  process.exit(0);
266773
267001
  } catch (error2) {
266774
267002
  const errorMessage = formatErrorMessage(error2);
266775
- const jsonOutput = getJsonFlag(command);
267003
+ const jsonOutput = getJsonFlag(cmd);
266776
267004
  if (jsonOutput) {
266777
- writeBulletinJsonError(errorMessage);
267005
+ console.error(JSON.stringify({ error: errorMessage }));
267006
+ process.exit(1);
266778
267007
  }
266779
267008
  console.error(source_default.red(`
266780
267009
  ✗ Error: ${errorMessage}
@@ -266782,61 +267011,35 @@ function attachBulletinCommands(root) {
266782
267011
  process.exit(1);
266783
267012
  }
266784
267013
  });
266785
- const statusCommand = addReporterOption(bulletinCommand.command("status [address]").description("Check authorization status for an account on Bulletin").option("--bulletin-rpc <wsUrl>", "Bulletin WebSocket RPC endpoint", DEFAULT_BULLETIN_RPC).option("--json", "Write machine-readable JSON to stdout", false));
266786
- addAuthOptions(statusCommand).action(async (positionalAddress, options2, command) => {
266787
- try {
266788
- const mergedOptions = getMergedOptions2(command, options2);
266789
- const jsonOutput = getJsonFlag(command);
266790
- const reporterMode = resolveReporterMode(mergedOptions.reporter);
266791
- const reporter = createCliReporter(mergedOptions.reporter);
266792
- const bulletinRpc = String(mergedOptions.bulletinRpc || DEFAULT_BULLETIN_RPC);
266793
- const targetAddress = await resolveTargetAddress(positionalAddress, mergedOptions, reporterMode);
266794
- const statusTask = reporter.task("Checking authorization");
266795
- const authStatus = await checkAuthorization(bulletinRpc, targetAddress);
266796
- statusTask.succeed("Authorization status read");
266797
- if (jsonOutput) {
266798
- writeBulletinJson({
266799
- address: targetAddress,
266800
- rpc: bulletinRpc,
266801
- authorized: authStatus.authorized,
266802
- expired: authStatus.expired ?? false,
266803
- transactions: authStatus.transactions ?? 0,
266804
- bytes: (authStatus.bytes ?? BigInt(0)).toString(),
266805
- expiresAt: expirationToISOString(authStatus.currentBlock, authStatus.expiration)
266806
- });
266807
- } else {
266808
- console.log(source_default.blue(`
266809
- ▶ Bulletin Authorization Status`));
266810
- console.log(source_default.gray(" account: ") + source_default.cyan(targetAddress));
266811
- console.log(source_default.gray(" rpc: ") + source_default.white(bulletinRpc));
266812
- if (!authStatus.authorized) {
266813
- console.log(source_default.gray(" status: ") + source_default.red("not authorized"));
266814
- console.log(source_default.gray(`
266815
- Authorize with: dotns bulletin authorize ` + targetAddress + `
267014
+ lookupCommand.option("-n, --name <label>", "Domain label to lookup").option("--json", "Output result as JSON (suppresses all other output)", false).action(async (options2, cmd) => {
267015
+ const subcommand = cmd.args?.[0];
267016
+ if (["name", "owner-of", "oo", "transfer"].includes(subcommand))
267017
+ return;
267018
+ if (options2.name) {
267019
+ try {
267020
+ const merged = { ...options2 ?? {}, ...getAuthOptions(cmd) };
267021
+ const jsonOutput = getJsonFlag(cmd);
267022
+ const { clientWrapper, account } = await maybeQuiet(jsonOutput, () => prepareReadOnlyContext(merged));
267023
+ if (!jsonOutput)
267024
+ console.log(source_default.bold(`
267025
+ Domain Lookup
266816
267026
  `));
267027
+ const result = await maybeQuiet(jsonOutput, () => performDomainLookup(merged.name, account.address, clientWrapper));
267028
+ if (jsonOutput) {
267029
+ console.log(JSON.stringify(result));
266817
267030
  } else {
266818
- const isExpired = authStatus.expired;
266819
- const dateDisplay = formatExpirationDisplay(authStatus.currentBlock, authStatus.expiration);
266820
- console.log(source_default.gray(" status: ") + (isExpired ? source_default.red("expired") : source_default.green("authorized")));
266821
- console.log(source_default.gray(isExpired ? " expired: " : " expires: ") + (isExpired ? source_default.red(dateDisplay) : source_default.white(dateDisplay)));
266822
- console.log(source_default.gray(" transactions: ") + source_default.white((authStatus.transactions ?? 0).toLocaleString()));
266823
- console.log(source_default.gray(" bytes: ") + source_default.white(formatBytes(authStatus.bytes ?? BigInt(0))));
266824
- if (isExpired) {
266825
- console.log(source_default.gray(`
266826
- Re-authorize with: dotns bulletin authorize ` + targetAddress + `
267031
+ console.log(source_default.green(`
267032
+ Complete
266827
267033
  `));
266828
- } else {
266829
- console.log();
266830
- }
266831
267034
  }
266832
- }
266833
- process.exit(0);
266834
- } catch (error2) {
266835
- const errorMessage = formatErrorMessage(error2);
266836
- const jsonOutput = getJsonFlag(command);
266837
- if (jsonOutput) {
266838
- writeBulletinJsonError(errorMessage);
266839
- } else {
267035
+ process.exit(0);
267036
+ } catch (error2) {
267037
+ const errorMessage = formatErrorMessage(error2);
267038
+ const jsonOutput = getJsonFlag(cmd);
267039
+ if (jsonOutput) {
267040
+ console.error(JSON.stringify({ error: errorMessage }));
267041
+ process.exit(1);
267042
+ }
266840
267043
  console.error(source_default.red(`
266841
267044
  ✗ Error: ${errorMessage}
266842
267045
  `));
@@ -266844,336 +267047,143 @@ function attachBulletinCommands(root) {
266844
267047
  }
266845
267048
  }
266846
267049
  });
266847
- const historyCommand = addReporterOption(bulletinCommand.command("history").alias("list").description("List all uploaded CIDs").option("--json", "Write machine-readable JSON to stdout", false));
266848
- historyCommand.action(async (_options, command) => {
267050
+ const ownerOfCommand = lookupCommand.command("owner-of <label>").description("Show whether a name is registered and who owns it").alias("oo").option("--json", "Output result as JSON (suppresses all other output)", false);
267051
+ addAuthOptions(ownerOfCommand).action(async (label, options2, cmd) => {
266849
267052
  try {
266850
- const jsonOutput = getJsonFlag(command);
266851
- const history = await readHistory();
266852
- if (jsonOutput) {
266853
- writeBulletinJson(history);
266854
- process.exit(0);
266855
- }
266856
- if (history.length === 0) {
266857
- console.log(source_default.yellow(`
266858
- No uploads in history.
266859
- `));
266860
- console.log(source_default.gray(` Upload files with: dotns bulletin upload <path>
266861
- `));
266862
- process.exit(0);
266863
- }
266864
- console.log(source_default.blue(`
266865
- ▶ Upload History
266866
- `));
266867
- console.log(source_default.gray(` ${history.length} upload(s) found
267053
+ const merged = { ...options2 ?? {}, ...getAuthOptions(cmd) };
267054
+ const jsonOutput = getJsonFlag(cmd);
267055
+ const { clientWrapper, account } = await maybeQuiet(jsonOutput, () => prepareReadOnlyContext(merged));
267056
+ if (!jsonOutput)
267057
+ console.log(source_default.bold(`
267058
+ ▶ Ownership Lookup
266868
267059
  `));
266869
- history.forEach((record2, index) => {
266870
- const num = (index + 1).toString().padStart(2, " ");
266871
- console.log(source_default.yellow(` ${num}.`) + source_default.white(` ${formatRecordTimestamp(record2)}`));
266872
- console.log(source_default.gray(" cid: ") + source_default.cyan(record2.cid));
266873
- console.log(source_default.gray(" path: ") + source_default.white(record2.path));
266874
- console.log(source_default.gray(" type: ") + source_default.white(record2.type));
266875
- if (record2.size > 0) {
266876
- console.log(source_default.gray(" size: ") + source_default.white(formatBytes(record2.size)));
266877
- }
266878
- console.log(source_default.gray(" preview: ") + source_default.blue(getPreviewUrl(record2)));
266879
- console.log();
266880
- });
266881
- process.exit(0);
266882
- } catch (error2) {
266883
- const jsonOutput = getJsonFlag(command);
267060
+ const result = await maybeQuiet(jsonOutput, () => performOwnerOfLookup(label, account.address, clientWrapper));
266884
267061
  if (jsonOutput) {
266885
- writeBulletinJsonError(error2);
266886
- }
266887
- console.error(source_default.red(`
266888
- ✗ Error: ${formatErrorMessage(error2)}
266889
- `));
266890
- process.exit(1);
266891
- }
266892
- });
266893
- bulletinCommand.command("history:remove <cid>").description("Remove an upload from history by CID").action(async (cid) => {
266894
- try {
266895
- const removed = await removeUploadRecord(cid);
266896
- if (removed) {
266897
- console.log(source_default.green(`
266898
- ✓ Removed ${cid} from history
266899
- `));
267062
+ console.log(JSON.stringify(result));
266900
267063
  } else {
266901
- console.log(source_default.yellow(`
266902
- CID not found in history: ${cid}
267064
+ console.log(source_default.green(`
267065
+ Complete
266903
267066
  `));
266904
267067
  }
266905
267068
  process.exit(0);
266906
267069
  } catch (error2) {
267070
+ const errorMessage = formatErrorMessage(error2);
267071
+ const jsonOutput = getJsonFlag(cmd);
267072
+ if (jsonOutput) {
267073
+ console.error(JSON.stringify({ error: errorMessage }));
267074
+ process.exit(1);
267075
+ }
266907
267076
  console.error(source_default.red(`
266908
- ✗ Error: ${formatErrorMessage(error2)}
266909
- `));
266910
- process.exit(1);
266911
- }
266912
- });
266913
- addReporterOption(bulletinCommand.command("history:clear").description("Clear all upload history")).action(async () => {
266914
- try {
266915
- const count2 = await clearHistory();
266916
- const historyPath = getHistoryPath();
266917
- console.log(source_default.green(`
266918
- ✓ Cleared ${count2} upload(s) from history`));
266919
- console.log(source_default.gray(` ${historyPath}
266920
- `));
266921
- process.exit(0);
266922
- } catch (error2) {
266923
- console.error(source_default.red(`
266924
- ✗ Error: ${formatErrorMessage(error2)}
267077
+ ✗ Error: ${errorMessage}
266925
267078
  `));
266926
267079
  process.exit(1);
266927
267080
  }
266928
267081
  });
266929
- addReporterOption(bulletinCommand.command("verify <cid>").description("Verify a CID is resolvable via IPFS gateways").option("--json", "Write machine-readable JSON to stdout", false)).action(async (cid, options2, command) => {
266930
- const mergedOptions = getMergedOptions2(command, options2);
266931
- const jsonOutput = getJsonFlag(command) || Boolean(options2.json);
266932
- const reporter = createCliReporter(mergedOptions.reporter);
266933
- const p2pTask = reporter.task("Connecting to Bulletin P2P");
267082
+ const transferCommand = lookupCommand.command("transfer [label]").description("Transfer domain ownership to another address or label").requiredOption("-d, --destination <destination>", "Transfer destination (EVM address, SS58 address, or domain label)").option("--json", "Output result as JSON (suppresses all other output)", false);
267083
+ addAuthOptions(transferCommand).action(async (positionalLabel, options2, cmd) => {
266934
267084
  try {
266935
- if (!jsonOutput) {
266936
- console.log(source_default.blue(`
266937
- Verifying CID`));
266938
- console.log(source_default.gray(" cid: ") + source_default.cyan(cid));
267085
+ const merged = { ...options2 ?? {}, ...getAuthOptions(cmd) };
267086
+ const jsonOutput = getJsonFlag(cmd);
267087
+ const label = positionalLabel || merged.name || cmd.parent?.opts()?.name;
267088
+ if (!label) {
267089
+ throw new Error("Domain label is required: dotns lookup transfer <label> -d <destination>");
266939
267090
  }
266940
- const p2pResult = await verifyCidViaP2P(cid);
266941
- if (p2pResult.resolvable) {
266942
- p2pTask.succeed("CID verified via P2P (bitswap)");
266943
- if (jsonOutput) {
266944
- writeBulletinJson({
266945
- cid,
266946
- resolvable: true,
266947
- method: "p2p",
266948
- gateways: [{ gateway: "p2p/bitswap", resolvable: true }]
266949
- });
266950
- } else {
266951
- console.log(source_default.gray(" ✓ ") + source_default.white("p2p/bitswap"));
266952
- console.log();
266953
- }
266954
- cleanupHeliaAndExit(0);
267091
+ const destination = merged.destination;
267092
+ if (!isValidTransferDestination(destination)) {
267093
+ throw new Error("Invalid transfer destination: must be a valid EVM address, Substrate address, or domain label");
266955
267094
  }
266956
- p2pTask.warn("P2P verification failed, falling back to gateways");
266957
- const gatewayTask = reporter.task("Checking IPFS gateways");
266958
- const results = await verifyCidWithMultipleGateways(cid);
266959
- const resolvableGateways = [];
266960
- const failedGateways = [];
266961
- for (const [gateway, result] of results) {
266962
- if (result.resolvable) {
266963
- resolvableGateways.push(gateway);
266964
- } else {
266965
- failedGateways.push(gateway);
266966
- }
267095
+ const context = await maybeQuiet(jsonOutput, () => prepareAssetHubContext(merged));
267096
+ const { clientWrapper, substrateAddress, signer, evmAddress } = context;
267097
+ if (!jsonOutput) {
267098
+ console.log(source_default.bold(`
267099
+ Transfer
267100
+ `));
267101
+ console.log(source_default.gray(" domain: ") + source_default.cyan(`${label}.dot`));
267102
+ console.log(source_default.gray(" to: ") + source_default.white(destination));
266967
267103
  }
267104
+ await maybeQuiet(jsonOutput, () => step("Ensuring account mapped", async () => clientWrapper.ensureAccountMapped(substrateAddress, signer)));
267105
+ const recipient = await maybeQuiet(jsonOutput, () => step("Resolving recipient", async () => resolveRecipientByKind(clientWrapper, substrateAddress, destination)));
267106
+ await maybeQuiet(jsonOutput, () => step("Transferring domain", async () => transferDomain(clientWrapper, substrateAddress, signer, evmAddress, recipient, label)));
267107
+ await maybeQuiet(jsonOutput, () => step("Verifying ownership", async () => verifyDomainOwnership(clientWrapper, substrateAddress, label, recipient)));
266968
267108
  if (jsonOutput) {
266969
- const entries = Array.from(results.entries()).map(([gateway, result]) => ({
266970
- gateway,
266971
- resolvable: result.resolvable,
266972
- statusCode: result.statusCode,
266973
- errorMessage: result.errorMessage
267109
+ console.log(JSON.stringify({
267110
+ label,
267111
+ domain: `${label}.dot`,
267112
+ destination,
267113
+ recipient,
267114
+ transferred: true
266974
267115
  }));
266975
- writeBulletinJson({
266976
- cid,
266977
- resolvable: resolvableGateways.length > 0,
266978
- method: resolvableGateways.length > 0 ? "gateway" : "none",
266979
- gateways: entries
266980
- });
266981
- cleanupHeliaAndExit(resolvableGateways.length > 0 ? 0 : 1);
266982
- }
266983
- if (resolvableGateways.length > 0) {
266984
- gatewayTask.succeed(`CID resolvable on ${resolvableGateways.length} gateway(s)`);
266985
- for (const gw of resolvableGateways) {
266986
- console.log(source_default.gray(" ✓ ") + source_default.white(gw));
266987
- }
266988
267116
  } else {
266989
- gatewayTask.fail("CID not resolvable on any gateway");
266990
- }
266991
- if (failedGateways.length > 0 && resolvableGateways.length > 0) {
266992
- for (const gw of failedGateways) {
266993
- console.log(source_default.gray(" ✗ ") + source_default.dim(gw));
266994
- }
267117
+ console.log(source_default.green(`
267118
+ ✓ Complete
267119
+ `));
266995
267120
  }
266996
- console.log();
266997
- cleanupHeliaAndExit(resolvableGateways.length > 0 ? 0 : 1);
267121
+ process.exit(0);
266998
267122
  } catch (error2) {
266999
- p2pTask.fail("Verification failed");
267000
267123
  const errorMessage = formatErrorMessage(error2);
267124
+ const jsonOutput = getJsonFlag(cmd);
267001
267125
  if (jsonOutput) {
267002
- writeBulletinJsonError(errorMessage);
267003
- } else {
267004
- console.error(source_default.red(`
267126
+ console.error(JSON.stringify({ error: errorMessage }));
267127
+ process.exit(1);
267128
+ }
267129
+ console.error(source_default.red(`
267005
267130
  ✗ Error: ${errorMessage}
267006
267131
  `));
267007
- cleanupHeliaAndExit(1);
267008
- }
267132
+ process.exit(1);
267009
267133
  }
267010
267134
  });
267011
267135
  }
267012
267136
 
267013
267137
  // src/cli/commands/content.ts
267014
- init_source();
267015
-
267016
- // src/commands/contentHash.ts
267017
- init_source();
267018
- init__esm();
267019
- init_constants();
267020
- init_contractInteractions();
267021
-
267022
- // src/commands/resolverAuth.ts
267023
- init__esm();
267024
- init_constants();
267025
- init_contractInteractions();
267026
- async function getResolverNodeInfo(clientWrapper, originSubstrateAddress, namehashNode) {
267027
- const exists2 = await performContractCall(clientWrapper, originSubstrateAddress, CONTRACTS.DOTNS_REGISTRY, DOTNS_REGISTRY_ABI, "recordExists", [namehashNode]);
267028
- const owner = await performContractCall(clientWrapper, originSubstrateAddress, CONTRACTS.DOTNS_REGISTRY, DOTNS_REGISTRY_ABI, "owner", [namehashNode]);
267029
- const caller = await clientWrapper.getEvmAddress(originSubstrateAddress);
267030
- return { exists: exists2 && owner !== zeroAddress, owner, caller };
267031
- }
267032
- async function requireResolverAuthorization(clientWrapper, originSubstrateAddress, owner, caller) {
267033
- const isOwner = checksumAddress(owner) === checksumAddress(caller);
267034
- const isApproved = await performContractCall(clientWrapper, originSubstrateAddress, CONTRACTS.DOTNS_CONTENT_RESOLVER, DOTNS_CONTENT_RESOLVER_ABI, "isApprovedForAll", [owner, caller]);
267035
- if (!isOwner && !isApproved) {
267036
- throw new Error(`You do not own this domain. Owner is ${owner}, but you are ${caller}`);
267037
- }
267038
- }
267039
-
267040
- // src/commands/contentHash.ts
267041
- function decodeContenthashToCid(contenthash) {
267042
- if (contenthash === "0x" || contenthash === "0x0" || contenthash.length < 6) {
267043
- return "No CID set";
267044
- }
267045
- const cid = decodeIpfsContenthash(contenthash);
267046
- return cid ?? `Unable to decode: ${contenthash}`;
267047
- }
267048
- function encodeCidToContenthash(cidString) {
267049
- const encoded = encodeIpfsContenthash(cidString);
267050
- return `0x${encoded}`;
267051
- }
267052
- async function viewDomainContentHash(clientWrapper, originSubstrateAddress, label, spinner) {
267053
- const namehashNode = namehash(`${label}.dot`);
267054
- spinner.start("Querying registry");
267055
- const recordExists = await performContractCall(clientWrapper, originSubstrateAddress, CONTRACTS.DOTNS_REGISTRY, DOTNS_REGISTRY_ABI, "recordExists", [namehashNode]);
267056
- const ownerAddress = await performContractCall(clientWrapper, originSubstrateAddress, CONTRACTS.DOTNS_REGISTRY, DOTNS_REGISTRY_ABI, "owner", [namehashNode]);
267057
- spinner.succeed("Registry read");
267058
- console.log(source_default.gray(" registry: ") + source_default.white(CONTRACTS.DOTNS_REGISTRY));
267059
- console.log(source_default.gray(" domain: ") + source_default.cyan(`${label}.dot`));
267060
- console.log(source_default.gray(" node: ") + source_default.white(namehashNode));
267061
- console.log(source_default.gray(" exists: ") + source_default.white(String(recordExists)));
267062
- console.log(source_default.gray(" owner: ") + source_default.white(ownerAddress));
267063
- console.log();
267064
- if (!recordExists || ownerAddress === zeroAddress) {
267065
- console.log(source_default.yellow(" status: Domain not registered"));
267066
- return;
267067
- }
267068
- const contentHashBytes = await performContractCall(clientWrapper, originSubstrateAddress, CONTRACTS.DOTNS_CONTENT_RESOLVER, DOTNS_CONTENT_RESOLVER_ABI, "contenthash", [namehashNode]);
267069
- const decodedCid = decodeContenthashToCid(contentHashBytes);
267070
- console.log(source_default.gray(" resolver: ") + source_default.white(CONTRACTS.DOTNS_CONTENT_RESOLVER));
267071
- console.log(source_default.gray(" contenthash: ") + source_default.white(contentHashBytes));
267072
- console.log(source_default.gray(" cid: ") + source_default.cyan(decodedCid));
267073
- }
267074
- async function setDomainContentHash(clientWrapper, originSubstrateAddress, signer, label, contentId, spinner) {
267075
- const namehashNode = namehash(`${label}.dot`);
267076
- console.log(source_default.gray(" domain: ") + source_default.cyan(`${label}.dot`));
267077
- console.log(source_default.gray(" node: ") + source_default.white(namehashNode));
267078
- console.log();
267079
- const { exists: exists2, owner, caller } = await getResolverNodeInfo(clientWrapper, originSubstrateAddress, namehashNode);
267080
- console.log(source_default.gray(" exists: ") + source_default.white(String(exists2)));
267081
- console.log(source_default.gray(" owner: ") + source_default.white(owner));
267082
- console.log(source_default.gray(" caller: ") + source_default.white(caller));
267083
- console.log();
267084
- if (!exists2) {
267085
- throw new Error(`Domain ${label}.dot is not registered`);
267086
- }
267087
- await requireResolverAuthorization(clientWrapper, originSubstrateAddress, owner, caller);
267088
- console.log(source_default.gray(" status: ") + source_default.green("Ownership verified"));
267089
- console.log();
267090
- try {
267091
- const currentContentHash = await performContractCall(clientWrapper, originSubstrateAddress, CONTRACTS.DOTNS_CONTENT_RESOLVER, DOTNS_CONTENT_RESOLVER_ABI, "contenthash", [namehashNode]);
267092
- const currentCid = decodeContenthashToCid(currentContentHash);
267093
- console.log(source_default.gray(" current: ") + source_default.cyan(currentCid));
267094
- } catch {
267095
- console.log(source_default.gray(" current: ") + source_default.yellow("Not set or error reading"));
267096
- }
267097
- console.log();
267098
- const contentBytes = encodeCidToContenthash(contentId);
267099
- console.log(source_default.gray(" resolver: ") + source_default.white(CONTRACTS.DOTNS_CONTENT_RESOLVER));
267100
- console.log(source_default.gray(" node: ") + source_default.white(namehashNode));
267101
- console.log(source_default.gray(" cid: ") + source_default.cyan(contentId));
267102
- console.log(source_default.gray(" bytes: ") + source_default.white(contentBytes));
267103
- spinner.start("Submitting setContenthash");
267104
- const transactionHash = await submitContractTransaction(clientWrapper, CONTRACTS.DOTNS_CONTENT_RESOLVER, 0n, DOTNS_CONTENT_RESOLVER_ABI, "setContenthash", [namehashNode, contentBytes], originSubstrateAddress, signer, spinner, "setContenthash");
267105
- console.log(source_default.gray(" tx: ") + source_default.blue(transactionHash));
267106
- const updatedContentHash = await performContractCall(clientWrapper, originSubstrateAddress, CONTRACTS.DOTNS_CONTENT_RESOLVER, DOTNS_CONTENT_RESOLVER_ABI, "contenthash", [namehashNode]);
267107
- const updatedCid = decodeContenthashToCid(updatedContentHash);
267108
- console.log();
267109
- console.log(source_default.gray(" new: ") + source_default.cyan(updatedCid));
267110
- }
267111
-
267112
- // src/cli/commands/content.ts
267113
- init_formatting();
267114
267138
  init_ora();
267115
- function getMergedOptions3(command, fallback) {
267116
- const mergedOptions = { ...fallback ?? {} };
267117
- let currentCommand = command?.parent;
267118
- while (currentCommand) {
267119
- if (typeof currentCommand.opts === "function") {
267120
- const parentOptions = currentCommand.opts();
267121
- for (const key in parentOptions) {
267122
- if (!(key in mergedOptions) && parentOptions[key] !== undefined) {
267123
- mergedOptions[key] = parentOptions[key];
267124
- }
267125
- }
267126
- }
267127
- currentCommand = currentCommand.parent;
267128
- }
267129
- return mergedOptions;
267130
- }
267131
267139
  function attachContentCommands(root) {
267132
267140
  const contentCommand = root.command("content").description("Manage domain content hashes");
267133
267141
  addAuthOptions(contentCommand);
267134
- const viewContentCommand = contentCommand.command("view <name>").description("View domain content hash");
267142
+ const viewContentCommand = contentCommand.command("view <name>").description("View domain content hash").option("--json", "Output result as JSON (suppresses all other output)", false);
267135
267143
  addAuthOptions(viewContentCommand).action(async (name10, options2, command) => {
267144
+ const jsonOutput = getJsonFlag(command);
267136
267145
  try {
267137
- const mergedOptions = getMergedOptions3(command, options2);
267138
- const context = await prepareReadOnlyContext(mergedOptions);
267139
- console.log(source_default.bold(`
267146
+ const mergedOptions = getMergedOptions(command, options2);
267147
+ const context = await maybeQuiet(jsonOutput, () => prepareReadOnlyContext(mergedOptions));
267148
+ if (!jsonOutput)
267149
+ console.log(source_default.bold(`
267140
267150
  ▶ Content View
267141
267151
  `));
267142
267152
  const spinner = ora();
267143
- await viewDomainContentHash(context.clientWrapper, context.account.address, name10, spinner);
267144
- console.log(source_default.green(`
267153
+ const result = await maybeQuiet(jsonOutput, () => viewDomainContentHash(context.clientWrapper, context.account.address, name10, spinner));
267154
+ if (!emitJsonResult(jsonOutput, result)) {
267155
+ console.log(source_default.green(`
267145
267156
  ✓ Complete
267146
267157
  `));
267158
+ }
267147
267159
  process.exit(0);
267148
267160
  } catch (error2) {
267149
- console.error(source_default.red(`
267150
- ✗ Error: ${formatErrorMessage(error2)}
267151
- `));
267152
- process.exit(1);
267161
+ handleCommandError(jsonOutput, error2);
267153
267162
  }
267154
267163
  });
267155
- const setContentCommand = contentCommand.command("set <name> <cid>").description("Set domain content hash (IPFS CID)");
267164
+ const setContentCommand = contentCommand.command("set <name> <cid>").description("Set domain content hash (IPFS CID)").option("--json", "Output result as JSON (suppresses all other output)", false);
267156
267165
  addAuthOptions(setContentCommand).action(async (name10, cid, options2, command) => {
267166
+ const jsonOutput = getJsonFlag(command);
267157
267167
  try {
267158
- const mergedOptions = getMergedOptions3(command, options2);
267168
+ const mergedOptions = getMergedOptions(command, options2);
267159
267169
  if (mergedOptions.mnemonic && mergedOptions.keyUri) {
267160
267170
  throw new Error("Cannot specify both --mnemonic and --key-uri");
267161
267171
  }
267162
- const context = await prepareContext({ ...mergedOptions, useRevive: true });
267163
- console.log(source_default.bold(`
267172
+ const context = await maybeQuiet(jsonOutput, () => prepareContext({ ...mergedOptions, useRevive: true }));
267173
+ if (!jsonOutput)
267174
+ console.log(source_default.bold(`
267164
267175
  ▶ Content Set
267165
267176
  `));
267166
267177
  const spinner = ora();
267167
- await setDomainContentHash(context.clientWrapper, context.substrateAddress, context.signer, name10, cid, spinner);
267168
- console.log(source_default.green(`
267178
+ const result = await maybeQuiet(jsonOutput, () => setDomainContentHash(context.clientWrapper, context.substrateAddress, context.signer, name10, cid, spinner));
267179
+ if (!emitJsonResult(jsonOutput, result)) {
267180
+ console.log(source_default.green(`
267169
267181
  ✓ Complete
267170
267182
  `));
267183
+ }
267171
267184
  process.exit(0);
267172
267185
  } catch (error2) {
267173
- console.error(source_default.red(`
267174
- ✗ Error: ${formatErrorMessage(error2)}
267175
- `));
267176
- process.exit(1);
267186
+ handleCommandError(jsonOutput, error2);
267177
267187
  }
267178
267188
  });
267179
267189
  }
@@ -267246,22 +267256,6 @@ async function setDomainText(clientWrapper, originSubstrateAddress, signer, labe
267246
267256
  // src/cli/commands/text.ts
267247
267257
  init_formatting();
267248
267258
  init_ora();
267249
- function getMergedOptions4(command, fallback) {
267250
- const mergedOptions = { ...fallback ?? {} };
267251
- let currentCommand = command?.parent;
267252
- while (currentCommand) {
267253
- if (typeof currentCommand.opts === "function") {
267254
- const parentOptions = currentCommand.opts();
267255
- for (const key in parentOptions) {
267256
- if (!(key in mergedOptions) && parentOptions[key] !== undefined) {
267257
- mergedOptions[key] = parentOptions[key];
267258
- }
267259
- }
267260
- }
267261
- currentCommand = currentCommand.parent;
267262
- }
267263
- return mergedOptions;
267264
- }
267265
267259
  function attachTextCommands(root) {
267266
267260
  const textCommand = root.command("text").description("Manage domain text records");
267267
267261
  addAuthOptions(textCommand);
@@ -267274,7 +267268,7 @@ function attachTextCommands(root) {
267274
267268
  process.stdout.write = process.stderr.write.bind(process.stderr);
267275
267269
  }
267276
267270
  try {
267277
- const mergedOptions = getMergedOptions4(command, options2);
267271
+ const mergedOptions = getMergedOptions(command, options2);
267278
267272
  const context = await prepareReadOnlyContext(mergedOptions);
267279
267273
  console.error(source_default.bold(`
267280
267274
  ▶ Text View
@@ -267298,7 +267292,7 @@ function attachTextCommands(root) {
267298
267292
  const setTextCommand = textCommand.command("set <name> <key> [value]").description("Set a domain text record (reads from stdin if value omitted)");
267299
267293
  addAuthOptions(setTextCommand).action(async (name10, key, value3, options2, command) => {
267300
267294
  try {
267301
- const mergedOptions = getMergedOptions4(command, options2);
267295
+ const mergedOptions = getMergedOptions(command, options2);
267302
267296
  if (!value3) {
267303
267297
  if (process.stdin.isTTY) {
267304
267298
  throw new Error("No value provided. Pass a value argument or pipe via stdin.");
@@ -267333,24 +267327,6 @@ function attachTextCommands(root) {
267333
267327
 
267334
267328
  // src/cli/commands/pop.ts
267335
267329
  init_source();
267336
- init_formatting();
267337
- function getMergedOptions5(command, fallback) {
267338
- const mergedOptions = { ...fallback ?? {} };
267339
- let currentCommand = command?.parent;
267340
- while (currentCommand) {
267341
- if (typeof currentCommand.opts === "function") {
267342
- const parentOptions = currentCommand.opts();
267343
- for (const key in parentOptions) {
267344
- const optionKey = key;
267345
- if (!(optionKey in mergedOptions) && parentOptions[optionKey] !== undefined) {
267346
- mergedOptions[optionKey] = parentOptions[optionKey];
267347
- }
267348
- }
267349
- }
267350
- currentCommand = currentCommand.parent;
267351
- }
267352
- return mergedOptions;
267353
- }
267354
267330
  async function readPopInfo(options2) {
267355
267331
  const context = await prepareReadOnlyContext(options2);
267356
267332
  const status = await getUserProofOfPersonhoodStatus(context.clientWrapper, context.account.address, context.evmAddress);
@@ -267363,51 +267339,58 @@ async function readPopInfo(options2) {
267363
267339
  function attachPopCommands(root) {
267364
267340
  const popCommand = root.command("pop").description("ProofOfPersonhood status management");
267365
267341
  addAuthOptions(popCommand);
267366
- const setPopCommand = popCommand.command("set <status>").description("Set ProofOfPersonhood status (none, lite, or full)");
267342
+ const setPopCommand = popCommand.command("set <status>").description("Set ProofOfPersonhood status (none, lite, or full)").option("--json", "Output result as JSON (suppresses all other output)", false);
267367
267343
  addAuthOptions(setPopCommand).action(async (status, options2, command) => {
267344
+ const jsonOutput = getJsonFlag(command);
267368
267345
  try {
267369
- const mergedOptions = getMergedOptions5(command, options2);
267370
- const context = await prepareContext(mergedOptions);
267346
+ const mergedOptions = getMergedOptions(command, options2);
267347
+ const context = await maybeQuiet(jsonOutput, () => prepareContext(mergedOptions));
267371
267348
  const parsedStatus = parseProofOfPersonhoodStatus(status);
267372
- await setUserProofOfPersonhoodStatus(context.clientWrapper, context.substrateAddress, context.signer, context.evmAddress, "", parsedStatus);
267373
- console.log(source_default.green(`
267349
+ await maybeQuiet(jsonOutput, () => setUserProofOfPersonhoodStatus(context.clientWrapper, context.substrateAddress, context.signer, context.evmAddress, "", parsedStatus));
267350
+ if (!emitJsonResult(jsonOutput, {
267351
+ ok: true,
267352
+ status: ProofOfPersonhoodStatus[parsedStatus].toLowerCase(),
267353
+ statusCode: parsedStatus
267354
+ })) {
267355
+ console.log(source_default.green(`
267374
267356
  ✓ PoP Status Updated
267375
267357
  `));
267358
+ }
267376
267359
  process.exit(0);
267377
267360
  } catch (error2) {
267378
- console.error(source_default.red(`
267379
- ✗ Error: ${formatErrorMessage(error2)}
267380
- `));
267381
- process.exit(1);
267361
+ handleCommandError(jsonOutput, error2);
267382
267362
  }
267383
267363
  });
267384
- const infoCommand = popCommand.command("info").description("Display ProofOfPersonhood status");
267364
+ const infoCommand = popCommand.command("info").description("Display ProofOfPersonhood status").option("--json", "Output result as JSON (suppresses all other output)", false);
267385
267365
  addAuthOptions(infoCommand).action(async (options2, command) => {
267366
+ const jsonOutput = getJsonFlag(command);
267386
267367
  try {
267387
- const mergedOptions = getMergedOptions5(command, options2);
267388
- const info2 = await readPopInfo(mergedOptions);
267389
- console.log(source_default.bold(`
267368
+ const mergedOptions = getMergedOptions(command, options2);
267369
+ const info2 = await maybeQuiet(jsonOutput, () => readPopInfo(mergedOptions));
267370
+ if (!emitJsonResult(jsonOutput, {
267371
+ substrate: info2.substrate,
267372
+ evm: info2.evm,
267373
+ status: ProofOfPersonhoodStatus[info2.status].toLowerCase(),
267374
+ statusCode: info2.status
267375
+ })) {
267376
+ console.log(source_default.bold(`
267390
267377
  \uD83D\uDCCB ProofOfPersonhood Status
267391
267378
  `));
267392
- console.log(source_default.gray(" substrate: ") + source_default.white(info2.substrate));
267393
- console.log(source_default.gray(" evm: ") + source_default.white(info2.evm));
267394
- console.log(source_default.gray(" status: ") + source_default.white(ProofOfPersonhoodStatus[info2.status]));
267395
- console.log(source_default.green(`
267379
+ console.log(source_default.gray(" substrate: ") + source_default.white(info2.substrate));
267380
+ console.log(source_default.gray(" evm: ") + source_default.white(info2.evm));
267381
+ console.log(source_default.gray(" status: ") + source_default.white(ProofOfPersonhoodStatus[info2.status]));
267382
+ console.log(source_default.green(`
267396
267383
  ✓ PoP Status Retrieved
267397
267384
  `));
267385
+ }
267398
267386
  process.exit(0);
267399
267387
  } catch (error2) {
267400
- console.error(source_default.red(`
267401
- ✗ Error: ${formatErrorMessage(error2)}
267402
- `));
267403
- process.exit(1);
267388
+ handleCommandError(jsonOutput, error2);
267404
267389
  }
267405
267390
  });
267406
267391
  }
267407
267392
 
267408
267393
  // src/cli/commands/registerCommand.ts
267409
- init_source();
267410
- init_formatting();
267411
267394
  init_constants();
267412
267395
  function resolveCommitmentBuffer(cliValue) {
267413
267396
  const raw = cliValue ?? process.env.DOTNS_COMMITMENT_BUFFER;
@@ -267421,7 +267404,8 @@ function resolveCommitmentBuffer(cliValue) {
267421
267404
  }
267422
267405
  function attachRegisterCommand(root) {
267423
267406
  const registerCommand = root.command("register").description("Domain registration commands");
267424
- const domainCommand = registerCommand.command("domain").description("Register a new base domain").option("-n, --name <label>", "Domain label to register (without .dot)").option("-s, --status <level>", "ProofOfPersonhood status: none, lite, or full").option("-r, --reverse", "Enable reverse record registration", false).option("-g, --governance", "Use governance registration path", false).option("-o, --owner <address>", "Owner address (EVM or Substrate, or label)").option("--transfer", "Transfer domain after registration", false).option("--to <destination>", "Transfer destination (EVM address, SS58, or domain label)").option("--cb, --commitment-buffer <seconds>", `Extra seconds to wait after minCommitmentAge (default: ${DEFAULT_COMMITMENT_BUFFER_SECONDS}, env: DOTNS_COMMITMENT_BUFFER)`).action(async (options2, cmd) => {
267407
+ const domainCommand = registerCommand.command("domain").description("Register a new base domain").option("-n, --name <label>", "Domain label to register (without .dot)").option("-s, --status <level>", "ProofOfPersonhood status: none, lite, or full").option("-r, --reverse", "Enable reverse record registration", false).option("-g, --governance", "Use governance registration path", false).option("-o, --owner <address>", "Owner address (EVM or Substrate, or label)").option("--transfer", "Transfer domain after registration", false).option("--to <destination>", "Transfer destination (EVM address, SS58, or domain label)").option("--cb, --commitment-buffer <seconds>", `Extra seconds to wait after minCommitmentAge (default: ${DEFAULT_COMMITMENT_BUFFER_SECONDS}, env: DOTNS_COMMITMENT_BUFFER)`).option("--json", "Output result as JSON (suppresses all other output)", false).action(async (options2, cmd) => {
267408
+ const jsonOutput = getJsonFlag(cmd);
267425
267409
  try {
267426
267410
  const merged = { ...options2, ...getAuthOptions(cmd) };
267427
267411
  const allOpts = typeof cmd.optsWithGlobals === "function" ? cmd.optsWithGlobals() : cmd.opts();
@@ -267430,26 +267414,23 @@ function attachRegisterCommand(root) {
267430
267414
  if (merged.transfer === true && !merged.to) {
267431
267415
  throw new Error("Missing transfer destination: use --to <evm|ss58|label>");
267432
267416
  }
267433
- await executeRegistration(merged);
267417
+ const result = await maybeQuiet(jsonOutput, () => executeRegistration(merged));
267418
+ emitJsonResult(jsonOutput, result);
267434
267419
  process.exit(0);
267435
267420
  } catch (error2) {
267436
- console.error(`
267437
- ${source_default.red.bold("✗ Error:")} ${formatErrorMessage(error2)}
267438
- `);
267439
- process.exit(1);
267421
+ handleCommandError(jsonOutput, error2);
267440
267422
  }
267441
267423
  });
267442
267424
  addAuthOptions(domainCommand);
267443
- const subnameCommand = registerCommand.command("subname").description("Register a subname under an existing domain").requiredOption("-n, --name <label>", "Subname label to register").requiredOption("-p, --parent <label>", "Parent domain label (without .dot)").option("-o, --owner <address>", "Owner address (EVM or Substrate, or label)").action(async (options2, cmd) => {
267425
+ const subnameCommand = registerCommand.command("subname").description("Register a subname under an existing domain").requiredOption("-n, --name <label>", "Subname label to register").requiredOption("-p, --parent <label>", "Parent domain label (without .dot)").option("-o, --owner <address>", "Owner address (EVM or Substrate, or label)").option("--json", "Output result as JSON (suppresses all other output)", false).action(async (options2, cmd) => {
267426
+ const jsonOutput = getJsonFlag(cmd);
267444
267427
  try {
267445
267428
  const merged = { ...options2, ...getAuthOptions(cmd) };
267446
- await executeSubnameRegistration(merged);
267429
+ const result = await maybeQuiet(jsonOutput, () => executeSubnameRegistration(merged));
267430
+ emitJsonResult(jsonOutput, result);
267447
267431
  process.exit(0);
267448
267432
  } catch (error2) {
267449
- console.error(`
267450
- ${source_default.red.bold("✗ Error:")} ${formatErrorMessage(error2)}
267451
- `);
267452
- process.exit(1);
267433
+ handleCommandError(jsonOutput, error2);
267453
267434
  }
267454
267435
  });
267455
267436
  addAuthOptions(subnameCommand);
@@ -267521,29 +267502,13 @@ async function whitelistAddress(clientWrapper, originAddress, signer, targetAddr
267521
267502
  }
267522
267503
 
267523
267504
  // src/cli/commands/info.ts
267524
- function getMergedOptions6(command, fallback) {
267525
- const mergedOptions = { ...fallback ?? {} };
267526
- let currentCommand = command?.parent;
267527
- while (currentCommand) {
267528
- if (typeof currentCommand.opts === "function") {
267529
- const parentOptions = currentCommand.opts();
267530
- for (const key in parentOptions) {
267531
- if (!(key in mergedOptions) && parentOptions[key] !== undefined) {
267532
- mergedOptions[key] = parentOptions[key];
267533
- }
267534
- }
267535
- }
267536
- currentCommand = currentCommand.parent;
267537
- }
267538
- return mergedOptions;
267539
- }
267540
267505
  function attachAccountCommands(root) {
267541
267506
  const accountCommand = root.command("account").description("Account management utilities");
267542
267507
  addAuthOptions(accountCommand);
267543
267508
  const addressCommand = accountCommand.command("address").description("Print the substrate address for the configured account (offline, no RPC)");
267544
267509
  addAuthOptions(addressCommand).action(async (options2, command) => {
267545
267510
  try {
267546
- const mergedOptions = getMergedOptions6(command, options2);
267511
+ const mergedOptions = getMergedOptions(command, options2);
267547
267512
  const keystorePath = resolveKeystorePath(mergedOptions.keystorePath);
267548
267513
  const auth = await resolveAuthSource({
267549
267514
  mnemonic: mergedOptions.mnemonic,
@@ -267563,7 +267528,7 @@ function attachAccountCommands(root) {
267563
267528
  const infoCommand = accountCommand.command("info").description("Display account information including balances");
267564
267529
  addAuthOptions(infoCommand).action(async (options2, command) => {
267565
267530
  try {
267566
- const mergedOptions = getMergedOptions6(command, options2);
267531
+ const mergedOptions = getMergedOptions(command, options2);
267567
267532
  const rpc = resolveRpc(mergedOptions.rpc);
267568
267533
  const keystorePath = resolveKeystorePath(mergedOptions.keystorePath);
267569
267534
  const client = await step(`Connecting RPC ${rpc}`, async () => createClient3(getWsProvider(rpc)).getTypedApi(paseo_default));
@@ -267595,7 +267560,7 @@ function attachAccountCommands(root) {
267595
267560
  const mapCommand = accountCommand.command("map").description("Map Substrate account to EVM address");
267596
267561
  addAuthOptions(mapCommand).action(async (options2, command) => {
267597
267562
  try {
267598
- const mergedOptions = getMergedOptions6(command, options2);
267563
+ const mergedOptions = getMergedOptions(command, options2);
267599
267564
  const rpc = resolveRpc(mergedOptions.rpc);
267600
267565
  const client = await step(`Connecting RPC ${rpc}`, async () => createClient3(getWsProvider(rpc)).getTypedApi(paseo_default));
267601
267566
  const clientWrapper = new ReviveClientWrapper(client);
@@ -267628,7 +267593,7 @@ function attachAccountCommands(root) {
267628
267593
  addAuthOptions(isMappedCommand).action(async (address, options2, cmd) => {
267629
267594
  const jsonOutput = getJsonFlag(cmd);
267630
267595
  try {
267631
- const mergedOptions = getMergedOptions6(cmd, options2);
267596
+ const mergedOptions = getMergedOptions(cmd, options2);
267632
267597
  const { clientWrapper } = await maybeQuiet(jsonOutput, () => prepareReadOnlyContext(mergedOptions));
267633
267598
  const result = await maybeQuiet(jsonOutput, () => checkAccountMapped(clientWrapper, address));
267634
267599
  if (jsonOutput)
@@ -267652,7 +267617,7 @@ function attachAccountCommands(root) {
267652
267617
  addAuthOptions(isWhitelistedCommand).action(async (address, options2, cmd) => {
267653
267618
  const jsonOutput = getJsonFlag(cmd);
267654
267619
  try {
267655
- const mergedOptions = getMergedOptions6(cmd, options2);
267620
+ const mergedOptions = getMergedOptions(cmd, options2);
267656
267621
  const { clientWrapper, account } = await maybeQuiet(jsonOutput, () => prepareReadOnlyContext(mergedOptions));
267657
267622
  const result = await maybeQuiet(jsonOutput, () => checkWhitelisted(clientWrapper, account.address, address));
267658
267623
  if (jsonOutput)
@@ -267676,7 +267641,7 @@ function attachAccountCommands(root) {
267676
267641
  addAuthOptions(whitelistCommand).action(async (address, options2, cmd) => {
267677
267642
  const jsonOutput = getJsonFlag(cmd);
267678
267643
  try {
267679
- const mergedOptions = getMergedOptions6(cmd, options2);
267644
+ const mergedOptions = getMergedOptions(cmd, options2);
267680
267645
  const enable = !mergedOptions.remove;
267681
267646
  const context = await maybeQuiet(jsonOutput, () => prepareAssetHubContext(mergedOptions));
267682
267647
  const result = await maybeQuiet(jsonOutput, () => whitelistAddress(context.clientWrapper, context.substrateAddress, context.signer, address, enable));
@@ -267712,7 +267677,7 @@ function validateEvmAddress(raw) {
267712
267677
  }
267713
267678
  return getAddress(raw);
267714
267679
  }
267715
- function handleCommandError(error2, cmd) {
267680
+ function handleCommandError2(error2, cmd) {
267716
267681
  const jsonOutput = getJsonFlag(cmd);
267717
267682
  const message2 = formatErrorMessage(error2);
267718
267683
  if (jsonOutput) {
@@ -267742,7 +267707,7 @@ function attachStoreCommands(root) {
267742
267707
  }
267743
267708
  process.exit(0);
267744
267709
  } catch (error2) {
267745
- handleCommandError(error2, cmd);
267710
+ handleCommandError2(error2, cmd);
267746
267711
  }
267747
267712
  });
267748
267713
  const listCommand = storeCommand.command("list").description("List all values in your Store").option("--json", "Output result as JSON", false);
@@ -267761,7 +267726,7 @@ function attachStoreCommands(root) {
267761
267726
  }
267762
267727
  process.exit(0);
267763
267728
  } catch (error2) {
267764
- handleCommandError(error2, cmd);
267729
+ handleCommandError2(error2, cmd);
267765
267730
  }
267766
267731
  });
267767
267732
  const namesCommand = storeCommand.command("names").description("List all .dot names in your Store").option("--json", "Output result as JSON", false);
@@ -267790,7 +267755,7 @@ function attachStoreCommands(root) {
267790
267755
  }
267791
267756
  process.exit(0);
267792
267757
  } catch (error2) {
267793
- handleCommandError(error2, cmd);
267758
+ handleCommandError2(error2, cmd);
267794
267759
  }
267795
267760
  });
267796
267761
  const cidsCommand = storeCommand.command("cids").description("List all uploaded CIDs in your Store").option("--json", "Output result as JSON", false);
@@ -267819,7 +267784,7 @@ function attachStoreCommands(root) {
267819
267784
  }
267820
267785
  process.exit(0);
267821
267786
  } catch (error2) {
267822
- handleCommandError(error2, cmd);
267787
+ handleCommandError2(error2, cmd);
267823
267788
  }
267824
267789
  });
267825
267790
  const getCommand = storeCommand.command("get <key>").description("Get a value by key (hex bytes32 or string, hashed via keccak256)").option("--json", "Output result as JSON", false);
@@ -267838,7 +267803,7 @@ function attachStoreCommands(root) {
267838
267803
  }
267839
267804
  process.exit(0);
267840
267805
  } catch (error2) {
267841
- handleCommandError(error2, cmd);
267806
+ handleCommandError2(error2, cmd);
267842
267807
  }
267843
267808
  });
267844
267809
  const setCommand = storeCommand.command("set <key> <value>").description("Set a key-value pair in your Store").option("--json", "Output result as JSON", false);
@@ -267858,7 +267823,7 @@ function attachStoreCommands(root) {
267858
267823
  }
267859
267824
  process.exit(0);
267860
267825
  } catch (error2) {
267861
- handleCommandError(error2, cmd);
267826
+ handleCommandError2(error2, cmd);
267862
267827
  }
267863
267828
  });
267864
267829
  const deleteCommand = storeCommand.command("delete <key>").description("Delete a value from your Store by key").option("--json", "Output result as JSON", false);
@@ -267878,7 +267843,7 @@ function attachStoreCommands(root) {
267878
267843
  }
267879
267844
  process.exit(0);
267880
267845
  } catch (error2) {
267881
- handleCommandError(error2, cmd);
267846
+ handleCommandError2(error2, cmd);
267882
267847
  }
267883
267848
  });
267884
267849
  const checkCommand = storeCommand.command("check <address>").description("Check whether an address is authorized or a DotNS controller on your Store").option("--json", "Output result as JSON", false);
@@ -267898,7 +267863,7 @@ function attachStoreCommands(root) {
267898
267863
  }
267899
267864
  process.exit(0);
267900
267865
  } catch (error2) {
267901
- handleCommandError(error2, cmd);
267866
+ handleCommandError2(error2, cmd);
267902
267867
  }
267903
267868
  });
267904
267869
  const authorizeCommand = storeCommand.command("authorize <address>").description("Authorize an address to write to your Store (setValueFor)").option("--json", "Output result as JSON", false);
@@ -267919,7 +267884,7 @@ function attachStoreCommands(root) {
267919
267884
  }
267920
267885
  process.exit(0);
267921
267886
  } catch (error2) {
267922
- handleCommandError(error2, cmd);
267887
+ handleCommandError2(error2, cmd);
267923
267888
  }
267924
267889
  });
267925
267890
  const unauthorizeCommand = storeCommand.command("unauthorize <address>").description("Revoke write access from an address on your Store").option("--json", "Output result as JSON", false);
@@ -267940,7 +267905,7 @@ function attachStoreCommands(root) {
267940
267905
  }
267941
267906
  process.exit(0);
267942
267907
  } catch (error2) {
267943
- handleCommandError(error2, cmd);
267908
+ handleCommandError2(error2, cmd);
267944
267909
  }
267945
267910
  });
267946
267911
  const authorizeControllerCommand = storeCommand.command("authorize-controller <address>").description("Authorize an address as a DotNS controller (locks keys permanently on write)").option("--json", "Output result as JSON", false);
@@ -267961,7 +267926,7 @@ function attachStoreCommands(root) {
267961
267926
  }
267962
267927
  process.exit(0);
267963
267928
  } catch (error2) {
267964
- handleCommandError(error2, cmd);
267929
+ handleCommandError2(error2, cmd);
267965
267930
  }
267966
267931
  });
267967
267932
  const unauthorizeControllerCommand = storeCommand.command("unauthorize-controller <address>").description("Revoke DotNS controller authorization from an address on your Store").option("--json", "Output result as JSON", false);
@@ -267982,7 +267947,7 @@ function attachStoreCommands(root) {
267982
267947
  }
267983
267948
  process.exit(0);
267984
267949
  } catch (error2) {
267985
- handleCommandError(error2, cmd);
267950
+ handleCommandError2(error2, cmd);
267986
267951
  }
267987
267952
  });
267988
267953
  const ensureAuthCommand = storeCommand.command("ensure-auth").description("Ensure DotNS system contracts are authorized on your Store").option("--json", "Output result as JSON", false);
@@ -267992,7 +267957,7 @@ function attachStoreCommands(root) {
267992
267957
  const jsonOutput = getJsonFlag(cmd);
267993
267958
  const context = await maybeQuiet(jsonOutput, () => prepareAssetHubContext(merged));
267994
267959
  const { clientWrapper, substrateAddress, signer, evmAddress } = context;
267995
- const result = await maybeQuiet(jsonOutput, () => ensureStoreAuthorizations2(clientWrapper, substrateAddress, signer, evmAddress));
267960
+ const result = await maybeQuiet(jsonOutput, () => ensureStoreAuthorizations(clientWrapper, substrateAddress, signer, evmAddress));
267996
267961
  if (jsonOutput) {
267997
267962
  console.log(JSON.stringify(result));
267998
267963
  } else {
@@ -268002,7 +267967,7 @@ function attachStoreCommands(root) {
268002
267967
  }
268003
267968
  process.exit(0);
268004
267969
  } catch (error2) {
268005
- handleCommandError(error2, cmd);
267970
+ handleCommandError2(error2, cmd);
268006
267971
  }
268007
267972
  });
268008
267973
  }