viveworker 0.8.5 → 0.8.6
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/package.json +18 -10
- package/scripts/share-cli.mjs +322 -41
- package/scripts/viveworker-bridge.mjs +458 -39
- package/web/app.css +195 -0
- package/web/app.js +1017 -237
- package/web/build-id.js +1 -1
- package/web/i18n.js +110 -16
- package/web/sw.js +4 -0
|
@@ -65,12 +65,21 @@ const REMOTE_PAIRING_RELAY_TOKEN_ROTATION_MS = 30 * 24 * 60 * 60 * 1000;
|
|
|
65
65
|
const DEFAULT_COMPLETION_REPLY_IMAGE_MAX_BYTES = 15 * 1024 * 1024;
|
|
66
66
|
const DEFAULT_COMPLETION_REPLY_UPLOAD_TTL_MS = 24 * 60 * 60 * 1000;
|
|
67
67
|
const DEFAULT_COMPLETION_REPLY_ACK_TIMEOUT_MS = 1200;
|
|
68
|
+
const DEFAULT_COMPLETION_REPLY_THREAD_REOPEN_WAIT_MS = 1800;
|
|
68
69
|
const MAX_COMPLETION_REPLY_IMAGE_COUNT = 4;
|
|
69
70
|
const COMPLETION_PUSH_CONTENT_DEDUPE_WINDOW_MS = 2 * 60 * 1000;
|
|
70
71
|
const HAZBASE_METADATA_TIMEOUT_MS = 1500;
|
|
71
72
|
const NPM_VERSION_CHECK_TIMEOUT_MS = 2500;
|
|
72
73
|
const NPM_VERSION_CHECK_CACHE_TTL_MS = 6 * 60 * 60 * 1000;
|
|
73
74
|
const TIMELINE_ACTIVITY_TTL_MS = 2 * 60 * 1000;
|
|
75
|
+
const PAYMENT_NETWORKS = {
|
|
76
|
+
base: { family: "evm", chainId: 8453, asset: "usdc", assets: ["usdc"], scheme: "exact", label: "Base", releaseStatus: "comingSoon" },
|
|
77
|
+
"base-sepolia": { family: "evm", chainId: 84532, asset: "usdc", assets: ["usdc"], scheme: "exact", label: "Base Sepolia" },
|
|
78
|
+
polygon: { family: "evm", chainId: 137, asset: "usdc", assets: ["usdc", "jpyc"], scheme: "exact", label: "Polygon", releaseStatus: "comingSoon" },
|
|
79
|
+
"polygon-amoy": { family: "evm", chainId: 80002, asset: "usdc", assets: ["usdc", "jpyc"], scheme: "exact", label: "Polygon Amoy" },
|
|
80
|
+
liquidtestnet: { family: "liquid", chainId: null, asset: "usdt", assets: ["usdt", "lbtc"], scheme: "exact-liquid-pset", label: "Liquid Testnet" },
|
|
81
|
+
liquidv1: { family: "liquid", chainId: null, asset: "usdt", assets: ["usdt", "lbtc"], scheme: "exact-liquid-pset", label: "Liquid", releaseStatus: "comingSoon" },
|
|
82
|
+
};
|
|
74
83
|
|
|
75
84
|
// Shared memo for buildDiffThreadGroups. Each call spawns 3 git subprocesses
|
|
76
85
|
// per tracked repo (`git diff --name-status`, `git status --porcelain`,
|
|
@@ -806,7 +815,7 @@ function localizedMcpApprovalPushContent(locale, approval) {
|
|
|
806
815
|
|
|
807
816
|
function localizedPaymentApprovalPushContent(locale, approval) {
|
|
808
817
|
const payment = isPlainObject(approval?.rawParams) ? approval.rawParams : {};
|
|
809
|
-
const amount = cleanText(payment.amountUsdc || payment.amountAtomic || "");
|
|
818
|
+
const amount = cleanText(`${payment.amountUsdc || payment.amountAtomic || ""} ${payment.assetLabel || ""}`.trim());
|
|
810
819
|
const network = cleanText(payment.network || "");
|
|
811
820
|
const resource = cleanText(payment.resource || payment.url || "");
|
|
812
821
|
const titleKey = cleanText(approval?.kind || "") === "hazbase_wallet_payment"
|
|
@@ -7962,7 +7971,7 @@ function createX402PaymentApproval({ config, body, now = Date.now() }) {
|
|
|
7962
7971
|
const requestId = cleanText(body.paymentRequestId || body.requestId || crypto.randomUUID());
|
|
7963
7972
|
const requestKey = `payment:${requestId}`;
|
|
7964
7973
|
const title = payment.amountUsdc
|
|
7965
|
-
? `Payment approval — ${payment.amountUsdc} USDC`
|
|
7974
|
+
? `Payment approval — ${payment.amountUsdc} ${payment.assetLabel || "USDC"}`
|
|
7966
7975
|
: "Payment approval";
|
|
7967
7976
|
return {
|
|
7968
7977
|
token,
|
|
@@ -8001,7 +8010,7 @@ function createHazbaseWalletPaymentApproval({ config, body, now = Date.now() })
|
|
|
8001
8010
|
const token = crypto.randomBytes(18).toString("hex");
|
|
8002
8011
|
const requestKey = `hazbase_wallet_payment:${paymentRequestId}`;
|
|
8003
8012
|
const title = payment.amountUsdc
|
|
8004
|
-
? `Hazbase wallet payment — ${payment.amountUsdc} USDC`
|
|
8013
|
+
? `Hazbase wallet payment — ${payment.amountUsdc} ${payment.assetLabel || "USDC"}`
|
|
8005
8014
|
: "Hazbase wallet payment";
|
|
8006
8015
|
return {
|
|
8007
8016
|
token,
|
|
@@ -8036,7 +8045,7 @@ function formatHazbaseWalletPaymentApprovalMessage(payment) {
|
|
|
8036
8045
|
const lines = [
|
|
8037
8046
|
"Hazbase Smart Wallet payment requested.",
|
|
8038
8047
|
"",
|
|
8039
|
-
`Amount: ${payment.amountUsdc || payment.amountAtomic} USDC`,
|
|
8048
|
+
`Amount: ${payment.amountUsdc || payment.amountAtomic} ${payment.assetLabel || "USDC"}`,
|
|
8040
8049
|
`Network: ${payment.network} (chainId ${payment.chainId})`,
|
|
8041
8050
|
`Pay to: ${payment.payTo}`,
|
|
8042
8051
|
`Asset: ${payment.asset}`,
|
|
@@ -8053,15 +8062,16 @@ function normalizeX402PaymentApprovalBody(body) {
|
|
|
8053
8062
|
if (!isPlainObject(body)) return null;
|
|
8054
8063
|
const payment = isPlainObject(body.payment) ? body.payment : {};
|
|
8055
8064
|
const network = cleanText(payment.network || "");
|
|
8056
|
-
const chainId = Number(payment.chainId) || 0;
|
|
8065
|
+
const chainId = payment.chainId === null ? null : Number(payment.chainId) || 0;
|
|
8057
8066
|
const amountAtomic = cleanText(payment.amountAtomic || "");
|
|
8058
|
-
const amountUsdc = cleanText(payment.amountUsdc || "");
|
|
8067
|
+
const amountUsdc = cleanText(payment.amountUsdc || payment.amount || "");
|
|
8068
|
+
const assetLabel = cleanText(payment.assetLabel || "");
|
|
8059
8069
|
const payTo = cleanText(payment.payTo || "");
|
|
8060
8070
|
const asset = cleanText(payment.asset || "");
|
|
8061
8071
|
const resource = cleanText(payment.resource || body.url || "");
|
|
8062
8072
|
const description = cleanText(payment.description || "");
|
|
8063
8073
|
const url = cleanText(body.url || resource);
|
|
8064
|
-
if (!network || !chainId || !amountAtomic || !payTo || !asset || !resource) {
|
|
8074
|
+
if (!network || (chainId !== null && !chainId) || !amountAtomic || !payTo || !asset || !resource) {
|
|
8065
8075
|
return null;
|
|
8066
8076
|
}
|
|
8067
8077
|
return {
|
|
@@ -8070,6 +8080,7 @@ function normalizeX402PaymentApprovalBody(body) {
|
|
|
8070
8080
|
chainId,
|
|
8071
8081
|
amountAtomic,
|
|
8072
8082
|
amountUsdc,
|
|
8083
|
+
assetLabel,
|
|
8073
8084
|
payTo,
|
|
8074
8085
|
asset,
|
|
8075
8086
|
resource,
|
|
@@ -8081,8 +8092,8 @@ function formatX402PaymentApprovalMessage(payment) {
|
|
|
8081
8092
|
const lines = [
|
|
8082
8093
|
"x402 payment approval requested.",
|
|
8083
8094
|
"",
|
|
8084
|
-
`Amount: ${payment.amountUsdc || payment.amountAtomic} USDC`,
|
|
8085
|
-
`Network: ${payment.network} (chainId ${payment.chainId})`,
|
|
8095
|
+
`Amount: ${payment.amountUsdc || payment.amountAtomic} ${payment.assetLabel || "USDC"}`,
|
|
8096
|
+
`Network: ${payment.network}${payment.chainId === null ? "" : ` (chainId ${payment.chainId})`}`,
|
|
8086
8097
|
`Pay to: ${payment.payTo}`,
|
|
8087
8098
|
`Asset: ${payment.asset}`,
|
|
8088
8099
|
`Resource: ${payment.resource}`,
|
|
@@ -9640,6 +9651,50 @@ function isIpcNoClientFoundError(errorValue) {
|
|
|
9640
9651
|
message.includes("client not found");
|
|
9641
9652
|
}
|
|
9642
9653
|
|
|
9654
|
+
function codexThreadDeepLink(conversationId) {
|
|
9655
|
+
const id = cleanText(conversationId || "");
|
|
9656
|
+
if (!id || !/^[0-9a-z-]{16,}$/iu.test(id)) {
|
|
9657
|
+
return "";
|
|
9658
|
+
}
|
|
9659
|
+
return `codex://local/${encodeURIComponent(id)}`;
|
|
9660
|
+
}
|
|
9661
|
+
|
|
9662
|
+
function openCodexThreadBestEffort(conversationId) {
|
|
9663
|
+
if (process.platform !== "darwin") {
|
|
9664
|
+
return false;
|
|
9665
|
+
}
|
|
9666
|
+
const url = codexThreadDeepLink(conversationId);
|
|
9667
|
+
if (!url) {
|
|
9668
|
+
return false;
|
|
9669
|
+
}
|
|
9670
|
+
try {
|
|
9671
|
+
const child = spawn("open", [url], {
|
|
9672
|
+
detached: true,
|
|
9673
|
+
stdio: "ignore",
|
|
9674
|
+
});
|
|
9675
|
+
child.unref?.();
|
|
9676
|
+
console.log(`[completion-reply] reopened Codex thread via ${url}`);
|
|
9677
|
+
return true;
|
|
9678
|
+
} catch (error) {
|
|
9679
|
+
console.warn(
|
|
9680
|
+
`[completion-reply] failed to reopen Codex thread=${cleanText(conversationId || "") || "unknown"} ` +
|
|
9681
|
+
`error=${cleanText(error?.message || error || "") || "unknown"}`
|
|
9682
|
+
);
|
|
9683
|
+
return false;
|
|
9684
|
+
}
|
|
9685
|
+
}
|
|
9686
|
+
|
|
9687
|
+
async function waitForCodexThreadOwner(runtime, conversationId, timeoutMs) {
|
|
9688
|
+
const deadline = Date.now() + Math.max(0, Number(timeoutMs) || 0);
|
|
9689
|
+
while (Date.now() <= deadline) {
|
|
9690
|
+
if (runtime.threadOwnerClientIds.has(conversationId)) {
|
|
9691
|
+
return true;
|
|
9692
|
+
}
|
|
9693
|
+
await sleep(100);
|
|
9694
|
+
}
|
|
9695
|
+
return false;
|
|
9696
|
+
}
|
|
9697
|
+
|
|
9643
9698
|
function buildDefaultCollaborationMode(threadState) {
|
|
9644
9699
|
// Fallback turns must leave Plan mode unless the caller explicitly opts in.
|
|
9645
9700
|
return buildRequestedCollaborationMode(threadState, "default");
|
|
@@ -14344,7 +14399,6 @@ async function handleCompletionReply({
|
|
|
14344
14399
|
stagedWorkspaceImagePaths
|
|
14345
14400
|
);
|
|
14346
14401
|
let lastError = null;
|
|
14347
|
-
const ownerClientId = runtime.threadOwnerClientIds.get(conversationId) ?? null;
|
|
14348
14402
|
const finalizeReplyAccepted = async () => {
|
|
14349
14403
|
if (timelineImageAliases.length > 0) {
|
|
14350
14404
|
const aliases = isPlainObject(state.timelineImagePathAliases)
|
|
@@ -14357,46 +14411,76 @@ async function handleCompletionReply({
|
|
|
14357
14411
|
}
|
|
14358
14412
|
scheduleBestEffortFileCleanup(stagedWorkspaceImagePaths);
|
|
14359
14413
|
};
|
|
14414
|
+
const sendReplyCandidate = async (candidate, attemptLabel = "initial") => {
|
|
14415
|
+
const currentOwnerClientId = runtime.threadOwnerClientIds.get(conversationId) ?? null;
|
|
14416
|
+
console.log(
|
|
14417
|
+
`[completion-reply] try candidate=${candidate.name} attempt=${attemptLabel} transport=${cleanText(candidate.transport || "thread-follower")} owner=${cleanText(currentOwnerClientId || "") || "none"} images=${normalizedLocalImagePaths.length} workspaceImages=${stagedWorkspaceImagePaths.length} cwd=${cleanText(resolvedCwd || "") || "none"}`
|
|
14418
|
+
);
|
|
14419
|
+
if (candidate.transport === "direct-turn-start" && currentOwnerClientId) {
|
|
14420
|
+
return runtime.ipcClient.startTurnDirect(
|
|
14421
|
+
conversationId,
|
|
14422
|
+
candidate.turnStartParams,
|
|
14423
|
+
currentOwnerClientId,
|
|
14424
|
+
{ timeoutMs: config.completionReplyAckTimeoutMs }
|
|
14425
|
+
);
|
|
14426
|
+
}
|
|
14427
|
+
return runtime.ipcClient.startTurn(
|
|
14428
|
+
conversationId,
|
|
14429
|
+
candidate.turnStartParams,
|
|
14430
|
+
currentOwnerClientId,
|
|
14431
|
+
{ timeoutMs: config.completionReplyAckTimeoutMs }
|
|
14432
|
+
);
|
|
14433
|
+
};
|
|
14434
|
+
let reopenedCodexThread = false;
|
|
14360
14435
|
|
|
14361
14436
|
for (const candidate of turnCandidates) {
|
|
14362
14437
|
try {
|
|
14363
|
-
|
|
14364
|
-
`[completion-reply] try candidate=${candidate.name} transport=${cleanText(candidate.transport || "thread-follower")} owner=${cleanText(ownerClientId || "") || "none"} images=${normalizedLocalImagePaths.length} workspaceImages=${stagedWorkspaceImagePaths.length} cwd=${cleanText(resolvedCwd || "") || "none"}`
|
|
14365
|
-
);
|
|
14366
|
-
if (candidate.transport === "direct-turn-start" && ownerClientId) {
|
|
14367
|
-
await runtime.ipcClient.startTurnDirect(
|
|
14368
|
-
conversationId,
|
|
14369
|
-
candidate.turnStartParams,
|
|
14370
|
-
ownerClientId,
|
|
14371
|
-
{ timeoutMs: config.completionReplyAckTimeoutMs }
|
|
14372
|
-
);
|
|
14373
|
-
} else {
|
|
14374
|
-
await runtime.ipcClient.startTurn(
|
|
14375
|
-
conversationId,
|
|
14376
|
-
candidate.turnStartParams,
|
|
14377
|
-
ownerClientId,
|
|
14378
|
-
{ timeoutMs: config.completionReplyAckTimeoutMs }
|
|
14379
|
-
);
|
|
14380
|
-
}
|
|
14438
|
+
await sendReplyCandidate(candidate);
|
|
14381
14439
|
console.log(
|
|
14382
14440
|
`[completion-reply] success at=${new Date().toISOString()} candidate=${candidate.name} transport=${cleanText(candidate.transport || "thread-follower")}`
|
|
14383
14441
|
);
|
|
14384
14442
|
await finalizeReplyAccepted();
|
|
14385
14443
|
return { alreadyAccepted: false };
|
|
14386
14444
|
} catch (error) {
|
|
14387
|
-
|
|
14445
|
+
let candidateError = error;
|
|
14446
|
+
if (isIpcNoClientFoundError(candidateError) && !reopenedCodexThread) {
|
|
14447
|
+
reopenedCodexThread = true;
|
|
14448
|
+
runtime.threadOwnerClientIds.delete(conversationId);
|
|
14449
|
+
const reopened = openCodexThreadBestEffort(conversationId);
|
|
14450
|
+
if (reopened) {
|
|
14451
|
+
const ownerReady = await waitForCodexThreadOwner(
|
|
14452
|
+
runtime,
|
|
14453
|
+
conversationId,
|
|
14454
|
+
config.completionReplyThreadReopenWaitMs
|
|
14455
|
+
);
|
|
14456
|
+
console.log(
|
|
14457
|
+
`[completion-reply] retry after Codex thread reopen candidate=${candidate.name} ownerReady=${ownerReady ? 1 : 0}`
|
|
14458
|
+
);
|
|
14459
|
+
try {
|
|
14460
|
+
await sendReplyCandidate(candidate, "after-reopen");
|
|
14461
|
+
console.log(
|
|
14462
|
+
`[completion-reply] success at=${new Date().toISOString()} candidate=${candidate.name} transport=${cleanText(candidate.transport || "thread-follower")} reopened=1`
|
|
14463
|
+
);
|
|
14464
|
+
await finalizeReplyAccepted();
|
|
14465
|
+
return { alreadyAccepted: false, reopenedCodexThread: true };
|
|
14466
|
+
} catch (retryError) {
|
|
14467
|
+
candidateError = retryError;
|
|
14468
|
+
}
|
|
14469
|
+
}
|
|
14470
|
+
}
|
|
14471
|
+
if (isStartTurnAckTimeout(candidateError)) {
|
|
14388
14472
|
// Codex can create the turn but fail to send the bridge an ACK before
|
|
14389
14473
|
// its internal follower timeout fires. Retrying risks duplicate user
|
|
14390
14474
|
// turns, so treat this as accepted and let the timeline catch up.
|
|
14391
14475
|
console.log(
|
|
14392
|
-
`[completion-reply] accepted at=${new Date().toISOString()} candidate=${candidate.name} transport=${cleanText(candidate.transport || "thread-follower")} ack-timeout=${normalizeIpcErrorMessage(
|
|
14476
|
+
`[completion-reply] accepted at=${new Date().toISOString()} candidate=${candidate.name} transport=${cleanText(candidate.transport || "thread-follower")} ack-timeout=${normalizeIpcErrorMessage(candidateError)}`
|
|
14393
14477
|
);
|
|
14394
14478
|
await finalizeReplyAccepted();
|
|
14395
14479
|
return { alreadyAccepted: false, ackTimeout: true };
|
|
14396
14480
|
}
|
|
14397
|
-
lastError =
|
|
14481
|
+
lastError = candidateError;
|
|
14398
14482
|
console.log(
|
|
14399
|
-
`[completion-reply] failed candidate=${candidate.name} transport=${cleanText(candidate.transport || "thread-follower")} error=${normalizeIpcErrorMessage(
|
|
14483
|
+
`[completion-reply] failed candidate=${candidate.name} transport=${cleanText(candidate.transport || "thread-follower")} error=${normalizeIpcErrorMessage(candidateError)} raw=${inspect(candidateError?.ipcError ?? candidateError, { depth: 6, breakLength: 160 })}`
|
|
14400
14484
|
);
|
|
14401
14485
|
}
|
|
14402
14486
|
}
|
|
@@ -16641,12 +16725,26 @@ if (url.pathname === "/api/hazbase/status" && req.method === "GET") {
|
|
|
16641
16725
|
errors.push(chainsResult.reason?.message || String(chainsResult.reason || "chains metadata unavailable"));
|
|
16642
16726
|
}
|
|
16643
16727
|
const supportedChains = Array.isArray(chains?.chains)
|
|
16644
|
-
? chains.chains.filter((entry) =>
|
|
16728
|
+
? chains.chains.filter((entry) => Boolean(paymentNetworkForChainId(Number(entry.chainId))))
|
|
16729
|
+
.filter((entry) => isPaymentNetworkAvailable(paymentNetworkForChainId(Number(entry.chainId))))
|
|
16645
16730
|
: [];
|
|
16646
16731
|
const payoutAddresses = {
|
|
16647
|
-
8453: resolveHazbaseAccountForChain(hazbase.accounts, 8453)?.smartAccountAddress || "",
|
|
16648
16732
|
84532: resolveHazbaseAccountForChain(hazbase.accounts, 84532)?.smartAccountAddress || "",
|
|
16733
|
+
80002: resolveHazbaseAccountForChain(hazbase.accounts, 80002)?.smartAccountAddress || "",
|
|
16649
16734
|
};
|
|
16735
|
+
const rawPaymentCapabilities = buildPaymentCapabilities({ hazbase, payments });
|
|
16736
|
+
const paymentCapabilities = applyAgentPaymentDefaults(rawPaymentCapabilities, hazbase.agentPaymentDefaults);
|
|
16737
|
+
const agentPaymentDefaults = agentPaymentDefaultsStatus(hazbase.agentPaymentDefaults, rawPaymentCapabilities);
|
|
16738
|
+
const supportedPayments = (Array.isArray(payments?.networks) ? payments.networks : [])
|
|
16739
|
+
.filter((entry) => isPaymentNetworkAvailable(normalizePaymentNetworkKey(entry?.network || entry?.id || "")));
|
|
16740
|
+
const defaultPaymentNetwork = isPaymentNetworkAvailable(normalizePaymentNetworkKey(payments?.defaultNetwork || ""))
|
|
16741
|
+
? payments.defaultNetwork
|
|
16742
|
+
: "base-sepolia";
|
|
16743
|
+
for (const capability of paymentCapabilities) {
|
|
16744
|
+
if (capability.payTo) {
|
|
16745
|
+
payoutAddresses[capability.network] = capability.payTo;
|
|
16746
|
+
}
|
|
16747
|
+
}
|
|
16650
16748
|
const signedIn = Boolean(hazbase.accessToken) && !hazbase.sessionInvalid;
|
|
16651
16749
|
return writeJson(res, 200, {
|
|
16652
16750
|
enabled: true,
|
|
@@ -16663,9 +16761,11 @@ if (url.pathname === "/api/hazbase/status" && req.method === "GET") {
|
|
|
16663
16761
|
credentialId: hazbase.credentialId,
|
|
16664
16762
|
highTrustExpiresAt: hazbase.highTrustExpiresAt,
|
|
16665
16763
|
accounts: hazbase.accounts,
|
|
16666
|
-
|
|
16764
|
+
paymentCapabilities,
|
|
16765
|
+
agentPaymentDefaults,
|
|
16766
|
+
supportedPayments,
|
|
16667
16767
|
supportedChains,
|
|
16668
|
-
defaultPaymentNetwork
|
|
16768
|
+
defaultPaymentNetwork,
|
|
16669
16769
|
payoutAddresses,
|
|
16670
16770
|
error: errors.join(" | "),
|
|
16671
16771
|
});
|
|
@@ -16680,16 +16780,48 @@ if (url.pathname === "/api/hazbase/payout-address" && req.method === "GET") {
|
|
|
16680
16780
|
}
|
|
16681
16781
|
const hazbase = normalizeHazbaseState(state.hazbase);
|
|
16682
16782
|
if (!hazbase.accessToken) return writeJson(res, 401, { error: "hazbase-auth-required" });
|
|
16683
|
-
const
|
|
16684
|
-
if (
|
|
16783
|
+
const networkParam = normalizePaymentNetworkKey(url.searchParams.get("network") || "");
|
|
16784
|
+
if (networkParam && PAYMENT_NETWORKS[networkParam]?.family === "liquid") {
|
|
16785
|
+
if (!isPaymentNetworkAvailable(networkParam)) {
|
|
16786
|
+
return writeJson(res, 409, { error: "payment-network-coming-soon" });
|
|
16787
|
+
}
|
|
16788
|
+
const capability = resolvePaymentCapability(
|
|
16789
|
+
buildPaymentCapabilities({ hazbase, payments: null }),
|
|
16790
|
+
networkParam,
|
|
16791
|
+
url.searchParams.get("asset") || "usdt",
|
|
16792
|
+
);
|
|
16793
|
+
if (!capability?.payTo) return writeJson(res, 409, { error: "hazbase-wallet-account-missing" });
|
|
16794
|
+
return writeJson(res, 200, {
|
|
16795
|
+
chainId: null,
|
|
16796
|
+
network: networkParam,
|
|
16797
|
+
asset: capability.asset,
|
|
16798
|
+
scheme: capability.scheme,
|
|
16799
|
+
payoutMethod: "external_liquid",
|
|
16800
|
+
payoutAddress: capability.payTo,
|
|
16801
|
+
payTo: capability.payTo,
|
|
16802
|
+
issuer: capability.issuer || "external",
|
|
16803
|
+
});
|
|
16804
|
+
}
|
|
16805
|
+
const chainId = networkParam && PAYMENT_NETWORKS[networkParam]?.family === "evm"
|
|
16806
|
+
? Number(PAYMENT_NETWORKS[networkParam].chainId || 0)
|
|
16807
|
+
: Number(url.searchParams.get("chainId") || 0);
|
|
16808
|
+
if (!paymentNetworkForChainId(chainId)) {
|
|
16685
16809
|
return writeJson(res, 400, { error: "unsupported-chain" });
|
|
16686
16810
|
}
|
|
16811
|
+
if (!isPaymentNetworkAvailable(paymentNetworkForChainId(chainId))) {
|
|
16812
|
+
return writeJson(res, 409, { error: "payment-network-coming-soon" });
|
|
16813
|
+
}
|
|
16814
|
+
const evmNetwork = paymentNetworkForChainId(chainId);
|
|
16815
|
+
const asset = normalizePaymentAssetKey(url.searchParams.get("asset") || PAYMENT_NETWORKS[evmNetwork]?.asset, evmNetwork);
|
|
16816
|
+
if (!asset) return writeJson(res, 400, { error: "unsupported-payment-asset" });
|
|
16687
16817
|
const account = resolveHazbaseAccountForChain(hazbase.accounts, chainId);
|
|
16688
16818
|
if (!account?.smartAccountAddress) {
|
|
16689
16819
|
return writeJson(res, 409, { error: "hazbase-wallet-account-missing" });
|
|
16690
16820
|
}
|
|
16691
16821
|
return writeJson(res, 200, {
|
|
16692
16822
|
chainId,
|
|
16823
|
+
network: evmNetwork,
|
|
16824
|
+
asset,
|
|
16693
16825
|
payoutMethod: "hazbase_wallet",
|
|
16694
16826
|
payoutAddress: account.smartAccountAddress,
|
|
16695
16827
|
smartAccountAddress: account.smartAccountAddress,
|
|
@@ -16867,9 +16999,12 @@ if (url.pathname === "/api/hazbase/account/bootstrap" && req.method === "POST")
|
|
|
16867
16999
|
}
|
|
16868
17000
|
const body = await parseJsonBody(req);
|
|
16869
17001
|
const chainId = Number(body?.chainId || 0);
|
|
16870
|
-
if (chainId
|
|
17002
|
+
if (!paymentNetworkForChainId(chainId)) {
|
|
16871
17003
|
return writeJson(res, 400, { error: "unsupported-chain" });
|
|
16872
17004
|
}
|
|
17005
|
+
if (!isPaymentNetworkAvailable(paymentNetworkForChainId(chainId))) {
|
|
17006
|
+
return writeJson(res, 409, { error: "payment-network-coming-soon" });
|
|
17007
|
+
}
|
|
16873
17008
|
let result;
|
|
16874
17009
|
try {
|
|
16875
17010
|
result = await bootstrapPasskeyAccount({
|
|
@@ -16900,6 +17035,88 @@ if (url.pathname === "/api/hazbase/account/bootstrap" && req.method === "POST")
|
|
|
16900
17035
|
return writeJson(res, 200, result);
|
|
16901
17036
|
}
|
|
16902
17037
|
|
|
17038
|
+
if (url.pathname === "/api/hazbase/payment-capability" && req.method === "POST") {
|
|
17039
|
+
const session = requireMutatingApiSession(req, res, config, state);
|
|
17040
|
+
if (!session) return;
|
|
17041
|
+
const hazbase = normalizeHazbaseState(state.hazbase);
|
|
17042
|
+
if (!hazbase.accessToken) return writeJson(res, 401, { error: "hazbase-auth-required" });
|
|
17043
|
+
const body = await parseJsonBody(req);
|
|
17044
|
+
const network = normalizePaymentNetworkKey(body?.network);
|
|
17045
|
+
if (!network || PAYMENT_NETWORKS[network]?.family !== "liquid") {
|
|
17046
|
+
return writeJson(res, 400, { error: "unsupported-payment-network" });
|
|
17047
|
+
}
|
|
17048
|
+
if (!isPaymentNetworkAvailable(network)) {
|
|
17049
|
+
return writeJson(res, 409, { error: "payment-network-coming-soon" });
|
|
17050
|
+
}
|
|
17051
|
+
const asset = normalizePaymentAssetKey(body?.asset || "usdt", network);
|
|
17052
|
+
if (!asset) return writeJson(res, 400, { error: "unsupported-payment-asset" });
|
|
17053
|
+
const payTo = cleanText(body?.payTo || body?.payoutAddress || "").toLowerCase();
|
|
17054
|
+
if (!isValidLiquidAddressForNetwork(payTo, network)) {
|
|
17055
|
+
return writeJson(res, 400, { error: "invalid-liquid-address" });
|
|
17056
|
+
}
|
|
17057
|
+
const nextCapability = {
|
|
17058
|
+
network,
|
|
17059
|
+
asset,
|
|
17060
|
+
scheme: PAYMENT_NETWORKS[network].scheme,
|
|
17061
|
+
payoutMethod: "external_liquid",
|
|
17062
|
+
payTo,
|
|
17063
|
+
configured: true,
|
|
17064
|
+
issuer: "external",
|
|
17065
|
+
};
|
|
17066
|
+
const existing = normalizeStoredPaymentCapabilities(hazbase.paymentCapabilities)
|
|
17067
|
+
.filter((entry) => !(entry.network === network && entry.asset === asset));
|
|
17068
|
+
state.hazbase = {
|
|
17069
|
+
...hazbase,
|
|
17070
|
+
paymentCapabilities: [...existing, nextCapability],
|
|
17071
|
+
sessionInvalid: false,
|
|
17072
|
+
sessionInvalidReason: "",
|
|
17073
|
+
sessionInvalidAt: "",
|
|
17074
|
+
};
|
|
17075
|
+
await saveState(config.stateFile, state);
|
|
17076
|
+
return writeJson(res, 200, { ok: true, capability: nextCapability });
|
|
17077
|
+
}
|
|
17078
|
+
|
|
17079
|
+
if (url.pathname === "/api/hazbase/agent-payment-defaults" && req.method === "POST") {
|
|
17080
|
+
const session = requireMutatingApiSession(req, res, config, state);
|
|
17081
|
+
if (!session) return;
|
|
17082
|
+
const hazbase = normalizeHazbaseState(state.hazbase);
|
|
17083
|
+
const body = await parseJsonBody(req);
|
|
17084
|
+
const mode = cleanText(body?.mode || "").toLowerCase() === "configured" ? "configured" : "custom";
|
|
17085
|
+
let nextDefaults;
|
|
17086
|
+
if (mode === "configured") {
|
|
17087
|
+
nextDefaults = { mode: "configured", accepts: [] };
|
|
17088
|
+
} else {
|
|
17089
|
+
const capabilities = buildPaymentCapabilities({ hazbase, payments: null });
|
|
17090
|
+
const configuredKeys = new Set(
|
|
17091
|
+
capabilities
|
|
17092
|
+
.filter((entry) => entry?.configured && entry?.enabled !== false && isPaymentNetworkAvailable(entry.network))
|
|
17093
|
+
.map((entry) => paymentCapabilityKey(entry))
|
|
17094
|
+
.filter(Boolean)
|
|
17095
|
+
);
|
|
17096
|
+
const rawAccepts = Array.isArray(body?.accepts) ? body.accepts : [];
|
|
17097
|
+
const accepts = [];
|
|
17098
|
+
const seen = new Set();
|
|
17099
|
+
for (const entry of rawAccepts) {
|
|
17100
|
+
const ref = paymentCapabilityRef(entry);
|
|
17101
|
+
if (!ref) return writeJson(res, 400, { error: "unsupported-payment-capability" });
|
|
17102
|
+
const key = `${ref.network}:${ref.asset}`;
|
|
17103
|
+
if (!configuredKeys.has(key)) {
|
|
17104
|
+
return writeJson(res, 409, { error: "payment-capability-not-configured", network: ref.network, asset: ref.asset });
|
|
17105
|
+
}
|
|
17106
|
+
if (seen.has(key)) continue;
|
|
17107
|
+
seen.add(key);
|
|
17108
|
+
accepts.push(ref);
|
|
17109
|
+
}
|
|
17110
|
+
nextDefaults = { mode: "custom", accepts };
|
|
17111
|
+
}
|
|
17112
|
+
state.hazbase = {
|
|
17113
|
+
...hazbase,
|
|
17114
|
+
agentPaymentDefaults: nextDefaults,
|
|
17115
|
+
};
|
|
17116
|
+
await saveState(config.stateFile, state);
|
|
17117
|
+
return writeJson(res, 200, { ok: true, agentPaymentDefaults: nextDefaults });
|
|
17118
|
+
}
|
|
17119
|
+
|
|
16903
17120
|
if (url.pathname === "/api/hazbase/session/refresh" && req.method === "POST") {
|
|
16904
17121
|
const hookSecret = req.headers["x-viveworker-hook-secret"] || "";
|
|
16905
17122
|
const hookAuth = config.sessionSecret && hookSecret === config.sessionSecret;
|
|
@@ -21246,6 +21463,10 @@ function buildConfig(cli) {
|
|
|
21246
21463
|
"COMPLETION_REPLY_ACK_TIMEOUT_MS",
|
|
21247
21464
|
DEFAULT_COMPLETION_REPLY_ACK_TIMEOUT_MS
|
|
21248
21465
|
),
|
|
21466
|
+
completionReplyThreadReopenWaitMs: numberEnv(
|
|
21467
|
+
"COMPLETION_REPLY_THREAD_REOPEN_WAIT_MS",
|
|
21468
|
+
DEFAULT_COMPLETION_REPLY_THREAD_REOPEN_WAIT_MS
|
|
21469
|
+
),
|
|
21249
21470
|
choicePageSize: numberEnv("CHOICE_PAGE_SIZE", 5),
|
|
21250
21471
|
completionReplyImageMaxBytes: numberEnv(
|
|
21251
21472
|
"COMPLETION_REPLY_IMAGE_MAX_BYTES",
|
|
@@ -21291,6 +21512,8 @@ function normalizeHazbaseState(raw) {
|
|
|
21291
21512
|
highTrustToken: cleanText(value.highTrustToken ?? ""),
|
|
21292
21513
|
highTrustExpiresAt: cleanText(value.highTrustExpiresAt ?? ""),
|
|
21293
21514
|
accounts,
|
|
21515
|
+
paymentCapabilities: normalizeStoredPaymentCapabilities(value.paymentCapabilities),
|
|
21516
|
+
agentPaymentDefaults: normalizeAgentPaymentDefaults(value.agentPaymentDefaults),
|
|
21294
21517
|
sessionInvalid: value.sessionInvalid === true,
|
|
21295
21518
|
sessionInvalidReason: cleanText(value.sessionInvalidReason ?? ""),
|
|
21296
21519
|
sessionInvalidAt: cleanText(value.sessionInvalidAt ?? ""),
|
|
@@ -21551,6 +21774,202 @@ function resolveHazbaseAccountForChain(accounts, chainId) {
|
|
|
21551
21774
|
return list.find((entry) => Number(entry?.chainId) === normalizedChainId && cleanText(entry?.smartAccountAddress || "")) || null;
|
|
21552
21775
|
}
|
|
21553
21776
|
|
|
21777
|
+
function normalizePaymentNetworkKey(raw) {
|
|
21778
|
+
const value = cleanText(raw || "").toLowerCase();
|
|
21779
|
+
if (value === "basesepolia") return "base-sepolia";
|
|
21780
|
+
if (value === "polygonamoy" || value === "amoy") return "polygon-amoy";
|
|
21781
|
+
if (value === "matic") return "polygon";
|
|
21782
|
+
if (value === "liquid" || value === "liquid-mainnet") return "liquidv1";
|
|
21783
|
+
return PAYMENT_NETWORKS[value] ? value : "";
|
|
21784
|
+
}
|
|
21785
|
+
|
|
21786
|
+
function isPaymentNetworkAvailable(network) {
|
|
21787
|
+
return PAYMENT_NETWORKS[network]?.releaseStatus !== "comingSoon";
|
|
21788
|
+
}
|
|
21789
|
+
|
|
21790
|
+
function paymentNetworkForChainId(chainId) {
|
|
21791
|
+
const id = Number(chainId || 0);
|
|
21792
|
+
return Object.entries(PAYMENT_NETWORKS).find(([, entry]) => entry.chainId === id)?.[0] || "";
|
|
21793
|
+
}
|
|
21794
|
+
|
|
21795
|
+
function normalizePaymentAssetKey(raw, network) {
|
|
21796
|
+
const networkInfo = PAYMENT_NETWORKS[network] || null;
|
|
21797
|
+
const value = cleanText(raw || networkInfo?.asset || "").toLowerCase();
|
|
21798
|
+
if (value === "usd-t" || value === "tether") return "usdt";
|
|
21799
|
+
if (value === "jpy" || value === "jpy-coin") return "jpyc";
|
|
21800
|
+
if (value === "bitcoin" || value === "btc") return "lbtc";
|
|
21801
|
+
const allowed = Array.isArray(networkInfo?.assets) ? networkInfo.assets : [networkInfo?.asset].filter(Boolean);
|
|
21802
|
+
if ((value === "usdc" || value === "jpyc" || value === "usdt" || value === "lbtc") && allowed.includes(value)) return value;
|
|
21803
|
+
return "";
|
|
21804
|
+
}
|
|
21805
|
+
|
|
21806
|
+
function isValidLiquidAddressForNetwork(address, network) {
|
|
21807
|
+
const value = cleanText(address || "").toLowerCase();
|
|
21808
|
+
if (network === "liquidtestnet") return /^(tlq1|tex1)[0-9a-z]{20,}$/u.test(value);
|
|
21809
|
+
if (network === "liquidv1") return /^(lq1|ex1)[0-9a-z]{20,}$/u.test(value);
|
|
21810
|
+
return false;
|
|
21811
|
+
}
|
|
21812
|
+
|
|
21813
|
+
function normalizeStoredPaymentCapabilities(value) {
|
|
21814
|
+
const list = Array.isArray(value) ? value : [];
|
|
21815
|
+
const out = [];
|
|
21816
|
+
for (const entry of list) {
|
|
21817
|
+
const network = normalizePaymentNetworkKey(entry?.network);
|
|
21818
|
+
if (!network || PAYMENT_NETWORKS[network]?.family !== "liquid") continue;
|
|
21819
|
+
const asset = normalizePaymentAssetKey(entry?.asset, network);
|
|
21820
|
+
const payTo = cleanText(entry?.payTo || entry?.payoutAddress || "").toLowerCase();
|
|
21821
|
+
if (!asset || !isValidLiquidAddressForNetwork(payTo, network)) continue;
|
|
21822
|
+
out.push({
|
|
21823
|
+
network,
|
|
21824
|
+
asset,
|
|
21825
|
+
scheme: PAYMENT_NETWORKS[network].scheme,
|
|
21826
|
+
payoutMethod: "external_liquid",
|
|
21827
|
+
payTo,
|
|
21828
|
+
configured: true,
|
|
21829
|
+
issuer: cleanText(entry?.issuer || "external") || "external",
|
|
21830
|
+
});
|
|
21831
|
+
}
|
|
21832
|
+
return out;
|
|
21833
|
+
}
|
|
21834
|
+
|
|
21835
|
+
function paymentCapabilityRef(entry) {
|
|
21836
|
+
const network = normalizePaymentNetworkKey(entry?.network);
|
|
21837
|
+
const asset = normalizePaymentAssetKey(entry?.asset, network);
|
|
21838
|
+
if (!network || !asset) return null;
|
|
21839
|
+
return { network, asset };
|
|
21840
|
+
}
|
|
21841
|
+
|
|
21842
|
+
function paymentCapabilityKey(entry) {
|
|
21843
|
+
const ref = paymentCapabilityRef(entry);
|
|
21844
|
+
return ref ? `${ref.network}:${ref.asset}` : "";
|
|
21845
|
+
}
|
|
21846
|
+
|
|
21847
|
+
function normalizeAgentPaymentDefaults(value) {
|
|
21848
|
+
const raw = value && typeof value === "object" ? value : {};
|
|
21849
|
+
const mode = cleanText(raw.mode || "").toLowerCase() === "custom" ? "custom" : "configured";
|
|
21850
|
+
const list = Array.isArray(raw.accepts) ? raw.accepts : Array.isArray(value) ? value : [];
|
|
21851
|
+
const accepts = [];
|
|
21852
|
+
const seen = new Set();
|
|
21853
|
+
for (const entry of list) {
|
|
21854
|
+
const ref = paymentCapabilityRef(entry);
|
|
21855
|
+
if (!ref || !isPaymentNetworkAvailable(ref.network)) continue;
|
|
21856
|
+
const key = `${ref.network}:${ref.asset}`;
|
|
21857
|
+
if (seen.has(key)) continue;
|
|
21858
|
+
seen.add(key);
|
|
21859
|
+
accepts.push(ref);
|
|
21860
|
+
}
|
|
21861
|
+
return { mode, accepts };
|
|
21862
|
+
}
|
|
21863
|
+
|
|
21864
|
+
function effectiveAgentPaymentDefaultRefs(defaults, capabilities) {
|
|
21865
|
+
const normalized = normalizeAgentPaymentDefaults(defaults);
|
|
21866
|
+
const configured = (Array.isArray(capabilities) ? capabilities : [])
|
|
21867
|
+
.filter((entry) => entry?.configured && entry?.enabled !== false && isPaymentNetworkAvailable(entry.network))
|
|
21868
|
+
.map((entry) => paymentCapabilityRef(entry))
|
|
21869
|
+
.filter(Boolean);
|
|
21870
|
+
if (normalized.mode !== "custom") {
|
|
21871
|
+
return configured;
|
|
21872
|
+
}
|
|
21873
|
+
const configuredKeys = new Set(configured.map((entry) => `${entry.network}:${entry.asset}`));
|
|
21874
|
+
return normalized.accepts.filter((entry) => configuredKeys.has(`${entry.network}:${entry.asset}`));
|
|
21875
|
+
}
|
|
21876
|
+
|
|
21877
|
+
function applyAgentPaymentDefaults(capabilities, defaults) {
|
|
21878
|
+
const effective = effectiveAgentPaymentDefaultRefs(defaults, capabilities);
|
|
21879
|
+
const enabledKeys = new Set(effective.map((entry) => `${entry.network}:${entry.asset}`));
|
|
21880
|
+
return (Array.isArray(capabilities) ? capabilities : []).map((entry) => ({
|
|
21881
|
+
...entry,
|
|
21882
|
+
agentEnabled: enabledKeys.has(paymentCapabilityKey(entry)),
|
|
21883
|
+
}));
|
|
21884
|
+
}
|
|
21885
|
+
|
|
21886
|
+
function agentPaymentDefaultsStatus(defaults, capabilities) {
|
|
21887
|
+
const normalized = normalizeAgentPaymentDefaults(defaults);
|
|
21888
|
+
return {
|
|
21889
|
+
mode: normalized.mode,
|
|
21890
|
+
accepts: normalized.accepts,
|
|
21891
|
+
effectiveAccepts: effectiveAgentPaymentDefaultRefs(normalized, capabilities),
|
|
21892
|
+
};
|
|
21893
|
+
}
|
|
21894
|
+
|
|
21895
|
+
function liquidCapabilityFromEnv(network) {
|
|
21896
|
+
if (!isPaymentNetworkAvailable(network)) return null;
|
|
21897
|
+
const suffix = network === "liquidtestnet" ? "LIQUIDTESTNET" : "LIQUIDV1";
|
|
21898
|
+
const payTo = cleanText(process.env[`VIVEWORKER_${suffix}_PAYOUT_ADDRESS`] || process.env[`VIVEWORKER_${suffix}_PAY_TO`] || "").toLowerCase();
|
|
21899
|
+
if (!isValidLiquidAddressForNetwork(payTo, network)) return null;
|
|
21900
|
+
return {
|
|
21901
|
+
network,
|
|
21902
|
+
asset: "usdt",
|
|
21903
|
+
scheme: PAYMENT_NETWORKS[network].scheme,
|
|
21904
|
+
payoutMethod: "external_liquid",
|
|
21905
|
+
payTo,
|
|
21906
|
+
configured: true,
|
|
21907
|
+
issuer: "env",
|
|
21908
|
+
};
|
|
21909
|
+
}
|
|
21910
|
+
|
|
21911
|
+
function buildPaymentCapabilities({ hazbase, payments }) {
|
|
21912
|
+
const supported = Array.isArray(payments?.networks) ? payments.networks : [];
|
|
21913
|
+
const supportedByNetwork = new Map(supported.map((entry) => [cleanText(entry?.network || entry?.id || ""), entry]));
|
|
21914
|
+
const capabilities = [];
|
|
21915
|
+
for (const network of ["base-sepolia", "base", "polygon-amoy", "polygon"]) {
|
|
21916
|
+
const meta = PAYMENT_NETWORKS[network];
|
|
21917
|
+
const paymentMeta = supportedByNetwork.get(network) || null;
|
|
21918
|
+
const supportedAssets = Array.isArray(paymentMeta?.assets) && paymentMeta.assets.length > 0
|
|
21919
|
+
? paymentMeta.assets.map((entry) => normalizePaymentAssetKey(entry?.asset, network)).filter(Boolean)
|
|
21920
|
+
: meta.assets || [meta.asset];
|
|
21921
|
+
const account = resolveHazbaseAccountForChain(hazbase.accounts, meta.chainId);
|
|
21922
|
+
const available = isPaymentNetworkAvailable(network);
|
|
21923
|
+
for (const asset of [...new Set(supportedAssets)]) {
|
|
21924
|
+
capabilities.push({
|
|
21925
|
+
network,
|
|
21926
|
+
family: "evm",
|
|
21927
|
+
chainId: meta.chainId,
|
|
21928
|
+
asset,
|
|
21929
|
+
scheme: "exact",
|
|
21930
|
+
payoutMethod: "hazbase_wallet",
|
|
21931
|
+
payTo: account?.smartAccountAddress || "",
|
|
21932
|
+
smartAccountAddress: account?.smartAccountAddress || "",
|
|
21933
|
+
configured: Boolean(account?.smartAccountAddress),
|
|
21934
|
+
enabled: available && (supportedByNetwork.has(network) || supported.length === 0),
|
|
21935
|
+
label: meta.label,
|
|
21936
|
+
issuer: "hazbase",
|
|
21937
|
+
releaseStatus: meta.releaseStatus || "available",
|
|
21938
|
+
});
|
|
21939
|
+
}
|
|
21940
|
+
}
|
|
21941
|
+
const stored = normalizeStoredPaymentCapabilities(hazbase.paymentCapabilities);
|
|
21942
|
+
for (const network of ["liquidtestnet", "liquidv1"]) {
|
|
21943
|
+
const meta = PAYMENT_NETWORKS[network];
|
|
21944
|
+
const existing = stored.find((entry) => entry.network === network) || liquidCapabilityFromEnv(network);
|
|
21945
|
+
const available = isPaymentNetworkAvailable(network);
|
|
21946
|
+
capabilities.push({
|
|
21947
|
+
network,
|
|
21948
|
+
family: "liquid",
|
|
21949
|
+
chainId: null,
|
|
21950
|
+
asset: existing?.asset || meta.asset,
|
|
21951
|
+
scheme: meta.scheme,
|
|
21952
|
+
payoutMethod: "external_liquid",
|
|
21953
|
+
payTo: existing?.payTo || "",
|
|
21954
|
+
configured: Boolean(existing?.payTo),
|
|
21955
|
+
enabled: available && (supportedByNetwork.has(network) || supported.length === 0),
|
|
21956
|
+
label: meta.label,
|
|
21957
|
+
issuer: existing?.issuer || "external",
|
|
21958
|
+
releaseStatus: meta.releaseStatus || "available",
|
|
21959
|
+
});
|
|
21960
|
+
}
|
|
21961
|
+
return capabilities;
|
|
21962
|
+
}
|
|
21963
|
+
|
|
21964
|
+
function resolvePaymentCapability(capabilities, network, asset = "") {
|
|
21965
|
+
const normalizedNetwork = normalizePaymentNetworkKey(network);
|
|
21966
|
+
const normalizedAsset = normalizePaymentAssetKey(asset, normalizedNetwork);
|
|
21967
|
+
return capabilities.find((entry) => (
|
|
21968
|
+
normalizePaymentNetworkKey(entry.network) === normalizedNetwork &&
|
|
21969
|
+
(!normalizedAsset || normalizePaymentAssetKey(entry.asset, normalizedNetwork) === normalizedAsset)
|
|
21970
|
+
)) || null;
|
|
21971
|
+
}
|
|
21972
|
+
|
|
21554
21973
|
|
|
21555
21974
|
async function finalizeHazbaseWalletPaymentFailure({ config, runtime, state, approval, error }) {
|
|
21556
21975
|
const errorCode = cleanText(error?.code || error?.message || "hazbase-wallet-payment-failed") || "hazbase-wallet-payment-failed";
|