@fased/fased 0.1.66 → 0.1.68

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 (175) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +4 -0
  3. package/config/signer-protocol-v2.json +4 -0
  4. package/dist/brand.js +1 -1
  5. package/dist/build-info.json +3 -3
  6. package/dist/canvas-host/a2ui/.bundle.hash +1 -1
  7. package/dist/cli/program/register.wallet.js +33 -2
  8. package/dist/commands/onboard.js +0 -8
  9. package/dist/commands/wallet.js +485 -58
  10. package/dist/control-ui/assets/{app-DYxADzeH.js → app-DyPTS0oM.js} +1526 -1526
  11. package/dist/control-ui/assets/{index-vw_10Zeo.js → index-Cu13yYHr.js} +3 -3
  12. package/dist/control-ui/assets/{wallet-DvStR_AT.js → wallet-ByKBcYhm.js} +272 -188
  13. package/dist/control-ui/index.html +1 -1
  14. package/dist/control-ui/version.json +1 -1
  15. package/dist/gateway/server-http.js +349 -47
  16. package/dist/infra/runtime-guard.js +16 -0
  17. package/dist/plugin-sdk/{actions-DRu135h0.js → actions-B-Vwj64K.js} +3 -3
  18. package/dist/plugin-sdk/{allow-list-DyAxs4yw.js → allow-list-Bf45HfoP.js} +1 -1
  19. package/dist/plugin-sdk/{audio-preflight-BFLoas5R.js → audio-preflight-_FVQjR1r.js} +26 -25
  20. package/dist/plugin-sdk/{audit-C5d2z5U1.js → audit-CUULWG22.js} +1 -1
  21. package/dist/plugin-sdk/{channel-access-configure-DaI5wevh.js → channel-access-configure-BoUsC-Ys.js} +1 -1
  22. package/dist/plugin-sdk/{channel-adapters.discord-BS4S4Svh.js → channel-adapters.discord-DSiR4X2Y.js} +1 -1
  23. package/dist/plugin-sdk/channel-plugin-common.d.ts +2 -2
  24. package/dist/plugin-sdk/channel-plugin-common.js +1 -1
  25. package/dist/plugin-sdk/{chunk-CyoyqpbG.js → chunk-CBpl-AiQ.js} +1 -1
  26. package/dist/plugin-sdk/{chunk-items-TErs62MC.js → chunk-items-LH4uKSnh.js} +1 -1
  27. package/dist/plugin-sdk/command-status.js +26 -25
  28. package/dist/plugin-sdk/{commands-registry-BfqdEWdF.js → commands-registry-G5KXSi_y.js} +1 -1
  29. package/dist/plugin-sdk/{config-schema-BPnaDBwZ.js → config-schema-DjU_uD-6.js} +192 -94
  30. package/dist/plugin-sdk/device-pair.d.ts +1 -1
  31. package/dist/plugin-sdk/{diagnostic-DLL_zekv.js → diagnostic-sYs6Agr-.js} +1 -1
  32. package/dist/plugin-sdk/{directory-live-CSuYxdwk.js → directory-live-BXMpuYAF.js} +1 -1
  33. package/dist/plugin-sdk/{directory-live-Dw7ZgC9_.js → directory-live-Rn1BzPof.js} +1 -1
  34. package/dist/plugin-sdk/discord.d.ts +4 -4
  35. package/dist/plugin-sdk/discord.js +20 -20
  36. package/dist/plugin-sdk/{draft-stream-controls-BqchW1vi.js → draft-stream-controls-CMNdKSx-.js} +1 -1
  37. package/dist/plugin-sdk/{guilds-CDhRIX57.js → guilds-Dkwl0yHv.js} +1 -1
  38. package/dist/plugin-sdk/{handle-action-hvwapMKg.js → handle-action-CRdM6B-8.js} +15 -15
  39. package/dist/plugin-sdk/index.d.ts +16 -16
  40. package/dist/plugin-sdk/index.js +28 -27
  41. package/dist/plugin-sdk/{login-Ca583Okc.js → login-D7WnK8-P.js} +1 -1
  42. package/dist/plugin-sdk/{login-qr-CGciBSNP.js → login-qr-CRueOKZK.js} +2 -114
  43. package/dist/plugin-sdk/{markdown-tables-DCOXEUfZ.js → markdown-tables-d7GlUs5R.js} +1 -1
  44. package/dist/plugin-sdk/{media-CAdQF5TG.js → media-BAoaxCrt.js} +1 -1
  45. package/dist/plugin-sdk/{monitor-FuBPk-AL.js → monitor-DUMMbG4h.js} +34 -33
  46. package/dist/plugin-sdk/{monitor-sW6dQdX1.js → monitor-DXIM16Hj.js} +44 -43
  47. package/dist/plugin-sdk/{openresponses-http-CVZq0H-P.js → openresponses-http-C8lKwta1.js} +26 -25
  48. package/dist/plugin-sdk/{outbound-attachment-QqTnIhZL.js → outbound-attachment-CyXM3cAv.js} +1 -1
  49. package/dist/plugin-sdk/{outbound-BgVDdHxx.js → outbound-zB6llMGW.js} +6 -6
  50. package/dist/plugin-sdk/{pairing-message-L6L6L2YF.d.ts → pairing-message-mKO92L-M.d.ts} +1 -1
  51. package/dist/plugin-sdk/{pairing-store-BChahgI2.js → pairing-store-B6J2C0vP.js} +1 -1
  52. package/dist/plugin-sdk/{pi-model-discovery-runtime-DxTMz_XF.js → pi-model-discovery-runtime-6JzLw9Gk.js} +1 -1
  53. package/dist/plugin-sdk/{probe-CJb5uR8B.js → probe-BM7Oe40M.js} +2 -2
  54. package/dist/plugin-sdk/{probe-l_whvGLY.js → probe-CMPksimh.js} +1 -1
  55. package/dist/plugin-sdk/{probe-BFroeK3g.js → probe-oVp12D6L.js} +1 -1
  56. package/dist/plugin-sdk/provider-web-search-config-contract.d.ts +1 -1
  57. package/dist/plugin-sdk/{pw-ai-natjh3rm.js → pw-ai-DJ_gTc4M.js} +1 -1
  58. package/dist/plugin-sdk/qr-image-B4MlMG4N.js +115 -0
  59. package/dist/plugin-sdk/{replies-ChYo5ZQM.js → replies-CY-V0Zob.js} +3 -3
  60. package/dist/plugin-sdk/{resolve-channels-87LZrFk_.js → resolve-channels-1HtDcMqh.js} +2 -2
  61. package/dist/plugin-sdk/{resolve-channels-_-nV-EK2.js → resolve-channels-CU72Qh2j.js} +1 -1
  62. package/dist/plugin-sdk/{resolve-outbound-target-D-KGgHJZ.js → resolve-outbound-target-nQSBVYz9.js} +2 -2
  63. package/dist/plugin-sdk/{resolve-route-C8tvQm8Z.js → resolve-route-Dufnu2cT.js} +1 -1
  64. package/dist/plugin-sdk/{resolve-users-DN9VQBwv.js → resolve-users-Bk_ss2vC.js} +1 -1
  65. package/dist/plugin-sdk/{resolve-users-V6dHh_2B.js → resolve-users-IevCOk4W.js} +2 -2
  66. package/dist/plugin-sdk/{runtime-2B9_WKoI.js → runtime-CXSQwMaR.js} +1 -1
  67. package/dist/plugin-sdk/{runtime-dependency-DczkvPxn.js → runtime-dependency-nup8niQL.js} +9 -9
  68. package/dist/plugin-sdk/sat-runtime.d.ts +18 -2
  69. package/dist/plugin-sdk/sat-runtime.js +26 -25
  70. package/dist/plugin-sdk/{send-DCO441U7.js → send-BXBS_mRK.js} +8 -8
  71. package/dist/plugin-sdk/{send-CFzGKJXo.js → send-D-HfKQZm.js} +4 -4
  72. package/dist/plugin-sdk/{send-B6KkGKZD.js → send-D1j12ZgX.js} +5 -5
  73. package/dist/plugin-sdk/{send-q9HXrSwK.js → send-DWKm6yay.js} +10 -10
  74. package/dist/plugin-sdk/{send-gDoGA-7e.js → send-Di1Jit3L.js} +7 -7
  75. package/dist/plugin-sdk/{skill-commands-FKEPLS7T.js → skill-commands-iJtvpqgb.js} +2 -2
  76. package/dist/plugin-sdk/{slack-BZISDxDd.js → slack-B-nOOLM0.js} +45 -44
  77. package/dist/plugin-sdk/{slack-actions-DhbMcp8M.js → slack-actions-BzUEszJa.js} +10 -10
  78. package/dist/plugin-sdk/slack.d.ts +4 -4
  79. package/dist/plugin-sdk/slack.js +6 -6
  80. package/dist/plugin-sdk/{sticker-cache-B0aPv6Lg.js → sticker-cache-BLiId8Em.js} +2 -2
  81. package/dist/plugin-sdk/{tables-B3jr0Pvq.js → tables-_1W36GII.js} +2 -2
  82. package/dist/plugin-sdk/{targets-CtSbTxYX.js → targets-DDbNnLnB.js} +2 -2
  83. package/dist/plugin-sdk/{telegram-actions-H3gePEn6.js → telegram-actions-1X9A32Dn.js} +30 -29
  84. package/dist/plugin-sdk/telegram.d.ts +4 -4
  85. package/dist/plugin-sdk/telegram.js +3 -3
  86. package/dist/plugin-sdk/{thread-bindings.lifecycle-QbkNl-m6.js → thread-bindings.lifecycle-Y9T3CQGf.js} +3 -3
  87. package/dist/plugin-sdk/{thread-bindings.state-D9hg_M1c.js → thread-bindings.state-D2oOWDpv.js} +1 -1
  88. package/dist/plugin-sdk/{tmp-fased-dir-d1CmFAnJ.d.ts → tmp-fased-dir-BkpWlGUe.d.ts} +7 -1
  89. package/dist/plugin-sdk/{types-DUjYaWB2.d.ts → types-B-zL9gSv.d.ts} +1 -1
  90. package/dist/plugin-sdk/{types-xWjHXl1z.d.ts → types-DtCOO_hW.d.ts} +1 -1
  91. package/dist/plugin-sdk/{web-JwP9PAZR.js → web-BvjNyYBz.js} +33 -32
  92. package/dist/plugin-sdk/{whatsapp-actions-Bj_rOncr.js → whatsapp-actions-Blu9-eX-.js} +9 -9
  93. package/dist/plugin-sdk/{whatsapp-heartbeat-B1WHkJIg.js → whatsapp-heartbeat-DjmQM7hq.js} +2 -2
  94. package/dist/plugin-sdk/whatsapp.d.ts +9 -9
  95. package/dist/plugin-sdk/whatsapp.js +7 -7
  96. package/dist/plugin-sdk/{wsl-J-HscWxS.js → wsl-CTvIWvZz.js} +1 -1
  97. package/dist/plugin-sdk/{zod-schema.providers-core-CIzCjXH9.d.ts → zod-schema.providers-core-BCDlgtls.d.ts} +63 -63
  98. package/dist/wallet/local-socket-signer-lifecycle.js +29 -1
  99. package/dist/wallet/local-socket-signer-protocol.js +27 -0
  100. package/dist/wallet/providers/local-socket-signer-adapter.js +8 -0
  101. package/dist/wallet/signer-network-admin.js +11 -118
  102. package/dist/wallet/signer-protocol-v2.generated.js +4 -0
  103. package/dist/wallet/solana-assets.js +9 -3
  104. package/dist/wallet/solana-rpc-read-fetch.js +65 -0
  105. package/dist/wallet/wallet-status.js +7 -0
  106. package/dist/web/login-qr.js +1 -1
  107. package/dist/wizard/onboarding.js +61 -61
  108. package/extensions/acpx/package.json +1 -1
  109. package/extensions/bluebubbles/package.json +1 -1
  110. package/extensions/copilot-proxy/package.json +1 -1
  111. package/extensions/diagnostics-otel/package.json +1 -1
  112. package/extensions/fased-federation/package.json +1 -1
  113. package/extensions/google-gemini-cli-auth/package.json +1 -1
  114. package/extensions/imessage/package.json +1 -1
  115. package/extensions/irc/package.json +1 -1
  116. package/extensions/line/package.json +1 -1
  117. package/extensions/llm-task/package.json +1 -1
  118. package/extensions/lobster/package.json +1 -1
  119. package/extensions/matrix/CHANGELOG.md +12 -0
  120. package/extensions/matrix/package.json +1 -1
  121. package/extensions/mattermost/package.json +1 -1
  122. package/extensions/memory-core/package.json +2 -2
  123. package/extensions/memory-lancedb/package.json +1 -1
  124. package/extensions/minimax-portal-auth/package.json +1 -1
  125. package/extensions/msteams/CHANGELOG.md +12 -0
  126. package/extensions/msteams/package.json +1 -1
  127. package/extensions/nextcloud-talk/package.json +1 -1
  128. package/extensions/nostr/CHANGELOG.md +12 -0
  129. package/extensions/nostr/package.json +1 -1
  130. package/extensions/open-prose/package.json +1 -1
  131. package/extensions/sat-mining/package.json +1 -1
  132. package/extensions/signal/package.json +1 -1
  133. package/extensions/synology-chat/package.json +1 -1
  134. package/extensions/tlon/package.json +1 -1
  135. package/extensions/twitch/CHANGELOG.md +12 -0
  136. package/extensions/twitch/package.json +1 -1
  137. package/extensions/voice-call/CHANGELOG.md +12 -0
  138. package/extensions/voice-call/package.json +1 -1
  139. package/extensions/zalo/CHANGELOG.md +12 -0
  140. package/extensions/zalo/package.json +1 -1
  141. package/extensions/zalouser/CHANGELOG.md +12 -0
  142. package/extensions/zalouser/package.json +1 -1
  143. package/fased.mjs +14 -1
  144. package/install.sh +16 -10
  145. package/package.json +6 -1
  146. package/scripts/fased-managed-updater.mjs +10 -2
  147. package/scripts/fased-signer-wallet-import-hosting.sh +78 -0
  148. package/scripts/install-managed-runtime.mjs +10 -7
  149. /package/dist/plugin-sdk/{active-listener-B_NMtgPL.js → active-listener-p9Ooc1qj.js} +0 -0
  150. /package/dist/plugin-sdk/{blocks-input-Bg-8S2pl.js → blocks-input-DiZaqaN7.js} +0 -0
  151. /package/dist/plugin-sdk/{channel-access-CcFRdRPO.js → channel-access-B0aZBSd2.js} +0 -0
  152. /package/dist/plugin-sdk/{channel-activity-DBDMQ3yI.js → channel-activity-F07DjroV.js} +0 -0
  153. /package/dist/plugin-sdk/{channel-core-CFRs8LPy.js → channel-core-DoF4X04L.js} +0 -0
  154. /package/dist/plugin-sdk/{client-pI228AVT.js → client-DLY-kWF_.js} +0 -0
  155. /package/dist/plugin-sdk/{command-poll-backoff-yJU83wEq.js → command-poll-backoff-NAePk3eL.js} +0 -0
  156. /package/dist/plugin-sdk/{date-time-uUevHscU.js → date-time-ChY_tHoG.js} +0 -0
  157. /package/dist/plugin-sdk/{draft-stream-loop-CrOPfV3F.js → draft-stream-loop-DMBwjjd1.js} +0 -0
  158. /package/dist/plugin-sdk/{fetch-BtmoPIlf.js → fetch-B0v6OTUZ.js} +0 -0
  159. /package/dist/plugin-sdk/{filesystem-manager-DoI8IBNF.js → filesystem-manager-BzuIJ9K9.js} +0 -0
  160. /package/dist/plugin-sdk/{gateway-registry-DZDsqicG.js → gateway-registry-Bgl2GFgy.js} +0 -0
  161. /package/dist/plugin-sdk/{inbound-context-DrAcXE0y.js → inbound-context-CQnfFFQ9.js} +0 -0
  162. /package/dist/plugin-sdk/{pi-model-discovery-BX_JLcEJ.js → pi-model-discovery-BHNVyRny.js} +0 -0
  163. /package/dist/plugin-sdk/{polls-DKQXVfJp.js → polls-DXd67TD8.js} +0 -0
  164. /package/dist/plugin-sdk/{proxy-DWpYcsvR.js → proxy-CLRUdShp.js} +0 -0
  165. /package/dist/plugin-sdk/{qmd-manager-CgRaTxUq.js → qmd-manager-B8Vnu0ND.js} +0 -0
  166. /package/dist/plugin-sdk/{render-e7fENCYH.js → render-DrBfVs0u.js} +0 -0
  167. /package/dist/plugin-sdk/{reply-reference-Bqk-7HXE.js → reply-reference-BnAk5Wjb.js} +0 -0
  168. /package/dist/plugin-sdk/{retry-policy-C6SjKjQA.js → retry-policy-CXh1owkc.js} +0 -0
  169. /package/dist/plugin-sdk/{sandbox-paths-Dx2lUqCu.js → sandbox-paths-Bzh1CuZc.js} +0 -0
  170. /package/dist/plugin-sdk/{session-DHD4ZKyG.js → session-iuGR_rdi.js} +0 -0
  171. /package/dist/plugin-sdk/{shared-CBgp_GBw.js → shared-D1AD2Ejn.js} +0 -0
  172. /package/dist/plugin-sdk/{target-errors-Ckufe179.js → target-errors-DfS1sLR9.js} +0 -0
  173. /package/dist/plugin-sdk/{tool-loop-detection-Bp8bPJLs.js → tool-loop-detection-sDZl27w7.js} +0 -0
  174. /package/dist/plugin-sdk/{ui-Be8cLHIS.js → ui-GQDYm_jw.js} +0 -0
  175. /package/dist/plugin-sdk/{with-timeout-ckWB8rTo.js → with-timeout-CVV8tytk.js} +0 -0
