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