@parity/product-deploy 0.8.3-rc.8 → 0.9.0-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/LICENSE +674 -201
  2. package/README.md +59 -2
  3. package/assets/environments.json +0 -1
  4. package/bin/bulletin-deploy +21 -1
  5. package/dist/auth/index.d.ts +2 -3
  6. package/dist/auth/index.js +2 -1
  7. package/dist/auth/vendor/index.d.ts +2 -3
  8. package/dist/auth/vendor/index.js +2 -1
  9. package/dist/auth/vendor/ui/index.d.ts +1 -2
  10. package/dist/auth-C-Pel0AT.d.ts +235 -0
  11. package/dist/auth-config.d.ts +30 -8
  12. package/dist/auth-config.js +14 -6
  13. package/dist/bug-report.js +4 -4
  14. package/dist/{chunk-YXGNQZZF.js → chunk-2SR5D4CP.js} +19 -15
  15. package/dist/{chunk-OFVBJOFB.js → chunk-6NW5M3F5.js} +9 -20
  16. package/dist/{chunk-7DGFJC6E.js → chunk-DHY2ZXVZ.js} +81 -42
  17. package/dist/chunk-G56VYTUD.js +75 -0
  18. package/dist/{chunk-5K3RI5C2.js → chunk-GL3U7K2B.js} +0 -1
  19. package/dist/{chunk-327NAPBD.js → chunk-H4LWILH4.js} +32 -6
  20. package/dist/chunk-J7CYVTAW.js +43 -0
  21. package/dist/{chunk-WHMNBSG7.js → chunk-LCKLYFAZ.js} +5 -4
  22. package/dist/{chunk-3OWKSL7K.js → chunk-NP4SLURL.js} +1 -1
  23. package/dist/{chunk-T4PAK4YK.js → chunk-O2NWQLYB.js} +2 -2
  24. package/dist/{chunk-RPU72Z4B.js → chunk-RD2QHYTL.js} +242 -36
  25. package/dist/{chunk-EGNMZHMR.js → chunk-WZBAQCA5.js} +15 -4
  26. package/dist/{chunk-YOQLRCQV.js → chunk-YIKGVALU.js} +3 -3
  27. package/dist/chunk-probe.js +3 -3
  28. package/dist/commands/login.d.ts +34 -9
  29. package/dist/commands/login.js +186 -30
  30. package/dist/commands/logout.d.ts +1 -2
  31. package/dist/commands/logout.js +5 -3
  32. package/dist/commands/whoami.d.ts +1 -2
  33. package/dist/commands/whoami.js +7 -4
  34. package/dist/deploy.d.ts +19 -4
  35. package/dist/deploy.js +14 -9
  36. package/dist/dotns.d.ts +7 -0
  37. package/dist/dotns.js +4 -4
  38. package/dist/environments.js +1 -1
  39. package/dist/index.js +11 -10
  40. package/dist/manifest/publish.js +11 -10
  41. package/dist/manifest/types.d.ts +1 -1
  42. package/dist/memory-report.js +2 -2
  43. package/dist/merkle.js +10 -9
  44. package/dist/personhood/bootstrap.js +10 -10
  45. package/dist/personhood/people-client.js +4 -4
  46. package/dist/run-state.js +1 -1
  47. package/dist/{signer-CriGqahj.d.ts → signer-vR6KKC7V.d.ts} +1 -1
  48. package/dist/spinner.d.ts +21 -0
  49. package/dist/spinner.js +6 -0
  50. package/dist/sss-allowance.d.ts +29 -0
  51. package/dist/sss-allowance.js +8 -0
  52. package/dist/storage-signer.d.ts +94 -2
  53. package/dist/storage-signer.js +18 -9
  54. package/dist/telemetry.js +2 -2
  55. package/dist/version-check.d.ts +0 -3
  56. package/dist/version-check.js +3 -3
  57. package/package.json +19 -15
  58. package/dist/allocations-B65Is4Md.d.ts +0 -97
  59. package/dist/auth-DkRZBK-T.d.ts +0 -122
@@ -1,3 +1,6 @@
1
+ import {
2
+ checkSSSAllowance
3
+ } from "./chunk-G56VYTUD.js";
1
4
  import {
2
5
  MirrorSkipped,
3
6
  mirrorToGitHubPages,
@@ -24,28 +27,30 @@ import {
24
27
  parseManifest
25
28
  } from "./chunk-S7EM5VMW.js";
26
29
  import {
27
- DOT_DAPP_ID,
28
30
  DOT_PRODUCT_ID,
31
+ STALE_SESSION_MESSAGE,
32
+ getPeopleChainEndpoints,
29
33
  hasPersistedSession
30
- } from "./chunk-327NAPBD.js";
34
+ } from "./chunk-H4LWILH4.js";
31
35
  import {
32
36
  setDeployContext
33
- } from "./chunk-T4PAK4YK.js";
37
+ } from "./chunk-O2NWQLYB.js";
34
38
  import {
35
39
  probeChunks
36
- } from "./chunk-3OWKSL7K.js";
40
+ } from "./chunk-NP4SLURL.js";
37
41
  import {
38
42
  packSection
39
43
  } from "./chunk-C2TS5MER.js";