@@ -14,7 +14,7 @@
14
14
  <link rel="icon" type="image/png" sizes="32x32" href="./favicon-32.png" />
15
15
  <link rel="apple-touch-icon" sizes="180x180" href="./apple-touch-icon.png" />
16
16
  <script data-fased-boot-watchdog src="./boot-watchdog.js" defer></script>
17
- <script type="module" crossorigin src="./assets/index-vw_10Zeo.js"></script>
17
+ <script type="module" crossorigin src="./assets/index-Cu13yYHr.js"></script>
18
18
  <link rel="stylesheet" crossorigin href="./assets/index-RwVraC3c.css">
19
19
  </head>
20
20
  <body>
@@ -1 +1 @@
1
- {"version":"0.1.66"}
1
+ {"version":"0.1.68"}
@@ -11,6 +11,7 @@ import { resolveLocalSignerSocketPath, resolveWalletRuntimeConfig } from "../wal
11
11
  import { WALLET_PROVIDER_IDS, checkNamedWalletDeletionSafety, deleteNamedWallet, normalizeWalletUserRole, readWalletProviderRegistry, resolveWalletSelection, resolveWalletUserRole, setAgentWalletAssignment, setDefaultWallet, setNamedWalletRole, setWalletProviderEnabled, upsertNamedWallet } from "../wallet/wallet-provider-registry.js";
