@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/router-client.js
CHANGED
|
@@ -4373,7 +4373,7 @@ function weierstrass(curveDef) {
|
|
|
4373
4373
|
function prepSig(msgHash, privateKey, opts = defaultSigOpts) {
|
|
4374
4374
|
if (["recovered", "canonical"].some((k) => k in opts))
|
|
4375
4375
|
throw new Error("sign() legacy options not supported");
|
|
4376
|
-
const { hash: hash4, randomBytes:
|
|
4376
|
+
const { hash: hash4, randomBytes: randomBytes9 } = CURVE;
|
|
4377
4377
|
let { lowS, prehash, extraEntropy: ent } = opts;
|
|
4378
4378
|
if (lowS == null)
|
|
4379
4379
|
lowS = true;
|
|
@@ -4385,7 +4385,7 @@ function weierstrass(curveDef) {
|
|
|
4385
4385
|
const d = normPrivateKeyToScalar(privateKey);
|
|
4386
4386
|
const seedArgs = [int2octets(d), int2octets(h1int)];
|
|
4387
4387
|
if (ent != null && ent !== false) {
|
|
4388
|
-
const e3 = ent === true ?
|
|
4388
|
+
const e3 = ent === true ? randomBytes9(Fp.BYTES) : ent;
|
|
4389
4389
|
seedArgs.push(ensureBytes("extraEntropy", e3));
|
|
4390
4390
|
}
|
|
4391
4391
|
const seed = concatBytes3(...seedArgs);
|
|
@@ -28522,8 +28522,8 @@ function date4(params) {
|
|
|
28522
28522
|
|
|
28523
28523
|
// src/agent-link.ts
|
|
28524
28524
|
import { createHash } from "node:crypto";
|
|
28525
|
-
import { mkdir as
|
|
28526
|
-
import { dirname as
|
|
28525
|
+
import { mkdir as mkdir4, open as open3, stat as stat3, unlink as unlink2 } from "node:fs/promises";
|
|
28526
|
+
import { dirname as dirname3 } from "node:path";
|
|
28527
28527
|
|
|
28528
28528
|
// ../../node_modules/.pnpm/viem@2.56.1_typescript@6.0.3_zod@4.5.4/node_modules/viem/_esm/utils/signature/hashMessage.js
|
|
28529
28529
|
init_keccak256();
|
|
@@ -30917,12 +30917,14 @@ function uid(length = 11) {
|
|
|
30917
30917
|
}
|
|
30918
30918
|
|
|
30919
30919
|
// src/audit.ts
|
|
30920
|
-
import { appendFile, chmod, mkdir, readFile } from "node:fs/promises";
|
|
30921
|
-
import { join as
|
|
30920
|
+
import { appendFile, chmod, mkdir as mkdir3, readFile as readFile3 } from "node:fs/promises";
|
|
30921
|
+
import { join as join3 } from "node:path";
|
|
30922
30922
|
|
|
30923
30923
|
// src/config.ts
|
|
30924
|
+
import { constants } from "node:fs";
|
|
30925
|
+
import { copyFile, mkdir as mkdir2, readFile as readFile2, rename as rename2, stat as stat2, writeFile } from "node:fs/promises";
|
|
30924
30926
|
import { homedir } from "node:os";
|
|
30925
|
-
import { dirname, join } from "node:path";
|
|
30927
|
+
import { dirname as dirname2, join as join2 } from "node:path";
|
|
30926
30928
|
|
|
30927
30929
|
// ../../node_modules/.pnpm/viem@2.56.1_typescript@6.0.3_zod@4.5.4/node_modules/viem/_esm/utils/abi/encodeEventTopics.js
|
|
30928
30930
|
init_abi();
|
|
@@ -36625,6 +36627,225 @@ init_getAddress();
|
|
|
36625
36627
|
init_isAddress();
|
|
36626
36628
|
init_toHex();
|
|
36627
36629
|
|
|
36630
|
+
// src/atomic-file.ts
|
|
36631
|
+
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
36632
|
+
import { link, mkdir, open as open2, readdir, readFile, rename, stat, unlink } from "node:fs/promises";
|
|
36633
|
+
import { basename, dirname, join } from "node:path";
|
|
36634
|
+
var DEFAULT_LOCK_TIMEOUT_MS = 2e3;
|
|
36635
|
+
var LOCK_RETRY_MS = 20;
|
|
36636
|
+
var LOCK_STALE_AFTER_MS = 12e4;
|
|
36637
|
+
var LOCK_HEARTBEAT_MS = 3e4;
|
|
36638
|
+
var atomicFileTestHooks;
|
|
36639
|
+
var FileLockedError = class extends Error {
|
|
36640
|
+
constructor(message = "Timed out waiting for the file lock.") {
|
|
36641
|
+
super(message);
|
|
36642
|
+
this.name = "FileLockedError";
|
|
36643
|
+
}
|
|
36644
|
+
};
|
|
36645
|
+
async function withFileLock(lockPath, fn, options = {}) {
|
|
36646
|
+
await mkdir(dirname(lockPath), { recursive: true, mode: 448 });
|
|
36647
|
+
const deadline = Date.now() + (options.timeoutMs ?? DEFAULT_LOCK_TIMEOUT_MS);
|
|
36648
|
+
const ownerRecord = {
|
|
36649
|
+
pid: process.pid,
|
|
36650
|
+
token: randomBytes2(16).toString("hex")
|
|
36651
|
+
};
|
|
36652
|
+
const owner = JSON.stringify(ownerRecord);
|
|
36653
|
+
const releasedOwner = JSON.stringify({ ...ownerRecord, released: true });
|
|
36654
|
+
let handle;
|
|
36655
|
+
while (handle === void 0) {
|
|
36656
|
+
await atomicFileTestHooks?.beforeLockAttempt?.(lockPath);
|
|
36657
|
+
try {
|
|
36658
|
+
const candidate = await open2(lockPath, "wx", 384);
|
|
36659
|
+
try {
|
|
36660
|
+
await candidate.writeFile(owner, "utf8");
|
|
36661
|
+
await candidate.sync();
|
|
36662
|
+
await atomicFileTestHooks?.afterLockCreate?.(lockPath);
|
|
36663
|
+
if (await lockTakeoverInProgress(lockPath)) {
|
|
36664
|
+
await candidate.close();
|
|
36665
|
+
await removeOwnedLock(lockPath, [owner]);
|
|
36666
|
+
await removeAbandonedLockTombs(lockPath);
|
|
36667
|
+
if (Date.now() >= deadline) {
|
|
36668
|
+
throw new FileLockedError(options.lockedMessage);
|
|
36669
|
+
}
|
|
36670
|
+
await new Promise((resolve) => setTimeout(resolve, LOCK_RETRY_MS));
|
|
36671
|
+
continue;
|
|
36672
|
+
}
|
|
36673
|
+
handle = candidate;
|
|
36674
|
+
} catch (error61) {
|
|
36675
|
+
await candidate.close().catch(() => void 0);
|
|
36676
|
+
await removeOwnedLock(lockPath, [owner]).catch(() => void 0);
|
|
36677
|
+
throw error61;
|
|
36678
|
+
}
|
|
36679
|
+
} catch (error61) {
|
|
36680
|
+
if (!hasCode(error61, "EEXIST")) throw error61;
|
|
36681
|
+
await removeStaleLock(lockPath);
|
|
36682
|
+
if (Date.now() >= deadline) {
|
|
36683
|
+
throw new FileLockedError(options.lockedMessage);
|
|
36684
|
+
}
|
|
36685
|
+
await new Promise((resolve) => setTimeout(resolve, LOCK_RETRY_MS));
|
|
36686
|
+
}
|
|
36687
|
+
}
|
|
36688
|
+
const ownedHandle = handle;
|
|
36689
|
+
const heartbeat = setInterval(() => {
|
|
36690
|
+
const time3 = /* @__PURE__ */ new Date();
|
|
36691
|
+
void ownedHandle.utimes(time3, time3).catch(() => void 0);
|
|
36692
|
+
}, LOCK_HEARTBEAT_MS);
|
|
36693
|
+
heartbeat.unref();
|
|
36694
|
+
try {
|
|
36695
|
+
return await fn();
|
|
36696
|
+
} finally {
|
|
36697
|
+
clearInterval(heartbeat);
|
|
36698
|
+
try {
|
|
36699
|
+
await markLockReleased(ownedHandle, releasedOwner);
|
|
36700
|
+
} finally {
|
|
36701
|
+
try {
|
|
36702
|
+
await ownedHandle.close();
|
|
36703
|
+
} finally {
|
|
36704
|
+
await removeOwnedLock(lockPath, [owner, releasedOwner]);
|
|
36705
|
+
}
|
|
36706
|
+
}
|
|
36707
|
+
}
|
|
36708
|
+
}
|
|
36709
|
+
async function removeStaleLock(path) {
|
|
36710
|
+
try {
|
|
36711
|
+
const owner = await readFile(path, "utf8");
|
|
36712
|
+
if (lockOwnerReleased(owner)) {
|
|
36713
|
+
await removeOwnedLock(path, [owner]);
|
|
36714
|
+
return;
|
|
36715
|
+
}
|
|
36716
|
+
const metadata = await stat(path);
|
|
36717
|
+
if (Date.now() - metadata.mtimeMs <= LOCK_STALE_AFTER_MS) return;
|
|
36718
|
+
const pid = lockOwnerPid(owner);
|
|
36719
|
+
if (pid !== void 0 && processIsAlive(pid)) return;
|
|
36720
|
+
await atomicFileTestHooks?.beforeStaleLockBreak?.(path);
|
|
36721
|
+
await removeOwnedLock(path, [owner]);
|
|
36722
|
+
} catch (error61) {
|
|
36723
|
+
if (!hasCode(error61, "ENOENT")) throw error61;
|
|
36724
|
+
}
|
|
36725
|
+
}
|
|
36726
|
+
async function removeOwnedLock(path, owners) {
|
|
36727
|
+
const tombPath = `${path}.stale.${randomBytes2(16).toString("hex")}`;
|
|
36728
|
+
try {
|
|
36729
|
+
await rename(path, tombPath);
|
|
36730
|
+
} catch (error61) {
|
|
36731
|
+
if (hasCode(error61, "ENOENT")) {
|
|
36732
|
+
await removeOwnedTombs(path, owners);
|
|
36733
|
+
const currentOwner = await readFile(path, "utf8").catch(() => void 0);
|
|
36734
|
+
if (currentOwner !== void 0 && owners.includes(currentOwner)) {
|
|
36735
|
+
await removeOwnedLock(path, owners);
|
|
36736
|
+
}
|
|
36737
|
+
return;
|
|
36738
|
+
}
|
|
36739
|
+
throw error61;
|
|
36740
|
+
}
|
|
36741
|
+
let pendingError;
|
|
36742
|
+
try {
|
|
36743
|
+
const renamedOwner = await readFile(tombPath, "utf8");
|
|
36744
|
+
if (!owners.includes(renamedOwner) && !lockOwnerReleased(renamedOwner)) {
|
|
36745
|
+
await atomicFileTestHooks?.afterMismatchedLockRename?.(path);
|
|
36746
|
+
const currentOwner = await readFile(tombPath, "utf8");
|
|
36747
|
+
if (!lockOwnerReleased(currentOwner)) await restoreRenamedLock(path, tombPath);
|
|
36748
|
+
await atomicFileTestHooks?.afterMismatchedLockRestore?.(path);
|
|
36749
|
+
}
|
|
36750
|
+
} catch (error61) {
|
|
36751
|
+
pendingError = error61;
|
|
36752
|
+
}
|
|
36753
|
+
try {
|
|
36754
|
+
await unlink(tombPath);
|
|
36755
|
+
} catch (error61) {
|
|
36756
|
+
if (!hasCode(error61, "ENOENT") && pendingError === void 0) {
|
|
36757
|
+
pendingError = error61;
|
|
36758
|
+
}
|
|
36759
|
+
}
|
|
36760
|
+
if (pendingError !== void 0) throw pendingError;
|
|
36761
|
+
}
|
|
36762
|
+
async function lockTakeoverInProgress(path) {
|
|
36763
|
+
const prefix = `${basename(path)}.stale.`;
|
|
36764
|
+
return (await readdir(dirname(path))).some((entry) => entry.startsWith(prefix));
|
|
36765
|
+
}
|
|
36766
|
+
async function removeAbandonedLockTombs(path) {
|
|
36767
|
+
const directory = dirname(path);
|
|
36768
|
+
const prefix = `${basename(path)}.stale.`;
|
|
36769
|
+
for (const entry of await readdir(directory)) {
|
|
36770
|
+
if (!entry.startsWith(prefix)) continue;
|
|
36771
|
+
const tombPath = join(directory, entry);
|
|
36772
|
+
const [metadata, owner] = await Promise.all([
|
|
36773
|
+
stat(tombPath).catch(() => void 0),
|
|
36774
|
+
readFile(tombPath, "utf8").catch(() => void 0)
|
|
36775
|
+
]);
|
|
36776
|
+
if (metadata === void 0 || owner === void 0) continue;
|
|
36777
|
+
const pid = lockOwnerPid(owner);
|
|
36778
|
+
const abandoned = lockOwnerReleased(owner) || Date.now() - metadata.mtimeMs > LOCK_STALE_AFTER_MS && (pid === void 0 || !processIsAlive(pid));
|
|
36779
|
+
if (!abandoned) continue;
|
|
36780
|
+
await unlink(tombPath).catch((error61) => {
|
|
36781
|
+
if (!hasCode(error61, "ENOENT")) throw error61;
|
|
36782
|
+
});
|
|
36783
|
+
}
|
|
36784
|
+
}
|
|
36785
|
+
async function removeOwnedTombs(path, owners) {
|
|
36786
|
+
const directory = dirname(path);
|
|
36787
|
+
const prefix = `${basename(path)}.stale.`;
|
|
36788
|
+
for (const entry of await readdir(directory)) {
|
|
36789
|
+
if (!entry.startsWith(prefix)) continue;
|
|
36790
|
+
const tombPath = join(directory, entry);
|
|
36791
|
+
const owner = await readFile(tombPath, "utf8").catch(() => void 0);
|
|
36792
|
+
if (owner === void 0 || !owners.includes(owner)) continue;
|
|
36793
|
+
await unlink(tombPath).catch((error61) => {
|
|
36794
|
+
if (!hasCode(error61, "ENOENT")) throw error61;
|
|
36795
|
+
});
|
|
36796
|
+
}
|
|
36797
|
+
}
|
|
36798
|
+
async function restoreRenamedLock(path, tombPath) {
|
|
36799
|
+
while (true) {
|
|
36800
|
+
try {
|
|
36801
|
+
await link(tombPath, path);
|
|
36802
|
+
return;
|
|
36803
|
+
} catch (error61) {
|
|
36804
|
+
if (hasCode(error61, "ENOENT")) return;
|
|
36805
|
+
if (!hasCode(error61, "EEXIST")) throw error61;
|
|
36806
|
+
await new Promise((resolve) => setTimeout(resolve, LOCK_RETRY_MS));
|
|
36807
|
+
}
|
|
36808
|
+
}
|
|
36809
|
+
}
|
|
36810
|
+
async function markLockReleased(handle, owner) {
|
|
36811
|
+
const contents = Buffer.from(owner, "utf8");
|
|
36812
|
+
await handle.write(contents, 0, contents.length, 0);
|
|
36813
|
+
await handle.truncate(contents.length);
|
|
36814
|
+
await handle.sync();
|
|
36815
|
+
}
|
|
36816
|
+
function lockOwnerPid(owner) {
|
|
36817
|
+
try {
|
|
36818
|
+
const value = JSON.parse(owner);
|
|
36819
|
+
if (typeof value !== "object" || value === null) return void 0;
|
|
36820
|
+
const pid = Reflect.get(value, "pid");
|
|
36821
|
+
return typeof pid === "number" && Number.isSafeInteger(pid) && pid > 0 ? pid : void 0;
|
|
36822
|
+
} catch {
|
|
36823
|
+
return void 0;
|
|
36824
|
+
}
|
|
36825
|
+
}
|
|
36826
|
+
function lockOwnerReleased(owner) {
|
|
36827
|
+
try {
|
|
36828
|
+
const value = JSON.parse(owner);
|
|
36829
|
+
return typeof value === "object" && value !== null && Reflect.get(value, "released") === true;
|
|
36830
|
+
} catch {
|
|
36831
|
+
return false;
|
|
36832
|
+
}
|
|
36833
|
+
}
|
|
36834
|
+
function processIsAlive(pid) {
|
|
36835
|
+
if (atomicFileTestHooks?.isProcessAlive !== void 0) {
|
|
36836
|
+
return atomicFileTestHooks.isProcessAlive(pid);
|
|
36837
|
+
}
|
|
36838
|
+
try {
|
|
36839
|
+
process.kill(pid, 0);
|
|
36840
|
+
return true;
|
|
36841
|
+
} catch (error61) {
|
|
36842
|
+
return hasCode(error61, "EPERM");
|
|
36843
|
+
}
|
|
36844
|
+
}
|
|
36845
|
+
function hasCode(error61, code) {
|
|
36846
|
+
return error61 instanceof Error && "code" in error61 && error61.code === code;
|
|
36847
|
+
}
|
|
36848
|
+
|
|
36628
36849
|
// src/x402-networks.ts
|
|
36629
36850
|
var BASE_MAINNET_CAIP2 = "eip155:8453";
|
|
36630
36851
|
var ARC_MAINNET_CAIP2 = "eip155:5042";
|
|
@@ -37044,6 +37265,12 @@ function createNetworkHttpTransport(rpcUrl, options = {}) {
|
|
|
37044
37265
|
var DEFAULT_REGISTRY_URL = "https://api.vapinetwork.ai";
|
|
37045
37266
|
var DEFAULT_DISCOVERY_URL = `${DEFAULT_REGISTRY_URL}/api/call/services`;
|
|
37046
37267
|
var DEFAULT_MARKETPLACE_DISCOVERY_URL = `${DEFAULT_REGISTRY_URL}/api/call/discovery`;
|
|
37268
|
+
var DEFAULT_REGISTRY_FALLBACKS = [
|
|
37269
|
+
{
|
|
37270
|
+
discoveryUrl: DEFAULT_DISCOVERY_URL,
|
|
37271
|
+
marketplaceDiscoveryUrl: DEFAULT_MARKETPLACE_DISCOVERY_URL
|
|
37272
|
+
}
|
|
37273
|
+
];
|
|
37047
37274
|
var DEFAULT_SPEND_CAPS = {
|
|
37048
37275
|
perCallAtomic: "100000",
|
|
37049
37276
|
perDayAtomic: "1000000"
|
|
@@ -37063,6 +37290,14 @@ var configSchema = external_exports.object({
|
|
|
37063
37290
|
})
|
|
37064
37291
|
).optional(),
|
|
37065
37292
|
allowPrivateNetwork: external_exports.boolean().default(false).optional(),
|
|
37293
|
+
tasksEscrowDurationOverrides: external_exports.record(
|
|
37294
|
+
external_exports.string().regex(/^\d+$/),
|
|
37295
|
+
external_exports.object({
|
|
37296
|
+
workDurationSeconds: external_exports.number().int().positive().max(Number.MAX_SAFE_INTEGER).optional(),
|
|
37297
|
+
reviewWindowSeconds: external_exports.number().int().positive().max(Number.MAX_SAFE_INTEGER).optional()
|
|
37298
|
+
}).strict()
|
|
37299
|
+
).optional(),
|
|
37300
|
+
tasksEscrowFactoryOverrides: external_exports.record(external_exports.string().regex(/^\d+$/), external_exports.string()).optional(),
|
|
37066
37301
|
networks: external_exports.record(
|
|
37067
37302
|
external_exports.string().refine((network) => isSupportedPaymentNetwork(network), {
|
|
37068
37303
|
message: "Expected a supported eip155:<chainId> or Solana network identifier."
|
|
@@ -37081,6 +37316,17 @@ var configSchema = external_exports.object({
|
|
|
37081
37316
|
spendCaps: spendCapsSchema,
|
|
37082
37317
|
device: external_exports.string().regex(/^[a-z0-9][a-z0-9-]{0,31}$/).optional()
|
|
37083
37318
|
}).superRefine((config2, context) => {
|
|
37319
|
+
for (const [chainId, factory] of Object.entries(config2.tasksEscrowFactoryOverrides ?? {})) {
|
|
37320
|
+
try {
|
|
37321
|
+
getAddress(factory);
|
|
37322
|
+
} catch {
|
|
37323
|
+
context.addIssue({
|
|
37324
|
+
code: "custom",
|
|
37325
|
+
path: ["tasksEscrowFactoryOverrides", chainId],
|
|
37326
|
+
message: "Expected a valid EVM escrow factory address."
|
|
37327
|
+
});
|
|
37328
|
+
}
|
|
37329
|
+
}
|
|
37084
37330
|
for (const [network, configured] of Object.entries(config2.networks)) {
|
|
37085
37331
|
if (isSolanaNetwork(network)) {
|
|
37086
37332
|
if (configured.usdc !== NETWORKS[SOLANA_MAINNET_CAIP2].usdc) {
|
|
@@ -37110,31 +37356,307 @@ var configSchema = external_exports.object({
|
|
|
37110
37356
|
}
|
|
37111
37357
|
}
|
|
37112
37358
|
});
|
|
37113
|
-
function getVapiPaths(directory = process.env.VAPI_HOME?.trim() ||
|
|
37359
|
+
function getVapiPaths(directory = process.env.VAPI_HOME?.trim() || join2(homedir(), ".vapi")) {
|
|
37114
37360
|
return {
|
|
37115
37361
|
directory,
|
|
37116
|
-
agentsDir:
|
|
37117
|
-
config:
|
|
37118
|
-
keystore:
|
|
37119
|
-
receipts:
|
|
37120
|
-
searches:
|
|
37121
|
-
ledger:
|
|
37362
|
+
agentsDir: join2(directory, "agents"),
|
|
37363
|
+
config: join2(directory, "config.json"),
|
|
37364
|
+
keystore: join2(directory, "keystore.json"),
|
|
37365
|
+
receipts: join2(directory, "receipts.jsonl"),
|
|
37366
|
+
searches: join2(directory, "searches.jsonl"),
|
|
37367
|
+
ledger: join2(directory, "spend-ledger.json")
|
|
37122
37368
|
};
|
|
37123
37369
|
}
|
|
37124
37370
|
var getAgentCashPaths = getVapiPaths;
|
|
37371
|
+
async function migrateLegacyVapiHome(options = {}) {
|
|
37372
|
+
const target = getVapiPaths(options.targetDirectory);
|
|
37373
|
+
const legacyDirectory = options.legacyDirectory ?? join2(homedir(), ".vapi", "agent-cash");
|
|
37374
|
+
if (legacyDirectory === target.directory || !await pathExists(legacyDirectory)) return [];
|
|
37375
|
+
await mkdir2(target.directory, { recursive: true, mode: 448 });
|
|
37376
|
+
const copied = [];
|
|
37377
|
+
for (const filename of [
|
|
37378
|
+
"config.json",
|
|
37379
|
+
"keystore.json",
|
|
37380
|
+
"receipts.jsonl",
|
|
37381
|
+
"searches.jsonl",
|
|
37382
|
+
"spend-ledger.json"
|
|
37383
|
+
]) {
|
|
37384
|
+
const source = join2(legacyDirectory, filename);
|
|
37385
|
+
const destination = join2(target.directory, filename);
|
|
37386
|
+
try {
|
|
37387
|
+
const copy = async () => await copyFile(source, destination, constants.COPYFILE_EXCL);
|
|
37388
|
+
if (filename === "receipts.jsonl") {
|
|
37389
|
+
await withFileLock(`${destination}.lock`, copy, {
|
|
37390
|
+
lockedMessage: "The receipt journal is busy. Wait for the other vapi command to finish, then retry."
|
|
37391
|
+
});
|
|
37392
|
+
} else {
|
|
37393
|
+
await copy();
|
|
37394
|
+
}
|
|
37395
|
+
copied.push(destination);
|
|
37396
|
+
} catch (error61) {
|
|
37397
|
+
if (isMissingFile(error61) || isAlreadyExists(error61)) continue;
|
|
37398
|
+
throw error61;
|
|
37399
|
+
}
|
|
37400
|
+
}
|
|
37401
|
+
if (copied.length > 0) {
|
|
37402
|
+
const message = `Migrated legacy vAPI wallet files from ${legacyDirectory} to ${target.directory}; originals were kept.`;
|
|
37403
|
+
(options.notice ?? ((value) => process.stderr.write(`${value}
|
|
37404
|
+
`)))(message);
|
|
37405
|
+
}
|
|
37406
|
+
return copied;
|
|
37407
|
+
}
|
|
37408
|
+
var LEGACY_REGISTRY_HOSTS = /* @__PURE__ */ new Map([
|
|
37409
|
+
["console.vapinetwork.ai", "api.vapinetwork.ai"],
|
|
37410
|
+
["console-staging.vapinetwork.ai", "api-staging.vapinetwork.ai"]
|
|
37411
|
+
]);
|
|
37412
|
+
var LEGACY_REGISTRY_PATHS = /* @__PURE__ */ new Map([
|
|
37413
|
+
["/api/network/services", "/api/call/services"],
|
|
37414
|
+
["/api/marketplace/discovery", "/api/call/discovery"]
|
|
37415
|
+
]);
|
|
37416
|
+
var REGISTRY_DOMAIN = "vapinetwork.ai";
|
|
37417
|
+
function rewriteLegacyRegistryUrl(value) {
|
|
37418
|
+
let url2;
|
|
37419
|
+
try {
|
|
37420
|
+
url2 = new URL(value);
|
|
37421
|
+
} catch {
|
|
37422
|
+
return value;
|
|
37423
|
+
}
|
|
37424
|
+
let rewritten = false;
|
|
37425
|
+
const host = LEGACY_REGISTRY_HOSTS.get(url2.hostname.toLowerCase());
|
|
37426
|
+
if (host) {
|
|
37427
|
+
url2.hostname = host;
|
|
37428
|
+
rewritten = true;
|
|
37429
|
+
}
|
|
37430
|
+
const hostname3 = url2.hostname.toLowerCase();
|
|
37431
|
+
if (hostname3 === REGISTRY_DOMAIN || hostname3.endsWith(`.${REGISTRY_DOMAIN}`)) {
|
|
37432
|
+
const path = LEGACY_REGISTRY_PATHS.get(url2.pathname.replace(/\/+$/, ""));
|
|
37433
|
+
if (path) {
|
|
37434
|
+
url2.pathname = path;
|
|
37435
|
+
rewritten = true;
|
|
37436
|
+
}
|
|
37437
|
+
}
|
|
37438
|
+
return rewritten ? url2.href : value;
|
|
37439
|
+
}
|
|
37440
|
+
function rewriteLegacyRegistryUrls(config2) {
|
|
37441
|
+
const changes = [];
|
|
37442
|
+
const rewrite = (field, from15) => {
|
|
37443
|
+
const to = rewriteLegacyRegistryUrl(from15);
|
|
37444
|
+
if (to !== from15) changes.push({ field, from: from15, to });
|
|
37445
|
+
return to;
|
|
37446
|
+
};
|
|
37447
|
+
const next = {
|
|
37448
|
+
...config2,
|
|
37449
|
+
discoveryUrl: rewrite("discoveryUrl", config2.discoveryUrl),
|
|
37450
|
+
marketplaceDiscoveryUrl: rewrite("marketplaceDiscoveryUrl", config2.marketplaceDiscoveryUrl),
|
|
37451
|
+
...config2.registryFallbacks ? {
|
|
37452
|
+
registryFallbacks: config2.registryFallbacks.map((fallback, index2) => ({
|
|
37453
|
+
discoveryUrl: rewrite(
|
|
37454
|
+
`registryFallbacks[${index2}].discoveryUrl`,
|
|
37455
|
+
fallback.discoveryUrl
|
|
37456
|
+
),
|
|
37457
|
+
marketplaceDiscoveryUrl: rewrite(
|
|
37458
|
+
`registryFallbacks[${index2}].marketplaceDiscoveryUrl`,
|
|
37459
|
+
fallback.marketplaceDiscoveryUrl
|
|
37460
|
+
)
|
|
37461
|
+
}))
|
|
37462
|
+
} : {}
|
|
37463
|
+
};
|
|
37464
|
+
return changes.length === 0 ? { config: config2, changes } : { config: next, changes };
|
|
37465
|
+
}
|
|
37466
|
+
function formatLegacyRegistryRewrites(changes) {
|
|
37467
|
+
return [
|
|
37468
|
+
"Rewrote retired vAPI registry URLs in the local config:",
|
|
37469
|
+
...changes.map((change) => ` ${change.field}: ${change.from} -> ${change.to}`)
|
|
37470
|
+
].join("\n");
|
|
37471
|
+
}
|
|
37472
|
+
function getDefaultConfig(source = process.env, options = {}) {
|
|
37473
|
+
const registry2 = registryEndpoints(source.VAPI_REGISTRY_URL?.trim() || DEFAULT_REGISTRY_URL);
|
|
37474
|
+
const requested = new Set(options.networks ?? ["base"]);
|
|
37475
|
+
const networks = {};
|
|
37476
|
+
const tasksEscrowFactoryOverrides = tasksFactoryOverridesFromEnv(source);
|
|
37477
|
+
if (requested.has("base") || requested.has(BASE_MAINNET_CAIP2)) {
|
|
37478
|
+
networks[BASE_MAINNET_CAIP2] = {
|
|
37479
|
+
rpcUrl: source.BASE_RPC_URL?.trim() || NETWORKS[BASE_MAINNET_CAIP2].publicRpcUrl,
|
|
37480
|
+
usdc: NETWORKS[BASE_MAINNET_CAIP2].usdc
|
|
37481
|
+
};
|
|
37482
|
+
}
|
|
37483
|
+
const arcMainnetRpcUrl = source.ARC_RPC_URL?.trim();
|
|
37484
|
+
if (arcMainnetRpcUrl || requested.has("arc") || requested.has(ARC_MAINNET_CAIP2)) {
|
|
37485
|
+
networks[ARC_MAINNET_CAIP2] = {
|
|
37486
|
+
rpcUrl: arcMainnetRpcUrl || NETWORKS[ARC_MAINNET_CAIP2].publicRpcUrl,
|
|
37487
|
+
usdc: NETWORKS[ARC_MAINNET_CAIP2].usdc
|
|
37488
|
+
};
|
|
37489
|
+
}
|
|
37490
|
+
const arcTestnetRpcUrl = source.ARC_TESTNET_RPC_URL?.trim();
|
|
37491
|
+
if (arcTestnetRpcUrl || requested.has("arc-testnet") || requested.has(ARC_TESTNET_CAIP2)) {
|
|
37492
|
+
if (!arcTestnetRpcUrl) {
|
|
37493
|
+
throw new Error(
|
|
37494
|
+
"Arc testnet RPC is required. Set ARC_TESTNET_RPC_URL before enabling Arc testnet."
|
|
37495
|
+
);
|
|
37496
|
+
}
|
|
37497
|
+
networks[ARC_TESTNET_CAIP2] = {
|
|
37498
|
+
rpcUrl: arcTestnetRpcUrl,
|
|
37499
|
+
usdc: NETWORKS[ARC_TESTNET_CAIP2].usdc
|
|
37500
|
+
};
|
|
37501
|
+
}
|
|
37502
|
+
const solanaRpcUrl = source.SOLANA_RPC_URL?.trim();
|
|
37503
|
+
if (solanaRpcUrl || requested.has("solana") || requested.has(SOLANA_MAINNET_CAIP2)) {
|
|
37504
|
+
networks[SOLANA_MAINNET_CAIP2] = {
|
|
37505
|
+
rpcUrl: solanaRpcUrl || NETWORKS[SOLANA_MAINNET_CAIP2].publicRpcUrl,
|
|
37506
|
+
usdc: NETWORKS[SOLANA_MAINNET_CAIP2].usdc
|
|
37507
|
+
};
|
|
37508
|
+
}
|
|
37509
|
+
return {
|
|
37510
|
+
discoveryUrl: source.VAPI_DISCOVERY_URL?.trim() || registry2.discoveryUrl,
|
|
37511
|
+
marketplaceDiscoveryUrl: source.VAPI_MARKETPLACE_DISCOVERY_URL?.trim() || registry2.marketplaceDiscoveryUrl,
|
|
37512
|
+
registryFallbacks: DEFAULT_REGISTRY_FALLBACKS.map((fallback) => ({ ...fallback })),
|
|
37513
|
+
allowPrivateNetwork: false,
|
|
37514
|
+
tasksEscrowFactoryOverrides,
|
|
37515
|
+
tasksEscrowDurationOverrides: tasksDurationOverridesFromEnv(source),
|
|
37516
|
+
networks,
|
|
37517
|
+
spendCaps: { ...DEFAULT_SPEND_CAPS }
|
|
37518
|
+
};
|
|
37519
|
+
}
|
|
37520
|
+
async function loadConfig(path = getVapiPaths().config, source = process.env, options = {}) {
|
|
37521
|
+
if (!process.env.VAPI_HOME?.trim() && path === getVapiPaths(join2(homedir(), ".vapi")).config) {
|
|
37522
|
+
await migrateLegacyVapiHome({ targetDirectory: join2(homedir(), ".vapi") });
|
|
37523
|
+
}
|
|
37524
|
+
let raw;
|
|
37525
|
+
try {
|
|
37526
|
+
raw = await readFile2(path, "utf8");
|
|
37527
|
+
} catch (error61) {
|
|
37528
|
+
if (isMissingFile(error61)) return getDefaultConfig(source);
|
|
37529
|
+
throw error61;
|
|
37530
|
+
}
|
|
37531
|
+
const parsed = configSchema.parse(JSON.parse(raw));
|
|
37532
|
+
parsed.registryFallbacks ??= DEFAULT_REGISTRY_FALLBACKS.map((fallback) => ({ ...fallback }));
|
|
37533
|
+
parsed.tasksEscrowFactoryOverrides ??= {};
|
|
37534
|
+
const migrated = rewriteLegacyRegistryUrls(parsed);
|
|
37535
|
+
const config2 = migrated.config;
|
|
37536
|
+
config2.tasksEscrowFactoryOverrides ??= {};
|
|
37537
|
+
if (migrated.changes.length > 0 && options.notice) {
|
|
37538
|
+
options.notice(
|
|
37539
|
+
`${formatLegacyRegistryRewrites(migrated.changes)}
|
|
37540
|
+
Run vapi init to write the new URLs to ${path}.`
|
|
37541
|
+
);
|
|
37542
|
+
}
|
|
37543
|
+
const baseRpcUrl = source.BASE_RPC_URL?.trim();
|
|
37544
|
+
const arcMainnetRpcUrl = source.ARC_RPC_URL?.trim();
|
|
37545
|
+
const arcTestnetRpcUrl = source.ARC_TESTNET_RPC_URL?.trim();
|
|
37546
|
+
const solanaRpcUrl = source.SOLANA_RPC_URL?.trim();
|
|
37547
|
+
const discoveryUrl = source.VAPI_DISCOVERY_URL?.trim();
|
|
37548
|
+
const marketplaceDiscoveryUrl = source.VAPI_MARKETPLACE_DISCOVERY_URL?.trim();
|
|
37549
|
+
const registryUrl = source.VAPI_REGISTRY_URL?.trim();
|
|
37550
|
+
Object.assign(config2.tasksEscrowFactoryOverrides, tasksFactoryOverridesFromEnv(source));
|
|
37551
|
+
config2.tasksEscrowDurationOverrides ??= {};
|
|
37552
|
+
for (const [chainId, overrides] of Object.entries(tasksDurationOverridesFromEnv(source))) {
|
|
37553
|
+
config2.tasksEscrowDurationOverrides[chainId] = {
|
|
37554
|
+
...config2.tasksEscrowDurationOverrides[chainId],
|
|
37555
|
+
...overrides
|
|
37556
|
+
};
|
|
37557
|
+
}
|
|
37558
|
+
if (config2.networks[BASE_MAINNET_CAIP2] && baseRpcUrl) {
|
|
37559
|
+
config2.networks[BASE_MAINNET_CAIP2].rpcUrl = baseRpcUrl;
|
|
37560
|
+
}
|
|
37561
|
+
if (arcMainnetRpcUrl) {
|
|
37562
|
+
const existing = config2.networks[ARC_MAINNET_CAIP2];
|
|
37563
|
+
config2.networks[ARC_MAINNET_CAIP2] = {
|
|
37564
|
+
...existing ?? { usdc: NETWORKS[ARC_MAINNET_CAIP2].usdc },
|
|
37565
|
+
rpcUrl: arcMainnetRpcUrl
|
|
37566
|
+
};
|
|
37567
|
+
}
|
|
37568
|
+
if (arcTestnetRpcUrl) {
|
|
37569
|
+
const existing = config2.networks[ARC_TESTNET_CAIP2];
|
|
37570
|
+
config2.networks[ARC_TESTNET_CAIP2] = {
|
|
37571
|
+
...existing ?? { usdc: NETWORKS[ARC_TESTNET_CAIP2].usdc },
|
|
37572
|
+
rpcUrl: arcTestnetRpcUrl
|
|
37573
|
+
};
|
|
37574
|
+
}
|
|
37575
|
+
if (solanaRpcUrl) {
|
|
37576
|
+
config2.networks[SOLANA_MAINNET_CAIP2] = {
|
|
37577
|
+
rpcUrl: solanaRpcUrl,
|
|
37578
|
+
usdc: config2.networks[SOLANA_MAINNET_CAIP2]?.usdc ?? NETWORKS[SOLANA_MAINNET_CAIP2].usdc
|
|
37579
|
+
};
|
|
37580
|
+
}
|
|
37581
|
+
if (registryUrl) {
|
|
37582
|
+
const registry2 = registryEndpoints(registryUrl);
|
|
37583
|
+
config2.discoveryUrl = registry2.discoveryUrl;
|
|
37584
|
+
config2.marketplaceDiscoveryUrl = registry2.marketplaceDiscoveryUrl;
|
|
37585
|
+
}
|
|
37586
|
+
if (discoveryUrl) config2.discoveryUrl = discoveryUrl;
|
|
37587
|
+
if (marketplaceDiscoveryUrl) config2.marketplaceDiscoveryUrl = marketplaceDiscoveryUrl;
|
|
37588
|
+
for (const [network, configured] of Object.entries(config2.networks)) {
|
|
37589
|
+
configured.rpcUrl = configured.rpcUrl.trim();
|
|
37590
|
+
configured.usdc = configured.usdc.trim();
|
|
37591
|
+
if (isSolanaNetwork(network) && configured.usdc !== NETWORKS[SOLANA_MAINNET_CAIP2].usdc) {
|
|
37592
|
+
throw new Error(`Network ${network} must use canonical Solana mainnet USDC.`);
|
|
37593
|
+
}
|
|
37594
|
+
if (!configured.rpcUrl) delete config2.networks[network];
|
|
37595
|
+
}
|
|
37596
|
+
return config2;
|
|
37597
|
+
}
|
|
37598
|
+
function tasksDurationOverridesFromEnv(source) {
|
|
37599
|
+
const result = {};
|
|
37600
|
+
for (const [name, raw] of Object.entries(source)) {
|
|
37601
|
+
const match = /^VAPI_TASKS_(WORK_DURATION|REVIEW_WINDOW)_SECONDS_(\d+)$/.exec(name);
|
|
37602
|
+
const value = raw?.trim();
|
|
37603
|
+
if (!match || !value) continue;
|
|
37604
|
+
const seconds = Number(value);
|
|
37605
|
+
if (!/^\d+$/.test(value) || !Number.isSafeInteger(seconds) || seconds <= 0)
|
|
37606
|
+
throw new Error(`Invalid trusted Tasks duration ${name}.`);
|
|
37607
|
+
const entry = result[match[2]] ??= {};
|
|
37608
|
+
if (match[1] === "WORK_DURATION") entry.workDurationSeconds = seconds;
|
|
37609
|
+
else entry.reviewWindowSeconds = seconds;
|
|
37610
|
+
}
|
|
37611
|
+
return result;
|
|
37612
|
+
}
|
|
37613
|
+
function tasksFactoryOverridesFromEnv(source) {
|
|
37614
|
+
const overrides = {};
|
|
37615
|
+
for (const [name, raw] of Object.entries(source)) {
|
|
37616
|
+
const match = /^VAPI_TASKS_ESCROW_FACTORY_(\d+)$/.exec(name);
|
|
37617
|
+
const value = raw?.trim();
|
|
37618
|
+
if (!match || !value) continue;
|
|
37619
|
+
overrides[match[1]] = getAddress(value);
|
|
37620
|
+
}
|
|
37621
|
+
return overrides;
|
|
37622
|
+
}
|
|
37623
|
+
function registryEndpoints(baseUrl) {
|
|
37624
|
+
const base = new URL(baseUrl);
|
|
37625
|
+
base.search = "";
|
|
37626
|
+
base.hash = "";
|
|
37627
|
+
const prefix = base.pathname === "/" ? "" : base.pathname.replace(/\/+$/, "");
|
|
37628
|
+
const discovery = new URL(base);
|
|
37629
|
+
discovery.pathname = `${prefix}/api/call/services`;
|
|
37630
|
+
const marketplace = new URL(base);
|
|
37631
|
+
marketplace.pathname = `${prefix}/api/call/discovery`;
|
|
37632
|
+
return { discoveryUrl: discovery.href, marketplaceDiscoveryUrl: marketplace.href };
|
|
37633
|
+
}
|
|
37634
|
+
async function pathExists(path) {
|
|
37635
|
+
try {
|
|
37636
|
+
await stat2(path);
|
|
37637
|
+
return true;
|
|
37638
|
+
} catch (error61) {
|
|
37639
|
+
if (isMissingFile(error61)) return false;
|
|
37640
|
+
throw error61;
|
|
37641
|
+
}
|
|
37642
|
+
}
|
|
37125
37643
|
function isMissingFile(error61) {
|
|
37126
37644
|
return error61 instanceof Error && "code" in error61 && error61.code === "ENOENT";
|
|
37127
37645
|
}
|
|
37646
|
+
function isAlreadyExists(error61) {
|
|
37647
|
+
return error61 instanceof Error && "code" in error61 && error61.code === "EEXIST";
|
|
37648
|
+
}
|
|
37128
37649
|
|
|
37129
37650
|
// src/audit.ts
|
|
37130
37651
|
var AUDIT_FILE = "audit.log";
|
|
37131
37652
|
function auditLogPath(home) {
|
|
37132
|
-
return
|
|
37653
|
+
return join3(home, AUDIT_FILE);
|
|
37133
37654
|
}
|
|
37134
37655
|
async function appendAudit(home, record2, options = {}) {
|
|
37135
37656
|
const entry = {
|
|
37136
37657
|
time: (options.now?.() ?? /* @__PURE__ */ new Date()).toISOString(),
|
|
37137
37658
|
event: record2.event,
|
|
37659
|
+
...record2.run === void 0 ? {} : { run: record2.run },
|
|
37138
37660
|
...record2.wallet === void 0 ? {} : { wallet: record2.wallet },
|
|
37139
37661
|
...record2.owner === void 0 ? {} : { owner: record2.owner },
|
|
37140
37662
|
tty: record2.tty,
|
|
@@ -37142,15 +37664,19 @@ async function appendAudit(home, record2, options = {}) {
|
|
|
37142
37664
|
...record2.detail === void 0 ? {} : { detail: record2.detail }
|
|
37143
37665
|
};
|
|
37144
37666
|
const path = auditLogPath(home);
|
|
37145
|
-
await
|
|
37667
|
+
await mkdir3(home, { recursive: true, mode: 448 });
|
|
37146
37668
|
await appendFile(path, `${JSON.stringify(entry)}
|
|
37147
37669
|
`, { encoding: "utf8", mode: 384 });
|
|
37148
37670
|
await chmod(path, 384);
|
|
37149
37671
|
return entry;
|
|
37150
37672
|
}
|
|
37151
37673
|
|
|
37674
|
+
// src/device.ts
|
|
37675
|
+
var DEVICE_NAME_PATTERN = /^[a-z0-9][a-z0-9-]{0,31}$/;
|
|
37676
|
+
|
|
37152
37677
|
// src/agent-link.ts
|
|
37153
37678
|
var AGENT_LINK_REVOKED_MESSAGE = "This agent's link was revoked or has expired. Run vapi login to link it again.";
|
|
37679
|
+
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.";
|
|
37154
37680
|
var AgentLinkError = class extends Error {
|
|
37155
37681
|
constructor(code, message) {
|
|
37156
37682
|
super(message);
|
|
@@ -37160,7 +37686,12 @@ var AgentLinkError = class extends Error {
|
|
|
37160
37686
|
code;
|
|
37161
37687
|
};
|
|
37162
37688
|
var DEFAULT_AGENT_SCOPES = ["mcp:call", "router.use"];
|
|
37163
|
-
var ALLOWED_AGENT_SCOPES = /* @__PURE__ */ new Set([
|
|
37689
|
+
var ALLOWED_AGENT_SCOPES = /* @__PURE__ */ new Set([
|
|
37690
|
+
...DEFAULT_AGENT_SCOPES,
|
|
37691
|
+
"call.publish",
|
|
37692
|
+
"tasks:read",
|
|
37693
|
+
"tasks:write"
|
|
37694
|
+
]);
|
|
37164
37695
|
var publicFetch = createPublicFetch({ allowPrivateNetwork: false });
|
|
37165
37696
|
var agentSecretAccounts = (wallet) => ({
|
|
37166
37697
|
tokens: `vapi.agent.${wallet}.tokens`,
|
|
@@ -37184,8 +37715,8 @@ async function agentFetch(args, url2, init) {
|
|
|
37184
37715
|
}
|
|
37185
37716
|
async function resolveAgentAccessToken(args, options) {
|
|
37186
37717
|
await args.wallets.reload();
|
|
37187
|
-
const
|
|
37188
|
-
if (
|
|
37718
|
+
const link2 = args.wallets.entry(args.wallet)?.link;
|
|
37719
|
+
if (link2 === void 0 || !args.secrets.available) throw notLinkedError();
|
|
37189
37720
|
const accounts = agentSecretAccounts(args.wallet);
|
|
37190
37721
|
const tokens = parseTokens(await safeGetSecret(args.secrets, accounts.tokens));
|
|
37191
37722
|
if (tokens === void 0) throw notLinkedError();
|
|
@@ -37193,7 +37724,7 @@ async function resolveAgentAccessToken(args, options) {
|
|
|
37193
37724
|
if (options.rejectedAccessToken === void 0 && tokens.expiresAt - 6e4 > now()) {
|
|
37194
37725
|
return tokens.accessToken;
|
|
37195
37726
|
}
|
|
37196
|
-
return await withAgentCredentialLock(args.wallets,
|
|
37727
|
+
return await withAgentCredentialLock(args.wallets, link2.clientId, async () => {
|
|
37197
37728
|
await args.wallets.reload();
|
|
37198
37729
|
const currentLink = args.wallets.entry(args.wallet)?.link;
|
|
37199
37730
|
if (currentLink === void 0) throw notLinkedError();
|
|
@@ -37206,6 +37737,9 @@ async function resolveAgentAccessToken(args, options) {
|
|
|
37206
37737
|
if (options.rejectedAccessToken === void 0 && current.expiresAt - 6e4 > now()) {
|
|
37207
37738
|
return current.accessToken;
|
|
37208
37739
|
}
|
|
37740
|
+
if (current.refreshToken === void 0) {
|
|
37741
|
+
throw new AgentLinkError("not_linked", REMOTE_AGENT_TOKEN_EXPIRED_MESSAGE);
|
|
37742
|
+
}
|
|
37209
37743
|
const response = await safeFetch(
|
|
37210
37744
|
args.fetchImpl ?? publicFetch,
|
|
37211
37745
|
apiEndpoint(currentLink.apiBase, "/oauth/token"),
|
|
@@ -37239,12 +37773,12 @@ async function resolveAgentAccessToken(args, options) {
|
|
|
37239
37773
|
async function withAgentCredentialLock(wallets, clientId, operation) {
|
|
37240
37774
|
const digest = createHash("sha256").update(clientId).digest("hex").slice(0, 24);
|
|
37241
37775
|
const lockPath = `${wallets.registryPath}.${digest}.agent-link.lock`;
|
|
37242
|
-
await
|
|
37776
|
+
await mkdir4(dirname3(lockPath), { recursive: true, mode: 448 });
|
|
37243
37777
|
const deadline = Date.now() + 3e4;
|
|
37244
37778
|
let handle;
|
|
37245
37779
|
while (!handle) {
|
|
37246
37780
|
try {
|
|
37247
|
-
handle = await
|
|
37781
|
+
handle = await open3(lockPath, "wx", 384);
|
|
37248
37782
|
} catch (error61) {
|
|
37249
37783
|
if (!(error61 instanceof Error && "code" in error61 && error61.code === "EEXIST")) throw error61;
|
|
37250
37784
|
await removeStaleCredentialLock(lockPath);
|
|
@@ -37258,13 +37792,13 @@ async function withAgentCredentialLock(wallets, clientId, operation) {
|
|
|
37258
37792
|
return await operation();
|
|
37259
37793
|
} finally {
|
|
37260
37794
|
await handle.close();
|
|
37261
|
-
await
|
|
37795
|
+
await unlink2(lockPath).catch(() => void 0);
|
|
37262
37796
|
}
|
|
37263
37797
|
}
|
|
37264
37798
|
async function removeStaleCredentialLock(path) {
|
|
37265
37799
|
try {
|
|
37266
|
-
const metadata = await
|
|
37267
|
-
if (Date.now() - metadata.mtimeMs > 5 * 6e4) await
|
|
37800
|
+
const metadata = await stat3(path);
|
|
37801
|
+
if (Date.now() - metadata.mtimeMs > 5 * 6e4) await unlink2(path);
|
|
37268
37802
|
} catch {
|
|
37269
37803
|
}
|
|
37270
37804
|
}
|
|
@@ -37293,12 +37827,18 @@ function parseTokens(value) {
|
|
|
37293
37827
|
if (!isRecord(parsed)) return void 0;
|
|
37294
37828
|
const accessToken = stringField(parsed, "accessToken");
|
|
37295
37829
|
const refreshToken = stringField(parsed, "refreshToken");
|
|
37830
|
+
const refreshable = parsed.refreshable;
|
|
37296
37831
|
const expiresAt = numberField(parsed, "expiresAt");
|
|
37297
37832
|
const scopes = parsed.scopes;
|
|
37298
|
-
if (accessToken === void 0 ||
|
|
37833
|
+
if (accessToken === void 0 || expiresAt === void 0 || !Array.isArray(scopes) || scopes.some((scope) => typeof scope !== "string") || (refreshToken === void 0 ? refreshable !== false : refreshable === false)) {
|
|
37299
37834
|
return void 0;
|
|
37300
37835
|
}
|
|
37301
|
-
return {
|
|
37836
|
+
return {
|
|
37837
|
+
accessToken,
|
|
37838
|
+
...refreshToken === void 0 ? {} : { refreshToken },
|
|
37839
|
+
expiresAt,
|
|
37840
|
+
scopes
|
|
37841
|
+
};
|
|
37302
37842
|
}
|
|
37303
37843
|
function withBearer(init, accessToken) {
|
|
37304
37844
|
const headers = new Headers(init?.headers);
|
|
@@ -37364,8 +37904,92 @@ function splitScopes(scope) {
|
|
|
37364
37904
|
}
|
|
37365
37905
|
|
|
37366
37906
|
// src/ceiling.ts
|
|
37367
|
-
import { randomBytes as
|
|
37368
|
-
|
|
37907
|
+
import { randomBytes as randomBytes7 } from "node:crypto";
|
|
37908
|
+
|
|
37909
|
+
// src/account-movement-lock.ts
|
|
37910
|
+
import { randomBytes as randomBytes3 } from "node:crypto";
|
|
37911
|
+
import { mkdir as mkdir5, open as open4, readFile as readFile4, stat as stat4, unlink as unlink3 } from "node:fs/promises";
|
|
37912
|
+
import { join as join4 } from "node:path";
|
|
37913
|
+
var AccountMovementLockedError = class extends Error {
|
|
37914
|
+
constructor(account) {
|
|
37915
|
+
super(`Another capital movement is already running for account ${account}.`);
|
|
37916
|
+
this.account = account;
|
|
37917
|
+
this.name = "AccountMovementLockedError";
|
|
37918
|
+
}
|
|
37919
|
+
account;
|
|
37920
|
+
code = "account_movement_locked";
|
|
37921
|
+
};
|
|
37922
|
+
async function withAccountMovementLock(home, account, timeoutMs, operation, signal) {
|
|
37923
|
+
const directory = join4(home, "movements");
|
|
37924
|
+
await mkdir5(directory, { recursive: true, mode: 448 });
|
|
37925
|
+
const path = join4(directory, `.${account}.lock`);
|
|
37926
|
+
const deadline = Date.now() + (timeoutMs ?? 2e3);
|
|
37927
|
+
const owner = JSON.stringify({ pid: process.pid, token: randomBytes3(16).toString("hex") });
|
|
37928
|
+
let handle;
|
|
37929
|
+
while (handle === void 0) {
|
|
37930
|
+
signal?.throwIfAborted();
|
|
37931
|
+
try {
|
|
37932
|
+
handle = await open4(path, "wx", 384);
|
|
37933
|
+
await handle.writeFile(owner, "utf8");
|
|
37934
|
+
await handle.sync();
|
|
37935
|
+
} catch (error61) {
|
|
37936
|
+
if (!(error61 instanceof Error && "code" in error61 && error61.code === "EEXIST")) throw error61;
|
|
37937
|
+
await removeStaleLock2(path);
|
|
37938
|
+
if (Date.now() >= deadline) throw new AccountMovementLockedError(account);
|
|
37939
|
+
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
37940
|
+
}
|
|
37941
|
+
}
|
|
37942
|
+
const ownedHandle = handle;
|
|
37943
|
+
const heartbeat = setInterval(() => {
|
|
37944
|
+
const time3 = /* @__PURE__ */ new Date();
|
|
37945
|
+
void ownedHandle.utimes(time3, time3).catch(() => void 0);
|
|
37946
|
+
}, 3e4);
|
|
37947
|
+
heartbeat.unref();
|
|
37948
|
+
try {
|
|
37949
|
+
return await operation();
|
|
37950
|
+
} finally {
|
|
37951
|
+
clearInterval(heartbeat);
|
|
37952
|
+
await ownedHandle.close();
|
|
37953
|
+
await unlinkOwnedLock(path, owner);
|
|
37954
|
+
}
|
|
37955
|
+
}
|
|
37956
|
+
async function removeStaleLock2(path) {
|
|
37957
|
+
try {
|
|
37958
|
+
const metadata = await stat4(path);
|
|
37959
|
+
if (Date.now() - metadata.mtimeMs <= 12e4) return;
|
|
37960
|
+
const owner = await readFile4(path, "utf8").catch(() => "");
|
|
37961
|
+
const pid = lockOwnerPid2(owner);
|
|
37962
|
+
if (pid !== void 0 && processIsAlive2(pid)) return;
|
|
37963
|
+
await unlinkOwnedLock(path, owner);
|
|
37964
|
+
} catch (error61) {
|
|
37965
|
+
if (!isMissingFile(error61)) throw error61;
|
|
37966
|
+
}
|
|
37967
|
+
}
|
|
37968
|
+
async function unlinkOwnedLock(path, owner) {
|
|
37969
|
+
try {
|
|
37970
|
+
if (await readFile4(path, "utf8") === owner) await unlink3(path);
|
|
37971
|
+
} catch (error61) {
|
|
37972
|
+
if (!isMissingFile(error61)) throw error61;
|
|
37973
|
+
}
|
|
37974
|
+
}
|
|
37975
|
+
function lockOwnerPid2(owner) {
|
|
37976
|
+
try {
|
|
37977
|
+
const value = JSON.parse(owner);
|
|
37978
|
+
if (typeof value !== "object" || value === null) return void 0;
|
|
37979
|
+
const pid = Reflect.get(value, "pid");
|
|
37980
|
+
return typeof pid === "number" && Number.isSafeInteger(pid) && pid > 0 ? pid : void 0;
|
|
37981
|
+
} catch {
|
|
37982
|
+
return void 0;
|
|
37983
|
+
}
|
|
37984
|
+
}
|
|
37985
|
+
function processIsAlive2(pid) {
|
|
37986
|
+
try {
|
|
37987
|
+
process.kill(pid, 0);
|
|
37988
|
+
return true;
|
|
37989
|
+
} catch (error61) {
|
|
37990
|
+
return error61 instanceof Error && "code" in error61 && error61.code === "EPERM";
|
|
37991
|
+
}
|
|
37992
|
+
}
|
|
37369
37993
|
|
|
37370
37994
|
// src/authorization-state.ts
|
|
37371
37995
|
var EIP3009_AUTHORIZATION_STATE_ABI = [
|
|
@@ -37429,9 +38053,47 @@ function missingAuthorization(receipt) {
|
|
|
37429
38053
|
return `Receipt ${receipt.id} records no signed payment authorization, so nothing from that call can settle.`;
|
|
37430
38054
|
}
|
|
37431
38055
|
|
|
37432
|
-
// src/
|
|
37433
|
-
import {
|
|
37434
|
-
import {
|
|
38056
|
+
// src/movement.ts
|
|
38057
|
+
import { randomBytes as randomBytes6 } from "node:crypto";
|
|
38058
|
+
import { chmod as chmod3, mkdir as mkdir10, open as open9, readFile as readFile9, readdir as readdir3, rename as rename6, unlink as unlink7 } from "node:fs/promises";
|
|
38059
|
+
import { join as join5 } from "node:path";
|
|
38060
|
+
|
|
38061
|
+
// src/allocation.ts
|
|
38062
|
+
var AllocationError = class extends Error {
|
|
38063
|
+
constructor(code, message) {
|
|
38064
|
+
super(message);
|
|
38065
|
+
this.code = code;
|
|
38066
|
+
this.name = "AllocationError";
|
|
38067
|
+
}
|
|
38068
|
+
code;
|
|
38069
|
+
};
|
|
38070
|
+
function parseUsdCents(value) {
|
|
38071
|
+
const raw = String(value);
|
|
38072
|
+
if (!/^\d+(?:\.\d{1,2})?$/.test(raw)) {
|
|
38073
|
+
throw new AllocationError("invalid_amount", "Enter a positive USDC amount in whole cents.");
|
|
38074
|
+
}
|
|
38075
|
+
const [whole = "0", fraction = ""] = raw.split(".");
|
|
38076
|
+
const cents = BigInt(whole) * 100n + BigInt(fraction.padEnd(2, "0") || "0");
|
|
38077
|
+
if (cents <= 0n) {
|
|
38078
|
+
throw new AllocationError("invalid_amount", "Enter a positive USDC amount in whole cents.");
|
|
38079
|
+
}
|
|
38080
|
+
return cents;
|
|
38081
|
+
}
|
|
38082
|
+
|
|
38083
|
+
// src/movement-open.ts
|
|
38084
|
+
function isOpenMovementLeg(leg, journalEvidence) {
|
|
38085
|
+
if (leg.status === "sent" || leg.status === "cancelled") return false;
|
|
38086
|
+
if (leg.restored === true) return true;
|
|
38087
|
+
if (leg.status === "planned" || leg.status === "unknown") return true;
|
|
38088
|
+
if (leg.status !== "failed") return false;
|
|
38089
|
+
if (leg.retryable === true) return true;
|
|
38090
|
+
return journalEvidence === "signed";
|
|
38091
|
+
}
|
|
38092
|
+
|
|
38093
|
+
// src/spend-policy.ts
|
|
38094
|
+
import { randomBytes as randomBytes4 } from "node:crypto";
|
|
38095
|
+
import { mkdir as mkdir6, open as open5, readFile as readFile5, rename as rename3, stat as stat5, unlink as unlink4, writeFile as writeFile2 } from "node:fs/promises";
|
|
38096
|
+
import { dirname as dirname4 } from "node:path";
|
|
37435
38097
|
|
|
37436
38098
|
// src/wallet-name.ts
|
|
37437
38099
|
var DEFAULT_WALLET_NAME = "main";
|
|
@@ -37441,11 +38103,363 @@ var walletNameSchema = external_exports.string().regex(
|
|
|
37441
38103
|
"A wallet name is 1 to 32 characters of lowercase letters, digits and dashes, starting with a letter or digit."
|
|
37442
38104
|
);
|
|
37443
38105
|
|
|
38106
|
+
// src/spend-policy.ts
|
|
38107
|
+
var ledgerSchema = external_exports.object({
|
|
38108
|
+
date: external_exports.string().regex(/^\d{4}-\d{2}-\d{2}$/),
|
|
38109
|
+
spentAtomic: external_exports.string().regex(/^\d+$/)
|
|
38110
|
+
});
|
|
38111
|
+
var spendReservationSchema = external_exports.object({
|
|
38112
|
+
id: external_exports.string().min(1),
|
|
38113
|
+
amountAtomic: external_exports.string().regex(/^\d+$/),
|
|
38114
|
+
kind: external_exports.enum(["payment", "escrow-funding", "dispute-fee"]).optional(),
|
|
38115
|
+
taskKey: external_exports.string().optional()
|
|
38116
|
+
});
|
|
38117
|
+
var ledgerRowSchema = ledgerSchema.extend({
|
|
38118
|
+
wallet: walletNameSchema.optional(),
|
|
38119
|
+
reservations: external_exports.array(spendReservationSchema).optional()
|
|
38120
|
+
});
|
|
38121
|
+
var ledgerFileSchema = external_exports.union([
|
|
38122
|
+
external_exports.object({ version: external_exports.literal(1), rows: external_exports.array(ledgerRowSchema) }),
|
|
38123
|
+
ledgerRowSchema
|
|
38124
|
+
]);
|
|
38125
|
+
var SpendCapError = class extends Error {
|
|
38126
|
+
constructor(code, message) {
|
|
38127
|
+
super(message);
|
|
38128
|
+
this.code = code;
|
|
38129
|
+
this.name = "SpendCapError";
|
|
38130
|
+
}
|
|
38131
|
+
code;
|
|
38132
|
+
};
|
|
38133
|
+
function utcDateKey(now = /* @__PURE__ */ new Date()) {
|
|
38134
|
+
return now.toISOString().slice(0, 10);
|
|
38135
|
+
}
|
|
38136
|
+
function sumTaskReservations(rows, wallet, taskKey, excludeReservationId) {
|
|
38137
|
+
let total = 0n;
|
|
38138
|
+
for (const row of rows) {
|
|
38139
|
+
if (row.wallet !== wallet) continue;
|
|
38140
|
+
for (const reservation of row.reservations ?? []) {
|
|
38141
|
+
if (reservation.taskKey === taskKey && reservation.id !== excludeReservationId)
|
|
38142
|
+
total += BigInt(reservation.amountAtomic);
|
|
38143
|
+
}
|
|
38144
|
+
}
|
|
38145
|
+
return total;
|
|
38146
|
+
}
|
|
38147
|
+
async function readStoredSpendLedgerRows(path, now, allDays = false) {
|
|
38148
|
+
let raw;
|
|
38149
|
+
try {
|
|
38150
|
+
raw = await readFile5(path, "utf8");
|
|
38151
|
+
} catch (error61) {
|
|
38152
|
+
if (isMissingFile(error61)) return [];
|
|
38153
|
+
throw error61;
|
|
38154
|
+
}
|
|
38155
|
+
const parsed = ledgerFileSchema.parse(JSON.parse(raw));
|
|
38156
|
+
const rows = "rows" in parsed ? parsed.rows : [parsed];
|
|
38157
|
+
const today = utcDateKey(now);
|
|
38158
|
+
return rows.filter((row) => allDays || row.date === today).map((row) => ({
|
|
38159
|
+
date: row.date,
|
|
38160
|
+
spentAtomic: row.spentAtomic,
|
|
38161
|
+
wallet: row.wallet ?? DEFAULT_WALLET_NAME,
|
|
38162
|
+
...row.reservations === void 0 ? {} : { reservations: row.reservations }
|
|
38163
|
+
}));
|
|
38164
|
+
}
|
|
38165
|
+
async function reserveSpend(amountAtomic, caps, options) {
|
|
38166
|
+
if (amountAtomic < 0n) {
|
|
38167
|
+
throw new Error("Spend amount cannot be negative.");
|
|
38168
|
+
}
|
|
38169
|
+
const kind = options?.kind ?? "payment";
|
|
38170
|
+
let perDayAtomic;
|
|
38171
|
+
if (kind === "escrow-funding" || kind === "dispute-fee") {
|
|
38172
|
+
const maxPerTaskAtomic = options?.maxPerTaskAtomic;
|
|
38173
|
+
if (maxPerTaskAtomic === void 0 || maxPerTaskAtomic < 0n) {
|
|
38174
|
+
throw new Error("Escrow funding requires a non-negative per-task cap.");
|
|
38175
|
+
}
|
|
38176
|
+
if (amountAtomic > maxPerTaskAtomic) {
|
|
38177
|
+
throw new SpendCapError(
|
|
38178
|
+
"per_task_cap_exceeded",
|
|
38179
|
+
`Escrow funding ${amountAtomic} atomic USDC exceeds the per-task cap ${maxPerTaskAtomic}. Refusing to sign.`
|
|
38180
|
+
);
|
|
38181
|
+
}
|
|
38182
|
+
perDayAtomic = parseAtomicCap(caps.perDayAtomic, "per-day");
|
|
38183
|
+
} else {
|
|
38184
|
+
const perCallAtomic = parseAtomicCap(caps.perCallAtomic, "per-call");
|
|
38185
|
+
perDayAtomic = parseAtomicCap(caps.perDayAtomic, "per-day");
|
|
38186
|
+
if (amountAtomic > perCallAtomic) {
|
|
38187
|
+
throw new SpendCapError(
|
|
38188
|
+
"per_call_cap_exceeded",
|
|
38189
|
+
`Payment quote ${amountAtomic} atomic USDC exceeds the per-call cap ${perCallAtomic}. Refusing to sign.`
|
|
38190
|
+
);
|
|
38191
|
+
}
|
|
38192
|
+
}
|
|
38193
|
+
const ledgerPath = options?.ledgerPath ?? getAgentCashPaths().ledger;
|
|
38194
|
+
const now = options?.now ?? /* @__PURE__ */ new Date();
|
|
38195
|
+
const wallet = options?.wallet ?? DEFAULT_WALLET_NAME;
|
|
38196
|
+
return await withLedgerLock(ledgerPath, async () => {
|
|
38197
|
+
const rows = await readStoredSpendLedgerRows(ledgerPath, now, true);
|
|
38198
|
+
const today = utcDateKey(now);
|
|
38199
|
+
const current = rows.find((row) => row.wallet === wallet && row.date === today);
|
|
38200
|
+
if ((kind === "escrow-funding" || kind === "dispute-fee") && options?.taskKey !== void 0) {
|
|
38201
|
+
const taskTotal = sumTaskReservations(
|
|
38202
|
+
rows,
|
|
38203
|
+
wallet,
|
|
38204
|
+
options.taskKey,
|
|
38205
|
+
options.reuseExistingEscrowReservation || options.resumeEscrowReservation ? options.reservationId : void 0
|
|
38206
|
+
) + amountAtomic;
|
|
38207
|
+
if (taskTotal > options.maxPerTaskAtomic)
|
|
38208
|
+
throw new SpendCapError(
|
|
38209
|
+
"per_task_cap_exceeded",
|
|
38210
|
+
`New spend ${amountAtomic} atomic USDC would raise the running total for task ${options.taskKey} and wallet ${wallet} to ${taskTotal}, above the per-task cap ${options.maxPerTaskAtomic}. Refusing to sign.`
|
|
38211
|
+
);
|
|
38212
|
+
}
|
|
38213
|
+
if (options?.reservationId !== void 0) {
|
|
38214
|
+
const existing = current?.reservations?.find(
|
|
38215
|
+
(reservation) => reservation.id === options.reservationId
|
|
38216
|
+
);
|
|
38217
|
+
if (existing && options.reuseExistingEscrowReservation) {
|
|
38218
|
+
if (kind !== "escrow-funding" && kind !== "dispute-fee" || existing.kind !== kind || existing.amountAtomic !== amountAtomic.toString() || existing.taskKey !== void 0 && existing.taskKey !== options.taskKey)
|
|
38219
|
+
throw new Error(
|
|
38220
|
+
`Spend reservation ${options.reservationId} does not match this funding.`
|
|
38221
|
+
);
|
|
38222
|
+
if (existing.taskKey === void 0 && options.taskKey !== void 0) {
|
|
38223
|
+
existing.taskKey = options.taskKey;
|
|
38224
|
+
await writeLedgerAtomically(ledgerPath, rows);
|
|
38225
|
+
}
|
|
38226
|
+
return {
|
|
38227
|
+
date: current.date,
|
|
38228
|
+
spentAtomic: current.spentAtomic,
|
|
38229
|
+
reservationReused: true
|
|
38230
|
+
};
|
|
38231
|
+
}
|
|
38232
|
+
if (existing) {
|
|
38233
|
+
throw new Error(`Spend reservation ${options.reservationId} already exists.`);
|
|
38234
|
+
}
|
|
38235
|
+
if (options.resumeEscrowReservation) {
|
|
38236
|
+
const resumed = options.resumeEscrowReservation;
|
|
38237
|
+
const historical = rows.find((row) => row.wallet === wallet && row.date === resumed.date);
|
|
38238
|
+
const stored = historical?.reservations?.find(
|
|
38239
|
+
(reservation) => reservation.id === resumed.id
|
|
38240
|
+
);
|
|
38241
|
+
if (kind !== "escrow-funding" && kind !== "dispute-fee" || resumed.id !== options.reservationId || resumed.wallet !== wallet || resumed.amountAtomic !== amountAtomic.toString() || resumed.date >= utcDateKey(now) || resumed.exposed !== true || resumed.invalidated === true || stored !== void 0 && (stored.kind !== kind || stored.amountAtomic !== amountAtomic.toString() || stored.taskKey !== void 0 && stored.taskKey !== options.taskKey))
|
|
38242
|
+
throw new Error(
|
|
38243
|
+
`Spend reservation ${options.reservationId} does not match this funding.`
|
|
38244
|
+
);
|
|
38245
|
+
if (stored?.taskKey === void 0 && options.taskKey !== void 0) {
|
|
38246
|
+
if (stored) stored.taskKey = options.taskKey;
|
|
38247
|
+
else {
|
|
38248
|
+
const reservation = {
|
|
38249
|
+
id: resumed.id,
|
|
38250
|
+
amountAtomic: resumed.amountAtomic,
|
|
38251
|
+
kind,
|
|
38252
|
+
taskKey: options.taskKey
|
|
38253
|
+
};
|
|
38254
|
+
if (historical)
|
|
38255
|
+
historical.reservations = [...historical.reservations ?? [], reservation];
|
|
38256
|
+
else
|
|
38257
|
+
rows.push({
|
|
38258
|
+
date: resumed.date,
|
|
38259
|
+
spentAtomic: resumed.amountAtomic,
|
|
38260
|
+
wallet,
|
|
38261
|
+
reservations: [reservation]
|
|
38262
|
+
});
|
|
38263
|
+
}
|
|
38264
|
+
await writeLedgerAtomically(ledgerPath, rows);
|
|
38265
|
+
}
|
|
38266
|
+
return {
|
|
38267
|
+
date: resumed.date,
|
|
38268
|
+
spentAtomic: current?.spentAtomic ?? "0",
|
|
38269
|
+
reservationReused: true
|
|
38270
|
+
};
|
|
38271
|
+
}
|
|
38272
|
+
}
|
|
38273
|
+
const spentAtomic = current?.spentAtomic ?? "0";
|
|
38274
|
+
const nextSpent = BigInt(spentAtomic) + amountAtomic;
|
|
38275
|
+
if (nextSpent > perDayAtomic) {
|
|
38276
|
+
throw new SpendCapError(
|
|
38277
|
+
"per_day_cap_exceeded",
|
|
38278
|
+
`${kind === "escrow-funding" ? "Escrow funding" : "Payment quote"} ${amountAtomic} atomic USDC would raise today's spend to ${nextSpent} for wallet ${wallet}, above the per-day cap ${perDayAtomic}. Refusing to sign.`
|
|
38279
|
+
);
|
|
38280
|
+
}
|
|
38281
|
+
const reservationId = options?.reservationId ?? (options?.taskKey === void 0 ? void 0 : randomBytes4(16).toString("hex"));
|
|
38282
|
+
const nextRow = {
|
|
38283
|
+
date: utcDateKey(now),
|
|
38284
|
+
spentAtomic: nextSpent.toString(),
|
|
38285
|
+
wallet,
|
|
38286
|
+
...(current?.reservations?.length ?? 0) === 0 && reservationId === void 0 ? {} : {
|
|
38287
|
+
reservations: [
|
|
38288
|
+
...current?.reservations ?? [],
|
|
38289
|
+
...reservationId === void 0 ? [] : [
|
|
38290
|
+
{
|
|
38291
|
+
id: reservationId,
|
|
38292
|
+
amountAtomic: amountAtomic.toString(),
|
|
38293
|
+
...kind !== "payment" ? { kind } : {},
|
|
38294
|
+
...options?.taskKey === void 0 ? {} : { taskKey: options.taskKey }
|
|
38295
|
+
}
|
|
38296
|
+
]
|
|
38297
|
+
]
|
|
38298
|
+
}
|
|
38299
|
+
};
|
|
38300
|
+
const otherRows = rows.filter((row) => row.wallet !== wallet || row.date !== today).map((row) => {
|
|
38301
|
+
if (row.wallet !== wallet || !options?.reuseExistingEscrowReservation || options.reservationId === void 0)
|
|
38302
|
+
return row;
|
|
38303
|
+
const previous = row.reservations?.find(
|
|
38304
|
+
(reservation) => reservation.id === options.reservationId
|
|
38305
|
+
);
|
|
38306
|
+
if (!previous) return row;
|
|
38307
|
+
if (kind !== "escrow-funding" && kind !== "dispute-fee" || previous.kind !== kind || previous.amountAtomic !== amountAtomic.toString() || previous.taskKey !== void 0 && previous.taskKey !== options.taskKey)
|
|
38308
|
+
throw new Error(
|
|
38309
|
+
`Spend reservation ${options.reservationId} does not match this funding.`
|
|
38310
|
+
);
|
|
38311
|
+
return {
|
|
38312
|
+
...row,
|
|
38313
|
+
reservations: row.reservations.filter(
|
|
38314
|
+
(reservation) => reservation.id !== options.reservationId
|
|
38315
|
+
)
|
|
38316
|
+
};
|
|
38317
|
+
});
|
|
38318
|
+
const nextRows = [...otherRows, nextRow].sort(
|
|
38319
|
+
(a, b) => a.wallet.localeCompare(b.wallet) || a.date.localeCompare(b.date)
|
|
38320
|
+
);
|
|
38321
|
+
await writeLedgerAtomically(ledgerPath, nextRows);
|
|
38322
|
+
return {
|
|
38323
|
+
date: nextRow.date,
|
|
38324
|
+
spentAtomic: nextRow.spentAtomic,
|
|
38325
|
+
...options?.reservationId === void 0 && reservationId !== void 0 ? { reservationId } : {}
|
|
38326
|
+
};
|
|
38327
|
+
});
|
|
38328
|
+
}
|
|
38329
|
+
async function releaseSpend(amountAtomic, options) {
|
|
38330
|
+
if (amountAtomic < 0n) {
|
|
38331
|
+
throw new Error("Spend amount cannot be negative.");
|
|
38332
|
+
}
|
|
38333
|
+
const ledgerPath = options.ledgerPath ?? getAgentCashPaths().ledger;
|
|
38334
|
+
const now = options.now ?? /* @__PURE__ */ new Date();
|
|
38335
|
+
const wallet = options.wallet ?? DEFAULT_WALLET_NAME;
|
|
38336
|
+
return await withLedgerLock(ledgerPath, async () => {
|
|
38337
|
+
const today = utcDateKey(now);
|
|
38338
|
+
const rows = await readStoredSpendLedgerRows(ledgerPath, now, true);
|
|
38339
|
+
const current = rows.find((row) => row.wallet === wallet && row.date === today);
|
|
38340
|
+
if (options.reservedOn !== today && options.reservationId === void 0) {
|
|
38341
|
+
return { date: today, spentAtomic: current?.spentAtomic ?? "0" };
|
|
38342
|
+
}
|
|
38343
|
+
const reserved = rows.find((row) => row.wallet === wallet && row.date === options.reservedOn);
|
|
38344
|
+
const reservation = options.reservationId === void 0 ? void 0 : reserved?.reservations?.find((candidate) => candidate.id === options.reservationId);
|
|
38345
|
+
if (options.reservationId !== void 0 && reservation === void 0) {
|
|
38346
|
+
return { date: today, spentAtomic: current?.spentAtomic ?? "0" };
|
|
38347
|
+
}
|
|
38348
|
+
if (reservation !== void 0 && reservation.amountAtomic !== amountAtomic.toString()) {
|
|
38349
|
+
throw new Error(`Spend reservation ${options.reservationId} has a different amount.`);
|
|
38350
|
+
}
|
|
38351
|
+
const spentAtomic = BigInt(reserved?.spentAtomic ?? "0");
|
|
38352
|
+
const releasedAmount = reservation === void 0 ? amountAtomic : BigInt(reservation.amountAtomic);
|
|
38353
|
+
const nextSpent = spentAtomic > releasedAmount ? spentAtomic - releasedAmount : 0n;
|
|
38354
|
+
const remainingReservations = reserved?.reservations?.filter(
|
|
38355
|
+
(candidate) => candidate.id !== options.reservationId
|
|
38356
|
+
);
|
|
38357
|
+
const nextRow = {
|
|
38358
|
+
date: options.reservedOn,
|
|
38359
|
+
spentAtomic: nextSpent.toString(),
|
|
38360
|
+
wallet,
|
|
38361
|
+
...remainingReservations === void 0 || remainingReservations.length === 0 ? {} : { reservations: remainingReservations }
|
|
38362
|
+
};
|
|
38363
|
+
const nextRows = [
|
|
38364
|
+
...rows.filter((row) => row.wallet !== wallet || row.date !== options.reservedOn),
|
|
38365
|
+
nextRow
|
|
38366
|
+
].sort((a, b) => a.wallet.localeCompare(b.wallet) || a.date.localeCompare(b.date));
|
|
38367
|
+
await writeLedgerAtomically(ledgerPath, nextRows);
|
|
38368
|
+
return {
|
|
38369
|
+
date: today,
|
|
38370
|
+
spentAtomic: options.reservedOn === today ? nextRow.spentAtomic : current?.spentAtomic ?? "0"
|
|
38371
|
+
};
|
|
38372
|
+
});
|
|
38373
|
+
}
|
|
38374
|
+
function parseAtomicCap(value, label) {
|
|
38375
|
+
if (!/^\d+$/.test(value)) {
|
|
38376
|
+
throw new Error(`Invalid ${label} spend cap ${JSON.stringify(value)}.`);
|
|
38377
|
+
}
|
|
38378
|
+
return BigInt(value);
|
|
38379
|
+
}
|
|
38380
|
+
async function writeLedgerAtomically(path, rows) {
|
|
38381
|
+
const ledger = rows.length === 1 && rows[0].wallet === DEFAULT_WALLET_NAME ? {
|
|
38382
|
+
date: rows[0].date,
|
|
38383
|
+
spentAtomic: rows[0].spentAtomic,
|
|
38384
|
+
...rows[0].reservations === void 0 ? {} : { reservations: rows[0].reservations }
|
|
38385
|
+
} : { version: 1, rows };
|
|
38386
|
+
await mkdir6(dirname4(path), { recursive: true, mode: 448 });
|
|
38387
|
+
const temporaryPath = `${path}.${process.pid}.${randomBytes4(6).toString("hex")}.tmp`;
|
|
38388
|
+
await writeFile2(temporaryPath, `${JSON.stringify(ledger, null, 2)}
|
|
38389
|
+
`, {
|
|
38390
|
+
encoding: "utf8",
|
|
38391
|
+
mode: 384,
|
|
38392
|
+
flag: "wx"
|
|
38393
|
+
});
|
|
38394
|
+
await rename3(temporaryPath, path);
|
|
38395
|
+
}
|
|
38396
|
+
async function withLedgerLock(path, operation) {
|
|
38397
|
+
await mkdir6(dirname4(path), { recursive: true, mode: 448 });
|
|
38398
|
+
const lockPath = `${path}.lock`;
|
|
38399
|
+
const deadline = Date.now() + 2e3;
|
|
38400
|
+
let handle;
|
|
38401
|
+
while (!handle) {
|
|
38402
|
+
try {
|
|
38403
|
+
handle = await open5(lockPath, "wx", 384);
|
|
38404
|
+
} catch (error61) {
|
|
38405
|
+
if (!(error61 instanceof Error && "code" in error61 && error61.code === "EEXIST")) {
|
|
38406
|
+
throw error61;
|
|
38407
|
+
}
|
|
38408
|
+
await removeStaleLock3(lockPath);
|
|
38409
|
+
if (Date.now() >= deadline) {
|
|
38410
|
+
throw new Error("Timed out waiting for the spend ledger lock.");
|
|
38411
|
+
}
|
|
38412
|
+
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
38413
|
+
}
|
|
38414
|
+
}
|
|
38415
|
+
try {
|
|
38416
|
+
return await operation();
|
|
38417
|
+
} finally {
|
|
38418
|
+
await handle.close();
|
|
38419
|
+
await unlink4(lockPath).catch(() => void 0);
|
|
38420
|
+
}
|
|
38421
|
+
}
|
|
38422
|
+
async function removeStaleLock3(path) {
|
|
38423
|
+
try {
|
|
38424
|
+
const metadata = await stat5(path);
|
|
38425
|
+
if (Date.now() - metadata.mtimeMs > 3e4) {
|
|
38426
|
+
await unlink4(path);
|
|
38427
|
+
}
|
|
38428
|
+
} catch (error61) {
|
|
38429
|
+
if (!isMissingFile(error61)) {
|
|
38430
|
+
throw error61;
|
|
38431
|
+
}
|
|
38432
|
+
}
|
|
38433
|
+
}
|
|
38434
|
+
|
|
38435
|
+
// src/transfer-journal.ts
|
|
38436
|
+
import { readFile as readFile7 } from "node:fs/promises";
|
|
38437
|
+
|
|
38438
|
+
// src/receipts.ts
|
|
38439
|
+
import { mkdir as mkdir7, open as open6, readFile as readFile6, rename as rename4, writeFile as writeFile3 } from "node:fs/promises";
|
|
38440
|
+
import { dirname as dirname5 } from "node:path";
|
|
38441
|
+
|
|
38442
|
+
// src/agent-run.ts
|
|
38443
|
+
var RUN_ID_PATTERN = /^run_[0-9a-f]{24}$/;
|
|
38444
|
+
var runRefSchema = external_exports.strictObject({
|
|
38445
|
+
id: external_exports.string().regex(RUN_ID_PATTERN),
|
|
38446
|
+
swarm: external_exports.string().optional(),
|
|
38447
|
+
member: external_exports.string().optional(),
|
|
38448
|
+
parentRunId: external_exports.string().optional()
|
|
38449
|
+
});
|
|
38450
|
+
|
|
37444
38451
|
// src/receipts.ts
|
|
38452
|
+
var RECEIPT_JOURNAL_BUSY_MESSAGE = "The receipt journal is busy. Wait for the other vapi command to finish, then retry.";
|
|
38453
|
+
async function withReceiptJournalLock(path, operation) {
|
|
38454
|
+
return await withFileLock(`${path}.lock`, operation, {
|
|
38455
|
+
lockedMessage: RECEIPT_JOURNAL_BUSY_MESSAGE
|
|
38456
|
+
});
|
|
38457
|
+
}
|
|
37445
38458
|
var durationSchema = external_exports.number().nonnegative().finite();
|
|
37446
38459
|
var receiptSchema = external_exports.strictObject({
|
|
37447
38460
|
id: external_exports.string().min(1),
|
|
37448
38461
|
timestamp: external_exports.iso.datetime(),
|
|
38462
|
+
run: runRefSchema.optional(),
|
|
37449
38463
|
kind: external_exports.literal("transfer").optional(),
|
|
37450
38464
|
wallet: walletNameSchema.optional(),
|
|
37451
38465
|
resourceUrl: external_exports.url(),
|
|
@@ -37552,20 +38566,22 @@ async function appendReceipt(receipt, path = getVapiPaths().receipts, options =
|
|
|
37552
38566
|
const parsed = parseReceipt(
|
|
37553
38567
|
receipt.wallet === void 0 && options.wallet !== void 0 ? { ...receipt, wallet: options.wallet } : receipt
|
|
37554
38568
|
);
|
|
37555
|
-
await
|
|
37556
|
-
|
|
37557
|
-
|
|
37558
|
-
|
|
38569
|
+
await withReceiptJournalLock(path, async () => {
|
|
38570
|
+
await mkdir7(dirname5(path), { recursive: true, mode: 448 });
|
|
38571
|
+
const handle = await open6(path, "a", 384);
|
|
38572
|
+
try {
|
|
38573
|
+
await handle.writeFile(`${JSON.stringify(parsed)}
|
|
37559
38574
|
`, "utf8");
|
|
37560
|
-
|
|
37561
|
-
|
|
37562
|
-
|
|
38575
|
+
} finally {
|
|
38576
|
+
await handle.close();
|
|
38577
|
+
}
|
|
38578
|
+
});
|
|
37563
38579
|
return parsed;
|
|
37564
38580
|
}
|
|
37565
38581
|
async function readReceipts(path = getVapiPaths().receipts, options = {}) {
|
|
37566
38582
|
let raw;
|
|
37567
38583
|
try {
|
|
37568
|
-
raw = await
|
|
38584
|
+
raw = await readFile6(path, "utf8");
|
|
37569
38585
|
} catch (error61) {
|
|
37570
38586
|
if (isMissingFile(error61)) return [];
|
|
37571
38587
|
throw error61;
|
|
@@ -37607,6 +38623,45 @@ function filterReceiptsByWallet(receipts, name) {
|
|
|
37607
38623
|
return receipts.filter((receipt) => receiptWallet(receipt) === name);
|
|
37608
38624
|
}
|
|
37609
38625
|
|
|
38626
|
+
// src/transfer-journal.ts
|
|
38627
|
+
async function readTransferJournal(home, from15, nonce) {
|
|
38628
|
+
let raw;
|
|
38629
|
+
try {
|
|
38630
|
+
raw = await readFile7(getVapiPaths(home).receipts, "utf8");
|
|
38631
|
+
} catch (error61) {
|
|
38632
|
+
if (isMissingFile(error61)) return { signed: false };
|
|
38633
|
+
throw error61;
|
|
38634
|
+
}
|
|
38635
|
+
let latest;
|
|
38636
|
+
let request;
|
|
38637
|
+
for (const [index2, line] of raw.split("\n").entries()) {
|
|
38638
|
+
if (!line.trim()) continue;
|
|
38639
|
+
let receipt;
|
|
38640
|
+
try {
|
|
38641
|
+
receipt = parseReceipt(JSON.parse(line));
|
|
38642
|
+
} catch (error61) {
|
|
38643
|
+
throw new Error(`Invalid receipt on JSONL line ${index2 + 1}.`, { cause: error61 });
|
|
38644
|
+
}
|
|
38645
|
+
if (receiptWallet(receipt) !== from15 || receipt.transfer?.nonce.toLowerCase() !== nonce.toLowerCase()) {
|
|
38646
|
+
continue;
|
|
38647
|
+
}
|
|
38648
|
+
latest = receipt;
|
|
38649
|
+
if (receipt.transfer.request !== void 0) request = receipt;
|
|
38650
|
+
}
|
|
38651
|
+
return {
|
|
38652
|
+
signed: latest !== void 0,
|
|
38653
|
+
...latest === void 0 ? {} : { latest },
|
|
38654
|
+
...request === void 0 ? {} : { request },
|
|
38655
|
+
...request?.transfer?.request?.authorization.validBefore === void 0 ? {} : { validBefore: request.transfer.request.authorization.validBefore },
|
|
38656
|
+
...(latest?.transfer?.reservedOn ?? request?.transfer?.reservedOn) === void 0 ? {} : { reservedOn: latest?.transfer?.reservedOn ?? request?.transfer?.reservedOn }
|
|
38657
|
+
};
|
|
38658
|
+
}
|
|
38659
|
+
|
|
38660
|
+
// src/transfer.ts
|
|
38661
|
+
import { randomBytes as randomBytes5, randomUUID } from "node:crypto";
|
|
38662
|
+
import { mkdir as mkdir9, open as open8, stat as stat7, unlink as unlink6 } from "node:fs/promises";
|
|
38663
|
+
import { dirname as dirname6 } from "node:path";
|
|
38664
|
+
|
|
37610
38665
|
// src/siblings.ts
|
|
37611
38666
|
var SiblingsError = class extends Error {
|
|
37612
38667
|
constructor(code, message, status) {
|
|
@@ -37645,8 +38700,8 @@ async function fetchSiblings(args) {
|
|
|
37645
38700
|
try {
|
|
37646
38701
|
response = await withAgentCredentialLock(args.wallets, `wallet:${args.account}`, async () => {
|
|
37647
38702
|
await args.wallets.reload();
|
|
37648
|
-
const
|
|
37649
|
-
if (
|
|
38703
|
+
const link2 = args.wallets.entry(args.account)?.link;
|
|
38704
|
+
if (link2 === void 0 || new URL(link2.apiBase).origin !== new URL(args.apiBase).origin) {
|
|
37650
38705
|
throw new SiblingsError(
|
|
37651
38706
|
"not_linked",
|
|
37652
38707
|
"The selected account is not linked to this vAPI server."
|
|
@@ -37660,7 +38715,7 @@ async function fetchSiblings(args) {
|
|
|
37660
38715
|
...args.fetchImpl === void 0 ? {} : { fetchImpl: args.fetchImpl },
|
|
37661
38716
|
...args.now === void 0 ? {} : { now: args.now }
|
|
37662
38717
|
},
|
|
37663
|
-
new URL("/api/agents/self/siblings", new URL(
|
|
38718
|
+
new URL("/api/agents/self/siblings", new URL(link2.apiBase).origin).toString(),
|
|
37664
38719
|
{ method: "GET", signal }
|
|
37665
38720
|
);
|
|
37666
38721
|
});
|
|
@@ -37699,6 +38754,20 @@ function invalidResponseError() {
|
|
|
37699
38754
|
return new SiblingsError("invalid_response", "The vAPI siblings response was invalid.");
|
|
37700
38755
|
}
|
|
37701
38756
|
|
|
38757
|
+
// src/wallet-store.ts
|
|
38758
|
+
import {
|
|
38759
|
+
chmod as chmod2,
|
|
38760
|
+
lstat,
|
|
38761
|
+
mkdir as mkdir8,
|
|
38762
|
+
open as open7,
|
|
38763
|
+
readdir as readdir2,
|
|
38764
|
+
readFile as readFile8,
|
|
38765
|
+
rename as rename5,
|
|
38766
|
+
stat as stat6,
|
|
38767
|
+
unlink as unlink5,
|
|
38768
|
+
writeFile as writeFile4
|
|
38769
|
+
} from "node:fs/promises";
|
|
38770
|
+
|
|
37702
38771
|
// ../../node_modules/.pnpm/@solana+errors@5.5.1_typescript@6.0.3/node_modules/@solana/errors/dist/index.node.mjs
|
|
37703
38772
|
var SOLANA_ERROR__BLOCK_HEIGHT_EXCEEDED = 1;
|
|
37704
38773
|
var SOLANA_ERROR__INVALID_NONCE = 2;
|
|
@@ -41618,6 +42687,62 @@ async function signModifyingAndPartialTransactionSigners(transactionMessage, mod
|
|
|
41618
42687
|
}
|
|
41619
42688
|
var o2 = globalThis.TextEncoder;
|
|
41620
42689
|
|
|
42690
|
+
// src/wallet-store.ts
|
|
42691
|
+
var agentLinkSchema = external_exports.object({
|
|
42692
|
+
apiBase: external_exports.string(),
|
|
42693
|
+
clientId: external_exports.string(),
|
|
42694
|
+
owner: external_exports.custom((value) => typeof value === "string" && value.startsWith("0x")),
|
|
42695
|
+
label: external_exports.string(),
|
|
42696
|
+
scopes: external_exports.array(external_exports.string()),
|
|
42697
|
+
linkedAt: external_exports.iso.datetime(),
|
|
42698
|
+
routerBaseUrl: external_exports.string().optional()
|
|
42699
|
+
});
|
|
42700
|
+
var ROUTER_TOPUP_TIERS = [1, 5, 20, 50];
|
|
42701
|
+
var routerRefillSchema = external_exports.object({
|
|
42702
|
+
belowUsd: external_exports.number().finite().min(0),
|
|
42703
|
+
tierUsd: external_exports.union([external_exports.literal(1), external_exports.literal(5), external_exports.literal(20), external_exports.literal(50)])
|
|
42704
|
+
});
|
|
42705
|
+
var ceilingSweepPendingSchema = external_exports.strictObject({
|
|
42706
|
+
owner: external_exports.string().regex(/^0x[0-9a-fA-F]{40}$/),
|
|
42707
|
+
target: external_exports.string().regex(/^0x[0-9a-fA-F]{40}$/).optional(),
|
|
42708
|
+
amountAtomic: external_exports.string().regex(/^\d+$/),
|
|
42709
|
+
nonce: external_exports.string().regex(/^0x[0-9a-fA-F]{64}$/),
|
|
42710
|
+
createdAt: external_exports.iso.datetime(),
|
|
42711
|
+
status: external_exports.enum(["planned", "unknown"])
|
|
42712
|
+
});
|
|
42713
|
+
var walletEntrySchema = external_exports.object({
|
|
42714
|
+
createdAt: external_exports.iso.datetime(),
|
|
42715
|
+
label: external_exports.string().trim().min(1).max(80).optional(),
|
|
42716
|
+
spendCaps: spendCapsSchema,
|
|
42717
|
+
// On disk, null explicitly disables ceiling sweeps; an absent field is the
|
|
42718
|
+
// backwards-compatible 5 USDC default and is not written during reads.
|
|
42719
|
+
ceilingAtomic: external_exports.string().regex(/^\d+$/).nullable().optional(),
|
|
42720
|
+
ceilingSweepAttemptedAt: external_exports.iso.datetime().optional(),
|
|
42721
|
+
ceilingSweepPending: ceilingSweepPendingSchema.optional(),
|
|
42722
|
+
routerRefill: routerRefillSchema.optional(),
|
|
42723
|
+
link: agentLinkSchema.optional()
|
|
42724
|
+
});
|
|
42725
|
+
var walletRegistrySchema = external_exports.object({
|
|
42726
|
+
version: external_exports.literal(1),
|
|
42727
|
+
default: walletNameSchema.optional(),
|
|
42728
|
+
wallets: external_exports.record(walletNameSchema, walletEntrySchema)
|
|
42729
|
+
});
|
|
42730
|
+
async function spendCapsForWallet(store, name = DEFAULT_WALLET_NAME) {
|
|
42731
|
+
const entry = store.entry(name);
|
|
42732
|
+
if (entry) return { ...entry.spendCaps };
|
|
42733
|
+
return await readConfiguredSpendCaps(getVapiPaths(store.home).config);
|
|
42734
|
+
}
|
|
42735
|
+
async function readConfiguredSpendCaps(configPath) {
|
|
42736
|
+
try {
|
|
42737
|
+
const parsed = JSON.parse(await readFile8(configPath, "utf8"));
|
|
42738
|
+
const caps = typeof parsed === "object" && parsed !== null ? Reflect.get(parsed, "spendCaps") : void 0;
|
|
42739
|
+
const result = spendCapsSchema.safeParse(caps);
|
|
42740
|
+
if (result.success) return result.data;
|
|
42741
|
+
} catch {
|
|
42742
|
+
}
|
|
42743
|
+
return { ...DEFAULT_SPEND_CAPS };
|
|
42744
|
+
}
|
|
42745
|
+
|
|
41621
42746
|
// ../../node_modules/.pnpm/@solana-program+token-2022@0.6.1_@solana+kit@5.5.1_typescript@6.0.3__@solana+sysvars@5.5.1_typescript@6.0.3_/node_modules/@solana-program/token-2022/dist/src/index.mjs
|
|
41622
42747
|
var AccountState = /* @__PURE__ */ ((AccountState2) => {
|
|
41623
42748
|
AccountState2[AccountState2["Uninitialized"] = 0] = "Uninitialized";
|
|
@@ -42627,6 +43752,104 @@ var ExactSvmScheme = class {
|
|
|
42627
43752
|
var addressEncoder = getAddressEncoder();
|
|
42628
43753
|
var compiledMessageDecoder = getCompiledTransactionMessageDecoder();
|
|
42629
43754
|
|
|
43755
|
+
// src/svm.ts
|
|
43756
|
+
var MAX_RPC_RESPONSE_BYTES = 4 * 1024 * 1024;
|
|
43757
|
+
function isSolanaAddress(value) {
|
|
43758
|
+
try {
|
|
43759
|
+
address(value);
|
|
43760
|
+
return true;
|
|
43761
|
+
} catch {
|
|
43762
|
+
return false;
|
|
43763
|
+
}
|
|
43764
|
+
}
|
|
43765
|
+
async function readSolanaUsdcBalance(args) {
|
|
43766
|
+
const accounts = await readSolanaUsdcAccounts(args);
|
|
43767
|
+
return accounts.reduce((total, account) => total + account.amountAtomic, 0n);
|
|
43768
|
+
}
|
|
43769
|
+
async function readSolanaUsdcAccounts(args) {
|
|
43770
|
+
if (!isSolanaAddress(args.address)) {
|
|
43771
|
+
throw new Error(`Invalid Solana address: ${args.address}.`);
|
|
43772
|
+
}
|
|
43773
|
+
const rpcUrl = requireRpcUrl(args.network, args.configured);
|
|
43774
|
+
const value = await solanaRpcCall(
|
|
43775
|
+
rpcUrl,
|
|
43776
|
+
"getTokenAccountsByOwner",
|
|
43777
|
+
[
|
|
43778
|
+
args.address,
|
|
43779
|
+
{ mint: args.configured.usdc },
|
|
43780
|
+
{ encoding: "jsonParsed", commitment: "confirmed" }
|
|
43781
|
+
],
|
|
43782
|
+
rpcOptionsFrom(args)
|
|
43783
|
+
);
|
|
43784
|
+
return value.map((entry) => {
|
|
43785
|
+
const info = entry.account.data.parsed.info;
|
|
43786
|
+
if (!isSolanaAddress(entry.pubkey) || info.mint !== args.configured.usdc || info.owner !== args.address || info.tokenAmount.decimals !== USDC_DECIMALS || !/^\d+$/.test(info.tokenAmount.amount)) {
|
|
43787
|
+
throw new Error("Solana RPC returned a malformed USDC token account.");
|
|
43788
|
+
}
|
|
43789
|
+
return { address: entry.pubkey, amountAtomic: BigInt(info.tokenAmount.amount) };
|
|
43790
|
+
});
|
|
43791
|
+
}
|
|
43792
|
+
async function solanaRpcCall(rpcUrl, method, params, options) {
|
|
43793
|
+
const fetchImpl = options.fetchImpl ?? createPublicFetch({
|
|
43794
|
+
allowPrivateNetwork: options.allowPrivateNetwork ?? false,
|
|
43795
|
+
...options.lookup ? { lookup: options.lookup } : {}
|
|
43796
|
+
});
|
|
43797
|
+
const response = await fetchImpl(rpcUrl, {
|
|
43798
|
+
method: "POST",
|
|
43799
|
+
headers: { "content-type": "application/json" },
|
|
43800
|
+
body: JSON.stringify({ jsonrpc: "2.0", id: 1, method, params }),
|
|
43801
|
+
redirect: "manual"
|
|
43802
|
+
});
|
|
43803
|
+
if (!response.ok) {
|
|
43804
|
+
discardBody(response);
|
|
43805
|
+
const rateLimit = response.status === 429 ? " (public RPC rate limit)" : "";
|
|
43806
|
+
throw new Error(`Solana RPC ${method} failed with HTTP ${response.status}${rateLimit}.`);
|
|
43807
|
+
}
|
|
43808
|
+
const envelope = await readBoundedRpcJson(response);
|
|
43809
|
+
if (!isRecord2(envelope) || envelope.jsonrpc !== "2.0") {
|
|
43810
|
+
throw new Error(`Solana RPC ${method} returned an invalid JSON-RPC envelope.`);
|
|
43811
|
+
}
|
|
43812
|
+
if (envelope.error !== void 0) {
|
|
43813
|
+
throw new Error(`Solana RPC ${method} failed: ${JSON.stringify(envelope.error)}.`);
|
|
43814
|
+
}
|
|
43815
|
+
const result = envelope.result;
|
|
43816
|
+
if (isRecord2(result) && "value" in result) return result.value;
|
|
43817
|
+
return result;
|
|
43818
|
+
}
|
|
43819
|
+
async function readBoundedRpcJson(response) {
|
|
43820
|
+
if (!response.body) throw new Error("Solana RPC response body is empty.");
|
|
43821
|
+
const reader = response.body.getReader();
|
|
43822
|
+
const chunks = [];
|
|
43823
|
+
let length = 0;
|
|
43824
|
+
while (true) {
|
|
43825
|
+
const next = await reader.read();
|
|
43826
|
+
if (next.done) break;
|
|
43827
|
+
length += next.value.byteLength;
|
|
43828
|
+
if (length > MAX_RPC_RESPONSE_BYTES) {
|
|
43829
|
+
await reader.cancel();
|
|
43830
|
+
throw new Error(`Solana RPC response exceeds ${MAX_RPC_RESPONSE_BYTES} bytes.`);
|
|
43831
|
+
}
|
|
43832
|
+
chunks.push(next.value);
|
|
43833
|
+
}
|
|
43834
|
+
const bytes = new Uint8Array(length);
|
|
43835
|
+
let offset = 0;
|
|
43836
|
+
for (const chunk of chunks) {
|
|
43837
|
+
bytes.set(chunk, offset);
|
|
43838
|
+
offset += chunk.byteLength;
|
|
43839
|
+
}
|
|
43840
|
+
return JSON.parse(new TextDecoder().decode(bytes));
|
|
43841
|
+
}
|
|
43842
|
+
function rpcOptionsFrom(options) {
|
|
43843
|
+
return {
|
|
43844
|
+
allowPrivateNetwork: options.allowPrivateNetwork,
|
|
43845
|
+
...options.fetchImpl ? { fetchImpl: options.fetchImpl } : {},
|
|
43846
|
+
...options.lookup ? { lookup: options.lookup } : {}
|
|
43847
|
+
};
|
|
43848
|
+
}
|
|
43849
|
+
function isRecord2(value) {
|
|
43850
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
43851
|
+
}
|
|
43852
|
+
|
|
42630
43853
|
// src/x402.ts
|
|
42631
43854
|
var MAX_CHALLENGE_BYTES = 1048576;
|
|
42632
43855
|
var MAX_UINT256 = (1n << 256n) - 1n;
|
|
@@ -42659,15 +43882,15 @@ var X402Error = class extends Error {
|
|
|
42659
43882
|
function isValidPaymentId(value) {
|
|
42660
43883
|
return typeof value === "string" && PAYMENT_ID_PATTERN.test(value);
|
|
42661
43884
|
}
|
|
42662
|
-
function createPaymentId(
|
|
43885
|
+
function createPaymentId(randomBytes9 = fillRandomBytes) {
|
|
42663
43886
|
const bytes = new Uint8Array(16);
|
|
42664
|
-
|
|
43887
|
+
randomBytes9(bytes);
|
|
42665
43888
|
return `pay_${bytesToHex(bytes).slice(2)}`;
|
|
42666
43889
|
}
|
|
42667
43890
|
function buildPaymentExtensions(serverExtensions, options = {}) {
|
|
42668
43891
|
const declaredBuilderCode = serverExtensions?.["builder-code"];
|
|
42669
|
-
const builderInfo =
|
|
42670
|
-
const serverBuilderInfo =
|
|
43892
|
+
const builderInfo = isRecord3(declaredBuilderCode) ? declaredBuilderCode.info : void 0;
|
|
43893
|
+
const serverBuilderInfo = isRecord3(builderInfo) ? builderInfo : void 0;
|
|
42671
43894
|
const declaredServerCodes = serverBuilderInfo?.s;
|
|
42672
43895
|
const serverCodes = typeof declaredServerCodes === "string" ? [declaredServerCodes] : Array.isArray(declaredServerCodes) ? declaredServerCodes : void 0;
|
|
42673
43896
|
const validServerCodes = [];
|
|
@@ -42684,10 +43907,10 @@ function buildPaymentExtensions(serverExtensions, options = {}) {
|
|
|
42684
43907
|
...typeof appCode === "string" && BUILDER_CODE_PATTERN.test(appCode) ? { a: appCode } : {},
|
|
42685
43908
|
s: [VAPI_BUILDER_CODE, ...validServerCodes]
|
|
42686
43909
|
},
|
|
42687
|
-
...
|
|
43910
|
+
...isRecord3(declaredBuilderCode) && declaredBuilderCode.schema !== void 0 ? { schema: declaredBuilderCode.schema } : {}
|
|
42688
43911
|
};
|
|
42689
43912
|
const paymentIdentifier = serverExtensions?.["payment-identifier"];
|
|
42690
|
-
if (!
|
|
43913
|
+
if (!isRecord3(paymentIdentifier)) {
|
|
42691
43914
|
return { ...serverExtensions, "builder-code": builderCode };
|
|
42692
43915
|
}
|
|
42693
43916
|
if (!isValidPaymentId(options.paymentId)) {
|
|
@@ -42696,7 +43919,7 @@ function buildPaymentExtensions(serverExtensions, options = {}) {
|
|
|
42696
43919
|
"paymentId must be 16-128 characters containing only letters, digits, underscores, or hyphens."
|
|
42697
43920
|
);
|
|
42698
43921
|
}
|
|
42699
|
-
const paymentInfo =
|
|
43922
|
+
const paymentInfo = isRecord3(paymentIdentifier.info) ? paymentIdentifier.info : {};
|
|
42700
43923
|
return {
|
|
42701
43924
|
...serverExtensions,
|
|
42702
43925
|
"builder-code": builderCode,
|
|
@@ -42707,7 +43930,7 @@ function buildPaymentExtensions(serverExtensions, options = {}) {
|
|
|
42707
43930
|
};
|
|
42708
43931
|
}
|
|
42709
43932
|
function parse402Challenge(challenge2, configuredNetworks, fallbackResourceUrl = "", requiredNetwork, expectedPayTo) {
|
|
42710
|
-
if (!
|
|
43933
|
+
if (!isRecord3(challenge2) || !Array.isArray(challenge2.accepts)) {
|
|
42711
43934
|
throw new X402Error("invalid_challenge", "x402 challenge has no accepts array.");
|
|
42712
43935
|
}
|
|
42713
43936
|
if (challenge2.x402Version !== void 0 && challenge2.x402Version !== 2) {
|
|
@@ -42716,7 +43939,7 @@ function parse402Challenge(challenge2, configuredNetworks, fallbackResourceUrl =
|
|
|
42716
43939
|
`Unsupported x402 version: ${String(challenge2.x402Version)}.`
|
|
42717
43940
|
);
|
|
42718
43941
|
}
|
|
42719
|
-
const candidates = challenge2.accepts.filter(
|
|
43942
|
+
const candidates = challenge2.accepts.filter(isRecord3);
|
|
42720
43943
|
const accepted = candidates.find((candidate) => {
|
|
42721
43944
|
const network2 = readString(candidate, "network");
|
|
42722
43945
|
const scheme = readString(candidate, "scheme");
|
|
@@ -42905,17 +44128,24 @@ function buildEip3009TypedData(args) {
|
|
|
42905
44128
|
if (!Number.isSafeInteger(args.nowSeconds) || args.nowSeconds < 0) {
|
|
42906
44129
|
throw new Error("nowSeconds must be a non-negative integer.");
|
|
42907
44130
|
}
|
|
44131
|
+
if (args.maximumValidBeforeSeconds !== void 0 && (!Number.isSafeInteger(args.maximumValidBeforeSeconds) || args.maximumValidBeforeSeconds <= args.nowSeconds)) {
|
|
44132
|
+
throw new Error("The payment allowance has expired; no authorization was signed.");
|
|
44133
|
+
}
|
|
42908
44134
|
if (!isEvmPaymentRequirements(args.quote.accepted)) {
|
|
42909
44135
|
throw new Error("EIP-3009 typed data can only be built for an EVM x402 quote.");
|
|
42910
44136
|
}
|
|
42911
44137
|
const accepted = args.quote.accepted;
|
|
42912
44138
|
const windowSeconds = Math.min(MAX_AUTHORIZATION_WINDOW_SECONDS, accepted.maxTimeoutSeconds);
|
|
44139
|
+
const validBefore = Math.min(
|
|
44140
|
+
args.nowSeconds + windowSeconds,
|
|
44141
|
+
args.maximumValidBeforeSeconds ?? Number.MAX_SAFE_INTEGER
|
|
44142
|
+
);
|
|
42913
44143
|
const authorization = {
|
|
42914
44144
|
from: getAddress(args.from),
|
|
42915
44145
|
to: accepted.payTo,
|
|
42916
44146
|
value: args.quote.amountAtomic.toString(),
|
|
42917
44147
|
validAfter: "0",
|
|
42918
|
-
validBefore:
|
|
44148
|
+
validBefore: validBefore.toString(),
|
|
42919
44149
|
nonce: args.nonce
|
|
42920
44150
|
};
|
|
42921
44151
|
return {
|
|
@@ -42941,7 +44171,7 @@ function buildEip3009TypedData(args) {
|
|
|
42941
44171
|
};
|
|
42942
44172
|
}
|
|
42943
44173
|
async function buildX402Payment(args) {
|
|
42944
|
-
const advertisesPaymentIdentifier =
|
|
44174
|
+
const advertisesPaymentIdentifier = isRecord3(args.quote.extensions?.["payment-identifier"]);
|
|
42945
44175
|
const paymentId = advertisesPaymentIdentifier ? args.paymentId ?? createPaymentId() : void 0;
|
|
42946
44176
|
const extensions = buildPaymentExtensions(args.quote.extensions, { paymentId });
|
|
42947
44177
|
if (isSvmPaymentRequirements(args.quote.accepted)) {
|
|
@@ -42986,7 +44216,8 @@ async function buildX402Payment(args) {
|
|
|
42986
44216
|
from: args.signer.address,
|
|
42987
44217
|
quote: args.quote,
|
|
42988
44218
|
nonce,
|
|
42989
|
-
nowSeconds: args.nowSeconds ?? Math.floor(Date.now() / 1e3)
|
|
44219
|
+
nowSeconds: args.nowSeconds ?? Math.floor(Date.now() / 1e3),
|
|
44220
|
+
...args.maximumValidBeforeSeconds === void 0 ? {} : { maximumValidBeforeSeconds: args.maximumValidBeforeSeconds }
|
|
42990
44221
|
});
|
|
42991
44222
|
const signature = await args.signer.signTypedData(typedData);
|
|
42992
44223
|
const payload = {
|
|
@@ -43009,6 +44240,7 @@ async function buildCompatibleX402Payment(args) {
|
|
|
43009
44240
|
signer: args.account,
|
|
43010
44241
|
quote: args.quote,
|
|
43011
44242
|
...args.nowSeconds === void 0 ? {} : { nowSeconds: args.nowSeconds },
|
|
44243
|
+
...args.maximumValidBeforeSeconds === void 0 ? {} : { maximumValidBeforeSeconds: args.maximumValidBeforeSeconds },
|
|
43012
44244
|
...args.nonce === void 0 ? {} : { nonce: args.nonce },
|
|
43013
44245
|
...args.fetchImpl === void 0 ? {} : { fetchImpl: args.fetchImpl },
|
|
43014
44246
|
...args.paymentId === void 0 ? {} : { paymentId: args.paymentId }
|
|
@@ -43068,7 +44300,7 @@ function parseSettlementResponse(headers) {
|
|
|
43068
44300
|
}
|
|
43069
44301
|
}
|
|
43070
44302
|
function classifySettlement(value) {
|
|
43071
|
-
if (!
|
|
44303
|
+
if (!isRecord3(value) || typeof value.success !== "boolean") {
|
|
43072
44304
|
return "unknown";
|
|
43073
44305
|
}
|
|
43074
44306
|
return value.success ? "succeeded" : "rejected";
|
|
@@ -43164,12 +44396,12 @@ function canonicalTokenDomain(network, configured) {
|
|
|
43164
44396
|
return configuredDomain;
|
|
43165
44397
|
}
|
|
43166
44398
|
function readBoundedJsonRecord(value) {
|
|
43167
|
-
if (!
|
|
44399
|
+
if (!isRecord3(value)) return null;
|
|
43168
44400
|
const seen = /* @__PURE__ */ new WeakSet();
|
|
43169
44401
|
const budget = { entries: 0 };
|
|
43170
44402
|
try {
|
|
43171
44403
|
const cloned = cloneBoundedJson(value, 0, seen, budget);
|
|
43172
|
-
return
|
|
44404
|
+
return isRecord3(cloned) ? cloned : null;
|
|
43173
44405
|
} catch {
|
|
43174
44406
|
return null;
|
|
43175
44407
|
}
|
|
@@ -43178,7 +44410,7 @@ function readChallengeExtensions(value) {
|
|
|
43178
44410
|
if (value === void 0) return void 0;
|
|
43179
44411
|
const extensions = readBoundedJsonRecord(value);
|
|
43180
44412
|
if (extensions) return extensions;
|
|
43181
|
-
if (!
|
|
44413
|
+
if (!isRecord3(value)) return void 0;
|
|
43182
44414
|
const paymentExtensions = /* @__PURE__ */ Object.create(null);
|
|
43183
44415
|
for (const name of ["builder-code", "payment-identifier"]) {
|
|
43184
44416
|
const extension = readBoundedJsonRecord(value[name]);
|
|
@@ -43231,7 +44463,7 @@ function cloneBoundedJson(value, depth, seen, budget) {
|
|
|
43231
44463
|
}
|
|
43232
44464
|
function readResource(value, fallbackUrl) {
|
|
43233
44465
|
if (typeof value === "string") return { url: value };
|
|
43234
|
-
if (!
|
|
44466
|
+
if (!isRecord3(value)) return { url: fallbackUrl };
|
|
43235
44467
|
const url2 = readString(value, "url") ?? fallbackUrl;
|
|
43236
44468
|
return {
|
|
43237
44469
|
url: url2,
|
|
@@ -43246,7 +44478,7 @@ function readString(value, key) {
|
|
|
43246
44478
|
const field = value[key];
|
|
43247
44479
|
return typeof field === "string" && field.length > 0 ? field : null;
|
|
43248
44480
|
}
|
|
43249
|
-
function
|
|
44481
|
+
function isRecord3(value) {
|
|
43250
44482
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
43251
44483
|
}
|
|
43252
44484
|
function decodeBase64Json(value) {
|
|
@@ -43262,421 +44494,6 @@ function encodeBase64Json(value) {
|
|
|
43262
44494
|
return globalThis.btoa(binary);
|
|
43263
44495
|
}
|
|
43264
44496
|
|
|
43265
|
-
// src/svm.ts
|
|
43266
|
-
var MAX_RPC_RESPONSE_BYTES = 4 * 1024 * 1024;
|
|
43267
|
-
function isSolanaAddress(value) {
|
|
43268
|
-
try {
|
|
43269
|
-
address(value);
|
|
43270
|
-
return true;
|
|
43271
|
-
} catch {
|
|
43272
|
-
return false;
|
|
43273
|
-
}
|
|
43274
|
-
}
|
|
43275
|
-
async function readSolanaUsdcBalance(args) {
|
|
43276
|
-
const accounts = await readSolanaUsdcAccounts(args);
|
|
43277
|
-
return accounts.reduce((total, account) => total + account.amountAtomic, 0n);
|
|
43278
|
-
}
|
|
43279
|
-
async function readSolanaUsdcAccounts(args) {
|
|
43280
|
-
if (!isSolanaAddress(args.address)) {
|
|
43281
|
-
throw new Error(`Invalid Solana address: ${args.address}.`);
|
|
43282
|
-
}
|
|
43283
|
-
const rpcUrl = requireRpcUrl(args.network, args.configured);
|
|
43284
|
-
const value = await solanaRpcCall(
|
|
43285
|
-
rpcUrl,
|
|
43286
|
-
"getTokenAccountsByOwner",
|
|
43287
|
-
[
|
|
43288
|
-
args.address,
|
|
43289
|
-
{ mint: args.configured.usdc },
|
|
43290
|
-
{ encoding: "jsonParsed", commitment: "confirmed" }
|
|
43291
|
-
],
|
|
43292
|
-
rpcOptionsFrom(args)
|
|
43293
|
-
);
|
|
43294
|
-
return value.map((entry) => {
|
|
43295
|
-
const info = entry.account.data.parsed.info;
|
|
43296
|
-
if (!isSolanaAddress(entry.pubkey) || info.mint !== args.configured.usdc || info.owner !== args.address || info.tokenAmount.decimals !== USDC_DECIMALS || !/^\d+$/.test(info.tokenAmount.amount)) {
|
|
43297
|
-
throw new Error("Solana RPC returned a malformed USDC token account.");
|
|
43298
|
-
}
|
|
43299
|
-
return { address: entry.pubkey, amountAtomic: BigInt(info.tokenAmount.amount) };
|
|
43300
|
-
});
|
|
43301
|
-
}
|
|
43302
|
-
async function solanaRpcCall(rpcUrl, method, params, options) {
|
|
43303
|
-
const fetchImpl = options.fetchImpl ?? createPublicFetch({
|
|
43304
|
-
allowPrivateNetwork: options.allowPrivateNetwork ?? false,
|
|
43305
|
-
...options.lookup ? { lookup: options.lookup } : {}
|
|
43306
|
-
});
|
|
43307
|
-
const response = await fetchImpl(rpcUrl, {
|
|
43308
|
-
method: "POST",
|
|
43309
|
-
headers: { "content-type": "application/json" },
|
|
43310
|
-
body: JSON.stringify({ jsonrpc: "2.0", id: 1, method, params }),
|
|
43311
|
-
redirect: "manual"
|
|
43312
|
-
});
|
|
43313
|
-
if (!response.ok) {
|
|
43314
|
-
discardBody(response);
|
|
43315
|
-
const rateLimit = response.status === 429 ? " (public RPC rate limit)" : "";
|
|
43316
|
-
throw new Error(`Solana RPC ${method} failed with HTTP ${response.status}${rateLimit}.`);
|
|
43317
|
-
}
|
|
43318
|
-
const envelope = await readBoundedRpcJson(response);
|
|
43319
|
-
if (!isRecord3(envelope) || envelope.jsonrpc !== "2.0") {
|
|
43320
|
-
throw new Error(`Solana RPC ${method} returned an invalid JSON-RPC envelope.`);
|
|
43321
|
-
}
|
|
43322
|
-
if (envelope.error !== void 0) {
|
|
43323
|
-
throw new Error(`Solana RPC ${method} failed: ${JSON.stringify(envelope.error)}.`);
|
|
43324
|
-
}
|
|
43325
|
-
const result = envelope.result;
|
|
43326
|
-
if (isRecord3(result) && "value" in result) return result.value;
|
|
43327
|
-
return result;
|
|
43328
|
-
}
|
|
43329
|
-
async function readBoundedRpcJson(response) {
|
|
43330
|
-
if (!response.body) throw new Error("Solana RPC response body is empty.");
|
|
43331
|
-
const reader = response.body.getReader();
|
|
43332
|
-
const chunks = [];
|
|
43333
|
-
let length = 0;
|
|
43334
|
-
while (true) {
|
|
43335
|
-
const next = await reader.read();
|
|
43336
|
-
if (next.done) break;
|
|
43337
|
-
length += next.value.byteLength;
|
|
43338
|
-
if (length > MAX_RPC_RESPONSE_BYTES) {
|
|
43339
|
-
await reader.cancel();
|
|
43340
|
-
throw new Error(`Solana RPC response exceeds ${MAX_RPC_RESPONSE_BYTES} bytes.`);
|
|
43341
|
-
}
|
|
43342
|
-
chunks.push(next.value);
|
|
43343
|
-
}
|
|
43344
|
-
const bytes = new Uint8Array(length);
|
|
43345
|
-
let offset = 0;
|
|
43346
|
-
for (const chunk of chunks) {
|
|
43347
|
-
bytes.set(chunk, offset);
|
|
43348
|
-
offset += chunk.byteLength;
|
|
43349
|
-
}
|
|
43350
|
-
return JSON.parse(new TextDecoder().decode(bytes));
|
|
43351
|
-
}
|
|
43352
|
-
function rpcOptionsFrom(options) {
|
|
43353
|
-
return {
|
|
43354
|
-
allowPrivateNetwork: options.allowPrivateNetwork,
|
|
43355
|
-
...options.fetchImpl ? { fetchImpl: options.fetchImpl } : {},
|
|
43356
|
-
...options.lookup ? { lookup: options.lookup } : {}
|
|
43357
|
-
};
|
|
43358
|
-
}
|
|
43359
|
-
function isRecord3(value) {
|
|
43360
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
43361
|
-
}
|
|
43362
|
-
|
|
43363
|
-
// src/sweep.ts
|
|
43364
|
-
var ERC20_ABI = [
|
|
43365
|
-
{
|
|
43366
|
-
type: "function",
|
|
43367
|
-
name: "balanceOf",
|
|
43368
|
-
stateMutability: "view",
|
|
43369
|
-
inputs: [{ name: "account", type: "address" }],
|
|
43370
|
-
outputs: [{ name: "balance", type: "uint256" }]
|
|
43371
|
-
},
|
|
43372
|
-
{
|
|
43373
|
-
type: "function",
|
|
43374
|
-
name: "transfer",
|
|
43375
|
-
stateMutability: "nonpayable",
|
|
43376
|
-
inputs: [
|
|
43377
|
-
{ name: "to", type: "address" },
|
|
43378
|
-
{ name: "amount", type: "uint256" }
|
|
43379
|
-
],
|
|
43380
|
-
outputs: [{ name: "success", type: "bool" }]
|
|
43381
|
-
}
|
|
43382
|
-
];
|
|
43383
|
-
async function readUsdcBalance(args) {
|
|
43384
|
-
if (isSolanaNetwork(args.network)) {
|
|
43385
|
-
return await readSolanaUsdcBalance({
|
|
43386
|
-
network: args.network,
|
|
43387
|
-
configured: args.configured,
|
|
43388
|
-
address: args.address,
|
|
43389
|
-
allowPrivateNetwork: args.allowPrivateNetwork,
|
|
43390
|
-
...args.fetchImpl ? { fetchImpl: args.fetchImpl } : {},
|
|
43391
|
-
...args.lookup ? { lookup: args.lookup } : {}
|
|
43392
|
-
});
|
|
43393
|
-
}
|
|
43394
|
-
const client = createNetworkPublicClient(args.network, args.configured, {
|
|
43395
|
-
allowPrivateNetwork: args.allowPrivateNetwork,
|
|
43396
|
-
...args.fetchImpl ? { fetch: args.fetchImpl } : {},
|
|
43397
|
-
...args.lookup ? { lookup: args.lookup } : {}
|
|
43398
|
-
});
|
|
43399
|
-
return await client.readContract({
|
|
43400
|
-
address: getAddress(
|
|
43401
|
-
usesUsdcGas(args.network) ? getNetworkDefinition(args.network).usdc : args.configured.usdc
|
|
43402
|
-
),
|
|
43403
|
-
abi: ERC20_ABI,
|
|
43404
|
-
functionName: "balanceOf",
|
|
43405
|
-
args: [getAddress(args.address)]
|
|
43406
|
-
});
|
|
43407
|
-
}
|
|
43408
|
-
|
|
43409
|
-
// src/transfer.ts
|
|
43410
|
-
import { randomBytes as randomBytes3, randomUUID } from "node:crypto";
|
|
43411
|
-
import { mkdir as mkdir6, open as open6, stat as stat4, unlink as unlink4 } from "node:fs/promises";
|
|
43412
|
-
import { dirname as dirname5 } from "node:path";
|
|
43413
|
-
|
|
43414
|
-
// src/spend-policy.ts
|
|
43415
|
-
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
43416
|
-
import { mkdir as mkdir4, open as open4, readFile as readFile3, rename as rename2, stat as stat2, unlink as unlink2, writeFile as writeFile2 } from "node:fs/promises";
|
|
43417
|
-
import { dirname as dirname4 } from "node:path";
|
|
43418
|
-
var ledgerSchema = external_exports.object({
|
|
43419
|
-
date: external_exports.string().regex(/^\d{4}-\d{2}-\d{2}$/),
|
|
43420
|
-
spentAtomic: external_exports.string().regex(/^\d+$/)
|
|
43421
|
-
});
|
|
43422
|
-
var spendReservationSchema = external_exports.object({
|
|
43423
|
-
id: external_exports.string().min(1),
|
|
43424
|
-
amountAtomic: external_exports.string().regex(/^\d+$/)
|
|
43425
|
-
});
|
|
43426
|
-
var ledgerRowSchema = ledgerSchema.extend({
|
|
43427
|
-
wallet: walletNameSchema.optional(),
|
|
43428
|
-
reservations: external_exports.array(spendReservationSchema).optional()
|
|
43429
|
-
});
|
|
43430
|
-
var ledgerFileSchema = external_exports.union([
|
|
43431
|
-
external_exports.object({ version: external_exports.literal(1), rows: external_exports.array(ledgerRowSchema) }),
|
|
43432
|
-
ledgerRowSchema
|
|
43433
|
-
]);
|
|
43434
|
-
var SpendCapError = class extends Error {
|
|
43435
|
-
constructor(code, message) {
|
|
43436
|
-
super(message);
|
|
43437
|
-
this.code = code;
|
|
43438
|
-
this.name = "SpendCapError";
|
|
43439
|
-
}
|
|
43440
|
-
code;
|
|
43441
|
-
};
|
|
43442
|
-
function utcDateKey(now = /* @__PURE__ */ new Date()) {
|
|
43443
|
-
return now.toISOString().slice(0, 10);
|
|
43444
|
-
}
|
|
43445
|
-
async function readStoredSpendLedgerRows(path, now) {
|
|
43446
|
-
let raw;
|
|
43447
|
-
try {
|
|
43448
|
-
raw = await readFile3(path, "utf8");
|
|
43449
|
-
} catch (error61) {
|
|
43450
|
-
if (isMissingFile(error61)) return [];
|
|
43451
|
-
throw error61;
|
|
43452
|
-
}
|
|
43453
|
-
const parsed = ledgerFileSchema.parse(JSON.parse(raw));
|
|
43454
|
-
const rows = "rows" in parsed ? parsed.rows : [parsed];
|
|
43455
|
-
const today = utcDateKey(now);
|
|
43456
|
-
return rows.filter((row) => row.date === today).map((row) => ({
|
|
43457
|
-
date: row.date,
|
|
43458
|
-
spentAtomic: row.spentAtomic,
|
|
43459
|
-
wallet: row.wallet ?? DEFAULT_WALLET_NAME,
|
|
43460
|
-
...row.reservations === void 0 ? {} : { reservations: row.reservations }
|
|
43461
|
-
}));
|
|
43462
|
-
}
|
|
43463
|
-
async function reserveSpend(amountAtomic, caps, options) {
|
|
43464
|
-
if (amountAtomic < 0n) {
|
|
43465
|
-
throw new Error("Spend amount cannot be negative.");
|
|
43466
|
-
}
|
|
43467
|
-
const perCallAtomic = parseAtomicCap(caps.perCallAtomic, "per-call");
|
|
43468
|
-
const perDayAtomic = parseAtomicCap(caps.perDayAtomic, "per-day");
|
|
43469
|
-
if (amountAtomic > perCallAtomic) {
|
|
43470
|
-
throw new SpendCapError(
|
|
43471
|
-
"per_call_cap_exceeded",
|
|
43472
|
-
`Payment quote ${amountAtomic} atomic USDC exceeds the per-call cap ${perCallAtomic}. Refusing to sign.`
|
|
43473
|
-
);
|
|
43474
|
-
}
|
|
43475
|
-
const ledgerPath = options?.ledgerPath ?? getAgentCashPaths().ledger;
|
|
43476
|
-
const now = options?.now ?? /* @__PURE__ */ new Date();
|
|
43477
|
-
const wallet = options?.wallet ?? DEFAULT_WALLET_NAME;
|
|
43478
|
-
return await withLedgerLock(ledgerPath, async () => {
|
|
43479
|
-
const rows = await readStoredSpendLedgerRows(ledgerPath, now);
|
|
43480
|
-
const current = rows.find((row) => row.wallet === wallet);
|
|
43481
|
-
if (options?.reservationId !== void 0) {
|
|
43482
|
-
if (current?.reservations?.some((reservation) => reservation.id === options.reservationId)) {
|
|
43483
|
-
throw new Error(`Spend reservation ${options.reservationId} already exists.`);
|
|
43484
|
-
}
|
|
43485
|
-
}
|
|
43486
|
-
const spentAtomic = current?.spentAtomic ?? "0";
|
|
43487
|
-
const nextSpent = BigInt(spentAtomic) + amountAtomic;
|
|
43488
|
-
if (nextSpent > perDayAtomic) {
|
|
43489
|
-
throw new SpendCapError(
|
|
43490
|
-
"per_day_cap_exceeded",
|
|
43491
|
-
`Payment quote ${amountAtomic} atomic USDC would raise today's spend to ${nextSpent} for wallet ${wallet}, above the per-day cap ${perDayAtomic}. Refusing to sign.`
|
|
43492
|
-
);
|
|
43493
|
-
}
|
|
43494
|
-
const nextRow = {
|
|
43495
|
-
date: utcDateKey(now),
|
|
43496
|
-
spentAtomic: nextSpent.toString(),
|
|
43497
|
-
wallet,
|
|
43498
|
-
...(current?.reservations?.length ?? 0) === 0 && options?.reservationId === void 0 ? {} : {
|
|
43499
|
-
reservations: [
|
|
43500
|
-
...current?.reservations ?? [],
|
|
43501
|
-
...options?.reservationId === void 0 ? [] : [{ id: options.reservationId, amountAtomic: amountAtomic.toString() }]
|
|
43502
|
-
]
|
|
43503
|
-
}
|
|
43504
|
-
};
|
|
43505
|
-
const nextRows = [...rows.filter((row) => row.wallet !== wallet), nextRow].sort(
|
|
43506
|
-
(a, b) => a.wallet.localeCompare(b.wallet)
|
|
43507
|
-
);
|
|
43508
|
-
await writeLedgerAtomically(ledgerPath, nextRows);
|
|
43509
|
-
return { date: nextRow.date, spentAtomic: nextRow.spentAtomic };
|
|
43510
|
-
});
|
|
43511
|
-
}
|
|
43512
|
-
async function releaseSpend(amountAtomic, options) {
|
|
43513
|
-
if (amountAtomic < 0n) {
|
|
43514
|
-
throw new Error("Spend amount cannot be negative.");
|
|
43515
|
-
}
|
|
43516
|
-
const ledgerPath = options.ledgerPath ?? getAgentCashPaths().ledger;
|
|
43517
|
-
const now = options.now ?? /* @__PURE__ */ new Date();
|
|
43518
|
-
const wallet = options.wallet ?? DEFAULT_WALLET_NAME;
|
|
43519
|
-
return await withLedgerLock(ledgerPath, async () => {
|
|
43520
|
-
const today = utcDateKey(now);
|
|
43521
|
-
const rows = await readStoredSpendLedgerRows(ledgerPath, now);
|
|
43522
|
-
const current = rows.find((row) => row.wallet === wallet);
|
|
43523
|
-
if (options.reservedOn !== today) {
|
|
43524
|
-
return { date: today, spentAtomic: current?.spentAtomic ?? "0" };
|
|
43525
|
-
}
|
|
43526
|
-
const reservation = options.reservationId === void 0 ? void 0 : current?.reservations?.find((candidate) => candidate.id === options.reservationId);
|
|
43527
|
-
if (options.reservationId !== void 0 && reservation === void 0) {
|
|
43528
|
-
return { date: today, spentAtomic: current?.spentAtomic ?? "0" };
|
|
43529
|
-
}
|
|
43530
|
-
if (reservation !== void 0 && reservation.amountAtomic !== amountAtomic.toString()) {
|
|
43531
|
-
throw new Error(`Spend reservation ${options.reservationId} has a different amount.`);
|
|
43532
|
-
}
|
|
43533
|
-
const spentAtomic = BigInt(current?.spentAtomic ?? "0");
|
|
43534
|
-
const releasedAmount = reservation === void 0 ? amountAtomic : BigInt(reservation.amountAtomic);
|
|
43535
|
-
const nextSpent = spentAtomic > releasedAmount ? spentAtomic - releasedAmount : 0n;
|
|
43536
|
-
const remainingReservations = current?.reservations?.filter(
|
|
43537
|
-
(candidate) => candidate.id !== options.reservationId
|
|
43538
|
-
);
|
|
43539
|
-
const nextRow = {
|
|
43540
|
-
date: today,
|
|
43541
|
-
spentAtomic: nextSpent.toString(),
|
|
43542
|
-
wallet,
|
|
43543
|
-
...remainingReservations === void 0 || remainingReservations.length === 0 ? {} : { reservations: remainingReservations }
|
|
43544
|
-
};
|
|
43545
|
-
const nextRows = [...rows.filter((row) => row.wallet !== wallet), nextRow].sort(
|
|
43546
|
-
(a, b) => a.wallet.localeCompare(b.wallet)
|
|
43547
|
-
);
|
|
43548
|
-
await writeLedgerAtomically(ledgerPath, nextRows);
|
|
43549
|
-
return { date: nextRow.date, spentAtomic: nextRow.spentAtomic };
|
|
43550
|
-
});
|
|
43551
|
-
}
|
|
43552
|
-
function parseAtomicCap(value, label) {
|
|
43553
|
-
if (!/^\d+$/.test(value)) {
|
|
43554
|
-
throw new Error(`Invalid ${label} spend cap ${JSON.stringify(value)}.`);
|
|
43555
|
-
}
|
|
43556
|
-
return BigInt(value);
|
|
43557
|
-
}
|
|
43558
|
-
async function writeLedgerAtomically(path, rows) {
|
|
43559
|
-
const ledger = rows.length === 1 && rows[0].wallet === DEFAULT_WALLET_NAME ? {
|
|
43560
|
-
date: rows[0].date,
|
|
43561
|
-
spentAtomic: rows[0].spentAtomic,
|
|
43562
|
-
...rows[0].reservations === void 0 ? {} : { reservations: rows[0].reservations }
|
|
43563
|
-
} : { version: 1, rows };
|
|
43564
|
-
await mkdir4(dirname4(path), { recursive: true, mode: 448 });
|
|
43565
|
-
const temporaryPath = `${path}.${process.pid}.${randomBytes2(6).toString("hex")}.tmp`;
|
|
43566
|
-
await writeFile2(temporaryPath, `${JSON.stringify(ledger, null, 2)}
|
|
43567
|
-
`, {
|
|
43568
|
-
encoding: "utf8",
|
|
43569
|
-
mode: 384,
|
|
43570
|
-
flag: "wx"
|
|
43571
|
-
});
|
|
43572
|
-
await rename2(temporaryPath, path);
|
|
43573
|
-
}
|
|
43574
|
-
async function withLedgerLock(path, operation) {
|
|
43575
|
-
await mkdir4(dirname4(path), { recursive: true, mode: 448 });
|
|
43576
|
-
const lockPath = `${path}.lock`;
|
|
43577
|
-
const deadline = Date.now() + 2e3;
|
|
43578
|
-
let handle;
|
|
43579
|
-
while (!handle) {
|
|
43580
|
-
try {
|
|
43581
|
-
handle = await open4(lockPath, "wx", 384);
|
|
43582
|
-
} catch (error61) {
|
|
43583
|
-
if (!(error61 instanceof Error && "code" in error61 && error61.code === "EEXIST")) {
|
|
43584
|
-
throw error61;
|
|
43585
|
-
}
|
|
43586
|
-
await removeStaleLock(lockPath);
|
|
43587
|
-
if (Date.now() >= deadline) {
|
|
43588
|
-
throw new Error("Timed out waiting for the spend ledger lock.");
|
|
43589
|
-
}
|
|
43590
|
-
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
43591
|
-
}
|
|
43592
|
-
}
|
|
43593
|
-
try {
|
|
43594
|
-
return await operation();
|
|
43595
|
-
} finally {
|
|
43596
|
-
await handle.close();
|
|
43597
|
-
await unlink2(lockPath).catch(() => void 0);
|
|
43598
|
-
}
|
|
43599
|
-
}
|
|
43600
|
-
async function removeStaleLock(path) {
|
|
43601
|
-
try {
|
|
43602
|
-
const metadata = await stat2(path);
|
|
43603
|
-
if (Date.now() - metadata.mtimeMs > 3e4) {
|
|
43604
|
-
await unlink2(path);
|
|
43605
|
-
}
|
|
43606
|
-
} catch (error61) {
|
|
43607
|
-
if (!isMissingFile(error61)) {
|
|
43608
|
-
throw error61;
|
|
43609
|
-
}
|
|
43610
|
-
}
|
|
43611
|
-
}
|
|
43612
|
-
|
|
43613
|
-
// src/wallet-store.ts
|
|
43614
|
-
import {
|
|
43615
|
-
chmod as chmod2,
|
|
43616
|
-
lstat,
|
|
43617
|
-
mkdir as mkdir5,
|
|
43618
|
-
open as open5,
|
|
43619
|
-
readdir,
|
|
43620
|
-
readFile as readFile4,
|
|
43621
|
-
rename as rename3,
|
|
43622
|
-
stat as stat3,
|
|
43623
|
-
unlink as unlink3,
|
|
43624
|
-
writeFile as writeFile3
|
|
43625
|
-
} from "node:fs/promises";
|
|
43626
|
-
var agentLinkSchema = external_exports.object({
|
|
43627
|
-
apiBase: external_exports.string(),
|
|
43628
|
-
clientId: external_exports.string(),
|
|
43629
|
-
owner: external_exports.custom((value) => typeof value === "string" && value.startsWith("0x")),
|
|
43630
|
-
label: external_exports.string(),
|
|
43631
|
-
scopes: external_exports.array(external_exports.string()),
|
|
43632
|
-
linkedAt: external_exports.iso.datetime(),
|
|
43633
|
-
routerBaseUrl: external_exports.string().optional()
|
|
43634
|
-
});
|
|
43635
|
-
var ROUTER_TOPUP_TIERS = [1, 5, 20, 50];
|
|
43636
|
-
var routerRefillSchema = external_exports.object({
|
|
43637
|
-
belowUsd: external_exports.number().finite().min(0),
|
|
43638
|
-
tierUsd: external_exports.union([external_exports.literal(1), external_exports.literal(5), external_exports.literal(20), external_exports.literal(50)])
|
|
43639
|
-
});
|
|
43640
|
-
var ceilingSweepPendingSchema = external_exports.strictObject({
|
|
43641
|
-
owner: external_exports.string().regex(/^0x[0-9a-fA-F]{40}$/),
|
|
43642
|
-
amountAtomic: external_exports.string().regex(/^\d+$/),
|
|
43643
|
-
nonce: external_exports.string().regex(/^0x[0-9a-fA-F]{64}$/),
|
|
43644
|
-
createdAt: external_exports.iso.datetime(),
|
|
43645
|
-
status: external_exports.enum(["planned", "unknown"])
|
|
43646
|
-
});
|
|
43647
|
-
var walletEntrySchema = external_exports.object({
|
|
43648
|
-
createdAt: external_exports.iso.datetime(),
|
|
43649
|
-
label: external_exports.string().trim().min(1).max(80).optional(),
|
|
43650
|
-
spendCaps: spendCapsSchema,
|
|
43651
|
-
// On disk, null explicitly disables ceiling sweeps; an absent field is the
|
|
43652
|
-
// backwards-compatible 5 USDC default and is not written during reads.
|
|
43653
|
-
ceilingAtomic: external_exports.string().regex(/^\d+$/).nullable().optional(),
|
|
43654
|
-
ceilingSweepAttemptedAt: external_exports.iso.datetime().optional(),
|
|
43655
|
-
ceilingSweepPending: ceilingSweepPendingSchema.optional(),
|
|
43656
|
-
routerRefill: routerRefillSchema.optional(),
|
|
43657
|
-
link: agentLinkSchema.optional()
|
|
43658
|
-
});
|
|
43659
|
-
var walletRegistrySchema = external_exports.object({
|
|
43660
|
-
version: external_exports.literal(1),
|
|
43661
|
-
default: walletNameSchema.optional(),
|
|
43662
|
-
wallets: external_exports.record(walletNameSchema, walletEntrySchema)
|
|
43663
|
-
});
|
|
43664
|
-
async function spendCapsForWallet(store, name = DEFAULT_WALLET_NAME) {
|
|
43665
|
-
const entry = store.entry(name);
|
|
43666
|
-
if (entry) return { ...entry.spendCaps };
|
|
43667
|
-
return await readConfiguredSpendCaps(getVapiPaths(store.home).config);
|
|
43668
|
-
}
|
|
43669
|
-
async function readConfiguredSpendCaps(configPath) {
|
|
43670
|
-
try {
|
|
43671
|
-
const parsed = JSON.parse(await readFile4(configPath, "utf8"));
|
|
43672
|
-
const caps = typeof parsed === "object" && parsed !== null ? Reflect.get(parsed, "spendCaps") : void 0;
|
|
43673
|
-
const result = spendCapsSchema.safeParse(caps);
|
|
43674
|
-
if (result.success) return result.data;
|
|
43675
|
-
} catch {
|
|
43676
|
-
}
|
|
43677
|
-
return { ...DEFAULT_SPEND_CAPS };
|
|
43678
|
-
}
|
|
43679
|
-
|
|
43680
44497
|
// src/transfer.ts
|
|
43681
44498
|
var TransferError = class extends Error {
|
|
43682
44499
|
constructor(code, message, options = {}) {
|
|
@@ -43731,6 +44548,9 @@ async function transferBetweenAccounts(args) {
|
|
|
43731
44548
|
throw transferError("not_linked");
|
|
43732
44549
|
}
|
|
43733
44550
|
const fromAddress = getAddress(rawFromAddress);
|
|
44551
|
+
if (args.expectedFromAddress !== void 0 && (!isAddress(args.expectedFromAddress, { strict: false }) || !sameAddress(fromAddress, args.expectedFromAddress))) {
|
|
44552
|
+
throw transferError("account_address_mismatch");
|
|
44553
|
+
}
|
|
43734
44554
|
let siblingResult;
|
|
43735
44555
|
try {
|
|
43736
44556
|
siblingResult = await fetchSiblings({
|
|
@@ -43762,8 +44582,12 @@ async function transferBetweenAccounts(args) {
|
|
|
43762
44582
|
owner: siblingResult.owner,
|
|
43763
44583
|
siblings: siblingResult.siblings
|
|
43764
44584
|
});
|
|
44585
|
+
if (args.expectedToAddress !== void 0 && (!isAddress(args.expectedToAddress, { strict: false }) || !sameAddress(recipient.address, args.expectedToAddress))) {
|
|
44586
|
+
throw transferError("account_address_mismatch");
|
|
44587
|
+
}
|
|
43765
44588
|
const purpose = args.purpose ?? "send";
|
|
43766
|
-
|
|
44589
|
+
const isSweepTarget = recipient.toKind === "owner" || recipient.toKind === "account" && args.sweepParent !== void 0 && isAddress(args.sweepParent, { strict: false }) && getAddress(args.sweepParent) === getAddress(recipient.address);
|
|
44590
|
+
if (purpose === "sweep" && !isSweepTarget) {
|
|
43767
44591
|
throw transferError("sweep_not_owner");
|
|
43768
44592
|
}
|
|
43769
44593
|
const home = args.home ?? args.store.home;
|
|
@@ -43784,7 +44608,7 @@ async function transferBetweenAccounts(args) {
|
|
|
43784
44608
|
if (args.resume !== void 0) {
|
|
43785
44609
|
return await resumeTransfer(context, args.resume);
|
|
43786
44610
|
}
|
|
43787
|
-
const nonce = args.nonce ?? bytesToHex(
|
|
44611
|
+
const nonce = args.nonce ?? bytesToHex(randomBytes5(32));
|
|
43788
44612
|
if (!NONCE_PATTERN.test(nonce)) {
|
|
43789
44613
|
throw new TransferError(
|
|
43790
44614
|
"authorization_invalid",
|
|
@@ -43806,7 +44630,9 @@ async function transferBetweenAccounts(args) {
|
|
|
43806
44630
|
);
|
|
43807
44631
|
reservedOn = reservation.date;
|
|
43808
44632
|
} catch (error61) {
|
|
43809
|
-
if (error61 instanceof SpendCapError
|
|
44633
|
+
if (error61 instanceof SpendCapError && isTransferErrorCode(error61.code)) {
|
|
44634
|
+
throw transferError(error61.code);
|
|
44635
|
+
}
|
|
43810
44636
|
throw transferError("network_error");
|
|
43811
44637
|
}
|
|
43812
44638
|
}
|
|
@@ -44009,8 +44835,8 @@ async function postRelay(context, request, options) {
|
|
|
44009
44835
|
`wallet:${context.args.from}`,
|
|
44010
44836
|
async () => {
|
|
44011
44837
|
await context.args.store.reload();
|
|
44012
|
-
const
|
|
44013
|
-
if (
|
|
44838
|
+
const link2 = context.args.store.entry(context.args.from)?.link;
|
|
44839
|
+
if (link2 === void 0 || safeOrigin(link2.apiBase) !== safeOrigin(context.args.apiBase)) {
|
|
44014
44840
|
throw transferError("not_linked");
|
|
44015
44841
|
}
|
|
44016
44842
|
return await agentFetch(
|
|
@@ -44167,7 +44993,7 @@ async function findTransferReceipt(context, nonce) {
|
|
|
44167
44993
|
return [...receipts].reverse().find((candidate) => candidate.transfer?.nonce.toLowerCase() === nonce.toLowerCase());
|
|
44168
44994
|
}
|
|
44169
44995
|
async function withTransferLock(context, nonce, operation) {
|
|
44170
|
-
await
|
|
44996
|
+
await mkdir9(dirname6(context.receiptsPath), { recursive: true, mode: 448 });
|
|
44171
44997
|
const lockPath = `${context.receiptsPath}.${context.args.from}.${nonce.toLowerCase()}.transfer.lock`;
|
|
44172
44998
|
const requestTimeout = Math.max(0, context.args.timeoutMs ?? DEFAULT_TIMEOUT_MS);
|
|
44173
44999
|
const deadline = Date.now() + Math.max(5e3, requestTimeout + 5e3);
|
|
@@ -44175,7 +45001,7 @@ async function withTransferLock(context, nonce, operation) {
|
|
|
44175
45001
|
let handle;
|
|
44176
45002
|
while (!handle) {
|
|
44177
45003
|
try {
|
|
44178
|
-
handle = await
|
|
45004
|
+
handle = await open8(lockPath, "wx", 384);
|
|
44179
45005
|
} catch (error61) {
|
|
44180
45006
|
if (!(error61 instanceof Error && "code" in error61 && error61.code === "EEXIST")) throw error61;
|
|
44181
45007
|
await removeStaleTransferLock(lockPath, staleAfterMs);
|
|
@@ -44189,13 +45015,13 @@ async function withTransferLock(context, nonce, operation) {
|
|
|
44189
45015
|
return await operation();
|
|
44190
45016
|
} finally {
|
|
44191
45017
|
await handle.close();
|
|
44192
|
-
await
|
|
45018
|
+
await unlink6(lockPath).catch(() => void 0);
|
|
44193
45019
|
}
|
|
44194
45020
|
}
|
|
44195
45021
|
async function removeStaleTransferLock(path, staleAfterMs) {
|
|
44196
45022
|
try {
|
|
44197
|
-
const metadata = await
|
|
44198
|
-
if (Date.now() - metadata.mtimeMs > staleAfterMs) await
|
|
45023
|
+
const metadata = await stat7(path);
|
|
45024
|
+
if (Date.now() - metadata.mtimeMs > staleAfterMs) await unlink6(path);
|
|
44199
45025
|
} catch (error61) {
|
|
44200
45026
|
if (!(error61 instanceof Error && "code" in error61 && error61.code === "ENOENT")) throw error61;
|
|
44201
45027
|
}
|
|
@@ -44349,6 +45175,7 @@ function isTransferErrorCode(value) {
|
|
|
44349
45175
|
"resume_not_found",
|
|
44350
45176
|
"resume_mismatch",
|
|
44351
45177
|
"signer_mismatch",
|
|
45178
|
+
"account_address_mismatch",
|
|
44352
45179
|
"signing_failed"
|
|
44353
45180
|
].includes(value);
|
|
44354
45181
|
}
|
|
@@ -44360,7 +45187,7 @@ function transferError(code, options = {}) {
|
|
|
44360
45187
|
unsupported: "This vAPI server does not relay transfers yet. No money moved.",
|
|
44361
45188
|
recipient_not_allowed: "The recipient is not the owner or an active sibling account. No money moved.",
|
|
44362
45189
|
ambiguous_recipient: "The sibling account name is ambiguous. No money moved.",
|
|
44363
|
-
sweep_not_owner: "A sweep can only send funds to the owner. No money moved.",
|
|
45190
|
+
sweep_not_owner: "A sweep can only send funds to the owner or its treasury. No money moved.",
|
|
44364
45191
|
per_call_cap_exceeded: "The amount exceeds the sending account's per-call cap. No money moved.",
|
|
44365
45192
|
per_day_cap_exceeded: "The amount exceeds the sending account's remaining daily cap. No money moved.",
|
|
44366
45193
|
payer_mismatch: "The relay rejected the sending account. No money moved.",
|
|
@@ -44373,17 +45200,644 @@ function transferError(code, options = {}) {
|
|
|
44373
45200
|
resume_not_found: "No resumable transfer was found for that nonce. No money moved.",
|
|
44374
45201
|
resume_mismatch: "The resumed transfer does not match the original request. No money moved.",
|
|
44375
45202
|
signer_mismatch: "The unlocked signer does not match the sending account. No money moved.",
|
|
45203
|
+
account_address_mismatch: "The sending or recipient account no longer matches the address recorded by its movement. No money moved.",
|
|
44376
45204
|
signing_failed: "The transfer authorization could not be signed. No money moved."
|
|
44377
45205
|
};
|
|
44378
45206
|
return new TransferError(code, messages[code], options);
|
|
44379
45207
|
}
|
|
44380
45208
|
|
|
45209
|
+
// src/movement.ts
|
|
45210
|
+
var movementIdSchema = external_exports.string().regex(/^mv_[A-Za-z0-9_-]{8,128}$/);
|
|
45211
|
+
var nonceSchema = external_exports.string().regex(/^0x[0-9a-fA-F]{64}$/);
|
|
45212
|
+
var amountSchema = external_exports.string().regex(/^\d+\.\d{2}$/);
|
|
45213
|
+
var movementReasonSchema = external_exports.enum([
|
|
45214
|
+
"distribute",
|
|
45215
|
+
"allocate",
|
|
45216
|
+
"rebalance",
|
|
45217
|
+
"delegate",
|
|
45218
|
+
"sweep",
|
|
45219
|
+
"send"
|
|
45220
|
+
]);
|
|
45221
|
+
var movementPurposeSchema = external_exports.enum(["send", "sweep"]);
|
|
45222
|
+
var movementAddressSchema = external_exports.string().refine((value) => isAddress(value, { strict: false }));
|
|
45223
|
+
var movementLegSchema = external_exports.strictObject({
|
|
45224
|
+
from: walletNameSchema,
|
|
45225
|
+
to: external_exports.string().trim().min(1),
|
|
45226
|
+
fromAddress: movementAddressSchema.optional(),
|
|
45227
|
+
toAddress: movementAddressSchema.optional(),
|
|
45228
|
+
amountUsd: amountSchema,
|
|
45229
|
+
purpose: movementPurposeSchema,
|
|
45230
|
+
nonce: nonceSchema,
|
|
45231
|
+
status: external_exports.enum(["planned", "sent", "failed", "unknown", "cancelled"]),
|
|
45232
|
+
txHash: external_exports.string().regex(/^0x[0-9a-fA-F]+$/).optional(),
|
|
45233
|
+
reason: external_exports.string().min(1).optional(),
|
|
45234
|
+
retryable: external_exports.boolean().optional(),
|
|
45235
|
+
restored: external_exports.literal(true).optional(),
|
|
45236
|
+
addressBindingSource: external_exports.enum(["journal", "terminal-review"]).optional()
|
|
45237
|
+
});
|
|
45238
|
+
var movementSchema = external_exports.strictObject({
|
|
45239
|
+
v: external_exports.literal(2),
|
|
45240
|
+
id: movementIdSchema,
|
|
45241
|
+
reason: movementReasonSchema,
|
|
45242
|
+
from: walletNameSchema,
|
|
45243
|
+
// The only account a `sweep` leg may name besides the owner. Sweeps skip the
|
|
45244
|
+
// spend caps, so the parent comes from the plan, never from a leg's `to`.
|
|
45245
|
+
treasury: walletNameSchema.optional(),
|
|
45246
|
+
network: external_exports.enum([BASE_MAINNET_CAIP2, ARC_MAINNET_CAIP2]),
|
|
45247
|
+
createdAt: external_exports.iso.datetime(),
|
|
45248
|
+
legs: external_exports.array(movementLegSchema).min(1)
|
|
45249
|
+
});
|
|
45250
|
+
var legacyMovementSchema = external_exports.strictObject({
|
|
45251
|
+
v: external_exports.literal(1),
|
|
45252
|
+
id: movementIdSchema,
|
|
45253
|
+
reason: external_exports.literal("distribute"),
|
|
45254
|
+
from: walletNameSchema,
|
|
45255
|
+
network: external_exports.enum([BASE_MAINNET_CAIP2, ARC_MAINNET_CAIP2]),
|
|
45256
|
+
createdAt: external_exports.iso.datetime(),
|
|
45257
|
+
legs: external_exports.array(
|
|
45258
|
+
external_exports.strictObject({
|
|
45259
|
+
to: external_exports.string().trim().min(1),
|
|
45260
|
+
amountUsd: amountSchema,
|
|
45261
|
+
nonce: nonceSchema,
|
|
45262
|
+
status: external_exports.enum(["planned", "sent", "failed", "unknown"]),
|
|
45263
|
+
txHash: external_exports.string().regex(/^0x[0-9a-fA-F]+$/).optional(),
|
|
45264
|
+
reason: external_exports.string().min(1).optional(),
|
|
45265
|
+
retryable: external_exports.boolean().optional()
|
|
45266
|
+
})
|
|
45267
|
+
).min(1)
|
|
45268
|
+
});
|
|
45269
|
+
var MovementError = class extends Error {
|
|
45270
|
+
constructor(code, message, movementId) {
|
|
45271
|
+
super(message);
|
|
45272
|
+
this.code = code;
|
|
45273
|
+
this.movementId = movementId;
|
|
45274
|
+
this.name = "MovementError";
|
|
45275
|
+
}
|
|
45276
|
+
code;
|
|
45277
|
+
movementId;
|
|
45278
|
+
};
|
|
45279
|
+
async function listUnfinishedMovements(options) {
|
|
45280
|
+
const directory = movementsDirectory(options.home);
|
|
45281
|
+
let names;
|
|
45282
|
+
try {
|
|
45283
|
+
names = await readdir3(directory);
|
|
45284
|
+
} catch (error61) {
|
|
45285
|
+
if (isMissingFile(error61)) return [];
|
|
45286
|
+
throw error61;
|
|
45287
|
+
}
|
|
45288
|
+
const summaries = [];
|
|
45289
|
+
for (const name of names.filter((candidate) => candidate.endsWith(".json")).sort()) {
|
|
45290
|
+
const movement = parseMovement(JSON.parse(await readFile9(join5(directory, name), "utf8")));
|
|
45291
|
+
if (options.from !== void 0 && !movement.legs.some((leg) => leg.from === options.from)) {
|
|
45292
|
+
continue;
|
|
45293
|
+
}
|
|
45294
|
+
const pendingLegs = movement.legs.filter((leg) => leg.status === "planned").length;
|
|
45295
|
+
const unknownLegs = movement.legs.filter((leg) => leg.status === "unknown").length;
|
|
45296
|
+
if (!await movementHasOpenLeg(options.home, movement, options.from)) continue;
|
|
45297
|
+
summaries.push({
|
|
45298
|
+
id: movement.id,
|
|
45299
|
+
from: movement.from,
|
|
45300
|
+
network: movement.network,
|
|
45301
|
+
createdAt: movement.createdAt,
|
|
45302
|
+
pendingLegs,
|
|
45303
|
+
unknownLegs
|
|
45304
|
+
});
|
|
45305
|
+
}
|
|
45306
|
+
return summaries.sort(
|
|
45307
|
+
(left, right) => left.createdAt.localeCompare(right.createdAt) || left.id.localeCompare(right.id)
|
|
45308
|
+
);
|
|
45309
|
+
}
|
|
45310
|
+
async function movementHasOpenLeg(home, movement, from15) {
|
|
45311
|
+
for (const leg of movement.legs) {
|
|
45312
|
+
if (from15 !== void 0 && leg.from !== from15) continue;
|
|
45313
|
+
if (isOpenMovementLeg(leg, "unsigned")) return true;
|
|
45314
|
+
if (leg.status !== "failed") continue;
|
|
45315
|
+
const journal = await readTransferJournal(home, leg.from, leg.nonce);
|
|
45316
|
+
if (isOpenMovementLeg(leg, journal.signed ? "signed" : "unsigned")) return true;
|
|
45317
|
+
}
|
|
45318
|
+
return false;
|
|
45319
|
+
}
|
|
45320
|
+
async function readMovement(home, id) {
|
|
45321
|
+
const safeId = parseMovementId(id);
|
|
45322
|
+
try {
|
|
45323
|
+
return parseMovement(
|
|
45324
|
+
JSON.parse(await readFile9(join5(movementsDirectory(home), `${safeId}.json`), "utf8"))
|
|
45325
|
+
);
|
|
45326
|
+
} catch (error61) {
|
|
45327
|
+
if (isMissingFile(error61)) {
|
|
45328
|
+
throw new MovementError(
|
|
45329
|
+
"movement_not_found",
|
|
45330
|
+
`No movement named ${safeId} was found.`,
|
|
45331
|
+
safeId
|
|
45332
|
+
);
|
|
45333
|
+
}
|
|
45334
|
+
throw error61;
|
|
45335
|
+
}
|
|
45336
|
+
}
|
|
45337
|
+
async function reconcileUnresolvedMovementsForAccountLocked(home, account, deps) {
|
|
45338
|
+
for (const summary of await listUnfinishedMovements({ home, from: account })) {
|
|
45339
|
+
let movement = await readMovement(home, summary.id);
|
|
45340
|
+
try {
|
|
45341
|
+
for (let index2 = 0; index2 < movement.legs.length; index2 += 1) {
|
|
45342
|
+
const leg = movement.legs[index2];
|
|
45343
|
+
if (leg?.from !== account || leg.status !== "planned" && !(leg.status === "failed" && leg.retryable === true)) {
|
|
45344
|
+
continue;
|
|
45345
|
+
}
|
|
45346
|
+
const reconciled = await reconcileJournaledLeg(movement, index2, home);
|
|
45347
|
+
if (reconciled !== movement) {
|
|
45348
|
+
movement = reconciled;
|
|
45349
|
+
await writeMovement(home, movement);
|
|
45350
|
+
}
|
|
45351
|
+
}
|
|
45352
|
+
for (let index2 = 0; index2 < movement.legs.length; index2 += 1) {
|
|
45353
|
+
const leg = movement.legs[index2];
|
|
45354
|
+
if (leg?.from !== account || leg.status !== "unknown") continue;
|
|
45355
|
+
movement = await resolveUnknownLeg(movement, index2, home, deps);
|
|
45356
|
+
}
|
|
45357
|
+
for (let index2 = 0; index2 < movement.legs.length; index2 += 1) {
|
|
45358
|
+
const leg = movement.legs[index2];
|
|
45359
|
+
if (leg?.from !== account || leg.status !== "failed") continue;
|
|
45360
|
+
movement = await prepareFailedLeg(movement, index2, home, deps);
|
|
45361
|
+
}
|
|
45362
|
+
} catch {
|
|
45363
|
+
}
|
|
45364
|
+
if (await movementHasOpenLeg(home, movement, account)) return movement;
|
|
45365
|
+
}
|
|
45366
|
+
return void 0;
|
|
45367
|
+
}
|
|
45368
|
+
async function resolveUnknownLeg(movement, index2, home, deps) {
|
|
45369
|
+
const leg = movement.legs[index2];
|
|
45370
|
+
if (isExpiredRestoredReview(leg)) {
|
|
45371
|
+
if (deps.replaceExpiredRestored !== true) {
|
|
45372
|
+
await throwRestoredLegReview(movement, leg, deps);
|
|
45373
|
+
}
|
|
45374
|
+
const journal2 = await readTransferJournal(home, leg.from, leg.nonce);
|
|
45375
|
+
await releaseExpiredReservation(leg, journal2, home, deps);
|
|
45376
|
+
const bound = leg.fromAddress === void 0 || leg.toAddress === void 0 ? await bindLegAddresses(leg, deps) : leg;
|
|
45377
|
+
movement = replaceLeg(
|
|
45378
|
+
movement,
|
|
45379
|
+
index2,
|
|
45380
|
+
plannedLeg(bound, (deps.randomNonce ?? createMovementNonce)())
|
|
45381
|
+
);
|
|
45382
|
+
await writeMovement(home, movement);
|
|
45383
|
+
return movement;
|
|
45384
|
+
}
|
|
45385
|
+
try {
|
|
45386
|
+
const result = await callTransfer(movement, leg, deps, true);
|
|
45387
|
+
if (result.status === "sent") {
|
|
45388
|
+
movement = replaceLeg(movement, index2, sentLeg(leg, result.txHash));
|
|
45389
|
+
await writeMovement(home, movement);
|
|
45390
|
+
return movement;
|
|
45391
|
+
}
|
|
45392
|
+
} catch (error61) {
|
|
45393
|
+
if (error61 instanceof TransferError || error61 instanceof SweepParentUnknownError) {
|
|
45394
|
+
} else {
|
|
45395
|
+
await reconcileCrash(movement, index2, home);
|
|
45396
|
+
throw error61;
|
|
45397
|
+
}
|
|
45398
|
+
}
|
|
45399
|
+
const journal = await readTransferJournal(home, leg.from, leg.nonce);
|
|
45400
|
+
const state = await authorizationState(movement, leg, journal.validBefore, home, deps);
|
|
45401
|
+
if (state === "settled") {
|
|
45402
|
+
movement = replaceLeg(movement, index2, sentLeg(leg));
|
|
45403
|
+
} else if (state === "expired") {
|
|
45404
|
+
if (leg.restored === true && deps.replaceExpiredRestored !== true) {
|
|
45405
|
+
movement = replaceLeg(movement, index2, restoredExpiredReviewLeg(leg));
|
|
45406
|
+
await writeMovement(home, movement);
|
|
45407
|
+
await throwRestoredLegReview(movement, movement.legs[index2], deps);
|
|
45408
|
+
}
|
|
45409
|
+
await releaseExpiredReservation(leg, journal, home, deps);
|
|
45410
|
+
movement = replaceLeg(
|
|
45411
|
+
movement,
|
|
45412
|
+
index2,
|
|
45413
|
+
plannedLeg(leg, (deps.randomNonce ?? createMovementNonce)())
|
|
45414
|
+
);
|
|
45415
|
+
}
|
|
45416
|
+
await writeMovement(home, movement);
|
|
45417
|
+
return movement;
|
|
45418
|
+
}
|
|
45419
|
+
async function reconcileJournaledLeg(movement, index2, home) {
|
|
45420
|
+
const leg = movement.legs[index2];
|
|
45421
|
+
const journal = await readTransferJournal(home, leg.from, leg.nonce);
|
|
45422
|
+
if (journal.latest?.transfer?.status === "sent") {
|
|
45423
|
+
return replaceLeg(
|
|
45424
|
+
movement,
|
|
45425
|
+
index2,
|
|
45426
|
+
sentLeg(
|
|
45427
|
+
leg,
|
|
45428
|
+
journal.latest.transfer.txHash === null ? void 0 : journal.latest.transfer.txHash
|
|
45429
|
+
)
|
|
45430
|
+
);
|
|
45431
|
+
}
|
|
45432
|
+
return journal.signed ? replaceLeg(movement, index2, unknownLeg(leg)) : movement;
|
|
45433
|
+
}
|
|
45434
|
+
async function prepareFailedLeg(movement, index2, home, deps) {
|
|
45435
|
+
const leg = movement.legs[index2];
|
|
45436
|
+
if (leg.retryable === true) {
|
|
45437
|
+
const reconciled = await reconcileJournaledLeg(movement, index2, home);
|
|
45438
|
+
if (reconciled !== movement) {
|
|
45439
|
+
await writeMovement(home, reconciled);
|
|
45440
|
+
return reconciled;
|
|
45441
|
+
}
|
|
45442
|
+
return replaceLeg(movement, index2, plannedLeg(leg, leg.nonce, true));
|
|
45443
|
+
}
|
|
45444
|
+
const journal = await readTransferJournal(home, leg.from, leg.nonce);
|
|
45445
|
+
if (!journal.signed) return movement;
|
|
45446
|
+
const state = await authorizationState(movement, leg, journal.validBefore, home, deps);
|
|
45447
|
+
if (state === "settled") {
|
|
45448
|
+
movement = replaceLeg(movement, index2, sentLeg(leg));
|
|
45449
|
+
} else if (state === "expired") {
|
|
45450
|
+
if (leg.restored === true && deps.replaceExpiredRestored !== true) {
|
|
45451
|
+
movement = replaceLeg(movement, index2, restoredExpiredReviewLeg(leg));
|
|
45452
|
+
await writeMovement(home, movement);
|
|
45453
|
+
await throwRestoredLegReview(movement, movement.legs[index2], deps);
|
|
45454
|
+
}
|
|
45455
|
+
await releaseExpiredReservation(leg, journal, home, deps);
|
|
45456
|
+
movement = replaceLeg(
|
|
45457
|
+
movement,
|
|
45458
|
+
index2,
|
|
45459
|
+
plannedLeg(leg, (deps.randomNonce ?? createMovementNonce)())
|
|
45460
|
+
);
|
|
45461
|
+
}
|
|
45462
|
+
await writeMovement(home, movement);
|
|
45463
|
+
return movement;
|
|
45464
|
+
}
|
|
45465
|
+
async function callTransfer(movement, leg, deps, resume) {
|
|
45466
|
+
if (leg.fromAddress !== void 0 && leg.toAddress !== void 0) {
|
|
45467
|
+
await assertLegAddressBinding(movement, leg, deps);
|
|
45468
|
+
} else if (!resume) {
|
|
45469
|
+
throw movementAddressUnboundError(movement);
|
|
45470
|
+
}
|
|
45471
|
+
const transfer = deps.transfer ?? transferBetweenAccounts;
|
|
45472
|
+
const sweepParent = await readSweepParent(movement, leg, deps);
|
|
45473
|
+
return await transfer({
|
|
45474
|
+
store: deps.store,
|
|
45475
|
+
secrets: deps.secrets,
|
|
45476
|
+
apiBase: deps.apiBase,
|
|
45477
|
+
from: leg.from,
|
|
45478
|
+
to: leg.to,
|
|
45479
|
+
amountUsd: leg.amountUsd,
|
|
45480
|
+
network: movement.network,
|
|
45481
|
+
purpose: leg.purpose,
|
|
45482
|
+
expectedFromAddress: leg.fromAddress,
|
|
45483
|
+
expectedToAddress: leg.toAddress,
|
|
45484
|
+
...sweepParent === void 0 ? {} : { sweepParent },
|
|
45485
|
+
home: deps.home ?? deps.store.home,
|
|
45486
|
+
...resume ? { resume: leg.nonce } : { nonce: leg.nonce },
|
|
45487
|
+
...deps.unlock === void 0 ? {} : { unlock: deps.unlock },
|
|
45488
|
+
...deps.fetchImpl === void 0 ? {} : { fetchImpl: deps.fetchImpl },
|
|
45489
|
+
...deps.now === void 0 ? {} : { now: deps.now },
|
|
45490
|
+
...deps.timeoutMs === void 0 ? {} : { timeoutMs: deps.timeoutMs },
|
|
45491
|
+
...deps.ledgerPath === void 0 ? {} : { ledgerPath: deps.ledgerPath }
|
|
45492
|
+
});
|
|
45493
|
+
}
|
|
45494
|
+
async function reconcileCrash(movement, index2, home) {
|
|
45495
|
+
const leg = movement.legs[index2];
|
|
45496
|
+
const journal = await readTransferJournal(home, leg.from, leg.nonce);
|
|
45497
|
+
let reconciled = leg;
|
|
45498
|
+
if (journal.latest?.transfer?.status === "sent") {
|
|
45499
|
+
reconciled = sentLeg(
|
|
45500
|
+
leg,
|
|
45501
|
+
journal.latest.transfer.txHash === null ? void 0 : journal.latest.transfer.txHash
|
|
45502
|
+
);
|
|
45503
|
+
} else if (journal.latest?.transfer?.status === "failed") {
|
|
45504
|
+
reconciled = journalFailedLeg(leg, journal.latest.error?.code ?? "transfer_failed");
|
|
45505
|
+
} else if (journal.signed) {
|
|
45506
|
+
reconciled = unknownLeg(leg);
|
|
45507
|
+
}
|
|
45508
|
+
await writeMovement(home, replaceLeg(movement, index2, reconciled));
|
|
45509
|
+
}
|
|
45510
|
+
async function authorizationState(movement, leg, validBefore, home, deps) {
|
|
45511
|
+
if (deps.authorizationState !== void 0) {
|
|
45512
|
+
return await deps.authorizationState({
|
|
45513
|
+
movement,
|
|
45514
|
+
leg,
|
|
45515
|
+
...validBefore === void 0 ? {} : { validBefore }
|
|
45516
|
+
});
|
|
45517
|
+
}
|
|
45518
|
+
const journal = await readTransferJournal(home, leg.from, leg.nonce);
|
|
45519
|
+
const receipt = journal.request;
|
|
45520
|
+
const request = receipt?.transfer?.request;
|
|
45521
|
+
if (receipt === void 0 || request === void 0) {
|
|
45522
|
+
throw new MovementError(
|
|
45523
|
+
"invalid_movement",
|
|
45524
|
+
`Movement ${movement.id} has no journaled authorization for nonce ${leg.nonce}.`,
|
|
45525
|
+
movement.id
|
|
45526
|
+
);
|
|
45527
|
+
}
|
|
45528
|
+
const config2 = await loadConfig(getVapiPaths(home).config);
|
|
45529
|
+
const settlementReceipt = {
|
|
45530
|
+
...receipt,
|
|
45531
|
+
authorization: {
|
|
45532
|
+
from: request.authorization.from,
|
|
45533
|
+
nonce: request.authorization.nonce,
|
|
45534
|
+
validBefore: request.authorization.validBefore
|
|
45535
|
+
}
|
|
45536
|
+
};
|
|
45537
|
+
return (await checkReceiptSettlement(settlementReceipt, config2, {
|
|
45538
|
+
...deps.fetchImpl === void 0 ? {} : { fetchImpl: deps.fetchImpl }
|
|
45539
|
+
})).state;
|
|
45540
|
+
}
|
|
45541
|
+
async function releaseExpiredReservation(leg, journal, home, deps) {
|
|
45542
|
+
if (journal.reservedOn === void 0) return;
|
|
45543
|
+
await releaseSpend(parseUsdCents(leg.amountUsd) * 10000n, {
|
|
45544
|
+
reservedOn: journal.reservedOn,
|
|
45545
|
+
ledgerPath: deps.ledgerPath ?? getVapiPaths(home).ledger,
|
|
45546
|
+
now: new Date((deps.now ?? Date.now)()),
|
|
45547
|
+
wallet: leg.from,
|
|
45548
|
+
reservationId: leg.nonce.toLowerCase()
|
|
45549
|
+
});
|
|
45550
|
+
}
|
|
45551
|
+
function sentLeg(leg, txHash) {
|
|
45552
|
+
return {
|
|
45553
|
+
from: leg.from,
|
|
45554
|
+
to: leg.to,
|
|
45555
|
+
...legAddressBinding(leg),
|
|
45556
|
+
amountUsd: leg.amountUsd,
|
|
45557
|
+
purpose: leg.purpose,
|
|
45558
|
+
nonce: leg.nonce,
|
|
45559
|
+
status: "sent",
|
|
45560
|
+
...restoredMarker(leg),
|
|
45561
|
+
...txHash === void 0 || txHash === null ? {} : { txHash }
|
|
45562
|
+
};
|
|
45563
|
+
}
|
|
45564
|
+
function unknownLeg(leg) {
|
|
45565
|
+
return {
|
|
45566
|
+
from: leg.from,
|
|
45567
|
+
to: leg.to,
|
|
45568
|
+
...legAddressBinding(leg),
|
|
45569
|
+
amountUsd: leg.amountUsd,
|
|
45570
|
+
purpose: leg.purpose,
|
|
45571
|
+
nonce: leg.nonce,
|
|
45572
|
+
status: "unknown",
|
|
45573
|
+
...restoredMarker(leg)
|
|
45574
|
+
};
|
|
45575
|
+
}
|
|
45576
|
+
function journalFailedLeg(leg, reason) {
|
|
45577
|
+
return {
|
|
45578
|
+
from: leg.from,
|
|
45579
|
+
to: leg.to,
|
|
45580
|
+
...legAddressBinding(leg),
|
|
45581
|
+
amountUsd: leg.amountUsd,
|
|
45582
|
+
purpose: leg.purpose,
|
|
45583
|
+
nonce: leg.nonce,
|
|
45584
|
+
status: "failed",
|
|
45585
|
+
reason,
|
|
45586
|
+
...restoredMarker(leg)
|
|
45587
|
+
};
|
|
45588
|
+
}
|
|
45589
|
+
function plannedLeg(leg, nonce, preserveRestored = false) {
|
|
45590
|
+
return {
|
|
45591
|
+
from: leg.from,
|
|
45592
|
+
to: leg.to,
|
|
45593
|
+
...legAddressBinding(leg),
|
|
45594
|
+
amountUsd: leg.amountUsd,
|
|
45595
|
+
purpose: leg.purpose,
|
|
45596
|
+
nonce,
|
|
45597
|
+
status: "planned",
|
|
45598
|
+
...preserveRestored ? restoredMarker(leg) : {}
|
|
45599
|
+
};
|
|
45600
|
+
}
|
|
45601
|
+
function restoredExpiredReviewLeg(leg) {
|
|
45602
|
+
return {
|
|
45603
|
+
from: leg.from,
|
|
45604
|
+
to: leg.to,
|
|
45605
|
+
...legAddressBinding(leg),
|
|
45606
|
+
amountUsd: leg.amountUsd,
|
|
45607
|
+
purpose: leg.purpose,
|
|
45608
|
+
nonce: leg.nonce,
|
|
45609
|
+
status: "unknown",
|
|
45610
|
+
reason: "restored_nonce_expired",
|
|
45611
|
+
restored: true
|
|
45612
|
+
};
|
|
45613
|
+
}
|
|
45614
|
+
function restoredMarker(leg) {
|
|
45615
|
+
return leg.restored === true ? { restored: true } : {};
|
|
45616
|
+
}
|
|
45617
|
+
function legAddressBinding(leg) {
|
|
45618
|
+
return {
|
|
45619
|
+
...leg.fromAddress === void 0 ? {} : { fromAddress: leg.fromAddress },
|
|
45620
|
+
...leg.toAddress === void 0 ? {} : { toAddress: leg.toAddress },
|
|
45621
|
+
...leg.addressBindingSource === void 0 ? {} : { addressBindingSource: leg.addressBindingSource }
|
|
45622
|
+
};
|
|
45623
|
+
}
|
|
45624
|
+
function isExpiredRestoredReview(leg) {
|
|
45625
|
+
return leg.restored === true && leg.reason === "restored_nonce_expired";
|
|
45626
|
+
}
|
|
45627
|
+
async function throwRestoredLegReview(movement, leg, deps, kind = "expired") {
|
|
45628
|
+
const address2 = await (deps.addressReader === void 0 ? deps.store.readAddress(leg.from) : deps.addressReader(leg.from));
|
|
45629
|
+
const explorer = address2 === void 0 ? void 0 : explorerAddressUrl(movement.network, address2);
|
|
45630
|
+
const addressHint = address2 ?? `account ${leg.from}`;
|
|
45631
|
+
const explorerHint = explorer === void 0 ? "a block explorer" : explorer;
|
|
45632
|
+
throw new MovementError(
|
|
45633
|
+
"restored_leg_review",
|
|
45634
|
+
`Movement ${movement.id} restored a ${leg.amountUsd} USDC transfer from ${leg.from} to ${leg.to}, ${kind === "expired" ? `but its saved authorization ${leg.nonce} has expired` : "that the backup recorded before it was signed"}. The original device may already have paid it with a later nonce, so nothing was signed. Check the balance with vapi balance --account ${leg.from}, then inspect ${addressHint} on ${explorerHint}. If the transfer was not paid, continue deliberately in a terminal with vapi accounts distribute --resume ${movement.id} --replace-expired-restored. This override is unavailable through MCP.`,
|
|
45635
|
+
movement.id
|
|
45636
|
+
);
|
|
45637
|
+
}
|
|
45638
|
+
function replaceLeg(movement, index2, leg) {
|
|
45639
|
+
const legs = [...movement.legs];
|
|
45640
|
+
legs[index2] = movementLegSchema.parse(leg);
|
|
45641
|
+
return movementSchema.parse({ ...movement, legs });
|
|
45642
|
+
}
|
|
45643
|
+
function parseMovement(value) {
|
|
45644
|
+
const current = movementSchema.safeParse(value);
|
|
45645
|
+
if (current.success) return current.data;
|
|
45646
|
+
const legacy = legacyMovementSchema.parse(value);
|
|
45647
|
+
return movementSchema.parse({
|
|
45648
|
+
...legacy,
|
|
45649
|
+
v: 2,
|
|
45650
|
+
legs: legacy.legs.map((leg) => ({
|
|
45651
|
+
...leg,
|
|
45652
|
+
from: legacy.from,
|
|
45653
|
+
purpose: "send"
|
|
45654
|
+
}))
|
|
45655
|
+
});
|
|
45656
|
+
}
|
|
45657
|
+
async function bindLegAddresses(leg, deps) {
|
|
45658
|
+
const fromAddress = await resolveAccountAddress(leg.from, deps);
|
|
45659
|
+
if (fromAddress === void 0) {
|
|
45660
|
+
throw new MovementError(
|
|
45661
|
+
"invalid_movement",
|
|
45662
|
+
`Cannot plan a movement from ${leg.from}: its local address is unavailable. Restore that account, then retry.`
|
|
45663
|
+
);
|
|
45664
|
+
}
|
|
45665
|
+
const toAddress = await resolveRecipientAddress(leg.from, leg.to, deps);
|
|
45666
|
+
if (toAddress === void 0) {
|
|
45667
|
+
throw new MovementError(
|
|
45668
|
+
"invalid_movement",
|
|
45669
|
+
`Cannot plan a movement to ${leg.to}: its address is unavailable. Restore or relink that account, then retry.`
|
|
45670
|
+
);
|
|
45671
|
+
}
|
|
45672
|
+
return { ...leg, fromAddress, toAddress };
|
|
45673
|
+
}
|
|
45674
|
+
async function assertLegAddressBinding(movement, leg, deps) {
|
|
45675
|
+
if (leg.fromAddress === void 0 || leg.toAddress === void 0) {
|
|
45676
|
+
throw movementAddressUnboundError(movement);
|
|
45677
|
+
}
|
|
45678
|
+
const fromAddress = await resolveAccountAddress(leg.from, deps);
|
|
45679
|
+
if (fromAddress === void 0 || !sameAddress2(fromAddress, leg.fromAddress)) {
|
|
45680
|
+
throw new MovementError(
|
|
45681
|
+
"account_address_mismatch",
|
|
45682
|
+
`Movement ${movement.id} bound sender ${leg.from} to ${leg.fromAddress}, but that name now resolves differently. Restore ${leg.from} to the recorded address before resuming, or wait for any signed authorization to expire and run vapi accounts distribute --cancel ${movement.id}.`,
|
|
45683
|
+
movement.id
|
|
45684
|
+
);
|
|
45685
|
+
}
|
|
45686
|
+
const toAddress = await resolveRecipientAddress(leg.from, leg.to, deps);
|
|
45687
|
+
if (toAddress === void 0 || !sameAddress2(toAddress, leg.toAddress)) {
|
|
45688
|
+
throw new MovementError(
|
|
45689
|
+
"account_address_mismatch",
|
|
45690
|
+
`Movement ${movement.id} bound recipient ${leg.to} to ${leg.toAddress}, but that name now resolves differently. Restore or relink ${leg.to} to the recorded address before resuming, or wait for any signed authorization to expire and run vapi accounts distribute --cancel ${movement.id}.`,
|
|
45691
|
+
movement.id
|
|
45692
|
+
);
|
|
45693
|
+
}
|
|
45694
|
+
}
|
|
45695
|
+
function movementAddressUnboundError(movement) {
|
|
45696
|
+
return new MovementError(
|
|
45697
|
+
"movement_address_unbound",
|
|
45698
|
+
`Movement ${movement.id} predates address binding, so vAPI cannot safely sign its planned leg. Check balances and the saved nonce, then create a new movement deliberately if no authorization can settle.`,
|
|
45699
|
+
movement.id
|
|
45700
|
+
);
|
|
45701
|
+
}
|
|
45702
|
+
async function resolveAccountAddress(account, deps) {
|
|
45703
|
+
const raw = await (deps.addressReader ?? ((name) => deps.store.readAddress(name)))(account);
|
|
45704
|
+
return raw !== void 0 && isAddress(raw, { strict: false }) ? getAddress(raw) : void 0;
|
|
45705
|
+
}
|
|
45706
|
+
async function resolveRecipientAddress(from15, recipient, deps) {
|
|
45707
|
+
const requested = recipient.trim();
|
|
45708
|
+
if (isAddress(requested, { strict: false })) return getAddress(requested);
|
|
45709
|
+
if (requested === "owner") {
|
|
45710
|
+
const owner = deps.store.entry(from15)?.link?.owner;
|
|
45711
|
+
if (owner !== void 0 && isAddress(owner, { strict: false })) return getAddress(owner);
|
|
45712
|
+
} else {
|
|
45713
|
+
const account = walletNameSchema.safeParse(requested);
|
|
45714
|
+
if (account.success) {
|
|
45715
|
+
const local = await resolveAccountAddress(account.data, deps);
|
|
45716
|
+
if (local !== void 0) return local;
|
|
45717
|
+
}
|
|
45718
|
+
}
|
|
45719
|
+
const resolved = await deps.recipientAddressReader?.(from15, requested);
|
|
45720
|
+
return resolved !== void 0 && isAddress(resolved, { strict: false }) ? getAddress(resolved) : void 0;
|
|
45721
|
+
}
|
|
45722
|
+
function sameAddress2(left, right) {
|
|
45723
|
+
return isAddress(left, { strict: false }) && isAddress(right, { strict: false }) && getAddress(left) === getAddress(right);
|
|
45724
|
+
}
|
|
45725
|
+
var SweepParentUnknownError = class extends Error {
|
|
45726
|
+
};
|
|
45727
|
+
function isOwnerSweep(to) {
|
|
45728
|
+
return to.trim() === "owner" || isAddress(to, { strict: false });
|
|
45729
|
+
}
|
|
45730
|
+
async function readSweepParent(movement, leg, deps) {
|
|
45731
|
+
if (leg.purpose !== "sweep" || isOwnerSweep(leg.to)) return void 0;
|
|
45732
|
+
if (movement.treasury === void 0 || leg.to !== movement.treasury) {
|
|
45733
|
+
throw new SweepParentUnknownError();
|
|
45734
|
+
}
|
|
45735
|
+
const account = walletNameSchema.safeParse(leg.to);
|
|
45736
|
+
if (!account.success) throw new SweepParentUnknownError();
|
|
45737
|
+
const address2 = deps.addressReader === void 0 ? await deps.store.readAddress(account.data) : await deps.addressReader(account.data);
|
|
45738
|
+
if (address2 === void 0 || !isAddress(address2, { strict: false })) {
|
|
45739
|
+
throw new SweepParentUnknownError();
|
|
45740
|
+
}
|
|
45741
|
+
return address2;
|
|
45742
|
+
}
|
|
45743
|
+
function parseMovementId(value) {
|
|
45744
|
+
const parsed = movementIdSchema.safeParse(value);
|
|
45745
|
+
if (!parsed.success) throw new MovementError("invalid_movement", "The movement id is invalid.");
|
|
45746
|
+
return parsed.data;
|
|
45747
|
+
}
|
|
45748
|
+
function movementsDirectory(home) {
|
|
45749
|
+
return join5(home, "movements");
|
|
45750
|
+
}
|
|
45751
|
+
function createMovementNonce() {
|
|
45752
|
+
return bytesToHex(randomBytes6(32));
|
|
45753
|
+
}
|
|
45754
|
+
async function writeMovement(home, movement) {
|
|
45755
|
+
const parsed = movementSchema.parse(movement);
|
|
45756
|
+
const directory = movementsDirectory(home);
|
|
45757
|
+
await mkdir10(directory, { recursive: true, mode: 448 });
|
|
45758
|
+
await chmod3(directory, 448);
|
|
45759
|
+
const path = join5(directory, `${parsed.id}.json`);
|
|
45760
|
+
const temporary = join5(
|
|
45761
|
+
directory,
|
|
45762
|
+
`.${parsed.id}.${process.pid}.${randomBytes6(6).toString("hex")}.tmp`
|
|
45763
|
+
);
|
|
45764
|
+
let handle;
|
|
45765
|
+
try {
|
|
45766
|
+
handle = await open9(temporary, "wx", 384);
|
|
45767
|
+
await handle.writeFile(serializeMovement(parsed), "utf8");
|
|
45768
|
+
await handle.sync();
|
|
45769
|
+
await handle.close();
|
|
45770
|
+
handle = void 0;
|
|
45771
|
+
await rename6(temporary, path);
|
|
45772
|
+
const directoryHandle = await open9(directory, "r");
|
|
45773
|
+
try {
|
|
45774
|
+
await directoryHandle.sync();
|
|
45775
|
+
} finally {
|
|
45776
|
+
await directoryHandle.close();
|
|
45777
|
+
}
|
|
45778
|
+
} catch (error61) {
|
|
45779
|
+
await handle?.close().catch(() => void 0);
|
|
45780
|
+
await unlink7(temporary).catch(() => void 0);
|
|
45781
|
+
throw error61;
|
|
45782
|
+
}
|
|
45783
|
+
}
|
|
45784
|
+
function serializeMovement(movement) {
|
|
45785
|
+
return `${JSON.stringify(movementSchema.parse(movement), null, 2)}
|
|
45786
|
+
`;
|
|
45787
|
+
}
|
|
45788
|
+
|
|
45789
|
+
// src/sweep.ts
|
|
45790
|
+
var ERC20_ABI = [
|
|
45791
|
+
{
|
|
45792
|
+
type: "function",
|
|
45793
|
+
name: "balanceOf",
|
|
45794
|
+
stateMutability: "view",
|
|
45795
|
+
inputs: [{ name: "account", type: "address" }],
|
|
45796
|
+
outputs: [{ name: "balance", type: "uint256" }]
|
|
45797
|
+
},
|
|
45798
|
+
{
|
|
45799
|
+
type: "function",
|
|
45800
|
+
name: "transfer",
|
|
45801
|
+
stateMutability: "nonpayable",
|
|
45802
|
+
inputs: [
|
|
45803
|
+
{ name: "to", type: "address" },
|
|
45804
|
+
{ name: "amount", type: "uint256" }
|
|
45805
|
+
],
|
|
45806
|
+
outputs: [{ name: "success", type: "bool" }]
|
|
45807
|
+
}
|
|
45808
|
+
];
|
|
45809
|
+
async function readUsdcBalance(args) {
|
|
45810
|
+
if (isSolanaNetwork(args.network)) {
|
|
45811
|
+
return await readSolanaUsdcBalance({
|
|
45812
|
+
network: args.network,
|
|
45813
|
+
configured: args.configured,
|
|
45814
|
+
address: args.address,
|
|
45815
|
+
allowPrivateNetwork: args.allowPrivateNetwork,
|
|
45816
|
+
...args.fetchImpl ? { fetchImpl: args.fetchImpl } : {},
|
|
45817
|
+
...args.lookup ? { lookup: args.lookup } : {}
|
|
45818
|
+
});
|
|
45819
|
+
}
|
|
45820
|
+
const client = createNetworkPublicClient(args.network, args.configured, {
|
|
45821
|
+
allowPrivateNetwork: args.allowPrivateNetwork,
|
|
45822
|
+
...args.fetchImpl ? { fetch: args.fetchImpl } : {},
|
|
45823
|
+
...args.lookup ? { lookup: args.lookup } : {}
|
|
45824
|
+
});
|
|
45825
|
+
return await client.readContract({
|
|
45826
|
+
address: getAddress(
|
|
45827
|
+
usesUsdcGas(args.network) ? getNetworkDefinition(args.network).usdc : args.configured.usdc
|
|
45828
|
+
),
|
|
45829
|
+
abi: ERC20_ABI,
|
|
45830
|
+
functionName: "balanceOf",
|
|
45831
|
+
args: [getAddress(args.address)]
|
|
45832
|
+
});
|
|
45833
|
+
}
|
|
45834
|
+
|
|
44381
45835
|
// src/ceiling.ts
|
|
44382
45836
|
var CEILING_SWEEP_MINIMUM_ATOMIC = 100000n;
|
|
44383
45837
|
var CEILING_SWEEP_GUARD_MS = 10 * 60 * 1e3;
|
|
44384
45838
|
var CEILING_SWEEP_TIMEOUT_MS = 1e4;
|
|
44385
45839
|
function sweepTarget(account) {
|
|
44386
|
-
return account.owner;
|
|
45840
|
+
return account.parent?.address ?? account.owner;
|
|
44387
45841
|
}
|
|
44388
45842
|
async function sweepAboveCeiling(args) {
|
|
44389
45843
|
try {
|
|
@@ -44410,6 +45864,25 @@ async function sweepAboveCeilingLocked(args) {
|
|
|
44410
45864
|
if (caps.effectiveCeilingAtomic === null) {
|
|
44411
45865
|
return skipped(args.account, "ceiling_off");
|
|
44412
45866
|
}
|
|
45867
|
+
const unresolvedMovement = await reconcileUnresolvedMovementsForAccountLocked(
|
|
45868
|
+
auditHome,
|
|
45869
|
+
args.account,
|
|
45870
|
+
{
|
|
45871
|
+
store: args.store,
|
|
45872
|
+
secrets: args.secrets,
|
|
45873
|
+
apiBase: args.apiBase,
|
|
45874
|
+
home: auditHome,
|
|
45875
|
+
...args.transfer === void 0 ? {} : { transfer: args.transfer },
|
|
45876
|
+
...args.addressReader === void 0 ? {} : { addressReader: args.addressReader },
|
|
45877
|
+
...args.fetchImpl === void 0 ? {} : { fetchImpl: args.fetchImpl },
|
|
45878
|
+
...args.now === void 0 ? {} : { now: args.now },
|
|
45879
|
+
...args.timeoutMs === void 0 ? {} : { timeoutMs: args.timeoutMs }
|
|
45880
|
+
}
|
|
45881
|
+
);
|
|
45882
|
+
if (unresolvedMovement !== void 0) {
|
|
45883
|
+
await auditFailure(auditHome, args.account, void 0, "unfinished_movement", now);
|
|
45884
|
+
return skipped(args.account, "unfinished_movement");
|
|
45885
|
+
}
|
|
44413
45886
|
const rawAddress = await (args.addressReader ?? ((account) => args.store.readAddress(account)))(
|
|
44414
45887
|
args.account
|
|
44415
45888
|
);
|
|
@@ -44436,12 +45909,12 @@ async function sweepAboveCeilingLocked(args) {
|
|
|
44436
45909
|
}
|
|
44437
45910
|
throw error61;
|
|
44438
45911
|
}
|
|
44439
|
-
const self = siblingResult.siblings.find((candidate) => candidate.self) ?? siblingResult.siblings.find((candidate) =>
|
|
45912
|
+
const self = siblingResult.siblings.find((candidate) => candidate.self) ?? siblingResult.siblings.find((candidate) => sameAddress3(candidate.address, address2));
|
|
44440
45913
|
if (self?.status === "revoked") return skipped(args.account, "revoked");
|
|
44441
45914
|
if (siblingResult.owner === null) return skipped(args.account, "no_known_owner");
|
|
44442
45915
|
const serverOwner = siblingResult.owner;
|
|
44443
45916
|
const linkedOwner = isAddress(entry.link.owner, { strict: false }) ? getAddress(entry.link.owner) : void 0;
|
|
44444
|
-
if (linkedOwner === void 0 || !
|
|
45917
|
+
if (linkedOwner === void 0 || !sameAddress3(linkedOwner, serverOwner)) {
|
|
44445
45918
|
safeWarn(
|
|
44446
45919
|
args.warn,
|
|
44447
45920
|
`Refusing to sweep ${args.account}: its stored owner differs from the linked server owner.`
|
|
@@ -44469,12 +45942,30 @@ async function sweepAboveCeilingLocked(args) {
|
|
|
44469
45942
|
CEILING_SWEEP_GUARD_MS
|
|
44470
45943
|
);
|
|
44471
45944
|
if (!claimed) return skipped(args.account, "throttled");
|
|
45945
|
+
if (pending !== void 0) {
|
|
45946
|
+
if (!sameAddress3(pending.owner, owner)) {
|
|
45947
|
+
return {
|
|
45948
|
+
account: args.account,
|
|
45949
|
+
status: "unknown",
|
|
45950
|
+
reason: "pending_owner_changed",
|
|
45951
|
+
amountUsd: formatAtomicUsd(BigInt(pending.amountAtomic))
|
|
45952
|
+
};
|
|
45953
|
+
}
|
|
45954
|
+
}
|
|
45955
|
+
const parent = await args.resolveParent?.(args.account);
|
|
45956
|
+
const target = sweepTarget({
|
|
45957
|
+
account: args.account,
|
|
45958
|
+
owner,
|
|
45959
|
+
...parent === void 0 ? {} : { parent }
|
|
45960
|
+
});
|
|
44472
45961
|
if (pending !== void 0) {
|
|
44473
45962
|
const reconciled = await executePendingSweep({
|
|
44474
45963
|
args,
|
|
44475
45964
|
pending,
|
|
44476
45965
|
address: address2,
|
|
44477
45966
|
owner,
|
|
45967
|
+
target,
|
|
45968
|
+
sweepParent: parent?.address,
|
|
44478
45969
|
readBalance: readBalance2
|
|
44479
45970
|
});
|
|
44480
45971
|
if (reconciled !== void 0) return reconciled;
|
|
@@ -44482,7 +45973,7 @@ async function sweepAboveCeilingLocked(args) {
|
|
|
44482
45973
|
}
|
|
44483
45974
|
await args.store.reload();
|
|
44484
45975
|
const currentEntry = args.store.entry(args.account);
|
|
44485
|
-
if (currentEntry?.link === void 0 || !sameOrigin(currentEntry.link.apiBase, args.apiBase) || !
|
|
45976
|
+
if (currentEntry?.link === void 0 || !sameOrigin(currentEntry.link.apiBase, args.apiBase) || !sameAddress3(currentEntry.link.owner, owner)) {
|
|
44486
45977
|
return skipped(args.account, "not_linked");
|
|
44487
45978
|
}
|
|
44488
45979
|
const currentCeiling = args.store.ceilingCaps(args.account).effectiveCeilingAtomic;
|
|
@@ -44498,8 +45989,9 @@ async function sweepAboveCeilingLocked(args) {
|
|
|
44498
45989
|
args.signal?.throwIfAborted();
|
|
44499
45990
|
pending = {
|
|
44500
45991
|
owner,
|
|
45992
|
+
target,
|
|
44501
45993
|
amountAtomic: amountAtomic.toString(),
|
|
44502
|
-
nonce: (args.randomNonce ?? (() => bytesToHex(
|
|
45994
|
+
nonce: (args.randomNonce ?? (() => bytesToHex(randomBytes7(32))))(),
|
|
44503
45995
|
createdAt: new Date(now()).toISOString(),
|
|
44504
45996
|
status: "planned"
|
|
44505
45997
|
};
|
|
@@ -44513,6 +46005,8 @@ async function sweepAboveCeilingLocked(args) {
|
|
|
44513
46005
|
pending: stored,
|
|
44514
46006
|
address: address2,
|
|
44515
46007
|
owner,
|
|
46008
|
+
target,
|
|
46009
|
+
sweepParent: parent?.address,
|
|
44516
46010
|
readBalance: readBalance2
|
|
44517
46011
|
}) ?? skipped(args.account, "below_threshold");
|
|
44518
46012
|
} catch (error61) {
|
|
@@ -44522,22 +46016,28 @@ async function sweepAboveCeilingLocked(args) {
|
|
|
44522
46016
|
}
|
|
44523
46017
|
}
|
|
44524
46018
|
async function executePendingSweep(input2) {
|
|
44525
|
-
const {
|
|
46019
|
+
const {
|
|
46020
|
+
args,
|
|
46021
|
+
pending,
|
|
46022
|
+
address: address2,
|
|
46023
|
+
owner,
|
|
46024
|
+
target: currentTarget,
|
|
46025
|
+
sweepParent: currentSweepParent,
|
|
46026
|
+
readBalance: readBalance2
|
|
46027
|
+
} = input2;
|
|
44526
46028
|
const now = args.now ?? Date.now;
|
|
44527
46029
|
const auditHome = args.auditHome ?? args.store.home;
|
|
44528
|
-
if (!sameAddress2(pending.owner, owner)) {
|
|
44529
|
-
return {
|
|
44530
|
-
account: args.account,
|
|
44531
|
-
status: "unknown",
|
|
44532
|
-
reason: "pending_owner_changed",
|
|
44533
|
-
amountUsd: formatAtomicUsd(BigInt(pending.amountAtomic))
|
|
44534
|
-
};
|
|
44535
|
-
}
|
|
44536
46030
|
const amountAtomic = BigInt(pending.amountAtomic);
|
|
44537
46031
|
const amountUsd = formatAtomicUsd(amountAtomic);
|
|
44538
|
-
const target = sweepTarget({ account: args.account, owner });
|
|
44539
46032
|
const hasJournal = await hasTransferJournal(auditHome, args.account, pending.nonce);
|
|
44540
46033
|
const resume = pending.status === "unknown" || hasJournal;
|
|
46034
|
+
const storedTarget = getAddress(pending.target ?? owner);
|
|
46035
|
+
if (!resume && !sameAddress3(storedTarget, currentTarget)) {
|
|
46036
|
+
await clearPendingSweep(args.store, args.account, pending.nonce);
|
|
46037
|
+
return void 0;
|
|
46038
|
+
}
|
|
46039
|
+
const target = resume ? storedTarget : currentTarget;
|
|
46040
|
+
const sweepParent = resume ? !sameAddress3(target, owner) ? target : void 0 : currentSweepParent;
|
|
44541
46041
|
try {
|
|
44542
46042
|
const result = await (args.transfer ?? transferBetweenAccounts)({
|
|
44543
46043
|
store: args.store,
|
|
@@ -44548,6 +46048,7 @@ async function executePendingSweep(input2) {
|
|
|
44548
46048
|
amountUsd,
|
|
44549
46049
|
network: "base",
|
|
44550
46050
|
purpose: "sweep",
|
|
46051
|
+
...sweepParent === void 0 ? {} : { sweepParent },
|
|
44551
46052
|
...resume ? { resume: pending.nonce } : { nonce: pending.nonce },
|
|
44552
46053
|
unlock: guardedSweepUnlock(args, address2, owner, amountAtomic, readBalance2),
|
|
44553
46054
|
...args.fetchImpl === void 0 ? {} : { fetchImpl: args.fetchImpl },
|
|
@@ -44685,7 +46186,7 @@ function guardedSweepUnlock(args, address2, owner, amountAtomic, readBalance2) {
|
|
|
44685
46186
|
args.signal?.throwIfAborted();
|
|
44686
46187
|
await args.store.reload();
|
|
44687
46188
|
const entry = args.store.entry(account);
|
|
44688
|
-
if (entry?.link === void 0 || !sameOrigin(entry.link.apiBase, args.apiBase) || !
|
|
46189
|
+
if (entry?.link === void 0 || !sameOrigin(entry.link.apiBase, args.apiBase) || !sameAddress3(entry.link.owner, owner)) {
|
|
44689
46190
|
throw new CeilingPolicyChangedError();
|
|
44690
46191
|
}
|
|
44691
46192
|
const ceiling = args.store.ceilingCaps(account).effectiveCeilingAtomic;
|
|
@@ -44751,77 +46252,17 @@ var CeilingPolicyChangedError = class extends Error {
|
|
|
44751
46252
|
code = "policy_changed";
|
|
44752
46253
|
};
|
|
44753
46254
|
async function withCeilingSweepLock(args, operation) {
|
|
44754
|
-
const path = `${args.store.registryPath}.${args.account}.ceiling.lock`;
|
|
44755
|
-
const waitMs = args.timeoutMs ?? CEILING_SWEEP_TIMEOUT_MS;
|
|
44756
|
-
const deadline = Date.now() + Math.max(0, waitMs);
|
|
44757
|
-
const staleAfterMs = Math.max(12e4, waitMs * 3);
|
|
44758
|
-
const owner = JSON.stringify({ pid: process.pid, token: randomBytes4(16).toString("hex") });
|
|
44759
|
-
let handle;
|
|
44760
|
-
while (handle === void 0) {
|
|
44761
|
-
args.signal?.throwIfAborted();
|
|
44762
|
-
try {
|
|
44763
|
-
handle = await open7(path, "wx", 384);
|
|
44764
|
-
await handle.writeFile(owner, "utf8");
|
|
44765
|
-
await handle.sync();
|
|
44766
|
-
} catch (error61) {
|
|
44767
|
-
if (!(error61 instanceof Error && "code" in error61 && error61.code === "EEXIST")) throw error61;
|
|
44768
|
-
await removeStaleSweepLock(path, staleAfterMs);
|
|
44769
|
-
if (Date.now() >= deadline) throw new CeilingSweepTimeoutError();
|
|
44770
|
-
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
44771
|
-
}
|
|
44772
|
-
}
|
|
44773
|
-
const ownedHandle = handle;
|
|
44774
|
-
const heartbeat = setInterval(
|
|
44775
|
-
() => {
|
|
44776
|
-
const time3 = /* @__PURE__ */ new Date();
|
|
44777
|
-
void ownedHandle.utimes(time3, time3).catch(() => void 0);
|
|
44778
|
-
},
|
|
44779
|
-
Math.min(3e4, Math.max(1e3, Math.floor(staleAfterMs / 3)))
|
|
44780
|
-
);
|
|
44781
|
-
heartbeat.unref();
|
|
44782
46255
|
try {
|
|
44783
|
-
return await
|
|
44784
|
-
|
|
44785
|
-
|
|
44786
|
-
|
|
44787
|
-
|
|
44788
|
-
|
|
44789
|
-
|
|
44790
|
-
async function removeStaleSweepLock(path, staleAfterMs) {
|
|
44791
|
-
try {
|
|
44792
|
-
const metadata = await stat5(path);
|
|
44793
|
-
if (Date.now() - metadata.mtimeMs <= staleAfterMs) return;
|
|
44794
|
-
const owner = await readFile5(path, "utf8").catch(() => "");
|
|
44795
|
-
const pid = lockOwnerPid(owner);
|
|
44796
|
-
if (pid !== void 0 && processIsAlive(pid)) return;
|
|
44797
|
-
await unlinkOwnedSweepLock(path, owner);
|
|
44798
|
-
} catch (error61) {
|
|
44799
|
-
if (!isMissingFile(error61)) throw error61;
|
|
44800
|
-
}
|
|
44801
|
-
}
|
|
44802
|
-
async function unlinkOwnedSweepLock(path, owner) {
|
|
44803
|
-
try {
|
|
44804
|
-
if (await readFile5(path, "utf8") === owner) await unlink5(path);
|
|
44805
|
-
} catch (error61) {
|
|
44806
|
-
if (!isMissingFile(error61)) throw error61;
|
|
44807
|
-
}
|
|
44808
|
-
}
|
|
44809
|
-
function lockOwnerPid(owner) {
|
|
44810
|
-
try {
|
|
44811
|
-
const value = JSON.parse(owner);
|
|
44812
|
-
if (typeof value !== "object" || value === null) return void 0;
|
|
44813
|
-
const pid = Reflect.get(value, "pid");
|
|
44814
|
-
return typeof pid === "number" && Number.isSafeInteger(pid) && pid > 0 ? pid : void 0;
|
|
44815
|
-
} catch {
|
|
44816
|
-
return void 0;
|
|
44817
|
-
}
|
|
44818
|
-
}
|
|
44819
|
-
function processIsAlive(pid) {
|
|
44820
|
-
try {
|
|
44821
|
-
process.kill(pid, 0);
|
|
44822
|
-
return true;
|
|
46256
|
+
return await withAccountMovementLock(
|
|
46257
|
+
args.store.home,
|
|
46258
|
+
args.account,
|
|
46259
|
+
args.timeoutMs ?? CEILING_SWEEP_TIMEOUT_MS,
|
|
46260
|
+
operation,
|
|
46261
|
+
args.signal
|
|
46262
|
+
);
|
|
44823
46263
|
} catch (error61) {
|
|
44824
|
-
|
|
46264
|
+
if (error61 instanceof AccountMovementLockedError) throw new CeilingSweepTimeoutError();
|
|
46265
|
+
throw error61;
|
|
44825
46266
|
}
|
|
44826
46267
|
}
|
|
44827
46268
|
function defaultBalanceReader(args) {
|
|
@@ -44868,7 +46309,7 @@ function sameOrigin(left, right) {
|
|
|
44868
46309
|
return false;
|
|
44869
46310
|
}
|
|
44870
46311
|
}
|
|
44871
|
-
function
|
|
46312
|
+
function sameAddress3(left, right) {
|
|
44872
46313
|
return isAddress(left, { strict: false }) && isAddress(right, { strict: false }) && getAddress(left) === getAddress(right);
|
|
44873
46314
|
}
|
|
44874
46315
|
function safeWarn(warn, message) {
|
|
@@ -44903,11 +46344,160 @@ async function safeAppendAudit(home, record2, now) {
|
|
|
44903
46344
|
await appendAudit(home, record2, { now: () => new Date(now()) }).catch(() => void 0);
|
|
44904
46345
|
}
|
|
44905
46346
|
|
|
46347
|
+
// src/swarm.ts
|
|
46348
|
+
import { readdir as readdir4, readFile as readFile10, stat as stat8, unlink as unlink8 } from "node:fs/promises";
|
|
46349
|
+
import { join as join6 } from "node:path";
|
|
46350
|
+
var SWARM_NAME_PATTERN = /^[a-z][a-z0-9-]{0,15}$/;
|
|
46351
|
+
var SWARM_ROLE_PATTERN = /^[a-z][a-z0-9]{0,7}$/;
|
|
46352
|
+
var atomicSchema = external_exports.string().regex(/^\d+$/);
|
|
46353
|
+
var addressSchema2 = external_exports.string().regex(/^0x[0-9a-fA-F]{40}$/);
|
|
46354
|
+
var capsSchema = external_exports.strictObject({ perCallAtomic: atomicSchema, perDayAtomic: atomicSchema });
|
|
46355
|
+
var movementIdSchema2 = external_exports.string().regex(/^mv_[A-Za-z0-9_-]{8,128}$/u);
|
|
46356
|
+
var amountUsdSchema = external_exports.string().regex(/^\d+\.\d{2}$/u);
|
|
46357
|
+
var setupFundingStatusSchema = external_exports.enum(["planned", "sent", "incomplete"]);
|
|
46358
|
+
var blockedFundingLegSchema = external_exports.strictObject({
|
|
46359
|
+
to: external_exports.string().trim().min(1),
|
|
46360
|
+
amountUsd: amountUsdSchema,
|
|
46361
|
+
reason: external_exports.string().trim().min(1)
|
|
46362
|
+
});
|
|
46363
|
+
var setupFundingSchema = external_exports.strictObject({
|
|
46364
|
+
amountUsd: amountUsdSchema,
|
|
46365
|
+
from: walletNameSchema,
|
|
46366
|
+
movementId: movementIdSchema2.optional(),
|
|
46367
|
+
status: setupFundingStatusSchema,
|
|
46368
|
+
policy: external_exports.strictObject({
|
|
46369
|
+
strategy: external_exports.enum(["targets", "even", "weights"]),
|
|
46370
|
+
movementId: movementIdSchema2.optional(),
|
|
46371
|
+
status: setupFundingStatusSchema,
|
|
46372
|
+
blocked: external_exports.array(blockedFundingLegSchema).optional()
|
|
46373
|
+
}).optional()
|
|
46374
|
+
});
|
|
46375
|
+
var accountSteps = {
|
|
46376
|
+
creating: external_exports.boolean().default(false),
|
|
46377
|
+
created: external_exports.boolean(),
|
|
46378
|
+
capped: external_exports.boolean(),
|
|
46379
|
+
linked: external_exports.boolean()
|
|
46380
|
+
};
|
|
46381
|
+
var treasurySchema = external_exports.strictObject({
|
|
46382
|
+
account: walletNameSchema,
|
|
46383
|
+
// A planned account has no address until its vault derivation succeeds.
|
|
46384
|
+
address: addressSchema2.optional(),
|
|
46385
|
+
steps: external_exports.strictObject(accountSteps)
|
|
46386
|
+
}).refine(
|
|
46387
|
+
(value) => !value.steps.created || value.address !== void 0,
|
|
46388
|
+
"Created accounts require an address."
|
|
46389
|
+
);
|
|
46390
|
+
var memberSchema = external_exports.strictObject({
|
|
46391
|
+
account: walletNameSchema,
|
|
46392
|
+
address: addressSchema2.optional(),
|
|
46393
|
+
role: external_exports.string().regex(SWARM_ROLE_PATTERN),
|
|
46394
|
+
weight: external_exports.number().int().positive(),
|
|
46395
|
+
targetAtomic: atomicSchema,
|
|
46396
|
+
ceilingAtomic: atomicSchema.nullable(),
|
|
46397
|
+
steps: external_exports.strictObject({ ...accountSteps, profiled: external_exports.boolean() })
|
|
46398
|
+
}).refine(
|
|
46399
|
+
(value) => !value.steps.created || value.address !== void 0,
|
|
46400
|
+
"Created accounts require an address."
|
|
46401
|
+
);
|
|
46402
|
+
var swarmFileSchema = external_exports.strictObject({
|
|
46403
|
+
v: external_exports.literal(1),
|
|
46404
|
+
name: external_exports.string().regex(SWARM_NAME_PATTERN),
|
|
46405
|
+
device: external_exports.string().regex(DEVICE_NAME_PATTERN),
|
|
46406
|
+
network: external_exports.enum([BASE_MAINNET_CAIP2, ARC_MAINNET_CAIP2]),
|
|
46407
|
+
createdAt: external_exports.iso.datetime(),
|
|
46408
|
+
treasury: treasurySchema,
|
|
46409
|
+
members: external_exports.array(memberSchema),
|
|
46410
|
+
setupFunding: setupFundingSchema.optional(),
|
|
46411
|
+
policy: external_exports.strictObject({
|
|
46412
|
+
strategy: external_exports.enum(["targets", "even", "weights"]),
|
|
46413
|
+
treasuryCaps: capsSchema
|
|
46414
|
+
})
|
|
46415
|
+
}).refine(
|
|
46416
|
+
(value) => (/* @__PURE__ */ new Set([value.treasury.account, ...value.members.map((member) => member.account)])).size === value.members.length + 1,
|
|
46417
|
+
"Swarm accounts must be unique."
|
|
46418
|
+
);
|
|
46419
|
+
var SwarmError = class extends Error {
|
|
46420
|
+
constructor(code, message) {
|
|
46421
|
+
super(message);
|
|
46422
|
+
this.code = code;
|
|
46423
|
+
this.name = "SwarmError";
|
|
46424
|
+
}
|
|
46425
|
+
code;
|
|
46426
|
+
};
|
|
46427
|
+
function swarmsDirectory(home) {
|
|
46428
|
+
return join6(home, "swarms");
|
|
46429
|
+
}
|
|
46430
|
+
function swarmFilePath(home, name) {
|
|
46431
|
+
assertName(name);
|
|
46432
|
+
return join6(swarmsDirectory(home), `${name}.json`);
|
|
46433
|
+
}
|
|
46434
|
+
async function readSwarm(home, name) {
|
|
46435
|
+
const path = swarmFilePath(home, name);
|
|
46436
|
+
try {
|
|
46437
|
+
const swarm = parseSwarm(JSON.parse(await readFile10(path, "utf8")));
|
|
46438
|
+
if (swarm.name !== name)
|
|
46439
|
+
throw new SwarmError("invalid_swarm", "Swarm name does not match its file.");
|
|
46440
|
+
return swarm;
|
|
46441
|
+
} catch (error61) {
|
|
46442
|
+
if (isMissingFile(error61)) throw new SwarmError("swarm_not_found", `No swarm named ${name}.`);
|
|
46443
|
+
if (error61 instanceof SwarmError) throw error61;
|
|
46444
|
+
throw new SwarmError("invalid_swarm", `Invalid swarm file for ${name}.`);
|
|
46445
|
+
}
|
|
46446
|
+
}
|
|
46447
|
+
async function listSwarms(home) {
|
|
46448
|
+
let files;
|
|
46449
|
+
try {
|
|
46450
|
+
files = await readdir4(swarmsDirectory(home));
|
|
46451
|
+
} catch (error61) {
|
|
46452
|
+
if (isMissingFile(error61)) return [];
|
|
46453
|
+
throw error61;
|
|
46454
|
+
}
|
|
46455
|
+
return await Promise.all(
|
|
46456
|
+
files.filter((file2) => file2.endsWith(".json")).sort().map((file2) => readSwarm(home, file2.slice(0, -5)))
|
|
46457
|
+
);
|
|
46458
|
+
}
|
|
46459
|
+
function swarmParentResolver(home) {
|
|
46460
|
+
return async (account) => {
|
|
46461
|
+
const swarms = await listSwarms(home);
|
|
46462
|
+
const matches = swarms.filter(
|
|
46463
|
+
(swarm) => swarm.members.some((member) => member.account === account)
|
|
46464
|
+
);
|
|
46465
|
+
if (matches.length > 1)
|
|
46466
|
+
throw new SwarmError(
|
|
46467
|
+
"account_in_other_swarm",
|
|
46468
|
+
`Account ${account} belongs to multiple swarms.`
|
|
46469
|
+
);
|
|
46470
|
+
const treasury = matches[0]?.treasury;
|
|
46471
|
+
if (treasury === void 0) return void 0;
|
|
46472
|
+
if (treasury.address === void 0)
|
|
46473
|
+
throw new SwarmError("invalid_swarm", "The swarm treasury is not created yet.");
|
|
46474
|
+
return { account: treasury.account, address: treasury.address };
|
|
46475
|
+
};
|
|
46476
|
+
}
|
|
46477
|
+
function parseSwarm(value) {
|
|
46478
|
+
const result = swarmFileSchema.safeParse(value);
|
|
46479
|
+
if (!result.success) throw new SwarmError("invalid_swarm", "Invalid swarm state.");
|
|
46480
|
+
return result.data;
|
|
46481
|
+
}
|
|
46482
|
+
function assertName(name) {
|
|
46483
|
+
if (!SWARM_NAME_PATTERN.test(name))
|
|
46484
|
+
throw new SwarmError(
|
|
46485
|
+
"invalid_swarm",
|
|
46486
|
+
"A swarm name must be 1 to 16 lowercase letters, digits or dashes, starting with a letter."
|
|
46487
|
+
);
|
|
46488
|
+
}
|
|
46489
|
+
|
|
44906
46490
|
// src/x402-pay.ts
|
|
44907
|
-
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
46491
|
+
import { randomBytes as randomBytes8, randomUUID as randomUUID2 } from "node:crypto";
|
|
46492
|
+
|
|
46493
|
+
// src/run-budget.ts
|
|
46494
|
+
var RunBudgetError = class extends Error {
|
|
46495
|
+
name = "RunBudgetError";
|
|
46496
|
+
code = "run_budget_exceeded";
|
|
46497
|
+
};
|
|
44908
46498
|
|
|
44909
46499
|
// src/support-report.ts
|
|
44910
|
-
var VAPI_CLIENT_VERSION = "0.
|
|
46500
|
+
var VAPI_CLIENT_VERSION = "0.9.0";
|
|
44911
46501
|
|
|
44912
46502
|
// src/x402-pay.ts
|
|
44913
46503
|
var X402PaymentError = class extends Error {
|
|
@@ -44926,6 +46516,7 @@ var X402PaymentError = class extends Error {
|
|
|
44926
46516
|
};
|
|
44927
46517
|
async function payRequest(args) {
|
|
44928
46518
|
const startedAt = args.now ?? /* @__PURE__ */ new Date();
|
|
46519
|
+
assertAllowanceCurrent(startedAt, args.allowanceExpiresAt);
|
|
44929
46520
|
const nowMs = args.nowMs ?? (() => performance.now());
|
|
44930
46521
|
const startedMs = nowMs();
|
|
44931
46522
|
const receiptId = randomUUID2();
|
|
@@ -44965,8 +46556,10 @@ async function payRequest(args) {
|
|
|
44965
46556
|
...args.preferNetworks === void 0 ? {} : { preferNetworks: args.preferNetworks },
|
|
44966
46557
|
...args.paymentId === void 0 ? {} : { paymentId: args.paymentId },
|
|
44967
46558
|
now: startedAt,
|
|
46559
|
+
...args.allowanceExpiresAt === void 0 ? {} : { allowanceExpiresAt: args.allowanceExpiresAt },
|
|
44968
46560
|
nowMs,
|
|
44969
46561
|
trace,
|
|
46562
|
+
...args.runBudget === void 0 ? {} : { runBudget: args.runBudget },
|
|
44970
46563
|
...args.ceilingSweep === void 0 ? {} : { ceilingSweep: args.ceilingSweep }
|
|
44971
46564
|
});
|
|
44972
46565
|
response = completed.response;
|
|
@@ -45000,6 +46593,8 @@ async function payRequest(args) {
|
|
|
45000
46593
|
return { response, receipt };
|
|
45001
46594
|
}
|
|
45002
46595
|
async function completeX402Payment(args) {
|
|
46596
|
+
const paymentTime = args.now ?? /* @__PURE__ */ new Date();
|
|
46597
|
+
assertAllowanceCurrent(paymentTime, args.allowanceExpiresAt);
|
|
45003
46598
|
const nowMs = args.nowMs ?? (() => performance.now());
|
|
45004
46599
|
const trace = args.trace;
|
|
45005
46600
|
const quoteStarted = args.quoteStartedMs ?? nowMs();
|
|
@@ -45022,11 +46617,29 @@ async function completeX402Payment(args) {
|
|
|
45022
46617
|
if (trace) trace.capsApplied = true;
|
|
45023
46618
|
assertMaximumAtomic(quote.amountAtomic, args.maxAtomic);
|
|
45024
46619
|
assertMaxPrice(quote.amountAtomic, args.maxPriceUsd);
|
|
45025
|
-
|
|
46620
|
+
const spendReservation = {
|
|
45026
46621
|
ledgerPath: args.ledgerPath ?? getVapiPaths().ledger,
|
|
45027
46622
|
now: args.now,
|
|
45028
46623
|
...args.wallet === void 0 ? {} : { wallet: args.wallet }
|
|
45029
|
-
}
|
|
46624
|
+
};
|
|
46625
|
+
let reservationId;
|
|
46626
|
+
let reservedOn;
|
|
46627
|
+
if (args.runBudget === void 0) {
|
|
46628
|
+
await reserveSpend(quote.amountAtomic, args.caps, spendReservation);
|
|
46629
|
+
} else {
|
|
46630
|
+
reservationId = args.paymentId ?? `x402_${randomBytes8(12).toString("hex")}`;
|
|
46631
|
+
const reservation = await reserveSpend(quote.amountAtomic, args.caps, {
|
|
46632
|
+
...spendReservation,
|
|
46633
|
+
reservationId
|
|
46634
|
+
});
|
|
46635
|
+
reservedOn = reservation.date;
|
|
46636
|
+
try {
|
|
46637
|
+
args.runBudget.reserve(quote.amountAtomic, reservationId);
|
|
46638
|
+
} catch (error61) {
|
|
46639
|
+
await releaseUnsentReservation(args, quote.amountAtomic, reservationId, reservedOn);
|
|
46640
|
+
throw error61;
|
|
46641
|
+
}
|
|
46642
|
+
}
|
|
45030
46643
|
const signStarted = nowMs();
|
|
45031
46644
|
let payment;
|
|
45032
46645
|
try {
|
|
@@ -45037,6 +46650,7 @@ async function completeX402Payment(args) {
|
|
|
45037
46650
|
allowPrivateNetwork: args.config.allowPrivateNetwork ?? false
|
|
45038
46651
|
}),
|
|
45039
46652
|
...args.now === void 0 ? {} : { nowSeconds: Math.floor(args.now.getTime() / 1e3) },
|
|
46653
|
+
...args.allowanceExpiresAt === void 0 ? {} : { maximumValidBeforeSeconds: Math.floor(args.allowanceExpiresAt.getTime() / 1e3) },
|
|
45040
46654
|
...args.paymentId === void 0 ? {} : { paymentId: args.paymentId }
|
|
45041
46655
|
});
|
|
45042
46656
|
const payer2 = isSvmPaymentRequirements(quote.accepted) ? args.account.solana?.address ?? args.account.address : args.account.address;
|
|
@@ -45048,6 +46662,17 @@ async function completeX402Payment(args) {
|
|
|
45048
46662
|
trace.authorization = { from: from15, nonce, validBefore };
|
|
45049
46663
|
}
|
|
45050
46664
|
}
|
|
46665
|
+
} catch (error61) {
|
|
46666
|
+
if (args.runBudget !== void 0 && reservationId !== void 0 && reservedOn !== void 0) {
|
|
46667
|
+
const released = await releaseUnsentReservation(
|
|
46668
|
+
args,
|
|
46669
|
+
quote.amountAtomic,
|
|
46670
|
+
reservationId,
|
|
46671
|
+
reservedOn
|
|
46672
|
+
);
|
|
46673
|
+
if (released) args.runBudget.release(reservationId);
|
|
46674
|
+
}
|
|
46675
|
+
throw error61;
|
|
45051
46676
|
} finally {
|
|
45052
46677
|
if (trace) trace.phases.signMs = elapsed(nowMs, signStarted);
|
|
45053
46678
|
}
|
|
@@ -45182,6 +46807,26 @@ async function completeX402Payment(args) {
|
|
|
45182
46807
|
paidAttempt.finish();
|
|
45183
46808
|
}
|
|
45184
46809
|
}
|
|
46810
|
+
function assertAllowanceCurrent(now, expiresAt) {
|
|
46811
|
+
if (expiresAt === void 0 || now.getTime() < expiresAt.getTime()) return;
|
|
46812
|
+
throw new Error(
|
|
46813
|
+
`Remote allowance expired at ${expiresAt.toISOString()}. Start a new Railway run before paying.`
|
|
46814
|
+
);
|
|
46815
|
+
}
|
|
46816
|
+
async function releaseUnsentReservation(args, amountAtomic, reservationId, reservedOn) {
|
|
46817
|
+
try {
|
|
46818
|
+
await releaseSpend(amountAtomic, {
|
|
46819
|
+
reservedOn,
|
|
46820
|
+
ledgerPath: args.ledgerPath ?? getVapiPaths().ledger,
|
|
46821
|
+
now: args.now,
|
|
46822
|
+
...args.wallet === void 0 ? {} : { wallet: args.wallet },
|
|
46823
|
+
reservationId
|
|
46824
|
+
});
|
|
46825
|
+
return true;
|
|
46826
|
+
} catch {
|
|
46827
|
+
return false;
|
|
46828
|
+
}
|
|
46829
|
+
}
|
|
45185
46830
|
async function selectQuote(args) {
|
|
45186
46831
|
const networks = toX402Networks(args.config.networks);
|
|
45187
46832
|
const waitFor = args.challenge.waitFor.bind(args.challenge);
|
|
@@ -45276,6 +46921,9 @@ function sanitizeThrownError(error61, secrets) {
|
|
|
45276
46921
|
if (error61 instanceof SpendCapError) {
|
|
45277
46922
|
return new SpendCapError(error61.code, message);
|
|
45278
46923
|
}
|
|
46924
|
+
if (error61 instanceof RunBudgetError) {
|
|
46925
|
+
return new RunBudgetError(message);
|
|
46926
|
+
}
|
|
45279
46927
|
if (error61 instanceof X402PaymentError) {
|
|
45280
46928
|
return new X402PaymentError(error61.code, message, error61.possibleSettlement, {
|
|
45281
46929
|
...cause === void 0 ? {} : { cause },
|
|
@@ -45365,6 +47013,7 @@ function directReceipt(args, request, trace, receiptId, startedAt, latencyMs, re
|
|
|
45365
47013
|
resourceUrl: request.url,
|
|
45366
47014
|
method: request.method,
|
|
45367
47015
|
source: args.source ?? "direct",
|
|
47016
|
+
...args.run === void 0 ? {} : { run: args.run },
|
|
45368
47017
|
...trace.quote ? { quote: trace.quote } : {},
|
|
45369
47018
|
...trace.payer && result.outcome !== "declined_policy" ? { payer: trace.payer } : {},
|
|
45370
47019
|
...trace.paymentId ? { paymentId: trace.paymentId } : {},
|
|
@@ -45392,7 +47041,7 @@ function receiptQuote(quote) {
|
|
|
45392
47041
|
};
|
|
45393
47042
|
}
|
|
45394
47043
|
function paymentErrorOutcome(error61) {
|
|
45395
|
-
if (error61 instanceof SpendCapError || error61 instanceof X402Error && error61.code === "max_price_exceeded") {
|
|
47044
|
+
if (error61 instanceof SpendCapError || error61 instanceof RunBudgetError || error61 instanceof X402Error && error61.code === "max_price_exceeded") {
|
|
45396
47045
|
return "declined_policy";
|
|
45397
47046
|
}
|
|
45398
47047
|
if (error61 instanceof X402PaymentError) {
|
|
@@ -45409,7 +47058,7 @@ function errorCode(error61) {
|
|
|
45409
47058
|
return "request_failed";
|
|
45410
47059
|
}
|
|
45411
47060
|
function receiptSafeErrorMessage(error61) {
|
|
45412
|
-
if (error61 instanceof SpendCapError || error61 instanceof X402Error || error61 instanceof X402PaymentError) {
|
|
47061
|
+
if (error61 instanceof SpendCapError || error61 instanceof RunBudgetError || error61 instanceof X402Error || error61 instanceof X402PaymentError) {
|
|
45413
47062
|
return error61.message;
|
|
45414
47063
|
}
|
|
45415
47064
|
return "The x402 request failed before it completed.";
|
|
@@ -45545,9 +47194,9 @@ async function buyRouterBalance(deps, tierUsd) {
|
|
|
45545
47194
|
if (!isRouterTopupTier(tierUsd)) {
|
|
45546
47195
|
throw new RouterClientError("http", "Router balance must use a $1, $5, $20, or $50 tier.");
|
|
45547
47196
|
}
|
|
45548
|
-
const { link, accessToken } = await purchaseCredentialSnapshot(deps);
|
|
47197
|
+
const { link: link2, accessToken } = await purchaseCredentialSnapshot(deps);
|
|
45549
47198
|
const { response, receipt } = await payRequest({
|
|
45550
|
-
url: `${trimTrailingSlashes(
|
|
47199
|
+
url: `${trimTrailingSlashes(link2.apiBase)}/api/router/top-up/${tierUsd}`,
|
|
45551
47200
|
init: {
|
|
45552
47201
|
method: "POST",
|
|
45553
47202
|
headers: { authorization: `Bearer ${accessToken}`, accept: "application/json" }
|
|
@@ -45561,6 +47210,9 @@ async function buyRouterBalance(deps, tierUsd) {
|
|
|
45561
47210
|
maxAtomic: BigInt(tierUsd) * 1000000n,
|
|
45562
47211
|
preferNetworks: ["eip155:8453"],
|
|
45563
47212
|
...deps.now === void 0 ? {} : { now: deps.now },
|
|
47213
|
+
...deps.allowanceExpiresAt === void 0 ? {} : { allowanceExpiresAt: deps.allowanceExpiresAt },
|
|
47214
|
+
...deps.runBudget === void 0 ? {} : { runBudget: deps.runBudget },
|
|
47215
|
+
...deps.run === void 0 ? {} : { run: deps.run },
|
|
45564
47216
|
source: "router.topup",
|
|
45565
47217
|
ceilingSweep: {
|
|
45566
47218
|
account: deps.wallet,
|
|
@@ -45568,10 +47220,11 @@ async function buyRouterBalance(deps, tierUsd) {
|
|
|
45568
47220
|
run: async (signal) => await sweepAboveCeiling({
|
|
45569
47221
|
store: deps.wallets,
|
|
45570
47222
|
secrets: deps.secrets,
|
|
45571
|
-
apiBase:
|
|
47223
|
+
apiBase: link2.apiBase,
|
|
45572
47224
|
account: deps.wallet,
|
|
45573
47225
|
config: deps.config,
|
|
45574
47226
|
signal,
|
|
47227
|
+
resolveParent: swarmParentResolver(deps.wallets.home),
|
|
45575
47228
|
...deps.fetchImpl === void 0 ? {} : { fetchImpl: deps.fetchImpl },
|
|
45576
47229
|
unlock: async () => deps.account
|
|
45577
47230
|
})
|
|
@@ -45590,14 +47243,14 @@ async function buyRouterBalance(deps, tierUsd) {
|
|
|
45590
47243
|
{
|
|
45591
47244
|
event: "router.buy",
|
|
45592
47245
|
wallet: deps.wallet,
|
|
45593
|
-
owner:
|
|
47246
|
+
owner: link2.owner,
|
|
45594
47247
|
tty: Boolean(process.stderr.isTTY),
|
|
45595
47248
|
detail: `$${tierUsd} on ${receipt.quote?.network ?? "eip155:8453"}`
|
|
45596
47249
|
},
|
|
45597
47250
|
deps.now === void 0 ? {} : { now: () => deps.now }
|
|
45598
47251
|
);
|
|
45599
47252
|
try {
|
|
45600
|
-
await ensureBalanceRouterKey(deps,
|
|
47253
|
+
await ensureBalanceRouterKey(deps, link2);
|
|
45601
47254
|
} catch {
|
|
45602
47255
|
throw new RouterClientError(
|
|
45603
47256
|
"http",
|
|
@@ -45636,7 +47289,12 @@ async function routerChat(deps, request) {
|
|
|
45636
47289
|
config: deps.refill.config,
|
|
45637
47290
|
caps,
|
|
45638
47291
|
...deps.refill.paths === void 0 ? {} : { paths: deps.refill.paths },
|
|
45639
|
-
...deps.refill.now === void 0 ? {} : {
|
|
47292
|
+
...deps.refill.now === void 0 ? {} : {
|
|
47293
|
+
now: typeof deps.refill.now === "function" ? deps.refill.now() : deps.refill.now
|
|
47294
|
+
},
|
|
47295
|
+
...deps.refill.allowanceExpiresAt === void 0 ? {} : { allowanceExpiresAt: deps.refill.allowanceExpiresAt },
|
|
47296
|
+
...deps.refill.runBudget === void 0 ? {} : { runBudget: deps.refill.runBudget },
|
|
47297
|
+
...deps.refill.run === void 0 ? {} : { run: deps.refill.run }
|
|
45640
47298
|
},
|
|
45641
47299
|
refill.tierUsd
|
|
45642
47300
|
)).balance;
|
|
@@ -45648,6 +47306,7 @@ async function routerChat(deps, request) {
|
|
|
45648
47306
|
tty: Boolean(process.stderr.isTTY),
|
|
45649
47307
|
detail: refillDeclinedDetail(refillError)
|
|
45650
47308
|
});
|
|
47309
|
+
if (isRunBudgetError(refillError)) throw refillError;
|
|
45651
47310
|
}
|
|
45652
47311
|
}
|
|
45653
47312
|
const mayHaveBalance = usage === void 0 || (balance?.remainingUsd ?? 0) > 0;
|
|
@@ -45755,8 +47414,8 @@ async function ensureBalanceRouterKey(deps, paidLink) {
|
|
|
45755
47414
|
deps.wallets,
|
|
45756
47415
|
`${paidLink.clientId}:router-balance-key`,
|
|
45757
47416
|
async () => {
|
|
45758
|
-
const
|
|
45759
|
-
if (!sameAgentLink(
|
|
47417
|
+
const link2 = await linkedWallet(deps);
|
|
47418
|
+
if (!sameAgentLink(link2, paidLink)) {
|
|
45760
47419
|
throw new RouterClientError(
|
|
45761
47420
|
"http",
|
|
45762
47421
|
"The agent link changed while fetching the Router balance key."
|
|
@@ -45775,7 +47434,7 @@ async function ensureBalanceRouterKey(deps, paidLink) {
|
|
|
45775
47434
|
"/api/agents/self/router-balance-key",
|
|
45776
47435
|
{ method: "POST" },
|
|
45777
47436
|
"The Router balance key request failed after payment.",
|
|
45778
|
-
|
|
47437
|
+
link2
|
|
45779
47438
|
);
|
|
45780
47439
|
if (!response.ok) {
|
|
45781
47440
|
throw new RouterClientError(
|
|
@@ -45796,7 +47455,7 @@ async function ensureBalanceRouterKey(deps, paidLink) {
|
|
|
45796
47455
|
);
|
|
45797
47456
|
}
|
|
45798
47457
|
const returnedBase = validRouterBaseUrl(parsed.data.router_base_url);
|
|
45799
|
-
const expectedBase = validRouterBaseUrl(
|
|
47458
|
+
const expectedBase = validRouterBaseUrl(link2.routerBaseUrl ?? DEFAULT_ROUTER_BASE_URL);
|
|
45800
47459
|
if (returnedBase !== expectedBase) {
|
|
45801
47460
|
throw new RouterClientError(
|
|
45802
47461
|
"http",
|
|
@@ -45815,8 +47474,8 @@ async function routerBalanceCredentialSnapshot(deps) {
|
|
|
45815
47474
|
const observedLink = await linkedWallet(deps);
|
|
45816
47475
|
try {
|
|
45817
47476
|
return await withAgentCredentialLock(deps.wallets, observedLink.clientId, async () => {
|
|
45818
|
-
const
|
|
45819
|
-
if (
|
|
47477
|
+
const link2 = await linkedWallet(deps);
|
|
47478
|
+
if (link2.clientId !== observedLink.clientId) {
|
|
45820
47479
|
throw new RouterClientError(
|
|
45821
47480
|
"http",
|
|
45822
47481
|
"The agent link changed while reading Router balance credentials."
|
|
@@ -45831,8 +47490,8 @@ async function routerBalanceCredentialSnapshot(deps) {
|
|
|
45831
47490
|
}
|
|
45832
47491
|
if (routerKey === void 0 || routerKey.length === 0) return void 0;
|
|
45833
47492
|
return {
|
|
45834
|
-
clientId:
|
|
45835
|
-
routerBaseUrl: validRouterBaseUrl(
|
|
47493
|
+
clientId: link2.clientId,
|
|
47494
|
+
routerBaseUrl: validRouterBaseUrl(link2.routerBaseUrl ?? DEFAULT_ROUTER_BASE_URL),
|
|
45836
47495
|
routerKey
|
|
45837
47496
|
};
|
|
45838
47497
|
});
|
|
@@ -45847,8 +47506,8 @@ async function routerBalanceCredentialSnapshot(deps) {
|
|
|
45847
47506
|
async function removeRejectedBalanceKey(deps, rejected) {
|
|
45848
47507
|
if (!deps.secrets.available) return;
|
|
45849
47508
|
await withAgentCredentialLock(deps.wallets, rejected.clientId, async () => {
|
|
45850
|
-
const
|
|
45851
|
-
if (
|
|
47509
|
+
const link2 = await linkedWallet(deps);
|
|
47510
|
+
if (link2.clientId !== rejected.clientId) return;
|
|
45852
47511
|
const account = agentSecretAccounts(deps.wallet).routerBalance;
|
|
45853
47512
|
if (await deps.secrets.get(account) === rejected.routerKey) {
|
|
45854
47513
|
await deps.secrets.remove(account);
|
|
@@ -45897,16 +47556,20 @@ function budgetExhaustedError(resetsAt, status) {
|
|
|
45897
47556
|
}
|
|
45898
47557
|
function refillDeclinedDetail(error61) {
|
|
45899
47558
|
if (error61 instanceof SpendCapError) return `${error61.name}: ${error61.code}`;
|
|
47559
|
+
if (isRunBudgetError(error61)) return `RunBudgetError: ${error61.code}`;
|
|
45900
47560
|
if (error61 instanceof RouterClientError) {
|
|
45901
47561
|
return `${error61.name}: ${error61.code}${error61.status === void 0 ? "" : ` (${error61.status})`}`;
|
|
45902
47562
|
}
|
|
45903
47563
|
return "Error: Router balance refill failed.";
|
|
45904
47564
|
}
|
|
47565
|
+
function isRunBudgetError(error61) {
|
|
47566
|
+
return typeof error61 === "object" && error61 !== null && Reflect.get(error61, "code") === "run_budget_exceeded";
|
|
47567
|
+
}
|
|
45905
47568
|
function isRouterTopupTier(value) {
|
|
45906
47569
|
return ROUTER_TOPUP_TIERS.some((tier) => tier === value);
|
|
45907
47570
|
}
|
|
45908
47571
|
async function rotateRouterKey(deps) {
|
|
45909
|
-
const
|
|
47572
|
+
const link2 = await linkedWallet(deps);
|
|
45910
47573
|
if (!deps.secrets.available) {
|
|
45911
47574
|
throw new Error(
|
|
45912
47575
|
"No OS secret store is available on this machine, so the Router key cannot be stored safely."
|
|
@@ -45917,7 +47580,7 @@ async function rotateRouterKey(deps) {
|
|
|
45917
47580
|
"/api/agents/self/router-key",
|
|
45918
47581
|
{ method: "POST" },
|
|
45919
47582
|
"The vAPI Router key rotation request failed.",
|
|
45920
|
-
|
|
47583
|
+
link2
|
|
45921
47584
|
);
|
|
45922
47585
|
if (!response.ok) {
|
|
45923
47586
|
throw new RouterClientError(
|
|
@@ -45932,9 +47595,9 @@ async function rotateRouterKey(deps) {
|
|
|
45932
47595
|
}
|
|
45933
47596
|
validRouterBaseUrl(parsed.data.router_base_url);
|
|
45934
47597
|
try {
|
|
45935
|
-
await withAgentCredentialLock(deps.wallets,
|
|
47598
|
+
await withAgentCredentialLock(deps.wallets, link2.clientId, async () => {
|
|
45936
47599
|
const currentLink = await linkedWallet(deps);
|
|
45937
|
-
if (!sameAgentLink(currentLink,
|
|
47600
|
+
if (!sameAgentLink(currentLink, link2)) {
|
|
45938
47601
|
throw new RouterClientError("http", "The agent link changed during Router key rotation.");
|
|
45939
47602
|
}
|
|
45940
47603
|
const account = agentSecretAccounts(deps.wallet).routerStake;
|
|
@@ -45998,15 +47661,15 @@ async function routerCredentialSnapshot(deps) {
|
|
|
45998
47661
|
const observedLink = await linkedWallet(deps);
|
|
45999
47662
|
try {
|
|
46000
47663
|
return await withAgentCredentialLock(deps.wallets, observedLink.clientId, async () => {
|
|
46001
|
-
const
|
|
46002
|
-
if (
|
|
47664
|
+
const link2 = await linkedWallet(deps);
|
|
47665
|
+
if (link2.clientId !== observedLink.clientId) {
|
|
46003
47666
|
throw new RouterClientError(
|
|
46004
47667
|
"http",
|
|
46005
47668
|
"The agent link changed while reading Router credentials."
|
|
46006
47669
|
);
|
|
46007
47670
|
}
|
|
46008
47671
|
return {
|
|
46009
|
-
routerBaseUrl: validRouterBaseUrl(
|
|
47672
|
+
routerBaseUrl: validRouterBaseUrl(link2.routerBaseUrl ?? DEFAULT_ROUTER_BASE_URL),
|
|
46010
47673
|
routerKey: await storedRouterKey(deps)
|
|
46011
47674
|
};
|
|
46012
47675
|
});
|
|
@@ -46027,9 +47690,9 @@ async function linkedWallet(deps) {
|
|
|
46027
47690
|
} catch {
|
|
46028
47691
|
throw new RouterClientError("http", "The wallet link could not be read.");
|
|
46029
47692
|
}
|
|
46030
|
-
const
|
|
46031
|
-
if (
|
|
46032
|
-
return
|
|
47693
|
+
const link2 = deps.wallets.entry(deps.wallet)?.link;
|
|
47694
|
+
if (link2 === void 0) throw notLinkedError2();
|
|
47695
|
+
return link2;
|
|
46033
47696
|
}
|
|
46034
47697
|
async function storedRouterKey(deps) {
|
|
46035
47698
|
if (!deps.secrets.available) throw noRouterKeyError();
|
|
@@ -46046,7 +47709,7 @@ function sameAgentLink(left, right) {
|
|
|
46046
47709
|
return left.apiBase === right.apiBase && left.clientId === right.clientId && left.owner === right.owner && left.label === right.label && left.linkedAt === right.linkedAt && left.routerBaseUrl === right.routerBaseUrl && left.scopes.length === right.scopes.length && left.scopes.every((scope, index2) => scope === right.scopes[index2]);
|
|
46047
47710
|
}
|
|
46048
47711
|
async function consoleRequest(deps, path, init, failureMessage, knownLink) {
|
|
46049
|
-
const
|
|
47712
|
+
const link2 = knownLink ?? await linkedWallet(deps);
|
|
46050
47713
|
try {
|
|
46051
47714
|
return await agentFetch(
|
|
46052
47715
|
{
|
|
@@ -46055,7 +47718,7 @@ async function consoleRequest(deps, path, init, failureMessage, knownLink) {
|
|
|
46055
47718
|
wallet: deps.wallet,
|
|
46056
47719
|
...deps.fetchImpl === void 0 ? {} : { fetchImpl: deps.fetchImpl }
|
|
46057
47720
|
},
|
|
46058
|
-
`${trimTrailingSlashes(
|
|
47721
|
+
`${trimTrailingSlashes(link2.apiBase)}${path}`,
|
|
46059
47722
|
init
|
|
46060
47723
|
);
|
|
46061
47724
|
} catch (error61) {
|
|
@@ -46066,7 +47729,7 @@ async function consoleRequest(deps, path, init, failureMessage, knownLink) {
|
|
|
46066
47729
|
}
|
|
46067
47730
|
}
|
|
46068
47731
|
async function verifyAgentLink(deps) {
|
|
46069
|
-
const
|
|
47732
|
+
const link2 = await linkedWallet(deps);
|
|
46070
47733
|
try {
|
|
46071
47734
|
await agentFetch(
|
|
46072
47735
|
{
|
|
@@ -46075,7 +47738,7 @@ async function verifyAgentLink(deps) {
|
|
|
46075
47738
|
wallet: deps.wallet,
|
|
46076
47739
|
...deps.fetchImpl === void 0 ? {} : { fetchImpl: deps.fetchImpl }
|
|
46077
47740
|
},
|
|
46078
|
-
`${trimTrailingSlashes(
|
|
47741
|
+
`${trimTrailingSlashes(link2.apiBase)}/api/agents/self/router`,
|
|
46079
47742
|
{ method: "GET" }
|
|
46080
47743
|
);
|
|
46081
47744
|
} catch (error61) {
|