@vapi-network/core 0.7.0 → 0.9.0
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/README.md +14 -1
- package/dist/account-add.d.ts +8 -1
- package/dist/account-add.d.ts.map +1 -1
- package/dist/account-movement-lock.d.ts +10 -0
- package/dist/account-movement-lock.d.ts.map +1 -0
- package/dist/agent-link.d.ts +1 -0
- package/dist/agent-link.d.ts.map +1 -1
- package/dist/agent-link.js +27 -11
- package/dist/agent-link.js.map +1 -1
- package/dist/agent-profile.d.ts +18 -2
- package/dist/agent-profile.d.ts.map +1 -1
- package/dist/agent-run.d.ts +12 -0
- package/dist/agent-run.d.ts.map +1 -0
- package/dist/allocation.d.ts +35 -4
- package/dist/allocation.d.ts.map +1 -1
- package/dist/api-key.js +47 -0
- package/dist/api-key.js.map +1 -1
- package/dist/atomic-file.d.ts +23 -0
- package/dist/atomic-file.d.ts.map +1 -0
- package/dist/audit.d.ts +4 -1
- package/dist/audit.d.ts.map +1 -1
- package/dist/backup.d.ts +155 -32
- package/dist/backup.d.ts.map +1 -1
- package/dist/backup.js +2600 -139
- package/dist/backup.js.map +3 -3
- package/dist/call-contracts-wire.d.ts +12 -12
- package/dist/ceiling.d.ts +8 -0
- package/dist/ceiling.d.ts.map +1 -1
- package/dist/cloud-backup.d.ts +8 -10
- package/dist/cloud-backup.d.ts.map +1 -1
- package/dist/cloud-backup.js +2876 -381
- package/dist/cloud-backup.js.map +3 -3
- package/dist/config.d.ts +9 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/distribute.d.ts +9 -1
- package/dist/distribute.d.ts.map +1 -1
- package/dist/docs.d.ts +40 -0
- package/dist/docs.d.ts.map +1 -0
- package/dist/funding-policy.d.ts +29 -0
- package/dist/funding-policy.d.ts.map +1 -0
- package/dist/index.d.ts +12 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5832 -1213
- package/dist/index.js.map +3 -3
- package/dist/marketplace-contracts.d.ts +14 -14
- package/dist/member-bundle.d.ts +110 -0
- package/dist/member-bundle.d.ts.map +1 -0
- package/dist/movement-open.d.ts +10 -0
- package/dist/movement-open.d.ts.map +1 -0
- package/dist/movement.d.ts +77 -7
- package/dist/movement.d.ts.map +1 -1
- package/dist/receipts.d.ts +9 -0
- package/dist/receipts.d.ts.map +1 -1
- package/dist/router-client.d.ts +9 -1
- package/dist/router-client.d.ts.map +1 -1
- package/dist/router-client.js +2272 -609
- package/dist/router-client.js.map +3 -3
- package/dist/run-budget.d.ts +17 -0
- package/dist/run-budget.d.ts.map +1 -0
- package/dist/secret-store.d.ts +6 -0
- package/dist/secret-store.d.ts.map +1 -1
- package/dist/secrets.d.ts +2 -1
- package/dist/secrets.d.ts.map +1 -1
- package/dist/secrets.js +912 -48
- package/dist/secrets.js.map +3 -3
- package/dist/spend-policy.d.ts +41 -7
- package/dist/spend-policy.d.ts.map +1 -1
- package/dist/support-report.d.ts +1 -1
- package/dist/swarm-capital.d.ts +147 -0
- package/dist/swarm-capital.d.ts.map +1 -0
- package/dist/swarm.d.ts +224 -0
- package/dist/swarm.d.ts.map +1 -0
- package/dist/tasks/actions.d.ts +152 -0
- package/dist/tasks/actions.d.ts.map +1 -0
- package/dist/tasks/canonical-json.d.ts +6 -0
- package/dist/tasks/canonical-json.d.ts.map +1 -0
- package/dist/tasks/chain-error.d.ts +6 -0
- package/dist/tasks/chain-error.d.ts.map +1 -0
- package/dist/tasks/chain-port.d.ts +88 -0
- package/dist/tasks/chain-port.d.ts.map +1 -0
- package/dist/tasks/chain-verification.d.ts +36 -0
- package/dist/tasks/chain-verification.d.ts.map +1 -0
- package/dist/tasks/chain.d.ts +22 -0
- package/dist/tasks/chain.d.ts.map +1 -0
- package/dist/tasks/client.d.ts +76 -0
- package/dist/tasks/client.d.ts.map +1 -0
- package/dist/tasks/delivery-manifest.d.ts +24 -0
- package/dist/tasks/delivery-manifest.d.ts.map +1 -0
- package/dist/tasks/dispute-fee.d.ts +4 -0
- package/dist/tasks/dispute-fee.d.ts.map +1 -0
- package/dist/tasks/index.d.ts +22 -0
- package/dist/tasks/index.d.ts.map +1 -0
- package/dist/tasks/money.d.ts +17 -0
- package/dist/tasks/money.d.ts.map +1 -0
- package/dist/tasks/operation-support.d.ts +127 -0
- package/dist/tasks/operation-support.d.ts.map +1 -0
- package/dist/tasks/operations.d.ts +722 -0
- package/dist/tasks/operations.d.ts.map +1 -0
- package/dist/tasks/pending-transactions.d.ts +52 -0
- package/dist/tasks/pending-transactions.d.ts.map +1 -0
- package/dist/tasks/plan-guard.d.ts +326 -0
- package/dist/tasks/plan-guard.d.ts.map +1 -0
- package/dist/tasks/rpc.d.ts +8 -0
- package/dist/tasks/rpc.d.ts.map +1 -0
- package/dist/tasks/scope-bindings.d.ts +20 -0
- package/dist/tasks/scope-bindings.d.ts.map +1 -0
- package/dist/tasks/scope-terms.d.ts +75 -0
- package/dist/tasks/scope-terms.d.ts.map +1 -0
- package/dist/tasks/trusted-deployments.d.ts +14 -0
- package/dist/tasks/trusted-deployments.d.ts.map +1 -0
- package/dist/tasks/types.d.ts +4439 -0
- package/dist/tasks/types.d.ts.map +1 -0
- package/dist/tasks/verbs.d.ts +4 -0
- package/dist/tasks/verbs.d.ts.map +1 -0
- package/dist/tasks.js +43906 -0
- package/dist/tasks.js.map +6 -0
- package/dist/transfer-journal.d.ts +11 -0
- package/dist/transfer-journal.d.ts.map +1 -0
- package/dist/transfer.d.ts +4 -1
- package/dist/transfer.d.ts.map +1 -1
- package/dist/vault.d.ts +11 -0
- package/dist/vault.d.ts.map +1 -1
- package/dist/wallet-store.d.ts +19 -8
- package/dist/wallet-store.d.ts.map +1 -1
- package/dist/x402-pay.d.ts +7 -0
- package/dist/x402-pay.d.ts.map +1 -1
- package/dist/x402.d.ts +3 -0
- package/dist/x402.d.ts.map +1 -1
- package/package.json +5 -1
package/dist/backup.js
CHANGED
|
@@ -3947,7 +3947,7 @@ function weierstrass(curveDef) {
|
|
|
3947
3947
|
function prepSig(msgHash, privateKey, opts = defaultSigOpts) {
|
|
3948
3948
|
if (["recovered", "canonical"].some((k) => k in opts))
|
|
3949
3949
|
throw new Error("sign() legacy options not supported");
|
|
3950
|
-
const { hash: hash2, randomBytes:
|
|
3950
|
+
const { hash: hash2, randomBytes: randomBytes7 } = CURVE;
|
|
3951
3951
|
let { lowS, prehash, extraEntropy: ent } = opts;
|
|
3952
3952
|
if (lowS == null)
|
|
3953
3953
|
lowS = true;
|
|
@@ -3959,7 +3959,7 @@ function weierstrass(curveDef) {
|
|
|
3959
3959
|
const d = normPrivateKeyToScalar(privateKey);
|
|
3960
3960
|
const seedArgs = [int2octets(d), int2octets(h1int)];
|
|
3961
3961
|
if (ent != null && ent !== false) {
|
|
3962
|
-
const e = ent === true ?
|
|
3962
|
+
const e = ent === true ? randomBytes7(Fp.BYTES) : ent;
|
|
3963
3963
|
seedArgs.push(ensureBytes("extraEntropy", e));
|
|
3964
3964
|
}
|
|
3965
3965
|
const seed = concatBytes3(...seedArgs);
|
|
@@ -4277,6 +4277,7 @@ import {
|
|
|
4277
4277
|
createCipheriv as createCipheriv2,
|
|
4278
4278
|
createDecipheriv as createDecipheriv2,
|
|
4279
4279
|
createHash,
|
|
4280
|
+
createHmac,
|
|
4280
4281
|
createPrivateKey,
|
|
4281
4282
|
createPublicKey,
|
|
4282
4283
|
diffieHellman,
|
|
@@ -4285,8 +4286,18 @@ import {
|
|
|
4285
4286
|
scrypt as scrypt2,
|
|
4286
4287
|
timingSafeEqual
|
|
4287
4288
|
} from "node:crypto";
|
|
4288
|
-
import {
|
|
4289
|
-
|
|
4289
|
+
import {
|
|
4290
|
+
chmod as chmod2,
|
|
4291
|
+
link as link2,
|
|
4292
|
+
lstat,
|
|
4293
|
+
mkdir as mkdir7,
|
|
4294
|
+
mkdtemp,
|
|
4295
|
+
open as openFile,
|
|
4296
|
+
readFile as readFile9,
|
|
4297
|
+
readdir as readdir5,
|
|
4298
|
+
rm as rm2
|
|
4299
|
+
} from "node:fs/promises";
|
|
4300
|
+
import { dirname as dirname4, join as join8 } from "node:path";
|
|
4290
4301
|
|
|
4291
4302
|
// ../../node_modules/.pnpm/viem@2.56.1_typescript@6.0.3_zod@4.5.4/node_modules/viem/_esm/accounts/utils/publicKeyToAddress.js
|
|
4292
4303
|
init_getAddress();
|
|
@@ -5329,6 +5340,7 @@ function serializeSignature({ r, s, to = "hex", v, yParity }) {
|
|
|
5329
5340
|
|
|
5330
5341
|
// ../../node_modules/.pnpm/viem@2.56.1_typescript@6.0.3_zod@4.5.4/node_modules/viem/_esm/index.js
|
|
5331
5342
|
init_getAddress();
|
|
5343
|
+
init_isAddress();
|
|
5332
5344
|
|
|
5333
5345
|
// ../../node_modules/.pnpm/@scure+bip32@1.7.0/node_modules/@scure/bip32/lib/esm/index.js
|
|
5334
5346
|
init_modular();
|
|
@@ -45487,6 +45499,263 @@ function date4(params) {
|
|
|
45487
45499
|
return _coercedDate(ZodDate, params);
|
|
45488
45500
|
}
|
|
45489
45501
|
|
|
45502
|
+
// src/agent-profile.ts
|
|
45503
|
+
import { mkdir as mkdir3, readFile as readFile3, readdir as readdir2, rename as rename3, unlink as unlink2, writeFile as writeFile2 } from "node:fs/promises";
|
|
45504
|
+
import { join as join4 } from "node:path";
|
|
45505
|
+
|
|
45506
|
+
// src/config.ts
|
|
45507
|
+
import { constants } from "node:fs";
|
|
45508
|
+
import { copyFile, mkdir as mkdir2, readFile as readFile2, rename as rename2, stat as stat2, writeFile } from "node:fs/promises";
|
|
45509
|
+
import { homedir } from "node:os";
|
|
45510
|
+
import { dirname as dirname2, join as join3 } from "node:path";
|
|
45511
|
+
|
|
45512
|
+
// src/atomic-file.ts
|
|
45513
|
+
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
45514
|
+
import { link, mkdir, open as open2, readdir, readFile, rename, stat, unlink } from "node:fs/promises";
|
|
45515
|
+
import { basename, dirname, join as join2 } from "node:path";
|
|
45516
|
+
var DEFAULT_LOCK_TIMEOUT_MS = 2e3;
|
|
45517
|
+
var LOCK_RETRY_MS = 20;
|
|
45518
|
+
var LOCK_STALE_AFTER_MS = 12e4;
|
|
45519
|
+
var LOCK_HEARTBEAT_MS = 3e4;
|
|
45520
|
+
var atomicFileTestHooks;
|
|
45521
|
+
var FileLockedError = class extends Error {
|
|
45522
|
+
constructor(message = "Timed out waiting for the file lock.") {
|
|
45523
|
+
super(message);
|
|
45524
|
+
this.name = "FileLockedError";
|
|
45525
|
+
}
|
|
45526
|
+
};
|
|
45527
|
+
async function writeJsonAtomic(path, value, options = {}) {
|
|
45528
|
+
const directory = dirname(path);
|
|
45529
|
+
await mkdir(directory, { recursive: true, mode: 448 });
|
|
45530
|
+
const temporaryPath = join2(
|
|
45531
|
+
directory,
|
|
45532
|
+
`.${basename(path)}.${process.pid}.${randomBytes2(6).toString("hex")}.tmp`
|
|
45533
|
+
);
|
|
45534
|
+
let handle;
|
|
45535
|
+
try {
|
|
45536
|
+
handle = await open2(temporaryPath, "wx", options.mode ?? 384);
|
|
45537
|
+
await handle.writeFile(`${JSON.stringify(value, null, 2)}
|
|
45538
|
+
`, "utf8");
|
|
45539
|
+
await handle.sync();
|
|
45540
|
+
await handle.close();
|
|
45541
|
+
handle = void 0;
|
|
45542
|
+
await rename(temporaryPath, path);
|
|
45543
|
+
const directoryHandle = await open2(directory, "r");
|
|
45544
|
+
try {
|
|
45545
|
+
await directoryHandle.sync();
|
|
45546
|
+
} finally {
|
|
45547
|
+
await directoryHandle.close();
|
|
45548
|
+
}
|
|
45549
|
+
} catch (error61) {
|
|
45550
|
+
await handle?.close().catch(() => void 0);
|
|
45551
|
+
await unlink(temporaryPath).catch(() => void 0);
|
|
45552
|
+
throw error61;
|
|
45553
|
+
}
|
|
45554
|
+
}
|
|
45555
|
+
async function withFileLock(lockPath, fn, options = {}) {
|
|
45556
|
+
await mkdir(dirname(lockPath), { recursive: true, mode: 448 });
|
|
45557
|
+
const deadline = Date.now() + (options.timeoutMs ?? DEFAULT_LOCK_TIMEOUT_MS);
|
|
45558
|
+
const ownerRecord = {
|
|
45559
|
+
pid: process.pid,
|
|
45560
|
+
token: randomBytes2(16).toString("hex")
|
|
45561
|
+
};
|
|
45562
|
+
const owner = JSON.stringify(ownerRecord);
|
|
45563
|
+
const releasedOwner = JSON.stringify({ ...ownerRecord, released: true });
|
|
45564
|
+
let handle;
|
|
45565
|
+
while (handle === void 0) {
|
|
45566
|
+
await atomicFileTestHooks?.beforeLockAttempt?.(lockPath);
|
|
45567
|
+
try {
|
|
45568
|
+
const candidate = await open2(lockPath, "wx", 384);
|
|
45569
|
+
try {
|
|
45570
|
+
await candidate.writeFile(owner, "utf8");
|
|
45571
|
+
await candidate.sync();
|
|
45572
|
+
await atomicFileTestHooks?.afterLockCreate?.(lockPath);
|
|
45573
|
+
if (await lockTakeoverInProgress(lockPath)) {
|
|
45574
|
+
await candidate.close();
|
|
45575
|
+
await removeOwnedLock(lockPath, [owner]);
|
|
45576
|
+
await removeAbandonedLockTombs(lockPath);
|
|
45577
|
+
if (Date.now() >= deadline) {
|
|
45578
|
+
throw new FileLockedError(options.lockedMessage);
|
|
45579
|
+
}
|
|
45580
|
+
await new Promise((resolve) => setTimeout(resolve, LOCK_RETRY_MS));
|
|
45581
|
+
continue;
|
|
45582
|
+
}
|
|
45583
|
+
handle = candidate;
|
|
45584
|
+
} catch (error61) {
|
|
45585
|
+
await candidate.close().catch(() => void 0);
|
|
45586
|
+
await removeOwnedLock(lockPath, [owner]).catch(() => void 0);
|
|
45587
|
+
throw error61;
|
|
45588
|
+
}
|
|
45589
|
+
} catch (error61) {
|
|
45590
|
+
if (!hasCode(error61, "EEXIST")) throw error61;
|
|
45591
|
+
await removeStaleLock(lockPath);
|
|
45592
|
+
if (Date.now() >= deadline) {
|
|
45593
|
+
throw new FileLockedError(options.lockedMessage);
|
|
45594
|
+
}
|
|
45595
|
+
await new Promise((resolve) => setTimeout(resolve, LOCK_RETRY_MS));
|
|
45596
|
+
}
|
|
45597
|
+
}
|
|
45598
|
+
const ownedHandle = handle;
|
|
45599
|
+
const heartbeat = setInterval(() => {
|
|
45600
|
+
const time3 = /* @__PURE__ */ new Date();
|
|
45601
|
+
void ownedHandle.utimes(time3, time3).catch(() => void 0);
|
|
45602
|
+
}, LOCK_HEARTBEAT_MS);
|
|
45603
|
+
heartbeat.unref();
|
|
45604
|
+
try {
|
|
45605
|
+
return await fn();
|
|
45606
|
+
} finally {
|
|
45607
|
+
clearInterval(heartbeat);
|
|
45608
|
+
try {
|
|
45609
|
+
await markLockReleased(ownedHandle, releasedOwner);
|
|
45610
|
+
} finally {
|
|
45611
|
+
try {
|
|
45612
|
+
await ownedHandle.close();
|
|
45613
|
+
} finally {
|
|
45614
|
+
await removeOwnedLock(lockPath, [owner, releasedOwner]);
|
|
45615
|
+
}
|
|
45616
|
+
}
|
|
45617
|
+
}
|
|
45618
|
+
}
|
|
45619
|
+
async function removeStaleLock(path) {
|
|
45620
|
+
try {
|
|
45621
|
+
const owner = await readFile(path, "utf8");
|
|
45622
|
+
if (lockOwnerReleased(owner)) {
|
|
45623
|
+
await removeOwnedLock(path, [owner]);
|
|
45624
|
+
return;
|
|
45625
|
+
}
|
|
45626
|
+
const metadata = await stat(path);
|
|
45627
|
+
if (Date.now() - metadata.mtimeMs <= LOCK_STALE_AFTER_MS) return;
|
|
45628
|
+
const pid = lockOwnerPid(owner);
|
|
45629
|
+
if (pid !== void 0 && processIsAlive(pid)) return;
|
|
45630
|
+
await atomicFileTestHooks?.beforeStaleLockBreak?.(path);
|
|
45631
|
+
await removeOwnedLock(path, [owner]);
|
|
45632
|
+
} catch (error61) {
|
|
45633
|
+
if (!hasCode(error61, "ENOENT")) throw error61;
|
|
45634
|
+
}
|
|
45635
|
+
}
|
|
45636
|
+
async function removeOwnedLock(path, owners) {
|
|
45637
|
+
const tombPath = `${path}.stale.${randomBytes2(16).toString("hex")}`;
|
|
45638
|
+
try {
|
|
45639
|
+
await rename(path, tombPath);
|
|
45640
|
+
} catch (error61) {
|
|
45641
|
+
if (hasCode(error61, "ENOENT")) {
|
|
45642
|
+
await removeOwnedTombs(path, owners);
|
|
45643
|
+
const currentOwner = await readFile(path, "utf8").catch(() => void 0);
|
|
45644
|
+
if (currentOwner !== void 0 && owners.includes(currentOwner)) {
|
|
45645
|
+
await removeOwnedLock(path, owners);
|
|
45646
|
+
}
|
|
45647
|
+
return;
|
|
45648
|
+
}
|
|
45649
|
+
throw error61;
|
|
45650
|
+
}
|
|
45651
|
+
let pendingError;
|
|
45652
|
+
try {
|
|
45653
|
+
const renamedOwner = await readFile(tombPath, "utf8");
|
|
45654
|
+
if (!owners.includes(renamedOwner) && !lockOwnerReleased(renamedOwner)) {
|
|
45655
|
+
await atomicFileTestHooks?.afterMismatchedLockRename?.(path);
|
|
45656
|
+
const currentOwner = await readFile(tombPath, "utf8");
|
|
45657
|
+
if (!lockOwnerReleased(currentOwner)) await restoreRenamedLock(path, tombPath);
|
|
45658
|
+
await atomicFileTestHooks?.afterMismatchedLockRestore?.(path);
|
|
45659
|
+
}
|
|
45660
|
+
} catch (error61) {
|
|
45661
|
+
pendingError = error61;
|
|
45662
|
+
}
|
|
45663
|
+
try {
|
|
45664
|
+
await unlink(tombPath);
|
|
45665
|
+
} catch (error61) {
|
|
45666
|
+
if (!hasCode(error61, "ENOENT") && pendingError === void 0) {
|
|
45667
|
+
pendingError = error61;
|
|
45668
|
+
}
|
|
45669
|
+
}
|
|
45670
|
+
if (pendingError !== void 0) throw pendingError;
|
|
45671
|
+
}
|
|
45672
|
+
async function lockTakeoverInProgress(path) {
|
|
45673
|
+
const prefix = `${basename(path)}.stale.`;
|
|
45674
|
+
return (await readdir(dirname(path))).some((entry) => entry.startsWith(prefix));
|
|
45675
|
+
}
|
|
45676
|
+
async function removeAbandonedLockTombs(path) {
|
|
45677
|
+
const directory = dirname(path);
|
|
45678
|
+
const prefix = `${basename(path)}.stale.`;
|
|
45679
|
+
for (const entry of await readdir(directory)) {
|
|
45680
|
+
if (!entry.startsWith(prefix)) continue;
|
|
45681
|
+
const tombPath = join2(directory, entry);
|
|
45682
|
+
const [metadata, owner] = await Promise.all([
|
|
45683
|
+
stat(tombPath).catch(() => void 0),
|
|
45684
|
+
readFile(tombPath, "utf8").catch(() => void 0)
|
|
45685
|
+
]);
|
|
45686
|
+
if (metadata === void 0 || owner === void 0) continue;
|
|
45687
|
+
const pid = lockOwnerPid(owner);
|
|
45688
|
+
const abandoned = lockOwnerReleased(owner) || Date.now() - metadata.mtimeMs > LOCK_STALE_AFTER_MS && (pid === void 0 || !processIsAlive(pid));
|
|
45689
|
+
if (!abandoned) continue;
|
|
45690
|
+
await unlink(tombPath).catch((error61) => {
|
|
45691
|
+
if (!hasCode(error61, "ENOENT")) throw error61;
|
|
45692
|
+
});
|
|
45693
|
+
}
|
|
45694
|
+
}
|
|
45695
|
+
async function removeOwnedTombs(path, owners) {
|
|
45696
|
+
const directory = dirname(path);
|
|
45697
|
+
const prefix = `${basename(path)}.stale.`;
|
|
45698
|
+
for (const entry of await readdir(directory)) {
|
|
45699
|
+
if (!entry.startsWith(prefix)) continue;
|
|
45700
|
+
const tombPath = join2(directory, entry);
|
|
45701
|
+
const owner = await readFile(tombPath, "utf8").catch(() => void 0);
|
|
45702
|
+
if (owner === void 0 || !owners.includes(owner)) continue;
|
|
45703
|
+
await unlink(tombPath).catch((error61) => {
|
|
45704
|
+
if (!hasCode(error61, "ENOENT")) throw error61;
|
|
45705
|
+
});
|
|
45706
|
+
}
|
|
45707
|
+
}
|
|
45708
|
+
async function restoreRenamedLock(path, tombPath) {
|
|
45709
|
+
while (true) {
|
|
45710
|
+
try {
|
|
45711
|
+
await link(tombPath, path);
|
|
45712
|
+
return;
|
|
45713
|
+
} catch (error61) {
|
|
45714
|
+
if (hasCode(error61, "ENOENT")) return;
|
|
45715
|
+
if (!hasCode(error61, "EEXIST")) throw error61;
|
|
45716
|
+
await new Promise((resolve) => setTimeout(resolve, LOCK_RETRY_MS));
|
|
45717
|
+
}
|
|
45718
|
+
}
|
|
45719
|
+
}
|
|
45720
|
+
async function markLockReleased(handle, owner) {
|
|
45721
|
+
const contents = Buffer.from(owner, "utf8");
|
|
45722
|
+
await handle.write(contents, 0, contents.length, 0);
|
|
45723
|
+
await handle.truncate(contents.length);
|
|
45724
|
+
await handle.sync();
|
|
45725
|
+
}
|
|
45726
|
+
function lockOwnerPid(owner) {
|
|
45727
|
+
try {
|
|
45728
|
+
const value = JSON.parse(owner);
|
|
45729
|
+
if (typeof value !== "object" || value === null) return void 0;
|
|
45730
|
+
const pid = Reflect.get(value, "pid");
|
|
45731
|
+
return typeof pid === "number" && Number.isSafeInteger(pid) && pid > 0 ? pid : void 0;
|
|
45732
|
+
} catch {
|
|
45733
|
+
return void 0;
|
|
45734
|
+
}
|
|
45735
|
+
}
|
|
45736
|
+
function lockOwnerReleased(owner) {
|
|
45737
|
+
try {
|
|
45738
|
+
const value = JSON.parse(owner);
|
|
45739
|
+
return typeof value === "object" && value !== null && Reflect.get(value, "released") === true;
|
|
45740
|
+
} catch {
|
|
45741
|
+
return false;
|
|
45742
|
+
}
|
|
45743
|
+
}
|
|
45744
|
+
function processIsAlive(pid) {
|
|
45745
|
+
if (atomicFileTestHooks?.isProcessAlive !== void 0) {
|
|
45746
|
+
return atomicFileTestHooks.isProcessAlive(pid);
|
|
45747
|
+
}
|
|
45748
|
+
try {
|
|
45749
|
+
process.kill(pid, 0);
|
|
45750
|
+
return true;
|
|
45751
|
+
} catch (error61) {
|
|
45752
|
+
return hasCode(error61, "EPERM");
|
|
45753
|
+
}
|
|
45754
|
+
}
|
|
45755
|
+
function hasCode(error61, code) {
|
|
45756
|
+
return error61 instanceof Error && "code" in error61 && error61.code === code;
|
|
45757
|
+
}
|
|
45758
|
+
|
|
45490
45759
|
// src/x402-networks.ts
|
|
45491
45760
|
var BASE_MAINNET_CAIP2 = "eip155:8453";
|
|
45492
45761
|
var ARC_MAINNET_CAIP2 = "eip155:5042";
|
|
@@ -45591,6 +45860,14 @@ function parseEip155ChainId(network) {
|
|
|
45591
45860
|
function isSolanaNetwork(network) {
|
|
45592
45861
|
return network === SOLANA_MAINNET_CAIP2 || network === X402_SOLANA_MAINNET_CAIP2;
|
|
45593
45862
|
}
|
|
45863
|
+
function explorerTransactionUrl(network, hash2) {
|
|
45864
|
+
try {
|
|
45865
|
+
const explorerUrl = getNetworkDefinition(network).explorerUrl;
|
|
45866
|
+
return explorerUrl ? `${explorerUrl}/tx/${hash2}` : void 0;
|
|
45867
|
+
} catch {
|
|
45868
|
+
return void 0;
|
|
45869
|
+
}
|
|
45870
|
+
}
|
|
45594
45871
|
function isSupportedPaymentNetwork(network) {
|
|
45595
45872
|
if (isSolanaNetwork(network)) return true;
|
|
45596
45873
|
try {
|
|
@@ -45600,15 +45877,39 @@ function isSupportedPaymentNetwork(network) {
|
|
|
45600
45877
|
return false;
|
|
45601
45878
|
}
|
|
45602
45879
|
}
|
|
45880
|
+
function getNetworkDefinition(network) {
|
|
45881
|
+
const known = NETWORKS[network];
|
|
45882
|
+
if (known) {
|
|
45883
|
+
return known;
|
|
45884
|
+
}
|
|
45885
|
+
const chainId = parseEip155ChainId(network);
|
|
45886
|
+
return {
|
|
45887
|
+
family: "evm",
|
|
45888
|
+
chainId,
|
|
45889
|
+
name: network,
|
|
45890
|
+
usdc: getAddress("0x0000000000000000000000000000000000000000"),
|
|
45891
|
+
gasToken: "ETH",
|
|
45892
|
+
rpcEnv: ""
|
|
45893
|
+
};
|
|
45894
|
+
}
|
|
45603
45895
|
|
|
45604
45896
|
// src/config.ts
|
|
45605
45897
|
var DEFAULT_REGISTRY_URL = "https://api.vapinetwork.ai";
|
|
45606
45898
|
var DEFAULT_DISCOVERY_URL = `${DEFAULT_REGISTRY_URL}/api/call/services`;
|
|
45607
45899
|
var DEFAULT_MARKETPLACE_DISCOVERY_URL = `${DEFAULT_REGISTRY_URL}/api/call/discovery`;
|
|
45900
|
+
var DEFAULT_REGISTRY_FALLBACKS = [
|
|
45901
|
+
{
|
|
45902
|
+
discoveryUrl: DEFAULT_DISCOVERY_URL,
|
|
45903
|
+
marketplaceDiscoveryUrl: DEFAULT_MARKETPLACE_DISCOVERY_URL
|
|
45904
|
+
}
|
|
45905
|
+
];
|
|
45608
45906
|
var DEFAULT_SPEND_CAPS = {
|
|
45609
45907
|
perCallAtomic: "100000",
|
|
45610
45908
|
perDayAtomic: "1000000"
|
|
45611
45909
|
};
|
|
45910
|
+
var DEFAULT_MAX_PER_TASK_USD = 100;
|
|
45911
|
+
var DEFAULT_AUTO_RELEASE_BELOW_USD = 25;
|
|
45912
|
+
var DEFAULT_CEILING_ATOMIC = 5000000n;
|
|
45612
45913
|
var atomicString = external_exports.string().regex(/^\d+$/, "Expected non-negative atomic units.");
|
|
45613
45914
|
var spendCapsSchema = external_exports.object({
|
|
45614
45915
|
perCallAtomic: atomicString,
|
|
@@ -45624,6 +45925,14 @@ var configSchema = external_exports.object({
|
|
|
45624
45925
|
})
|
|
45625
45926
|
).optional(),
|
|
45626
45927
|
allowPrivateNetwork: external_exports.boolean().default(false).optional(),
|
|
45928
|
+
tasksEscrowDurationOverrides: external_exports.record(
|
|
45929
|
+
external_exports.string().regex(/^\d+$/),
|
|
45930
|
+
external_exports.object({
|
|
45931
|
+
workDurationSeconds: external_exports.number().int().positive().max(Number.MAX_SAFE_INTEGER).optional(),
|
|
45932
|
+
reviewWindowSeconds: external_exports.number().int().positive().max(Number.MAX_SAFE_INTEGER).optional()
|
|
45933
|
+
}).strict()
|
|
45934
|
+
).optional(),
|
|
45935
|
+
tasksEscrowFactoryOverrides: external_exports.record(external_exports.string().regex(/^\d+$/), external_exports.string()).optional(),
|
|
45627
45936
|
networks: external_exports.record(
|
|
45628
45937
|
external_exports.string().refine((network) => isSupportedPaymentNetwork(network), {
|
|
45629
45938
|
message: "Expected a supported eip155:<chainId> or Solana network identifier."
|
|
@@ -45642,6 +45951,17 @@ var configSchema = external_exports.object({
|
|
|
45642
45951
|
spendCaps: spendCapsSchema,
|
|
45643
45952
|
device: external_exports.string().regex(/^[a-z0-9][a-z0-9-]{0,31}$/).optional()
|
|
45644
45953
|
}).superRefine((config2, context) => {
|
|
45954
|
+
for (const [chainId, factory] of Object.entries(config2.tasksEscrowFactoryOverrides ?? {})) {
|
|
45955
|
+
try {
|
|
45956
|
+
getAddress(factory);
|
|
45957
|
+
} catch {
|
|
45958
|
+
context.addIssue({
|
|
45959
|
+
code: "custom",
|
|
45960
|
+
path: ["tasksEscrowFactoryOverrides", chainId],
|
|
45961
|
+
message: "Expected a valid EVM escrow factory address."
|
|
45962
|
+
});
|
|
45963
|
+
}
|
|
45964
|
+
}
|
|
45645
45965
|
for (const [network, configured] of Object.entries(config2.networks)) {
|
|
45646
45966
|
if (isSolanaNetwork(network)) {
|
|
45647
45967
|
if (configured.usdc !== NETWORKS[SOLANA_MAINNET_CAIP2].usdc) {
|
|
@@ -45671,9 +45991,379 @@ var configSchema = external_exports.object({
|
|
|
45671
45991
|
}
|
|
45672
45992
|
}
|
|
45673
45993
|
});
|
|
45994
|
+
function getVapiPaths(directory = process.env.VAPI_HOME?.trim() || join3(homedir(), ".vapi")) {
|
|
45995
|
+
return {
|
|
45996
|
+
directory,
|
|
45997
|
+
agentsDir: join3(directory, "agents"),
|
|
45998
|
+
config: join3(directory, "config.json"),
|
|
45999
|
+
keystore: join3(directory, "keystore.json"),
|
|
46000
|
+
receipts: join3(directory, "receipts.jsonl"),
|
|
46001
|
+
searches: join3(directory, "searches.jsonl"),
|
|
46002
|
+
ledger: join3(directory, "spend-ledger.json")
|
|
46003
|
+
};
|
|
46004
|
+
}
|
|
46005
|
+
async function migrateLegacyVapiHome(options = {}) {
|
|
46006
|
+
const target = getVapiPaths(options.targetDirectory);
|
|
46007
|
+
const legacyDirectory = options.legacyDirectory ?? join3(homedir(), ".vapi", "agent-cash");
|
|
46008
|
+
if (legacyDirectory === target.directory || !await pathExists(legacyDirectory)) return [];
|
|
46009
|
+
await mkdir2(target.directory, { recursive: true, mode: 448 });
|
|
46010
|
+
const copied = [];
|
|
46011
|
+
for (const filename of [
|
|
46012
|
+
"config.json",
|
|
46013
|
+
"keystore.json",
|
|
46014
|
+
"receipts.jsonl",
|
|
46015
|
+
"searches.jsonl",
|
|
46016
|
+
"spend-ledger.json"
|
|
46017
|
+
]) {
|
|
46018
|
+
const source = join3(legacyDirectory, filename);
|
|
46019
|
+
const destination = join3(target.directory, filename);
|
|
46020
|
+
try {
|
|
46021
|
+
const copy = async () => await copyFile(source, destination, constants.COPYFILE_EXCL);
|
|
46022
|
+
if (filename === "receipts.jsonl") {
|
|
46023
|
+
await withFileLock(`${destination}.lock`, copy, {
|
|
46024
|
+
lockedMessage: "The receipt journal is busy. Wait for the other vapi command to finish, then retry."
|
|
46025
|
+
});
|
|
46026
|
+
} else {
|
|
46027
|
+
await copy();
|
|
46028
|
+
}
|
|
46029
|
+
copied.push(destination);
|
|
46030
|
+
} catch (error61) {
|
|
46031
|
+
if (isMissingFile(error61) || isAlreadyExists(error61)) continue;
|
|
46032
|
+
throw error61;
|
|
46033
|
+
}
|
|
46034
|
+
}
|
|
46035
|
+
if (copied.length > 0) {
|
|
46036
|
+
const message = `Migrated legacy vAPI wallet files from ${legacyDirectory} to ${target.directory}; originals were kept.`;
|
|
46037
|
+
(options.notice ?? ((value) => process.stderr.write(`${value}
|
|
46038
|
+
`)))(message);
|
|
46039
|
+
}
|
|
46040
|
+
return copied;
|
|
46041
|
+
}
|
|
46042
|
+
var LEGACY_REGISTRY_HOSTS = /* @__PURE__ */ new Map([
|
|
46043
|
+
["console.vapinetwork.ai", "api.vapinetwork.ai"],
|
|
46044
|
+
["console-staging.vapinetwork.ai", "api-staging.vapinetwork.ai"]
|
|
46045
|
+
]);
|
|
46046
|
+
var LEGACY_REGISTRY_PATHS = /* @__PURE__ */ new Map([
|
|
46047
|
+
["/api/network/services", "/api/call/services"],
|
|
46048
|
+
["/api/marketplace/discovery", "/api/call/discovery"]
|
|
46049
|
+
]);
|
|
46050
|
+
var REGISTRY_DOMAIN = "vapinetwork.ai";
|
|
46051
|
+
function rewriteLegacyRegistryUrl(value) {
|
|
46052
|
+
let url2;
|
|
46053
|
+
try {
|
|
46054
|
+
url2 = new URL(value);
|
|
46055
|
+
} catch {
|
|
46056
|
+
return value;
|
|
46057
|
+
}
|
|
46058
|
+
let rewritten = false;
|
|
46059
|
+
const host = LEGACY_REGISTRY_HOSTS.get(url2.hostname.toLowerCase());
|
|
46060
|
+
if (host) {
|
|
46061
|
+
url2.hostname = host;
|
|
46062
|
+
rewritten = true;
|
|
46063
|
+
}
|
|
46064
|
+
const hostname3 = url2.hostname.toLowerCase();
|
|
46065
|
+
if (hostname3 === REGISTRY_DOMAIN || hostname3.endsWith(`.${REGISTRY_DOMAIN}`)) {
|
|
46066
|
+
const path = LEGACY_REGISTRY_PATHS.get(url2.pathname.replace(/\/+$/, ""));
|
|
46067
|
+
if (path) {
|
|
46068
|
+
url2.pathname = path;
|
|
46069
|
+
rewritten = true;
|
|
46070
|
+
}
|
|
46071
|
+
}
|
|
46072
|
+
return rewritten ? url2.href : value;
|
|
46073
|
+
}
|
|
46074
|
+
function rewriteLegacyRegistryUrls(config2) {
|
|
46075
|
+
const changes = [];
|
|
46076
|
+
const rewrite = (field, from) => {
|
|
46077
|
+
const to = rewriteLegacyRegistryUrl(from);
|
|
46078
|
+
if (to !== from) changes.push({ field, from, to });
|
|
46079
|
+
return to;
|
|
46080
|
+
};
|
|
46081
|
+
const next = {
|
|
46082
|
+
...config2,
|
|
46083
|
+
discoveryUrl: rewrite("discoveryUrl", config2.discoveryUrl),
|
|
46084
|
+
marketplaceDiscoveryUrl: rewrite("marketplaceDiscoveryUrl", config2.marketplaceDiscoveryUrl),
|
|
46085
|
+
...config2.registryFallbacks ? {
|
|
46086
|
+
registryFallbacks: config2.registryFallbacks.map((fallback, index) => ({
|
|
46087
|
+
discoveryUrl: rewrite(
|
|
46088
|
+
`registryFallbacks[${index}].discoveryUrl`,
|
|
46089
|
+
fallback.discoveryUrl
|
|
46090
|
+
),
|
|
46091
|
+
marketplaceDiscoveryUrl: rewrite(
|
|
46092
|
+
`registryFallbacks[${index}].marketplaceDiscoveryUrl`,
|
|
46093
|
+
fallback.marketplaceDiscoveryUrl
|
|
46094
|
+
)
|
|
46095
|
+
}))
|
|
46096
|
+
} : {}
|
|
46097
|
+
};
|
|
46098
|
+
return changes.length === 0 ? { config: config2, changes } : { config: next, changes };
|
|
46099
|
+
}
|
|
46100
|
+
function formatLegacyRegistryRewrites(changes) {
|
|
46101
|
+
return [
|
|
46102
|
+
"Rewrote retired vAPI registry URLs in the local config:",
|
|
46103
|
+
...changes.map((change) => ` ${change.field}: ${change.from} -> ${change.to}`)
|
|
46104
|
+
].join("\n");
|
|
46105
|
+
}
|
|
46106
|
+
function getDefaultConfig(source = process.env, options = {}) {
|
|
46107
|
+
const registry2 = registryEndpoints(source.VAPI_REGISTRY_URL?.trim() || DEFAULT_REGISTRY_URL);
|
|
46108
|
+
const requested = new Set(options.networks ?? ["base"]);
|
|
46109
|
+
const networks = {};
|
|
46110
|
+
const tasksEscrowFactoryOverrides = tasksFactoryOverridesFromEnv(source);
|
|
46111
|
+
if (requested.has("base") || requested.has(BASE_MAINNET_CAIP2)) {
|
|
46112
|
+
networks[BASE_MAINNET_CAIP2] = {
|
|
46113
|
+
rpcUrl: source.BASE_RPC_URL?.trim() || NETWORKS[BASE_MAINNET_CAIP2].publicRpcUrl,
|
|
46114
|
+
usdc: NETWORKS[BASE_MAINNET_CAIP2].usdc
|
|
46115
|
+
};
|
|
46116
|
+
}
|
|
46117
|
+
const arcMainnetRpcUrl = source.ARC_RPC_URL?.trim();
|
|
46118
|
+
if (arcMainnetRpcUrl || requested.has("arc") || requested.has(ARC_MAINNET_CAIP2)) {
|
|
46119
|
+
networks[ARC_MAINNET_CAIP2] = {
|
|
46120
|
+
rpcUrl: arcMainnetRpcUrl || NETWORKS[ARC_MAINNET_CAIP2].publicRpcUrl,
|
|
46121
|
+
usdc: NETWORKS[ARC_MAINNET_CAIP2].usdc
|
|
46122
|
+
};
|
|
46123
|
+
}
|
|
46124
|
+
const arcTestnetRpcUrl = source.ARC_TESTNET_RPC_URL?.trim();
|
|
46125
|
+
if (arcTestnetRpcUrl || requested.has("arc-testnet") || requested.has(ARC_TESTNET_CAIP2)) {
|
|
46126
|
+
if (!arcTestnetRpcUrl) {
|
|
46127
|
+
throw new Error(
|
|
46128
|
+
"Arc testnet RPC is required. Set ARC_TESTNET_RPC_URL before enabling Arc testnet."
|
|
46129
|
+
);
|
|
46130
|
+
}
|
|
46131
|
+
networks[ARC_TESTNET_CAIP2] = {
|
|
46132
|
+
rpcUrl: arcTestnetRpcUrl,
|
|
46133
|
+
usdc: NETWORKS[ARC_TESTNET_CAIP2].usdc
|
|
46134
|
+
};
|
|
46135
|
+
}
|
|
46136
|
+
const solanaRpcUrl = source.SOLANA_RPC_URL?.trim();
|
|
46137
|
+
if (solanaRpcUrl || requested.has("solana") || requested.has(SOLANA_MAINNET_CAIP2)) {
|
|
46138
|
+
networks[SOLANA_MAINNET_CAIP2] = {
|
|
46139
|
+
rpcUrl: solanaRpcUrl || NETWORKS[SOLANA_MAINNET_CAIP2].publicRpcUrl,
|
|
46140
|
+
usdc: NETWORKS[SOLANA_MAINNET_CAIP2].usdc
|
|
46141
|
+
};
|
|
46142
|
+
}
|
|
46143
|
+
return {
|
|
46144
|
+
discoveryUrl: source.VAPI_DISCOVERY_URL?.trim() || registry2.discoveryUrl,
|
|
46145
|
+
marketplaceDiscoveryUrl: source.VAPI_MARKETPLACE_DISCOVERY_URL?.trim() || registry2.marketplaceDiscoveryUrl,
|
|
46146
|
+
registryFallbacks: DEFAULT_REGISTRY_FALLBACKS.map((fallback) => ({ ...fallback })),
|
|
46147
|
+
allowPrivateNetwork: false,
|
|
46148
|
+
tasksEscrowFactoryOverrides,
|
|
46149
|
+
tasksEscrowDurationOverrides: tasksDurationOverridesFromEnv(source),
|
|
46150
|
+
networks,
|
|
46151
|
+
spendCaps: { ...DEFAULT_SPEND_CAPS }
|
|
46152
|
+
};
|
|
46153
|
+
}
|
|
46154
|
+
async function loadConfig(path = getVapiPaths().config, source = process.env, options = {}) {
|
|
46155
|
+
if (!process.env.VAPI_HOME?.trim() && path === getVapiPaths(join3(homedir(), ".vapi")).config) {
|
|
46156
|
+
await migrateLegacyVapiHome({ targetDirectory: join3(homedir(), ".vapi") });
|
|
46157
|
+
}
|
|
46158
|
+
let raw;
|
|
46159
|
+
try {
|
|
46160
|
+
raw = await readFile2(path, "utf8");
|
|
46161
|
+
} catch (error61) {
|
|
46162
|
+
if (isMissingFile(error61)) return getDefaultConfig(source);
|
|
46163
|
+
throw error61;
|
|
46164
|
+
}
|
|
46165
|
+
const parsed = configSchema.parse(JSON.parse(raw));
|
|
46166
|
+
parsed.registryFallbacks ??= DEFAULT_REGISTRY_FALLBACKS.map((fallback) => ({ ...fallback }));
|
|
46167
|
+
parsed.tasksEscrowFactoryOverrides ??= {};
|
|
46168
|
+
const migrated = rewriteLegacyRegistryUrls(parsed);
|
|
46169
|
+
const config2 = migrated.config;
|
|
46170
|
+
config2.tasksEscrowFactoryOverrides ??= {};
|
|
46171
|
+
if (migrated.changes.length > 0 && options.notice) {
|
|
46172
|
+
options.notice(
|
|
46173
|
+
`${formatLegacyRegistryRewrites(migrated.changes)}
|
|
46174
|
+
Run vapi init to write the new URLs to ${path}.`
|
|
46175
|
+
);
|
|
46176
|
+
}
|
|
46177
|
+
const baseRpcUrl = source.BASE_RPC_URL?.trim();
|
|
46178
|
+
const arcMainnetRpcUrl = source.ARC_RPC_URL?.trim();
|
|
46179
|
+
const arcTestnetRpcUrl = source.ARC_TESTNET_RPC_URL?.trim();
|
|
46180
|
+
const solanaRpcUrl = source.SOLANA_RPC_URL?.trim();
|
|
46181
|
+
const discoveryUrl = source.VAPI_DISCOVERY_URL?.trim();
|
|
46182
|
+
const marketplaceDiscoveryUrl = source.VAPI_MARKETPLACE_DISCOVERY_URL?.trim();
|
|
46183
|
+
const registryUrl = source.VAPI_REGISTRY_URL?.trim();
|
|
46184
|
+
Object.assign(config2.tasksEscrowFactoryOverrides, tasksFactoryOverridesFromEnv(source));
|
|
46185
|
+
config2.tasksEscrowDurationOverrides ??= {};
|
|
46186
|
+
for (const [chainId, overrides] of Object.entries(tasksDurationOverridesFromEnv(source))) {
|
|
46187
|
+
config2.tasksEscrowDurationOverrides[chainId] = {
|
|
46188
|
+
...config2.tasksEscrowDurationOverrides[chainId],
|
|
46189
|
+
...overrides
|
|
46190
|
+
};
|
|
46191
|
+
}
|
|
46192
|
+
if (config2.networks[BASE_MAINNET_CAIP2] && baseRpcUrl) {
|
|
46193
|
+
config2.networks[BASE_MAINNET_CAIP2].rpcUrl = baseRpcUrl;
|
|
46194
|
+
}
|
|
46195
|
+
if (arcMainnetRpcUrl) {
|
|
46196
|
+
const existing = config2.networks[ARC_MAINNET_CAIP2];
|
|
46197
|
+
config2.networks[ARC_MAINNET_CAIP2] = {
|
|
46198
|
+
...existing ?? { usdc: NETWORKS[ARC_MAINNET_CAIP2].usdc },
|
|
46199
|
+
rpcUrl: arcMainnetRpcUrl
|
|
46200
|
+
};
|
|
46201
|
+
}
|
|
46202
|
+
if (arcTestnetRpcUrl) {
|
|
46203
|
+
const existing = config2.networks[ARC_TESTNET_CAIP2];
|
|
46204
|
+
config2.networks[ARC_TESTNET_CAIP2] = {
|
|
46205
|
+
...existing ?? { usdc: NETWORKS[ARC_TESTNET_CAIP2].usdc },
|
|
46206
|
+
rpcUrl: arcTestnetRpcUrl
|
|
46207
|
+
};
|
|
46208
|
+
}
|
|
46209
|
+
if (solanaRpcUrl) {
|
|
46210
|
+
config2.networks[SOLANA_MAINNET_CAIP2] = {
|
|
46211
|
+
rpcUrl: solanaRpcUrl,
|
|
46212
|
+
usdc: config2.networks[SOLANA_MAINNET_CAIP2]?.usdc ?? NETWORKS[SOLANA_MAINNET_CAIP2].usdc
|
|
46213
|
+
};
|
|
46214
|
+
}
|
|
46215
|
+
if (registryUrl) {
|
|
46216
|
+
const registry2 = registryEndpoints(registryUrl);
|
|
46217
|
+
config2.discoveryUrl = registry2.discoveryUrl;
|
|
46218
|
+
config2.marketplaceDiscoveryUrl = registry2.marketplaceDiscoveryUrl;
|
|
46219
|
+
}
|
|
46220
|
+
if (discoveryUrl) config2.discoveryUrl = discoveryUrl;
|
|
46221
|
+
if (marketplaceDiscoveryUrl) config2.marketplaceDiscoveryUrl = marketplaceDiscoveryUrl;
|
|
46222
|
+
for (const [network, configured] of Object.entries(config2.networks)) {
|
|
46223
|
+
configured.rpcUrl = configured.rpcUrl.trim();
|
|
46224
|
+
configured.usdc = configured.usdc.trim();
|
|
46225
|
+
if (isSolanaNetwork(network) && configured.usdc !== NETWORKS[SOLANA_MAINNET_CAIP2].usdc) {
|
|
46226
|
+
throw new Error(`Network ${network} must use canonical Solana mainnet USDC.`);
|
|
46227
|
+
}
|
|
46228
|
+
if (!configured.rpcUrl) delete config2.networks[network];
|
|
46229
|
+
}
|
|
46230
|
+
return config2;
|
|
46231
|
+
}
|
|
46232
|
+
function tasksDurationOverridesFromEnv(source) {
|
|
46233
|
+
const result = {};
|
|
46234
|
+
for (const [name, raw] of Object.entries(source)) {
|
|
46235
|
+
const match = /^VAPI_TASKS_(WORK_DURATION|REVIEW_WINDOW)_SECONDS_(\d+)$/.exec(name);
|
|
46236
|
+
const value = raw?.trim();
|
|
46237
|
+
if (!match || !value) continue;
|
|
46238
|
+
const seconds = Number(value);
|
|
46239
|
+
if (!/^\d+$/.test(value) || !Number.isSafeInteger(seconds) || seconds <= 0)
|
|
46240
|
+
throw new Error(`Invalid trusted Tasks duration ${name}.`);
|
|
46241
|
+
const entry = result[match[2]] ??= {};
|
|
46242
|
+
if (match[1] === "WORK_DURATION") entry.workDurationSeconds = seconds;
|
|
46243
|
+
else entry.reviewWindowSeconds = seconds;
|
|
46244
|
+
}
|
|
46245
|
+
return result;
|
|
46246
|
+
}
|
|
46247
|
+
function tasksFactoryOverridesFromEnv(source) {
|
|
46248
|
+
const overrides = {};
|
|
46249
|
+
for (const [name, raw] of Object.entries(source)) {
|
|
46250
|
+
const match = /^VAPI_TASKS_ESCROW_FACTORY_(\d+)$/.exec(name);
|
|
46251
|
+
const value = raw?.trim();
|
|
46252
|
+
if (!match || !value) continue;
|
|
46253
|
+
overrides[match[1]] = getAddress(value);
|
|
46254
|
+
}
|
|
46255
|
+
return overrides;
|
|
46256
|
+
}
|
|
46257
|
+
function registryEndpoints(baseUrl) {
|
|
46258
|
+
const base = new URL(baseUrl);
|
|
46259
|
+
base.search = "";
|
|
46260
|
+
base.hash = "";
|
|
46261
|
+
const prefix = base.pathname === "/" ? "" : base.pathname.replace(/\/+$/, "");
|
|
46262
|
+
const discovery = new URL(base);
|
|
46263
|
+
discovery.pathname = `${prefix}/api/call/services`;
|
|
46264
|
+
const marketplace = new URL(base);
|
|
46265
|
+
marketplace.pathname = `${prefix}/api/call/discovery`;
|
|
46266
|
+
return { discoveryUrl: discovery.href, marketplaceDiscoveryUrl: marketplace.href };
|
|
46267
|
+
}
|
|
46268
|
+
async function pathExists(path) {
|
|
46269
|
+
try {
|
|
46270
|
+
await stat2(path);
|
|
46271
|
+
return true;
|
|
46272
|
+
} catch (error61) {
|
|
46273
|
+
if (isMissingFile(error61)) return false;
|
|
46274
|
+
throw error61;
|
|
46275
|
+
}
|
|
46276
|
+
}
|
|
46277
|
+
function isMissingFile(error61) {
|
|
46278
|
+
return error61 instanceof Error && "code" in error61 && error61.code === "ENOENT";
|
|
46279
|
+
}
|
|
46280
|
+
function isAlreadyExists(error61) {
|
|
46281
|
+
return error61 instanceof Error && "code" in error61 && error61.code === "EEXIST";
|
|
46282
|
+
}
|
|
46283
|
+
|
|
46284
|
+
// src/wallet-name.ts
|
|
46285
|
+
var DEFAULT_WALLET_NAME = "main";
|
|
46286
|
+
var WALLET_NAME_PATTERN = /^[a-z0-9][a-z0-9-]{0,31}$/;
|
|
46287
|
+
var walletNameSchema = external_exports.string().regex(
|
|
46288
|
+
WALLET_NAME_PATTERN,
|
|
46289
|
+
"A wallet name is 1 to 32 characters of lowercase letters, digits and dashes, starting with a letter or digit."
|
|
46290
|
+
);
|
|
46291
|
+
|
|
46292
|
+
// src/agent-profile.ts
|
|
46293
|
+
var DEFAULT_AGENT_TOOLS = ["call.search", "call.inspect", "call.pay"];
|
|
46294
|
+
var AGENT_GRANTS = ["read", "delegate", "allocate"];
|
|
46295
|
+
var agentProfileShape = {
|
|
46296
|
+
version: external_exports.literal(1),
|
|
46297
|
+
name: walletNameSchema,
|
|
46298
|
+
wallet: external_exports.string(),
|
|
46299
|
+
model: external_exports.string().min(1),
|
|
46300
|
+
instructions: external_exports.string().max(2e4),
|
|
46301
|
+
verifiedOnly: external_exports.boolean().default(true),
|
|
46302
|
+
approveAboveUsd: external_exports.number().min(0).default(0.5),
|
|
46303
|
+
maxPerTaskUsd: external_exports.number().min(0).default(DEFAULT_MAX_PER_TASK_USD),
|
|
46304
|
+
autoReleaseBelowUsd: external_exports.number().min(0).default(DEFAULT_AUTO_RELEASE_BELOW_USD),
|
|
46305
|
+
maxSteps: external_exports.number().int().min(1).max(50).default(12),
|
|
46306
|
+
paused: external_exports.boolean().default(false),
|
|
46307
|
+
grants: external_exports.array(external_exports.enum(AGENT_GRANTS)).default([]),
|
|
46308
|
+
createdAt: external_exports.string()
|
|
46309
|
+
};
|
|
46310
|
+
function createAgentProfileSchema(toolNames) {
|
|
46311
|
+
if (toolNames.length === 0) {
|
|
46312
|
+
throw new Error("Agent profiles require at least one registered tool.");
|
|
46313
|
+
}
|
|
46314
|
+
const tools = [...toolNames];
|
|
46315
|
+
return external_exports.object({ ...agentProfileShape, tools: external_exports.array(external_exports.enum(tools)).default(tools) });
|
|
46316
|
+
}
|
|
46317
|
+
var agentProfileSchema = createAgentProfileSchema(DEFAULT_AGENT_TOOLS);
|
|
46318
|
+
async function listAgentProfiles(home, options = {}) {
|
|
46319
|
+
const directory = getVapiPaths(home).agentsDir;
|
|
46320
|
+
let files;
|
|
46321
|
+
try {
|
|
46322
|
+
files = await readdir2(directory);
|
|
46323
|
+
} catch (error61) {
|
|
46324
|
+
if (isMissingFile(error61)) return [];
|
|
46325
|
+
throw error61;
|
|
46326
|
+
}
|
|
46327
|
+
const warn = options.warn ?? ((message) => console.warn(message));
|
|
46328
|
+
const profiles = [];
|
|
46329
|
+
for (const file2 of files) {
|
|
46330
|
+
if (!file2.endsWith(".json")) continue;
|
|
46331
|
+
try {
|
|
46332
|
+
profiles.push(
|
|
46333
|
+
(options.schema ?? agentProfileSchema).parse(
|
|
46334
|
+
JSON.parse(await readFile3(join4(directory, file2), "utf8"))
|
|
46335
|
+
)
|
|
46336
|
+
);
|
|
46337
|
+
} catch {
|
|
46338
|
+
options.onInvalid?.(file2);
|
|
46339
|
+
warn(`Skipping invalid agent profile ${file2}.`);
|
|
46340
|
+
}
|
|
46341
|
+
}
|
|
46342
|
+
profiles.sort((left, right) => left.name < right.name ? -1 : left.name > right.name ? 1 : 0);
|
|
46343
|
+
return profiles;
|
|
46344
|
+
}
|
|
46345
|
+
|
|
46346
|
+
// src/device.ts
|
|
46347
|
+
import { hostname as operatingSystemHostname } from "node:os";
|
|
46348
|
+
var DEVICE_NAME_PATTERN = /^[a-z0-9][a-z0-9-]{0,31}$/;
|
|
46349
|
+
function normalizedDeviceName(value) {
|
|
46350
|
+
return value.toLowerCase().replace(/[^a-z0-9-]/gu, "-").replace(/-+/gu, "-").replace(/^-+|-+$/gu, "").slice(0, 32).replace(/-+$/gu, "");
|
|
46351
|
+
}
|
|
46352
|
+
function slugifyDeviceName(value) {
|
|
46353
|
+
return normalizedDeviceName(value) || "device";
|
|
46354
|
+
}
|
|
46355
|
+
function deviceName(args = {}) {
|
|
46356
|
+
const environmentValue = args.env === void 0 ? process.env.VAPI_DEVICE : args.env.VAPI_DEVICE;
|
|
46357
|
+
for (const value of [environmentValue?.trim(), args.config?.device]) {
|
|
46358
|
+
if (value === void 0 || value.length === 0) continue;
|
|
46359
|
+
const normalized = normalizedDeviceName(value);
|
|
46360
|
+
if (normalized.length > 0) return normalized;
|
|
46361
|
+
}
|
|
46362
|
+
return slugifyDeviceName(args.hostname ?? operatingSystemHostname());
|
|
46363
|
+
}
|
|
45674
46364
|
|
|
45675
46365
|
// src/keystore.ts
|
|
45676
|
-
import { createCipheriv, createDecipheriv, randomBytes as
|
|
46366
|
+
import { createCipheriv, createDecipheriv, randomBytes as randomBytes3, scrypt } from "node:crypto";
|
|
45677
46367
|
var SCRYPT_N = 2 ** 15;
|
|
45678
46368
|
var SCRYPT_R = 8;
|
|
45679
46369
|
var SCRYPT_P = 1;
|
|
@@ -45748,7 +46438,7 @@ function encryptWithKey(key, plaintext) {
|
|
|
45748
46438
|
if (key.byteLength !== KEY_LENGTH) {
|
|
45749
46439
|
throw new Error(`Expected a ${KEY_LENGTH}-byte AES-256-GCM key.`);
|
|
45750
46440
|
}
|
|
45751
|
-
const nonce =
|
|
46441
|
+
const nonce = randomBytes3(12);
|
|
45752
46442
|
const cipher = createCipheriv("aes-256-gcm", key, nonce, {
|
|
45753
46443
|
authTagLength: AUTH_TAG_LENGTH
|
|
45754
46444
|
});
|
|
@@ -45814,17 +46504,290 @@ function deriveEvmPrivateKey(seed, path = EVM_DERIVATION_PATH) {
|
|
|
45814
46504
|
return `0x${Buffer.from(privateKey).toString("hex")}`;
|
|
45815
46505
|
}
|
|
45816
46506
|
|
|
45817
|
-
// src/
|
|
45818
|
-
|
|
45819
|
-
|
|
45820
|
-
|
|
45821
|
-
|
|
45822
|
-
|
|
46507
|
+
// src/movement.ts
|
|
46508
|
+
import { randomBytes as randomBytes6 } from "node:crypto";
|
|
46509
|
+
import { chmod, mkdir as mkdir6, open as open5, readFile as readFile7, readdir as readdir3, rename as rename5, unlink as unlink5 } from "node:fs/promises";
|
|
46510
|
+
import { join as join6 } from "node:path";
|
|
46511
|
+
|
|
46512
|
+
// src/account-movement-lock.ts
|
|
46513
|
+
import { randomBytes as randomBytes4 } from "node:crypto";
|
|
46514
|
+
import { mkdir as mkdir4, open as open3, readFile as readFile4, stat as stat3, unlink as unlink3 } from "node:fs/promises";
|
|
46515
|
+
import { join as join5 } from "node:path";
|
|
46516
|
+
var AccountMovementLockedError = class extends Error {
|
|
46517
|
+
constructor(account) {
|
|
46518
|
+
super(`Another capital movement is already running for account ${account}.`);
|
|
46519
|
+
this.account = account;
|
|
46520
|
+
this.name = "AccountMovementLockedError";
|
|
46521
|
+
}
|
|
46522
|
+
account;
|
|
46523
|
+
code = "account_movement_locked";
|
|
46524
|
+
};
|
|
46525
|
+
async function withAccountMovementLock(home, account, timeoutMs, operation, signal) {
|
|
46526
|
+
const directory = join5(home, "movements");
|
|
46527
|
+
await mkdir4(directory, { recursive: true, mode: 448 });
|
|
46528
|
+
const path = join5(directory, `.${account}.lock`);
|
|
46529
|
+
const deadline = Date.now() + (timeoutMs ?? 2e3);
|
|
46530
|
+
const owner = JSON.stringify({ pid: process.pid, token: randomBytes4(16).toString("hex") });
|
|
46531
|
+
let handle;
|
|
46532
|
+
while (handle === void 0) {
|
|
46533
|
+
signal?.throwIfAborted();
|
|
46534
|
+
try {
|
|
46535
|
+
handle = await open3(path, "wx", 384);
|
|
46536
|
+
await handle.writeFile(owner, "utf8");
|
|
46537
|
+
await handle.sync();
|
|
46538
|
+
} catch (error61) {
|
|
46539
|
+
if (!(error61 instanceof Error && "code" in error61 && error61.code === "EEXIST")) throw error61;
|
|
46540
|
+
await removeStaleLock2(path);
|
|
46541
|
+
if (Date.now() >= deadline) throw new AccountMovementLockedError(account);
|
|
46542
|
+
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
46543
|
+
}
|
|
46544
|
+
}
|
|
46545
|
+
const ownedHandle = handle;
|
|
46546
|
+
const heartbeat = setInterval(() => {
|
|
46547
|
+
const time3 = /* @__PURE__ */ new Date();
|
|
46548
|
+
void ownedHandle.utimes(time3, time3).catch(() => void 0);
|
|
46549
|
+
}, 3e4);
|
|
46550
|
+
heartbeat.unref();
|
|
46551
|
+
try {
|
|
46552
|
+
return await operation();
|
|
46553
|
+
} finally {
|
|
46554
|
+
clearInterval(heartbeat);
|
|
46555
|
+
await ownedHandle.close();
|
|
46556
|
+
await unlinkOwnedLock(path, owner);
|
|
46557
|
+
}
|
|
46558
|
+
}
|
|
46559
|
+
async function withAccountMovementLocks(home, accounts, timeoutMs, operation) {
|
|
46560
|
+
return await acquireAccountMovementLocks(
|
|
46561
|
+
home,
|
|
46562
|
+
[...new Set(accounts)].sort(),
|
|
46563
|
+
timeoutMs,
|
|
46564
|
+
operation
|
|
46565
|
+
);
|
|
46566
|
+
}
|
|
46567
|
+
async function acquireAccountMovementLocks(home, accounts, timeoutMs, operation) {
|
|
46568
|
+
const [account, ...remaining] = accounts;
|
|
46569
|
+
if (account === void 0) return await operation();
|
|
46570
|
+
return await withAccountMovementLock(
|
|
46571
|
+
home,
|
|
46572
|
+
account,
|
|
46573
|
+
timeoutMs,
|
|
46574
|
+
async () => acquireAccountMovementLocks(home, remaining, timeoutMs, operation)
|
|
46575
|
+
);
|
|
46576
|
+
}
|
|
46577
|
+
async function removeStaleLock2(path) {
|
|
46578
|
+
try {
|
|
46579
|
+
const metadata = await stat3(path);
|
|
46580
|
+
if (Date.now() - metadata.mtimeMs <= 12e4) return;
|
|
46581
|
+
const owner = await readFile4(path, "utf8").catch(() => "");
|
|
46582
|
+
const pid = lockOwnerPid2(owner);
|
|
46583
|
+
if (pid !== void 0 && processIsAlive2(pid)) return;
|
|
46584
|
+
await unlinkOwnedLock(path, owner);
|
|
46585
|
+
} catch (error61) {
|
|
46586
|
+
if (!isMissingFile(error61)) throw error61;
|
|
46587
|
+
}
|
|
46588
|
+
}
|
|
46589
|
+
async function unlinkOwnedLock(path, owner) {
|
|
46590
|
+
try {
|
|
46591
|
+
if (await readFile4(path, "utf8") === owner) await unlink3(path);
|
|
46592
|
+
} catch (error61) {
|
|
46593
|
+
if (!isMissingFile(error61)) throw error61;
|
|
46594
|
+
}
|
|
46595
|
+
}
|
|
46596
|
+
function lockOwnerPid2(owner) {
|
|
46597
|
+
try {
|
|
46598
|
+
const value = JSON.parse(owner);
|
|
46599
|
+
if (typeof value !== "object" || value === null) return void 0;
|
|
46600
|
+
const pid = Reflect.get(value, "pid");
|
|
46601
|
+
return typeof pid === "number" && Number.isSafeInteger(pid) && pid > 0 ? pid : void 0;
|
|
46602
|
+
} catch {
|
|
46603
|
+
return void 0;
|
|
46604
|
+
}
|
|
46605
|
+
}
|
|
46606
|
+
function processIsAlive2(pid) {
|
|
46607
|
+
try {
|
|
46608
|
+
process.kill(pid, 0);
|
|
46609
|
+
return true;
|
|
46610
|
+
} catch (error61) {
|
|
46611
|
+
return error61 instanceof Error && "code" in error61 && error61.code === "EPERM";
|
|
46612
|
+
}
|
|
46613
|
+
}
|
|
46614
|
+
|
|
46615
|
+
// src/agent-run.ts
|
|
46616
|
+
var RUN_ID_PATTERN = /^run_[0-9a-f]{24}$/;
|
|
46617
|
+
var runRefSchema = external_exports.strictObject({
|
|
46618
|
+
id: external_exports.string().regex(RUN_ID_PATTERN),
|
|
46619
|
+
swarm: external_exports.string().optional(),
|
|
46620
|
+
member: external_exports.string().optional(),
|
|
46621
|
+
parentRunId: external_exports.string().optional()
|
|
46622
|
+
});
|
|
46623
|
+
|
|
46624
|
+
// src/receipts.ts
|
|
46625
|
+
var RECEIPT_JOURNAL_BUSY_MESSAGE = "The receipt journal is busy. Wait for the other vapi command to finish, then retry.";
|
|
46626
|
+
async function withReceiptJournalLock(path, operation) {
|
|
46627
|
+
return await withFileLock(`${path}.lock`, operation, {
|
|
46628
|
+
lockedMessage: RECEIPT_JOURNAL_BUSY_MESSAGE
|
|
46629
|
+
});
|
|
46630
|
+
}
|
|
46631
|
+
var durationSchema = external_exports.number().nonnegative().finite();
|
|
46632
|
+
var receiptSchema = external_exports.strictObject({
|
|
46633
|
+
id: external_exports.string().min(1),
|
|
46634
|
+
timestamp: external_exports.iso.datetime(),
|
|
46635
|
+
run: runRefSchema.optional(),
|
|
46636
|
+
kind: external_exports.literal("transfer").optional(),
|
|
46637
|
+
wallet: walletNameSchema.optional(),
|
|
46638
|
+
resourceUrl: external_exports.url(),
|
|
46639
|
+
method: external_exports.string().min(1).optional(),
|
|
46640
|
+
source: external_exports.string().min(1).optional(),
|
|
46641
|
+
provenance: external_exports.array(
|
|
46642
|
+
external_exports.strictObject({
|
|
46643
|
+
source: external_exports.string().min(1),
|
|
46644
|
+
sourceUrl: external_exports.string().optional(),
|
|
46645
|
+
ref: external_exports.string().optional()
|
|
46646
|
+
})
|
|
46647
|
+
).optional(),
|
|
46648
|
+
quote: external_exports.strictObject({
|
|
46649
|
+
network: external_exports.string(),
|
|
46650
|
+
asset: external_exports.string().optional(),
|
|
46651
|
+
amountAtomic: external_exports.string().regex(/^\d+$/),
|
|
46652
|
+
payTo: external_exports.string().optional()
|
|
46653
|
+
}).optional(),
|
|
46654
|
+
payer: external_exports.string().optional(),
|
|
46655
|
+
paymentId: external_exports.string().regex(/^[A-Za-z0-9_-]{16,128}$/).optional(),
|
|
46656
|
+
authorization: external_exports.strictObject({
|
|
46657
|
+
from: external_exports.string().regex(/^0x[0-9a-fA-F]{40}$/),
|
|
46658
|
+
nonce: external_exports.string().regex(/^0x[0-9a-fA-F]{64}$/),
|
|
46659
|
+
validBefore: external_exports.string().regex(/^\d+$/)
|
|
46660
|
+
}).optional(),
|
|
46661
|
+
transfer: external_exports.strictObject({
|
|
46662
|
+
to: external_exports.string().regex(/^0x[0-9a-fA-F]{40}$/),
|
|
46663
|
+
toName: external_exports.string().min(1),
|
|
46664
|
+
toKind: external_exports.enum(["owner", "account"]),
|
|
46665
|
+
amountAtomic: external_exports.string().regex(/^\d+$/),
|
|
46666
|
+
network: external_exports.string().min(1),
|
|
46667
|
+
nonce: external_exports.string().regex(/^0x[0-9a-fA-F]{64}$/),
|
|
46668
|
+
status: external_exports.enum(["sent", "unknown", "failed"]),
|
|
46669
|
+
txHash: external_exports.string().regex(/^0x[0-9a-fA-F]+$/).nullable(),
|
|
46670
|
+
replayed: external_exports.boolean(),
|
|
46671
|
+
reservedOn: external_exports.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional(),
|
|
46672
|
+
request: external_exports.strictObject({
|
|
46673
|
+
authorization: external_exports.strictObject({
|
|
46674
|
+
from: external_exports.string().regex(/^0x[0-9a-fA-F]{40}$/),
|
|
46675
|
+
to: external_exports.string().regex(/^0x[0-9a-fA-F]{40}$/),
|
|
46676
|
+
value: external_exports.string().regex(/^\d+$/),
|
|
46677
|
+
validAfter: external_exports.string().regex(/^\d+$/),
|
|
46678
|
+
validBefore: external_exports.string().regex(/^\d+$/),
|
|
46679
|
+
nonce: external_exports.string().regex(/^0x[0-9a-fA-F]{64}$/)
|
|
46680
|
+
}),
|
|
46681
|
+
signature: external_exports.string().regex(/^0x[0-9a-fA-F]+$/)
|
|
46682
|
+
}).optional()
|
|
46683
|
+
}).optional(),
|
|
46684
|
+
settlement: external_exports.strictObject({
|
|
46685
|
+
outcome: external_exports.enum(["succeeded", "rejected", "unknown"]),
|
|
46686
|
+
transaction: external_exports.string().optional(),
|
|
46687
|
+
explorerUrl: external_exports.url().optional(),
|
|
46688
|
+
evidence: external_exports.unknown().optional()
|
|
46689
|
+
}).optional(),
|
|
46690
|
+
latencyMs: external_exports.number().nonnegative().finite().optional(),
|
|
46691
|
+
status: external_exports.number().int().min(100).max(599).optional(),
|
|
46692
|
+
error: external_exports.strictObject({ code: external_exports.string(), message: external_exports.string() }).optional(),
|
|
46693
|
+
phases: external_exports.strictObject({
|
|
46694
|
+
discoverMs: durationSchema.optional(),
|
|
46695
|
+
quoteMs: durationSchema.optional(),
|
|
46696
|
+
signMs: durationSchema.optional(),
|
|
46697
|
+
requestMs: durationSchema.optional(),
|
|
46698
|
+
settleMs: durationSchema.optional()
|
|
46699
|
+
}).optional(),
|
|
46700
|
+
listing: external_exports.strictObject({
|
|
46701
|
+
name: external_exports.string().min(1).optional(),
|
|
46702
|
+
providerHost: external_exports.string().min(1).optional(),
|
|
46703
|
+
source: external_exports.string().min(1)
|
|
46704
|
+
}).optional(),
|
|
46705
|
+
retry: external_exports.number().int().nonnegative().optional(),
|
|
46706
|
+
policy: external_exports.strictObject({
|
|
46707
|
+
maxPriceUsd: external_exports.string().regex(/^\d+(?:\.\d{1,6})?$/).optional(),
|
|
46708
|
+
capsApplied: external_exports.boolean()
|
|
46709
|
+
}).optional(),
|
|
46710
|
+
client: external_exports.strictObject({ name: external_exports.literal("vapi-network"), version: external_exports.string().min(1) }).optional(),
|
|
46711
|
+
outcome: external_exports.enum([
|
|
46712
|
+
"paid",
|
|
46713
|
+
"signed_in",
|
|
46714
|
+
"declined_policy",
|
|
46715
|
+
"failed_request",
|
|
46716
|
+
"settlement_rejected",
|
|
46717
|
+
"settlement_unknown"
|
|
46718
|
+
]).optional()
|
|
46719
|
+
});
|
|
46720
|
+
function parseReceipt(value) {
|
|
46721
|
+
const receipt = receiptSchema.parse(value);
|
|
46722
|
+
const transaction = receipt.settlement?.transaction;
|
|
46723
|
+
const network = receipt.quote?.network;
|
|
46724
|
+
const explorerUrl = transaction === void 0 || network === void 0 ? void 0 : explorerTransactionUrl(network, transaction);
|
|
46725
|
+
if (explorerUrl === void 0 || receipt.settlement?.explorerUrl === explorerUrl) {
|
|
46726
|
+
return receipt;
|
|
46727
|
+
}
|
|
46728
|
+
return {
|
|
46729
|
+
...receipt,
|
|
46730
|
+
settlement: {
|
|
46731
|
+
outcome: receipt.settlement.outcome,
|
|
46732
|
+
...receipt.settlement.transaction ? { transaction: receipt.settlement.transaction } : {},
|
|
46733
|
+
explorerUrl,
|
|
46734
|
+
...receipt.settlement.evidence === void 0 ? {} : { evidence: receipt.settlement.evidence }
|
|
46735
|
+
}
|
|
46736
|
+
};
|
|
46737
|
+
}
|
|
46738
|
+
function receiptWallet(receipt) {
|
|
46739
|
+
return receipt.wallet ?? DEFAULT_WALLET_NAME;
|
|
46740
|
+
}
|
|
46741
|
+
|
|
46742
|
+
// src/movement-open.ts
|
|
46743
|
+
function isOpenMovementLeg(leg, journalEvidence) {
|
|
46744
|
+
if (leg.status === "sent" || leg.status === "cancelled") return false;
|
|
46745
|
+
if (leg.restored === true) return true;
|
|
46746
|
+
if (leg.status === "planned" || leg.status === "unknown") return true;
|
|
46747
|
+
if (leg.status !== "failed") return false;
|
|
46748
|
+
if (leg.retryable === true) return true;
|
|
46749
|
+
return journalEvidence === "signed";
|
|
46750
|
+
}
|
|
46751
|
+
|
|
46752
|
+
// src/transfer-journal.ts
|
|
46753
|
+
import { readFile as readFile5 } from "node:fs/promises";
|
|
46754
|
+
async function readTransferJournal(home, from, nonce) {
|
|
46755
|
+
let raw;
|
|
46756
|
+
try {
|
|
46757
|
+
raw = await readFile5(getVapiPaths(home).receipts, "utf8");
|
|
46758
|
+
} catch (error61) {
|
|
46759
|
+
if (isMissingFile(error61)) return { signed: false };
|
|
46760
|
+
throw error61;
|
|
46761
|
+
}
|
|
46762
|
+
let latest;
|
|
46763
|
+
let request;
|
|
46764
|
+
for (const [index, line] of raw.split("\n").entries()) {
|
|
46765
|
+
if (!line.trim()) continue;
|
|
46766
|
+
let receipt;
|
|
46767
|
+
try {
|
|
46768
|
+
receipt = parseReceipt(JSON.parse(line));
|
|
46769
|
+
} catch (error61) {
|
|
46770
|
+
throw new Error(`Invalid receipt on JSONL line ${index + 1}.`, { cause: error61 });
|
|
46771
|
+
}
|
|
46772
|
+
if (receiptWallet(receipt) !== from || receipt.transfer?.nonce.toLowerCase() !== nonce.toLowerCase()) {
|
|
46773
|
+
continue;
|
|
46774
|
+
}
|
|
46775
|
+
latest = receipt;
|
|
46776
|
+
if (receipt.transfer.request !== void 0) request = receipt;
|
|
46777
|
+
}
|
|
46778
|
+
return {
|
|
46779
|
+
signed: latest !== void 0,
|
|
46780
|
+
...latest === void 0 ? {} : { latest },
|
|
46781
|
+
...request === void 0 ? {} : { request },
|
|
46782
|
+
...request?.transfer?.request?.authorization.validBefore === void 0 ? {} : { validBefore: request.transfer.request.authorization.validBefore },
|
|
46783
|
+
...(latest?.transfer?.reservedOn ?? request?.transfer?.reservedOn) === void 0 ? {} : { reservedOn: latest?.transfer?.reservedOn ?? request?.transfer?.reservedOn }
|
|
46784
|
+
};
|
|
46785
|
+
}
|
|
45823
46786
|
|
|
45824
46787
|
// src/vault.ts
|
|
45825
|
-
import { randomBytes as
|
|
45826
|
-
import { mkdir, open as
|
|
45827
|
-
import { dirname } from "node:path";
|
|
46788
|
+
import { randomBytes as randomBytes5 } from "node:crypto";
|
|
46789
|
+
import { mkdir as mkdir5, open as open4, readFile as readFile6, rename as rename4, rm, stat as stat4, unlink as unlink4, writeFile as writeFile3 } from "node:fs/promises";
|
|
46790
|
+
import { dirname as dirname3 } from "node:path";
|
|
45828
46791
|
var VaultError = class extends Error {
|
|
45829
46792
|
code;
|
|
45830
46793
|
constructor(code, message, options) {
|
|
@@ -46369,9 +47332,9 @@ async function readVaultFile(path, key) {
|
|
|
46369
47332
|
async function readVaultFileUnlocked(path) {
|
|
46370
47333
|
let raw;
|
|
46371
47334
|
try {
|
|
46372
|
-
raw = await
|
|
47335
|
+
raw = await readFile6(path, "utf8");
|
|
46373
47336
|
} catch (error61) {
|
|
46374
|
-
if (
|
|
47337
|
+
if (isMissingFile2(error61)) {
|
|
46375
47338
|
throw new VaultError("not_found", "No vault file was found at the requested path.", {
|
|
46376
47339
|
cause: error61
|
|
46377
47340
|
});
|
|
@@ -46382,9 +47345,9 @@ async function readVaultFileUnlocked(path) {
|
|
|
46382
47345
|
}
|
|
46383
47346
|
async function refuseExistingVault(path) {
|
|
46384
47347
|
try {
|
|
46385
|
-
await
|
|
47348
|
+
await stat4(path);
|
|
46386
47349
|
} catch (error61) {
|
|
46387
|
-
if (
|
|
47350
|
+
if (isMissingFile2(error61)) return;
|
|
46388
47351
|
throw new VaultError("corrupt", "Unable to check whether the vault already exists.", {
|
|
46389
47352
|
cause: error61
|
|
46390
47353
|
});
|
|
@@ -46392,29 +47355,29 @@ async function refuseExistingVault(path) {
|
|
|
46392
47355
|
throw new VaultError("corrupt", "A vault already exists at the requested path.");
|
|
46393
47356
|
}
|
|
46394
47357
|
async function writeVaultFile(path, file2) {
|
|
46395
|
-
await
|
|
46396
|
-
const temporaryPath = `${path}.${process.pid}.${
|
|
47358
|
+
await mkdir5(dirname3(path), { recursive: true, mode: 448 });
|
|
47359
|
+
const temporaryPath = `${path}.${process.pid}.${randomBytes5(6).toString("hex")}.tmp`;
|
|
46397
47360
|
try {
|
|
46398
|
-
await
|
|
47361
|
+
await writeFile3(temporaryPath, `${JSON.stringify(file2, null, 2)}
|
|
46399
47362
|
`, {
|
|
46400
47363
|
encoding: "utf8",
|
|
46401
47364
|
mode: 384,
|
|
46402
47365
|
flag: "wx"
|
|
46403
47366
|
});
|
|
46404
|
-
await
|
|
47367
|
+
await rename4(temporaryPath, path);
|
|
46405
47368
|
} catch (error61) {
|
|
46406
47369
|
await rm(temporaryPath, { force: true }).catch(() => void 0);
|
|
46407
47370
|
throw error61;
|
|
46408
47371
|
}
|
|
46409
47372
|
}
|
|
46410
47373
|
async function withVaultLock(path, operation) {
|
|
46411
|
-
await
|
|
47374
|
+
await mkdir5(dirname3(path), { recursive: true, mode: 448 });
|
|
46412
47375
|
const lockPath = `${path}.lock`;
|
|
46413
47376
|
const deadline = Date.now() + VAULT_LOCK_TIMEOUT_MS;
|
|
46414
47377
|
let handle;
|
|
46415
47378
|
while (!handle) {
|
|
46416
47379
|
try {
|
|
46417
|
-
handle = await
|
|
47380
|
+
handle = await open4(lockPath, "wx", 384);
|
|
46418
47381
|
} catch (error61) {
|
|
46419
47382
|
if (!isExistingFile(error61)) throw error61;
|
|
46420
47383
|
await removeStaleVaultLock(lockPath);
|
|
@@ -46428,18 +47391,18 @@ async function withVaultLock(path, operation) {
|
|
|
46428
47391
|
return await operation();
|
|
46429
47392
|
} finally {
|
|
46430
47393
|
await handle.close();
|
|
46431
|
-
await
|
|
47394
|
+
await unlink4(lockPath).catch(() => void 0);
|
|
46432
47395
|
}
|
|
46433
47396
|
}
|
|
46434
47397
|
async function removeStaleVaultLock(path) {
|
|
46435
47398
|
try {
|
|
46436
|
-
const metadata = await
|
|
46437
|
-
if (Date.now() - metadata.mtimeMs > STALE_VAULT_LOCK_MS) await
|
|
47399
|
+
const metadata = await stat4(path);
|
|
47400
|
+
if (Date.now() - metadata.mtimeMs > STALE_VAULT_LOCK_MS) await unlink4(path);
|
|
46438
47401
|
} catch (error61) {
|
|
46439
|
-
if (!
|
|
47402
|
+
if (!isMissingFile2(error61)) throw error61;
|
|
46440
47403
|
}
|
|
46441
47404
|
}
|
|
46442
|
-
function
|
|
47405
|
+
function isMissingFile2(error61) {
|
|
46443
47406
|
return typeof error61 === "object" && error61 !== null && "code" in error61 && Reflect.get(error61, "code") === "ENOENT";
|
|
46444
47407
|
}
|
|
46445
47408
|
function isExistingFile(error61) {
|
|
@@ -46465,6 +47428,7 @@ var routerRefillSchema = external_exports.object({
|
|
|
46465
47428
|
});
|
|
46466
47429
|
var ceilingSweepPendingSchema = external_exports.strictObject({
|
|
46467
47430
|
owner: external_exports.string().regex(/^0x[0-9a-fA-F]{40}$/),
|
|
47431
|
+
target: external_exports.string().regex(/^0x[0-9a-fA-F]{40}$/).optional(),
|
|
46468
47432
|
amountAtomic: external_exports.string().regex(/^\d+$/),
|
|
46469
47433
|
nonce: external_exports.string().regex(/^0x[0-9a-fA-F]{64}$/),
|
|
46470
47434
|
createdAt: external_exports.iso.datetime(),
|
|
@@ -46488,6 +47452,426 @@ var walletRegistrySchema = external_exports.object({
|
|
|
46488
47452
|
wallets: external_exports.record(walletNameSchema, walletEntrySchema)
|
|
46489
47453
|
});
|
|
46490
47454
|
|
|
47455
|
+
// src/movement.ts
|
|
47456
|
+
var movementIdSchema = external_exports.string().regex(/^mv_[A-Za-z0-9_-]{8,128}$/);
|
|
47457
|
+
var nonceSchema = external_exports.string().regex(/^0x[0-9a-fA-F]{64}$/);
|
|
47458
|
+
var amountSchema = external_exports.string().regex(/^\d+\.\d{2}$/);
|
|
47459
|
+
var movementReasonSchema = external_exports.enum([
|
|
47460
|
+
"distribute",
|
|
47461
|
+
"allocate",
|
|
47462
|
+
"rebalance",
|
|
47463
|
+
"delegate",
|
|
47464
|
+
"sweep",
|
|
47465
|
+
"send"
|
|
47466
|
+
]);
|
|
47467
|
+
var movementPurposeSchema = external_exports.enum(["send", "sweep"]);
|
|
47468
|
+
var movementAddressSchema = external_exports.string().refine((value) => isAddress(value, { strict: false }));
|
|
47469
|
+
var movementLegSchema = external_exports.strictObject({
|
|
47470
|
+
from: walletNameSchema,
|
|
47471
|
+
to: external_exports.string().trim().min(1),
|
|
47472
|
+
fromAddress: movementAddressSchema.optional(),
|
|
47473
|
+
toAddress: movementAddressSchema.optional(),
|
|
47474
|
+
amountUsd: amountSchema,
|
|
47475
|
+
purpose: movementPurposeSchema,
|
|
47476
|
+
nonce: nonceSchema,
|
|
47477
|
+
status: external_exports.enum(["planned", "sent", "failed", "unknown", "cancelled"]),
|
|
47478
|
+
txHash: external_exports.string().regex(/^0x[0-9a-fA-F]+$/).optional(),
|
|
47479
|
+
reason: external_exports.string().min(1).optional(),
|
|
47480
|
+
retryable: external_exports.boolean().optional(),
|
|
47481
|
+
restored: external_exports.literal(true).optional(),
|
|
47482
|
+
addressBindingSource: external_exports.enum(["journal", "terminal-review"]).optional()
|
|
47483
|
+
});
|
|
47484
|
+
var movementSchema = external_exports.strictObject({
|
|
47485
|
+
v: external_exports.literal(2),
|
|
47486
|
+
id: movementIdSchema,
|
|
47487
|
+
reason: movementReasonSchema,
|
|
47488
|
+
from: walletNameSchema,
|
|
47489
|
+
// The only account a `sweep` leg may name besides the owner. Sweeps skip the
|
|
47490
|
+
// spend caps, so the parent comes from the plan, never from a leg's `to`.
|
|
47491
|
+
treasury: walletNameSchema.optional(),
|
|
47492
|
+
network: external_exports.enum([BASE_MAINNET_CAIP2, ARC_MAINNET_CAIP2]),
|
|
47493
|
+
createdAt: external_exports.iso.datetime(),
|
|
47494
|
+
legs: external_exports.array(movementLegSchema).min(1)
|
|
47495
|
+
});
|
|
47496
|
+
var legacyMovementSchema = external_exports.strictObject({
|
|
47497
|
+
v: external_exports.literal(1),
|
|
47498
|
+
id: movementIdSchema,
|
|
47499
|
+
reason: external_exports.literal("distribute"),
|
|
47500
|
+
from: walletNameSchema,
|
|
47501
|
+
network: external_exports.enum([BASE_MAINNET_CAIP2, ARC_MAINNET_CAIP2]),
|
|
47502
|
+
createdAt: external_exports.iso.datetime(),
|
|
47503
|
+
legs: external_exports.array(
|
|
47504
|
+
external_exports.strictObject({
|
|
47505
|
+
to: external_exports.string().trim().min(1),
|
|
47506
|
+
amountUsd: amountSchema,
|
|
47507
|
+
nonce: nonceSchema,
|
|
47508
|
+
status: external_exports.enum(["planned", "sent", "failed", "unknown"]),
|
|
47509
|
+
txHash: external_exports.string().regex(/^0x[0-9a-fA-F]+$/).optional(),
|
|
47510
|
+
reason: external_exports.string().min(1).optional(),
|
|
47511
|
+
retryable: external_exports.boolean().optional()
|
|
47512
|
+
})
|
|
47513
|
+
).min(1)
|
|
47514
|
+
});
|
|
47515
|
+
var MovementError = class extends Error {
|
|
47516
|
+
constructor(code, message, movementId) {
|
|
47517
|
+
super(message);
|
|
47518
|
+
this.code = code;
|
|
47519
|
+
this.movementId = movementId;
|
|
47520
|
+
this.name = "MovementError";
|
|
47521
|
+
}
|
|
47522
|
+
code;
|
|
47523
|
+
movementId;
|
|
47524
|
+
};
|
|
47525
|
+
async function listUnfinishedMovementSnapshots(options) {
|
|
47526
|
+
const directory = movementsDirectory(options.home);
|
|
47527
|
+
let names;
|
|
47528
|
+
try {
|
|
47529
|
+
names = await readdir3(directory);
|
|
47530
|
+
} catch (error61) {
|
|
47531
|
+
if (isMissingFile(error61)) return [];
|
|
47532
|
+
throw error61;
|
|
47533
|
+
}
|
|
47534
|
+
const snapshots = [];
|
|
47535
|
+
for (const name of names.filter((candidate) => candidate.endsWith(".json")).sort()) {
|
|
47536
|
+
const movement = parseMovement(JSON.parse(await readFile7(join6(directory, name), "utf8")));
|
|
47537
|
+
if (options.from !== void 0 && !movement.legs.some((leg) => leg.from === options.from)) {
|
|
47538
|
+
continue;
|
|
47539
|
+
}
|
|
47540
|
+
const pendingLegs = movement.legs.filter((leg) => leg.status === "planned").length;
|
|
47541
|
+
const unknownLegs = movement.legs.filter((leg) => leg.status === "unknown").length;
|
|
47542
|
+
const evidence = await movementOpenEvidence(options.home, movement, options.from);
|
|
47543
|
+
if (!evidence.open) continue;
|
|
47544
|
+
snapshots.push({
|
|
47545
|
+
movement,
|
|
47546
|
+
summary: {
|
|
47547
|
+
id: movement.id,
|
|
47548
|
+
from: movement.from,
|
|
47549
|
+
network: movement.network,
|
|
47550
|
+
createdAt: movement.createdAt,
|
|
47551
|
+
pendingLegs,
|
|
47552
|
+
unknownLegs
|
|
47553
|
+
},
|
|
47554
|
+
signedFailedLegKeys: evidence.signedFailedLegKeys
|
|
47555
|
+
});
|
|
47556
|
+
}
|
|
47557
|
+
return snapshots.sort(
|
|
47558
|
+
(left, right) => left.summary.createdAt.localeCompare(right.summary.createdAt) || left.summary.id.localeCompare(right.summary.id)
|
|
47559
|
+
);
|
|
47560
|
+
}
|
|
47561
|
+
async function movementOpenEvidence(home, movement, from) {
|
|
47562
|
+
let open6 = false;
|
|
47563
|
+
const signedFailedLegKeys = [];
|
|
47564
|
+
for (const leg of movement.legs) {
|
|
47565
|
+
if (from !== void 0 && leg.from !== from) continue;
|
|
47566
|
+
if (isOpenMovementLeg(leg, "unsigned")) open6 = true;
|
|
47567
|
+
if (leg.status !== "failed") continue;
|
|
47568
|
+
const journal = await readTransferJournal(home, leg.from, leg.nonce);
|
|
47569
|
+
if (journal.signed) signedFailedLegKeys.push(movementTransferKey(leg.from, leg.nonce));
|
|
47570
|
+
if (isOpenMovementLeg(leg, journal.signed ? "signed" : "unsigned")) open6 = true;
|
|
47571
|
+
}
|
|
47572
|
+
return { open: open6, signedFailedLegKeys };
|
|
47573
|
+
}
|
|
47574
|
+
async function readMovement(home, id) {
|
|
47575
|
+
const safeId = parseMovementId(id);
|
|
47576
|
+
try {
|
|
47577
|
+
return parseMovement(
|
|
47578
|
+
JSON.parse(await readFile7(join6(movementsDirectory(home), `${safeId}.json`), "utf8"))
|
|
47579
|
+
);
|
|
47580
|
+
} catch (error61) {
|
|
47581
|
+
if (isMissingFile(error61)) {
|
|
47582
|
+
throw new MovementError(
|
|
47583
|
+
"movement_not_found",
|
|
47584
|
+
`No movement named ${safeId} was found.`,
|
|
47585
|
+
safeId
|
|
47586
|
+
);
|
|
47587
|
+
}
|
|
47588
|
+
throw error61;
|
|
47589
|
+
}
|
|
47590
|
+
}
|
|
47591
|
+
function movementTransferKey(from, nonce) {
|
|
47592
|
+
return `${from}\0${nonce.toLowerCase()}`;
|
|
47593
|
+
}
|
|
47594
|
+
async function reconcileSignedLegs(home, id, signedKeys) {
|
|
47595
|
+
const initial = await readMovement(home, id);
|
|
47596
|
+
return await withMovementLocks(home, movementSenders(initial), void 0, async () => {
|
|
47597
|
+
const movement = await readMovement(home, id);
|
|
47598
|
+
const reconciled = movementWithSignedLegsUnknown(movement, signedKeys);
|
|
47599
|
+
if (reconciled !== movement) await writeMovement(home, reconciled);
|
|
47600
|
+
return reconciled;
|
|
47601
|
+
});
|
|
47602
|
+
}
|
|
47603
|
+
async function markOpenLegsRestored(home, id) {
|
|
47604
|
+
let initial;
|
|
47605
|
+
try {
|
|
47606
|
+
initial = await readMovement(home, id);
|
|
47607
|
+
} catch (error61) {
|
|
47608
|
+
if (error61 instanceof MovementError && error61.code === "movement_not_found" || error61 instanceof SyntaxError || error61 instanceof external_exports.ZodError) {
|
|
47609
|
+
return void 0;
|
|
47610
|
+
}
|
|
47611
|
+
throw error61;
|
|
47612
|
+
}
|
|
47613
|
+
return await withMovementLocks(home, movementSenders(initial), void 0, async () => {
|
|
47614
|
+
const movement = await readMovement(home, id);
|
|
47615
|
+
let changed = false;
|
|
47616
|
+
const legs = movement.legs.map((leg) => {
|
|
47617
|
+
if (leg.status === "sent" || leg.status === "cancelled" || leg.restored === true) return leg;
|
|
47618
|
+
changed = true;
|
|
47619
|
+
return { ...leg, restored: true };
|
|
47620
|
+
});
|
|
47621
|
+
if (!changed) return movement;
|
|
47622
|
+
const restored = movementSchema.parse({ ...movement, legs });
|
|
47623
|
+
await writeMovement(home, restored);
|
|
47624
|
+
return restored;
|
|
47625
|
+
});
|
|
47626
|
+
}
|
|
47627
|
+
function unknownLeg(leg) {
|
|
47628
|
+
return {
|
|
47629
|
+
from: leg.from,
|
|
47630
|
+
to: leg.to,
|
|
47631
|
+
...legAddressBinding(leg),
|
|
47632
|
+
amountUsd: leg.amountUsd,
|
|
47633
|
+
purpose: leg.purpose,
|
|
47634
|
+
nonce: leg.nonce,
|
|
47635
|
+
status: "unknown",
|
|
47636
|
+
...restoredMarker(leg)
|
|
47637
|
+
};
|
|
47638
|
+
}
|
|
47639
|
+
function restoredMarker(leg) {
|
|
47640
|
+
return leg.restored === true ? { restored: true } : {};
|
|
47641
|
+
}
|
|
47642
|
+
function legAddressBinding(leg) {
|
|
47643
|
+
return {
|
|
47644
|
+
...leg.fromAddress === void 0 ? {} : { fromAddress: leg.fromAddress },
|
|
47645
|
+
...leg.toAddress === void 0 ? {} : { toAddress: leg.toAddress },
|
|
47646
|
+
...leg.addressBindingSource === void 0 ? {} : { addressBindingSource: leg.addressBindingSource }
|
|
47647
|
+
};
|
|
47648
|
+
}
|
|
47649
|
+
function isExpiredRestoredReview(leg) {
|
|
47650
|
+
return leg.restored === true && leg.reason === "restored_nonce_expired";
|
|
47651
|
+
}
|
|
47652
|
+
function movementWithSignedLegsUnknown(movement, signedKeys) {
|
|
47653
|
+
let changed = false;
|
|
47654
|
+
const legs = movement.legs.map((leg) => {
|
|
47655
|
+
if (isExpiredRestoredReview(leg)) return leg;
|
|
47656
|
+
if (leg.status === "sent" || leg.status === "cancelled" || !signedKeys.has(movementTransferKey(leg.from, leg.nonce)) || leg.status === "unknown" && leg.txHash === void 0 && leg.reason === void 0 && leg.retryable === void 0) {
|
|
47657
|
+
return leg;
|
|
47658
|
+
}
|
|
47659
|
+
changed = true;
|
|
47660
|
+
return unknownLeg(leg);
|
|
47661
|
+
});
|
|
47662
|
+
return changed ? movementSchema.parse({ ...movement, legs }) : movement;
|
|
47663
|
+
}
|
|
47664
|
+
function parseMovement(value) {
|
|
47665
|
+
const current = movementSchema.safeParse(value);
|
|
47666
|
+
if (current.success) return current.data;
|
|
47667
|
+
const legacy = legacyMovementSchema.parse(value);
|
|
47668
|
+
return movementSchema.parse({
|
|
47669
|
+
...legacy,
|
|
47670
|
+
v: 2,
|
|
47671
|
+
legs: legacy.legs.map((leg) => ({
|
|
47672
|
+
...leg,
|
|
47673
|
+
from: legacy.from,
|
|
47674
|
+
purpose: "send"
|
|
47675
|
+
}))
|
|
47676
|
+
});
|
|
47677
|
+
}
|
|
47678
|
+
function movementSenders(movement) {
|
|
47679
|
+
return sortedUniqueSenders(movement.legs.map((leg) => leg.from));
|
|
47680
|
+
}
|
|
47681
|
+
function sortedUniqueSenders(senders) {
|
|
47682
|
+
return [...new Set(senders)].sort();
|
|
47683
|
+
}
|
|
47684
|
+
function parseMovementId(value) {
|
|
47685
|
+
const parsed = movementIdSchema.safeParse(value);
|
|
47686
|
+
if (!parsed.success) throw new MovementError("invalid_movement", "The movement id is invalid.");
|
|
47687
|
+
return parsed.data;
|
|
47688
|
+
}
|
|
47689
|
+
function movementsDirectory(home) {
|
|
47690
|
+
return join6(home, "movements");
|
|
47691
|
+
}
|
|
47692
|
+
async function writeMovement(home, movement) {
|
|
47693
|
+
const parsed = movementSchema.parse(movement);
|
|
47694
|
+
const directory = movementsDirectory(home);
|
|
47695
|
+
await mkdir6(directory, { recursive: true, mode: 448 });
|
|
47696
|
+
await chmod(directory, 448);
|
|
47697
|
+
const path = join6(directory, `${parsed.id}.json`);
|
|
47698
|
+
const temporary = join6(
|
|
47699
|
+
directory,
|
|
47700
|
+
`.${parsed.id}.${process.pid}.${randomBytes6(6).toString("hex")}.tmp`
|
|
47701
|
+
);
|
|
47702
|
+
let handle;
|
|
47703
|
+
try {
|
|
47704
|
+
handle = await open5(temporary, "wx", 384);
|
|
47705
|
+
await handle.writeFile(serializeMovement(parsed), "utf8");
|
|
47706
|
+
await handle.sync();
|
|
47707
|
+
await handle.close();
|
|
47708
|
+
handle = void 0;
|
|
47709
|
+
await rename5(temporary, path);
|
|
47710
|
+
const directoryHandle = await open5(directory, "r");
|
|
47711
|
+
try {
|
|
47712
|
+
await directoryHandle.sync();
|
|
47713
|
+
} finally {
|
|
47714
|
+
await directoryHandle.close();
|
|
47715
|
+
}
|
|
47716
|
+
} catch (error61) {
|
|
47717
|
+
await handle?.close().catch(() => void 0);
|
|
47718
|
+
await unlink5(temporary).catch(() => void 0);
|
|
47719
|
+
throw error61;
|
|
47720
|
+
}
|
|
47721
|
+
}
|
|
47722
|
+
function serializeMovement(movement) {
|
|
47723
|
+
return `${JSON.stringify(movementSchema.parse(movement), null, 2)}
|
|
47724
|
+
`;
|
|
47725
|
+
}
|
|
47726
|
+
async function withMovementLock(home, from, timeoutMs, operation) {
|
|
47727
|
+
try {
|
|
47728
|
+
return await withAccountMovementLocks(home, [from], timeoutMs, operation);
|
|
47729
|
+
} catch (error61) {
|
|
47730
|
+
if (error61 instanceof AccountMovementLockedError) {
|
|
47731
|
+
throw new MovementError(
|
|
47732
|
+
"movement_locked",
|
|
47733
|
+
`Another movement is already running for account ${from}.`
|
|
47734
|
+
);
|
|
47735
|
+
}
|
|
47736
|
+
throw error61;
|
|
47737
|
+
}
|
|
47738
|
+
}
|
|
47739
|
+
async function withMovementLocks(home, senders, timeoutMs, operation) {
|
|
47740
|
+
const [sender, ...remaining] = senders;
|
|
47741
|
+
if (sender === void 0) return await operation();
|
|
47742
|
+
return await withMovementLock(
|
|
47743
|
+
home,
|
|
47744
|
+
sender,
|
|
47745
|
+
timeoutMs,
|
|
47746
|
+
async () => withMovementLocks(home, remaining, timeoutMs, operation)
|
|
47747
|
+
);
|
|
47748
|
+
}
|
|
47749
|
+
|
|
47750
|
+
// src/swarm.ts
|
|
47751
|
+
import { readdir as readdir4, readFile as readFile8, stat as stat5, unlink as unlink6 } from "node:fs/promises";
|
|
47752
|
+
import { join as join7 } from "node:path";
|
|
47753
|
+
var SWARM_NAME_PATTERN = /^[a-z][a-z0-9-]{0,15}$/;
|
|
47754
|
+
var SWARM_ROLE_PATTERN = /^[a-z][a-z0-9]{0,7}$/;
|
|
47755
|
+
var atomicSchema = external_exports.string().regex(/^\d+$/);
|
|
47756
|
+
var addressSchema2 = external_exports.string().regex(/^0x[0-9a-fA-F]{40}$/);
|
|
47757
|
+
var capsSchema = external_exports.strictObject({ perCallAtomic: atomicSchema, perDayAtomic: atomicSchema });
|
|
47758
|
+
var movementIdSchema2 = external_exports.string().regex(/^mv_[A-Za-z0-9_-]{8,128}$/u);
|
|
47759
|
+
var amountUsdSchema = external_exports.string().regex(/^\d+\.\d{2}$/u);
|
|
47760
|
+
var setupFundingStatusSchema = external_exports.enum(["planned", "sent", "incomplete"]);
|
|
47761
|
+
var blockedFundingLegSchema = external_exports.strictObject({
|
|
47762
|
+
to: external_exports.string().trim().min(1),
|
|
47763
|
+
amountUsd: amountUsdSchema,
|
|
47764
|
+
reason: external_exports.string().trim().min(1)
|
|
47765
|
+
});
|
|
47766
|
+
var setupFundingSchema = external_exports.strictObject({
|
|
47767
|
+
amountUsd: amountUsdSchema,
|
|
47768
|
+
from: walletNameSchema,
|
|
47769
|
+
movementId: movementIdSchema2.optional(),
|
|
47770
|
+
status: setupFundingStatusSchema,
|
|
47771
|
+
policy: external_exports.strictObject({
|
|
47772
|
+
strategy: external_exports.enum(["targets", "even", "weights"]),
|
|
47773
|
+
movementId: movementIdSchema2.optional(),
|
|
47774
|
+
status: setupFundingStatusSchema,
|
|
47775
|
+
blocked: external_exports.array(blockedFundingLegSchema).optional()
|
|
47776
|
+
}).optional()
|
|
47777
|
+
});
|
|
47778
|
+
var accountSteps = {
|
|
47779
|
+
creating: external_exports.boolean().default(false),
|
|
47780
|
+
created: external_exports.boolean(),
|
|
47781
|
+
capped: external_exports.boolean(),
|
|
47782
|
+
linked: external_exports.boolean()
|
|
47783
|
+
};
|
|
47784
|
+
var treasurySchema = external_exports.strictObject({
|
|
47785
|
+
account: walletNameSchema,
|
|
47786
|
+
// A planned account has no address until its vault derivation succeeds.
|
|
47787
|
+
address: addressSchema2.optional(),
|
|
47788
|
+
steps: external_exports.strictObject(accountSteps)
|
|
47789
|
+
}).refine(
|
|
47790
|
+
(value) => !value.steps.created || value.address !== void 0,
|
|
47791
|
+
"Created accounts require an address."
|
|
47792
|
+
);
|
|
47793
|
+
var memberSchema = external_exports.strictObject({
|
|
47794
|
+
account: walletNameSchema,
|
|
47795
|
+
address: addressSchema2.optional(),
|
|
47796
|
+
role: external_exports.string().regex(SWARM_ROLE_PATTERN),
|
|
47797
|
+
weight: external_exports.number().int().positive(),
|
|
47798
|
+
targetAtomic: atomicSchema,
|
|
47799
|
+
ceilingAtomic: atomicSchema.nullable(),
|
|
47800
|
+
steps: external_exports.strictObject({ ...accountSteps, profiled: external_exports.boolean() })
|
|
47801
|
+
}).refine(
|
|
47802
|
+
(value) => !value.steps.created || value.address !== void 0,
|
|
47803
|
+
"Created accounts require an address."
|
|
47804
|
+
);
|
|
47805
|
+
var swarmFileSchema = external_exports.strictObject({
|
|
47806
|
+
v: external_exports.literal(1),
|
|
47807
|
+
name: external_exports.string().regex(SWARM_NAME_PATTERN),
|
|
47808
|
+
device: external_exports.string().regex(DEVICE_NAME_PATTERN),
|
|
47809
|
+
network: external_exports.enum([BASE_MAINNET_CAIP2, ARC_MAINNET_CAIP2]),
|
|
47810
|
+
createdAt: external_exports.iso.datetime(),
|
|
47811
|
+
treasury: treasurySchema,
|
|
47812
|
+
members: external_exports.array(memberSchema),
|
|
47813
|
+
setupFunding: setupFundingSchema.optional(),
|
|
47814
|
+
policy: external_exports.strictObject({
|
|
47815
|
+
strategy: external_exports.enum(["targets", "even", "weights"]),
|
|
47816
|
+
treasuryCaps: capsSchema
|
|
47817
|
+
})
|
|
47818
|
+
}).refine(
|
|
47819
|
+
(value) => (/* @__PURE__ */ new Set([value.treasury.account, ...value.members.map((member) => member.account)])).size === value.members.length + 1,
|
|
47820
|
+
"Swarm accounts must be unique."
|
|
47821
|
+
);
|
|
47822
|
+
var SwarmError = class extends Error {
|
|
47823
|
+
constructor(code, message) {
|
|
47824
|
+
super(message);
|
|
47825
|
+
this.code = code;
|
|
47826
|
+
this.name = "SwarmError";
|
|
47827
|
+
}
|
|
47828
|
+
code;
|
|
47829
|
+
};
|
|
47830
|
+
function swarmsDirectory(home) {
|
|
47831
|
+
return join7(home, "swarms");
|
|
47832
|
+
}
|
|
47833
|
+
function swarmFilePath(home, name) {
|
|
47834
|
+
assertName(name);
|
|
47835
|
+
return join7(swarmsDirectory(home), `${name}.json`);
|
|
47836
|
+
}
|
|
47837
|
+
async function readSwarm(home, name) {
|
|
47838
|
+
const path = swarmFilePath(home, name);
|
|
47839
|
+
try {
|
|
47840
|
+
const swarm = parseSwarm(JSON.parse(await readFile8(path, "utf8")));
|
|
47841
|
+
if (swarm.name !== name)
|
|
47842
|
+
throw new SwarmError("invalid_swarm", "Swarm name does not match its file.");
|
|
47843
|
+
return swarm;
|
|
47844
|
+
} catch (error61) {
|
|
47845
|
+
if (isMissingFile(error61)) throw new SwarmError("swarm_not_found", `No swarm named ${name}.`);
|
|
47846
|
+
if (error61 instanceof SwarmError) throw error61;
|
|
47847
|
+
throw new SwarmError("invalid_swarm", `Invalid swarm file for ${name}.`);
|
|
47848
|
+
}
|
|
47849
|
+
}
|
|
47850
|
+
async function listSwarms(home) {
|
|
47851
|
+
let files;
|
|
47852
|
+
try {
|
|
47853
|
+
files = await readdir4(swarmsDirectory(home));
|
|
47854
|
+
} catch (error61) {
|
|
47855
|
+
if (isMissingFile(error61)) return [];
|
|
47856
|
+
throw error61;
|
|
47857
|
+
}
|
|
47858
|
+
return await Promise.all(
|
|
47859
|
+
files.filter((file2) => file2.endsWith(".json")).sort().map((file2) => readSwarm(home, file2.slice(0, -5)))
|
|
47860
|
+
);
|
|
47861
|
+
}
|
|
47862
|
+
function parseSwarm(value) {
|
|
47863
|
+
const result = swarmFileSchema.safeParse(value);
|
|
47864
|
+
if (!result.success) throw new SwarmError("invalid_swarm", "Invalid swarm state.");
|
|
47865
|
+
return result.data;
|
|
47866
|
+
}
|
|
47867
|
+
function assertName(name) {
|
|
47868
|
+
if (!SWARM_NAME_PATTERN.test(name))
|
|
47869
|
+
throw new SwarmError(
|
|
47870
|
+
"invalid_swarm",
|
|
47871
|
+
"A swarm name must be 1 to 16 lowercase letters, digits or dashes, starting with a letter."
|
|
47872
|
+
);
|
|
47873
|
+
}
|
|
47874
|
+
|
|
46491
47875
|
// src/backup.ts
|
|
46492
47876
|
var ERROR_MESSAGES = {
|
|
46493
47877
|
backup_too_large: "Backup envelope exceeds 65,536 bytes.",
|
|
@@ -46496,6 +47880,7 @@ var ERROR_MESSAGES = {
|
|
|
46496
47880
|
unsupported_signature: "Unsupported signature.",
|
|
46497
47881
|
weak_password: "Password must contain at least 12 characters.",
|
|
46498
47882
|
backup_invalid_input: "Invalid backup input.",
|
|
47883
|
+
backup_snapshot_busy: "Backup files changed while they were being captured. Retry the backup; no backup was created or uploaded.",
|
|
46499
47884
|
relay_open_failed: "Unable to open relay payload.",
|
|
46500
47885
|
relay_invalid_key: "Invalid relay key.",
|
|
46501
47886
|
vault_exists: "A vault already exists."
|
|
@@ -46517,6 +47902,15 @@ var SCRYPT_N2 = 131072;
|
|
|
46517
47902
|
var SCRYPT_R2 = 8;
|
|
46518
47903
|
var SCRYPT_P2 = 1;
|
|
46519
47904
|
var SCRYPT_MAXMEM = 256 * 1024 * 1024;
|
|
47905
|
+
var RESTORE_MARKER_FILE = ".backup-restore.json";
|
|
47906
|
+
var restoreDigestSchema = external_exports.string().regex(/^[0-9a-f]{64}$/u);
|
|
47907
|
+
var restoreMarkerSchema = external_exports.strictObject({
|
|
47908
|
+
v: external_exports.literal(1),
|
|
47909
|
+
fingerprint: restoreDigestSchema,
|
|
47910
|
+
vaultSha256: restoreDigestSchema,
|
|
47911
|
+
registrySha256: restoreDigestSchema,
|
|
47912
|
+
mac: restoreDigestSchema
|
|
47913
|
+
});
|
|
46520
47914
|
var SECP256K1_ORDER = BigInt(
|
|
46521
47915
|
"0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141"
|
|
46522
47916
|
);
|
|
@@ -46537,7 +47931,7 @@ var X25519_PRIVATE_DER_PREFIX = Buffer.from("302e020100300506032b656e04220420",
|
|
|
46537
47931
|
var X25519_PUBLIC_DER_PREFIX = Buffer.from("302a300506032b656e032100", "hex");
|
|
46538
47932
|
var CROCKFORD_BASE32 = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
|
|
46539
47933
|
var isoUtcMillisecondsSchema = external_exports.string().refine(isIsoUtcMilliseconds);
|
|
46540
|
-
var
|
|
47934
|
+
var addressSchema3 = external_exports.custom(
|
|
46541
47935
|
(value) => typeof value === "string" && ADDRESS_PATTERN2.test(value)
|
|
46542
47936
|
);
|
|
46543
47937
|
var privateKeySchema = external_exports.custom(
|
|
@@ -46546,7 +47940,7 @@ var privateKeySchema = external_exports.custom(
|
|
|
46546
47940
|
var safeNonnegativeIntegerSchema = external_exports.number().int().nonnegative().refine(Number.isSafeInteger);
|
|
46547
47941
|
var accountFields2 = {
|
|
46548
47942
|
name: walletNameSchema,
|
|
46549
|
-
address:
|
|
47943
|
+
address: addressSchema3,
|
|
46550
47944
|
createdAt: isoUtcMillisecondsSchema
|
|
46551
47945
|
};
|
|
46552
47946
|
var backupAccountSchema = external_exports.discriminatedUnion("kind", [
|
|
@@ -46570,26 +47964,191 @@ var backupRegistryAccountSchema = external_exports.object({
|
|
|
46570
47964
|
label: external_exports.string().optional(),
|
|
46571
47965
|
spendCaps: spendCapsSchema.strict()
|
|
46572
47966
|
}).strict();
|
|
46573
|
-
var
|
|
47967
|
+
var backupRegistryV1Schema = external_exports.object({
|
|
46574
47968
|
default: walletNameSchema.optional(),
|
|
46575
47969
|
accounts: external_exports.array(backupRegistryAccountSchema),
|
|
46576
47970
|
networks: external_exports.array(external_exports.string().regex(CAIP2_PATTERN))
|
|
46577
47971
|
}).strict();
|
|
46578
|
-
var
|
|
47972
|
+
var routerRefillSchema2 = external_exports.strictObject({
|
|
47973
|
+
belowUsd: external_exports.number().finite().min(0),
|
|
47974
|
+
tierUsd: external_exports.union([external_exports.literal(1), external_exports.literal(5), external_exports.literal(20), external_exports.literal(50)])
|
|
47975
|
+
});
|
|
47976
|
+
var backupRegistryAccountV2Schema = external_exports.strictObject({
|
|
47977
|
+
name: walletNameSchema,
|
|
47978
|
+
label: external_exports.string().optional(),
|
|
47979
|
+
spendCaps: spendCapsSchema.strict(),
|
|
47980
|
+
ceilingAtomic: external_exports.string().regex(/^\d+$/u).nullable().optional(),
|
|
47981
|
+
routerRefill: routerRefillSchema2.optional()
|
|
47982
|
+
});
|
|
47983
|
+
var backupRegistryV2Schema = external_exports.strictObject({
|
|
47984
|
+
default: walletNameSchema.optional(),
|
|
47985
|
+
accounts: external_exports.array(backupRegistryAccountV2Schema),
|
|
47986
|
+
networks: external_exports.array(external_exports.string().regex(CAIP2_PATTERN))
|
|
47987
|
+
});
|
|
47988
|
+
var backupAgentProfileSchema = external_exports.strictObject({
|
|
47989
|
+
version: external_exports.literal(1),
|
|
47990
|
+
name: walletNameSchema,
|
|
47991
|
+
wallet: external_exports.string(),
|
|
47992
|
+
model: external_exports.string().min(1),
|
|
47993
|
+
instructions: external_exports.string().max(2e4),
|
|
47994
|
+
verifiedOnly: external_exports.boolean(),
|
|
47995
|
+
approveAboveUsd: external_exports.number().min(0),
|
|
47996
|
+
maxPerTaskUsd: external_exports.number().min(0).optional(),
|
|
47997
|
+
autoReleaseBelowUsd: external_exports.number().min(0).optional(),
|
|
47998
|
+
maxSteps: external_exports.number().int().min(1).max(50),
|
|
47999
|
+
paused: external_exports.boolean(),
|
|
48000
|
+
createdAt: external_exports.string(),
|
|
48001
|
+
tools: external_exports.array(external_exports.string().min(1)),
|
|
48002
|
+
grants: external_exports.array(external_exports.enum(["read", "delegate", "allocate"])).min(1).optional()
|
|
48003
|
+
});
|
|
48004
|
+
var movementIdSchema3 = external_exports.string().regex(/^mv_[A-Za-z0-9_-]{8,128}$/u);
|
|
48005
|
+
var movementNonceSchema = external_exports.string().regex(/^0x[0-9a-fA-F]{64}$/u);
|
|
48006
|
+
var movementAmountSchema = external_exports.string().regex(/^\d+\.\d{2}$/u);
|
|
48007
|
+
var movementStatusSchema = external_exports.enum(["planned", "sent", "failed", "unknown"]);
|
|
48008
|
+
var movementV2StatusSchema = external_exports.enum(["planned", "sent", "failed", "unknown", "cancelled"]);
|
|
48009
|
+
var movementOptionalFields = {
|
|
48010
|
+
txHash: external_exports.string().regex(/^0x[0-9a-fA-F]+$/u).optional(),
|
|
48011
|
+
reason: external_exports.string().min(1).optional(),
|
|
48012
|
+
retryable: external_exports.boolean().optional()
|
|
48013
|
+
};
|
|
48014
|
+
var backupMovementV1Schema = external_exports.strictObject({
|
|
48015
|
+
v: external_exports.literal(1),
|
|
48016
|
+
id: movementIdSchema3,
|
|
48017
|
+
reason: external_exports.literal("distribute"),
|
|
48018
|
+
from: walletNameSchema,
|
|
48019
|
+
network: external_exports.enum([BASE_MAINNET_CAIP2, ARC_MAINNET_CAIP2]),
|
|
48020
|
+
createdAt: external_exports.iso.datetime(),
|
|
48021
|
+
legs: external_exports.array(
|
|
48022
|
+
external_exports.strictObject({
|
|
48023
|
+
to: external_exports.string().trim().min(1),
|
|
48024
|
+
amountUsd: movementAmountSchema,
|
|
48025
|
+
nonce: movementNonceSchema,
|
|
48026
|
+
status: movementStatusSchema,
|
|
48027
|
+
...movementOptionalFields
|
|
48028
|
+
})
|
|
48029
|
+
).min(1)
|
|
48030
|
+
});
|
|
48031
|
+
var backupMovementV2Schema = external_exports.strictObject({
|
|
48032
|
+
v: external_exports.literal(2),
|
|
48033
|
+
id: movementIdSchema3,
|
|
48034
|
+
reason: external_exports.enum(["distribute", "allocate", "rebalance", "delegate", "sweep", "send"]),
|
|
48035
|
+
from: walletNameSchema,
|
|
48036
|
+
treasury: walletNameSchema.optional(),
|
|
48037
|
+
network: external_exports.enum([BASE_MAINNET_CAIP2, ARC_MAINNET_CAIP2]),
|
|
48038
|
+
createdAt: external_exports.iso.datetime(),
|
|
48039
|
+
legs: external_exports.array(
|
|
48040
|
+
external_exports.strictObject({
|
|
48041
|
+
from: walletNameSchema,
|
|
48042
|
+
to: external_exports.string().trim().min(1),
|
|
48043
|
+
amountUsd: movementAmountSchema,
|
|
48044
|
+
purpose: external_exports.enum(["send", "sweep"]),
|
|
48045
|
+
nonce: movementNonceSchema,
|
|
48046
|
+
status: movementV2StatusSchema,
|
|
48047
|
+
...movementOptionalFields
|
|
48048
|
+
})
|
|
48049
|
+
).min(1)
|
|
48050
|
+
});
|
|
48051
|
+
var backupMovementSchema = external_exports.discriminatedUnion("v", [
|
|
48052
|
+
backupMovementV1Schema,
|
|
48053
|
+
backupMovementV2Schema
|
|
48054
|
+
]);
|
|
48055
|
+
var backupTransferRequestSchema = external_exports.strictObject({
|
|
48056
|
+
authorization: external_exports.strictObject({
|
|
48057
|
+
from: addressSchema3,
|
|
48058
|
+
to: addressSchema3,
|
|
48059
|
+
value: external_exports.string().regex(/^\d+$/u),
|
|
48060
|
+
validAfter: external_exports.string().regex(/^\d+$/u),
|
|
48061
|
+
validBefore: external_exports.string().regex(/^\d+$/u),
|
|
48062
|
+
nonce: movementNonceSchema
|
|
48063
|
+
}),
|
|
48064
|
+
signature: external_exports.string().regex(/^0x[0-9a-fA-F]+$/u)
|
|
48065
|
+
});
|
|
48066
|
+
var backupTransferSchema = external_exports.strictObject({
|
|
48067
|
+
to: addressSchema3,
|
|
48068
|
+
toName: external_exports.string().min(1),
|
|
48069
|
+
toKind: external_exports.enum(["owner", "account"]),
|
|
48070
|
+
amountAtomic: external_exports.string().regex(/^\d+$/u),
|
|
48071
|
+
network: external_exports.string().min(1),
|
|
48072
|
+
nonce: movementNonceSchema,
|
|
48073
|
+
status: external_exports.enum(["sent", "unknown", "failed"]),
|
|
48074
|
+
txHash: external_exports.string().regex(/^0x[0-9a-fA-F]+$/u).nullable(),
|
|
48075
|
+
replayed: external_exports.boolean(),
|
|
48076
|
+
reservedOn: external_exports.string().regex(/^\d{4}-\d{2}-\d{2}$/u).optional(),
|
|
48077
|
+
request: backupTransferRequestSchema.optional()
|
|
48078
|
+
});
|
|
48079
|
+
var backupTransferReceiptSchema = external_exports.strictObject({
|
|
48080
|
+
id: external_exports.string().min(1),
|
|
48081
|
+
timestamp: external_exports.iso.datetime(),
|
|
48082
|
+
kind: external_exports.literal("transfer"),
|
|
48083
|
+
wallet: walletNameSchema,
|
|
48084
|
+
resourceUrl: external_exports.url(),
|
|
48085
|
+
quote: external_exports.strictObject({
|
|
48086
|
+
network: external_exports.string().min(1),
|
|
48087
|
+
asset: external_exports.string().min(1),
|
|
48088
|
+
amountAtomic: external_exports.string().regex(/^\d+$/u)
|
|
48089
|
+
}),
|
|
48090
|
+
transfer: backupTransferSchema,
|
|
48091
|
+
error: external_exports.strictObject({ code: external_exports.string(), message: external_exports.string() }).optional()
|
|
48092
|
+
}).superRefine((receipt, context) => {
|
|
48093
|
+
try {
|
|
48094
|
+
parseReceipt(receipt);
|
|
48095
|
+
} catch {
|
|
48096
|
+
context.addIssue({ code: "custom", message: "Invalid transfer receipt." });
|
|
48097
|
+
}
|
|
48098
|
+
});
|
|
48099
|
+
var backupSourceV1Schema = external_exports.object({
|
|
46579
48100
|
phrase: external_exports.string().min(1),
|
|
46580
48101
|
nextDerivedIndex: safeNonnegativeIntegerSchema,
|
|
46581
48102
|
accounts: external_exports.array(backupAccountSchema),
|
|
46582
|
-
registry:
|
|
48103
|
+
registry: backupRegistryV1Schema,
|
|
46583
48104
|
protected: external_exports.boolean()
|
|
46584
48105
|
}).strict().superRefine(validateBackupSourceRelationships);
|
|
46585
|
-
var
|
|
48106
|
+
var backupSourceV2Schema = external_exports.strictObject({
|
|
48107
|
+
phrase: external_exports.string().min(1),
|
|
48108
|
+
nextDerivedIndex: safeNonnegativeIntegerSchema,
|
|
48109
|
+
accounts: external_exports.array(backupAccountSchema),
|
|
48110
|
+
registry: backupRegistryV2Schema,
|
|
48111
|
+
protected: external_exports.boolean(),
|
|
48112
|
+
agents: external_exports.array(backupAgentProfileSchema),
|
|
48113
|
+
swarms: external_exports.array(swarmFileSchema),
|
|
48114
|
+
movements: external_exports.array(backupMovementSchema),
|
|
48115
|
+
transferReceipts: external_exports.array(backupTransferReceiptSchema),
|
|
48116
|
+
skipped: external_exports.array(
|
|
48117
|
+
external_exports.strictObject({
|
|
48118
|
+
kind: external_exports.literal("agent"),
|
|
48119
|
+
name: external_exports.string().min(1),
|
|
48120
|
+
reason: external_exports.literal("invalid_profile")
|
|
48121
|
+
})
|
|
48122
|
+
).optional()
|
|
48123
|
+
}).superRefine((source, context) => {
|
|
48124
|
+
validateBackupSourceRelationships(source, context);
|
|
48125
|
+
validateBackupV2Uniqueness(source, context);
|
|
48126
|
+
});
|
|
48127
|
+
var backupSourceSchema = external_exports.union([backupSourceV1Schema, backupSourceV2Schema]);
|
|
48128
|
+
var backupPlaintextV1Schema = external_exports.strictObject({
|
|
46586
48129
|
v: external_exports.literal(1),
|
|
46587
48130
|
phrase: external_exports.string().min(1),
|
|
46588
48131
|
nextDerivedIndex: safeNonnegativeIntegerSchema,
|
|
46589
48132
|
accounts: external_exports.array(backupAccountSchema),
|
|
46590
|
-
registry:
|
|
48133
|
+
registry: backupRegistryV1Schema,
|
|
46591
48134
|
protected: external_exports.boolean()
|
|
46592
|
-
})
|
|
48135
|
+
});
|
|
48136
|
+
var backupPlaintextV2Schema = external_exports.strictObject({
|
|
48137
|
+
v: external_exports.literal(2),
|
|
48138
|
+
phrase: external_exports.string().min(1),
|
|
48139
|
+
nextDerivedIndex: safeNonnegativeIntegerSchema,
|
|
48140
|
+
accounts: external_exports.array(backupAccountSchema),
|
|
48141
|
+
registry: backupRegistryV2Schema,
|
|
48142
|
+
protected: external_exports.boolean(),
|
|
48143
|
+
agents: external_exports.array(backupAgentProfileSchema).optional(),
|
|
48144
|
+
swarms: external_exports.array(swarmFileSchema).optional(),
|
|
48145
|
+
movements: external_exports.array(backupMovementSchema).optional(),
|
|
48146
|
+
transferReceipts: external_exports.array(backupTransferReceiptSchema).optional()
|
|
48147
|
+
});
|
|
48148
|
+
var backupPlaintextSchema = external_exports.discriminatedUnion("v", [backupPlaintextV1Schema, backupPlaintextV2Schema]).superRefine((plaintext, context) => {
|
|
48149
|
+
validateBackupSourceRelationships(plaintext, context);
|
|
48150
|
+
if (plaintext.v === 2) validateBackupV2Uniqueness(plaintext, context);
|
|
48151
|
+
});
|
|
46593
48152
|
function VAULT_BACKUP_TYPED_DATA(owner) {
|
|
46594
48153
|
const checksummedOwner = checksummedAddress(owner);
|
|
46595
48154
|
return {
|
|
@@ -46711,8 +48270,8 @@ async function createBackup(options) {
|
|
|
46711
48270
|
assertDevice(options.device);
|
|
46712
48271
|
assertOwnerKey(options.ownerKey);
|
|
46713
48272
|
const createdAt = readCreatedAt(options.now ?? (() => /* @__PURE__ */ new Date()));
|
|
46714
|
-
const
|
|
46715
|
-
const nonce = readRandomBytes(
|
|
48273
|
+
const randomBytes7 = options.randomBytes ?? cryptoRandomBytes;
|
|
48274
|
+
const nonce = readRandomBytes(randomBytes7, 12);
|
|
46716
48275
|
const salt = encodeBase64Url(options.ownerKey.salt);
|
|
46717
48276
|
const kdf = options.ownerKey.kdf === "hkdf-sha256" ? { name: "hkdf-sha256", salt } : { name: "scrypt", salt, N: SCRYPT_N2, r: SCRYPT_R2, p: SCRYPT_P2 };
|
|
46718
48277
|
const cipher = { name: CIPHER_NAME, nonce: encodeBase64Url(nonce) };
|
|
@@ -46725,30 +48284,54 @@ async function createBackup(options) {
|
|
|
46725
48284
|
kdf,
|
|
46726
48285
|
cipher
|
|
46727
48286
|
};
|
|
46728
|
-
const plaintextValue = buildPlaintext(parsed.data);
|
|
46729
|
-
const plaintext = Buffer.from(JSON.stringify(plaintextValue), "utf8");
|
|
46730
48287
|
const key = Buffer.from(options.ownerKey.key);
|
|
46731
|
-
let encrypted;
|
|
46732
48288
|
try {
|
|
46733
|
-
|
|
46734
|
-
const
|
|
46735
|
-
|
|
46736
|
-
|
|
46737
|
-
|
|
46738
|
-
|
|
46739
|
-
|
|
46740
|
-
|
|
46741
|
-
|
|
46742
|
-
|
|
46743
|
-
|
|
46744
|
-
|
|
46745
|
-
|
|
46746
|
-
|
|
48289
|
+
const candidates = backupCandidates(parsed.data);
|
|
48290
|
+
for (const candidate of candidates) {
|
|
48291
|
+
const plaintext = Buffer.from(JSON.stringify(candidate.plaintext), "utf8");
|
|
48292
|
+
let encrypted;
|
|
48293
|
+
try {
|
|
48294
|
+
encrypted = encryptAesGcm(
|
|
48295
|
+
key,
|
|
48296
|
+
nonce,
|
|
48297
|
+
plaintext,
|
|
48298
|
+
Buffer.from(buildBackupAad(header), "utf8")
|
|
48299
|
+
);
|
|
48300
|
+
const envelope = JSON.stringify({
|
|
48301
|
+
format: header.format,
|
|
48302
|
+
v: header.v,
|
|
48303
|
+
owner: header.owner,
|
|
48304
|
+
device: header.device,
|
|
48305
|
+
createdAt: header.createdAt,
|
|
48306
|
+
kdf: header.kdf,
|
|
48307
|
+
cipher: header.cipher,
|
|
48308
|
+
ciphertext: encodeBase64Url(encrypted)
|
|
48309
|
+
});
|
|
48310
|
+
const bytes = Buffer.byteLength(envelope, "utf8");
|
|
48311
|
+
const relayPayload = buildRelayRestorePayload({
|
|
48312
|
+
owner,
|
|
48313
|
+
device: "d".repeat(32),
|
|
48314
|
+
kdf,
|
|
48315
|
+
key: Buffer.alloc(32),
|
|
48316
|
+
vault: candidate.plaintext
|
|
48317
|
+
});
|
|
48318
|
+
const relayBytes = Buffer.byteLength(relayPayload, "utf8");
|
|
48319
|
+
if (bytes <= MAX_BACKUP_BYTES && relayBytes <= RELAY_PLAINTEXT_LIMIT) {
|
|
48320
|
+
return {
|
|
48321
|
+
envelope,
|
|
48322
|
+
bytes,
|
|
48323
|
+
...candidate.omitted.length === 0 ? {} : { omitted: candidate.omitted }
|
|
48324
|
+
};
|
|
48325
|
+
}
|
|
48326
|
+
} finally {
|
|
48327
|
+
plaintext.fill(0);
|
|
48328
|
+
encrypted?.fill(0);
|
|
48329
|
+
}
|
|
48330
|
+
}
|
|
48331
|
+
throw new BackupError("backup_too_large");
|
|
46747
48332
|
} finally {
|
|
46748
|
-
plaintext.fill(0);
|
|
46749
48333
|
key.fill(0);
|
|
46750
48334
|
nonce.fill(0);
|
|
46751
|
-
encrypted?.fill(0);
|
|
46752
48335
|
}
|
|
46753
48336
|
}
|
|
46754
48337
|
async function openBackup(options) {
|
|
@@ -46773,7 +48356,7 @@ async function openBackup(options) {
|
|
|
46773
48356
|
const value = JSON.parse(json2);
|
|
46774
48357
|
const validated = backupPlaintextSchema.safeParse(value);
|
|
46775
48358
|
if (!validated.success) throw new BackupError("backup_open_failed");
|
|
46776
|
-
return
|
|
48359
|
+
return validated.data.v === 1 ? buildPlaintextV1(validated.data) : buildPlaintextV2(validated.data);
|
|
46777
48360
|
} catch {
|
|
46778
48361
|
throw new BackupError("backup_open_failed");
|
|
46779
48362
|
} finally {
|
|
@@ -46831,7 +48414,7 @@ function relayCode(publicKey) {
|
|
|
46831
48414
|
}
|
|
46832
48415
|
function sealTo(options) {
|
|
46833
48416
|
const plaintext = relayPlaintext(options.plaintext);
|
|
46834
|
-
const
|
|
48417
|
+
const randomBytes7 = options.randomBytes ?? cryptoRandomBytes;
|
|
46835
48418
|
let recipientPublicKey;
|
|
46836
48419
|
let ephemeralPrivateKey;
|
|
46837
48420
|
let ephemeralPublicKey;
|
|
@@ -46845,7 +48428,7 @@ function sealTo(options) {
|
|
|
46845
48428
|
}
|
|
46846
48429
|
assertRelayKey(options.recipientPublicKey);
|
|
46847
48430
|
recipientPublicKey = Buffer.from(options.recipientPublicKey);
|
|
46848
|
-
ephemeralPrivateKey = readRelayRandomBytes(
|
|
48431
|
+
ephemeralPrivateKey = readRelayRandomBytes(randomBytes7, 32);
|
|
46849
48432
|
try {
|
|
46850
48433
|
ephemeralPublicKey = x25519PublicFromPrivate(ephemeralPrivateKey);
|
|
46851
48434
|
shared = x25519SharedSecret(ephemeralPrivateKey, recipientPublicKey);
|
|
@@ -46853,7 +48436,7 @@ function sealTo(options) {
|
|
|
46853
48436
|
} catch {
|
|
46854
48437
|
throw new BackupError("relay_invalid_key");
|
|
46855
48438
|
}
|
|
46856
|
-
nonce = readRelayRandomBytes(
|
|
48439
|
+
nonce = readRelayRandomBytes(randomBytes7, 12);
|
|
46857
48440
|
const encodedPublicKey = encodeBase64Url(ephemeralPublicKey);
|
|
46858
48441
|
const encodedNonce = encodeBase64Url(nonce);
|
|
46859
48442
|
const salt = Buffer.concat([ephemeralPublicKey, recipientPublicKey]);
|
|
@@ -46936,6 +48519,30 @@ function openSealed(options) {
|
|
|
46936
48519
|
plaintext?.fill(0);
|
|
46937
48520
|
}
|
|
46938
48521
|
}
|
|
48522
|
+
function buildRelayRestorePayload(options) {
|
|
48523
|
+
try {
|
|
48524
|
+
const owner = normalizedOwner(options.owner);
|
|
48525
|
+
assertDevice(options.device);
|
|
48526
|
+
assertBytes(options.key, 32);
|
|
48527
|
+
if (!isRecord(options.kdf)) throw new Error();
|
|
48528
|
+
const kdf = parseKdf(options.kdf);
|
|
48529
|
+
const parsed = backupPlaintextSchema.safeParse(options.vault);
|
|
48530
|
+
if (!parsed.success) throw new Error();
|
|
48531
|
+
const vault = parsed.data.v === 1 ? buildPlaintextV1(parsed.data) : buildPlaintextV2(parsed.data);
|
|
48532
|
+
return JSON.stringify({
|
|
48533
|
+
format: "vapi-vault-relay-payload",
|
|
48534
|
+
v: 1,
|
|
48535
|
+
purpose: "restore",
|
|
48536
|
+
owner,
|
|
48537
|
+
device: options.device,
|
|
48538
|
+
kdf,
|
|
48539
|
+
key: encodeBase64Url(options.key),
|
|
48540
|
+
vault
|
|
48541
|
+
});
|
|
48542
|
+
} catch {
|
|
48543
|
+
throw new BackupError("backup_invalid_input");
|
|
48544
|
+
}
|
|
48545
|
+
}
|
|
46939
48546
|
async function exportBackupSource(options) {
|
|
46940
48547
|
try {
|
|
46941
48548
|
assertBytes(options.vaultKey, 32);
|
|
@@ -46943,62 +48550,142 @@ async function exportBackupSource(options) {
|
|
|
46943
48550
|
throw new BackupError("backup_invalid_input");
|
|
46944
48551
|
}
|
|
46945
48552
|
const networks = normalizeNetworks(options.networks ?? []);
|
|
46946
|
-
|
|
46947
|
-
|
|
46948
|
-
|
|
46949
|
-
|
|
46950
|
-
|
|
46951
|
-
readVaultFileUnlocked(path)
|
|
46952
|
-
]);
|
|
46953
|
-
const accounts2 = [];
|
|
46954
|
-
for (const account of vault.accounts()) {
|
|
46955
|
-
if (account.kind === "derived") {
|
|
46956
|
-
accounts2.push({ ...account });
|
|
46957
|
-
} else {
|
|
46958
|
-
const privateKey = await exportVaultAccountKey({
|
|
46959
|
-
path,
|
|
46960
|
-
key: options.vaultKey,
|
|
46961
|
-
name: account.name
|
|
46962
|
-
});
|
|
46963
|
-
accounts2.push({ ...account, privateKey });
|
|
46964
|
-
}
|
|
48553
|
+
for (let attempt = 0; attempt < 3; attempt += 1) {
|
|
48554
|
+
try {
|
|
48555
|
+
return await exportBackupSourceAttempt(options, networks);
|
|
48556
|
+
} catch (error61) {
|
|
48557
|
+
if (!(error61 instanceof BackupSnapshotChangedError)) throw error61;
|
|
46965
48558
|
}
|
|
46966
|
-
return { phrase: phrase2, file: file3, accounts: accounts2 };
|
|
46967
|
-
});
|
|
46968
|
-
const snapshot = await options.store.reload();
|
|
46969
|
-
const names = new Set(accounts.map((account) => account.name));
|
|
46970
|
-
const source = {
|
|
46971
|
-
phrase,
|
|
46972
|
-
nextDerivedIndex: file2.nextDerivedIndex ?? accounts.reduce(
|
|
46973
|
-
(next, account) => account.kind === "derived" ? Math.max(next, account.index + 1) : next,
|
|
46974
|
-
0
|
|
46975
|
-
),
|
|
46976
|
-
accounts,
|
|
46977
|
-
registry: {
|
|
46978
|
-
...snapshot.default !== void 0 && names.has(snapshot.default) ? { default: snapshot.default } : {},
|
|
46979
|
-
accounts: accounts.map((account) => {
|
|
46980
|
-
const entry = snapshot.wallets[account.name];
|
|
46981
|
-
return {
|
|
46982
|
-
name: account.name,
|
|
46983
|
-
...entry?.label === void 0 ? {} : { label: entry.label },
|
|
46984
|
-
spendCaps: {
|
|
46985
|
-
perCallAtomic: entry?.spendCaps.perCallAtomic ?? DEFAULT_SPEND_CAPS.perCallAtomic,
|
|
46986
|
-
perDayAtomic: entry?.spendCaps.perDayAtomic ?? DEFAULT_SPEND_CAPS.perDayAtomic
|
|
46987
|
-
}
|
|
46988
|
-
};
|
|
46989
|
-
}),
|
|
46990
|
-
networks
|
|
46991
|
-
},
|
|
46992
|
-
protected: file2.protected
|
|
46993
|
-
};
|
|
46994
|
-
if (!backupSourceSchema.safeParse(source).success) {
|
|
46995
|
-
throw new BackupError("backup_invalid_input");
|
|
46996
48559
|
}
|
|
46997
|
-
|
|
46998
|
-
} catch {
|
|
48560
|
+
throw new BackupError("backup_snapshot_busy");
|
|
48561
|
+
} catch (error61) {
|
|
48562
|
+
if (error61 instanceof BackupError && error61.code === "backup_snapshot_busy") throw error61;
|
|
46999
48563
|
throw new BackupError("backup_invalid_input");
|
|
47000
48564
|
}
|
|
47001
48565
|
}
|
|
48566
|
+
var BackupSnapshotChangedError = class extends Error {
|
|
48567
|
+
};
|
|
48568
|
+
async function exportBackupSourceAttempt(options, networks) {
|
|
48569
|
+
const path = join8(options.store.home, "vault.json");
|
|
48570
|
+
const { phrase, file: file2, accounts } = await withVaultLock(path, async () => {
|
|
48571
|
+
const [phrase2, vault, file3] = await Promise.all([
|
|
48572
|
+
exportVaultPhrase({ path, key: options.vaultKey }),
|
|
48573
|
+
openVault({ path, key: options.vaultKey }),
|
|
48574
|
+
readVaultFileUnlocked(path)
|
|
48575
|
+
]);
|
|
48576
|
+
const accounts2 = [];
|
|
48577
|
+
for (const account of vault.accounts()) {
|
|
48578
|
+
if (account.kind === "derived") {
|
|
48579
|
+
accounts2.push({ ...account });
|
|
48580
|
+
} else {
|
|
48581
|
+
const privateKey = await exportVaultAccountKey({
|
|
48582
|
+
path,
|
|
48583
|
+
key: options.vaultKey,
|
|
48584
|
+
name: account.name
|
|
48585
|
+
});
|
|
48586
|
+
accounts2.push({ ...account, privateKey });
|
|
48587
|
+
}
|
|
48588
|
+
}
|
|
48589
|
+
return { phrase: phrase2, file: file3, accounts: accounts2 };
|
|
48590
|
+
});
|
|
48591
|
+
const snapshot = await options.store.reload();
|
|
48592
|
+
const names = new Set(accounts.map((account) => account.name));
|
|
48593
|
+
const home = options.store.home;
|
|
48594
|
+
const skipped = [];
|
|
48595
|
+
const [agents, swarms, movementSnapshots] = await Promise.all([
|
|
48596
|
+
listAgentProfiles(home, {
|
|
48597
|
+
schema: options.agentProfileSchema ?? agentProfileSchema,
|
|
48598
|
+
warn: () => void 0,
|
|
48599
|
+
onInvalid: (profileFile) => {
|
|
48600
|
+
skipped.push({
|
|
48601
|
+
kind: "agent",
|
|
48602
|
+
name: profileFile.endsWith(".json") ? profileFile.slice(0, -5) : profileFile,
|
|
48603
|
+
reason: "invalid_profile"
|
|
48604
|
+
});
|
|
48605
|
+
}
|
|
48606
|
+
}),
|
|
48607
|
+
listSwarms(home),
|
|
48608
|
+
listUnfinishedMovementSnapshots({ home })
|
|
48609
|
+
]);
|
|
48610
|
+
const localMovements = await Promise.all(
|
|
48611
|
+
movementSnapshots.map(async ({ summary }) => await readMovement(home, summary.id))
|
|
48612
|
+
);
|
|
48613
|
+
localMovements.sort(compareMovements);
|
|
48614
|
+
const expectedSignedFailedLegs = movementSnapshots.flatMap(
|
|
48615
|
+
({ signedFailedLegKeys }) => signedFailedLegKeys
|
|
48616
|
+
);
|
|
48617
|
+
const receipts = await readReceiptJournal(getVapiPaths(home).receipts);
|
|
48618
|
+
const receiptLegs = new Set(
|
|
48619
|
+
receipts.flatMap(
|
|
48620
|
+
(receipt) => receipt.kind === "transfer" && receipt.transfer !== void 0 ? [movementTransferKey(receiptWallet(receipt), receipt.transfer.nonce)] : []
|
|
48621
|
+
)
|
|
48622
|
+
);
|
|
48623
|
+
const requiredReceiptLegs = /* @__PURE__ */ new Set([
|
|
48624
|
+
...expectedSignedFailedLegs,
|
|
48625
|
+
...localMovements.flatMap(
|
|
48626
|
+
(movement) => movement.legs.filter((leg) => leg.status === "unknown").map((leg) => movementTransferKey(leg.from, leg.nonce))
|
|
48627
|
+
)
|
|
48628
|
+
]);
|
|
48629
|
+
const verifiedFile = await readVaultFileUnlocked(path);
|
|
48630
|
+
const verifiedSnapshot = await options.store.reload();
|
|
48631
|
+
if ([...requiredReceiptLegs].some((leg) => !receiptLegs.has(leg)) || JSON.stringify(file2) !== JSON.stringify(verifiedFile) || JSON.stringify(snapshot) !== JSON.stringify(verifiedSnapshot)) {
|
|
48632
|
+
throw new BackupSnapshotChangedError();
|
|
48633
|
+
}
|
|
48634
|
+
const movements = localMovements.map(toBackupMovement);
|
|
48635
|
+
const includedLegs = new Set(
|
|
48636
|
+
localMovements.flatMap(
|
|
48637
|
+
(movement) => movement.legs.map((leg) => movementTransferKey(leg.from, leg.nonce))
|
|
48638
|
+
)
|
|
48639
|
+
);
|
|
48640
|
+
const transferReceipts = receipts.filter(
|
|
48641
|
+
(receipt) => receipt.kind === "transfer" && receipt.transfer !== void 0 && includedLegs.has(movementTransferKey(receiptWallet(receipt), receipt.transfer.nonce))
|
|
48642
|
+
).map(toBackupTransferReceipt);
|
|
48643
|
+
const source = {
|
|
48644
|
+
phrase,
|
|
48645
|
+
nextDerivedIndex: file2.nextDerivedIndex ?? accounts.reduce(
|
|
48646
|
+
(next, account) => account.kind === "derived" ? Math.max(next, account.index + 1) : next,
|
|
48647
|
+
0
|
|
48648
|
+
),
|
|
48649
|
+
accounts,
|
|
48650
|
+
registry: {
|
|
48651
|
+
...snapshot.default !== void 0 && names.has(snapshot.default) ? { default: snapshot.default } : {},
|
|
48652
|
+
accounts: accounts.map((account) => {
|
|
48653
|
+
const entry = snapshot.wallets[account.name];
|
|
48654
|
+
const ceilingAtomic = entry?.ceilingAtomic === null ? null : entry?.ceilingAtomic ?? DEFAULT_CEILING_ATOMIC.toString();
|
|
48655
|
+
return {
|
|
48656
|
+
name: account.name,
|
|
48657
|
+
...entry?.label === void 0 ? {} : { label: entry.label },
|
|
48658
|
+
spendCaps: {
|
|
48659
|
+
perCallAtomic: entry?.spendCaps.perCallAtomic ?? DEFAULT_SPEND_CAPS.perCallAtomic,
|
|
48660
|
+
perDayAtomic: entry?.spendCaps.perDayAtomic ?? DEFAULT_SPEND_CAPS.perDayAtomic
|
|
48661
|
+
},
|
|
48662
|
+
...ceilingAtomic === DEFAULT_CEILING_ATOMIC.toString() ? {} : { ceilingAtomic },
|
|
48663
|
+
...entry?.routerRefill === void 0 ? {} : {
|
|
48664
|
+
routerRefill: {
|
|
48665
|
+
belowUsd: entry.routerRefill.belowUsd,
|
|
48666
|
+
tierUsd: entry.routerRefill.tierUsd
|
|
48667
|
+
}
|
|
48668
|
+
}
|
|
48669
|
+
};
|
|
48670
|
+
}),
|
|
48671
|
+
networks
|
|
48672
|
+
},
|
|
48673
|
+
protected: file2.protected,
|
|
48674
|
+
agents: agents.map(
|
|
48675
|
+
({ grants, ...profile }) => backupAgentProfileSchema.parse(
|
|
48676
|
+
grants === void 0 || grants.length === 0 ? profile : { ...profile, grants }
|
|
48677
|
+
)
|
|
48678
|
+
).sort(compareNamedValues),
|
|
48679
|
+
swarms: [...swarms].sort(compareNamedValues),
|
|
48680
|
+
movements,
|
|
48681
|
+
transferReceipts,
|
|
48682
|
+
...skipped.length === 0 ? {} : { skipped: skipped.sort(compareNamedValues) }
|
|
48683
|
+
};
|
|
48684
|
+
if (!backupSourceSchema.safeParse(source).success) {
|
|
48685
|
+
throw new BackupError("backup_invalid_input");
|
|
48686
|
+
}
|
|
48687
|
+
return source;
|
|
48688
|
+
}
|
|
47002
48689
|
async function restoreFromBackup(options) {
|
|
47003
48690
|
return await restoreBackupPlaintext(options);
|
|
47004
48691
|
}
|
|
@@ -47010,20 +48697,24 @@ async function restoreBackupPlaintext(options) {
|
|
|
47010
48697
|
if (typeof options.home !== "string" || options.home.length === 0) {
|
|
47011
48698
|
throw new BackupError("backup_invalid_input");
|
|
47012
48699
|
}
|
|
48700
|
+
if (options.device !== void 0 && !DEVICE_NAME_PATTERN.test(options.device)) {
|
|
48701
|
+
throw new BackupError("backup_invalid_input");
|
|
48702
|
+
}
|
|
47013
48703
|
try {
|
|
47014
48704
|
assertBytes(options.vaultKey, 32);
|
|
47015
48705
|
} catch {
|
|
47016
48706
|
throw new BackupError("backup_invalid_input");
|
|
47017
48707
|
}
|
|
47018
|
-
const vaultPath =
|
|
47019
|
-
const registryPath =
|
|
48708
|
+
const vaultPath = join8(options.home, "vault.json");
|
|
48709
|
+
const registryPath = join8(options.home, "wallets.json");
|
|
47020
48710
|
let stagingHome;
|
|
47021
48711
|
let beforePublishError;
|
|
48712
|
+
let afterPublishError;
|
|
47022
48713
|
try {
|
|
47023
|
-
await
|
|
47024
|
-
stagingHome = await mkdtemp(
|
|
47025
|
-
const stagedVaultPath =
|
|
47026
|
-
const stagedRegistryPath =
|
|
48714
|
+
await mkdir7(options.home, { recursive: true, mode: 448 });
|
|
48715
|
+
stagingHome = await mkdtemp(join8(options.home, ".backup-restore-"));
|
|
48716
|
+
const stagedVaultPath = join8(stagingHome, "vault.json");
|
|
48717
|
+
const stagedRegistryPath = join8(stagingHome, "wallets.json");
|
|
47027
48718
|
await createVault({
|
|
47028
48719
|
path: stagedVaultPath,
|
|
47029
48720
|
key: options.vaultKey,
|
|
@@ -47069,16 +48760,59 @@ async function restoreBackupPlaintext(options) {
|
|
|
47069
48760
|
} finally {
|
|
47070
48761
|
await handle.close();
|
|
47071
48762
|
}
|
|
48763
|
+
const stagedV2 = plaintext.v === 2 ? await stageV2Restore({
|
|
48764
|
+
plaintext,
|
|
48765
|
+
stagingHome,
|
|
48766
|
+
home: options.home,
|
|
48767
|
+
...options.device === void 0 ? {} : { device: options.device }
|
|
48768
|
+
}) : void 0;
|
|
47072
48769
|
const accounts = (await openVault({ path: stagedVaultPath, key: options.vaultKey })).accounts();
|
|
47073
|
-
const
|
|
48770
|
+
const baseResult = {
|
|
47074
48771
|
accounts,
|
|
47075
48772
|
...plaintext.registry.default === void 0 ? {} : { default: plaintext.registry.default },
|
|
47076
48773
|
protected: plaintext.protected,
|
|
47077
48774
|
networks: [...plaintext.registry.networks]
|
|
47078
48775
|
};
|
|
48776
|
+
const markerPath = join8(options.home, RESTORE_MARKER_FILE);
|
|
48777
|
+
const fingerprint = restoreFingerprint(
|
|
48778
|
+
plaintext,
|
|
48779
|
+
stagedV2?.restoreDevice ?? options.device,
|
|
48780
|
+
options.vaultKey
|
|
48781
|
+
);
|
|
47079
48782
|
await withVaultLock(vaultPath, async () => {
|
|
47080
|
-
|
|
47081
|
-
|
|
48783
|
+
const existingVault = await pathExists2(vaultPath);
|
|
48784
|
+
const existingRegistry = await pathExists2(registryPath);
|
|
48785
|
+
let marker;
|
|
48786
|
+
if (existingVault || existingRegistry) {
|
|
48787
|
+
if (!existingVault) throw new BackupError("vault_exists");
|
|
48788
|
+
marker = await readRestoreMarker(markerPath, fingerprint, options.vaultKey);
|
|
48789
|
+
if (await sha256File(vaultPath) !== marker.vaultSha256) {
|
|
48790
|
+
throw new BackupError("vault_exists");
|
|
48791
|
+
}
|
|
48792
|
+
if (existingRegistry) {
|
|
48793
|
+
if (await sha256File(registryPath) !== marker.registrySha256) {
|
|
48794
|
+
throw new BackupError("vault_exists");
|
|
48795
|
+
}
|
|
48796
|
+
} else if (await sha256File(stagedRegistryPath) !== marker.registrySha256) {
|
|
48797
|
+
throw new BackupError("vault_exists");
|
|
48798
|
+
}
|
|
48799
|
+
} else {
|
|
48800
|
+
marker = await createRestoreMarker(
|
|
48801
|
+
stagedVaultPath,
|
|
48802
|
+
stagedRegistryPath,
|
|
48803
|
+
fingerprint,
|
|
48804
|
+
options.vaultKey
|
|
48805
|
+
);
|
|
48806
|
+
await writeJsonAtomic(markerPath, marker, { mode: 384 });
|
|
48807
|
+
await notifyRestorePublish(options.afterPublish, { kind: "marker" }, (error61) => {
|
|
48808
|
+
afterPublishError = error61;
|
|
48809
|
+
});
|
|
48810
|
+
}
|
|
48811
|
+
if (stagedV2 !== void 0) {
|
|
48812
|
+
await publishV2RestoreSafety(stagedV2, options.home);
|
|
48813
|
+
await notifyRestorePublish(options.afterPublish, { kind: "safety" }, (error61) => {
|
|
48814
|
+
afterPublishError = error61;
|
|
48815
|
+
});
|
|
47082
48816
|
}
|
|
47083
48817
|
try {
|
|
47084
48818
|
await options.beforePublish?.({ vault: stagedVaultPath, registry: stagedRegistryPath });
|
|
@@ -47086,21 +48820,66 @@ async function restoreBackupPlaintext(options) {
|
|
|
47086
48820
|
beforePublishError = error61;
|
|
47087
48821
|
throw error61;
|
|
47088
48822
|
}
|
|
48823
|
+
if (!existingVault && !existingRegistry) {
|
|
48824
|
+
const refreshedMarker = await createRestoreMarker(
|
|
48825
|
+
stagedVaultPath,
|
|
48826
|
+
stagedRegistryPath,
|
|
48827
|
+
fingerprint,
|
|
48828
|
+
options.vaultKey
|
|
48829
|
+
);
|
|
48830
|
+
if (refreshedMarker.vaultSha256 !== marker.vaultSha256 || refreshedMarker.registrySha256 !== marker.registrySha256) {
|
|
48831
|
+
marker = refreshedMarker;
|
|
48832
|
+
await writeJsonAtomic(markerPath, marker, { mode: 384 });
|
|
48833
|
+
}
|
|
48834
|
+
}
|
|
47089
48835
|
let publishedVault = false;
|
|
47090
|
-
|
|
47091
|
-
|
|
47092
|
-
|
|
47093
|
-
|
|
47094
|
-
|
|
47095
|
-
|
|
47096
|
-
|
|
48836
|
+
if (!existingVault) {
|
|
48837
|
+
try {
|
|
48838
|
+
await link2(stagedVaultPath, vaultPath);
|
|
48839
|
+
publishedVault = true;
|
|
48840
|
+
} catch (error61) {
|
|
48841
|
+
await rm2(markerPath, { force: true }).catch(() => void 0);
|
|
48842
|
+
throw error61;
|
|
48843
|
+
}
|
|
48844
|
+
await notifyRestorePublish(options.afterPublish, { kind: "vault" }, (error61) => {
|
|
48845
|
+
afterPublishError = error61;
|
|
48846
|
+
});
|
|
48847
|
+
}
|
|
48848
|
+
if (!existingRegistry) {
|
|
48849
|
+
try {
|
|
48850
|
+
await link2(stagedRegistryPath, registryPath);
|
|
48851
|
+
} catch (error61) {
|
|
48852
|
+
if (publishedVault) await rm2(vaultPath, { force: true }).catch(() => void 0);
|
|
48853
|
+
await rm2(markerPath, { force: true }).catch(() => void 0);
|
|
48854
|
+
throw error61;
|
|
48855
|
+
}
|
|
48856
|
+
await notifyRestorePublish(options.afterPublish, { kind: "registry" }, (error61) => {
|
|
48857
|
+
afterPublishError = error61;
|
|
48858
|
+
});
|
|
47097
48859
|
}
|
|
48860
|
+
if (stagedV2 !== void 0) {
|
|
48861
|
+
await publishV2Restore(stagedV2, options.home, async (step) => {
|
|
48862
|
+
await notifyRestorePublish(options.afterPublish, step, (error61) => {
|
|
48863
|
+
afterPublishError = error61;
|
|
48864
|
+
});
|
|
48865
|
+
});
|
|
48866
|
+
}
|
|
48867
|
+
await rm2(markerPath, { force: true });
|
|
47098
48868
|
});
|
|
47099
|
-
return
|
|
48869
|
+
if (stagedV2 === void 0) return baseResult;
|
|
48870
|
+
return {
|
|
48871
|
+
...baseResult,
|
|
48872
|
+
agents: stagedV2.restoredAgents,
|
|
48873
|
+
swarms: stagedV2.restoredSwarms,
|
|
48874
|
+
movements: stagedV2.restoredMovements,
|
|
48875
|
+
skipped: stagedV2.skipped,
|
|
48876
|
+
conflicts: stagedV2.conflicts
|
|
48877
|
+
};
|
|
47100
48878
|
} catch (error61) {
|
|
47101
48879
|
if (error61 === beforePublishError) throw error61;
|
|
48880
|
+
if (error61 === afterPublishError) throw error61;
|
|
47102
48881
|
if (error61 instanceof BackupError && error61.code === "vault_exists") throw error61;
|
|
47103
|
-
if (isExistingFile2(error61) || await
|
|
48882
|
+
if (isExistingFile2(error61) || await pathExists2(vaultPath).catch(() => false) || await pathExists2(registryPath).catch(() => false)) {
|
|
47104
48883
|
throw new BackupError("vault_exists");
|
|
47105
48884
|
}
|
|
47106
48885
|
throw new BackupError("backup_invalid_input");
|
|
@@ -47110,6 +48889,397 @@ async function restoreBackupPlaintext(options) {
|
|
|
47110
48889
|
}
|
|
47111
48890
|
}
|
|
47112
48891
|
}
|
|
48892
|
+
async function stageV2Restore(options) {
|
|
48893
|
+
const skipped = [];
|
|
48894
|
+
const conflicts = [];
|
|
48895
|
+
const accounts = new Map(options.plaintext.accounts.map((account) => [account.name, account]));
|
|
48896
|
+
const validAgents = (options.plaintext.agents ?? []).filter((profile) => {
|
|
48897
|
+
if (accounts.has(profile.wallet)) return true;
|
|
48898
|
+
skipped.push({ kind: "agent", name: profile.name, reason: "missing_account" });
|
|
48899
|
+
return false;
|
|
48900
|
+
});
|
|
48901
|
+
const validSwarms = (options.plaintext.swarms ?? []).filter((swarm) => {
|
|
48902
|
+
const treasury = accounts.get(swarm.treasury.account);
|
|
48903
|
+
if (treasury === void 0) {
|
|
48904
|
+
if (swarm.treasury.steps.created || swarm.treasury.steps.creating) {
|
|
48905
|
+
skipped.push({ kind: "swarm", name: swarm.name, reason: "missing_account" });
|
|
48906
|
+
return false;
|
|
48907
|
+
}
|
|
48908
|
+
} else if (swarm.treasury.address !== void 0 && swarm.treasury.address.toLowerCase() !== treasury.address.toLowerCase()) {
|
|
48909
|
+
skipped.push({ kind: "swarm", name: swarm.name, reason: "address_mismatch" });
|
|
48910
|
+
return false;
|
|
48911
|
+
}
|
|
48912
|
+
for (const member of swarm.members) {
|
|
48913
|
+
const account = accounts.get(member.account);
|
|
48914
|
+
if (account === void 0) {
|
|
48915
|
+
if (member.steps.created || member.steps.creating) {
|
|
48916
|
+
skipped.push({ kind: "swarm", name: swarm.name, reason: "missing_account" });
|
|
48917
|
+
return false;
|
|
48918
|
+
}
|
|
48919
|
+
continue;
|
|
48920
|
+
}
|
|
48921
|
+
if (member.address !== void 0 && member.address.toLowerCase() !== account.address.toLowerCase()) {
|
|
48922
|
+
skipped.push({ kind: "swarm", name: swarm.name, reason: "address_mismatch" });
|
|
48923
|
+
return false;
|
|
48924
|
+
}
|
|
48925
|
+
}
|
|
48926
|
+
return true;
|
|
48927
|
+
});
|
|
48928
|
+
const allMovementLegs = /* @__PURE__ */ new Set();
|
|
48929
|
+
const backupMovementIds = [];
|
|
48930
|
+
const validMovements = [];
|
|
48931
|
+
for (const backupMovement of options.plaintext.movements ?? []) {
|
|
48932
|
+
const movement = currentMovement(backupMovement);
|
|
48933
|
+
backupMovementIds.push(movement.id);
|
|
48934
|
+
const legKeys = movement.legs.map((leg) => movementTransferKey(leg.from, leg.nonce));
|
|
48935
|
+
for (const key of legKeys) allMovementLegs.add(key);
|
|
48936
|
+
const senders = /* @__PURE__ */ new Set([
|
|
48937
|
+
movement.from,
|
|
48938
|
+
...movement.treasury === void 0 ? [] : [movement.treasury],
|
|
48939
|
+
...movement.legs.map((leg) => leg.from)
|
|
48940
|
+
]);
|
|
48941
|
+
if ([...senders].some((sender) => !accounts.has(sender))) {
|
|
48942
|
+
skipped.push({ kind: "movement", name: movement.id, reason: "missing_account" });
|
|
48943
|
+
continue;
|
|
48944
|
+
}
|
|
48945
|
+
validMovements.push(movement);
|
|
48946
|
+
}
|
|
48947
|
+
const validReceipts = [];
|
|
48948
|
+
for (const receipt of options.plaintext.transferReceipts ?? []) {
|
|
48949
|
+
const key = movementTransferKey(receipt.wallet, receipt.transfer.nonce);
|
|
48950
|
+
if (!accounts.has(receipt.wallet)) {
|
|
48951
|
+
skipped.push({ kind: "receipt", name: receipt.id, reason: "missing_account" });
|
|
48952
|
+
} else if (!allMovementLegs.has(key)) {
|
|
48953
|
+
skipped.push({ kind: "receipt", name: receipt.id, reason: "missing_movement" });
|
|
48954
|
+
} else {
|
|
48955
|
+
validReceipts.push({
|
|
48956
|
+
name: receipt.id,
|
|
48957
|
+
line: JSON.stringify(parseReceipt(receipt)),
|
|
48958
|
+
legKey: key
|
|
48959
|
+
});
|
|
48960
|
+
}
|
|
48961
|
+
}
|
|
48962
|
+
let receiptsPath;
|
|
48963
|
+
let stagedReceipts = validReceipts;
|
|
48964
|
+
if (validReceipts.length > 0) {
|
|
48965
|
+
receiptsPath = getVapiPaths(options.stagingHome).receipts;
|
|
48966
|
+
const staged = await stageRestoreBytes(
|
|
48967
|
+
receiptsPath,
|
|
48968
|
+
`${validReceipts.map((receipt) => receipt.line).join("\n")}
|
|
48969
|
+
`
|
|
48970
|
+
);
|
|
48971
|
+
if (!staged) {
|
|
48972
|
+
for (const receipt of validReceipts) {
|
|
48973
|
+
skipped.push({ kind: "receipt", name: receipt.name, reason: "write_failed" });
|
|
48974
|
+
}
|
|
48975
|
+
stagedReceipts = [];
|
|
48976
|
+
receiptsPath = void 0;
|
|
48977
|
+
}
|
|
48978
|
+
}
|
|
48979
|
+
const assumedReceiptKeys = new Set(validReceipts.map((receipt) => receipt.legKey));
|
|
48980
|
+
try {
|
|
48981
|
+
const existingJournal = await readRestoreReceiptJournal(getVapiPaths(options.home).receipts);
|
|
48982
|
+
for (const key of existingJournal.signedKeys) assumedReceiptKeys.add(key);
|
|
48983
|
+
} catch {
|
|
48984
|
+
}
|
|
48985
|
+
const agents = [];
|
|
48986
|
+
for (const profile of validAgents) {
|
|
48987
|
+
const stagedPath = join8(options.stagingHome, "agents", `${profile.name}.json`);
|
|
48988
|
+
if (await stageRestoreBytes(stagedPath, prettyJson(profile))) {
|
|
48989
|
+
agents.push({
|
|
48990
|
+
kind: "agent",
|
|
48991
|
+
name: profile.name,
|
|
48992
|
+
stagedPath,
|
|
48993
|
+
targetPath: join8(options.home, "agents", `${profile.name}.json`)
|
|
48994
|
+
});
|
|
48995
|
+
} else {
|
|
48996
|
+
skipped.push({ kind: "agent", name: profile.name, reason: "write_failed" });
|
|
48997
|
+
}
|
|
48998
|
+
}
|
|
48999
|
+
let restoreDevice = options.device;
|
|
49000
|
+
if (validSwarms.length > 0 && restoreDevice === void 0) {
|
|
49001
|
+
try {
|
|
49002
|
+
restoreDevice = deviceName({ config: await loadConfig(getVapiPaths(options.home).config) });
|
|
49003
|
+
} catch {
|
|
49004
|
+
for (const swarm of validSwarms) {
|
|
49005
|
+
skipped.push({ kind: "swarm", name: swarm.name, reason: "write_failed" });
|
|
49006
|
+
}
|
|
49007
|
+
}
|
|
49008
|
+
}
|
|
49009
|
+
const swarms = [];
|
|
49010
|
+
if (restoreDevice !== void 0) {
|
|
49011
|
+
for (const value of validSwarms) {
|
|
49012
|
+
const swarm = swarmFileSchema.parse({ ...value, device: restoreDevice });
|
|
49013
|
+
const stagedPath = join8(options.stagingHome, "swarms", `${swarm.name}.json`);
|
|
49014
|
+
if (await stageRestoreBytes(stagedPath, prettyJson(swarm))) {
|
|
49015
|
+
swarms.push({
|
|
49016
|
+
kind: "swarm",
|
|
49017
|
+
name: swarm.name,
|
|
49018
|
+
stagedPath,
|
|
49019
|
+
targetPath: join8(options.home, "swarms", `${swarm.name}.json`)
|
|
49020
|
+
});
|
|
49021
|
+
} else {
|
|
49022
|
+
skipped.push({ kind: "swarm", name: swarm.name, reason: "write_failed" });
|
|
49023
|
+
}
|
|
49024
|
+
}
|
|
49025
|
+
}
|
|
49026
|
+
const movements = [];
|
|
49027
|
+
for (const movement of validMovements) {
|
|
49028
|
+
const restored = reconcileRestoredMovement(movement, assumedReceiptKeys);
|
|
49029
|
+
const stagedPath = join8(options.stagingHome, "movements", `${restored.id}.json`);
|
|
49030
|
+
if (await stageRestoreBytes(stagedPath, serializeMovement(restored))) {
|
|
49031
|
+
movements.push({
|
|
49032
|
+
kind: "movement",
|
|
49033
|
+
name: restored.id,
|
|
49034
|
+
stagedPath,
|
|
49035
|
+
targetPath: join8(options.home, "movements", `${restored.id}.json`),
|
|
49036
|
+
movement: restored
|
|
49037
|
+
});
|
|
49038
|
+
} else {
|
|
49039
|
+
skipped.push({ kind: "movement", name: restored.id, reason: "write_failed" });
|
|
49040
|
+
}
|
|
49041
|
+
}
|
|
49042
|
+
return {
|
|
49043
|
+
agents,
|
|
49044
|
+
swarms,
|
|
49045
|
+
movements,
|
|
49046
|
+
backupMovementIds,
|
|
49047
|
+
receipts: stagedReceipts,
|
|
49048
|
+
signedReceiptKeys: assumedReceiptKeys,
|
|
49049
|
+
...receiptsPath === void 0 ? {} : { receiptsPath },
|
|
49050
|
+
restoredAgents: [],
|
|
49051
|
+
restoredSwarms: [],
|
|
49052
|
+
restoredMovements: [],
|
|
49053
|
+
skipped,
|
|
49054
|
+
conflicts,
|
|
49055
|
+
...restoreDevice === void 0 ? {} : { restoreDevice }
|
|
49056
|
+
};
|
|
49057
|
+
}
|
|
49058
|
+
async function publishV2Restore(staged, home, afterPublish) {
|
|
49059
|
+
for (const file2 of staged.agents) {
|
|
49060
|
+
const outcome = await publishRestoreFile(file2, staged);
|
|
49061
|
+
if (outcome === "restored") staged.restoredAgents.push(file2.name);
|
|
49062
|
+
if (outcome === "restored") await afterPublish?.({ kind: "agent", name: file2.name });
|
|
49063
|
+
}
|
|
49064
|
+
for (const file2 of staged.swarms) {
|
|
49065
|
+
const outcome = await publishRestoreFile(file2, staged);
|
|
49066
|
+
if (outcome === "restored") staged.restoredSwarms.push(file2.name);
|
|
49067
|
+
if (outcome === "restored") await afterPublish?.({ kind: "swarm", name: file2.name });
|
|
49068
|
+
}
|
|
49069
|
+
const restoredMovementIds = [];
|
|
49070
|
+
for (const file2 of staged.movements) {
|
|
49071
|
+
const outcome = await publishRestoreFile(file2, staged);
|
|
49072
|
+
if (outcome === "restored") restoredMovementIds.push(file2.movement.id);
|
|
49073
|
+
if (outcome === "restored") await afterPublish?.({ kind: "movement", name: file2.name });
|
|
49074
|
+
}
|
|
49075
|
+
await reconcilePublishedMovements(staged, home);
|
|
49076
|
+
for (const id of restoredMovementIds) {
|
|
49077
|
+
try {
|
|
49078
|
+
staged.restoredMovements.push(movementSummary(await readMovement(home, id)));
|
|
49079
|
+
} catch {
|
|
49080
|
+
staged.skipped.push({ kind: "movement", name: id, reason: "write_failed" });
|
|
49081
|
+
}
|
|
49082
|
+
}
|
|
49083
|
+
}
|
|
49084
|
+
async function publishV2RestoreSafety(staged, home) {
|
|
49085
|
+
for (const id of staged.backupMovementIds) {
|
|
49086
|
+
await markOpenLegsRestored(home, id);
|
|
49087
|
+
}
|
|
49088
|
+
await publishRestoreReceipts(staged, getVapiPaths(home).receipts);
|
|
49089
|
+
await reconcilePublishedMovements(staged, home, true);
|
|
49090
|
+
}
|
|
49091
|
+
async function publishRestoreFile(file2, staged) {
|
|
49092
|
+
try {
|
|
49093
|
+
const directory = dirname4(file2.targetPath);
|
|
49094
|
+
await mkdir7(directory, { recursive: true, mode: 448 });
|
|
49095
|
+
await chmod2(directory, 448);
|
|
49096
|
+
try {
|
|
49097
|
+
await link2(file2.stagedPath, file2.targetPath);
|
|
49098
|
+
await chmod2(file2.targetPath, 384);
|
|
49099
|
+
return "restored";
|
|
49100
|
+
} catch (error61) {
|
|
49101
|
+
if (!isExistingFile2(error61)) throw error61;
|
|
49102
|
+
}
|
|
49103
|
+
let identical = false;
|
|
49104
|
+
try {
|
|
49105
|
+
const [source, target] = await Promise.all([
|
|
49106
|
+
readFile9(file2.stagedPath),
|
|
49107
|
+
readFile9(file2.targetPath)
|
|
49108
|
+
]);
|
|
49109
|
+
identical = source.equals(target);
|
|
49110
|
+
} catch {
|
|
49111
|
+
}
|
|
49112
|
+
if (identical) {
|
|
49113
|
+
await chmod2(file2.targetPath, 384);
|
|
49114
|
+
return "restored";
|
|
49115
|
+
}
|
|
49116
|
+
staged.conflicts.push({
|
|
49117
|
+
kind: file2.kind,
|
|
49118
|
+
name: file2.name,
|
|
49119
|
+
path: file2.targetPath
|
|
49120
|
+
});
|
|
49121
|
+
return "conflict";
|
|
49122
|
+
} catch {
|
|
49123
|
+
staged.skipped.push({ kind: file2.kind, name: file2.name, reason: "write_failed" });
|
|
49124
|
+
return "failed";
|
|
49125
|
+
}
|
|
49126
|
+
}
|
|
49127
|
+
async function publishRestoreReceipts(staged, targetPath) {
|
|
49128
|
+
return await withReceiptJournalLock(
|
|
49129
|
+
targetPath,
|
|
49130
|
+
async () => publishRestoreReceiptsLocked(staged, targetPath)
|
|
49131
|
+
);
|
|
49132
|
+
}
|
|
49133
|
+
async function publishRestoreReceiptsLocked(staged, targetPath) {
|
|
49134
|
+
const restored = /* @__PURE__ */ new Set();
|
|
49135
|
+
if (staged.receipts.length === 0 || staged.receiptsPath === void 0) return restored;
|
|
49136
|
+
let existing = "";
|
|
49137
|
+
let existingLines = /* @__PURE__ */ new Set();
|
|
49138
|
+
try {
|
|
49139
|
+
await readFile9(staged.receiptsPath, "utf8");
|
|
49140
|
+
await mkdir7(dirname4(targetPath), { recursive: true, mode: 448 });
|
|
49141
|
+
try {
|
|
49142
|
+
const journal = await readRestoreReceiptJournal(targetPath);
|
|
49143
|
+
existing = journal.raw;
|
|
49144
|
+
existingLines = journal.lines;
|
|
49145
|
+
await chmod2(targetPath, 384);
|
|
49146
|
+
} catch (error61) {
|
|
49147
|
+
if (!isMissingFile3(error61)) throw error61;
|
|
49148
|
+
}
|
|
49149
|
+
} catch {
|
|
49150
|
+
for (const receipt of staged.receipts) {
|
|
49151
|
+
staged.skipped.push({ kind: "receipt", name: receipt.name, reason: "write_failed" });
|
|
49152
|
+
}
|
|
49153
|
+
return restored;
|
|
49154
|
+
}
|
|
49155
|
+
let needsLeadingNewline = existing.length > 0 && !existing.endsWith("\n");
|
|
49156
|
+
for (const receipt of staged.receipts) {
|
|
49157
|
+
if (existingLines.has(receipt.line)) {
|
|
49158
|
+
restored.add(receipt.legKey);
|
|
49159
|
+
continue;
|
|
49160
|
+
}
|
|
49161
|
+
let handle;
|
|
49162
|
+
try {
|
|
49163
|
+
handle = await openFile(targetPath, "a", 384);
|
|
49164
|
+
await handle.chmod(384);
|
|
49165
|
+
await handle.writeFile(`${needsLeadingNewline ? "\n" : ""}${receipt.line}
|
|
49166
|
+
`, "utf8");
|
|
49167
|
+
await handle.close();
|
|
49168
|
+
handle = void 0;
|
|
49169
|
+
needsLeadingNewline = false;
|
|
49170
|
+
existingLines.add(receipt.line);
|
|
49171
|
+
restored.add(receipt.legKey);
|
|
49172
|
+
} catch {
|
|
49173
|
+
await handle?.close().catch(() => void 0);
|
|
49174
|
+
staged.skipped.push({ kind: "receipt", name: receipt.name, reason: "write_failed" });
|
|
49175
|
+
}
|
|
49176
|
+
}
|
|
49177
|
+
return restored;
|
|
49178
|
+
}
|
|
49179
|
+
async function reconcilePublishedMovements(staged, home, requireDurableSafety = false) {
|
|
49180
|
+
const signedKeys = new Set(staged.signedReceiptKeys);
|
|
49181
|
+
const journaledKeys = /* @__PURE__ */ new Set();
|
|
49182
|
+
try {
|
|
49183
|
+
const journal = await readRestoreReceiptJournal(getVapiPaths(home).receipts);
|
|
49184
|
+
for (const key of journal.signedKeys) {
|
|
49185
|
+
signedKeys.add(key);
|
|
49186
|
+
journaledKeys.add(key);
|
|
49187
|
+
}
|
|
49188
|
+
} catch {
|
|
49189
|
+
}
|
|
49190
|
+
if (signedKeys.size === 0) return;
|
|
49191
|
+
const unjournaledKeys = new Set([...signedKeys].filter((key) => !journaledKeys.has(key)));
|
|
49192
|
+
let files;
|
|
49193
|
+
try {
|
|
49194
|
+
files = await readdir5(join8(home, "movements"));
|
|
49195
|
+
} catch (error61) {
|
|
49196
|
+
if (isMissingFile3(error61)) return;
|
|
49197
|
+
if (requireDurableSafety && unjournaledKeys.size > 0) throw error61;
|
|
49198
|
+
return;
|
|
49199
|
+
}
|
|
49200
|
+
for (const file2 of files) {
|
|
49201
|
+
if (!file2.endsWith(".json")) continue;
|
|
49202
|
+
const id = file2.slice(0, -5);
|
|
49203
|
+
let movement;
|
|
49204
|
+
try {
|
|
49205
|
+
movement = await readMovement(home, id);
|
|
49206
|
+
} catch (error61) {
|
|
49207
|
+
if (requireDurableSafety && unjournaledKeys.size > 0) throw error61;
|
|
49208
|
+
continue;
|
|
49209
|
+
}
|
|
49210
|
+
if (!movement.legs.some(
|
|
49211
|
+
(leg) => leg.status !== "sent" && leg.status !== "cancelled" && signedKeys.has(movementTransferKey(leg.from, leg.nonce))
|
|
49212
|
+
)) {
|
|
49213
|
+
continue;
|
|
49214
|
+
}
|
|
49215
|
+
try {
|
|
49216
|
+
await reconcileSignedLegs(home, id, signedKeys);
|
|
49217
|
+
} catch (error61) {
|
|
49218
|
+
staged.skipped.push({ kind: "movement", name: id, reason: "write_failed" });
|
|
49219
|
+
if (requireDurableSafety && movement.legs.some(
|
|
49220
|
+
(leg) => leg.status !== "sent" && leg.status !== "cancelled" && unjournaledKeys.has(movementTransferKey(leg.from, leg.nonce))
|
|
49221
|
+
)) {
|
|
49222
|
+
throw error61;
|
|
49223
|
+
}
|
|
49224
|
+
}
|
|
49225
|
+
}
|
|
49226
|
+
}
|
|
49227
|
+
async function stageRestoreBytes(path, bytes) {
|
|
49228
|
+
let handle;
|
|
49229
|
+
try {
|
|
49230
|
+
await mkdir7(dirname4(path), { recursive: true, mode: 448 });
|
|
49231
|
+
handle = await openFile(path, "wx", 384);
|
|
49232
|
+
await handle.writeFile(bytes, "utf8");
|
|
49233
|
+
await handle.close();
|
|
49234
|
+
return true;
|
|
49235
|
+
} catch {
|
|
49236
|
+
await handle?.close().catch(() => void 0);
|
|
49237
|
+
return false;
|
|
49238
|
+
}
|
|
49239
|
+
}
|
|
49240
|
+
function currentMovement(movement) {
|
|
49241
|
+
if (movement.v === 2) return movement;
|
|
49242
|
+
return {
|
|
49243
|
+
...movement,
|
|
49244
|
+
v: 2,
|
|
49245
|
+
legs: movement.legs.map((leg) => ({
|
|
49246
|
+
...leg,
|
|
49247
|
+
from: movement.from,
|
|
49248
|
+
purpose: "send"
|
|
49249
|
+
}))
|
|
49250
|
+
};
|
|
49251
|
+
}
|
|
49252
|
+
function reconcileRestoredMovement(movement, receiptKeys) {
|
|
49253
|
+
return {
|
|
49254
|
+
...movement,
|
|
49255
|
+
legs: movement.legs.map((leg) => {
|
|
49256
|
+
if (leg.status === "cancelled") return leg;
|
|
49257
|
+
return leg.status !== "sent" && receiptKeys.has(movementTransferKey(leg.from, leg.nonce)) ? {
|
|
49258
|
+
from: leg.from,
|
|
49259
|
+
to: leg.to,
|
|
49260
|
+
amountUsd: leg.amountUsd,
|
|
49261
|
+
purpose: leg.purpose,
|
|
49262
|
+
nonce: leg.nonce,
|
|
49263
|
+
status: "unknown",
|
|
49264
|
+
restored: true
|
|
49265
|
+
} : { ...leg, restored: true };
|
|
49266
|
+
})
|
|
49267
|
+
};
|
|
49268
|
+
}
|
|
49269
|
+
function movementSummary(movement) {
|
|
49270
|
+
return {
|
|
49271
|
+
id: movement.id,
|
|
49272
|
+
from: movement.from,
|
|
49273
|
+
network: movement.network,
|
|
49274
|
+
createdAt: movement.createdAt,
|
|
49275
|
+
pendingLegs: movement.legs.filter((leg) => leg.status === "planned").length,
|
|
49276
|
+
unknownLegs: movement.legs.filter((leg) => leg.status === "unknown").length
|
|
49277
|
+
};
|
|
49278
|
+
}
|
|
49279
|
+
function prettyJson(value) {
|
|
49280
|
+
return `${JSON.stringify(value, null, 2)}
|
|
49281
|
+
`;
|
|
49282
|
+
}
|
|
47113
49283
|
function parseSealed(sealed) {
|
|
47114
49284
|
const value = JSON.parse(sealed);
|
|
47115
49285
|
if (!isRecord(value) || !hasExactKeys(value, RELAY_KEYS) || value.format !== RELAY_FORMAT || value.v !== RELAY_VERSION || typeof value.epk !== "string" || typeof value.nonce !== "string" || typeof value.ciphertext !== "string" || decodeBase64Url(value.epk)?.byteLength !== 32 || decodeBase64Url(value.nonce)?.byteLength !== 12 || (decodeBase64Url(value.ciphertext)?.byteLength ?? 0) < 16) {
|
|
@@ -47127,9 +49297,9 @@ function assertRelayKey(value) {
|
|
|
47127
49297
|
throw new BackupError("relay_invalid_key");
|
|
47128
49298
|
}
|
|
47129
49299
|
}
|
|
47130
|
-
function readRelayRandomBytes(
|
|
49300
|
+
function readRelayRandomBytes(randomBytes7, length) {
|
|
47131
49301
|
try {
|
|
47132
|
-
const value =
|
|
49302
|
+
const value = randomBytes7(length);
|
|
47133
49303
|
if (!(value instanceof Uint8Array) || value.byteLength !== length) throw new Error();
|
|
47134
49304
|
return Buffer.from(value);
|
|
47135
49305
|
} catch {
|
|
@@ -47185,7 +49355,7 @@ function validateRestorePlaintext(value) {
|
|
|
47185
49355
|
try {
|
|
47186
49356
|
const parsed = backupPlaintextSchema.safeParse(value);
|
|
47187
49357
|
if (!parsed.success) throw new Error();
|
|
47188
|
-
const plaintext =
|
|
49358
|
+
const plaintext = parsed.data.v === 1 ? buildPlaintextV1(parsed.data) : buildPlaintextV2(parsed.data);
|
|
47189
49359
|
const phrase = validateRecoveryPhrase(plaintext.phrase);
|
|
47190
49360
|
let seed;
|
|
47191
49361
|
try {
|
|
@@ -47208,12 +49378,22 @@ function buildRestoredRegistry(plaintext) {
|
|
|
47208
49378
|
for (let index = 0; index < plaintext.accounts.length; index += 1) {
|
|
47209
49379
|
const account = plaintext.accounts[index];
|
|
47210
49380
|
const metadata = plaintext.registry.accounts[index];
|
|
49381
|
+
const metadataV2 = plaintext.v === 2 ? plaintext.registry.accounts[index] : void 0;
|
|
47211
49382
|
wallets[account.name] = {
|
|
47212
49383
|
createdAt: account.createdAt,
|
|
47213
49384
|
...metadata.label === void 0 ? {} : { label: metadata.label },
|
|
47214
49385
|
spendCaps: {
|
|
47215
49386
|
perCallAtomic: metadata.spendCaps.perCallAtomic,
|
|
47216
49387
|
perDayAtomic: metadata.spendCaps.perDayAtomic
|
|
49388
|
+
},
|
|
49389
|
+
...metadataV2 === void 0 ? {} : {
|
|
49390
|
+
ceilingAtomic: metadataV2.ceilingAtomic === void 0 ? DEFAULT_CEILING_ATOMIC.toString() : metadataV2.ceilingAtomic,
|
|
49391
|
+
...metadataV2.routerRefill === void 0 ? {} : {
|
|
49392
|
+
routerRefill: {
|
|
49393
|
+
belowUsd: metadataV2.routerRefill.belowUsd,
|
|
49394
|
+
tierUsd: metadataV2.routerRefill.tierUsd
|
|
49395
|
+
}
|
|
49396
|
+
}
|
|
47217
49397
|
}
|
|
47218
49398
|
};
|
|
47219
49399
|
}
|
|
@@ -47223,7 +49403,48 @@ function buildRestoredRegistry(plaintext) {
|
|
|
47223
49403
|
wallets
|
|
47224
49404
|
});
|
|
47225
49405
|
}
|
|
47226
|
-
|
|
49406
|
+
function restoreFingerprint(plaintext, device, vaultKey) {
|
|
49407
|
+
return createHmac("sha256", vaultKey).update("vapi-backup-restore/v1\n", "utf8").update(JSON.stringify({ device: device ?? null, plaintext }), "utf8").digest("hex");
|
|
49408
|
+
}
|
|
49409
|
+
async function createRestoreMarker(vaultPath, registryPath, fingerprint, vaultKey) {
|
|
49410
|
+
const marker = {
|
|
49411
|
+
v: 1,
|
|
49412
|
+
fingerprint,
|
|
49413
|
+
vaultSha256: await sha256File(vaultPath),
|
|
49414
|
+
registrySha256: await sha256File(registryPath)
|
|
49415
|
+
};
|
|
49416
|
+
return { ...marker, mac: restoreMarkerMac(marker, vaultKey) };
|
|
49417
|
+
}
|
|
49418
|
+
async function readRestoreMarker(path, fingerprint, vaultKey) {
|
|
49419
|
+
try {
|
|
49420
|
+
const marker = restoreMarkerSchema.parse(JSON.parse(await readFile9(path, "utf8")));
|
|
49421
|
+
const expectedMac = restoreMarkerMac(marker, vaultKey);
|
|
49422
|
+
if (marker.fingerprint !== fingerprint || !timingSafeEqual(Buffer.from(marker.mac, "hex"), Buffer.from(expectedMac, "hex"))) {
|
|
49423
|
+
throw new Error();
|
|
49424
|
+
}
|
|
49425
|
+
return marker;
|
|
49426
|
+
} catch {
|
|
49427
|
+
throw new BackupError("vault_exists");
|
|
49428
|
+
}
|
|
49429
|
+
}
|
|
49430
|
+
function restoreMarkerMac(marker, vaultKey) {
|
|
49431
|
+
return createHmac("sha256", vaultKey).update(
|
|
49432
|
+
`vapi-backup-restore-marker/v1|${marker.fingerprint}|${marker.vaultSha256}|${marker.registrySha256}`,
|
|
49433
|
+
"utf8"
|
|
49434
|
+
).digest("hex");
|
|
49435
|
+
}
|
|
49436
|
+
async function sha256File(path) {
|
|
49437
|
+
return createHash("sha256").update(await readFile9(path)).digest("hex");
|
|
49438
|
+
}
|
|
49439
|
+
async function notifyRestorePublish(hook, step, rememberError) {
|
|
49440
|
+
try {
|
|
49441
|
+
await hook?.(step);
|
|
49442
|
+
} catch (error61) {
|
|
49443
|
+
rememberError(error61);
|
|
49444
|
+
throw error61;
|
|
49445
|
+
}
|
|
49446
|
+
}
|
|
49447
|
+
async function pathExists2(path) {
|
|
47227
49448
|
try {
|
|
47228
49449
|
await lstat(path);
|
|
47229
49450
|
return true;
|
|
@@ -47273,7 +49494,23 @@ function validateBackupSourceRelationships(source, context) {
|
|
|
47273
49494
|
}
|
|
47274
49495
|
}
|
|
47275
49496
|
}
|
|
47276
|
-
function
|
|
49497
|
+
function validateBackupV2Uniqueness(source, context) {
|
|
49498
|
+
validateUniqueValues(source.agents, (agent) => agent.name, "agent name", context);
|
|
49499
|
+
validateUniqueValues(source.swarms, (swarm) => swarm.name, "swarm name", context);
|
|
49500
|
+
validateUniqueValues(source.movements, (movement) => movement.id, "movement id", context);
|
|
49501
|
+
}
|
|
49502
|
+
function validateUniqueValues(values, key, label, context) {
|
|
49503
|
+
if (values === void 0) return;
|
|
49504
|
+
const seen = /* @__PURE__ */ new Set();
|
|
49505
|
+
for (const value of values) {
|
|
49506
|
+
const current = key(value);
|
|
49507
|
+
if (seen.has(current)) {
|
|
49508
|
+
context.addIssue({ code: "custom", message: `Duplicate ${label}.` });
|
|
49509
|
+
}
|
|
49510
|
+
seen.add(current);
|
|
49511
|
+
}
|
|
49512
|
+
}
|
|
49513
|
+
function buildPlaintextV1(source) {
|
|
47277
49514
|
const accounts = source.accounts.map(
|
|
47278
49515
|
(account) => account.kind === "derived" ? {
|
|
47279
49516
|
name: account.name,
|
|
@@ -47311,6 +49548,229 @@ function buildPlaintext(source) {
|
|
|
47311
49548
|
protected: source.protected
|
|
47312
49549
|
};
|
|
47313
49550
|
}
|
|
49551
|
+
function buildPlaintextV2(source, included = { agents: true, swarms: true, movements: true }) {
|
|
49552
|
+
const accounts = buildPlaintextV1({
|
|
49553
|
+
phrase: source.phrase,
|
|
49554
|
+
nextDerivedIndex: source.nextDerivedIndex,
|
|
49555
|
+
accounts: source.accounts,
|
|
49556
|
+
registry: {
|
|
49557
|
+
...source.registry.default === void 0 ? {} : { default: source.registry.default },
|
|
49558
|
+
accounts: source.registry.accounts,
|
|
49559
|
+
networks: source.registry.networks
|
|
49560
|
+
},
|
|
49561
|
+
protected: source.protected
|
|
49562
|
+
}).accounts;
|
|
49563
|
+
const registry2 = {
|
|
49564
|
+
...source.registry.default === void 0 ? {} : { default: source.registry.default },
|
|
49565
|
+
accounts: source.registry.accounts.map((account) => ({
|
|
49566
|
+
name: account.name,
|
|
49567
|
+
...account.label === void 0 ? {} : { label: account.label },
|
|
49568
|
+
spendCaps: {
|
|
49569
|
+
perCallAtomic: account.spendCaps.perCallAtomic,
|
|
49570
|
+
perDayAtomic: account.spendCaps.perDayAtomic
|
|
49571
|
+
},
|
|
49572
|
+
...account.ceilingAtomic === void 0 || account.ceilingAtomic === DEFAULT_CEILING_ATOMIC.toString() ? {} : { ceilingAtomic: account.ceilingAtomic },
|
|
49573
|
+
...account.routerRefill === void 0 ? {} : {
|
|
49574
|
+
routerRefill: {
|
|
49575
|
+
belowUsd: account.routerRefill.belowUsd,
|
|
49576
|
+
tierUsd: account.routerRefill.tierUsd
|
|
49577
|
+
}
|
|
49578
|
+
}
|
|
49579
|
+
})),
|
|
49580
|
+
networks: [...source.registry.networks]
|
|
49581
|
+
};
|
|
49582
|
+
const agents = source.agents;
|
|
49583
|
+
const swarms = source.swarms;
|
|
49584
|
+
const movements = source.movements;
|
|
49585
|
+
const transferReceipts = source.transferReceipts;
|
|
49586
|
+
return {
|
|
49587
|
+
v: 2,
|
|
49588
|
+
phrase: source.phrase,
|
|
49589
|
+
nextDerivedIndex: source.nextDerivedIndex,
|
|
49590
|
+
accounts,
|
|
49591
|
+
registry: registry2,
|
|
49592
|
+
protected: source.protected,
|
|
49593
|
+
...included.agents !== false && agents !== void 0 && agents.length > 0 ? { agents: [...agents].sort(compareNamedValues).map(copyAgentProfile) } : {},
|
|
49594
|
+
...included.swarms !== false && swarms !== void 0 && swarms.length > 0 ? { swarms: [...swarms].sort(compareNamedValues).map(copySwarm) } : {},
|
|
49595
|
+
...included.movements !== false && movements !== void 0 && movements.length > 0 ? { movements: [...movements].sort(compareMovements).map(copyMovement) } : {},
|
|
49596
|
+
...included.movements !== false && transferReceipts !== void 0 && transferReceipts.length > 0 ? { transferReceipts: transferReceipts.map(copyTransferReceipt) } : {}
|
|
49597
|
+
};
|
|
49598
|
+
}
|
|
49599
|
+
function backupCandidates(source) {
|
|
49600
|
+
if (!isBackupSourceV2(source)) {
|
|
49601
|
+
return [{ plaintext: buildPlaintextV1(source), omitted: [] }];
|
|
49602
|
+
}
|
|
49603
|
+
const candidates = [];
|
|
49604
|
+
const included = { agents: true, swarms: true, movements: true };
|
|
49605
|
+
const omitted = [];
|
|
49606
|
+
const addCandidate = () => {
|
|
49607
|
+
candidates.push({
|
|
49608
|
+
plaintext: hasV2OnlyData(source, included) ? buildPlaintextV2(source, included) : buildPlaintextV1(source),
|
|
49609
|
+
omitted: [...omitted]
|
|
49610
|
+
});
|
|
49611
|
+
};
|
|
49612
|
+
addCandidate();
|
|
49613
|
+
if (source.agents.length > 0) {
|
|
49614
|
+
included.agents = false;
|
|
49615
|
+
omitted.push("agents");
|
|
49616
|
+
addCandidate();
|
|
49617
|
+
}
|
|
49618
|
+
if (source.swarms.length > 0) {
|
|
49619
|
+
included.swarms = false;
|
|
49620
|
+
omitted.push("swarms");
|
|
49621
|
+
addCandidate();
|
|
49622
|
+
}
|
|
49623
|
+
if (source.movements.length > 0 || source.transferReceipts.length > 0) {
|
|
49624
|
+
included.movements = false;
|
|
49625
|
+
omitted.push("movements");
|
|
49626
|
+
addCandidate();
|
|
49627
|
+
}
|
|
49628
|
+
return candidates;
|
|
49629
|
+
}
|
|
49630
|
+
function hasV2OnlyData(source, included) {
|
|
49631
|
+
return source.registry.accounts.some(
|
|
49632
|
+
(account) => account.ceilingAtomic !== void 0 && account.ceilingAtomic !== DEFAULT_CEILING_ATOMIC.toString() || account.routerRefill !== void 0
|
|
49633
|
+
) || included.agents !== false && source.agents.length > 0 || included.swarms !== false && source.swarms.length > 0 || included.movements !== false && (source.movements.length > 0 || source.transferReceipts.length > 0);
|
|
49634
|
+
}
|
|
49635
|
+
function isBackupSourceV2(source) {
|
|
49636
|
+
return "agents" in source;
|
|
49637
|
+
}
|
|
49638
|
+
function copyAgentProfile(profile) {
|
|
49639
|
+
return {
|
|
49640
|
+
version: profile.version,
|
|
49641
|
+
name: profile.name,
|
|
49642
|
+
wallet: profile.wallet,
|
|
49643
|
+
model: profile.model,
|
|
49644
|
+
instructions: profile.instructions,
|
|
49645
|
+
verifiedOnly: profile.verifiedOnly,
|
|
49646
|
+
approveAboveUsd: profile.approveAboveUsd,
|
|
49647
|
+
...profile.maxPerTaskUsd === void 0 ? {} : { maxPerTaskUsd: profile.maxPerTaskUsd },
|
|
49648
|
+
...profile.autoReleaseBelowUsd === void 0 ? {} : { autoReleaseBelowUsd: profile.autoReleaseBelowUsd },
|
|
49649
|
+
maxSteps: profile.maxSteps,
|
|
49650
|
+
paused: profile.paused,
|
|
49651
|
+
createdAt: profile.createdAt,
|
|
49652
|
+
tools: [...profile.tools],
|
|
49653
|
+
...profile.grants === void 0 ? {} : { grants: [...profile.grants] }
|
|
49654
|
+
};
|
|
49655
|
+
}
|
|
49656
|
+
function copySwarm(swarm) {
|
|
49657
|
+
return swarmFileSchema.parse(swarm);
|
|
49658
|
+
}
|
|
49659
|
+
function copyMovement(movement) {
|
|
49660
|
+
return toBackupMovement(movement);
|
|
49661
|
+
}
|
|
49662
|
+
function toBackupMovement(movement) {
|
|
49663
|
+
if (movement.v === 1) return backupMovementSchema.parse(movement);
|
|
49664
|
+
return backupMovementSchema.parse({
|
|
49665
|
+
...movement,
|
|
49666
|
+
legs: movement.legs.map(
|
|
49667
|
+
({
|
|
49668
|
+
restored: _restored,
|
|
49669
|
+
fromAddress: _fromAddress,
|
|
49670
|
+
toAddress: _toAddress,
|
|
49671
|
+
addressBindingSource: _addressBindingSource,
|
|
49672
|
+
...leg
|
|
49673
|
+
}) => leg
|
|
49674
|
+
)
|
|
49675
|
+
});
|
|
49676
|
+
}
|
|
49677
|
+
function toBackupTransferReceipt(receipt) {
|
|
49678
|
+
if (receipt.quote?.asset === void 0) throw new BackupError("backup_invalid_input");
|
|
49679
|
+
return copyTransferReceipt({
|
|
49680
|
+
id: receipt.id,
|
|
49681
|
+
timestamp: receipt.timestamp,
|
|
49682
|
+
kind: "transfer",
|
|
49683
|
+
wallet: receiptWallet(receipt),
|
|
49684
|
+
resourceUrl: receipt.resourceUrl,
|
|
49685
|
+
quote: {
|
|
49686
|
+
network: receipt.quote.network,
|
|
49687
|
+
asset: receipt.quote.asset,
|
|
49688
|
+
amountAtomic: receipt.quote.amountAtomic
|
|
49689
|
+
},
|
|
49690
|
+
transfer: receipt.transfer,
|
|
49691
|
+
...receipt.error === void 0 ? {} : { error: receipt.error }
|
|
49692
|
+
});
|
|
49693
|
+
}
|
|
49694
|
+
function copyTransferReceipt(receipt) {
|
|
49695
|
+
const transfer = receipt.transfer;
|
|
49696
|
+
const request = transfer.request;
|
|
49697
|
+
return backupTransferReceiptSchema.parse({
|
|
49698
|
+
id: receipt.id,
|
|
49699
|
+
timestamp: receipt.timestamp,
|
|
49700
|
+
kind: "transfer",
|
|
49701
|
+
wallet: receipt.wallet,
|
|
49702
|
+
resourceUrl: receipt.resourceUrl,
|
|
49703
|
+
quote: {
|
|
49704
|
+
network: receipt.quote.network,
|
|
49705
|
+
asset: receipt.quote.asset,
|
|
49706
|
+
amountAtomic: receipt.quote.amountAtomic
|
|
49707
|
+
},
|
|
49708
|
+
transfer: {
|
|
49709
|
+
to: transfer.to,
|
|
49710
|
+
toName: transfer.toName,
|
|
49711
|
+
toKind: transfer.toKind,
|
|
49712
|
+
amountAtomic: transfer.amountAtomic,
|
|
49713
|
+
network: transfer.network,
|
|
49714
|
+
nonce: transfer.nonce,
|
|
49715
|
+
status: transfer.status,
|
|
49716
|
+
txHash: transfer.txHash,
|
|
49717
|
+
replayed: transfer.replayed,
|
|
49718
|
+
...transfer.reservedOn === void 0 ? {} : { reservedOn: transfer.reservedOn },
|
|
49719
|
+
...request === void 0 ? {} : {
|
|
49720
|
+
request: {
|
|
49721
|
+
authorization: {
|
|
49722
|
+
from: request.authorization.from,
|
|
49723
|
+
to: request.authorization.to,
|
|
49724
|
+
value: request.authorization.value,
|
|
49725
|
+
validAfter: request.authorization.validAfter,
|
|
49726
|
+
validBefore: request.authorization.validBefore,
|
|
49727
|
+
nonce: request.authorization.nonce
|
|
49728
|
+
},
|
|
49729
|
+
signature: request.signature
|
|
49730
|
+
}
|
|
49731
|
+
}
|
|
49732
|
+
},
|
|
49733
|
+
...receipt.error === void 0 ? {} : { error: { code: receipt.error.code, message: receipt.error.message } }
|
|
49734
|
+
});
|
|
49735
|
+
}
|
|
49736
|
+
async function readReceiptJournal(path) {
|
|
49737
|
+
let raw;
|
|
49738
|
+
try {
|
|
49739
|
+
raw = await readFile9(path, "utf8");
|
|
49740
|
+
} catch (error61) {
|
|
49741
|
+
if (isMissingFile3(error61)) return [];
|
|
49742
|
+
throw error61;
|
|
49743
|
+
}
|
|
49744
|
+
return raw.split("\n").filter((line) => line.trim().length > 0).map((line) => parseReceipt(JSON.parse(line)));
|
|
49745
|
+
}
|
|
49746
|
+
async function readRestoreReceiptJournal(path) {
|
|
49747
|
+
let raw;
|
|
49748
|
+
try {
|
|
49749
|
+
raw = await readFile9(path, "utf8");
|
|
49750
|
+
} catch (error61) {
|
|
49751
|
+
if (isMissingFile3(error61)) return { raw: "", lines: /* @__PURE__ */ new Set(), signedKeys: /* @__PURE__ */ new Set() };
|
|
49752
|
+
throw error61;
|
|
49753
|
+
}
|
|
49754
|
+
const lines = new Set(raw.split("\n"));
|
|
49755
|
+
const signedKeys = /* @__PURE__ */ new Set();
|
|
49756
|
+
for (const line of lines) {
|
|
49757
|
+
if (line.trim().length === 0) continue;
|
|
49758
|
+
try {
|
|
49759
|
+
const receipt = parseReceipt(JSON.parse(line));
|
|
49760
|
+
if (receipt.transfer !== void 0) {
|
|
49761
|
+
signedKeys.add(movementTransferKey(receiptWallet(receipt), receipt.transfer.nonce));
|
|
49762
|
+
}
|
|
49763
|
+
} catch {
|
|
49764
|
+
}
|
|
49765
|
+
}
|
|
49766
|
+
return { raw, lines, signedKeys };
|
|
49767
|
+
}
|
|
49768
|
+
function compareNamedValues(left, right) {
|
|
49769
|
+
return left.name < right.name ? -1 : left.name > right.name ? 1 : 0;
|
|
49770
|
+
}
|
|
49771
|
+
function compareMovements(left, right) {
|
|
49772
|
+
return left.createdAt.localeCompare(right.createdAt) || left.id.localeCompare(right.id);
|
|
49773
|
+
}
|
|
47314
49774
|
function parseEnvelope(envelope) {
|
|
47315
49775
|
if (typeof envelope !== "string") throw new BackupError("backup_unsupported");
|
|
47316
49776
|
if (Buffer.byteLength(envelope, "utf8") > MAX_BACKUP_BYTES) {
|
|
@@ -47489,8 +49949,8 @@ function assertOwnerKey(ownerKey) {
|
|
|
47489
49949
|
assertBytes(ownerKey.key, 32);
|
|
47490
49950
|
assertBytes(ownerKey.salt, 32);
|
|
47491
49951
|
}
|
|
47492
|
-
function readRandomBytes(
|
|
47493
|
-
const value =
|
|
49952
|
+
function readRandomBytes(randomBytes7, length) {
|
|
49953
|
+
const value = randomBytes7(length);
|
|
47494
49954
|
if (!(value instanceof Uint8Array) || value.byteLength !== length) {
|
|
47495
49955
|
throw new BackupError("backup_invalid_input");
|
|
47496
49956
|
}
|
|
@@ -47523,6 +49983,7 @@ function hasExactKeys(value, expected) {
|
|
|
47523
49983
|
export {
|
|
47524
49984
|
BackupError,
|
|
47525
49985
|
VAULT_BACKUP_TYPED_DATA,
|
|
49986
|
+
buildRelayRestorePayload,
|
|
47526
49987
|
createBackup,
|
|
47527
49988
|
createRelayKeyPair,
|
|
47528
49989
|
deriveKeyFromPassword,
|