12
12
  import { resolveAgentPublicOrigin, resolveFederationBaseUrl, resolveFederationBondWalletId, resolveFederationHandle } from "../federation/runtime.js";
13
13
  import { isValidSolanaAddress } from "../wallet/solana-address.js";
14
+ import { fetchPinnedSolanaRpcRead } from "../wallet/solana-rpc-read-fetch.js";
14
15
  import { fetchSolanaMintInfoViaRpc, fetchSolanaNativeBalanceViaRpc, fetchSolanaTokenBalanceViaRpc, fetchSolanaWalletAssetsViaRpc, invalidateSolanaAssetRpcCaches, summarizeSolanaAssetRpcMetrics } from "../wallet/solana-assets.js";
15
16
  import { tryResolveSatRuntimeIds } from "../config/sat-runtime-ids.js";
16
17
  import { commitWalletPolicyConfigUpdate, prepareWalletPolicyConfigUpdate, resolveWalletPolicyConfig, resolveWalletRecurringTransferPolicy } from "../wallet/wallet-policy.js";
@@ -35,12 +36,15 @@ import { digestSatSubmissionIntent } from "../extensions/sat-mining/src/submissi
35
36
  import { runWithSatSubmissionWorkflow, submitSatCancelBondUnlock, submitSatClaimBondStakingRewards, submitSatFinalizeBondUnlock, submitSatIncreaseBondPosition, submitSatOpenBondPosition, submitSatRequestBondUnlock, submitSatSyncBondStakingPosition, submitSatSyncBondStakingRewards } from "../extensions/sat-mining/src/solana-submit.js";
