@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/cloud-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);
|
|
@@ -4273,13 +4273,14 @@ var init_secp256k1 = __esm({
|
|
|
4273
4273
|
});
|
|
4274
4274
|
|
|
4275
4275
|
// src/cloud-backup.ts
|
|
4276
|
-
import { join as
|
|
4276
|
+
import { join as join9 } from "node:path";
|
|
4277
4277
|
|
|
4278
4278
|
// src/backup.ts
|
|
4279
4279
|
import {
|
|
4280
4280
|
createCipheriv as createCipheriv2,
|
|
4281
4281
|
createDecipheriv as createDecipheriv2,
|
|
4282
|
-
createHash,
|
|
4282
|
+
createHash as createHash2,
|
|
4283
|
+
createHmac,
|
|
4283
4284
|
createPrivateKey,
|
|
4284
4285
|
createPublicKey,
|
|
4285
4286
|
diffieHellman,
|
|
@@ -4288,8 +4289,18 @@ import {
|
|
|
4288
4289
|
scrypt as scrypt2,
|
|
4289
4290
|
timingSafeEqual
|
|
4290
4291
|
} from "node:crypto";
|
|
4291
|
-
import {
|
|
4292
|
-
|
|
4292
|
+
import {
|
|
4293
|
+
chmod as chmod2,
|
|
4294
|
+
link as link2,
|
|
4295
|
+
lstat,
|
|
4296
|
+
mkdir as mkdir8,
|
|
4297
|
+
mkdtemp,
|
|
4298
|
+
open as openFile,
|
|
4299
|
+
readFile as readFile9,
|
|
4300
|
+
readdir as readdir5,
|
|
4301
|
+
rm as rm2
|
|
4302
|
+
} from "node:fs/promises";
|
|
4303
|
+
import { dirname as dirname5, join as join8 } from "node:path";
|
|
4293
4304
|
|
|
4294
4305
|
// ../../node_modules/.pnpm/viem@2.56.1_typescript@6.0.3_zod@4.5.4/node_modules/viem/_esm/accounts/utils/publicKeyToAddress.js
|
|
4295
4306
|
init_getAddress();
|
|
@@ -5332,6 +5343,7 @@ function serializeSignature({ r, s, to = "hex", v, yParity }) {
|
|
|
5332
5343
|
|
|
5333
5344
|
// ../../node_modules/.pnpm/viem@2.56.1_typescript@6.0.3_zod@4.5.4/node_modules/viem/_esm/index.js
|
|
5334
5345
|
init_getAddress();
|
|
5346
|
+
init_isAddress();
|
|
5335
5347
|
|
|
5336
5348
|
// ../../node_modules/.pnpm/@scure+bip32@1.7.0/node_modules/@scure/bip32/lib/esm/index.js
|
|
5337
5349
|
init_modular();
|
|
@@ -45490,6 +45502,263 @@ function date4(params) {
|
|
|
45490
45502
|
return _coercedDate(ZodDate, params);
|
|
45491
45503
|
}
|
|
45492
45504
|
|
|
45505
|
+
// src/agent-profile.ts
|
|
45506
|
+
import { mkdir as mkdir3, readFile as readFile3, readdir as readdir2, rename as rename3, unlink as unlink2, writeFile as writeFile2 } from "node:fs/promises";
|
|
45507
|
+
import { join as join4 } from "node:path";
|
|
45508
|
+
|
|
45509
|
+
// src/config.ts
|
|
45510
|
+
import { constants } from "node:fs";
|
|
45511
|
+
import { copyFile, mkdir as mkdir2, readFile as readFile2, rename as rename2, stat as stat2, writeFile } from "node:fs/promises";
|
|
45512
|
+
import { homedir } from "node:os";
|
|
45513
|
+
import { dirname as dirname2, join as join3 } from "node:path";
|
|
45514
|
+
|
|
45515
|
+
// src/atomic-file.ts
|
|
45516
|
+
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
45517
|
+
import { link, mkdir, open as open2, readdir, readFile, rename, stat, unlink } from "node:fs/promises";
|
|
45518
|
+
import { basename, dirname, join as join2 } from "node:path";
|
|
45519
|
+
var DEFAULT_LOCK_TIMEOUT_MS = 2e3;
|
|
45520
|
+
var LOCK_RETRY_MS = 20;
|
|
45521
|
+
var LOCK_STALE_AFTER_MS = 12e4;
|
|
45522
|
+
var LOCK_HEARTBEAT_MS = 3e4;
|
|
45523
|
+
var atomicFileTestHooks;
|
|
45524
|
+
var FileLockedError = class extends Error {
|
|
45525
|
+
constructor(message = "Timed out waiting for the file lock.") {
|
|
45526
|
+
super(message);
|
|
45527
|
+
this.name = "FileLockedError";
|
|
45528
|
+
}
|
|
45529
|
+
};
|
|
45530
|
+
async function writeJsonAtomic(path, value, options = {}) {
|
|
45531
|
+
const directory = dirname(path);
|
|
45532
|
+
await mkdir(directory, { recursive: true, mode: 448 });
|
|
45533
|
+
const temporaryPath = join2(
|
|
45534
|
+
directory,
|
|
45535
|
+
`.${basename(path)}.${process.pid}.${randomBytes2(6).toString("hex")}.tmp`
|
|
45536
|
+
);
|
|
45537
|
+
let handle;
|
|
45538
|
+
try {
|
|
45539
|
+
handle = await open2(temporaryPath, "wx", options.mode ?? 384);
|
|
45540
|
+
await handle.writeFile(`${JSON.stringify(value, null, 2)}
|
|
45541
|
+
`, "utf8");
|
|
45542
|
+
await handle.sync();
|
|
45543
|
+
await handle.close();
|
|
45544
|
+
handle = void 0;
|
|
45545
|
+
await rename(temporaryPath, path);
|
|
45546
|
+
const directoryHandle = await open2(directory, "r");
|
|
45547
|
+
try {
|
|
45548
|
+
await directoryHandle.sync();
|
|
45549
|
+
} finally {
|
|
45550
|
+
await directoryHandle.close();
|
|
45551
|
+
}
|
|
45552
|
+
} catch (error61) {
|
|
45553
|
+
await handle?.close().catch(() => void 0);
|
|
45554
|
+
await unlink(temporaryPath).catch(() => void 0);
|
|
45555
|
+
throw error61;
|
|
45556
|
+
}
|
|
45557
|
+
}
|
|
45558
|
+
async function withFileLock(lockPath, fn, options = {}) {
|
|
45559
|
+
await mkdir(dirname(lockPath), { recursive: true, mode: 448 });
|
|
45560
|
+
const deadline = Date.now() + (options.timeoutMs ?? DEFAULT_LOCK_TIMEOUT_MS);
|
|
45561
|
+
const ownerRecord = {
|
|
45562
|
+
pid: process.pid,
|
|
45563
|
+
token: randomBytes2(16).toString("hex")
|
|
45564
|
+
};
|
|
45565
|
+
const owner = JSON.stringify(ownerRecord);
|
|
45566
|
+
const releasedOwner = JSON.stringify({ ...ownerRecord, released: true });
|
|
45567
|
+
let handle;
|
|
45568
|
+
while (handle === void 0) {
|
|
45569
|
+
await atomicFileTestHooks?.beforeLockAttempt?.(lockPath);
|
|
45570
|
+
try {
|
|
45571
|
+
const candidate = await open2(lockPath, "wx", 384);
|
|
45572
|
+
try {
|
|
45573
|
+
await candidate.writeFile(owner, "utf8");
|
|
45574
|
+
await candidate.sync();
|
|
45575
|
+
await atomicFileTestHooks?.afterLockCreate?.(lockPath);
|
|
45576
|
+
if (await lockTakeoverInProgress(lockPath)) {
|
|
45577
|
+
await candidate.close();
|
|
45578
|
+
await removeOwnedLock(lockPath, [owner]);
|
|
45579
|
+
await removeAbandonedLockTombs(lockPath);
|
|
45580
|
+
if (Date.now() >= deadline) {
|
|
45581
|
+
throw new FileLockedError(options.lockedMessage);
|
|
45582
|
+
}
|
|
45583
|
+
await new Promise((resolve) => setTimeout(resolve, LOCK_RETRY_MS));
|
|
45584
|
+
continue;
|
|
45585
|
+
}
|
|
45586
|
+
handle = candidate;
|
|
45587
|
+
} catch (error61) {
|
|
45588
|
+
await candidate.close().catch(() => void 0);
|
|
45589
|
+
await removeOwnedLock(lockPath, [owner]).catch(() => void 0);
|
|
45590
|
+
throw error61;
|
|
45591
|
+
}
|
|
45592
|
+
} catch (error61) {
|
|
45593
|
+
if (!hasCode(error61, "EEXIST")) throw error61;
|
|
45594
|
+
await removeStaleLock(lockPath);
|
|
45595
|
+
if (Date.now() >= deadline) {
|
|
45596
|
+
throw new FileLockedError(options.lockedMessage);
|
|
45597
|
+
}
|
|
45598
|
+
await new Promise((resolve) => setTimeout(resolve, LOCK_RETRY_MS));
|
|
45599
|
+
}
|
|
45600
|
+
}
|
|
45601
|
+
const ownedHandle = handle;
|
|
45602
|
+
const heartbeat = setInterval(() => {
|
|
45603
|
+
const time3 = /* @__PURE__ */ new Date();
|
|
45604
|
+
void ownedHandle.utimes(time3, time3).catch(() => void 0);
|
|
45605
|
+
}, LOCK_HEARTBEAT_MS);
|
|
45606
|
+
heartbeat.unref();
|
|
45607
|
+
try {
|
|
45608
|
+
return await fn();
|
|
45609
|
+
} finally {
|
|
45610
|
+
clearInterval(heartbeat);
|
|
45611
|
+
try {
|
|
45612
|
+
await markLockReleased(ownedHandle, releasedOwner);
|
|
45613
|
+
} finally {
|
|
45614
|
+
try {
|
|
45615
|
+
await ownedHandle.close();
|
|
45616
|
+
} finally {
|
|
45617
|
+
await removeOwnedLock(lockPath, [owner, releasedOwner]);
|
|
45618
|
+
}
|
|
45619
|
+
}
|
|
45620
|
+
}
|
|
45621
|
+
}
|
|
45622
|
+
async function removeStaleLock(path) {
|
|
45623
|
+
try {
|
|
45624
|
+
const owner = await readFile(path, "utf8");
|
|
45625
|
+
if (lockOwnerReleased(owner)) {
|
|
45626
|
+
await removeOwnedLock(path, [owner]);
|
|
45627
|
+
return;
|
|
45628
|
+
}
|
|
45629
|
+
const metadata = await stat(path);
|
|
45630
|
+
if (Date.now() - metadata.mtimeMs <= LOCK_STALE_AFTER_MS) return;
|
|
45631
|
+
const pid = lockOwnerPid(owner);
|
|
45632
|
+
if (pid !== void 0 && processIsAlive(pid)) return;
|
|
45633
|
+
await atomicFileTestHooks?.beforeStaleLockBreak?.(path);
|
|
45634
|
+
await removeOwnedLock(path, [owner]);
|
|
45635
|
+
} catch (error61) {
|
|
45636
|
+
if (!hasCode(error61, "ENOENT")) throw error61;
|
|
45637
|
+
}
|
|
45638
|
+
}
|
|
45639
|
+
async function removeOwnedLock(path, owners) {
|
|
45640
|
+
const tombPath = `${path}.stale.${randomBytes2(16).toString("hex")}`;
|
|
45641
|
+
try {
|
|
45642
|
+
await rename(path, tombPath);
|
|
45643
|
+
} catch (error61) {
|
|
45644
|
+
if (hasCode(error61, "ENOENT")) {
|
|
45645
|
+
await removeOwnedTombs(path, owners);
|
|
45646
|
+
const currentOwner = await readFile(path, "utf8").catch(() => void 0);
|
|
45647
|
+
if (currentOwner !== void 0 && owners.includes(currentOwner)) {
|
|
45648
|
+
await removeOwnedLock(path, owners);
|
|
45649
|
+
}
|
|
45650
|
+
return;
|
|
45651
|
+
}
|
|
45652
|
+
throw error61;
|
|
45653
|
+
}
|
|
45654
|
+
let pendingError;
|
|
45655
|
+
try {
|
|
45656
|
+
const renamedOwner = await readFile(tombPath, "utf8");
|
|
45657
|
+
if (!owners.includes(renamedOwner) && !lockOwnerReleased(renamedOwner)) {
|
|
45658
|
+
await atomicFileTestHooks?.afterMismatchedLockRename?.(path);
|
|
45659
|
+
const currentOwner = await readFile(tombPath, "utf8");
|
|
45660
|
+
if (!lockOwnerReleased(currentOwner)) await restoreRenamedLock(path, tombPath);
|
|
45661
|
+
await atomicFileTestHooks?.afterMismatchedLockRestore?.(path);
|
|
45662
|
+
}
|
|
45663
|
+
} catch (error61) {
|
|
45664
|
+
pendingError = error61;
|
|
45665
|
+
}
|
|
45666
|
+
try {
|
|
45667
|
+
await unlink(tombPath);
|
|
45668
|
+
} catch (error61) {
|
|
45669
|
+
if (!hasCode(error61, "ENOENT") && pendingError === void 0) {
|
|
45670
|
+
pendingError = error61;
|
|
45671
|
+
}
|
|
45672
|
+
}
|
|
45673
|
+
if (pendingError !== void 0) throw pendingError;
|
|
45674
|
+
}
|
|
45675
|
+
async function lockTakeoverInProgress(path) {
|
|
45676
|
+
const prefix = `${basename(path)}.stale.`;
|
|
45677
|
+
return (await readdir(dirname(path))).some((entry) => entry.startsWith(prefix));
|
|
45678
|
+
}
|
|
45679
|
+
async function removeAbandonedLockTombs(path) {
|
|
45680
|
+
const directory = dirname(path);
|
|
45681
|
+
const prefix = `${basename(path)}.stale.`;
|
|
45682
|
+
for (const entry of await readdir(directory)) {
|
|
45683
|
+
if (!entry.startsWith(prefix)) continue;
|
|
45684
|
+
const tombPath = join2(directory, entry);
|
|
45685
|
+
const [metadata, owner] = await Promise.all([
|
|
45686
|
+
stat(tombPath).catch(() => void 0),
|
|
45687
|
+
readFile(tombPath, "utf8").catch(() => void 0)
|
|
45688
|
+
]);
|
|
45689
|
+
if (metadata === void 0 || owner === void 0) continue;
|
|
45690
|
+
const pid = lockOwnerPid(owner);
|
|
45691
|
+
const abandoned = lockOwnerReleased(owner) || Date.now() - metadata.mtimeMs > LOCK_STALE_AFTER_MS && (pid === void 0 || !processIsAlive(pid));
|
|
45692
|
+
if (!abandoned) continue;
|
|
45693
|
+
await unlink(tombPath).catch((error61) => {
|
|
45694
|
+
if (!hasCode(error61, "ENOENT")) throw error61;
|
|
45695
|
+
});
|
|
45696
|
+
}
|
|
45697
|
+
}
|
|
45698
|
+
async function removeOwnedTombs(path, owners) {
|
|
45699
|
+
const directory = dirname(path);
|
|
45700
|
+
const prefix = `${basename(path)}.stale.`;
|
|
45701
|
+
for (const entry of await readdir(directory)) {
|
|
45702
|
+
if (!entry.startsWith(prefix)) continue;
|
|
45703
|
+
const tombPath = join2(directory, entry);
|
|
45704
|
+
const owner = await readFile(tombPath, "utf8").catch(() => void 0);
|
|
45705
|
+
if (owner === void 0 || !owners.includes(owner)) continue;
|
|
45706
|
+
await unlink(tombPath).catch((error61) => {
|
|
45707
|
+
if (!hasCode(error61, "ENOENT")) throw error61;
|
|
45708
|
+
});
|
|
45709
|
+
}
|
|
45710
|
+
}
|
|
45711
|
+
async function restoreRenamedLock(path, tombPath) {
|
|
45712
|
+
while (true) {
|
|
45713
|
+
try {
|
|
45714
|
+
await link(tombPath, path);
|
|
45715
|
+
return;
|
|
45716
|
+
} catch (error61) {
|
|
45717
|
+
if (hasCode(error61, "ENOENT")) return;
|
|
45718
|
+
if (!hasCode(error61, "EEXIST")) throw error61;
|
|
45719
|
+
await new Promise((resolve) => setTimeout(resolve, LOCK_RETRY_MS));
|
|
45720
|
+
}
|
|
45721
|
+
}
|
|
45722
|
+
}
|
|
45723
|
+
async function markLockReleased(handle, owner) {
|
|
45724
|
+
const contents = Buffer.from(owner, "utf8");
|
|
45725
|
+
await handle.write(contents, 0, contents.length, 0);
|
|
45726
|
+
await handle.truncate(contents.length);
|
|
45727
|
+
await handle.sync();
|
|
45728
|
+
}
|
|
45729
|
+
function lockOwnerPid(owner) {
|
|
45730
|
+
try {
|
|
45731
|
+
const value = JSON.parse(owner);
|
|
45732
|
+
if (typeof value !== "object" || value === null) return void 0;
|
|
45733
|
+
const pid = Reflect.get(value, "pid");
|
|
45734
|
+
return typeof pid === "number" && Number.isSafeInteger(pid) && pid > 0 ? pid : void 0;
|
|
45735
|
+
} catch {
|
|
45736
|
+
return void 0;
|
|
45737
|
+
}
|
|
45738
|
+
}
|
|
45739
|
+
function lockOwnerReleased(owner) {
|
|
45740
|
+
try {
|
|
45741
|
+
const value = JSON.parse(owner);
|
|
45742
|
+
return typeof value === "object" && value !== null && Reflect.get(value, "released") === true;
|
|
45743
|
+
} catch {
|
|
45744
|
+
return false;
|
|
45745
|
+
}
|
|
45746
|
+
}
|
|
45747
|
+
function processIsAlive(pid) {
|
|
45748
|
+
if (atomicFileTestHooks?.isProcessAlive !== void 0) {
|
|
45749
|
+
return atomicFileTestHooks.isProcessAlive(pid);
|
|
45750
|
+
}
|
|
45751
|
+
try {
|
|
45752
|
+
process.kill(pid, 0);
|
|
45753
|
+
return true;
|
|
45754
|
+
} catch (error61) {
|
|
45755
|
+
return hasCode(error61, "EPERM");
|
|
45756
|
+
}
|
|
45757
|
+
}
|
|
45758
|
+
function hasCode(error61, code) {
|
|
45759
|
+
return error61 instanceof Error && "code" in error61 && error61.code === code;
|
|
45760
|
+
}
|
|
45761
|
+
|
|
45493
45762
|
// src/x402-networks.ts
|
|
45494
45763
|
var BASE_MAINNET_CAIP2 = "eip155:8453";
|
|
45495
45764
|
var ARC_MAINNET_CAIP2 = "eip155:5042";
|
|
@@ -45793,6 +46062,14 @@ function parseEip155ChainId(network) {
|
|
|
45793
46062
|
function isSolanaNetwork(network) {
|
|
45794
46063
|
return network === SOLANA_MAINNET_CAIP2 || network === X402_SOLANA_MAINNET_CAIP2;
|
|
45795
46064
|
}
|
|
46065
|
+
function explorerTransactionUrl(network, hash2) {
|
|
46066
|
+
try {
|
|
46067
|
+
const explorerUrl = getNetworkDefinition(network).explorerUrl;
|
|
46068
|
+
return explorerUrl ? `${explorerUrl}/tx/${hash2}` : void 0;
|
|
46069
|
+
} catch {
|
|
46070
|
+
return void 0;
|
|
46071
|
+
}
|
|
46072
|
+
}
|
|
45796
46073
|
function isSupportedPaymentNetwork(network) {
|
|
45797
46074
|
if (isSolanaNetwork(network)) return true;
|
|
45798
46075
|
try {
|
|
@@ -45802,15 +46079,39 @@ function isSupportedPaymentNetwork(network) {
|
|
|
45802
46079
|
return false;
|
|
45803
46080
|
}
|
|
45804
46081
|
}
|
|
46082
|
+
function getNetworkDefinition(network) {
|
|
46083
|
+
const known = NETWORKS[network];
|
|
46084
|
+
if (known) {
|
|
46085
|
+
return known;
|
|
46086
|
+
}
|
|
46087
|
+
const chainId = parseEip155ChainId(network);
|
|
46088
|
+
return {
|
|
46089
|
+
family: "evm",
|
|
46090
|
+
chainId,
|
|
46091
|
+
name: network,
|
|
46092
|
+
usdc: getAddress("0x0000000000000000000000000000000000000000"),
|
|
46093
|
+
gasToken: "ETH",
|
|
46094
|
+
rpcEnv: ""
|
|
46095
|
+
};
|
|
46096
|
+
}
|
|
45805
46097
|
|
|
45806
46098
|
// src/config.ts
|
|
45807
46099
|
var DEFAULT_REGISTRY_URL = "https://api.vapinetwork.ai";
|
|
45808
46100
|
var DEFAULT_DISCOVERY_URL = `${DEFAULT_REGISTRY_URL}/api/call/services`;
|
|
45809
46101
|
var DEFAULT_MARKETPLACE_DISCOVERY_URL = `${DEFAULT_REGISTRY_URL}/api/call/discovery`;
|
|
46102
|
+
var DEFAULT_REGISTRY_FALLBACKS = [
|
|
46103
|
+
{
|
|
46104
|
+
discoveryUrl: DEFAULT_DISCOVERY_URL,
|
|
46105
|
+
marketplaceDiscoveryUrl: DEFAULT_MARKETPLACE_DISCOVERY_URL
|
|
46106
|
+
}
|
|
46107
|
+
];
|
|
45810
46108
|
var DEFAULT_SPEND_CAPS = {
|
|
45811
46109
|
perCallAtomic: "100000",
|
|
45812
46110
|
perDayAtomic: "1000000"
|
|
45813
46111
|
};
|
|
46112
|
+
var DEFAULT_MAX_PER_TASK_USD = 100;
|
|
46113
|
+
var DEFAULT_AUTO_RELEASE_BELOW_USD = 25;
|
|
46114
|
+
var DEFAULT_CEILING_ATOMIC = 5000000n;
|
|
45814
46115
|
var atomicString = external_exports.string().regex(/^\d+$/, "Expected non-negative atomic units.");
|
|
45815
46116
|
var spendCapsSchema = external_exports.object({
|
|
45816
46117
|
perCallAtomic: atomicString,
|
|
@@ -45826,6 +46127,14 @@ var configSchema = external_exports.object({
|
|
|
45826
46127
|
})
|
|
45827
46128
|
).optional(),
|
|
45828
46129
|
allowPrivateNetwork: external_exports.boolean().default(false).optional(),
|
|
46130
|
+
tasksEscrowDurationOverrides: external_exports.record(
|
|
46131
|
+
external_exports.string().regex(/^\d+$/),
|
|
46132
|
+
external_exports.object({
|
|
46133
|
+
workDurationSeconds: external_exports.number().int().positive().max(Number.MAX_SAFE_INTEGER).optional(),
|
|
46134
|
+
reviewWindowSeconds: external_exports.number().int().positive().max(Number.MAX_SAFE_INTEGER).optional()
|
|
46135
|
+
}).strict()
|
|
46136
|
+
).optional(),
|
|
46137
|
+
tasksEscrowFactoryOverrides: external_exports.record(external_exports.string().regex(/^\d+$/), external_exports.string()).optional(),
|
|
45829
46138
|
networks: external_exports.record(
|
|
45830
46139
|
external_exports.string().refine((network) => isSupportedPaymentNetwork(network), {
|
|
45831
46140
|
message: "Expected a supported eip155:<chainId> or Solana network identifier."
|
|
@@ -45844,6 +46153,17 @@ var configSchema = external_exports.object({
|
|
|
45844
46153
|
spendCaps: spendCapsSchema,
|
|
45845
46154
|
device: external_exports.string().regex(/^[a-z0-9][a-z0-9-]{0,31}$/).optional()
|
|
45846
46155
|
}).superRefine((config2, context) => {
|
|
46156
|
+
for (const [chainId, factory] of Object.entries(config2.tasksEscrowFactoryOverrides ?? {})) {
|
|
46157
|
+
try {
|
|
46158
|
+
getAddress(factory);
|
|
46159
|
+
} catch {
|
|
46160
|
+
context.addIssue({
|
|
46161
|
+
code: "custom",
|
|
46162
|
+
path: ["tasksEscrowFactoryOverrides", chainId],
|
|
46163
|
+
message: "Expected a valid EVM escrow factory address."
|
|
46164
|
+
});
|
|
46165
|
+
}
|
|
46166
|
+
}
|
|
45847
46167
|
for (const [network, configured] of Object.entries(config2.networks)) {
|
|
45848
46168
|
if (isSolanaNetwork(network)) {
|
|
45849
46169
|
if (configured.usdc !== NETWORKS[SOLANA_MAINNET_CAIP2].usdc) {
|
|
@@ -45873,9 +46193,379 @@ var configSchema = external_exports.object({
|
|
|
45873
46193
|
}
|
|
45874
46194
|
}
|
|
45875
46195
|
});
|
|
46196
|
+
function getVapiPaths(directory = process.env.VAPI_HOME?.trim() || join3(homedir(), ".vapi")) {
|
|
46197
|
+
return {
|
|
46198
|
+
directory,
|
|
46199
|
+
agentsDir: join3(directory, "agents"),
|
|
46200
|
+
config: join3(directory, "config.json"),
|
|
46201
|
+
keystore: join3(directory, "keystore.json"),
|
|
46202
|
+
receipts: join3(directory, "receipts.jsonl"),
|
|
46203
|
+
searches: join3(directory, "searches.jsonl"),
|
|
46204
|
+
ledger: join3(directory, "spend-ledger.json")
|
|
46205
|
+
};
|
|
46206
|
+
}
|
|
46207
|
+
async function migrateLegacyVapiHome(options = {}) {
|
|
46208
|
+
const target = getVapiPaths(options.targetDirectory);
|
|
46209
|
+
const legacyDirectory = options.legacyDirectory ?? join3(homedir(), ".vapi", "agent-cash");
|
|
46210
|
+
if (legacyDirectory === target.directory || !await pathExists(legacyDirectory)) return [];
|
|
46211
|
+
await mkdir2(target.directory, { recursive: true, mode: 448 });
|
|
46212
|
+
const copied = [];
|
|
46213
|
+
for (const filename of [
|
|
46214
|
+
"config.json",
|
|
46215
|
+
"keystore.json",
|
|
46216
|
+
"receipts.jsonl",
|
|
46217
|
+
"searches.jsonl",
|
|
46218
|
+
"spend-ledger.json"
|
|
46219
|
+
]) {
|
|
46220
|
+
const source = join3(legacyDirectory, filename);
|
|
46221
|
+
const destination = join3(target.directory, filename);
|
|
46222
|
+
try {
|
|
46223
|
+
const copy = async () => await copyFile(source, destination, constants.COPYFILE_EXCL);
|
|
46224
|
+
if (filename === "receipts.jsonl") {
|
|
46225
|
+
await withFileLock(`${destination}.lock`, copy, {
|
|
46226
|
+
lockedMessage: "The receipt journal is busy. Wait for the other vapi command to finish, then retry."
|
|
46227
|
+
});
|
|
46228
|
+
} else {
|
|
46229
|
+
await copy();
|
|
46230
|
+
}
|
|
46231
|
+
copied.push(destination);
|
|
46232
|
+
} catch (error61) {
|
|
46233
|
+
if (isMissingFile(error61) || isAlreadyExists(error61)) continue;
|
|
46234
|
+
throw error61;
|
|
46235
|
+
}
|
|
46236
|
+
}
|
|
46237
|
+
if (copied.length > 0) {
|
|
46238
|
+
const message = `Migrated legacy vAPI wallet files from ${legacyDirectory} to ${target.directory}; originals were kept.`;
|
|
46239
|
+
(options.notice ?? ((value) => process.stderr.write(`${value}
|
|
46240
|
+
`)))(message);
|
|
46241
|
+
}
|
|
46242
|
+
return copied;
|
|
46243
|
+
}
|
|
46244
|
+
var LEGACY_REGISTRY_HOSTS = /* @__PURE__ */ new Map([
|
|
46245
|
+
["console.vapinetwork.ai", "api.vapinetwork.ai"],
|
|
46246
|
+
["console-staging.vapinetwork.ai", "api-staging.vapinetwork.ai"]
|
|
46247
|
+
]);
|
|
46248
|
+
var LEGACY_REGISTRY_PATHS = /* @__PURE__ */ new Map([
|
|
46249
|
+
["/api/network/services", "/api/call/services"],
|
|
46250
|
+
["/api/marketplace/discovery", "/api/call/discovery"]
|
|
46251
|
+
]);
|
|
46252
|
+
var REGISTRY_DOMAIN = "vapinetwork.ai";
|
|
46253
|
+
function rewriteLegacyRegistryUrl(value) {
|
|
46254
|
+
let url2;
|
|
46255
|
+
try {
|
|
46256
|
+
url2 = new URL(value);
|
|
46257
|
+
} catch {
|
|
46258
|
+
return value;
|
|
46259
|
+
}
|
|
46260
|
+
let rewritten = false;
|
|
46261
|
+
const host = LEGACY_REGISTRY_HOSTS.get(url2.hostname.toLowerCase());
|
|
46262
|
+
if (host) {
|
|
46263
|
+
url2.hostname = host;
|
|
46264
|
+
rewritten = true;
|
|
46265
|
+
}
|
|
46266
|
+
const hostname3 = url2.hostname.toLowerCase();
|
|
46267
|
+
if (hostname3 === REGISTRY_DOMAIN || hostname3.endsWith(`.${REGISTRY_DOMAIN}`)) {
|
|
46268
|
+
const path = LEGACY_REGISTRY_PATHS.get(url2.pathname.replace(/\/+$/, ""));
|
|
46269
|
+
if (path) {
|
|
46270
|
+
url2.pathname = path;
|
|
46271
|
+
rewritten = true;
|
|
46272
|
+
}
|
|
46273
|
+
}
|
|
46274
|
+
return rewritten ? url2.href : value;
|
|
46275
|
+
}
|
|
46276
|
+
function rewriteLegacyRegistryUrls(config2) {
|
|
46277
|
+
const changes = [];
|
|
46278
|
+
const rewrite = (field, from) => {
|
|
46279
|
+
const to = rewriteLegacyRegistryUrl(from);
|
|
46280
|
+
if (to !== from) changes.push({ field, from, to });
|
|
46281
|
+
return to;
|
|
46282
|
+
};
|
|
46283
|
+
const next = {
|
|
46284
|
+
...config2,
|
|
46285
|
+
discoveryUrl: rewrite("discoveryUrl", config2.discoveryUrl),
|
|
46286
|
+
marketplaceDiscoveryUrl: rewrite("marketplaceDiscoveryUrl", config2.marketplaceDiscoveryUrl),
|
|
46287
|
+
...config2.registryFallbacks ? {
|
|
46288
|
+
registryFallbacks: config2.registryFallbacks.map((fallback, index) => ({
|
|
46289
|
+
discoveryUrl: rewrite(
|
|
46290
|
+
`registryFallbacks[${index}].discoveryUrl`,
|
|
46291
|
+
fallback.discoveryUrl
|
|
46292
|
+
),
|
|
46293
|
+
marketplaceDiscoveryUrl: rewrite(
|
|
46294
|
+
`registryFallbacks[${index}].marketplaceDiscoveryUrl`,
|
|
46295
|
+
fallback.marketplaceDiscoveryUrl
|
|
46296
|
+
)
|
|
46297
|
+
}))
|
|
46298
|
+
} : {}
|
|
46299
|
+
};
|
|
46300
|
+
return changes.length === 0 ? { config: config2, changes } : { config: next, changes };
|
|
46301
|
+
}
|
|
46302
|
+
function formatLegacyRegistryRewrites(changes) {
|
|
46303
|
+
return [
|
|
46304
|
+
"Rewrote retired vAPI registry URLs in the local config:",
|
|
46305
|
+
...changes.map((change) => ` ${change.field}: ${change.from} -> ${change.to}`)
|
|
46306
|
+
].join("\n");
|
|
46307
|
+
}
|
|
46308
|
+
function getDefaultConfig(source = process.env, options = {}) {
|
|
46309
|
+
const registry2 = registryEndpoints(source.VAPI_REGISTRY_URL?.trim() || DEFAULT_REGISTRY_URL);
|
|
46310
|
+
const requested = new Set(options.networks ?? ["base"]);
|
|
46311
|
+
const networks = {};
|
|
46312
|
+
const tasksEscrowFactoryOverrides = tasksFactoryOverridesFromEnv(source);
|
|
46313
|
+
if (requested.has("base") || requested.has(BASE_MAINNET_CAIP2)) {
|
|
46314
|
+
networks[BASE_MAINNET_CAIP2] = {
|
|
46315
|
+
rpcUrl: source.BASE_RPC_URL?.trim() || NETWORKS[BASE_MAINNET_CAIP2].publicRpcUrl,
|
|
46316
|
+
usdc: NETWORKS[BASE_MAINNET_CAIP2].usdc
|
|
46317
|
+
};
|
|
46318
|
+
}
|
|
46319
|
+
const arcMainnetRpcUrl = source.ARC_RPC_URL?.trim();
|
|
46320
|
+
if (arcMainnetRpcUrl || requested.has("arc") || requested.has(ARC_MAINNET_CAIP2)) {
|
|
46321
|
+
networks[ARC_MAINNET_CAIP2] = {
|
|
46322
|
+
rpcUrl: arcMainnetRpcUrl || NETWORKS[ARC_MAINNET_CAIP2].publicRpcUrl,
|
|
46323
|
+
usdc: NETWORKS[ARC_MAINNET_CAIP2].usdc
|
|
46324
|
+
};
|
|
46325
|
+
}
|
|
46326
|
+
const arcTestnetRpcUrl = source.ARC_TESTNET_RPC_URL?.trim();
|
|
46327
|
+
if (arcTestnetRpcUrl || requested.has("arc-testnet") || requested.has(ARC_TESTNET_CAIP2)) {
|
|
46328
|
+
if (!arcTestnetRpcUrl) {
|
|
46329
|
+
throw new Error(
|
|
46330
|
+
"Arc testnet RPC is required. Set ARC_TESTNET_RPC_URL before enabling Arc testnet."
|
|
46331
|
+
);
|
|
46332
|
+
}
|
|
46333
|
+
networks[ARC_TESTNET_CAIP2] = {
|
|
46334
|
+
rpcUrl: arcTestnetRpcUrl,
|
|
46335
|
+
usdc: NETWORKS[ARC_TESTNET_CAIP2].usdc
|
|
46336
|
+
};
|
|
46337
|
+
}
|
|
46338
|
+
const solanaRpcUrl = source.SOLANA_RPC_URL?.trim();
|
|
46339
|
+
if (solanaRpcUrl || requested.has("solana") || requested.has(SOLANA_MAINNET_CAIP2)) {
|
|
46340
|
+
networks[SOLANA_MAINNET_CAIP2] = {
|
|
46341
|
+
rpcUrl: solanaRpcUrl || NETWORKS[SOLANA_MAINNET_CAIP2].publicRpcUrl,
|
|
46342
|
+
usdc: NETWORKS[SOLANA_MAINNET_CAIP2].usdc
|
|
46343
|
+
};
|
|
46344
|
+
}
|
|
46345
|
+
return {
|
|
46346
|
+
discoveryUrl: source.VAPI_DISCOVERY_URL?.trim() || registry2.discoveryUrl,
|
|
46347
|
+
marketplaceDiscoveryUrl: source.VAPI_MARKETPLACE_DISCOVERY_URL?.trim() || registry2.marketplaceDiscoveryUrl,
|
|
46348
|
+
registryFallbacks: DEFAULT_REGISTRY_FALLBACKS.map((fallback) => ({ ...fallback })),
|
|
46349
|
+
allowPrivateNetwork: false,
|
|
46350
|
+
tasksEscrowFactoryOverrides,
|
|
46351
|
+
tasksEscrowDurationOverrides: tasksDurationOverridesFromEnv(source),
|
|
46352
|
+
networks,
|
|
46353
|
+
spendCaps: { ...DEFAULT_SPEND_CAPS }
|
|
46354
|
+
};
|
|
46355
|
+
}
|
|
46356
|
+
async function loadConfig(path = getVapiPaths().config, source = process.env, options = {}) {
|
|
46357
|
+
if (!process.env.VAPI_HOME?.trim() && path === getVapiPaths(join3(homedir(), ".vapi")).config) {
|
|
46358
|
+
await migrateLegacyVapiHome({ targetDirectory: join3(homedir(), ".vapi") });
|
|
46359
|
+
}
|
|
46360
|
+
let raw;
|
|
46361
|
+
try {
|
|
46362
|
+
raw = await readFile2(path, "utf8");
|
|
46363
|
+
} catch (error61) {
|
|
46364
|
+
if (isMissingFile(error61)) return getDefaultConfig(source);
|
|
46365
|
+
throw error61;
|
|
46366
|
+
}
|
|
46367
|
+
const parsed = configSchema.parse(JSON.parse(raw));
|
|
46368
|
+
parsed.registryFallbacks ??= DEFAULT_REGISTRY_FALLBACKS.map((fallback) => ({ ...fallback }));
|
|
46369
|
+
parsed.tasksEscrowFactoryOverrides ??= {};
|
|
46370
|
+
const migrated = rewriteLegacyRegistryUrls(parsed);
|
|
46371
|
+
const config2 = migrated.config;
|
|
46372
|
+
config2.tasksEscrowFactoryOverrides ??= {};
|
|
46373
|
+
if (migrated.changes.length > 0 && options.notice) {
|
|
46374
|
+
options.notice(
|
|
46375
|
+
`${formatLegacyRegistryRewrites(migrated.changes)}
|
|
46376
|
+
Run vapi init to write the new URLs to ${path}.`
|
|
46377
|
+
);
|
|
46378
|
+
}
|
|
46379
|
+
const baseRpcUrl = source.BASE_RPC_URL?.trim();
|
|
46380
|
+
const arcMainnetRpcUrl = source.ARC_RPC_URL?.trim();
|
|
46381
|
+
const arcTestnetRpcUrl = source.ARC_TESTNET_RPC_URL?.trim();
|
|
46382
|
+
const solanaRpcUrl = source.SOLANA_RPC_URL?.trim();
|
|
46383
|
+
const discoveryUrl = source.VAPI_DISCOVERY_URL?.trim();
|
|
46384
|
+
const marketplaceDiscoveryUrl = source.VAPI_MARKETPLACE_DISCOVERY_URL?.trim();
|
|
46385
|
+
const registryUrl = source.VAPI_REGISTRY_URL?.trim();
|
|
46386
|
+
Object.assign(config2.tasksEscrowFactoryOverrides, tasksFactoryOverridesFromEnv(source));
|
|
46387
|
+
config2.tasksEscrowDurationOverrides ??= {};
|
|
46388
|
+
for (const [chainId, overrides] of Object.entries(tasksDurationOverridesFromEnv(source))) {
|
|
46389
|
+
config2.tasksEscrowDurationOverrides[chainId] = {
|
|
46390
|
+
...config2.tasksEscrowDurationOverrides[chainId],
|
|
46391
|
+
...overrides
|
|
46392
|
+
};
|
|
46393
|
+
}
|
|
46394
|
+
if (config2.networks[BASE_MAINNET_CAIP2] && baseRpcUrl) {
|
|
46395
|
+
config2.networks[BASE_MAINNET_CAIP2].rpcUrl = baseRpcUrl;
|
|
46396
|
+
}
|
|
46397
|
+
if (arcMainnetRpcUrl) {
|
|
46398
|
+
const existing = config2.networks[ARC_MAINNET_CAIP2];
|
|
46399
|
+
config2.networks[ARC_MAINNET_CAIP2] = {
|
|
46400
|
+
...existing ?? { usdc: NETWORKS[ARC_MAINNET_CAIP2].usdc },
|
|
46401
|
+
rpcUrl: arcMainnetRpcUrl
|
|
46402
|
+
};
|
|
46403
|
+
}
|
|
46404
|
+
if (arcTestnetRpcUrl) {
|
|
46405
|
+
const existing = config2.networks[ARC_TESTNET_CAIP2];
|
|
46406
|
+
config2.networks[ARC_TESTNET_CAIP2] = {
|
|
46407
|
+
...existing ?? { usdc: NETWORKS[ARC_TESTNET_CAIP2].usdc },
|
|
46408
|
+
rpcUrl: arcTestnetRpcUrl
|
|
46409
|
+
};
|
|
46410
|
+
}
|
|
46411
|
+
if (solanaRpcUrl) {
|
|
46412
|
+
config2.networks[SOLANA_MAINNET_CAIP2] = {
|
|
46413
|
+
rpcUrl: solanaRpcUrl,
|
|
46414
|
+
usdc: config2.networks[SOLANA_MAINNET_CAIP2]?.usdc ?? NETWORKS[SOLANA_MAINNET_CAIP2].usdc
|
|
46415
|
+
};
|
|
46416
|
+
}
|
|
46417
|
+
if (registryUrl) {
|
|
46418
|
+
const registry2 = registryEndpoints(registryUrl);
|
|
46419
|
+
config2.discoveryUrl = registry2.discoveryUrl;
|
|
46420
|
+
config2.marketplaceDiscoveryUrl = registry2.marketplaceDiscoveryUrl;
|
|
46421
|
+
}
|
|
46422
|
+
if (discoveryUrl) config2.discoveryUrl = discoveryUrl;
|
|
46423
|
+
if (marketplaceDiscoveryUrl) config2.marketplaceDiscoveryUrl = marketplaceDiscoveryUrl;
|
|
46424
|
+
for (const [network, configured] of Object.entries(config2.networks)) {
|
|
46425
|
+
configured.rpcUrl = configured.rpcUrl.trim();
|
|
46426
|
+
configured.usdc = configured.usdc.trim();
|
|
46427
|
+
if (isSolanaNetwork(network) && configured.usdc !== NETWORKS[SOLANA_MAINNET_CAIP2].usdc) {
|
|
46428
|
+
throw new Error(`Network ${network} must use canonical Solana mainnet USDC.`);
|
|
46429
|
+
}
|
|
46430
|
+
if (!configured.rpcUrl) delete config2.networks[network];
|
|
46431
|
+
}
|
|
46432
|
+
return config2;
|
|
46433
|
+
}
|
|
46434
|
+
function tasksDurationOverridesFromEnv(source) {
|
|
46435
|
+
const result = {};
|
|
46436
|
+
for (const [name, raw] of Object.entries(source)) {
|
|
46437
|
+
const match = /^VAPI_TASKS_(WORK_DURATION|REVIEW_WINDOW)_SECONDS_(\d+)$/.exec(name);
|
|
46438
|
+
const value = raw?.trim();
|
|
46439
|
+
if (!match || !value) continue;
|
|
46440
|
+
const seconds = Number(value);
|
|
46441
|
+
if (!/^\d+$/.test(value) || !Number.isSafeInteger(seconds) || seconds <= 0)
|
|
46442
|
+
throw new Error(`Invalid trusted Tasks duration ${name}.`);
|
|
46443
|
+
const entry = result[match[2]] ??= {};
|
|
46444
|
+
if (match[1] === "WORK_DURATION") entry.workDurationSeconds = seconds;
|
|
46445
|
+
else entry.reviewWindowSeconds = seconds;
|
|
46446
|
+
}
|
|
46447
|
+
return result;
|
|
46448
|
+
}
|
|
46449
|
+
function tasksFactoryOverridesFromEnv(source) {
|
|
46450
|
+
const overrides = {};
|
|
46451
|
+
for (const [name, raw] of Object.entries(source)) {
|
|
46452
|
+
const match = /^VAPI_TASKS_ESCROW_FACTORY_(\d+)$/.exec(name);
|
|
46453
|
+
const value = raw?.trim();
|
|
46454
|
+
if (!match || !value) continue;
|
|
46455
|
+
overrides[match[1]] = getAddress(value);
|
|
46456
|
+
}
|
|
46457
|
+
return overrides;
|
|
46458
|
+
}
|
|
46459
|
+
function registryEndpoints(baseUrl) {
|
|
46460
|
+
const base = new URL(baseUrl);
|
|
46461
|
+
base.search = "";
|
|
46462
|
+
base.hash = "";
|
|
46463
|
+
const prefix = base.pathname === "/" ? "" : base.pathname.replace(/\/+$/, "");
|
|
46464
|
+
const discovery = new URL(base);
|
|
46465
|
+
discovery.pathname = `${prefix}/api/call/services`;
|
|
46466
|
+
const marketplace = new URL(base);
|
|
46467
|
+
marketplace.pathname = `${prefix}/api/call/discovery`;
|
|
46468
|
+
return { discoveryUrl: discovery.href, marketplaceDiscoveryUrl: marketplace.href };
|
|
46469
|
+
}
|
|
46470
|
+
async function pathExists(path) {
|
|
46471
|
+
try {
|
|
46472
|
+
await stat2(path);
|
|
46473
|
+
return true;
|
|
46474
|
+
} catch (error61) {
|
|
46475
|
+
if (isMissingFile(error61)) return false;
|
|
46476
|
+
throw error61;
|
|
46477
|
+
}
|
|
46478
|
+
}
|
|
46479
|
+
function isMissingFile(error61) {
|
|
46480
|
+
return error61 instanceof Error && "code" in error61 && error61.code === "ENOENT";
|
|
46481
|
+
}
|
|
46482
|
+
function isAlreadyExists(error61) {
|
|
46483
|
+
return error61 instanceof Error && "code" in error61 && error61.code === "EEXIST";
|
|
46484
|
+
}
|
|
46485
|
+
|
|
46486
|
+
// src/wallet-name.ts
|
|
46487
|
+
var DEFAULT_WALLET_NAME = "main";
|
|
46488
|
+
var WALLET_NAME_PATTERN = /^[a-z0-9][a-z0-9-]{0,31}$/;
|
|
46489
|
+
var walletNameSchema = external_exports.string().regex(
|
|
46490
|
+
WALLET_NAME_PATTERN,
|
|
46491
|
+
"A wallet name is 1 to 32 characters of lowercase letters, digits and dashes, starting with a letter or digit."
|
|
46492
|
+
);
|
|
46493
|
+
|
|
46494
|
+
// src/agent-profile.ts
|
|
46495
|
+
var DEFAULT_AGENT_TOOLS = ["call.search", "call.inspect", "call.pay"];
|
|
46496
|
+
var AGENT_GRANTS = ["read", "delegate", "allocate"];
|
|
46497
|
+
var agentProfileShape = {
|
|
46498
|
+
version: external_exports.literal(1),
|
|
46499
|
+
name: walletNameSchema,
|
|
46500
|
+
wallet: external_exports.string(),
|
|
46501
|
+
model: external_exports.string().min(1),
|
|
46502
|
+
instructions: external_exports.string().max(2e4),
|
|
46503
|
+
verifiedOnly: external_exports.boolean().default(true),
|
|
46504
|
+
approveAboveUsd: external_exports.number().min(0).default(0.5),
|
|
46505
|
+
maxPerTaskUsd: external_exports.number().min(0).default(DEFAULT_MAX_PER_TASK_USD),
|
|
46506
|
+
autoReleaseBelowUsd: external_exports.number().min(0).default(DEFAULT_AUTO_RELEASE_BELOW_USD),
|
|
46507
|
+
maxSteps: external_exports.number().int().min(1).max(50).default(12),
|
|
46508
|
+
paused: external_exports.boolean().default(false),
|
|
46509
|
+
grants: external_exports.array(external_exports.enum(AGENT_GRANTS)).default([]),
|
|
46510
|
+
createdAt: external_exports.string()
|
|
46511
|
+
};
|
|
46512
|
+
function createAgentProfileSchema(toolNames) {
|
|
46513
|
+
if (toolNames.length === 0) {
|
|
46514
|
+
throw new Error("Agent profiles require at least one registered tool.");
|
|
46515
|
+
}
|
|
46516
|
+
const tools = [...toolNames];
|
|
46517
|
+
return external_exports.object({ ...agentProfileShape, tools: external_exports.array(external_exports.enum(tools)).default(tools) });
|
|
46518
|
+
}
|
|
46519
|
+
var agentProfileSchema = createAgentProfileSchema(DEFAULT_AGENT_TOOLS);
|
|
46520
|
+
async function listAgentProfiles(home, options = {}) {
|
|
46521
|
+
const directory = getVapiPaths(home).agentsDir;
|
|
46522
|
+
let files;
|
|
46523
|
+
try {
|
|
46524
|
+
files = await readdir2(directory);
|
|
46525
|
+
} catch (error61) {
|
|
46526
|
+
if (isMissingFile(error61)) return [];
|
|
46527
|
+
throw error61;
|
|
46528
|
+
}
|
|
46529
|
+
const warn = options.warn ?? ((message) => console.warn(message));
|
|
46530
|
+
const profiles = [];
|
|
46531
|
+
for (const file2 of files) {
|
|
46532
|
+
if (!file2.endsWith(".json")) continue;
|
|
46533
|
+
try {
|
|
46534
|
+
profiles.push(
|
|
46535
|
+
(options.schema ?? agentProfileSchema).parse(
|
|
46536
|
+
JSON.parse(await readFile3(join4(directory, file2), "utf8"))
|
|
46537
|
+
)
|
|
46538
|
+
);
|
|
46539
|
+
} catch {
|
|
46540
|
+
options.onInvalid?.(file2);
|
|
46541
|
+
warn(`Skipping invalid agent profile ${file2}.`);
|
|
46542
|
+
}
|
|
46543
|
+
}
|
|
46544
|
+
profiles.sort((left, right) => left.name < right.name ? -1 : left.name > right.name ? 1 : 0);
|
|
46545
|
+
return profiles;
|
|
46546
|
+
}
|
|
46547
|
+
|
|
46548
|
+
// src/device.ts
|
|
46549
|
+
import { hostname as operatingSystemHostname } from "node:os";
|
|
46550
|
+
var DEVICE_NAME_PATTERN = /^[a-z0-9][a-z0-9-]{0,31}$/;
|
|
46551
|
+
function normalizedDeviceName(value) {
|
|
46552
|
+
return value.toLowerCase().replace(/[^a-z0-9-]/gu, "-").replace(/-+/gu, "-").replace(/^-+|-+$/gu, "").slice(0, 32).replace(/-+$/gu, "");
|
|
46553
|
+
}
|
|
46554
|
+
function slugifyDeviceName(value) {
|
|
46555
|
+
return normalizedDeviceName(value) || "device";
|
|
46556
|
+
}
|
|
46557
|
+
function deviceName(args = {}) {
|
|
46558
|
+
const environmentValue = args.env === void 0 ? process.env.VAPI_DEVICE : args.env.VAPI_DEVICE;
|
|
46559
|
+
for (const value of [environmentValue?.trim(), args.config?.device]) {
|
|
46560
|
+
if (value === void 0 || value.length === 0) continue;
|
|
46561
|
+
const normalized = normalizedDeviceName(value);
|
|
46562
|
+
if (normalized.length > 0) return normalized;
|
|
46563
|
+
}
|
|
46564
|
+
return slugifyDeviceName(args.hostname ?? operatingSystemHostname());
|
|
46565
|
+
}
|
|
45876
46566
|
|
|
45877
46567
|
// src/keystore.ts
|
|
45878
|
-
import { createCipheriv, createDecipheriv, randomBytes as
|
|
46568
|
+
import { createCipheriv, createDecipheriv, randomBytes as randomBytes3, scrypt } from "node:crypto";
|
|
45879
46569
|
var SCRYPT_N = 2 ** 15;
|
|
45880
46570
|
var SCRYPT_R = 8;
|
|
45881
46571
|
var SCRYPT_P = 1;
|
|
@@ -45950,7 +46640,7 @@ function encryptWithKey(key, plaintext) {
|
|
|
45950
46640
|
if (key.byteLength !== KEY_LENGTH) {
|
|
45951
46641
|
throw new Error(`Expected a ${KEY_LENGTH}-byte AES-256-GCM key.`);
|
|
45952
46642
|
}
|
|
45953
|
-
const nonce =
|
|
46643
|
+
const nonce = randomBytes3(12);
|
|
45954
46644
|
const cipher = createCipheriv("aes-256-gcm", key, nonce, {
|
|
45955
46645
|
authTagLength: AUTH_TAG_LENGTH
|
|
45956
46646
|
});
|
|
@@ -46038,20 +46728,522 @@ function deriveEvmPrivateKey(seed, path = EVM_DERIVATION_PATH) {
|
|
|
46038
46728
|
return `0x${Buffer.from(privateKey).toString("hex")}`;
|
|
46039
46729
|
}
|
|
46040
46730
|
|
|
46041
|
-
// src/
|
|
46042
|
-
|
|
46043
|
-
|
|
46044
|
-
|
|
46045
|
-
|
|
46046
|
-
|
|
46731
|
+
// src/movement.ts
|
|
46732
|
+
import { randomBytes as randomBytes6 } from "node:crypto";
|
|
46733
|
+
import { chmod, mkdir as mkdir7, open as open6, readFile as readFile7, readdir as readdir3, rename as rename5, unlink as unlink6 } from "node:fs/promises";
|
|
46734
|
+
import { join as join6 } from "node:path";
|
|
46735
|
+
|
|
46736
|
+
// src/account-movement-lock.ts
|
|
46737
|
+
import { randomBytes as randomBytes4 } from "node:crypto";
|
|
46738
|
+
import { mkdir as mkdir4, open as open3, readFile as readFile4, stat as stat3, unlink as unlink3 } from "node:fs/promises";
|
|
46739
|
+
import { join as join5 } from "node:path";
|
|
46740
|
+
var AccountMovementLockedError = class extends Error {
|
|
46741
|
+
constructor(account) {
|
|
46742
|
+
super(`Another capital movement is already running for account ${account}.`);
|
|
46743
|
+
this.account = account;
|
|
46744
|
+
this.name = "AccountMovementLockedError";
|
|
46745
|
+
}
|
|
46746
|
+
account;
|
|
46747
|
+
code = "account_movement_locked";
|
|
46748
|
+
};
|
|
46749
|
+
async function withAccountMovementLock(home, account, timeoutMs, operation, signal) {
|
|
46750
|
+
const directory = join5(home, "movements");
|
|
46751
|
+
await mkdir4(directory, { recursive: true, mode: 448 });
|
|
46752
|
+
const path = join5(directory, `.${account}.lock`);
|
|
46753
|
+
const deadline = Date.now() + (timeoutMs ?? 2e3);
|
|
46754
|
+
const owner = JSON.stringify({ pid: process.pid, token: randomBytes4(16).toString("hex") });
|
|
46755
|
+
let handle;
|
|
46756
|
+
while (handle === void 0) {
|
|
46757
|
+
signal?.throwIfAborted();
|
|
46758
|
+
try {
|
|
46759
|
+
handle = await open3(path, "wx", 384);
|
|
46760
|
+
await handle.writeFile(owner, "utf8");
|
|
46761
|
+
await handle.sync();
|
|
46762
|
+
} catch (error61) {
|
|
46763
|
+
if (!(error61 instanceof Error && "code" in error61 && error61.code === "EEXIST")) throw error61;
|
|
46764
|
+
await removeStaleLock2(path);
|
|
46765
|
+
if (Date.now() >= deadline) throw new AccountMovementLockedError(account);
|
|
46766
|
+
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
46767
|
+
}
|
|
46768
|
+
}
|
|
46769
|
+
const ownedHandle = handle;
|
|
46770
|
+
const heartbeat = setInterval(() => {
|
|
46771
|
+
const time3 = /* @__PURE__ */ new Date();
|
|
46772
|
+
void ownedHandle.utimes(time3, time3).catch(() => void 0);
|
|
46773
|
+
}, 3e4);
|
|
46774
|
+
heartbeat.unref();
|
|
46775
|
+
try {
|
|
46776
|
+
return await operation();
|
|
46777
|
+
} finally {
|
|
46778
|
+
clearInterval(heartbeat);
|
|
46779
|
+
await ownedHandle.close();
|
|
46780
|
+
await unlinkOwnedLock(path, owner);
|
|
46781
|
+
}
|
|
46782
|
+
}
|
|
46783
|
+
async function withAccountMovementLocks(home, accounts, timeoutMs, operation) {
|
|
46784
|
+
return await acquireAccountMovementLocks(
|
|
46785
|
+
home,
|
|
46786
|
+
[...new Set(accounts)].sort(),
|
|
46787
|
+
timeoutMs,
|
|
46788
|
+
operation
|
|
46789
|
+
);
|
|
46790
|
+
}
|
|
46791
|
+
async function acquireAccountMovementLocks(home, accounts, timeoutMs, operation) {
|
|
46792
|
+
const [account, ...remaining] = accounts;
|
|
46793
|
+
if (account === void 0) return await operation();
|
|
46794
|
+
return await withAccountMovementLock(
|
|
46795
|
+
home,
|
|
46796
|
+
account,
|
|
46797
|
+
timeoutMs,
|
|
46798
|
+
async () => acquireAccountMovementLocks(home, remaining, timeoutMs, operation)
|
|
46799
|
+
);
|
|
46800
|
+
}
|
|
46801
|
+
async function removeStaleLock2(path) {
|
|
46802
|
+
try {
|
|
46803
|
+
const metadata = await stat3(path);
|
|
46804
|
+
if (Date.now() - metadata.mtimeMs <= 12e4) return;
|
|
46805
|
+
const owner = await readFile4(path, "utf8").catch(() => "");
|
|
46806
|
+
const pid = lockOwnerPid2(owner);
|
|
46807
|
+
if (pid !== void 0 && processIsAlive2(pid)) return;
|
|
46808
|
+
await unlinkOwnedLock(path, owner);
|
|
46809
|
+
} catch (error61) {
|
|
46810
|
+
if (!isMissingFile(error61)) throw error61;
|
|
46811
|
+
}
|
|
46812
|
+
}
|
|
46813
|
+
async function unlinkOwnedLock(path, owner) {
|
|
46814
|
+
try {
|
|
46815
|
+
if (await readFile4(path, "utf8") === owner) await unlink3(path);
|
|
46816
|
+
} catch (error61) {
|
|
46817
|
+
if (!isMissingFile(error61)) throw error61;
|
|
46818
|
+
}
|
|
46819
|
+
}
|
|
46820
|
+
function lockOwnerPid2(owner) {
|
|
46821
|
+
try {
|
|
46822
|
+
const value = JSON.parse(owner);
|
|
46823
|
+
if (typeof value !== "object" || value === null) return void 0;
|
|
46824
|
+
const pid = Reflect.get(value, "pid");
|
|
46825
|
+
return typeof pid === "number" && Number.isSafeInteger(pid) && pid > 0 ? pid : void 0;
|
|
46826
|
+
} catch {
|
|
46827
|
+
return void 0;
|
|
46828
|
+
}
|
|
46829
|
+
}
|
|
46830
|
+
function processIsAlive2(pid) {
|
|
46831
|
+
try {
|
|
46832
|
+
process.kill(pid, 0);
|
|
46833
|
+
return true;
|
|
46834
|
+
} catch (error61) {
|
|
46835
|
+
return error61 instanceof Error && "code" in error61 && error61.code === "EPERM";
|
|
46836
|
+
}
|
|
46837
|
+
}
|
|
46838
|
+
|
|
46839
|
+
// src/agent-run.ts
|
|
46840
|
+
var RUN_ID_PATTERN = /^run_[0-9a-f]{24}$/;
|
|
46841
|
+
var runRefSchema = external_exports.strictObject({
|
|
46842
|
+
id: external_exports.string().regex(RUN_ID_PATTERN),
|
|
46843
|
+
swarm: external_exports.string().optional(),
|
|
46844
|
+
member: external_exports.string().optional(),
|
|
46845
|
+
parentRunId: external_exports.string().optional()
|
|
46846
|
+
});
|
|
46847
|
+
|
|
46848
|
+
// src/receipts.ts
|
|
46849
|
+
var RECEIPT_JOURNAL_BUSY_MESSAGE = "The receipt journal is busy. Wait for the other vapi command to finish, then retry.";
|
|
46850
|
+
async function withReceiptJournalLock(path, operation) {
|
|
46851
|
+
return await withFileLock(`${path}.lock`, operation, {
|
|
46852
|
+
lockedMessage: RECEIPT_JOURNAL_BUSY_MESSAGE
|
|
46853
|
+
});
|
|
46854
|
+
}
|
|
46855
|
+
var durationSchema = external_exports.number().nonnegative().finite();
|
|
46856
|
+
var receiptSchema = external_exports.strictObject({
|
|
46857
|
+
id: external_exports.string().min(1),
|
|
46858
|
+
timestamp: external_exports.iso.datetime(),
|
|
46859
|
+
run: runRefSchema.optional(),
|
|
46860
|
+
kind: external_exports.literal("transfer").optional(),
|
|
46861
|
+
wallet: walletNameSchema.optional(),
|
|
46862
|
+
resourceUrl: external_exports.url(),
|
|
46863
|
+
method: external_exports.string().min(1).optional(),
|
|
46864
|
+
source: external_exports.string().min(1).optional(),
|
|
46865
|
+
provenance: external_exports.array(
|
|
46866
|
+
external_exports.strictObject({
|
|
46867
|
+
source: external_exports.string().min(1),
|
|
46868
|
+
sourceUrl: external_exports.string().optional(),
|
|
46869
|
+
ref: external_exports.string().optional()
|
|
46870
|
+
})
|
|
46871
|
+
).optional(),
|
|
46872
|
+
quote: external_exports.strictObject({
|
|
46873
|
+
network: external_exports.string(),
|
|
46874
|
+
asset: external_exports.string().optional(),
|
|
46875
|
+
amountAtomic: external_exports.string().regex(/^\d+$/),
|
|
46876
|
+
payTo: external_exports.string().optional()
|
|
46877
|
+
}).optional(),
|
|
46878
|
+
payer: external_exports.string().optional(),
|
|
46879
|
+
paymentId: external_exports.string().regex(/^[A-Za-z0-9_-]{16,128}$/).optional(),
|
|
46880
|
+
authorization: external_exports.strictObject({
|
|
46881
|
+
from: external_exports.string().regex(/^0x[0-9a-fA-F]{40}$/),
|
|
46882
|
+
nonce: external_exports.string().regex(/^0x[0-9a-fA-F]{64}$/),
|
|
46883
|
+
validBefore: external_exports.string().regex(/^\d+$/)
|
|
46884
|
+
}).optional(),
|
|
46885
|
+
transfer: external_exports.strictObject({
|
|
46886
|
+
to: external_exports.string().regex(/^0x[0-9a-fA-F]{40}$/),
|
|
46887
|
+
toName: external_exports.string().min(1),
|
|
46888
|
+
toKind: external_exports.enum(["owner", "account"]),
|
|
46889
|
+
amountAtomic: external_exports.string().regex(/^\d+$/),
|
|
46890
|
+
network: external_exports.string().min(1),
|
|
46891
|
+
nonce: external_exports.string().regex(/^0x[0-9a-fA-F]{64}$/),
|
|
46892
|
+
status: external_exports.enum(["sent", "unknown", "failed"]),
|
|
46893
|
+
txHash: external_exports.string().regex(/^0x[0-9a-fA-F]+$/).nullable(),
|
|
46894
|
+
replayed: external_exports.boolean(),
|
|
46895
|
+
reservedOn: external_exports.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional(),
|
|
46896
|
+
request: external_exports.strictObject({
|
|
46897
|
+
authorization: external_exports.strictObject({
|
|
46898
|
+
from: external_exports.string().regex(/^0x[0-9a-fA-F]{40}$/),
|
|
46899
|
+
to: external_exports.string().regex(/^0x[0-9a-fA-F]{40}$/),
|
|
46900
|
+
value: external_exports.string().regex(/^\d+$/),
|
|
46901
|
+
validAfter: external_exports.string().regex(/^\d+$/),
|
|
46902
|
+
validBefore: external_exports.string().regex(/^\d+$/),
|
|
46903
|
+
nonce: external_exports.string().regex(/^0x[0-9a-fA-F]{64}$/)
|
|
46904
|
+
}),
|
|
46905
|
+
signature: external_exports.string().regex(/^0x[0-9a-fA-F]+$/)
|
|
46906
|
+
}).optional()
|
|
46907
|
+
}).optional(),
|
|
46908
|
+
settlement: external_exports.strictObject({
|
|
46909
|
+
outcome: external_exports.enum(["succeeded", "rejected", "unknown"]),
|
|
46910
|
+
transaction: external_exports.string().optional(),
|
|
46911
|
+
explorerUrl: external_exports.url().optional(),
|
|
46912
|
+
evidence: external_exports.unknown().optional()
|
|
46913
|
+
}).optional(),
|
|
46914
|
+
latencyMs: external_exports.number().nonnegative().finite().optional(),
|
|
46915
|
+
status: external_exports.number().int().min(100).max(599).optional(),
|
|
46916
|
+
error: external_exports.strictObject({ code: external_exports.string(), message: external_exports.string() }).optional(),
|
|
46917
|
+
phases: external_exports.strictObject({
|
|
46918
|
+
discoverMs: durationSchema.optional(),
|
|
46919
|
+
quoteMs: durationSchema.optional(),
|
|
46920
|
+
signMs: durationSchema.optional(),
|
|
46921
|
+
requestMs: durationSchema.optional(),
|
|
46922
|
+
settleMs: durationSchema.optional()
|
|
46923
|
+
}).optional(),
|
|
46924
|
+
listing: external_exports.strictObject({
|
|
46925
|
+
name: external_exports.string().min(1).optional(),
|
|
46926
|
+
providerHost: external_exports.string().min(1).optional(),
|
|
46927
|
+
source: external_exports.string().min(1)
|
|
46928
|
+
}).optional(),
|
|
46929
|
+
retry: external_exports.number().int().nonnegative().optional(),
|
|
46930
|
+
policy: external_exports.strictObject({
|
|
46931
|
+
maxPriceUsd: external_exports.string().regex(/^\d+(?:\.\d{1,6})?$/).optional(),
|
|
46932
|
+
capsApplied: external_exports.boolean()
|
|
46933
|
+
}).optional(),
|
|
46934
|
+
client: external_exports.strictObject({ name: external_exports.literal("vapi-network"), version: external_exports.string().min(1) }).optional(),
|
|
46935
|
+
outcome: external_exports.enum([
|
|
46936
|
+
"paid",
|
|
46937
|
+
"signed_in",
|
|
46938
|
+
"declined_policy",
|
|
46939
|
+
"failed_request",
|
|
46940
|
+
"settlement_rejected",
|
|
46941
|
+
"settlement_unknown"
|
|
46942
|
+
]).optional()
|
|
46943
|
+
});
|
|
46944
|
+
function parseReceipt(value) {
|
|
46945
|
+
const receipt = receiptSchema.parse(value);
|
|
46946
|
+
const transaction = receipt.settlement?.transaction;
|
|
46947
|
+
const network = receipt.quote?.network;
|
|
46948
|
+
const explorerUrl = transaction === void 0 || network === void 0 ? void 0 : explorerTransactionUrl(network, transaction);
|
|
46949
|
+
if (explorerUrl === void 0 || receipt.settlement?.explorerUrl === explorerUrl) {
|
|
46950
|
+
return receipt;
|
|
46951
|
+
}
|
|
46952
|
+
return {
|
|
46953
|
+
...receipt,
|
|
46954
|
+
settlement: {
|
|
46955
|
+
outcome: receipt.settlement.outcome,
|
|
46956
|
+
...receipt.settlement.transaction ? { transaction: receipt.settlement.transaction } : {},
|
|
46957
|
+
explorerUrl,
|
|
46958
|
+
...receipt.settlement.evidence === void 0 ? {} : { evidence: receipt.settlement.evidence }
|
|
46959
|
+
}
|
|
46960
|
+
};
|
|
46961
|
+
}
|
|
46962
|
+
function receiptWallet(receipt) {
|
|
46963
|
+
return receipt.wallet ?? DEFAULT_WALLET_NAME;
|
|
46964
|
+
}
|
|
46965
|
+
|
|
46966
|
+
// src/agent-link.ts
|
|
46967
|
+
import { createHash } from "node:crypto";
|
|
46968
|
+
import { mkdir as mkdir5, open as open4, stat as stat4, unlink as unlink4 } from "node:fs/promises";
|
|
46969
|
+
import { dirname as dirname3 } from "node:path";
|
|
46970
|
+
var AGENT_LINK_REVOKED_MESSAGE = "This agent's link was revoked or has expired. Run vapi login to link it again.";
|
|
46971
|
+
var REMOTE_AGENT_TOKEN_EXPIRED_MESSAGE = "The Railway run's short-lived vAPI access token expired. Start a new Railway run from the owner machine.";
|
|
46972
|
+
var AgentLinkError = class extends Error {
|
|
46973
|
+
constructor(code, message) {
|
|
46974
|
+
super(message);
|
|
46975
|
+
this.code = code;
|
|
46976
|
+
this.name = "AgentLinkError";
|
|
46977
|
+
}
|
|
46978
|
+
code;
|
|
46979
|
+
};
|
|
46980
|
+
var DEFAULT_AGENT_SCOPES = ["mcp:call", "router.use"];
|
|
46981
|
+
var ALLOWED_AGENT_SCOPES = /* @__PURE__ */ new Set([
|
|
46982
|
+
...DEFAULT_AGENT_SCOPES,
|
|
46983
|
+
"call.publish",
|
|
46984
|
+
"tasks:read",
|
|
46985
|
+
"tasks:write"
|
|
46986
|
+
]);
|
|
46987
|
+
var publicFetch = createPublicFetch({ allowPrivateNetwork: false });
|
|
46988
|
+
var agentSecretAccounts = (wallet) => ({
|
|
46989
|
+
tokens: `vapi.agent.${wallet}.tokens`,
|
|
46990
|
+
routerStake: `vapi.agent.${wallet}.router.stake`,
|
|
46991
|
+
routerBalance: `vapi.agent.${wallet}.router.balance`
|
|
46992
|
+
});
|
|
46993
|
+
async function agentFetch(args, url2, init) {
|
|
46994
|
+
const fetchImpl = args.fetchImpl ?? publicFetch;
|
|
46995
|
+
const accessToken = await resolveAgentAccessToken(args, {});
|
|
46996
|
+
const response = await safeAgentFetch(fetchImpl, url2, withBearer(init, accessToken));
|
|
46997
|
+
if (response.status !== 401) return response;
|
|
46998
|
+
const refreshedToken = await resolveAgentAccessToken(args, { rejectedAccessToken: accessToken });
|
|
46999
|
+
const refreshedResponse = await safeAgentFetch(fetchImpl, url2, withBearer(init, refreshedToken));
|
|
47000
|
+
if (refreshedResponse.status === 401) {
|
|
47001
|
+
throw new AgentLinkError("not_linked", AGENT_LINK_REVOKED_MESSAGE);
|
|
47002
|
+
}
|
|
47003
|
+
return refreshedResponse;
|
|
47004
|
+
}
|
|
47005
|
+
async function resolveAgentAccessToken(args, options) {
|
|
47006
|
+
await args.wallets.reload();
|
|
47007
|
+
const link3 = args.wallets.entry(args.wallet)?.link;
|
|
47008
|
+
if (link3 === void 0 || !args.secrets.available) throw notLinkedError();
|
|
47009
|
+
const accounts = agentSecretAccounts(args.wallet);
|
|
47010
|
+
const tokens = parseTokens(await safeGetSecret(args.secrets, accounts.tokens));
|
|
47011
|
+
if (tokens === void 0) throw notLinkedError();
|
|
47012
|
+
const now = args.now ?? Date.now;
|
|
47013
|
+
if (options.rejectedAccessToken === void 0 && tokens.expiresAt - 6e4 > now()) {
|
|
47014
|
+
return tokens.accessToken;
|
|
47015
|
+
}
|
|
47016
|
+
return await withAgentCredentialLock(args.wallets, link3.clientId, async () => {
|
|
47017
|
+
await args.wallets.reload();
|
|
47018
|
+
const currentLink = args.wallets.entry(args.wallet)?.link;
|
|
47019
|
+
if (currentLink === void 0) throw notLinkedError();
|
|
47020
|
+
const currentAccounts = agentSecretAccounts(args.wallet);
|
|
47021
|
+
const current = parseTokens(await safeGetSecret(args.secrets, currentAccounts.tokens));
|
|
47022
|
+
if (current === void 0) throw notLinkedError();
|
|
47023
|
+
if (options.rejectedAccessToken !== void 0 && current.accessToken !== options.rejectedAccessToken) {
|
|
47024
|
+
return current.accessToken;
|
|
47025
|
+
}
|
|
47026
|
+
if (options.rejectedAccessToken === void 0 && current.expiresAt - 6e4 > now()) {
|
|
47027
|
+
return current.accessToken;
|
|
47028
|
+
}
|
|
47029
|
+
if (current.refreshToken === void 0) {
|
|
47030
|
+
throw new AgentLinkError("not_linked", REMOTE_AGENT_TOKEN_EXPIRED_MESSAGE);
|
|
47031
|
+
}
|
|
47032
|
+
const response = await safeFetch(
|
|
47033
|
+
args.fetchImpl ?? publicFetch,
|
|
47034
|
+
apiEndpoint(currentLink.apiBase, "/oauth/token"),
|
|
47035
|
+
{
|
|
47036
|
+
method: "POST",
|
|
47037
|
+
headers: { "content-type": "application/x-www-form-urlencoded" },
|
|
47038
|
+
body: new URLSearchParams({
|
|
47039
|
+
grant_type: "refresh_token",
|
|
47040
|
+
refresh_token: current.refreshToken,
|
|
47041
|
+
client_id: currentLink.clientId
|
|
47042
|
+
})
|
|
47043
|
+
}
|
|
47044
|
+
);
|
|
47045
|
+
const payload = await responseRecord(response);
|
|
47046
|
+
if (!response.ok) {
|
|
47047
|
+
if (response.status === 400 && stringField(payload, "error") === "invalid_grant") {
|
|
47048
|
+
throw new AgentLinkError("not_linked", AGENT_LINK_REVOKED_MESSAGE);
|
|
47049
|
+
}
|
|
47050
|
+
throw httpError("The vAPI agent token refresh failed.");
|
|
47051
|
+
}
|
|
47052
|
+
const refreshed = {
|
|
47053
|
+
accessToken: requiredString(payload, "access_token"),
|
|
47054
|
+
refreshToken: stringField(payload, "refresh_token") ?? current.refreshToken,
|
|
47055
|
+
expiresAt: now() + requiredNumber(payload, "expires_in") * 1e3,
|
|
47056
|
+
scopes: stringField(payload, "scope") === void 0 ? [...current.scopes] : splitScopes(requiredString(payload, "scope"))
|
|
47057
|
+
};
|
|
47058
|
+
await safeSetSecret(args.secrets, currentAccounts.tokens, JSON.stringify(refreshed));
|
|
47059
|
+
return refreshed.accessToken;
|
|
47060
|
+
});
|
|
47061
|
+
}
|
|
47062
|
+
async function withAgentCredentialLock(wallets, clientId, operation) {
|
|
47063
|
+
const digest = createHash("sha256").update(clientId).digest("hex").slice(0, 24);
|
|
47064
|
+
const lockPath = `${wallets.registryPath}.${digest}.agent-link.lock`;
|
|
47065
|
+
await mkdir5(dirname3(lockPath), { recursive: true, mode: 448 });
|
|
47066
|
+
const deadline = Date.now() + 3e4;
|
|
47067
|
+
let handle;
|
|
47068
|
+
while (!handle) {
|
|
47069
|
+
try {
|
|
47070
|
+
handle = await open4(lockPath, "wx", 384);
|
|
47071
|
+
} catch (error61) {
|
|
47072
|
+
if (!(error61 instanceof Error && "code" in error61 && error61.code === "EEXIST")) throw error61;
|
|
47073
|
+
await removeStaleCredentialLock(lockPath);
|
|
47074
|
+
if (Date.now() >= deadline) {
|
|
47075
|
+
throw httpError("Timed out waiting to update the agent credentials.");
|
|
47076
|
+
}
|
|
47077
|
+
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
47078
|
+
}
|
|
47079
|
+
}
|
|
47080
|
+
try {
|
|
47081
|
+
return await operation();
|
|
47082
|
+
} finally {
|
|
47083
|
+
await handle.close();
|
|
47084
|
+
await unlink4(lockPath).catch(() => void 0);
|
|
47085
|
+
}
|
|
47086
|
+
}
|
|
47087
|
+
async function removeStaleCredentialLock(path) {
|
|
47088
|
+
try {
|
|
47089
|
+
const metadata = await stat4(path);
|
|
47090
|
+
if (Date.now() - metadata.mtimeMs > 5 * 6e4) await unlink4(path);
|
|
47091
|
+
} catch {
|
|
47092
|
+
}
|
|
47093
|
+
}
|
|
47094
|
+
async function safeGetSecret(secrets, account) {
|
|
47095
|
+
try {
|
|
47096
|
+
return await secrets.get(account);
|
|
47097
|
+
} catch {
|
|
47098
|
+
throw httpError("The agent tokens could not be read from the OS secret store.");
|
|
47099
|
+
}
|
|
47100
|
+
}
|
|
47101
|
+
async function safeSetSecret(secrets, account, value) {
|
|
47102
|
+
try {
|
|
47103
|
+
await secrets.set(account, value);
|
|
47104
|
+
} catch {
|
|
47105
|
+
throw httpError("The agent credentials could not be stored in the OS secret store.");
|
|
47106
|
+
}
|
|
47107
|
+
}
|
|
47108
|
+
function parseTokens(value) {
|
|
47109
|
+
if (value === void 0) return void 0;
|
|
47110
|
+
let parsed;
|
|
47111
|
+
try {
|
|
47112
|
+
parsed = JSON.parse(value);
|
|
47113
|
+
} catch {
|
|
47114
|
+
return void 0;
|
|
47115
|
+
}
|
|
47116
|
+
if (!isRecord(parsed)) return void 0;
|
|
47117
|
+
const accessToken = stringField(parsed, "accessToken");
|
|
47118
|
+
const refreshToken = stringField(parsed, "refreshToken");
|
|
47119
|
+
const refreshable = parsed.refreshable;
|
|
47120
|
+
const expiresAt = numberField(parsed, "expiresAt");
|
|
47121
|
+
const scopes = parsed.scopes;
|
|
47122
|
+
if (accessToken === void 0 || expiresAt === void 0 || !Array.isArray(scopes) || scopes.some((scope) => typeof scope !== "string") || (refreshToken === void 0 ? refreshable !== false : refreshable === false)) {
|
|
47123
|
+
return void 0;
|
|
47124
|
+
}
|
|
47125
|
+
return {
|
|
47126
|
+
accessToken,
|
|
47127
|
+
...refreshToken === void 0 ? {} : { refreshToken },
|
|
47128
|
+
expiresAt,
|
|
47129
|
+
scopes
|
|
47130
|
+
};
|
|
47131
|
+
}
|
|
47132
|
+
function withBearer(init, accessToken) {
|
|
47133
|
+
const headers = new Headers(init?.headers);
|
|
47134
|
+
headers.set("authorization", `Bearer ${accessToken}`);
|
|
47135
|
+
return { ...init, headers };
|
|
47136
|
+
}
|
|
47137
|
+
async function safeFetch(fetchImpl, input2, init) {
|
|
47138
|
+
try {
|
|
47139
|
+
return await fetchImpl(input2, init);
|
|
47140
|
+
} catch {
|
|
47141
|
+
throw httpError("The vAPI agent link request failed.");
|
|
47142
|
+
}
|
|
47143
|
+
}
|
|
47144
|
+
async function safeAgentFetch(fetchImpl, input2, init) {
|
|
47145
|
+
try {
|
|
47146
|
+
return await fetchImpl(input2, init);
|
|
47147
|
+
} catch {
|
|
47148
|
+
throw httpError("The vAPI agent request failed.");
|
|
47149
|
+
}
|
|
47150
|
+
}
|
|
47151
|
+
function notLinkedError() {
|
|
47152
|
+
return new AgentLinkError("not_linked", "Run vapi login first.");
|
|
47153
|
+
}
|
|
47154
|
+
function httpError(message) {
|
|
47155
|
+
return new AgentLinkError("http", message);
|
|
47156
|
+
}
|
|
47157
|
+
function apiEndpoint(apiBase, path) {
|
|
47158
|
+
return new URL(path, new URL(apiBase).origin).toString();
|
|
47159
|
+
}
|
|
47160
|
+
async function responseRecord(response) {
|
|
47161
|
+
let payload;
|
|
47162
|
+
try {
|
|
47163
|
+
payload = await response.json();
|
|
47164
|
+
} catch {
|
|
47165
|
+
throw httpError("The vAPI agent link response was invalid.");
|
|
47166
|
+
}
|
|
47167
|
+
if (!isRecord(payload)) throw httpError("The vAPI agent link response was invalid.");
|
|
47168
|
+
return payload;
|
|
47169
|
+
}
|
|
47170
|
+
function isRecord(value) {
|
|
47171
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
47172
|
+
}
|
|
47173
|
+
function stringField(record2, key) {
|
|
47174
|
+
const value = record2[key];
|
|
47175
|
+
return typeof value === "string" ? value : void 0;
|
|
47176
|
+
}
|
|
47177
|
+
function numberField(record2, key) {
|
|
47178
|
+
const value = record2[key];
|
|
47179
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
47180
|
+
}
|
|
47181
|
+
function requiredString(record2, key) {
|
|
47182
|
+
const value = stringField(record2, key);
|
|
47183
|
+
if (value === void 0) throw httpError("The vAPI agent link response was invalid.");
|
|
47184
|
+
return value;
|
|
47185
|
+
}
|
|
47186
|
+
function requiredNumber(record2, key) {
|
|
47187
|
+
const value = numberField(record2, key);
|
|
47188
|
+
if (value === void 0) throw httpError("The vAPI agent link response was invalid.");
|
|
47189
|
+
return value;
|
|
47190
|
+
}
|
|
47191
|
+
function splitScopes(scope) {
|
|
47192
|
+
return scope.split(/\s+/u).filter(Boolean);
|
|
47193
|
+
}
|
|
47194
|
+
|
|
47195
|
+
// src/movement-open.ts
|
|
47196
|
+
function isOpenMovementLeg(leg, journalEvidence) {
|
|
47197
|
+
if (leg.status === "sent" || leg.status === "cancelled") return false;
|
|
47198
|
+
if (leg.restored === true) return true;
|
|
47199
|
+
if (leg.status === "planned" || leg.status === "unknown") return true;
|
|
47200
|
+
if (leg.status !== "failed") return false;
|
|
47201
|
+
if (leg.retryable === true) return true;
|
|
47202
|
+
return journalEvidence === "signed";
|
|
47203
|
+
}
|
|
47204
|
+
|
|
47205
|
+
// src/transfer-journal.ts
|
|
47206
|
+
import { readFile as readFile5 } from "node:fs/promises";
|
|
47207
|
+
async function readTransferJournal(home, from, nonce) {
|
|
47208
|
+
let raw;
|
|
47209
|
+
try {
|
|
47210
|
+
raw = await readFile5(getVapiPaths(home).receipts, "utf8");
|
|
47211
|
+
} catch (error61) {
|
|
47212
|
+
if (isMissingFile(error61)) return { signed: false };
|
|
47213
|
+
throw error61;
|
|
47214
|
+
}
|
|
47215
|
+
let latest;
|
|
47216
|
+
let request;
|
|
47217
|
+
for (const [index, line] of raw.split("\n").entries()) {
|
|
47218
|
+
if (!line.trim()) continue;
|
|
47219
|
+
let receipt;
|
|
47220
|
+
try {
|
|
47221
|
+
receipt = parseReceipt(JSON.parse(line));
|
|
47222
|
+
} catch (error61) {
|
|
47223
|
+
throw new Error(`Invalid receipt on JSONL line ${index + 1}.`, { cause: error61 });
|
|
47224
|
+
}
|
|
47225
|
+
if (receiptWallet(receipt) !== from || receipt.transfer?.nonce.toLowerCase() !== nonce.toLowerCase()) {
|
|
47226
|
+
continue;
|
|
47227
|
+
}
|
|
47228
|
+
latest = receipt;
|
|
47229
|
+
if (receipt.transfer.request !== void 0) request = receipt;
|
|
47230
|
+
}
|
|
47231
|
+
return {
|
|
47232
|
+
signed: latest !== void 0,
|
|
47233
|
+
...latest === void 0 ? {} : { latest },
|
|
47234
|
+
...request === void 0 ? {} : { request },
|
|
47235
|
+
...request?.transfer?.request?.authorization.validBefore === void 0 ? {} : { validBefore: request.transfer.request.authorization.validBefore },
|
|
47236
|
+
...(latest?.transfer?.reservedOn ?? request?.transfer?.reservedOn) === void 0 ? {} : { reservedOn: latest?.transfer?.reservedOn ?? request?.transfer?.reservedOn }
|
|
47237
|
+
};
|
|
47238
|
+
}
|
|
46047
47239
|
|
|
46048
47240
|
// src/vault-key.ts
|
|
46049
47241
|
import { randomBytes as cryptoRandomBytes } from "node:crypto";
|
|
46050
47242
|
|
|
46051
47243
|
// src/vault.ts
|
|
46052
|
-
import { randomBytes as
|
|
46053
|
-
import { mkdir, open as
|
|
46054
|
-
import { dirname } from "node:path";
|
|
47244
|
+
import { randomBytes as randomBytes5 } from "node:crypto";
|
|
47245
|
+
import { mkdir as mkdir6, open as open5, readFile as readFile6, rename as rename4, rm, stat as stat5, unlink as unlink5, writeFile as writeFile3 } from "node:fs/promises";
|
|
47246
|
+
import { dirname as dirname4 } from "node:path";
|
|
46055
47247
|
var VaultError = class extends Error {
|
|
46056
47248
|
code;
|
|
46057
47249
|
constructor(code, message, options) {
|
|
@@ -46596,9 +47788,9 @@ async function readVaultFile(path, key) {
|
|
|
46596
47788
|
async function readVaultFileUnlocked(path) {
|
|
46597
47789
|
let raw;
|
|
46598
47790
|
try {
|
|
46599
|
-
raw = await
|
|
47791
|
+
raw = await readFile6(path, "utf8");
|
|
46600
47792
|
} catch (error61) {
|
|
46601
|
-
if (
|
|
47793
|
+
if (isMissingFile2(error61)) {
|
|
46602
47794
|
throw new VaultError("not_found", "No vault file was found at the requested path.", {
|
|
46603
47795
|
cause: error61
|
|
46604
47796
|
});
|
|
@@ -46609,9 +47801,9 @@ async function readVaultFileUnlocked(path) {
|
|
|
46609
47801
|
}
|
|
46610
47802
|
async function refuseExistingVault(path) {
|
|
46611
47803
|
try {
|
|
46612
|
-
await
|
|
47804
|
+
await stat5(path);
|
|
46613
47805
|
} catch (error61) {
|
|
46614
|
-
if (
|
|
47806
|
+
if (isMissingFile2(error61)) return;
|
|
46615
47807
|
throw new VaultError("corrupt", "Unable to check whether the vault already exists.", {
|
|
46616
47808
|
cause: error61
|
|
46617
47809
|
});
|
|
@@ -46619,29 +47811,29 @@ async function refuseExistingVault(path) {
|
|
|
46619
47811
|
throw new VaultError("corrupt", "A vault already exists at the requested path.");
|
|
46620
47812
|
}
|
|
46621
47813
|
async function writeVaultFile(path, file2) {
|
|
46622
|
-
await
|
|
46623
|
-
const temporaryPath = `${path}.${process.pid}.${
|
|
47814
|
+
await mkdir6(dirname4(path), { recursive: true, mode: 448 });
|
|
47815
|
+
const temporaryPath = `${path}.${process.pid}.${randomBytes5(6).toString("hex")}.tmp`;
|
|
46624
47816
|
try {
|
|
46625
|
-
await
|
|
47817
|
+
await writeFile3(temporaryPath, `${JSON.stringify(file2, null, 2)}
|
|
46626
47818
|
`, {
|
|
46627
47819
|
encoding: "utf8",
|
|
46628
47820
|
mode: 384,
|
|
46629
47821
|
flag: "wx"
|
|
46630
47822
|
});
|
|
46631
|
-
await
|
|
47823
|
+
await rename4(temporaryPath, path);
|
|
46632
47824
|
} catch (error61) {
|
|
46633
47825
|
await rm(temporaryPath, { force: true }).catch(() => void 0);
|
|
46634
47826
|
throw error61;
|
|
46635
47827
|
}
|
|
46636
47828
|
}
|
|
46637
47829
|
async function withVaultLock(path, operation) {
|
|
46638
|
-
await
|
|
47830
|
+
await mkdir6(dirname4(path), { recursive: true, mode: 448 });
|
|
46639
47831
|
const lockPath = `${path}.lock`;
|
|
46640
47832
|
const deadline = Date.now() + VAULT_LOCK_TIMEOUT_MS;
|
|
46641
47833
|
let handle;
|
|
46642
47834
|
while (!handle) {
|
|
46643
47835
|
try {
|
|
46644
|
-
handle = await
|
|
47836
|
+
handle = await open5(lockPath, "wx", 384);
|
|
46645
47837
|
} catch (error61) {
|
|
46646
47838
|
if (!isExistingFile(error61)) throw error61;
|
|
46647
47839
|
await removeStaleVaultLock(lockPath);
|
|
@@ -46655,18 +47847,18 @@ async function withVaultLock(path, operation) {
|
|
|
46655
47847
|
return await operation();
|
|
46656
47848
|
} finally {
|
|
46657
47849
|
await handle.close();
|
|
46658
|
-
await
|
|
47850
|
+
await unlink5(lockPath).catch(() => void 0);
|
|
46659
47851
|
}
|
|
46660
47852
|
}
|
|
46661
47853
|
async function removeStaleVaultLock(path) {
|
|
46662
47854
|
try {
|
|
46663
|
-
const metadata = await
|
|
46664
|
-
if (Date.now() - metadata.mtimeMs > STALE_VAULT_LOCK_MS) await
|
|
47855
|
+
const metadata = await stat5(path);
|
|
47856
|
+
if (Date.now() - metadata.mtimeMs > STALE_VAULT_LOCK_MS) await unlink5(path);
|
|
46665
47857
|
} catch (error61) {
|
|
46666
|
-
if (!
|
|
47858
|
+
if (!isMissingFile2(error61)) throw error61;
|
|
46667
47859
|
}
|
|
46668
47860
|
}
|
|
46669
|
-
function
|
|
47861
|
+
function isMissingFile2(error61) {
|
|
46670
47862
|
return typeof error61 === "object" && error61 !== null && "code" in error61 && Reflect.get(error61, "code") === "ENOENT";
|
|
46671
47863
|
}
|
|
46672
47864
|
function isExistingFile(error61) {
|
|
@@ -46810,8 +48002,8 @@ function passwordRequiredError() {
|
|
|
46810
48002
|
"A vault password is needed. Pass it or set VAPI_VAULT_PASSWORD."
|
|
46811
48003
|
);
|
|
46812
48004
|
}
|
|
46813
|
-
function randomKeyBytes(
|
|
46814
|
-
const key = Buffer.from((
|
|
48005
|
+
function randomKeyBytes(randomBytes7) {
|
|
48006
|
+
const key = Buffer.from((randomBytes7 ?? cryptoRandomBytes)(DEVICE_KEY_LENGTH));
|
|
46815
48007
|
if (key.byteLength !== DEVICE_KEY_LENGTH) {
|
|
46816
48008
|
key.fill(0);
|
|
46817
48009
|
throw new VaultError("corrupt", "A generated device vault key has the wrong length.");
|
|
@@ -46879,6 +48071,7 @@ var routerRefillSchema = external_exports.object({
|
|
|
46879
48071
|
});
|
|
46880
48072
|
var ceilingSweepPendingSchema = external_exports.strictObject({
|
|
46881
48073
|
owner: external_exports.string().regex(/^0x[0-9a-fA-F]{40}$/),
|
|
48074
|
+
target: external_exports.string().regex(/^0x[0-9a-fA-F]{40}$/).optional(),
|
|
46882
48075
|
amountAtomic: external_exports.string().regex(/^\d+$/),
|
|
46883
48076
|
nonce: external_exports.string().regex(/^0x[0-9a-fA-F]{64}$/),
|
|
46884
48077
|
createdAt: external_exports.iso.datetime(),
|
|
@@ -46902,6 +48095,426 @@ var walletRegistrySchema = external_exports.object({
|
|
|
46902
48095
|
wallets: external_exports.record(walletNameSchema, walletEntrySchema)
|
|
46903
48096
|
});
|
|
46904
48097
|
|
|
48098
|
+
// src/movement.ts
|
|
48099
|
+
var movementIdSchema = external_exports.string().regex(/^mv_[A-Za-z0-9_-]{8,128}$/);
|
|
48100
|
+
var nonceSchema = external_exports.string().regex(/^0x[0-9a-fA-F]{64}$/);
|
|
48101
|
+
var amountSchema = external_exports.string().regex(/^\d+\.\d{2}$/);
|
|
48102
|
+
var movementReasonSchema = external_exports.enum([
|
|
48103
|
+
"distribute",
|
|
48104
|
+
"allocate",
|
|
48105
|
+
"rebalance",
|
|
48106
|
+
"delegate",
|
|
48107
|
+
"sweep",
|
|
48108
|
+
"send"
|
|
48109
|
+
]);
|
|
48110
|
+
var movementPurposeSchema = external_exports.enum(["send", "sweep"]);
|
|
48111
|
+
var movementAddressSchema = external_exports.string().refine((value) => isAddress(value, { strict: false }));
|
|
48112
|
+
var movementLegSchema = external_exports.strictObject({
|
|
48113
|
+
from: walletNameSchema,
|
|
48114
|
+
to: external_exports.string().trim().min(1),
|
|
48115
|
+
fromAddress: movementAddressSchema.optional(),
|
|
48116
|
+
toAddress: movementAddressSchema.optional(),
|
|
48117
|
+
amountUsd: amountSchema,
|
|
48118
|
+
purpose: movementPurposeSchema,
|
|
48119
|
+
nonce: nonceSchema,
|
|
48120
|
+
status: external_exports.enum(["planned", "sent", "failed", "unknown", "cancelled"]),
|
|
48121
|
+
txHash: external_exports.string().regex(/^0x[0-9a-fA-F]+$/).optional(),
|
|
48122
|
+
reason: external_exports.string().min(1).optional(),
|
|
48123
|
+
retryable: external_exports.boolean().optional(),
|
|
48124
|
+
restored: external_exports.literal(true).optional(),
|
|
48125
|
+
addressBindingSource: external_exports.enum(["journal", "terminal-review"]).optional()
|
|
48126
|
+
});
|
|
48127
|
+
var movementSchema = external_exports.strictObject({
|
|
48128
|
+
v: external_exports.literal(2),
|
|
48129
|
+
id: movementIdSchema,
|
|
48130
|
+
reason: movementReasonSchema,
|
|
48131
|
+
from: walletNameSchema,
|
|
48132
|
+
// The only account a `sweep` leg may name besides the owner. Sweeps skip the
|
|
48133
|
+
// spend caps, so the parent comes from the plan, never from a leg's `to`.
|
|
48134
|
+
treasury: walletNameSchema.optional(),
|
|
48135
|
+
network: external_exports.enum([BASE_MAINNET_CAIP2, ARC_MAINNET_CAIP2]),
|
|
48136
|
+
createdAt: external_exports.iso.datetime(),
|
|
48137
|
+
legs: external_exports.array(movementLegSchema).min(1)
|
|
48138
|
+
});
|
|
48139
|
+
var legacyMovementSchema = external_exports.strictObject({
|
|
48140
|
+
v: external_exports.literal(1),
|
|
48141
|
+
id: movementIdSchema,
|
|
48142
|
+
reason: external_exports.literal("distribute"),
|
|
48143
|
+
from: walletNameSchema,
|
|
48144
|
+
network: external_exports.enum([BASE_MAINNET_CAIP2, ARC_MAINNET_CAIP2]),
|
|
48145
|
+
createdAt: external_exports.iso.datetime(),
|
|
48146
|
+
legs: external_exports.array(
|
|
48147
|
+
external_exports.strictObject({
|
|
48148
|
+
to: external_exports.string().trim().min(1),
|
|
48149
|
+
amountUsd: amountSchema,
|
|
48150
|
+
nonce: nonceSchema,
|
|
48151
|
+
status: external_exports.enum(["planned", "sent", "failed", "unknown"]),
|
|
48152
|
+
txHash: external_exports.string().regex(/^0x[0-9a-fA-F]+$/).optional(),
|
|
48153
|
+
reason: external_exports.string().min(1).optional(),
|
|
48154
|
+
retryable: external_exports.boolean().optional()
|
|
48155
|
+
})
|
|
48156
|
+
).min(1)
|
|
48157
|
+
});
|
|
48158
|
+
var MovementError = class extends Error {
|
|
48159
|
+
constructor(code, message, movementId) {
|
|
48160
|
+
super(message);
|
|
48161
|
+
this.code = code;
|
|
48162
|
+
this.movementId = movementId;
|
|
48163
|
+
this.name = "MovementError";
|
|
48164
|
+
}
|
|
48165
|
+
code;
|
|
48166
|
+
movementId;
|
|
48167
|
+
};
|
|
48168
|
+
async function listUnfinishedMovementSnapshots(options) {
|
|
48169
|
+
const directory = movementsDirectory(options.home);
|
|
48170
|
+
let names;
|
|
48171
|
+
try {
|
|
48172
|
+
names = await readdir3(directory);
|
|
48173
|
+
} catch (error61) {
|
|
48174
|
+
if (isMissingFile(error61)) return [];
|
|
48175
|
+
throw error61;
|
|
48176
|
+
}
|
|
48177
|
+
const snapshots = [];
|
|
48178
|
+
for (const name of names.filter((candidate) => candidate.endsWith(".json")).sort()) {
|
|
48179
|
+
const movement = parseMovement(JSON.parse(await readFile7(join6(directory, name), "utf8")));
|
|
48180
|
+
if (options.from !== void 0 && !movement.legs.some((leg) => leg.from === options.from)) {
|
|
48181
|
+
continue;
|
|
48182
|
+
}
|
|
48183
|
+
const pendingLegs = movement.legs.filter((leg) => leg.status === "planned").length;
|
|
48184
|
+
const unknownLegs = movement.legs.filter((leg) => leg.status === "unknown").length;
|
|
48185
|
+
const evidence = await movementOpenEvidence(options.home, movement, options.from);
|
|
48186
|
+
if (!evidence.open) continue;
|
|
48187
|
+
snapshots.push({
|
|
48188
|
+
movement,
|
|
48189
|
+
summary: {
|
|
48190
|
+
id: movement.id,
|
|
48191
|
+
from: movement.from,
|
|
48192
|
+
network: movement.network,
|
|
48193
|
+
createdAt: movement.createdAt,
|
|
48194
|
+
pendingLegs,
|
|
48195
|
+
unknownLegs
|
|
48196
|
+
},
|
|
48197
|
+
signedFailedLegKeys: evidence.signedFailedLegKeys
|
|
48198
|
+
});
|
|
48199
|
+
}
|
|
48200
|
+
return snapshots.sort(
|
|
48201
|
+
(left, right) => left.summary.createdAt.localeCompare(right.summary.createdAt) || left.summary.id.localeCompare(right.summary.id)
|
|
48202
|
+
);
|
|
48203
|
+
}
|
|
48204
|
+
async function movementOpenEvidence(home, movement, from) {
|
|
48205
|
+
let open7 = false;
|
|
48206
|
+
const signedFailedLegKeys = [];
|
|
48207
|
+
for (const leg of movement.legs) {
|
|
48208
|
+
if (from !== void 0 && leg.from !== from) continue;
|
|
48209
|
+
if (isOpenMovementLeg(leg, "unsigned")) open7 = true;
|
|
48210
|
+
if (leg.status !== "failed") continue;
|
|
48211
|
+
const journal = await readTransferJournal(home, leg.from, leg.nonce);
|
|
48212
|
+
if (journal.signed) signedFailedLegKeys.push(movementTransferKey(leg.from, leg.nonce));
|
|
48213
|
+
if (isOpenMovementLeg(leg, journal.signed ? "signed" : "unsigned")) open7 = true;
|
|
48214
|
+
}
|
|
48215
|
+
return { open: open7, signedFailedLegKeys };
|
|
48216
|
+
}
|
|
48217
|
+
async function readMovement(home, id) {
|
|
48218
|
+
const safeId = parseMovementId(id);
|
|
48219
|
+
try {
|
|
48220
|
+
return parseMovement(
|
|
48221
|
+
JSON.parse(await readFile7(join6(movementsDirectory(home), `${safeId}.json`), "utf8"))
|
|
48222
|
+
);
|
|
48223
|
+
} catch (error61) {
|
|
48224
|
+
if (isMissingFile(error61)) {
|
|
48225
|
+
throw new MovementError(
|
|
48226
|
+
"movement_not_found",
|
|
48227
|
+
`No movement named ${safeId} was found.`,
|
|
48228
|
+
safeId
|
|
48229
|
+
);
|
|
48230
|
+
}
|
|
48231
|
+
throw error61;
|
|
48232
|
+
}
|
|
48233
|
+
}
|
|
48234
|
+
function movementTransferKey(from, nonce) {
|
|
48235
|
+
return `${from}\0${nonce.toLowerCase()}`;
|
|
48236
|
+
}
|
|
48237
|
+
async function reconcileSignedLegs(home, id, signedKeys) {
|
|
48238
|
+
const initial = await readMovement(home, id);
|
|
48239
|
+
return await withMovementLocks(home, movementSenders(initial), void 0, async () => {
|
|
48240
|
+
const movement = await readMovement(home, id);
|
|
48241
|
+
const reconciled = movementWithSignedLegsUnknown(movement, signedKeys);
|
|
48242
|
+
if (reconciled !== movement) await writeMovement(home, reconciled);
|
|
48243
|
+
return reconciled;
|
|
48244
|
+
});
|
|
48245
|
+
}
|
|
48246
|
+
async function markOpenLegsRestored(home, id) {
|
|
48247
|
+
let initial;
|
|
48248
|
+
try {
|
|
48249
|
+
initial = await readMovement(home, id);
|
|
48250
|
+
} catch (error61) {
|
|
48251
|
+
if (error61 instanceof MovementError && error61.code === "movement_not_found" || error61 instanceof SyntaxError || error61 instanceof external_exports.ZodError) {
|
|
48252
|
+
return void 0;
|
|
48253
|
+
}
|
|
48254
|
+
throw error61;
|
|
48255
|
+
}
|
|
48256
|
+
return await withMovementLocks(home, movementSenders(initial), void 0, async () => {
|
|
48257
|
+
const movement = await readMovement(home, id);
|
|
48258
|
+
let changed = false;
|
|
48259
|
+
const legs = movement.legs.map((leg) => {
|
|
48260
|
+
if (leg.status === "sent" || leg.status === "cancelled" || leg.restored === true) return leg;
|
|
48261
|
+
changed = true;
|
|
48262
|
+
return { ...leg, restored: true };
|
|
48263
|
+
});
|
|
48264
|
+
if (!changed) return movement;
|
|
48265
|
+
const restored = movementSchema.parse({ ...movement, legs });
|
|
48266
|
+
await writeMovement(home, restored);
|
|
48267
|
+
return restored;
|
|
48268
|
+
});
|
|
48269
|
+
}
|
|
48270
|
+
function unknownLeg(leg) {
|
|
48271
|
+
return {
|
|
48272
|
+
from: leg.from,
|
|
48273
|
+
to: leg.to,
|
|
48274
|
+
...legAddressBinding(leg),
|
|
48275
|
+
amountUsd: leg.amountUsd,
|
|
48276
|
+
purpose: leg.purpose,
|
|
48277
|
+
nonce: leg.nonce,
|
|
48278
|
+
status: "unknown",
|
|
48279
|
+
...restoredMarker(leg)
|
|
48280
|
+
};
|
|
48281
|
+
}
|
|
48282
|
+
function restoredMarker(leg) {
|
|
48283
|
+
return leg.restored === true ? { restored: true } : {};
|
|
48284
|
+
}
|
|
48285
|
+
function legAddressBinding(leg) {
|
|
48286
|
+
return {
|
|
48287
|
+
...leg.fromAddress === void 0 ? {} : { fromAddress: leg.fromAddress },
|
|
48288
|
+
...leg.toAddress === void 0 ? {} : { toAddress: leg.toAddress },
|
|
48289
|
+
...leg.addressBindingSource === void 0 ? {} : { addressBindingSource: leg.addressBindingSource }
|
|
48290
|
+
};
|
|
48291
|
+
}
|
|
48292
|
+
function isExpiredRestoredReview(leg) {
|
|
48293
|
+
return leg.restored === true && leg.reason === "restored_nonce_expired";
|
|
48294
|
+
}
|
|
48295
|
+
function movementWithSignedLegsUnknown(movement, signedKeys) {
|
|
48296
|
+
let changed = false;
|
|
48297
|
+
const legs = movement.legs.map((leg) => {
|
|
48298
|
+
if (isExpiredRestoredReview(leg)) return leg;
|
|
48299
|
+
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) {
|
|
48300
|
+
return leg;
|
|
48301
|
+
}
|
|
48302
|
+
changed = true;
|
|
48303
|
+
return unknownLeg(leg);
|
|
48304
|
+
});
|
|
48305
|
+
return changed ? movementSchema.parse({ ...movement, legs }) : movement;
|
|
48306
|
+
}
|
|
48307
|
+
function parseMovement(value) {
|
|
48308
|
+
const current = movementSchema.safeParse(value);
|
|
48309
|
+
if (current.success) return current.data;
|
|
48310
|
+
const legacy = legacyMovementSchema.parse(value);
|
|
48311
|
+
return movementSchema.parse({
|
|
48312
|
+
...legacy,
|
|
48313
|
+
v: 2,
|
|
48314
|
+
legs: legacy.legs.map((leg) => ({
|
|
48315
|
+
...leg,
|
|
48316
|
+
from: legacy.from,
|
|
48317
|
+
purpose: "send"
|
|
48318
|
+
}))
|
|
48319
|
+
});
|
|
48320
|
+
}
|
|
48321
|
+
function movementSenders(movement) {
|
|
48322
|
+
return sortedUniqueSenders(movement.legs.map((leg) => leg.from));
|
|
48323
|
+
}
|
|
48324
|
+
function sortedUniqueSenders(senders) {
|
|
48325
|
+
return [...new Set(senders)].sort();
|
|
48326
|
+
}
|
|
48327
|
+
function parseMovementId(value) {
|
|
48328
|
+
const parsed = movementIdSchema.safeParse(value);
|
|
48329
|
+
if (!parsed.success) throw new MovementError("invalid_movement", "The movement id is invalid.");
|
|
48330
|
+
return parsed.data;
|
|
48331
|
+
}
|
|
48332
|
+
function movementsDirectory(home) {
|
|
48333
|
+
return join6(home, "movements");
|
|
48334
|
+
}
|
|
48335
|
+
async function writeMovement(home, movement) {
|
|
48336
|
+
const parsed = movementSchema.parse(movement);
|
|
48337
|
+
const directory = movementsDirectory(home);
|
|
48338
|
+
await mkdir7(directory, { recursive: true, mode: 448 });
|
|
48339
|
+
await chmod(directory, 448);
|
|
48340
|
+
const path = join6(directory, `${parsed.id}.json`);
|
|
48341
|
+
const temporary = join6(
|
|
48342
|
+
directory,
|
|
48343
|
+
`.${parsed.id}.${process.pid}.${randomBytes6(6).toString("hex")}.tmp`
|
|
48344
|
+
);
|
|
48345
|
+
let handle;
|
|
48346
|
+
try {
|
|
48347
|
+
handle = await open6(temporary, "wx", 384);
|
|
48348
|
+
await handle.writeFile(serializeMovement(parsed), "utf8");
|
|
48349
|
+
await handle.sync();
|
|
48350
|
+
await handle.close();
|
|
48351
|
+
handle = void 0;
|
|
48352
|
+
await rename5(temporary, path);
|
|
48353
|
+
const directoryHandle = await open6(directory, "r");
|
|
48354
|
+
try {
|
|
48355
|
+
await directoryHandle.sync();
|
|
48356
|
+
} finally {
|
|
48357
|
+
await directoryHandle.close();
|
|
48358
|
+
}
|
|
48359
|
+
} catch (error61) {
|
|
48360
|
+
await handle?.close().catch(() => void 0);
|
|
48361
|
+
await unlink6(temporary).catch(() => void 0);
|
|
48362
|
+
throw error61;
|
|
48363
|
+
}
|
|
48364
|
+
}
|
|
48365
|
+
function serializeMovement(movement) {
|
|
48366
|
+
return `${JSON.stringify(movementSchema.parse(movement), null, 2)}
|
|
48367
|
+
`;
|
|
48368
|
+
}
|
|
48369
|
+
async function withMovementLock(home, from, timeoutMs, operation) {
|
|
48370
|
+
try {
|
|
48371
|
+
return await withAccountMovementLocks(home, [from], timeoutMs, operation);
|
|
48372
|
+
} catch (error61) {
|
|
48373
|
+
if (error61 instanceof AccountMovementLockedError) {
|
|
48374
|
+
throw new MovementError(
|
|
48375
|
+
"movement_locked",
|
|
48376
|
+
`Another movement is already running for account ${from}.`
|
|
48377
|
+
);
|
|
48378
|
+
}
|
|
48379
|
+
throw error61;
|
|
48380
|
+
}
|
|
48381
|
+
}
|
|
48382
|
+
async function withMovementLocks(home, senders, timeoutMs, operation) {
|
|
48383
|
+
const [sender, ...remaining] = senders;
|
|
48384
|
+
if (sender === void 0) return await operation();
|
|
48385
|
+
return await withMovementLock(
|
|
48386
|
+
home,
|
|
48387
|
+
sender,
|
|
48388
|
+
timeoutMs,
|
|
48389
|
+
async () => withMovementLocks(home, remaining, timeoutMs, operation)
|
|
48390
|
+
);
|
|
48391
|
+
}
|
|
48392
|
+
|
|
48393
|
+
// src/swarm.ts
|
|
48394
|
+
import { readdir as readdir4, readFile as readFile8, stat as stat6, unlink as unlink7 } from "node:fs/promises";
|
|
48395
|
+
import { join as join7 } from "node:path";
|
|
48396
|
+
var SWARM_NAME_PATTERN = /^[a-z][a-z0-9-]{0,15}$/;
|
|
48397
|
+
var SWARM_ROLE_PATTERN = /^[a-z][a-z0-9]{0,7}$/;
|
|
48398
|
+
var atomicSchema = external_exports.string().regex(/^\d+$/);
|
|
48399
|
+
var addressSchema2 = external_exports.string().regex(/^0x[0-9a-fA-F]{40}$/);
|
|
48400
|
+
var capsSchema = external_exports.strictObject({ perCallAtomic: atomicSchema, perDayAtomic: atomicSchema });
|
|
48401
|
+
var movementIdSchema2 = external_exports.string().regex(/^mv_[A-Za-z0-9_-]{8,128}$/u);
|
|
48402
|
+
var amountUsdSchema = external_exports.string().regex(/^\d+\.\d{2}$/u);
|
|
48403
|
+
var setupFundingStatusSchema = external_exports.enum(["planned", "sent", "incomplete"]);
|
|
48404
|
+
var blockedFundingLegSchema = external_exports.strictObject({
|
|
48405
|
+
to: external_exports.string().trim().min(1),
|
|
48406
|
+
amountUsd: amountUsdSchema,
|
|
48407
|
+
reason: external_exports.string().trim().min(1)
|
|
48408
|
+
});
|
|
48409
|
+
var setupFundingSchema = external_exports.strictObject({
|
|
48410
|
+
amountUsd: amountUsdSchema,
|
|
48411
|
+
from: walletNameSchema,
|
|
48412
|
+
movementId: movementIdSchema2.optional(),
|
|
48413
|
+
status: setupFundingStatusSchema,
|
|
48414
|
+
policy: external_exports.strictObject({
|
|
48415
|
+
strategy: external_exports.enum(["targets", "even", "weights"]),
|
|
48416
|
+
movementId: movementIdSchema2.optional(),
|
|
48417
|
+
status: setupFundingStatusSchema,
|
|
48418
|
+
blocked: external_exports.array(blockedFundingLegSchema).optional()
|
|
48419
|
+
}).optional()
|
|
48420
|
+
});
|
|
48421
|
+
var accountSteps = {
|
|
48422
|
+
creating: external_exports.boolean().default(false),
|
|
48423
|
+
created: external_exports.boolean(),
|
|
48424
|
+
capped: external_exports.boolean(),
|
|
48425
|
+
linked: external_exports.boolean()
|
|
48426
|
+
};
|
|
48427
|
+
var treasurySchema = external_exports.strictObject({
|
|
48428
|
+
account: walletNameSchema,
|
|
48429
|
+
// A planned account has no address until its vault derivation succeeds.
|
|
48430
|
+
address: addressSchema2.optional(),
|
|
48431
|
+
steps: external_exports.strictObject(accountSteps)
|
|
48432
|
+
}).refine(
|
|
48433
|
+
(value) => !value.steps.created || value.address !== void 0,
|
|
48434
|
+
"Created accounts require an address."
|
|
48435
|
+
);
|
|
48436
|
+
var memberSchema = external_exports.strictObject({
|
|
48437
|
+
account: walletNameSchema,
|
|
48438
|
+
address: addressSchema2.optional(),
|
|
48439
|
+
role: external_exports.string().regex(SWARM_ROLE_PATTERN),
|
|
48440
|
+
weight: external_exports.number().int().positive(),
|
|
48441
|
+
targetAtomic: atomicSchema,
|
|
48442
|
+
ceilingAtomic: atomicSchema.nullable(),
|
|
48443
|
+
steps: external_exports.strictObject({ ...accountSteps, profiled: external_exports.boolean() })
|
|
48444
|
+
}).refine(
|
|
48445
|
+
(value) => !value.steps.created || value.address !== void 0,
|
|
48446
|
+
"Created accounts require an address."
|
|
48447
|
+
);
|
|
48448
|
+
var swarmFileSchema = external_exports.strictObject({
|
|
48449
|
+
v: external_exports.literal(1),
|
|
48450
|
+
name: external_exports.string().regex(SWARM_NAME_PATTERN),
|
|
48451
|
+
device: external_exports.string().regex(DEVICE_NAME_PATTERN),
|
|
48452
|
+
network: external_exports.enum([BASE_MAINNET_CAIP2, ARC_MAINNET_CAIP2]),
|
|
48453
|
+
createdAt: external_exports.iso.datetime(),
|
|
48454
|
+
treasury: treasurySchema,
|
|
48455
|
+
members: external_exports.array(memberSchema),
|
|
48456
|
+
setupFunding: setupFundingSchema.optional(),
|
|
48457
|
+
policy: external_exports.strictObject({
|
|
48458
|
+
strategy: external_exports.enum(["targets", "even", "weights"]),
|
|
48459
|
+
treasuryCaps: capsSchema
|
|
48460
|
+
})
|
|
48461
|
+
}).refine(
|
|
48462
|
+
(value) => (/* @__PURE__ */ new Set([value.treasury.account, ...value.members.map((member) => member.account)])).size === value.members.length + 1,
|
|
48463
|
+
"Swarm accounts must be unique."
|
|
48464
|
+
);
|
|
48465
|
+
var SwarmError = class extends Error {
|
|
48466
|
+
constructor(code, message) {
|
|
48467
|
+
super(message);
|
|
48468
|
+
this.code = code;
|
|
48469
|
+
this.name = "SwarmError";
|
|
48470
|
+
}
|
|
48471
|
+
code;
|
|
48472
|
+
};
|
|
48473
|
+
function swarmsDirectory(home) {
|
|
48474
|
+
return join7(home, "swarms");
|
|
48475
|
+
}
|
|
48476
|
+
function swarmFilePath(home, name) {
|
|
48477
|
+
assertName(name);
|
|
48478
|
+
return join7(swarmsDirectory(home), `${name}.json`);
|
|
48479
|
+
}
|
|
48480
|
+
async function readSwarm(home, name) {
|
|
48481
|
+
const path = swarmFilePath(home, name);
|
|
48482
|
+
try {
|
|
48483
|
+
const swarm = parseSwarm(JSON.parse(await readFile8(path, "utf8")));
|
|
48484
|
+
if (swarm.name !== name)
|
|
48485
|
+
throw new SwarmError("invalid_swarm", "Swarm name does not match its file.");
|
|
48486
|
+
return swarm;
|
|
48487
|
+
} catch (error61) {
|
|
48488
|
+
if (isMissingFile(error61)) throw new SwarmError("swarm_not_found", `No swarm named ${name}.`);
|
|
48489
|
+
if (error61 instanceof SwarmError) throw error61;
|
|
48490
|
+
throw new SwarmError("invalid_swarm", `Invalid swarm file for ${name}.`);
|
|
48491
|
+
}
|
|
48492
|
+
}
|
|
48493
|
+
async function listSwarms(home) {
|
|
48494
|
+
let files;
|
|
48495
|
+
try {
|
|
48496
|
+
files = await readdir4(swarmsDirectory(home));
|
|
48497
|
+
} catch (error61) {
|
|
48498
|
+
if (isMissingFile(error61)) return [];
|
|
48499
|
+
throw error61;
|
|
48500
|
+
}
|
|
48501
|
+
return await Promise.all(
|
|
48502
|
+
files.filter((file2) => file2.endsWith(".json")).sort().map((file2) => readSwarm(home, file2.slice(0, -5)))
|
|
48503
|
+
);
|
|
48504
|
+
}
|
|
48505
|
+
function parseSwarm(value) {
|
|
48506
|
+
const result = swarmFileSchema.safeParse(value);
|
|
48507
|
+
if (!result.success) throw new SwarmError("invalid_swarm", "Invalid swarm state.");
|
|
48508
|
+
return result.data;
|
|
48509
|
+
}
|
|
48510
|
+
function assertName(name) {
|
|
48511
|
+
if (!SWARM_NAME_PATTERN.test(name))
|
|
48512
|
+
throw new SwarmError(
|
|
48513
|
+
"invalid_swarm",
|
|
48514
|
+
"A swarm name must be 1 to 16 lowercase letters, digits or dashes, starting with a letter."
|
|
48515
|
+
);
|
|
48516
|
+
}
|
|
48517
|
+
|
|
46905
48518
|
// src/backup.ts
|
|
46906
48519
|
var ERROR_MESSAGES = {
|
|
46907
48520
|
backup_too_large: "Backup envelope exceeds 65,536 bytes.",
|
|
@@ -46910,6 +48523,7 @@ var ERROR_MESSAGES = {
|
|
|
46910
48523
|
unsupported_signature: "Unsupported signature.",
|
|
46911
48524
|
weak_password: "Password must contain at least 12 characters.",
|
|
46912
48525
|
backup_invalid_input: "Invalid backup input.",
|
|
48526
|
+
backup_snapshot_busy: "Backup files changed while they were being captured. Retry the backup; no backup was created or uploaded.",
|
|
46913
48527
|
relay_open_failed: "Unable to open relay payload.",
|
|
46914
48528
|
relay_invalid_key: "Invalid relay key.",
|
|
46915
48529
|
vault_exists: "A vault already exists."
|
|
@@ -46931,6 +48545,15 @@ var SCRYPT_N2 = 131072;
|
|
|
46931
48545
|
var SCRYPT_R2 = 8;
|
|
46932
48546
|
var SCRYPT_P2 = 1;
|
|
46933
48547
|
var SCRYPT_MAXMEM = 256 * 1024 * 1024;
|
|
48548
|
+
var RESTORE_MARKER_FILE = ".backup-restore.json";
|
|
48549
|
+
var restoreDigestSchema = external_exports.string().regex(/^[0-9a-f]{64}$/u);
|
|
48550
|
+
var restoreMarkerSchema = external_exports.strictObject({
|
|
48551
|
+
v: external_exports.literal(1),
|
|
48552
|
+
fingerprint: restoreDigestSchema,
|
|
48553
|
+
vaultSha256: restoreDigestSchema,
|
|
48554
|
+
registrySha256: restoreDigestSchema,
|
|
48555
|
+
mac: restoreDigestSchema
|
|
48556
|
+
});
|
|
46934
48557
|
var SECP256K1_ORDER = BigInt(
|
|
46935
48558
|
"0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141"
|
|
46936
48559
|
);
|
|
@@ -46950,7 +48573,7 @@ var X25519_PRIVATE_DER_PREFIX = Buffer.from("302e020100300506032b656e04220420",
|
|
|
46950
48573
|
var X25519_PUBLIC_DER_PREFIX = Buffer.from("302a300506032b656e032100", "hex");
|
|
46951
48574
|
var CROCKFORD_BASE32 = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
|
|
46952
48575
|
var isoUtcMillisecondsSchema = external_exports.string().refine(isIsoUtcMilliseconds);
|
|
46953
|
-
var
|
|
48576
|
+
var addressSchema3 = external_exports.custom(
|
|
46954
48577
|
(value) => typeof value === "string" && ADDRESS_PATTERN2.test(value)
|
|
46955
48578
|
);
|
|
46956
48579
|
var privateKeySchema = external_exports.custom(
|
|
@@ -46959,7 +48582,7 @@ var privateKeySchema = external_exports.custom(
|
|
|
46959
48582
|
var safeNonnegativeIntegerSchema = external_exports.number().int().nonnegative().refine(Number.isSafeInteger);
|
|
46960
48583
|
var accountFields2 = {
|
|
46961
48584
|
name: walletNameSchema,
|
|
46962
|
-
address:
|
|
48585
|
+
address: addressSchema3,
|
|
46963
48586
|
createdAt: isoUtcMillisecondsSchema
|
|
46964
48587
|
};
|
|
46965
48588
|
var backupAccountSchema = external_exports.discriminatedUnion("kind", [
|
|
@@ -46983,26 +48606,191 @@ var backupRegistryAccountSchema = external_exports.object({
|
|
|
46983
48606
|
label: external_exports.string().optional(),
|
|
46984
48607
|
spendCaps: spendCapsSchema.strict()
|
|
46985
48608
|
}).strict();
|
|
46986
|
-
var
|
|
48609
|
+
var backupRegistryV1Schema = external_exports.object({
|
|
46987
48610
|
default: walletNameSchema.optional(),
|
|
46988
48611
|
accounts: external_exports.array(backupRegistryAccountSchema),
|
|
46989
48612
|
networks: external_exports.array(external_exports.string().regex(CAIP2_PATTERN))
|
|
46990
48613
|
}).strict();
|
|
46991
|
-
var
|
|
48614
|
+
var routerRefillSchema2 = external_exports.strictObject({
|
|
48615
|
+
belowUsd: external_exports.number().finite().min(0),
|
|
48616
|
+
tierUsd: external_exports.union([external_exports.literal(1), external_exports.literal(5), external_exports.literal(20), external_exports.literal(50)])
|
|
48617
|
+
});
|
|
48618
|
+
var backupRegistryAccountV2Schema = external_exports.strictObject({
|
|
48619
|
+
name: walletNameSchema,
|
|
48620
|
+
label: external_exports.string().optional(),
|
|
48621
|
+
spendCaps: spendCapsSchema.strict(),
|
|
48622
|
+
ceilingAtomic: external_exports.string().regex(/^\d+$/u).nullable().optional(),
|
|
48623
|
+
routerRefill: routerRefillSchema2.optional()
|
|
48624
|
+
});
|
|
48625
|
+
var backupRegistryV2Schema = external_exports.strictObject({
|
|
48626
|
+
default: walletNameSchema.optional(),
|
|
48627
|
+
accounts: external_exports.array(backupRegistryAccountV2Schema),
|
|
48628
|
+
networks: external_exports.array(external_exports.string().regex(CAIP2_PATTERN))
|
|
48629
|
+
});
|
|
48630
|
+
var backupAgentProfileSchema = external_exports.strictObject({
|
|
48631
|
+
version: external_exports.literal(1),
|
|
48632
|
+
name: walletNameSchema,
|
|
48633
|
+
wallet: external_exports.string(),
|
|
48634
|
+
model: external_exports.string().min(1),
|
|
48635
|
+
instructions: external_exports.string().max(2e4),
|
|
48636
|
+
verifiedOnly: external_exports.boolean(),
|
|
48637
|
+
approveAboveUsd: external_exports.number().min(0),
|
|
48638
|
+
maxPerTaskUsd: external_exports.number().min(0).optional(),
|
|
48639
|
+
autoReleaseBelowUsd: external_exports.number().min(0).optional(),
|
|
48640
|
+
maxSteps: external_exports.number().int().min(1).max(50),
|
|
48641
|
+
paused: external_exports.boolean(),
|
|
48642
|
+
createdAt: external_exports.string(),
|
|
48643
|
+
tools: external_exports.array(external_exports.string().min(1)),
|
|
48644
|
+
grants: external_exports.array(external_exports.enum(["read", "delegate", "allocate"])).min(1).optional()
|
|
48645
|
+
});
|
|
48646
|
+
var movementIdSchema3 = external_exports.string().regex(/^mv_[A-Za-z0-9_-]{8,128}$/u);
|
|
48647
|
+
var movementNonceSchema = external_exports.string().regex(/^0x[0-9a-fA-F]{64}$/u);
|
|
48648
|
+
var movementAmountSchema = external_exports.string().regex(/^\d+\.\d{2}$/u);
|
|
48649
|
+
var movementStatusSchema = external_exports.enum(["planned", "sent", "failed", "unknown"]);
|
|
48650
|
+
var movementV2StatusSchema = external_exports.enum(["planned", "sent", "failed", "unknown", "cancelled"]);
|
|
48651
|
+
var movementOptionalFields = {
|
|
48652
|
+
txHash: external_exports.string().regex(/^0x[0-9a-fA-F]+$/u).optional(),
|
|
48653
|
+
reason: external_exports.string().min(1).optional(),
|
|
48654
|
+
retryable: external_exports.boolean().optional()
|
|
48655
|
+
};
|
|
48656
|
+
var backupMovementV1Schema = external_exports.strictObject({
|
|
48657
|
+
v: external_exports.literal(1),
|
|
48658
|
+
id: movementIdSchema3,
|
|
48659
|
+
reason: external_exports.literal("distribute"),
|
|
48660
|
+
from: walletNameSchema,
|
|
48661
|
+
network: external_exports.enum([BASE_MAINNET_CAIP2, ARC_MAINNET_CAIP2]),
|
|
48662
|
+
createdAt: external_exports.iso.datetime(),
|
|
48663
|
+
legs: external_exports.array(
|
|
48664
|
+
external_exports.strictObject({
|
|
48665
|
+
to: external_exports.string().trim().min(1),
|
|
48666
|
+
amountUsd: movementAmountSchema,
|
|
48667
|
+
nonce: movementNonceSchema,
|
|
48668
|
+
status: movementStatusSchema,
|
|
48669
|
+
...movementOptionalFields
|
|
48670
|
+
})
|
|
48671
|
+
).min(1)
|
|
48672
|
+
});
|
|
48673
|
+
var backupMovementV2Schema = external_exports.strictObject({
|
|
48674
|
+
v: external_exports.literal(2),
|
|
48675
|
+
id: movementIdSchema3,
|
|
48676
|
+
reason: external_exports.enum(["distribute", "allocate", "rebalance", "delegate", "sweep", "send"]),
|
|
48677
|
+
from: walletNameSchema,
|
|
48678
|
+
treasury: walletNameSchema.optional(),
|
|
48679
|
+
network: external_exports.enum([BASE_MAINNET_CAIP2, ARC_MAINNET_CAIP2]),
|
|
48680
|
+
createdAt: external_exports.iso.datetime(),
|
|
48681
|
+
legs: external_exports.array(
|
|
48682
|
+
external_exports.strictObject({
|
|
48683
|
+
from: walletNameSchema,
|
|
48684
|
+
to: external_exports.string().trim().min(1),
|
|
48685
|
+
amountUsd: movementAmountSchema,
|
|
48686
|
+
purpose: external_exports.enum(["send", "sweep"]),
|
|
48687
|
+
nonce: movementNonceSchema,
|
|
48688
|
+
status: movementV2StatusSchema,
|
|
48689
|
+
...movementOptionalFields
|
|
48690
|
+
})
|
|
48691
|
+
).min(1)
|
|
48692
|
+
});
|
|
48693
|
+
var backupMovementSchema = external_exports.discriminatedUnion("v", [
|
|
48694
|
+
backupMovementV1Schema,
|
|
48695
|
+
backupMovementV2Schema
|
|
48696
|
+
]);
|
|
48697
|
+
var backupTransferRequestSchema = external_exports.strictObject({
|
|
48698
|
+
authorization: external_exports.strictObject({
|
|
48699
|
+
from: addressSchema3,
|
|
48700
|
+
to: addressSchema3,
|
|
48701
|
+
value: external_exports.string().regex(/^\d+$/u),
|
|
48702
|
+
validAfter: external_exports.string().regex(/^\d+$/u),
|
|
48703
|
+
validBefore: external_exports.string().regex(/^\d+$/u),
|
|
48704
|
+
nonce: movementNonceSchema
|
|
48705
|
+
}),
|
|
48706
|
+
signature: external_exports.string().regex(/^0x[0-9a-fA-F]+$/u)
|
|
48707
|
+
});
|
|
48708
|
+
var backupTransferSchema = external_exports.strictObject({
|
|
48709
|
+
to: addressSchema3,
|
|
48710
|
+
toName: external_exports.string().min(1),
|
|
48711
|
+
toKind: external_exports.enum(["owner", "account"]),
|
|
48712
|
+
amountAtomic: external_exports.string().regex(/^\d+$/u),
|
|
48713
|
+
network: external_exports.string().min(1),
|
|
48714
|
+
nonce: movementNonceSchema,
|
|
48715
|
+
status: external_exports.enum(["sent", "unknown", "failed"]),
|
|
48716
|
+
txHash: external_exports.string().regex(/^0x[0-9a-fA-F]+$/u).nullable(),
|
|
48717
|
+
replayed: external_exports.boolean(),
|
|
48718
|
+
reservedOn: external_exports.string().regex(/^\d{4}-\d{2}-\d{2}$/u).optional(),
|
|
48719
|
+
request: backupTransferRequestSchema.optional()
|
|
48720
|
+
});
|
|
48721
|
+
var backupTransferReceiptSchema = external_exports.strictObject({
|
|
48722
|
+
id: external_exports.string().min(1),
|
|
48723
|
+
timestamp: external_exports.iso.datetime(),
|
|
48724
|
+
kind: external_exports.literal("transfer"),
|
|
48725
|
+
wallet: walletNameSchema,
|
|
48726
|
+
resourceUrl: external_exports.url(),
|
|
48727
|
+
quote: external_exports.strictObject({
|
|
48728
|
+
network: external_exports.string().min(1),
|
|
48729
|
+
asset: external_exports.string().min(1),
|
|
48730
|
+
amountAtomic: external_exports.string().regex(/^\d+$/u)
|
|
48731
|
+
}),
|
|
48732
|
+
transfer: backupTransferSchema,
|
|
48733
|
+
error: external_exports.strictObject({ code: external_exports.string(), message: external_exports.string() }).optional()
|
|
48734
|
+
}).superRefine((receipt, context) => {
|
|
48735
|
+
try {
|
|
48736
|
+
parseReceipt(receipt);
|
|
48737
|
+
} catch {
|
|
48738
|
+
context.addIssue({ code: "custom", message: "Invalid transfer receipt." });
|
|
48739
|
+
}
|
|
48740
|
+
});
|
|
48741
|
+
var backupSourceV1Schema = external_exports.object({
|
|
46992
48742
|
phrase: external_exports.string().min(1),
|
|
46993
48743
|
nextDerivedIndex: safeNonnegativeIntegerSchema,
|
|
46994
48744
|
accounts: external_exports.array(backupAccountSchema),
|
|
46995
|
-
registry:
|
|
48745
|
+
registry: backupRegistryV1Schema,
|
|
46996
48746
|
protected: external_exports.boolean()
|
|
46997
48747
|
}).strict().superRefine(validateBackupSourceRelationships);
|
|
46998
|
-
var
|
|
48748
|
+
var backupSourceV2Schema = external_exports.strictObject({
|
|
48749
|
+
phrase: external_exports.string().min(1),
|
|
48750
|
+
nextDerivedIndex: safeNonnegativeIntegerSchema,
|
|
48751
|
+
accounts: external_exports.array(backupAccountSchema),
|
|
48752
|
+
registry: backupRegistryV2Schema,
|
|
48753
|
+
protected: external_exports.boolean(),
|
|
48754
|
+
agents: external_exports.array(backupAgentProfileSchema),
|
|
48755
|
+
swarms: external_exports.array(swarmFileSchema),
|
|
48756
|
+
movements: external_exports.array(backupMovementSchema),
|
|
48757
|
+
transferReceipts: external_exports.array(backupTransferReceiptSchema),
|
|
48758
|
+
skipped: external_exports.array(
|
|
48759
|
+
external_exports.strictObject({
|
|
48760
|
+
kind: external_exports.literal("agent"),
|
|
48761
|
+
name: external_exports.string().min(1),
|
|
48762
|
+
reason: external_exports.literal("invalid_profile")
|
|
48763
|
+
})
|
|
48764
|
+
).optional()
|
|
48765
|
+
}).superRefine((source, context) => {
|
|
48766
|
+
validateBackupSourceRelationships(source, context);
|
|
48767
|
+
validateBackupV2Uniqueness(source, context);
|
|
48768
|
+
});
|
|
48769
|
+
var backupSourceSchema = external_exports.union([backupSourceV1Schema, backupSourceV2Schema]);
|
|
48770
|
+
var backupPlaintextV1Schema = external_exports.strictObject({
|
|
46999
48771
|
v: external_exports.literal(1),
|
|
47000
48772
|
phrase: external_exports.string().min(1),
|
|
47001
48773
|
nextDerivedIndex: safeNonnegativeIntegerSchema,
|
|
47002
48774
|
accounts: external_exports.array(backupAccountSchema),
|
|
47003
|
-
registry:
|
|
48775
|
+
registry: backupRegistryV1Schema,
|
|
47004
48776
|
protected: external_exports.boolean()
|
|
47005
|
-
})
|
|
48777
|
+
});
|
|
48778
|
+
var backupPlaintextV2Schema = external_exports.strictObject({
|
|
48779
|
+
v: external_exports.literal(2),
|
|
48780
|
+
phrase: external_exports.string().min(1),
|
|
48781
|
+
nextDerivedIndex: safeNonnegativeIntegerSchema,
|
|
48782
|
+
accounts: external_exports.array(backupAccountSchema),
|
|
48783
|
+
registry: backupRegistryV2Schema,
|
|
48784
|
+
protected: external_exports.boolean(),
|
|
48785
|
+
agents: external_exports.array(backupAgentProfileSchema).optional(),
|
|
48786
|
+
swarms: external_exports.array(swarmFileSchema).optional(),
|
|
48787
|
+
movements: external_exports.array(backupMovementSchema).optional(),
|
|
48788
|
+
transferReceipts: external_exports.array(backupTransferReceiptSchema).optional()
|
|
48789
|
+
});
|
|
48790
|
+
var backupPlaintextSchema = external_exports.discriminatedUnion("v", [backupPlaintextV1Schema, backupPlaintextV2Schema]).superRefine((plaintext, context) => {
|
|
48791
|
+
validateBackupSourceRelationships(plaintext, context);
|
|
48792
|
+
if (plaintext.v === 2) validateBackupV2Uniqueness(plaintext, context);
|
|
48793
|
+
});
|
|
47006
48794
|
async function createBackup(options) {
|
|
47007
48795
|
const parsed = backupSourceSchema.safeParse(options.vault);
|
|
47008
48796
|
if (!parsed.success) throw new BackupError("backup_invalid_input");
|
|
@@ -47010,8 +48798,8 @@ async function createBackup(options) {
|
|
|
47010
48798
|
assertDevice(options.device);
|
|
47011
48799
|
assertOwnerKey(options.ownerKey);
|
|
47012
48800
|
const createdAt = readCreatedAt(options.now ?? (() => /* @__PURE__ */ new Date()));
|
|
47013
|
-
const
|
|
47014
|
-
const nonce = readRandomBytes(
|
|
48801
|
+
const randomBytes7 = options.randomBytes ?? cryptoRandomBytes2;
|
|
48802
|
+
const nonce = readRandomBytes(randomBytes7, 12);
|
|
47015
48803
|
const salt = encodeBase64Url(options.ownerKey.salt);
|
|
47016
48804
|
const kdf = options.ownerKey.kdf === "hkdf-sha256" ? { name: "hkdf-sha256", salt } : { name: "scrypt", salt, N: SCRYPT_N2, r: SCRYPT_R2, p: SCRYPT_P2 };
|
|
47017
48805
|
const cipher = { name: CIPHER_NAME, nonce: encodeBase64Url(nonce) };
|
|
@@ -47024,30 +48812,54 @@ async function createBackup(options) {
|
|
|
47024
48812
|
kdf,
|
|
47025
48813
|
cipher
|
|
47026
48814
|
};
|
|
47027
|
-
const plaintextValue = buildPlaintext(parsed.data);
|
|
47028
|
-
const plaintext = Buffer.from(JSON.stringify(plaintextValue), "utf8");
|
|
47029
48815
|
const key = Buffer.from(options.ownerKey.key);
|
|
47030
|
-
let encrypted;
|
|
47031
48816
|
try {
|
|
47032
|
-
|
|
47033
|
-
const
|
|
47034
|
-
|
|
47035
|
-
|
|
47036
|
-
|
|
47037
|
-
|
|
47038
|
-
|
|
47039
|
-
|
|
47040
|
-
|
|
47041
|
-
|
|
47042
|
-
|
|
47043
|
-
|
|
47044
|
-
|
|
47045
|
-
|
|
48817
|
+
const candidates = backupCandidates(parsed.data);
|
|
48818
|
+
for (const candidate of candidates) {
|
|
48819
|
+
const plaintext = Buffer.from(JSON.stringify(candidate.plaintext), "utf8");
|
|
48820
|
+
let encrypted;
|
|
48821
|
+
try {
|
|
48822
|
+
encrypted = encryptAesGcm(
|
|
48823
|
+
key,
|
|
48824
|
+
nonce,
|
|
48825
|
+
plaintext,
|
|
48826
|
+
Buffer.from(buildBackupAad(header), "utf8")
|
|
48827
|
+
);
|
|
48828
|
+
const envelope = JSON.stringify({
|
|
48829
|
+
format: header.format,
|
|
48830
|
+
v: header.v,
|
|
48831
|
+
owner: header.owner,
|
|
48832
|
+
device: header.device,
|
|
48833
|
+
createdAt: header.createdAt,
|
|
48834
|
+
kdf: header.kdf,
|
|
48835
|
+
cipher: header.cipher,
|
|
48836
|
+
ciphertext: encodeBase64Url(encrypted)
|
|
48837
|
+
});
|
|
48838
|
+
const bytes = Buffer.byteLength(envelope, "utf8");
|
|
48839
|
+
const relayPayload = buildRelayRestorePayload({
|
|
48840
|
+
owner,
|
|
48841
|
+
device: "d".repeat(32),
|
|
48842
|
+
kdf,
|
|
48843
|
+
key: Buffer.alloc(32),
|
|
48844
|
+
vault: candidate.plaintext
|
|
48845
|
+
});
|
|
48846
|
+
const relayBytes = Buffer.byteLength(relayPayload, "utf8");
|
|
48847
|
+
if (bytes <= MAX_BACKUP_BYTES && relayBytes <= RELAY_PLAINTEXT_LIMIT) {
|
|
48848
|
+
return {
|
|
48849
|
+
envelope,
|
|
48850
|
+
bytes,
|
|
48851
|
+
...candidate.omitted.length === 0 ? {} : { omitted: candidate.omitted }
|
|
48852
|
+
};
|
|
48853
|
+
}
|
|
48854
|
+
} finally {
|
|
48855
|
+
plaintext.fill(0);
|
|
48856
|
+
encrypted?.fill(0);
|
|
48857
|
+
}
|
|
48858
|
+
}
|
|
48859
|
+
throw new BackupError("backup_too_large");
|
|
47046
48860
|
} finally {
|
|
47047
|
-
plaintext.fill(0);
|
|
47048
48861
|
key.fill(0);
|
|
47049
48862
|
nonce.fill(0);
|
|
47050
|
-
encrypted?.fill(0);
|
|
47051
48863
|
}
|
|
47052
48864
|
}
|
|
47053
48865
|
function createRelayKeyPair(options = {}) {
|
|
@@ -47070,7 +48882,7 @@ function createRelayKeyPair(options = {}) {
|
|
|
47070
48882
|
}
|
|
47071
48883
|
function relayCode(publicKey) {
|
|
47072
48884
|
assertRelayKey(publicKey);
|
|
47073
|
-
const digest =
|
|
48885
|
+
const digest = createHash2("sha256").update(publicKey).digest();
|
|
47074
48886
|
try {
|
|
47075
48887
|
let bits = BigInt(`0x${digest.subarray(0, 8).toString("hex")}`) >> 4n;
|
|
47076
48888
|
const characters = Array.from({ length: 12 }, () => "0");
|
|
@@ -47134,69 +48946,173 @@ function openSealed(options) {
|
|
|
47134
48946
|
plaintext?.fill(0);
|
|
47135
48947
|
}
|
|
47136
48948
|
}
|
|
48949
|
+
function buildRelayRestorePayload(options) {
|
|
48950
|
+
try {
|
|
48951
|
+
const owner = normalizedOwner(options.owner);
|
|
48952
|
+
assertDevice(options.device);
|
|
48953
|
+
assertBytes(options.key, 32);
|
|
48954
|
+
if (!isRecord2(options.kdf)) throw new Error();
|
|
48955
|
+
const kdf = parseKdf(options.kdf);
|
|
48956
|
+
const parsed = backupPlaintextSchema.safeParse(options.vault);
|
|
48957
|
+
if (!parsed.success) throw new Error();
|
|
48958
|
+
const vault = parsed.data.v === 1 ? buildPlaintextV1(parsed.data) : buildPlaintextV2(parsed.data);
|
|
48959
|
+
return JSON.stringify({
|
|
48960
|
+
format: "vapi-vault-relay-payload",
|
|
48961
|
+
v: 1,
|
|
48962
|
+
purpose: "restore",
|
|
48963
|
+
owner,
|
|
48964
|
+
device: options.device,
|
|
48965
|
+
kdf,
|
|
48966
|
+
key: encodeBase64Url(options.key),
|
|
48967
|
+
vault
|
|
48968
|
+
});
|
|
48969
|
+
} catch {
|
|
48970
|
+
throw new BackupError("backup_invalid_input");
|
|
48971
|
+
}
|
|
48972
|
+
}
|
|
47137
48973
|
async function exportBackupSource(options) {
|
|
47138
48974
|
try {
|
|
47139
48975
|
assertBytes(options.vaultKey, 32);
|
|
47140
|
-
if (!
|
|
48976
|
+
if (!isRecord2(options.store) || typeof options.store.home !== "string" || typeof options.store.snapshot !== "function" || typeof options.store.reload !== "function") {
|
|
47141
48977
|
throw new BackupError("backup_invalid_input");
|
|
47142
48978
|
}
|
|
47143
48979
|
const networks = normalizeNetworks(options.networks ?? []);
|
|
47144
|
-
|
|
47145
|
-
|
|
47146
|
-
|
|
47147
|
-
|
|
47148
|
-
|
|
47149
|
-
readVaultFileUnlocked(path)
|
|
47150
|
-
]);
|
|
47151
|
-
const accounts2 = [];
|
|
47152
|
-
for (const account of vault.accounts()) {
|
|
47153
|
-
if (account.kind === "derived") {
|
|
47154
|
-
accounts2.push({ ...account });
|
|
47155
|
-
} else {
|
|
47156
|
-
const privateKey = await exportVaultAccountKey({
|
|
47157
|
-
path,
|
|
47158
|
-
key: options.vaultKey,
|
|
47159
|
-
name: account.name
|
|
47160
|
-
});
|
|
47161
|
-
accounts2.push({ ...account, privateKey });
|
|
47162
|
-
}
|
|
48980
|
+
for (let attempt = 0; attempt < 3; attempt += 1) {
|
|
48981
|
+
try {
|
|
48982
|
+
return await exportBackupSourceAttempt(options, networks);
|
|
48983
|
+
} catch (error61) {
|
|
48984
|
+
if (!(error61 instanceof BackupSnapshotChangedError)) throw error61;
|
|
47163
48985
|
}
|
|
47164
|
-
return { phrase: phrase2, file: file3, accounts: accounts2 };
|
|
47165
|
-
});
|
|
47166
|
-
const snapshot = await options.store.reload();
|
|
47167
|
-
const names = new Set(accounts.map((account) => account.name));
|
|
47168
|
-
const source = {
|
|
47169
|
-
phrase,
|
|
47170
|
-
nextDerivedIndex: file2.nextDerivedIndex ?? accounts.reduce(
|
|
47171
|
-
(next, account) => account.kind === "derived" ? Math.max(next, account.index + 1) : next,
|
|
47172
|
-
0
|
|
47173
|
-
),
|
|
47174
|
-
accounts,
|
|
47175
|
-
registry: {
|
|
47176
|
-
...snapshot.default !== void 0 && names.has(snapshot.default) ? { default: snapshot.default } : {},
|
|
47177
|
-
accounts: accounts.map((account) => {
|
|
47178
|
-
const entry = snapshot.wallets[account.name];
|
|
47179
|
-
return {
|
|
47180
|
-
name: account.name,
|
|
47181
|
-
...entry?.label === void 0 ? {} : { label: entry.label },
|
|
47182
|
-
spendCaps: {
|
|
47183
|
-
perCallAtomic: entry?.spendCaps.perCallAtomic ?? DEFAULT_SPEND_CAPS.perCallAtomic,
|
|
47184
|
-
perDayAtomic: entry?.spendCaps.perDayAtomic ?? DEFAULT_SPEND_CAPS.perDayAtomic
|
|
47185
|
-
}
|
|
47186
|
-
};
|
|
47187
|
-
}),
|
|
47188
|
-
networks
|
|
47189
|
-
},
|
|
47190
|
-
protected: file2.protected
|
|
47191
|
-
};
|
|
47192
|
-
if (!backupSourceSchema.safeParse(source).success) {
|
|
47193
|
-
throw new BackupError("backup_invalid_input");
|
|
47194
48986
|
}
|
|
47195
|
-
|
|
47196
|
-
} catch {
|
|
48987
|
+
throw new BackupError("backup_snapshot_busy");
|
|
48988
|
+
} catch (error61) {
|
|
48989
|
+
if (error61 instanceof BackupError && error61.code === "backup_snapshot_busy") throw error61;
|
|
47197
48990
|
throw new BackupError("backup_invalid_input");
|
|
47198
48991
|
}
|
|
47199
48992
|
}
|
|
48993
|
+
var BackupSnapshotChangedError = class extends Error {
|
|
48994
|
+
};
|
|
48995
|
+
async function exportBackupSourceAttempt(options, networks) {
|
|
48996
|
+
const path = join8(options.store.home, "vault.json");
|
|
48997
|
+
const { phrase, file: file2, accounts } = await withVaultLock(path, async () => {
|
|
48998
|
+
const [phrase2, vault, file3] = await Promise.all([
|
|
48999
|
+
exportVaultPhrase({ path, key: options.vaultKey }),
|
|
49000
|
+
openVault({ path, key: options.vaultKey }),
|
|
49001
|
+
readVaultFileUnlocked(path)
|
|
49002
|
+
]);
|
|
49003
|
+
const accounts2 = [];
|
|
49004
|
+
for (const account of vault.accounts()) {
|
|
49005
|
+
if (account.kind === "derived") {
|
|
49006
|
+
accounts2.push({ ...account });
|
|
49007
|
+
} else {
|
|
49008
|
+
const privateKey = await exportVaultAccountKey({
|
|
49009
|
+
path,
|
|
49010
|
+
key: options.vaultKey,
|
|
49011
|
+
name: account.name
|
|
49012
|
+
});
|
|
49013
|
+
accounts2.push({ ...account, privateKey });
|
|
49014
|
+
}
|
|
49015
|
+
}
|
|
49016
|
+
return { phrase: phrase2, file: file3, accounts: accounts2 };
|
|
49017
|
+
});
|
|
49018
|
+
const snapshot = await options.store.reload();
|
|
49019
|
+
const names = new Set(accounts.map((account) => account.name));
|
|
49020
|
+
const home = options.store.home;
|
|
49021
|
+
const skipped = [];
|
|
49022
|
+
const [agents, swarms, movementSnapshots] = await Promise.all([
|
|
49023
|
+
listAgentProfiles(home, {
|
|
49024
|
+
schema: options.agentProfileSchema ?? agentProfileSchema,
|
|
49025
|
+
warn: () => void 0,
|
|
49026
|
+
onInvalid: (profileFile) => {
|
|
49027
|
+
skipped.push({
|
|
49028
|
+
kind: "agent",
|
|
49029
|
+
name: profileFile.endsWith(".json") ? profileFile.slice(0, -5) : profileFile,
|
|
49030
|
+
reason: "invalid_profile"
|
|
49031
|
+
});
|
|
49032
|
+
}
|
|
49033
|
+
}),
|
|
49034
|
+
listSwarms(home),
|
|
49035
|
+
listUnfinishedMovementSnapshots({ home })
|
|
49036
|
+
]);
|
|
49037
|
+
const localMovements = await Promise.all(
|
|
49038
|
+
movementSnapshots.map(async ({ summary }) => await readMovement(home, summary.id))
|
|
49039
|
+
);
|
|
49040
|
+
localMovements.sort(compareMovements);
|
|
49041
|
+
const expectedSignedFailedLegs = movementSnapshots.flatMap(
|
|
49042
|
+
({ signedFailedLegKeys }) => signedFailedLegKeys
|
|
49043
|
+
);
|
|
49044
|
+
const receipts = await readReceiptJournal(getVapiPaths(home).receipts);
|
|
49045
|
+
const receiptLegs = new Set(
|
|
49046
|
+
receipts.flatMap(
|
|
49047
|
+
(receipt) => receipt.kind === "transfer" && receipt.transfer !== void 0 ? [movementTransferKey(receiptWallet(receipt), receipt.transfer.nonce)] : []
|
|
49048
|
+
)
|
|
49049
|
+
);
|
|
49050
|
+
const requiredReceiptLegs = /* @__PURE__ */ new Set([
|
|
49051
|
+
...expectedSignedFailedLegs,
|
|
49052
|
+
...localMovements.flatMap(
|
|
49053
|
+
(movement) => movement.legs.filter((leg) => leg.status === "unknown").map((leg) => movementTransferKey(leg.from, leg.nonce))
|
|
49054
|
+
)
|
|
49055
|
+
]);
|
|
49056
|
+
const verifiedFile = await readVaultFileUnlocked(path);
|
|
49057
|
+
const verifiedSnapshot = await options.store.reload();
|
|
49058
|
+
if ([...requiredReceiptLegs].some((leg) => !receiptLegs.has(leg)) || JSON.stringify(file2) !== JSON.stringify(verifiedFile) || JSON.stringify(snapshot) !== JSON.stringify(verifiedSnapshot)) {
|
|
49059
|
+
throw new BackupSnapshotChangedError();
|
|
49060
|
+
}
|
|
49061
|
+
const movements = localMovements.map(toBackupMovement);
|
|
49062
|
+
const includedLegs = new Set(
|
|
49063
|
+
localMovements.flatMap(
|
|
49064
|
+
(movement) => movement.legs.map((leg) => movementTransferKey(leg.from, leg.nonce))
|
|
49065
|
+
)
|
|
49066
|
+
);
|
|
49067
|
+
const transferReceipts = receipts.filter(
|
|
49068
|
+
(receipt) => receipt.kind === "transfer" && receipt.transfer !== void 0 && includedLegs.has(movementTransferKey(receiptWallet(receipt), receipt.transfer.nonce))
|
|
49069
|
+
).map(toBackupTransferReceipt);
|
|
49070
|
+
const source = {
|
|
49071
|
+
phrase,
|
|
49072
|
+
nextDerivedIndex: file2.nextDerivedIndex ?? accounts.reduce(
|
|
49073
|
+
(next, account) => account.kind === "derived" ? Math.max(next, account.index + 1) : next,
|
|
49074
|
+
0
|
|
49075
|
+
),
|
|
49076
|
+
accounts,
|
|
49077
|
+
registry: {
|
|
49078
|
+
...snapshot.default !== void 0 && names.has(snapshot.default) ? { default: snapshot.default } : {},
|
|
49079
|
+
accounts: accounts.map((account) => {
|
|
49080
|
+
const entry = snapshot.wallets[account.name];
|
|
49081
|
+
const ceilingAtomic = entry?.ceilingAtomic === null ? null : entry?.ceilingAtomic ?? DEFAULT_CEILING_ATOMIC.toString();
|
|
49082
|
+
return {
|
|
49083
|
+
name: account.name,
|
|
49084
|
+
...entry?.label === void 0 ? {} : { label: entry.label },
|
|
49085
|
+
spendCaps: {
|
|
49086
|
+
perCallAtomic: entry?.spendCaps.perCallAtomic ?? DEFAULT_SPEND_CAPS.perCallAtomic,
|
|
49087
|
+
perDayAtomic: entry?.spendCaps.perDayAtomic ?? DEFAULT_SPEND_CAPS.perDayAtomic
|
|
49088
|
+
},
|
|
49089
|
+
...ceilingAtomic === DEFAULT_CEILING_ATOMIC.toString() ? {} : { ceilingAtomic },
|
|
49090
|
+
...entry?.routerRefill === void 0 ? {} : {
|
|
49091
|
+
routerRefill: {
|
|
49092
|
+
belowUsd: entry.routerRefill.belowUsd,
|
|
49093
|
+
tierUsd: entry.routerRefill.tierUsd
|
|
49094
|
+
}
|
|
49095
|
+
}
|
|
49096
|
+
};
|
|
49097
|
+
}),
|
|
49098
|
+
networks
|
|
49099
|
+
},
|
|
49100
|
+
protected: file2.protected,
|
|
49101
|
+
agents: agents.map(
|
|
49102
|
+
({ grants, ...profile }) => backupAgentProfileSchema.parse(
|
|
49103
|
+
grants === void 0 || grants.length === 0 ? profile : { ...profile, grants }
|
|
49104
|
+
)
|
|
49105
|
+
).sort(compareNamedValues),
|
|
49106
|
+
swarms: [...swarms].sort(compareNamedValues),
|
|
49107
|
+
movements,
|
|
49108
|
+
transferReceipts,
|
|
49109
|
+
...skipped.length === 0 ? {} : { skipped: skipped.sort(compareNamedValues) }
|
|
49110
|
+
};
|
|
49111
|
+
if (!backupSourceSchema.safeParse(source).success) {
|
|
49112
|
+
throw new BackupError("backup_invalid_input");
|
|
49113
|
+
}
|
|
49114
|
+
return source;
|
|
49115
|
+
}
|
|
47200
49116
|
function parseBackupPlaintext(value) {
|
|
47201
49117
|
return validateRestorePlaintext(value);
|
|
47202
49118
|
}
|
|
@@ -47205,20 +49121,24 @@ async function restoreBackupPlaintext(options) {
|
|
|
47205
49121
|
if (typeof options.home !== "string" || options.home.length === 0) {
|
|
47206
49122
|
throw new BackupError("backup_invalid_input");
|
|
47207
49123
|
}
|
|
49124
|
+
if (options.device !== void 0 && !DEVICE_NAME_PATTERN.test(options.device)) {
|
|
49125
|
+
throw new BackupError("backup_invalid_input");
|
|
49126
|
+
}
|
|
47208
49127
|
try {
|
|
47209
49128
|
assertBytes(options.vaultKey, 32);
|
|
47210
49129
|
} catch {
|
|
47211
49130
|
throw new BackupError("backup_invalid_input");
|
|
47212
49131
|
}
|
|
47213
|
-
const vaultPath =
|
|
47214
|
-
const registryPath =
|
|
49132
|
+
const vaultPath = join8(options.home, "vault.json");
|
|
49133
|
+
const registryPath = join8(options.home, "wallets.json");
|
|
47215
49134
|
let stagingHome;
|
|
47216
49135
|
let beforePublishError;
|
|
49136
|
+
let afterPublishError;
|
|
47217
49137
|
try {
|
|
47218
|
-
await
|
|
47219
|
-
stagingHome = await mkdtemp(
|
|
47220
|
-
const stagedVaultPath =
|
|
47221
|
-
const stagedRegistryPath =
|
|
49138
|
+
await mkdir8(options.home, { recursive: true, mode: 448 });
|
|
49139
|
+
stagingHome = await mkdtemp(join8(options.home, ".backup-restore-"));
|
|
49140
|
+
const stagedVaultPath = join8(stagingHome, "vault.json");
|
|
49141
|
+
const stagedRegistryPath = join8(stagingHome, "wallets.json");
|
|
47222
49142
|
await createVault({
|
|
47223
49143
|
path: stagedVaultPath,
|
|
47224
49144
|
key: options.vaultKey,
|
|
@@ -47264,16 +49184,59 @@ async function restoreBackupPlaintext(options) {
|
|
|
47264
49184
|
} finally {
|
|
47265
49185
|
await handle.close();
|
|
47266
49186
|
}
|
|
49187
|
+
const stagedV2 = plaintext.v === 2 ? await stageV2Restore({
|
|
49188
|
+
plaintext,
|
|
49189
|
+
stagingHome,
|
|
49190
|
+
home: options.home,
|
|
49191
|
+
...options.device === void 0 ? {} : { device: options.device }
|
|
49192
|
+
}) : void 0;
|
|
47267
49193
|
const accounts = (await openVault({ path: stagedVaultPath, key: options.vaultKey })).accounts();
|
|
47268
|
-
const
|
|
49194
|
+
const baseResult = {
|
|
47269
49195
|
accounts,
|
|
47270
49196
|
...plaintext.registry.default === void 0 ? {} : { default: plaintext.registry.default },
|
|
47271
49197
|
protected: plaintext.protected,
|
|
47272
49198
|
networks: [...plaintext.registry.networks]
|
|
47273
49199
|
};
|
|
49200
|
+
const markerPath = join8(options.home, RESTORE_MARKER_FILE);
|
|
49201
|
+
const fingerprint = restoreFingerprint(
|
|
49202
|
+
plaintext,
|
|
49203
|
+
stagedV2?.restoreDevice ?? options.device,
|
|
49204
|
+
options.vaultKey
|
|
49205
|
+
);
|
|
47274
49206
|
await withVaultLock(vaultPath, async () => {
|
|
47275
|
-
|
|
47276
|
-
|
|
49207
|
+
const existingVault = await pathExists2(vaultPath);
|
|
49208
|
+
const existingRegistry = await pathExists2(registryPath);
|
|
49209
|
+
let marker;
|
|
49210
|
+
if (existingVault || existingRegistry) {
|
|
49211
|
+
if (!existingVault) throw new BackupError("vault_exists");
|
|
49212
|
+
marker = await readRestoreMarker(markerPath, fingerprint, options.vaultKey);
|
|
49213
|
+
if (await sha256File(vaultPath) !== marker.vaultSha256) {
|
|
49214
|
+
throw new BackupError("vault_exists");
|
|
49215
|
+
}
|
|
49216
|
+
if (existingRegistry) {
|
|
49217
|
+
if (await sha256File(registryPath) !== marker.registrySha256) {
|
|
49218
|
+
throw new BackupError("vault_exists");
|
|
49219
|
+
}
|
|
49220
|
+
} else if (await sha256File(stagedRegistryPath) !== marker.registrySha256) {
|
|
49221
|
+
throw new BackupError("vault_exists");
|
|
49222
|
+
}
|
|
49223
|
+
} else {
|
|
49224
|
+
marker = await createRestoreMarker(
|
|
49225
|
+
stagedVaultPath,
|
|
49226
|
+
stagedRegistryPath,
|
|
49227
|
+
fingerprint,
|
|
49228
|
+
options.vaultKey
|
|
49229
|
+
);
|
|
49230
|
+
await writeJsonAtomic(markerPath, marker, { mode: 384 });
|
|
49231
|
+
await notifyRestorePublish(options.afterPublish, { kind: "marker" }, (error61) => {
|
|
49232
|
+
afterPublishError = error61;
|
|
49233
|
+
});
|
|
49234
|
+
}
|
|
49235
|
+
if (stagedV2 !== void 0) {
|
|
49236
|
+
await publishV2RestoreSafety(stagedV2, options.home);
|
|
49237
|
+
await notifyRestorePublish(options.afterPublish, { kind: "safety" }, (error61) => {
|
|
49238
|
+
afterPublishError = error61;
|
|
49239
|
+
});
|
|
47277
49240
|
}
|
|
47278
49241
|
try {
|
|
47279
49242
|
await options.beforePublish?.({ vault: stagedVaultPath, registry: stagedRegistryPath });
|
|
@@ -47281,21 +49244,66 @@ async function restoreBackupPlaintext(options) {
|
|
|
47281
49244
|
beforePublishError = error61;
|
|
47282
49245
|
throw error61;
|
|
47283
49246
|
}
|
|
49247
|
+
if (!existingVault && !existingRegistry) {
|
|
49248
|
+
const refreshedMarker = await createRestoreMarker(
|
|
49249
|
+
stagedVaultPath,
|
|
49250
|
+
stagedRegistryPath,
|
|
49251
|
+
fingerprint,
|
|
49252
|
+
options.vaultKey
|
|
49253
|
+
);
|
|
49254
|
+
if (refreshedMarker.vaultSha256 !== marker.vaultSha256 || refreshedMarker.registrySha256 !== marker.registrySha256) {
|
|
49255
|
+
marker = refreshedMarker;
|
|
49256
|
+
await writeJsonAtomic(markerPath, marker, { mode: 384 });
|
|
49257
|
+
}
|
|
49258
|
+
}
|
|
47284
49259
|
let publishedVault = false;
|
|
47285
|
-
|
|
47286
|
-
|
|
47287
|
-
|
|
47288
|
-
|
|
47289
|
-
|
|
47290
|
-
|
|
47291
|
-
|
|
49260
|
+
if (!existingVault) {
|
|
49261
|
+
try {
|
|
49262
|
+
await link2(stagedVaultPath, vaultPath);
|
|
49263
|
+
publishedVault = true;
|
|
49264
|
+
} catch (error61) {
|
|
49265
|
+
await rm2(markerPath, { force: true }).catch(() => void 0);
|
|
49266
|
+
throw error61;
|
|
49267
|
+
}
|
|
49268
|
+
await notifyRestorePublish(options.afterPublish, { kind: "vault" }, (error61) => {
|
|
49269
|
+
afterPublishError = error61;
|
|
49270
|
+
});
|
|
49271
|
+
}
|
|
49272
|
+
if (!existingRegistry) {
|
|
49273
|
+
try {
|
|
49274
|
+
await link2(stagedRegistryPath, registryPath);
|
|
49275
|
+
} catch (error61) {
|
|
49276
|
+
if (publishedVault) await rm2(vaultPath, { force: true }).catch(() => void 0);
|
|
49277
|
+
await rm2(markerPath, { force: true }).catch(() => void 0);
|
|
49278
|
+
throw error61;
|
|
49279
|
+
}
|
|
49280
|
+
await notifyRestorePublish(options.afterPublish, { kind: "registry" }, (error61) => {
|
|
49281
|
+
afterPublishError = error61;
|
|
49282
|
+
});
|
|
49283
|
+
}
|
|
49284
|
+
if (stagedV2 !== void 0) {
|
|
49285
|
+
await publishV2Restore(stagedV2, options.home, async (step) => {
|
|
49286
|
+
await notifyRestorePublish(options.afterPublish, step, (error61) => {
|
|
49287
|
+
afterPublishError = error61;
|
|
49288
|
+
});
|
|
49289
|
+
});
|
|
47292
49290
|
}
|
|
49291
|
+
await rm2(markerPath, { force: true });
|
|
47293
49292
|
});
|
|
47294
|
-
return
|
|
49293
|
+
if (stagedV2 === void 0) return baseResult;
|
|
49294
|
+
return {
|
|
49295
|
+
...baseResult,
|
|
49296
|
+
agents: stagedV2.restoredAgents,
|
|
49297
|
+
swarms: stagedV2.restoredSwarms,
|
|
49298
|
+
movements: stagedV2.restoredMovements,
|
|
49299
|
+
skipped: stagedV2.skipped,
|
|
49300
|
+
conflicts: stagedV2.conflicts
|
|
49301
|
+
};
|
|
47295
49302
|
} catch (error61) {
|
|
47296
49303
|
if (error61 === beforePublishError) throw error61;
|
|
49304
|
+
if (error61 === afterPublishError) throw error61;
|
|
47297
49305
|
if (error61 instanceof BackupError && error61.code === "vault_exists") throw error61;
|
|
47298
|
-
if (isExistingFile2(error61) || await
|
|
49306
|
+
if (isExistingFile2(error61) || await pathExists2(vaultPath).catch(() => false) || await pathExists2(registryPath).catch(() => false)) {
|
|
47299
49307
|
throw new BackupError("vault_exists");
|
|
47300
49308
|
}
|
|
47301
49309
|
throw new BackupError("backup_invalid_input");
|
|
@@ -47305,9 +49313,400 @@ async function restoreBackupPlaintext(options) {
|
|
|
47305
49313
|
}
|
|
47306
49314
|
}
|
|
47307
49315
|
}
|
|
49316
|
+
async function stageV2Restore(options) {
|
|
49317
|
+
const skipped = [];
|
|
49318
|
+
const conflicts = [];
|
|
49319
|
+
const accounts = new Map(options.plaintext.accounts.map((account) => [account.name, account]));
|
|
49320
|
+
const validAgents = (options.plaintext.agents ?? []).filter((profile) => {
|
|
49321
|
+
if (accounts.has(profile.wallet)) return true;
|
|
49322
|
+
skipped.push({ kind: "agent", name: profile.name, reason: "missing_account" });
|
|
49323
|
+
return false;
|
|
49324
|
+
});
|
|
49325
|
+
const validSwarms = (options.plaintext.swarms ?? []).filter((swarm) => {
|
|
49326
|
+
const treasury = accounts.get(swarm.treasury.account);
|
|
49327
|
+
if (treasury === void 0) {
|
|
49328
|
+
if (swarm.treasury.steps.created || swarm.treasury.steps.creating) {
|
|
49329
|
+
skipped.push({ kind: "swarm", name: swarm.name, reason: "missing_account" });
|
|
49330
|
+
return false;
|
|
49331
|
+
}
|
|
49332
|
+
} else if (swarm.treasury.address !== void 0 && swarm.treasury.address.toLowerCase() !== treasury.address.toLowerCase()) {
|
|
49333
|
+
skipped.push({ kind: "swarm", name: swarm.name, reason: "address_mismatch" });
|
|
49334
|
+
return false;
|
|
49335
|
+
}
|
|
49336
|
+
for (const member of swarm.members) {
|
|
49337
|
+
const account = accounts.get(member.account);
|
|
49338
|
+
if (account === void 0) {
|
|
49339
|
+
if (member.steps.created || member.steps.creating) {
|
|
49340
|
+
skipped.push({ kind: "swarm", name: swarm.name, reason: "missing_account" });
|
|
49341
|
+
return false;
|
|
49342
|
+
}
|
|
49343
|
+
continue;
|
|
49344
|
+
}
|
|
49345
|
+
if (member.address !== void 0 && member.address.toLowerCase() !== account.address.toLowerCase()) {
|
|
49346
|
+
skipped.push({ kind: "swarm", name: swarm.name, reason: "address_mismatch" });
|
|
49347
|
+
return false;
|
|
49348
|
+
}
|
|
49349
|
+
}
|
|
49350
|
+
return true;
|
|
49351
|
+
});
|
|
49352
|
+
const allMovementLegs = /* @__PURE__ */ new Set();
|
|
49353
|
+
const backupMovementIds = [];
|
|
49354
|
+
const validMovements = [];
|
|
49355
|
+
for (const backupMovement of options.plaintext.movements ?? []) {
|
|
49356
|
+
const movement = currentMovement(backupMovement);
|
|
49357
|
+
backupMovementIds.push(movement.id);
|
|
49358
|
+
const legKeys = movement.legs.map((leg) => movementTransferKey(leg.from, leg.nonce));
|
|
49359
|
+
for (const key of legKeys) allMovementLegs.add(key);
|
|
49360
|
+
const senders = /* @__PURE__ */ new Set([
|
|
49361
|
+
movement.from,
|
|
49362
|
+
...movement.treasury === void 0 ? [] : [movement.treasury],
|
|
49363
|
+
...movement.legs.map((leg) => leg.from)
|
|
49364
|
+
]);
|
|
49365
|
+
if ([...senders].some((sender) => !accounts.has(sender))) {
|
|
49366
|
+
skipped.push({ kind: "movement", name: movement.id, reason: "missing_account" });
|
|
49367
|
+
continue;
|
|
49368
|
+
}
|
|
49369
|
+
validMovements.push(movement);
|
|
49370
|
+
}
|
|
49371
|
+
const validReceipts = [];
|
|
49372
|
+
for (const receipt of options.plaintext.transferReceipts ?? []) {
|
|
49373
|
+
const key = movementTransferKey(receipt.wallet, receipt.transfer.nonce);
|
|
49374
|
+
if (!accounts.has(receipt.wallet)) {
|
|
49375
|
+
skipped.push({ kind: "receipt", name: receipt.id, reason: "missing_account" });
|
|
49376
|
+
} else if (!allMovementLegs.has(key)) {
|
|
49377
|
+
skipped.push({ kind: "receipt", name: receipt.id, reason: "missing_movement" });
|
|
49378
|
+
} else {
|
|
49379
|
+
validReceipts.push({
|
|
49380
|
+
name: receipt.id,
|
|
49381
|
+
line: JSON.stringify(parseReceipt(receipt)),
|
|
49382
|
+
legKey: key
|
|
49383
|
+
});
|
|
49384
|
+
}
|
|
49385
|
+
}
|
|
49386
|
+
let receiptsPath;
|
|
49387
|
+
let stagedReceipts = validReceipts;
|
|
49388
|
+
if (validReceipts.length > 0) {
|
|
49389
|
+
receiptsPath = getVapiPaths(options.stagingHome).receipts;
|
|
49390
|
+
const staged = await stageRestoreBytes(
|
|
49391
|
+
receiptsPath,
|
|
49392
|
+
`${validReceipts.map((receipt) => receipt.line).join("\n")}
|
|
49393
|
+
`
|
|
49394
|
+
);
|
|
49395
|
+
if (!staged) {
|
|
49396
|
+
for (const receipt of validReceipts) {
|
|
49397
|
+
skipped.push({ kind: "receipt", name: receipt.name, reason: "write_failed" });
|
|
49398
|
+
}
|
|
49399
|
+
stagedReceipts = [];
|
|
49400
|
+
receiptsPath = void 0;
|
|
49401
|
+
}
|
|
49402
|
+
}
|
|
49403
|
+
const assumedReceiptKeys = new Set(validReceipts.map((receipt) => receipt.legKey));
|
|
49404
|
+
try {
|
|
49405
|
+
const existingJournal = await readRestoreReceiptJournal(getVapiPaths(options.home).receipts);
|
|
49406
|
+
for (const key of existingJournal.signedKeys) assumedReceiptKeys.add(key);
|
|
49407
|
+
} catch {
|
|
49408
|
+
}
|
|
49409
|
+
const agents = [];
|
|
49410
|
+
for (const profile of validAgents) {
|
|
49411
|
+
const stagedPath = join8(options.stagingHome, "agents", `${profile.name}.json`);
|
|
49412
|
+
if (await stageRestoreBytes(stagedPath, prettyJson(profile))) {
|
|
49413
|
+
agents.push({
|
|
49414
|
+
kind: "agent",
|
|
49415
|
+
name: profile.name,
|
|
49416
|
+
stagedPath,
|
|
49417
|
+
targetPath: join8(options.home, "agents", `${profile.name}.json`)
|
|
49418
|
+
});
|
|
49419
|
+
} else {
|
|
49420
|
+
skipped.push({ kind: "agent", name: profile.name, reason: "write_failed" });
|
|
49421
|
+
}
|
|
49422
|
+
}
|
|
49423
|
+
let restoreDevice = options.device;
|
|
49424
|
+
if (validSwarms.length > 0 && restoreDevice === void 0) {
|
|
49425
|
+
try {
|
|
49426
|
+
restoreDevice = deviceName({ config: await loadConfig(getVapiPaths(options.home).config) });
|
|
49427
|
+
} catch {
|
|
49428
|
+
for (const swarm of validSwarms) {
|
|
49429
|
+
skipped.push({ kind: "swarm", name: swarm.name, reason: "write_failed" });
|
|
49430
|
+
}
|
|
49431
|
+
}
|
|
49432
|
+
}
|
|
49433
|
+
const swarms = [];
|
|
49434
|
+
if (restoreDevice !== void 0) {
|
|
49435
|
+
for (const value of validSwarms) {
|
|
49436
|
+
const swarm = swarmFileSchema.parse({ ...value, device: restoreDevice });
|
|
49437
|
+
const stagedPath = join8(options.stagingHome, "swarms", `${swarm.name}.json`);
|
|
49438
|
+
if (await stageRestoreBytes(stagedPath, prettyJson(swarm))) {
|
|
49439
|
+
swarms.push({
|
|
49440
|
+
kind: "swarm",
|
|
49441
|
+
name: swarm.name,
|
|
49442
|
+
stagedPath,
|
|
49443
|
+
targetPath: join8(options.home, "swarms", `${swarm.name}.json`)
|
|
49444
|
+
});
|
|
49445
|
+
} else {
|
|
49446
|
+
skipped.push({ kind: "swarm", name: swarm.name, reason: "write_failed" });
|
|
49447
|
+
}
|
|
49448
|
+
}
|
|
49449
|
+
}
|
|
49450
|
+
const movements = [];
|
|
49451
|
+
for (const movement of validMovements) {
|
|
49452
|
+
const restored = reconcileRestoredMovement(movement, assumedReceiptKeys);
|
|
49453
|
+
const stagedPath = join8(options.stagingHome, "movements", `${restored.id}.json`);
|
|
49454
|
+
if (await stageRestoreBytes(stagedPath, serializeMovement(restored))) {
|
|
49455
|
+
movements.push({
|
|
49456
|
+
kind: "movement",
|
|
49457
|
+
name: restored.id,
|
|
49458
|
+
stagedPath,
|
|
49459
|
+
targetPath: join8(options.home, "movements", `${restored.id}.json`),
|
|
49460
|
+
movement: restored
|
|
49461
|
+
});
|
|
49462
|
+
} else {
|
|
49463
|
+
skipped.push({ kind: "movement", name: restored.id, reason: "write_failed" });
|
|
49464
|
+
}
|
|
49465
|
+
}
|
|
49466
|
+
return {
|
|
49467
|
+
agents,
|
|
49468
|
+
swarms,
|
|
49469
|
+
movements,
|
|
49470
|
+
backupMovementIds,
|
|
49471
|
+
receipts: stagedReceipts,
|
|
49472
|
+
signedReceiptKeys: assumedReceiptKeys,
|
|
49473
|
+
...receiptsPath === void 0 ? {} : { receiptsPath },
|
|
49474
|
+
restoredAgents: [],
|
|
49475
|
+
restoredSwarms: [],
|
|
49476
|
+
restoredMovements: [],
|
|
49477
|
+
skipped,
|
|
49478
|
+
conflicts,
|
|
49479
|
+
...restoreDevice === void 0 ? {} : { restoreDevice }
|
|
49480
|
+
};
|
|
49481
|
+
}
|
|
49482
|
+
async function publishV2Restore(staged, home, afterPublish) {
|
|
49483
|
+
for (const file2 of staged.agents) {
|
|
49484
|
+
const outcome = await publishRestoreFile(file2, staged);
|
|
49485
|
+
if (outcome === "restored") staged.restoredAgents.push(file2.name);
|
|
49486
|
+
if (outcome === "restored") await afterPublish?.({ kind: "agent", name: file2.name });
|
|
49487
|
+
}
|
|
49488
|
+
for (const file2 of staged.swarms) {
|
|
49489
|
+
const outcome = await publishRestoreFile(file2, staged);
|
|
49490
|
+
if (outcome === "restored") staged.restoredSwarms.push(file2.name);
|
|
49491
|
+
if (outcome === "restored") await afterPublish?.({ kind: "swarm", name: file2.name });
|
|
49492
|
+
}
|
|
49493
|
+
const restoredMovementIds = [];
|
|
49494
|
+
for (const file2 of staged.movements) {
|
|
49495
|
+
const outcome = await publishRestoreFile(file2, staged);
|
|
49496
|
+
if (outcome === "restored") restoredMovementIds.push(file2.movement.id);
|
|
49497
|
+
if (outcome === "restored") await afterPublish?.({ kind: "movement", name: file2.name });
|
|
49498
|
+
}
|
|
49499
|
+
await reconcilePublishedMovements(staged, home);
|
|
49500
|
+
for (const id of restoredMovementIds) {
|
|
49501
|
+
try {
|
|
49502
|
+
staged.restoredMovements.push(movementSummary(await readMovement(home, id)));
|
|
49503
|
+
} catch {
|
|
49504
|
+
staged.skipped.push({ kind: "movement", name: id, reason: "write_failed" });
|
|
49505
|
+
}
|
|
49506
|
+
}
|
|
49507
|
+
}
|
|
49508
|
+
async function publishV2RestoreSafety(staged, home) {
|
|
49509
|
+
for (const id of staged.backupMovementIds) {
|
|
49510
|
+
await markOpenLegsRestored(home, id);
|
|
49511
|
+
}
|
|
49512
|
+
await publishRestoreReceipts(staged, getVapiPaths(home).receipts);
|
|
49513
|
+
await reconcilePublishedMovements(staged, home, true);
|
|
49514
|
+
}
|
|
49515
|
+
async function publishRestoreFile(file2, staged) {
|
|
49516
|
+
try {
|
|
49517
|
+
const directory = dirname5(file2.targetPath);
|
|
49518
|
+
await mkdir8(directory, { recursive: true, mode: 448 });
|
|
49519
|
+
await chmod2(directory, 448);
|
|
49520
|
+
try {
|
|
49521
|
+
await link2(file2.stagedPath, file2.targetPath);
|
|
49522
|
+
await chmod2(file2.targetPath, 384);
|
|
49523
|
+
return "restored";
|
|
49524
|
+
} catch (error61) {
|
|
49525
|
+
if (!isExistingFile2(error61)) throw error61;
|
|
49526
|
+
}
|
|
49527
|
+
let identical = false;
|
|
49528
|
+
try {
|
|
49529
|
+
const [source, target] = await Promise.all([
|
|
49530
|
+
readFile9(file2.stagedPath),
|
|
49531
|
+
readFile9(file2.targetPath)
|
|
49532
|
+
]);
|
|
49533
|
+
identical = source.equals(target);
|
|
49534
|
+
} catch {
|
|
49535
|
+
}
|
|
49536
|
+
if (identical) {
|
|
49537
|
+
await chmod2(file2.targetPath, 384);
|
|
49538
|
+
return "restored";
|
|
49539
|
+
}
|
|
49540
|
+
staged.conflicts.push({
|
|
49541
|
+
kind: file2.kind,
|
|
49542
|
+
name: file2.name,
|
|
49543
|
+
path: file2.targetPath
|
|
49544
|
+
});
|
|
49545
|
+
return "conflict";
|
|
49546
|
+
} catch {
|
|
49547
|
+
staged.skipped.push({ kind: file2.kind, name: file2.name, reason: "write_failed" });
|
|
49548
|
+
return "failed";
|
|
49549
|
+
}
|
|
49550
|
+
}
|
|
49551
|
+
async function publishRestoreReceipts(staged, targetPath) {
|
|
49552
|
+
return await withReceiptJournalLock(
|
|
49553
|
+
targetPath,
|
|
49554
|
+
async () => publishRestoreReceiptsLocked(staged, targetPath)
|
|
49555
|
+
);
|
|
49556
|
+
}
|
|
49557
|
+
async function publishRestoreReceiptsLocked(staged, targetPath) {
|
|
49558
|
+
const restored = /* @__PURE__ */ new Set();
|
|
49559
|
+
if (staged.receipts.length === 0 || staged.receiptsPath === void 0) return restored;
|
|
49560
|
+
let existing = "";
|
|
49561
|
+
let existingLines = /* @__PURE__ */ new Set();
|
|
49562
|
+
try {
|
|
49563
|
+
await readFile9(staged.receiptsPath, "utf8");
|
|
49564
|
+
await mkdir8(dirname5(targetPath), { recursive: true, mode: 448 });
|
|
49565
|
+
try {
|
|
49566
|
+
const journal = await readRestoreReceiptJournal(targetPath);
|
|
49567
|
+
existing = journal.raw;
|
|
49568
|
+
existingLines = journal.lines;
|
|
49569
|
+
await chmod2(targetPath, 384);
|
|
49570
|
+
} catch (error61) {
|
|
49571
|
+
if (!isMissingFile3(error61)) throw error61;
|
|
49572
|
+
}
|
|
49573
|
+
} catch {
|
|
49574
|
+
for (const receipt of staged.receipts) {
|
|
49575
|
+
staged.skipped.push({ kind: "receipt", name: receipt.name, reason: "write_failed" });
|
|
49576
|
+
}
|
|
49577
|
+
return restored;
|
|
49578
|
+
}
|
|
49579
|
+
let needsLeadingNewline = existing.length > 0 && !existing.endsWith("\n");
|
|
49580
|
+
for (const receipt of staged.receipts) {
|
|
49581
|
+
if (existingLines.has(receipt.line)) {
|
|
49582
|
+
restored.add(receipt.legKey);
|
|
49583
|
+
continue;
|
|
49584
|
+
}
|
|
49585
|
+
let handle;
|
|
49586
|
+
try {
|
|
49587
|
+
handle = await openFile(targetPath, "a", 384);
|
|
49588
|
+
await handle.chmod(384);
|
|
49589
|
+
await handle.writeFile(`${needsLeadingNewline ? "\n" : ""}${receipt.line}
|
|
49590
|
+
`, "utf8");
|
|
49591
|
+
await handle.close();
|
|
49592
|
+
handle = void 0;
|
|
49593
|
+
needsLeadingNewline = false;
|
|
49594
|
+
existingLines.add(receipt.line);
|
|
49595
|
+
restored.add(receipt.legKey);
|
|
49596
|
+
} catch {
|
|
49597
|
+
await handle?.close().catch(() => void 0);
|
|
49598
|
+
staged.skipped.push({ kind: "receipt", name: receipt.name, reason: "write_failed" });
|
|
49599
|
+
}
|
|
49600
|
+
}
|
|
49601
|
+
return restored;
|
|
49602
|
+
}
|
|
49603
|
+
async function reconcilePublishedMovements(staged, home, requireDurableSafety = false) {
|
|
49604
|
+
const signedKeys = new Set(staged.signedReceiptKeys);
|
|
49605
|
+
const journaledKeys = /* @__PURE__ */ new Set();
|
|
49606
|
+
try {
|
|
49607
|
+
const journal = await readRestoreReceiptJournal(getVapiPaths(home).receipts);
|
|
49608
|
+
for (const key of journal.signedKeys) {
|
|
49609
|
+
signedKeys.add(key);
|
|
49610
|
+
journaledKeys.add(key);
|
|
49611
|
+
}
|
|
49612
|
+
} catch {
|
|
49613
|
+
}
|
|
49614
|
+
if (signedKeys.size === 0) return;
|
|
49615
|
+
const unjournaledKeys = new Set([...signedKeys].filter((key) => !journaledKeys.has(key)));
|
|
49616
|
+
let files;
|
|
49617
|
+
try {
|
|
49618
|
+
files = await readdir5(join8(home, "movements"));
|
|
49619
|
+
} catch (error61) {
|
|
49620
|
+
if (isMissingFile3(error61)) return;
|
|
49621
|
+
if (requireDurableSafety && unjournaledKeys.size > 0) throw error61;
|
|
49622
|
+
return;
|
|
49623
|
+
}
|
|
49624
|
+
for (const file2 of files) {
|
|
49625
|
+
if (!file2.endsWith(".json")) continue;
|
|
49626
|
+
const id = file2.slice(0, -5);
|
|
49627
|
+
let movement;
|
|
49628
|
+
try {
|
|
49629
|
+
movement = await readMovement(home, id);
|
|
49630
|
+
} catch (error61) {
|
|
49631
|
+
if (requireDurableSafety && unjournaledKeys.size > 0) throw error61;
|
|
49632
|
+
continue;
|
|
49633
|
+
}
|
|
49634
|
+
if (!movement.legs.some(
|
|
49635
|
+
(leg) => leg.status !== "sent" && leg.status !== "cancelled" && signedKeys.has(movementTransferKey(leg.from, leg.nonce))
|
|
49636
|
+
)) {
|
|
49637
|
+
continue;
|
|
49638
|
+
}
|
|
49639
|
+
try {
|
|
49640
|
+
await reconcileSignedLegs(home, id, signedKeys);
|
|
49641
|
+
} catch (error61) {
|
|
49642
|
+
staged.skipped.push({ kind: "movement", name: id, reason: "write_failed" });
|
|
49643
|
+
if (requireDurableSafety && movement.legs.some(
|
|
49644
|
+
(leg) => leg.status !== "sent" && leg.status !== "cancelled" && unjournaledKeys.has(movementTransferKey(leg.from, leg.nonce))
|
|
49645
|
+
)) {
|
|
49646
|
+
throw error61;
|
|
49647
|
+
}
|
|
49648
|
+
}
|
|
49649
|
+
}
|
|
49650
|
+
}
|
|
49651
|
+
async function stageRestoreBytes(path, bytes) {
|
|
49652
|
+
let handle;
|
|
49653
|
+
try {
|
|
49654
|
+
await mkdir8(dirname5(path), { recursive: true, mode: 448 });
|
|
49655
|
+
handle = await openFile(path, "wx", 384);
|
|
49656
|
+
await handle.writeFile(bytes, "utf8");
|
|
49657
|
+
await handle.close();
|
|
49658
|
+
return true;
|
|
49659
|
+
} catch {
|
|
49660
|
+
await handle?.close().catch(() => void 0);
|
|
49661
|
+
return false;
|
|
49662
|
+
}
|
|
49663
|
+
}
|
|
49664
|
+
function currentMovement(movement) {
|
|
49665
|
+
if (movement.v === 2) return movement;
|
|
49666
|
+
return {
|
|
49667
|
+
...movement,
|
|
49668
|
+
v: 2,
|
|
49669
|
+
legs: movement.legs.map((leg) => ({
|
|
49670
|
+
...leg,
|
|
49671
|
+
from: movement.from,
|
|
49672
|
+
purpose: "send"
|
|
49673
|
+
}))
|
|
49674
|
+
};
|
|
49675
|
+
}
|
|
49676
|
+
function reconcileRestoredMovement(movement, receiptKeys) {
|
|
49677
|
+
return {
|
|
49678
|
+
...movement,
|
|
49679
|
+
legs: movement.legs.map((leg) => {
|
|
49680
|
+
if (leg.status === "cancelled") return leg;
|
|
49681
|
+
return leg.status !== "sent" && receiptKeys.has(movementTransferKey(leg.from, leg.nonce)) ? {
|
|
49682
|
+
from: leg.from,
|
|
49683
|
+
to: leg.to,
|
|
49684
|
+
amountUsd: leg.amountUsd,
|
|
49685
|
+
purpose: leg.purpose,
|
|
49686
|
+
nonce: leg.nonce,
|
|
49687
|
+
status: "unknown",
|
|
49688
|
+
restored: true
|
|
49689
|
+
} : { ...leg, restored: true };
|
|
49690
|
+
})
|
|
49691
|
+
};
|
|
49692
|
+
}
|
|
49693
|
+
function movementSummary(movement) {
|
|
49694
|
+
return {
|
|
49695
|
+
id: movement.id,
|
|
49696
|
+
from: movement.from,
|
|
49697
|
+
network: movement.network,
|
|
49698
|
+
createdAt: movement.createdAt,
|
|
49699
|
+
pendingLegs: movement.legs.filter((leg) => leg.status === "planned").length,
|
|
49700
|
+
unknownLegs: movement.legs.filter((leg) => leg.status === "unknown").length
|
|
49701
|
+
};
|
|
49702
|
+
}
|
|
49703
|
+
function prettyJson(value) {
|
|
49704
|
+
return `${JSON.stringify(value, null, 2)}
|
|
49705
|
+
`;
|
|
49706
|
+
}
|
|
47308
49707
|
function parseSealed(sealed) {
|
|
47309
49708
|
const value = JSON.parse(sealed);
|
|
47310
|
-
if (!
|
|
49709
|
+
if (!isRecord2(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) {
|
|
47311
49710
|
throw new Error();
|
|
47312
49711
|
}
|
|
47313
49712
|
return { epk: value.epk, nonce: value.nonce, ciphertext: value.ciphertext };
|
|
@@ -47317,9 +49716,9 @@ function assertRelayKey(value) {
|
|
|
47317
49716
|
throw new BackupError("relay_invalid_key");
|
|
47318
49717
|
}
|
|
47319
49718
|
}
|
|
47320
|
-
function readRelayRandomBytes(
|
|
49719
|
+
function readRelayRandomBytes(randomBytes7, length) {
|
|
47321
49720
|
try {
|
|
47322
|
-
const value =
|
|
49721
|
+
const value = randomBytes7(length);
|
|
47323
49722
|
if (!(value instanceof Uint8Array) || value.byteLength !== length) throw new Error();
|
|
47324
49723
|
return Buffer.from(value);
|
|
47325
49724
|
} catch {
|
|
@@ -47375,7 +49774,7 @@ function validateRestorePlaintext(value) {
|
|
|
47375
49774
|
try {
|
|
47376
49775
|
const parsed = backupPlaintextSchema.safeParse(value);
|
|
47377
49776
|
if (!parsed.success) throw new Error();
|
|
47378
|
-
const plaintext =
|
|
49777
|
+
const plaintext = parsed.data.v === 1 ? buildPlaintextV1(parsed.data) : buildPlaintextV2(parsed.data);
|
|
47379
49778
|
const phrase = validateRecoveryPhrase(plaintext.phrase);
|
|
47380
49779
|
let seed;
|
|
47381
49780
|
try {
|
|
@@ -47398,12 +49797,22 @@ function buildRestoredRegistry(plaintext) {
|
|
|
47398
49797
|
for (let index = 0; index < plaintext.accounts.length; index += 1) {
|
|
47399
49798
|
const account = plaintext.accounts[index];
|
|
47400
49799
|
const metadata = plaintext.registry.accounts[index];
|
|
49800
|
+
const metadataV2 = plaintext.v === 2 ? plaintext.registry.accounts[index] : void 0;
|
|
47401
49801
|
wallets[account.name] = {
|
|
47402
49802
|
createdAt: account.createdAt,
|
|
47403
49803
|
...metadata.label === void 0 ? {} : { label: metadata.label },
|
|
47404
49804
|
spendCaps: {
|
|
47405
49805
|
perCallAtomic: metadata.spendCaps.perCallAtomic,
|
|
47406
49806
|
perDayAtomic: metadata.spendCaps.perDayAtomic
|
|
49807
|
+
},
|
|
49808
|
+
...metadataV2 === void 0 ? {} : {
|
|
49809
|
+
ceilingAtomic: metadataV2.ceilingAtomic === void 0 ? DEFAULT_CEILING_ATOMIC.toString() : metadataV2.ceilingAtomic,
|
|
49810
|
+
...metadataV2.routerRefill === void 0 ? {} : {
|
|
49811
|
+
routerRefill: {
|
|
49812
|
+
belowUsd: metadataV2.routerRefill.belowUsd,
|
|
49813
|
+
tierUsd: metadataV2.routerRefill.tierUsd
|
|
49814
|
+
}
|
|
49815
|
+
}
|
|
47407
49816
|
}
|
|
47408
49817
|
};
|
|
47409
49818
|
}
|
|
@@ -47413,7 +49822,48 @@ function buildRestoredRegistry(plaintext) {
|
|
|
47413
49822
|
wallets
|
|
47414
49823
|
});
|
|
47415
49824
|
}
|
|
47416
|
-
|
|
49825
|
+
function restoreFingerprint(plaintext, device, vaultKey) {
|
|
49826
|
+
return createHmac("sha256", vaultKey).update("vapi-backup-restore/v1\n", "utf8").update(JSON.stringify({ device: device ?? null, plaintext }), "utf8").digest("hex");
|
|
49827
|
+
}
|
|
49828
|
+
async function createRestoreMarker(vaultPath, registryPath, fingerprint, vaultKey) {
|
|
49829
|
+
const marker = {
|
|
49830
|
+
v: 1,
|
|
49831
|
+
fingerprint,
|
|
49832
|
+
vaultSha256: await sha256File(vaultPath),
|
|
49833
|
+
registrySha256: await sha256File(registryPath)
|
|
49834
|
+
};
|
|
49835
|
+
return { ...marker, mac: restoreMarkerMac(marker, vaultKey) };
|
|
49836
|
+
}
|
|
49837
|
+
async function readRestoreMarker(path, fingerprint, vaultKey) {
|
|
49838
|
+
try {
|
|
49839
|
+
const marker = restoreMarkerSchema.parse(JSON.parse(await readFile9(path, "utf8")));
|
|
49840
|
+
const expectedMac = restoreMarkerMac(marker, vaultKey);
|
|
49841
|
+
if (marker.fingerprint !== fingerprint || !timingSafeEqual(Buffer.from(marker.mac, "hex"), Buffer.from(expectedMac, "hex"))) {
|
|
49842
|
+
throw new Error();
|
|
49843
|
+
}
|
|
49844
|
+
return marker;
|
|
49845
|
+
} catch {
|
|
49846
|
+
throw new BackupError("vault_exists");
|
|
49847
|
+
}
|
|
49848
|
+
}
|
|
49849
|
+
function restoreMarkerMac(marker, vaultKey) {
|
|
49850
|
+
return createHmac("sha256", vaultKey).update(
|
|
49851
|
+
`vapi-backup-restore-marker/v1|${marker.fingerprint}|${marker.vaultSha256}|${marker.registrySha256}`,
|
|
49852
|
+
"utf8"
|
|
49853
|
+
).digest("hex");
|
|
49854
|
+
}
|
|
49855
|
+
async function sha256File(path) {
|
|
49856
|
+
return createHash2("sha256").update(await readFile9(path)).digest("hex");
|
|
49857
|
+
}
|
|
49858
|
+
async function notifyRestorePublish(hook, step, rememberError) {
|
|
49859
|
+
try {
|
|
49860
|
+
await hook?.(step);
|
|
49861
|
+
} catch (error61) {
|
|
49862
|
+
rememberError(error61);
|
|
49863
|
+
throw error61;
|
|
49864
|
+
}
|
|
49865
|
+
}
|
|
49866
|
+
async function pathExists2(path) {
|
|
47417
49867
|
try {
|
|
47418
49868
|
await lstat(path);
|
|
47419
49869
|
return true;
|
|
@@ -47429,7 +49879,7 @@ function isExistingFile2(error61) {
|
|
|
47429
49879
|
return isErrorCode(error61, "EEXIST");
|
|
47430
49880
|
}
|
|
47431
49881
|
function isErrorCode(error61, code) {
|
|
47432
|
-
return
|
|
49882
|
+
return isRecord2(error61) && Reflect.get(error61, "code") === code;
|
|
47433
49883
|
}
|
|
47434
49884
|
function validateBackupSourceRelationships(source, context) {
|
|
47435
49885
|
const names = /* @__PURE__ */ new Set();
|
|
@@ -47463,7 +49913,23 @@ function validateBackupSourceRelationships(source, context) {
|
|
|
47463
49913
|
}
|
|
47464
49914
|
}
|
|
47465
49915
|
}
|
|
47466
|
-
function
|
|
49916
|
+
function validateBackupV2Uniqueness(source, context) {
|
|
49917
|
+
validateUniqueValues(source.agents, (agent) => agent.name, "agent name", context);
|
|
49918
|
+
validateUniqueValues(source.swarms, (swarm) => swarm.name, "swarm name", context);
|
|
49919
|
+
validateUniqueValues(source.movements, (movement) => movement.id, "movement id", context);
|
|
49920
|
+
}
|
|
49921
|
+
function validateUniqueValues(values, key, label, context) {
|
|
49922
|
+
if (values === void 0) return;
|
|
49923
|
+
const seen = /* @__PURE__ */ new Set();
|
|
49924
|
+
for (const value of values) {
|
|
49925
|
+
const current = key(value);
|
|
49926
|
+
if (seen.has(current)) {
|
|
49927
|
+
context.addIssue({ code: "custom", message: `Duplicate ${label}.` });
|
|
49928
|
+
}
|
|
49929
|
+
seen.add(current);
|
|
49930
|
+
}
|
|
49931
|
+
}
|
|
49932
|
+
function buildPlaintextV1(source) {
|
|
47467
49933
|
const accounts = source.accounts.map(
|
|
47468
49934
|
(account) => account.kind === "derived" ? {
|
|
47469
49935
|
name: account.name,
|
|
@@ -47501,7 +49967,247 @@ function buildPlaintext(source) {
|
|
|
47501
49967
|
protected: source.protected
|
|
47502
49968
|
};
|
|
47503
49969
|
}
|
|
49970
|
+
function buildPlaintextV2(source, included = { agents: true, swarms: true, movements: true }) {
|
|
49971
|
+
const accounts = buildPlaintextV1({
|
|
49972
|
+
phrase: source.phrase,
|
|
49973
|
+
nextDerivedIndex: source.nextDerivedIndex,
|
|
49974
|
+
accounts: source.accounts,
|
|
49975
|
+
registry: {
|
|
49976
|
+
...source.registry.default === void 0 ? {} : { default: source.registry.default },
|
|
49977
|
+
accounts: source.registry.accounts,
|
|
49978
|
+
networks: source.registry.networks
|
|
49979
|
+
},
|
|
49980
|
+
protected: source.protected
|
|
49981
|
+
}).accounts;
|
|
49982
|
+
const registry2 = {
|
|
49983
|
+
...source.registry.default === void 0 ? {} : { default: source.registry.default },
|
|
49984
|
+
accounts: source.registry.accounts.map((account) => ({
|
|
49985
|
+
name: account.name,
|
|
49986
|
+
...account.label === void 0 ? {} : { label: account.label },
|
|
49987
|
+
spendCaps: {
|
|
49988
|
+
perCallAtomic: account.spendCaps.perCallAtomic,
|
|
49989
|
+
perDayAtomic: account.spendCaps.perDayAtomic
|
|
49990
|
+
},
|
|
49991
|
+
...account.ceilingAtomic === void 0 || account.ceilingAtomic === DEFAULT_CEILING_ATOMIC.toString() ? {} : { ceilingAtomic: account.ceilingAtomic },
|
|
49992
|
+
...account.routerRefill === void 0 ? {} : {
|
|
49993
|
+
routerRefill: {
|
|
49994
|
+
belowUsd: account.routerRefill.belowUsd,
|
|
49995
|
+
tierUsd: account.routerRefill.tierUsd
|
|
49996
|
+
}
|
|
49997
|
+
}
|
|
49998
|
+
})),
|
|
49999
|
+
networks: [...source.registry.networks]
|
|
50000
|
+
};
|
|
50001
|
+
const agents = source.agents;
|
|
50002
|
+
const swarms = source.swarms;
|
|
50003
|
+
const movements = source.movements;
|
|
50004
|
+
const transferReceipts = source.transferReceipts;
|
|
50005
|
+
return {
|
|
50006
|
+
v: 2,
|
|
50007
|
+
phrase: source.phrase,
|
|
50008
|
+
nextDerivedIndex: source.nextDerivedIndex,
|
|
50009
|
+
accounts,
|
|
50010
|
+
registry: registry2,
|
|
50011
|
+
protected: source.protected,
|
|
50012
|
+
...included.agents !== false && agents !== void 0 && agents.length > 0 ? { agents: [...agents].sort(compareNamedValues).map(copyAgentProfile) } : {},
|
|
50013
|
+
...included.swarms !== false && swarms !== void 0 && swarms.length > 0 ? { swarms: [...swarms].sort(compareNamedValues).map(copySwarm) } : {},
|
|
50014
|
+
...included.movements !== false && movements !== void 0 && movements.length > 0 ? { movements: [...movements].sort(compareMovements).map(copyMovement) } : {},
|
|
50015
|
+
...included.movements !== false && transferReceipts !== void 0 && transferReceipts.length > 0 ? { transferReceipts: transferReceipts.map(copyTransferReceipt) } : {}
|
|
50016
|
+
};
|
|
50017
|
+
}
|
|
50018
|
+
function backupCandidates(source) {
|
|
50019
|
+
if (!isBackupSourceV2(source)) {
|
|
50020
|
+
return [{ plaintext: buildPlaintextV1(source), omitted: [] }];
|
|
50021
|
+
}
|
|
50022
|
+
const candidates = [];
|
|
50023
|
+
const included = { agents: true, swarms: true, movements: true };
|
|
50024
|
+
const omitted = [];
|
|
50025
|
+
const addCandidate = () => {
|
|
50026
|
+
candidates.push({
|
|
50027
|
+
plaintext: hasV2OnlyData(source, included) ? buildPlaintextV2(source, included) : buildPlaintextV1(source),
|
|
50028
|
+
omitted: [...omitted]
|
|
50029
|
+
});
|
|
50030
|
+
};
|
|
50031
|
+
addCandidate();
|
|
50032
|
+
if (source.agents.length > 0) {
|
|
50033
|
+
included.agents = false;
|
|
50034
|
+
omitted.push("agents");
|
|
50035
|
+
addCandidate();
|
|
50036
|
+
}
|
|
50037
|
+
if (source.swarms.length > 0) {
|
|
50038
|
+
included.swarms = false;
|
|
50039
|
+
omitted.push("swarms");
|
|
50040
|
+
addCandidate();
|
|
50041
|
+
}
|
|
50042
|
+
if (source.movements.length > 0 || source.transferReceipts.length > 0) {
|
|
50043
|
+
included.movements = false;
|
|
50044
|
+
omitted.push("movements");
|
|
50045
|
+
addCandidate();
|
|
50046
|
+
}
|
|
50047
|
+
return candidates;
|
|
50048
|
+
}
|
|
50049
|
+
function hasV2OnlyData(source, included) {
|
|
50050
|
+
return source.registry.accounts.some(
|
|
50051
|
+
(account) => account.ceilingAtomic !== void 0 && account.ceilingAtomic !== DEFAULT_CEILING_ATOMIC.toString() || account.routerRefill !== void 0
|
|
50052
|
+
) || 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);
|
|
50053
|
+
}
|
|
50054
|
+
function isBackupSourceV2(source) {
|
|
50055
|
+
return "agents" in source;
|
|
50056
|
+
}
|
|
50057
|
+
function copyAgentProfile(profile) {
|
|
50058
|
+
return {
|
|
50059
|
+
version: profile.version,
|
|
50060
|
+
name: profile.name,
|
|
50061
|
+
wallet: profile.wallet,
|
|
50062
|
+
model: profile.model,
|
|
50063
|
+
instructions: profile.instructions,
|
|
50064
|
+
verifiedOnly: profile.verifiedOnly,
|
|
50065
|
+
approveAboveUsd: profile.approveAboveUsd,
|
|
50066
|
+
...profile.maxPerTaskUsd === void 0 ? {} : { maxPerTaskUsd: profile.maxPerTaskUsd },
|
|
50067
|
+
...profile.autoReleaseBelowUsd === void 0 ? {} : { autoReleaseBelowUsd: profile.autoReleaseBelowUsd },
|
|
50068
|
+
maxSteps: profile.maxSteps,
|
|
50069
|
+
paused: profile.paused,
|
|
50070
|
+
createdAt: profile.createdAt,
|
|
50071
|
+
tools: [...profile.tools],
|
|
50072
|
+
...profile.grants === void 0 ? {} : { grants: [...profile.grants] }
|
|
50073
|
+
};
|
|
50074
|
+
}
|
|
50075
|
+
function copySwarm(swarm) {
|
|
50076
|
+
return swarmFileSchema.parse(swarm);
|
|
50077
|
+
}
|
|
50078
|
+
function copyMovement(movement) {
|
|
50079
|
+
return toBackupMovement(movement);
|
|
50080
|
+
}
|
|
50081
|
+
function toBackupMovement(movement) {
|
|
50082
|
+
if (movement.v === 1) return backupMovementSchema.parse(movement);
|
|
50083
|
+
return backupMovementSchema.parse({
|
|
50084
|
+
...movement,
|
|
50085
|
+
legs: movement.legs.map(
|
|
50086
|
+
({
|
|
50087
|
+
restored: _restored,
|
|
50088
|
+
fromAddress: _fromAddress,
|
|
50089
|
+
toAddress: _toAddress,
|
|
50090
|
+
addressBindingSource: _addressBindingSource,
|
|
50091
|
+
...leg
|
|
50092
|
+
}) => leg
|
|
50093
|
+
)
|
|
50094
|
+
});
|
|
50095
|
+
}
|
|
50096
|
+
function toBackupTransferReceipt(receipt) {
|
|
50097
|
+
if (receipt.quote?.asset === void 0) throw new BackupError("backup_invalid_input");
|
|
50098
|
+
return copyTransferReceipt({
|
|
50099
|
+
id: receipt.id,
|
|
50100
|
+
timestamp: receipt.timestamp,
|
|
50101
|
+
kind: "transfer",
|
|
50102
|
+
wallet: receiptWallet(receipt),
|
|
50103
|
+
resourceUrl: receipt.resourceUrl,
|
|
50104
|
+
quote: {
|
|
50105
|
+
network: receipt.quote.network,
|
|
50106
|
+
asset: receipt.quote.asset,
|
|
50107
|
+
amountAtomic: receipt.quote.amountAtomic
|
|
50108
|
+
},
|
|
50109
|
+
transfer: receipt.transfer,
|
|
50110
|
+
...receipt.error === void 0 ? {} : { error: receipt.error }
|
|
50111
|
+
});
|
|
50112
|
+
}
|
|
50113
|
+
function copyTransferReceipt(receipt) {
|
|
50114
|
+
const transfer = receipt.transfer;
|
|
50115
|
+
const request = transfer.request;
|
|
50116
|
+
return backupTransferReceiptSchema.parse({
|
|
50117
|
+
id: receipt.id,
|
|
50118
|
+
timestamp: receipt.timestamp,
|
|
50119
|
+
kind: "transfer",
|
|
50120
|
+
wallet: receipt.wallet,
|
|
50121
|
+
resourceUrl: receipt.resourceUrl,
|
|
50122
|
+
quote: {
|
|
50123
|
+
network: receipt.quote.network,
|
|
50124
|
+
asset: receipt.quote.asset,
|
|
50125
|
+
amountAtomic: receipt.quote.amountAtomic
|
|
50126
|
+
},
|
|
50127
|
+
transfer: {
|
|
50128
|
+
to: transfer.to,
|
|
50129
|
+
toName: transfer.toName,
|
|
50130
|
+
toKind: transfer.toKind,
|
|
50131
|
+
amountAtomic: transfer.amountAtomic,
|
|
50132
|
+
network: transfer.network,
|
|
50133
|
+
nonce: transfer.nonce,
|
|
50134
|
+
status: transfer.status,
|
|
50135
|
+
txHash: transfer.txHash,
|
|
50136
|
+
replayed: transfer.replayed,
|
|
50137
|
+
...transfer.reservedOn === void 0 ? {} : { reservedOn: transfer.reservedOn },
|
|
50138
|
+
...request === void 0 ? {} : {
|
|
50139
|
+
request: {
|
|
50140
|
+
authorization: {
|
|
50141
|
+
from: request.authorization.from,
|
|
50142
|
+
to: request.authorization.to,
|
|
50143
|
+
value: request.authorization.value,
|
|
50144
|
+
validAfter: request.authorization.validAfter,
|
|
50145
|
+
validBefore: request.authorization.validBefore,
|
|
50146
|
+
nonce: request.authorization.nonce
|
|
50147
|
+
},
|
|
50148
|
+
signature: request.signature
|
|
50149
|
+
}
|
|
50150
|
+
}
|
|
50151
|
+
},
|
|
50152
|
+
...receipt.error === void 0 ? {} : { error: { code: receipt.error.code, message: receipt.error.message } }
|
|
50153
|
+
});
|
|
50154
|
+
}
|
|
50155
|
+
async function readReceiptJournal(path) {
|
|
50156
|
+
let raw;
|
|
50157
|
+
try {
|
|
50158
|
+
raw = await readFile9(path, "utf8");
|
|
50159
|
+
} catch (error61) {
|
|
50160
|
+
if (isMissingFile3(error61)) return [];
|
|
50161
|
+
throw error61;
|
|
50162
|
+
}
|
|
50163
|
+
return raw.split("\n").filter((line) => line.trim().length > 0).map((line) => parseReceipt(JSON.parse(line)));
|
|
50164
|
+
}
|
|
50165
|
+
async function readRestoreReceiptJournal(path) {
|
|
50166
|
+
let raw;
|
|
50167
|
+
try {
|
|
50168
|
+
raw = await readFile9(path, "utf8");
|
|
50169
|
+
} catch (error61) {
|
|
50170
|
+
if (isMissingFile3(error61)) return { raw: "", lines: /* @__PURE__ */ new Set(), signedKeys: /* @__PURE__ */ new Set() };
|
|
50171
|
+
throw error61;
|
|
50172
|
+
}
|
|
50173
|
+
const lines = new Set(raw.split("\n"));
|
|
50174
|
+
const signedKeys = /* @__PURE__ */ new Set();
|
|
50175
|
+
for (const line of lines) {
|
|
50176
|
+
if (line.trim().length === 0) continue;
|
|
50177
|
+
try {
|
|
50178
|
+
const receipt = parseReceipt(JSON.parse(line));
|
|
50179
|
+
if (receipt.transfer !== void 0) {
|
|
50180
|
+
signedKeys.add(movementTransferKey(receiptWallet(receipt), receipt.transfer.nonce));
|
|
50181
|
+
}
|
|
50182
|
+
} catch {
|
|
50183
|
+
}
|
|
50184
|
+
}
|
|
50185
|
+
return { raw, lines, signedKeys };
|
|
50186
|
+
}
|
|
50187
|
+
function compareNamedValues(left, right) {
|
|
50188
|
+
return left.name < right.name ? -1 : left.name > right.name ? 1 : 0;
|
|
50189
|
+
}
|
|
50190
|
+
function compareMovements(left, right) {
|
|
50191
|
+
return left.createdAt.localeCompare(right.createdAt) || left.id.localeCompare(right.id);
|
|
50192
|
+
}
|
|
47504
50193
|
var RELAY_KEYS = ["format", "v", "epk", "nonce", "ciphertext"];
|
|
50194
|
+
var HKDF_KEYS = ["name", "salt"];
|
|
50195
|
+
var SCRYPT_KEYS = ["name", "salt", "N", "r", "p"];
|
|
50196
|
+
function parseKdf(value) {
|
|
50197
|
+
if (value.name === "hkdf-sha256" && hasExactKeys(value, HKDF_KEYS)) {
|
|
50198
|
+
if (typeof value.salt !== "string" || decodeBase64Url(value.salt)?.byteLength !== 32) {
|
|
50199
|
+
throw new BackupError("backup_unsupported");
|
|
50200
|
+
}
|
|
50201
|
+
return { name: "hkdf-sha256", salt: value.salt };
|
|
50202
|
+
}
|
|
50203
|
+
if (value.name === "scrypt" && hasExactKeys(value, SCRYPT_KEYS)) {
|
|
50204
|
+
if (typeof value.salt !== "string" || decodeBase64Url(value.salt)?.byteLength !== 32 || value.N !== SCRYPT_N2 || value.r !== SCRYPT_R2 || value.p !== SCRYPT_P2) {
|
|
50205
|
+
throw new BackupError("backup_unsupported");
|
|
50206
|
+
}
|
|
50207
|
+
return { name: "scrypt", salt: value.salt, N: SCRYPT_N2, r: SCRYPT_R2, p: SCRYPT_P2 };
|
|
50208
|
+
}
|
|
50209
|
+
throw new BackupError("backup_unsupported");
|
|
50210
|
+
}
|
|
47505
50211
|
function buildBackupAad(header) {
|
|
47506
50212
|
const parameters = header.kdf.name === "scrypt" ? [String(header.kdf.N), String(header.kdf.r), String(header.kdf.p)] : ["", "", ""];
|
|
47507
50213
|
return [
|
|
@@ -47572,14 +50278,14 @@ function assertBytes(value, length) {
|
|
|
47572
50278
|
}
|
|
47573
50279
|
}
|
|
47574
50280
|
function assertOwnerKey(ownerKey) {
|
|
47575
|
-
if (!
|
|
50281
|
+
if (!isRecord2(ownerKey) || ownerKey.kdf !== "hkdf-sha256" && ownerKey.kdf !== "scrypt") {
|
|
47576
50282
|
throw new BackupError("backup_invalid_input");
|
|
47577
50283
|
}
|
|
47578
50284
|
assertBytes(ownerKey.key, 32);
|
|
47579
50285
|
assertBytes(ownerKey.salt, 32);
|
|
47580
50286
|
}
|
|
47581
|
-
function readRandomBytes(
|
|
47582
|
-
const value =
|
|
50287
|
+
function readRandomBytes(randomBytes7, length) {
|
|
50288
|
+
const value = randomBytes7(length);
|
|
47583
50289
|
if (!(value instanceof Uint8Array) || value.byteLength !== length) {
|
|
47584
50290
|
throw new BackupError("backup_invalid_input");
|
|
47585
50291
|
}
|
|
@@ -47602,7 +50308,7 @@ function isIsoUtcMilliseconds(value) {
|
|
|
47602
50308
|
return false;
|
|
47603
50309
|
}
|
|
47604
50310
|
}
|
|
47605
|
-
function
|
|
50311
|
+
function isRecord2(value) {
|
|
47606
50312
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
47607
50313
|
}
|
|
47608
50314
|
function hasExactKeys(value, expected) {
|
|
@@ -47610,225 +50316,6 @@ function hasExactKeys(value, expected) {
|
|
|
47610
50316
|
return keys.length === expected.length && expected.every((key) => Object.hasOwn(value, key));
|
|
47611
50317
|
}
|
|
47612
50318
|
|
|
47613
|
-
// src/agent-link.ts
|
|
47614
|
-
import { createHash as createHash2 } from "node:crypto";
|
|
47615
|
-
import { mkdir as mkdir3, open as open3, stat as stat2, unlink as unlink2 } from "node:fs/promises";
|
|
47616
|
-
import { dirname as dirname2 } from "node:path";
|
|
47617
|
-
|
|
47618
|
-
// src/device.ts
|
|
47619
|
-
var DEVICE_NAME_PATTERN = /^[a-z0-9][a-z0-9-]{0,31}$/;
|
|
47620
|
-
|
|
47621
|
-
// src/agent-link.ts
|
|
47622
|
-
var AGENT_LINK_REVOKED_MESSAGE = "This agent's link was revoked or has expired. Run vapi login to link it again.";
|
|
47623
|
-
var AgentLinkError = class extends Error {
|
|
47624
|
-
constructor(code, message) {
|
|
47625
|
-
super(message);
|
|
47626
|
-
this.code = code;
|
|
47627
|
-
this.name = "AgentLinkError";
|
|
47628
|
-
}
|
|
47629
|
-
code;
|
|
47630
|
-
};
|
|
47631
|
-
var DEFAULT_AGENT_SCOPES = ["mcp:call", "router.use"];
|
|
47632
|
-
var ALLOWED_AGENT_SCOPES = /* @__PURE__ */ new Set([...DEFAULT_AGENT_SCOPES, "call.publish"]);
|
|
47633
|
-
var publicFetch = createPublicFetch({ allowPrivateNetwork: false });
|
|
47634
|
-
var agentSecretAccounts = (wallet) => ({
|
|
47635
|
-
tokens: `vapi.agent.${wallet}.tokens`,
|
|
47636
|
-
routerStake: `vapi.agent.${wallet}.router.stake`,
|
|
47637
|
-
routerBalance: `vapi.agent.${wallet}.router.balance`
|
|
47638
|
-
});
|
|
47639
|
-
async function agentFetch(args, url2, init) {
|
|
47640
|
-
const fetchImpl = args.fetchImpl ?? publicFetch;
|
|
47641
|
-
const accessToken = await resolveAgentAccessToken(args, {});
|
|
47642
|
-
const response = await safeAgentFetch(fetchImpl, url2, withBearer(init, accessToken));
|
|
47643
|
-
if (response.status !== 401) return response;
|
|
47644
|
-
const refreshedToken = await resolveAgentAccessToken(args, { rejectedAccessToken: accessToken });
|
|
47645
|
-
const refreshedResponse = await safeAgentFetch(fetchImpl, url2, withBearer(init, refreshedToken));
|
|
47646
|
-
if (refreshedResponse.status === 401) {
|
|
47647
|
-
throw new AgentLinkError("not_linked", AGENT_LINK_REVOKED_MESSAGE);
|
|
47648
|
-
}
|
|
47649
|
-
return refreshedResponse;
|
|
47650
|
-
}
|
|
47651
|
-
async function resolveAgentAccessToken(args, options) {
|
|
47652
|
-
await args.wallets.reload();
|
|
47653
|
-
const link2 = args.wallets.entry(args.wallet)?.link;
|
|
47654
|
-
if (link2 === void 0 || !args.secrets.available) throw notLinkedError();
|
|
47655
|
-
const accounts = agentSecretAccounts(args.wallet);
|
|
47656
|
-
const tokens = parseTokens(await safeGetSecret(args.secrets, accounts.tokens));
|
|
47657
|
-
if (tokens === void 0) throw notLinkedError();
|
|
47658
|
-
const now = args.now ?? Date.now;
|
|
47659
|
-
if (options.rejectedAccessToken === void 0 && tokens.expiresAt - 6e4 > now()) {
|
|
47660
|
-
return tokens.accessToken;
|
|
47661
|
-
}
|
|
47662
|
-
return await withAgentCredentialLock(args.wallets, link2.clientId, async () => {
|
|
47663
|
-
await args.wallets.reload();
|
|
47664
|
-
const currentLink = args.wallets.entry(args.wallet)?.link;
|
|
47665
|
-
if (currentLink === void 0) throw notLinkedError();
|
|
47666
|
-
const currentAccounts = agentSecretAccounts(args.wallet);
|
|
47667
|
-
const current = parseTokens(await safeGetSecret(args.secrets, currentAccounts.tokens));
|
|
47668
|
-
if (current === void 0) throw notLinkedError();
|
|
47669
|
-
if (options.rejectedAccessToken !== void 0 && current.accessToken !== options.rejectedAccessToken) {
|
|
47670
|
-
return current.accessToken;
|
|
47671
|
-
}
|
|
47672
|
-
if (options.rejectedAccessToken === void 0 && current.expiresAt - 6e4 > now()) {
|
|
47673
|
-
return current.accessToken;
|
|
47674
|
-
}
|
|
47675
|
-
const response = await safeFetch(
|
|
47676
|
-
args.fetchImpl ?? publicFetch,
|
|
47677
|
-
apiEndpoint(currentLink.apiBase, "/oauth/token"),
|
|
47678
|
-
{
|
|
47679
|
-
method: "POST",
|
|
47680
|
-
headers: { "content-type": "application/x-www-form-urlencoded" },
|
|
47681
|
-
body: new URLSearchParams({
|
|
47682
|
-
grant_type: "refresh_token",
|
|
47683
|
-
refresh_token: current.refreshToken,
|
|
47684
|
-
client_id: currentLink.clientId
|
|
47685
|
-
})
|
|
47686
|
-
}
|
|
47687
|
-
);
|
|
47688
|
-
const payload = await responseRecord(response);
|
|
47689
|
-
if (!response.ok) {
|
|
47690
|
-
if (response.status === 400 && stringField(payload, "error") === "invalid_grant") {
|
|
47691
|
-
throw new AgentLinkError("not_linked", AGENT_LINK_REVOKED_MESSAGE);
|
|
47692
|
-
}
|
|
47693
|
-
throw httpError("The vAPI agent token refresh failed.");
|
|
47694
|
-
}
|
|
47695
|
-
const refreshed = {
|
|
47696
|
-
accessToken: requiredString(payload, "access_token"),
|
|
47697
|
-
refreshToken: stringField(payload, "refresh_token") ?? current.refreshToken,
|
|
47698
|
-
expiresAt: now() + requiredNumber(payload, "expires_in") * 1e3,
|
|
47699
|
-
scopes: stringField(payload, "scope") === void 0 ? [...current.scopes] : splitScopes(requiredString(payload, "scope"))
|
|
47700
|
-
};
|
|
47701
|
-
await safeSetSecret(args.secrets, currentAccounts.tokens, JSON.stringify(refreshed));
|
|
47702
|
-
return refreshed.accessToken;
|
|
47703
|
-
});
|
|
47704
|
-
}
|
|
47705
|
-
async function withAgentCredentialLock(wallets, clientId, operation) {
|
|
47706
|
-
const digest = createHash2("sha256").update(clientId).digest("hex").slice(0, 24);
|
|
47707
|
-
const lockPath = `${wallets.registryPath}.${digest}.agent-link.lock`;
|
|
47708
|
-
await mkdir3(dirname2(lockPath), { recursive: true, mode: 448 });
|
|
47709
|
-
const deadline = Date.now() + 3e4;
|
|
47710
|
-
let handle;
|
|
47711
|
-
while (!handle) {
|
|
47712
|
-
try {
|
|
47713
|
-
handle = await open3(lockPath, "wx", 384);
|
|
47714
|
-
} catch (error61) {
|
|
47715
|
-
if (!(error61 instanceof Error && "code" in error61 && error61.code === "EEXIST")) throw error61;
|
|
47716
|
-
await removeStaleCredentialLock(lockPath);
|
|
47717
|
-
if (Date.now() >= deadline) {
|
|
47718
|
-
throw httpError("Timed out waiting to update the agent credentials.");
|
|
47719
|
-
}
|
|
47720
|
-
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
47721
|
-
}
|
|
47722
|
-
}
|
|
47723
|
-
try {
|
|
47724
|
-
return await operation();
|
|
47725
|
-
} finally {
|
|
47726
|
-
await handle.close();
|
|
47727
|
-
await unlink2(lockPath).catch(() => void 0);
|
|
47728
|
-
}
|
|
47729
|
-
}
|
|
47730
|
-
async function removeStaleCredentialLock(path) {
|
|
47731
|
-
try {
|
|
47732
|
-
const metadata = await stat2(path);
|
|
47733
|
-
if (Date.now() - metadata.mtimeMs > 5 * 6e4) await unlink2(path);
|
|
47734
|
-
} catch {
|
|
47735
|
-
}
|
|
47736
|
-
}
|
|
47737
|
-
async function safeGetSecret(secrets, account) {
|
|
47738
|
-
try {
|
|
47739
|
-
return await secrets.get(account);
|
|
47740
|
-
} catch {
|
|
47741
|
-
throw httpError("The agent tokens could not be read from the OS secret store.");
|
|
47742
|
-
}
|
|
47743
|
-
}
|
|
47744
|
-
async function safeSetSecret(secrets, account, value) {
|
|
47745
|
-
try {
|
|
47746
|
-
await secrets.set(account, value);
|
|
47747
|
-
} catch {
|
|
47748
|
-
throw httpError("The agent credentials could not be stored in the OS secret store.");
|
|
47749
|
-
}
|
|
47750
|
-
}
|
|
47751
|
-
function parseTokens(value) {
|
|
47752
|
-
if (value === void 0) return void 0;
|
|
47753
|
-
let parsed;
|
|
47754
|
-
try {
|
|
47755
|
-
parsed = JSON.parse(value);
|
|
47756
|
-
} catch {
|
|
47757
|
-
return void 0;
|
|
47758
|
-
}
|
|
47759
|
-
if (!isRecord2(parsed)) return void 0;
|
|
47760
|
-
const accessToken = stringField(parsed, "accessToken");
|
|
47761
|
-
const refreshToken = stringField(parsed, "refreshToken");
|
|
47762
|
-
const expiresAt = numberField(parsed, "expiresAt");
|
|
47763
|
-
const scopes = parsed.scopes;
|
|
47764
|
-
if (accessToken === void 0 || refreshToken === void 0 || expiresAt === void 0 || !Array.isArray(scopes) || scopes.some((scope) => typeof scope !== "string")) {
|
|
47765
|
-
return void 0;
|
|
47766
|
-
}
|
|
47767
|
-
return { accessToken, refreshToken, expiresAt, scopes };
|
|
47768
|
-
}
|
|
47769
|
-
function withBearer(init, accessToken) {
|
|
47770
|
-
const headers = new Headers(init?.headers);
|
|
47771
|
-
headers.set("authorization", `Bearer ${accessToken}`);
|
|
47772
|
-
return { ...init, headers };
|
|
47773
|
-
}
|
|
47774
|
-
async function safeFetch(fetchImpl, input2, init) {
|
|
47775
|
-
try {
|
|
47776
|
-
return await fetchImpl(input2, init);
|
|
47777
|
-
} catch {
|
|
47778
|
-
throw httpError("The vAPI agent link request failed.");
|
|
47779
|
-
}
|
|
47780
|
-
}
|
|
47781
|
-
async function safeAgentFetch(fetchImpl, input2, init) {
|
|
47782
|
-
try {
|
|
47783
|
-
return await fetchImpl(input2, init);
|
|
47784
|
-
} catch {
|
|
47785
|
-
throw httpError("The vAPI agent request failed.");
|
|
47786
|
-
}
|
|
47787
|
-
}
|
|
47788
|
-
function notLinkedError() {
|
|
47789
|
-
return new AgentLinkError("not_linked", "Run vapi login first.");
|
|
47790
|
-
}
|
|
47791
|
-
function httpError(message) {
|
|
47792
|
-
return new AgentLinkError("http", message);
|
|
47793
|
-
}
|
|
47794
|
-
function apiEndpoint(apiBase, path) {
|
|
47795
|
-
return new URL(path, new URL(apiBase).origin).toString();
|
|
47796
|
-
}
|
|
47797
|
-
async function responseRecord(response) {
|
|
47798
|
-
let payload;
|
|
47799
|
-
try {
|
|
47800
|
-
payload = await response.json();
|
|
47801
|
-
} catch {
|
|
47802
|
-
throw httpError("The vAPI agent link response was invalid.");
|
|
47803
|
-
}
|
|
47804
|
-
if (!isRecord2(payload)) throw httpError("The vAPI agent link response was invalid.");
|
|
47805
|
-
return payload;
|
|
47806
|
-
}
|
|
47807
|
-
function isRecord2(value) {
|
|
47808
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
47809
|
-
}
|
|
47810
|
-
function stringField(record2, key) {
|
|
47811
|
-
const value = record2[key];
|
|
47812
|
-
return typeof value === "string" ? value : void 0;
|
|
47813
|
-
}
|
|
47814
|
-
function numberField(record2, key) {
|
|
47815
|
-
const value = record2[key];
|
|
47816
|
-
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
47817
|
-
}
|
|
47818
|
-
function requiredString(record2, key) {
|
|
47819
|
-
const value = stringField(record2, key);
|
|
47820
|
-
if (value === void 0) throw httpError("The vAPI agent link response was invalid.");
|
|
47821
|
-
return value;
|
|
47822
|
-
}
|
|
47823
|
-
function requiredNumber(record2, key) {
|
|
47824
|
-
const value = numberField(record2, key);
|
|
47825
|
-
if (value === void 0) throw httpError("The vAPI agent link response was invalid.");
|
|
47826
|
-
return value;
|
|
47827
|
-
}
|
|
47828
|
-
function splitScopes(scope) {
|
|
47829
|
-
return scope.split(/\s+/u).filter(Boolean);
|
|
47830
|
-
}
|
|
47831
|
-
|
|
47832
50319
|
// src/cloud-backup.ts
|
|
47833
50320
|
var ERROR_MESSAGES2 = {
|
|
47834
50321
|
relay_create_failed: "The backup approval request could not be started. Try again.",
|
|
@@ -47854,8 +50341,8 @@ var RELAY_PAYLOAD_FORMAT = "vapi-vault-relay-payload";
|
|
|
47854
50341
|
var OWNER_PATTERN = /^0x[0-9a-f]{40}$/u;
|
|
47855
50342
|
var BASE64URL_PATTERN2 = /^[A-Za-z0-9_-]*$/u;
|
|
47856
50343
|
var ISO_UTC_PATTERN = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/u;
|
|
47857
|
-
var
|
|
47858
|
-
var
|
|
50344
|
+
var HKDF_KEYS2 = ["name", "salt"];
|
|
50345
|
+
var SCRYPT_KEYS2 = ["name", "salt", "N", "r", "p"];
|
|
47859
50346
|
var ENROLL_PAYLOAD_KEYS = ["format", "v", "purpose", "owner", "device", "kdf", "key"];
|
|
47860
50347
|
var RESTORE_PAYLOAD_KEYS = [...ENROLL_PAYLOAD_KEYS, "vault"];
|
|
47861
50348
|
var STORED_KEY_KEYS = ["owner", "device", "kdf", "key"];
|
|
@@ -48037,7 +50524,7 @@ async function uploadBackupLocked(options, signal) {
|
|
|
48037
50524
|
try {
|
|
48038
50525
|
let vaultFile;
|
|
48039
50526
|
try {
|
|
48040
|
-
vaultFile = await readVaultFileUnlocked(
|
|
50527
|
+
vaultFile = await readVaultFileUnlocked(join9(options.store.home, "vault.json"));
|
|
48041
50528
|
} catch (error61) {
|
|
48042
50529
|
if (error61 instanceof VaultError && error61.code === "not_found") {
|
|
48043
50530
|
return { status: "skipped", reason: "no_vault" };
|
|
@@ -48046,7 +50533,7 @@ async function uploadBackupLocked(options, signal) {
|
|
|
48046
50533
|
}
|
|
48047
50534
|
try {
|
|
48048
50535
|
vaultKey = vaultFile.protected ? await unlockProtectedVault({
|
|
48049
|
-
path:
|
|
50536
|
+
path: join9(options.store.home, "vault.json"),
|
|
48050
50537
|
secrets: options.secrets,
|
|
48051
50538
|
env: options.env,
|
|
48052
50539
|
...options.now === void 0 ? {} : { now: options.now }
|
|
@@ -48057,7 +50544,8 @@ async function uploadBackupLocked(options, signal) {
|
|
|
48057
50544
|
const source = await exportBackupSource({
|
|
48058
50545
|
store: options.store,
|
|
48059
50546
|
vaultKey,
|
|
48060
|
-
...options.networks === void 0 ? {} : { networks: options.networks }
|
|
50547
|
+
...options.networks === void 0 ? {} : { networks: options.networks },
|
|
50548
|
+
...options.agentProfileSchema === void 0 ? {} : { agentProfileSchema: options.agentProfileSchema }
|
|
48061
50549
|
});
|
|
48062
50550
|
salt = decodeBase64Url2(stored.kdf.salt);
|
|
48063
50551
|
if (salt?.byteLength !== 32) return { status: "skipped", reason: "rejected" };
|
|
@@ -48080,8 +50568,8 @@ async function uploadBackupLocked(options, signal) {
|
|
|
48080
50568
|
`wallet:${selected.name}`,
|
|
48081
50569
|
async () => {
|
|
48082
50570
|
await options.store.reload();
|
|
48083
|
-
const
|
|
48084
|
-
if (!linkMatches(
|
|
50571
|
+
const link3 = options.store.entry(selected.name)?.link;
|
|
50572
|
+
if (!linkMatches(link3, stored.owner, options.apiBase)) {
|
|
48085
50573
|
throw new AgentLinkError("not_linked", "Run vapi login first.");
|
|
48086
50574
|
}
|
|
48087
50575
|
return await agentFetch(
|
|
@@ -48092,7 +50580,7 @@ async function uploadBackupLocked(options, signal) {
|
|
|
48092
50580
|
...options.fetchImpl === void 0 ? {} : { fetchImpl: options.fetchImpl },
|
|
48093
50581
|
...now === void 0 ? {} : { now: () => now().getTime() }
|
|
48094
50582
|
},
|
|
48095
|
-
apiEndpoint2(
|
|
50583
|
+
apiEndpoint2(link3.apiBase, "/api/agents/self/backup"),
|
|
48096
50584
|
{
|
|
48097
50585
|
method: "PUT",
|
|
48098
50586
|
headers: { "content-type": "application/json" },
|
|
@@ -48122,13 +50610,18 @@ async function uploadBackupLocked(options, signal) {
|
|
|
48122
50610
|
status: "uploaded",
|
|
48123
50611
|
account: selected.name,
|
|
48124
50612
|
bytes: created.bytes,
|
|
48125
|
-
uploadedAt
|
|
50613
|
+
uploadedAt,
|
|
50614
|
+
...created.omitted === void 0 ? {} : { omitted: created.omitted },
|
|
50615
|
+
...source.skipped === void 0 ? {} : { skipped: source.skipped }
|
|
48126
50616
|
};
|
|
48127
50617
|
} catch (error61) {
|
|
48128
50618
|
if (signal.aborted) return { status: "skipped", reason: "timeout" };
|
|
48129
50619
|
if (error61 instanceof AgentLinkError && error61.code === "not_linked") {
|
|
48130
50620
|
return { status: "skipped", reason: "not_linked" };
|
|
48131
50621
|
}
|
|
50622
|
+
if (error61 instanceof BackupError && error61.code === "backup_snapshot_busy") {
|
|
50623
|
+
return { status: "skipped", reason: "snapshot_busy" };
|
|
50624
|
+
}
|
|
48132
50625
|
return { status: "skipped", reason: "rejected" };
|
|
48133
50626
|
} finally {
|
|
48134
50627
|
vaultKey?.fill(0);
|
|
@@ -48146,6 +50639,7 @@ async function restoreFromOwner(options) {
|
|
|
48146
50639
|
plaintext: options.payload.vault,
|
|
48147
50640
|
home: options.home,
|
|
48148
50641
|
vaultKey: options.vaultKey,
|
|
50642
|
+
device: options.payload.device,
|
|
48149
50643
|
...options.now === void 0 ? {} : { now: options.now },
|
|
48150
50644
|
beforePublish: async ({ vault }) => {
|
|
48151
50645
|
if (options.payload.vault?.protected === true) {
|
|
@@ -48216,7 +50710,7 @@ function parseRelayPayload(value) {
|
|
|
48216
50710
|
if (!hasExactKeys2(value, expectedKeys) || value.format !== RELAY_PAYLOAD_FORMAT || value.v !== 1 || typeof value.owner !== "string" || !OWNER_PATTERN.test(value.owner) || typeof value.device !== "string" || !DEVICE_NAME_PATTERN.test(value.device) || !isRecord3(value.kdf) || typeof value.key !== "string") {
|
|
48217
50711
|
throw new CloudBackupError("payload_invalid");
|
|
48218
50712
|
}
|
|
48219
|
-
const kdf =
|
|
50713
|
+
const kdf = parseKdf2(value.kdf);
|
|
48220
50714
|
const key = decodeBase64Url2(value.key);
|
|
48221
50715
|
if (kdf === void 0 || key?.byteLength !== 32) {
|
|
48222
50716
|
key?.fill(0);
|
|
@@ -48246,7 +50740,7 @@ function parseStoredBackupKey(value) {
|
|
|
48246
50740
|
if (!isRecord3(value) || !hasExactKeys2(value, STORED_KEY_KEYS) || typeof value.owner !== "string" || !OWNER_PATTERN.test(value.owner) || typeof value.device !== "string" || !DEVICE_NAME_PATTERN.test(value.device) || !isRecord3(value.kdf) || typeof value.key !== "string") {
|
|
48247
50741
|
return void 0;
|
|
48248
50742
|
}
|
|
48249
|
-
const kdf =
|
|
50743
|
+
const kdf = parseKdf2(value.kdf);
|
|
48250
50744
|
const key = decodeBase64Url2(value.key);
|
|
48251
50745
|
if (kdf === void 0 || key?.byteLength !== 32) {
|
|
48252
50746
|
key?.fill(0);
|
|
@@ -48256,11 +50750,11 @@ function parseStoredBackupKey(value) {
|
|
|
48256
50750
|
key.fill(0);
|
|
48257
50751
|
return { owner: value.owner, device: value.device, kdf, key: copiedKey };
|
|
48258
50752
|
}
|
|
48259
|
-
function
|
|
48260
|
-
if (value.name === "hkdf-sha256" && hasExactKeys2(value,
|
|
50753
|
+
function parseKdf2(value) {
|
|
50754
|
+
if (value.name === "hkdf-sha256" && hasExactKeys2(value, HKDF_KEYS2) && typeof value.salt === "string" && isBase64UrlBytes(value.salt, 32)) {
|
|
48261
50755
|
return { name: "hkdf-sha256", salt: value.salt };
|
|
48262
50756
|
}
|
|
48263
|
-
if (value.name === "scrypt" && hasExactKeys2(value,
|
|
50757
|
+
if (value.name === "scrypt" && hasExactKeys2(value, SCRYPT_KEYS2) && typeof value.salt === "string" && isBase64UrlBytes(value.salt, 32) && value.N === 131072 && value.r === 8 && value.p === 1) {
|
|
48264
50758
|
return { name: "scrypt", salt: value.salt, N: 131072, r: 8, p: 1 };
|
|
48265
50759
|
}
|
|
48266
50760
|
return void 0;
|
|
@@ -48295,11 +50789,11 @@ async function selectLinkedAccount(options, owner) {
|
|
|
48295
50789
|
}
|
|
48296
50790
|
return void 0;
|
|
48297
50791
|
}
|
|
48298
|
-
function linkMatches(
|
|
48299
|
-
if (
|
|
50792
|
+
function linkMatches(link3, owner, apiBase) {
|
|
50793
|
+
if (link3 === void 0 || link3.owner.toLowerCase() !== owner.toLowerCase()) return false;
|
|
48300
50794
|
if (apiBase === void 0) return true;
|
|
48301
50795
|
try {
|
|
48302
|
-
return new URL(
|
|
50796
|
+
return new URL(link3.apiBase).origin === new URL(apiBase).origin;
|
|
48303
50797
|
} catch {
|
|
48304
50798
|
return false;
|
|
48305
50799
|
}
|
|
@@ -48391,6 +50885,7 @@ export {
|
|
|
48391
50885
|
awaitRelay,
|
|
48392
50886
|
backupKeyAccount,
|
|
48393
50887
|
backupStateAccount,
|
|
50888
|
+
buildRelayRestorePayload,
|
|
48394
50889
|
forgetBackupKey,
|
|
48395
50890
|
readBackupKey,
|
|
48396
50891
|
readCloudBackupState,
|