@xona-labs/xpay 0.2.24 → 0.2.26
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/CHANGELOG.md +101 -78
- package/README.md +71 -69
- package/SKILL.md +22 -22
- package/dist/agenc/api.d.ts +8 -8
- package/dist/agenc/api.js +11 -11
- package/dist/agenc/hire.d.ts +4 -4
- package/dist/agenc/hire.js +17 -17
- package/dist/agenc/price.d.ts +1 -1
- package/dist/agenc/price.js +1 -1
- package/dist/biometric/index.d.ts +3 -3
- package/dist/biometric/index.js +6 -6
- package/dist/cli/accounts.d.ts +1 -1
- package/dist/cli/accounts.js +1 -1
- package/dist/cli/agenc.d.ts +2 -2
- package/dist/cli/agenc.js +4 -4
- package/dist/cli/balance.d.ts +1 -1
- package/dist/cli/balance.js +4 -4
- package/dist/cli/bento.d.ts +3 -3
- package/dist/cli/bento.js +8 -8
- package/dist/cli/bento.js.map +1 -1
- package/dist/cli/biometric.d.ts +1 -1
- package/dist/cli/biometric.js +8 -8
- package/dist/cli/common.js +6 -6
- package/dist/cli/common.js.map +1 -1
- package/dist/cli/discover.d.ts +2 -2
- package/dist/cli/discover.js +4 -4
- package/dist/cli/guardrail.d.ts +1 -1
- package/dist/cli/guardrail.js +3 -3
- package/dist/cli/index.d.ts +2 -2
- package/dist/cli/index.js +12 -12
- package/dist/cli/init.d.ts +1 -1
- package/dist/cli/init.js +2 -2
- package/dist/cli/magicblock.d.ts +1 -1
- package/dist/cli/magicblock.js +4 -4
- package/dist/cli/mcp-server.d.ts +2 -2
- package/dist/cli/mcp-server.d.ts.map +1 -1
- package/dist/cli/mcp-server.js +25 -22
- package/dist/cli/mcp-server.js.map +1 -1
- package/dist/cli/pay.d.ts +1 -1
- package/dist/cli/pay.js +1 -1
- package/dist/cli/report.d.ts +2 -2
- package/dist/cli/report.js +4 -4
- package/dist/cli/report.js.map +1 -1
- package/dist/cli/sana.d.ts +1 -1
- package/dist/cli/sana.js +3 -3
- package/dist/cli/shop.d.ts +1 -1
- package/dist/cli/shop.js +1 -1
- package/dist/cli/swap.d.ts +1 -1
- package/dist/cli/swap.js +3 -3
- package/dist/cli/token.d.ts +2 -2
- package/dist/cli/token.js +2 -2
- package/dist/cli/trade.d.ts +2 -2
- package/dist/cli/trade.js +8 -8
- package/dist/cli/trade.js.map +1 -1
- package/dist/cli/transfer.d.ts +1 -1
- package/dist/cli/transfer.js +1 -1
- package/dist/cli/x.d.ts +1 -1
- package/dist/cli/x.js +1 -1
- package/dist/cli/zauth.d.ts +1 -1
- package/dist/cli/zauth.js +5 -5
- package/dist/discover/cache.js +1 -1
- package/dist/discover/index.d.ts +2 -2
- package/dist/discover/index.js +6 -6
- package/dist/discover/orbitx402.d.ts +2 -2
- package/dist/discover/orbitx402.js +1 -1
- package/dist/discover/payai.js +1 -1
- package/dist/do/index.d.ts +2 -2
- package/dist/do/index.js +1 -1
- package/dist/guardrail/index.d.ts +4 -4
- package/dist/guardrail/index.js +14 -14
- package/dist/guardrail/index.js.map +1 -1
- package/dist/index.d.ts +7 -7
- package/dist/index.js +2 -2
- package/dist/magicblock/client.d.ts +2 -2
- package/dist/magicblock/client.js +4 -4
- package/dist/profile/index.d.ts +3 -3
- package/dist/profile/index.js +3 -3
- package/dist/profile/storage.d.ts +1 -1
- package/dist/profile/storage.js +4 -4
- package/dist/profile/types.d.ts +6 -6
- package/dist/profile/types.js +1 -1
- package/dist/report/index.d.ts +1 -1
- package/dist/report/index.js +2 -2
- package/dist/sana/tools.js +8 -8
- package/dist/signers/raw-evm.d.ts +1 -1
- package/dist/signers/raw-evm.js +5 -5
- package/dist/signers/raw-solana.d.ts +1 -1
- package/dist/signers/raw-solana.js +4 -4
- package/dist/swap/index.d.ts +6 -6
- package/dist/swap/index.js +12 -12
- package/dist/token/index.d.ts +8 -8
- package/dist/token/index.js +14 -14
- package/dist/token/rwa.d.ts +3 -3
- package/dist/token/rwa.js +3 -3
- package/dist/tools/index.d.ts +15 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +71 -31
- package/dist/tools/index.js.map +1 -1
- package/dist/trading/discovery.d.ts +6 -6
- package/dist/trading/discovery.js +7 -7
- package/dist/trading/index.d.ts +6 -6
- package/dist/trading/index.js +13 -13
- package/dist/transfer/index.d.ts +4 -4
- package/dist/transfer/index.js +8 -8
- package/dist/types.d.ts +7 -7
- package/dist/use/index.d.ts +5 -5
- package/dist/use/index.js +20 -20
- package/dist/wallet/index.d.ts +2 -2
- package/dist/wallet/index.js +3 -3
- package/dist/x402/evm-payment.d.ts +4 -4
- package/dist/x402/evm-payment.js +6 -6
- package/dist/x402/extract.d.ts +1 -1
- package/dist/x402/extract.js +1 -1
- package/dist/x402/svm-payment.d.ts +2 -2
- package/dist/x402/svm-payment.js +2 -2
- package/dist/zauth/index.d.ts +4 -4
- package/dist/zauth/index.js +5 -5
- package/package.json +4 -3
package/dist/agenc/api.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* AgenC marketplace client
|
|
2
|
+
* AgenC marketplace client - keyless reads of Solana-mainnet marketplace state.
|
|
3
3
|
*
|
|
4
4
|
* AgenC (https://agenc.ag) lists standing service offers from registered
|
|
5
5
|
* on-chain agents. Discovery goes through AgenC's hosted indexer via the SDK's
|
|
6
|
-
* `createIndexerClient`
|
|
6
|
+
* `createIndexerClient` - their documented "intended scale read path" (the
|
|
7
7
|
* legacy REST `?hireable=true` filter went dark after a program upgrade).
|
|
8
|
-
* Execution is NOT x402
|
|
8
|
+
* Execution is NOT x402 - hiring escrows native SOL on-chain via
|
|
9
9
|
* `@tetsuo-ai/marketplace-sdk` (see ./hire.ts).
|
|
10
10
|
*/
|
|
11
11
|
import { z } from "zod";
|
|
@@ -37,7 +37,7 @@ const AgencListingSchema = z
|
|
|
37
37
|
createdAtUnix: z.number().optional(),
|
|
38
38
|
})
|
|
39
39
|
.passthrough();
|
|
40
|
-
/** Task shape returned by GET /api/tasks/:pda
|
|
40
|
+
/** Task shape returned by GET /api/tasks/:pda - used to poll hire progress. */
|
|
41
41
|
const AgencTaskSchema = z
|
|
42
42
|
.object({
|
|
43
43
|
pda: z.string(),
|
|
@@ -54,7 +54,7 @@ const AgencTaskSchema = z
|
|
|
54
54
|
* Fetch all active, SOL-priced listings via AgenC's hosted indexer and map
|
|
55
55
|
* them into xpay's Resource shape. Discovery is deliberately permissive:
|
|
56
56
|
* listings without a fresh moderation attestation are still shown (matching
|
|
57
|
-
* agenc.ag's own browse page)
|
|
57
|
+
* agenc.ag's own browse page) - the hire path is the fail-closed gate.
|
|
58
58
|
*/
|
|
59
59
|
export async function fetchAgencResources(opts = {}) {
|
|
60
60
|
const endpoint = opts.endpoint ?? process.env.XPAY_AGENC_ENDPOINT ?? DEFAULT_ENDPOINT;
|
|
@@ -76,7 +76,7 @@ export async function fetchAgencResources(opts = {}) {
|
|
|
76
76
|
/**
|
|
77
77
|
* Map an AgenC listing into xpay's Resource shape. Everything `hire.ts` needs
|
|
78
78
|
* to execute rides in `accepts[0].extra`, so a discovered resource is
|
|
79
|
-
* self-contained
|
|
79
|
+
* self-contained - no extra API round-trips at hire time.
|
|
80
80
|
*/
|
|
81
81
|
export function listingToResource(l) {
|
|
82
82
|
return {
|
|
@@ -117,7 +117,7 @@ export function isAgencResource(r) {
|
|
|
117
117
|
return r.accepts.some((a) => a.scheme === AGENC_SCHEME);
|
|
118
118
|
}
|
|
119
119
|
/**
|
|
120
|
-
* Fetch a single listing by PDA via the indexer
|
|
120
|
+
* Fetch a single listing by PDA via the indexer - used as the pre-hire
|
|
121
121
|
* freshness check. Decodes the raw on-chain account bytes for byte-true
|
|
122
122
|
* parity with what the program will see. Returns undefined when the listing
|
|
123
123
|
* doesn't exist.
|
|
@@ -186,17 +186,17 @@ function normalizeAccount(pda, a) {
|
|
|
186
186
|
};
|
|
187
187
|
}
|
|
188
188
|
catch {
|
|
189
|
-
return null; // malformed row
|
|
189
|
+
return null; // malformed row - skip rather than kill discovery
|
|
190
190
|
}
|
|
191
191
|
}
|
|
192
|
-
/** Fetch a task by PDA (GET /api/tasks/:pda)
|
|
192
|
+
/** Fetch a task by PDA (GET /api/tasks/:pda) - the status of a hire. */
|
|
193
193
|
export async function fetchAgencTask(pda, opts = {}) {
|
|
194
194
|
const endpoint = opts.endpoint ?? process.env.XPAY_AGENC_ENDPOINT ?? DEFAULT_ENDPOINT;
|
|
195
195
|
const fetchImpl = opts.fetch ?? fetch;
|
|
196
196
|
const url = new URL(`/api/tasks/${encodeURIComponent(pda)}`, endpoint);
|
|
197
197
|
const res = await fetchImpl(url.toString(), { headers: { accept: "application/json" } });
|
|
198
198
|
if (res.status === 404) {
|
|
199
|
-
throw new Error(`AgenC task ${pda} not found. The API snapshot rebuilds ~every 45s
|
|
199
|
+
throw new Error(`AgenC task ${pda} not found. The API snapshot rebuilds ~every 45s - ` +
|
|
200
200
|
`if this hire just landed, retry in about a minute.`);
|
|
201
201
|
}
|
|
202
202
|
if (!res.ok)
|
|
@@ -214,7 +214,7 @@ async function apiError(what, res) {
|
|
|
214
214
|
try {
|
|
215
215
|
const body = (await res.json());
|
|
216
216
|
if (body?.error)
|
|
217
|
-
reason = `
|
|
217
|
+
reason = ` - ${body.error}`;
|
|
218
218
|
}
|
|
219
219
|
catch {
|
|
220
220
|
/* no JSON body */
|
package/dist/agenc/hire.d.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* AgenC hire execution
|
|
2
|
+
* AgenC hire execution - the non-x402 payment rail.
|
|
3
3
|
*
|
|
4
4
|
* Hiring a listing is a Solana transaction (program
|
|
5
5
|
* HJsZ53Zb27b8QMRbQpuDngE44AdwCGxvEZr61Zmxw1xK) that escrows the listing's
|
|
6
6
|
* price in native SOL and creates a Task + HireRecord on-chain. The provider
|
|
7
7
|
* then works ASYNCHRONOUSLY: escrow → work → review → settle. So unlike an
|
|
8
8
|
* x402 call, `use()` on an AgenC resource returns a hire *receipt*, not a
|
|
9
|
-
* response body
|
|
9
|
+
* response body - poll progress with `xpay agenc status <taskPda>`.
|
|
10
10
|
*
|
|
11
11
|
* The humanless entry point pins the task to CreatorReview, so escrowed funds
|
|
12
12
|
* never auto-release without the buyer accepting the result.
|
|
@@ -26,7 +26,7 @@ export interface AgencHireReceipt {
|
|
|
26
26
|
kind: "agenc-hire-receipt";
|
|
27
27
|
/** Listing PDA that was hired. */
|
|
28
28
|
listing: string;
|
|
29
|
-
/** Task PDA created by the hire
|
|
29
|
+
/** Task PDA created by the hire - the handle for status polling and review. */
|
|
30
30
|
task: string;
|
|
31
31
|
/** HireRecord PDA linking the task back to the listing. */
|
|
32
32
|
hireRecord: string;
|
|
@@ -53,7 +53,7 @@ interface ResolvedModeration {
|
|
|
53
53
|
* 2. v2 seeds ["listing_moderation_v2", listing, specHash, globalAuthority]
|
|
54
54
|
* 3. getProgramAccounts scan (discriminator + listing memcmp)
|
|
55
55
|
* Whatever record is found, its CONTENT names the moderator the hire gate
|
|
56
|
-
* expects
|
|
56
|
+
* expects - return both so the facade can skip its own derivation.
|
|
57
57
|
*/
|
|
58
58
|
export declare function resolveListingModeration(sdk: AgencSdk, rpc: unknown, listingPda: string, specHashHex: string): Promise<ResolvedModeration | null>;
|
|
59
59
|
export {};
|
package/dist/agenc/hire.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* AgenC hire execution
|
|
2
|
+
* AgenC hire execution - the non-x402 payment rail.
|
|
3
3
|
*
|
|
4
4
|
* Hiring a listing is a Solana transaction (program
|
|
5
5
|
* HJsZ53Zb27b8QMRbQpuDngE44AdwCGxvEZr61Zmxw1xK) that escrows the listing's
|
|
6
6
|
* price in native SOL and creates a Task + HireRecord on-chain. The provider
|
|
7
7
|
* then works ASYNCHRONOUSLY: escrow → work → review → settle. So unlike an
|
|
8
8
|
* x402 call, `use()` on an AgenC resource returns a hire *receipt*, not a
|
|
9
|
-
* response body
|
|
9
|
+
* response body - poll progress with `xpay agenc status <taskPda>`.
|
|
10
10
|
*
|
|
11
11
|
* The humanless entry point pins the task to CreatorReview, so escrowed funds
|
|
12
12
|
* never auto-release without the buyer accepting the result.
|
|
@@ -21,9 +21,9 @@ export async function useAgencHire(args) {
|
|
|
21
21
|
}
|
|
22
22
|
const extra = parseExtra(req);
|
|
23
23
|
const cfg = args.agenc ?? {};
|
|
24
|
-
// Guardrail runs *before* signing
|
|
24
|
+
// Guardrail runs *before* signing - same security boundary as the x402 path.
|
|
25
25
|
await args.guardrail.check({ resource: args.resource, requirement: req });
|
|
26
|
-
// Freshness check: the catalog snapshot may be stale
|
|
26
|
+
// Freshness check: the catalog snapshot may be stale - refuse if the listing
|
|
27
27
|
// was paused/retired or repriced upward since discovery. The on-chain
|
|
28
28
|
// expectedPrice/expectedVersion checks are the fail-closed backstop.
|
|
29
29
|
const live = await fetchAgencListing(extra.listingPda, { endpoint: cfg.endpoint });
|
|
@@ -43,14 +43,14 @@ export async function useAgencHire(args) {
|
|
|
43
43
|
const kitSigner = (await signer.getKitSigner());
|
|
44
44
|
const rpcUrl = cfg.rpcUrl ?? process.env.XPAY_SOLANA_RPC ?? DEFAULT_RPC;
|
|
45
45
|
// Pre-flight: the hire escrows priceLamports in NATIVE SOL (plus fees/rent),
|
|
46
|
-
// so check the SOL balance
|
|
46
|
+
// so check the SOL balance - the USDC insufficient-balance path doesn't apply.
|
|
47
47
|
const { createSolanaRpc, address } = await import("@solana/kit");
|
|
48
48
|
const rpc = createSolanaRpc(rpcUrl);
|
|
49
49
|
const FEE_BUFFER_LAMPORTS = 10000000n; // ~0.01 SOL for fees + PDA rent
|
|
50
50
|
const needed = BigInt(live.priceLamports) + FEE_BUFFER_LAMPORTS;
|
|
51
51
|
const { value: lamports } = await rpc.getBalance(address(signer.address)).send();
|
|
52
52
|
if (BigInt(lamports) < needed) {
|
|
53
|
-
throw new Error(`xpay.agenc: insufficient SOL
|
|
53
|
+
throw new Error(`xpay.agenc: insufficient SOL - hire needs ~${fmtSol(needed)} SOL ` +
|
|
54
54
|
`(${fmtSol(BigInt(live.priceLamports))} escrow + fees) but ${signer.address} ` +
|
|
55
55
|
`holds ${fmtSol(BigInt(lamports))} SOL. Fund the wallet and retry.`);
|
|
56
56
|
}
|
|
@@ -63,19 +63,19 @@ export async function useAgencHire(args) {
|
|
|
63
63
|
const surface = await deployedSurface(sdk, rpc);
|
|
64
64
|
if (!surface.listings) {
|
|
65
65
|
throw new Error(`xpay.agenc: the deployed AgenC program (surface revision ${surface.surfaceRevision}) ` +
|
|
66
|
-
`does not expose listings/hires on this cluster. xpay may need an update
|
|
66
|
+
`does not expose listings/hires on this cluster. xpay may need an update - check ` +
|
|
67
67
|
`for a newer @xona-labs/xpay release.`);
|
|
68
68
|
}
|
|
69
69
|
// Resolve the listing's moderation attestation. The record's on-chain
|
|
70
70
|
// ADDRESS has changed seed schemes across program upgrades, but its CONTENT
|
|
71
|
-
// always names the moderator the hire gate expects
|
|
71
|
+
// always names the moderator the hire gate expects - so we locate the
|
|
72
72
|
// record (trying each known derivation, newest data wins) and pass both
|
|
73
73
|
// the moderator and the explicit record address to the facade. Fail-closed
|
|
74
74
|
// BEFORE the guardrail/payment path if no attestation exists.
|
|
75
75
|
const moderation = await resolveListingModeration(sdk, rpc, extra.listingPda, extra.specHash);
|
|
76
76
|
if (!moderation) {
|
|
77
77
|
throw new Error(`xpay.agenc: listing ${extra.listingPda} has no on-chain moderation attestation yet ` +
|
|
78
|
-
`(AgenC re-attests listings after program upgrades). The hire gate is fail-closed
|
|
78
|
+
`(AgenC re-attests listings after program upgrades). The hire gate is fail-closed - ` +
|
|
79
79
|
`pick another listing or retry later.`);
|
|
80
80
|
}
|
|
81
81
|
const client = createMarketplaceClient({ rpcUrl, signer: kitSigner });
|
|
@@ -103,7 +103,7 @@ export async function useAgencHire(args) {
|
|
|
103
103
|
txSig: signature,
|
|
104
104
|
status: "escrowed",
|
|
105
105
|
statusHint: "SOL is escrowed on-chain; the provider works asynchronously. Poll progress with " +
|
|
106
|
-
`\`xpay agenc status ${taskPda}\`
|
|
106
|
+
`\`xpay agenc status ${taskPda}\` - funds settle to the provider after your review window.`,
|
|
107
107
|
explorer: `https://solscan.io/tx/${signature}`,
|
|
108
108
|
surfaceRevision: surface.surfaceRevision,
|
|
109
109
|
};
|
|
@@ -127,13 +127,13 @@ function parseExtra(req) {
|
|
|
127
127
|
if (typeof listingPda !== "string" ||
|
|
128
128
|
typeof specHash !== "string" ||
|
|
129
129
|
typeof version !== "string") {
|
|
130
|
-
throw new Error("xpay.agenc: malformed AgenC resource
|
|
130
|
+
throw new Error("xpay.agenc: malformed AgenC resource - missing listingPda/specHash/version in " +
|
|
131
131
|
"accepts[].extra. Re-run discover to get a fresh listing.");
|
|
132
132
|
}
|
|
133
133
|
return { listingPda, specHash, version };
|
|
134
134
|
}
|
|
135
135
|
let surfaceCache;
|
|
136
|
-
/** Deployed instruction surface, cached 10 min
|
|
136
|
+
/** Deployed instruction surface, cached 10 min - one account fetch. */
|
|
137
137
|
async function deployedSurface(sdk, rpc) {
|
|
138
138
|
if (surfaceCache && Date.now() - surfaceCache.at < 600_000)
|
|
139
139
|
return surfaceCache.value;
|
|
@@ -149,13 +149,13 @@ async function deployedSurface(sdk, rpc) {
|
|
|
149
149
|
* 2. v2 seeds ["listing_moderation_v2", listing, specHash, globalAuthority]
|
|
150
150
|
* 3. getProgramAccounts scan (discriminator + listing memcmp)
|
|
151
151
|
* Whatever record is found, its CONTENT names the moderator the hire gate
|
|
152
|
-
* expects
|
|
152
|
+
* expects - return both so the facade can skip its own derivation.
|
|
153
153
|
*/
|
|
154
154
|
export async function resolveListingModeration(sdk, rpc, listingPda, specHashHex) {
|
|
155
155
|
const { getProgramDerivedAddress, getAddressEncoder, getUtf8Encoder, getBase58Decoder, address } = await import("@solana/kit");
|
|
156
156
|
const specHash = hexToBytes(specHashHex);
|
|
157
157
|
const candidates = [];
|
|
158
|
-
// 1) v1 seeds
|
|
158
|
+
// 1) v1 seeds - pre-upgrade attestations (still honored by the program).
|
|
159
159
|
const [v1] = await getProgramDerivedAddress({
|
|
160
160
|
programAddress: sdk.AGENC_COORDINATION_PROGRAM_ADDRESS,
|
|
161
161
|
seeds: [
|
|
@@ -179,7 +179,7 @@ export async function resolveListingModeration(sdk, rpc, listingPda, specHashHex
|
|
|
179
179
|
}
|
|
180
180
|
}
|
|
181
181
|
catch {
|
|
182
|
-
/* moderation config unreadable
|
|
182
|
+
/* moderation config unreadable - continue with what we have */
|
|
183
183
|
}
|
|
184
184
|
for (const candidate of candidates) {
|
|
185
185
|
const found = await tryDecodeModeration(sdk, rpc, candidate);
|
|
@@ -187,7 +187,7 @@ export async function resolveListingModeration(sdk, rpc, listingPda, specHashHex
|
|
|
187
187
|
return found;
|
|
188
188
|
}
|
|
189
189
|
// 3) Seed-agnostic: scan the program's moderation records for this listing.
|
|
190
|
-
// Best effort
|
|
190
|
+
// Best effort - some RPCs rate-limit or forbid getProgramAccounts.
|
|
191
191
|
try {
|
|
192
192
|
const disc = getBase58Decoder().decode(sdk.LISTING_MODERATION_DISCRIMINATOR);
|
|
193
193
|
const rows = await rpc
|
|
@@ -206,7 +206,7 @@ export async function resolveListingModeration(sdk, rpc, listingPda, specHashHex
|
|
|
206
206
|
}
|
|
207
207
|
}
|
|
208
208
|
catch {
|
|
209
|
-
/* scan unavailable
|
|
209
|
+
/* scan unavailable - fall through to fail-closed */
|
|
210
210
|
}
|
|
211
211
|
return null;
|
|
212
212
|
}
|
package/dist/agenc/price.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* SOL/USD spot price
|
|
2
|
+
* SOL/USD spot price - AgenC listings are priced in native SOL, but the
|
|
3
3
|
* guardrail's policy surface (maxPerTx / maxPerDay / requireApprovalAbove)
|
|
4
4
|
* is USD-denominated. Tries several keyless public feeds in order (corporate
|
|
5
5
|
* proxies commonly block one or another), cached for 60s.
|
package/dist/agenc/price.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* SOL/USD spot price
|
|
2
|
+
* SOL/USD spot price - AgenC listings are priced in native SOL, but the
|
|
3
3
|
* guardrail's policy surface (maxPerTx / maxPerDay / requireApprovalAbove)
|
|
4
4
|
* is USD-denominated. Tries several keyless public feeds in order (corporate
|
|
5
5
|
* proxies commonly block one or another), cached for 60s.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Biometric unlock (macOS Touch ID / Watch)
|
|
2
|
+
* Biometric unlock (macOS Touch ID / Watch) - gates access to the wallet
|
|
3
3
|
* passphrase, it does not replace it. The passphrase is stored in the user's
|
|
4
4
|
* login keychain and only released by a small native helper after a
|
|
5
5
|
* successful LocalAuthentication check. The scrypt/AES wallet encryption is
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* Embedding the source keeps the npm package dependency-free and lets tsc
|
|
11
11
|
* ship it inside dist/ without a native build step at install time.
|
|
12
12
|
*/
|
|
13
|
-
/** Keychain item identity
|
|
13
|
+
/** Keychain item identity - must match `kService` in the Swift source. */
|
|
14
14
|
export declare const KEYCHAIN_SERVICE = "com.xona-labs.xpay.wallet";
|
|
15
15
|
export type BiometryType = "touchid" | "faceid";
|
|
16
16
|
/** Thrown when biometrics can't work here at all (non-macOS, no swiftc, no enrolled biometry). */
|
|
@@ -34,7 +34,7 @@ export declare function biometricPrompt(reason: string): Promise<boolean>;
|
|
|
34
34
|
export declare function storeBiometricPassphrase(account: string, passphrase: string): Promise<void>;
|
|
35
35
|
/**
|
|
36
36
|
* Biometric-gated read of the stored passphrase. Returns null when the user
|
|
37
|
-
* cancels/fails the check or no passphrase is stored
|
|
37
|
+
* cancels/fails the check or no passphrase is stored - both mean "fall back
|
|
38
38
|
* to typing the passphrase". Throws {@link BiometricUnavailableError} when
|
|
39
39
|
* biometry can't be evaluated at all.
|
|
40
40
|
*/
|
package/dist/biometric/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Biometric unlock (macOS Touch ID / Watch)
|
|
2
|
+
* Biometric unlock (macOS Touch ID / Watch) - gates access to the wallet
|
|
3
3
|
* passphrase, it does not replace it. The passphrase is stored in the user's
|
|
4
4
|
* login keychain and only released by a small native helper after a
|
|
5
5
|
* successful LocalAuthentication check. The scrypt/AES wallet encryption is
|
|
@@ -16,7 +16,7 @@ import { existsSync, mkdirSync, writeFileSync, readdirSync, unlinkSync } from "n
|
|
|
16
16
|
import { createHash } from "node:crypto";
|
|
17
17
|
import { execFileSync, execFile } from "node:child_process";
|
|
18
18
|
import { xpayHome } from "../profile/storage.js";
|
|
19
|
-
/** Keychain item identity
|
|
19
|
+
/** Keychain item identity - must match `kService` in the Swift source. */
|
|
20
20
|
export const KEYCHAIN_SERVICE = "com.xona-labs.xpay.wallet";
|
|
21
21
|
const HELPER_BASENAME = "xpay-biometric";
|
|
22
22
|
// Helper exit codes (mirrored in the Swift source).
|
|
@@ -24,7 +24,7 @@ const EXIT_OK = 0;
|
|
|
24
24
|
const EXIT_UNAVAILABLE = 2;
|
|
25
25
|
const EXIT_DENIED = 3;
|
|
26
26
|
const EXIT_NOT_FOUND = 4;
|
|
27
|
-
const SWIFT_SOURCE = `// xpay-biometric
|
|
27
|
+
const SWIFT_SOURCE = `// xpay-biometric - generated by @xona-labs/xpay; do not edit.
|
|
28
28
|
import Foundation
|
|
29
29
|
import LocalAuthentication
|
|
30
30
|
import Security
|
|
@@ -136,7 +136,7 @@ function binDir() {
|
|
|
136
136
|
* Compile the helper if this exact source hasn't been compiled yet. The
|
|
137
137
|
* binary name carries a source hash so upgrades recompile automatically;
|
|
138
138
|
* stale versions are swept. Note: a freshly compiled binary triggers a new
|
|
139
|
-
* macOS "allow keychain access" prompt on its first read
|
|
139
|
+
* macOS "allow keychain access" prompt on its first read - expected.
|
|
140
140
|
*/
|
|
141
141
|
function ensureHelper() {
|
|
142
142
|
if (!biometricPlatformSupported()) {
|
|
@@ -153,7 +153,7 @@ function ensureHelper() {
|
|
|
153
153
|
execFileSync("swiftc", ["-O", "-o", bin, src], { stdio: ["ignore", "ignore", "pipe"] });
|
|
154
154
|
}
|
|
155
155
|
catch {
|
|
156
|
-
throw new BiometricUnavailableError("Could not compile the Touch ID helper
|
|
156
|
+
throw new BiometricUnavailableError("Could not compile the Touch ID helper - are the Xcode Command Line Tools installed? (xcode-select --install)");
|
|
157
157
|
}
|
|
158
158
|
for (const f of readdirSync(binDir())) {
|
|
159
159
|
if (f.startsWith(`${HELPER_BASENAME}-`) && f !== `${HELPER_BASENAME}-${hash}`) {
|
|
@@ -216,7 +216,7 @@ export async function storeBiometricPassphrase(account, passphrase) {
|
|
|
216
216
|
}
|
|
217
217
|
/**
|
|
218
218
|
* Biometric-gated read of the stored passphrase. Returns null when the user
|
|
219
|
-
* cancels/fails the check or no passphrase is stored
|
|
219
|
+
* cancels/fails the check or no passphrase is stored - both mean "fall back
|
|
220
220
|
* to typing the passphrase". Throws {@link BiometricUnavailableError} when
|
|
221
221
|
* biometry can't be evaluated at all.
|
|
222
222
|
*/
|
package/dist/cli/accounts.d.ts
CHANGED
package/dist/cli/accounts.js
CHANGED
package/dist/cli/agenc.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `xpay agenc <hire|status>`
|
|
2
|
+
* `xpay agenc <hire|status>` - AgenC marketplace (agenc.ag).
|
|
3
3
|
*
|
|
4
4
|
* AgenC listings show up in `xpay discover` alongside x402 services, but they
|
|
5
5
|
* execute differently: hiring escrows the price in native SOL on-chain, the
|
|
@@ -17,6 +17,6 @@ export declare function runAgencHire(listingPda: string, opts: AgencHireCmdOptio
|
|
|
17
17
|
export interface AgencStatusCmdOptions {
|
|
18
18
|
json?: boolean;
|
|
19
19
|
}
|
|
20
|
-
/** Read-only
|
|
20
|
+
/** Read-only - no profile unlock needed. */
|
|
21
21
|
export declare function runAgencStatus(taskPda: string, opts: AgencStatusCmdOptions): Promise<void>;
|
|
22
22
|
//# sourceMappingURL=agenc.d.ts.map
|
package/dist/cli/agenc.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `xpay agenc <hire|status>`
|
|
2
|
+
* `xpay agenc <hire|status>` - AgenC marketplace (agenc.ag).
|
|
3
3
|
*
|
|
4
4
|
* AgenC listings show up in `xpay discover` alongside x402 services, but they
|
|
5
5
|
* execute differently: hiring escrows the price in native SOL on-chain, the
|
|
@@ -60,7 +60,7 @@ export async function runAgencHire(listingPda, opts) {
|
|
|
60
60
|
const receipt = result.data;
|
|
61
61
|
const elapsed = Date.now() - t0;
|
|
62
62
|
console.log("");
|
|
63
|
-
console.log(chalk.green(`✔ Hired
|
|
63
|
+
console.log(chalk.green(`✔ Hired - ◎${sol.toFixed(4)} SOL escrowed in ${elapsed}ms`));
|
|
64
64
|
console.log(` ${chalk.dim("task:")} ${receipt.task}`);
|
|
65
65
|
console.log(` ${chalk.dim("tx:")} ${receipt.txSig}`);
|
|
66
66
|
console.log(` ${chalk.dim("explorer:")} ${receipt.explorer}`);
|
|
@@ -75,7 +75,7 @@ export async function runAgencHire(listingPda, opts) {
|
|
|
75
75
|
process.exit(1);
|
|
76
76
|
}
|
|
77
77
|
}
|
|
78
|
-
/** Read-only
|
|
78
|
+
/** Read-only - no profile unlock needed. */
|
|
79
79
|
export async function runAgencStatus(taskPda, opts) {
|
|
80
80
|
try {
|
|
81
81
|
const task = await fetchAgencTask(taskPda);
|
|
@@ -99,7 +99,7 @@ export async function runAgencStatus(taskPda, opts) {
|
|
|
99
99
|
}
|
|
100
100
|
console.log("");
|
|
101
101
|
if (task.status === "review") {
|
|
102
|
-
console.log(chalk.dim("The provider submitted a result
|
|
102
|
+
console.log(chalk.dim("The provider submitted a result - review it at:"));
|
|
103
103
|
console.log(chalk.cyan(` https://agenc.ag/tasks/${taskPda}`));
|
|
104
104
|
}
|
|
105
105
|
else if (!["settled", "cancelled", "disputed"].includes(task.status)) {
|
package/dist/cli/balance.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `xpay balance`
|
|
2
|
+
* `xpay balance` - show USDC balance per network for the active profile.
|
|
3
3
|
*
|
|
4
4
|
* Unlocks the active profile (prompts for passphrase if encrypted, unless
|
|
5
5
|
* $XPAY_PASSPHRASE is set for non-interactive use), then queries each
|
package/dist/cli/balance.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `xpay balance`
|
|
2
|
+
* `xpay balance` - show USDC balance per network for the active profile.
|
|
3
3
|
*
|
|
4
4
|
* Unlocks the active profile (prompts for passphrase if encrypted, unless
|
|
5
5
|
* $XPAY_PASSPHRASE is set for non-interactive use), then queries each
|
|
@@ -16,7 +16,7 @@ export async function runBalance(opts) {
|
|
|
16
16
|
const signers = signersFromProfile(profile);
|
|
17
17
|
// Robinhood Chain and Stable always have a signer (see signersFromProfile)
|
|
18
18
|
// even when they're not in the profile's `networks`, so surface them in the
|
|
19
|
-
// default view too
|
|
19
|
+
// default view too - handy as the deposit address for funding trades and
|
|
20
20
|
// for /stable/* x402 calls.
|
|
21
21
|
const configured = profile.config.networks;
|
|
22
22
|
const networks = opts.network
|
|
@@ -41,7 +41,7 @@ export async function runBalance(opts) {
|
|
|
41
41
|
// lookup (Solana only; EVM balances pass through unchanged).
|
|
42
42
|
let tokens = net === "solana" ? await enrichTokenBalances(raw) : raw;
|
|
43
43
|
// Robinhood Chain: the signer only knows a hardcoded token list (WETH),
|
|
44
|
-
// so pull the wallet's full ERC-20 holdings from the chain explorer
|
|
44
|
+
// so pull the wallet's full ERC-20 holdings from the chain explorer -
|
|
45
45
|
// this is how memecoins bought via `xpay trade` show up. Keep the
|
|
46
46
|
// signer's native ETH entry and take every ERC-20 from the explorer.
|
|
47
47
|
if (net === "robinhood") {
|
|
@@ -59,7 +59,7 @@ export async function runBalance(opts) {
|
|
|
59
59
|
const flag = t.verified === false && !t.isNative ? chalk.yellow(" ⚠ unverified") : "";
|
|
60
60
|
console.log(` ${label.padEnd(18)} ${chalk.white(val.padEnd(14))} ${usd}${flag}`);
|
|
61
61
|
// USDT0 is Stable's USDT (an ERC-20 OFT), and its native gas coin
|
|
62
|
-
// reports as USDT
|
|
62
|
+
// reports as USDT - both are dollars, so both count.
|
|
63
63
|
if (t.symbol === "USDC" || t.symbol === "USDT" || t.symbol === "USDT0")
|
|
64
64
|
usdcTotal += t.balance;
|
|
65
65
|
if (t.usdValue !== undefined)
|
package/dist/cli/bento.d.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `xpay bento enable|disable|status`
|
|
2
|
+
* `xpay bento enable|disable|status` - toggle the Bento Guard intent firewall
|
|
3
3
|
* for the active profile.
|
|
4
4
|
*
|
|
5
5
|
* Unlike Sana, Bento has no API key: it authenticates with the wallet's own
|
|
6
6
|
* key (createXPay exposes it via AGENT_WALLET_PRIVATE_KEY at runtime). The one
|
|
7
|
-
* manual step is registering the wallet address once at app.bentoguard.xyz
|
|
7
|
+
* manual step is registering the wallet address once at app.bentoguard.xyz -
|
|
8
8
|
* `enable` prints that address and walks you through it.
|
|
9
9
|
*
|
|
10
10
|
* When enabled, every pay / transfer is screened by Bento's protect() for
|
|
11
|
-
* malicious intent (prompt-injection, wallet-drain) before signing
|
|
11
|
+
* malicious intent (prompt-injection, wallet-drain) before signing - a second
|
|
12
12
|
* gate on top of the local guardrail caps.
|
|
13
13
|
*/
|
|
14
14
|
export declare function runBentoEnable(opts: {
|
package/dist/cli/bento.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `xpay bento enable|disable|status`
|
|
2
|
+
* `xpay bento enable|disable|status` - toggle the Bento Guard intent firewall
|
|
3
3
|
* for the active profile.
|
|
4
4
|
*
|
|
5
5
|
* Unlike Sana, Bento has no API key: it authenticates with the wallet's own
|
|
6
6
|
* key (createXPay exposes it via AGENT_WALLET_PRIVATE_KEY at runtime). The one
|
|
7
|
-
* manual step is registering the wallet address once at app.bentoguard.xyz
|
|
7
|
+
* manual step is registering the wallet address once at app.bentoguard.xyz -
|
|
8
8
|
* `enable` prints that address and walks you through it.
|
|
9
9
|
*
|
|
10
10
|
* When enabled, every pay / transfer is screened by Bento's protect() for
|
|
11
|
-
* malicious intent (prompt-injection, wallet-drain) before signing
|
|
11
|
+
* malicious intent (prompt-injection, wallet-drain) before signing - a second
|
|
12
12
|
* gate on top of the local guardrail caps.
|
|
13
13
|
*/
|
|
14
14
|
import chalk from "chalk";
|
|
@@ -31,18 +31,18 @@ export function runBentoEnable(opts) {
|
|
|
31
31
|
console.log("");
|
|
32
32
|
console.log(chalk.green(`✔ Bento intent firewall enabled for "${name}".`));
|
|
33
33
|
console.log("");
|
|
34
|
-
console.log(chalk.bold(" One-time setup
|
|
34
|
+
console.log(chalk.bold(" One-time setup - register this AGENT wallet:"));
|
|
35
35
|
console.log(` 1. Log in to ${chalk.cyan(DASHBOARD)} with your owner wallet (e.g. Phantom).`);
|
|
36
|
-
console.log(` 2. Register THIS agent wallet address (an on-chain tx
|
|
36
|
+
console.log(` 2. Register THIS agent wallet address (an on-chain tx - owner pays gas):`);
|
|
37
37
|
console.log(` ${chalk.yellow(addresses.solana)}`);
|
|
38
|
-
console.log(` 3. Leave spend limits unset
|
|
39
|
-
console.log(chalk.dim(" Note: the owner wallet (dashboard login) and this agent wallet are usually different
|
|
38
|
+
console.log(` 3. Leave spend limits unset - xPay's own guardrail handles caps; Bento adds intent screening.`);
|
|
39
|
+
console.log(chalk.dim(" Note: the owner wallet (dashboard login) and this agent wallet are usually different - "));
|
|
40
40
|
console.log(chalk.dim(" register the address shown above, not your login wallet."));
|
|
41
41
|
console.log("");
|
|
42
42
|
console.log(chalk.dim(" From now on every pay / transfer is screened by Bento's intent"));
|
|
43
43
|
console.log(chalk.dim(" analysis (prompt-injection / wallet-drain) before signing."));
|
|
44
44
|
console.log(chalk.dim(" Until the address is registered, paid calls fail with a"));
|
|
45
|
-
console.log(chalk.dim(' "not registered" error
|
|
45
|
+
console.log(chalk.dim(' "not registered" error - that is expected.'));
|
|
46
46
|
console.log("");
|
|
47
47
|
}
|
|
48
48
|
export function runBentoDisable(opts) {
|
package/dist/cli/bento.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bento.js","sourceRoot":"","sources":["../../src/cli/bento.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACzE,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACnF,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEjD,MAAM,SAAS,GAAG,6BAA6B,CAAC;AAEhD,SAAS,oBAAoB,CAAC,WAAoB;IAChD,MAAM,IAAI,GAAG,WAAW,IAAI,gBAAgB,EAAE,CAAC;IAC/C,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC;QACzB,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,cAAc,IAAI,uCAAuC,CAAC,CAAC,CAAC;QACpF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,IAA0B;IACvD,MAAM,IAAI,GAAG,oBAAoB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAChD,MAAM,EAAE,SAAS,EAAE,GAAG,cAAc,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC;IAExD,eAAe,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAE5B,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,wCAAwC,IAAI,IAAI,CAAC,CAAC,CAAC;IAC3E,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,gDAAgD,CAAC,CAAC,CAAC;IAC1E,OAAO,CAAC,GAAG,CAAC,oBAAoB,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,yCAAyC,CAAC,CAAC;IAChG,OAAO,CAAC,GAAG,CAAC,8EAA8E,CAAC,CAAC;IAC5F,OAAO,CAAC,GAAG,CAAC,UAAU,KAAK,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACxD,OAAO,CAAC,GAAG,CACT,mGAAmG,CACpG,CAAC;IACF,OAAO,CAAC,GAAG,CACT,KAAK,CAAC,GAAG,CACP,
|
|
1
|
+
{"version":3,"file":"bento.js","sourceRoot":"","sources":["../../src/cli/bento.ts"],"names":[],"mappings":"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"}
|
package/dist/cli/biometric.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `xpay biometric enable|disable|status`
|
|
2
|
+
* `xpay biometric enable|disable|status` - Touch ID unlock for a profile
|
|
3
3
|
* (macOS only). `enable` verifies the passphrase against the wallet, runs a
|
|
4
4
|
* Touch ID check, then stores the passphrase in the login keychain; from
|
|
5
5
|
* then on `unlockActive` offers Touch ID before falling back to typing.
|
package/dist/cli/biometric.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `xpay biometric enable|disable|status`
|
|
2
|
+
* `xpay biometric enable|disable|status` - Touch ID unlock for a profile
|
|
3
3
|
* (macOS only). `enable` verifies the passphrase against the wallet, runs a
|
|
4
4
|
* Touch ID check, then stores the passphrase in the login keychain; from
|
|
5
5
|
* then on `unlockActive` offers Touch ID before falling back to typing.
|
|
@@ -26,7 +26,7 @@ export async function runBiometricEnable(opts) {
|
|
|
26
26
|
}
|
|
27
27
|
const wallet = readWalletFile(profilePath(name));
|
|
28
28
|
if (!wallet.encrypted) {
|
|
29
|
-
console.error(chalk.red(`✗ Profile "${name}" is unencrypted (dev wallet)
|
|
29
|
+
console.error(chalk.red(`✗ Profile "${name}" is unencrypted (dev wallet) - there is no passphrase to protect.`));
|
|
30
30
|
process.exit(1);
|
|
31
31
|
}
|
|
32
32
|
let biometry;
|
|
@@ -38,7 +38,7 @@ export async function runBiometricEnable(opts) {
|
|
|
38
38
|
process.exit(1);
|
|
39
39
|
}
|
|
40
40
|
if (!biometry) {
|
|
41
|
-
console.error(chalk.red("✗ No usable biometry
|
|
41
|
+
console.error(chalk.red("✗ No usable biometry - enroll Touch ID in System Settings first."));
|
|
42
42
|
process.exit(1);
|
|
43
43
|
}
|
|
44
44
|
// Verify the passphrase actually unlocks this wallet before storing it.
|
|
@@ -59,14 +59,14 @@ export async function runBiometricEnable(opts) {
|
|
|
59
59
|
try {
|
|
60
60
|
const ok = await biometricPrompt(`enable Touch ID unlock for the xPay profile "${name}"`);
|
|
61
61
|
if (!ok) {
|
|
62
|
-
console.log(chalk.yellow("Cancelled
|
|
62
|
+
console.log(chalk.yellow("Cancelled - biometric unlock not enabled."));
|
|
63
63
|
return;
|
|
64
64
|
}
|
|
65
65
|
await storeBiometricPassphrase(name, passphrase);
|
|
66
66
|
}
|
|
67
67
|
catch (err) {
|
|
68
68
|
const msg = err instanceof BiometricUnavailableError
|
|
69
|
-
? `${err.message}
|
|
69
|
+
? `${err.message} - biometric unlock not enabled.`
|
|
70
70
|
: err.message;
|
|
71
71
|
console.error(chalk.red(`✗ ${msg}`));
|
|
72
72
|
process.exit(1);
|
|
@@ -75,7 +75,7 @@ export async function runBiometricEnable(opts) {
|
|
|
75
75
|
console.log("");
|
|
76
76
|
console.log(chalk.green(`✔ Touch ID unlock enabled for "${name}".`));
|
|
77
77
|
console.log(chalk.dim(" The passphrase now lives in your login keychain, gated by Touch ID."));
|
|
78
|
-
console.log(chalk.dim(" macOS may ask once to allow keychain access
|
|
78
|
+
console.log(chalk.dim(" macOS may ask once to allow keychain access - choose \"Always Allow\"."));
|
|
79
79
|
console.log(chalk.dim(" Your passphrase still works everywhere and remains the recovery path."));
|
|
80
80
|
}
|
|
81
81
|
export async function runBiometricDisable(opts) {
|
|
@@ -88,10 +88,10 @@ export async function runBiometricDisable(opts) {
|
|
|
88
88
|
console.error(chalk.red(`✗ ${err.message}`));
|
|
89
89
|
process.exit(1);
|
|
90
90
|
}
|
|
91
|
-
// Helper unbuildable (e.g. CLT removed)
|
|
91
|
+
// Helper unbuildable (e.g. CLT removed) - still clear the config flag.
|
|
92
92
|
}
|
|
93
93
|
setProfileBiometric(name, false);
|
|
94
|
-
console.log(chalk.green(`✔ Touch ID unlock disabled for "${name}"
|
|
94
|
+
console.log(chalk.green(`✔ Touch ID unlock disabled for "${name}" - keychain entry removed.`));
|
|
95
95
|
}
|
|
96
96
|
export async function runBiometricStatus(profileName) {
|
|
97
97
|
const name = resolveProfileOrExit(profileName);
|