@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
@@ -292,6 +292,16 @@ const LocalSocketSignerSatLookupBindingV2Schema = Type.Object({
292
292
  Type.Literal("unknown")
293
293
  ]))
294
294
  }, { additionalProperties: false });
295
+ const LocalSocketSignerNetworkSummaryV2Schema = Type.Object({
296
+ walletId: Type.String({
297
+ minLength: 1,
298
+ maxLength: 64
299
+ }),
300
+ configured: Type.Boolean(),
301
+ version: Type.Integer({ minimum: 0 }),
302
+ hash: Type.Optional(Type.String({ pattern: "^hmac-sha256:[0-9a-f]{64}$" })),
303
+ ready: Type.Boolean()
304
+ }, { additionalProperties: false });
295
305
  const LocalSocketSignerRequestSchema = Type.Union([
296
306
  Type.Object({ op: Type.Literal("health") }, { additionalProperties: false }),
297
307
  Type.Object({ op: Type.Literal("v2.capabilities") }, { additionalProperties: false }),
@@ -303,6 +313,21 @@ const LocalSocketSignerRequestSchema = Type.Union([
303
313
  op: Type.Literal("v2.policy.get"),
304
314
  walletId: Type.String()
305
315
  }, { additionalProperties: false }),
316
+ Type.Object({
317
+ op: Type.Literal("v2.network.get"),
318
+ walletId: Type.String()
319
+ }, { additionalProperties: false }),
320
+ Type.Object({
321
+ op: Type.Literal("v2.network.bootstrap"),
322
+ walletId: Type.String(),
323
+ request: Type.Object({
324
+ expectedVersion: Type.Integer({ minimum: 0 }),
325
+ primaryRpcUrl: Type.String({
326
+ minLength: 1,
327
+ maxLength: 2048
328
+ })
329
+ }, { additionalProperties: false })
330
+ }, { additionalProperties: false }),
306
331
  Type.Object({
307
332
  op: Type.Literal("v2.policy.put"),
308
333
  walletId: Type.String(),
@@ -665,6 +690,8 @@ function validateLocalSocketSignerResult(op, result) {
665
690
  case "health":
666
691
  case "v2.capabilities": return Value.Check(LocalSocketSignerHealthResultSchema, result);
667
692
  case "v2.jupiter.trigger.history": return Value.Check(LocalSocketSignerJupiterTriggerHistoryV2Schema, result);
693
+ case "v2.network.get":
694
+ case "v2.network.bootstrap": return Value.Check(LocalSocketSignerNetworkSummaryV2Schema, result);
668
695
  case "v2.policy.get":
669
696
  case "v2.policy.put":
670
697
  case "v2.policy.tighten": return Value.Check(LocalSocketSignerPolicyV2Schema, result);
@@ -14,6 +14,8 @@ const REQUIRED_PROTOCOL_V2_FEATURES = SIGNER_PROTOCOL_V2_REQUIRED_CLIENT_FEATURE
14
14
  const SIGNER_SOCKET_TIMEOUT_MS = {
15
15
  health: 2e3,
16
16
  "v2.capabilities": 2e3,
17
+ "v2.network.get": 15e3,
18
+ "v2.network.bootstrap": 3e4,
17
19
  "v2.jupiter.trigger.history": 3e4,
18
20
  "v2.policy.get": 5e3,
19
21
  "v2.policy.put": 5e3,
@@ -227,6 +229,12 @@ var LocalSocketSignerAdapter = class {
227
229
  provider: this.id,
228
230
  configured: true,
229
231
  checkedAt: new Date().toISOString(),
232
+ ...details.webAuthn ? { nativeSignerApproval: {
233
+ configured: details.webAuthn.configured,
234
+ ready: details.webAuthn.ready,
235
+ credentialCount: details.webAuthn.credentialCount,
236
+ credentialVersion: details.webAuthn.credentialVersion
237
+ } } : {},
230
238
  details: [
231
239
  details?.details ?? "",
232
240
  ...!details?.details ? [
@@ -1,10 +1,7 @@
1
- import { resolveLocalSignerControlSocketPath } from "./wallet-runtime-config.js";
2
- import path from "node:path";
3
- import { spawnSync } from "node:child_process";
1
+ import { resolveLocalSignerSocketPath } from "./wallet-runtime-config.js";
2
+ import { configureSignerOwnedWalletPrimaryRpc } from "./local-socket-signer-lifecycle.js";
4
3
 
5
4
  //#region src/wallet/signer-network-admin.ts
6
- const NETWORK_ADMIN_TIMEOUT_MS = 3e4;
7
- const NETWORK_ADMIN_MAX_OUTPUT_BYTES = 256 * 1024;
8
5
  function canonicalSignerWalletId(raw) {
9
6
  const normalized = raw.trim().toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
10
7
  return normalized || "default";
@@ -47,62 +44,7 @@ function parseSignerNetworkSummary(raw, expectedWalletId) {
47
44
  ready: record.ready
48
45
  };
49
46
  }
50
- function redactSignerNetworkError(raw, secrets) {
51
- let value = raw;
52
- for (const secret of secrets.filter(Boolean).toSorted((a, b) => b.length - a.length)) {
53
- value = value.split(secret).join("[redacted-rpc-url]");
54
- }
55
- value = value.replace(/https?:\/\/[^\s"'<>]+/gi, "[redacted-rpc-url]");
56
- value = value.replace(/\b(api[_-]?key|access[_-]?token|token|key)=([^\s&"']+)/gi, "$1=[redacted]");
57
- return value.trim().slice(0, 2e3) || "signer network administration failed";
58
- }
59
- function signerAdminEnvironment(env) {
60
- const out = {};
61
- for (const key of [
62
- "HOME",
63
- "PATH",
64
- "TMPDIR",
65
- "LANG",
66
- "LC_ALL",
67
- "TZ"
68
- ]) {
69
- const value = String(env[key] ?? "").trim();
70
- if (value) {
71
- out[key] = value;
72
- }
73
- }
74
- return out;
75
- }
76
- function runSignerAdmin(params) {
77
- const child = spawnSync(params.command, params.args, {
78
- input: params.input,
79
- env: params.env,
80
- encoding: "utf8",
81
- timeout: NETWORK_ADMIN_TIMEOUT_MS,
82
- maxBuffer: NETWORK_ADMIN_MAX_OUTPUT_BYTES,
83
- stdio: [
84
- "pipe",
85
- "pipe",
86
- "pipe"
87
- ]
88
- });
89
- if (child.error || child.status !== 0) {
90
- const detail = [
91
- child.stderr,
92
- child.stdout,
93
- child.error?.message
94
- ].filter((part) => typeof part === "string" && part.trim().length > 0).join("\n");
95
- throw new Error(redactSignerNetworkError(detail, params.secrets));
96
- }
97
- return child.stdout;
98
- }
99
- function assertReadyNetworkSummary(summary, previousVersion) {
100
- if (!summary.configured || !summary.ready || !summary.hash || summary.version !== previousVersion + 1) {
101
- throw new Error("signer network update did not acknowledge the exact next ready version");
102
- }
103
- return summary;
104
- }
105
- function configureSignerOwnedWalletNetwork(params) {
47
+ async function configureSignerOwnedWalletNetwork(params) {
106
48
  const env = params.env ?? process.env;
107
49
  const walletId = canonicalSignerWalletId(params.walletId);
108
50
  const primaryRpcUrl = params.primaryRpcUrl.trim();
@@ -111,61 +53,14 @@ function configureSignerOwnedWalletNetwork(params) {
111
53
  if (!primaryRpcUrl) {
112
54
  throw new Error("signer-owned wallet network requires a primary RPC URL");
113
55
  }
114
- const secrets = [
115
- primaryRpcUrl,
116
- executionFallbackRpcUrl,
117
- verificationRpcUrl
118
- ];
119
- const hosting = String(env.FASED_HOST_PROFILE ?? "").trim().toLowerCase() === "hosting";
120
- if (hosting) {
121
- return {
122
- walletId,
123
- configured: false,
124
- version: 0,
125
- ready: false,
126
- rootAdminRequired: true,
127
- rootCommand: `/usr/local/sbin/fased-signer-network --wallet-id ${walletId} ` + "--network-file /root/fased-network.json"
128
- };
56
+ if (executionFallbackRpcUrl || verificationRpcUrl) {
57
+ throw new Error("Normal wallet setup accepts one primary RPC. Configure an advanced execution fallback or explicit witness with the native signer admin network command.");
129
58
  }
130
- const signerBinPath = params.signerBinPath ?? (String(env.FASED_WALLET_LOCAL_SIGNER_BIN ?? "").trim() || path.join(String(env.HOME ?? ""), ".fased", "bin", "fased-signerd"));
131
- const controlSocketPath = params.controlSocketPath ?? resolveLocalSignerControlSocketPath(env);
132
- const adminEnv = signerAdminEnvironment(env);
133
- const commonArgs = [
134
- "--control-socket",
135
- controlSocketPath,
136
- "--wallet-id",
137
- walletId
138
- ];
139
- const current = parseSignerNetworkSummary(runSignerAdmin({
140
- command: signerBinPath,
141
- args: [
142
- "admin",
143
- "network",
144
- "get",
145
- ...commonArgs
146
- ],
147
- env: adminEnv,
148
- secrets
149
- }), walletId);
150
- const input = `${JSON.stringify({
151
- expectedVersion: current.version,
152
- primaryRpcUrl,
153
- ...executionFallbackRpcUrl ? { executionFallbackRpcUrl } : {},
154
- ...verificationRpcUrl ? { verificationRpcUrl } : {}
155
- })}\n`;
156
- const updated = parseSignerNetworkSummary(runSignerAdmin({
157
- command: signerBinPath,
158
- args: [
159
- "admin",
160
- "network",
161
- "put",
162
- ...commonArgs
163
- ],
164
- input,
165
- env: adminEnv,
166
- secrets
167
- }), walletId);
168
- return assertReadyNetworkSummary(updated, current.version);
59
+ return configureSignerOwnedWalletPrimaryRpc({
60
+ socketPath: params.socketPath ?? resolveLocalSignerSocketPath(env),
61
+ walletId,
62
+ primaryRpcUrl
63
+ });
169
64
  }
170
65
  /**
171
66
  * Hosted policy changes are deliberately unavailable to the app process. New signer-owned
@@ -181,9 +76,7 @@ function applyHostedSignerOwnedWalletPolicy(params) {
181
76
  }
182
77
  const __testing = {
183
78
  canonicalSignerWalletId,
184
- parseSignerNetworkSummary,
185
- redactSignerNetworkError,
186
- signerAdminEnvironment
79
+ parseSignerNetworkSummary
187
80
  };
188
81
 
189
82
  //#endregion
@@ -38,6 +38,9 @@ const SIGNER_PROTOCOL_V2 = {
38
38
  "signerOwnedSuccessorRotation",
39
39
  "permanentRetiredWalletPolicies",
40
40
  "signerOwnedRPC",
41
+ "applicationNetworkBootstrap",
42
+ "signerOwnedEncryptedRecovery",
43
+ "signerOwnedRawExport",
41
44
  "typedSolanaTransactions",
42
45
  "typedA2aPaymentMemos",
43
46
  "typedA2aRefundMemos",
@@ -75,6 +78,7 @@ const SIGNER_PROTOCOL_V2_REQUIRED_CLIENT_FEATURES = [
75
78
  "ambiguousBroadcastReconciliation",
76
79
  "signerOwnedKeys",
77
80
  "signerOwnedRPC",
81
+ "applicationNetworkBootstrap",
78
82
  "typedSolanaTransactions",
79
83
  "typedA2aPaymentMemos",
80
84
  "typedA2aRefundMemos",
@@ -1,3 +1,4 @@
1
+ import { fetchPinnedSolanaRpcRead } from "./solana-rpc-read-fetch.js";
1
2
  import { PublicKey } from "@solana/web3.js";
2
3
 
3
4
  //#region src/wallet/solana-assets.ts
@@ -427,10 +428,11 @@ function formatTokenAmount(raw, decimals) {
427
428
  }
428
429
  }
429
430
  async function fetchSolanaRpc(rpcUrl, method, params) {
431
+ let release;
430
432
  try {
431
- const response = await fetch(rpcUrl, {
432
- method: "POST",
433
- headers: { "Content-Type": "application/json" },
433
+ const guarded = await fetchPinnedSolanaRpcRead({
434
+ rpcUrl,
435
+ timeoutMs: 1e4,
434
436
  body: JSON.stringify({
435
437
  jsonrpc: "2.0",
436
438
  id: 1,
@@ -438,6 +440,8 @@ async function fetchSolanaRpc(rpcUrl, method, params) {
438
440
  params
439
441
  })
440
442
  });
443
+ release = guarded.release;
444
+ const response = guarded.response;
441
445
  if (!response.ok) {
442
446
  recordSolanaAssetRpcMethod(method, "failure");
443
447
  return null;
@@ -452,6 +456,8 @@ async function fetchSolanaRpc(rpcUrl, method, params) {
452
456
  } catch {
453
457
  recordSolanaAssetRpcMethod(method, "failure");
454
458
  return null;
459
+ } finally {
460
+ await release?.();
455
461
  }
456
462
  }
457
463
  async function fetchSolanaMetadataFromMintViaRpc(params) {
@@ -0,0 +1,65 @@
1
+ import { closeDispatcher, createPinnedDispatcher, resolvePinnedHostnameWithPolicy } from "../infra/net/ssrf.js";
2
+
3
+ //#region src/wallet/solana-rpc-read-fetch.ts
4
+ function normalizeRpcHostname(hostname) {
5
+ const normalized = hostname.trim().toLowerCase().replace(/\.$/, "");
6
+ return normalized.startsWith("[") && normalized.endsWith("]") ? normalized.slice(1, -1) : normalized;
7
+ }
8
+ function isLoopbackRpcHostname(hostname) {
9
+ const normalized = normalizeRpcHostname(hostname);
10
+ return normalized === "localhost" || normalized.endsWith(".localhost") || normalized === "::1" || /^127(?:\.\d{1,3}){3}$/.test(normalized);
11
+ }
12
+ function isLoopbackRpcAddress(address) {
13
+ const normalized = normalizeRpcHostname(address);
14
+ return normalized === "::1" || normalized === "0:0:0:0:0:0:0:1" || /^127(?:\.\d{1,3}){3}$/.test(normalized) || /^::ffff:127(?:\.\d{1,3}){3}$/.test(normalized);
15
+ }
16
+ async function resolveWithTimeout(hostname, policy, timeoutMs) {
17
+ let timer;
18
+ try {
19
+ return await Promise.race([resolvePinnedHostnameWithPolicy(hostname, { policy }), new Promise((_resolve, reject) => {
20
+ timer = setTimeout(() => reject(new Error("stored Solana RPC hostname resolution timed out")), Math.max(250, timeoutMs));
21
+ timer.unref?.();
22
+ })]);
23
+ } finally {
24
+ if (timer) {
25
+ clearTimeout(timer);
26
+ }
27
+ }
28
+ }
29
+ async function fetchPinnedSolanaRpcRead(params) {
30
+ const parsed = new URL(params.rpcUrl);
31
+ const hostname = normalizeRpcHostname(parsed.hostname);
32
+ const loopback = isLoopbackRpcHostname(hostname);
33
+ if (!hostname || parsed.username || parsed.password || parsed.hash || parsed.protocol !== "https:" && !(parsed.protocol === "http:" && loopback)) {
34
+ throw new Error("stored Solana RPC URL is not safe for Gateway reads");
35
+ }
36
+ const policy = {
37
+ hostnameAllowlist: [hostname],
38
+ ...loopback ? { allowPrivateNetwork: true } : {}
39
+ };
40
+ const pinned = await resolveWithTimeout(hostname, policy, params.timeoutMs);
41
+ if (loopback && !pinned.addresses.every(isLoopbackRpcAddress)) {
42
+ throw new Error("stored Local Solana RPC hostname did not resolve only to loopback addresses");
43
+ }
44
+ const dispatcher = createPinnedDispatcher(pinned);
45
+ try {
46
+ const response = await fetch(parsed.toString(), {
47
+ method: "POST",
48
+ headers: { "content-type": "application/json" },
49
+ body: params.body,
50
+ redirect: "error",
51
+ signal: AbortSignal.timeout(Math.max(250, params.timeoutMs)),
52
+ dispatcher
53
+ });
54
+ return {
55
+ response,
56
+ release: async () => closeDispatcher(dispatcher)
57
+ };
58
+ } catch (error) {
59
+ await closeDispatcher(dispatcher);
60
+ throw error;
61
+ }
62
+ }
63
+
64
+ //#endregion
65
+ export { fetchPinnedSolanaRpcRead };
@@ -28,6 +28,9 @@ async function readWalletStatusSnapshot(params) {
28
28
  const checkedAt = new Date().toISOString();
29
29
  let providerHealth = {
30
30
  ok: false,
31
+ provider: providerId,
32
+ configured: false,
33
+ checkedAt,
31
34
  details: "provider health probe unavailable"
32
35
  };
33
36
  let addresses;
@@ -53,6 +56,9 @@ async function readWalletStatusSnapshot(params) {
53
56
  } catch (err) {
54
57
  providerHealth = {
55
58
  ok: false,
59
+ provider: providerId,
60
+ configured: false,
61
+ checkedAt,
56
62
  details: walletDiagnosticErrorString(err)
57
63
  };
58
64
  }
@@ -90,6 +96,7 @@ async function readWalletStatusSnapshot(params) {
90
96
  }
91
97
  },
92
98
  approvalAuth,
99
+ ...providerHealth.nativeSignerApproval ? { nativeSignerApproval: providerHealth.nativeSignerApproval } : {},
93
100
  addresses,
94
101
  paths: {
95
102
  rootDir: paths.rootDir,
@@ -5,8 +5,8 @@ import { loadConfig } from "../config/io.js";
5
5
  import "../config/config.js";
6
6
  import { logoutWeb, readWebSelfId, webAuthExists } from "./auth-store.js";
7
7
  import { resolveWhatsAppAccount } from "./accounts.js";
8
- import { createWaSocket, formatError, getStatusCode, waitForWaConnection } from "./session.js";
9
8
  import { renderQrPngBase64 } from "./qr-image.js";
9
+ import { createWaSocket, formatError, getStatusCode, waitForWaConnection } from "./session.js";
10
10
  import { randomUUID } from "node:crypto";
11
11
  import { DisconnectReason } from "@whiskeysockets/baileys";
12
12
 
@@ -7,12 +7,13 @@ import { ensureAuthProfileStore } from "../agents/auth-profiles/store.js";
7
7
  import { readConfigFileSnapshot, writeConfigFile } from "../config/io.js";
8
8
  import "../config/config.js";
9
9
  import "../agents/auth-profiles.js";
10
- import { ensureWalletStateDir, resolveLocalSignerControlSocketPath, resolveLocalSignerMaterialRootDir, resolveLocalSignerSocketPath } from "../wallet/wallet-runtime-config.js";
10
+ import { ensureWalletStateDir, resolveLocalSignerMaterialRootDir, resolveLocalSignerSocketPath } from "../wallet/wallet-runtime-config.js";
11
11
  import { checkNamedWalletDeletionSafety, deleteNamedWallet, readWalletProviderRegistry, resolveWalletUserRole, setDefaultWallet, setNamedWalletRole, upsertNamedWallet } from "../wallet/wallet-provider-registry.js";
12
12
  import { DEFAULT_WORKSPACE, applyWizardMetadata, ensureWorkspaceAndSessions, handleOnboardingRepair, printWizardHeader, probeGatewayReachable, summarizeExistingConfig } from "../commands/onboard-helpers.js";
13
13
  import { configureSignerOwnedWalletNetwork } from "../wallet/signer-network-admin.js";
14
14
  import { configureWalletForOnboarding, installSignerdBinary, resolveSignerdBinaryPath, restartLocalSocketSigner } from "./onboarding.wallet.js";
15
- import { collectWalletSignerDoctorReport, walletSetupCommand } from "../commands/wallet.js";
15
+ import { collectWalletSignerDoctorReport, walletRecoveryExportCommand, walletSetupCommand } from "../commands/wallet.js";
16
+ import { lockSignerOwnedWalletForArchive } from "../wallet/local-socket-signer-archive.js";
16
17
  import { promptAuthChoiceGrouped } from "../commands/auth-choice-prompt.js";
17
18
  import { applyPrimaryModel, resolveAuthenticatedDefaultModel } from "../commands/model-picker.js";
18
19
  import { logConfigUpdated } from "../config/logging.js";
@@ -26,7 +27,6 @@ import { promptCustomApiConfig } from "../commands/onboard-custom.js";
26
27
  import { setupInternalHooks } from "../commands/onboard-hooks.js";
27
28
  import { promptRemoteGatewayConfig } from "../commands/onboard-remote.js";
28
29
  import { setupSkills } from "../commands/onboard-skills.js";
29
- import { lockSignerOwnedWalletForArchive } from "../wallet/local-socket-signer-archive.js";
30
30
  import { discoverSolanaNetworkFromRpc } from "../wallet/solana-network-discovery.js";
31
31
  import { isHostedSecurityCapableSession } from "./host-security-capability.js";
32
32
  import { noteBullet, noteCommand, noteHeading, noteLabel, noteStep, noteSuccess, noteWarn } from "./onboarding-note-format.js";
@@ -96,10 +96,6 @@ async function runOnboardingWizard(opts, runtime = defaultRuntime, prompter) {
96
96
  }
97
97
  return "FASED_WALLET_SOLANA_RPC_URL";
98
98
  };
99
- const executionFallbackRpcEnvKeyFor = (walletId) => {
100
- const suffix = walletIdEnvSuffix(walletId);
101
- return suffix ? `FASED_WALLET_SOLANA_EXECUTION_FALLBACK_RPC_URL__${suffix}` : "FASED_WALLET_SOLANA_EXECUTION_FALLBACK_RPC_URL";
102
- };
103
99
  const setConfigEnvVar = (cfg, key, value) => {
104
100
  const vars = { ...cfg.env?.vars };
105
101
  if (value == null || value === "") {
@@ -219,15 +215,6 @@ async function runOnboardingWizard(opts, runtime = defaultRuntime, prompter) {
219
215
  process.env[rpcKey] = effectiveRpcUrl;
220
216
  return effectiveRpcUrl;
221
217
  };
222
- const firstNonEmptyEnvValue = (...values) => {
223
- for (const value of values) {
224
- const normalized = String(value ?? "").trim();
225
- if (normalized) {
226
- return normalized;
227
- }
228
- }
229
- return undefined;
230
- };
231
218
  const readSatMiningConfig = (cfg) => {
232
219
  const config = cfg.plugins?.entries?.["sat-mining"]?.config;
233
220
  if (!config || typeof config !== "object" || Array.isArray(config)) {
@@ -991,28 +978,23 @@ async function runOnboardingWizard(opts, runtime = defaultRuntime, prompter) {
991
978
  currentValue: configuredSolanaRpcUrl
992
979
  });
993
980
  let signerNetworkVersion;
994
- let signerNetworkRootCommand;
995
981
  if (targetWallet.providerId === "local-socket-signer") {
996
982
  const effectiveEnv = {
997
983
  ...process.env,
998
984
  ...nextConfig.env?.vars,
999
985
  FASED_HOST_PROFILE: hostProfile
1000
986
  };
1001
- const walletSuffix = walletIdEnvSuffix(walletId);
1002
- const legacyExecutionFallbackKey = walletSuffix ? `FASED_WALLET_SOLANA_WRITE_RPC_FALLBACK_URL__${walletSuffix}` : "FASED_WALLET_SOLANA_WRITE_RPC_FALLBACK_URL";
1003
- const network = configureSignerOwnedWalletNetwork({
987
+ const network = await configureSignerOwnedWalletNetwork({
1004
988
  walletId,
1005
989
  primaryRpcUrl: effectiveSolanaRpcUrl,
1006
- executionFallbackRpcUrl: firstNonEmptyEnvValue(effectiveEnv[executionFallbackRpcEnvKeyFor(walletId)], effectiveEnv[legacyExecutionFallbackKey]),
1007
990
  env: effectiveEnv,
1008
- signerBinPath: hostProfile === "hosting" ? undefined : resolveSignerdBinaryPath(effectiveEnv)
991
+ socketPath: resolveLocalSignerSocketPath(effectiveEnv)
1009
992
  });
1010
993
  signerNetworkVersion = network.version;
1011
- signerNetworkRootCommand = network.rootCommand;
1012
994
  } else {
1013
995
  await restartLocalSocketSigner(ensureWalletStateDir(process.env).rootDir);
1014
996
  }
1015
- await prompter.note([`Updated the app-side Solana RPC setting for ${targetWallet.name} (${walletId})${signerNetworkVersion ? `; signer network version ${signerNetworkVersion} is ready` : "."}`, ...signerNetworkRootCommand ? ["The hosted signer was not changed because the app has no root channel. Activate the RPC from a root-owned file:", signerNetworkRootCommand] : []].join("\n"), "Wallet setup");
997
+ await prompter.note([`Updated the app-side Solana RPC setting for ${targetWallet.name} (${walletId})${signerNetworkVersion ? `; signer network version ${signerNetworkVersion} is ready` : "."}`].join("\n"), "Wallet setup");
1016
998
  addAnotherWallet = await prompter.confirm({
1017
999
  message: "Run another wallet setup action?",
1018
1000
  initialValue: false
@@ -1160,8 +1142,8 @@ async function runOnboardingWizard(opts, runtime = defaultRuntime, prompter) {
1160
1142
  }
1161
1143
  attemptedSelfHostedSetupThisRun = true;
1162
1144
  const chain = "solana";
1163
- const walletPurpose = flow === "quickstart" && !snapshot.exists ? "agent" : await prompter.select({
1164
- message: "Wallet purpose",
1145
+ const walletPurpose = await prompter.select({
1146
+ message: "Wallet role (required)",
1165
1147
  options: [
1166
1148
  {
1167
1149
  value: "agent",
@@ -1175,9 +1157,11 @@ async function runOnboardingWizard(opts, runtime = defaultRuntime, prompter) {
1175
1157
  value: "vault",
1176
1158
  label: "Vault"
1177
1159
  }
1178
- ],
1179
- initialValue: "agent"
1160
+ ]
1180
1161
  });
1162
+ if (walletPurpose !== "agent" && walletPurpose !== "mining" && walletPurpose !== "vault") {
1163
+ throw new Error("Wallet role selection is required; Agent is never selected silently.");
1164
+ }
1181
1165
  const selfHostedAction = await prompter.select({
1182
1166
  message: "Wallet action",
1183
1167
  options: [{
@@ -1193,7 +1177,7 @@ async function runOnboardingWizard(opts, runtime = defaultRuntime, prompter) {
1193
1177
  const existingMiningWalletId = satMiningAttachment.walletId ?? "";
1194
1178
  const existingMiningWallet = readWalletProviderRegistry(process.env).wallets.find((wallet) => wallet.id === "mining");
1195
1179
  if (existingMiningWalletId || existingMiningWallet) {
1196
- await prompter.note([`Mining wallet already exists: ${existingMiningWalletId || "mining"}.`, "Delete that Mining wallet from onboarding first if you want to replace it."].join("\n"), "Mining");
1180
+ await prompter.note([`Mining wallet already exists: ${existingMiningWalletId || "mining"}.`, "Open it to continue, or use the reviewed Replace/Archive flow after mining is stopped, rewards and capital are settled, and backup/readiness checks pass."].join("\n"), "Mining");
1197
1181
  addAnotherWallet = await prompter.confirm({
1198
1182
  message: "Run another wallet setup action?",
1199
1183
  initialValue: false
@@ -1201,39 +1185,31 @@ async function runOnboardingWizard(opts, runtime = defaultRuntime, prompter) {
1201
1185
  continue;
1202
1186
  }
1203
1187
  }
1204
- const mode = "local-signer-create";
1188
+ const mode = selfHostedAction === "import" ? "local-signer-import" : "local-signer-create";
1205
1189
  const walletIdentity = await resolveWalletIdentityForOnboarding({
1206
1190
  flow,
1207
1191
  purpose: walletPurpose
1208
1192
  });
1209
1193
  const walletName = walletIdentity.walletName;
1210
1194
  const walletId = walletIdentity.walletId || undefined;
1211
- if (selfHostedAction === "import") {
1212
- const effectiveWalletId = walletId ?? walletPurpose;
1213
- const hosting = hostProfile === "hosting";
1214
- const signerBin = hosting ? "/opt/fased/signer/fased-signerd" : resolveSignerdBinaryPath();
1215
- const controlSocket = hosting ? "/run/fased-signerd/control.sock" : resolveLocalSignerControlSocketPath(process.env);
1216
- const signerImportCommand = [
1217
- shellQuote(signerBin),
1218
- "admin wallet import",
1219
- `--control-socket ${shellQuote(controlSocket)}`,
1220
- `--wallet-id ${shellQuote(effectiveWalletId)}`,
1221
- `--locked-role ${shellQuote(walletPurpose)}`,
1222
- `< ${shellQuote("/absolute/path/to/solana-keypair.json")}`
1223
- ].join(" ");
1224
- const command = hosting ? `/usr/sbin/runuser -u fased-signer -- /usr/bin/env HOME=/var/lib/fased-signerd ${signerImportCommand}` : signerImportCommand;
1195
+ if (selfHostedAction === "import" && hostProfile === "hosting") {
1196
+ const signerWalletId = (walletId ?? walletPurpose).trim().toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
1225
1197
  await prompter.note([
1226
- "The normal Gateway and Node wizard paths do not accept plaintext wallet keys. Local same-user isolation is not a hard compromise boundary; Hosting isolates the signer account.",
1227
- hosting ? "Open the VPS provider root console and run this root-administrator command. Do not run it from the app shell and do not grant app sudo:" : "Run this from an authenticated terminal on the signer host:",
1228
- command,
1229
- "Then rerun wallet setup and choose Manage wallet to register/verify the returned public address."
1230
- ].join("\n"), "Native signer import");
1198
+ "Hosting keeps plaintext wallet keys outside the app/Gateway OS account.",
1199
+ "From the VPS provider root console, place the Solana keypair in /root/wallet.json with mode 600, then run:",
1200
+ `/usr/local/sbin/fased-signer-wallet-import --wallet-id ${shellQuote(signerWalletId)} --locked-role ${shellQuote(walletPurpose)} < /root/wallet.json`,
1201
+ "Return to the app account, rerun onboarding, and choose Create with the same role and wallet ID. The signer reuses only that exact existing deny-all wallet, then activates the one RPC and registers it."
1202
+ ].join("\n"), "Signer-owned Hosting import");
1231
1203
  addAnotherWallet = await prompter.confirm({
1232
1204
  message: "Run another wallet setup action?",
1233
1205
  initialValue: false
1234
1206
  });
1235
1207
  continue;
1236
1208
  }
1209
+ const importFile = selfHostedAction === "import" ? (await prompter.text({
1210
+ message: "Absolute path to owner-only Solana keypair JSON",
1211
+ validate: (value) => value.trim().startsWith("/") ? undefined : "Enter an absolute path and run chmod 600 on the file first"
1212
+ })).trim() : undefined;
1237
1213
  const rpcKey = rpcEnvKeyFor(chain, walletId);
1238
1214
  const currentRpcValue = (nextConfig.env?.vars?.[rpcKey] ?? "").trim() || String(process.env[rpcKey] ?? "").trim();
1239
1215
  const rpcUrlInput = (await prompter.text({
@@ -1245,8 +1221,6 @@ async function runOnboardingWizard(opts, runtime = defaultRuntime, prompter) {
1245
1221
  if (!effectiveRpcUrl) {
1246
1222
  throw new Error(`${chain.toUpperCase()} RPC URL is required for wallets.`);
1247
1223
  }
1248
- nextConfig = setConfigEnvVar(nextConfig, rpcKey, effectiveRpcUrl);
1249
- process.env[rpcKey] = effectiveRpcUrl;
1250
1224
  try {
1251
1225
  if (mode === "local-signer-create") {
1252
1226
  try {
@@ -1256,6 +1230,7 @@ async function runOnboardingWizard(opts, runtime = defaultRuntime, prompter) {
1256
1230
  walletId,
1257
1231
  walletName,
1258
1232
  rpcUrl: effectiveRpcUrl,
1233
+ role: walletPurpose,
1259
1234
  force: true,
1260
1235
  noDoctor: true,
1261
1236
  noSignerHints: true,
@@ -1277,6 +1252,7 @@ async function runOnboardingWizard(opts, runtime = defaultRuntime, prompter) {
1277
1252
  walletId,
1278
1253
  walletName,
1279
1254
  rpcUrl: effectiveRpcUrl,
1255
+ role: walletPurpose,
1280
1256
  force: true,
1281
1257
  noDoctor: true,
1282
1258
  noSignerHints: true,
@@ -1286,17 +1262,22 @@ async function runOnboardingWizard(opts, runtime = defaultRuntime, prompter) {
1286
1262
  throw err;
1287
1263
  }
1288
1264
  }
1289
- if (hostProfile === "hosting") {
1290
- const nativeWalletId = (walletId ?? walletPurpose).trim().toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
1291
- await prompter.note([
1292
- "The wallet key was created inside the root-managed signer with deny-all policy.",
1293
- "The Gateway has no sudo or root control socket, so RPC activation is a separate host-administrator step.",
1294
- "From a provider/root console, copy the installed network template to a root-owned mode-0600 file, set the exact RPC URLs, then run:",
1295
- `/usr/local/sbin/fased-signer-network --wallet-id ${nativeWalletId} --network-file /root/fased-network.json`,
1296
- "Signing stays disabled until network activation, owner enrollment, and an explicit positive-cap policy all succeed."
1297
- ].join("\n"), "Hosted wallet owner handoff");
1298
- }
1265
+ } else {
1266
+ await walletSetupCommand(runtime, {
1267
+ mode,
1268
+ chain,
1269
+ walletId,
1270
+ walletName,
1271
+ role: walletPurpose,
1272
+ rpcUrl: effectiveRpcUrl,
1273
+ importFile,
1274
+ noDoctor: true,
1275
+ noSignerHints: true,
1276
+ nonInteractive: true
1277
+ });
1299
1278
  }
1279
+ nextConfig = setConfigEnvVar(nextConfig, rpcKey, effectiveRpcUrl);
1280
+ process.env[rpcKey] = effectiveRpcUrl;
1300
1281
  walletCeremonyEvents.push({
1301
1282
  mode,
1302
1283
  chain,
@@ -1307,6 +1288,25 @@ async function runOnboardingWizard(opts, runtime = defaultRuntime, prompter) {
1307
1288
  ok: true
1308
1289
  });
1309
1290
  createdOrImportedSelfHostedWalletThisRun = true;
1291
+ if (hostProfile !== "hosting") {
1292
+ const exportRecovery = await prompter.confirm({
1293
+ message: "Create an encrypted recovery package now?",
1294
+ initialValue: true
1295
+ });
1296
+ if (exportRecovery) {
1297
+ const defaultRecoveryPath = path.join(process.env.HOME || process.cwd(), `fased-${walletId ?? walletPurpose}-recovery.json`);
1298
+ const recoveryOutput = (await prompter.text({
1299
+ message: "New encrypted recovery package path",
1300
+ initialValue: defaultRecoveryPath,
1301
+ validate: (value) => path.isAbsolute(value.trim()) ? undefined : "Enter an absolute path for a new file"
1302
+ })).trim();
1303
+ await walletRecoveryExportCommand(runtime, {
1304
+ walletId: walletId ?? walletPurpose,
1305
+ output: recoveryOutput
1306
+ });
1307
+ await prompter.note([`Encrypted recovery package: ${recoveryOutput}`, "Keep the recovery password separately. The file contains ciphertext, wallet id, role, public address, and format metadata; it never contains plaintext key material."].join("\n"), "Wallet recovery");
1308
+ }
1309
+ }
1310
1310
  const agentDefaultBefore = readAgentDefaultWallet();
1311
1311
  const existingWallet = readWalletProviderRegistry(process.env).wallets.find((entry) => entry.id === walletId);
1312
1312
  upsertNamedWallet({
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fased/acpx",
3
- "version": "0.1.66",
3
+ "version": "0.1.68",
4
4
  "description": "FasedAgent ACP runtime backend via acpx",
5
5
  "type": "module",
6
6
  "dependencies": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fased/bluebubbles",
3
- "version": "0.1.66",
3
+ "version": "0.1.68",
4
4
  "description": "FasedAgent BlueBubbles channel plugin",
5
5
  "type": "module",
6
6
  "fased": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fased/copilot-proxy",
3
- "version": "0.1.66",
3
+ "version": "0.1.68",
4
4
  "private": true,
5
5
  "description": "FasedAgent Copilot Proxy provider plugin",
6
6
  "type": "module",