@ametyst/cli 0.3.4 → 0.3.5
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/dist/index.js +117 -57
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -112197,7 +112197,7 @@ var init_version5 = __esm({
|
|
|
112197
112197
|
"src/version.ts"() {
|
|
112198
112198
|
"use strict";
|
|
112199
112199
|
init_esm_shims();
|
|
112200
|
-
CLI_VERSION = true ? "0.3.
|
|
112200
|
+
CLI_VERSION = true ? "0.3.5" : "0.0.0-dev";
|
|
112201
112201
|
}
|
|
112202
112202
|
});
|
|
112203
112203
|
|
|
@@ -115417,6 +115417,46 @@ var McpEventLogger = class {
|
|
|
115417
115417
|
}
|
|
115418
115418
|
};
|
|
115419
115419
|
|
|
115420
|
+
// src/mcp-server/newest-grant.ts
|
|
115421
|
+
init_esm_shims();
|
|
115422
|
+
function nonEmptyLowerCase(value2) {
|
|
115423
|
+
return typeof value2 === "string" && value2.trim() !== "" ? value2.toLowerCase() : null;
|
|
115424
|
+
}
|
|
115425
|
+
function isNewerGrant(candidate, incumbent) {
|
|
115426
|
+
const candidateId = Number(candidate?.id);
|
|
115427
|
+
const incumbentId = Number(incumbent?.id);
|
|
115428
|
+
const candidateIdUsable = Number.isFinite(candidateId);
|
|
115429
|
+
const incumbentIdUsable = Number.isFinite(incumbentId);
|
|
115430
|
+
if (candidateIdUsable && incumbentIdUsable && candidateId !== incumbentId) {
|
|
115431
|
+
return candidateId > incumbentId;
|
|
115432
|
+
}
|
|
115433
|
+
const candidateAt = Date.parse(typeof candidate?.createdAt === "string" ? candidate.createdAt : "");
|
|
115434
|
+
const incumbentAt = Date.parse(typeof incumbent?.createdAt === "string" ? incumbent.createdAt : "");
|
|
115435
|
+
if (Number.isFinite(candidateAt) && Number.isFinite(incumbentAt) && candidateAt !== incumbentAt) {
|
|
115436
|
+
return candidateAt > incumbentAt;
|
|
115437
|
+
}
|
|
115438
|
+
return candidateIdUsable && !incumbentIdUsable;
|
|
115439
|
+
}
|
|
115440
|
+
function selectNewestApprovedWallet(wallets, eoaAddress) {
|
|
115441
|
+
if (!Array.isArray(wallets)) return void 0;
|
|
115442
|
+
const eoa = nonEmptyLowerCase(eoaAddress);
|
|
115443
|
+
if (!eoa) return void 0;
|
|
115444
|
+
let newest;
|
|
115445
|
+
for (const wallet of wallets) {
|
|
115446
|
+
if (wallet?.status !== "approved") continue;
|
|
115447
|
+
if (nonEmptyLowerCase(wallet?.address) !== eoa) continue;
|
|
115448
|
+
if (newest === void 0 || isNewerGrant(wallet, newest)) newest = wallet;
|
|
115449
|
+
}
|
|
115450
|
+
return newest;
|
|
115451
|
+
}
|
|
115452
|
+
function findCurrentApprovedWallet(wallets, eoaAddress, pendingWalletId) {
|
|
115453
|
+
if (!Array.isArray(wallets)) return void 0;
|
|
115454
|
+
if (pendingWalletId) {
|
|
115455
|
+
return wallets.find((w) => String(w?.id) === String(pendingWalletId) && w?.status === "approved");
|
|
115456
|
+
}
|
|
115457
|
+
return selectNewestApprovedWallet(wallets, eoaAddress);
|
|
115458
|
+
}
|
|
115459
|
+
|
|
115420
115460
|
// src/mcp-server/start-session.ts
|
|
115421
115461
|
init_esm_shims();
|
|
115422
115462
|
init_dist();
|
|
@@ -115551,38 +115591,57 @@ async function runStartSession(ctx, timing = new TimingCollector("start_session"
|
|
|
115551
115591
|
let virtualWalletId = ctx.virtualWalletId;
|
|
115552
115592
|
let paymentManagerAddress = ctx.paymentManagerAddress;
|
|
115553
115593
|
let policyId = ctx.policyId;
|
|
115554
|
-
|
|
115555
|
-
|
|
115556
|
-
|
|
115557
|
-
|
|
115558
|
-
|
|
115559
|
-
|
|
115560
|
-
|
|
115561
|
-
|
|
115562
|
-
|
|
115563
|
-
|
|
115564
|
-
|
|
115565
|
-
|
|
115566
|
-
|
|
115567
|
-
|
|
115568
|
-
|
|
115569
|
-
|
|
115570
|
-
|
|
115571
|
-
|
|
115572
|
-
|
|
115573
|
-
|
|
115594
|
+
let activeWallet;
|
|
115595
|
+
let grantChanged = false;
|
|
115596
|
+
try {
|
|
115597
|
+
activeWallet = selectNewestApprovedWallet(wallets, ctx.eoaAddress);
|
|
115598
|
+
if (activeWallet) {
|
|
115599
|
+
const newestId = String(activeWallet.id);
|
|
115600
|
+
const rowWalletAddress = activeWallet.walletAddress || activeWallet.kernelAccountAddress;
|
|
115601
|
+
const rowPaymentManagerAddress = activeWallet.paymentManagerAddress;
|
|
115602
|
+
const rowPolicyId = activeWallet.policyAssociated != null ? String(activeWallet.policyAssociated) : void 0;
|
|
115603
|
+
grantChanged = !!virtualWalletId && virtualWalletId !== newestId;
|
|
115604
|
+
if (grantChanged) {
|
|
115605
|
+
console.error(
|
|
115606
|
+
`[start_session] The active grant moved on \u2014 virtual wallet ${virtualWalletId} -> ${newestId}. Adopting the newest approved grant and dropping the session state derived from the old one.`
|
|
115607
|
+
);
|
|
115608
|
+
walletAddress = rowWalletAddress;
|
|
115609
|
+
paymentManagerAddress = rowPaymentManagerAddress;
|
|
115610
|
+
policyId = rowPolicyId;
|
|
115611
|
+
} else {
|
|
115612
|
+
walletAddress = rowWalletAddress || walletAddress;
|
|
115613
|
+
paymentManagerAddress = rowPaymentManagerAddress || paymentManagerAddress;
|
|
115614
|
+
policyId = rowPolicyId ?? policyId;
|
|
115615
|
+
}
|
|
115616
|
+
virtualWalletId = newestId;
|
|
115617
|
+
credentials.walletAddress = walletAddress;
|
|
115618
|
+
credentials.virtualWalletId = virtualWalletId;
|
|
115619
|
+
credentials.paymentManagerAddress = paymentManagerAddress;
|
|
115620
|
+
credentials.policyId = policyId;
|
|
115621
|
+
credentials.authorizationStatus = "approved";
|
|
115622
|
+
if (activeWallet.policyName != null) {
|
|
115623
|
+
credentials.policyName = activeWallet.policyName;
|
|
115624
|
+
}
|
|
115625
|
+
if (activeWallet.policyValidUntil != null) {
|
|
115626
|
+
credentials.policyValidUntil = BigInt(activeWallet.policyValidUntil);
|
|
115627
|
+
}
|
|
115628
|
+
} else if (walletsFetchFailed && virtualWalletId) {
|
|
115574
115629
|
console.warn(
|
|
115575
|
-
|
|
115576
|
-
backendError instanceof Error ? backendError.message : String(backendError)
|
|
115630
|
+
`[start_session] \u26A0\uFE0F could not verify the active grant \u2014 signing with the cached one (virtual wallet ${virtualWalletId}, policy ${policyId ?? "unknown"}). If a newer grant was approved since, spends are metered against the older one and may hit its spending limit.`
|
|
115577
115631
|
);
|
|
115578
115632
|
}
|
|
115633
|
+
} catch (backendError) {
|
|
115634
|
+
console.warn(
|
|
115635
|
+
"[start_session] Could not check backend:",
|
|
115636
|
+
backendError instanceof Error ? backendError.message : String(backendError)
|
|
115637
|
+
);
|
|
115579
115638
|
}
|
|
115580
115639
|
const mergedAuth = credentials.authorizationStatus !== void 0 ? credentials.authorizationStatus : ctx.authorizationStatus;
|
|
115581
115640
|
console.error(
|
|
115582
115641
|
`[start_session] State check \u2014 walletAddress: ${walletAddress}, virtualWalletId: ${virtualWalletId}, paymentManagerAddress: ${paymentManagerAddress}, policyId: ${policyId}`
|
|
115583
115642
|
);
|
|
115584
115643
|
if (mergedAuth === "expired") {
|
|
115585
|
-
return { responseKey: "unlocked_expired", credentials, timingReport: timing.report() };
|
|
115644
|
+
return { responseKey: "unlocked_expired", credentials, timingReport: timing.report(), grantChanged };
|
|
115586
115645
|
}
|
|
115587
115646
|
if (walletAddress && virtualWalletId && paymentManagerAddress && policyId) {
|
|
115588
115647
|
let privateKey = null;
|
|
@@ -115604,12 +115663,9 @@ async function runStartSession(ctx, timing = new TimingCollector("start_session"
|
|
|
115604
115663
|
}
|
|
115605
115664
|
credentials.walletAddress = walletAddress;
|
|
115606
115665
|
credentials.paymentManagerAddress = paymentManagerAddress;
|
|
115607
|
-
const
|
|
115608
|
-
|
|
115609
|
-
|
|
115610
|
-
const preloadedWalletData = approvedWallet != null && approvedWallet.policyValidAfter != null && approvedWallet.policyValidUntil != null ? {
|
|
115611
|
-
policyValidAfter: Number(approvedWallet.policyValidAfter),
|
|
115612
|
-
policyValidUntil: Number(approvedWallet.policyValidUntil)
|
|
115666
|
+
const preloadedWalletData = activeWallet != null && activeWallet.policyValidAfter != null && activeWallet.policyValidUntil != null ? {
|
|
115667
|
+
policyValidAfter: Number(activeWallet.policyValidAfter),
|
|
115668
|
+
policyValidUntil: Number(activeWallet.policyValidUntil)
|
|
115613
115669
|
} : void 0;
|
|
115614
115670
|
if (ctx.readPolicyActiveOnchain) {
|
|
115615
115671
|
const nowSeconds = Math.floor(Date.now() / 1e3);
|
|
@@ -115631,7 +115687,7 @@ async function runStartSession(ctx, timing = new TimingCollector("start_session"
|
|
|
115631
115687
|
maxAttempts
|
|
115632
115688
|
});
|
|
115633
115689
|
if (bootAction === "reauthorize") {
|
|
115634
|
-
return { responseKey: "unlocked_pending_authorization", credentials, timingReport: timing.report() };
|
|
115690
|
+
return { responseKey: "unlocked_pending_authorization", credentials, timingReport: timing.report(), grantChanged };
|
|
115635
115691
|
}
|
|
115636
115692
|
}
|
|
115637
115693
|
const { kernelClient: virtualWalletKernelAccountClient, permissionPlugin: virtualWalletPermissionPlugin, kernelDomain } = await virtualWalletsManagers2.configureVirtualWalletKernelAccount(
|
|
@@ -115661,13 +115717,14 @@ async function runStartSession(ctx, timing = new TimingCollector("start_session"
|
|
|
115661
115717
|
console.warn("[start_session] \u26A0\uFE0F Could not fetch credit token address (Path B routing disabled):", e);
|
|
115662
115718
|
}
|
|
115663
115719
|
}
|
|
115664
|
-
return { responseKey: "unlocked_and_authorized", credentials, timingReport: timing.report() };
|
|
115720
|
+
return { responseKey: "unlocked_and_authorized", credentials, timingReport: timing.report(), grantChanged };
|
|
115665
115721
|
} catch (error) {
|
|
115666
115722
|
console.error("\u274C [start_session] Kernel client creation failed:", error);
|
|
115667
115723
|
return {
|
|
115668
115724
|
responseKey: "unlocked_not_configured",
|
|
115669
115725
|
credentials,
|
|
115670
115726
|
timingReport: timing.report(),
|
|
115727
|
+
grantChanged,
|
|
115671
115728
|
kernelErrorMessage: error instanceof Error ? error.message : String(error)
|
|
115672
115729
|
};
|
|
115673
115730
|
} finally {
|
|
@@ -115675,9 +115732,9 @@ async function runStartSession(ctx, timing = new TimingCollector("start_session"
|
|
|
115675
115732
|
}
|
|
115676
115733
|
}
|
|
115677
115734
|
if (!walletsFetchFailed && wallets.length === 0) {
|
|
115678
|
-
return { responseKey: "unlocked_provisioning_pending", credentials, timingReport: timing.report() };
|
|
115735
|
+
return { responseKey: "unlocked_provisioning_pending", credentials, timingReport: timing.report(), grantChanged };
|
|
115679
115736
|
}
|
|
115680
|
-
return { responseKey: "unlocked_pending_authorization", credentials, timingReport: timing.report() };
|
|
115737
|
+
return { responseKey: "unlocked_pending_authorization", credentials, timingReport: timing.report(), grantChanged };
|
|
115681
115738
|
}
|
|
115682
115739
|
var WALLET_SCOPED_CREDENTIAL_KEYS = [
|
|
115683
115740
|
"virtualWalletId",
|
|
@@ -115707,9 +115764,9 @@ function clearWalletScopedCredentials(target) {
|
|
|
115707
115764
|
function sameAddress(a, b) {
|
|
115708
115765
|
return String(a ?? "").toLowerCase() === String(b ?? "").toLowerCase();
|
|
115709
115766
|
}
|
|
115710
|
-
function mergeStartSessionCredentials(target, patch, responseKey) {
|
|
115767
|
+
function mergeStartSessionCredentials(target, patch, responseKey, options = {}) {
|
|
115711
115768
|
const walletChanged = patch.eoaAddress !== void 0 && !sameAddress(patch.eoaAddress, target.eoaAddress);
|
|
115712
|
-
if (walletChanged) {
|
|
115769
|
+
if (walletChanged || options.grantChanged) {
|
|
115713
115770
|
clearWalletScopedCredentials(target);
|
|
115714
115771
|
}
|
|
115715
115772
|
if (patch.walletKeystoreJson !== void 0) {
|
|
@@ -122739,17 +122796,6 @@ var approvalWaitConfig = (() => {
|
|
|
122739
122796
|
delay: (ms) => new Promise((resolve) => setTimeout(resolve, ms))
|
|
122740
122797
|
};
|
|
122741
122798
|
})();
|
|
122742
|
-
function findCurrentApprovedWallet(wallets, eoaAddress, pendingWalletId) {
|
|
122743
|
-
if (!Array.isArray(wallets)) return void 0;
|
|
122744
|
-
if (pendingWalletId) {
|
|
122745
|
-
return wallets.find(
|
|
122746
|
-
(w) => String(w?.id) === String(pendingWalletId) && w?.status === "approved"
|
|
122747
|
-
);
|
|
122748
|
-
}
|
|
122749
|
-
return wallets.find(
|
|
122750
|
-
(w) => w?.address?.toLowerCase() === eoaAddress?.toLowerCase() && w?.status === "approved"
|
|
122751
|
-
);
|
|
122752
|
-
}
|
|
122753
122799
|
async function tryResolvePendingApproval(probe) {
|
|
122754
122800
|
if (currentCredentials.authorizationStatus === "approved") return true;
|
|
122755
122801
|
if (currentCredentials.authorizationStatus !== "pending" || !currentCredentials.apiKey || !currentCredentials.eoaAddress) {
|
|
@@ -122887,11 +122933,20 @@ async function applyStartSessionUnlock(passphrase, resetRequested) {
|
|
|
122887
122933
|
persistVault: persistVault2,
|
|
122888
122934
|
wipeVault: wipeVault2,
|
|
122889
122935
|
sdk,
|
|
122890
|
-
|
|
122936
|
+
// FORCED REFRESH. `start_session` verifies the session's grant against the
|
|
122937
|
+
// backend (see `runStartSession`), and a verification run against the cache
|
|
122938
|
+
// this process loaded at boot verifies nothing: a grant approved since boot
|
|
122939
|
+
// would be invisible to exactly the check that exists to find it.
|
|
122940
|
+
fetchWallets: () => fetchVirtualWalletsFromBackend(currentCredentials.apiKey, true),
|
|
122891
122941
|
readPolicyActiveOnchain: buildPolicyActiveOnchainReader(sdk)
|
|
122892
122942
|
}
|
|
122893
122943
|
);
|
|
122894
|
-
const { walletChanged } = mergeStartSessionCredentials(
|
|
122944
|
+
const { walletChanged } = mergeStartSessionCredentials(
|
|
122945
|
+
currentCredentials,
|
|
122946
|
+
result.credentials,
|
|
122947
|
+
result.responseKey,
|
|
122948
|
+
{ grantChanged: result.grantChanged }
|
|
122949
|
+
);
|
|
122895
122950
|
refreshPlaintextVaultMode();
|
|
122896
122951
|
if (walletChanged) {
|
|
122897
122952
|
walletsCache = null;
|
|
@@ -123390,10 +123445,9 @@ server.tool(
|
|
|
123390
123445
|
fetchVirtualWalletsFromBackend(currentCredentials.apiKey, !refreshedFromBackend),
|
|
123391
123446
|
fetchPoliciesFromBackend(currentCredentials.apiKey, true)
|
|
123392
123447
|
]);
|
|
123393
|
-
const freshWallet = freshWallets.
|
|
123394
|
-
|
|
123395
|
-
)
|
|
123396
|
-
if (freshWallet && freshWallet.status === "approved") {
|
|
123448
|
+
const freshWallet = selectNewestApprovedWallet(freshWallets, currentCredentials.eoaAddress);
|
|
123449
|
+
const freshWalletIsActiveGrant = String(freshWallet?.id) === String(currentCredentials.virtualWalletId);
|
|
123450
|
+
if (freshWallet && freshWalletIsActiveGrant) {
|
|
123397
123451
|
if (freshWallet.policyAssociated != null) {
|
|
123398
123452
|
currentCredentials.policyId = String(freshWallet.policyAssociated);
|
|
123399
123453
|
}
|
|
@@ -123414,6 +123468,14 @@ server.tool(
|
|
|
123414
123468
|
eoaAddress: currentCredentials.eoaAddress || null,
|
|
123415
123469
|
walletAddress: currentCredentials.walletAddress || null,
|
|
123416
123470
|
kernelClientActive: !!currentCredentials.virtualWalletKernelAccountClient,
|
|
123471
|
+
// WHICH GRANT THIS SESSION SIGNS WITH. The 2026-09-01 incident — three
|
|
123472
|
+
// approved rows for one EOA, every session silently signing with the oldest
|
|
123473
|
+
// — was invisible in every payload: diagnosing it took a nonce-key autopsy.
|
|
123474
|
+
// These two ids are the row `start_session` actually selected, so the next
|
|
123475
|
+
// stale grant is a field to read rather than a forensic exercise. Null until
|
|
123476
|
+
// a session has adopted one.
|
|
123477
|
+
activeVirtualWalletId: currentCredentials.virtualWalletId != null ? String(currentCredentials.virtualWalletId) : null,
|
|
123478
|
+
activePolicyId: currentCredentials.policyId != null ? String(currentCredentials.policyId) : null,
|
|
123417
123479
|
// WHICH WORKSPACE THIS IS. The 2026-08-31 incident produced writes into
|
|
123418
123480
|
// the wrong workspace with nothing in any payload naming one — an agent
|
|
123419
123481
|
// reading wallet status could not have told, and neither could a human
|
|
@@ -123427,7 +123489,7 @@ server.tool(
|
|
|
123427
123489
|
}
|
|
123428
123490
|
}
|
|
123429
123491
|
if (walletsCache && currentCredentials.eoaAddress) {
|
|
123430
|
-
const wallet = walletsCache.find(
|
|
123492
|
+
const wallet = selectNewestApprovedWallet(walletsCache, currentCredentials.eoaAddress) ?? walletsCache.find(
|
|
123431
123493
|
(w) => w.address?.toLowerCase() === currentCredentials.eoaAddress?.toLowerCase()
|
|
123432
123494
|
);
|
|
123433
123495
|
if (wallet) {
|
|
@@ -125792,9 +125854,7 @@ async function reconcileApprovalStateOnConnect() {
|
|
|
125792
125854
|
}
|
|
125793
125855
|
try {
|
|
125794
125856
|
const wallets = await fetchVirtualWalletsFromBackend(currentCredentials.apiKey, true);
|
|
125795
|
-
const approved = wallets.
|
|
125796
|
-
(w) => w.address?.toLowerCase() === currentCredentials.eoaAddress?.toLowerCase() && w.status === "approved"
|
|
125797
|
-
);
|
|
125857
|
+
const approved = selectNewestApprovedWallet(wallets, currentCredentials.eoaAddress);
|
|
125798
125858
|
if (!approved) return;
|
|
125799
125859
|
if (typeof approved.id === "number" && hasProcessedApprovalEventId(approved.id)) return;
|
|
125800
125860
|
console.error(
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ametyst/cli",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.5",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Ametyst CLI — embedded MCP server, wallet ops, agent payments",
|
|
6
6
|
"type": "module",
|
|
@@ -90,10 +90,10 @@
|
|
|
90
90
|
"esbuild@>=0.27.3 <0.28.1": "0.28.1"
|
|
91
91
|
},
|
|
92
92
|
"optionalDependencies": {
|
|
93
|
-
"@ametyst/cli-darwin-arm64": "0.3.
|
|
94
|
-
"@ametyst/cli-darwin-x64": "0.3.
|
|
95
|
-
"@ametyst/cli-linux-x64-gnu": "0.3.
|
|
96
|
-
"@ametyst/cli-win32-x64-msvc": "0.3.
|
|
93
|
+
"@ametyst/cli-darwin-arm64": "0.3.5",
|
|
94
|
+
"@ametyst/cli-darwin-x64": "0.3.5",
|
|
95
|
+
"@ametyst/cli-linux-x64-gnu": "0.3.5",
|
|
96
|
+
"@ametyst/cli-win32-x64-msvc": "0.3.5"
|
|
97
97
|
},
|
|
98
98
|
"scripts": {
|
|
99
99
|
"build:native": "cd native && cargo build --release && napi build --release",
|