36
37
  import { resolveAgentAvatar } from "../agents/identity-avatar.js";
37
38
  import { resolveNativeSignerWalletId } from "../wallet/native-signer-wallet-id.js";
39
+ import { configureSignerOwnedWalletNetwork } from "../wallet/signer-network-admin.js";
38
40
  import { listWalletInboundEvents, pollWalletInboundEvents, reconcileWalletInboundEvents, recordWalletInboundWebhookEvent } from "../wallet/wallet-inbound-events.js";
39
41
  import { walletSetupCommand } from "../commands/wallet.js";
40
42
  import { getDiagnosticStabilitySnapshot } from "../logging/diagnostic-stability.js";
41
43
  import { resolveManagedFederationPublicUrl } from "../managed/federation.js";
44
+ import { lockSignerOwnedWalletForArchive } from "../wallet/local-socket-signer-archive.js";
42
45
  import { LocalSignerPolicyAdminRequiredError, buildLocalSignerPolicyTightening, localSignerPolicyState } from "../wallet/local-socket-signer-policy.js";
43
46
  import { executeWalletStandardReview, prepareWalletStandardReview, readWalletStandardReviewTxHash } from "../wallet/wallet-standard-review.js";
47
+ import { renderQrPngBase64 } from "../web/qr-image.js";
44
48
  import { createA2aHandler } from "./a2a-http.js";
45
49
  import { CONTROL_UI_BOOT_CHECK_PATH, resolveControlUiBootCheck } from "./control-ui-boot-check.js";
46
50
  import { normalizePublicHost, resolveControlUiPublicHost } from "./control-ui-login.js";
@@ -100,12 +104,19 @@ function parseCookies(raw) {
100
104
  }
101
105
  return cookies;
102
106
  }