40
44
  import {
41
45
  DotNS,
46
+ PUBLISHER_ABI,
42
47
  PublisherNotSupportedError,
43
48
  TX_TIMEOUT_MS,
44
49
  fetchNonce,
45
50
  parseDomainName,
46
51
  popStatusName,
47
52
  verifyNonceAdvanced
48
- } from "./chunk-EGNMZHMR.js";
53
+ } from "./chunk-WZBAQCA5.js";
49
54
  import {
50
55
  derivePoolAccounts,
51
56
  detectTestnet,
@@ -67,13 +72,13 @@ import {
67
72
  truncateAddress,
68
73
  withDeploySpan,
69
74
  withSpan
70
- } from "./chunk-WHMNBSG7.js";
75
+ } from "./chunk-LCKLYFAZ.js";
71
76
  import {
72
77
  DEFAULT_ENV_ID,
73
78
  getPopSelfServeConfig,
74
79
  loadEnvironments,
75
80
  resolveEndpoints
76
- } from "./chunk-5K3RI5C2.js";
81
+ } from "./chunk-GL3U7K2B.js";
77
82
  import {
78
83
  NonRetryableError
79
84
  } from "./chunk-ZOC4GITL.js";
@@ -104,6 +109,7 @@ import { base32 } from "multiformats/bases/base32";
104
109
  import { base58btc } from "multiformats/bases/base58";
105
110
  import * as dagPB from "@ipld/dag-pb";
106
111
  import { UnixFS } from "ipfs-unixfs";
112
+ import { keccak256, toBytes } from "viem";
107
113
  import { cryptoWaitReady } from "@polkadot/util-crypto";
108
114
  import { getPolkadotSigner as getPolkadotSigner2 } from "polkadot-api/signer";
109
115
  import { sr25519CreateDerive as sr25519CreateDerive2 } from "@polkadot-labs/hdkd";
@@ -247,10 +253,80 @@ function extractBulletinSlotKey(outcomes) {
247
253
  if (allocated?.tag !== "BulletInAllowance") continue;
248
254
  const key = allocated.value?.slotAccountKey;
249
255
  if (!(key instanceof Uint8Array)) continue;
250
- return toHex(normalizeSchnorrkelKey(key));
256
+ return toHex(key);
251
257
  }
252
258
  return null;
253
259
  }
260
+ var BulletinSlotAuthError = class extends Error {
261
+ reason;
262
+ /** The on-chain expiration block (only set when reason === "expired"). */
263
+ expiration;
264
+ constructor(reason, ss58, expiration) {
265
+ const detail = reason === "expired" && expiration != null ? `expired at block ${expiration}` : "no on-chain authorization found";
266
+ super(`Slot account ${ss58} not authorized on Bulletin (${detail})`);
267
+ this.reason = reason;
268
+ this.expiration = expiration;
269
+ this.name = "BulletinSlotAuthError";
270
+ }
271
+ };
272
+ function isBulletinAuthActive(auth, blockNumber) {
273
+ if (auth == null) return { active: false, reason: "missing" };
274
+ const exp = Number(auth.expiration ?? 0);
275
+ if (exp <= blockNumber) return { active: false, reason: "expired", expiration: exp };
276
+ return { active: true, expiration: exp };
277
+ }
278
+ async function pollUntilBulletinAuthorized(queryFn, opts = {}) {
279
+ const { pollMs = 2e3, timeoutMs = 9e4 } = opts;
280
+ const debug = Boolean(process.env.DOT_DEBUG);
281
+ const deadline = Date.now() + timeoutMs;
282
+ while (Date.now() < deadline) {
283
+ try {
284
+ const { auth, blockNumber } = await queryFn();
285
+ const result = isBulletinAuthActive(auth, blockNumber);
286
+ if (result.active) {
287
+ if (debug) console.error(`[auth-poll] active expiration=${result.expiration}`);
288
+ return { authorized: true, expiration: result.expiration };
289
+ }
290
+ if (debug) {
291
+ console.error(
292
+ `[auth-poll] not-active reason=${result.reason}` + (result.expiration != null ? ` expiration=${result.expiration}` : "")
293
+ );
294
+ }
295
+ } catch (e) {
296
+ if (debug) console.error(`[auth-poll] query-errored: ${e instanceof Error ? e.message : String(e)}`);
297
+ }
298
+ const remaining = deadline - Date.now();
299
+ if (remaining <= 0) break;
300
+ await new Promise((r) => setTimeout(r, Math.min(pollMs, remaining)));
301
+ }
302
+ return { authorized: false, reason: "timeout" };
303
+ }
304
+ async function waitForBulletinAuthorization(ss58, opts = {}) {
305
+ const { quiet, endpoints, ...pollOpts } = opts;
306
+ const eps = endpoints && endpoints.length > 0 ? endpoints : BULLETIN_ENDPOINTS;
307
+ const primary = eps[0];
308
+ const onStatusChanged = quiet ? () => {
309
+ } : makeBulletinStatusHandler(primary);
310
+ const client = createPolkadotClient(getWsProvider(
311
+ eps,
312
+ { heartbeatTimeout: WS_HEARTBEAT_TIMEOUT_MS, onStatusChanged }
313
+ ));
314
+ const unsafeApi = client.getUnsafeApi();
315
+ try {
316
+ return await pollUntilBulletinAuthorized(
317
+ async () => {
318
+ const [auth, block] = await Promise.all([
319
+ unsafeApi.query.TransactionStorage.Authorizations.getValue(Enum("Account", ss58)),
320
+ client.getFinalizedBlock()
321
+ ]);
322
+ return { auth, blockNumber: block.number };
323
+ },
324
+ pollOpts
325
+ );
326
+ } finally {
327
+ client.destroy();
328
+ }
329
+ }
254
330
  async function getSlotSignerProvider(signer, ss58) {
255
331
  const primary = BULLETIN_ENDPOINTS[0];
256
332
  console.log(` Connecting to Bulletin (slot signer): ${primary}`);
@@ -263,12 +339,12 @@ async function getSlotSignerProvider(signer, ss58) {
263
339
  unsafeApi.query.TransactionStorage.Authorizations.getValue(Enum("Account", ss58)),
264
340
  client.getFinalizedBlock()
265
341
  ]);
