@fased/fased 0.1.67 → 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.
- package/CHANGELOG.md +12 -0
- package/config/signer-protocol-v2.json +4 -0
- package/dist/brand.js +1 -1
- package/dist/build-info.json +3 -3
- package/dist/canvas-host/a2ui/.bundle.hash +1 -1
- package/dist/cli/program/register.wallet.js +33 -2
- package/dist/commands/wallet.js +485 -58
- package/dist/control-ui/assets/{app-De8gTtZU.js → app-DyPTS0oM.js} +1526 -1526
- package/dist/control-ui/assets/{index-DYYBRBOS.js → index-Cu13yYHr.js} +3 -3
- package/dist/control-ui/assets/{wallet-DvStR_AT.js → wallet-ByKBcYhm.js} +272 -188
- package/dist/control-ui/index.html +1 -1
- package/dist/control-ui/version.json +1 -1
- package/dist/gateway/server-http.js +349 -47
- package/dist/plugin-sdk/{actions-DRu135h0.js → actions-B-Vwj64K.js} +3 -3
- package/dist/plugin-sdk/{allow-list-DyAxs4yw.js → allow-list-Bf45HfoP.js} +1 -1
- package/dist/plugin-sdk/{audio-preflight-Dq_rJtqH.js → audio-preflight-_FVQjR1r.js} +25 -24
- package/dist/plugin-sdk/{audit-C5d2z5U1.js → audit-CUULWG22.js} +1 -1
- package/dist/plugin-sdk/{channel-access-configure-DaI5wevh.js → channel-access-configure-BoUsC-Ys.js} +1 -1
- package/dist/plugin-sdk/{channel-adapters.discord-BS4S4Svh.js → channel-adapters.discord-DSiR4X2Y.js} +1 -1
- package/dist/plugin-sdk/channel-plugin-common.d.ts +2 -2
- package/dist/plugin-sdk/channel-plugin-common.js +1 -1
- package/dist/plugin-sdk/{chunk-CyoyqpbG.js → chunk-CBpl-AiQ.js} +1 -1
- package/dist/plugin-sdk/{chunk-items-Dunoq4Er.js → chunk-items-LH4uKSnh.js} +1 -1
- package/dist/plugin-sdk/command-status.js +25 -24
- package/dist/plugin-sdk/{commands-registry-BfqdEWdF.js → commands-registry-G5KXSi_y.js} +1 -1
- package/dist/plugin-sdk/{config-schema--vl89M7d.js → config-schema-DjU_uD-6.js} +186 -88
- package/dist/plugin-sdk/device-pair.d.ts +1 -1
- package/dist/plugin-sdk/{diagnostic-DLL_zekv.js → diagnostic-sYs6Agr-.js} +1 -1
- package/dist/plugin-sdk/{directory-live-CSuYxdwk.js → directory-live-BXMpuYAF.js} +1 -1
- package/dist/plugin-sdk/{directory-live-Dw7ZgC9_.js → directory-live-Rn1BzPof.js} +1 -1
- package/dist/plugin-sdk/discord.d.ts +4 -4
- package/dist/plugin-sdk/discord.js +20 -20
- package/dist/plugin-sdk/{draft-stream-controls-BqchW1vi.js → draft-stream-controls-CMNdKSx-.js} +1 -1
- package/dist/plugin-sdk/{guilds-CDhRIX57.js → guilds-Dkwl0yHv.js} +1 -1
- package/dist/plugin-sdk/{handle-action-hvwapMKg.js → handle-action-CRdM6B-8.js} +15 -15
- package/dist/plugin-sdk/index.d.ts +13 -13
- package/dist/plugin-sdk/index.js +27 -26
- package/dist/plugin-sdk/{login-Ca583Okc.js → login-D7WnK8-P.js} +1 -1
- package/dist/plugin-sdk/{login-qr-CGciBSNP.js → login-qr-CRueOKZK.js} +2 -114
- package/dist/plugin-sdk/{markdown-tables-DCOXEUfZ.js → markdown-tables-d7GlUs5R.js} +1 -1
- package/dist/plugin-sdk/{media-CAdQF5TG.js → media-BAoaxCrt.js} +1 -1
- package/dist/plugin-sdk/{monitor-DrTy_4CL.js → monitor-DUMMbG4h.js} +33 -32
- package/dist/plugin-sdk/{monitor-CV1sr0p2.js → monitor-DXIM16Hj.js} +41 -40
- package/dist/plugin-sdk/{openresponses-http-BAjgURZa.js → openresponses-http-C8lKwta1.js} +25 -24
- package/dist/plugin-sdk/{outbound-attachment-QqTnIhZL.js → outbound-attachment-CyXM3cAv.js} +1 -1
- package/dist/plugin-sdk/{outbound-BgVDdHxx.js → outbound-zB6llMGW.js} +6 -6
- package/dist/plugin-sdk/{pairing-message-BjIJYVvp.d.ts → pairing-message-mKO92L-M.d.ts} +1 -1
- package/dist/plugin-sdk/{pairing-store-BChahgI2.js → pairing-store-B6J2C0vP.js} +1 -1
- package/dist/plugin-sdk/{pi-model-discovery-runtime-DxTMz_XF.js → pi-model-discovery-runtime-6JzLw9Gk.js} +1 -1
- package/dist/plugin-sdk/{probe-CJb5uR8B.js → probe-BM7Oe40M.js} +2 -2
- package/dist/plugin-sdk/{probe-l_whvGLY.js → probe-CMPksimh.js} +1 -1
- package/dist/plugin-sdk/{probe-BFroeK3g.js → probe-oVp12D6L.js} +1 -1
- package/dist/plugin-sdk/provider-web-search-config-contract.d.ts +1 -1
- package/dist/plugin-sdk/qr-image-B4MlMG4N.js +115 -0
- package/dist/plugin-sdk/{replies-ChYo5ZQM.js → replies-CY-V0Zob.js} +3 -3
- package/dist/plugin-sdk/{resolve-channels-87LZrFk_.js → resolve-channels-1HtDcMqh.js} +2 -2
- package/dist/plugin-sdk/{resolve-channels-_-nV-EK2.js → resolve-channels-CU72Qh2j.js} +1 -1
- package/dist/plugin-sdk/{resolve-outbound-target-D-KGgHJZ.js → resolve-outbound-target-nQSBVYz9.js} +2 -2
- package/dist/plugin-sdk/{resolve-route-C8tvQm8Z.js → resolve-route-Dufnu2cT.js} +1 -1
- package/dist/plugin-sdk/{resolve-users-DN9VQBwv.js → resolve-users-Bk_ss2vC.js} +1 -1
- package/dist/plugin-sdk/{resolve-users-V6dHh_2B.js → resolve-users-IevCOk4W.js} +2 -2
- package/dist/plugin-sdk/{runtime-2B9_WKoI.js → runtime-CXSQwMaR.js} +1 -1
- package/dist/plugin-sdk/sat-runtime.d.ts +18 -2
- package/dist/plugin-sdk/sat-runtime.js +25 -24
- package/dist/plugin-sdk/{send-DCO441U7.js → send-BXBS_mRK.js} +8 -8
- package/dist/plugin-sdk/{send-CFzGKJXo.js → send-D-HfKQZm.js} +4 -4
- package/dist/plugin-sdk/{send-B6KkGKZD.js → send-D1j12ZgX.js} +5 -5
- package/dist/plugin-sdk/{send-q9HXrSwK.js → send-DWKm6yay.js} +10 -10
- package/dist/plugin-sdk/{send-gDoGA-7e.js → send-Di1Jit3L.js} +7 -7
- package/dist/plugin-sdk/{skill-commands-FKEPLS7T.js → skill-commands-iJtvpqgb.js} +2 -2
- package/dist/plugin-sdk/{slack-DSC6T2KW.js → slack-B-nOOLM0.js} +44 -43
- package/dist/plugin-sdk/{slack-actions-DhbMcp8M.js → slack-actions-BzUEszJa.js} +10 -10
- package/dist/plugin-sdk/slack.d.ts +4 -4
- package/dist/plugin-sdk/slack.js +6 -6
- package/dist/plugin-sdk/{sticker-cache-C0l_5IKg.js → sticker-cache-BLiId8Em.js} +2 -2
- package/dist/plugin-sdk/{tables-B3jr0Pvq.js → tables-_1W36GII.js} +2 -2
- package/dist/plugin-sdk/{targets-CtSbTxYX.js → targets-DDbNnLnB.js} +2 -2
- package/dist/plugin-sdk/{telegram-actions-BwGBIVDE.js → telegram-actions-1X9A32Dn.js} +29 -28
- package/dist/plugin-sdk/telegram.d.ts +4 -4
- package/dist/plugin-sdk/telegram.js +3 -3
- package/dist/plugin-sdk/{thread-bindings.lifecycle-QbkNl-m6.js → thread-bindings.lifecycle-Y9T3CQGf.js} +3 -3
- package/dist/plugin-sdk/{thread-bindings.state-D9hg_M1c.js → thread-bindings.state-D2oOWDpv.js} +1 -1
- package/dist/plugin-sdk/{tmp-fased-dir-DXnGKKgi.d.ts → tmp-fased-dir-BkpWlGUe.d.ts} +7 -1
- package/dist/plugin-sdk/{types-Cegc86EP.d.ts → types-B-zL9gSv.d.ts} +2 -2
- package/dist/plugin-sdk/{types-eG9aYqXF.d.ts → types-DtCOO_hW.d.ts} +1 -1
- package/dist/plugin-sdk/{web-C_AH5PyB.js → web-BvjNyYBz.js} +32 -31
- package/dist/plugin-sdk/{whatsapp-actions-Bj_rOncr.js → whatsapp-actions-Blu9-eX-.js} +9 -9
- package/dist/plugin-sdk/{whatsapp-heartbeat-B1WHkJIg.js → whatsapp-heartbeat-DjmQM7hq.js} +2 -2
- package/dist/plugin-sdk/whatsapp.d.ts +7 -7
- package/dist/plugin-sdk/whatsapp.js +7 -7
- package/dist/plugin-sdk/{wsl-J-HscWxS.js → wsl-CTvIWvZz.js} +1 -1
- package/dist/plugin-sdk/{zod-schema.providers-core-FaYR6KdD.d.ts → zod-schema.providers-core-BCDlgtls.d.ts} +52 -52
- package/dist/wallet/local-socket-signer-lifecycle.js +29 -1
- package/dist/wallet/local-socket-signer-protocol.js +27 -0
- package/dist/wallet/providers/local-socket-signer-adapter.js +8 -0
- package/dist/wallet/signer-network-admin.js +11 -118
- package/dist/wallet/signer-protocol-v2.generated.js +4 -0
- package/dist/wallet/solana-assets.js +9 -3
- package/dist/wallet/solana-rpc-read-fetch.js +65 -0
- package/dist/wallet/wallet-status.js +7 -0
- package/dist/web/login-qr.js +1 -1
- package/dist/wizard/onboarding.js +61 -61
- package/extensions/acpx/package.json +1 -1
- package/extensions/bluebubbles/package.json +1 -1
- package/extensions/copilot-proxy/package.json +1 -1
- package/extensions/diagnostics-otel/package.json +1 -1
- package/extensions/fased-federation/package.json +1 -1
- package/extensions/google-gemini-cli-auth/package.json +1 -1
- package/extensions/imessage/package.json +1 -1
- package/extensions/irc/package.json +1 -1
- package/extensions/line/package.json +1 -1
- package/extensions/llm-task/package.json +1 -1
- package/extensions/lobster/package.json +1 -1
- package/extensions/matrix/CHANGELOG.md +6 -0
- package/extensions/matrix/package.json +1 -1
- package/extensions/mattermost/package.json +1 -1
- package/extensions/memory-core/package.json +2 -2
- package/extensions/memory-lancedb/package.json +1 -1
- package/extensions/minimax-portal-auth/package.json +1 -1
- package/extensions/msteams/CHANGELOG.md +6 -0
- package/extensions/msteams/package.json +1 -1
- package/extensions/nextcloud-talk/package.json +1 -1
- package/extensions/nostr/CHANGELOG.md +6 -0
- package/extensions/nostr/package.json +1 -1
- package/extensions/open-prose/package.json +1 -1
- package/extensions/sat-mining/package.json +1 -1
- package/extensions/signal/package.json +1 -1
- package/extensions/synology-chat/package.json +1 -1
- package/extensions/tlon/package.json +1 -1
- package/extensions/twitch/CHANGELOG.md +6 -0
- package/extensions/twitch/package.json +1 -1
- package/extensions/voice-call/CHANGELOG.md +6 -0
- package/extensions/voice-call/package.json +1 -1
- package/extensions/zalo/CHANGELOG.md +6 -0
- package/extensions/zalo/package.json +1 -1
- package/extensions/zalouser/CHANGELOG.md +6 -0
- package/extensions/zalouser/package.json +1 -1
- package/install.sh +16 -10
- package/package.json +2 -1
- package/scripts/fased-managed-updater.mjs +10 -2
- package/scripts/fased-signer-wallet-import-hosting.sh +78 -0
- package/scripts/install-managed-runtime.mjs +10 -7
- /package/dist/plugin-sdk/{active-listener-B_NMtgPL.js → active-listener-p9Ooc1qj.js} +0 -0
- /package/dist/plugin-sdk/{blocks-input-Bg-8S2pl.js → blocks-input-DiZaqaN7.js} +0 -0
- /package/dist/plugin-sdk/{channel-access-CcFRdRPO.js → channel-access-B0aZBSd2.js} +0 -0
- /package/dist/plugin-sdk/{channel-activity-DBDMQ3yI.js → channel-activity-F07DjroV.js} +0 -0
- /package/dist/plugin-sdk/{channel-core-CFRs8LPy.js → channel-core-DoF4X04L.js} +0 -0
- /package/dist/plugin-sdk/{client-pI228AVT.js → client-DLY-kWF_.js} +0 -0
- /package/dist/plugin-sdk/{command-poll-backoff-yJU83wEq.js → command-poll-backoff-NAePk3eL.js} +0 -0
- /package/dist/plugin-sdk/{date-time-uUevHscU.js → date-time-ChY_tHoG.js} +0 -0
- /package/dist/plugin-sdk/{draft-stream-loop-CrOPfV3F.js → draft-stream-loop-DMBwjjd1.js} +0 -0
- /package/dist/plugin-sdk/{fetch-BtmoPIlf.js → fetch-B0v6OTUZ.js} +0 -0
- /package/dist/plugin-sdk/{filesystem-manager-DoI8IBNF.js → filesystem-manager-BzuIJ9K9.js} +0 -0
- /package/dist/plugin-sdk/{gateway-registry-DZDsqicG.js → gateway-registry-Bgl2GFgy.js} +0 -0
- /package/dist/plugin-sdk/{inbound-context-DrAcXE0y.js → inbound-context-CQnfFFQ9.js} +0 -0
- /package/dist/plugin-sdk/{pi-model-discovery-BX_JLcEJ.js → pi-model-discovery-BHNVyRny.js} +0 -0
- /package/dist/plugin-sdk/{polls-DKQXVfJp.js → polls-DXd67TD8.js} +0 -0
- /package/dist/plugin-sdk/{proxy-DWpYcsvR.js → proxy-CLRUdShp.js} +0 -0
- /package/dist/plugin-sdk/{pw-ai-DlxqX6Es.js → pw-ai-DJ_gTc4M.js} +0 -0
- /package/dist/plugin-sdk/{qmd-manager-CgRaTxUq.js → qmd-manager-B8Vnu0ND.js} +0 -0
- /package/dist/plugin-sdk/{render-e7fENCYH.js → render-DrBfVs0u.js} +0 -0
- /package/dist/plugin-sdk/{reply-reference-Bqk-7HXE.js → reply-reference-BnAk5Wjb.js} +0 -0
- /package/dist/plugin-sdk/{retry-policy-C6SjKjQA.js → retry-policy-CXh1owkc.js} +0 -0
- /package/dist/plugin-sdk/{sandbox-paths-Dx2lUqCu.js → sandbox-paths-Bzh1CuZc.js} +0 -0
- /package/dist/plugin-sdk/{session-DHD4ZKyG.js → session-iuGR_rdi.js} +0 -0
- /package/dist/plugin-sdk/{shared-CBgp_GBw.js → shared-D1AD2Ejn.js} +0 -0
- /package/dist/plugin-sdk/{target-errors-Ckufe179.js → target-errors-DfS1sLR9.js} +0 -0
- /package/dist/plugin-sdk/{tool-loop-detection-Bp8bPJLs.js → tool-loop-detection-sDZl27w7.js} +0 -0
- /package/dist/plugin-sdk/{ui-Be8cLHIS.js → ui-GQDYm_jw.js} +0 -0
- /package/dist/plugin-sdk/{with-timeout-ckWB8rTo.js → with-timeout-CVV8tytk.js} +0 -0
package/dist/commands/wallet.js
CHANGED
|
@@ -18,6 +18,7 @@ import { buildWalletCanaryReport, runWalletProviderCanaryReport } from "../walle
|
|
|
18
18
|
import { installSignerdBinary, resolveSignerdBinaryPath, restartLocalSocketSigner, writeLocalSignerEnvFile } from "../wizard/onboarding.wallet.js";
|
|
19
19
|
import fs from "node:fs";
|
|
20
20
|
import path from "node:path";
|
|
21
|
+
import { spawnSync } from "node:child_process";
|
|
21
22
|
import { createInterface } from "node:readline/promises";
|
|
22
23
|
|
|
23
24
|
//#region src/commands/wallet.ts
|
|
@@ -261,10 +262,6 @@ function rpcEnvKeyFor(chain, walletId) {
|
|
|
261
262
|
}
|
|
262
263
|
return "FASED_WALLET_SOLANA_RPC_URL";
|
|
263
264
|
}
|
|
264
|
-
function executionFallbackRpcEnvKeyFor(walletId) {
|
|
265
|
-
const suffix = walletIdEnvSuffix(walletId);
|
|
266
|
-
return suffix ? `FASED_WALLET_SOLANA_EXECUTION_FALLBACK_RPC_URL__${suffix}` : "FASED_WALLET_SOLANA_EXECUTION_FALLBACK_RPC_URL";
|
|
267
|
-
}
|
|
268
265
|
function resolveRpcUrlForChain(env, chain, walletId, explicit) {
|
|
269
266
|
const explicitValue = explicit?.trim();
|
|
270
267
|
if (explicitValue) {
|
|
@@ -381,17 +378,23 @@ async function createSignerOwnedWalletForSetup(params) {
|
|
|
381
378
|
const walletId = params.walletId?.trim() || params.role;
|
|
382
379
|
const hosted = String(params.env.FASED_HOST_PROFILE ?? "").trim().toLowerCase() === "hosting";
|
|
383
380
|
let cfg = ensureLocalSignerProviderConfig(loadConfig(), params.env);
|
|
384
|
-
if (!hosted) {
|
|
385
|
-
cfg = setConfigEnvVar(cfg, rpcEnvKeyFor(params.chain, walletId), params.rpcUrl);
|
|
386
|
-
}
|
|
387
|
-
await writeConfigFile(cfg);
|
|
388
381
|
const mergedEnv = {
|
|
389
382
|
...params.env,
|
|
390
383
|
...cfg.env?.vars
|
|
391
384
|
};
|
|
392
385
|
const socketPath = resolveLocalSignerSocketPath(mergedEnv);
|
|
393
386
|
const expectedSignerWalletId = normalizeNativeSignerWalletId(walletId);
|
|
394
|
-
const
|
|
387
|
+
const registeredWallets = readWalletProviderRegistry(params.env).wallets;
|
|
388
|
+
if (params.role === "mining") {
|
|
389
|
+
const activeMiningWallet = registeredWallets.find((entry) => {
|
|
390
|
+
const role = normalizeWalletUserRole(entry.metadata?.role ?? entry.metadata?.purpose);
|
|
391
|
+
return role === "mining" || entry.id === "mining";
|
|
392
|
+
});
|
|
393
|
+
if (activeMiningWallet && activeMiningWallet.id !== walletId) {
|
|
394
|
+
throw new Error(`Mining already has one active wallet (${activeMiningWallet.id}). Archive it after the safety checks or complete a reviewed replacement before creating ${walletId}.`);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
const existingSignerIdCollision = findNativeSignerWalletIdCollision(registeredWallets, walletId, expectedSignerWalletId);
|
|
395
398
|
if (existingSignerIdCollision) {
|
|
396
399
|
throw new Error(`native signer wallet ID ${expectedSignerWalletId} is already registered as ${existingSignerIdCollision.id}; choose a distinct wallet ID`);
|
|
397
400
|
}
|
|
@@ -428,15 +431,13 @@ async function createSignerOwnedWalletForSetup(params) {
|
|
|
428
431
|
if (signerIdCollision) {
|
|
429
432
|
throw new Error(`native signer wallet ID ${signerWalletId} is already registered as ${signerIdCollision.id}; choose a distinct wallet ID`);
|
|
430
433
|
}
|
|
431
|
-
const
|
|
432
|
-
const network = configureSignerOwnedWalletNetwork({
|
|
434
|
+
const network = await configureSignerOwnedWalletNetwork({
|
|
433
435
|
walletId: signerWalletId,
|
|
434
436
|
primaryRpcUrl: params.rpcUrl,
|
|
435
|
-
executionFallbackRpcUrl: executionFallbackRpcUrl || undefined,
|
|
436
437
|
env: mergedEnv,
|
|
437
|
-
|
|
438
|
-
controlSocketPath: hosted ? undefined : resolveLocalSignerControlSocketPath(mergedEnv)
|
|
438
|
+
socketPath
|
|
439
439
|
});
|
|
440
|
+
cfg = setConfigEnvVar(cfg, rpcEnvKeyFor(params.chain, walletId), params.rpcUrl);
|
|
440
441
|
await writeConfigFile(cfg);
|
|
441
442
|
const wallet = upsertNamedWallet({
|
|
442
443
|
walletId,
|
|
@@ -463,6 +464,28 @@ async function createSignerOwnedWalletForSetup(params) {
|
|
|
463
464
|
env: params.env
|
|
464
465
|
});
|
|
465
466
|
}
|
|
467
|
+
if (params.role === "mining") {
|
|
468
|
+
const currentEntry = cfg.plugins?.entries?.["sat-mining"];
|
|
469
|
+
const currentSatConfig = currentEntry?.config && typeof currentEntry.config === "object" && !Array.isArray(currentEntry.config) ? currentEntry.config : {};
|
|
470
|
+
cfg = {
|
|
471
|
+
...cfg,
|
|
472
|
+
plugins: {
|
|
473
|
+
...cfg.plugins,
|
|
474
|
+
entries: {
|
|
475
|
+
...cfg.plugins?.entries,
|
|
476
|
+
"sat-mining": {
|
|
477
|
+
enabled: true,
|
|
478
|
+
...currentEntry,
|
|
479
|
+
config: {
|
|
480
|
+
...currentSatConfig,
|
|
481
|
+
walletId: wallet.id
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
};
|
|
487
|
+
await writeConfigFile(cfg);
|
|
488
|
+
}
|
|
466
489
|
if (params.options.json) {
|
|
467
490
|
params.runtime.log(JSON.stringify({
|
|
468
491
|
ok: true,
|
|
@@ -475,25 +498,289 @@ async function createSignerOwnedWalletForSetup(params) {
|
|
|
475
498
|
policyVersion: result.policy.version,
|
|
476
499
|
policyState: "locked",
|
|
477
500
|
networkVersion: network.version,
|
|
478
|
-
networkReady: network.ready
|
|
479
|
-
...network.rootAdminRequired ? {
|
|
480
|
-
networkActivation: "root-admin-required",
|
|
481
|
-
rootCommand: network.rootCommand
|
|
482
|
-
} : {}
|
|
501
|
+
networkReady: network.ready
|
|
483
502
|
}, null, 2));
|
|
484
503
|
} else {
|
|
485
504
|
params.runtime.log(`Signer wallet ID: ${signerWalletId}`);
|
|
486
505
|
params.runtime.log(`${params.chain.toUpperCase()} address: ${result.wallet.publicKey}`);
|
|
487
506
|
if (!params.options.noSignerHints) {
|
|
488
507
|
params.runtime.log("Signer-owned wallet created with a deny-all policy. Configure explicit operations, destinations, assets, and positive caps before sending.");
|
|
489
|
-
if (network.rootAdminRequired && network.rootCommand) {
|
|
490
|
-
params.runtime.log("Hosted wallet remains fail-closed until a root administrator activates its RPC network from a root-owned file:");
|
|
491
|
-
params.runtime.log(network.rootCommand);
|
|
492
|
-
}
|
|
493
508
|
}
|
|
494
509
|
}
|
|
495
510
|
return result;
|
|
496
511
|
}
|
|
512
|
+
function requireOwnerOnlySignerImportFile(rawPath) {
|
|
513
|
+
const importPath = path.resolve(rawPath.trim());
|
|
514
|
+
if (!rawPath.trim() || !path.isAbsolute(rawPath.trim()) || importPath !== rawPath.trim()) {
|
|
515
|
+
throw new Error("--import-file must be an absolute clean path");
|
|
516
|
+
}
|
|
517
|
+
const info = fs.lstatSync(importPath);
|
|
518
|
+
if (!info.isFile() || info.isSymbolicLink() || info.nlink !== 1) {
|
|
519
|
+
throw new Error("wallet import file must be one regular, non-symlink, single-link file");
|
|
520
|
+
}
|
|
521
|
+
if ((info.mode & 63) !== 0) {
|
|
522
|
+
throw new Error("wallet import file must be owner-only (chmod 600)");
|
|
523
|
+
}
|
|
524
|
+
const effectiveUid = process.geteuid?.();
|
|
525
|
+
if (effectiveUid !== undefined && info.uid !== effectiveUid) {
|
|
526
|
+
throw new Error("wallet import file must be owned by the current terminal user");
|
|
527
|
+
}
|
|
528
|
+
const fd = fs.openSync(importPath, fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW);
|
|
529
|
+
try {
|
|
530
|
+
const opened = fs.fstatSync(fd);
|
|
531
|
+
if (!opened.isFile() || opened.nlink !== 1 || opened.dev !== info.dev || opened.ino !== info.ino || (opened.mode & 63) !== 0 || effectiveUid !== undefined && opened.uid !== effectiveUid) {
|
|
532
|
+
throw new Error("wallet import file changed or became unsafe before it could be read");
|
|
533
|
+
}
|
|
534
|
+
return {
|
|
535
|
+
path: importPath,
|
|
536
|
+
fd
|
|
537
|
+
};
|
|
538
|
+
} catch (error) {
|
|
539
|
+
fs.closeSync(fd);
|
|
540
|
+
throw error;
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
function parseNativeSignerImportResult(params) {
|
|
544
|
+
let value;
|
|
545
|
+
try {
|
|
546
|
+
value = JSON.parse(params.stdout.trim());
|
|
547
|
+
} catch {
|
|
548
|
+
throw new Error("native signer import returned invalid JSON");
|
|
549
|
+
}
|
|
550
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
551
|
+
throw new Error("native signer import returned an invalid result");
|
|
552
|
+
}
|
|
553
|
+
const result = value;
|
|
554
|
+
if (result.wallet?.walletId !== params.walletId || typeof result.wallet.publicKey !== "string" || !result.wallet.publicKey.trim() || !Number.isSafeInteger(result.wallet.version) || result.policy?.walletId !== params.walletId || result.policy.role !== params.role || result.policy.version !== 1 || result.policy.operations?.length !== 0 || result.policy.programs?.length !== 0 || result.policy.assets?.length !== 0 || typeof result.policy.hash !== "string" || !/^sha256:[0-9a-f]{64}$/.test(result.policy.hash)) {
|
|
555
|
+
throw new Error("native signer import did not return the exact deny-all wallet result");
|
|
556
|
+
}
|
|
557
|
+
return result;
|
|
558
|
+
}
|
|
559
|
+
function invokeNativeSignerWalletImport(params) {
|
|
560
|
+
if (params.hosted) {
|
|
561
|
+
throw new Error("Hosting private-key import is a signer-owner operation. Run /usr/local/sbin/fased-signer-wallet-import from the provider root console, then rerun wallet setup with Create and the same wallet ID to register the existing signer wallet.");
|
|
562
|
+
}
|
|
563
|
+
const input = requireOwnerOnlySignerImportFile(params.importFile);
|
|
564
|
+
const command = params.signerBinPath;
|
|
565
|
+
const args = [
|
|
566
|
+
"admin",
|
|
567
|
+
"wallet",
|
|
568
|
+
"import",
|
|
569
|
+
"--control-socket",
|
|
570
|
+
params.controlSocketPath,
|
|
571
|
+
"--wallet-id",
|
|
572
|
+
params.walletId,
|
|
573
|
+
"--locked-role",
|
|
574
|
+
params.role
|
|
575
|
+
];
|
|
576
|
+
try {
|
|
577
|
+
const child = spawnSync(command, args, {
|
|
578
|
+
env: {
|
|
579
|
+
HOME: params.env.HOME,
|
|
580
|
+
LANG: params.env.LANG || "C.UTF-8",
|
|
581
|
+
PATH: "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
|
|
582
|
+
},
|
|
583
|
+
stdio: [
|
|
584
|
+
input.fd,
|
|
585
|
+
"pipe",
|
|
586
|
+
"pipe"
|
|
587
|
+
],
|
|
588
|
+
encoding: "utf8",
|
|
589
|
+
maxBuffer: 256 * 1024,
|
|
590
|
+
timeout: 3e4
|
|
591
|
+
});
|
|
592
|
+
if (child.error) {
|
|
593
|
+
throw child.error;
|
|
594
|
+
}
|
|
595
|
+
if (child.status !== 0) {
|
|
596
|
+
const detail = String(child.stderr || "native signer import failed").trim();
|
|
597
|
+
throw new Error(redactWalletDiagnosticText(detail));
|
|
598
|
+
}
|
|
599
|
+
return parseNativeSignerImportResult({
|
|
600
|
+
stdout: String(child.stdout ?? ""),
|
|
601
|
+
walletId: params.walletId,
|
|
602
|
+
role: params.role
|
|
603
|
+
});
|
|
604
|
+
} finally {
|
|
605
|
+
fs.closeSync(input.fd);
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
function invokeNativeSignerRecoveryImport(params) {
|
|
609
|
+
if (params.hosted) {
|
|
610
|
+
throw new Error("Hosting recovery import is a signer-owner operation. Run it from the provider root console; the Gateway must not receive the recovery password.");
|
|
611
|
+
}
|
|
612
|
+
const input = requireOwnerOnlySignerImportFile(params.recoveryFile);
|
|
613
|
+
try {
|
|
614
|
+
const child = spawnSync(params.signerBinPath, [
|
|
615
|
+
"admin",
|
|
616
|
+
"wallet",
|
|
617
|
+
"recovery-import",
|
|
618
|
+
"--control-socket",
|
|
619
|
+
params.controlSocketPath,
|
|
620
|
+
"--wallet-id",
|
|
621
|
+
params.walletId,
|
|
622
|
+
"--locked-role",
|
|
623
|
+
params.role,
|
|
624
|
+
"--recovery-file",
|
|
625
|
+
input.path
|
|
626
|
+
], {
|
|
627
|
+
env: {
|
|
628
|
+
HOME: params.env.HOME,
|
|
629
|
+
LANG: params.env.LANG || "C.UTF-8",
|
|
630
|
+
PATH: "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
|
|
631
|
+
},
|
|
632
|
+
stdio: [
|
|
633
|
+
"inherit",
|
|
634
|
+
"pipe",
|
|
635
|
+
"pipe"
|
|
636
|
+
],
|
|
637
|
+
encoding: "utf8",
|
|
638
|
+
maxBuffer: 256 * 1024,
|
|
639
|
+
timeout: 12e4
|
|
640
|
+
});
|
|
641
|
+
if (child.error) {
|
|
642
|
+
throw child.error;
|
|
643
|
+
}
|
|
644
|
+
if (child.status !== 0) {
|
|
645
|
+
const detail = String(child.stderr || "native signer recovery import failed").trim();
|
|
646
|
+
throw new Error(redactWalletDiagnosticText(detail));
|
|
647
|
+
}
|
|
648
|
+
return parseNativeSignerImportResult({
|
|
649
|
+
stdout: String(child.stdout ?? ""),
|
|
650
|
+
walletId: params.walletId,
|
|
651
|
+
role: params.role
|
|
652
|
+
});
|
|
653
|
+
} finally {
|
|
654
|
+
fs.closeSync(input.fd);
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
async function importSignerOwnedWalletForSetup(params) {
|
|
658
|
+
if (params.chain !== "solana") {
|
|
659
|
+
throw new Error("fased-signerd protocol v2 currently supports Solana wallet import only");
|
|
660
|
+
}
|
|
661
|
+
const hosted = String(params.env.FASED_HOST_PROFILE ?? "").trim().toLowerCase() === "hosting";
|
|
662
|
+
let cfg = ensureLocalSignerProviderConfig(loadConfig(), params.env);
|
|
663
|
+
const mergedEnv = {
|
|
664
|
+
...params.env,
|
|
665
|
+
...cfg.env?.vars
|
|
666
|
+
};
|
|
667
|
+
const socketPath = resolveLocalSignerSocketPath(mergedEnv);
|
|
668
|
+
const signerWalletId = normalizeNativeSignerWalletId(params.walletId);
|
|
669
|
+
const registeredWallets = readWalletProviderRegistry(params.env).wallets;
|
|
670
|
+
if (params.role === "mining" && registeredWallets.some((entry) => {
|
|
671
|
+
const role = normalizeWalletUserRole(entry.metadata?.role ?? entry.metadata?.purpose);
|
|
672
|
+
return (role === "mining" || entry.id === "mining") && entry.id !== params.walletId;
|
|
673
|
+
})) {
|
|
674
|
+
throw new Error("Mining already has one active wallet. Complete the guarded Archive/Replace flow first.");
|
|
675
|
+
}
|
|
676
|
+
const collision = findNativeSignerWalletIdCollision(registeredWallets, params.walletId, signerWalletId);
|
|
677
|
+
if (collision) {
|
|
678
|
+
throw new Error(`native signer wallet ID ${signerWalletId} is already registered as ${collision.id}; choose a distinct wallet ID`);
|
|
679
|
+
}
|
|
680
|
+
const signerBinPath = hosted ? "/opt/fased/signer/fased-signerd" : resolveSignerdBinaryPath(mergedEnv);
|
|
681
|
+
const controlSocketPath = hosted ? "/run/fased-signerd/control.sock" : resolveLocalSignerControlSocketPath(mergedEnv);
|
|
682
|
+
if (!hosted) {
|
|
683
|
+
writeLocalSignerEnvFile({
|
|
684
|
+
config: cfg,
|
|
685
|
+
env: mergedEnv
|
|
686
|
+
});
|
|
687
|
+
if (!fs.existsSync(signerBinPath)) {
|
|
688
|
+
installSignerdBinary(signerBinPath);
|
|
689
|
+
}
|
|
690
|
+
await restartLocalSocketSigner(undefined, mergedEnv);
|
|
691
|
+
}
|
|
692
|
+
const result = params.options.mode === "local-signer-recovery-import" ? invokeNativeSignerRecoveryImport({
|
|
693
|
+
hosted,
|
|
694
|
+
signerBinPath,
|
|
695
|
+
controlSocketPath,
|
|
696
|
+
walletId: signerWalletId,
|
|
697
|
+
role: params.role,
|
|
698
|
+
recoveryFile: params.importFile,
|
|
699
|
+
env: mergedEnv
|
|
700
|
+
}) : invokeNativeSignerWalletImport({
|
|
701
|
+
hosted,
|
|
702
|
+
signerBinPath,
|
|
703
|
+
controlSocketPath,
|
|
704
|
+
walletId: signerWalletId,
|
|
705
|
+
role: params.role,
|
|
706
|
+
importFile: params.importFile,
|
|
707
|
+
env: mergedEnv
|
|
708
|
+
});
|
|
709
|
+
const network = await configureSignerOwnedWalletNetwork({
|
|
710
|
+
walletId: signerWalletId,
|
|
711
|
+
primaryRpcUrl: params.rpcUrl,
|
|
712
|
+
env: mergedEnv,
|
|
713
|
+
socketPath
|
|
714
|
+
});
|
|
715
|
+
cfg = setConfigEnvVar(cfg, rpcEnvKeyFor(params.chain, params.walletId), params.rpcUrl);
|
|
716
|
+
await writeConfigFile(cfg);
|
|
717
|
+
const wallet = upsertNamedWallet({
|
|
718
|
+
walletId: params.walletId,
|
|
719
|
+
name: params.options.walletName || "Wallet",
|
|
720
|
+
providerId: "local-socket-signer",
|
|
721
|
+
addresses: { solana: result.wallet.publicKey },
|
|
722
|
+
metadata: {
|
|
723
|
+
role: params.role,
|
|
724
|
+
purpose: params.role,
|
|
725
|
+
keyAuthority: "signer-owned-v2",
|
|
726
|
+
signerWalletId,
|
|
727
|
+
policyHash: result.policy.hash,
|
|
728
|
+
policyVersion: result.policy.version,
|
|
729
|
+
policyState: "locked",
|
|
730
|
+
networkHash: network.hash,
|
|
731
|
+
networkVersion: network.version,
|
|
732
|
+
networkReady: network.ready
|
|
733
|
+
},
|
|
734
|
+
env: params.env
|
|
735
|
+
});
|
|
736
|
+
if (params.role === "agent") {
|
|
737
|
+
setDefaultWallet({
|
|
738
|
+
walletId: wallet.id,
|
|
739
|
+
env: params.env
|
|
740
|
+
});
|
|
741
|
+
}
|
|
742
|
+
if (params.role === "mining") {
|
|
743
|
+
const currentEntry = cfg.plugins?.entries?.["sat-mining"];
|
|
744
|
+
const currentConfig = currentEntry?.config && typeof currentEntry.config === "object" && !Array.isArray(currentEntry.config) ? currentEntry.config : {};
|
|
745
|
+
cfg = {
|
|
746
|
+
...cfg,
|
|
747
|
+
plugins: {
|
|
748
|
+
...cfg.plugins,
|
|
749
|
+
entries: {
|
|
750
|
+
...cfg.plugins?.entries,
|
|
751
|
+
"sat-mining": {
|
|
752
|
+
enabled: true,
|
|
753
|
+
...currentEntry,
|
|
754
|
+
config: {
|
|
755
|
+
...currentConfig,
|
|
756
|
+
walletId: wallet.id
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
};
|
|
762
|
+
await writeConfigFile(cfg);
|
|
763
|
+
}
|
|
764
|
+
if (params.options.json) {
|
|
765
|
+
params.runtime.log(JSON.stringify({
|
|
766
|
+
ok: true,
|
|
767
|
+
provider: "local-socket-signer",
|
|
768
|
+
imported: true,
|
|
769
|
+
walletId: wallet.id,
|
|
770
|
+
signerWalletId,
|
|
771
|
+
role: params.role,
|
|
772
|
+
address: result.wallet.publicKey,
|
|
773
|
+
policyState: "locked",
|
|
774
|
+
networkVersion: network.version,
|
|
775
|
+
networkReady: network.ready
|
|
776
|
+
}, null, 2));
|
|
777
|
+
} else {
|
|
778
|
+
params.runtime.log(`Imported ${params.role} wallet ${wallet.id} into fased-signerd.`);
|
|
779
|
+
params.runtime.log(`SOLANA address: ${result.wallet.publicKey}`);
|
|
780
|
+
params.runtime.log(`Readiness: key=yes RPC=${network.ready ? "yes" : "no"} policy=receive-only`);
|
|
781
|
+
}
|
|
782
|
+
return result;
|
|
783
|
+
}
|
|
497
784
|
async function walletSetupCommand(runtime = defaultRuntime, options = {}) {
|
|
498
785
|
const env = process.env;
|
|
499
786
|
const interactive = !options.nonInteractive;
|
|
@@ -591,27 +878,32 @@ async function walletSetupCommand(runtime = defaultRuntime, options = {}) {
|
|
|
591
878
|
if (!mode) {
|
|
592
879
|
runtime.log("Wallet setup modes:");
|
|
593
880
|
runtime.log(" local-signer-create Create a signer-owned Solana wallet (default)");
|
|
594
|
-
runtime.log(" local-signer-import
|
|
881
|
+
runtime.log(" local-signer-import Import an owner-only Solana keypair into the signer");
|
|
882
|
+
runtime.log(" local-signer-recovery-import Restore an encrypted signer recovery package");
|
|
595
883
|
runtime.log(" turnkey Configure hosted provider (Turnkey)");
|
|
596
884
|
runtime.log(" alchemy Configure hosted provider (Alchemy)");
|
|
597
885
|
const picked = await prompt("Choose wallet setup mode", "local-signer-create");
|
|
598
|
-
mode = picked === "local-signer-create" || picked === "local-signer-import" || picked === "local-signer" || picked === "turnkey" || picked === "alchemy" ? picked : "local-signer-create";
|
|
886
|
+
mode = picked === "local-signer-create" || picked === "local-signer-import" || picked === "local-signer-recovery-import" || picked === "local-signer" || picked === "turnkey" || picked === "alchemy" ? picked : "local-signer-create";
|
|
599
887
|
}
|
|
600
888
|
if (mode === "embedded" || mode === "embedded-create" || mode === "embedded-import") {
|
|
601
889
|
throwLegacyEmbeddedKeystoreMigrationRequired(`wallet setup mode ${mode} is unavailable`);
|
|
602
890
|
}
|
|
603
|
-
if (mode !== "local-signer-create" && mode !== "local-signer-import" && mode !== "local-signer" && mode !== "turnkey" && mode !== "alchemy" && mode !== "privy") {
|
|
604
|
-
throw new Error(`Unsupported wallet setup mode: ${String(mode)}. ` + "Use one of: local-signer-create, local-signer-import, local-signer, turnkey, alchemy. Privy is unavailable.");
|
|
891
|
+
if (mode !== "local-signer-create" && mode !== "local-signer-import" && mode !== "local-signer-recovery-import" && mode !== "local-signer" && mode !== "turnkey" && mode !== "alchemy" && mode !== "privy") {
|
|
892
|
+
throw new Error(`Unsupported wallet setup mode: ${String(mode)}. ` + "Use one of: local-signer-create, local-signer-import, local-signer-recovery-import, local-signer, turnkey, alchemy. Privy is unavailable.");
|
|
605
893
|
}
|
|
606
894
|
if (mode === "local-signer-create") {
|
|
607
895
|
const chain = options.chain ?? "solana";
|
|
608
|
-
const
|
|
896
|
+
const roleInput = options.role ?? (interactive ? await prompt("Wallet role (agent|mining|vault)", "") : "");
|
|
897
|
+
const role = normalizeWalletUserRole(roleInput);
|
|
898
|
+
if (!role) {
|
|
899
|
+
throw new Error("--role is required for non-interactive wallet creation and must be agent, mining, or vault");
|
|
900
|
+
}
|
|
901
|
+
const walletId = options.walletId ?? ((await prompt("Wallet id", role)).trim() || role);
|
|
609
902
|
const rpcUrlFallback = resolveRpcUrlForChain(env, chain, walletId, options.rpcUrl);
|
|
610
903
|
const rpcUrl = (await prompt(`${chain.toUpperCase()} RPC URL (required for balances/readiness/send)`, rpcUrlFallback)).trim();
|
|
611
904
|
if (!rpcUrl) {
|
|
612
905
|
throw new Error(`${chain.toUpperCase()} RPC URL is required for self-hosted wallet setup. ` + "Pass --rpc-url or set FASED_WALLET_<CHAIN>_RPC_URL.");
|
|
613
906
|
}
|
|
614
|
-
const role = normalizeWalletUserRole(options.role) ?? (walletId?.trim().toLowerCase() === "mining" ? "mining" : walletId?.trim().toLowerCase() === "vault" ? "vault" : "agent");
|
|
615
907
|
await createSignerOwnedWalletForSetup({
|
|
616
908
|
runtime,
|
|
617
909
|
options,
|
|
@@ -627,35 +919,35 @@ async function walletSetupCommand(runtime = defaultRuntime, options = {}) {
|
|
|
627
919
|
await configureLimitOrdersIfRequested();
|
|
628
920
|
return;
|
|
629
921
|
}
|
|
630
|
-
if (mode === "local-signer-import") {
|
|
631
|
-
const
|
|
632
|
-
const role = normalizeWalletUserRole(
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
env: effectiveEnv
|
|
643
|
-
});
|
|
644
|
-
const signerBinPath = resolveSignerdBinaryPath(effectiveEnv);
|
|
645
|
-
if (!fs.existsSync(signerBinPath)) {
|
|
646
|
-
installSignerdBinary(signerBinPath);
|
|
647
|
-
}
|
|
648
|
-
await restartLocalSocketSigner(undefined, effectiveEnv);
|
|
922
|
+
if (mode === "local-signer-import" || mode === "local-signer-recovery-import") {
|
|
923
|
+
const roleInput = options.role ?? (interactive ? await prompt("Wallet role (agent|mining|vault)", "") : "");
|
|
924
|
+
const role = normalizeWalletUserRole(roleInput);
|
|
925
|
+
if (!role) {
|
|
926
|
+
throw new Error("--role is required for non-interactive wallet import and must be agent, mining, or vault");
|
|
927
|
+
}
|
|
928
|
+
const friendlyWalletId = options.walletId ?? (interactive ? await prompt("Wallet id", role) : role);
|
|
929
|
+
const walletId = friendlyWalletId.trim() || role;
|
|
930
|
+
const recoveryImport = mode === "local-signer-recovery-import";
|
|
931
|
+
const importFile = ((recoveryImport ? options.recoveryFile : options.importFile) ?? (interactive ? await prompt(recoveryImport ? "Absolute path to owner-only encrypted recovery package" : "Absolute path to owner-only Solana keypair JSON", "") : "")).trim();
|
|
932
|
+
if (!importFile) {
|
|
933
|
+
throw new Error(recoveryImport ? "--recovery-file is required for non-interactive native recovery import" : "--import-file is required for non-interactive native wallet import");
|
|
649
934
|
}
|
|
650
|
-
const
|
|
651
|
-
const
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
935
|
+
const chain = options.chain ?? "solana";
|
|
936
|
+
const rpcUrlFallback = resolveRpcUrlForChain(env, chain, walletId, options.rpcUrl);
|
|
937
|
+
const rpcUrl = (await prompt(`${chain.toUpperCase()} RPC URL (one primary execution RPC)`, rpcUrlFallback)).trim();
|
|
938
|
+
if (!rpcUrl) {
|
|
939
|
+
throw new Error("--rpc-url is required for non-interactive native wallet import");
|
|
940
|
+
}
|
|
941
|
+
await importSignerOwnedWalletForSetup({
|
|
942
|
+
runtime,
|
|
943
|
+
options,
|
|
944
|
+
env,
|
|
945
|
+
chain,
|
|
946
|
+
walletId,
|
|
947
|
+
rpcUrl,
|
|
948
|
+
role,
|
|
949
|
+
importFile
|
|
950
|
+
});
|
|
659
951
|
return;
|
|
660
952
|
}
|
|
661
953
|
if (mode === "local-signer") {
|
|
@@ -743,6 +1035,141 @@ async function walletSetupCommand(runtime = defaultRuntime, options = {}) {
|
|
|
743
1035
|
return;
|
|
744
1036
|
}
|
|
745
1037
|
}
|
|
1038
|
+
async function walletRecoveryExportCommand(runtime = defaultRuntime, options) {
|
|
1039
|
+
const walletId = options.walletId.trim();
|
|
1040
|
+
const output = options.output.trim();
|
|
1041
|
+
if (!walletId) {
|
|
1042
|
+
throw new Error("--wallet-id is required");
|
|
1043
|
+
}
|
|
1044
|
+
if (!output || !path.isAbsolute(output) || path.resolve(output) !== output) {
|
|
1045
|
+
throw new Error("--output must be an absolute clean path for a new recovery file");
|
|
1046
|
+
}
|
|
1047
|
+
const cfg = loadConfig();
|
|
1048
|
+
const effectiveEnv = {
|
|
1049
|
+
...process.env,
|
|
1050
|
+
...cfg.env?.vars
|
|
1051
|
+
};
|
|
1052
|
+
const wallet = readWalletProviderRegistry(effectiveEnv).wallets.find((entry) => entry.id === walletId);
|
|
1053
|
+
if (!wallet || wallet.providerId !== "local-socket-signer") {
|
|
1054
|
+
throw new Error(`native signer wallet not found: ${walletId}`);
|
|
1055
|
+
}
|
|
1056
|
+
const publicKey = wallet.addresses?.solana?.trim() || "";
|
|
1057
|
+
if (!publicKey) {
|
|
1058
|
+
throw new Error(`native signer wallet ${walletId} has no verified Solana public address`);
|
|
1059
|
+
}
|
|
1060
|
+
const hosted = String(effectiveEnv.FASED_HOST_PROFILE ?? "").trim().toLowerCase() === "hosting";
|
|
1061
|
+
if (hosted) {
|
|
1062
|
+
throw new Error("Hosting recovery export remains root/signer-owner only so a compromised Gateway cannot choose the encryption password and export a wallet. Use the provider root console and fased-signerd admin wallet recovery-export until a signer-owned owner-authorization ceremony is configured.");
|
|
1063
|
+
}
|
|
1064
|
+
const signerWalletId = typeof wallet.metadata?.signerWalletId === "string" && wallet.metadata.signerWalletId.trim() ? wallet.metadata.signerWalletId.trim() : normalizeNativeSignerWalletId(wallet.id);
|
|
1065
|
+
const signerBinPath = resolveSignerdBinaryPath(effectiveEnv);
|
|
1066
|
+
const controlSocketPath = resolveLocalSignerControlSocketPath(effectiveEnv);
|
|
1067
|
+
runtime.log(`Creating encrypted recovery package for ${wallet.id}. The signer will ask for and confirm the recovery password without echoing it.`);
|
|
1068
|
+
const child = spawnSync(signerBinPath, [
|
|
1069
|
+
"admin",
|
|
1070
|
+
"wallet",
|
|
1071
|
+
"recovery-export",
|
|
1072
|
+
"--control-socket",
|
|
1073
|
+
controlSocketPath,
|
|
1074
|
+
"--wallet-id",
|
|
1075
|
+
signerWalletId,
|
|
1076
|
+
"--expected-public-key",
|
|
1077
|
+
publicKey,
|
|
1078
|
+
"--output",
|
|
1079
|
+
output
|
|
1080
|
+
], {
|
|
1081
|
+
env: {
|
|
1082
|
+
HOME: effectiveEnv.HOME,
|
|
1083
|
+
LANG: effectiveEnv.LANG || "C.UTF-8",
|
|
1084
|
+
PATH: "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
|
|
1085
|
+
},
|
|
1086
|
+
stdio: "inherit",
|
|
1087
|
+
timeout: 12e4
|
|
1088
|
+
});
|
|
1089
|
+
if (child.error) {
|
|
1090
|
+
throw child.error;
|
|
1091
|
+
}
|
|
1092
|
+
if (child.status !== 0) {
|
|
1093
|
+
throw new Error(`encrypted recovery export failed with exit code ${child.status ?? "unknown"}`);
|
|
1094
|
+
}
|
|
1095
|
+
runtime.log(`Encrypted recovery package written: ${output} (owner-only)`);
|
|
1096
|
+
runtime.log(`Wallet: ${wallet.id} · Role: ${resolveWalletUserRole(wallet)} · Address: ${publicKey}`);
|
|
1097
|
+
}
|
|
1098
|
+
async function walletRecoveryImportCommand(runtime = defaultRuntime, options) {
|
|
1099
|
+
await walletSetupCommand(runtime, {
|
|
1100
|
+
mode: "local-signer-recovery-import",
|
|
1101
|
+
chain: "solana",
|
|
1102
|
+
walletId: options.walletId,
|
|
1103
|
+
walletName: options.walletName,
|
|
1104
|
+
role: options.role,
|
|
1105
|
+
recoveryFile: options.recoveryFile,
|
|
1106
|
+
rpcUrl: options.rpcUrl,
|
|
1107
|
+
nonInteractive: true,
|
|
1108
|
+
noDoctor: true,
|
|
1109
|
+
noSignerHints: true
|
|
1110
|
+
});
|
|
1111
|
+
}
|
|
1112
|
+
async function walletRawExportCommand(runtime = defaultRuntime, options) {
|
|
1113
|
+
const walletId = options.walletId.trim();
|
|
1114
|
+
const output = options.output.trim();
|
|
1115
|
+
if (!walletId) {
|
|
1116
|
+
throw new Error("--wallet-id is required");
|
|
1117
|
+
}
|
|
1118
|
+
if (!options.acknowledgeCustodyReduction) {
|
|
1119
|
+
throw new Error("--acknowledge-custody-reduction is required for raw private-key export");
|
|
1120
|
+
}
|
|
1121
|
+
if (!output || !path.isAbsolute(output) || path.resolve(output) !== output) {
|
|
1122
|
+
throw new Error("--output must be an absolute clean path for a new owner-only keypair file");
|
|
1123
|
+
}
|
|
1124
|
+
const cfg = loadConfig();
|
|
1125
|
+
const effectiveEnv = {
|
|
1126
|
+
...process.env,
|
|
1127
|
+
...cfg.env?.vars
|
|
1128
|
+
};
|
|
1129
|
+
const wallet = readWalletProviderRegistry(effectiveEnv).wallets.find((entry) => entry.id === walletId);
|
|
1130
|
+
if (!wallet || wallet.providerId !== "local-socket-signer") {
|
|
1131
|
+
throw new Error(`native signer wallet not found: ${walletId}`);
|
|
1132
|
+
}
|
|
1133
|
+
const publicKey = wallet.addresses?.solana?.trim() || "";
|
|
1134
|
+
if (!publicKey) {
|
|
1135
|
+
throw new Error(`native signer wallet ${walletId} has no verified Solana public address`);
|
|
1136
|
+
}
|
|
1137
|
+
if (String(effectiveEnv.FASED_HOST_PROFILE ?? "").trim().toLowerCase() === "hosting") {
|
|
1138
|
+
throw new Error("Hosting raw-key export remains signer-owner only. Run the native signer admin command from the provider root console so the Gateway cannot export custody material.");
|
|
1139
|
+
}
|
|
1140
|
+
const signerWalletId = typeof wallet.metadata?.signerWalletId === "string" && wallet.metadata.signerWalletId.trim() ? wallet.metadata.signerWalletId.trim() : normalizeNativeSignerWalletId(wallet.id);
|
|
1141
|
+
runtime.log("WARNING: raw private-key export reduces signer custody protection.");
|
|
1142
|
+
const child = spawnSync(resolveSignerdBinaryPath(effectiveEnv), [
|
|
1143
|
+
"admin",
|
|
1144
|
+
"wallet",
|
|
1145
|
+
"export-raw",
|
|
1146
|
+
"--control-socket",
|
|
1147
|
+
resolveLocalSignerControlSocketPath(effectiveEnv),
|
|
1148
|
+
"--wallet-id",
|
|
1149
|
+
signerWalletId,
|
|
1150
|
+
"--expected-public-key",
|
|
1151
|
+
publicKey,
|
|
1152
|
+
"--output",
|
|
1153
|
+
output,
|
|
1154
|
+
"--acknowledge-custody-reduction"
|
|
1155
|
+
], {
|
|
1156
|
+
env: {
|
|
1157
|
+
HOME: effectiveEnv.HOME,
|
|
1158
|
+
LANG: effectiveEnv.LANG || "C.UTF-8",
|
|
1159
|
+
PATH: "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
|
|
1160
|
+
},
|
|
1161
|
+
stdio: "inherit",
|
|
1162
|
+
timeout: 12e4
|
|
1163
|
+
});
|
|
1164
|
+
if (child.error) {
|
|
1165
|
+
throw child.error;
|
|
1166
|
+
}
|
|
1167
|
+
if (child.status !== 0) {
|
|
1168
|
+
throw new Error(`raw private-key export failed with exit code ${child.status ?? "unknown"}`);
|
|
1169
|
+
}
|
|
1170
|
+
runtime.log(`Raw private key written: ${output} (owner-only)`);
|
|
1171
|
+
runtime.log(`Wallet: ${wallet.id} · Address: ${publicKey}`);
|
|
1172
|
+
}
|
|
746
1173
|
async function walletRoleSetCommand(runtime = defaultRuntime, options) {
|
|
747
1174
|
const walletId = options.walletId.trim();
|
|
748
1175
|
const role = normalizeWalletRoleForCli(options.role);
|
|
@@ -1509,4 +1936,4 @@ async function walletInboundReconcileCommand(runtime = defaultRuntime, options =
|
|
|
1509
1936
|
}
|
|
1510
1937
|
|
|
1511
1938
|
//#endregion
|
|
1512
|
-
export { collectWalletSignerDoctorReport, walletCanaryCommand, walletInboundListCommand, walletInboundPollCommand, walletInboundReconcileCommand, walletLegacyMigrationFinalizeCommand, walletLimitOrdersConfigureCommand, walletMigrateCommand, walletPolicyProfileApplyCommand, walletProviderConfigureCommand, walletRoleSetCommand, walletRotateKeysCommand, walletSetupCommand, walletSignerDoctorCommand, walletSignerServeCommand, walletStatusCommand };
|
|
1939
|
+
export { collectWalletSignerDoctorReport, walletCanaryCommand, walletInboundListCommand, walletInboundPollCommand, walletInboundReconcileCommand, walletLegacyMigrationFinalizeCommand, walletLimitOrdersConfigureCommand, walletMigrateCommand, walletPolicyProfileApplyCommand, walletProviderConfigureCommand, walletRawExportCommand, walletRecoveryExportCommand, walletRecoveryImportCommand, walletRoleSetCommand, walletRotateKeysCommand, walletSetupCommand, walletSignerDoctorCommand, walletSignerServeCommand, walletStatusCommand };
|