103
- function isLocalHostName(host) {
107
+ function normalizeHostName(host) {
104
108
  const raw = host.trim().toLowerCase();
105
- const normalized = raw.startsWith("[") ? (() => {
109
+ return raw.startsWith("[") ? (() => {
106
110
  const end = raw.indexOf("]");
107
111
  return end > 0 ? raw.slice(1, end) : raw;
108
112
  })() : raw.replace(/:\d+$/, "");
113
+ }
114
+ function isLiteralLoopbackHostName(host) {
115
+ const normalized = normalizeHostName(host);
116
+ return normalized === "localhost" || normalized === "127.0.0.1" || normalized === "::1";
117
+ }
118
+ function isLocalHostName(host) {
119
+ const normalized = normalizeHostName(host);
109
120
  return normalized === "localhost" || normalized === "127.0.0.1" || normalized === "::1" || normalized.endsWith(".local");
110
121
  }
111
122
  function isLoopbackBrowserOrigin(origin) {
@@ -114,7 +125,18 @@ function isLoopbackBrowserOrigin(origin) {
114
125
  if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
115
126
  return false;
116
127
  }
117
- return isLocalHostName(parsed.host);
128
+ return isLiteralLoopbackHostName(parsed.host);
129
+ } catch {
130
+ return false;
131
+ }
132
+ }
133
+ function isSameBrowserOriginHost(origin, host) {
134
+ try {
135
+ const parsed = new URL(origin);
136
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
137
+ return false;
138
+ }
139
+ return parsed.host.toLowerCase() === host.trim().toLowerCase();
118
140
  } catch {
119
141
  return false;
120
142
  }
@@ -132,7 +154,7 @@ function applyLoopbackCorsIfAllowed(req, res, host) {
132
154
  if (!origin) {
133
155
  return false;
134
156
  }
135
- if (!isLocalHostName(host) || !isLoopbackBrowserOrigin(origin)) {
157
+ if (!isLocalHostName(host) || !isLoopbackBrowserOrigin(origin) && !isSameBrowserOriginHost(origin, host)) {
136
158
  return false;
137
159
  }
138
160
  res.setHeader("Access-Control-Allow-Origin", origin);
@@ -935,20 +957,20 @@ async function fetchSolanaLamportsViaRpc(params) {
935
957
  if (!rpcUrl || !address) {
936
958
  return null;
937
959
  }
938
- const controller = new AbortController();
939
- const timeout = setTimeout(() => controller.abort(), Math.max(250, params.timeoutMs));
960
+ let release;
940
961
  try {
941
- const response = await fetch(rpcUrl, {
942
- method: "POST",
943
- headers: { "content-type": "application/json" },
962
+ const guarded = await fetchPinnedSolanaRpcRead({
963
+ rpcUrl,
964
+ timeoutMs: params.timeoutMs,
944
965
  body: JSON.stringify({
945
966
  jsonrpc: "2.0",
946
967
  id: "wallet-balance-fallback",
947
968
  method: "getBalance",
948
969
  params: [address]
949
- }),
950
- signal: controller.signal
970
+ })
951
971
  });
972
+ release = guarded.release;
973
+ const response = guarded.response;
952
974
  if (!response.ok) {
953
975
  recordWalletGatewayRpcMethod("direct.getBalance", "failure");
954
976
  return null;
@@ -962,7 +984,7 @@ async function fetchSolanaLamportsViaRpc(params) {
962
984
  recordWalletGatewayRpcMethod("direct.getBalance", "failure");
963
985
  return null;
964
986
  } finally {
965
- clearTimeout(timeout);
987
+ await release?.();
966
988
  }
967
989
  }
968
990
  function resolveWalletProbeTimeoutMs(env) {
@@ -1004,11 +1026,18 @@ function resolveMiningAgentWalletConflict(walletId) {
1004
1026
  return null;
1005
1027
  }
1006
1028
  const activeMiningWalletId = readSatMiningWalletIdFromConfig(loadConfig());
1007
- if (activeMiningWalletId === normalizedWalletId) {
1008
- return null;
1029
+ if (activeMiningWalletId && activeMiningWalletId !== normalizedWalletId) {
1030
+ return `SAT Mining already uses ${activeMiningWalletId}. Archive that singleton wallet before attaching a replacement.`;
1009
1031
  }
1010
1032
  const registry = readWalletProviderRegistry(process.env);
1011
1033
  const wallet = registry.wallets.find((entry) => entry.id === normalizedWalletId);
1034
+ const otherMiningWallet = registry.wallets.find((entry) => entry.id !== normalizedWalletId && resolveWalletUserRole(entry) === "mining");
1035
+ if (otherMiningWallet) {
1036
+ return `SAT Mining already has the singleton wallet ${otherMiningWallet.id}. Archive it before attaching a replacement.`;
1037
+ }
1038
+ if (!wallet) {
1039
+ return "SAT Mining requires an existing dedicated Mining wallet.";
1040
+ }
1012
1041
  const purpose = resolveWalletUserRole(wallet);
1013
1042
  if (normalizedWalletId === registry.defaultWalletId || purpose === "agent") {
1014
1043
  return "SAT Mining must use a dedicated Mining wallet. Create a new Mining wallet instead of reusing an Agent wallet.";
@@ -1288,9 +1317,9 @@ async function checkSolanaAtaReadiness(address, env) {
1288
1317
  }
1289
1318
  try {
1290
1319
  const USDC_MINT = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";
1291
- const response = await fetch(rpcUrl, {
1292
- method: "POST",
1293
- headers: { "Content-Type": "application/json" },
1320
+ const guarded = await fetchPinnedSolanaRpcRead({
1321
+ rpcUrl,
1322
+ timeoutMs: 1e4,
1294
1323
  body: JSON.stringify({
1295
1324
  jsonrpc: "2.0",
1296
1325
  id: 1,
@@ -1302,15 +1331,19 @@ async function checkSolanaAtaReadiness(address, env) {
1302
1331
  ]
1303
1332
  })
1304
1333
  });
1305
- if (!response.ok) {
1306
- recordWalletGatewayRpcMethod("direct.getTokenAccountsByOwner", "failure");
1307
- return false;
1334
+ try {
1335
+ if (!guarded.response.ok) {
1336
+ recordWalletGatewayRpcMethod("direct.getTokenAccountsByOwner", "failure");
1337
+ return false;
1338
+ }
1339
+ const payload = await guarded.response.json();
1340
+ const accounts = payload?.result?.value;
1341
+ const ok = Array.isArray(accounts);
1342
+ recordWalletGatewayRpcMethod("direct.getTokenAccountsByOwner", ok ? "success" : "failure");
1343
+ return ok && accounts.length > 0;
1344
+ } finally {
1345
+ await guarded.release();
1308
1346
  }
1309
- const payload = await response.json();
1310
- const accounts = payload?.result?.value;
1311
- const ok = Array.isArray(accounts);
1312
- recordWalletGatewayRpcMethod("direct.getTokenAccountsByOwner", ok ? "success" : "failure");
1313
- return ok && accounts.length > 0;
1314
1347
  } catch {
1315
1348
  recordWalletGatewayRpcMethod("direct.getTokenAccountsByOwner", "failure");
1316
1349
  return false;
@@ -3065,7 +3098,20 @@ function createGatewayHttpServer(opts) {
3065
3098
  const directLocalRequest = !tailscaleProxyConfigured && isDirectLoopbackRequest(req, trustedProxies);
3066
3099
  const ensureWalletApiAuthorized = async () => {
3067
3100
  if (directLocalRequest) {
3068
- return true;
3101
+ const requestOrigin = String(getHeader(req, "origin") ?? "").trim();
3102
+ if (!requestOrigin || isLoopbackBrowserOrigin(requestOrigin)) {
3103
+ return true;
3104
+ }
3105
+ if (!loopbackCorsApplied) {
3106
+ sendLoginResponse(403, {
3107
+ ok: false,
3108
+ error: {
3109
+ code: "forbidden_origin",
3110
+ message: "cross-origin browser requests are not allowed on the loopback wallet API"
3111
+ }
3112
+ });
3113
+ return false;
3114
+ }
3069
3115
  }
3070
3116
  let authorized = false;
3071
3117
  const bearer = getBearerToken(req);
@@ -4476,24 +4522,24 @@ function createGatewayHttpServer(opts) {
4476
4522
  const requestedWalletId = typeof payload.walletId === "string" ? payload.walletId.trim() : "";
4477
4523
  const roleProvided = Object.prototype.hasOwnProperty.call(payload, "role");
4478
4524
  const normalizedRole = roleProvided ? normalizeWalletUserRole(payload.role) : undefined;
4479
- const requestedRole = normalizedRole === "agent" || normalizedRole === "vault" ? normalizedRole : undefined;
4525
+ const requestedRole = normalizedRole === "agent" || normalizedRole === "mining" || normalizedRole === "vault" ? normalizedRole : undefined;
4480
4526
  if (roleProvided && !requestedRole) {
4481
4527
  sendLoginResponse(400, {
4482
4528
  ok: false,
4483
4529
  error: {
4484
4530
  code: "invalid_wallet_role",
4485
- message: "role must be agent or vault"
4531
+ message: "role must be agent, mining, or vault"
4486
4532
  }
4487
4533
  });
4488
4534
  return;
4489
4535
  }
4490
4536
  const activeMiningWalletId = readSatMiningWalletIdFromConfig(loadConfig());
4491
- if (requestedRole && requestedWalletId && requestedWalletId === activeMiningWalletId) {
4537
+ if (requestedRole === "mining" && activeMiningWalletId && requestedWalletId !== activeMiningWalletId) {
4492
4538
  sendLoginResponse(409, {
4493
4539
  ok: false,
4494
4540
  error: {
4495
4541
  code: "wallet_in_use",
4496
- message: "walletId is the singleton SAT mining wallet; delete and recreate @wallet:mining before changing this wallet role"
4542
+ message: `Mining already uses the singleton wallet ${activeMiningWalletId}; use the reviewed Replace/Archive flow instead of creating a second Mining wallet`
4497
4543
  }
4498
4544
  });
4499
4545
  return;
@@ -4581,6 +4627,16 @@ function createGatewayHttpServer(opts) {
4581
4627
  });
4582
4628
  return;
4583
4629
  }
4630
+ if (!requestedRole) {
4631
+ sendLoginResponse(400, {
4632
+ ok: false,
4633
+ error: {
4634
+ code: "invalid_wallet_role",
4635
+ message: "choose agent, mining, or vault; the native signer never infers a role"
4636
+ }
4637
+ });
4638
+ return;
4639
+ }
4584
4640
  const chain = inferLocalSignerCreateChain({
4585
4641
  payloadChain: payload.chain,
4586
4642
  walletId: requestedWalletId,
@@ -4597,18 +4653,26 @@ function createGatewayHttpServer(opts) {
4597
4653
  });
4598
4654
  return;
4599
4655
  }
4656
+ const rpcUrl = typeof payload.rpcUrl === "string" ? payload.rpcUrl.trim() : "";
4657
+ if (!rpcUrl) {
4658
+ sendLoginResponse(400, {
4659
+ ok: false,
4660
+ error: {
4661
+ code: "invalid_request",
4662
+ message: "one primary Solana RPC URL is required for native signer wallet creation"
4663
+ }
4664
+ });
4665
+ return;
4666
+ }
4600
4667
  try {
4601
- const effectiveEnv = {
4602
- ...process.env,
4603
- ...cfg.env?.vars
4604
- };
4605
4668
  await walletSetupCommand(silentWalletSetupRuntime, {
4606
4669
  mode: "local-signer-create",
4607
4670
  chain,
4608
4671
  walletId: requestedWalletId,
4609
4672
  walletName,
4610
- rpcUrl: resolveWalletRpcUrlFromEnv(effectiveEnv, chain, requestedWalletId),
4673
+ rpcUrl,
4611
4674
  role: requestedRole,
4675
+ force: true,
4612
4676
  nonInteractive: true,
4613
4677
  noSignerHints: true,
4614
4678
  noDoctor: true
@@ -4738,13 +4802,15 @@ function createGatewayHttpServer(opts) {
4738
4802
  const name = typeof payload.name === "string" && payload.name.trim() ? payload.name.trim() : undefined;
4739
4803
  const roleProvided = Object.prototype.hasOwnProperty.call(payload, "role");
4740
4804
  const normalizedRole = roleProvided ? normalizeWalletUserRole(payload.role) : undefined;
4741
- const requestedRole = normalizedRole === "agent" || normalizedRole === "vault" ? normalizedRole : undefined;
4742
- if (!walletId || !name && !roleProvided) {
4805
+ const requestedRole = normalizedRole === "agent" || normalizedRole === "mining" || normalizedRole === "vault" ? normalizedRole : undefined;
4806
+ const rpcUrl = typeof payload.rpcUrl === "string" ? payload.rpcUrl.trim() : "";
4807
+ const rpcProvided = Object.prototype.hasOwnProperty.call(payload, "rpcUrl");
4808
+ if (!walletId || !name && !roleProvided && !rpcProvided) {
4743
4809
  sendLoginResponse(400, {
4744
4810
  ok: false,
4745
4811
  error: {
4746
4812
  code: "invalid_request",
4747
- message: "walletId and name or role are required"
4813
+ message: "walletId and role or rpcUrl are required"
4748
4814
  }
4749
4815
  });
4750
4816
  return;
@@ -4754,7 +4820,7 @@ function createGatewayHttpServer(opts) {
4754
4820
  ok: false,
4755
4821
  error: {
4756
4822
  code: "invalid_wallet_role",
4757
- message: "role must be agent or vault"
4823
+ message: "role must be agent, mining, or vault"
4758
4824
  }
4759
4825
  });
4760
4826
  return;
@@ -4781,13 +4847,117 @@ function createGatewayHttpServer(opts) {
4781
4847
  });
4782
4848
  return;
4783
4849
  }
4850
+ if (rpcProvided) {
4851
+ if (!rpcUrl) {
4852
+ sendLoginResponse(400, {
4853
+ ok: false,
4854
+ error: {
4855
+ code: "invalid_request",
4856
+ message: "rpcUrl must be a non-empty primary Solana RPC URL"
4857
+ }
4858
+ });
4859
+ return;
4860
+ }
4861
+ if (existing.providerId !== "local-socket-signer") {
4862
+ sendLoginResponse(409, {
4863
+ ok: false,
4864
+ error: {
4865
+ code: "invalid_provider",
4866
+ message: "primary RPC editing is available only for native signer wallets"
4867
+ }
4868
+ });
4869
+ return;
4870
+ }
4871
+ const cfg = loadConfig();
4872
+ if (!ensureWalletApprovalAuthorized({
4873
+ operation: "wallet.network",
4874
+ cfg
4875
+ })) {
4876
+ return;
4877
+ }
4878
+ try {
4879
+ const effectiveEnv = {
4880
+ ...process.env,
4881
+ ...cfg.env?.vars
4882
+ };
4883
+ const signerWalletId = resolveNativeSignerWalletId(existing);
4884
+ const network = await configureSignerOwnedWalletNetwork({
4885
+ walletId: signerWalletId,
4886
+ primaryRpcUrl: rpcUrl,
4887
+ env: effectiveEnv,
4888
+ socketPath: resolveLocalSignerSocketPath(effectiveEnv)
4889
+ });
4890
+ const suffix = normalizeWalletIdForEnvSuffix(walletId)?.toUpperCase();
4891
+ const rpcKey = suffix ? `FASED_WALLET_SOLANA_RPC_URL__${suffix}` : "FASED_WALLET_SOLANA_RPC_URL";
4892
+ const configUpdate = await updateWalletConfig({
4893
+ env: effectiveEnv,
4894
+ mutate: (next) => {
4895
+ next.env = {
4896
+ ...next.env,
4897
+ vars: {
4898
+ ...next.env?.vars,
4899
+ [rpcKey]: rpcUrl
4900
+ }
4901
+ };
4902
+ }
4903
+ });
4904
+ if (!configUpdate.ok) {
4905
+ throw new Error(`signer accepted network version ${network.version}, but app configuration persistence failed: ${configUpdate.message}`);
4906
+ }
4907
+ upsertNamedWallet({
4908
+ walletId: existing.id,
4909
+ name: existing.name,
4910
+ providerId: existing.providerId,
4911
+ addresses: existing.addresses,
4912
+ metadata: {
4913
+ ...existing.metadata,
4914
+ networkHash: network.hash,
4915
+ networkVersion: network.version,
4916
+ networkReady: network.ready
4917
+ },
4918
+ env: process.env
4919
+ });
4920
+ appendWalletAuditEntry({
4921
+ action: "wallet_rpc_updated",
4922
+ actor: "control-ui",
4923
+ details: {
4924
+ walletId,
4925
+ networkVersion: network.version,
4926
+ networkReady: network.ready
4927
+ },
4928
+ env: process.env
4929
+ });
4930
+ } catch (err) {
4931
+ sendLoginResponse(502, {
4932
+ ok: false,
4933
+ error: {
4934
+ code: "wallet_rpc_update_failed",
4935
+ message: err instanceof Error ? err.message : String(err)
4936
+ }
4937
+ });
4938
+ return;
4939
+ }
4940
+ }
4784
4941
  const activeMiningWalletId = readSatMiningWalletIdFromConfig(loadConfig());
4785
- if (requestedRole && walletId === activeMiningWalletId) {
4942
+ if (requestedRole === "mining") {
4943
+ const miningConflict = resolveMiningAgentWalletConflict(walletId);
4944
+ if (miningConflict) {
4945
+ sendLoginResponse(409, {
4946
+ ok: false,
4947
+ error: {
4948
+ code: "wallet_in_use",
4949
+ message: miningConflict
4950
+ }
4951
+ });
4952
+ return;
4953
+ }
4954
+ }
4955
+ if (requestedRole && requestedRole !== "mining" && walletId === activeMiningWalletId) {
4786
4956
  sendLoginResponse(409, {
4787
4957
  ok: false,
4788
4958
  error: {
4789
4959
  code: "wallet_in_use",
4790
- message: "walletId is the singleton SAT mining wallet; delete and recreate @wallet:mining before changing this wallet role"
4960
+ message: "walletId is the singleton SAT Mining wallet; use the guarded Archive/Replace flow before creating a wallet with a different role"
4791
4961
  }
4792
4962
  });
4793
4963
  return;
@@ -4806,7 +4976,7 @@ function createGatewayHttpServer(opts) {
4806
4976
  ok: false,
4807
4977
  error: {
4808
4978
  code: "signer_wallet_role_immutable",
4809
- message: `The native signer owns this wallet as role=${signerPolicy.role}; the Gateway cannot change it to ${requestedRole}. ` + "Create a new signer-owned wallet with the required locked role through Local onboarding or an authenticated Hosting administrator session."
4979
+ message: `The native signer owns this wallet as role=${signerPolicy.role}; the Gateway cannot change it to ${requestedRole}. ` + "Create a new signer-owned wallet with the required locked role, then use the guarded Archive/Replace flow for the old wallet."
4810
4980
  }
4811
4981
  });
4812
4982
  return;
@@ -4863,6 +5033,8 @@ function createGatewayHttpServer(opts) {
4863
5033
  }
4864
5034
  const payload = body.value ?? {};
4865
5035
  const walletId = typeof payload.walletId === "string" ? payload.walletId.trim() : "";
5036
+ const archiveRequested = payload.archive === true;
5037
+ const archiveConfirmation = typeof payload.confirmWalletId === "string" ? payload.confirmWalletId.trim() : "";
4866
5038
  if (!walletId) {
4867
5039
  sendLoginResponse(400, {
4868
5040
  ok: false,
@@ -4873,13 +5045,23 @@ function createGatewayHttpServer(opts) {
4873
5045
  });
4874
5046
  return;
4875
5047
  }
5048
+ if (archiveRequested && archiveConfirmation !== walletId) {
5049
+ sendLoginResponse(400, {
5050
+ ok: false,
5051
+ error: {
5052
+ code: "archive_confirmation_required",
5053
+ message: "confirmWalletId must exactly match walletId"
5054
+ }
5055
+ });
5056
+ return;
5057
+ }
4876
5058
  const activeMiningWalletId = readSatMiningWalletIdFromConfig(loadConfig());
4877
- if (activeMiningWalletId === walletId) {
5059
+ if (activeMiningWalletId === walletId && !archiveRequested) {
4878
5060
  sendLoginResponse(409, {
4879
5061
  ok: false,
4880
5062
  error: {
4881
5063
  code: "wallet_in_use",
4882
- message: "walletId is the singleton SAT mining wallet; use onboarding or CLI guarded delete for @wallet:mining instead of this API"
5064
+ message: "walletId is the singleton SAT Mining wallet; request the guarded Archive flow instead of ordinary delete"
4883
5065
  }
4884
5066
  });
4885
5067
  return;
@@ -4911,6 +5093,88 @@ function createGatewayHttpServer(opts) {
4911
5093
  });
4912
5094
  return;
4913
5095
  }
5096
+ if (existing.providerId === "local-socket-signer" && archiveRequested) {
5097
+ const cfg = loadConfig();
5098
+ if (!ensureWalletApprovalAuthorized({
5099
+ operation: "wallet.archive",
5100
+ cfg
5101
+ })) {
5102
+ return;
5103
+ }
5104
+ let archivedPolicy;
5105
+ try {
5106
+ const effectiveEnv = {
5107
+ ...process.env,
5108
+ ...cfg.env?.vars
5109
+ };
5110
+ archivedPolicy = await lockSignerOwnedWalletForArchive({
5111
+ wallet: existing,
5112
+ socketPath: resolveLocalSignerSocketPath(effectiveEnv)
5113
+ });
5114
+ const federationWalletId = resolveFederationBondDefaultWalletId(cfg);
5115
+ const suffix = normalizeWalletIdForEnvSuffix(walletId)?.toUpperCase();
5116
+ const rpcKey = suffix ? `FASED_WALLET_SOLANA_RPC_URL__${suffix}` : "FASED_WALLET_SOLANA_RPC_URL";
5117
+ const updated = await updateWalletConfig({
5118
+ env: effectiveEnv,
5119
+ mutate: (next) => {
5120
+ if (next.env?.vars) {
5121
+ delete next.env.vars[rpcKey];
5122
+ }
5123
+ const currentEntry = next.plugins?.entries?.["sat-mining"];
5124
+ const currentSatConfig = currentEntry?.config && typeof currentEntry.config === "object" && !Array.isArray(currentEntry.config) ? { ...currentEntry.config } : {};
5125
+ if (currentSatConfig.walletId === walletId) {
5126
+ delete currentSatConfig.walletId;
5127
+ next.plugins = {
5128
+ ...next.plugins,
5129
+ entries: {
5130
+ ...next.plugins?.entries,
5131
+ "sat-mining": {
5132
+ enabled: true,
5133
+ ...currentEntry,
5134
+ config: currentSatConfig
5135
+ }
5136
+ }
5137
+ };
5138
+ }
5139
+ if (federationWalletId === walletId) {
5140
+ applyFederationBondWalletConfig(next, null);
5141
+ }
5142
+ }
5143
+ });
5144
+ if (!updated.ok) {
5145
+ throw new Error(`signer locked the wallet, but attachment cleanup failed: ${updated.message}`);
5146
+ }
5147
+ } catch (err) {
5148
+ sendLoginResponse(502, {
5149
+ ok: false,
5150
+ error: {
5151
+ code: "wallet_archive_failed",
5152
+ message: err instanceof Error ? err.message : String(err)
5153
+ }
5154
+ });
5155
+ return;
5156
+ }
5157
+ const removed = deleteNamedWallet({
5158
+ walletId,
5159
+ env: process.env
5160
+ });
5161
+ appendWalletAuditEntry({
5162
+ action: "wallet_archived",
5163
+ actor: "control-ui",
5164
+ details: {
5165
+ walletId,
5166
+ signerWalletId: archivedPolicy.walletId,
5167
+ denyAllPolicyVersion: archivedPolicy.version
5168
+ },
5169
+ env: process.env
5170
+ });
5171
+ sendLoginResponse(200, {
5172
+ ok: true,
5173
+ removed: removed.removed,
5174
+ archived: true
5175
+ });
5176
+ return;
5177
+ }
4914
5178
  if (existing.providerId === "local-socket-signer" || existing.providerId === "embedded-keystore" || existing.metadata?.selfHosted === true) {
4915
5179
  sendLoginResponse(409, {
4916
5180
  ok: false,
@@ -4943,6 +5207,44 @@ function createGatewayHttpServer(opts) {
4943
5207
  });
4944
5208
  return;
4945
5209
  }
5210
+ if (requestPath === "/api/wallet/receive-qr") {
5211
+ if (!await ensureWalletApiAuthorized()) {
5212
+ return;
5213
+ }
5214
+ if (req.method !== "GET") {
5215
+ res.statusCode = 405;
5216
+ res.setHeader("Allow", "GET");
5217
+ sendLoginResponse(405, {
5218
+ ok: false,
5219
+ error: {
5220
+ code: "method_not_allowed",
5221
+ message: "method must be GET"
5222
+ }
5223
+ });
5224
+ return;
5225
+ }
5226
+ const walletId = parsedUrl.searchParams.get("walletId")?.trim() ?? "";
5227
+ const wallet = readWalletProviderRegistry(process.env).wallets.find((entry) => entry.id === walletId);
5228
+ const address = wallet?.addresses?.solana?.trim() ?? "";
5229
+ if (!walletId || !address || !isValidSolanaAddress(address)) {
5230
+ sendLoginResponse(404, {
5231
+ ok: false,
5232
+ error: {
5233
+ code: "wallet_address_not_found",
5234
+ message: "wallet address not found"
5235
+ }
5236
+ });
5237
+ return;
5238
+ }
5239
+ const png = Buffer.from(await renderQrPngBase64(`solana:${address}`), "base64");
5240
+ res.statusCode = 200;
5241
+ res.setHeader("Content-Type", "image/png");
5242
+ res.setHeader("Content-Length", String(png.byteLength));
5243
+ res.setHeader("Cache-Control", "private, no-store");
5244
+ res.setHeader("X-Content-Type-Options", "nosniff");
5245
+ res.end(png);
5246
+ return;
5247
+ }
4946
5248
  if (requestPath === "/api/wallet/assignments") {
4947
5249
  if (!await ensureWalletApiAuthorized()) {
4948
5250
  return;
@@ -4987,7 +5289,7 @@ function createGatewayHttpServer(opts) {
4987
5289
  ok: false,
4988
5290
  error: {
4989
5291
  code: "wallet_in_use",
4990
- message: "walletId is the singleton SAT mining wallet; delete and recreate @wallet:mining before making it the primary Agent wallet"
5292
+ message: "walletId is the singleton SAT Mining wallet; Archive/Replace Mining with an Agent wallet before making it the primary Agent wallet"
4991
5293
  }
4992
5294
  });
4993
5295
  return;
@@ -7129,7 +7431,7 @@ function createGatewayHttpServer(opts) {
7129
7431
  });
7130
7432
  return;
7131
7433
  }
7132
- if (operation !== "wallet.approve" && operation !== "wallet.policy" && operation !== "wallet.settings" && operation !== "wallet.provider-credentials" && operation !== "wallet.passkey-enroll" && operation !== "wallet.passkey-remove" && operation !== "wallet.reset" && operation !== "wallet.rotate" && operation !== "wallet.execution-mode" && operation !== "wallet.send" && operation !== "mining.capital" && operation !== "mining.policy") {
7434
+ if (operation !== "wallet.approve" && operation !== "wallet.policy" && operation !== "wallet.settings" && operation !== "wallet.provider-credentials" && operation !== "wallet.network" && operation !== "wallet.archive" && operation !== "wallet.passkey-enroll" && operation !== "wallet.passkey-remove" && operation !== "wallet.reset" && operation !== "wallet.rotate" && operation !== "wallet.execution-mode" && operation !== "wallet.send" && operation !== "mining.capital" && operation !== "mining.policy") {
7133
7435
  sendLoginResponse(400, {
7134
7436
  ok: false,
7135
7437
  error: {
@@ -7263,7 +7565,7 @@ function createGatewayHttpServer(opts) {
7263
7565
  const payloadRaw = body.value ?? {};
7264
7566
  const requestId = typeof payloadRaw.requestId === "string" ? payloadRaw.requestId.trim() || undefined : undefined;
7265
7567
  const operation = typeof payloadRaw.operation === "string" ? payloadRaw.operation.trim() || undefined : undefined;
7266
- if (operation && operation !== "wallet.approve" && operation !== "wallet.policy" && operation !== "wallet.settings" && operation !== "wallet.provider-credentials" && operation !== "wallet.passkey-enroll" && operation !== "wallet.passkey-remove" && operation !== "wallet.reset" && operation !== "wallet.rotate" && operation !== "wallet.execution-mode" && operation !== "wallet.send" && operation !== "mining.capital" && operation !== "mining.policy") {
7568
+ if (operation && operation !== "wallet.approve" && operation !== "wallet.policy" && operation !== "wallet.settings" && operation !== "wallet.provider-credentials" && operation !== "wallet.network" && operation !== "wallet.archive" && operation !== "wallet.passkey-enroll" && operation !== "wallet.passkey-remove" && operation !== "wallet.reset" && operation !== "wallet.rotate" && operation !== "wallet.execution-mode" && operation !== "wallet.send" && operation !== "mining.capital" && operation !== "mining.policy") {
7267
7569
  sendLoginResponse(400, {
7268
7570
  ok: false,
7269
7571
  error: {
@@ -45,6 +45,7 @@ function detectRuntime() {
45
45
  version,
46
46
  execPath: process.execPath ?? null,
47
47
  pathEnv: process.env.PATH ?? "(not set)",
48
+ platform: process.platform,
48
49
  sqliteAvailable: kind === "node" ? detectNodeSqliteSupport() : false
49
50
  };
50
51
  }
@@ -57,6 +58,10 @@ function detectNodeSqliteSupport() {
57
58
  }
58
59
  }
59
60
  function runtimeSatisfies(details) {
61
+ const platform = details.platform ?? process.platform;
62
+ if (platform === "win32") {
63
+ return false;
64
+ }
60
65
  const parsed = parseSemver(details.version);
61
66
  if (details.kind === "node") {
62
67
  return isAtLeast(parsed, MIN_NODE) && details.sqliteAvailable !== false;
@@ -70,6 +75,17 @@ function assertSupportedRuntime(runtime = defaultRuntime, details = detectRuntim
70
75
  if (runtimeSatisfies(details)) {
71
76
  return;
72
77
  }
78
+ const platform = details.platform ?? process.platform;
79
+ if (platform === "win32") {
80
+ runtime.error([
81
+ "Native Windows is not a supported Fased runtime.",
82
+ "Install Ubuntu in WSL2, enable systemd, and run Fased inside the Ubuntu shell.",
83
+ "PowerShell is supported only for installing WSL2 or connecting to a remote Linux VPS.",
84
+ "Guide: https://docs.fased.ai/platforms/windows"
85
+ ].join("\n"));
86
+ runtime.exit(1);
87
+ return;
88
+ }
73
89
  const versionLabel = details.version ?? "unknown";
74
90
  const runtimeLabel = details.kind === "unknown" ? "unknown runtime" : `${details.kind} ${versionLabel}`;
75
91
  const execLabel = details.execPath ?? "unknown";