@bolloon/bolloon-agent 0.4.33 → 0.5.1

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 (214) hide show
  1. package/dist/agents/contacts/chain.js +27 -7
  2. package/dist/agents/execution-supervisor.js +580 -40
  3. package/dist/agents/external-events.js +35 -6
  4. package/dist/agents/gateway-group.js +100 -12
  5. package/dist/agents/goal-criteria.js +10 -3
  6. package/dist/agents/goal-flywheel/block-executor.js +346 -0
  7. package/dist/agents/goal-flywheel/continuation-decision.js +592 -0
  8. package/dist/agents/goal-flywheel/goal-change.js +754 -0
  9. package/dist/agents/goal-flywheel/index.js +26 -0
  10. package/dist/agents/goal-flywheel/memory-layers.js +227 -0
  11. package/dist/agents/goal-flywheel/run-closure.js +809 -0
  12. package/dist/agents/goal-flywheel/skill-candidate.js +628 -0
  13. package/dist/agents/goal-flywheel/types.js +329 -0
  14. package/dist/agents/goal-flywheel/wiring/change.js +288 -0
  15. package/dist/agents/goal-flywheel/wiring/closure.js +697 -0
  16. package/dist/agents/goal-flywheel/wiring/continuation.js +432 -0
  17. package/dist/agents/goal-flywheel/wiring/contract.js +270 -0
  18. package/dist/agents/goal-flywheel/wiring/index.js +26 -0
  19. package/dist/agents/goal-flywheel/wiring/monitor.js +254 -0
  20. package/dist/agents/goal-flywheel/wiring/seams.js +592 -0
  21. package/dist/agents/goal-flywheel/work-contract.js +478 -0
  22. package/dist/agents/goal-flywheel/work-monitor.js +488 -0
  23. package/dist/agents/goal-flywheel-wiring.js +1870 -0
  24. package/dist/agents/goal-state-reducer.js +406 -0
  25. package/dist/agents/goal-store.js +35 -1
  26. package/dist/agents/mobile-flywheel-view.js +144 -0
  27. package/dist/agents/mobile-task-actions-verify.js +74 -0
  28. package/dist/agents/mobile-task-actions.js +178 -0
  29. package/dist/agents/mobile-task-views.js +199 -0
  30. package/dist/agents/model-policy.js +651 -0
  31. package/dist/agents/network-pulse.js +304 -18
  32. package/dist/agents/pi-sdk-tools.js +84 -33
  33. package/dist/agents/pi-sdk.js +157 -42
  34. package/dist/agents/run-store.js +71 -5
  35. package/dist/agents/skill-readiness.js +52 -10
  36. package/dist/agents/subagent-manager.js +209 -3
  37. package/dist/agents/task/task-runner.js +17 -4
  38. package/dist/agents/task-group.js +8 -51
  39. package/dist/agents/task-public-text.js +89 -0
  40. package/dist/agents/workflow-pivot-loop.js +19 -12
  41. package/dist/cli/clipboard.js +65 -0
  42. package/dist/cli/commands/index.js +1 -0
  43. package/dist/cli/commands/tasks.js +350 -2
  44. package/dist/cli/identity-command.js +177 -0
  45. package/dist/cli/ink-app.js +145 -17
  46. package/dist/cli/log-gate.js +386 -0
  47. package/dist/cli/mention-data.js +2 -0
  48. package/dist/cli/model-selector.js +538 -0
  49. package/dist/cli/reply-hygiene.js +54 -0
  50. package/dist/cli/setup-wizard.js +757 -69
  51. package/dist/cli/startup-notice.js +160 -0
  52. package/dist/cli/stores.js +22 -1
  53. package/dist/cli/theme.js +49 -0
  54. package/dist/cli/tui-select.js +970 -0
  55. package/dist/cli/update-commands.js +150 -24
  56. package/dist/cli-entry.js +137 -20
  57. package/dist/electron-build/agents/contacts/chain.js +26 -6
  58. package/dist/electron-build/agents/contacts/chain.js.map +1 -1
  59. package/dist/electron-build/agents/execution-supervisor.js +578 -38
  60. package/dist/electron-build/agents/execution-supervisor.js.map +1 -1
  61. package/dist/electron-build/agents/external-events.js +34 -5
  62. package/dist/electron-build/agents/external-events.js.map +1 -1
  63. package/dist/electron-build/agents/goal-criteria.js +10 -3
  64. package/dist/electron-build/agents/goal-criteria.js.map +1 -1
  65. package/dist/electron-build/agents/goal-flywheel/continuation-decision.js +598 -0
  66. package/dist/electron-build/agents/goal-flywheel/continuation-decision.js.map +1 -0
  67. package/dist/electron-build/agents/goal-flywheel/goal-change.js +773 -0
  68. package/dist/electron-build/agents/goal-flywheel/goal-change.js.map +1 -0
  69. package/dist/electron-build/agents/goal-flywheel/memory-layers.js +269 -0
  70. package/dist/electron-build/agents/goal-flywheel/memory-layers.js.map +1 -0
  71. package/dist/electron-build/agents/goal-flywheel/run-closure.js +818 -0
  72. package/dist/electron-build/agents/goal-flywheel/run-closure.js.map +1 -0
  73. package/dist/electron-build/agents/goal-flywheel/skill-candidate.js +642 -0
  74. package/dist/electron-build/agents/goal-flywheel/skill-candidate.js.map +1 -0
  75. package/dist/electron-build/agents/goal-flywheel/types.js +333 -0
  76. package/dist/electron-build/agents/goal-flywheel/types.js.map +1 -0
  77. package/dist/electron-build/agents/goal-flywheel/wiring/change.js +294 -0
  78. package/dist/electron-build/agents/goal-flywheel/wiring/change.js.map +1 -0
  79. package/dist/electron-build/agents/goal-flywheel/wiring/closure.js +742 -0
  80. package/dist/electron-build/agents/goal-flywheel/wiring/closure.js.map +1 -0
  81. package/dist/electron-build/agents/goal-flywheel/wiring/continuation.js +444 -0
  82. package/dist/electron-build/agents/goal-flywheel/wiring/continuation.js.map +1 -0
  83. package/dist/electron-build/agents/goal-flywheel/wiring/contract.js +279 -0
  84. package/dist/electron-build/agents/goal-flywheel/wiring/contract.js.map +1 -0
  85. package/dist/electron-build/agents/goal-flywheel/wiring/index.js +111 -0
  86. package/dist/electron-build/agents/goal-flywheel/wiring/index.js.map +1 -0
  87. package/dist/electron-build/agents/goal-flywheel/wiring/monitor.js +264 -0
  88. package/dist/electron-build/agents/goal-flywheel/wiring/monitor.js.map +1 -0
  89. package/dist/electron-build/agents/goal-flywheel/wiring/seams.js +612 -0
  90. package/dist/electron-build/agents/goal-flywheel/wiring/seams.js.map +1 -0
  91. package/dist/electron-build/agents/goal-flywheel/work-contract.js +488 -0
  92. package/dist/electron-build/agents/goal-flywheel/work-contract.js.map +1 -0
  93. package/dist/electron-build/agents/goal-flywheel/work-monitor.js +501 -0
  94. package/dist/electron-build/agents/goal-flywheel/work-monitor.js.map +1 -0
  95. package/dist/electron-build/agents/goal-flywheel-wiring.js +1962 -0
  96. package/dist/electron-build/agents/goal-flywheel-wiring.js.map +1 -0
  97. package/dist/electron-build/agents/goal-state-reducer.js +414 -0
  98. package/dist/electron-build/agents/goal-state-reducer.js.map +1 -0
  99. package/dist/electron-build/agents/goal-store.js +35 -1
  100. package/dist/electron-build/agents/goal-store.js.map +1 -1
  101. package/dist/electron-build/agents/model-policy.js +706 -0
  102. package/dist/electron-build/agents/model-policy.js.map +1 -0
  103. package/dist/electron-build/agents/network-pulse.js +307 -19
  104. package/dist/electron-build/agents/network-pulse.js.map +1 -1
  105. package/dist/electron-build/agents/pi-sdk-tools.js +84 -33
  106. package/dist/electron-build/agents/pi-sdk-tools.js.map +1 -1
  107. package/dist/electron-build/agents/pi-sdk-types.js.map +1 -1
  108. package/dist/electron-build/agents/pi-sdk.js +156 -41
  109. package/dist/electron-build/agents/pi-sdk.js.map +1 -1
  110. package/dist/electron-build/agents/run-store.js +75 -6
  111. package/dist/electron-build/agents/run-store.js.map +1 -1
  112. package/dist/electron-build/agents/skill-readiness.js +52 -9
  113. package/dist/electron-build/agents/skill-readiness.js.map +1 -1
  114. package/dist/electron-build/agents/task/task-runner.js +16 -3
  115. package/dist/electron-build/agents/task/task-runner.js.map +1 -1
  116. package/dist/electron-build/agents/workflow-pivot-loop.js +19 -12
  117. package/dist/electron-build/agents/workflow-pivot-loop.js.map +1 -1
  118. package/dist/electron-build/llm/config-store.js +146 -6
  119. package/dist/electron-build/llm/config-store.js.map +1 -1
  120. package/dist/electron-build/llm/connection-probe.js +764 -0
  121. package/dist/electron-build/llm/connection-probe.js.map +1 -0
  122. package/dist/electron-build/llm/custom-provider-store.js +272 -0
  123. package/dist/electron-build/llm/custom-provider-store.js.map +1 -0
  124. package/dist/electron-build/llm/data/provider-catalog-baked.js +23 -0
  125. package/dist/electron-build/llm/data/provider-catalog-baked.js.map +1 -0
  126. package/dist/electron-build/llm/model-catalog.js +778 -0
  127. package/dist/electron-build/llm/model-catalog.js.map +1 -0
  128. package/dist/electron-build/llm/model-discovery.js +1202 -0
  129. package/dist/electron-build/llm/model-discovery.js.map +1 -0
  130. package/dist/electron-build/llm/model-selection.js +1315 -0
  131. package/dist/electron-build/llm/model-selection.js.map +1 -0
  132. package/dist/electron-build/llm/pi-ai.js +70 -11
  133. package/dist/electron-build/llm/pi-ai.js.map +1 -1
  134. package/dist/electron-build/llm/provider-catalog.js +969 -0
  135. package/dist/electron-build/llm/provider-catalog.js.map +1 -0
  136. package/dist/electron-build/llm/provider-registry.js +950 -0
  137. package/dist/electron-build/llm/provider-registry.js.map +1 -0
  138. package/dist/electron-build/llm/tool-name.js +266 -0
  139. package/dist/electron-build/llm/tool-name.js.map +1 -0
  140. package/dist/electron-build/orbitdb/cid-database.js +83 -17
  141. package/dist/electron-build/orbitdb/cid-database.js.map +1 -1
  142. package/dist/electron-build/orbitdb/ipfs-node.js +31 -2
  143. package/dist/electron-build/orbitdb/ipfs-node.js.map +1 -1
  144. package/dist/electron-build/utils/dual-source-facts.js +236 -0
  145. package/dist/electron-build/utils/dual-source-facts.js.map +1 -0
  146. package/dist/electron-build/utils/dual-source.js +347 -0
  147. package/dist/electron-build/utils/dual-source.js.map +1 -0
  148. package/dist/electron-build/utils/update-manager.js +328 -104
  149. package/dist/electron-build/utils/update-manager.js.map +1 -1
  150. package/dist/electron-build/utils/update-state.js +9 -11
  151. package/dist/electron-build/utils/update-state.js.map +1 -1
  152. package/dist/electron-build/utils/version-identity.js +193 -0
  153. package/dist/electron-build/utils/version-identity.js.map +1 -0
  154. package/dist/electron-build/utils/version-info.js +57 -31
  155. package/dist/electron-build/utils/version-info.js.map +1 -1
  156. package/dist/index.js +385 -108
  157. package/dist/ios/bolloon-web.json +10 -0
  158. package/dist/ios/chain-explorer.js +617 -0
  159. package/dist/ios/explorer.html +163 -0
  160. package/dist/ios/index.html +96 -0
  161. package/dist/ios/mobile-agent.js +2 -2
  162. package/dist/ios/mobile-contacts.js +362 -0
  163. package/dist/ios/mobile-core.js +3102 -310
  164. package/dist/ios/mobile-privacy.js +185 -0
  165. package/dist/ios/mobile-tasks.js +339 -0
  166. package/dist/ios/mobile-update.js +1442 -0
  167. package/dist/ios/mobile.css +19 -0
  168. package/dist/ios/mobile.html +96 -0
  169. package/dist/ios/mobile.js +1013 -41
  170. package/dist/ios/routes-chain-index.js +108 -0
  171. package/dist/ios/routes-contacts.js +420 -0
  172. package/dist/ios/routes-llm-config.js +344 -52
  173. package/dist/ios/routes-mobile-tasks.js +353 -0
  174. package/dist/ios/routes-onchain-trade.js +99 -0
  175. package/dist/ios/routes-x402-info.js +1 -0
  176. package/dist/ios/server.js +921 -0
  177. package/dist/ios/sw.js +1 -1
  178. package/dist/ios/task-frame-server.js +211 -0
  179. package/dist/llm/config-store.js +146 -6
  180. package/dist/llm/connection-probe.js +744 -0
  181. package/dist/llm/custom-provider-store.js +224 -0
  182. package/dist/llm/data/provider-catalog-baked.js +19 -0
  183. package/dist/llm/model-catalog.js +717 -0
  184. package/dist/llm/model-discovery.js +1137 -0
  185. package/dist/llm/model-selection.js +1241 -0
  186. package/dist/llm/pi-ai.js +70 -11
  187. package/dist/llm/provider-catalog.js +898 -0
  188. package/dist/llm/provider-registry.js +917 -0
  189. package/dist/llm/tool-name.js +220 -0
  190. package/dist/orbitdb/cid-database.js +81 -16
  191. package/dist/orbitdb/ipfs-node.js +31 -2
  192. package/dist/setup/onboard.js +34 -8
  193. package/dist/utils/dual-source-facts.js +223 -0
  194. package/dist/utils/dual-source.js +297 -0
  195. package/dist/utils/update-cli.js +16 -21
  196. package/dist/utils/update-health.js +35 -5
  197. package/dist/utils/update-manager.js +325 -96
  198. package/dist/utils/update-state.js +13 -10
  199. package/dist/utils/version-identity.js +176 -0
  200. package/dist/utils/version-info.js +27 -22
  201. package/dist/web/bolloon-web.json +10 -0
  202. package/dist/web/mobile-contacts.js +44 -11
  203. package/dist/web/mobile-core.js +2901 -580
  204. package/dist/web/mobile-tasks.js +339 -0
  205. package/dist/web/mobile-update.js +1442 -0
  206. package/dist/web/mobile.css +19 -0
  207. package/dist/web/mobile.html +64 -0
  208. package/dist/web/mobile.js +838 -35
  209. package/dist/web/routes-llm-config.js +344 -52
  210. package/dist/web/routes-mobile-tasks.js +353 -0
  211. package/dist/web/server.js +163 -4
  212. package/dist/web/sw.js +1 -1
  213. package/package.json +3 -1
  214. package/scripts/build-web.ts +36 -0
@@ -1374,7 +1374,7 @@
1374
1374
  }
1375
1375
  function encode(data, alphabet4, bitsPerChar) {
1376
1376
  const pad2 = alphabet4[alphabet4.length - 1] === "=";
1377
- const mask = (1 << bitsPerChar) - 1;
1377
+ const mask2 = (1 << bitsPerChar) - 1;
1378
1378
  let out = "";
1379
1379
  let bits = 0;
1380
1380
  let buffer6 = 0;
@@ -1383,11 +1383,11 @@
1383
1383
  bits += 8;
1384
1384
  while (bits > bitsPerChar) {
1385
1385
  bits -= bitsPerChar;
1386
- out += alphabet4[mask & buffer6 >> bits];
1386
+ out += alphabet4[mask2 & buffer6 >> bits];
1387
1387
  }
1388
1388
  }
1389
1389
  if (bits !== 0) {
1390
- out += alphabet4[mask & buffer6 << bitsPerChar - bits];
1390
+ out += alphabet4[mask2 & buffer6 << bitsPerChar - bits];
1391
1391
  }
1392
1392
  if (pad2) {
1393
1393
  while ((out.length * bitsPerChar & 7) !== 0) {
@@ -3361,8 +3361,8 @@
3361
3361
  }
3362
3362
  function encodeInteger(value2) {
3363
3363
  const contents = new Uint8ArrayList();
3364
- const mask = 128;
3365
- const positive2 = (value2.subarray()[0] & mask) === mask;
3364
+ const mask2 = 128;
3365
+ const positive2 = (value2.subarray()[0] & mask2) === mask2;
3366
3366
  if (positive2) {
3367
3367
  contents.append(Uint8Array.from([0]));
3368
3368
  }
@@ -4974,13 +4974,13 @@
4974
4974
  const windows = Math.ceil(scalarBits2 / W2) + 1;
4975
4975
  const windowSize = 2 ** (W2 - 1);
4976
4976
  const maxNumber = 2 ** W2;
4977
- const mask = bitMask(W2);
4977
+ const mask2 = bitMask(W2);
4978
4978
  const shiftBy = BigInt(W2);
4979
- return { windows, windowSize, mask, maxNumber, shiftBy };
4979
+ return { windows, windowSize, mask: mask2, maxNumber, shiftBy };
4980
4980
  }
4981
4981
  function calcOffsets(n, window2, wOpts) {
4982
- const { windowSize, mask, maxNumber, shiftBy } = wOpts;
4983
- let wbits = Number(n & mask);
4982
+ const { windowSize, mask: mask2, maxNumber, shiftBy } = wOpts;
4983
+ let wbits = Number(n & mask2);
4984
4984
  let nextN = n >> shiftBy;
4985
4985
  if (wbits > windowSize) {
4986
4986
  wbits -= maxNumber;
@@ -6801,18 +6801,18 @@
6801
6801
  * Zig-zag encodes this long bits
6802
6802
  */
6803
6803
  zzEncode() {
6804
- const mask = this.hi >> 31;
6805
- this.hi = ((this.hi << 1 | this.lo >>> 31) ^ mask) >>> 0;
6806
- this.lo = (this.lo << 1 ^ mask) >>> 0;
6804
+ const mask2 = this.hi >> 31;
6805
+ this.hi = ((this.hi << 1 | this.lo >>> 31) ^ mask2) >>> 0;
6806
+ this.lo = (this.lo << 1 ^ mask2) >>> 0;
6807
6807
  return this;
6808
6808
  }
6809
6809
  /**
6810
6810
  * Zig-zag decodes this long bits
6811
6811
  */
6812
6812
  zzDecode() {
6813
- const mask = -(this.lo & 1);
6814
- this.lo = ((this.lo >>> 1 | this.hi << 31) ^ mask) >>> 0;
6815
- this.hi = (this.hi >>> 1 ^ mask) >>> 0;
6813
+ const mask2 = -(this.lo & 1);
6814
+ this.lo = ((this.lo >>> 1 | this.hi << 31) ^ mask2) >>> 0;
6815
+ this.hi = (this.hi >>> 1 ^ mask2) >>> 0;
6816
6816
  return this;
6817
6817
  }
6818
6818
  /**
@@ -12897,9 +12897,9 @@ ${indent}[Error list was empty]`;
12897
12897
  }
12898
12898
  }
12899
12899
  }
12900
- function simpleMaskLength(mask) {
12900
+ function simpleMaskLength(mask2) {
12901
12901
  let ones = 0;
12902
- for (let [index, byte] of mask.entries()) {
12902
+ for (let [index, byte] of mask2.entries()) {
12903
12903
  if (byte === 255) {
12904
12904
  ones += 8;
12905
12905
  continue;
@@ -12911,8 +12911,8 @@ ${indent}[Error list was empty]`;
12911
12911
  if ((byte & 128) != 0) {
12912
12912
  return -1;
12913
12913
  }
12914
- for (let i = index + 1; i < mask.length; i++) {
12915
- if (mask[i] != 0) {
12914
+ for (let i = index + 1; i < mask2.length; i++) {
12915
+ if (mask2[i] != 0) {
12916
12916
  return -1;
12917
12917
  }
12918
12918
  }
@@ -12920,9 +12920,9 @@ ${indent}[Error list was empty]`;
12920
12920
  }
12921
12921
  return ones;
12922
12922
  }
12923
- function maskToHex(mask) {
12923
+ function maskToHex(mask2) {
12924
12924
  let hex = "0x";
12925
- for (const byte of mask) {
12925
+ for (const byte of mask2) {
12926
12926
  hex += (byte >> 4).toString(16) + (byte & 15).toString(16);
12927
12927
  }
12928
12928
  return hex;
@@ -12934,20 +12934,20 @@ ${indent}[Error list was empty]`;
12934
12934
  });
12935
12935
 
12936
12936
  // node_modules/@chainsafe/netmask/dist/src/ip.js
12937
- function maskIp(ip, mask) {
12938
- if (mask.length === IPv6Len && ip.length === IPv4Len && allFF(mask, 0, 11)) {
12939
- mask = mask.slice(12);
12937
+ function maskIp(ip, mask2) {
12938
+ if (mask2.length === IPv6Len && ip.length === IPv4Len && allFF(mask2, 0, 11)) {
12939
+ mask2 = mask2.slice(12);
12940
12940
  }
12941
- if (mask.length === IPv4Len && ip.length === IPv6Len && deepEqual(ip, ipv4Prefix, 0, 11)) {
12941
+ if (mask2.length === IPv4Len && ip.length === IPv6Len && deepEqual(ip, ipv4Prefix, 0, 11)) {
12942
12942
  ip = ip.slice(12);
12943
12943
  }
12944
12944
  const n = ip.length;
12945
- if (n != mask.length) {
12945
+ if (n != mask2.length) {
12946
12946
  throw new Error("Failed to mask ip");
12947
12947
  }
12948
12948
  const out = new Uint8Array(n);
12949
12949
  for (let i = 0; i < n; i++) {
12950
- out[i] = ip[i] & mask[i];
12950
+ out[i] = ip[i] & mask2[i];
12951
12951
  }
12952
12952
  return out;
12953
12953
  }
@@ -13009,10 +13009,10 @@ ${indent}[Error list was empty]`;
13009
13009
  if (Number.isNaN(m2) || String(m2).length !== maskString.length || m2 < 0 || m2 > ipLength * 8) {
13010
13010
  throw new Error("Failed to parse given CIDR: " + s2);
13011
13011
  }
13012
- const mask = cidrMask(m2, 8 * ipLength);
13012
+ const mask2 = cidrMask(m2, 8 * ipLength);
13013
13013
  return {
13014
- network: maskIp(ip, mask),
13015
- mask
13014
+ network: maskIp(ip, mask2),
13015
+ mask: mask2
13016
13016
  };
13017
13017
  }
13018
13018
  function cidrMask(ones, bits) {
@@ -13054,18 +13054,18 @@ ${indent}[Error list was empty]`;
13054
13054
  * @param ipOrCidr either network ip or full cidr address
13055
13055
  * @param mask in case ipOrCidr is network this can be either mask in decimal format or as ip address
13056
13056
  */
13057
- constructor(ipOrCidr, mask) {
13058
- if (mask == null) {
13057
+ constructor(ipOrCidr, mask2) {
13058
+ if (mask2 == null) {
13059
13059
  ({ network: this.network, mask: this.mask } = parseCidr(ipOrCidr));
13060
13060
  } else {
13061
13061
  const ipResult = parseIP(ipOrCidr);
13062
13062
  if (ipResult == null) {
13063
13063
  throw new Error("Failed to parse network");
13064
13064
  }
13065
- mask = String(mask);
13066
- const m2 = parseInt(mask, 10);
13067
- if (Number.isNaN(m2) || String(m2).length !== mask.length || m2 < 0 || m2 > ipResult.length * 8) {
13068
- const maskResult = parseIP(mask);
13065
+ mask2 = String(mask2);
13066
+ const m2 = parseInt(mask2, 10);
13067
+ if (Number.isNaN(m2) || String(m2).length !== mask2.length || m2 < 0 || m2 > ipResult.length * 8) {
13068
+ const maskResult = parseIP(mask2);
13069
13069
  if (maskResult == null) {
13070
13070
  throw new Error("Failed to parse mask");
13071
13071
  }
@@ -13087,8 +13087,8 @@ ${indent}[Error list was empty]`;
13087
13087
  /**Serializes back to string format */
13088
13088
  toString() {
13089
13089
  const l = simpleMaskLength(this.mask);
13090
- const mask = l !== -1 ? String(l) : maskToHex(this.mask);
13091
- return ipToString(this.network) + "/" + mask;
13090
+ const mask2 = l !== -1 ? String(l) : maskToHex(this.mask);
13091
+ return ipToString(this.network) + "/" + mask2;
13092
13092
  }
13093
13093
  };
13094
13094
  }
@@ -13309,11 +13309,11 @@ ${indent}[Error list was empty]`;
13309
13309
  }
13310
13310
  }
13311
13311
  var Netmask4Impl = class _Netmask4Impl {
13312
- constructor(net, mask) {
13312
+ constructor(net, mask2) {
13313
13313
  if (typeof net !== "string") {
13314
13314
  throw new Error("Missing `net' parameter");
13315
13315
  }
13316
- let maskStr = mask;
13316
+ let maskStr = mask2;
13317
13317
  if (!maskStr) {
13318
13318
  const parts = net.split("/", 2);
13319
13319
  net = parts[0];
@@ -13498,11 +13498,11 @@ ${indent}[Error list was empty]`;
13498
13498
  }
13499
13499
  }
13500
13500
  var Netmask6Impl = class _Netmask6Impl {
13501
- constructor(net, mask) {
13501
+ constructor(net, mask2) {
13502
13502
  if (typeof net !== "string") {
13503
13503
  throw new Error("Missing `net' parameter");
13504
13504
  }
13505
- let prefixLen = mask;
13505
+ let prefixLen = mask2;
13506
13506
  if (prefixLen === void 0 || prefixLen === null) {
13507
13507
  const slashIdx = net.indexOf("/");
13508
13508
  if (slashIdx !== -1) {
@@ -13585,15 +13585,15 @@ ${indent}[Error list was empty]`;
13585
13585
  } });
13586
13586
  var netmask6_1 = require_netmask6();
13587
13587
  var Netmask2 = class _Netmask {
13588
- constructor(net, mask) {
13588
+ constructor(net, mask2) {
13589
13589
  if (typeof net !== "string") {
13590
13590
  throw new Error("Missing `net' parameter");
13591
13591
  }
13592
13592
  const addrPart = net.indexOf("/") !== -1 ? net.substring(0, net.indexOf("/")) : net;
13593
13593
  if (addrPart.indexOf(":") !== -1) {
13594
- this._impl = new netmask6_1.Netmask6Impl(net, mask);
13594
+ this._impl = new netmask6_1.Netmask6Impl(net, mask2);
13595
13595
  } else {
13596
- this._impl = new netmask4_1.Netmask4Impl(net, mask);
13596
+ this._impl = new netmask4_1.Netmask4Impl(net, mask2);
13597
13597
  }
13598
13598
  this.base = this._impl.base;
13599
13599
  this.mask = this._impl.mask;
@@ -18414,11 +18414,11 @@ ${indent}[Error list was empty]`;
18414
18414
  function toToken2(data, pos, prefix, length20, options2) {
18415
18415
  const totLength = prefix + length20;
18416
18416
  assertEnoughData(data, pos, totLength);
18417
- const tok = new Token(Type.string, toStr(data, pos + prefix, pos + totLength), totLength);
18417
+ const tok2 = new Token(Type.string, toStr(data, pos + prefix, pos + totLength), totLength);
18418
18418
  if (options2.retainStringBytes === true) {
18419
- tok.byteValue = data.slice(pos + prefix, pos + totLength);
18419
+ tok2.byteValue = data.slice(pos + prefix, pos + totLength);
18420
18420
  }
18421
- return tok;
18421
+ return tok2;
18422
18422
  }
18423
18423
  function decodeStringCompact(data, pos, minor, options2) {
18424
18424
  return toToken2(data, pos, 1, minor, options2);
@@ -21481,18 +21481,18 @@ ${indent}[Error list was empty]`;
21481
21481
  * Zig-zag encodes this long bits
21482
21482
  */
21483
21483
  zzEncode() {
21484
- const mask = this.hi >> 31;
21485
- this.hi = ((this.hi << 1 | this.lo >>> 31) ^ mask) >>> 0;
21486
- this.lo = (this.lo << 1 ^ mask) >>> 0;
21484
+ const mask2 = this.hi >> 31;
21485
+ this.hi = ((this.hi << 1 | this.lo >>> 31) ^ mask2) >>> 0;
21486
+ this.lo = (this.lo << 1 ^ mask2) >>> 0;
21487
21487
  return this;
21488
21488
  }
21489
21489
  /**
21490
21490
  * Zig-zag decodes this long bits
21491
21491
  */
21492
21492
  zzDecode() {
21493
- const mask = -(this.lo & 1);
21494
- this.lo = ((this.lo >>> 1 | this.hi << 31) ^ mask) >>> 0;
21495
- this.hi = (this.hi >>> 1 ^ mask) >>> 0;
21493
+ const mask2 = -(this.lo & 1);
21494
+ this.lo = ((this.lo >>> 1 | this.hi << 31) ^ mask2) >>> 0;
21495
+ this.hi = (this.hi >>> 1 ^ mask2) >>> 0;
21496
21496
  return this;
21497
21497
  }
21498
21498
  /**
@@ -24141,18 +24141,18 @@ ${indent}[Error list was empty]`;
24141
24141
  * Zig-zag encodes this long bits
24142
24142
  */
24143
24143
  zzEncode() {
24144
- const mask = this.hi >> 31;
24145
- this.hi = ((this.hi << 1 | this.lo >>> 31) ^ mask) >>> 0;
24146
- this.lo = (this.lo << 1 ^ mask) >>> 0;
24144
+ const mask2 = this.hi >> 31;
24145
+ this.hi = ((this.hi << 1 | this.lo >>> 31) ^ mask2) >>> 0;
24146
+ this.lo = (this.lo << 1 ^ mask2) >>> 0;
24147
24147
  return this;
24148
24148
  }
24149
24149
  /**
24150
24150
  * Zig-zag decodes this long bits
24151
24151
  */
24152
24152
  zzDecode() {
24153
- const mask = -(this.lo & 1);
24154
- this.lo = ((this.lo >>> 1 | this.hi << 31) ^ mask) >>> 0;
24155
- this.hi = (this.hi >>> 1 ^ mask) >>> 0;
24153
+ const mask2 = -(this.lo & 1);
24154
+ this.lo = ((this.lo >>> 1 | this.hi << 31) ^ mask2) >>> 0;
24155
+ this.hi = (this.hi >>> 1 ^ mask2) >>> 0;
24156
24156
  return this;
24157
24157
  }
24158
24158
  /**
@@ -28712,18 +28712,18 @@ ${indent}[Error list was empty]`;
28712
28712
  }
28713
28713
  function multiaddrToIpNet(ma) {
28714
28714
  const config = getNetConfig(ma);
28715
- let mask = config.cidr;
28715
+ let mask2 = config.cidr;
28716
28716
  if (config.type !== "ip4" && config.type !== "ip6") {
28717
28717
  throw new InvalidParametersError(`Multiaddr ${ma} was not an IPv4 or IPv6 address`);
28718
28718
  }
28719
- if (mask == null) {
28719
+ if (mask2 == null) {
28720
28720
  switch (config.type) {
28721
28721
  case "ip4": {
28722
- mask = 32;
28722
+ mask2 = 32;
28723
28723
  break;
28724
28724
  }
28725
28725
  case "ip6": {
28726
- mask = 128;
28726
+ mask2 = 128;
28727
28727
  break;
28728
28728
  }
28729
28729
  default: {
@@ -28731,7 +28731,7 @@ ${indent}[Error list was empty]`;
28731
28731
  }
28732
28732
  }
28733
28733
  }
28734
- return new IpNet(config.host, mask);
28734
+ return new IpNet(config.host, mask2);
28735
28735
  }
28736
28736
  function isDirect(ma) {
28737
28737
  return !Circuit.exactMatch(ma);
@@ -35926,12 +35926,12 @@ ${[...listenStats.errors.entries()].map(([addr, err2]) => {
35926
35926
  g[i] &= 8191;
35927
35927
  }
35928
35928
  g[9] -= 1 << 13;
35929
- let mask = (c ^ 1) - 1;
35929
+ let mask2 = (c ^ 1) - 1;
35930
35930
  for (let i = 0; i < 10; i++)
35931
- g[i] &= mask;
35932
- mask = ~mask;
35931
+ g[i] &= mask2;
35932
+ mask2 = ~mask2;
35933
35933
  for (let i = 0; i < 10; i++)
35934
- h3[i] = h3[i] & mask | g[i];
35934
+ h3[i] = h3[i] & mask2 | g[i];
35935
35935
  h3[0] = (h3[0] | h3[1] << 13) & 65535;
35936
35936
  h3[1] = (h3[1] >>> 3 | h3[2] << 10) & 65535;
35937
35937
  h3[2] = (h3[2] >>> 6 | h3[3] << 7) & 65535;
@@ -36669,7 +36669,7 @@ ${[...listenStats.errors.entries()].map(([addr, err2]) => {
36669
36669
  }
36670
36670
  function encode10(data, alphabet4, bitsPerChar) {
36671
36671
  const pad2 = alphabet4[alphabet4.length - 1] === "=";
36672
- const mask = (1 << bitsPerChar) - 1;
36672
+ const mask2 = (1 << bitsPerChar) - 1;
36673
36673
  let out = "";
36674
36674
  let bits = 0;
36675
36675
  let buffer6 = 0;
@@ -36678,11 +36678,11 @@ ${[...listenStats.errors.entries()].map(([addr, err2]) => {
36678
36678
  bits += 8;
36679
36679
  while (bits > bitsPerChar) {
36680
36680
  bits -= bitsPerChar;
36681
- out += alphabet4[mask & buffer6 >> bits];
36681
+ out += alphabet4[mask2 & buffer6 >> bits];
36682
36682
  }
36683
36683
  }
36684
36684
  if (bits !== 0) {
36685
- out += alphabet4[mask & buffer6 << bitsPerChar - bits];
36685
+ out += alphabet4[mask2 & buffer6 << bitsPerChar - bits];
36686
36686
  }
36687
36687
  if (pad2) {
36688
36688
  while ((out.length * bitsPerChar & 7) !== 0) {
@@ -38978,18 +38978,18 @@ ${[...listenStats.errors.entries()].map(([addr, err2]) => {
38978
38978
  * Zig-zag encodes this long bits
38979
38979
  */
38980
38980
  zzEncode() {
38981
- const mask = this.hi >> 31;
38982
- this.hi = ((this.hi << 1 | this.lo >>> 31) ^ mask) >>> 0;
38983
- this.lo = (this.lo << 1 ^ mask) >>> 0;
38981
+ const mask2 = this.hi >> 31;
38982
+ this.hi = ((this.hi << 1 | this.lo >>> 31) ^ mask2) >>> 0;
38983
+ this.lo = (this.lo << 1 ^ mask2) >>> 0;
38984
38984
  return this;
38985
38985
  }
38986
38986
  /**
38987
38987
  * Zig-zag decodes this long bits
38988
38988
  */
38989
38989
  zzDecode() {
38990
- const mask = -(this.lo & 1);
38991
- this.lo = ((this.lo >>> 1 | this.hi << 31) ^ mask) >>> 0;
38992
- this.hi = (this.hi >>> 1 ^ mask) >>> 0;
38990
+ const mask2 = -(this.lo & 1);
38991
+ this.lo = ((this.lo >>> 1 | this.hi << 31) ^ mask2) >>> 0;
38992
+ this.hi = (this.hi >>> 1 ^ mask2) >>> 0;
38993
38993
  return this;
38994
38994
  }
38995
38995
  /**
@@ -41706,18 +41706,18 @@ ${[...listenStats.errors.entries()].map(([addr, err2]) => {
41706
41706
  * Zig-zag encodes this long bits
41707
41707
  */
41708
41708
  zzEncode() {
41709
- const mask = this.hi >> 31;
41710
- this.hi = ((this.hi << 1 | this.lo >>> 31) ^ mask) >>> 0;
41711
- this.lo = (this.lo << 1 ^ mask) >>> 0;
41709
+ const mask2 = this.hi >> 31;
41710
+ this.hi = ((this.hi << 1 | this.lo >>> 31) ^ mask2) >>> 0;
41711
+ this.lo = (this.lo << 1 ^ mask2) >>> 0;
41712
41712
  return this;
41713
41713
  }
41714
41714
  /**
41715
41715
  * Zig-zag decodes this long bits
41716
41716
  */
41717
41717
  zzDecode() {
41718
- const mask = -(this.lo & 1);
41719
- this.lo = ((this.lo >>> 1 | this.hi << 31) ^ mask) >>> 0;
41720
- this.hi = (this.hi >>> 1 ^ mask) >>> 0;
41718
+ const mask2 = -(this.lo & 1);
41719
+ this.lo = ((this.lo >>> 1 | this.hi << 31) ^ mask2) >>> 0;
41720
+ this.hi = (this.hi >>> 1 ^ mask2) >>> 0;
41721
41721
  return this;
41722
41722
  }
41723
41723
  /**
@@ -43635,18 +43635,18 @@ ${[...listenStats.errors.entries()].map(([addr, err2]) => {
43635
43635
  * Zig-zag encodes this long bits
43636
43636
  */
43637
43637
  zzEncode() {
43638
- const mask = this.hi >> 31;
43639
- this.hi = ((this.hi << 1 | this.lo >>> 31) ^ mask) >>> 0;
43640
- this.lo = (this.lo << 1 ^ mask) >>> 0;
43638
+ const mask2 = this.hi >> 31;
43639
+ this.hi = ((this.hi << 1 | this.lo >>> 31) ^ mask2) >>> 0;
43640
+ this.lo = (this.lo << 1 ^ mask2) >>> 0;
43641
43641
  return this;
43642
43642
  }
43643
43643
  /**
43644
43644
  * Zig-zag decodes this long bits
43645
43645
  */
43646
43646
  zzDecode() {
43647
- const mask = -(this.lo & 1);
43648
- this.lo = ((this.lo >>> 1 | this.hi << 31) ^ mask) >>> 0;
43649
- this.hi = (this.hi >>> 1 ^ mask) >>> 0;
43647
+ const mask2 = -(this.lo & 1);
43648
+ this.lo = ((this.lo >>> 1 | this.hi << 31) ^ mask2) >>> 0;
43649
+ this.hi = (this.hi >>> 1 ^ mask2) >>> 0;
43650
43650
  return this;
43651
43651
  }
43652
43652
  /**
@@ -45169,18 +45169,18 @@ ${[...listenStats.errors.entries()].map(([addr, err2]) => {
45169
45169
  * Zig-zag encodes this long bits
45170
45170
  */
45171
45171
  zzEncode() {
45172
- const mask = this.hi >> 31;
45173
- this.hi = ((this.hi << 1 | this.lo >>> 31) ^ mask) >>> 0;
45174
- this.lo = (this.lo << 1 ^ mask) >>> 0;
45172
+ const mask2 = this.hi >> 31;
45173
+ this.hi = ((this.hi << 1 | this.lo >>> 31) ^ mask2) >>> 0;
45174
+ this.lo = (this.lo << 1 ^ mask2) >>> 0;
45175
45175
  return this;
45176
45176
  }
45177
45177
  /**
45178
45178
  * Zig-zag decodes this long bits
45179
45179
  */
45180
45180
  zzDecode() {
45181
- const mask = -(this.lo & 1);
45182
- this.lo = ((this.lo >>> 1 | this.hi << 31) ^ mask) >>> 0;
45183
- this.hi = (this.hi >>> 1 ^ mask) >>> 0;
45181
+ const mask2 = -(this.lo & 1);
45182
+ this.lo = ((this.lo >>> 1 | this.hi << 31) ^ mask2) >>> 0;
45183
+ this.hi = (this.hi >>> 1 ^ mask2) >>> 0;
45184
45184
  return this;
45185
45185
  }
45186
45186
  /**
@@ -66360,8 +66360,8 @@ ${[...listenStats.errors.entries()].map(([addr, err2]) => {
66360
66360
  const view = new DataView(localKadId.buffer, localKadId.byteOffset, localKadId.byteLength);
66361
66361
  const localPrefix = view.getUint16(0, false);
66362
66362
  const toggledLocalPrefix = localPrefix ^ 32768 >> targetCommonPrefixLength;
66363
- const mask = 65535 << 16 - (targetCommonPrefixLength + 1);
66364
- const targetPrefix = toggledLocalPrefix & mask | randomPrefix & ~mask;
66363
+ const mask2 = 65535 << 16 - (targetCommonPrefixLength + 1);
66364
+ const targetPrefix = toggledLocalPrefix & mask2 | randomPrefix & ~mask2;
66365
66365
  const keyPrefix3 = generated_prefix_list_browser_default[targetPrefix];
66366
66366
  const keyBuffer = new ArrayBuffer(34);
66367
66367
  const keyView = new DataView(keyBuffer, 0, keyBuffer.byteLength);
@@ -67641,18 +67641,18 @@ ${[...listenStats.errors.entries()].map(([addr, err2]) => {
67641
67641
  * Zig-zag encodes this long bits
67642
67642
  */
67643
67643
  zzEncode() {
67644
- const mask = this.hi >> 31;
67645
- this.hi = ((this.hi << 1 | this.lo >>> 31) ^ mask) >>> 0;
67646
- this.lo = (this.lo << 1 ^ mask) >>> 0;
67644
+ const mask2 = this.hi >> 31;
67645
+ this.hi = ((this.hi << 1 | this.lo >>> 31) ^ mask2) >>> 0;
67646
+ this.lo = (this.lo << 1 ^ mask2) >>> 0;
67647
67647
  return this;
67648
67648
  }
67649
67649
  /**
67650
67650
  * Zig-zag decodes this long bits
67651
67651
  */
67652
67652
  zzDecode() {
67653
- const mask = -(this.lo & 1);
67654
- this.lo = ((this.lo >>> 1 | this.hi << 31) ^ mask) >>> 0;
67655
- this.hi = (this.hi >>> 1 ^ mask) >>> 0;
67653
+ const mask2 = -(this.lo & 1);
67654
+ this.lo = ((this.lo >>> 1 | this.hi << 31) ^ mask2) >>> 0;
67655
+ this.hi = (this.hi >>> 1 ^ mask2) >>> 0;
67656
67656
  return this;
67657
67657
  }
67658
67658
  /**
@@ -75209,18 +75209,18 @@ ${[...listenStats.errors.entries()].map(([addr, err2]) => {
75209
75209
  * Zig-zag encodes this long bits
75210
75210
  */
75211
75211
  zzEncode() {
75212
- const mask = this.hi >> 31;
75213
- this.hi = ((this.hi << 1 | this.lo >>> 31) ^ mask) >>> 0;
75214
- this.lo = (this.lo << 1 ^ mask) >>> 0;
75212
+ const mask2 = this.hi >> 31;
75213
+ this.hi = ((this.hi << 1 | this.lo >>> 31) ^ mask2) >>> 0;
75214
+ this.lo = (this.lo << 1 ^ mask2) >>> 0;
75215
75215
  return this;
75216
75216
  }
75217
75217
  /**
75218
75218
  * Zig-zag decodes this long bits
75219
75219
  */
75220
75220
  zzDecode() {
75221
- const mask = -(this.lo & 1);
75222
- this.lo = ((this.lo >>> 1 | this.hi << 31) ^ mask) >>> 0;
75223
- this.hi = (this.hi >>> 1 ^ mask) >>> 0;
75221
+ const mask2 = -(this.lo & 1);
75222
+ this.lo = ((this.lo >>> 1 | this.hi << 31) ^ mask2) >>> 0;
75223
+ this.hi = (this.hi >>> 1 ^ mask2) >>> 0;
75224
75224
  return this;
75225
75225
  }
75226
75226
  /**
@@ -81244,7 +81244,7 @@ ${[...listenStats.errors.entries()].map(([addr, err2]) => {
81244
81244
  let carry = 0;
81245
81245
  let pos = 0;
81246
81246
  const max = powers[from6];
81247
- const mask = powers[to] - 1;
81247
+ const mask2 = powers[to] - 1;
81248
81248
  const res = [];
81249
81249
  for (const n of data) {
81250
81250
  anumber6(n);
@@ -81255,13 +81255,13 @@ ${[...listenStats.errors.entries()].map(([addr, err2]) => {
81255
81255
  throw new Error(`convertRadix2: carry overflow pos=${pos} from=${from6}`);
81256
81256
  pos += from6;
81257
81257
  for (; pos >= to; pos -= to)
81258
- res.push((carry >> pos - to & mask) >>> 0);
81258
+ res.push((carry >> pos - to & mask2) >>> 0);
81259
81259
  const pow = powers[pos];
81260
81260
  if (pow === void 0)
81261
81261
  throw new Error("invalid carry");
81262
81262
  carry &= pow - 1;
81263
81263
  }
81264
- carry = carry << to - pos & mask;
81264
+ carry = carry << to - pos & mask2;
81265
81265
  if (!padding2 && pos >= from6)
81266
81266
  throw new Error("Excess padding");
81267
81267
  if (!padding2 && carry > 0)
@@ -84769,13 +84769,13 @@ zoo`.split("\n");
84769
84769
  const windows = Math.ceil(scalarBits2 / W2) + 1;
84770
84770
  const windowSize = 2 ** (W2 - 1);
84771
84771
  const maxNumber = 2 ** W2;
84772
- const mask = bitMask2(W2);
84772
+ const mask2 = bitMask2(W2);
84773
84773
  const shiftBy = BigInt(W2);
84774
- return { windows, windowSize, mask, maxNumber, shiftBy };
84774
+ return { windows, windowSize, mask: mask2, maxNumber, shiftBy };
84775
84775
  }
84776
84776
  function calcOffsets2(n, window2, wOpts) {
84777
- const { windowSize, mask, maxNumber, shiftBy } = wOpts;
84778
- let wbits = Number(n & mask);
84777
+ const { windowSize, mask: mask2, maxNumber, shiftBy } = wOpts;
84778
+ let wbits = Number(n & mask2);
84779
84779
  let nextN = n >> shiftBy;
84780
84780
  if (wbits > windowSize) {
84781
84781
  wbits -= maxNumber;
@@ -87666,6 +87666,406 @@ ${r.steps.map((s3) => `${s3.ok ? "\u2713" : "\u2717"} ${s3.step}: ${s3.note}`).j
87666
87666
  }
87667
87667
  });
87668
87668
 
87669
+ // src/agents/contacts/grant-payload.ts
87670
+ function canonicalGrantPayload(g) {
87671
+ return JSON.stringify({
87672
+ grantId: g.grantId,
87673
+ identityId: g.identityId,
87674
+ ownerDid: g.ownerDid,
87675
+ level: g.level,
87676
+ channels: g.channels,
87677
+ contactScope: g.contactScope,
87678
+ taskScope: g.taskScope,
87679
+ contentScope: g.contentScope,
87680
+ replyWakeAllowed: g.replyWakeAllowed,
87681
+ autoSend: g.autoSend,
87682
+ sensitiveContentAllowed: g.sensitiveContentAllowed,
87683
+ newRecipientAllowed: g.newRecipientAllowed,
87684
+ grantedAt: g.grantedAt,
87685
+ grantedBy: g.grantedBy,
87686
+ grantedVia: g.grantedVia,
87687
+ deviceIds: [...g.deviceIds].sort(),
87688
+ status: g.status,
87689
+ revokedAt: g.revokedAt || "",
87690
+ grantVersion: g.grantVersion
87691
+ });
87692
+ }
87693
+ function canonicalRevocationPayload(r) {
87694
+ return JSON.stringify({ grantId: r.grantId, grantVersion: r.grantVersion, revokedAt: r.revokedAt, by: r.by });
87695
+ }
87696
+ function presetForChoice(choice) {
87697
+ const base6 = { channels: "both", replyWakeAllowed: true, autoSend: true, newRecipientAllowed: false };
87698
+ switch (choice) {
87699
+ case "task_once":
87700
+ return { ...base6, level: "task_once", contactScope: "verified_contacts", taskScope: "current_goal", contentScope: "normal", autoSend: false, sensitiveContentAllowed: false };
87701
+ case "persistent":
87702
+ return { ...base6, level: "persistent", contactScope: "verified_contacts", taskScope: "all_future_goals", contentScope: "normal", sensitiveContentAllowed: false };
87703
+ case "full_contact_access":
87704
+ return { ...base6, level: "full_contact_access", contactScope: "all_contacts", taskScope: "all_future_goals", contentScope: "sensitive", sensitiveContentAllowed: true };
87705
+ }
87706
+ }
87707
+ var init_grant_payload = __esm({
87708
+ "src/agents/contacts/grant-payload.ts"() {
87709
+ "use strict";
87710
+ }
87711
+ });
87712
+
87713
+ // src/web/mobile-contacts.ts
87714
+ var mobile_contacts_exports = {};
87715
+ __export(mobile_contacts_exports, {
87716
+ authorizeFromPhone: () => authorizeFromPhone,
87717
+ bindFromPhone: () => bindFromPhone,
87718
+ buildPhoneCard: () => buildPhoneCard,
87719
+ decideApprovalFromPhone: () => decideApprovalFromPhone,
87720
+ ed25519Available: () => ed25519Available,
87721
+ flushQueuedGrants: () => flushQueuedGrants,
87722
+ loadCapabilityCopy: () => loadCapabilityCopy,
87723
+ loadFromDesktop: () => loadFromDesktop,
87724
+ loadOrCreateDeviceKey: () => loadOrCreateDeviceKey,
87725
+ pendingQueue: () => pendingQueue,
87726
+ registerDeviceOnDesktop: () => registerDeviceOnDesktop,
87727
+ revokeFromPhone: () => revokeFromPhone,
87728
+ saveCapabilityCopy: () => saveCapabilityCopy,
87729
+ sha256HexOf: () => sha256HexOf,
87730
+ signGrantOnDevice: () => signGrantOnDevice,
87731
+ signPayloadOnDevice: () => signPayloadOnDevice,
87732
+ signPayloadWithMaterial: () => signPayloadWithMaterial,
87733
+ signRevocationOnDevice: () => signRevocationOnDevice,
87734
+ verifyFromPhone: () => verifyFromPhone
87735
+ });
87736
+ function ed25519Available(c) {
87737
+ const cobj = cryptoOf(c);
87738
+ if (!cobj?.subtle) return false;
87739
+ try {
87740
+ const alg = { name: "Ed25519" };
87741
+ return typeof cobj.subtle.importKey === "function" && !!alg;
87742
+ } catch {
87743
+ return false;
87744
+ }
87745
+ }
87746
+ function bytesOf(s2) {
87747
+ const src4 = new TextEncoder().encode(s2);
87748
+ const out = new Uint8Array(new ArrayBuffer(src4.byteLength));
87749
+ out.set(src4);
87750
+ return out;
87751
+ }
87752
+ function b64(buf) {
87753
+ const arr = new Uint8Array(buf);
87754
+ let out = "";
87755
+ for (let i = 0; i < arr.length; i++) out += String.fromCharCode(arr[i]);
87756
+ return btoa(out);
87757
+ }
87758
+ function pemFromSpki(spki) {
87759
+ const b = b64(spki).replace(/(.{64})/g, "$1\n").trim();
87760
+ return `-----BEGIN PUBLIC KEY-----
87761
+ ${b}
87762
+ -----END PUBLIC KEY-----
87763
+ `;
87764
+ }
87765
+ async function sha256Hex(c, text) {
87766
+ const d2 = await c.subtle.digest("SHA-256", bytesOf(text));
87767
+ return [...new Uint8Array(d2)].map((x) => x.toString(16).padStart(2, "0")).join("");
87768
+ }
87769
+ async function loadOrCreateDeviceKey(storage2, c) {
87770
+ const cobj = cryptoOf(c);
87771
+ if (!cobj?.subtle) throw new Error("device_signing_unavailable: \u672C\u673A\u6CA1\u6709 WebCrypto");
87772
+ const raw = storage2.getItem(K_DEVICE);
87773
+ if (raw) {
87774
+ try {
87775
+ const parsed = JSON.parse(raw);
87776
+ if (parsed?.privateKeyJwk && parsed?.publicKeyPem && parsed?.deviceId) return parsed;
87777
+ } catch {
87778
+ }
87779
+ }
87780
+ let pair;
87781
+ try {
87782
+ pair = await cobj.subtle.generateKey({ name: "Ed25519" }, true, ["sign", "verify"]);
87783
+ } catch (err2) {
87784
+ throw new Error(`device_signing_unavailable: ${String(err2?.message || err2)}`);
87785
+ }
87786
+ const publicKeyPem = pemFromSpki(await cobj.subtle.exportKey("spki", pair.publicKey));
87787
+ const privateKeyJwk = await cobj.subtle.exportKey("jwk", pair.privateKey);
87788
+ const deviceId = `dev-${(await sha256Hex(cobj, publicKeyPem)).slice(0, 12)}`;
87789
+ const material = { deviceId, publicKeyPem, privateKeyJwk };
87790
+ storage2.setItem(K_DEVICE, JSON.stringify(material));
87791
+ return material;
87792
+ }
87793
+ async function signGrantOnDevice(grant, storage2, c) {
87794
+ return signPayloadOnDevice(canonicalGrantPayload(grant), storage2, c);
87795
+ }
87796
+ async function signRevocationOnDevice(rev, storage2, c) {
87797
+ return signPayloadOnDevice(canonicalRevocationPayload(rev), storage2, c);
87798
+ }
87799
+ async function signPayloadOnDevice(payload, storage2, c) {
87800
+ const cobj = cryptoOf(c);
87801
+ if (!cobj?.subtle) throw new Error("device_signing_unavailable: \u672C\u673A\u6CA1\u6709 WebCrypto");
87802
+ const material = await loadOrCreateDeviceKey(storage2, cobj);
87803
+ const key = await cobj.subtle.importKey("jwk", material.privateKeyJwk, { name: "Ed25519" }, false, ["sign"]);
87804
+ const bytes = bytesOf(String(payload ?? ""));
87805
+ const sig = await cobj.subtle.sign({ name: "Ed25519" }, key, bytes);
87806
+ return { deviceId: material.deviceId, alg: "ed25519", payloadHash: await sha256Hex(cobj, String(payload ?? "")), sig: b64(sig) };
87807
+ }
87808
+ async function sha256HexOf(text, c) {
87809
+ const cobj = cryptoOf(c);
87810
+ if (!cobj?.subtle) throw new Error("device_signing_unavailable: \u672C\u673A\u6CA1\u6709 WebCrypto");
87811
+ return sha256Hex(cobj, String(text ?? ""));
87812
+ }
87813
+ async function signPayloadWithMaterial(payload, material, c) {
87814
+ const cobj = cryptoOf(c);
87815
+ if (!cobj?.subtle) throw new Error("device_signing_unavailable: \u672C\u673A\u6CA1\u6709 WebCrypto");
87816
+ const key = await cobj.subtle.importKey("jwk", material.privateKeyJwk, { name: "Ed25519" }, false, ["sign"]);
87817
+ const sig = await cobj.subtle.sign({ name: "Ed25519" }, key, bytesOf(String(payload ?? "")));
87818
+ return { deviceId: material.deviceId, alg: "ed25519", payloadHash: await sha256Hex(cobj, String(payload ?? "")), sig: b64(sig) };
87819
+ }
87820
+ async function post(base6, path, body, f) {
87821
+ const res = await f(`${String(base6).replace(/\/+$/, "")}${path}`, {
87822
+ method: "POST",
87823
+ headers: { "Content-Type": "application/json" },
87824
+ body: JSON.stringify(body ?? {})
87825
+ });
87826
+ let json = null;
87827
+ try {
87828
+ json = await res.json();
87829
+ } catch {
87830
+ }
87831
+ return { status: res.status, json };
87832
+ }
87833
+ async function get(base6, path, f) {
87834
+ const res = await f(`${String(base6).replace(/\/+$/, "")}${path}`);
87835
+ let json = null;
87836
+ try {
87837
+ json = await res.json();
87838
+ } catch {
87839
+ }
87840
+ return { status: res.status, json };
87841
+ }
87842
+ async function registerDeviceOnDesktop(base6, storage2, opts = {}) {
87843
+ const f = opts.fetchImpl || fetch;
87844
+ const dev = await loadOrCreateDeviceKey(storage2, opts.cryptoObj);
87845
+ const r = await post(base6, "/api/contacts/devices", { deviceId: dev.deviceId, publicKeyPem: dev.publicKeyPem, label: opts.label || "\u624B\u673A" }, f);
87846
+ if (r.status !== 200 || !r.json?.ok) return { ok: false, error: `\u684C\u9762\u672A\u63A5\u53D7\u8BBE\u5907\u767B\u8BB0: ${r.json?.error || r.status}`, data: dev };
87847
+ return { ok: true, data: dev };
87848
+ }
87849
+ function saveCapabilityCopy(storage2, items) {
87850
+ const clean8 = (items || []).map((c) => ({
87851
+ contactId: String(c.contactId || ""),
87852
+ kind: c.kind,
87853
+ displayValue: String(c.displayValue || ""),
87854
+ verificationStatus: String(c.verificationStatus || ""),
87855
+ capabilities: [...c.capabilities || []],
87856
+ provider: c.provider ? String(c.provider) : void 0,
87857
+ state: c.state ? String(c.state) : void 0
87858
+ }));
87859
+ storage2.setItem(K_CAPS, JSON.stringify(clean8));
87860
+ }
87861
+ function loadCapabilityCopy(storage2) {
87862
+ try {
87863
+ const v = JSON.parse(storage2.getItem(K_CAPS) || "[]");
87864
+ return Array.isArray(v) ? v : [];
87865
+ } catch {
87866
+ return [];
87867
+ }
87868
+ }
87869
+ function pendingQueue(storage2) {
87870
+ try {
87871
+ const v = JSON.parse(storage2.getItem(K_QUEUE) || "[]");
87872
+ return Array.isArray(v) ? v : [];
87873
+ } catch {
87874
+ return [];
87875
+ }
87876
+ }
87877
+ function setQueue(storage2, q) {
87878
+ storage2.setItem(K_QUEUE, JSON.stringify(q.slice(-20)));
87879
+ }
87880
+ async function flushQueuedGrants(base6, storage2, opts = {}) {
87881
+ const f = opts.fetchImpl || fetch;
87882
+ const q = pendingQueue(storage2);
87883
+ const keep = [];
87884
+ const results = [];
87885
+ let sent = 0, failed = 0;
87886
+ for (const item of q) {
87887
+ try {
87888
+ const r = await post(base6, "/api/contacts/grants/sync", { grant: { ...item.grant, signature: item.signature } }, f);
87889
+ if (r.status === 200 && r.json?.ok) {
87890
+ sent++;
87891
+ results.push({ grantId: item.grant.grantId, ok: true });
87892
+ continue;
87893
+ }
87894
+ failed++;
87895
+ keep.push(item);
87896
+ results.push({ grantId: item.grant.grantId, ok: false, error: r.json?.code || r.status });
87897
+ } catch {
87898
+ failed++;
87899
+ keep.push(item);
87900
+ results.push({ grantId: item.grant.grantId, ok: false, error: "network" });
87901
+ }
87902
+ }
87903
+ setQueue(storage2, keep);
87904
+ return { sent, failed, results };
87905
+ }
87906
+ async function authorizeFromPhone(input) {
87907
+ const c = cryptoOf(input.cryptoObj);
87908
+ if (!ed25519Available(c || void 0)) {
87909
+ return { ok: false, error: "device_signing_unavailable: \u672C\u673A WebView \u4E0D\u652F\u6301 Ed25519 \u8BBE\u5907\u7B7E\u540D \u2014\u2014 \u8BF7\u5728\u684C\u9762\u7AEF\u6388\u6743 (\u6216\u5347\u7EA7\u7CFB\u7EDF WebView)" };
87910
+ }
87911
+ const dev = await loadOrCreateDeviceKey(input.storage, c);
87912
+ const preset = presetForChoice(input.choice);
87913
+ const grant = {
87914
+ grantId: `gr-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`,
87915
+ identityId: input.identityId || `sid-phone-${dev.deviceId.slice(-6)}`,
87916
+ ownerDid: input.ownerDid,
87917
+ ...preset,
87918
+ grantedAt: (/* @__PURE__ */ new Date()).toISOString(),
87919
+ grantedBy: input.grantedBy || "leo",
87920
+ grantedVia: "mobile",
87921
+ deviceIds: [dev.deviceId],
87922
+ status: "active",
87923
+ grantVersion: 1
87924
+ };
87925
+ const signature = await signGrantOnDevice(grant, input.storage, c);
87926
+ const f = input.fetchImpl || fetch;
87927
+ const reg = await registerDeviceOnDesktop(input.base, input.storage, { fetchImpl: f, cryptoObj: c }).catch(() => ({ ok: false }));
87928
+ if (!reg.ok) {
87929
+ const q2 = pendingQueue(input.storage);
87930
+ q2.push({ grant, signature, queuedAt: (/* @__PURE__ */ new Date()).toISOString() });
87931
+ setQueue(input.storage, q2);
87932
+ return {
87933
+ ok: false,
87934
+ queued: true,
87935
+ error: `\u684C\u9762\u6682\u4E0D\u53EF\u8FBE \u2192 \u6388\u6743\u5DF2\u5B58\u5728\u624B\u673A, \u5F85\u684C\u9762\u5728\u7EBF\u65F6\u81EA\u52A8\u540C\u6B65 (${reg.error || "offline"})`,
87936
+ data: { grantId: grant.grantId, level: grant.level, signature },
87937
+ note: "\u684C\u9762\u4E0D\u5728\u7EBF\u65F6\u4E0D\u5047\u88C5\u5DF2\u751F\u6548: \u6388\u6743\u5728\u672C\u5730\u961F\u5217\u91CC, \u684C\u9762\u4E00\u4E0A\u7EBF\u5C31\u8865\u540C\u6B65"
87938
+ };
87939
+ }
87940
+ const r = await post(input.base, "/api/contacts/grants/sync", { grant: { ...grant, signature } }, f);
87941
+ if (r.status === 200 && r.json?.ok) {
87942
+ return { ok: true, data: { grantId: grant.grantId, level: grant.level, signature }, note: "\u684C\u9762\u5DF2\u9A8C\u7B7E\u63A5\u53D7" };
87943
+ }
87944
+ const q = pendingQueue(input.storage);
87945
+ q.push({ grant, signature, queuedAt: (/* @__PURE__ */ new Date()).toISOString() });
87946
+ setQueue(input.storage, q);
87947
+ return {
87948
+ ok: false,
87949
+ queued: true,
87950
+ error: `\u684C\u9762\u62D2\u7EDD/\u672A\u63A5\u53D7: ${r.json?.code || r.json?.reason || r.status}`,
87951
+ data: { grantId: grant.grantId, level: grant.level, signature },
87952
+ note: "\u5DF2\u4FDD\u5B58\u5728\u624B\u673A\u672C\u5730\u961F\u5217 (\u4E0D\u4F1A\u56E0\u4E3A\u5931\u8D25\u5C31\u5F53\u6210\u5DF2\u6388\u6743)"
87953
+ };
87954
+ }
87955
+ async function revokeFromPhone(opts) {
87956
+ const c = cryptoOf(opts.cryptoObj);
87957
+ if (!ed25519Available(c || void 0)) return { ok: false, error: "device_signing_unavailable: \u672C\u673A\u4E0D\u652F\u6301\u8BBE\u5907\u7B7E\u540D, \u64A4\u9500\u9700\u5728\u684C\u9762\u7AEF\u8FDB\u884C" };
87958
+ const revokedAt = (/* @__PURE__ */ new Date()).toISOString();
87959
+ const payload = { grantId: opts.grantId, grantVersion: opts.version || 1, revokedAt, by: "mobile" };
87960
+ const signature = await signRevocationOnDevice(payload, opts.storage, c);
87961
+ const f = opts.fetchImpl || fetch;
87962
+ const r = await post(opts.base, "/api/contacts/grants/revoke-sync", { grantId: opts.grantId, by: "mobile", reason: opts.reason, version: payload.grantVersion, revokedAt, signature }, f);
87963
+ if (r.status === 200 && r.json?.ok) return { ok: true, data: { grantId: opts.grantId }, note: "\u684C\u9762\u5DF2\u9A8C\u7B7E\u5E76\u7ACB\u5373\u5931\u6548" };
87964
+ return { ok: false, error: `\u64A4\u9500\u672A\u9001\u8FBE: ${r.json?.code || r.json?.error || r.status}`, note: "\u684C\u9762\u4E0D\u5728\u7EBF\u65F6\u64A4\u9500**\u4E0D\u4F1A**\u88AB\u5F53\u4F5C\u5DF2\u5B8C\u6210 \u2014\u2014 \u8BF7\u5728\u684C\u9762\u5728\u7EBF\u540E\u91CD\u8BD5 (\u6216\u684C\u9762\u7AEF\u64A4\u9500)" };
87965
+ }
87966
+ async function loadFromDesktop(base6, storage2, opts = {}) {
87967
+ const f = opts.fetchImpl || fetch;
87968
+ if (!base6) {
87969
+ const caps = loadCapabilityCopy(storage2);
87970
+ return {
87971
+ ok: false,
87972
+ error: "desktop_offline",
87973
+ data: { contacts: caps, grants: [], effective: "\u684C\u9762\u4E0D\u5728\u7EBF (\u663E\u793A\u624B\u673A\u672C\u5730\u526F\u672C)", approvals: [], waiting: [] },
87974
+ note: "\u4EE5\u4E0B\u662F\u4F60\u624B\u673A\u4E0A\u4FDD\u5B58\u7684\u80FD\u529B\u526F\u672C (\u4E0D\u662F\u684C\u9762\u5B9E\u65F6\u72B6\u6001)"
87975
+ };
87976
+ }
87977
+ try {
87978
+ const a = await get(base6, "/api/contacts", f);
87979
+ const b = await get(base6, "/api/contacts/grants", f);
87980
+ if (a.status !== 200 && b.status !== 200) return { ok: false, error: `desktop_http_${a.status || b.status}` };
87981
+ const view = {
87982
+ contacts: a.json?.contacts || [],
87983
+ grants: b.json?.grants || [],
87984
+ effective: b.json?.effective || "",
87985
+ approvals: a.json?.approvals || [],
87986
+ waiting: a.json?.waiting || []
87987
+ };
87988
+ saveCapabilityCopy(storage2, (view.contacts || []).filter((c) => c.capabilities?.includes("send")).map((c) => ({
87989
+ contactId: c.contactId,
87990
+ kind: c.kind,
87991
+ displayValue: c.displayValue,
87992
+ verificationStatus: c.status,
87993
+ capabilities: c.capabilities,
87994
+ provider: c.provider,
87995
+ state: c.status
87996
+ })));
87997
+ return { ok: true, data: view };
87998
+ } catch (err2) {
87999
+ const caps = loadCapabilityCopy(storage2);
88000
+ return {
88001
+ ok: false,
88002
+ error: "desktop_offline",
88003
+ data: { contacts: caps, grants: [], effective: "\u684C\u9762\u4E0D\u5728\u7EBF (\u663E\u793A\u624B\u673A\u672C\u5730\u526F\u672C)", approvals: [], waiting: [] },
88004
+ note: `\u8FDE\u4E0D\u4E0A\u684C\u9762 (${String(err2?.message || err2).slice(0, 60)}) \u2014\u2014 \u663E\u793A\u672C\u5730\u526F\u672C`
88005
+ };
88006
+ }
88007
+ }
88008
+ async function bindFromPhone(base6, opts, storage2, f) {
88009
+ const impl = f || fetch;
88010
+ if (!base6) return { ok: false, error: "desktop_offline: \u7ED1\u5B9A\u9700\u8981\u684C\u9762\u7AEF\u5728\u7EBF (\u684C\u9762\u8D1F\u8D23\u843D\u76D8\u4E0E\u9A8C\u8BC1\u7801\u901A\u9053)" };
88011
+ const r = await post(base6, "/api/contacts/bind", { kind: opts.kind, value: opts.value, region: opts.region }, impl);
88012
+ if (r.status === 200 && r.json?.ok) return { ok: true, data: r.json };
88013
+ return { ok: false, error: r.json?.error || `HTTP ${r.status}` };
88014
+ }
88015
+ async function verifyFromPhone(base6, opts, storage2, f) {
88016
+ const impl = f || fetch;
88017
+ if (!base6) return { ok: false, error: "desktop_offline: \u9A8C\u8BC1\u9700\u8981\u684C\u9762\u7AEF\u5728\u7EBF" };
88018
+ const r = await post(base6, "/api/contacts/verify", { contactId: opts.contactId, challengeId: opts.challengeId, code: opts.code }, impl);
88019
+ if (r.status === 200 && r.json?.ok) return { ok: true, data: r.json };
88020
+ return { ok: false, error: r.json?.error || `HTTP ${r.status}` };
88021
+ }
88022
+ async function decideApprovalFromPhone(base6, opts, f) {
88023
+ const impl = f || fetch;
88024
+ if (!base6) return { ok: false, error: "desktop_offline: \u6279\u51C6\u9700\u8981\u684C\u9762\u7AEF\u5728\u7EBF (\u624B\u673A\u4E0D\u5728\u7EBF\u65F6\u4E0D\u4F1A\u5047\u88C5\u5DF2\u6279\u51C6)" };
88025
+ const r = await post(base6, `/api/contacts/approvals/${encodeURIComponent(opts.consentId)}/${opts.action}`, { by: "leo", via: "mobile", body: opts.body, reason: opts.reason }, impl);
88026
+ if (r.status === 200 && r.json?.ok) return { ok: true, data: r.json };
88027
+ return { ok: false, error: r.json?.error || `HTTP ${r.status}` };
88028
+ }
88029
+ function buildPhoneCard(view, opts) {
88030
+ return {
88031
+ title: "\u8BA9 Bolloon \u4EE3\u8868\u4F60\u8054\u7CFB\u5916\u90E8\u7684\u4EBA",
88032
+ effective: view?.effective || opts.offline || "\u672A\u77E5",
88033
+ contacts: (view?.contacts || []).map((c) => ({
88034
+ displayValue: c.displayValue || "",
88035
+ kind: c.kind || "",
88036
+ state: c.status || c.verificationStatus || "",
88037
+ label: c.capabilities?.includes("send") ? "\u53EF\u8054\u7CFB" : "\u4E0D\u53EF\u8054\u7CFB"
88038
+ })),
88039
+ approvals: (view?.approvals || []).map((a) => ({
88040
+ consentId: a.consentId,
88041
+ contactName: a.contactName,
88042
+ channel: a.channel,
88043
+ subject: a.subject,
88044
+ bodyPreview: a.bodyPreview || "",
88045
+ reason: a.reason,
88046
+ reallySent: !!a.reallySent
88047
+ })),
88048
+ choices: [
88049
+ { id: "task_once", label: "\u4EC5\u672C\u6B21\u4EFB\u52A1" },
88050
+ { id: "persistent", label: "\u957F\u671F\u4F7F\u7528 (\u63A8\u8350)" },
88051
+ { id: "full_contact_access", label: "\u5B8C\u5168\u6388\u6743\u8054\u7CFB\u65B9\u5F0F\u80FD\u529B" }
88052
+ ],
88053
+ willNotGet: ["\u8BFB\u53D6\u5168\u90E8\u90AE\u7BB1", "\u8BFB\u53D6\u901A\u8BAF\u5F55", "\u7FA4\u53D1\u6D88\u606F", "\u81EA\u52A8\u652F\u4ED8/\u8F6C\u8D26", "\u4EE3\u8868\u4F60\u7B7E\u7F72\u5408\u540C", "\u62FF\u5230\u624B\u673A\u53F7/\u90AE\u7BB1\u660E\u6587"],
88054
+ deviceSigning: opts.deviceSigning
88055
+ };
88056
+ }
88057
+ var K_DEVICE, K_QUEUE, K_CAPS, cryptoOf;
88058
+ var init_mobile_contacts = __esm({
88059
+ "src/web/mobile-contacts.ts"() {
88060
+ "use strict";
88061
+ init_grant_payload();
88062
+ K_DEVICE = "bolloon.contacts.device.v1";
88063
+ K_QUEUE = "bolloon.contacts.queue.v1";
88064
+ K_CAPS = "bolloon.contacts.capabilities.v1";
88065
+ cryptoOf = (c) => c || globalThis.crypto || null;
88066
+ }
88067
+ });
88068
+
87669
88069
  // src/web/mobile-social.ts
87670
88070
  var mobile_social_exports = {};
87671
88071
  __export(mobile_social_exports, {
@@ -88980,7 +89380,7 @@ ${r.steps.map((s3) => `${s3.ok ? "\u2713" : "\u2717"} ${s3.step}: ${s3.note}`).j
88980
89380
  });
88981
89381
 
88982
89382
  // node_modules/viem/_esm/utils/encoding/toHex.js
88983
- function toHex(value2, opts = {}) {
89383
+ function toHex2(value2, opts = {}) {
88984
89384
  if (typeof value2 === "number" || typeof value2 === "bigint")
88985
89385
  return numberToHex(value2, opts);
88986
89386
  if (typeof value2 === "string") {
@@ -90103,13 +90503,13 @@ ${r.steps.map((s3) => `${s3.ok ? "\u2713" : "\u2717"} ${s3.step}: ${s3.note}`).j
90103
90503
  const windows = Math.ceil(scalarBits2 / W2) + 1;
90104
90504
  const windowSize = 2 ** (W2 - 1);
90105
90505
  const maxNumber = 2 ** W2;
90106
- const mask = bitMask3(W2);
90506
+ const mask2 = bitMask3(W2);
90107
90507
  const shiftBy = BigInt(W2);
90108
- return { windows, windowSize, mask, maxNumber, shiftBy };
90508
+ return { windows, windowSize, mask: mask2, maxNumber, shiftBy };
90109
90509
  }
90110
90510
  function calcOffsets3(n, window2, wOpts) {
90111
- const { windowSize, mask, maxNumber, shiftBy } = wOpts;
90112
- let wbits = Number(n & mask);
90511
+ const { windowSize, mask: mask2, maxNumber, shiftBy } = wOpts;
90512
+ let wbits = Number(n & mask2);
90113
90513
  let nextN = n >> shiftBy;
90114
90514
  if (wbits > windowSize) {
90115
90515
  wbits -= maxNumber;
@@ -91555,7 +91955,7 @@ ${r.steps.map((s3) => `${s3.ok ? "\u2713" : "\u2717"} ${s3.step}: ${s3.note}`).j
91555
91955
  const bytes = keccak_2562(isHex(value2, { strict: false }) ? toBytes3(value2) : value2);
91556
91956
  if (to === "bytes")
91557
91957
  return bytes;
91558
- return toHex(bytes);
91958
+ return toHex2(bytes);
91559
91959
  }
91560
91960
  var init_keccak256 = __esm({
91561
91961
  "node_modules/viem/_esm/utils/hash/keccak256.js"() {
@@ -92241,9 +92641,9 @@ ${r.steps.map((s3) => `${s3.ok ? "\u2713" : "\u2717"} ${s3.step}: ${s3.note}`).j
92241
92641
  const { chainId, nonce, ...signature } = authorization;
92242
92642
  const contractAddress = authorization.address;
92243
92643
  serializedAuthorizationList.push([
92244
- chainId ? toHex(chainId) : "0x",
92644
+ chainId ? toHex2(chainId) : "0x",
92245
92645
  contractAddress,
92246
- nonce ? toHex(nonce) : "0x",
92646
+ nonce ? toHex2(nonce) : "0x",
92247
92647
  ...toYParitySignatureArray({}, signature)
92248
92648
  ]);
92249
92649
  }
@@ -92309,7 +92709,7 @@ ${r.steps.map((s3) => `${s3.ok ? "\u2713" : "\u2717"} ${s3.step}: ${s3.note}`).j
92309
92709
  const bytes = sha2568(isHex(value2, { strict: false }) ? toBytes3(value2) : value2);
92310
92710
  if (to === "bytes")
92311
92711
  return bytes;
92312
- return toHex(bytes);
92712
+ return toHex2(bytes);
92313
92713
  }
92314
92714
  var init_sha2562 = __esm({
92315
92715
  "node_modules/viem/_esm/utils/hash/sha256.js"() {
@@ -93758,7 +94158,7 @@ ${r.steps.map((s3) => `${s3.ok ? "\u2713" : "\u2717"} ${s3.step}: ${s3.note}`).j
93758
94158
  return encodeAbiParameters(encodedTypes, encodedValues);
93759
94159
  }
93760
94160
  function hashType({ primaryType, types }) {
93761
- const encodedHashType = toHex(encodeType({ primaryType, types }));
94161
+ const encodedHashType = toHex2(encodeType({ primaryType, types }));
93762
94162
  return keccak256(encodedHashType);
93763
94163
  }
93764
94164
  function encodeType({ primaryType, types }) {
@@ -93793,7 +94193,7 @@ ${r.steps.map((s3) => `${s3.ok ? "\u2713" : "\u2717"} ${s3.step}: ${s3.note}`).j
93793
94193
  if (type === "bytes")
93794
94194
  return [{ type: "bytes32" }, keccak256(value2)];
93795
94195
  if (type === "string")
93796
- return [{ type: "bytes32" }, keccak256(toHex(value2))];
94196
+ return [{ type: "bytes32" }, keccak256(toHex2(value2))];
93797
94197
  if (type.lastIndexOf("]") === type.length - 1) {
93798
94198
  const parsedType = type.slice(0, type.lastIndexOf("["));
93799
94199
  const typeValuePairs = value2.map((item) => encodeField({
@@ -93838,7 +94238,7 @@ ${r.steps.map((s3) => `${s3.ok ? "\u2713" : "\u2717"} ${s3.step}: ${s3.note}`).j
93838
94238
  // node_modules/viem/_esm/accounts/privateKeyToAccount.js
93839
94239
  function privateKeyToAccount(privateKey, options2 = {}) {
93840
94240
  const { nonceManager } = options2;
93841
- const publicKey2 = toHex(secp256k13.getPublicKey(privateKey.slice(2), false));
94241
+ const publicKey2 = toHex2(secp256k13.getPublicKey(privateKey.slice(2), false));
93842
94242
  const address = publicKeyToAddress(publicKey2);
93843
94243
  const account = toAccount({
93844
94244
  address,
@@ -94318,7 +94718,7 @@ ${r.steps.map((s3) => `${s3.ok ? "\u2713" : "\u2717"} ${s3.step}: ${s3.note}`).j
94318
94718
  } else {
94319
94719
  for (let i = 0; i < bytes.length; i++) bytes[i] = Math.floor(Math.random() * 256);
94320
94720
  }
94321
- return toHex(bytes);
94721
+ return toHex2(bytes);
94322
94722
  }
94323
94723
  function bytesToBase64(bytes) {
94324
94724
  let out = "";
@@ -94569,7 +94969,7 @@ ${r.steps.map((s3) => `${s3.ok ? "\u2713" : "\u2717"} ${s3.step}: ${s3.note}`).j
94569
94969
  }
94570
94970
  }
94571
94971
  function deriveTokenId(agentId, serviceName) {
94572
- return BigInt(keccak256(toHex(`${agentId}:${serviceName}`)));
94972
+ return BigInt(keccak256(toHex2(`${agentId}:${serviceName}`)));
94573
94973
  }
94574
94974
  async function registerServiceOnChain(params) {
94575
94975
  try {
@@ -95154,10 +95554,10 @@ ${r.steps.map((s3) => `${s3.ok ? "\u2713" : "\u2717"} ${s3.step}: ${s3.note}`).j
95154
95554
  let f = G;
95155
95555
  const pow_2_w = 2 ** W;
95156
95556
  const maxNum = pow_2_w;
95157
- const mask = big(pow_2_w - 1);
95557
+ const mask2 = big(pow_2_w - 1);
95158
95558
  const shiftBy = big(W);
95159
95559
  for (let w2 = 0; w2 < pwindows; w2++) {
95160
- let wbits = Number(n & mask);
95560
+ let wbits = Number(n & mask2);
95161
95561
  n >>= shiftBy;
95162
95562
  if (wbits > pwindowSize) {
95163
95563
  wbits -= maxNum;
@@ -96895,11 +97295,11 @@ ${r.steps.map((s3) => `${s3.ok ? "\u2713" : "\u2717"} ${s3.step}: ${s3.note}`).j
96895
97295
  function toToken6(data, pos, prefix, length20, options2) {
96896
97296
  const totLength = prefix + length20;
96897
97297
  assertEnoughData2(data, pos, totLength);
96898
- const tok = new Token2(Type2.string, toStr2(data, pos + prefix, pos + totLength), totLength);
97298
+ const tok2 = new Token2(Type2.string, toStr2(data, pos + prefix, pos + totLength), totLength);
96899
97299
  if (options2.retainStringBytes === true) {
96900
- tok.byteValue = data.slice(pos + prefix, pos + totLength);
97300
+ tok2.byteValue = data.slice(pos + prefix, pos + totLength);
96901
97301
  }
96902
- return tok;
97302
+ return tok2;
96903
97303
  }
96904
97304
  function decodeStringCompact2(data, pos, minor, options2) {
96905
97305
  return toToken6(data, pos, 1, minor, options2);
@@ -98382,7 +98782,7 @@ ${r.steps.map((s3) => `${s3.ok ? "\u2713" : "\u2717"} ${s3.step}: ${s3.note}`).j
98382
98782
  }
98383
98783
  function encode15(data, alphabet4, bitsPerChar) {
98384
98784
  const pad2 = alphabet4[alphabet4.length - 1] === "=";
98385
- const mask = (1 << bitsPerChar) - 1;
98785
+ const mask2 = (1 << bitsPerChar) - 1;
98386
98786
  let out = "";
98387
98787
  let bits = 0;
98388
98788
  let buffer6 = 0;
@@ -98391,11 +98791,11 @@ ${r.steps.map((s3) => `${s3.ok ? "\u2713" : "\u2717"} ${s3.step}: ${s3.note}`).j
98391
98791
  bits += 8;
98392
98792
  while (bits > bitsPerChar) {
98393
98793
  bits -= bitsPerChar;
98394
- out += alphabet4[mask & buffer6 >> bits];
98794
+ out += alphabet4[mask2 & buffer6 >> bits];
98395
98795
  }
98396
98796
  }
98397
98797
  if (bits !== 0) {
98398
- out += alphabet4[mask & buffer6 << bitsPerChar - bits];
98798
+ out += alphabet4[mask2 & buffer6 << bitsPerChar - bits];
98399
98799
  }
98400
98800
  if (pad2) {
98401
98801
  while ((out.length * bitsPerChar & 7) !== 0) {
@@ -99216,7 +99616,7 @@ ${r.steps.map((s3) => `${s3.ok ? "\u2713" : "\u2717"} ${s3.step}: ${s3.note}`).j
99216
99616
  computeCid: () => computeCid,
99217
99617
  createIpfsClient: () => createIpfsClient,
99218
99618
  createMemoryStorage: () => createMemoryStorage,
99219
- defaultStorage: () => defaultStorage2,
99619
+ defaultStorage: () => defaultStorage3,
99220
99620
  getIpfsConfig: () => getIpfsConfig,
99221
99621
  ipfsCacheGet: () => ipfsCacheGet,
99222
99622
  ipfsCacheSet: () => ipfsCacheSet,
@@ -99247,7 +99647,7 @@ ${r.steps.map((s3) => `${s3.ok ? "\u2713" : "\u2717"} ${s3.step}: ${s3.note}`).j
99247
99647
  }
99248
99648
  };
99249
99649
  }
99250
- function defaultStorage2() {
99650
+ function defaultStorage3() {
99251
99651
  try {
99252
99652
  if (typeof localStorage !== "undefined" && localStorage) {
99253
99653
  const probe = "__bolloon_ipfs_probe__";
@@ -99317,7 +99717,7 @@ ${r.steps.map((s3) => `${s3.ok ? "\u2713" : "\u2717"} ${s3.step}: ${s3.note}`).j
99317
99717
  }
99318
99718
  }
99319
99719
  }
99320
- function getIpfsConfig(storage2 = defaultStorage2()) {
99720
+ function getIpfsConfig(storage2 = defaultStorage3()) {
99321
99721
  const base6 = { mode: "public", gateways: [...DEFAULT_GATEWAYS] };
99322
99722
  try {
99323
99723
  const raw = storage2.getItem(IPFS_CONFIG_KEY);
@@ -99336,7 +99736,7 @@ ${r.steps.map((s3) => `${s3.ok ? "\u2713" : "\u2717"} ${s3.step}: ${s3.note}`).j
99336
99736
  return base6;
99337
99737
  }
99338
99738
  }
99339
- function setIpfsConfig(cfg, storage2 = defaultStorage2()) {
99739
+ function setIpfsConfig(cfg, storage2 = defaultStorage3()) {
99340
99740
  try {
99341
99741
  if (cfg.mode !== void 0 && !VALID_MODES.includes(cfg.mode)) {
99342
99742
  return { ok: false, error: `\u975E\u6CD5 mode: ${String(cfg.mode)} (\u5E94\u4E3A public|remote|pinata)` };
@@ -99384,7 +99784,7 @@ ${r.steps.map((s3) => `${s3.ok ? "\u2713" : "\u2717"} ${s3.step}: ${s3.note}`).j
99384
99784
  }
99385
99785
  return kept;
99386
99786
  }
99387
- function ipfsCacheGet(cid, storage2 = defaultStorage2()) {
99787
+ function ipfsCacheGet(cid, storage2 = defaultStorage3()) {
99388
99788
  try {
99389
99789
  const key = normalizeCid(cid);
99390
99790
  if (!key) return null;
@@ -99404,7 +99804,7 @@ ${r.steps.map((s3) => `${s3.ok ? "\u2713" : "\u2717"} ${s3.step}: ${s3.note}`).j
99404
99804
  return null;
99405
99805
  }
99406
99806
  }
99407
- function ipfsCacheSet(cid, text, storage2 = defaultStorage2()) {
99807
+ function ipfsCacheSet(cid, text, storage2 = defaultStorage3()) {
99408
99808
  try {
99409
99809
  const key = normalizeCid(cid);
99410
99810
  if (!key) return { ok: false, error: "cid \u4E0D\u80FD\u4E3A\u7A7A" };
@@ -99488,7 +99888,7 @@ ${r.steps.map((s3) => `${s3.ok ? "\u2713" : "\u2717"} ${s3.step}: ${s3.note}`).j
99488
99888
  async function ipfsFetch(cid, opts = {}) {
99489
99889
  const key = normalizeCid(cid);
99490
99890
  if (!key) return { ok: false, error: "cid \u4E0D\u80FD\u4E3A\u7A7A" };
99491
- const storage2 = opts.storage ?? defaultStorage2();
99891
+ const storage2 = opts.storage ?? defaultStorage3();
99492
99892
  const useCache = opts.useCache !== false;
99493
99893
  if (useCache) {
99494
99894
  const cached = ipfsCacheGet(key, storage2);
@@ -100794,8 +101194,8 @@ ${r.steps.map((s3) => `${s3.ok ? "\u2713" : "\u2717"} ${s3.step}: ${s3.note}`).j
100794
101194
  }
100795
101195
  function encodeInteger2(value2) {
100796
101196
  const contents = new Uint8ArrayList10();
100797
- const mask = 128;
100798
- const positive2 = (value2.subarray()[0] & mask) === mask;
101197
+ const mask2 = 128;
101198
+ const positive2 = (value2.subarray()[0] & mask2) === mask2;
100799
101199
  if (positive2) {
100800
101200
  contents.append(Uint8Array.from([0]));
100801
101201
  }
@@ -100952,18 +101352,18 @@ ${r.steps.map((s3) => `${s3.ok ? "\u2713" : "\u2717"} ${s3.step}: ${s3.note}`).j
100952
101352
  * Zig-zag encodes this long bits
100953
101353
  */
100954
101354
  zzEncode() {
100955
- const mask = this.hi >> 31;
100956
- this.hi = ((this.hi << 1 | this.lo >>> 31) ^ mask) >>> 0;
100957
- this.lo = (this.lo << 1 ^ mask) >>> 0;
101355
+ const mask2 = this.hi >> 31;
101356
+ this.hi = ((this.hi << 1 | this.lo >>> 31) ^ mask2) >>> 0;
101357
+ this.lo = (this.lo << 1 ^ mask2) >>> 0;
100958
101358
  return this;
100959
101359
  }
100960
101360
  /**
100961
101361
  * Zig-zag decodes this long bits
100962
101362
  */
100963
101363
  zzDecode() {
100964
- const mask = -(this.lo & 1);
100965
- this.lo = ((this.lo >>> 1 | this.hi << 31) ^ mask) >>> 0;
100966
- this.hi = (this.hi >>> 1 ^ mask) >>> 0;
101364
+ const mask2 = -(this.lo & 1);
101365
+ this.lo = ((this.lo >>> 1 | this.hi << 31) ^ mask2) >>> 0;
101366
+ this.hi = (this.hi >>> 1 ^ mask2) >>> 0;
100967
101367
  return this;
100968
101368
  }
100969
101369
  /**
@@ -103864,18 +104264,18 @@ ${r.steps.map((s3) => `${s3.ok ? "\u2713" : "\u2717"} ${s3.step}: ${s3.note}`).j
103864
104264
  * Zig-zag encodes this long bits
103865
104265
  */
103866
104266
  zzEncode() {
103867
- const mask = this.hi >> 31;
103868
- this.hi = ((this.hi << 1 | this.lo >>> 31) ^ mask) >>> 0;
103869
- this.lo = (this.lo << 1 ^ mask) >>> 0;
104267
+ const mask2 = this.hi >> 31;
104268
+ this.hi = ((this.hi << 1 | this.lo >>> 31) ^ mask2) >>> 0;
104269
+ this.lo = (this.lo << 1 ^ mask2) >>> 0;
103870
104270
  return this;
103871
104271
  }
103872
104272
  /**
103873
104273
  * Zig-zag decodes this long bits
103874
104274
  */
103875
104275
  zzDecode() {
103876
- const mask = -(this.lo & 1);
103877
- this.lo = ((this.lo >>> 1 | this.hi << 31) ^ mask) >>> 0;
103878
- this.hi = (this.hi >>> 1 ^ mask) >>> 0;
104276
+ const mask2 = -(this.lo & 1);
104277
+ this.lo = ((this.lo >>> 1 | this.hi << 31) ^ mask2) >>> 0;
104278
+ this.hi = (this.hi >>> 1 ^ mask2) >>> 0;
103879
104279
  return this;
103880
104280
  }
103881
104281
  /**
@@ -114756,18 +115156,18 @@ ${values.join("\n")}` : `${blockName} :`;
114756
115156
  * Zig-zag encodes this long bits
114757
115157
  */
114758
115158
  zzEncode() {
114759
- const mask = this.hi >> 31;
114760
- this.hi = ((this.hi << 1 | this.lo >>> 31) ^ mask) >>> 0;
114761
- this.lo = (this.lo << 1 ^ mask) >>> 0;
115159
+ const mask2 = this.hi >> 31;
115160
+ this.hi = ((this.hi << 1 | this.lo >>> 31) ^ mask2) >>> 0;
115161
+ this.lo = (this.lo << 1 ^ mask2) >>> 0;
114762
115162
  return this;
114763
115163
  }
114764
115164
  /**
114765
115165
  * Zig-zag decodes this long bits
114766
115166
  */
114767
115167
  zzDecode() {
114768
- const mask = -(this.lo & 1);
114769
- this.lo = ((this.lo >>> 1 | this.hi << 31) ^ mask) >>> 0;
114770
- this.hi = (this.hi >>> 1 ^ mask) >>> 0;
115168
+ const mask2 = -(this.lo & 1);
115169
+ this.lo = ((this.lo >>> 1 | this.hi << 31) ^ mask2) >>> 0;
115170
+ this.hi = (this.hi >>> 1 ^ mask2) >>> 0;
114771
115171
  return this;
114772
115172
  }
114773
115173
  /**
@@ -117096,18 +117496,18 @@ ${values.join("\n")}` : `${blockName} :`;
117096
117496
  * Zig-zag encodes this long bits
117097
117497
  */
117098
117498
  zzEncode() {
117099
- const mask = this.hi >> 31;
117100
- this.hi = ((this.hi << 1 | this.lo >>> 31) ^ mask) >>> 0;
117101
- this.lo = (this.lo << 1 ^ mask) >>> 0;
117499
+ const mask2 = this.hi >> 31;
117500
+ this.hi = ((this.hi << 1 | this.lo >>> 31) ^ mask2) >>> 0;
117501
+ this.lo = (this.lo << 1 ^ mask2) >>> 0;
117102
117502
  return this;
117103
117503
  }
117104
117504
  /**
117105
117505
  * Zig-zag decodes this long bits
117106
117506
  */
117107
117507
  zzDecode() {
117108
- const mask = -(this.lo & 1);
117109
- this.lo = ((this.lo >>> 1 | this.hi << 31) ^ mask) >>> 0;
117110
- this.hi = (this.hi >>> 1 ^ mask) >>> 0;
117508
+ const mask2 = -(this.lo & 1);
117509
+ this.lo = ((this.lo >>> 1 | this.hi << 31) ^ mask2) >>> 0;
117510
+ this.hi = (this.hi >>> 1 ^ mask2) >>> 0;
117111
117511
  return this;
117112
117512
  }
117113
117513
  /**
@@ -122983,7 +123383,7 @@ ${values.join("\n")}` : `${blockName} :`;
122983
123383
  // node_modules/process/browser.js
122984
123384
  var require_browser4 = __commonJS({
122985
123385
  "node_modules/process/browser.js"(exports, module) {
122986
- var process = module.exports = {};
123386
+ var process2 = module.exports = {};
122987
123387
  var cachedSetTimeout;
122988
123388
  var cachedClearTimeout;
122989
123389
  function defaultSetTimout() {
@@ -123088,7 +123488,7 @@ ${values.join("\n")}` : `${blockName} :`;
123088
123488
  draining = false;
123089
123489
  runClearTimeout(timeout);
123090
123490
  }
123091
- process.nextTick = function(fun) {
123491
+ process2.nextTick = function(fun) {
123092
123492
  var args = new Array(arguments.length - 1);
123093
123493
  if (arguments.length > 1) {
123094
123494
  for (var i = 1; i < arguments.length; i++) {
@@ -123107,36 +123507,36 @@ ${values.join("\n")}` : `${blockName} :`;
123107
123507
  Item.prototype.run = function() {
123108
123508
  this.fun.apply(null, this.array);
123109
123509
  };
123110
- process.title = "browser";
123111
- process.browser = true;
123112
- process.env = {};
123113
- process.argv = [];
123114
- process.version = "";
123115
- process.versions = {};
123510
+ process2.title = "browser";
123511
+ process2.browser = true;
123512
+ process2.env = {};
123513
+ process2.argv = [];
123514
+ process2.version = "";
123515
+ process2.versions = {};
123116
123516
  function noop16() {
123117
123517
  }
123118
- process.on = noop16;
123119
- process.addListener = noop16;
123120
- process.once = noop16;
123121
- process.off = noop16;
123122
- process.removeListener = noop16;
123123
- process.removeAllListeners = noop16;
123124
- process.emit = noop16;
123125
- process.prependListener = noop16;
123126
- process.prependOnceListener = noop16;
123127
- process.listeners = function(name10) {
123518
+ process2.on = noop16;
123519
+ process2.addListener = noop16;
123520
+ process2.once = noop16;
123521
+ process2.off = noop16;
123522
+ process2.removeListener = noop16;
123523
+ process2.removeAllListeners = noop16;
123524
+ process2.emit = noop16;
123525
+ process2.prependListener = noop16;
123526
+ process2.prependOnceListener = noop16;
123527
+ process2.listeners = function(name10) {
123128
123528
  return [];
123129
123529
  };
123130
- process.binding = function(name10) {
123530
+ process2.binding = function(name10) {
123131
123531
  throw new Error("process.binding is not supported");
123132
123532
  };
123133
- process.cwd = function() {
123533
+ process2.cwd = function() {
123134
123534
  return "/";
123135
123535
  };
123136
- process.chdir = function(dir) {
123536
+ process2.chdir = function(dir) {
123137
123537
  throw new Error("process.chdir is not supported");
123138
123538
  };
123139
- process.umask = function() {
123539
+ process2.umask = function() {
123140
123540
  return 0;
123141
123541
  };
123142
123542
  }
@@ -123355,7 +123755,7 @@ ${values.join("\n")}` : `${blockName} :`;
123355
123755
  var require_end_of_stream = __commonJS({
123356
123756
  "node_modules/@libp2p/http-utils/node_modules/readable-stream/lib/internal/streams/end-of-stream.js"(exports, module) {
123357
123757
  "use strict";
123358
- var process = require_browser4();
123758
+ var process2 = require_browser4();
123359
123759
  var { AbortError: AbortError6, codes } = require_errors();
123360
123760
  var { ERR_INVALID_ARG_TYPE, ERR_STREAM_PREMATURE_CLOSE } = codes;
123361
123761
  var { kEmptyObject, once } = require_util();
@@ -123491,17 +123891,17 @@ ${values.join("\n")}` : `${blockName} :`;
123491
123891
  }
123492
123892
  stream.on("close", onclose);
123493
123893
  if (closed) {
123494
- process.nextTick(onclose);
123894
+ process2.nextTick(onclose);
123495
123895
  } else if (wState !== null && wState !== void 0 && wState.errorEmitted || rState !== null && rState !== void 0 && rState.errorEmitted) {
123496
123896
  if (!willEmitClose) {
123497
- process.nextTick(onclosed);
123897
+ process2.nextTick(onclosed);
123498
123898
  }
123499
123899
  } else if (!readable && (!willEmitClose || isReadable(stream)) && (writableFinished || isWritable(stream) === false)) {
123500
- process.nextTick(onclosed);
123900
+ process2.nextTick(onclosed);
123501
123901
  } else if (!writable && (!willEmitClose || isWritable(stream)) && (readableFinished || isReadable(stream) === false)) {
123502
- process.nextTick(onclosed);
123902
+ process2.nextTick(onclosed);
123503
123903
  } else if (rState && stream.req && stream.aborted) {
123504
- process.nextTick(onclosed);
123904
+ process2.nextTick(onclosed);
123505
123905
  }
123506
123906
  const cleanup = () => {
123507
123907
  callback = nop;
@@ -123529,7 +123929,7 @@ ${values.join("\n")}` : `${blockName} :`;
123529
123929
  );
123530
123930
  };
123531
123931
  if (options2.signal.aborted) {
123532
- process.nextTick(abort);
123932
+ process2.nextTick(abort);
123533
123933
  } else {
123534
123934
  addAbortListener = addAbortListener || require_util().addAbortListener;
123535
123935
  const disposable = addAbortListener(options2.signal, abort);
@@ -123556,7 +123956,7 @@ ${values.join("\n")}` : `${blockName} :`;
123556
123956
  );
123557
123957
  };
123558
123958
  if (options2.signal.aborted) {
123559
- process.nextTick(abort);
123959
+ process2.nextTick(abort);
123560
123960
  } else {
123561
123961
  addAbortListener = addAbortListener || require_util().addAbortListener;
123562
123962
  const disposable = addAbortListener(options2.signal, abort);
@@ -123569,7 +123969,7 @@ ${values.join("\n")}` : `${blockName} :`;
123569
123969
  }
123570
123970
  const resolverFn = (...args) => {
123571
123971
  if (!isAborted) {
123572
- process.nextTick(() => callback.apply(stream, args));
123972
+ process2.nextTick(() => callback.apply(stream, args));
123573
123973
  }
123574
123974
  };
123575
123975
  PromisePrototypeThen(stream[kIsClosedPromise].promise, resolverFn, resolverFn);
@@ -123607,7 +124007,7 @@ ${values.join("\n")}` : `${blockName} :`;
123607
124007
  var require_destroy = __commonJS({
123608
124008
  "node_modules/@libp2p/http-utils/node_modules/readable-stream/lib/internal/streams/destroy.js"(exports, module) {
123609
124009
  "use strict";
123610
- var process = require_browser4();
124010
+ var process2 = require_browser4();
123611
124011
  var {
123612
124012
  aggregateTwoErrors,
123613
124013
  codes: { ERR_MULTIPLE_CALLBACK },
@@ -123674,9 +124074,9 @@ ${values.join("\n")}` : `${blockName} :`;
123674
124074
  cb(err3);
123675
124075
  }
123676
124076
  if (err3) {
123677
- process.nextTick(emitErrorCloseNT, self2, err3);
124077
+ process2.nextTick(emitErrorCloseNT, self2, err3);
123678
124078
  } else {
123679
- process.nextTick(emitCloseNT, self2);
124079
+ process2.nextTick(emitCloseNT, self2);
123680
124080
  }
123681
124081
  }
123682
124082
  try {
@@ -123761,7 +124161,7 @@ ${values.join("\n")}` : `${blockName} :`;
123761
124161
  r.errored = err2;
123762
124162
  }
123763
124163
  if (sync) {
123764
- process.nextTick(emitErrorNT, stream, err2);
124164
+ process2.nextTick(emitErrorNT, stream, err2);
123765
124165
  } else {
123766
124166
  emitErrorNT(stream, err2);
123767
124167
  }
@@ -123783,7 +124183,7 @@ ${values.join("\n")}` : `${blockName} :`;
123783
124183
  if (stream.listenerCount(kConstruct) > 1) {
123784
124184
  return;
123785
124185
  }
123786
- process.nextTick(constructNT, stream);
124186
+ process2.nextTick(constructNT, stream);
123787
124187
  }
123788
124188
  function constructNT(stream) {
123789
124189
  let called = false;
@@ -123807,15 +124207,15 @@ ${values.join("\n")}` : `${blockName} :`;
123807
124207
  } else if (err2) {
123808
124208
  errorOrDestroy(stream, err2, true);
123809
124209
  } else {
123810
- process.nextTick(emitConstructNT, stream);
124210
+ process2.nextTick(emitConstructNT, stream);
123811
124211
  }
123812
124212
  }
123813
124213
  try {
123814
124214
  stream._construct((err2) => {
123815
- process.nextTick(onConstruct, err2);
124215
+ process2.nextTick(onConstruct, err2);
123816
124216
  });
123817
124217
  } catch (err2) {
123818
- process.nextTick(onConstruct, err2);
124218
+ process2.nextTick(onConstruct, err2);
123819
124219
  }
123820
124220
  }
123821
124221
  function emitConstructNT(stream) {
@@ -123829,7 +124229,7 @@ ${values.join("\n")}` : `${blockName} :`;
123829
124229
  }
123830
124230
  function emitErrorCloseLegacy(stream, err2) {
123831
124231
  stream.emit("error", err2);
123832
- process.nextTick(emitCloseLegacy, stream);
124232
+ process2.nextTick(emitCloseLegacy, stream);
123833
124233
  }
123834
124234
  function destroyer(stream, err2) {
123835
124235
  if (!stream || isDestroyed(stream)) {
@@ -123850,9 +124250,9 @@ ${values.join("\n")}` : `${blockName} :`;
123850
124250
  } else if (typeof stream.close === "function") {
123851
124251
  stream.close();
123852
124252
  } else if (err2) {
123853
- process.nextTick(emitErrorCloseLegacy, stream, err2);
124253
+ process2.nextTick(emitErrorCloseLegacy, stream, err2);
123854
124254
  } else {
123855
- process.nextTick(emitCloseLegacy, stream);
124255
+ process2.nextTick(emitCloseLegacy, stream);
123856
124256
  }
123857
124257
  if (!stream.destroyed) {
123858
124258
  stream[kIsDestroyed] = true;
@@ -124494,7 +124894,7 @@ ${values.join("\n")}` : `${blockName} :`;
124494
124894
  var require_from = __commonJS({
124495
124895
  "node_modules/@libp2p/http-utils/node_modules/readable-stream/lib/internal/streams/from.js"(exports, module) {
124496
124896
  "use strict";
124497
- var process = require_browser4();
124897
+ var process2 = require_browser4();
124498
124898
  var { PromisePrototypeThen, SymbolAsyncIterator, SymbolIterator } = require_primordials();
124499
124899
  var { Buffer: Buffer2 } = require_buffer();
124500
124900
  var { ERR_INVALID_ARG_TYPE, ERR_STREAM_NULL_VALUES } = require_errors().codes;
@@ -124536,9 +124936,9 @@ ${values.join("\n")}` : `${blockName} :`;
124536
124936
  readable._destroy = function(error, cb) {
124537
124937
  PromisePrototypeThen(
124538
124938
  close(error),
124539
- () => process.nextTick(cb, error),
124939
+ () => process2.nextTick(cb, error),
124540
124940
  // nextTick is here in case cb throws
124541
- (e) => process.nextTick(cb, e || error)
124941
+ (e) => process2.nextTick(cb, e || error)
124542
124942
  );
124543
124943
  };
124544
124944
  async function close(error) {
@@ -124589,7 +124989,7 @@ ${values.join("\n")}` : `${blockName} :`;
124589
124989
  var require_readable = __commonJS({
124590
124990
  "node_modules/@libp2p/http-utils/node_modules/readable-stream/lib/internal/streams/readable.js"(exports, module) {
124591
124991
  "use strict";
124592
- var process = require_browser4();
124992
+ var process2 = require_browser4();
124593
124993
  var {
124594
124994
  ArrayPrototypeIndexOf,
124595
124995
  NumberIsInteger,
@@ -124974,7 +125374,7 @@ ${values.join("\n")}` : `${blockName} :`;
124974
125374
  if (!state2.emittedReadable) {
124975
125375
  debug("emitReadable", state2.flowing);
124976
125376
  state2.emittedReadable = true;
124977
- process.nextTick(emitReadable_, stream);
125377
+ process2.nextTick(emitReadable_, stream);
124978
125378
  }
124979
125379
  }
124980
125380
  function emitReadable_(stream) {
@@ -124990,7 +125390,7 @@ ${values.join("\n")}` : `${blockName} :`;
124990
125390
  function maybeReadMore(stream, state2) {
124991
125391
  if (!state2.readingMore && state2.constructed) {
124992
125392
  state2.readingMore = true;
124993
- process.nextTick(maybeReadMore_, stream, state2);
125393
+ process2.nextTick(maybeReadMore_, stream, state2);
124994
125394
  }
124995
125395
  }
124996
125396
  function maybeReadMore_(stream, state2) {
@@ -125017,9 +125417,9 @@ ${values.join("\n")}` : `${blockName} :`;
125017
125417
  }
125018
125418
  state2.pipes.push(dest);
125019
125419
  debug("pipe count=%d opts=%j", state2.pipes.length, pipeOpts);
125020
- const doEnd = (!pipeOpts || pipeOpts.end !== false) && dest !== process.stdout && dest !== process.stderr;
125420
+ const doEnd = (!pipeOpts || pipeOpts.end !== false) && dest !== process2.stdout && dest !== process2.stderr;
125021
125421
  const endFn = doEnd ? onend : unpipe;
125022
- if (state2.endEmitted) process.nextTick(endFn);
125422
+ if (state2.endEmitted) process2.nextTick(endFn);
125023
125423
  else src4.once("end", endFn);
125024
125424
  dest.on("unpipe", onunpipe);
125025
125425
  function onunpipe(readable, unpipeInfo) {
@@ -125169,7 +125569,7 @@ ${values.join("\n")}` : `${blockName} :`;
125169
125569
  if (state2.length) {
125170
125570
  emitReadable(this);
125171
125571
  } else if (!state2.reading) {
125172
- process.nextTick(nReadingNextTick, this);
125572
+ process2.nextTick(nReadingNextTick, this);
125173
125573
  }
125174
125574
  }
125175
125575
  }
@@ -125179,7 +125579,7 @@ ${values.join("\n")}` : `${blockName} :`;
125179
125579
  Readable.prototype.removeListener = function(ev, fn) {
125180
125580
  const res = Stream.prototype.removeListener.call(this, ev, fn);
125181
125581
  if (ev === "readable") {
125182
- process.nextTick(updateReadableListening, this);
125582
+ process2.nextTick(updateReadableListening, this);
125183
125583
  }
125184
125584
  return res;
125185
125585
  };
@@ -125187,7 +125587,7 @@ ${values.join("\n")}` : `${blockName} :`;
125187
125587
  Readable.prototype.removeAllListeners = function(ev) {
125188
125588
  const res = Stream.prototype.removeAllListeners.apply(this, arguments);
125189
125589
  if (ev === "readable" || ev === void 0) {
125190
- process.nextTick(updateReadableListening, this);
125590
+ process2.nextTick(updateReadableListening, this);
125191
125591
  }
125192
125592
  return res;
125193
125593
  };
@@ -125219,7 +125619,7 @@ ${values.join("\n")}` : `${blockName} :`;
125219
125619
  function resume(stream, state2) {
125220
125620
  if (!state2.resumeScheduled) {
125221
125621
  state2.resumeScheduled = true;
125222
- process.nextTick(resume_, stream, state2);
125622
+ process2.nextTick(resume_, stream, state2);
125223
125623
  }
125224
125624
  }
125225
125625
  function resume_(stream, state2) {
@@ -125496,7 +125896,7 @@ ${values.join("\n")}` : `${blockName} :`;
125496
125896
  debug("endReadable", state2.endEmitted);
125497
125897
  if (!state2.endEmitted) {
125498
125898
  state2.ended = true;
125499
- process.nextTick(endReadableNT, state2, stream);
125899
+ process2.nextTick(endReadableNT, state2, stream);
125500
125900
  }
125501
125901
  }
125502
125902
  function endReadableNT(state2, stream) {
@@ -125505,7 +125905,7 @@ ${values.join("\n")}` : `${blockName} :`;
125505
125905
  state2.endEmitted = true;
125506
125906
  stream.emit("end");
125507
125907
  if (stream.writable && stream.allowHalfOpen === false) {
125508
- process.nextTick(endWritableNT, stream);
125908
+ process2.nextTick(endWritableNT, stream);
125509
125909
  } else if (state2.autoDestroy) {
125510
125910
  const wState = stream._writableState;
125511
125911
  const autoDestroy = !wState || wState.autoDestroy && // We don't expect the writable to ever 'finish'
@@ -125555,7 +125955,7 @@ ${values.join("\n")}` : `${blockName} :`;
125555
125955
  var require_writable = __commonJS({
125556
125956
  "node_modules/@libp2p/http-utils/node_modules/readable-stream/lib/internal/streams/writable.js"(exports, module) {
125557
125957
  "use strict";
125558
- var process = require_browser4();
125958
+ var process2 = require_browser4();
125559
125959
  var {
125560
125960
  ArrayPrototypeSlice,
125561
125961
  Error: Error2,
@@ -125708,7 +126108,7 @@ ${values.join("\n")}` : `${blockName} :`;
125708
126108
  err2 = new ERR_STREAM_DESTROYED("write");
125709
126109
  }
125710
126110
  if (err2) {
125711
- process.nextTick(cb, err2);
126111
+ process2.nextTick(cb, err2);
125712
126112
  errorOrDestroy(stream, err2, true);
125713
126113
  return err2;
125714
126114
  }
@@ -125798,7 +126198,7 @@ ${values.join("\n")}` : `${blockName} :`;
125798
126198
  stream._readableState.errored = er;
125799
126199
  }
125800
126200
  if (sync) {
125801
- process.nextTick(onwriteError, stream, state2, er, cb);
126201
+ process2.nextTick(onwriteError, stream, state2, er, cb);
125802
126202
  } else {
125803
126203
  onwriteError(stream, state2, er, cb);
125804
126204
  }
@@ -125816,7 +126216,7 @@ ${values.join("\n")}` : `${blockName} :`;
125816
126216
  stream,
125817
126217
  state: state2
125818
126218
  };
125819
- process.nextTick(afterWriteTick, state2.afterWriteTickInfo);
126219
+ process2.nextTick(afterWriteTick, state2.afterWriteTickInfo);
125820
126220
  }
125821
126221
  } else {
125822
126222
  afterWrite(stream, state2, 1, cb);
@@ -125953,7 +126353,7 @@ ${values.join("\n")}` : `${blockName} :`;
125953
126353
  }
125954
126354
  if (typeof cb === "function") {
125955
126355
  if (err2 || state2.finished) {
125956
- process.nextTick(cb, err2);
126356
+ process2.nextTick(cb, err2);
125957
126357
  } else {
125958
126358
  state2[kOnFinished].push(cb);
125959
126359
  }
@@ -125982,7 +126382,7 @@ ${values.join("\n")}` : `${blockName} :`;
125982
126382
  state2.prefinished = true;
125983
126383
  stream.emit("prefinish");
125984
126384
  state2.pendingcb++;
125985
- process.nextTick(finish, stream, state2);
126385
+ process2.nextTick(finish, stream, state2);
125986
126386
  }
125987
126387
  }
125988
126388
  state2.sync = true;
@@ -126011,7 +126411,7 @@ ${values.join("\n")}` : `${blockName} :`;
126011
126411
  if (state2.pendingcb === 0) {
126012
126412
  if (sync) {
126013
126413
  state2.pendingcb++;
126014
- process.nextTick(
126414
+ process2.nextTick(
126015
126415
  (stream2, state3) => {
126016
126416
  if (needFinish(state3)) {
126017
126417
  finish(stream2, state3);
@@ -126146,7 +126546,7 @@ ${values.join("\n")}` : `${blockName} :`;
126146
126546
  Writable.prototype.destroy = function(err2, cb) {
126147
126547
  const state2 = this._writableState;
126148
126548
  if (!state2.destroyed && (state2.bufferedIndex < state2.buffered.length || state2[kOnFinished].length)) {
126149
- process.nextTick(errorBuffer, state2);
126549
+ process2.nextTick(errorBuffer, state2);
126150
126550
  }
126151
126551
  destroy.call(this, err2, cb);
126152
126552
  return this;
@@ -126175,7 +126575,7 @@ ${values.join("\n")}` : `${blockName} :`;
126175
126575
  // node_modules/@libp2p/http-utils/node_modules/readable-stream/lib/internal/streams/duplexify.js
126176
126576
  var require_duplexify = __commonJS({
126177
126577
  "node_modules/@libp2p/http-utils/node_modules/readable-stream/lib/internal/streams/duplexify.js"(exports, module) {
126178
- var process = require_browser4();
126578
+ var process2 = require_browser4();
126179
126579
  var bufferModule = require_buffer();
126180
126580
  var {
126181
126581
  isReadable,
@@ -126288,9 +126688,9 @@ ${values.join("\n")}` : `${blockName} :`;
126288
126688
  final(async () => {
126289
126689
  try {
126290
126690
  await promise;
126291
- process.nextTick(cb, null);
126691
+ process2.nextTick(cb, null);
126292
126692
  } catch (err2) {
126293
- process.nextTick(cb, err2);
126693
+ process2.nextTick(cb, err2);
126294
126694
  }
126295
126695
  });
126296
126696
  },
@@ -126369,7 +126769,7 @@ ${values.join("\n")}` : `${blockName} :`;
126369
126769
  const _promise = promise;
126370
126770
  promise = null;
126371
126771
  const { chunk, done, cb } = await _promise;
126372
- process.nextTick(cb);
126772
+ process2.nextTick(cb);
126373
126773
  if (done) return;
126374
126774
  if (signal.aborted)
126375
126775
  throw new AbortError6(void 0, {
@@ -126760,7 +127160,7 @@ ${values.join("\n")}` : `${blockName} :`;
126760
127160
  // node_modules/@libp2p/http-utils/node_modules/readable-stream/lib/internal/streams/pipeline.js
126761
127161
  var require_pipeline = __commonJS({
126762
127162
  "node_modules/@libp2p/http-utils/node_modules/readable-stream/lib/internal/streams/pipeline.js"(exports, module) {
126763
- var process = require_browser4();
127163
+ var process2 = require_browser4();
126764
127164
  var { ArrayIsArray, Promise: Promise2, SymbolAsyncIterator, SymbolDispose } = require_primordials();
126765
127165
  var eos = require_end_of_stream();
126766
127166
  var { once } = require_util();
@@ -126962,7 +127362,7 @@ ${values.join("\n")}` : `${blockName} :`;
126962
127362
  if (!error) {
126963
127363
  lastStreamCleanup.forEach((fn) => fn());
126964
127364
  }
126965
- process.nextTick(callback, error, value2);
127365
+ process2.nextTick(callback, error, value2);
126966
127366
  }
126967
127367
  }
126968
127368
  let ret;
@@ -127041,11 +127441,11 @@ ${values.join("\n")}` : `${blockName} :`;
127041
127441
  if (end) {
127042
127442
  pt.end();
127043
127443
  }
127044
- process.nextTick(finish);
127444
+ process2.nextTick(finish);
127045
127445
  },
127046
127446
  (err2) => {
127047
127447
  pt.destroy(err2);
127048
- process.nextTick(finish, err2);
127448
+ process2.nextTick(finish, err2);
127049
127449
  }
127050
127450
  );
127051
127451
  } else if (isIterable2(ret, true)) {
@@ -127126,7 +127526,7 @@ ${values.join("\n")}` : `${blockName} :`;
127126
127526
  }
127127
127527
  }
127128
127528
  if (signal !== null && signal !== void 0 && signal.aborted || outerSignal !== null && outerSignal !== void 0 && outerSignal.aborted) {
127129
- process.nextTick(abort);
127529
+ process2.nextTick(abort);
127130
127530
  }
127131
127531
  return ret;
127132
127532
  }
@@ -127147,7 +127547,7 @@ ${values.join("\n")}` : `${blockName} :`;
127147
127547
  };
127148
127548
  var endFn = endFn2;
127149
127549
  if (isReadableFinished(src4)) {
127150
- process.nextTick(endFn2);
127550
+ process2.nextTick(endFn2);
127151
127551
  } else {
127152
127552
  src4.once("end", endFn2);
127153
127553
  }
@@ -129350,12 +129750,12 @@ ${values.join("\n")}` : `${blockName} :`;
129350
129750
  type: MESSAGE_TYPES[opcode]
129351
129751
  };
129352
129752
  }
129353
- let mask;
129753
+ let mask2;
129354
129754
  if (isMasked) {
129355
129755
  if (buf.byteLength < offset + 4) {
129356
129756
  return;
129357
129757
  }
129358
- mask = buf.subarray(offset, offset + 4);
129758
+ mask2 = buf.subarray(offset, offset + 4);
129359
129759
  offset += 4;
129360
129760
  }
129361
129761
  if (buf.byteLength < offset + length20) {
@@ -129363,8 +129763,8 @@ ${values.join("\n")}` : `${blockName} :`;
129363
129763
  }
129364
129764
  let data = buf.subarray(offset, offset + length20);
129365
129765
  offset += length20;
129366
- if (mask != null) {
129367
- data = applyMask(data, mask);
129766
+ if (mask2 != null) {
129767
+ data = applyMask(data, mask2);
129368
129768
  }
129369
129769
  buf.consume(offset);
129370
129770
  return {
@@ -129372,12 +129772,12 @@ ${values.join("\n")}` : `${blockName} :`;
129372
129772
  data
129373
129773
  };
129374
129774
  }
129375
- function applyMask(data, mask) {
129775
+ function applyMask(data, mask2) {
129376
129776
  let m2 = 0;
129377
129777
  for (let i = 0; i < data.byteLength; i++) {
129378
- data[i] = data[i] ^ mask[m2];
129778
+ data[i] = data[i] ^ mask2[m2];
129379
129779
  m2++;
129380
- if (m2 === mask.byteLength) {
129780
+ if (m2 === mask2.byteLength) {
129381
129781
  m2 = 0;
129382
129782
  }
129383
129783
  }
@@ -131917,18 +132317,18 @@ ${values.join("\n")}` : `${blockName} :`;
131917
132317
  * Zig-zag encodes this long bits
131918
132318
  */
131919
132319
  zzEncode() {
131920
- const mask = this.hi >> 31;
131921
- this.hi = ((this.hi << 1 | this.lo >>> 31) ^ mask) >>> 0;
131922
- this.lo = (this.lo << 1 ^ mask) >>> 0;
132320
+ const mask2 = this.hi >> 31;
132321
+ this.hi = ((this.hi << 1 | this.lo >>> 31) ^ mask2) >>> 0;
132322
+ this.lo = (this.lo << 1 ^ mask2) >>> 0;
131923
132323
  return this;
131924
132324
  }
131925
132325
  /**
131926
132326
  * Zig-zag decodes this long bits
131927
132327
  */
131928
132328
  zzDecode() {
131929
- const mask = -(this.lo & 1);
131930
- this.lo = ((this.lo >>> 1 | this.hi << 31) ^ mask) >>> 0;
131931
- this.hi = (this.hi >>> 1 ^ mask) >>> 0;
132329
+ const mask2 = -(this.lo & 1);
132330
+ this.lo = ((this.lo >>> 1 | this.hi << 31) ^ mask2) >>> 0;
132331
+ this.hi = (this.hi >>> 1 ^ mask2) >>> 0;
131932
132332
  return this;
131933
132333
  }
131934
132334
  /**
@@ -135418,12 +135818,12 @@ ${values.join("\n")}` : `${blockName} :`;
135418
135818
  g[i] &= 8191;
135419
135819
  }
135420
135820
  g[9] -= 1 << 13;
135421
- let mask = (c ^ 1) - 1;
135821
+ let mask2 = (c ^ 1) - 1;
135422
135822
  for (let i = 0; i < 10; i++)
135423
- g[i] &= mask;
135424
- mask = ~mask;
135823
+ g[i] &= mask2;
135824
+ mask2 = ~mask2;
135425
135825
  for (let i = 0; i < 10; i++)
135426
- h3[i] = h3[i] & mask | g[i];
135826
+ h3[i] = h3[i] & mask2 | g[i];
135427
135827
  h3[0] = (h3[0] | h3[1] << 13) & 65535;
135428
135828
  h3[1] = (h3[1] >>> 3 | h3[2] << 10) & 65535;
135429
135829
  h3[2] = (h3[2] >>> 6 | h3[3] << 7) & 65535;
@@ -136870,18 +137270,18 @@ ${values.join("\n")}` : `${blockName} :`;
136870
137270
  * Zig-zag encodes this long bits
136871
137271
  */
136872
137272
  zzEncode() {
136873
- const mask = this.hi >> 31;
136874
- this.hi = ((this.hi << 1 | this.lo >>> 31) ^ mask) >>> 0;
136875
- this.lo = (this.lo << 1 ^ mask) >>> 0;
137273
+ const mask2 = this.hi >> 31;
137274
+ this.hi = ((this.hi << 1 | this.lo >>> 31) ^ mask2) >>> 0;
137275
+ this.lo = (this.lo << 1 ^ mask2) >>> 0;
136876
137276
  return this;
136877
137277
  }
136878
137278
  /**
136879
137279
  * Zig-zag decodes this long bits
136880
137280
  */
136881
137281
  zzDecode() {
136882
- const mask = -(this.lo & 1);
136883
- this.lo = ((this.lo >>> 1 | this.hi << 31) ^ mask) >>> 0;
136884
- this.hi = (this.hi >>> 1 ^ mask) >>> 0;
137282
+ const mask2 = -(this.lo & 1);
137283
+ this.lo = ((this.lo >>> 1 | this.hi << 31) ^ mask2) >>> 0;
137284
+ this.hi = (this.hi >>> 1 ^ mask2) >>> 0;
136885
137285
  return this;
136886
137286
  }
136887
137287
  /**
@@ -139954,11 +140354,11 @@ a=end-of-candidates
139954
140354
  function toToken10(data, pos, prefix, length20, options2) {
139955
140355
  const totLength = prefix + length20;
139956
140356
  assertEnoughData3(data, pos, totLength);
139957
- const tok = new Token3(Type3.string, toStr3(data, pos + prefix, pos + totLength), totLength);
140357
+ const tok2 = new Token3(Type3.string, toStr3(data, pos + prefix, pos + totLength), totLength);
139958
140358
  if (options2.retainStringBytes === true) {
139959
- tok.byteValue = data.slice(pos + prefix, pos + totLength);
140359
+ tok2.byteValue = data.slice(pos + prefix, pos + totLength);
139960
140360
  }
139961
- return tok;
140361
+ return tok2;
139962
140362
  }
139963
140363
  function decodeStringCompact3(data, pos, minor, options2) {
139964
140364
  return toToken10(data, pos, 1, minor, options2);
@@ -142177,11 +142577,11 @@ a=end-of-candidates
142177
142577
  function toToken14(data, pos, prefix, length20, options2) {
142178
142578
  const totLength = prefix + length20;
142179
142579
  assertEnoughData4(data, pos, totLength);
142180
- const tok = new Token4(Type4.string, toStr4(data, pos + prefix, pos + totLength), totLength);
142580
+ const tok2 = new Token4(Type4.string, toStr4(data, pos + prefix, pos + totLength), totLength);
142181
142581
  if (options2.retainStringBytes === true) {
142182
- tok.byteValue = data.slice(pos + prefix, pos + totLength);
142582
+ tok2.byteValue = data.slice(pos + prefix, pos + totLength);
142183
142583
  }
142184
- return tok;
142584
+ return tok2;
142185
142585
  }
142186
142586
  function decodeStringCompact4(data, pos, minor, options2) {
142187
142587
  return toToken14(data, pos, 1, minor, options2);
@@ -148151,11 +148551,11 @@ ${indent}[Error list was empty]`;
148151
148551
  function toToken18(data, pos, prefix, length20, options2) {
148152
148552
  const totLength = prefix + length20;
148153
148553
  assertEnoughData5(data, pos, totLength);
148154
- const tok = new Token5(Type5.string, toStr5(data, pos + prefix, pos + totLength), totLength);
148554
+ const tok2 = new Token5(Type5.string, toStr5(data, pos + prefix, pos + totLength), totLength);
148155
148555
  if (options2.retainStringBytes === true) {
148156
- tok.byteValue = data.slice(pos + prefix, pos + totLength);
148556
+ tok2.byteValue = data.slice(pos + prefix, pos + totLength);
148157
148557
  }
148158
- return tok;
148558
+ return tok2;
148159
148559
  }
148160
148560
  function decodeStringCompact5(data, pos, minor, options2) {
148161
148561
  return toToken18(data, pos, 1, minor, options2);
@@ -150573,14 +150973,14 @@ ${error.stack}` : head;
150573
150973
  return out;
150574
150974
  }
150575
150975
  }
150576
- function heliaEnabled(storage2 = defaultStorage2()) {
150976
+ function heliaEnabled(storage2 = defaultStorage3()) {
150577
150977
  try {
150578
150978
  return storage2.getItem(HELIA_ENABLED_KEY) === "1";
150579
150979
  } catch {
150580
150980
  return false;
150581
150981
  }
150582
150982
  }
150583
- function setHeliaEnabled(enabled, storage2 = defaultStorage2()) {
150983
+ function setHeliaEnabled(enabled, storage2 = defaultStorage3()) {
150584
150984
  try {
150585
150985
  storage2.setItem(HELIA_ENABLED_KEY, enabled ? "1" : "0");
150586
150986
  return { ok: true, enabled: !!enabled };
@@ -150926,8 +151326,8 @@ ${error.stack}` : head;
150926
151326
  N3: 40,
150927
151327
  N4: 10
150928
151328
  };
150929
- exports.isValid = function isValid2(mask) {
150930
- return mask != null && mask !== "" && !isNaN(mask) && mask >= 0 && mask <= 7;
151329
+ exports.isValid = function isValid2(mask2) {
151330
+ return mask2 != null && mask2 !== "" && !isNaN(mask2) && mask2 >= 0 && mask2 <= 7;
150931
151331
  };
150932
151332
  exports.from = function from6(value2) {
150933
151333
  return exports.isValid(value2) ? parseInt(value2, 10) : void 0;
@@ -151731,8 +152131,8 @@ ${error.stack}` : head;
151731
152131
  var G15 = 1 << 10 | 1 << 8 | 1 << 5 | 1 << 4 | 1 << 2 | 1 << 1 | 1 << 0;
151732
152132
  var G15_MASK = 1 << 14 | 1 << 12 | 1 << 10 | 1 << 4 | 1 << 1;
151733
152133
  var G15_BCH = Utils.getBCHDigit(G15);
151734
- exports.getEncodedBits = function getEncodedBits(errorCorrectionLevel, mask) {
151735
- const data = errorCorrectionLevel.bit << 3 | mask;
152134
+ exports.getEncodedBits = function getEncodedBits(errorCorrectionLevel, mask2) {
152135
+ const data = errorCorrectionLevel.bit << 3 | mask2;
151736
152136
  let d2 = data << 10;
151737
152137
  while (Utils.getBCHDigit(d2) - G15_BCH >= 0) {
151738
152138
  d2 ^= G15 << Utils.getBCHDigit(d2) - G15_BCH;
@@ -152459,16 +152859,16 @@ ${error.stack}` : head;
152459
152859
  }
152460
152860
  let errorCorrectionLevel = ECLevel.M;
152461
152861
  let version4;
152462
- let mask;
152862
+ let mask2;
152463
152863
  if (typeof options2 !== "undefined") {
152464
152864
  errorCorrectionLevel = ECLevel.from(options2.errorCorrectionLevel, ECLevel.M);
152465
152865
  version4 = Version.from(options2.version);
152466
- mask = MaskPattern.from(options2.maskPattern);
152866
+ mask2 = MaskPattern.from(options2.maskPattern);
152467
152867
  if (options2.toSJISFunc) {
152468
152868
  Utils.setToSJISFunction(options2.toSJISFunc);
152469
152869
  }
152470
152870
  }
152471
- return createSymbol(data, version4, errorCorrectionLevel, mask);
152871
+ return createSymbol(data, version4, errorCorrectionLevel, mask2);
152472
152872
  };
152473
152873
  }
152474
152874
  });
@@ -153787,8 +154187,8 @@ ${error.stack}` : head;
153787
154187
  var bitsLeft = 8 - this.bitOffset;
153788
154188
  var toRead = numBits < bitsLeft ? numBits : bitsLeft;
153789
154189
  var bitsToNotRead = bitsLeft - toRead;
153790
- var mask = 255 >> 8 - toRead << bitsToNotRead;
153791
- result = (this.bytes[this.byteOffset] & mask) >> bitsToNotRead;
154190
+ var mask2 = 255 >> 8 - toRead << bitsToNotRead;
154191
+ result = (this.bytes[this.byteOffset] & mask2) >> bitsToNotRead;
153792
154192
  numBits -= toRead;
153793
154193
  this.bitOffset += toRead;
153794
154194
  if (this.bitOffset === 8) {
@@ -153804,8 +154204,8 @@ ${error.stack}` : head;
153804
154204
  }
153805
154205
  if (numBits > 0) {
153806
154206
  var bitsToNotRead = 8 - numBits;
153807
- var mask = 255 >> bitsToNotRead << bitsToNotRead;
153808
- result = result << numBits | (this.bytes[this.byteOffset] & mask) >> bitsToNotRead;
154207
+ var mask2 = 255 >> bitsToNotRead << bitsToNotRead;
154208
+ result = result << numBits | (this.bytes[this.byteOffset] & mask2) >> bitsToNotRead;
153809
154209
  this.bitOffset += numBits;
153810
154210
  }
153811
154211
  }
@@ -162923,129 +163323,345 @@ ${error.stack}` : head;
162923
163323
  return result;
162924
163324
  }
162925
163325
 
162926
- // src/agents/contacts/grant-payload.ts
162927
- function canonicalGrantPayload(g) {
163326
+ // src/web/mobile-core.ts
163327
+ init_mobile_contacts();
163328
+
163329
+ // src/agents/mobile-task-actions.ts
163330
+ var MOBILE_TASK_ACTION_KINDS = [
163331
+ "group_join",
163332
+ // 入群 (加入一个 OrbitDB 群 store)
163333
+ "group_leave",
163334
+ // 退群
163335
+ "group_create",
163336
+ // 建群 (会自动入群)
163337
+ "announce_publish",
163338
+ // 发布任务公告 (落盘公告 + 向注册表公告 + 记脉冲)
163339
+ "announce_to_group",
163340
+ // 把板上的公告推进群里 (CLI: bolloon task announce)
163341
+ "trail_post"
163342
+ // 过程留痕: 接单/交付/初筛/终审 (CLI: bolloon task post)
163343
+ ];
163344
+ var TASK_ACTION_TTL_MS = 10 * 6e4;
163345
+ var TASK_ACTION_DEFAULT_TTL_MS = 5 * 6e4;
163346
+ var TASK_ACTION_CLOCK_SKEW_MS = 2 * 6e4;
163347
+ function tok(v) {
163348
+ if (v === null || v === void 0) return "-";
163349
+ const s2 = String(v);
163350
+ if (!s2) return "-";
163351
+ return s2.replace(/\r?\n/g, "\\n");
163352
+ }
163353
+ function taskActionContentText(req) {
163354
+ const kind = String(req?.kind ?? "");
163355
+ const base6 = [
163356
+ `kind=${tok(kind)}`,
163357
+ `group=${tok(req?.groupRef)}`,
163358
+ `ann=${tok(req?.announcementId)}`
163359
+ ];
163360
+ if (kind === "announce_publish") {
163361
+ base6.push(`cap=${tok(req?.capability)}`);
163362
+ base6.push(`budget=${tok(req?.budgetHuman)}${req?.currency ? `@${tok(req.currency)}` : ""}`);
163363
+ base6.push(`deadline=${tok(req?.deadline)}`);
163364
+ base6.push(`instruction=${tok(req?.instruction)}`);
163365
+ } else if (kind === "announce_to_group") {
163366
+ base6.push(`criteria=${tok(req?.criteria)}`);
163367
+ } else if (kind === "trail_post") {
163368
+ base6.push(`trail=${tok(req?.trailKind)}`);
163369
+ base6.push(`round=${tok(req?.round)}`);
163370
+ base6.push(`price=${tok(req?.price)}`);
163371
+ base6.push(`hash=${tok(req?.hash)}`);
163372
+ base6.push(`bytes=${tok(req?.bytes)}`);
163373
+ base6.push(`checks=${tok(req?.checks)}`);
163374
+ base6.push(`verdict=${tok(req?.verdict)}`);
163375
+ }
163376
+ return base6.join("\n");
163377
+ }
163378
+ function canonicalTaskActionPayload(a) {
162928
163379
  return JSON.stringify({
162929
- grantId: g.grantId,
162930
- identityId: g.identityId,
162931
- ownerDid: g.ownerDid,
162932
- level: g.level,
162933
- channels: g.channels,
162934
- contactScope: g.contactScope,
162935
- taskScope: g.taskScope,
162936
- contentScope: g.contentScope,
162937
- replyWakeAllowed: g.replyWakeAllowed,
162938
- autoSend: g.autoSend,
162939
- sensitiveContentAllowed: g.sensitiveContentAllowed,
162940
- newRecipientAllowed: g.newRecipientAllowed,
162941
- grantedAt: g.grantedAt,
162942
- grantedBy: g.grantedBy,
162943
- grantedVia: g.grantedVia,
162944
- deviceIds: [...g.deviceIds].sort(),
162945
- status: g.status,
162946
- revokedAt: g.revokedAt || "",
162947
- grantVersion: g.grantVersion
163380
+ actionId: a.actionId,
163381
+ kind: a.kind,
163382
+ deviceId: a.deviceId,
163383
+ ownerDid: a.ownerDid,
163384
+ targetRef: a.targetRef || "",
163385
+ contentDigest: a.contentDigest,
163386
+ createdAt: a.createdAt,
163387
+ expiresAt: a.expiresAt,
163388
+ grantedBy: a.grantedBy,
163389
+ via: a.via
162948
163390
  });
162949
163391
  }
162950
- function canonicalRevocationPayload(r) {
162951
- return JSON.stringify({ grantId: r.grantId, grantVersion: r.grantVersion, revokedAt: r.revokedAt, by: r.by });
162952
- }
162953
- function presetForChoice(choice) {
162954
- const base6 = { channels: "both", replyWakeAllowed: true, autoSend: true, newRecipientAllowed: false };
162955
- switch (choice) {
162956
- case "task_once":
162957
- return { ...base6, level: "task_once", contactScope: "verified_contacts", taskScope: "current_goal", contentScope: "normal", autoSend: false, sensitiveContentAllowed: false };
162958
- case "persistent":
162959
- return { ...base6, level: "persistent", contactScope: "verified_contacts", taskScope: "all_future_goals", contentScope: "normal", sensitiveContentAllowed: false };
162960
- case "full_contact_access":
162961
- return { ...base6, level: "full_contact_access", contactScope: "all_contacts", taskScope: "all_future_goals", contentScope: "sensitive", sensitiveContentAllowed: true };
162962
- }
163392
+ function isIso(s2) {
163393
+ return typeof s2 === "string" && s2.length >= 20 && Number.isFinite(Date.parse(s2));
163394
+ }
163395
+ function checkTaskActionFreshness(a, now) {
163396
+ if (!isIso(a?.createdAt) || !isIso(a?.expiresAt)) return { ok: false, reason: "bad_time" };
163397
+ const created = Date.parse(a.createdAt);
163398
+ const expires = Date.parse(a.expiresAt);
163399
+ if (expires - created > TASK_ACTION_TTL_MS) return { ok: false, reason: "ttl_too_long" };
163400
+ if (expires < created) return { ok: false, reason: "bad_time" };
163401
+ if (now > expires) return { ok: false, reason: "expired" };
163402
+ if (now < created - TASK_ACTION_CLOCK_SKEW_MS) return { ok: false, reason: "not_yet_valid" };
163403
+ return { ok: true };
162963
163404
  }
162964
163405
 
162965
- // src/web/mobile-contacts.ts
162966
- var K_DEVICE = "bolloon.contacts.device.v1";
162967
- var K_QUEUE = "bolloon.contacts.queue.v1";
162968
- var K_CAPS = "bolloon.contacts.capabilities.v1";
162969
- var cryptoOf = (c) => c || globalThis.crypto || null;
162970
- function ed25519Available(c) {
162971
- const cobj = cryptoOf(c);
162972
- if (!cobj?.subtle) return false;
162973
- try {
162974
- const alg = { name: "Ed25519" };
162975
- return typeof cobj.subtle.importKey === "function" && !!alg;
162976
- } catch {
162977
- return false;
162978
- }
163406
+ // src/agents/task-public-text.ts
163407
+ var PRIVACY_RULES = [
163408
+ { rule: "wallet-address", why: "\u4EE5\u592A\u574A/EVM \u94B1\u5305\u5730\u5740", re: /(?<![0-9a-fA-FxX])0x[0-9a-fA-F]{40}(?![0-9a-fA-F])/ },
163409
+ { rule: "private-key-hex", why: "32 \u5B57\u8282\u79C1\u94A5\u5F62\u6001 (0x + 64 hex)", re: /(?<![0-9a-fA-FxX])0x[0-9a-fA-F]{64}(?![0-9a-fA-F])/ },
163410
+ { rule: "hex-0x-blob", why: "0x \u5F00\u5934\u7684\u5341\u516D\u8FDB\u5236\u5757 (\u7B7E\u540D/\u54C8\u5E0C/\u5BC6\u94A5\u90FD\u957F\u8FD9\u6837, \u7FA4\u91CC\u4E00\u5F8B\u4E0D\u653E\u539F\u6587)", re: /(?<![0-9a-fA-FxX])0x[0-9a-fA-F]{8,}(?![0-9a-fA-F])/ },
163411
+ { rule: "did", why: "\u53BB\u4E2D\u5FC3\u5316\u8EAB\u4EFD DID", re: /did:[a-z0-9]+:[A-Za-z0-9._:%-]{3,}/i },
163412
+ { rule: "peer-id", why: "libp2p peerId (12D3Koo\u2026/Qm\u2026)", re: /12D3Koo[A-Za-z0-9]{8,}|Qm[1-9A-HJ-NP-Za-km-z]{30,}/ },
163413
+ { rule: "multiaddr", why: "libp2p multiaddr \u6216 store \u5730\u5740 (/ip4 /tcp /p2p /orbitdb \u2026)", re: /\/(ip4|ip6|dns|dns4|dns6|tcp|udp|ws|wss|quic|p2p|p2p-circuit|ipfs|orbitdb)(?![A-Za-z0-9])/i },
163414
+ { rule: "orbitdb-link", why: "OrbitDB \u94FE\u63A5 (\u7FA4\u9080\u8BF7\u94FE\u63A5\u672C\u8EAB\u4E5F\u4E0D\u8FDB\u7FA4\u6D88\u606F)", re: /orbitdb:\/\//i },
163415
+ { rule: "ipv4", why: "IPv4 \u5730\u5740", re: /(?<!\d)\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}(?!\d)/ },
163416
+ { rule: "ipv6", why: "IPv6 \u5730\u5740 (\u4E0D\u542B ISO \u65F6\u95F4\u6233)", re: /(?<![0-9A-Za-z])(?:[0-9a-fA-F]{1,4})?::(?:[0-9a-fA-F]{1,4}:){0,6}[0-9a-fA-F]{1,4}(?![0-9A-Za-z])|(?<![0-9A-Za-z:])(?:[0-9a-fA-F]{1,4}:){3,}[0-9a-fA-F]{1,4}(?![0-9A-Za-z])/ },
163417
+ { rule: "pem", why: "PEM \u6750\u6599 (\u79C1\u94A5/\u8BC1\u4E66)", re: /-----BEGIN [A-Z ]*(PRIVATE KEY|CERTIFICATE)/ },
163418
+ { rule: "secret-words", why: "\u79C1\u94A5/\u52A9\u8BB0\u8BCD/\u5BC6\u94A5\u5B57\u6837 (\u6B63\u6587\u91CC\u63D0\u5230\u5373\u505C, \u4EBA\u4E0D\u8BE5\u628A\u5BC6\u94A5\u5199\u8FDB\u7FA4)", re: /(private\s*key|seed\s*phrase|mnemonic|私钥|助记词|密钥材料)/i },
163419
+ { rule: "url", why: "http(s) \u7AEF\u70B9 (\u7AEF\u70B9\u4F1A\u5E26 host/IP \u4E0E\u7528\u9014\u6307\u7EB9)", re: /https?:\/\/\S+/i },
163420
+ { rule: "email", why: "\u90AE\u7BB1\u5730\u5740", re: /[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}/ }
163421
+ ];
163422
+ function mask(s2) {
163423
+ const t = String(s2 || "");
163424
+ return t.length <= 4 ? "****" : `${t.slice(0, 4)}\u2026`;
162979
163425
  }
162980
- function bytesOf(s2) {
162981
- const src4 = new TextEncoder().encode(s2);
162982
- const out = new Uint8Array(new ArrayBuffer(src4.byteLength));
162983
- out.set(src4);
163426
+ function scanPublicText(text) {
163427
+ const t = String(text ?? "");
163428
+ const out = [];
163429
+ const seen = /* @__PURE__ */ new Set();
163430
+ for (const r of PRIVACY_RULES) {
163431
+ const m2 = r.re.exec(t);
163432
+ if (!m2) continue;
163433
+ if (seen.has(r.rule)) continue;
163434
+ seen.add(r.rule);
163435
+ out.push({ rule: r.rule, why: r.why, masked: mask(String(m2[0])), chars: String(m2[0]).length });
163436
+ }
162984
163437
  return out;
162985
163438
  }
162986
- function b64(buf) {
162987
- const arr = new Uint8Array(buf);
162988
- let out = "";
162989
- for (let i = 0; i < arr.length; i++) out += String.fromCharCode(arr[i]);
162990
- return btoa(out);
162991
- }
162992
- function pemFromSpki(spki) {
162993
- const b = b64(spki).replace(/(.{64})/g, "$1\n").trim();
162994
- return `-----BEGIN PUBLIC KEY-----
162995
- ${b}
162996
- -----END PUBLIC KEY-----
162997
- `;
163439
+ function requirePublicText(text) {
163440
+ const v = scanPublicText(text);
163441
+ return v.length === 0 ? { ok: true } : { ok: false, violations: v };
163442
+ }
163443
+ var NODE_IDENTITY_RULES = PRIVACY_RULES.filter((r) => r.rule !== "orbitdb-link").map((r) => r.rule === "multiaddr" ? { ...r, re: /\/(ip4|ip6|dns|dns4|dns6|tcp|udp|ws|wss|quic|p2p|p2p-circuit|ipfs)(?![A-Za-z0-9])/i } : r);
163444
+
163445
+ // src/agents/mobile-task-views.ts
163446
+ function shortId(id, n = 12) {
163447
+ const s2 = String(id ?? "").trim();
163448
+ if (!s2) return "";
163449
+ return s2.length <= n ? s2 : `${s2.slice(0, n)}\u2026`;
163450
+ }
163451
+ function isProtocolId(s2) {
163452
+ const v = String(s2 ?? "").trim();
163453
+ return /^ann-[0-9a-f]{8,}$/i.test(v) || /^act-[a-z0-9-]{6,}$/i.test(v) || /^zdpu[0-9a-zA-Z]{8,}$/.test(v) || /^\/orbitdb\//.test(v);
163454
+ }
163455
+ function formatRelative(ms2, lang) {
163456
+ if (ms2 === null || !Number.isFinite(ms2)) return lang === "zh" ? "\u2014" : "\u2014";
163457
+ const past = ms2 >= 0;
163458
+ const abs = Math.abs(ms2);
163459
+ const min = Math.floor(abs / 6e4);
163460
+ const hr = Math.floor(abs / 36e5);
163461
+ const day = Math.floor(abs / 864e5);
163462
+ const unit = day >= 1 ? lang === "zh" ? `${day} \u5929` : `${day} d` : hr >= 1 ? lang === "zh" ? `${hr} \u5C0F\u65F6` : `${hr} h` : min >= 1 ? lang === "zh" ? `${min} \u5206\u949F` : `${min} min` : lang === "zh" ? "\u4E0D\u5230 1 \u5206\u949F" : "under a minute";
163463
+ if (lang === "zh") return past ? `${unit}\u524D` : `${unit}\u540E`;
163464
+ return past ? `${unit} ago` : `in ${unit}`;
163465
+ }
163466
+ var STATUS_LABELS = {
163467
+ open: { zh: "\u53EF\u63A5\u5355", en: "Open for claims" },
163468
+ claimed: { zh: "\u5DF2\u88AB\u63A5\u5355", en: "Claimed" },
163469
+ cancelled: { zh: "\u5DF2\u53D6\u6D88", en: "Cancelled" },
163470
+ unknown: { zh: "\u72B6\u6001\u672A\u77E5", en: "Unknown status" }
163471
+ };
163472
+ function budgetLabelOf(b) {
163473
+ if (!b || typeof b !== "object") return "";
163474
+ const amt = String(b.maxAmount ?? "").trim();
163475
+ if (!amt) return "";
163476
+ return `${amt} ${String(b.currency || "").toUpperCase()}${b.network ? ` \xB7 ${String(b.network)}` : ""}`;
163477
+ }
163478
+ function boardItemOf(e, now) {
163479
+ const dl = Number.isFinite(Number(e?.deadline)) ? Number(e.deadline) : null;
163480
+ const ca = Number.isFinite(Number(e?.createdAt)) ? Number(e.createdAt) : null;
163481
+ const status = String(e?.status || "unknown");
163482
+ return {
163483
+ idShort: shortId(e?.announcementId),
163484
+ ref: String(e?.announcementId || ""),
163485
+ capability: String(e?.capability || ""),
163486
+ status,
163487
+ statusLabel: STATUS_LABELS[status] || STATUS_LABELS.unknown,
163488
+ budgetLabel: budgetLabelOf(e?.budget),
163489
+ deadlineInMs: dl === null ? null : dl - now,
163490
+ createdAtMs: ca,
163491
+ claimCount: Number.isFinite(Number(e?.claimCount)) ? Number(e.claimCount) : 0,
163492
+ preview: e?.instructionPreview ? String(e.instructionPreview).slice(0, 120) : null,
163493
+ remote: !!e?.remote,
163494
+ claimable: !!e?.claimable,
163495
+ signatureVerified: e?.signatureVerified === true ? true : e?.signatureVerified === false ? false : null
163496
+ };
162998
163497
  }
162999
- async function sha256Hex(c, text) {
163000
- const d2 = await c.subtle.digest("SHA-256", bytesOf(text));
163001
- return [...new Uint8Array(d2)].map((x) => x.toString(16).padStart(2, "0")).join("");
163498
+ function buildBoardView(view, now) {
163499
+ const items = (Array.isArray(view?.entries) ? view.entries : []).map((e) => boardItemOf(e, now));
163500
+ return {
163501
+ items,
163502
+ localCount: Number(view?.localCount) || 0,
163503
+ remoteCount: Number(view?.remoteCount) || 0,
163504
+ registryReady: view?.registryReady === true,
163505
+ registryError: view?.registryError ? String(view.registryError).slice(0, 160) : null,
163506
+ notes: Array.isArray(view?.notes) ? view.notes.map((n) => String(n)) : [],
163507
+ openCount: items.filter((i) => i.claimable).length
163508
+ };
163002
163509
  }
163003
- async function loadOrCreateDeviceKey(storage2, c) {
163004
- const cobj = cryptoOf(c);
163005
- if (!cobj?.subtle) throw new Error("device_signing_unavailable: \u672C\u673A\u6CA1\u6709 WebCrypto");
163006
- const raw = storage2.getItem(K_DEVICE);
163007
- if (raw) {
163008
- try {
163009
- const parsed = JSON.parse(raw);
163010
- if (parsed?.privateKeyJwk && parsed?.publicKeyPem && parsed?.deviceId) return parsed;
163011
- } catch {
163510
+ var TRAIL_KIND_LABELS = {
163511
+ announce: { zh: "\u516C\u544A", en: "Announce" },
163512
+ claim: { zh: "\u63A5\u5355\u58F0\u660E", en: "Claim" },
163513
+ deliver: { zh: "\u4EA4\u4ED8", en: "Delivery" },
163514
+ screen: { zh: "\u521D\u7B5B\u7ED3\u679C", en: "Screening" },
163515
+ final: { zh: "\u7EC8\u5BA1\u7ED3\u8BBA", en: "Final verdict" }
163516
+ };
163517
+ var INCONSISTENCY_LABELS = {
163518
+ "final-accept-without-delivery": { zh: "\u8BF4\u901A\u8FC7\u4E86, \u4F46\u7FA4\u91CC\u6CA1\u6709\u4EA4\u4ED8\u75D5\u8FF9", en: "Accepted, but no delivery trace in the group" },
163519
+ "screen-without-delivery": { zh: "\u6709\u521D\u7B5B\u7ED3\u679C, \u4F46\u6CA1\u6709\u4EA4\u4ED8\u75D5\u8FF9", en: "Screening exists without a delivery trace" },
163520
+ "claim-without-announce": { zh: "\u6709\u4EBA\u63A5\u5355, \u4F46\u7FA4\u91CC\u6CA1\u6709\u516C\u544A\u75D5\u8FF9", en: "Claim without an announcement trace" },
163521
+ "deliver-without-claim": { zh: "\u6709\u4EA4\u4ED8, \u4F46\u6CA1\u4EBA\u63A5\u8FC7\u5355", en: "Delivery without a claim" },
163522
+ "both-accept-and-reject": { zh: "\u540C\u65F6\u51FA\u73B0\u901A\u8FC7\u548C\u62D2\u7EDD", en: "Both accepted and rejected appear" },
163523
+ "group-message-hit-privacy-rule": { zh: "\u7FA4\u91CC\u6709\u4EBA\u53D1\u4E86\u5E26\u6807\u8BC6\u7B26\u7684\u6D88\u606F (\u5DF2\u906E\u853D, \u4E0D\u5F53\u4E8B\u5B9E\u91C7\u7528)", en: "A group message hit a privacy rule (masked, not used as fact)" }
163524
+ };
163525
+ function buildTrailView(summary) {
163526
+ const entries = (Array.isArray(summary?.entries) ? summary.entries : []).map((e) => {
163527
+ const facts = [];
163528
+ for (const [k, v] of Object.entries(e?.fields || {})) {
163529
+ if (k === "kind") continue;
163530
+ const raw = String(v ?? "");
163531
+ const hit = scanPublicText(raw);
163532
+ if (hit.length) {
163533
+ facts.push({ k: String(k), v: `[\u5DF2\u906E\u853D:${hit[0].rule}]` });
163534
+ continue;
163535
+ }
163536
+ facts.push({ k: String(k), v: k === "id" || isProtocolId(raw) ? shortId(raw) : raw });
163012
163537
  }
163013
- }
163014
- let pair;
163538
+ const kind = String(e?.kind || "");
163539
+ return {
163540
+ idShort: shortId(e?.announcementId),
163541
+ kind,
163542
+ kindLabel: TRAIL_KIND_LABELS[kind] || { zh: kind, en: kind },
163543
+ sender: String(e?.sender || "unknown"),
163544
+ atMs: Number.isFinite(Number(e?.at)) ? Number(e.at) : 0,
163545
+ facts
163546
+ };
163547
+ });
163548
+ const inc = (Array.isArray(summary?.inconsistencies) ? summary.inconsistencies : []).map((c) => ({
163549
+ code: String(c),
163550
+ label: INCONSISTENCY_LABELS[String(c)] || { zh: String(c), en: String(c) }
163551
+ }));
163552
+ return {
163553
+ count: Number(summary?.count) || entries.length,
163554
+ byKind: summary?.byKind || {},
163555
+ entries,
163556
+ announcementIdsShort: (Array.isArray(summary?.announcements) ? summary.announcements : []).map((a) => shortId(a)),
163557
+ flags: summary?.flags || {},
163558
+ inconsistencies: inc,
163559
+ redactedCount: Array.isArray(summary?.redacted) ? summary.redacted.length : 0,
163560
+ ignoredMessages: Number(summary?.ignoredMessages) || 0,
163561
+ privacyHits: inc.some((x) => x.code === "group-message-hit-privacy-rule") || Array.isArray(summary?.redacted) && summary.redacted.length > 0
163562
+ };
163563
+ }
163564
+ function buildGroupsView(groups) {
163565
+ return (Array.isArray(groups) ? groups : []).map((g) => ({
163566
+ idShort: shortId(g?.id),
163567
+ name: String(g?.name || ""),
163568
+ joinedAt: String(g?.createdAt || "")
163569
+ }));
163570
+ }
163571
+ function describeTaskActionForConfirm(kind, req) {
163572
+ const lines = [];
163573
+ const push = (k, kEn, v) => {
163574
+ const s2 = String(v ?? "").trim();
163575
+ if (s2 && s2 !== "-") lines.push({ k, kEn, v: s2 });
163576
+ };
163577
+ push("\u7FA4", "group", req?.groupRef ? shortId(req.groupRef, 24) : "");
163578
+ push("\u516C\u544A\u53F7", "announcement", req?.announcementId ? shortId(req.announcementId) : "");
163579
+ push("\u80FD\u529B", "capability", req?.capability);
163580
+ push("\u9884\u7B97", "budget", req?.budgetHuman ? `${req.budgetHuman} ${String(req?.currency || "").toUpperCase()}` : "");
163581
+ push("\u4EFB\u52A1\u6B63\u6587", "instruction", req?.instruction);
163582
+ push("\u622A\u6B62", "deadline", req?.deadline);
163583
+ push("\u9A8C\u6536\u5224\u636E", "criteria", req?.criteria);
163584
+ push("\u671F\u53F7", "round", req?.round);
163585
+ push("\u75D5\u8FF9\u7C7B\u578B", "trail kind", req?.trailKind ? TRAIL_KIND_LABELS[String(req.trailKind)]?.zh || String(req.trailKind) : "");
163586
+ push("\u58F0\u660E\u4EF7\u683C", "declared price", req?.price);
163587
+ push("\u4EA4\u4ED8\u54C8\u5E0C", "delivery hash", req?.hash);
163588
+ push("\u5B57\u8282\u6570", "bytes", req?.bytes);
163589
+ push("\u9010\u6761\u521D\u7B5B", "per-item screen", req?.checks);
163590
+ push("\u7EC8\u5BA1\u7ED3\u8BBA", "final verdict", req?.verdict);
163591
+ const titles = {
163592
+ group_join: { zh: "\u52A0\u5165\u8FD9\u4E2A\u7FA4", en: "Join this group" },
163593
+ group_leave: { zh: "\u9000\u51FA\u8FD9\u4E2A\u7FA4", en: "Leave this group" },
163594
+ group_create: { zh: "\u5EFA\u4E00\u4E2A\u65B0\u7FA4", en: "Create a new group" },
163595
+ announce_publish: { zh: "\u53D1\u5E03\u4EFB\u52A1\u516C\u544A", en: "Publish a task announcement" },
163596
+ announce_to_group: { zh: "\u628A\u8FD9\u671F\u516C\u544A\u53D1\u8FDB\u7FA4", en: "Post this announcement into the group" },
163597
+ trail_post: { zh: "\u5F80\u7FA4\u91CC\u7559\u4E00\u6761\u8FC7\u7A0B\u75D5\u8FF9", en: "Post a process trail entry into the group" }
163598
+ };
163599
+ const t = titles[String(kind)] || { zh: "\u6267\u884C\u8FD9\u4E2A\u52A8\u4F5C", en: "Run this action" };
163600
+ return { titleZh: t.zh, titleEn: t.en, lines };
163601
+ }
163602
+
163603
+ // src/agents/goal-flywheel/types.ts
163604
+ var MUST_NOT_EXPOSE_FIELDS = [
163605
+ "lease",
163606
+ "reducer",
163607
+ "internal_status",
163608
+ "retry_counter",
163609
+ "worker_owner"
163610
+ ];
163611
+
163612
+ // src/agents/goal-flywheel/work-monitor.ts
163613
+ var NO_PROGRESS_FALLBACK_MS = 30 * 60 * 1e3;
163614
+ var DEFAULT_REPORT_GRACE_MS = 60 * 1e3;
163615
+ var DEFAULT_ESCALATION_MS = 10 * 60 * 1e3;
163616
+ var TERMINAL_GOAL_STATES = ["completed", "failed", "abandoned"];
163617
+ var TERMINAL_GOAL_STATUSES = [...TERMINAL_GOAL_STATES];
163618
+
163619
+ // src/agents/mobile-flywheel-view.ts
163620
+ function findInternalFieldLeaks(text) {
163621
+ const t = String(text ?? "");
163622
+ return MUST_NOT_EXPOSE_FIELDS.filter((f) => t.includes(f));
163623
+ }
163624
+
163625
+ // src/web/mobile-tasks.ts
163626
+ init_mobile_contacts();
163627
+ var K_TASKS_HINT = "bolloon.mobile.tasks.hint.v1";
163628
+ function defaultStorage2() {
163015
163629
  try {
163016
- pair = await cobj.subtle.generateKey({ name: "Ed25519" }, true, ["sign", "verify"]);
163017
- } catch (err2) {
163018
- throw new Error(`device_signing_unavailable: ${String(err2?.message || err2)}`);
163630
+ if (typeof localStorage !== "undefined") return localStorage;
163631
+ } catch {
163019
163632
  }
163020
- const publicKeyPem = pemFromSpki(await cobj.subtle.exportKey("spki", pair.publicKey));
163021
- const privateKeyJwk = await cobj.subtle.exportKey("jwk", pair.privateKey);
163022
- const deviceId = `dev-${(await sha256Hex(cobj, publicKeyPem)).slice(0, 12)}`;
163023
- const material = { deviceId, publicKeyPem, privateKeyJwk };
163024
- storage2.setItem(K_DEVICE, JSON.stringify(material));
163025
- return material;
163633
+ const mem2 = /* @__PURE__ */ new Map();
163634
+ return { getItem: (k) => mem2.has(k) ? mem2.get(k) : null, setItem: (k, v) => {
163635
+ mem2.set(k, v);
163636
+ }, removeItem: (k) => {
163637
+ mem2.delete(k);
163638
+ } };
163026
163639
  }
163027
- async function importPrivate(storage2, c) {
163028
- const k = await loadOrCreateDeviceKey(storage2, c);
163029
- return c.subtle.importKey("jwk", k.privateKeyJwk, { name: "Ed25519" }, false, ["sign"]);
163640
+ async function baseOf(deps) {
163641
+ if (deps.base && String(deps.base).trim()) return String(deps.base).replace(/\/+$/, "");
163642
+ try {
163643
+ if (typeof localStorage !== "undefined") {
163644
+ const v = localStorage.getItem("bolloon_desktop_base_url");
163645
+ if (v) return String(v).replace(/\/+$/, "");
163646
+ }
163647
+ } catch {
163648
+ }
163649
+ return "";
163030
163650
  }
163031
- async function signGrantOnDevice(grant, storage2, c) {
163032
- const cobj = cryptoOf(c);
163033
- const key = await importPrivate(storage2, cobj);
163034
- const payload = canonicalGrantPayload(grant);
163035
- const sig = await cobj.subtle.sign({ name: "Ed25519" }, key, bytesOf(payload));
163036
- const dev = await loadOrCreateDeviceKey(storage2, cobj);
163037
- return { deviceId: dev.deviceId, alg: "ed25519", payloadHash: await sha256Hex(cobj, payload), sig: b64(sig) };
163651
+ function fetchOf(deps) {
163652
+ return deps.fetchImpl || globalThis.fetch;
163038
163653
  }
163039
- async function signRevocationOnDevice(rev, storage2, c) {
163040
- const cobj = cryptoOf(c);
163041
- const key = await importPrivate(storage2, cobj);
163042
- const payload = canonicalRevocationPayload(rev);
163043
- const sig = await cobj.subtle.sign({ name: "Ed25519" }, key, bytesOf(payload));
163044
- const dev = await loadOrCreateDeviceKey(storage2, cobj);
163045
- return { deviceId: dev.deviceId, alg: "ed25519", payloadHash: await sha256Hex(cobj, payload), sig: b64(sig) };
163654
+ async function getJson(base6, path, f) {
163655
+ const res = await f(`${base6}${path}`);
163656
+ let json = null;
163657
+ try {
163658
+ json = await res.json();
163659
+ } catch {
163660
+ }
163661
+ return { status: res.status, json };
163046
163662
  }
163047
- async function post(base6, path, body, f) {
163048
- const res = await f(`${String(base6).replace(/\/+$/, "")}${path}`, {
163663
+ async function postJson(base6, path, body, f) {
163664
+ const res = await f(`${base6}${path}`, {
163049
163665
  method: "POST",
163050
163666
  headers: { "Content-Type": "application/json" },
163051
163667
  body: JSON.stringify(body ?? {})
@@ -163057,232 +163673,1809 @@ ${b}
163057
163673
  }
163058
163674
  return { status: res.status, json };
163059
163675
  }
163060
- async function get(base6, path, f) {
163061
- const res = await f(`${String(base6).replace(/\/+$/, "")}${path}`);
163062
- let json = null;
163676
+ var OFFLINE_NOTE = "\u5730\u5740\u5728 \u6211 \u2192 \u8BBE\u7F6E \u2192 \u7535\u8111\u7AEF\u5730\u5740 \u91CC\u586B; \u624B\u673A\u4E0D\u66FF\u684C\u9762\u8BB0\u8D26";
163677
+ function mapFail(r, what) {
163678
+ const code10 = r.json?.code ? String(r.json.code) : r.status ? `desktop_http_${r.status}` : "desktop_unreachable";
163679
+ return { ok: false, code: code10, error: `${what} \u5931\u8D25: ${r.json?.error || r.json?.message || code10}`, note: OFFLINE_NOTE };
163680
+ }
163681
+ function emptyTaskActionRequest(kind) {
163682
+ return {
163683
+ kind,
163684
+ groupRef: null,
163685
+ announcementId: null,
163686
+ capability: null,
163687
+ instruction: null,
163688
+ budgetHuman: null,
163689
+ currency: null,
163690
+ deadline: null,
163691
+ criteria: null,
163692
+ round: null,
163693
+ price: null,
163694
+ hash: null,
163695
+ bytes: null,
163696
+ checks: null,
163697
+ verdict: null,
163698
+ trailKind: null
163699
+ };
163700
+ }
163701
+ async function signTaskAction(req, deps = {}) {
163702
+ const storage2 = deps.storage || defaultStorage2();
163703
+ const c = deps.cryptoObj || globalThis.crypto;
163704
+ if (!ed25519Available(c)) {
163705
+ return { ok: false, code: "device_signing_unavailable", error: "\u672C\u673A WebView \u4E0D\u652F\u6301 Ed25519 \u8BBE\u5907\u7B7E\u540D \u2014\u2014 \u8BF7\u5728\u684C\u9762\u7AEF\u6267\u884C\u8FD9\u4E2A\u52A8\u4F5C (\u6216\u5347\u7EA7\u7CFB\u7EDF WebView)" };
163706
+ }
163707
+ if (!MOBILE_TASK_ACTION_KINDS.includes(String(req?.kind))) {
163708
+ return { ok: false, code: "unknown_kind", error: `\u672A\u77E5\u52A8\u4F5C: ${String(req?.kind || "")}` };
163709
+ }
163710
+ const text = taskActionContentText(req);
163711
+ if (req.kind === "trail_post" || req.kind === "announce_publish" || req.kind === "announce_to_group") {
163712
+ for (const fieldText of [req.round, req.price, req.criteria, req.checks, req.verdict, req.capability, req.hash]) {
163713
+ if (!fieldText) continue;
163714
+ const gate = requirePublicText(String(fieldText));
163715
+ if (!gate.ok) {
163716
+ return { ok: false, code: "privacy_denied", error: `\u672C\u5730\u9690\u79C1\u95F8\u62E6\u4E0B: ${gate.violations.map((v) => v.rule).join(", ")} (\u7FA4\u91CC/\u516C\u544A\u91CC\u4E0D\u653E\u6807\u8BC6\u7B26)`, violations: gate.violations };
163717
+ }
163718
+ }
163719
+ }
163720
+ const now = deps.now ? deps.now() : Date.now();
163721
+ const ttl = Math.min(Number(deps.ttlMs) || TASK_ACTION_DEFAULT_TTL_MS, TASK_ACTION_DEFAULT_TTL_MS);
163722
+ let contentDigest;
163063
163723
  try {
163064
- json = await res.json();
163724
+ contentDigest = await sha256HexOf(text, c);
163725
+ } catch (e) {
163726
+ return { ok: false, code: "device_signing_unavailable", error: String(e?.message || e) };
163727
+ }
163728
+ const action = {
163729
+ actionId: `act-${now.toString(36)}-${Math.random().toString(36).slice(2, 8)}`,
163730
+ kind: req.kind,
163731
+ deviceId: "",
163732
+ // 由 signPayloadOnDevice 填 (它知道设备 id)
163733
+ ownerDid: String(deps.ownerDid || "").trim(),
163734
+ targetRef: String(req.groupRef || req.announcementId || ""),
163735
+ contentDigest,
163736
+ createdAt: new Date(now).toISOString(),
163737
+ expiresAt: new Date(now + ttl).toISOString(),
163738
+ grantedBy: deps.grantedBy || "leo",
163739
+ via: "mobile"
163740
+ };
163741
+ let signature;
163742
+ try {
163743
+ const { loadOrCreateDeviceKey: loadOrCreateDeviceKey2, signPayloadWithMaterial: signPayloadWithMaterial2 } = await Promise.resolve().then(() => (init_mobile_contacts(), mobile_contacts_exports));
163744
+ const material = await loadOrCreateDeviceKey2(storage2, c);
163745
+ action.deviceId = material.deviceId;
163746
+ signature = await signPayloadWithMaterial2(canonicalTaskActionPayload(action), material, c);
163747
+ if (signature.deviceId !== action.deviceId) {
163748
+ return { ok: false, code: "device_signing_unavailable", error: "\u8BBE\u5907\u5BC6\u94A5\u4E0D\u4E00\u81F4 (\u7B7E\u540D\u4E0E\u8F7D\u8377\u8BBE\u5907\u5BF9\u4E0D\u4E0A) \u2192 \u4E0D\u53D1\u9001" };
163749
+ }
163750
+ const fresh = checkTaskActionFreshness(action, now);
163751
+ if (!fresh.ok) return { ok: false, code: fresh.reason, error: `\u52A8\u4F5C\u65F6\u6548\u4E0D\u5408\u6CD5: ${fresh.reason}` };
163752
+ } catch (e) {
163753
+ return { ok: false, code: "device_signing_unavailable", error: String(e?.message || e).slice(0, 160) };
163754
+ }
163755
+ return {
163756
+ ok: true,
163757
+ data: { signed: { action, signature }, contentText: text, confirm: describeTaskActionForConfirm(req.kind, req) }
163758
+ };
163759
+ }
163760
+ async function executeTaskAction(req, deps = {}) {
163761
+ const storage2 = deps.storage || defaultStorage2();
163762
+ const base6 = await baseOf(deps);
163763
+ if (!base6) return { ok: false, code: "desktop_offline", error: "\u8FD8\u6CA1\u8BBE\u7F6E\u7535\u8111\u7AEF\u5730\u5740: \u9AD8\u98CE\u9669\u52A8\u4F5C\u5FC5\u987B\u7531\u684C\u9762\u6267\u884C (\u624B\u673A\u7B7E\u4E86\u540D\u4E5F\u8981\u9001\u5230\u684C\u9762)", note: OFFLINE_NOTE };
163764
+ const signed = await signTaskAction(req, { ...deps, storage: storage2 });
163765
+ if (!signed.ok || !signed.data) return { ok: false, code: signed.code, error: signed.error, violations: signed.violations };
163766
+ const f = fetchOf(deps);
163767
+ let r;
163768
+ try {
163769
+ r = await postJson(base6, "/api/mobile/tasks/execute", { request: req, ...signed.data.signed }, f);
163770
+ } catch (e) {
163771
+ return { ok: false, code: "desktop_offline", error: `\u684C\u9762\u4E0D\u53EF\u8FBE: ${String(e?.message || e).slice(0, 120)}`, note: OFFLINE_NOTE };
163772
+ }
163773
+ if (r.status !== 200 || !r.json?.ok) return mapFail(r, "\u6267\u884C");
163774
+ return { ok: true, data: r.json, note: "\u684C\u9762\u5DF2\u9A8C\u7B7E\u5E76\u6267\u884C (\u540C\u4E00\u5957\u5B58\u50A8\u4E0E\u534F\u8BAE)" };
163775
+ }
163776
+ async function previewGroupMessage(req, deps = {}) {
163777
+ const base6 = await baseOf(deps);
163778
+ if (!base6) return { ok: false, code: "desktop_offline", error: "\u9884\u89C8\u9700\u8981\u684C\u9762\u5728\u7EBF (\u7FA4\u6D88\u606F\u7531\u684C\u9762\u7684\u6784\u9020\u5668\u751F\u6210)", note: OFFLINE_NOTE };
163779
+ const c = deps.cryptoObj || globalThis.crypto;
163780
+ let digestPreview = "";
163781
+ try {
163782
+ digestPreview = await sha256HexOf(taskActionContentText(req), c);
163065
163783
  } catch {
163784
+ digestPreview = "";
163785
+ }
163786
+ const f = fetchOf(deps);
163787
+ try {
163788
+ const r = await postJson(base6, "/api/mobile/tasks/preview", { request: req }, f);
163789
+ if (r.status !== 200 || !r.json?.ok) return mapFail(r, "\u9884\u89C8");
163790
+ return { ok: true, data: { text: String(r.json.text || ""), contentText: taskActionContentText(req), digestPreview } };
163791
+ } catch (e) {
163792
+ return { ok: false, code: "desktop_offline", error: `\u684C\u9762\u4E0D\u53EF\u8FBE: ${String(e?.message || e).slice(0, 120)}`, note: OFFLINE_NOTE };
163066
163793
  }
163067
- return { status: res.status, json };
163068
163794
  }
163069
- async function registerDeviceOnDesktop(base6, storage2, opts = {}) {
163070
- const f = opts.fetchImpl || fetch;
163071
- const dev = await loadOrCreateDeviceKey(storage2, opts.cryptoObj);
163072
- const r = await post(base6, "/api/contacts/devices", { deviceId: dev.deviceId, publicKeyPem: dev.publicKeyPem, label: opts.label || "\u624B\u673A" }, f);
163073
- if (r.status !== 200 || !r.json?.ok) return { ok: false, error: `\u684C\u9762\u672A\u63A5\u53D7\u8BBE\u5907\u767B\u8BB0: ${r.json?.error || r.status}`, data: dev };
163074
- return { ok: true, data: dev };
163795
+ async function listGroups(deps = {}) {
163796
+ const base6 = await baseOf(deps);
163797
+ if (!base6) return { ok: false, code: "desktop_offline", error: "\u770B\u7FA4\u5217\u8868\u9700\u8981\u7535\u8111\u7AEF\u5728\u7EBF (\u7FA4\u5B58\u5728\u4E8E\u684C\u9762\u8282\u70B9\u7684 OrbitDB)", note: OFFLINE_NOTE };
163798
+ try {
163799
+ const r = await getJson(base6, "/api/mobile/tasks/groups", fetchOf(deps));
163800
+ if (r.status !== 200 || !r.json?.ok) return mapFail(r, "\u8BFB\u7FA4\u5217\u8868");
163801
+ return { ok: true, data: buildGroupsView(r.json.groups || []) };
163802
+ } catch (e) {
163803
+ return { ok: false, code: "desktop_offline", error: `\u684C\u9762\u4E0D\u53EF\u8FBE: ${String(e?.message || e).slice(0, 120)}`, note: OFFLINE_NOTE };
163804
+ }
163075
163805
  }
163076
- function saveCapabilityCopy(storage2, items) {
163077
- const clean8 = (items || []).map((c) => ({
163078
- contactId: String(c.contactId || ""),
163079
- kind: c.kind,
163080
- displayValue: String(c.displayValue || ""),
163081
- verificationStatus: String(c.verificationStatus || ""),
163082
- capabilities: [...c.capabilities || []],
163083
- provider: c.provider ? String(c.provider) : void 0,
163084
- state: c.state ? String(c.state) : void 0
163085
- }));
163086
- storage2.setItem(K_CAPS, JSON.stringify(clean8));
163806
+ function joinGroup(link, deps = {}) {
163807
+ const req = { ...emptyTaskActionRequest("group_join"), groupRef: String(link || "").trim() };
163808
+ return executeTaskAction(req, deps);
163087
163809
  }
163088
- function loadCapabilityCopy(storage2) {
163810
+ function leaveGroup(groupRef, deps = {}) {
163811
+ const req = { ...emptyTaskActionRequest("group_leave"), groupRef: String(groupRef || "").trim() };
163812
+ return executeTaskAction(req, deps);
163813
+ }
163814
+ function createGroup(name10, deps = {}) {
163815
+ const req = { ...emptyTaskActionRequest("group_create"), groupRef: String(name10 || "").trim() };
163816
+ return executeTaskAction(req, deps);
163817
+ }
163818
+ async function loadBoard(deps = {}) {
163819
+ const base6 = await baseOf(deps);
163820
+ if (!base6) return { ok: false, code: "desktop_offline", error: "\u770B\u677F\u9700\u8981\u7535\u8111\u7AEF\u5728\u7EBF (\u516C\u544A\u677F\u5728\u684C\u9762\u8282\u70B9\u4E0A)", note: OFFLINE_NOTE };
163821
+ const now = deps.now ? deps.now() : Date.now();
163089
163822
  try {
163090
- const v = JSON.parse(storage2.getItem(K_CAPS) || "[]");
163091
- return Array.isArray(v) ? v : [];
163823
+ const r = await getJson(base6, "/api/mobile/tasks/board", fetchOf(deps));
163824
+ if (r.status !== 200 || !r.json?.ok) return mapFail(r, "\u8BFB\u770B\u677F");
163825
+ return { ok: true, data: buildBoardView(r.json, now) };
163826
+ } catch (e) {
163827
+ return { ok: false, code: "desktop_offline", error: `\u684C\u9762\u4E0D\u53EF\u8FBE: ${String(e?.message || e).slice(0, 120)}`, note: OFFLINE_NOTE };
163828
+ }
163829
+ }
163830
+ function publishAnnouncement(input, deps = {}) {
163831
+ const req = {
163832
+ ...emptyTaskActionRequest("announce_publish"),
163833
+ capability: String(input.capability || "").trim(),
163834
+ instruction: String(input.instruction || ""),
163835
+ budgetHuman: String(input.budgetHuman || "").trim(),
163836
+ currency: String(input.currency || "USDC").toUpperCase(),
163837
+ deadline: input.deadline ? String(input.deadline).trim() : null
163838
+ };
163839
+ return executeTaskAction(req, deps);
163840
+ }
163841
+ function announceIntoGroup(input, deps = {}) {
163842
+ const req = {
163843
+ ...emptyTaskActionRequest("announce_to_group"),
163844
+ groupRef: String(input.groupRef || "").trim(),
163845
+ announcementId: String(input.announcementId || "").trim(),
163846
+ round: input.round ? String(input.round) : null,
163847
+ criteria: input.criteria ? String(input.criteria) : null
163848
+ };
163849
+ return executeTaskAction(req, deps);
163850
+ }
163851
+ function postTrail(input, deps = {}) {
163852
+ const req = {
163853
+ ...emptyTaskActionRequest("trail_post"),
163854
+ groupRef: String(input.groupRef || "").trim(),
163855
+ announcementId: String(input.announcementId || "").trim(),
163856
+ round: input.round ? String(input.round) : null,
163857
+ price: input.price ? String(input.price) : null,
163858
+ hash: input.hash ? String(input.hash) : null,
163859
+ bytes: typeof input.bytes === "number" ? input.bytes : null,
163860
+ checks: input.checks ? String(input.checks) : null,
163861
+ verdict: input.verdict ? String(input.verdict) : null,
163862
+ trailKind: input.kind
163863
+ };
163864
+ return executeTaskAction(req, deps);
163865
+ }
163866
+ async function loadTrail(input, deps = {}) {
163867
+ const base6 = await baseOf(deps);
163868
+ if (!base6) return { ok: false, code: "desktop_offline", error: "\u8BFB\u7FA4\u75D5\u8FF9\u9700\u8981\u7535\u8111\u7AEF\u5728\u7EBF", note: OFFLINE_NOTE };
163869
+ const q = new URLSearchParams();
163870
+ q.set("group", String(input.groupRef || "").trim());
163871
+ if (input.announcementId) q.set("announcementId", String(input.announcementId));
163872
+ if (input.limit) q.set("limit", String(input.limit));
163873
+ try {
163874
+ const r = await getJson(base6, `/api/mobile/tasks/trail?${q.toString()}`, fetchOf(deps));
163875
+ if (r.status !== 200 || !r.json?.ok) return mapFail(r, "\u8BFB\u7FA4\u75D5\u8FF9");
163876
+ return { ok: true, data: buildTrailView(r.json.summary || r.json) };
163877
+ } catch (e) {
163878
+ return { ok: false, code: "desktop_offline", error: `\u684C\u9762\u4E0D\u53EF\u8FBE: ${String(e?.message || e).slice(0, 120)}`, note: OFFLINE_NOTE };
163879
+ }
163880
+ }
163881
+ async function loadFlywheel(deps = {}) {
163882
+ const base6 = await baseOf(deps);
163883
+ if (!base6) return { ok: false, code: "desktop_offline", error: "\u770B\u98DE\u8F6E\u8FDB\u5EA6\u9700\u8981\u7535\u8111\u7AEF\u5728\u7EBF (Goal \u8BB0\u5F55\u5728\u684C\u9762\u8282\u70B9\u4E0A)", note: OFFLINE_NOTE };
163884
+ try {
163885
+ const r = await getJson(base6, "/api/mobile/tasks/flywheel", fetchOf(deps));
163886
+ if (r.status !== 200 || !r.json?.ok) return mapFail(r, "\u8BFB\u98DE\u8F6E\u8FDB\u5EA6");
163887
+ const goals = Array.isArray(r.json.goals) ? r.json.goals : [];
163888
+ const leaks = findInternalFieldLeaks(JSON.stringify(goals));
163889
+ if (leaks.length) return { ok: false, code: "view_leak", error: `\u89C6\u56FE\u91CC\u542B\u5185\u90E8\u5B57\u6BB5 (${leaks.join(", ")}) \u2192 \u62D2\u7EDD\u6E32\u67D3` };
163890
+ return { ok: true, data: { goals, hint: r.json.hint ? String(r.json.hint) : null } };
163891
+ } catch (e) {
163892
+ return { ok: false, code: "desktop_offline", error: `\u684C\u9762\u4E0D\u53EF\u8FBE: ${String(e?.message || e).slice(0, 120)}`, note: OFFLINE_NOTE };
163893
+ }
163894
+ }
163895
+ function setTaskHint(text, storage2 = defaultStorage2()) {
163896
+ try {
163897
+ storage2.setItem(K_TASKS_HINT, String(text || "").slice(0, 160));
163092
163898
  } catch {
163093
- return [];
163094
163899
  }
163095
163900
  }
163096
- function pendingQueue(storage2) {
163901
+ function getTaskHint(storage2 = defaultStorage2()) {
163097
163902
  try {
163098
- const v = JSON.parse(storage2.getItem(K_QUEUE) || "[]");
163099
- return Array.isArray(v) ? v : [];
163903
+ return String(storage2.getItem(K_TASKS_HINT) || "");
163100
163904
  } catch {
163101
- return [];
163905
+ return "";
163102
163906
  }
163103
163907
  }
163104
- function setQueue(storage2, q) {
163105
- storage2.setItem(K_QUEUE, JSON.stringify(q.slice(-20)));
163908
+
163909
+ // src/utils/version-identity.ts
163910
+ var PKG_NAME = "@bolloon/bolloon-agent";
163911
+ var GITHUB_UPSTREAM_SLUG = "logos-42/bolloon";
163912
+ function envValue(key) {
163913
+ try {
163914
+ const cfg = globalThis?.__bolloonUpdateConfig;
163915
+ if (cfg && cfg[key] !== void 0 && cfg[key] !== null) return String(cfg[key]);
163916
+ const env = typeof process !== "undefined" ? process?.env : null;
163917
+ const v = env ? env[key] : void 0;
163918
+ return v === void 0 || v === null ? void 0 : String(v);
163919
+ } catch {
163920
+ return void 0;
163921
+ }
163106
163922
  }
163107
- async function flushQueuedGrants(base6, storage2, opts = {}) {
163108
- const f = opts.fetchImpl || fetch;
163109
- const q = pendingQueue(storage2);
163110
- const keep = [];
163111
- const results = [];
163112
- let sent = 0, failed = 0;
163113
- for (const item of q) {
163114
- try {
163115
- const r = await post(base6, "/api/contacts/grants/sync", { grant: { ...item.grant, signature: item.signature } }, f);
163116
- if (r.status === 200 && r.json?.ok) {
163117
- sent++;
163118
- results.push({ grantId: item.grant.grantId, ok: true });
163119
- continue;
163120
- }
163121
- failed++;
163122
- keep.push(item);
163123
- results.push({ grantId: item.grant.grantId, ok: false, error: r.json?.code || r.status });
163124
- } catch {
163125
- failed++;
163126
- keep.push(item);
163127
- results.push({ grantId: item.grant.grantId, ok: false, error: "network" });
163128
- }
163923
+ var NPM_REGISTRY_BASE = (envValue("BOLLOON_NPM_REGISTRY") || "https://registry.npmjs.org").replace(/\/+$/, "");
163924
+ var GITHUB_API_BASE = (envValue("BOLLOON_GITHUB_API") || "https://api.github.com").replace(/\/+$/, "");
163925
+ var DEV_BRANCH = "master";
163926
+ var DEV_REF = `refs/heads/${DEV_BRANCH}`;
163927
+ function resolveUpdateChannel(explicit, env) {
163928
+ const raw = (explicit || (env ? env.BOLLOON_UPDATE_CHANNEL : envValue("BOLLOON_UPDATE_CHANNEL")) || "").trim().toLowerCase();
163929
+ if (raw === "stable" || raw === "beta" || raw === "dev") return raw;
163930
+ return "stable";
163931
+ }
163932
+ function distTagForChannel(channel) {
163933
+ return channel === "beta" ? "beta" : "latest";
163934
+ }
163935
+ function channelKindOf(channel) {
163936
+ return channel === "dev" ? "git-ref" : "semver";
163937
+ }
163938
+ var DEV_IDENTITY_SEP = "+dev.";
163939
+ function devIdentity(baseVersion, sha4) {
163940
+ return `${baseVersionOf(baseVersion)}${DEV_IDENTITY_SEP}${String(sha4).slice(0, 7)}`;
163941
+ }
163942
+ function baseVersionOf(v) {
163943
+ return String(v || "").split(DEV_IDENTITY_SEP)[0].trim();
163944
+ }
163945
+ function isDevIdentity(v) {
163946
+ return String(v || "").includes(DEV_IDENTITY_SEP);
163947
+ }
163948
+ function devShaFromIdentity(v) {
163949
+ if (!isDevIdentity(v)) return null;
163950
+ const sha4 = String(v).split(DEV_IDENTITY_SEP)[1]?.trim();
163951
+ return sha4 ? sha4 : null;
163952
+ }
163953
+ var DEV_CHANNEL_WARNING = "\u26A0\uFE0F dev \u901A\u9053 = GitHub master HEAD \u7684\u5373\u65F6\u5FEB\u7167 (\u672A\u8D70\u53D1\u5E03\u95E8): \u53EF\u80FD\u4E2D\u65AD\u6B63\u5728\u8DD1\u7684 Goal/Run, \u4E14\u4E0D\u4FDD\u8BC1\u53EF\u56DE\u6EDA\u5230\u4E0A\u4E00\u4E2A dev \u7248\u3002";
163954
+ var DEV_BACK_TO_STABLE_HINT_MOBILE = "\u4E00\u952E\u56DE\u7A33\u5B9A\u7248: \u624B\u673A\u7AEF\u300C\u66F4\u65B0\u300D\u9875 \u2192 \u5207\u56DE stable";
163955
+ function parseVersion(v) {
163956
+ const clean8 = String(v || "").trim().replace(/^v/, "").split("-")[0];
163957
+ const parts = clean8.split(".").map((p) => parseInt(p.replace(/\D.*$/, ""), 10));
163958
+ return parts.map((n) => Number.isFinite(n) ? n : 0);
163959
+ }
163960
+ function compareVersions(a, b) {
163961
+ const pa = parseVersion(a);
163962
+ const pb = parseVersion(b);
163963
+ for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
163964
+ const x = pa[i] ?? 0;
163965
+ const y2 = pb[i] ?? 0;
163966
+ if (x < y2) return -1;
163967
+ if (x > y2) return 1;
163129
163968
  }
163130
- setQueue(storage2, keep);
163131
- return { sent, failed, results };
163969
+ return 0;
163132
163970
  }
163133
- async function authorizeFromPhone(input) {
163134
- const c = cryptoOf(input.cryptoObj);
163135
- if (!ed25519Available(c || void 0)) {
163136
- return { ok: false, error: "device_signing_unavailable: \u672C\u673A WebView \u4E0D\u652F\u6301 Ed25519 \u8BBE\u5907\u7B7E\u540D \u2014\u2014 \u8BF7\u5728\u684C\u9762\u7AEF\u6388\u6743 (\u6216\u5347\u7EA7\u7CFB\u7EDF WebView)" };
163971
+ function isKnownVersion(v) {
163972
+ return !!v && v !== "unknown" && /^\d/.test(String(v).trim().replace(/^v/, ""));
163973
+ }
163974
+ var CHECK_STATUSES = [
163975
+ "up_to_date",
163976
+ // 确认最新
163977
+ "update_available",
163978
+ // 有新版本
163979
+ "check_skipped",
163980
+ // 节流/显式跳过, 结论来自缓存
163981
+ "offline",
163982
+ // 网络不可达 (≠ 最新)
163983
+ "registry_unavailable",
163984
+ // 可达但拿不到有效数据 (5xx / 包不存在 / 无 dist-tags)
163985
+ "local_version_unknown",
163986
+ // 读不到本地版本 (不猜 0.0.0)
163987
+ "unsupported_installation",
163988
+ // 认得出本地版本, 但这种安装方式不支持自动更新
163989
+ "github_unavailable",
163990
+ // GitHub 源不可用 (offline / rate_limited / not_found / http_error)
163991
+ "cross_check_mismatch"
163992
+ // npm 与 GitHub 两个源不一致 → 拒绝更新
163993
+ ];
163994
+ var REFUSED_STATUSES = ["offline", "registry_unavailable", "local_version_unknown", "github_unavailable", "cross_check_mismatch"];
163995
+ function describeSourceIdentity(info) {
163996
+ const src4 = info.installedDevSha ? "dev (GitHub master \u5FEB\u7167)" : "stable (npm registry)";
163997
+ const ident = info.installedDevSha ? `commit ${info.installedDevSha}` : baseVersionOf(info.packageVersion);
163998
+ const back = info.switchableTo ? ` \xB7 \u53EF\u5207\u56DE ${info.switchableTo.channel} (${info.switchableTo.source})` : "";
163999
+ return `\u5F53\u524D\u5B89\u88C5\u6E90: ${src4} \xB7 \u7248\u672C/commit: ${ident} \xB7 \u6BD4\u8F83\u8BED\u4E49: ${info.channelKind}${back}`;
164000
+ }
164001
+
164002
+ // src/utils/dual-source-facts.ts
164003
+ function toGithubReport(r) {
164004
+ if (!r.ok) {
164005
+ return {
164006
+ reachable: false,
164007
+ reason: r.reason,
164008
+ detail: r.detail,
164009
+ retryAt: r.retryAt,
164010
+ headSha: r.facts?.headSha ?? null,
164011
+ ref: r.facts?.ref ?? `refs/heads/${DEV_BRANCH}`,
164012
+ releases: r.facts?.releases.length,
164013
+ tags: r.facts?.tags.length,
164014
+ newestVersion: r.facts ? newestGithubVersion(r.facts) : null,
164015
+ partial: r.facts?.partial,
164016
+ fetchedAt: r.facts?.fetchedAt
164017
+ };
163137
164018
  }
163138
- const dev = await loadOrCreateDeviceKey(input.storage, c);
163139
- const preset = presetForChoice(input.choice);
163140
- const grant = {
163141
- grantId: `gr-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`,
163142
- identityId: input.identityId || `sid-phone-${dev.deviceId.slice(-6)}`,
163143
- ownerDid: input.ownerDid,
163144
- ...preset,
163145
- grantedAt: (/* @__PURE__ */ new Date()).toISOString(),
163146
- grantedBy: input.grantedBy || "leo",
163147
- grantedVia: "mobile",
163148
- deviceIds: [dev.deviceId],
163149
- status: "active",
163150
- grantVersion: 1
164019
+ return {
164020
+ reachable: true,
164021
+ headSha: r.facts.headSha,
164022
+ ref: r.facts.ref,
164023
+ releases: r.facts.releases.length,
164024
+ tags: r.facts.tags.length,
164025
+ newestVersion: newestGithubVersion(r.facts),
164026
+ partial: r.facts.partial,
164027
+ fetchedAt: r.facts.fetchedAt
163151
164028
  };
163152
- const signature = await signGrantOnDevice(grant, input.storage, c);
163153
- const f = input.fetchImpl || fetch;
163154
- const reg = await registerDeviceOnDesktop(input.base, input.storage, { fetchImpl: f, cryptoObj: c }).catch(() => ({ ok: false }));
163155
- if (!reg.ok) {
163156
- const q2 = pendingQueue(input.storage);
163157
- q2.push({ grant, signature, queuedAt: (/* @__PURE__ */ new Date()).toISOString() });
163158
- setQueue(input.storage, q2);
164029
+ }
164030
+ function renderGithubReportLine(g) {
164031
+ if (!g) return "GitHub \u6E90: \u672A\u67E5\u8BE2";
164032
+ if (!g.reachable) {
164033
+ const retry = g.retryAt ? `, \u9650\u6D41\u91CD\u8BD5\u65F6\u95F4 ${g.retryAt}` : "";
164034
+ return `GitHub \u6E90: \u4E0D\u53EF\u8FBE \u2014 github_unavailable(${g.reason}) ${g.detail || ""}${retry}`;
164035
+ }
164036
+ return `GitHub \u6E90: \u53EF\u8FBE \xB7 master HEAD ${g.headSha ? g.headSha.slice(0, 7) : "\u672A\u77E5"} \xB7 Release ${g.releases ?? 0} \u4E2A \xB7 Tag ${g.tags ?? 0} \u4E2A \xB7 \u6700\u65B0\u7248\u672C\u6807\u7B7E ${g.newestVersion || "\u65E0"}${g.partial ? " (\u90E8\u5206\u8BF7\u6C42\u5931\u8D25)" : ""}`;
164037
+ }
164038
+ var OFFLINE_CODES = /* @__PURE__ */ new Set(["ENOTFOUND", "EAI_AGAIN", "ECONNREFUSED", "ECONNRESET", "ETIMEDOUT", "EHOSTUNREACH", "ENETUNREACH", "ENETDOWN", "EAI_FAIL", "UND_ERR_CONNECT_TIMEOUT", "CERT_HAS_EXPIRED"]);
164039
+ function collectNetEvidence(err2) {
164040
+ const codes = [];
164041
+ const msgs = [];
164042
+ let cur = err2;
164043
+ for (let depth = 0; cur && depth < 6; depth++) {
164044
+ const inner = Array.isArray(cur.errors) ? cur.errors : [];
164045
+ for (const e of inner) {
164046
+ if (e?.code) codes.push(String(e.code));
164047
+ if (e?.message) msgs.push(String(e.message));
164048
+ }
164049
+ if (cur.code) codes.push(String(cur.code));
164050
+ if (cur.errno) codes.push(String(cur.errno));
164051
+ if (cur.message) msgs.push(String(cur.message));
164052
+ cur = cur.cause;
164053
+ }
164054
+ const code10 = codes.find((c) => OFFLINE_CODES.has(c)) || codes[0] || "";
164055
+ const message16 = msgs.join(" \xB7 ");
164056
+ const offline = codes.some((c) => OFFLINE_CODES.has(c)) || /timeout|timed out|ENOTFOUND|getaddrinfo|network|socket hang up|超时|Failed to fetch|fetch failed|NetworkError|Load failed|ECONNREFUSED|ECONNRESET/i.test(message16);
164057
+ return { code: code10, message: message16, offline };
164058
+ }
164059
+ function classifyGithubError(err2, httpStatus, headers = {}) {
164060
+ const retryAt = headers["x-ratelimit-reset"] ? new Date(Number(headers["x-ratelimit-reset"]) * 1e3).toISOString() : null;
164061
+ const net = collectNetEvidence(err2);
164062
+ if (net.offline) {
164063
+ return { kind: "github_unavailable", reason: "offline", detail: `${net.code || net.message || "\u7F51\u7EDC\u4E0D\u53EF\u8FBE"}`, retryAt: null };
164064
+ }
164065
+ if (httpStatus === 403 || httpStatus === 429) {
164066
+ return { kind: "github_unavailable", reason: "rate_limited", detail: `GitHub \u8FD4\u56DE HTTP ${httpStatus} (\u9650\u6D41/\u6743\u9650)`, retryAt };
164067
+ }
164068
+ if (httpStatus === 404) {
164069
+ return { kind: "github_unavailable", reason: "not_found", detail: `GitHub \u4E0A\u6CA1\u6709 ${GITHUB_UPSTREAM_SLUG} \u7684\u8BE5\u9879\u8BB0\u5F55 (HTTP 404)`, retryAt: null };
164070
+ }
164071
+ if (httpStatus && httpStatus >= 400) {
164072
+ return { kind: "github_unavailable", reason: "http_error", detail: `GitHub \u8FD4\u56DE HTTP ${httpStatus}`, retryAt };
164073
+ }
164074
+ return { kind: "github_unavailable", reason: "http_error", detail: net.code || net.message || "\u672A\u77E5\u9519\u8BEF", retryAt: null };
164075
+ }
164076
+ function classifyRegistryError(err2, httpStatus) {
164077
+ const net = collectNetEvidence(err2);
164078
+ if (net.offline) {
164079
+ return { kind: "offline", detail: `${net.code || net.message || "\u7F51\u7EDC\u4E0D\u53EF\u8FBE"}` };
164080
+ }
164081
+ if (httpStatus && httpStatus >= 500) return { kind: "registry_unavailable", detail: `registry \u8FD4\u56DE HTTP ${httpStatus}` };
164082
+ if (httpStatus === 404) return { kind: "registry_unavailable", detail: `registry \u4E0A\u6CA1\u6709\u8BE5\u5305 (HTTP 404)` };
164083
+ if (httpStatus && httpStatus >= 400) return { kind: "registry_unavailable", detail: `registry \u8FD4\u56DE HTTP ${httpStatus}` };
164084
+ return { kind: "registry_unavailable", detail: net.code || net.message || "\u672A\u77E5\u9519\u8BEF" };
164085
+ }
164086
+ function newestGithubVersion(facts) {
164087
+ const all2 = [...facts.releases, ...facts.tags.map((t) => t.name)].map((n) => String(n).trim().replace(/^v/, "")).filter((n) => /^\d+\.\d+\.\d+/.test(n));
164088
+ if (all2.length === 0) return null;
164089
+ return all2.sort((a, b) => numericCompare(a, b) < 0 ? 1 : -1)[0];
164090
+ }
164091
+ function numericCompare(a, b) {
164092
+ const p = (v) => v.split("-")[0].split(".").map((x) => parseInt(x.replace(/\D.*$/, ""), 10) || 0);
164093
+ const pa = p(a);
164094
+ const pb = p(b);
164095
+ for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
164096
+ const x = pa[i] ?? 0;
164097
+ const y2 = pb[i] ?? 0;
164098
+ if (x < y2) return -1;
164099
+ if (x > y2) return 1;
164100
+ }
164101
+ return 0;
164102
+ }
164103
+ function hasName(names, v) {
164104
+ const set = new Set(names.map((n) => String(n).trim().replace(/^v/, "")));
164105
+ return set.has(v.replace(/^v/, ""));
164106
+ }
164107
+ function crossCheckStable(registryLatest, facts) {
164108
+ const common = {
164109
+ registryLatest,
164110
+ githubVersion: newestGithubVersion(facts),
164111
+ releases: facts.releases.length,
164112
+ tags: facts.tags.length
164113
+ };
164114
+ if (!registryLatest) {
164115
+ return { ...common, kind: "no_record_at_all", hasTag: false, hasRelease: false, blocking: false, detail: "npm \u6CA1\u6709\u7ED9\u51FA latest, \u65E0\u4ECE\u4EA4\u53C9\u6821\u9A8C" };
164116
+ }
164117
+ const allNames = [...facts.releases, ...facts.tags.map((t) => t.name)];
164118
+ const hasTag = hasName(facts.tags.map((t) => t.name), registryLatest);
164119
+ const hasRelease = hasName(facts.releases, registryLatest);
164120
+ const ghNewest = common.githubVersion;
164121
+ if (ghNewest && numericCompare(ghNewest, registryLatest) > 0) {
163159
164122
  return {
163160
- ok: false,
163161
- queued: true,
163162
- error: `\u684C\u9762\u6682\u4E0D\u53EF\u8FBE \u2192 \u6388\u6743\u5DF2\u5B58\u5728\u624B\u673A, \u5F85\u684C\u9762\u5728\u7EBF\u65F6\u81EA\u52A8\u540C\u6B65 (${reg.error || "offline"})`,
163163
- data: { grantId: grant.grantId, level: grant.level, signature },
163164
- note: "\u684C\u9762\u4E0D\u5728\u7EBF\u65F6\u4E0D\u5047\u88C5\u5DF2\u751F\u6548: \u6388\u6743\u5728\u672C\u5730\u961F\u5217\u91CC, \u684C\u9762\u4E00\u4E0A\u7EBF\u5C31\u8865\u540C\u6B65"
164123
+ ...common,
164124
+ kind: "mismatch",
164125
+ hasTag,
164126
+ hasRelease,
164127
+ blocking: true,
164128
+ detail: `GitHub \u4E0A\u6709 ${ghNewest} \u7684\u53D1\u5E03\u8BB0\u5F55, \u4F46 npm registry \u7684 latest \u8FD8\u662F ${registryLatest} \u2014 \u62D2\u7EDD\u6309 GitHub \u7684\u8BB0\u5F55\u5B89\u88C5`
163165
164129
  };
163166
164130
  }
163167
- const r = await post(input.base, "/api/contacts/grants/sync", { grant: { ...grant, signature } }, f);
163168
- if (r.status === 200 && r.json?.ok) {
163169
- return { ok: true, data: { grantId: grant.grantId, level: grant.level, signature }, note: "\u684C\u9762\u5DF2\u9A8C\u7B7E\u63A5\u53D7" };
164131
+ if (hasTag || hasRelease) {
164132
+ return {
164133
+ ...common,
164134
+ kind: "agree",
164135
+ hasTag,
164136
+ hasRelease,
164137
+ blocking: false,
164138
+ detail: `npm latest=${registryLatest} \u5728 GitHub \u4E0A\u6709\u540C\u540D\u8BB0\u5F55 (${hasRelease ? `Release v${registryLatest}` : `Tag v${registryLatest}`}) \u2014 \u4E24\u4E2A\u6E90\u6307\u5411\u540C\u4E00\u7248`
164139
+ };
164140
+ }
164141
+ if (allNames.length === 0) {
164142
+ return {
164143
+ ...common,
164144
+ kind: "no_record_at_all",
164145
+ hasTag: false,
164146
+ hasRelease: false,
164147
+ blocking: false,
164148
+ detail: `GitHub \u4E0A\u6682\u65E0\u4EFB\u4F55 tag / Release (0 \u4E2A) \u2014 \u6682\u65E0\u53EF\u4EA4\u53C9\u6821\u9A8C\u7684\u8BB0\u5F55, npm \u4ECD\u662F\u6743\u5A01 (latest=${registryLatest})`
164149
+ };
163170
164150
  }
163171
- const q = pendingQueue(input.storage);
163172
- q.push({ grant, signature, queuedAt: (/* @__PURE__ */ new Date()).toISOString() });
163173
- setQueue(input.storage, q);
163174
164151
  return {
163175
- ok: false,
163176
- queued: true,
163177
- error: `\u684C\u9762\u62D2\u7EDD/\u672A\u63A5\u53D7: ${r.json?.code || r.json?.reason || r.status}`,
163178
- data: { grantId: grant.grantId, level: grant.level, signature },
163179
- note: "\u5DF2\u4FDD\u5B58\u5728\u624B\u673A\u672C\u5730\u961F\u5217 (\u4E0D\u4F1A\u56E0\u4E3A\u5931\u8D25\u5C31\u5F53\u6210\u5DF2\u6388\u6743)"
164152
+ ...common,
164153
+ kind: "missing_record",
164154
+ hasTag,
164155
+ hasRelease,
164156
+ blocking: false,
164157
+ detail: `npm latest=${registryLatest} \u5728 GitHub \u4E0A\u6CA1\u6709\u5BF9\u5E94\u8BB0\u5F55 (GitHub \u6700\u65B0\u7248\u672C\u6807\u7B7E ${ghNewest || "\u65E0"}; Release ${facts.releases.length} \u4E2A / Tag ${facts.tags.length} \u4E2A) \u2014 \u53D1\u5E03\u8BB0\u5F55\u7F3A Tag, \u4E0D\u963B\u585E stable \u66F4\u65B0`
163180
164158
  };
163181
164159
  }
163182
- async function revokeFromPhone(opts) {
163183
- const c = cryptoOf(opts.cryptoObj);
163184
- if (!ed25519Available(c || void 0)) return { ok: false, error: "device_signing_unavailable: \u672C\u673A\u4E0D\u652F\u6301\u8BBE\u5907\u7B7E\u540D, \u64A4\u9500\u9700\u5728\u684C\u9762\u7AEF\u8FDB\u884C" };
163185
- const revokedAt = (/* @__PURE__ */ new Date()).toISOString();
163186
- const payload = { grantId: opts.grantId, grantVersion: opts.version || 1, revokedAt, by: "mobile" };
163187
- const signature = await signRevocationOnDevice(payload, opts.storage, c);
163188
- const f = opts.fetchImpl || fetch;
163189
- const r = await post(opts.base, "/api/contacts/grants/revoke-sync", { grantId: opts.grantId, by: "mobile", reason: opts.reason, version: payload.grantVersion, revokedAt, signature }, f);
163190
- if (r.status === 200 && r.json?.ok) return { ok: true, data: { grantId: opts.grantId }, note: "\u684C\u9762\u5DF2\u9A8C\u7B7E\u5E76\u7ACB\u5373\u5931\u6548" };
163191
- return { ok: false, error: `\u64A4\u9500\u672A\u9001\u8FBE: ${r.json?.code || r.json?.error || r.status}`, note: "\u684C\u9762\u4E0D\u5728\u7EBF\u65F6\u64A4\u9500**\u4E0D\u4F1A**\u88AB\u5F53\u4F5C\u5DF2\u5B8C\u6210 \u2014\u2014 \u8BF7\u5728\u684C\u9762\u5728\u7EBF\u540E\u91CD\u8BD5 (\u6216\u684C\u9762\u7AEF\u64A4\u9500)" };
164160
+ function compareDevSnapshots(installedSha, headSha, ref = `refs/heads/${DEV_BRANCH}`) {
164161
+ const short = (s2) => s2 ? s2.slice(0, 7) : "\u672A\u77E5";
164162
+ if (!headSha) {
164163
+ return { ref, headSha, installedSha, same: false, behind: false, detail: `${ref} \u7684 HEAD \u8BFB\u4E0D\u5230 \u2014 \u65E0\u6CD5\u5224\u5B9A dev \u5FEB\u7167\u65B0\u65E7` };
164164
+ }
164165
+ if (!installedSha) {
164166
+ return { ref, headSha, installedSha, same: false, behind: false, detail: `\u5F53\u524D\u4E0D\u662F dev \u5FEB\u7167 (stable \u5B89\u88C5) \u2014 \u5207\u5230 dev \u5C06\u5F97\u5230 ${ref} @ ${short(headSha)}` };
164167
+ }
164168
+ const same = installedSha.slice(0, 7) === headSha.slice(0, 7);
164169
+ return {
164170
+ ref,
164171
+ headSha,
164172
+ installedSha,
164173
+ same,
164174
+ behind: !same,
164175
+ detail: same ? `\u5DF2\u662F\u6700\u65B0\u7684 dev \u5FEB\u7167 (${ref} @ ${short(headSha)}) \u2014 \u540C\u4E00\u4E2A commit` : `dev \u5FEB\u7167\u843D\u540E: \u5DF2\u88C5 ${short(installedSha)} \u2192 master HEAD ${short(headSha)} (\u6309 commit sha \u5224\u5B9A, \u4E0D\u770B\u7248\u672C\u53F7)`
164176
+ };
163192
164177
  }
163193
- async function loadFromDesktop(base6, storage2, opts = {}) {
163194
- const f = opts.fetchImpl || fetch;
163195
- if (!base6) {
163196
- const caps = loadCapabilityCopy(storage2);
164178
+
164179
+ // src/web/mobile-update.ts
164180
+ var MOBILE_WEB_STATUSES = [
164181
+ ...CHECK_STATUSES,
164182
+ "dev_bundle_unavailable",
164183
+ "dev_sha_mismatch",
164184
+ "target_unpublished",
164185
+ "digest_mismatch",
164186
+ "web_bundle_malformed",
164187
+ "decompress_unavailable",
164188
+ "payload_too_large",
164189
+ "native_shell_not_wired"
164190
+ ];
164191
+ var MOBILE_REFUSED_STATUSES = [
164192
+ ...REFUSED_STATUSES,
164193
+ "target_unpublished",
164194
+ "dev_bundle_unavailable",
164195
+ "dev_sha_mismatch",
164196
+ "digest_mismatch",
164197
+ "web_bundle_malformed",
164198
+ "decompress_unavailable",
164199
+ "payload_too_large",
164200
+ "native_shell_not_wired"
164201
+ ];
164202
+ var MOBILE_WEB_REQUIRED_FILES = ["mobile.html", "mobile.js", "mobile-core.js", "mobile.css"];
164203
+ var MOBILE_WEB_JS_FILES = ["mobile.js", "mobile-core.js", "a2ui-client.js"];
164204
+ var MOBILE_MAX_PAYLOAD_BYTES = 80 * 1024 * 1024;
164205
+ var WEB_LAYOUT = {
164206
+ current: "current",
164207
+ staging: "staging",
164208
+ previous: "previous",
164209
+ state: "state.json"
164210
+ };
164211
+ function joinRel(...parts) {
164212
+ return parts.map((p) => String(p).replace(/^\/+|\/+$/g, "")).filter(Boolean).join("/");
164213
+ }
164214
+ function createMemoryWebStore(opts = {}) {
164215
+ const files = /* @__PURE__ */ new Map();
164216
+ const dirs = /* @__PURE__ */ new Set([""]);
164217
+ const norm = (rel) => joinRel(rel);
164218
+ const isDir = (rel) => dirs.has(norm(rel));
164219
+ return {
164220
+ kind: opts.kind || "memory",
164221
+ rootLabel: opts.rootLabel || "memory://bolloon-web",
164222
+ async list(rel) {
164223
+ const d2 = norm(rel);
164224
+ const out = [];
164225
+ for (const k of files.keys()) {
164226
+ if (d2 === "") {
164227
+ if (!k.includes("/")) out.push(k);
164228
+ continue;
164229
+ }
164230
+ if (k.startsWith(d2 + "/")) {
164231
+ const rest = k.slice(d2.length + 1);
164232
+ if (!rest.includes("/")) out.push(rest);
164233
+ }
164234
+ }
164235
+ return out.sort();
164236
+ },
164237
+ async exists(rel) {
164238
+ const k = norm(rel);
164239
+ return files.has(k) || isDir(k);
164240
+ },
164241
+ async readBytes(rel) {
164242
+ const v = files.get(norm(rel));
164243
+ return v ? v.slice() : null;
164244
+ },
164245
+ async readText(rel) {
164246
+ const v = files.get(norm(rel));
164247
+ return v ? new TextDecoder().decode(v) : null;
164248
+ },
164249
+ async writeBytes(rel, data) {
164250
+ const k = norm(rel);
164251
+ const parts = k.split("/");
164252
+ for (let i = 1; i < parts.length; i++) dirs.add(parts.slice(0, i).join("/"));
164253
+ files.set(k, data.slice ? data.slice() : data);
164254
+ },
164255
+ async remove(rel) {
164256
+ const k = norm(rel);
164257
+ for (const key of Array.from(files.keys())) if (key === k || key.startsWith(k + "/")) files.delete(key);
164258
+ for (const d2 of Array.from(dirs)) if (d2 === k || d2.startsWith(k + "/")) dirs.delete(d2);
164259
+ },
164260
+ async rename(from6, to) {
164261
+ const f = norm(from6);
164262
+ const t = norm(to);
164263
+ const moves = [];
164264
+ for (const key of Array.from(files.keys())) {
164265
+ if (key === f) moves.push({ from: key, to: t });
164266
+ else if (key.startsWith(f + "/")) moves.push({ from: key, to: t + key.slice(f.length) });
164267
+ }
164268
+ await this.remove(t);
164269
+ for (const m2 of moves) {
164270
+ const v = files.get(m2.from);
164271
+ files.delete(m2.from);
164272
+ const parts = m2.to.split("/");
164273
+ for (let i = 1; i < parts.length; i++) dirs.add(parts.slice(0, i).join("/"));
164274
+ files.set(m2.to, v);
164275
+ }
164276
+ dirs.delete(f);
164277
+ dirs.add(t);
164278
+ },
164279
+ baseUrl() {
164280
+ return opts.baseUrl ?? null;
164281
+ }
164282
+ };
164283
+ }
164284
+ function createCapacitorWebStore(plugin, opts = {}) {
164285
+ const directory = opts.directory ?? "DATA";
164286
+ const basePath = (opts.basePath || "bolloon-web").replace(/^\/+|\/+$/g, "");
164287
+ const full = (rel) => joinRel(basePath, rel);
164288
+ const b64ToBytes2 = (b642) => {
164289
+ if (typeof atob === "function") {
164290
+ const bin = atob(b642);
164291
+ const out = new Uint8Array(bin.length);
164292
+ for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);
164293
+ return out;
164294
+ }
164295
+ return new Uint8Array(Buffer.from(b642, "base64"));
164296
+ };
164297
+ const bytesToB642 = (data) => {
164298
+ let bin = "";
164299
+ for (let i = 0; i < data.length; i++) bin += String.fromCharCode(data[i]);
164300
+ if (typeof btoa === "function") return btoa(bin);
164301
+ return Buffer.from(data).toString("base64");
164302
+ };
164303
+ return {
164304
+ kind: "capacitor-fs",
164305
+ rootLabel: `${String(directory)}:${basePath}`,
164306
+ async list(rel) {
164307
+ try {
164308
+ const r = await plugin.readdir({ path: full(rel), directory });
164309
+ return (r?.files || []).filter((f) => f?.type !== "directory").map((f) => String(f.name)).sort();
164310
+ } catch {
164311
+ return [];
164312
+ }
164313
+ },
164314
+ async exists(rel) {
164315
+ try {
164316
+ await plugin.stat({ path: full(rel), directory });
164317
+ return true;
164318
+ } catch {
164319
+ return false;
164320
+ }
164321
+ },
164322
+ async readBytes(rel) {
164323
+ try {
164324
+ const r = await plugin.readFile({ path: full(rel), directory });
164325
+ const data = String(r?.data || "");
164326
+ return data ? b64ToBytes2(data) : null;
164327
+ } catch {
164328
+ return null;
164329
+ }
164330
+ },
164331
+ async readText(rel) {
164332
+ try {
164333
+ const r = await plugin.readFile({ path: full(rel), directory, encoding: "utf8" });
164334
+ return r?.data === void 0 ? null : String(r.data);
164335
+ } catch {
164336
+ return null;
164337
+ }
164338
+ },
164339
+ async writeBytes(rel, data) {
164340
+ const path = full(rel);
164341
+ const parts = full(rel).split("/");
164342
+ for (let i = 1; i < parts.length; i++) {
164343
+ try {
164344
+ await plugin.mkdir({ path: parts.slice(0, i).join("/"), directory, recursive: true });
164345
+ } catch {
164346
+ }
164347
+ }
164348
+ await plugin.writeFile({ path, data: bytesToB642(data), directory });
164349
+ },
164350
+ async remove(rel) {
164351
+ try {
164352
+ await plugin.rmdir({ path: full(rel), directory, recursive: true });
164353
+ return;
164354
+ } catch {
164355
+ }
164356
+ try {
164357
+ await plugin.rmdir({ path: full(rel), directory });
164358
+ } catch {
164359
+ }
164360
+ },
164361
+ async rename(from6, to) {
164362
+ try {
164363
+ await plugin.rmdir({ path: full(to), directory, recursive: true });
164364
+ } catch {
164365
+ }
164366
+ await plugin.rename({ from: full(from6), to: full(to), directory, toDirectory: directory });
164367
+ },
164368
+ baseUrl() {
164369
+ return opts.baseUrl ?? null;
164370
+ }
164371
+ };
164372
+ }
164373
+ async function gunzipBytes(data) {
164374
+ const DS = globalThis.DecompressionStream;
164375
+ if (typeof DS !== "function") return null;
164376
+ try {
164377
+ const stream = new globalThis.Blob([data]).stream().pipeThrough(new DS("gzip"));
164378
+ const buf = await new Response(stream).arrayBuffer();
164379
+ return new Uint8Array(buf);
164380
+ } catch {
164381
+ return null;
164382
+ }
164383
+ }
164384
+ function untarBytes(buf) {
164385
+ const out = [];
164386
+ const td = new TextDecoder();
164387
+ const readStr = (off, len) => {
164388
+ let end = off;
164389
+ while (end < off + len && buf[end] !== 0) end++;
164390
+ return td.decode(buf.subarray(off, end));
164391
+ };
164392
+ const readOctal = (off, len) => {
164393
+ const s2 = readStr(off, len).trim().replace(/\0/g, "");
164394
+ const v = parseInt(s2, 8);
164395
+ return Number.isFinite(v) ? v : 0;
164396
+ };
164397
+ let p = 0;
164398
+ let paxPath = null;
164399
+ while (p + 512 <= buf.length) {
164400
+ const name0 = readStr(p, 100);
164401
+ if (!name0) break;
164402
+ const size2 = readOctal(p + 124, 12);
164403
+ const typeflag = String.fromCharCode(buf[p + 156] || 48);
164404
+ const prefix = readStr(p + 345, 155);
164405
+ const dataStart = p + 512;
164406
+ const dataEnd = dataStart + size2;
164407
+ if (dataEnd > buf.length) break;
164408
+ let name10 = prefix ? `${prefix}/${name0}` : name0;
164409
+ if (typeflag === "x" || typeflag === "g") {
164410
+ const text = td.decode(buf.subarray(dataStart, dataEnd));
164411
+ const m2 = text.match(/\d+ path=([^\n]+)\n/);
164412
+ if (m2) paxPath = m2[1];
164413
+ } else if (typeflag === "L") {
164414
+ paxPath = td.decode(buf.subarray(dataStart, dataEnd)).replace(/\0+$/, "").trim();
164415
+ } else if (typeflag === "0" || typeflag === "\0" || typeflag === "" || typeflag === "0") {
164416
+ if (paxPath) {
164417
+ name10 = paxPath;
164418
+ paxPath = null;
164419
+ }
164420
+ out.push({ path: name10.replace(/^\.\//, ""), data: buf.slice(dataStart, dataEnd) });
164421
+ } else {
164422
+ paxPath = null;
164423
+ }
164424
+ p = dataStart + Math.ceil(size2 / 512) * 512;
164425
+ }
164426
+ return out;
164427
+ }
164428
+ var WEB_PATH_PREFIXES = ["package/dist/web/", "dist/web/", "./dist/web/"];
164429
+ function isWebResourcePath(path) {
164430
+ const p = String(path).replace(/^\.\//, "");
164431
+ for (const pre of WEB_PATH_PREFIXES) {
164432
+ if (p.startsWith(pre)) return { hit: true, rel: p.slice(pre.length) };
164433
+ }
164434
+ return { hit: false, rel: "" };
164435
+ }
164436
+ function pickWebLayer(entries) {
164437
+ const files = [];
164438
+ let snapshot2 = null;
164439
+ const td = new TextDecoder();
164440
+ for (const e of entries) {
164441
+ const p = String(e.path).replace(/^\.\//, "");
164442
+ if (/(^|\/)\.bolloon-dev-snapshot\.json$/.test(p)) {
164443
+ try {
164444
+ snapshot2 = JSON.parse(td.decode(e.data));
164445
+ } catch {
164446
+ snapshot2 = null;
164447
+ }
164448
+ continue;
164449
+ }
164450
+ const w2 = isWebResourcePath(p);
164451
+ if (w2.hit && w2.rel && !w2.rel.endsWith("/")) files.push({ rel: w2.rel, data: e.data });
164452
+ }
164453
+ return { files, snapshot: snapshot2 };
164454
+ }
164455
+ function toHex(buf) {
164456
+ const b = buf instanceof Uint8Array ? buf : new Uint8Array(buf);
164457
+ let s2 = "";
164458
+ for (let i = 0; i < b.length; i++) s2 += b[i].toString(16).padStart(2, "0");
164459
+ return s2;
164460
+ }
164461
+ function normalizeAlgo(a) {
164462
+ const k = String(a || "").toLowerCase().replace(/[^a-z0-9]/g, "");
164463
+ if (k === "sha1") return "SHA-1";
164464
+ if (k === "sha256") return "SHA-256";
164465
+ if (k === "sha512") return "SHA-512";
164466
+ return null;
164467
+ }
164468
+ async function digestHex(data, algo) {
164469
+ const c = globalThis.crypto?.subtle;
164470
+ if (!c) return null;
164471
+ const name10 = normalizeAlgo(algo);
164472
+ if (!name10) return null;
164473
+ try {
164474
+ return toHex(await c.digest(name10, data));
164475
+ } catch {
164476
+ return null;
164477
+ }
164478
+ }
164479
+ function parseSri(integrity) {
164480
+ const s2 = String(integrity || "").trim();
164481
+ const m2 = s2.match(/^(sha512|sha256|sha1)-([A-Za-z0-9+/=]+)/);
164482
+ if (!m2) return null;
164483
+ return { algo: m2[1], b64: m2[2] };
164484
+ }
164485
+ function b64Of(bytes) {
164486
+ let bin = "";
164487
+ for (let i = 0; i < bytes.length; i++) bin += String.fromCharCode(bytes[i]);
164488
+ if (typeof btoa === "function") return btoa(bin);
164489
+ return Buffer.from(bytes).toString("base64");
164490
+ }
164491
+ var MOBILE_UPDATE_STATE_SCHEMA = "bolloon-mobile-web/1";
164492
+ function emptyMobileUpdateState() {
164493
+ return {
164494
+ schema: MOBILE_UPDATE_STATE_SCHEMA,
164495
+ currentVersion: null,
164496
+ installedChannel: null,
164497
+ installedDevSha: null,
164498
+ channelPref: null,
164499
+ installedFrom: null,
164500
+ switchableTo: null,
164501
+ lastDevSha: null,
164502
+ devRef: null,
164503
+ devCheckedAt: null,
164504
+ lastCheck: null,
164505
+ lastUpdate: null,
164506
+ lastFailure: null,
164507
+ autoPrepared: null,
164508
+ history: [],
164509
+ needsReload: false
164510
+ };
164511
+ }
164512
+ async function readMobileUpdateState(store) {
164513
+ const base6 = emptyMobileUpdateState();
164514
+ const text = await store.readText(WEB_LAYOUT.state);
164515
+ if (!text) return base6;
164516
+ try {
164517
+ const raw = JSON.parse(text);
164518
+ if (!raw || raw.schema !== MOBILE_UPDATE_STATE_SCHEMA) return base6;
164519
+ return { ...base6, ...raw, history: Array.isArray(raw.history) ? raw.history.slice(0, 20) : [] };
164520
+ } catch {
164521
+ return base6;
164522
+ }
164523
+ }
164524
+ async function writeMobileUpdateState(patch, store) {
164525
+ const cur = await readMobileUpdateState(store);
164526
+ const next = { ...cur, ...patch, schema: MOBILE_UPDATE_STATE_SCHEMA };
164527
+ await store.writeBytes(WEB_LAYOUT.state, new TextEncoder().encode(JSON.stringify(next, null, 2)));
164528
+ return next;
164529
+ }
164530
+ async function fetchJson(url, fetchImpl, timeoutMs = 15e3) {
164531
+ try {
164532
+ const ac = globalThis.AbortController ? new AbortController() : null;
164533
+ const timer = ac ? setTimeout(() => {
164534
+ try {
164535
+ ac.abort();
164536
+ } catch {
164537
+ }
164538
+ }, timeoutMs) : null;
164539
+ const res = await fetchImpl(url, { headers: { Accept: "application/json" }, signal: ac?.signal });
164540
+ const status = Number(res?.status || 0);
164541
+ const text = await res.text();
164542
+ if (timer) clearTimeout(timer);
164543
+ let json = null;
164544
+ try {
164545
+ json = JSON.parse(text);
164546
+ } catch {
164547
+ json = null;
164548
+ }
164549
+ return { ok: status >= 200 && status < 300, status, json, text };
164550
+ } catch (e) {
164551
+ return { ok: false, status: 0, json: null, text: "", err: e };
164552
+ }
164553
+ }
164554
+ async function queryMobileNpmFacts(deps, pkg) {
164555
+ if (deps.npmDoc !== void 0 && deps.npmDoc !== null) return normalizeNpmDoc(deps.npmDoc, null);
164556
+ if (deps.npmDoc === null && deps.githubFacts !== void 0) {
164557
+ return { reachable: false, kind: "offline", detail: "\u6CE8\u5165\u7684 registry \u4E8B\u5B9E\u4E3A null (\u53D7\u63A7\u4E0D\u53EF\u8FBE: offline)", latest: null, distTags: {}, versions: [], dist: null };
164558
+ }
164559
+ const base6 = (deps.registryBase || NPM_REGISTRY_BASE).replace(/\/+$/, "");
164560
+ const url = `${base6}/${encodeURIComponent(pkg).replace("%40", "@")}`;
164561
+ const fetchImpl = deps.fetchImpl || globalThis.fetch?.bind(globalThis);
164562
+ if (!fetchImpl) return { reachable: false, kind: "offline", detail: "\u672C\u673A\u6CA1\u6709 fetch (offline)", latest: null, distTags: {}, versions: [], dist: null };
164563
+ const r = await fetchJson(url, fetchImpl);
164564
+ if (!r.ok) {
164565
+ const cls = classifyRegistryError(r.err || null, r.status || void 0);
164566
+ return { reachable: false, kind: cls.kind, detail: cls.kind === "offline" ? `offline: ${cls.detail}` : `registry_unavailable: ${cls.detail}`, latest: null, distTags: {}, versions: [], dist: null };
164567
+ }
164568
+ if (!r.json) return { reachable: false, kind: "registry_unavailable", detail: "registry_unavailable: registry \u8FD4\u56DE\u4E86\u65E0\u6CD5\u89E3\u6790\u7684\u5185\u5BB9", latest: null, distTags: {}, versions: [], dist: null };
164569
+ return normalizeNpmDoc(r.json, null);
164570
+ }
164571
+ function normalizeNpmDoc(doc, detail) {
164572
+ const distTags = doc?.["dist-tags"] || {};
164573
+ const versions = Object.keys(doc?.versions || {});
164574
+ if (!distTags.latest && versions.length === 0) {
164575
+ return { reachable: false, detail: detail || "registry \u4E0A\u6CA1\u6709\u53EF\u7528\u7248\u672C", latest: null, distTags: {}, versions: [], dist: null };
164576
+ }
164577
+ const latest = distTags.latest || versions[versions.length - 1] || null;
164578
+ const v = latest ? doc?.versions?.[latest] : null;
164579
+ return {
164580
+ reachable: true,
164581
+ latest,
164582
+ distTags,
164583
+ versions,
164584
+ dist: v?.dist ? { tarball: String(v.dist.tarball || ""), shasum: v.dist.shasum ? String(v.dist.shasum) : null, integrity: v.dist.integrity ? String(v.dist.integrity) : null } : null
164585
+ };
164586
+ }
164587
+ async function queryMobileGithubFacts(deps) {
164588
+ if (deps.githubFacts !== void 0 && deps.githubFacts !== null) return deps.githubFacts;
164589
+ if (deps.githubFacts === null) return null;
164590
+ const fetchImpl = deps.fetchImpl || globalThis.fetch?.bind(globalThis);
164591
+ if (!fetchImpl) return null;
164592
+ const base6 = (deps.apiBase || GITHUB_API_BASE).replace(/\/+$/, "");
164593
+ const missing = [];
164594
+ let releases = [];
164595
+ let tags = [];
164596
+ let headSha = null;
164597
+ const failures = [];
164598
+ const note = (e, what) => {
164599
+ missing.push(what);
164600
+ failures.push({ reason: e.reason, detail: `${what}: ${e.detail}`, retryAt: e.retryAt });
164601
+ };
164602
+ const rel = await fetchJson(`${base6}/repos/${GITHUB_UPSTREAM_SLUG}/releases?per_page=100`, fetchImpl);
164603
+ if (rel.ok && Array.isArray(rel.json)) releases = rel.json.map((x) => String(x?.tag_name || "")).filter(Boolean);
164604
+ else note(classifyGithubError(rel.err || null, rel.status || void 0), "releases");
164605
+ const tg = await fetchJson(`${base6}/repos/${GITHUB_UPSTREAM_SLUG}/tags?per_page=100`, fetchImpl);
164606
+ if (tg.ok && Array.isArray(tg.json)) tags = tg.json.map((x) => ({ name: String(x?.name || ""), sha: x?.commit?.sha ? String(x.commit.sha) : null })).filter((t) => t.name);
164607
+ else note(classifyGithubError(tg.err || null, tg.status || void 0), "tags");
164608
+ const hd = await fetchJson(`${base6}/repos/${GITHUB_UPSTREAM_SLUG}/commits/${DEV_BRANCH}`, fetchImpl);
164609
+ if (hd.ok && hd.json?.sha) headSha = String(hd.json.sha);
164610
+ else if (hd.ok) note({ reason: "parse_error", detail: "commits/master \u6CA1\u6709 sha \u5B57\u6BB5", retryAt: null }, "head");
164611
+ else note(classifyGithubError(hd.err || null, hd.status || void 0), "head");
164612
+ const facts = { slug: GITHUB_UPSTREAM_SLUG, ref: DEV_REF, headSha, releases, tags, fetchedAt: (/* @__PURE__ */ new Date()).toISOString(), partial: missing.length > 0, missing };
164613
+ if (failures.length) {
164614
+ const f = failures[0];
164615
+ return { ok: false, kind: "github_unavailable", reason: f.reason, detail: f.detail, retryAt: f.retryAt, facts };
164616
+ }
164617
+ return { ok: true, facts };
164618
+ }
164619
+ async function resolveLocalWebIdentity(deps) {
164620
+ if (deps.localIdentity && isKnownVersion(deps.localIdentity.version)) {
164621
+ const devSha = devShaFromIdentity(deps.localIdentity.version);
164622
+ return { version: String(deps.localIdentity.version), channel: devSha ? "dev" : deps.localIdentity.channel || "stable", source: "shell-injected" };
164623
+ }
164624
+ const st = await readMobileUpdateState(deps.store);
164625
+ if (st.currentVersion && isKnownVersion(st.currentVersion)) {
164626
+ const devSha = devShaFromIdentity(st.currentVersion);
164627
+ return { version: st.currentVersion, channel: devSha ? "dev" : st.installedChannel || "stable", source: "ota-state" };
164628
+ }
164629
+ const url = deps.localIdentityUrl === null ? null : deps.localIdentityUrl || "./bolloon-web.json";
164630
+ const fetchImpl = deps.fetchImpl || globalThis.fetch?.bind(globalThis);
164631
+ if (url && fetchImpl) {
164632
+ const r = await fetchJson(url, fetchImpl, 8e3).catch(() => null);
164633
+ const v = r?.json?.version;
164634
+ if (r?.ok && isKnownVersion(v)) {
164635
+ const devSha = devShaFromIdentity(v);
164636
+ return { version: String(v), channel: devSha ? "dev" : r.json.channel === "dev" ? "dev" : "stable", source: "build-stamp" };
164637
+ }
164638
+ }
164639
+ return null;
164640
+ }
164641
+ function refused(s2) {
164642
+ return MOBILE_REFUSED_STATUSES.includes(s2);
164643
+ }
164644
+ function nativeWired(deps) {
164645
+ if (deps.nativeWritable === true) return true;
164646
+ if (deps.nativeWritable === false) return false;
164647
+ return deps.store.kind === "memory";
164648
+ }
164649
+ function warnFor(channel, wired = true) {
164650
+ const w2 = channelKindOf(channel) === "git-ref" ? [DEV_CHANNEL_WARNING, DEV_BACK_TO_STABLE_HINT_MOBILE] : [];
164651
+ if (!wired) w2.push(NATIVE_CEILING_LINE);
164652
+ return w2;
164653
+ }
164654
+ async function checkMobileUpdate(deps) {
164655
+ const store = deps.store;
164656
+ const now = new Date(deps.now ? deps.now() : Date.now()).toISOString();
164657
+ const local = await resolveLocalWebIdentity(deps);
164658
+ const channel = resolveUpdateChannel(deps.channel);
164659
+ const channelKind = channelKindOf(channel);
164660
+ const base6 = {
164661
+ channel,
164662
+ channelKind,
164663
+ currentVersion: local?.version || "unknown",
164664
+ installedChannel: local ? devShaFromIdentity(local.version) ? "dev" : "stable" : null,
164665
+ installedDevSha: local ? devShaFromIdentity(local.version) : null,
164666
+ latestVersion: null,
164667
+ targetIdentity: null,
164668
+ switchableTo: null,
164669
+ crossCheck: null,
164670
+ github: null,
164671
+ dev: null,
164672
+ sourceFacts: null,
164673
+ targetPublished: null,
164674
+ artifact: null,
164675
+ warnings: warnFor(channel, nativeWired(deps)),
164676
+ checkedAt: now,
164677
+ identityLine: describeSourceIdentity({
164678
+ packageVersion: local?.version || "unknown",
164679
+ installedDevSha: local ? devShaFromIdentity(local.version) : null,
164680
+ switchableTo: null,
164681
+ channelKind
164682
+ }),
164683
+ localIdentitySource: local?.source || null,
164684
+ storeKind: store.kind,
164685
+ nativeWritable: nativeWired(deps),
164686
+ effectiveAfterReload: nativeWired(deps)
164687
+ };
164688
+ if (!local || !isKnownVersion(local.version)) {
164689
+ return { ...base6, status: "local_version_unknown", reason: `\u8BFB\u4E0D\u5230\u672C\u673A web \u5C42\u7684\u7248\u672C\u8EAB\u4EFD (\u58F3\u5C42\u6CE8\u5165 / OTA state / \u6784\u5EFA\u6233 bolloon-web.json \u90FD\u6CA1\u6709) \u2014 \u4E0D\u5224\u65AD\u6709\u6CA1\u6709\u66F4\u65B0` };
164690
+ }
164691
+ const npm = await queryMobileNpmFacts(deps, deps.pkgName || PKG_NAME);
164692
+ const githubRes = await queryMobileGithubFacts(deps);
164693
+ const github = githubRes ? toGithubReport(githubRes) : null;
164694
+ const npmLatest = npm.reachable ? npm.distTags[distTagForChannel(channel)] || npm.latest : null;
164695
+ const sourceFacts = {
164696
+ npm: npm.reachable ? { reachable: true, latest: npmLatest } : { reachable: false, latest: null, detail: npm.detail },
164697
+ github: github ? {
164698
+ reachable: github.reachable,
164699
+ reason: github.reason,
164700
+ detail: github.detail,
164701
+ retryAt: github.retryAt ?? null,
164702
+ ref: github.ref,
164703
+ headSha: github.headSha,
164704
+ newestVersion: github.newestVersion,
164705
+ releases: github.releases,
164706
+ tags: github.tags
164707
+ } : null,
164708
+ crossCheck: null
164709
+ };
164710
+ const switchableTo = base6.installedDevSha ? { channel: "stable", source: "npm", target: npmLatest } : { channel: "dev", source: "github", target: github?.headSha ? github.headSha.slice(0, 7) : null };
164711
+ const identityLine = describeSourceIdentity({
164712
+ packageVersion: local.version,
164713
+ installedDevSha: base6.installedDevSha,
164714
+ switchableTo,
164715
+ channelKind
164716
+ });
164717
+ const withSources = { ...base6, github, sourceFacts, switchableTo, identityLine };
164718
+ if (channelKind === "git-ref") {
164719
+ if (!github || !github.reachable) {
164720
+ const reason = github?.reason || "offline";
164721
+ const detail = github?.detail || (githubRes === null ? "\u672C\u6B21\u672A\u67E5\u8BE2 GitHub \u6E90" : "GitHub \u6E90\u4E0D\u53EF\u8FBE");
164722
+ return {
164723
+ ...withSources,
164724
+ status: "github_unavailable",
164725
+ latestVersion: null,
164726
+ reason: `github_unavailable(${reason}): ${detail} \u2014 dev \u901A\u9053\u53EA\u6709 GitHub \u4E00\u4E2A\u6E90, \u62D2\u7EDD\u5B89\u88C5 (\u4E0D\u56DE\u843D\u5230 stable \u88C5\u4E00\u4E2A npm \u7248\u672C)`
164727
+ };
164728
+ }
164729
+ if (!github.headSha) {
164730
+ return { ...withSources, status: "github_unavailable", latestVersion: null, reason: `github_unavailable(not_found): GitHub \u4E0A\u6CA1\u6709 ${DEV_REF} \u6216\u8BFB\u4E0D\u5230 HEAD \u2014 \u62D2\u7EDD\u5B89\u88C5\u4E00\u4E2A\u8EAB\u4EFD\u4E0D\u660E\u7684\u5FEB\u7167` };
164731
+ }
164732
+ const dev = compareDevSnapshots(base6.installedDevSha, github.headSha, DEV_REF);
164733
+ const targetIdentity = devIdentity(baseVersionOf(local.version), github.headSha);
164734
+ const devBundleUrl = deps.devBundleUrl !== void 0 ? deps.devBundleUrl : envValue("BOLLOON_MOBILE_DEV_BUNDLE_URL");
164735
+ const devBundleSha256 = deps.devBundleSha256 !== void 0 ? deps.devBundleSha256 : envValue("BOLLOON_MOBILE_DEV_BUNDLE_SHA256");
164736
+ const common = { ...withSources, dev, targetIdentity, latestVersion: targetIdentity, sourceFacts: { ...sourceFacts, github: sourceFacts.github } };
164737
+ if (dev.same) {
164738
+ return { ...common, status: "up_to_date", reason: dev.detail };
164739
+ }
164740
+ if (!devBundleUrl || !/\{sha\}/.test(devBundleUrl)) {
164741
+ return {
164742
+ ...common,
164743
+ status: "dev_bundle_unavailable",
164744
+ reason: `dev_bundle_unavailable: dev \u901A\u9053\u7684 web \u5305\u8981\u7531\u6709\u6784\u5EFA\u80FD\u529B\u7684\u4E00\u7AEF\u4EA7\u51FA \u2014\u2014 GitHub master \u7684\u6E90\u7801\u5FEB\u7167\u91CC\u6CA1\u6709 dist/web (dist \u662F\u6784\u5EFA\u4EA7\u7269, \u4E0D\u5728 git \u91CC), \u624B\u673A\u7AEF\u65E0\u6CD5\u73B0\u573A\u6784\u5EFA\u3002\u8BF7\u7528\u684C\u9762\u5BFC\u51FA: npx tsx scripts/build-mobile-web-bundle.ts --channel dev (\u4EA7\u51FA dist/web + .bolloon-dev-snapshot.json \u7684 tar.gz \u4E0E\u540C\u540D .sha256), \u5E76\u628A\u5730\u5740\u914D\u5230\u624B\u673A\u7AEF BOLLOON_MOBILE_DEV_BUNDLE_URL (\u6A21\u677F\u9700\u542B {sha}, {sha} \u4F1A\u88AB\u66FF\u6362\u6210 master HEAD \u7684\u5B8C\u6574 40 \u4F4D sha)\u3002${devBundleUrl ? ` \u5F53\u524D\u914D\u7F6E\u7684\u6A21\u677F\u4E0D\u542B {sha}: ${devBundleUrl}` : " \u5F53\u524D\u6CA1\u6709\u914D\u7F6E dev web \u5305\u5730\u5740\u3002"} stable \u901A\u9053\u4E0D\u53D7\u5F71\u54CD (npm tarball \u81EA\u5E26 dist/web)\u3002`
164745
+ };
164746
+ }
164747
+ const pinned = devBundleSha256 ? String(devBundleSha256).trim().toLowerCase() : null;
164748
+ const url = String(devBundleUrl).replace("{sha}", github.headSha);
163197
164749
  return {
163198
- ok: false,
163199
- error: "desktop_offline",
163200
- data: { contacts: caps, grants: [], effective: "\u684C\u9762\u4E0D\u5728\u7EBF (\u663E\u793A\u624B\u673A\u672C\u5730\u526F\u672C)", approvals: [], waiting: [] },
163201
- note: "\u4EE5\u4E0B\u662F\u4F60\u624B\u673A\u4E0A\u4FDD\u5B58\u7684\u80FD\u529B\u526F\u672C (\u4E0D\u662F\u684C\u9762\u5B9E\u65F6\u72B6\u6001)"
164750
+ ...common,
164751
+ status: "update_available",
164752
+ targetPublished: true,
164753
+ artifact: {
164754
+ channel: "dev",
164755
+ identity: targetIdentity,
164756
+ source: `github master @ ${github.headSha.slice(0, 7)}`,
164757
+ url,
164758
+ // pin 了才校验载荷摘要; 没 pin 就如实标"没有载荷锚点"(不给虚假的安全感)
164759
+ digest: pinned ? { algo: "sha256", hex: pinned } : null,
164760
+ expectedDevSha: github.headSha,
164761
+ expectedBundleSha256: pinned
164762
+ },
164763
+ reason: `${dev.detail} \xB7 web \u5305\u6765\u81EA ${url} (\u5305\u5185 sha \u5FC5\u987B\u7B49\u4E8E master HEAD, \u5426\u5219\u62D2\u7EDD)` + (pinned ? ` \xB7 \u8F7D\u8377\u6458\u8981\u5DF2 pin (sha256:${pinned.slice(0, 12)}\u2026)` : " \xB7 \u8F7D\u8377\u6458\u8981\u672A pin (dev \u901A\u9053\u56FA\u6709: \u53EA\u6709\u8EAB\u4EFD\u88AB GitHub \u951A\u5B9A)"),
164764
+ warnings: common.warnings.concat(pinned ? [] : [
164765
+ 'dev \u8F7D\u8377\u6458\u8981\u672A pin \u21D2 \u53EA\u4FDD\u8BC1"\u8EAB\u4EFD\u662F master HEAD \u7684\u90A3\u4E2A commit", \u4E0D\u4FDD\u8BC1"\u5B57\u8282\u5C31\u662F\u684C\u9762\u6253\u7684\u90A3\u4E00\u4EFD" (\u8981\u5F3A\u6821\u9A8C\u5C31\u628A .sha256 \u6E05\u5355\u7684\u503C\u914D\u5230 BOLLOON_MOBILE_DEV_BUNDLE_SHA256)'
164766
+ ])
163202
164767
  };
163203
164768
  }
163204
- try {
163205
- const a = await get(base6, "/api/contacts", f);
163206
- const b = await get(base6, "/api/contacts/grants", f);
163207
- if (a.status !== 200 && b.status !== 200) return { ok: false, error: `desktop_http_${a.status || b.status}` };
163208
- const view = {
163209
- contacts: a.json?.contacts || [],
163210
- grants: b.json?.grants || [],
163211
- effective: b.json?.effective || "",
163212
- approvals: a.json?.approvals || [],
163213
- waiting: a.json?.waiting || []
164769
+ if (!npm.reachable) {
164770
+ const detail = npm.detail || "registry \u4E0D\u53EF\u8FBE";
164771
+ const offline = npm.kind === "offline";
164772
+ return {
164773
+ ...withSources,
164774
+ status: offline ? "offline" : "registry_unavailable",
164775
+ latestVersion: null,
164776
+ reason: `${detail}${github ? ` \xB7 ${renderGithubReportLine(github)}` : ""} \u2014 \u4E0D\u80FD\u5224\u5B9A\u4E3A\u6700\u65B0`
164777
+ };
164778
+ }
164779
+ const latest = npmLatest;
164780
+ const facts = githubRes && githubRes.ok ? githubRes.facts : null;
164781
+ const crossCheck = facts ? crossCheckStable(latest, facts) : null;
164782
+ const withCross = {
164783
+ ...withSources,
164784
+ crossCheck,
164785
+ latestVersion: latest,
164786
+ sourceFacts: { ...sourceFacts, crossCheck: crossCheck ? { kind: crossCheck.kind, blocking: crossCheck.blocking, detail: crossCheck.detail } : null }
164787
+ };
164788
+ if (crossCheck?.kind === "mismatch") {
164789
+ return {
164790
+ ...withCross,
164791
+ status: "cross_check_mismatch",
164792
+ reason: `cross_check_mismatch: ${crossCheck.detail} (npm latest=${latest} / GitHub ${crossCheck.githubVersion || "\u65E0\u8BB0\u5F55"})`
163214
164793
  };
163215
- saveCapabilityCopy(storage2, (view.contacts || []).filter((c) => c.capabilities?.includes("send")).map((c) => ({
163216
- contactId: c.contactId,
163217
- kind: c.kind,
163218
- displayValue: c.displayValue,
163219
- verificationStatus: c.status,
163220
- capabilities: c.capabilities,
163221
- provider: c.provider,
163222
- state: c.status
163223
- })));
163224
- return { ok: true, data: view };
163225
- } catch (err2) {
163226
- const caps = loadCapabilityCopy(storage2);
164794
+ }
164795
+ if (isDevIdentity(local.version)) {
164796
+ const target = latest;
164797
+ if (!target || !npm.versions.includes(baseVersionOf(target))) {
164798
+ return { ...withCross, status: "target_unpublished", reason: `target_unpublished: registry \u4E0A\u6CA1\u6709 ${target || "\u76EE\u6807\u7248\u672C"} \u8FD9\u4E2A\u7248\u672C \u2014 \u62D2\u7EDD\u5207\u56DE (\u5F53\u524D dev \u5FEB\u7167 ${local.version} \u539F\u6837\u4FDD\u7559)` };
164799
+ }
164800
+ if (!npm.dist?.tarball) {
164801
+ return { ...withCross, status: "target_unpublished", reason: `target_unpublished: registry \u6CA1\u7ED9\u51FA ${target} \u7684\u4E0B\u8F7D\u5730\u5740 (dist.tarball \u7F3A\u5931) \u2014 \u62D2\u7EDD\u5207\u56DE` };
164802
+ }
164803
+ return {
164804
+ ...withCross,
164805
+ status: "update_available",
164806
+ targetPublished: true,
164807
+ artifact: {
164808
+ channel: "stable",
164809
+ identity: String(target),
164810
+ source: "npm registry (dist-tags.latest)",
164811
+ url: npm.dist.tarball,
164812
+ digest: npm.dist.shasum ? { algo: "sha1", hex: npm.dist.shasum } : npm.dist.integrity ? { algo: "sha512", b64: parseSri(npm.dist.integrity)?.b64 ?? null } : null
164813
+ },
164814
+ reason: `\u5F53\u524D\u88C5\u7684\u662F dev \u5FEB\u7167 ${local.version} \u2192 \u5207\u56DE stable \u7684 ${target} (${DEV_BACK_TO_STABLE_HINT_MOBILE})`
164815
+ };
164816
+ }
164817
+ if (latest && !npm.versions.includes(latest)) {
164818
+ return { ...withCross, status: "target_unpublished", targetPublished: false, reason: `target_unpublished: registry \u7684 dist-tags \u6307\u5230 ${latest}, \u4F46 versions \u91CC\u6CA1\u6709\u8FD9\u4E2A\u7248\u672C \u2014 \u62D2\u7EDD\u66F4\u65B0 (0 \u5B57\u8282\u4E0B\u8F7D)` };
164819
+ }
164820
+ if (latest && compareVersions(baseVersionOf(local.version), latest) < 0) {
164821
+ if (!npm.dist?.tarball) {
164822
+ return { ...withCross, status: "target_unpublished", targetPublished: true, reason: `target_unpublished: registry \u6CA1\u7ED9\u51FA ${latest} \u7684\u4E0B\u8F7D\u5730\u5740 (dist.tarball \u7F3A\u5931) \u2014 \u62D2\u7EDD\u66F4\u65B0` };
164823
+ }
164824
+ return {
164825
+ ...withCross,
164826
+ status: "update_available",
164827
+ targetPublished: true,
164828
+ artifact: {
164829
+ channel: "stable",
164830
+ identity: latest,
164831
+ source: "npm registry (dist-tags.latest)",
164832
+ url: npm.dist.tarball,
164833
+ digest: npm.dist.shasum ? { algo: "sha1", hex: npm.dist.shasum } : npm.dist.integrity ? { algo: "sha512", b64: parseSri(npm.dist.integrity)?.b64 ?? null } : null
164834
+ },
164835
+ reason: `\u5F53\u524D ${local.version} \u2192 \u76EE\u6807 ${latest}${crossCheck ? ` \xB7 \u4EA4\u53C9\u6821\u9A8C: ${crossCheck.kind}` : ""}`
164836
+ };
164837
+ }
164838
+ return { ...withCross, status: "up_to_date", targetPublished: latest ? npm.versions.includes(latest) : null };
164839
+ }
164840
+ async function downloadArtifact(artifact, deps) {
164841
+ const fetchImpl = deps.fetchImpl || globalThis.fetch?.bind(globalThis);
164842
+ const maxBytes = deps.maxPayloadBytes ?? MOBILE_MAX_PAYLOAD_BYTES;
164843
+ if (!fetchImpl) return { ok: false, status: "offline", detail: "\u672C\u673A\u6CA1\u6709 fetch, \u65E0\u6CD5\u4E0B\u8F7D", digest: { algo: "", matched: null } };
164844
+ let bytes;
164845
+ try {
164846
+ const res = await fetchImpl(artifact.url, { headers: { Accept: "application/octet-stream, application/gzip, */*" } });
164847
+ const status = Number(res?.status || 0);
164848
+ if (!(status >= 200 && status < 300)) {
164849
+ const cls = classifyGithubError(null, status || void 0);
164850
+ const bucket = cls.reason === "not_found" ? "target_unpublished" : cls.reason === "offline" ? "offline" : "registry_unavailable";
164851
+ return { ok: false, status: bucket, detail: `\u4E0B\u8F7D ${artifact.url} \u5931\u8D25: HTTP ${status || "\u65E0\u54CD\u5E94"} (${cls.reason})`, digest: { algo: "", matched: null } };
164852
+ }
164853
+ const lenHint = Number(res?.headers?.get?.("content-length") || 0);
164854
+ if (lenHint && lenHint > maxBytes) {
164855
+ return { ok: false, status: "payload_too_large", detail: `\u4E0B\u8F7D\u7269 ${(lenHint / 1048576).toFixed(1)} MiB \u8D85\u8FC7\u4E0A\u9650 ${(maxBytes / 1048576).toFixed(0)} MiB \u2014 \u62D2\u7EDD`, digest: { algo: "", matched: null } };
164856
+ }
164857
+ const buf = await res.arrayBuffer();
164858
+ bytes = new Uint8Array(buf);
164859
+ if (bytes.length > maxBytes) {
164860
+ return { ok: false, status: "payload_too_large", detail: `\u4E0B\u8F7D\u7269 ${(bytes.length / 1048576).toFixed(1)} MiB \u8D85\u8FC7\u4E0A\u9650 ${(maxBytes / 1048576).toFixed(0)} MiB \u2014 \u62D2\u7EDD`, digest: { algo: "", matched: null } };
164861
+ }
164862
+ } catch (e) {
164863
+ const cls = classifyGithubError(e);
164864
+ return { ok: false, status: cls.reason === "offline" ? "offline" : "registry_unavailable", detail: `\u4E0B\u8F7D ${artifact.url} \u5931\u8D25: ${cls.detail}`, digest: { algo: "", matched: null } };
164865
+ }
164866
+ if (!artifact.digest) {
164867
+ return { ok: true, detail: "\u8BE5\u6E90\u6CA1\u6709\u63D0\u4F9B\u6458\u8981 (dev web \u5305\u4EE5\u5305\u5185 sha \u4F5C\u4E3A\u8EAB\u4EFD\u6821\u9A8C, \u89C1 dev_sha_mismatch)", bytes, digest: { algo: "", matched: null } };
164868
+ }
164869
+ const algo = artifact.digest.algo;
164870
+ const want = artifact.digest.hex || artifact.digest.b64;
164871
+ if (!want) return { ok: true, detail: "\u58F0\u660E\u7684\u6458\u8981\u4E3A\u7A7A (\u8DF3\u8FC7)", bytes, digest: { algo, matched: null } };
164872
+ let got = null;
164873
+ if (artifact.digest.hex) got = await digestHex(bytes, algo);
164874
+ else {
164875
+ const bytesDigest = await digestHex(bytes, algo);
164876
+ if (bytesDigest) got = null;
164877
+ const h3 = bytesDigest;
164878
+ if (h3) {
164879
+ const pairs = h3.match(/.{2}/g) || [];
164880
+ const arr = new Uint8Array(pairs.map((x) => parseInt(x, 16)));
164881
+ got = b64Of(arr) === artifact.digest.b64 ? artifact.digest.b64 : b64Of(arr);
164882
+ }
164883
+ }
164884
+ const matched = got !== null && got.toLowerCase() === want.toLowerCase();
164885
+ if (!matched) {
163227
164886
  return {
163228
164887
  ok: false,
163229
- error: "desktop_offline",
163230
- data: { contacts: caps, grants: [], effective: "\u684C\u9762\u4E0D\u5728\u7EBF (\u663E\u793A\u624B\u673A\u672C\u5730\u526F\u672C)", approvals: [], waiting: [] },
163231
- note: `\u8FDE\u4E0D\u4E0A\u684C\u9762 (${String(err2?.message || err2).slice(0, 60)}) \u2014\u2014 \u663E\u793A\u672C\u5730\u526F\u672C`
164888
+ status: "digest_mismatch",
164889
+ detail: `digest_mismatch: \u6458\u8981\u5BF9\u4E0D\u4E0A (${algo}) \u2014 \u58F0\u660E ${String(want).slice(0, 24)}\u2026, \u5B9E\u9645 ${String(got).slice(0, 24)}\u2026, \u62D2\u7EDD\u5B89\u88C5`,
164890
+ bytes,
164891
+ digest: { algo, hex: artifact.digest.hex ? got : null, b64: artifact.digest.b64 ? got : null, matched: false }
163232
164892
  };
163233
164893
  }
164894
+ return { ok: true, detail: `\u6458\u8981\u5DF2\u6838\u5BF9 (${algo})`, bytes, digest: { algo, hex: artifact.digest.hex ? got : null, b64: artifact.digest.b64 ? got : null, matched: true } };
163234
164895
  }
163235
- async function bindFromPhone(base6, opts, storage2, f) {
163236
- const impl = f || fetch;
163237
- if (!base6) return { ok: false, error: "desktop_offline: \u7ED1\u5B9A\u9700\u8981\u684C\u9762\u7AEF\u5728\u7EBF (\u684C\u9762\u8D1F\u8D23\u843D\u76D8\u4E0E\u9A8C\u8BC1\u7801\u901A\u9053)" };
163238
- const r = await post(base6, "/api/contacts/bind", { kind: opts.kind, value: opts.value, region: opts.region }, impl);
163239
- if (r.status === 200 && r.json?.ok) return { ok: true, data: r.json };
163240
- return { ok: false, error: r.json?.error || `HTTP ${r.status}` };
164896
+ function scriptRefsOf(html) {
164897
+ const out = [];
164898
+ const re = /<script[^>]*\ssrc=["']([^"']+)["']/gi;
164899
+ let m2;
164900
+ while (m2 = re.exec(html)) out.push(m2[1]);
164901
+ return out;
163241
164902
  }
163242
- async function verifyFromPhone(base6, opts, storage2, f) {
163243
- const impl = f || fetch;
163244
- if (!base6) return { ok: false, error: "desktop_offline: \u9A8C\u8BC1\u9700\u8981\u684C\u9762\u7AEF\u5728\u7EBF" };
163245
- const r = await post(base6, "/api/contacts/verify", { contactId: opts.contactId, challengeId: opts.challengeId, code: opts.code }, impl);
163246
- if (r.status === 200 && r.json?.ok) return { ok: true, data: r.json };
163247
- return { ok: false, error: r.json?.error || `HTTP ${r.status}` };
164903
+ async function verifyWebLayer(store, prefix, opts = {}) {
164904
+ const checks = [];
164905
+ const present = {};
164906
+ for (const f of MOBILE_WEB_REQUIRED_FILES) {
164907
+ const text = await store.readText(joinRel(prefix, f));
164908
+ const bytes = text === null ? await store.readBytes(joinRel(prefix, f)) : null;
164909
+ const size2 = text !== null ? text.length : bytes?.length ?? 0;
164910
+ present[f] = String(text ?? "");
164911
+ checks.push({ name: `\u5FC5\u9700\u6587\u4EF6 ${f}`, ok: size2 > 0, detail: size2 > 0 ? `${size2} \u5B57\u8282` : "\u7F3A\u5931\u6216\u4E3A\u7A7A" });
164912
+ }
164913
+ const html = present["mobile.html"] || "";
164914
+ const refs = scriptRefsOf(html);
164915
+ const refMiss = [];
164916
+ for (const r of refs) {
164917
+ if (/^https?:|^\/\//i.test(r)) continue;
164918
+ const rel = r.replace(/^\.\//, "").split("?")[0];
164919
+ if (!await store.exists(joinRel(prefix, rel))) refMiss.push(r);
164920
+ }
164921
+ checks.push({ name: "mobile.html \u5F15\u7528\u7684\u811A\u672C\u90FD\u5728", ok: refMiss.length === 0, detail: refMiss.length ? `\u7F3A\u5931: ${refMiss.join(", ")}` : `${refs.length} \u4E2A\u5F15\u7528\u90FD\u547D\u4E2D` });
164922
+ const syntaxBad = [];
164923
+ for (const f of MOBILE_WEB_JS_FILES) {
164924
+ const src4 = await store.readText(joinRel(prefix, f));
164925
+ if (src4 === null || src4 === "") continue;
164926
+ try {
164927
+ new Function(src4);
164928
+ } catch (e) {
164929
+ syntaxBad.push(`${f}: ${String(e?.message || e).slice(0, 120)}`);
164930
+ }
164931
+ }
164932
+ checks.push({ name: "JS \u8BED\u6CD5\u81EA\u68C0 (\u771F\u7F16\u8BD1\u4E00\u6B21)", ok: syntaxBad.length === 0, detail: syntaxBad.length ? syntaxBad.join(" | ") : `${MOBILE_WEB_JS_FILES.length} \u4E2A\u6587\u4EF6\u53EF\u7F16\u8BD1` });
164933
+ const structural = checks.every((c) => c.ok);
164934
+ if (!structural) {
164935
+ return { ok: false, mode: "static-only", detail: `\u7ED3\u6784\u4E0E\u8BED\u6CD5\u68C0\u67E5\u672A\u901A\u8FC7: ${checks.filter((c) => !c.ok).map((c) => `${c.name}(${c.detail})`).join("; ")}`, checks };
164936
+ }
164937
+ const base6 = store.baseUrl();
164938
+ if (!base6) {
164939
+ return { ok: true, mode: "static-only", detail: '\u7ED3\u6784 + \u8BED\u6CD5\u901A\u8FC7; \u672C store \u6CA1\u6709\u53EF\u52A0\u8F7D URL \u21D2 \u672A\u505A\u771F\u542F\u52A8\u63A2\u6D4B (\u5982\u5B9E\u6807\u6CE8, \u4E0D\u5F53\u6210"\u5DF2\u9A8C\u8BC1\u80FD\u542F\u52A8")', checks };
164940
+ }
164941
+ if (!hasDom()) {
164942
+ return {
164943
+ ok: true,
164944
+ mode: "static-only",
164945
+ detail: "\u7ED3\u6784 + \u8BED\u6CD5\u901A\u8FC7; \u6709\u53EF\u52A0\u8F7D URL \u4F46\u672C\u73AF\u5883\u6CA1\u6709 DOM (\u975E\u6D4F\u89C8\u5668) \u21D2 \u672A\u505A\u771F\u542F\u52A8\u63A2\u6D4B (\u5982\u5B9E\u6807\u6CE8; \u771F\u63A2\u6D4B\u89C1 scripts/verify-mobile-update-ui.ts)",
164946
+ checks
164947
+ };
164948
+ }
164949
+ const url = `${String(base6).replace(/\/+$/, "")}/${joinRel(prefix, "mobile.html")}`;
164950
+ const probe = await iframeBootProbe(url, opts.bootTimeoutMs ?? 12e3);
164951
+ checks.push({ name: "\u771F\u542F\u52A8\u63A2\u6D4B (iframe \u52A0\u8F7D staged \u8D44\u6E90)", ok: probe.ok, detail: probe.detail });
164952
+ return { ok: probe.ok, mode: "iframe", detail: probe.ok ? `\u771F\u542F\u52A8\u63A2\u6D4B\u901A\u8FC7: ${probe.detail}` : `\u771F\u542F\u52A8\u63A2\u6D4B\u5931\u8D25: ${probe.detail}`, checks };
164953
+ }
164954
+ function hasDom() {
164955
+ const d2 = globalThis.document;
164956
+ return !!(d2 && typeof d2.createElement === "function");
164957
+ }
164958
+ async function iframeBootProbe(url, timeoutMs) {
164959
+ const doc = globalThis.document;
164960
+ if (!doc || typeof doc.createElement !== "function") return { ok: false, detail: "\u6CA1\u6709 DOM, \u65E0\u6CD5\u505A\u542F\u52A8\u63A2\u6D4B" };
164961
+ return await new Promise((resolve) => {
164962
+ let done = false;
164963
+ const frame = doc.createElement("iframe");
164964
+ frame.style.cssText = "position:absolute;left:-9999px;width:430px;height:932px;border:0;";
164965
+ frame.setAttribute("aria-hidden", "true");
164966
+ frame.src = url;
164967
+ const finish = (ok, detail) => {
164968
+ if (done) return;
164969
+ done = true;
164970
+ try {
164971
+ frame.remove();
164972
+ } catch {
164973
+ }
164974
+ resolve({ ok, detail });
164975
+ };
164976
+ const timer = setTimeout(() => {
164977
+ const w2 = (() => {
164978
+ try {
164979
+ return frame.contentWindow;
164980
+ } catch {
164981
+ return null;
164982
+ }
164983
+ })();
164984
+ finish(false, `\u7B49 ${timeoutMs}ms \u672A\u89C1 staged \u8D44\u6E90\u542F\u52A8 (readyState=${w2?.document?.readyState || "\u672A\u77E5"}, BolloonCore=${typeof w2?.BolloonCore})`);
164985
+ }, timeoutMs);
164986
+ frame.onload = () => {
164987
+ let tries = 0;
164988
+ const tick = () => {
164989
+ tries++;
164990
+ let w2 = null;
164991
+ try {
164992
+ w2 = frame.contentWindow;
164993
+ } catch {
164994
+ }
164995
+ const d2 = w2?.document;
164996
+ if (d2 && d2.readyState === "complete" && d2.querySelector && d2.querySelector(".tabbar") && w2.BolloonCore && typeof w2.BolloonCore === "object") {
164997
+ clearTimeout(timer);
164998
+ finish(true, `staged \u8D44\u6E90\u771F\u8D77\u6765\u4E86 (tabbar + BolloonCore, ${tries} \u6B21\u63A2\u6D4B)`);
164999
+ return;
165000
+ }
165001
+ if (tries > 60) {
165002
+ clearTimeout(timer);
165003
+ finish(false, `staged \u8D44\u6E90\u52A0\u8F7D\u5B8C\u4F46\u6CA1\u8D77\u6765 (readyState=${d2?.readyState || "\u672A\u77E5"}, tabbar=${!!d2?.querySelector?.(".tabbar")}, BolloonCore=${typeof w2?.BolloonCore})`);
165004
+ return;
165005
+ }
165006
+ setTimeout(tick, 150);
165007
+ };
165008
+ tick();
165009
+ };
165010
+ frame.onerror = () => {
165011
+ clearTimeout(timer);
165012
+ finish(false, "iframe \u52A0\u8F7D\u5931\u8D25");
165013
+ };
165014
+ try {
165015
+ doc.body.appendChild(frame);
165016
+ } catch (e) {
165017
+ clearTimeout(timer);
165018
+ finish(false, `\u6302\u8F7D iframe \u5931\u8D25: ${e?.message || e}`);
165019
+ }
165020
+ });
163248
165021
  }
163249
- async function decideApprovalFromPhone(base6, opts, f) {
163250
- const impl = f || fetch;
163251
- if (!base6) return { ok: false, error: "desktop_offline: \u6279\u51C6\u9700\u8981\u684C\u9762\u7AEF\u5728\u7EBF (\u624B\u673A\u4E0D\u5728\u7EBF\u65F6\u4E0D\u4F1A\u5047\u88C5\u5DF2\u6279\u51C6)" };
163252
- const r = await post(base6, `/api/contacts/approvals/${encodeURIComponent(opts.consentId)}/${opts.action}`, { by: "leo", via: "mobile", body: opts.body, reason: opts.reason }, impl);
163253
- if (r.status === 200 && r.json?.ok) return { ok: true, data: r.json };
163254
- return { ok: false, error: r.json?.error || `HTTP ${r.status}` };
165022
+ async function prepareMobileUpdate(deps, check) {
165023
+ const store = deps.store;
165024
+ const now = new Date(deps.now ? deps.now() : Date.now()).toISOString();
165025
+ const stage = (s2, detail) => {
165026
+ try {
165027
+ deps.onStage?.(s2, detail);
165028
+ } catch {
165029
+ }
165030
+ };
165031
+ const steps = [];
165032
+ const mark = (s2) => {
165033
+ steps.push(s2);
165034
+ };
165035
+ const r = check || await checkMobileUpdate(deps);
165036
+ const from6 = r.currentVersion;
165037
+ const to = r.artifact?.identity || r.targetIdentity || from6;
165038
+ const fail = async (stageName, status, reason, extra = {}) => {
165039
+ mark(`${stageName}: ${reason}`);
165040
+ await store.remove(WEB_LAYOUT.staging);
165041
+ await writeMobileUpdateState({
165042
+ lastCheck: { at: now, status: r.status, reason: r.reason, channel: r.channel, latestVersion: r.latestVersion },
165043
+ lastFailure: { at: now, stage: stageName, reason },
165044
+ autoPrepared: null
165045
+ }, store);
165046
+ return { ok: false, stage: "failed", failedAt: stageName, status, from: from6, to, reason, steps, fileCount: 0, digest: null, probe: null, skippedDownload: false, ...extra };
165047
+ };
165048
+ if (refused(r.status)) {
165049
+ return fail("blocked", r.status, `\u62D2\u7EDD\u66F4\u65B0 (${r.status}): ${r.reason || "\u6E90\u4E0D\u53EF\u8FBE / \u7248\u672C\u4E0D\u5B58\u5728"}`, { stage: "blocked" });
165050
+ }
165051
+ if (r.status === "up_to_date") {
165052
+ mark(`up_to_date: ${r.reason || "\u5DF2\u662F\u6700\u65B0"}`);
165053
+ return { ok: false, stage: "blocked", status: "up_to_date", from: from6, to, reason: `\u5DF2\u662F\u6700\u65B0 (${from6}), \u65E0\u9700\u66F4\u65B0`, steps, fileCount: 0, digest: null, probe: null, skippedDownload: true };
165054
+ }
165055
+ if (!r.artifact) return fail("planned", "target_unpublished", "\u6CA1\u6709\u53EF\u6267\u884C\u7684\u4E0B\u8F7D\u8BA1\u5212 (artifact \u7F3A\u5931)");
165056
+ const prevState = await readMobileUpdateState(store);
165057
+ if (prevState.autoPrepared?.identity === to && await store.exists(joinRel(WEB_LAYOUT.staging, "mobile.html"))) {
165058
+ mark(`reuse-staging: \u590D\u7528\u5DF2\u51C6\u5907\u5E76\u9A8C\u8BC1\u8FC7\u7684 ${to}`);
165059
+ const probe2 = await verifyWebLayer(store, WEB_LAYOUT.staging, { bootTimeoutMs: deps.bootTimeoutMs });
165060
+ if (probe2.ok) {
165061
+ return { ok: true, stage: "awaiting_human", status: "update_available", from: from6, to, reason: `staging \u91CC\u5DF2\u6709\u9A8C\u8BC1\u8FC7\u7684 ${to} (digest ${prevState.autoPrepared.digest || "\u65E0"}) \u2014 \u7B49\u4EBA\u5DE5\u786E\u8BA4\u5207\u6362`, steps, fileCount: 0, digest: prevState.autoPrepared.digest, probe: probe2, skippedDownload: true };
165062
+ }
165063
+ mark("reuse-staging: \u590D\u7528\u7684 staging \u6CA1\u901A\u8FC7\u9A8C\u8BC1 \u2192 \u91CD\u65B0\u4E0B\u8F7D");
165064
+ }
165065
+ stage("downloading", `${r.artifact.channel} \xB7 ${r.artifact.identity} \u2190 ${r.artifact.url}`);
165066
+ mark(`downloading: ${r.artifact.url}`);
165067
+ const dl = await downloadArtifact(r.artifact, deps);
165068
+ if (!dl.ok || !dl.bytes) {
165069
+ return fail("downloading", dl.status || "registry_unavailable", dl.detail);
165070
+ }
165071
+ stage("downloaded", `\u5DF2\u4E0B\u8F7D ${(dl.bytes.length / 1048576).toFixed(2)} MiB \xB7 ${dl.detail}`);
165072
+ mark(`downloaded: ${(dl.bytes.length / 1048576).toFixed(2)} MiB \xB7 ${dl.detail}`);
165073
+ stage("extracting", "\u89E3\u538B tar.gz \u5E76\u6311\u51FA web \u8D44\u6E90 (dist/web)");
165074
+ const tarBytes = await gunzipBytes(dl.bytes);
165075
+ if (!tarBytes) {
165076
+ return fail(
165077
+ "extracting",
165078
+ "decompress_unavailable",
165079
+ "decompress_unavailable: \u672C\u673A WebView \u6CA1\u6709 DecompressionStream('gzip') \u21D2 \u65E0\u6CD5\u89E3\u5F00 tar.gz \u2014 \u62D2\u7EDD (\u4E0D\u88C5\u534A\u4E2A)\u3002 iOS 16.4+ / Chrome 80+ \u624D\u6709; \u65E7 WebView \u8BF7\u5347\u7EA7\u7CFB\u7EDF\u6216\u8D70\u5546\u5E97\u66F4\u65B0\u6574\u4E2A App\u3002"
165080
+ );
165081
+ }
165082
+ const entries = untarBytes(tarBytes);
165083
+ const { files, snapshot: snapshot2 } = pickWebLayer(entries);
165084
+ if (files.length === 0) {
165085
+ return fail("extracting", "web_bundle_malformed", `web_bundle_malformed: \u5305\u91CC\u6CA1\u6709 dist/web/** \u8D44\u6E90 (tar \u6761\u76EE ${entries.length} \u4E2A) \u2014 \u62D2\u7EDD`);
165086
+ }
165087
+ mark(`extracting: ${files.length} \u4E2A web \u6587\u4EF6, \u5305\u5185 snapshot \u6807\u8BB0 = ${snapshot2 ? `\u6709 (sha ${String(snapshot2.sha || "").slice(0, 7)})` : "\u65E0"}`);
165088
+ if (r.artifact.channel === "dev") {
165089
+ const want = (r.artifact.expectedDevSha || "").slice(0, 7);
165090
+ const got = snapshot2?.sha ? String(snapshot2.sha).slice(0, 7) : null;
165091
+ if (!snapshot2) {
165092
+ return fail("extracting", "dev_sha_mismatch", `dev_sha_mismatch: dev web \u5305\u91CC\u6CA1\u6709 .bolloon-dev-snapshot.json \u21D2 \u65E0\u6CD5\u8BC1\u660E\u5B83\u662F ${want} \u7684\u5FEB\u7167 \u2014 \u62D2\u7EDD`);
165093
+ }
165094
+ if (snapshot2.channel !== "dev") {
165095
+ return fail("extracting", "dev_sha_mismatch", `dev_sha_mismatch: \u5305\u5185 snapshot \u6807\u7684\u662F channel=${snapshot2.channel} (\u4E0D\u662F dev) \u2014 \u62D2\u7EDD`);
165096
+ }
165097
+ if (!got || got !== want) {
165098
+ return fail("extracting", "dev_sha_mismatch", `dev_sha_mismatch: \u5305\u5185 sha=${got || "\u7A7A"} \u2260 GitHub master HEAD ${want} \u2014 \u62D2\u7EDD\u5B89\u88C5\u8EAB\u4EFD\u5BF9\u4E0D\u4E0A\u7684\u5FEB\u7167`);
165099
+ }
165100
+ if (snapshot2.identity && to && String(snapshot2.identity) !== to) {
165101
+ return fail("extracting", "dev_sha_mismatch", `dev_sha_mismatch: \u5305\u5185 identity=${snapshot2.identity} \u4E0E\u76EE\u6807\u8EAB\u4EFD ${to} \u4E0D\u4E00\u81F4 \u2014 \u62D2\u7EDD`);
165102
+ }
165103
+ }
165104
+ await store.remove(WEB_LAYOUT.staging);
165105
+ for (const f of files) await store.writeBytes(joinRel(WEB_LAYOUT.staging, f.rel), f.data);
165106
+ stage("staged", `${files.length} \u4E2A\u6587\u4EF6\u5DF2\u5199\u5165 ${store.rootLabel}/${WEB_LAYOUT.staging}`);
165107
+ mark(`staged: ${files.length} \u4E2A\u6587\u4EF6 \u2192 ${WEB_LAYOUT.staging}`);
165108
+ stage("verifying", "\u9A8C\u8BC1 staging \u80FD\u4E0D\u80FD\u542F\u52A8 (\u7ED3\u6784 + \u8BED\u6CD5 + iframe \u771F\u52A0\u8F7D)");
165109
+ const probe = await verifyWebLayer(store, WEB_LAYOUT.staging, { bootTimeoutMs: deps.bootTimeoutMs });
165110
+ mark(`verifying: ${probe.mode} \u2014 ${probe.detail}`);
165111
+ if (!probe.ok) {
165112
+ return fail("verifying", "web_bundle_malformed", `\u5207\u6362\u524D\u9A8C\u8BC1\u5931\u8D25 (staged \u8D44\u6E90\u8D77\u4E0D\u6765): ${probe.detail}`, { probe });
165113
+ }
165114
+ const digest3 = dl.digest.matched === true ? `${dl.digest.algo}:${dl.digest.hex || dl.digest.b64}` : null;
165115
+ await writeMobileUpdateState({
165116
+ lastCheck: { at: now, status: r.status, reason: r.reason, channel: r.channel, latestVersion: r.latestVersion },
165117
+ autoPrepared: { identity: to, digest: digest3, verifiedAt: now, steps: steps.slice() }
165118
+ }, store);
165119
+ return { ok: true, stage: "awaiting_human", status: "update_available", from: from6, to, reason: `\u5DF2\u51C6\u5907\u5E76\u9A8C\u8BC1 ${to} (\u7B49\u4EBA\u5DE5\u786E\u8BA4\u5207\u6362)`, steps, fileCount: files.length, digest: digest3, probe, skippedDownload: false };
165120
+ }
165121
+ async function applyMobileUpdate(opts) {
165122
+ const started = Date.now();
165123
+ const store = opts.store;
165124
+ const now = new Date(opts.now ? opts.now() : Date.now()).toISOString();
165125
+ const stage = (s2, detail) => {
165126
+ try {
165127
+ opts.onStage?.(s2, detail);
165128
+ } catch {
165129
+ }
165130
+ };
165131
+ const r = await checkMobileUpdate(opts);
165132
+ const from6 = r.currentVersion;
165133
+ const to = r.artifact?.identity || r.targetIdentity || from6;
165134
+ const steps = [];
165135
+ const finish = async (o) => {
165136
+ const out = { ...o, durationMs: Date.now() - started, steps };
165137
+ const hist = [{ at: now, from: out.from, to: out.to, stage: out.stage, reason: out.reason }];
165138
+ const st = await readMobileUpdateState(store);
165139
+ await writeMobileUpdateState({
165140
+ lastUpdate: hist[0],
165141
+ history: [...hist, ...st.history || []].slice(0, 20),
165142
+ lastFailure: out.ok ? null : { at: now, stage: out.failedAt || out.stage, reason: out.reason },
165143
+ needsReload: out.ok ? true : st.needsReload,
165144
+ ...out.ok ? {
165145
+ currentVersion: out.to,
165146
+ installedChannel: r.artifact?.channel || "stable",
165147
+ installedDevSha: devShaFromIdentity(out.to),
165148
+ installedFrom: {
165149
+ channel: r.artifact?.channel || "stable",
165150
+ source: r.artifact?.source || "unknown",
165151
+ url: r.artifact?.url || null,
165152
+ digest: out.digest,
165153
+ digestAlgo: out.digest ? out.digest.split(":")[0] : null,
165154
+ at: now
165155
+ },
165156
+ switchableTo: r.artifact?.channel === "dev" ? { channel: "stable", source: "npm", target: r.sourceFacts?.npm?.latest ?? null } : { channel: "dev", source: "github", target: r.github?.headSha ? r.github.headSha.slice(0, 7) : null },
165157
+ // 从 dev 切回 stable 时, **保留**刚离开的那个 dev 快照 sha: 用户回头想回 dev 时知道上次是哪个
165158
+ ...r.artifact?.channel === "dev" ? { lastDevSha: devShaFromIdentity(out.to), devRef: DEV_REF, devCheckedAt: now } : { lastDevSha: r.installedDevSha || null },
165159
+ autoPrepared: null
165160
+ } : {}
165161
+ }, store);
165162
+ return out;
165163
+ };
165164
+ if (refused(r.status)) {
165165
+ stage("blocked", `\u62D2\u7EDD\u66F4\u65B0 (${r.status})`);
165166
+ return finish({ ok: false, stage: "blocked", status: r.status, from: from6, to, reason: `\u62D2\u7EDD\u66F4\u65B0 (${r.status}): ${r.reason || "\u6E90\u4E0D\u53EF\u8FBE / \u7248\u672C\u4E0D\u5B58\u5728"} \u2014 \u4E00\u4E2A\u5B57\u8282\u90FD\u6CA1\u4E0B\u8F7D, \u4E5F\u6CA1\u52A8 current`, digest: null, probe: null, needsReload: false });
165167
+ }
165168
+ if (!r.artifact) {
165169
+ stage("blocked", "\u6CA1\u6709\u53EF\u6267\u884C\u7684\u66F4\u65B0");
165170
+ return finish({ ok: false, stage: "blocked", status: r.status, from: from6, to, reason: r.status === "up_to_date" ? `\u5DF2\u662F\u6700\u65B0 (${from6})` : `\u6CA1\u6709\u53EF\u6267\u884C\u7684\u66F4\u65B0 (${r.status})`, digest: null, probe: null, needsReload: false });
165171
+ }
165172
+ if (!opts.confirm) {
165173
+ stage("blocked", "\u9700\u8981\u4EBA\u5DE5\u786E\u8BA4");
165174
+ return finish({
165175
+ ok: false,
165176
+ stage: "blocked",
165177
+ status: "update_available",
165178
+ from: from6,
165179
+ to,
165180
+ reason: "human_confirm_required: \u5207\u6362 web \u8D44\u6E90\u5C42\u4F1A\u6362\u6389\u6B63\u5728\u8DD1\u7684\u754C\u9762\u4E0E\u903B\u8F91 \u2014 \u5FC5\u987B\u6709\u4EBA\u5728\u73AF (\u667A\u80FD\u4F53\u53EA\u80FD\u81EA\u52A8\u8DD1\u5230 prepare/verify)",
165181
+ digest: null,
165182
+ probe: null,
165183
+ needsReload: false
165184
+ });
165185
+ }
165186
+ let prep = null;
165187
+ if (opts.reuseStaging) {
165188
+ stage("verifying", "\u590D\u7528 staging \u91CC\u5DF2\u51C6\u5907\u5E76\u9A8C\u8BC1\u8FC7\u7684\u8D44\u6E90");
165189
+ const probe = await verifyWebLayer(store, WEB_LAYOUT.staging, { bootTimeoutMs: opts.bootTimeoutMs });
165190
+ if (probe.ok) {
165191
+ const st = await readMobileUpdateState(store);
165192
+ const identity5 = st.autoPrepared?.identity || to;
165193
+ if (identity5 !== to) {
165194
+ stage("blocked", `staging \u7684\u8EAB\u4EFD ${identity5} \u2260 \u76EE\u6807 ${to}`);
165195
+ return finish({ ok: false, stage: "blocked", status: "web_bundle_malformed", from: from6, to, reason: `staging \u91CC\u51C6\u5907\u7684\u662F ${identity5}, \u4E0E\u672C\u6B21\u76EE\u6807 ${to} \u4E0D\u4E00\u81F4 \u2014 \u62D2\u7EDD\u5207\u6362 (\u5B81\u53EF\u4E0D\u88C5, \u4E0D\u88C5\u9519\u7684)`, digest: null, probe, needsReload: false });
165196
+ }
165197
+ prep = { ok: true, stage: "awaiting_human", status: "update_available", from: from6, to, reason: "\u590D\u7528 staging", steps: st.autoPrepared?.steps || [], fileCount: 0, digest: st.autoPrepared?.digest ?? null, probe, skippedDownload: true };
165198
+ steps.push(...(prep.steps || []).map((s2) => `auto: ${s2}`));
165199
+ }
165200
+ }
165201
+ if (!prep) {
165202
+ const p = await prepareMobileUpdate(opts, r);
165203
+ if (!p.ok) {
165204
+ return finish({ ok: false, stage: p.stage, failedAt: p.failedAt, status: p.status, from: from6, to, reason: p.reason, digest: p.digest, probe: p.probe, needsReload: false });
165205
+ }
165206
+ prep = p;
165207
+ steps.push(...p.steps);
165208
+ }
165209
+ if (!nativeWired(opts)) {
165210
+ stage("blocked", "\u539F\u751F\u58F3\u8FD8\u6CA1\u6307\u5411\u53EF\u5199\u76EE\u5F55 \u2192 \u4E0D\u5207\u6362");
165211
+ steps.push(`native_shell_not_wired: \u8D44\u6E90\u5DF2\u51C6\u5907\u5E76\u9A8C\u8BC1\u8FC7 (${WEB_LAYOUT.staging}), \u4F46\u5F53\u524D\u539F\u751F\u58F3\u4E0D\u4F1A\u52A0\u8F7D\u53EF\u5199\u76EE\u5F55\u91CC\u7684\u8D44\u6E90 \u21D2 \u5207\u6362\u53EA\u4F1A\u628A"\u80FD\u7528\u7684\u65E7\u7248\u672C"\u6362\u6210"\u58F3\u5C42\u52A0\u8F7D\u4E0D\u5230\u7684\u65B0\u7248\u672C" \u2014 \u62D2\u7EDD\u5207\u6362`);
165212
+ return finish({
165213
+ ok: false,
165214
+ stage: "blocked",
165215
+ status: "native_shell_not_wired",
165216
+ from: from6,
165217
+ to,
165218
+ reason: `native_shell_not_wired: \u624B\u673A App \u7684\u539F\u751F\u58F3\u8FD8\u6307\u5411\u5305\u5185\u8D44\u6E90 (\u6CA1\u6709\u6307\u5411\u53EF\u5199\u76EE\u5F55), \u6240\u4EE5\u8FD9\u6B21\u88C5\u5230 ${to} \u4E0D\u4F1A\u751F\u6548 \u2014 \u5DF2\u5728 ${WEB_LAYOUT.staging} \u51C6\u5907\u597D\u5E76\u901A\u8FC7\u542F\u52A8\u9A8C\u8BC1, \u4F46\u6CA1\u6709\u5207\u6362 current\u3002\u4FEE\u6CD5 (\u4E8C\u9009\u4E00, \u89C1 update-protocol \xA713.4): \u2460 \u628A\u58F3\u5C42 server.url \u6307\u5411\u672C\u673A http \u670D\u52A1\u7684\u53EF\u5199\u76EE\u5F55; \u2461 \u7B49\u539F\u751F\u5C42\u6539\u9020\u968F\u5546\u5E97/\u4FA7\u8F7D\u53D1\u4E00\u6B21\u3002` + NATIVE_CEILING_LINE,
165219
+ digest: prep.digest,
165220
+ probe: prep.probe,
165221
+ needsReload: false
165222
+ });
165223
+ }
165224
+ stage("switching", `\u539F\u5B50\u66FF\u6362: ${WEB_LAYOUT.current} \u2192 ${WEB_LAYOUT.previous}, ${WEB_LAYOUT.staging} \u2192 ${WEB_LAYOUT.current}`);
165225
+ const hadCurrent = await store.exists(joinRel(WEB_LAYOUT.current, "mobile.html"));
165226
+ let backupMade = false;
165227
+ try {
165228
+ const injectAt = opts.failSwitch === true ? "before_backup" : opts.failSwitch;
165229
+ if (injectAt === "before_backup") throw Object.assign(new Error("\u6CE8\u5165\u7684\u5207\u6362\u5931\u8D25 (\u5728\u52A8 current \u4E4B\u524D)"), { injected: true });
165230
+ await store.remove(WEB_LAYOUT.previous);
165231
+ if (hadCurrent) {
165232
+ await store.rename(WEB_LAYOUT.current, WEB_LAYOUT.previous);
165233
+ backupMade = true;
165234
+ }
165235
+ if (injectAt === "after_backup") throw Object.assign(new Error("\u6CE8\u5165\u7684\u5207\u6362\u5931\u8D25 (\u5907\u4EFD\u5DF2\u505A, staging \u672A\u4E0A\u4F4D)"), { injected: true });
165236
+ await store.rename(WEB_LAYOUT.staging, WEB_LAYOUT.current);
165237
+ steps.push(`switching: ${WEB_LAYOUT.current} \u5DF2\u66FF\u6362 (\u65E7\u8D44\u6E90\u4FDD\u7559\u5728 ${WEB_LAYOUT.previous})`);
165238
+ } catch (e) {
165239
+ const reason = String(e?.message || e);
165240
+ let rolled = false;
165241
+ try {
165242
+ await store.remove(WEB_LAYOUT.staging);
165243
+ if (backupMade) {
165244
+ await store.remove(WEB_LAYOUT.current);
165245
+ await store.rename(WEB_LAYOUT.previous, WEB_LAYOUT.current);
165246
+ rolled = true;
165247
+ } else if (hadCurrent) {
165248
+ rolled = false;
165249
+ }
165250
+ const probeAfter = rolled || hadCurrent ? await verifyWebLayer(store, WEB_LAYOUT.current, { bootTimeoutMs: opts.bootTimeoutMs }) : null;
165251
+ steps.push(`rolled_back: ${rolled ? "\u5DF2\u628A previous \u642C\u56DE current" : "current \u672A\u88AB\u6539\u52A8"} \xB7 \u542F\u52A8\u9A8C\u8BC1 ${probeAfter ? probeAfter.ok ? "\u901A\u8FC7" : "\u672A\u901A\u8FC7" : "\u8DF3\u8FC7"}`);
165252
+ stage(rolled ? "rolled_back" : "failed", `${reason}; ${rolled ? "\u5DF2\u56DE\u6EDA\u5230\u65E7\u8D44\u6E90" : "\u65E7\u8D44\u6E90\u672A\u88AB\u6539\u52A8"}`);
165253
+ return finish({
165254
+ ok: false,
165255
+ stage: rolled ? "rolled_back" : "failed",
165256
+ failedAt: "switching",
165257
+ status: "update_available",
165258
+ from: from6,
165259
+ to,
165260
+ reason: `${reason}; ${rolled ? `\u5DF2\u56DE\u6EDA\u5230 ${from6} (\u65E7\u8D44\u6E90\u56DE\u5230 current \u4E14\u9A8C\u8BC1${probeAfter?.ok ? "\u901A\u8FC7" : "\u672A\u901A\u8FC7"})` : "current \u672A\u88AB\u6539\u52A8, \u65E7\u8D44\u6E90\u4ECD\u5728"}`,
165261
+ digest: prep.digest,
165262
+ probe: probeAfter,
165263
+ needsReload: false
165264
+ });
165265
+ } catch (e2) {
165266
+ stage("failed", `${reason}; \u56DE\u6EDA\u8FC7\u7A0B\u51FA\u9519: ${e2?.message || e2}`);
165267
+ return finish({
165268
+ ok: false,
165269
+ stage: "failed",
165270
+ failedAt: "switching",
165271
+ status: "update_available",
165272
+ from: from6,
165273
+ to,
165274
+ reason: `${reason}; \u56DE\u6EDA\u8FC7\u7A0B\u51FA\u9519: ${e2?.message || e2}`,
165275
+ digest: prep.digest,
165276
+ probe: null,
165277
+ needsReload: false
165278
+ });
165279
+ }
165280
+ }
165281
+ stage("verifying", "\u5207\u6362\u540E\u518D\u9A8C\u4E00\u6B21 (\u80FD\u4E0D\u80FD\u542F\u52A8)");
165282
+ const after = await verifyWebLayer(store, WEB_LAYOUT.current, { bootTimeoutMs: opts.bootTimeoutMs });
165283
+ steps.push(`post-switch verify: ${after.mode} \u2014 ${after.detail}`);
165284
+ if (!after.ok) {
165285
+ let rolled = false;
165286
+ try {
165287
+ await store.remove(WEB_LAYOUT.current);
165288
+ if (backupMade) {
165289
+ await store.rename(WEB_LAYOUT.previous, WEB_LAYOUT.current);
165290
+ rolled = true;
165291
+ }
165292
+ stage("rolled_back", `\u5207\u6362\u540E\u9A8C\u8BC1\u5931\u8D25, ${rolled ? "\u5DF2\u56DE\u6EDA" : "\u6CA1\u6709\u53EF\u56DE\u6EDA\u7684\u65E7\u8D44\u6E90"}`);
165293
+ } catch {
165294
+ }
165295
+ return finish({
165296
+ ok: false,
165297
+ stage: rolled ? "rolled_back" : "failed",
165298
+ failedAt: "verifying",
165299
+ status: "update_available",
165300
+ from: from6,
165301
+ to,
165302
+ reason: `\u5207\u6362\u540E\u9A8C\u8BC1\u5931\u8D25: ${after.detail}; ${rolled ? `\u5DF2\u56DE\u6EDA\u5230 ${from6}` : "\u56DE\u6EDA\u5931\u8D25 \u2014 \u9700\u8981\u91CD\u88C5 App"}`,
165303
+ digest: prep.digest,
165304
+ probe: after,
165305
+ needsReload: false
165306
+ });
165307
+ }
165308
+ stage("succeeded", `\u5DF2\u5207\u5230 ${to}`);
165309
+ return finish({ ok: true, stage: "succeeded", status: "update_available", from: from6, to, reason: `\u5DF2\u66F4\u65B0\u5230 ${to} (\u91CD\u542F/\u91CD\u8F7D webview \u540E\u751F\u6548)`, digest: prep.digest, probe: after, needsReload: true });
163255
165310
  }
163256
- function buildPhoneCard(view, opts) {
163257
- return {
163258
- title: "\u8BA9 Bolloon \u4EE3\u8868\u4F60\u8054\u7CFB\u5916\u90E8\u7684\u4EBA",
163259
- effective: view?.effective || opts.offline || "\u672A\u77E5",
163260
- contacts: (view?.contacts || []).map((c) => ({
163261
- displayValue: c.displayValue || "",
163262
- kind: c.kind || "",
163263
- state: c.status || c.verificationStatus || "",
163264
- label: c.capabilities?.includes("send") ? "\u53EF\u8054\u7CFB" : "\u4E0D\u53EF\u8054\u7CFB"
163265
- })),
163266
- approvals: (view?.approvals || []).map((a) => ({
163267
- consentId: a.consentId,
163268
- contactName: a.contactName,
163269
- channel: a.channel,
163270
- subject: a.subject,
163271
- bodyPreview: a.bodyPreview || "",
163272
- reason: a.reason,
163273
- reallySent: !!a.reallySent
163274
- })),
163275
- choices: [
163276
- { id: "task_once", label: "\u4EC5\u672C\u6B21\u4EFB\u52A1" },
163277
- { id: "persistent", label: "\u957F\u671F\u4F7F\u7528 (\u63A8\u8350)" },
163278
- { id: "full_contact_access", label: "\u5B8C\u5168\u6388\u6743\u8054\u7CFB\u65B9\u5F0F\u80FD\u529B" }
163279
- ],
163280
- willNotGet: ["\u8BFB\u53D6\u5168\u90E8\u90AE\u7BB1", "\u8BFB\u53D6\u901A\u8BAF\u5F55", "\u7FA4\u53D1\u6D88\u606F", "\u81EA\u52A8\u652F\u4ED8/\u8F6C\u8D26", "\u4EE3\u8868\u4F60\u7B7E\u7F72\u5408\u540C", "\u62FF\u5230\u624B\u673A\u53F7/\u90AE\u7BB1\u660E\u6587"],
163281
- deviceSigning: opts.deviceSigning
165311
+ async function rollbackMobileUpdate(opts) {
165312
+ const store = opts.store;
165313
+ const started = Date.now();
165314
+ const now = new Date(opts.now ? opts.now() : Date.now()).toISOString();
165315
+ const st = await readMobileUpdateState(store);
165316
+ const from6 = st.currentVersion || "unknown";
165317
+ const steps = [];
165318
+ const finish = async (o) => {
165319
+ const out = { ...o, durationMs: Date.now() - started, steps };
165320
+ await writeMobileUpdateState({
165321
+ lastUpdate: { at: now, from: out.from, to: out.to, stage: out.stage, reason: out.reason },
165322
+ history: [{ at: now, from: out.from, to: out.to, stage: out.stage, reason: out.reason }, ...st.history || []].slice(0, 20)
165323
+ }, store);
165324
+ return out;
163282
165325
  };
165326
+ if (!opts.confirm) {
165327
+ return finish({ ok: false, stage: "blocked", status: "update_available", from: from6, to: from6, reason: "human_confirm_required: \u56DE\u6EDA\u4F1A\u6362\u6389\u6B63\u5728\u8DD1\u7684 web \u8D44\u6E90 \u2014 \u5FC5\u987B\u6709\u4EBA\u5728\u73AF", digest: null, probe: null, needsReload: false });
165328
+ }
165329
+ if (!await store.exists(joinRel(WEB_LAYOUT.previous, "mobile.html"))) {
165330
+ return finish({ ok: false, stage: "blocked", status: "web_bundle_malformed", from: from6, to: from6, reason: `\u6CA1\u6709\u53EF\u56DE\u6EDA\u7684\u65E7\u8D44\u6E90 (${WEB_LAYOUT.previous} \u7A7A) \u2014 \u4E0D\u5047\u88C5\u56DE\u6EDA\u8FC7`, digest: null, probe: null, needsReload: false });
165331
+ }
165332
+ const probeBefore = await verifyWebLayer(store, WEB_LAYOUT.previous, { bootTimeoutMs: opts.bootTimeoutMs });
165333
+ steps.push(`previous \u63A2\u6D4B: ${probeBefore.detail}`);
165334
+ if (!probeBefore.ok) {
165335
+ return finish({ ok: false, stage: "blocked", status: "web_bundle_malformed", from: from6, to: from6, reason: `previous \u91CC\u7684\u65E7\u8D44\u6E90\u8D77\u4E0D\u6765 (${probeBefore.detail}) \u2014 \u56DE\u6EDA\u5B83\u53EA\u4F1A\u66F4\u7CDF, \u62D2\u7EDD`, digest: null, probe: probeBefore, needsReload: false });
165336
+ }
165337
+ try {
165338
+ await store.remove(joinRel(WEB_LAYOUT.current, ""));
165339
+ await store.rename(WEB_LAYOUT.previous, WEB_LAYOUT.current);
165340
+ } catch (e) {
165341
+ return finish({ ok: false, stage: "failed", failedAt: "switching", status: "update_available", from: from6, to: from6, reason: `\u56DE\u6EDA\u5931\u8D25: ${e?.message || e}`, digest: null, probe: null, needsReload: false });
165342
+ }
165343
+ const after = await verifyWebLayer(store, WEB_LAYOUT.current, { bootTimeoutMs: opts.bootTimeoutMs });
165344
+ steps.push(`\u56DE\u6EDA\u540E\u63A2\u6D4B: ${after.detail}`);
165345
+ return finish({
165346
+ ok: after.ok,
165347
+ stage: after.ok ? "rolled_back" : "failed",
165348
+ status: "update_available",
165349
+ from: from6,
165350
+ to: from6,
165351
+ reason: after.ok ? `\u5DF2\u56DE\u6EDA\u5230\u4E0A\u4E00\u4E2A\u8D44\u6E90\u5C42 (\u9700\u8981\u91CD\u8F7D webview \u751F\u6548)` : `\u56DE\u6EDA\u540E\u8D44\u6E90\u8D77\u4E0D\u6765: ${after.detail}`,
165352
+ digest: null,
165353
+ probe: after,
165354
+ needsReload: after.ok
165355
+ });
165356
+ }
165357
+ var MOBILE_UPDATE_AGENT_CONTRACT = {
165358
+ schema: "bolloon-mobile-agent-update/1",
165359
+ /** 可以自动做 (只读 + 只在 staging 里写, 不影响正在跑的资源) */
165360
+ autoAllowedSteps: ["check", "prepare", "download", "digest-verify", "extract", "stage", "verify"],
165361
+ /** 必须人在环 (会换掉正在跑的界面与逻辑 ⇒ 代价不对称) */
165362
+ humanRequiredSteps: ["switch", "reload", "rollback", "channel-switch"],
165363
+ preconditions: [
165364
+ "\u672C\u673A web \u5C42\u7684\u8EAB\u4EFD\u53EF\u8BFB (\u58F3\u5C42\u6CE8\u5165 / OTA state / \u6784\u5EFA\u6233 bolloon-web.json \u4E09\u8005\u4E4B\u4E00) \u2014\u2014 \u8BFB\u4E0D\u5230\u5C31 local_version_unknown, \u4E0D\u731C",
165365
+ "\u539F\u751F\u58F3\u5DF2\u6307\u5411\u53EF\u5199\u76EE\u5F55 (native_writable) \u2014\u2014 \u5426\u5219\u8D44\u6E90\u51C6\u5907\u597D\u4E86\u4E5F\u4E0D\u4F1A\u751F\u6548, \u4E00\u5F8B native_shell_not_wired \u5E76\u8BF4\u6E05\u4FEE\u6CD5",
165366
+ "\u8BBE\u5907\u5B58\u50A8\u4F59\u91CF\u591F (\u4E0B\u8F7D\u7269 \u2264 \u4E0A\u9650, \u9ED8\u8BA4 80 MiB) \u2014\u2014 \u8D85\u4E86\u5224 payload_too_large, \u4E0D\u8D4C\u5185\u5B58",
165367
+ "\u672C\u673A WebView \u6709 DecompressionStream('gzip') (iOS 16.4+ / Chrome 80+) \u2014\u2014 \u6CA1\u6709\u5C31 decompress_unavailable",
165368
+ '\u7A33\u5B9A\u7F51\u7EDC: \u6E90\u4E0D\u53EF\u8FBE\u4E00\u5F8B offline / registry_unavailable / github_unavailable(reason) \u62D2\u7EDD, \u4E0D\u9759\u9ED8\u91CD\u8BD5\u5230"\u770B\u8D77\u6765\u6210\u529F"',
165369
+ "\u6458\u8981\u53EF\u6821\u9A8C: npm \u4FA7\u7528 dist.shasum/dist.integrity; dev \u4FA7\u7528\u5305\u5185 .bolloon-dev-snapshot.json \u7684 sha + bundleSha256"
165370
+ ],
165371
+ riskBoundaries: [
165372
+ 'iOS \u539F\u751F\u58F3\u5C42**\u4E0D\u80FD\u81EA\u66F4** (App Store \u89C4\u5219) \u21D2 \u4E0D\u627F\u8BFA"iOS \u6574\u4F53\u81EA\u52A8\u66F4\u65B0"; web \u5C42\u80FD\u66F4, \u4E8C\u8FDB\u5236\u8D70\u5546\u5E97/TestFlight',
165373
+ "Android \u4FA7\u8F7D APK \u81EA\u66F4**\u9700\u7528\u6237\u5141\u8BB8\u672A\u77E5\u6765\u6E90** \u21D2 \u5FC5\u987B\u663E\u5F0F\u544A\u77E5 + \u7528\u6237\u5728\u73AF, \u672C\u8F6E\u4E0D\u505A\u9759\u9ED8\u88C5\u5305",
165374
+ '\u5207\u6362\u524D\u5FC5\u987B\u771F\u8DD1\u4E00\u6B21"\u80FD\u4E0D\u80FD\u542F\u52A8"\u63A2\u6D4B (\u7ED3\u6784+\u8BED\u6CD5+iframe), \u63A2\u6D4B\u4E0D\u901A\u8FC7\u4E0D\u8BB8\u5207\u8FDB current',
165375
+ '\u5207\u6362\u5FC5\u987B\u539F\u5B50 (rename) \u4E14\u4FDD\u7559 previous; \u4EFB\u4F55\u4E00\u6B65\u5931\u8D25\u90FD\u8981\u56DE\u5230"\u80FD\u542F\u52A8\u7684\u65E7\u8D44\u6E90"',
165376
+ "dev \u901A\u9053\u4E0D\u4FDD\u8BC1\u53EF\u56DE\u6EDA\u5230\u4E0A\u4E00\u4E2A dev \u7248 (\u4E0E\u684C\u9762\u540C\u4E00\u53E5\u8B66\u544A); \u4E00\u952E\u56DE stable \u59CB\u7EC8\u53EF\u7528",
165377
+ "\u6E90\u4E0D\u53EF\u8FBE/\u7248\u672C\u4E0D\u5B58\u5728/\u4EA4\u53C9\u6821\u9A8C\u4E0D\u4E00\u81F4/\u6458\u8981\u4E0D\u7B26 \u21D2 \u62D2\u7EDD\u5E76\u5206\u7C7B\u8BF4\u6E05, \u7EDD\u4E0D\u9759\u9ED8\u88C5\u56DE\u65E7\u7248\u3001\u7EDD\u4E0D\u5047\u88C5\u6210\u529F"
165378
+ ]
165379
+ };
165380
+ async function autoPrepareMobileUpdate(deps) {
165381
+ return prepareMobileUpdate(deps);
165382
+ }
165383
+ function renderMobileUpdateReport(r, st) {
165384
+ const L2 = [];
165385
+ L2.push(r.identityLine);
165386
+ L2.push(`\u66F4\u65B0\u5C42: web \u8D44\u6E90\u5C42 (\u754C\u9762\u4E0E\u903B\u8F91: \u5165\u7FA4 / \u53D1\u4EFB\u52A1\u516C\u544A / \u770B\u98DE\u8F6E\u8FDB\u5EA6 / \u6388\u6743\u7B7E\u540D) \xB7 store=${r.storeKind}`);
165387
+ L2.push(`\u80FD\u5207\u56DE: ${r.switchableTo ? `${r.switchableTo.channel} (${r.switchableTo.source}${r.switchableTo.target ? ` @ ${r.switchableTo.target}` : ""})` : "\u65E0 (\u672A\u77E5\u6765\u6E90)"}`);
165388
+ L2.push(`\u76EE\u6807\u7248\u672C: ${r.targetIdentity || r.latestVersion || "\u672A\u77E5"}`);
165389
+ L2.push(`\u66F4\u65B0\u901A\u9053: ${r.channel} (\u6BD4\u8F83\u8BED\u4E49: ${r.channelKind})`);
165390
+ L2.push(renderGithubReportLine(r.github));
165391
+ if (r.crossCheck) L2.push(`\u4EA4\u53C9\u6821\u9A8C: ${r.crossCheck.kind}${r.crossCheck.blocking ? " (\u963B\u585E)" : ""} \u2014 ${r.crossCheck.detail}`);
165392
+ if (r.dev) L2.push(`dev \u5224\u5B9A: ${r.dev.detail}`);
165393
+ if (r.sourceFacts?.npm) L2.push(`npm \u6E90: ${r.sourceFacts.npm.reachable ? `\u53EF\u8FBE (latest=${r.sourceFacts.npm.latest || "\u672A\u77E5"})` : `\u4E0D\u53EF\u8FBE (${r.sourceFacts.npm.detail || "\u672A\u77E5"})`}`);
165394
+ if (st?.installedFrom) L2.push(`\u5F53\u524D\u6765\u6E90: ${st.installedFrom.channel} \xB7 ${st.installedFrom.source}${st.installedFrom.digest ? ` \xB7 \u6458\u8981 ${st.installedFrom.digest}` : ""}${st.installedFrom.at ? ` \xB7 ${st.installedFrom.at}` : ""}`);
165395
+ if (st?.lastDevSha && r.installedChannel !== "dev") L2.push(`\u4E0A\u6B21 dev: commit ${st.lastDevSha} (\u5DF2\u5207\u56DE stable)`);
165396
+ L2.push(`\u7ED3\u8BBA: ${r.status}${refused(r.status) ? " \u2014 \u62D2\u7EDD (\u6CA1\u6709\u5B89\u88C5\u4EFB\u4F55\u4E1C\u897F)" : ""}`);
165397
+ if (r.reason) L2.push(`\u8BF4\u660E: ${r.reason}`);
165398
+ for (const w2 of r.warnings) L2.push(w2);
165399
+ return L2;
165400
+ }
165401
+ var NATIVE_CEILING_LINE = '\u539F\u751F\u58F3\u5C42\u4E0D\u80FD\u5728\u8FD9\u91CC\u81EA\u66F4: iOS \u53D7 App Store \u89C4\u5219\u9650\u5236 (\u4E8C\u8FDB\u5236\u8D70\u5546\u5E97/TestFlight), Android \u4FA7\u8F7D APK \u81EA\u66F4\u9700\u8981\u4F60\u5141\u8BB8"\u672A\u77E5\u6765\u6E90" \u2014\u2014 \u8FD9\u91CC\u80FD\u81EA\u52A8\u66F4\u65B0\u7684\u662F web \u8D44\u6E90\u5C42 (\u754C\u9762\u4E0E\u903B\u8F91)\u3002';
165402
+ var MOBILE_CHANNEL_KEY = "bolloon.update.channel";
165403
+ function createAutoWebStore() {
165404
+ const injected = globalThis.__bolloonWebStore;
165405
+ if (injected && typeof injected.writeBytes === "function" && typeof injected.rename === "function") return injected;
165406
+ const cap = globalThis.Capacitor;
165407
+ const fs = cap?.Plugins?.Filesystem;
165408
+ if (fs && typeof fs.writeFile === "function") {
165409
+ const base6 = globalThis.__bolloonWebBaseUrl || null;
165410
+ return createCapacitorWebStore(fs, { basePath: "bolloon-web", directory: "DATA", baseUrl: base6 });
165411
+ }
165412
+ return createMemoryWebStore({ kind: "memory", rootLabel: "memory://webview", baseUrl: globalThis.__bolloonWebBaseUrl || null });
165413
+ }
165414
+ function storedMobileChannel() {
165415
+ try {
165416
+ const v = globalThis.localStorage?.getItem?.(MOBILE_CHANNEL_KEY);
165417
+ return v ? String(v) : null;
165418
+ } catch {
165419
+ return null;
165420
+ }
165421
+ }
165422
+ async function setMobileUpdateChannel(channel, store) {
165423
+ const want = String(channel || "").toLowerCase();
165424
+ if (want !== "stable" && want !== "dev") {
165425
+ const cur = storedMobileChannel() || "stable";
165426
+ return { ok: false, channel: cur, detail: `\u672A\u77E5\u901A\u9053 "${channel}" (\u53EA\u6709 stable / dev) \u2014 \u4FDD\u6301 ${cur}` };
165427
+ }
165428
+ const prev = storedMobileChannel();
165429
+ try {
165430
+ globalThis.localStorage?.setItem?.(MOBILE_CHANNEL_KEY, want);
165431
+ } catch {
165432
+ }
165433
+ if (store) await writeMobileUpdateState({ channelPref: want }, store);
165434
+ return { ok: true, channel: want, detail: `\u901A\u9053 ${prev || "stable"} \u2192 ${want} (\u53EA\u6539\u504F\u597D, \u8FD8\u6CA1\u6709\u5B89\u88C5\u4EFB\u4F55\u4E1C\u897F; \u70B9\u300C\u68C0\u67E5\u66F4\u65B0\u300D\u518D\u51B3\u5B9A\u88C5\u4E0D\u88C5)` };
165435
+ }
165436
+ async function reloadMobileWeb() {
165437
+ const cap = globalThis.Capacitor;
165438
+ try {
165439
+ if (cap?.Plugins?.WebView?.reload) {
165440
+ await cap.Plugins.WebView.reload();
165441
+ return { ok: true, how: "capacitor-webview", detail: "\u5DF2\u8BF7\u6C42 WebView \u91CD\u8F7D (\u65B0 web \u8D44\u6E90\u751F\u6548)" };
165442
+ }
165443
+ } catch (e) {
165444
+ return { ok: false, how: "capacitor-webview", detail: `WebView \u91CD\u8F7D\u5931\u8D25: ${String(e?.message || e)} \u2014 \u8BF7\u624B\u52A8\u91CD\u542F App` };
165445
+ }
165446
+ const loc = globalThis.location;
165447
+ if (loc && typeof loc.reload === "function") {
165448
+ loc.reload();
165449
+ return { ok: true, how: "location.reload", detail: "\u5DF2\u91CD\u8F7D\u9875\u9762" };
165450
+ }
165451
+ return { ok: false, how: "none", detail: "\u672C\u73AF\u5883\u6CA1\u6709\u53EF\u91CD\u8F7D\u7684 WebView (\u9A8C\u6536/Node) \u2014 \u672A\u5047\u88C5\u91CD\u8F7D" };
163283
165452
  }
163284
165453
 
163285
165454
  // src/web/mobile-core.ts
165455
+ var __webStore = null;
165456
+ function currentWebStore() {
165457
+ if (!__webStore) __webStore = createAutoWebStore();
165458
+ return __webStore;
165459
+ }
165460
+ function injectedWebIdentity() {
165461
+ const v = globalThis.__bolloonWebIdentity;
165462
+ if (v && typeof v === "object" && typeof v.version === "string" && v.version) {
165463
+ return { version: v.version, channel: v.channel === "dev" ? "dev" : v.channel === "stable" ? "stable" : void 0 };
165464
+ }
165465
+ return null;
165466
+ }
165467
+ function mobileUpdateDeps(opts = {}) {
165468
+ return {
165469
+ // 透传: 壳层/验收可以覆盖 registryBase / apiBase / devBundleUrl / devBundleSha256 / fetchImpl …
165470
+ // (只有 store / 通道 / 身份 / 壳层判据由本层接管 —— 那几项是"设备说了算"的)
165471
+ ...opts,
165472
+ store: currentWebStore(),
165473
+ channel: opts.channel || storedMobileChannel() || void 0,
165474
+ localIdentity: opts.localIdentity !== void 0 ? opts.localIdentity : injectedWebIdentity(),
165475
+ nativeWritable: opts.nativeWritable !== void 0 ? opts.nativeWritable : void 0,
165476
+ onStage: opts.onStage
165477
+ };
165478
+ }
163286
165479
  var busHandlers = /* @__PURE__ */ new Set();
163287
165480
  function busBroadcast(msg) {
163288
165481
  for (const h3 of busHandlers) h3(msg);
@@ -164259,6 +166452,134 @@ ${b}
164259
166452
  return snap && Array.isArray(snap.skills) ? snap.skills : [];
164260
166453
  }
164261
166454
  },
166455
+ /**
166456
+ * 任务协作 (2026-09-25) — 手机与桌面/CLI 共用同一套存储与协议:
166457
+ * 入群 (`task group …`) · 发公告 (`task publish/announce/post`) · 看飞轮进度 (goal-flywheel 冻结类型, 只读)
166458
+ * 四个高风险动作 (入群/退群/建群/发公告/留痕) 一律**先签设备名再由桌面验签执行** —— 手机不自己执行、不记账。
166459
+ * 桌面不可达 → 明确失败 (任务动作**不进离线队列**: 几小时后突然生效的批准比失败更危险)。
166460
+ */
166461
+ tasks: {
166462
+ /** 本机能不能签 (老 WebView 没 Ed25519 → 高风险动作一律拒, 不降级) */
166463
+ deviceSigning() {
166464
+ return ed25519Available();
166465
+ },
166466
+ /** 桌面基址 (拿不到 → null, 不猜) */
166467
+ async desktopBase() {
166468
+ return core.contacts.desktopBase();
166469
+ },
166470
+ deps() {
166471
+ return { storage: core.contacts.storage(), ownerDid: void 0, grantedBy: "leo" };
166472
+ },
166473
+ /** ① 群: 列本机已加入的群 (脱敏, 只给缩短 id/群名/时间) */
166474
+ listGroups: () => listGroups(core.tasks.deps()),
166475
+ /** ① 入群 (高风险: 展示待发内容 → 设备签名 → 桌面验签执行) */
166476
+ joinGroup: (link) => joinGroup(link, core.tasks.deps()),
166477
+ /** ① 退群 (高风险) */
166478
+ leaveGroup: (ref) => leaveGroup(ref, core.tasks.deps()),
166479
+ /** ① 建群 (高风险; 建完自动入群) */
166480
+ createGroup: (name10) => createGroup(name10, core.tasks.deps()),
166481
+ /** ② 看板 (只读; 与 CLI `task board` 同一份事实) */
166482
+ loadBoard: () => loadBoard(core.tasks.deps()),
166483
+ /** ② 发布公告 (高风险) */
166484
+ publishAnnouncement: (input) => publishAnnouncement(input, core.tasks.deps()),
166485
+ /** ② 把公告发进群 (高风险; 预览 → 确认 → 签名) */
166486
+ announceIntoGroup: (input) => announceIntoGroup(input, core.tasks.deps()),
166487
+ /** ② 过程留痕 (高风险: claim/deliver/screen/final) */
166488
+ postTrail: (input) => postTrail(input, core.tasks.deps()),
166489
+ /** ② 读群里的一期痕迹 (只读) */
166490
+ loadTrail: (input) => loadTrail(input, core.tasks.deps()),
166491
+ /** 预览群消息那一行 (由桌面同一个构造器生成) */
166492
+ previewGroupMessage: (req) => previewGroupMessage(req, core.tasks.deps()),
166493
+ /** ③ 飞轮进度 (只读消费 goal-flywheel 冻结类型; 投影在桌面做) */
166494
+ loadFlywheel: () => loadFlywheel(core.tasks.deps()),
166495
+ /** 确认页文案 (展示待发内容) */
166496
+ describeConfirm: (kind, req) => describeTaskActionForConfirm(kind, req),
166497
+ /** 直接执行一个已构造好的请求 (UI 的统一入口: 签名 → 桌面验签执行) */
166498
+ execute: (req) => executeTaskAction(req, core.tasks.deps()),
166499
+ /** 相对时间人话 (毫秒差 → 双语; 只用于写文字节点) */
166500
+ relative: (ms2, lang) => formatRelative(ms2, lang === "en" ? "en" : "zh"),
166501
+ /** id 缩短 (页面上不出现原文) */
166502
+ shorten: (id, n) => shortId(id, n),
166503
+ /** 公开文本红线 (UI 最后一道自检: 渲染前扫一遍, 命中就不显示原文) */
166504
+ scanText: (text) => scanPublicText(String(text ?? "")),
166505
+ /** 构造一个空请求 (UI 填字段用) */
166506
+ emptyRequest: (kind) => emptyTaskActionRequest(kind),
166507
+ /** 最近一条操作提示 */
166508
+ hint() {
166509
+ return getTaskHint(core.contacts.storage());
166510
+ },
166511
+ setHint(text) {
166512
+ setTaskHint(text, core.contacts.storage());
166513
+ }
166514
+ },
166515
+ /**
166516
+ * web 资源层双源 OTA 更新 (2026-09-26, update-protocol §13) — 与桌面 `bolloon update` 同语义:
166517
+ * ① 身份: stable = npm dist-tag 权威 + GitHub Tag 交叉校验; dev = master `ref` + commit sha, 身份 `<版本>+dev.<sha7>`
166518
+ * ② 拒绝: 源不可达 / 版本不存在 / 交叉校验不一致 ⇒ **拒绝并说清分类**, 绝不静默装回旧版、绝不假装成功
166519
+ * ③ 可见: 当前装的是哪个源 / 哪个 sha / 能切回哪个源 (与桌面同措辞)
166520
+ * ④ 智能体: check/plan/download/verify 可程序调用; **switch / reload / rollback / 切通道必须人确认**
166521
+ *
166522
+ * 天花板 (不许承诺做不到的): iOS 原生壳层**不能自更** (App Store 规则) —— 自更的只是 web 资源层;
166523
+ * 二进制要走商店 / TestFlight。Android 侧载 APK 可自更但要用户允许未知来源 (显式告知 + 人在环)。
166524
+ */
166525
+ update: {
166526
+ /** 结论/阶段枚举与拒绝表 (给 UI 与智能体做判断, 不用猜字符串) */
166527
+ vocabulary() {
166528
+ return {
166529
+ statuses: MOBILE_WEB_STATUSES.slice(),
166530
+ refused: MOBILE_REFUSED_STATUSES.slice(),
166531
+ requiredFiles: MOBILE_WEB_REQUIRED_FILES.slice(),
166532
+ layout: { ...WEB_LAYOUT },
166533
+ nativeCeiling: NATIVE_CEILING_LINE,
166534
+ agentContract: MOBILE_UPDATE_AGENT_CONTRACT
166535
+ };
166536
+ },
166537
+ /** 可写 web 资源目录接上没有 (真机: 原生壳要指向它才生效) */
166538
+ nativeWired() {
166539
+ return nativeWired(mobileUpdateDeps());
166540
+ },
166541
+ /** 当前 web 层的身份/来源/能切回谁 (读 state + 壳层注入的构建戳) */
166542
+ async state() {
166543
+ return readMobileUpdateState(currentWebStore());
166544
+ },
166545
+ async identity() {
166546
+ return resolveLocalWebIdentity(mobileUpdateDeps());
166547
+ },
166548
+ /** 检查两源 (只读, 不下载任何东西) */
166549
+ async check(opts = {}) {
166550
+ return checkMobileUpdate(mobileUpdateDeps(opts));
166551
+ },
166552
+ /** 检查 + 说明 (报告行: 与桌面 `update --status` 同口径) */
166553
+ async report(opts = {}) {
166554
+ const r = await checkMobileUpdate(mobileUpdateDeps(opts));
166555
+ const st = await readMobileUpdateState(currentWebStore());
166556
+ return { result: r, lines: renderMobileUpdateReport(r, st) };
166557
+ },
166558
+ /** 下载 → 解压 → 落 staging → 验证可启动; **不切换** (智能体能自动跑到这一步) */
166559
+ async prepare(opts = {}) {
166560
+ return autoPrepareMobileUpdate(mobileUpdateDeps(opts));
166561
+ },
166562
+ /** 切换 (人在环: 必须 confirm) → 原子替换 + 失败回滚; 成功后提示重载 */
166563
+ async apply(opts = {}) {
166564
+ return applyMobileUpdate({ ...mobileUpdateDeps(opts), confirm: opts.confirm === true, reuseStaging: opts.reuseStaging === true });
166565
+ },
166566
+ /** 回滚到 previous (人在环) */
166567
+ async rollback(opts = {}) {
166568
+ return rollbackMobileUpdate({ ...mobileUpdateDeps(opts), confirm: opts.confirm === true });
166569
+ },
166570
+ /** 切换通道 (stable ↔ dev; 只写偏好, 不装东西) */
166571
+ async setChannel(channel) {
166572
+ return setMobileUpdateChannel(channel, currentWebStore());
166573
+ },
166574
+ /** 当前通道偏好 */
166575
+ channel() {
166576
+ return storedMobileChannel() || "stable";
166577
+ },
166578
+ /** 重载 web 层让新资源生效 (人在环) */
166579
+ async reload() {
166580
+ return reloadMobileWeb();
166581
+ }
166582
+ },
164262
166583
  message: {
164263
166584
  async send({ text, channelId }) {
164264
166585
  if (!text || !channelId) return { ok: false, error: "text \u548C channelId \u5FC5\u586B" };