266
- const now = currentBlock.number;
267
- if (!auth || Number(auth.expiration ?? 0) <= now) {
342
+ const result = isBulletinAuthActive(auth, currentBlock.number);
343
+ if (!result.active) {
268
344
  client.destroy();
269
- throw new Error(`Slot account ${ss58} not authorized on Bulletin`);
345
+ throw new BulletinSlotAuthError(result.reason, ss58, result.expiration);
270
346
  }
271
- console.log(` Using slot signer: ${ss58} (authorized until block ${Number(auth?.expiration ?? 0)})`);
347
+ console.log(` Using slot signer: ${ss58} (authorized until block ${result.expiration})`);
272
348
  setDeployAttribute("deploy.signer.mode", "slot");
273
349
  setDeployAttribute("deploy.signer.address", truncateAddress(ss58));
274
350
  return { client, unsafeApi, signer, ss58 };
@@ -452,8 +528,41 @@ async function getDirectProvider(mnemonic, derivationPath = "") {
452
528
  setDeployAttribute("deploy.signer.address", truncateAddress(ss58));
453
529
  return { client, unsafeApi, signer, ss58 };
454
530
  }
531
+ async function getSignerProvider(signer, ss58) {
532
+ const primary = BULLETIN_ENDPOINTS[0];
533
+ console.log(` Connecting to Bulletin: ${primary}`);
534
+ const client = createPolkadotClient2(getWsProvider2(
535
+ BULLETIN_ENDPOINTS,
536
+ { heartbeatTimeout: WS_HEARTBEAT_TIMEOUT_MS, onStatusChanged: makeBulletinStatusHandler(primary) }
537
+ ));
538
+ const unsafeApi = client.getUnsafeApi();
539
+ console.log(` Using external signer: ${ss58}`);
540
+ let [auth, currentBlock] = await Promise.all([
541
+ unsafeApi.query.TransactionStorage.Authorizations.getValue(Enum2("Account", ss58)),
542
+ client.getFinalizedBlock()
543
+ ]);
544
+ let now = currentBlock.number;
545
+ if (!auth || Number(auth.expiration ?? 0) <= now) {
546
+ try {
547
+ await ensureAuthorized(unsafeApi, ss58, "external signer");
548
+ [auth, currentBlock] = await Promise.all([
549
+ unsafeApi.query.TransactionStorage.Authorizations.getValue(Enum2("Account", ss58)),
550
+ client.getFinalizedBlock()
551
+ ]);
552
+ now = currentBlock.number;
553
+ } catch (e) {
554
+ client.destroy();
555
+ throw new NonRetryableError(`Account ${ss58} is not authorized for Bulletin storage and auto-authorization failed: ${e.message}`);
556
+ }
557
+ }
558
+ console.log(` Authorization: expires at block ${Number(auth?.expiration ?? 0)} (current: ${now})`);
559
+ setDeployAttribute("deploy.signer.mode", "external");
560
+ setDeployAttribute("deploy.signer.address", truncateAddress(ss58));
561
+ return { client, unsafeApi, signer, ss58 };
562
+ }
455
563
  function __selectStorageProviderModeForTest(options) {
456
564
  if (options.storageSigner && options.storageSignerAddress) return "storageSigner";
565
+ if (options.signer && options.signerAddress) return "signer";
457
566
  if (options.mnemonic) return "direct";
458
567
  return "pool";
459
568
  }
@@ -464,6 +573,10 @@ function chooseSignerInput(opts) {
464
573
  if (opts.hasSession) return "resolve";
465
574
  return "pool";
466
575
  }
576
+ function formatStorageSignerLine(slotAddress, failReason) {
577
+ if (slotAddress) return ` Storage signer: allowance slot ${slotAddress}`;
578
+ return ` Storage signer: pool fallback (${failReason ?? "no session"})`;
579
+ }
467
580
  function selectStorageReconnect(options) {
468
581
  if (options.storageSigner && options.storageSignerAddress) {
469
582
  let useSlot = true;
@@ -471,13 +584,26 @@ function selectStorageReconnect(options) {
471
584
  if (!useSlot) return getProvider();
472
585
  try {
473
586
  return await getSlotSignerProvider(options.storageSigner, options.storageSignerAddress);
474
- } catch {
587
+ } catch (e) {
475
588
  useSlot = false;
476
589
  setDeployAttribute("deploy.signer.mode", "pool-fallback");
590
+ let reason;
591
+ if (e instanceof BulletinSlotAuthError) {
592
+ reason = e.reason === "expired" && e.expiration != null ? `expired at block ${e.expiration}` : "no on-chain authorization found";
593
+ } else {
594
+ reason = e instanceof Error ? e.message : String(e);
595
+ }
596
+ console.warn(
597
+ `\u26A0 Bulletin allowance slot not usable: ${reason}
598
+ Falling back to the shared pool account for storage (fine on testnet).
599
+ To use your own allowance, run: bulletin-deploy logout && bulletin-deploy login`
600
+ );
477
601
  return getProvider();
478
602
  }
479
603
  };
480
604
  }
605
+ if (options.signer && options.signerAddress)
606
+ return () => getSignerProvider(options.signer, options.signerAddress);
481
607
  if (options.mnemonic)
482
608
  return () => getDirectProvider(options.mnemonic, options.derivationPath);
483
609
  return () => getProvider();
@@ -1687,8 +1813,7 @@ async function publish(dotns, parsed, failOnError) {
1687
1813
  setDeployAttribute("deploy.publish.status", "failed");
1688
1814
  if (failOnError) throw e;
1689
1815
  const msg = e?.message ?? String(e);
1690
- console.error(` Warning: publish failed (non-fatal): ${msg}`);
1691
- captureWarning("publish failed", { error: msg.slice(0, 200) });
1816
+ console.log(` Publish failed: ${msg}`);
1692
1817
  }
1693
1818
  }
1694
1819
  async function unpublish(domainName, options = {}) {
@@ -1830,36 +1955,56 @@ async function deploy(content, domainName = null, options = {}) {
1830
1955
  } catch (e) {
1831
1956
  if (options.suri) throw e;
1832
1957
  if (e?.name === "SignerNotAvailableError") {
1833
- console.log(
1834
- " Login session unavailable or expired \u2014 falling back to pool. Run `bulletin-deploy login` to use your identity."
1835
- );
1958
+ if (hasSession) {
1959
+ console.error(STALE_SESSION_MESSAGE);
1960
+ } else {
1961
+ console.log(
1962
+ " Login session unavailable or expired \u2014 falling back to pool. Run `bulletin-deploy login` to use your identity."
1963
+ );
1964
+ }
1836
1965
  } else {
1837
1966
  throw e;
1838
1967
  }
1839
1968
  }
1840
1969
  }
1970
+ if (resolvedUserSession && options.signer) {
1971
+ try {
1972
+ const productPubkey = options.signer.publicKey;
1973
+ const peopleEndpoints = await getPeopleChainEndpoints(envId);
1974
+ const allowed = await checkSSSAllowance(productPubkey, peopleEndpoints);
1975
+ if (allowed === false) {
1976
+ throw new NonRetryableError(
1977
+ "Session signing allowance has expired (~2-3 days after login). Run `bulletin-deploy login` to renew."
1978
+ );
1979
+ }
1980
+ } catch (e) {
1981
+ if (e instanceof NonRetryableError) throw e;
1982
+ }
1983
+ }
1841
1984
  if (!options.storageSigner) {
1985
+ let storageLine = null;
1842
1986
  try {
1843
- let slotResult = await readBulletinSlotSigner(DOT_DAPP_ID);
1844
- if (!slotResult && resolvedUserSession?.userSession) {
1845
- const { requestResourceAllocation } = await import("./auth/index.js");
1846
- console.log("Requesting Bulletin storage allowance \u2014 check your phone to approve");
1847
- const outcomes = await requestResourceAllocation(
1848
- resolvedUserSession.userSession,
1849
- DOT_PRODUCT_ID,
1850
- [{ tag: "BulletInAllowance", value: void 0 }]
1987
+ if (resolvedUserSession?.userSession && resolvedUserSession?.adapter) {
1988
+ const { ss58Encode } = await import("@parity/product-sdk-address");
1989
+ const signerResult = await resolvedUserSession.adapter.allowance.getBulletinSigner(
1990
+ resolvedUserSession.userSession.id,
1991
+ DOT_PRODUCT_ID
1851
1992
  );
1852
- const hexKey = extractBulletinSlotKey(outcomes);
1853
- if (hexKey) {
1854
- await writeBulletinSlotKey(DOT_DAPP_ID, hexKey);
1855
- slotResult = await readBulletinSlotSigner(DOT_DAPP_ID);
1993
+ if (signerResult.isOk()) {
1994
+ const slotSigner = signerResult.value;
1995
+ const slotAddress = ss58Encode(slotSigner.publicKey);
1996
+ options = { ...options, storageSigner: slotSigner, storageSignerAddress: slotAddress };
1997
+ storageLine = formatStorageSignerLine(slotAddress);
1998
+ } else {
1999
+ storageLine = formatStorageSignerLine(null, signerResult.error.reason);
1856
2000
  }
1857
- }
1858
- if (slotResult) {
1859
- options = { ...options, storageSigner: slotResult.signer, storageSignerAddress: slotResult.ss58 };
2001
+ } else {
2002
+ storageLine = formatStorageSignerLine(null);
1860
2003
  }
1861
2004
  } catch {
2005
+ storageLine = formatStorageSignerLine(null, "error");
1862
2006
  }
2007
+ if (storageLine) console.log(storageLine);
1863
2008
  }
1864
2009
  initTelemetry();
1865
2010
  const randomSuffix = Math.floor(Math.random() * 100).toString().padStart(2, "0");
@@ -1894,6 +2039,7 @@ async function deploy(content, domainName = null, options = {}) {
1894
2039
  const reconnect = selectStorageReconnect(options);
1895
2040
  let dotnsPreflight = null;
1896
2041
  let previousContenthashCid = null;
2042
+ let preflightPublishNeeded = false;
1897
2043
  try {
1898
2044
  console.log("\n" + "=".repeat(60));
1899
2045
  console.log("Preflight");
@@ -1919,10 +2065,34 @@ async function deploy(content, domainName = null, options = {}) {
1919
2065
  }
1920
2066
  console.log(` Mode: subdomain (parent ${parsed.parentLabel}.dot owned by signer)`);
1921
2067
  } else {
2068
+ preflightPublishNeeded = false;
1922
2069
  try {
1923
2070
  dotnsPreflight = await preflight.preflight(name);
1924
2071
  previousContenthashCid = await readPreviousContenthashSafe(preflight, name);
1925
2072
  setDeployAttribute("deploy.incremental", previousContenthashCid ? "true" : "false");
2073
+ if (options.publish && parsed && !parsed.isSubdomain) {
2074
+ const publisher = preflight._contracts?.PUBLISHER;
2075
+ const zeroAddr = "0x0000000000000000000000000000000000000000";
2076
+ if (!publisher || publisher === zeroAddr) {
2077
+ console.log(` Publish: not supported on this environment \u2014 will be skipped`);
2078
+ } else {
2079
+ const labelhash = keccak256(toBytes(name));
2080
+ try {
2081
+ const alreadyPublished = await preflight.contractCall(
2082
+ publisher,
2083
+ PUBLISHER_ABI,
2084
+ "isPublished",
2085
+ [labelhash]
2086
+ );
2087
+ preflightPublishNeeded = !alreadyPublished;
2088
+ if (!preflightPublishNeeded) {
2089
+ console.log(` Publish: already published \u2014 will be skipped`);
2090
+ }
2091
+ } catch {
2092
+ preflightPublishNeeded = true;
2093
+ }
2094
+ }
2095
+ }
1926
2096
  } finally {
1927
2097
  preflight.disconnect();
1928
2098
  }
@@ -1944,6 +2114,20 @@ async function deploy(content, domainName = null, options = {}) {
1944
2114
  );
1945
2115
  }
1946
2116
  }
2117
+ if (options.signer && options.signerAddress) {
2118
+ const steps = computePhoneSigningSteps(dotnsPreflight, preflightPublishNeeded);
2119
+ if (steps.length === 1) {
2120
+ console.log(`
2121
+ \u{1F4F1} Have your phone ready \u2014 1 signature needed (${steps[0].toLowerCase()})`);
2122
+ } else if (steps.length > 1) {
2123
+ const display = steps.flatMap(
2124
+ (s, i) => s === "Register" && steps[i - 1] === "Commitment" ? ["(wait)", s] : [s]
2125
+ );
2126
+ console.log(`
2127
+ \u{1F4F1} Have your phone ready \u2014 ${steps.length} signatures needed`);
2128
+ console.log(` ${display.map((s) => s.toLowerCase()).join(" \xB7 ")}`);
2129
+ }
2130
+ }
1947
2131
  provider = await reconnect();
1948
2132
  const providerWithReconnect = { ...provider, reconnect };
1949
2133
  const isTestnet = await detectTestnet(provider.unsafeApi);
@@ -2128,7 +2312,11 @@ async function deploy(content, domainName = null, options = {}) {
2128
2312
  console.log("=".repeat(60));
2129
2313
  await withSpan("deploy.dotns", "2. dotns", { "deploy.domain": name, "deploy.subdomain": String(parsed?.isSubdomain ?? false) }, async () => {
2130
2314
  const dotns = new DotNS();
2131
- await dotns.connect(resolveDotnsConnectOptions(options, envAssetHub, envAutoAccountMapping, envContracts, envNativeToEthRatio, envId, envPopSelfServe, envRegisterStorageDeposit));
2315
+ await dotns.connect({
2316
+ ...resolveDotnsConnectOptions(options, envAssetHub, envAutoAccountMapping, envContracts, envNativeToEthRatio, envId, envPopSelfServe, envRegisterStorageDeposit),
2317
+ ...options.signer && options.signerAddress ? { onPhoneSigningRequired: (label) => console.log(`
2318
+ Check your phone \u2192 ${label}`) } : {}
2319
+ });
2132
2320
  if (parsed?.isSubdomain) {
2133
2321
  const { owned, owner } = await dotns.checkSubdomainOwnership(parsed.sublabel, parsed.parentLabel);
2134
2322
  if (owned) {
@@ -2153,7 +2341,9 @@ async function deploy(content, domainName = null, options = {}) {
2153
2341
  const contenthashHex = `0x${encodeContenthash(cid)}`;
2154
2342
  await dotns.setContenthash(name, contenthashHex);
2155
2343
  if (options.publish && parsed) {
2156
- await publish(dotns, parsed, options.failOnPublishError);
2344
+ if (preflightPublishNeeded !== false) {
2345
+ await publish(dotns, parsed, options.failOnPublishError);
2346
+ }
2157
2347
  }
2158
2348
  dotns.disconnect();
2159
2349
  });
@@ -2219,6 +2409,16 @@ async function deploy(content, domainName = null, options = {}) {
2219
2409
  sessionCleanup?.();
2220
2410
  }
2221
2411
  }
2412
+ function computePhoneSigningSteps(dotnsPreflight, publishNeeded) {
2413
+ if (!dotnsPreflight || dotnsPreflight.plannedAction === "abort") return [];
2414
+ const steps = [];
2415
+ if (dotnsPreflight.plannedAction === "register") {
2416
+ steps.push("Commitment", "Register");
2417
+ }
2418
+ steps.push("Link content");
2419
+ if (publishNeeded) steps.push("Publish to registry");
2420
+ return steps;
2421
+ }
2222
2422
 
2223
2423
  // src/merkle.ts
2224
2424
  var CidPreservingBlockstore = class {
@@ -2620,6 +2820,10 @@ export {
2620
2820
  readBulletinSlotSigner,
2621
2821
  writeBulletinSlotKey,
2622
2822
  extractBulletinSlotKey,
2823
+ BulletinSlotAuthError,
2824
+ isBulletinAuthActive,
2825
+ pollUntilBulletinAuthorized,
2826
+ waitForBulletinAuthorization,
2623
2827
  getSlotSignerProvider,
2624
2828
  friendlyChainError,
2625
2829
  DEFAULT_BULLETIN_RPC,
@@ -2643,6 +2847,7 @@ export {
2643
2847
  encryptContent,
2644
2848
  __selectStorageProviderModeForTest,
2645
2849
  chooseSignerInput,
2850
+ formatStorageSignerLine,
2646
2851
  storeFile,
2647
2852
  __assignDenseNoncesForTest,
2648
2853
  storeChunkedContent,
@@ -2664,5 +2869,6 @@ export {
2664
2869
  browserUrlFor,
2665
2870
  interpretBitswapResult,
2666
2871
  probeP2pRetrieval,
2667
- deploy
2872
+ deploy,
2873
+ computePhoneSigningSteps
2668
2874
  };
@@ -8,10 +8,10 @@ import {
8
8
  setDeploySentryTag,
9
9
  truncateAddress,
10
10
  withSpan
11
- } from "./chunk-WHMNBSG7.js";
11
+ } from "./chunk-LCKLYFAZ.js";
12
12
  import {
13
13
  validateContractAddresses
14
- } from "./chunk-5K3RI5C2.js";
14
+ } from "./chunk-GL3U7K2B.js";
15
15
  import {
16
16
  NonRetryableError
17
17
  } from "./chunk-ZOC4GITL.js";
@@ -759,8 +759,8 @@ var ReviveClientWrapper = class _ReviveClientWrapper {
759
759
  const decision = classifyTxRetryDecision(e);
760
760
  if (decision === "abort" || attempt === DOTNS_TX_MAX_ATTEMPTS) break;
761
761
  const ms = dotnsRetryBackoffMs(attempt);
762
- const short = (e?.message ?? String(e)).slice(0, 80);
763
- console.log(` ${label}: attempt ${attempt}/${DOTNS_TX_MAX_ATTEMPTS} failed (${short}), retrying in ${ms}ms\u2026`);
762
+ const msg = e?.message ?? String(e);
763
+ console.log(` ${label}: attempt ${attempt}/${DOTNS_TX_MAX_ATTEMPTS} failed (${msg}), retrying in ${ms}ms\u2026`);
764
764
  await new Promise((r) => setTimeout(r, ms));
765
765
  }
766
766
  }
@@ -958,6 +958,7 @@ var DotNS = class {
958
958
  _environmentId = null;
959
959
  _popSelfServe = null;
960
960
  _registerStorageDeposit = MINIMUM_REGISTER_STORAGE_DEPOSIT;
961
+ _onPhoneSigningRequired = void 0;
961
962
  // Test-only seam: consumed once by classifyAliasAccountState() then cleared.
962
963
  // Mirrors the __setDeployRootSpanForTest / __setSentryForTest pattern.
963
964
  _classifyOverrideForTest = null;
@@ -1005,6 +1006,9 @@ var DotNS = class {
1005
1006
  if (options.registerStorageDeposit !== void 0) {
1006
1007
  this._registerStorageDeposit = options.registerStorageDeposit;
1007
1008
  }
1009
+ if (options.onPhoneSigningRequired !== void 0) {
1010
+ this._onPhoneSigningRequired = options.onPhoneSigningRequired;
1011
+ }
1008
1012
  const rpc = options.rpc || process.env.DOTNS_RPC || this.assetHubEndpoints[0];
1009
1013
  this.rpc = rpc;
1010
1014
  this._usesExternalSigner = Boolean(options.signer && options.signerAddress);
@@ -1657,6 +1661,9 @@ var DotNS = class {
1657
1661
  await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
1658
1662
  }
1659
1663
  };
1664
+ console.log(`
1665
+ Linking content...`);
1666
+ this._onPhoneSigningRequired?.("Link content");
1660
1667
  const txRes = await this.contractTransaction(this._contracts.DOTNS_CONTENT_RESOLVER, 0n, DOTNS_CONTENT_RESOLVER_ABI, "setContenthash", [node, contenthashHex], (s) => console.log(` ${s}`), { useNoncePolling: true, verifyEffect });
1661
1668
  const finalOnChain = (await this.getContenthash(domainName) || "0x").toLowerCase();
1662
1669
  if (finalOnChain !== expected) {
@@ -1884,6 +1891,7 @@ var DotNS = class {
1884
1891
  }
1885
1892
  };
1886
1893
  try {
1894
+ this._onPhoneSigningRequired?.("Publish to registry");
1887
1895
  const txRes = await this.contractTransaction(publisher, 0n, PUBLISHER_ABI, "publish", [label], (s) => console.log(` ${s}`), { useNoncePolling: true, verifyEffect });
1888
1896
  const finalPublished = await withTimeout(
1889
1897
  this.contractCall(publisher, PUBLISHER_ABI, "isPublished", [labelhash]),
@@ -2028,6 +2036,7 @@ var DotNS = class {
2028
2036
  this.ensureConnected();
2029
2037
  console.log(`
2030
2038
  Submitting commitment...`);
2039
+ this._onPhoneSigningRequired?.("Commitment");
2031
2040
  const commitTxRes = await this.contractTransaction(this._contracts.DOTNS_REGISTRAR_CONTROLLER, 0n, DOTNS_REGISTRAR_CONTROLLER_ABI, "commit", [commitment], (s) => console.log(` ${s}`));
2032
2041
  logTxResolution(commitTxRes);
2033
2042
  console.log(` Committed at: ${(/* @__PURE__ */ new Date()).toISOString()}`);
@@ -2122,6 +2131,7 @@ var DotNS = class {
2122
2131
  setDeployAttribute("deploy.payment_wei", priceWei.toString());
2123
2132
  console.log(` Oracle price: ${formatEther(priceWei)} PAS`);
2124
2133
  console.log(` Paying: ${formatEther(bufferedPaymentWei)} PAS`);
2134
+ this._onPhoneSigningRequired?.("Register");
2125
2135
  const registerTxRes = await this.contractTransaction(this._contracts.DOTNS_REGISTRAR_CONTROLLER, bufferedPaymentNative, DOTNS_REGISTRAR_CONTROLLER_ABI, "register", [registration], (s) => console.log(` ${s}`));
2126
2136
  logTxResolution(registerTxRes);
2127
2137
  if (registerTxRes.kind === TX_KIND_HASH) {
@@ -2545,6 +2555,7 @@ var DotNS = class {
2545
2555
  this.connected = false;
2546
2556
  }
2547
2557
  this._usesExternalSigner = false;
2558
+ this._onPhoneSigningRequired = void 0;
2548
2559
  }
2549
2560
  };
2550
2561
  var dotns = new DotNS();
@@ -6,15 +6,15 @@ import {
6
6
  resolveDotnsConnectOptions,
7
7
  storeDirectory,
8
8
  storeFile
9
- } from "./chunk-RPU72Z4B.js";
9
+ } from "./chunk-RD2QHYTL.js";
10
10
  import {
11
11
  DotNS
12
- } from "./chunk-EGNMZHMR.js";
12
+ } from "./chunk-WZBAQCA5.js";
13
13
  import {
14
14
  getPopSelfServeConfig,
15
15
  loadEnvironments,
16
16
  resolveEndpoints
17
- } from "./chunk-5K3RI5C2.js";
17
+ } from "./chunk-GL3U7K2B.js";
18
18
  import {
19
19
  NonRetryableError
20
20
  } from "./chunk-ZOC4GITL.js";
@@ -5,9 +5,9 @@ import {
5
5
  _decodeStorageValue,
6
6
  _resetProbeSession,
7
7
  probeChunks
8
- } from "./chunk-3OWKSL7K.js";
9
- import "./chunk-WHMNBSG7.js";
10
- import "./chunk-YXGNQZZF.js";
8
+ } from "./chunk-NP4SLURL.js";
9
+ import "./chunk-LCKLYFAZ.js";
10
+ import "./chunk-2SR5D4CP.js";
11
11
  export {
12
12
  ChainProbeCrossValidationError,
13
13
  ChainProbeMetadataError,
@@ -1,6 +1,3 @@
1
- import { b as AllocationSummary } from '../allocations-B65Is4Md.js';
2
- import '@parity/product-sdk-terminal';
3
-
4
1
  /**
5
2
  * login — QR/mobile sign-in command.
6
3
  *
@@ -8,15 +5,43 @@ import '@parity/product-sdk-terminal';
8
5
  * 1. connect() → existing session (already logged in) OR QR code
9
6
  * 2. Print QR → user scans on phone
10
7
  * 3. waitForLogin() → phone approves → paired
11
- * 4. requestAllocation() — REQUIRED before the fresh session can sign (RFC-0010)
12
- * 5. Print summarizeLogin()
13
- * 6. destroy()
8
+ * 4. getBulletinSigner() — resolves the slot account from host-papp's allowance service.
9
+ * Uses DOT_PRODUCT_ID ("playground.dot") as callingProductId, which matches what the
10
+ * mobile wallet allocated during pairing. A 60s timeout prevents hanging on unresponsive phones.
11
+ * 5. Print summarizeLogin() + slot address on success; map AllowanceError.reason → message on failure
12
+ * 6. Destroy adapters and exit
14
13
  */
15
-
14
+ /**
15
+ * Map an AllowanceError.reason to a user-facing message.
16
+ *
17
+ * - NoSession: the paired session was not found (should not happen right after login)
18
+ * - Rejected: the user dismissed the wallet prompt
19
+ * - NotAvailable: the wallet has no allocation for this product (re-pair needed)
20
+ * - UnexpectedResponse: protocol-level error in the wire exchange
21
+ */
22
+ declare function allocationErrorMessage(reason: string): string;
16
23
  /**
17
24
  * Format a post-login summary. Pure function, unit-testable.
25
+ *
26
+ * @param address SS58 product address (the identity signing on-chain)
27
+ * @param slotAddress SS58 slot account address for Bulletin storage, or null if not obtained
28
+ */
29
+ declare function summarizeLogin(address: string, slotAddress: string | null): string;
30
+ /**
31
+ * Produce the on-chain authorization result message shown after the active-wait completes.
32
+ * Pure function — unit-testable without a real WS connection.
33
+ *
34
+ * - authorized = true → ✓ success message with expiration block (exit-0, show summarizeLogin).
35
+ * - authorized = false → soft-warning: active wait timed out before the authorization landed.
36
+ * Login is still valid — the slot is deterministic and often already authorized; the deploy
37
+ * path re-probes before use and falls back to the pool if still absent. isWarning=true so
38
+ * callers know it's a non-fatal outcome. The timeout bound (timeoutSeconds) is embedded in
39
+ * the message so users understand the ceiling that was used.
18
40
  */
19
- declare function summarizeLogin(address: string, summary: AllocationSummary): string;
41
+ declare function bulletinAuthSummary(authorized: boolean, expiration?: number, timeoutSeconds?: number): {
42
+ message: string;
43
+ isWarning: boolean;
44
+ };
20
45
  interface LoginOptions {
21
46
  suri?: string;
22
47
  }
@@ -25,4 +50,4 @@ interface LoginOptions {
25
50
  */
26
51
  declare function runLogin(envId: string, _opts?: LoginOptions): Promise<void>;
27
52
 
28
- export { type LoginOptions, runLogin, summarizeLogin };
53
+ export { type LoginOptions, allocationErrorMessage, bulletinAuthSummary, runLogin, summarizeLogin };