@jaw.id/cli 0.1.26 → 0.2.1
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/base-command.js +3 -1
- package/dist/base-command.js.map +1 -1
- package/dist/commands/config/set.js +140 -12
- package/dist/commands/config/set.js.map +1 -1
- package/dist/commands/config/show.js +3 -1
- package/dist/commands/config/show.js.map +1 -1
- package/dist/commands/config/write.js +6 -4
- package/dist/commands/config/write.js.map +1 -1
- package/dist/commands/disconnect.js +24 -10
- package/dist/commands/disconnect.js.map +1 -1
- package/dist/commands/mcp/index.js +2426 -94
- package/dist/commands/mcp/index.js.map +1 -1
- package/dist/commands/rpc/call.js +197 -45
- package/dist/commands/rpc/call.js.map +1 -1
- package/dist/commands/session/add.js +1547 -0
- package/dist/commands/session/add.js.map +1 -0
- package/dist/commands/session/revoke.js +181 -54
- package/dist/commands/session/revoke.js.map +1 -1
- package/dist/commands/session/setup.js +516 -65
- package/dist/commands/session/setup.js.map +1 -1
- package/dist/commands/session/status.js +315 -6
- package/dist/commands/session/status.js.map +1 -1
- package/dist/commands/version.js +3 -1
- package/dist/commands/version.js.map +1 -1
- package/dist/commands/x402/log.js +344 -0
- package/dist/commands/x402/log.js.map +1 -0
- package/dist/commands/x402/pay.js +2122 -0
- package/dist/commands/x402/pay.js.map +1 -0
- package/dist/commands/x402/status.js +1047 -0
- package/dist/commands/x402/status.js.map +1 -0
- package/dist/index.js +41 -14
- package/dist/index.js.map +1 -1
- package/dist/lib/bridge-singleton.js +41 -14
- package/dist/lib/bridge-singleton.js.map +1 -1
- package/dist/lib/config.js +26 -3
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/keystore.js +13 -2
- package/dist/lib/keystore.js.map +1 -1
- package/dist/lib/paths.js +3 -1
- package/dist/lib/paths.js.map +1 -1
- package/dist/lib/payment-lock.js +121 -0
- package/dist/lib/payment-lock.js.map +1 -0
- package/dist/lib/session-bridge.js +148 -24
- package/dist/lib/session-bridge.js.map +1 -1
- package/dist/lib/session-config.js +78 -11
- package/dist/lib/session-config.js.map +1 -1
- package/dist/lib/terminal.js +22 -0
- package/dist/lib/terminal.js.map +1 -0
- package/dist/lib/validation.js +3 -3
- package/dist/lib/validation.js.map +1 -1
- package/dist/lib/ws-bridge.js +22 -10
- package/dist/lib/ws-bridge.js.map +1 -1
- package/dist/mcp/handlers/config.js +73 -6
- package/dist/mcp/handlers/config.js.map +1 -1
- package/dist/mcp/handlers/daemon.js +43 -12
- package/dist/mcp/handlers/daemon.js.map +1 -1
- package/dist/mcp/handlers/resources.js +119 -0
- package/dist/mcp/handlers/resources.js.map +1 -1
- package/dist/mcp/handlers/rpc.js +269 -60
- package/dist/mcp/handlers/rpc.js.map +1 -1
- package/dist/mcp/helpers.js +50 -3
- package/dist/mcp/helpers.js.map +1 -1
- package/dist/mcp/server.js +2426 -94
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/tools.js +43 -3
- package/dist/mcp/tools.js.map +1 -1
- package/dist/x402/log-view.js +160 -0
- package/dist/x402/log-view.js.map +1 -0
- package/dist/x402/status-report.js +90 -0
- package/dist/x402/status-report.js.map +1 -0
- package/oclif.manifest.json +405 -11
- package/package.json +5 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/lib/paths.ts","../../../src/lib/config.ts","../../../src/lib/output.ts","../../../src/base-command.ts","../../../src/lib/keystore.ts","../../../src/lib/session-config.ts","../../../src/x402/asset-registry.ts","../../../src/x402/balance.ts","../../../src/x402/payer.ts","../../../src/x402/ledger.ts","../../../src/x402/amount.ts","../../../src/x402/period.ts","../../../src/x402/policy.ts","../../../src/x402/permission-onchain.ts","../../../src/x402/spend-window.ts","../../../src/x402/gas-reserve.ts","../../../src/lib/terminal.ts","../../../src/x402/status-report.ts","../../../src/x402/permission-recovery.ts","../../../src/commands/x402/status.ts"],"names":["fs2","fs3","fs4","parseAbi","base"],"mappings":"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","file":"status.js","sourcesContent":["import * as path from 'node:path';\nimport * as os from 'node:os';\n\nconst JAW_DIR = path.join(os.homedir(), '.jaw');\n\nexport const PATHS = {\n root: JAW_DIR,\n config: path.join(JAW_DIR, 'config.json'),\n session: path.join(JAW_DIR, 'session.json'),\n relay: path.join(JAW_DIR, 'relay.json'),\n keystore: path.join(JAW_DIR, 'keystore.json'),\n sessionConfig: path.join(JAW_DIR, 'session-config.json'),\n x402Log: path.join(JAW_DIR, 'x402-log.jsonl'),\n paymentLock: path.join(JAW_DIR, 'x402-payment.lock'),\n} as const;\n","import * as fs from 'node:fs';\nimport { PATHS } from './paths.js';\nimport type { JawConfig, SettableConfigKey } from './types.js';\nimport type { X402Policy, X402PolicyKey } from '../x402/policy.js';\nimport { isValidKeysUrl, isValidRelayUrl } from './validation.js';\n\nexport function ensureDir(dir: string): void {\n fs.mkdirSync(dir, { recursive: true, mode: 0o700 });\n fs.chmodSync(dir, 0o700);\n}\n\nfunction migrateConfig(config: JawConfig): JawConfig {\n if (config.paymasterUrl && !config.paymasters) {\n const chainId = config.defaultChain ?? 1;\n config.paymasters = { [chainId]: { url: config.paymasterUrl } };\n delete config.paymasterUrl;\n saveConfig(config);\n }\n return config;\n}\n\nexport function loadConfig(): JawConfig {\n if (!fs.existsSync(PATHS.config)) {\n return {};\n }\n const raw = fs.readFileSync(PATHS.config, 'utf-8');\n try {\n const config = JSON.parse(raw) as JawConfig;\n return migrateConfig(config);\n } catch {\n throw new Error(\n `Config file at ${PATHS.config} is not valid JSON. Run \\`jaw config set apiKey=<key>\\` to reset it.`\n );\n }\n}\n\nexport function saveConfig(config: JawConfig): void {\n ensureDir(PATHS.root);\n fs.writeFileSync(PATHS.config, JSON.stringify(config, null, 2) + '\\n', {\n encoding: 'utf-8',\n mode: 0o600,\n });\n}\n\n/** Paymaster URLs embed provider API keys as query params (e.g. Pimlico's ?apikey=...). */\nfunction redactUrlSecrets(url: string): string {\n try {\n const parsed = new URL(url);\n for (const key of [...parsed.searchParams.keys()]) {\n parsed.searchParams.set(key, '***');\n }\n return parsed.toString();\n } catch {\n return '***';\n }\n}\n\nexport function redactConfig(config: JawConfig): Record<string, unknown> {\n return {\n ...config,\n apiKey: config.apiKey ? `${config.apiKey.slice(0, 8)}...` : undefined,\n ...(config.paymasters && {\n paymasters: Object.fromEntries(\n Object.entries(config.paymasters).map(([chainId, pm]) => [\n chainId,\n // context is a free-form object (usually just a sponsorshipPolicyId) but a\n // provider could stash a token there, so mask it rather than hand it to the agent.\n { ...pm, url: redactUrlSecrets(pm.url), context: pm.context ? '***' : undefined },\n ])\n ),\n }),\n };\n}\n\n/**\n * Set one field of the `x402` payment policy. Array fields (allow-lists) are\n * comma-split. Intentionally NOT reachable from the MCP tool surface: an agent\n * must not be able to raise its own spending caps — only a human at the CLI.\n */\nexport function setX402PolicyValue(key: X402PolicyKey, value: string): void {\n const config = loadConfig();\n const x402: X402Policy = { ...(config.x402 ?? {}) };\n if (key === 'maxAmountPerPayment' || key === 'maxTotalPerSession' || key === 'topUpFloat') {\n // These are base-unit amounts read via BigInt() on the hot payment path;\n // reject anything that isn't a non-negative integer here so a bad value\n // can never turn into a per-payment failure later.\n if (!/^\\d+$/.test(value.trim())) {\n throw new Error(`${key} must be a non-negative integer (base units), got: ${value}`);\n }\n x402[key] = value.trim();\n } else {\n x402[key] = value\n .split(',')\n .map((v) => v.trim())\n .filter(Boolean);\n }\n saveConfig({ ...config, x402 });\n}\n\nexport function setConfigValue(key: SettableConfigKey, value: string | number): void {\n if (key === 'keysUrl' && typeof value === 'string' && !isValidKeysUrl(value)) {\n throw new Error(`Untrusted keysUrl: ${value}. Must be a *.jaw.id domain (HTTPS) or localhost.`);\n }\n if (key === 'relayUrl' && typeof value === 'string' && !isValidRelayUrl(value)) {\n throw new Error(`Untrusted relayUrl: ${value}. Must be wss://*.jaw.id or ws://localhost.`);\n }\n // Numeric keys: coerce + validate here so EVERY caller is safe. The MCP tool\n // passes raw strings (its schema types value as a string), so without this a\n // `sessionExpiry: \"abc\"` would land as a string and turn `expiry * 86400`\n // into NaN, and a `defaultChain` string would ride into an RPC URL verbatim.\n let toStore: string | number = value;\n if (key === 'defaultChain' || key === 'sessionExpiry') {\n // Strict: reject anything parseInt would silently truncate ('1.5' -> 1,\n // '0x10' -> 0, '10abc' -> 10). Only a bare positive decimal integer passes.\n const n = typeof value === 'number' ? value : /^\\d+$/.test(value.trim()) ? parseInt(value.trim(), 10) : NaN;\n if (!Number.isInteger(n) || n <= 0) {\n throw new Error(`${key} must be a positive integer, got: ${JSON.stringify(value)}`);\n }\n toStore = n;\n }\n const config = loadConfig();\n const updated = { ...config, [key]: toStore };\n saveConfig(updated);\n}\n","import type { OutputFormat } from './types.js';\n\nexport function formatOutput(data: unknown, format: OutputFormat): string {\n if (format === 'json') {\n return JSON.stringify(data, replaceBigInt, 2);\n }\n return formatHuman(data);\n}\n\nfunction replaceBigInt(_key: string, value: unknown): unknown {\n if (typeof value === 'bigint') {\n return value.toString();\n }\n return value;\n}\n\nfunction formatHuman(data: unknown, indent = 0): string {\n if (data === null || data === undefined) {\n return 'null';\n }\n\n if (typeof data === 'string' || typeof data === 'number' || typeof data === 'boolean' || typeof data === 'bigint') {\n return String(data);\n }\n\n if (Array.isArray(data)) {\n if (data.length === 0) return '(empty)';\n return data.map((item, i) => `${i + 1}. ${formatHuman(item, indent + 2)}`).join('\\n');\n }\n\n if (typeof data === 'object') {\n const entries = Object.entries(data as Record<string, unknown>);\n if (entries.length === 0) return '(empty)';\n const pad = ' '.repeat(indent);\n const maxKeyLen = Math.max(...entries.map(([k]) => k.length));\n return entries\n .map(([key, val]) => {\n const paddedKey = key.padEnd(maxKeyLen);\n const valStr = typeof val === 'object' && val !== null ? '\\n' + formatHuman(val, indent + 2) : String(val);\n return `${pad}${paddedKey} ${valStr}`;\n })\n .join('\\n');\n }\n\n return String(data);\n}\n","import { Command, Flags } from '@oclif/core';\nimport type { OutputFormat } from './lib/types.js';\nimport { loadConfig } from './lib/config.js';\nimport { formatOutput } from './lib/output.js';\n\nexport abstract class BaseCommand extends Command {\n static override baseFlags = {\n output: Flags.string({\n char: 'o',\n description: 'Output format',\n options: ['json', 'human'],\n default: 'human',\n env: 'JAW_OUTPUT',\n }),\n chain: Flags.integer({\n char: 'c',\n description: 'Chain ID',\n env: 'JAW_CHAIN_ID',\n }),\n 'api-key': Flags.string({\n description: 'JAW API key',\n env: 'JAW_API_KEY',\n }),\n yes: Flags.boolean({\n char: 'y',\n description: 'Skip confirmations (for AI agents)',\n default: false,\n }),\n quiet: Flags.boolean({\n char: 'q',\n description: 'Suppress non-essential output',\n default: false,\n }),\n };\n\n protected resolveApiKey(flags: { 'api-key'?: string }): string {\n const apiKey = flags['api-key'] ?? loadConfig().apiKey;\n if (!apiKey) {\n this.error('API key required. Set via --api-key, JAW_API_KEY env, or `jaw config set apiKey <key>`');\n }\n return apiKey;\n }\n\n protected resolveChainId(flags: { chain?: number }): number {\n const chainId = flags.chain ?? loadConfig().defaultChain;\n if (!chainId) {\n this.error('Chain ID required. Set via --chain, JAW_CHAIN_ID env, or `jaw config set defaultChain <id>`');\n }\n return chainId;\n }\n\n protected outputResult(data: unknown, format: OutputFormat): void {\n const output = formatOutput(data, format);\n this.log(output);\n }\n}\n","import * as crypto from 'node:crypto';\nimport * as fs from 'node:fs';\nimport { PATHS } from './paths.js';\nimport { ensureDir } from './config.js';\n\ninterface KeystoreFile {\n version: 2;\n privateKey: string;\n address: string;\n createdAt: string;\n}\n\n/**\n * Generate a random secp256k1 private key as 0x-prefixed hex.\n */\nexport function generateSessionKey(): `0x${string}` {\n const bytes = crypto.randomBytes(32);\n return `0x${bytes.toString('hex')}` as `0x${string}`;\n}\n\n/**\n * Save private key to keystore.json.\n * On-chain PermissionManager is the real security boundary — the session key\n * can only act within its granted permission scope regardless of local access.\n */\nexport function saveKeystore(privateKeyHex: string, address: string): void {\n const keystore: KeystoreFile = {\n version: 2,\n privateKey: privateKeyHex,\n address,\n createdAt: new Date().toISOString(),\n };\n\n ensureDir(PATHS.root);\n fs.writeFileSync(PATHS.keystore, JSON.stringify(keystore, null, 2) + '\\n', {\n encoding: 'utf-8',\n mode: 0o600,\n });\n // `mode` is only honored on file creation, not overwrite — re-apply explicitly.\n fs.chmodSync(PATHS.keystore, 0o600);\n}\n\n/**\n * Load private key hex from keystore.json.\n */\nexport function loadSessionKey(): string {\n if (!fs.existsSync(PATHS.keystore)) {\n throw new Error('No session configured. Run `jaw session setup` first.');\n }\n\n const contents = fs.readFileSync(PATHS.keystore, 'utf-8');\n let parsed: KeystoreFile;\n try {\n parsed = JSON.parse(contents) as KeystoreFile;\n } catch {\n throw new Error(`Keystore at ${PATHS.keystore} is corrupted. Run \\`jaw session setup\\` to recreate it.`);\n }\n return parsed.privateKey;\n}\n\n/**\n * Load the session address from keystore.json, or null if it cannot be read.\n * Used to name the key in messages, so a corrupt keystore degrades the message\n * rather than the command.\n */\nexport function tryLoadKeystoreAddress(): string | null {\n try {\n if (!fs.existsSync(PATHS.keystore)) return null;\n const parsed = JSON.parse(fs.readFileSync(PATHS.keystore, 'utf-8')) as KeystoreFile;\n return parsed.address ?? null;\n } catch {\n return null;\n }\n}\n\n/**\n * Delete keystore.json.\n */\nexport function deleteKeystore(): void {\n if (fs.existsSync(PATHS.keystore)) {\n fs.unlinkSync(PATHS.keystore);\n }\n}\n\n/**\n * Check if keystore.json exists.\n */\nexport function keystoreExists(): boolean {\n return fs.existsSync(PATHS.keystore);\n}\n","import * as fs from 'node:fs';\nimport { PATHS } from './paths.js';\nimport { ensureDir } from './config.js';\n\n/**\n * How the session account address is derived, as it appears on disk.\n *\n * 'eip7702' is the only one written now: the session key EOA itself, upgraded\n * in place via a delegation attached to its first userOp, so the session\n * account and the x402 payer are one address.\n *\n * 'counterfactual' is what earlier versions wrote, and what a config from\n * before the field existed means: a CREATE2 prediction from the account\n * factory, a second address that holds nothing and so cannot be charged for\n * the gas of the ops it sends. Still in the union because those files exist and\n * `SessionBridge` has to recognise them; `SessionSetup` never writes it.\n */\nexport type SessionMode = 'counterfactual' | 'eip7702';\n\n/**\n * The granted permission as `JustaPermissionManager` stores it.\n *\n * Every view on the manager takes `Permission calldata` and hashes it inside:\n * `isApproved`, `isRevoked` and `getCurrentPeriod` all do, and none of them\n * accept an id. A session holding only `permissionId` therefore holds the one\n * field the chain will not answer about, which is why nothing local could tell\n * that a permission had been revoked from another device.\n *\n * The grant response already carries all of it, so this costs a wider write\n * rather than a network call. Optional on read for two reasons: configs written\n * before this field exist, and a wallet running an older core answers with the\n * id alone. Consumers fall back to the local file, which is what those sessions\n * already meant.\n */\nexport interface GrantedPermission {\n account: string;\n spender: string;\n /**\n * Unix seconds the permission starts at. Also the anchor the contract steps\n * its period windows from, and what the local policy anchors on. A summary\n * field used to hold an approximation of it taken from the local clock.\n */\n start: number;\n /** Unix seconds the permission ends at. */\n end: number;\n /** Hex, as the grant returned it. Widened to a bigint at encode time. */\n salt: string;\n calls: Array<{ target: string; selector: string }>;\n spends: Array<{ token: string; allowance: string; unit: string; multiplier: number }>;\n}\n\nconst ADDRESS_RE = /^0x[0-9a-fA-F]{40}$/;\nconst SELECTOR_RE = /^0x[0-9a-fA-F]{8}$/;\nconst HEX_RE = /^0x[0-9a-fA-F]+$/;\n/** Decimal or hex, matching what the SDK hands to `BigInt()`. */\nconst ALLOWANCE_RE = /^(0x[0-9a-fA-F]+|[0-9]+)$/;\n/** The units a grant may carry. `year` has no on-chain enum and is rewritten before encoding. */\nconst SPEND_UNITS = new Set(['minute', 'hour', 'day', 'week', 'month', 'year', 'forever']);\n\nfunction isPositiveInt(value: unknown): value is number {\n return typeof value === 'number' && Number.isInteger(value) && value > 0;\n}\n\n/**\n * The permission struct out of a `wallet_grantPermissions` response, or\n * undefined when the response does not carry a usable one.\n *\n * Undefined rather than a throw, on every malformed field. By the time this\n * runs the grant is already on chain, and refusing to write the session over a\n * field that arrived in an unexpected shape would strand a live permission with\n * no local record of it. A session that skips this keeps behaving the way every\n * session behaved before the field existed.\n *\n * Strict about what it does accept: a struct that is wrong in any part hashes\n * to something other than the granted permission, so every on-chain read made\n * from it would quietly answer about a permission that does not exist.\n */\nexport function parseGrantedPermission(raw: unknown): GrantedPermission | undefined {\n if (typeof raw !== 'object' || raw === null) return undefined;\n const r = raw as Record<string, unknown>;\n\n const { account, spender, salt } = r;\n if (typeof account !== 'string' || !ADDRESS_RE.test(account)) return undefined;\n if (typeof spender !== 'string' || !ADDRESS_RE.test(spender)) return undefined;\n if (typeof salt !== 'string' || !HEX_RE.test(salt)) return undefined;\n if (!isPositiveInt(r.start) || !isPositiveInt(r.end)) return undefined;\n\n if (!Array.isArray(r.calls) || r.calls.length === 0) return undefined;\n const calls: GrantedPermission['calls'] = [];\n for (const entry of r.calls) {\n if (typeof entry !== 'object' || entry === null) return undefined;\n const { target, selector } = entry as Record<string, unknown>;\n if (typeof target !== 'string' || !ADDRESS_RE.test(target)) return undefined;\n // The response builds the selector from the signature, so an entry without\n // one cannot be reconstructed: the signature it came from is not returned.\n if (typeof selector !== 'string' || !SELECTOR_RE.test(selector)) return undefined;\n calls.push({ target, selector });\n }\n\n if (!Array.isArray(r.spends)) return undefined;\n const spends: GrantedPermission['spends'] = [];\n for (const entry of r.spends) {\n if (typeof entry !== 'object' || entry === null) return undefined;\n const { token, allowance, unit, multiplier } = entry as Record<string, unknown>;\n if (typeof token !== 'string' || !ADDRESS_RE.test(token)) return undefined;\n if (typeof allowance !== 'string' || !ALLOWANCE_RE.test(allowance)) return undefined;\n if (typeof unit !== 'string' || !SPEND_UNITS.has(unit)) return undefined;\n // `multiplier` is a uint16 on chain and defaults to 1 in the grant, but the\n // encoded struct has to carry the number the permission was hashed with,\n // so an absent one is a struct we cannot rebuild rather than a 1.\n if (!isPositiveInt(multiplier) || multiplier > 65535) return undefined;\n spends.push({ token, allowance, unit, multiplier });\n }\n\n return { account, spender, start: r.start, end: r.end, salt, calls, spends };\n}\n\n/**\n * Whether this session predates the CLI settling on one account derivation, so\n * its permission belongs to an address separate from the session key and no op\n * it sends can be charged for its own gas.\n *\n * Named rather than compared inline: three callers ask this, and each one\n * spelled as `mode !== 'eip7702'` reads like a check for a variant among\n * several, when the only question is whether the session is still usable.\n */\nexport function isLegacySession(config: Pick<SessionConfig, 'mode'>): boolean {\n return config.mode !== 'eip7702';\n}\n\n/**\n * A permission this CLI granted and then stopped tracking.\n *\n * `session setup` replaces a session rather than adding to it, and it does not\n * always revoke what it replaces: the interactive path takes no for an answer,\n * and `--yes` never revokes at all. The session key is reused by default, so\n * the address the session signs with then holds two live grants while the\n * config names one. Its real authority is the sum of both, `x402 status`\n * reports only the new one, and `session revoke` could not reach the old one\n * at all, because the id lived in the file that was overwritten.\n *\n * Keeping the id is what makes it reachable again. Nothing here revokes on its\n * own: setup already asked.\n */\nexport interface OrphanedPermission {\n id: string;\n chainId: number;\n /** Unix seconds. */\n expiry: number;\n}\n\nexport interface SessionConfig {\n ownerAddress: string;\n sessionAddress: string;\n permissionId: string;\n chainId: number;\n expiry: number;\n createdAt: string;\n mode?: SessionMode;\n /** The struct the on-chain reads need. Absent on older sessions; see the type. */\n permission?: GrantedPermission;\n /** Permissions this key still holds that the session no longer names. */\n orphanedPermissions?: OrphanedPermission[];\n /**\n * Whether the permission this session names has already been revoked, while\n * something else it holds has not.\n *\n * Its own field rather than a rewritten `expiry`, which is what this was\n * first. `expiry` means when the permission ends, and the recovered-struct\n * check compares it against the permission's own `end` to know the relay\n * answered about this session: overwriting it made that comparison fail\n * forever, so a partly revoked session could never recover its struct and\n * went back to reporting \"cannot tell\", which is the state recovery exists to\n * end. A field that says one thing cannot be borrowed to say another.\n */\n permissionRevoked?: boolean;\n}\n\n/**\n * The orphans still worth carrying, newest first.\n *\n * An expired permission authorises nothing, so it is dropped rather than\n * accumulating in the file for the life of the machine. Dropping it is the same\n * decision `session revoke` makes when it skips the browser for an expired\n * session.\n */\nexport function liveOrphans(\n orphans: OrphanedPermission[] | undefined,\n now: number = Date.now() / 1000\n): OrphanedPermission[] {\n return (orphans ?? []).filter((orphan) => orphan.expiry > now);\n}\n\n/**\n * `mode` is required and pinned here, unlike on the read side where it stays\n * optional to describe files earlier versions wrote. Nothing but `SessionSetup`\n * writes a session, and a session written without the mode would be refused by\n * `SessionBridge` as if an old CLI had made it, so the compiler holds the\n * invariant rather than a test having to.\n */\nexport function saveSessionConfig(\n input: Omit<SessionConfig, 'createdAt' | 'mode'> & {\n mode: 'eip7702';\n /**\n * Carried over when a session is being replaced in place rather than\n * started, which `session add` does. `createdAt` is what the session total\n * is counted from (`sumSpentSince(payer, session.createdAt)`), so stamping a\n * fresh one there would hand the session cap a clean slate as a side effect\n * of adding a capability.\n */\n createdAt?: string;\n }\n): void {\n writeSessionConfig({ ...input, createdAt: input.createdAt ?? new Date().toISOString() });\n}\n\n/**\n * Written to a temporary file and renamed over the real one, which is atomic on\n * the same filesystem, so a reader never sees a half-written config.\n *\n * It matters more than it did: recovering the permission struct made two\n * commands that only ever read (`x402 status`, `session status`) into writers,\n * and the MCP server runs alongside a terminal, so two processes writing at\n * once is ordinary rather than exotic. A torn file loses the permission id,\n * which is exactly the stranding the orphan list exists to prevent.\n */\nfunction writeSessionConfig(config: SessionConfig): void {\n ensureDir(PATHS.root);\n const temp = `${PATHS.sessionConfig}.${process.pid}.tmp`;\n fs.writeFileSync(temp, JSON.stringify(config, null, 2) + '\\n', { encoding: 'utf-8', mode: 0o600 });\n fs.chmodSync(temp, 0o600);\n fs.renameSync(temp, PATHS.sessionConfig);\n}\n\n/**\n * Store a permission struct recovered for a session that was written without\n * one, leaving the rest of the file alone. Same reason as below for not going\n * through `saveSessionConfig`: it stamps a fresh `createdAt`.\n */\nexport function saveRecoveredPermission(config: SessionConfig, permission: GrantedPermission): boolean {\n // Merged into the file as it is now, not into the snapshot the caller loaded.\n // Recovery holds its copy across a relay round trip, and in that time a\n // `session revoke` running beside it writes progress between browser\n // approvals: renaming the old copy back over it would restore the expiry and\n // the orphan list that revoke had just cleared, and the next revoke would\n // re-attempt ids that are already gone. Reading immediately before the write\n // does not make this a transaction, it makes the window microseconds instead\n // of the length of a network call.\n //\n // A session that disappeared in the meantime stays gone: the only thing being\n // added here is a cache of something the relay can produce again.\n const current = tryLoadSessionConfig();\n if (!current || current.permissionId !== config.permissionId) return false;\n writeSessionConfig({ ...current, permission });\n return true;\n}\n\n/**\n * Record what a revoke has already done, so the rest of it can be retried.\n *\n * Revoking is not idempotent: core reads the permission from the relay before\n * sending and deletes it from there afterwards, so a second attempt at an id\n * already revoked fails before it sends anything. A session left naming an id\n * that is gone therefore costs a browser round trip that can only fail, which\n * is why what succeeded has to come out of the file as it succeeds.\n *\n * `ownPermissionRevoked` is recorded on its own field. Borrowing `expiry` for\n * it, which is what this did first, made that field mean two things at once and\n * broke the recovered-struct check that compares it against the permission's\n * own `end`. What it is for is unchanged: stopping the next revoke from\n * attempting an id the relay no longer has.\n *\n * Separate from `saveSessionConfig` because that one stamps a fresh\n * `createdAt`, and `createdAt` is what the session total is counted from\n * (`sumSpentSince(payer, session.createdAt)`). Editing a session through it\n * would hand the session cap a clean slate as a side effect of revoking one\n * permission.\n */\nexport function saveRevokeProgress(\n config: SessionConfig,\n progress: { orphans: OrphanedPermission[]; ownPermissionRevoked: boolean }\n): void {\n // Merged into the file as it stands, for the same reason\n // `saveRecoveredPermission` does: this runs between browser round trips, and\n // a recovery finishing beside it would otherwise be dropped by the next\n // progress write.\n const next: SessionConfig = { ...(tryLoadSessionConfig() ?? config) };\n if (progress.orphans.length > 0) next.orphanedPermissions = progress.orphans;\n else delete next.orphanedPermissions;\n if (progress.ownPermissionRevoked) next.permissionRevoked = true;\n writeSessionConfig(next);\n}\n\nexport function sessionConfigExists(): boolean {\n return fs.existsSync(PATHS.sessionConfig);\n}\n\nexport function loadSessionConfig(): SessionConfig {\n if (!fs.existsSync(PATHS.sessionConfig)) {\n throw new Error('No session configured. Run `jaw session setup` first.');\n }\n const raw = fs.readFileSync(PATHS.sessionConfig, 'utf-8');\n try {\n return JSON.parse(raw) as SessionConfig;\n } catch {\n throw new Error(`Session config at ${PATHS.sessionConfig} is corrupted. Run \\`jaw session setup\\` to recreate it.`);\n }\n}\n\n/**\n * Like `loadSessionConfig`, but returns null instead of throwing when the file\n * is missing or unreadable. For callers that can recover from a keystore whose\n * session-config is gone (interrupted setup, manual deletion, partial restore)\n * rather than callers that need an existing session to do their job.\n */\nexport function tryLoadSessionConfig(): SessionConfig | null {\n try {\n return loadSessionConfig();\n } catch {\n return null;\n }\n}\n\nexport function deleteSessionConfig(): void {\n if (fs.existsSync(PATHS.sessionConfig)) {\n fs.unlinkSync(PATHS.sessionConfig);\n }\n}\n","// USDC asset registry, mirrored from the backend's\n// `apps/ens/src/external/payment/asset-registry.ts`. Keep this in sync when the\n// server adds a chain. `wireNetwork` is the CAIP-2 id used on the x402 v2 wire.\n\nexport interface UsdcAsset {\n address: `0x${string}`;\n chainId: number;\n wireNetwork: string;\n /** EIP-712 domain `name` for this deployment's USDC. */\n usdcName: string;\n /** EIP-712 domain `version`. */\n usdcVersion: string;\n /**\n * Token decimals. Every USDC deployment here is 6, but carrying it on the\n * registry entry (rather than a literal `6` at the format site) keeps the\n * source of truth in one place and is ready for a non-6-decimal asset when\n * the registry grows past USDC. Reading it off-chain from the contract is\n * deliberately avoided: the registry is a controlled allowlist, so an extra\n * RPC round-trip and trusting a token's self-reported decimals buy nothing.\n */\n decimals: number;\n}\n\nexport const USDC_BY_NETWORK: Record<string, UsdcAsset> = {\n 'eip155:8453': {\n address: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',\n chainId: 8453,\n wireNetwork: 'eip155:8453',\n usdcName: 'USD Coin',\n usdcVersion: '2',\n decimals: 6,\n },\n 'eip155:84532': {\n address: '0x036CbD53842c5426634e7929541eC2318f3dCF7e',\n chainId: 84532,\n wireNetwork: 'eip155:84532',\n usdcName: 'USDC',\n usdcVersion: '2',\n decimals: 6,\n },\n 'eip155:137': {\n address: '0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359',\n chainId: 137,\n wireNetwork: 'eip155:137',\n usdcName: 'USD Coin',\n usdcVersion: '2',\n decimals: 6,\n },\n 'eip155:80002': {\n address: '0x41E94Eb019C0762f9Bfcf9Fb1E58725BfB0e7582',\n chainId: 80002,\n wireNetwork: 'eip155:80002',\n usdcName: 'USDC',\n usdcVersion: '2',\n decimals: 6,\n },\n};\n\n/**\n * Look up USDC metadata by CAIP-2 network id, or `undefined` if unsupported.\n *\n * Own keys only. The network reaches here from a 402 challenge and from the\n * Bazaar catalogue, both untrusted, and a plain index answers `constructor` or\n * `toString` with something off the prototype: callers then read `.address` off\n * a function and throw where they expected an unsupported network.\n */\nexport function usdcForNetwork(network: string): UsdcAsset | undefined {\n return Object.hasOwn(USDC_BY_NETWORK, network) ? USDC_BY_NETWORK[network] : undefined;\n}\n","import { createPublicClient, http, erc20Abi, formatUnits, type Chain, type PublicClient } from 'viem';\nimport { base, baseSepolia, polygon, polygonAmoy } from 'viem/chains';\nimport { usdcForNetwork, USDC_BY_NETWORK, type UsdcAsset } from './asset-registry.js';\nimport { loadConfig } from '../lib/config.js';\n\n// The JAW proxy RPC endpoint, mirrored from core's JAW_RPC_URL. Kept as a local\n// literal rather than an import because `@jaw.id/core` is lazy-loaded in the CLI\n// (a static import would pull it into startup); keep in sync if core's URL moves.\nconst JAW_RPC_URL = 'https://api.justaname.id/proxy/v1/rpc';\n\nconst CHAINS: Record<number, Chain> = {\n [base.id]: base,\n [baseSepolia.id]: baseSepolia,\n [polygon.id]: polygon,\n [polygonAmoy.id]: polygonAmoy,\n};\n\n// The viem chains here and USDC_BY_NETWORK in asset-registry are two lists that\n// must cover the same chain ids. Assert it at load so adding a USDC entry\n// without its viem chain fails loudly here instead of silently building a\n// client with `chain: undefined` (wrong gas/explorer defaults) at read time.\nfor (const chainId of Object.values(USDC_BY_NETWORK).map((a) => a.chainId)) {\n if (!CHAINS[chainId]) {\n throw new Error(\n `x402 balance: USDC registry has chain ${chainId} but no viem chain is mapped for it in balance.ts`\n );\n }\n}\n\n// One client per (chain, apiKey) across the process — a fresh transport per read\n// is wasted setup once balance checks run more than once per payment. Keying on\n// the apiKey too matters for the long-lived `jaw mcp` server: a first read before\n// a key is configured would otherwise cache the public-RPC client forever, so a\n// later `jaw config set apiKey` (or a key change) never takes effect. A new key\n// yields a new cache entry and a keyed transport; the stale entry just goes cold.\nconst clients = new Map<string, PublicClient>();\n\n/**\n * Route reads through the same JAW proxy core uses, so the x402 modules see the\n * same node/state core does and don't depend on viem's public RPC fallback\n * (rate-limited and flaky). This matters for the delegation probe in payer.ts:\n * a dropped getCode there would sign a raw signature where a wrapped one was\n * needed. Falls back to the public RPC when no apiKey is configured (e.g. tests).\n */\nfunction rpcTransport(chainId: number, apiKey?: string) {\n if (!apiKey) return http();\n return http(`${JAW_RPC_URL}?chainId=${chainId}&api-key=${apiKey}`);\n}\n\n/** Shared per-(chain, apiKey) public client for the x402 modules (reads only). */\nexport function publicClientFor(chainId: number): PublicClient {\n const chain = CHAINS[chainId];\n if (!chain) throw new Error(`x402: no viem chain configured for chainId ${chainId}`);\n const apiKey = loadConfig().apiKey;\n const key = `${chainId}:${apiKey ?? ''}`;\n let client = clients.get(key);\n if (!client) {\n client = createPublicClient({ chain, transport: rpcTransport(chainId, apiKey) });\n clients.set(key, client);\n }\n return client;\n}\n\n/** Reads the raw USDC balance (base units) of `owner`. Injectable for tests. */\nexport type BalanceReader = (asset: UsdcAsset, owner: `0x${string}`) => Promise<bigint>;\n\nconst readOnChain: BalanceReader = (asset, owner) =>\n publicClientFor(asset.chainId).readContract({\n address: asset.address,\n abi: erc20Abi,\n functionName: 'balanceOf',\n args: [owner],\n });\n\nexport interface UsdcBalance {\n network: string;\n asset: `0x${string}`;\n /** Base units, decimal string. */\n raw: string;\n /** Human-readable amount, formatted with the asset's decimals from the registry. */\n formatted: string;\n}\n\n/**\n * Read an address's USDC balance on a CAIP-2 network so an agent can tell\n * whether it can afford a payment (or confirm one landed).\n */\nexport async function usdcBalance(\n network: string,\n owner: `0x${string}`,\n read: BalanceReader = readOnChain\n): Promise<UsdcBalance> {\n const asset = usdcForNetwork(network);\n if (!asset) throw new Error(`Unsupported x402 network: ${network}`);\n const raw = await read(asset, owner);\n return { network, asset: asset.address, raw: raw.toString(), formatted: formatUnits(raw, asset.decimals) };\n}\n","import { privateKeyToAccount } from 'viem/accounts';\nimport { parseAbi } from 'viem';\nimport { keystoreExists, loadSessionKey } from '../lib/keystore.js';\nimport { hashTypedData as erc7739HashTypedData, wrapTypedDataSignature } from 'viem/experimental/erc7739';\nimport { buildExactPayment, type BuildExactOptions, type ExactTypedData } from './scheme-exact-evm.js';\nimport { buildUptoPayment, type UptoTypedData } from './scheme-upto-evm.js';\nimport { PERMIT2_ADDRESS } from './permit2.js';\nimport { publicClientFor } from './balance.js';\nimport { usdcForNetwork, type UsdcAsset } from './asset-registry.js';\nimport type { X402PaymentPayload, X402PaymentRequirement } from './types.js';\n\n/**\n * Produces the x402 payment for a chosen requirement. The interface is\n * settlement-mode agnostic so the same tool surface can serve both settlement\n * styles:\n *\n * - pull (`Eip3009EoaPayer`): the session-key EOA signs an EIP-3009\n * authorization; the facilitator broadcasts. `from` is the EOA, funds live\n * in it, capped by the tool policy + its own balance.\n * - push: the smart account executes via `wallet_sendCalls(permissionId)` and\n * returns a receipt, bounded by the on-chain permission.\n */\nexport interface Payer {\n /** The paying address — `from` in the authorization. */\n readonly address: `0x${string}`;\n /** Build (and, in pull mode, sign) the payment for a requirement. */\n pay(requirement: X402PaymentRequirement, opts?: PayOptions): Promise<X402PaymentPayload>;\n}\n\nexport interface PayOptions extends BuildExactOptions {\n /**\n * The payer's Permit2 allowance, when the caller already read it. The funder\n * reads exactly this to decide whether to grant one, on the same contract and\n * through the same client, moments earlier, so a second read here would ask\n * the same question twice per payment and learn nothing. Absent, or short of\n * what this payment needs, the read below happens as before.\n */\n permit2Allowance?: bigint;\n}\n\n/**\n * The typed data a scheme asks to have signed, and a signer that takes either.\n * The envelope around them is the same; only the struct differs, so the payer\n * holds one signer rather than one per scheme.\n */\ntype SchemeTypedData = ExactTypedData | UptoTypedData;\ntype SchemeSigner = (typedData: SchemeTypedData) => Promise<`0x${string}`>;\n\n/** EIP-7702 delegation designator prefix (the EOA \"has code\" once delegated). */\nconst EIP7702_CODE_PREFIX = '0xef0100';\n\n/** The EIP-712 domain of the delegated account, read from it on chain. */\ninterface AccountEip712Domain {\n name: string;\n version: string;\n chainId: bigint;\n verifyingContract: `0x${string}`;\n salt: `0x${string}`;\n}\n\nconst ERC20_ALLOWANCE_ABI = parseAbi(['function allowance(address owner, address spender) view returns (uint256)']);\n\nconst EIP712_DOMAIN_ABI = parseAbi([\n 'function eip712Domain() view returns (bytes1 fields, string name, string version, uint256 chainId, address verifyingContract, bytes32 salt, uint256[] extensions)',\n]);\n\n/**\n * Pull-mode payer: the local session-key EOA signs the EIP-3009 authorization.\n * The funds must sit in this EOA.\n *\n * Delegation-aware: while the EOA has no code the signature is the plain\n * typed-data one (ecrecover path). Once the session was upgraded in place via\n * EIP-7702 (which the first userOp of a session does), USDC validates through\n * EIP-1271/ERC-7739 instead, so the payer detects the delegation designator and\n * signs the wrapped TypedDataSign envelope (see `wrappedSigner`).\n */\nexport class Eip3009EoaPayer implements Payer {\n readonly address: `0x${string}`;\n private readonly signTypedData: SchemeSigner;\n private readonly signHash: (hash: `0x${string}`) => Promise<`0x${string}`>;\n /**\n * eip712Domain() of the delegate, cached per chain after the first wrapped\n * payment there. Keyed by chainId: the domain embeds block.chainid, so a\n * domain read on one chain must never sign an envelope for another.\n */\n private readonly accountDomainByChain = new Map<number, AccountEip712Domain>();\n\n private constructor(\n address: `0x${string}`,\n signTypedData: SchemeSigner,\n signHash: (hash: `0x${string}`) => Promise<`0x${string}`>\n ) {\n this.address = address;\n this.signTypedData = signTypedData;\n this.signHash = signHash;\n }\n\n /** Load the session key from the keystore and build a pull-mode payer. */\n static fromSessionKey(): Eip3009EoaPayer {\n if (!keystoreExists()) {\n throw new Error('No session key. Run `jaw session setup` to enable autonomous payments.');\n }\n const account = privateKeyToAccount(loadSessionKey() as `0x${string}`);\n // viem's strict TypedData generics don't line up with our concrete\n // ExactTypedData shape; the runtime call is identical.\n const signTypedData: SchemeSigner = (typedData) => account.signTypedData(typedData as never);\n const signHash = (hash: `0x${string}`) => account.sign({ hash });\n return new Eip3009EoaPayer(account.address, signTypedData, signHash);\n }\n\n async pay(requirement: X402PaymentRequirement, opts?: PayOptions): Promise<X402PaymentPayload> {\n const sign = (await this.isDelegated(requirement.network)) ? this.wrappedSigner() : this.signTypedData;\n if (requirement.scheme === 'upto') {\n await this.assertPermit2Approved(requirement, opts?.permit2Allowance);\n return buildUptoPayment(requirement, this.address, sign, opts);\n }\n return buildExactPayment(requirement, this.address, sign, opts);\n }\n\n /**\n * Refuse an `upto` payment the payer has not enabled, before signing it.\n *\n * Permit2 moves tokens through the canonical ERC-20 allowance, so a payer that\n * never approved it produces an authorization the proxy cannot execute. The\n * settlement then fails, and by the ledger's rule a failed attempt reserves its\n * whole ceiling against the cap, which spends the user's budget on a payment\n * that could never have worked. This is the same trade the delegation check\n * above already makes: refusing before signing costs a retry, guessing costs\n * the budget.\n *\n * The approval is granted once per chain and is not automatic yet.\n *\n * `known` is the figure the funder already read. It is taken only when it\n * covers this payment, so the check can be satisfied early but never talked\n * down: anything short falls through to the chain. The read stays for every\n * caller that arrives without one, since a payer signing outside the funding\n * hook has nothing else between it and an unsettleable signature.\n */\n private async assertPermit2Approved(requirement: X402PaymentRequirement, known?: bigint): Promise<void> {\n const asset = usdcForNetwork(requirement.network);\n if (!asset) return; // the builder refuses an unknown network with a better message\n const needed = BigInt(requirement.amount);\n if (known !== undefined && known >= needed) return;\n const allowance = await this.permit2Allowance(asset);\n if (allowance < needed) {\n throw new Error(\n `The payer ${this.address} has approved Permit2 for ${allowance} of ${asset.address} on ${requirement.network}, ` +\n `and this payment authorizes up to ${needed}. Permit2 moves the token through that allowance, so the ` +\n 'payment could not settle. Approve Permit2 once on this chain and retry.'\n );\n }\n }\n\n private permit2Allowance(asset: UsdcAsset): Promise<bigint> {\n return publicClientFor(asset.chainId).readContract({\n address: asset.address,\n abi: ERC20_ALLOWANCE_ABI,\n functionName: 'allowance',\n args: [this.address, PERMIT2_ADDRESS],\n });\n }\n\n /**\n * True once the EOA carries an EIP-7702 delegation designator on-chain, which\n * decides whether USDC will route this signature through ecrecover or\n * EIP-1271, and so which of the two signatures to produce.\n *\n * Throws rather than guessing when the chain cannot be read. Guessing raw was\n * the old default, from when a session was usually never delegated; a session\n * is delegated from its first userOp now, so the guess is wrong nearly every\n * time it is made. And the guess is not free: a raw signature against a\n * delegated account is refused by the settlement endpoint, which reads as a\n * failed payment, and a failed payment counts against the session cap on the\n * grounds that the facilitator may have broadcast it anyway. It cannot have,\n * since USDC rejects the signature, so guessing spends the user's budget on a\n * payment that could never have settled. Refusing before signing costs a\n * retry instead.\n */\n private async isDelegated(network: string): Promise<boolean> {\n const asset = usdcForNetwork(network);\n if (!asset) return false;\n const code = await publicClientFor(asset.chainId).getCode({ address: this.address });\n return (code ?? '0x').toLowerCase().startsWith(EIP7702_CODE_PREFIX);\n }\n\n /**\n * ERC-7739 wrapped signer for the delegated (EIP-1271) validation path.\n *\n * JustanAccount answers 1271 with Solady's ERC-7739 validation, which rejects\n * raw signatures from on-chain callers by design (anti cross-account replay).\n * So the key signs a nested TypedDataSign envelope carrying the account's own\n * domain, and ships a blob the account unwraps. USDC v2.2 accepts\n * arbitrary-length `bytes` signatures, so it travels on the normal x402 wire.\n *\n * The envelope and the blob come from viem, which derives the contents type\n * from the typed data instead of taking a hand-written string, so the type\n * cannot drift from what is being signed. `erc7739.vectors.test.ts` pins the\n * bytes both produce against a payment that settled on chain.\n */\n private wrappedSigner(): SchemeSigner {\n return async (typedData: SchemeTypedData) => {\n const verifierDomain = await this.readAccountDomain(typedData.domain.chainId);\n // viem's TypedData generics don't line up with our concrete shapes; the\n // runtime calls take exactly the typed data we already build.\n const digest = erc7739HashTypedData({ ...typedData, verifierDomain } as never);\n const signature = await this.signHash(digest);\n return wrapTypedDataSignature({ ...typedData, signature } as never);\n };\n }\n\n /** Read (once per chain) the delegate's EIP-712 domain from the account. */\n private async readAccountDomain(chainId: number): Promise<AccountEip712Domain> {\n const cached = this.accountDomainByChain.get(chainId);\n if (cached) return cached;\n const [, name, version, domainChainId, verifyingContract, salt] = await publicClientFor(chainId).readContract({\n address: this.address,\n abi: EIP712_DOMAIN_ABI,\n functionName: 'eip712Domain',\n });\n const domain: AccountEip712Domain = { name, version, chainId: domainChainId, verifyingContract, salt };\n this.accountDomainByChain.set(chainId, domain);\n return domain;\n }\n}\n\n/**\n * The address pull-mode payments are made from, which is the session key's own\n * EOA and also the session address: the EOA is the session account, upgraded in\n * place. This is the address that must hold USDC for `jaw_pay_and_fetch` to\n * pay; expose it so a user/agent knows where the funds end up. Derives the\n * public address only (no signing, no key exposure). Throws if no session key\n * exists.\n */\nexport function sessionPayerAddress(): `0x${string}` {\n if (!keystoreExists()) {\n throw new Error('No session key. Run `jaw session setup` first.');\n }\n return privateKeyToAccount(loadSessionKey() as `0x${string}`).address;\n}\n","import * as fs from 'node:fs';\nimport { PATHS } from '../lib/paths.js';\nimport { ensureDir } from '../lib/config.js';\nimport { errorMessage } from '../lib/errors.js';\n\n/**\n * One line of the append-only x402 payment ledger (`~/.jaw/x402-log.jsonl`).\n * Every payment attempt an agent makes is recorded so spend is auditable and an\n * ambiguous settlement can be reconciled by nonce/txHash after the fact.\n */\nexport interface X402LogEntry {\n /** ISO timestamp of the attempt. */\n at: string;\n url: string;\n /** The paying EOA. */\n payer: string;\n /** paid = settled; failed = signed+sent but settlement failed; refused = never signed. */\n status: 'paid' | 'failed' | 'refused';\n /**\n * What actually left the payer. Under `exact` that is the amount signed for.\n * Under `upto` the server chooses it at settlement, anywhere from zero up to\n * `authorized`, and the receipt is the only place it exists.\n */\n amount?: string;\n /**\n * The ceiling the signature authorized, which is what a live authorization is\n * worth to whoever holds it. Equal to `amount` under `exact`. Absent on\n * entries written before the field existed, where `amount` was both.\n */\n authorized?: string;\n /**\n * When the authorization expires. Recorded but not yet read: reconciling a\n * failed payment means proving its nonce was never consumed and its deadline\n * has passed, and that check cannot be written against entries that never\n * stored the deadline.\n */\n deadline?: string;\n asset?: string;\n network?: string;\n payTo?: string;\n nonce?: string;\n txHash?: string;\n /** Base units refilled into the payer through the permission, when a top-up ran. */\n topUpAmount?: string;\n /** wallet_sendCalls id of that top-up, for on-chain reconciliation. */\n topUpBatchId?: string;\n /**\n * wallet_sendCalls id of the Permit2 approval, when this payment granted one.\n * Never summed with `topUpAmount`: it moves no principal, only the gas the\n * payer was charged for it. Recorded so a userOp the user paid for is not\n * missing from the audit trail.\n */\n approvalBatchId?: string;\n /** Reason for a refused/failed attempt. */\n reason?: string;\n}\n\n/**\n * Append one entry. Never throws — logging must not break a payment.\n *\n * The newline is a PREFIX, not a suffix: a torn write (crash/ENOSPC mid-append)\n * then leaves an incomplete line that the NEXT append starts on a fresh line\n * instead of concatenating onto, so one bad write loses at most its own record,\n * never the following one too.\n *\n * A write failure is surfaced to stderr (not thrown): the caller's payment\n * still succeeds, but the operator needs to know the audit trail — and the\n * restart-time spend-cap seed that reads it — just lost an entry.\n */\nexport function appendX402Log(entry: X402LogEntry): void {\n try {\n ensureDir(PATHS.root);\n fs.appendFileSync(PATHS.x402Log, '\\n' + JSON.stringify(entry), { encoding: 'utf-8', mode: 0o600 });\n } catch (err) {\n const msg = errorMessage(err);\n process.stderr.write(`[jaw] warning: failed to write x402 ledger (${msg}); spend audit/cap may undercount\\n`);\n }\n}\n\n/**\n * Read the ledger, oldest first. `limit` returns only the most recent N entries.\n * Malformed lines are skipped; a missing file is an empty log.\n */\nexport function readX402Log(limit?: number): X402LogEntry[] {\n let raw: string;\n try {\n raw = fs.readFileSync(PATHS.x402Log, 'utf-8');\n } catch {\n return [];\n }\n const entries = raw\n .split('\\n')\n .filter((line) => line.trim().length > 0)\n .map((line) => {\n try {\n return JSON.parse(line) as X402LogEntry;\n } catch {\n return null;\n }\n })\n .filter((e): e is X402LogEntry => e !== null);\n return limit && limit > 0 ? entries.slice(-limit) : entries;\n}\n\n/**\n * What one row contributes to a spend cap.\n *\n * The single definition of the rule, exported because more than one place needs\n * it and the three copies that existed before this had already drifted apart by\n * hand. `jaw x402 log` reports against it, and the caps enforce against it, so\n * the number a user reads and the number that refuses their next payment are\n * the same number.\n *\n * A settled payment costs what settled. A failed one costs the ceiling it\n * authorized, because an authorization that was signed and sent stays spendable\n * up to that ceiling until its nonce is consumed or its deadline passes, and\n * nothing yet proves either. Under `exact` the two figures are equal and this\n * is the rule that has always applied.\n *\n * Every parse failure reads as zero and the failed case takes the larger of the\n * two, so one unparseable field cannot shrink an enforced cap: a torn write or a\n * hand edit can only ever leave the cap where it was or higher. Negatives clamp\n * for the same reason, since `BigInt('-5')` parses fine and would otherwise\n * subtract.\n */\nexport function spendFigureOf(entry: X402LogEntry): bigint {\n if (entry.status !== 'paid' && entry.status !== 'failed') return 0n;\n const parse = (value?: string): bigint => {\n if (!value) return 0n;\n try {\n const parsed = BigInt(value);\n return parsed > 0n ? parsed : 0n;\n } catch {\n return 0n;\n }\n };\n if (entry.status === 'paid') return parse(entry.amount);\n const ceiling = parse(entry.authorized);\n const charge = parse(entry.amount);\n return ceiling > charge ? ceiling : charge;\n}\n\n/**\n * Sum a payer's settled and attempted payments since an ISO instant (its whole\n * history when `since` is omitted).\n *\n * Reading it from the ledger rather than an in-memory counter is what makes a\n * cap survive a process restart, which an agent could otherwise relaunch its way\n * past. What each row costs is `spendFigureOf`.\n */\nexport function sumSpentSince(payerAddress: string, since?: string): bigint {\n const payer = payerAddress.toLowerCase();\n return readX402Log().reduce((total, entry) => {\n if (entry.payer?.toLowerCase() !== payer) return total;\n if (since && entry.at < since) return total;\n return total + spendFigureOf(entry);\n }, 0n);\n}\n\n/**\n * Sum what a payer pulled through the permission since an ISO instant (its whole\n * history when `since` is omitted).\n *\n * Distinct from `sumSpentSince` because the two meter different things: the\n * on-chain allowance is drawn down by the top-up, not by the payment it later\n * funds. With a `topUpFloat` the two run apart by whatever is still sitting in\n * the payer, so measuring the granted per-period cap by payments reads a\n * permission as having more left than it does.\n *\n * Every status counts, refusals included: the pull settled on-chain before the\n * payment it was for was ever attempted, so the allowance is gone either way.\n */\nexport function sumToppedUpSince(payerAddress: string, since?: string): bigint {\n const payer = payerAddress.toLowerCase();\n return readX402Log().reduce((total, entry) => {\n if (!entry.topUpAmount) return total;\n if (entry.payer?.toLowerCase() !== payer) return total;\n if (since && entry.at < since) return total;\n try {\n return total + BigInt(entry.topUpAmount);\n } catch {\n return total; // a hand-edited amount must not take the cap down\n }\n }, 0n);\n}\n","/**\n * Canonical base-unit amount parser. Every place that turns an amount string\n * (from an untrusted 402 challenge or from hand-edited config) into a bigint\n * goes through here, so the failure surface is uniform and a malformed value\n * can never crash a hot path. Sign policy is left to the caller: this returns\n * the parsed value (which may be negative) or null when it is not an integer.\n */\nexport function parseBigInt(value: string | undefined | null): bigint | null {\n // Empty string is \"unset\", not zero: BigInt('') is 0n, but an absent config\n // value must read as null so callers treat it as \"no bound\", not \"cap of 0\".\n if (value === undefined || value === null || value === '') return null;\n try {\n return BigInt(value);\n } catch {\n return null;\n }\n}\n\n/** Parse a non-negative base-unit amount; undefined when absent, invalid, or negative. */\nexport function parseNonNegativeBigInt(value: string | undefined | null): bigint | undefined {\n const parsed = parseBigInt(value);\n return parsed !== null && parsed >= 0n ? parsed : undefined;\n}\n","/**\n * Spend-limit period windows, mirroring `JustaPermissionManager._getCurrentPeriod`.\n *\n * A granted allowance is per period, not per session: the contract rebuilds a\n * fresh window once the previous one elapses, so the allowance resets. The local\n * policy has to compute the same window to refuse for the same reason the chain\n * would, before a signature is produced.\n */\n\n/** Period units the contract's `PeriodUnit` enum accepts. */\nexport const PERIOD_UNITS = ['minute', 'hour', 'day', 'week', 'month', 'forever'] as const;\nexport type PeriodUnit = (typeof PERIOD_UNITS)[number];\n\nexport function isPeriodUnit(value: unknown): value is PeriodUnit {\n return typeof value === 'string' && (PERIOD_UNITS as readonly string[]).includes(value);\n}\n\n/**\n * Normalise a granted spend's unit and multiplier to what the contract stores.\n *\n * The CLI and the SDK both accept `year`, which has no `PeriodUnit` on chain:\n * `permissionToContractFormat` rewrites it to `month` with the multiplier times\n * twelve before encoding. This mirrors that, so a yearly grant lands on the same\n * window locally as the one the permission actually enforces. Returns undefined\n * for a unit with no on-chain meaning, which callers treat as \"no period\n * recorded\" rather than guessing a window.\n */\nexport function normalizePeriod(\n unit: string | undefined,\n multiplier?: number\n): { unit: PeriodUnit; multiplier: number } | undefined {\n const m = Math.max(1, Math.floor(multiplier ?? 1));\n if (unit === 'year') return { unit: 'month', multiplier: m * 12 };\n if (isPeriodUnit(unit)) return { unit, multiplier: m };\n return undefined;\n}\n\n/** Seconds per fixed-duration unit. `month` is calendar-based, `forever` unbounded. */\nconst FIXED_UNIT_SECONDS: Record<Exclude<PeriodUnit, 'month' | 'forever'>, number> = {\n minute: 60,\n hour: 3600,\n day: 86400,\n week: 604800,\n};\n\nexport interface PeriodWindow {\n /** Unix seconds, inclusive. */\n start: number;\n /** Unix seconds, exclusive, clamped at the permission's expiry. */\n end: number;\n}\n\nexport interface PeriodWindowInput {\n /** Unix seconds the periods are anchored at (the permission's start). */\n anchor: number;\n unit: PeriodUnit;\n /** Period multiplier, >= 1. Values below 1 are treated as 1, matching the grant default. */\n multiplier?: number;\n /** Unix seconds to locate the window around. */\n now: number;\n /** Unix seconds the permission expires. The window never extends past it. */\n permissionEnd: number;\n}\n\n/**\n * Add `months` calendar months to a unix timestamp, clamping the day-of-month to\n * the target month's length. Mirrors Solady's `DateTimeLib.addMonths`, which the\n * contract uses: Jan 31 plus one month is Feb 28, not Mar 3.\n */\nfunction addMonths(unixSeconds: number, months: number): number {\n const d = new Date(unixSeconds * 1000);\n const day = d.getUTCDate();\n const target = new Date(\n Date.UTC(d.getUTCFullYear(), d.getUTCMonth() + months, 1, d.getUTCHours(), d.getUTCMinutes(), d.getUTCSeconds())\n );\n // Day 0 of the following month is the last day of the target month.\n const daysInTarget = new Date(Date.UTC(target.getUTCFullYear(), target.getUTCMonth() + 1, 0)).getUTCDate();\n target.setUTCDate(Math.min(day, daysInTarget));\n return Math.floor(target.getTime() / 1000);\n}\n\n/**\n * The period window containing `now`, anchored at the permission start.\n *\n * `forever` is a single window spanning the whole permission, which is why a\n * `forever` grant behaves like a plain session-wide cap. Fixed units step in\n * whole durations from the anchor. `month` steps in calendar months.\n *\n * `now` before the anchor yields the first window rather than a negative index,\n * so a small clock skew between the grant and the local machine cannot produce a\n * window in the past.\n */\nexport function currentPeriodWindow(input: PeriodWindowInput): PeriodWindow {\n const { anchor, unit, now, permissionEnd } = input;\n const multiplier = Math.max(1, Math.floor(input.multiplier ?? 1));\n\n if (unit === 'forever') {\n return { start: anchor, end: permissionEnd };\n }\n\n let start: number;\n let end: number;\n\n if (unit === 'month') {\n // Walk calendar months from the anchor. Month lengths vary, so stepping is\n // the honest way to find the index; grants run for a bounded number of\n // periods, and the loop is cheap next to the network call it guards.\n let index = 0;\n let cursor = anchor;\n let next = addMonths(anchor, multiplier);\n while (next <= now) {\n index += 1;\n cursor = next;\n next = addMonths(anchor, (index + 1) * multiplier);\n }\n start = cursor;\n end = next;\n } else {\n const duration = FIXED_UNIT_SECONDS[unit] * multiplier;\n const elapsed = Math.max(0, now - anchor);\n const index = Math.floor(elapsed / duration);\n start = anchor + index * duration;\n end = start + duration;\n }\n\n // The contract caps the window at the permission end; past expiry nothing is\n // spendable anyway, so a degenerate window is the correct answer.\n return { start, end: Math.min(end, permissionEnd) };\n}\n\n/** Human-readable period label for refusal messages, e.g. \"day\" or \"3 days\". */\nexport function describePeriod(unit: PeriodUnit, multiplier?: number): string {\n // 'the whole permission' reads right inside a refusal about an allowance and\n // wrong after 'per', which is why the plain form below is its own function\n // rather than a flag on this one.\n if (unit === 'forever') return 'the whole permission';\n return describeSpendPeriod(unit, multiplier);\n}\n\n/**\n * A spend limit's window, as it reads after \"per\".\n *\n * Takes a plain string because a grant may say `year`, which is not a\n * `PeriodUnit`: the contract has no such unit and the SDK rewrites it to months\n * before encoding, but the document the user wrote still says it.\n *\n * Here rather than beside each caller. Two byte-identical copies of this had\n * grown in `grant-ceiling` and `merge-permissions`, the second one added while\n * fixing a review comment about the first.\n */\nexport function describeSpendPeriod(unit: string, multiplier?: number): string {\n const n = Math.max(1, Math.floor(multiplier ?? 1));\n return n === 1 ? unit : `${n} ${unit}s`;\n}\n\n/**\n * How long one period lasts in seconds, at its shortest or its longest.\n *\n * Two bounds because a calendar month is 28 to 31 days and a year 365 or 366,\n * and which end to take depends on which side of a comparison the period is on:\n * measuring a grant at its shortest and a ceiling at its longest keeps the\n * rounding from opening either one up. `year` is here even though the contract\n * has no such unit, because a grant may be written with it and the SDK rewrites\n * it to months before encoding.\n *\n * Returns null for a unit with no meaning, which callers treat as \"cannot be\n * compared\" rather than guessing a length.\n */\nexport function periodLengthSeconds(unit: string, multiplier: number, bound: 'min' | 'max'): number | null {\n const lengths: Record<string, number> = {\n minute: 60,\n hour: 3600,\n day: 86400,\n week: 604800,\n month: (bound === 'min' ? 28 : 31) * 86400,\n year: (bound === 'min' ? 365 : 366) * 86400,\n forever: Number.POSITIVE_INFINITY,\n };\n if (!Object.hasOwn(lengths, unit)) return null;\n return lengths[unit] * Math.max(1, Math.floor(multiplier));\n}\n","import { USDC_BY_NETWORK, usdcForNetwork } from './asset-registry.js';\nimport { parseBigInt, parseNonNegativeBigInt } from './amount.js';\nimport { describePeriod, normalizePeriod, type PeriodUnit } from './period.js';\nimport { isHexShaped, isPayableAddress, isZeroAddress } from './address.js';\nimport { UPTO_VERIFIED_CHAIN_IDS } from './permit2.js';\nimport { isX402Scheme, type X402PaymentRequirement } from './types.js';\nimport type { GrantedPermission } from '../lib/session-config.js';\n\n/**\n * One spend limit the grant puts on the payment token.\n *\n * A list of these rather than a single allowance and window, because\n * `_checkAndIncrementSpend` charges every limit whose token matches and does\n * not stop at the first. Which one refuses depends on the amount and on the\n * moment, so a single pair cannot answer it, and picking one on the caller's\n * behalf is what reported a month's budget as a day's.\n */\nexport interface GrantedPeriodLimit {\n /** Base units, decimal string. */\n allowance: string;\n unit: PeriodUnit;\n multiplier: number;\n /** ISO timestamp the windows are anchored at: the permission's own start. */\n anchor: string;\n}\n\n/**\n * A limit and what has been drawn against it inside the window containing now.\n *\n * Both meters, because the two caps count different things and always have:\n * the allowance is drawn down by what is pulled through the permission, while\n * a payment is what the payer sends out of the float it already holds.\n */\nexport interface LimitUsage extends GrantedPeriodLimit {\n /** Payments made from the payer inside this window. */\n spent: bigint;\n /** Pulled through the permission inside this window, which is what the allowance loses. */\n toppedUp: bigint;\n /** When this limit's current window ends. */\n endsAt: Date;\n /**\n * Where `toppedUp` came from. From the ledger it is a floor, since the ledger\n * only sees what went through `payAndFetch`; from the chain it is what the\n * contract will meter against.\n */\n source: 'ledger' | 'chain';\n}\n\n/**\n * Tool-level x402 limits (from `~/.jaw/config.json`'s `x402` block). An agent\n * holding a signing key must be fenced in: no single payment above the cap, no\n * cumulative overspend, and only allow-listed assets/networks/recipients. In\n * pull mode these sit alongside the session-EOA's own balance; in push mode the\n * on-chain permission is the hard ceiling and these are the inner guardrails.\n */\nexport interface X402Policy {\n /** Max base units for a single payment. */\n maxAmountPerPayment?: string;\n /**\n * Max cumulative base units across the whole session. Not a per-process cap:\n * the running total is rebuilt from the payment ledger since the session was\n * created, so it survives restarts. User-configured only, never seeded from\n * the grant (see `perPeriod` for that).\n */\n maxTotalPerSession?: string;\n /**\n * Max base units within one period of the on-chain grant, seeded from it and\n * resetting every period exactly as the permission does. This is the field\n * that mirrors the chain; `maxTotalPerSession` is the user's own ceiling on\n * top. Set together with `period`.\n */\n perPeriod?: GrantedPeriodLimit[];\n /** Allowed asset contract addresses (case-insensitive). Empty/undefined = any. */\n allowedAssets?: string[];\n /** Allowed CAIP-2 networks. Empty/undefined = any. */\n allowedNetworks?: string[];\n /** Allowed resource hostnames. Empty/undefined = any. */\n allowedHosts?: string[];\n /** Allowed `payTo` addresses (case-insensitive). Empty/undefined = any. */\n allowedPayTo?: string[];\n /**\n * Flow 2b: refill target (base units) for the session payer when a top-up\n * runs. Unset = top up exactly the shortfall. Bounded on-chain by the\n * permission either way.\n */\n topUpFloat?: string;\n}\n\n/**\n * Conservative caps applied when the user has not configured `x402` limits, so a\n * fresh setup is never unbounded: capped amounts, and only the registry's USDC\n * deployments on their known networks (any other asset a server asks for is\n * refused). USDC has 6 decimals: 1_000_000 = 1 USDC. Override per field via\n * `jaw config set x402.*`.\n */\nexport const DEFAULT_X402_POLICY: X402Policy = {\n maxAmountPerPayment: '1000000', // 1 USDC per payment\n maxTotalPerSession: '10000000', // 10 USDC per process\n allowedAssets: Object.values(USDC_BY_NETWORK).map((asset) => asset.address),\n allowedNetworks: Object.keys(USDC_BY_NETWORK),\n};\n\n/**\n * The x402 policy a granted permission implies.\n *\n * Derived on read rather than stored beside the permission. The session used to\n * carry both: the struct, and a `grantedSpend` summary of one USDC limit\n * written at grant time. Two shapes for one fact, at different fidelities, and\n * every consumer had to know which one it was reading. Each of the bugs in this\n * area was a version of the two disagreeing, and the reconciliation between\n * them was itself a source of them. A value that cannot be stored out of date\n * cannot go out of date.\n *\n * Every limit is carried, not reduced to one: the contract charges each of\n * them, so which refuses depends on the amount and the moment, and a single\n * pair cannot answer that.\n */\nexport function policyFromPermission(permission: GrantedPermission | undefined, chainId: number): X402Policy {\n if (!permission) return {};\n const usdc = Object.values(USDC_BY_NETWORK).find((asset) => asset.chainId === chainId);\n if (!usdc) return {};\n\n const forToken = permission.spends.filter((spend) => spend.token.toLowerCase() === usdc.address.toLowerCase());\n if (forToken.length === 0) return {};\n\n // Guarded on the Date, not on the number. `toISOString` throws for a value\n // outside the Date range, and `parseGrantedPermission` only checks that the\n // start is a positive integer: 99999999999999 passes that and is finite, and\n // still raises. The session file is JSON a person can edit, and everything\n // else in this module degrades on bad input rather than taking the command\n // down.\n const start = new Date(permission.start * 1000);\n if (Number.isNaN(start.getTime())) return {};\n const anchor = start.toISOString();\n const perPeriod: GrantedPeriodLimit[] = [];\n for (const spend of forToken) {\n let allowance: string;\n try {\n const parsed = BigInt(spend.allowance);\n // A negative allowance would seed a cap that refuses everything or worse.\n if (parsed < 0n) continue;\n allowance = parsed.toString();\n } catch {\n continue;\n }\n // Normalised the way the SDK normalises before encoding, so a `year` grant\n // lands on the same month-based window the permission actually enforces.\n const period = normalizePeriod(spend.unit, spend.multiplier);\n if (!period) continue;\n perPeriod.push({ allowance, unit: period.unit, multiplier: period.multiplier, anchor });\n }\n if (perPeriod.length === 0) return {};\n\n return {\n // The registry's canonical address, not the permission's literal string:\n // they match case-insensitively and this seeds an allowlist compared that\n // way.\n allowedAssets: [usdc.address],\n allowedNetworks: [usdc.wireNetwork],\n perPeriod,\n };\n}\n\nexport function resolveX402Policy(configPolicy?: X402Policy, grantPolicy?: X402Policy): X402Policy {\n const merged: X402Policy = { ...DEFAULT_X402_POLICY, ...(grantPolicy ?? {}), ...(configPolicy ?? {}) };\n // The default session cap is the guardrail for an unconfigured setup that has\n // no grant to bound it. Once a grant supplies a per-period cap, that cap is\n // what the user approved on chain, and leaving the 10-USDC default sitting on\n // top of it would strand a longer grant well short of its own limit: a\n // 5-per-day grant over 7 days permits 35, and the default would stop it at 10.\n // A session cap the user set explicitly still applies on top.\n if (grantPolicy?.perPeriod !== undefined && configPolicy?.maxTotalPerSession === undefined) {\n delete merged.maxTotalPerSession;\n }\n return merged;\n}\n\n/**\n * Resolve the policy for a live session. Every front end goes through this, so\n * `jaw x402 pay`, `jaw x402 status` and the MCP tool all enforce and report the\n * same caps. Resolving from config alone is what let them drift: the CLI paid\n * under the 10-USDC defaults on every registry network while the MCP tool\n * refused at the granted per-period allowance, and status printed a session cap\n * that the grant had already deleted.\n */\nexport function resolveSessionX402Policy(\n configPolicy?: X402Policy,\n session?: { chainId: number; permission?: GrantedPermission } | null\n): X402Policy {\n return resolveX402Policy(configPolicy, policyFromPermission(session?.permission, session?.chainId ?? 0));\n}\n\n/**\n * The most a single top-up may move into the payer: the smallest cap that\n * actually binds, never a preferred one. Preferring the per-period cap let a\n * 5-USDC/day grant pre-fund 5 USDC into a session the user had explicitly capped\n * at 1, which is the idle-funds-at-risk case `TopUpOptions.maxTopUp` exists to\n * prevent. Undefined when neither cap is set, meaning the on-chain permission is\n * the only bound.\n *\n * What is left of each cap, not the whole cap: a 10/day grant with 9 already\n * used has 1 to give, and pulling 10 through the permission reverts on chain.\n * With `topUpFloat` set near the cap that revert is a refused payment whose\n * price fit comfortably, so what is already used has to be subtracted here.\n *\n * Each cap is measured against what actually consumes it, which is not the same\n * meter for both. A per-period cap mirrors the on-chain allowance, and what draws\n * that down is the top-up itself, so it counts top-ups. `maxTotalPerSession` is\n * the user's own ceiling on what the session may spend, so it counts payments.\n * Reading the period cap off payments made it lag by whatever float the payer\n * still held, and the pull that overshot was refused on chain.\n */\n/**\n * Two limits are the same budget when they meter the same window for the same\n * allowance.\n *\n * The allowance is part of it, not decoration. `normalizePeriod` maps a yearly\n * grant onto months, while the merge keys on the raw unit, so `session add` can\n * leave a `year` and a `month` limit that both normalise to the same window.\n * Keyed on the window alone they both resolved to the first usage entry, and\n * the on-chain figures, which are matched by allowance too, were then read off\n * the wrong limit.\n */\nexport function sameLimit(\n a: { unit: string; multiplier: number; allowance: string },\n b: { unit: string; multiplier: number; allowance: string }\n): boolean {\n return a.unit === b.unit && a.multiplier === b.multiplier && a.allowance === b.allowance;\n}\n\nexport function topUpCeiling(\n policy: X402Policy,\n used: { periodUsage?: LimitUsage[]; spentThisSession?: bigint } = {}\n): bigint | undefined {\n const left = (cap: string | undefined, alreadyUsed = 0n): bigint | undefined => {\n const parsed = parseNonNegativeBigInt(cap);\n if (parsed === undefined) return undefined;\n return parsed > alreadyUsed ? parsed - alreadyUsed : 0n;\n };\n const caps = [\n // Every limit the policy holds, not every entry the caller built. The\n // contract charges all of them, so a refill sized against any single one\n // can still be refused by another, and a limit whose usage could not be\n // computed still bounds the pull at its full width rather than vanishing.\n // An allowance that cannot be read bounds at zero rather than dropping out.\n // `checkPolicy` refuses outright on the same input, and letting it vanish\n // here is the shape this set out to remove: with the session default\n // deleted by a seeded grant, nothing local would bound the pull.\n ...(policy.perPeriod ?? []).map(\n (limit) =>\n left(limit.allowance, (used.periodUsage ?? []).find((entry) => sameLimit(entry, limit))?.toppedUp) ?? 0n\n ),\n left(policy.maxTotalPerSession, used.spentThisSession),\n ].filter((cap): cap is bigint => cap !== undefined);\n return caps.length > 0 ? caps.reduce((a, b) => (a < b ? a : b)) : undefined;\n}\n\n/** Policy keys settable from the CLI, split by value shape. */\nexport const X402_SCALAR_KEYS = ['maxAmountPerPayment', 'maxTotalPerSession', 'topUpFloat'] as const;\nexport const X402_ARRAY_KEYS = ['allowedAssets', 'allowedNetworks', 'allowedHosts', 'allowedPayTo'] as const;\nexport type X402PolicyKey = (typeof X402_SCALAR_KEYS)[number] | (typeof X402_ARRAY_KEYS)[number];\n\nexport function isX402PolicyKey(key: string): key is X402PolicyKey {\n return (X402_SCALAR_KEYS as readonly string[]).includes(key) || (X402_ARRAY_KEYS as readonly string[]).includes(key);\n}\n\nexport interface PolicyContext {\n /** Hostname of the resource being paid for (for `allowedHosts`). */\n host?: string;\n /** Base units already spent this session (for `maxTotalPerSession`). */\n spentThisSession?: bigint;\n /**\n * Every limit on the payment token with what has been drawn against it inside\n * the window containing now. Each must be counted over its own window, which\n * resets as that limit does.\n */\n periodUsage?: LimitUsage[];\n}\n\nexport interface PolicyResult {\n ok: boolean;\n reason?: string;\n}\n\nconst has = (list: string[] | undefined): list is string[] => Array.isArray(list) && list.length > 0;\nconst eqAddr = (a: string, b: string) => a.toLowerCase() === b.toLowerCase();\n\n/**\n * How much of the user's budget a requirement asks for.\n *\n * Under `exact` that is the price. Under `upto` it is a ceiling the server may\n * charge anything below, and the caps still measure it, because a signature is\n * worth its ceiling to whoever holds it and no cap can be enforced against a\n * number nobody knows yet. Saying so in the refusal is the difference between a\n * user understanding why a five dollar ceiling was refused on a service that\n * charges a fraction of a cent, and thinking the cap is broken.\n *\n * Exported because the caps are not the only thing that refuses over this\n * number: `--max-amount` does too, from the selection loop, and a refusal that\n * spelled it differently there would teach the reader the ceiling is a price.\n */\nexport const asks = (requirement: X402PaymentRequirement): string =>\n requirement.scheme === 'upto' ? `up to ${requirement.amount}` : requirement.amount;\n\n/**\n * Decide whether a chosen payment requirement is allowed to be paid. Returns the\n * first failing reason so the caller can refuse clearly instead of overpaying.\n */\nexport function checkPolicy(\n requirement: X402PaymentRequirement,\n policy: X402Policy,\n ctx: PolicyContext = {}\n): PolicyResult {\n // The wire value is untrusted: it arrives as a plain string and is only cast\n // to the union, so this is a runtime check and not a redundant one.\n if (!isX402Scheme(requirement.scheme)) {\n return { ok: false, reason: `unsupported scheme: ${String(requirement.scheme)}` };\n }\n\n // The settlement proxy is deployed on fewer chains than the asset registry\n // knows, and a permit naming a spender with no code can never settle. The\n // check belongs here rather than in the signer: this runs during selection,\n // so an `upto` option on an unverified chain is skipped instead of shadowing\n // a payable `exact` option on the same challenge, a dry run reports the\n // refusal a real run would make, and nothing has been funded yet when it does.\n if (requirement.scheme === 'upto') {\n const asset = usdcForNetwork(requirement.network);\n if (!asset) {\n return { ok: false, reason: `unsupported x402 network: ${requirement.network}` };\n }\n if (!UPTO_VERIFIED_CHAIN_IDS.includes(asset.chainId)) {\n return {\n ok: false,\n reason:\n `x402 upto is not available on ${requirement.network}: the settlement proxy is only verified on ` +\n `chain ids ${UPTO_VERIFIED_CHAIN_IDS.join(', ')}`,\n };\n }\n // The witness names the only address the proxy accepts as the settling\n // caller, so a challenge without one can never be paid. The signer refuses\n // it too, but by then the payer has been topped up for a payment that was\n // never going to happen; refused here, the option is skipped during\n // selection like the unverified chain above, and an `exact` option on the\n // same challenge still pays.\n const facilitator = requirement.extra?.['facilitatorAddress'];\n if (!isHexShaped(facilitator) || isZeroAddress(facilitator)) {\n return {\n ok: false,\n reason:\n `x402 upto needs a settling facilitator in extra.facilitatorAddress on ${requirement.network}, ` +\n `got ${JSON.stringify(facilitator)}`,\n };\n }\n // Present and the right shape, but unreadable: a different problem from an\n // absent one, and it sends whoever reads this somewhere else.\n if (!isPayableAddress(facilitator)) {\n return {\n ok: false,\n reason: `extra.facilitatorAddress is not a readable address on ${requirement.network}: ${facilitator}`,\n };\n }\n }\n\n // Ahead of every allowlist, because these hold whether or not one is\n // configured and because a challenge nobody can act on should say so rather\n // than report whichever allowlist it also happened to miss. Here rather than\n // in a signer so nothing has been funded when they refuse: a settlement that\n // cannot succeed still reserves its whole figure against the caps, on the\n // rule that a failed attempt may have been broadcast. See address.ts.\n for (const [field, value] of [\n ['asset', requirement.asset],\n ['payTo', requirement.payTo],\n ] as const) {\n if (!isPayableAddress(value)) {\n return { ok: false, reason: `${field} is not a readable address on ${requirement.network}: ${value}` };\n }\n }\n if (isZeroAddress(requirement.payTo)) {\n return { ok: false, reason: `payTo is the zero address on ${requirement.network}` };\n }\n\n if (has(policy.allowedNetworks) && !policy.allowedNetworks.includes(requirement.network)) {\n return { ok: false, reason: `network not allowed: ${requirement.network}` };\n }\n\n if (has(policy.allowedAssets) && !policy.allowedAssets.some((a) => eqAddr(a, requirement.asset))) {\n return { ok: false, reason: `asset not allowed: ${requirement.asset}` };\n }\n\n if (has(policy.allowedPayTo) && !policy.allowedPayTo.some((a) => eqAddr(a, requirement.payTo))) {\n return { ok: false, reason: `payTo not allowed: ${requirement.payTo}` };\n }\n\n if (has(policy.allowedHosts) && (!ctx.host || !policy.allowedHosts.includes(ctx.host))) {\n return { ok: false, reason: `host not allowed: ${ctx.host ?? '(unknown)'}` };\n }\n\n const amount = parseBigInt(requirement.amount);\n if (amount === null) {\n return { ok: false, reason: `invalid amount: ${requirement.amount}` };\n }\n if (amount < 0n) {\n return { ok: false, reason: `negative amount: ${requirement.amount}` };\n }\n\n if (policy.maxAmountPerPayment !== undefined) {\n const cap = parseBigInt(policy.maxAmountPerPayment);\n if (cap === null) {\n return { ok: false, reason: `invalid maxAmountPerPayment in config: ${policy.maxAmountPerPayment}` };\n }\n if (amount > cap) {\n return {\n ok: false,\n reason: `amount ${asks(requirement)} exceeds maxAmountPerPayment ${policy.maxAmountPerPayment}`,\n };\n }\n }\n\n // Checked before the session cap: this is the one that mirrors the on-chain\n // permission, so when both would refuse, the reason the chain would give is\n // the more useful one to report.\n // Driven by the limits the policy holds, with usage looked up per limit and\n // absent usage read as zero. Iterating the caller's list instead made a cap\n // exist only when someone managed to build an entry for it: a limit dropped\n // while computing usage, which happens on an unparseable anchor, silently\n // stopped being enforced, and since a seeded grant deletes the session\n // default there was then nothing bounding a pull at all.\n const exceeded: Array<{ limit: GrantedPeriodLimit; usage?: LimitUsage }> = [];\n for (const limit of policy.perPeriod ?? []) {\n const cap = parseBigInt(limit.allowance);\n if (cap === null) {\n return { ok: false, reason: `invalid allowance from grant: ${limit.allowance}` };\n }\n const usage = (ctx.periodUsage ?? []).find((entry) => sameLimit(entry, limit));\n const spent = usage?.spent ?? 0n;\n if (spent + amount > cap) exceeded.push({ limit, usage });\n }\n if (exceeded.length > 0) {\n // The one that frees up last, not the smallest. A refusal is read to decide\n // what to do next, and naming the day limit when the month will refuse\n // again tomorrow sends someone back for nothing. Without a window from the\n // caller the order is undefined, so the first is as good as any.\n const latest = exceeded.reduce((a, b) =>\n (a.usage?.endsAt?.getTime() ?? 0) >= (b.usage?.endsAt?.getTime() ?? 0) ? a : b\n );\n const others = exceeded.length - 1;\n const window = describePeriod(latest.limit.unit, latest.limit.multiplier);\n const resets = latest.usage ? `, which resets ${latest.usage.endsAt.toISOString()}` : '';\n return {\n ok: false,\n reason:\n `payment ${asks(requirement)} would exceed the granted ${latest.limit.allowance} per ${window}${resets}` +\n (others > 0 ? ` (${others} other limit${others === 1 ? '' : 's'} also applies)` : ''),\n };\n }\n\n if (policy.maxTotalPerSession !== undefined) {\n const cap = parseBigInt(policy.maxTotalPerSession);\n if (cap === null) {\n return { ok: false, reason: `invalid maxTotalPerSession in config: ${policy.maxTotalPerSession}` };\n }\n const spent = ctx.spentThisSession ?? 0n;\n if (spent + amount > cap) {\n return {\n ok: false,\n reason:\n `payment ${asks(requirement)} would exceed maxTotalPerSession ${policy.maxTotalPerSession} ` +\n `(already spent ${spent} since the session was created; raise it with ` +\n `\\`jaw config set x402.maxTotalPerSession <base units>\\`)`,\n };\n }\n }\n\n return { ok: true };\n}\n","import { parseAbi, zeroAddress, ContractFunctionRevertedError, BaseError } from 'viem';\nimport { publicClientFor } from './balance.js';\nimport type { GrantedPermission } from '../lib/session-config.js';\n\n/**\n * What the permission manager will say about a session's own permission.\n *\n * Everything the CLI knew about its permission came out of\n * `~/.jaw/session-config.json`, which is written once at setup and never\n * updated. A permission revoked from keys.jaw.id or from another machine, or an\n * allowance drawn down by something other than this CLI, leaves that file\n * saying what was true the day it was written. The chain knows, and the reads\n * are views.\n *\n * None of them take the permission id. `isApproved`, `isRevoked` and\n * `getCurrentPeriod` all take `Permission calldata` and hash it internally, so\n * the struct has to be rebuilt exactly as it was approved. That is what\n * `GrantedPermission` is for, and why `permissionHash` is checked against the\n * id the grant returned before any answer here is trusted.\n */\n\nexport const PERMISSION_MANAGER_ABI = parseAbi([\n 'struct CallPermission { address target; bytes4 selector; address checker; }',\n 'struct SpendLimit { address token; uint160 allowance; uint8 unit; uint16 multiplier; }',\n 'struct Permission { address account; address spender; uint48 start; uint48 end; uint256 salt; CallPermission[] calls; SpendLimit[] spends; }',\n 'struct PeriodSpend { uint48 start; uint48 end; uint160 spend; }',\n 'function getHash(Permission permission) view returns (bytes32)',\n 'function isApproved(Permission permission) view returns (bool)',\n 'function isRevoked(Permission permission) view returns (bool)',\n 'function getCurrentPeriod(Permission permission, SpendLimit spendLimit) view returns (PeriodSpend)',\n // Carried so the two time-bound reverts can be told apart from a node that\n // did not answer. Everything else the manager can revert with decodes to an\n // unnamed error, which is treated as unavailable rather than guessed at.\n 'error JustaPermissionManager_BeforePermissionStart(uint48 currentTimestamp, uint48 start)',\n 'error JustaPermissionManager_AfterPermissionEnd(uint48 currentTimestamp, uint48 end)',\n]);\n\nconst TIME_BOUND_ERRORS = new Set([\n 'JustaPermissionManager_BeforePermissionStart',\n 'JustaPermissionManager_AfterPermissionEnd',\n]);\n\n/**\n * `PeriodUnit` in the contract, by declaration order. `year` is absent on\n * purpose: it has no on-chain unit, and the SDK rewrites it to `month` with the\n * multiplier times twelve before encoding. The struct has to be rebuilt with\n * the same rewrite or it hashes to a different permission.\n */\nconst PERIOD_UNIT_ENUM: Record<string, number> = {\n minute: 0,\n hour: 1,\n day: 2,\n week: 3,\n month: 4,\n forever: 5,\n};\n\ntype ContractSpendLimit = {\n token: `0x${string}`;\n allowance: bigint;\n unit: number;\n multiplier: number;\n};\n\ntype ContractPermission = {\n account: `0x${string}`;\n spender: `0x${string}`;\n start: number;\n end: number;\n salt: bigint;\n calls: ReadonlyArray<{ target: `0x${string}`; selector: `0x${string}`; checker: `0x${string}` }>;\n spends: ReadonlyArray<ContractSpendLimit>;\n};\n\nfunction toContractSpendLimit(spend: GrantedPermission['spends'][number]): ContractSpendLimit | null {\n const unit = spend.unit === 'year' ? 'month' : spend.unit;\n const multiplier = spend.unit === 'year' ? spend.multiplier * 12 : spend.multiplier;\n if (!Object.hasOwn(PERIOD_UNIT_ENUM, unit)) return null;\n return {\n token: spend.token as `0x${string}`,\n allowance: BigInt(spend.allowance),\n unit: PERIOD_UNIT_ENUM[unit],\n multiplier,\n };\n}\n\n/**\n * The stored permission in the shape the contract hashes, or null when it\n * cannot be rebuilt.\n *\n * `checker` is filled with the zero address because the grant response does not\n * carry one and `apiPermissionsToPermission` in the SDK always sets it that\n * way. The day a call checker is actually used that stops being true, and the\n * struct built here hashes to a permission that does not exist. Nothing here\n * can detect that on its own, which is why every caller goes through\n * `readPermissionState` and its hash check first.\n */\nexport function toContractPermission(permission: GrantedPermission): ContractPermission | null {\n const spends: ContractSpendLimit[] = [];\n for (const spend of permission.spends) {\n const converted = toContractSpendLimit(spend);\n if (!converted) return null;\n spends.push(converted);\n }\n let salt: bigint;\n try {\n salt = BigInt(permission.salt);\n } catch {\n return null;\n }\n return {\n account: permission.account as `0x${string}`,\n spender: permission.spender as `0x${string}`,\n start: permission.start,\n end: permission.end,\n salt,\n calls: permission.calls.map((call) => ({\n target: call.target as `0x${string}`,\n selector: call.selector as `0x${string}`,\n checker: zeroAddress,\n })),\n spends,\n };\n}\n\n/**\n * What the chain says about the permission, or why it could not say.\n *\n * `mismatch` is its own answer rather than an error: it means the struct on\n * disk does not hash to the id the grant returned, so every read made from it\n * would be about some other permission. Reporting that is more useful than\n * either a false \"revoked\" or silence.\n */\nexport type PermissionState =\n | { status: 'ok'; approved: boolean; revoked: boolean }\n | { status: 'mismatch' }\n | { status: 'unavailable' };\n\nexport interface PermissionReadTarget {\n chainId: number;\n permissionId: string;\n permission?: GrantedPermission;\n}\n\n/** Injectable for tests, and the seam that keeps the network out of the unit tests. */\nexport interface ReadDeps {\n readContract?: (args: {\n address: `0x${string}`;\n abi: typeof PERMISSION_MANAGER_ABI;\n functionName: 'getHash' | 'isApproved' | 'isRevoked' | 'getCurrentPeriod';\n args: readonly unknown[];\n }) => Promise<unknown>;\n /** The permission manager, injectable so a test does not have to import core. */\n manager?: `0x${string}`;\n /**\n * How long to wait on the node.\n *\n * These reads sit in `session status` and `x402 status`, which were local\n * commands before them, and in the payment path. viem retries three times at\n * ten seconds, so an unreachable node would hold a status report open for\n * most of a minute to add a fact that is optional in every caller. Timing out\n * is the same answer as any other failed read: not knowing.\n */\n timeoutMs?: number;\n}\n\nconst DEFAULT_TIMEOUT_MS = 5_000;\n\n/** Rejects the read once the node has had long enough. */\nasync function within<T>(work: Promise<T>, timeoutMs = DEFAULT_TIMEOUT_MS): Promise<T> {\n let timer: ReturnType<typeof setTimeout> | undefined;\n try {\n const expired = new Promise<never>((_, reject) => {\n timer = setTimeout(() => reject(new Error('timed out')), timeoutMs);\n });\n return await Promise.race([work, expired]);\n } finally {\n clearTimeout(timer);\n }\n}\n\nasync function managerAddress(override?: `0x${string}`): Promise<`0x${string}`> {\n if (override) return override;\n // Lazy, like every other core import in the CLI: a static one pulls the whole\n // SDK into startup for a command that may never read the chain.\n const { PERMISSIONS_MANAGER_ADDRESS } = await import('@jaw.id/core');\n return PERMISSIONS_MANAGER_ADDRESS as `0x${string}`;\n}\n\n/**\n * The read function for this chain, or null when there is none.\n *\n * `publicClientFor` throws for a chain it has no viem entry for, and a session\n * can live on one: `session status` runs for any chain the account supports,\n * while the client here covers the four the USDC registry names. Null rather\n * than a throw, so a chain without a client reads as not knowing instead of\n * taking the command down.\n */\nfunction reader(chainId: number, deps: ReadDeps) {\n if (deps.readContract) return deps.readContract;\n try {\n const client = publicClientFor(chainId);\n return (args: Parameters<NonNullable<ReadDeps['readContract']>>[0]) =>\n client.readContract(args as never) as Promise<unknown>;\n } catch {\n return null;\n }\n}\n\n/**\n * Ask the chain whether the permission is approved and whether it has been\n * revoked.\n *\n * Expiry is deliberately not asked for. The contract's `end` is the same number\n * already in the session config, so a read would only confirm what is on disk.\n * What no local file can know is that someone revoked from another device, and\n * that is the whole reason to make the call.\n */\nexport async function readPermissionState(target: PermissionReadTarget, deps: ReadDeps = {}): Promise<PermissionState> {\n if (!target.permission) return { status: 'unavailable' };\n const permission = toContractPermission(target.permission);\n if (!permission) return { status: 'unavailable' };\n\n const read = reader(target.chainId, deps);\n if (!read) return { status: 'unavailable' };\n\n try {\n const address = await managerAddress(deps.manager);\n const [hash, approved, revoked] = await within(\n Promise.all([\n read({ address, abi: PERMISSION_MANAGER_ABI, functionName: 'getHash', args: [permission] }),\n read({ address, abi: PERMISSION_MANAGER_ABI, functionName: 'isApproved', args: [permission] }),\n read({ address, abi: PERMISSION_MANAGER_ABI, functionName: 'isRevoked', args: [permission] }),\n ]),\n deps.timeoutMs\n );\n if (typeof hash !== 'string' || hash.toLowerCase() !== target.permissionId.toLowerCase()) {\n return { status: 'mismatch' };\n }\n return { status: 'ok', approved: approved === true, revoked: revoked === true };\n } catch {\n // An unreachable node is not evidence about a permission. Callers report\n // that they could not tell, never that the session is dead.\n return { status: 'unavailable' };\n }\n}\n\n/**\n * What the on-chain allowance has actually lost this period.\n *\n * `outside-window` is the permission being expired or not yet started:\n * `getCurrentPeriod` runs the same time-bound check the spend path does and\n * reverts rather than answering zero, so the revert is the answer and not a\n * failure.\n */\nexport type OnChainPeriod =\n | { status: 'ok'; start: number; end: number; spend: bigint }\n | { status: 'outside-window' }\n | { status: 'unavailable' };\n\n/** One limit's counter, tagged with the limit it belongs to. */\nexport interface OnChainLimit {\n token: string;\n unit: string;\n multiplier: number;\n allowance: string;\n period: OnChainPeriod;\n}\n\n/**\n * The current period for every spend limit the permission puts on `token`.\n *\n * All of them, because the contract charges all of them: the counter lives at\n * `_lastUpdatedPeriod[permissionHash][spendLimitHash]`, so a permission with\n * several limits on one token has several counters and reading one answers\n * about one budget. Issued together and awaited as one, so N limits cost the\n * latency of one rather than N; the client does not batch them into a single\n * request, so it is still N+1 calls on the wire.\n *\n * The hash is checked alongside them. `getCurrentPeriod` does not require the\n * permission to exist: handed a struct that hashes to something else it answers\n * about that other hash, as a zeroed counter over a window built from the\n * on-disk start.\n */\nexport async function readCurrentPeriods(\n target: PermissionReadTarget & { token: string },\n deps: ReadDeps = {}\n): Promise<OnChainLimit[]> {\n if (!target.permission) return [];\n const permission = toContractPermission(target.permission);\n if (!permission) return [];\n\n const granted = target.permission.spends;\n const indexes = granted\n .map((spend, index) => ({ spend, index }))\n .filter(({ spend }) => spend.token.toLowerCase() === target.token.toLowerCase());\n if (indexes.length === 0) return [];\n\n const unreadable = indexes.map(({ spend }) => ({ ...spend, period: { status: 'unavailable' as const } }));\n\n const read = reader(target.chainId, deps);\n if (!read) return unreadable;\n\n try {\n const address = await managerAddress(deps.manager);\n const settled = await within(\n Promise.allSettled([\n read({ address, abi: PERMISSION_MANAGER_ABI, functionName: 'getHash', args: [permission] }),\n ...indexes.map(({ index }) =>\n read({\n address,\n abi: PERMISSION_MANAGER_ABI,\n functionName: 'getCurrentPeriod',\n args: [permission, permission.spends[index]],\n })\n ),\n ]),\n deps.timeoutMs\n );\n\n const [hashed, ...counters] = settled;\n const hash = hashed.status === 'fulfilled' ? hashed.value : null;\n if (typeof hash !== 'string' || hash.toLowerCase() !== target.permissionId.toLowerCase()) {\n return unreadable;\n }\n\n return indexes.map(({ spend }, i) => {\n const result = counters[i];\n if (result.status === 'rejected') {\n return {\n ...spend,\n period: isTimeBoundRevert(result.reason)\n ? ({ status: 'outside-window' } as const)\n : ({ status: 'unavailable' } as const),\n };\n }\n const period = result.value as { start: number; end: number; spend: bigint } | undefined;\n return {\n ...spend,\n period: period\n ? ({\n status: 'ok',\n start: Number(period.start),\n end: Number(period.end),\n spend: BigInt(period.spend),\n } as const)\n : ({ status: 'unavailable' } as const),\n };\n });\n } catch {\n return unreadable;\n }\n}\n\n/** Whether a failed read was the contract refusing on time bounds rather than a node not answering. */\nfunction isTimeBoundRevert(err: unknown): boolean {\n if (!(err instanceof BaseError)) return false;\n const revert = err.walk((e) => e instanceof ContractFunctionRevertedError);\n return revert instanceof ContractFunctionRevertedError && TIME_BOUND_ERRORS.has(revert.data?.errorName ?? '');\n}\n\n/**\n * What the chain says about a session's permission, flattened to one word.\n *\n * `unknown` covers every way of not knowing, which callers treat alike: a node\n * that did not answer, a chain with no client, a session written before the\n * struct was stored, and a struct that cannot be rebuilt. None of them are\n * evidence about a permission, so none of them may read as dead.\n */\nexport type PermissionLiveness = 'active' | 'revoked' | 'unapproved' | 'mismatch' | 'unknown';\n\n/** `readPermissionState` for the three status surfaces, which all want the one word. */\nexport async function readLiveness(session: PermissionReadTarget, deps: ReadDeps = {}): Promise<PermissionLiveness> {\n const state = await readPermissionState(session, deps);\n if (state.status === 'unavailable') return 'unknown';\n if (state.status === 'mismatch') return 'mismatch';\n if (state.revoked) return 'revoked';\n return state.approved ? 'active' : 'unapproved';\n}\n","import { currentPeriodWindow, normalizePeriod } from './period.js';\nimport { sumSpentSince, sumToppedUpSince } from './ledger.js';\nimport { parseBigInt } from './amount.js';\nimport { readCurrentPeriods, type ReadDeps } from './permission-onchain.js';\nimport { USDC_BY_NETWORK } from './asset-registry.js';\nimport type { LimitUsage, X402Policy } from './policy.js';\nimport type { SessionConfig } from '../lib/session-config.js';\n\n/**\n * What every limit on the payment token has already lost, in the window each of\n * them is currently in.\n *\n * A list rather than one figure, because the contract charges every limit whose\n * token matches and each keeps its own counter. Reducing them to one number\n * meant answering \"what refuses this payment\" and \"what runs out first\" with\n * the same value, and those come apart: 50 a day beside 100 a month reported\n * the 50 and overstated the month fifteenfold.\n *\n * Re-read on every call, never cached: another process holding the payment lock\n * may have spent inside these windows too.\n */\nexport function currentLimitUsage(\n policy: X402Policy,\n payerAddress: string,\n session: { expiry: number } | null | undefined,\n now: Date = new Date()\n): LimitUsage[] {\n if (!session || !policy.perPeriod) return [];\n\n const usage: LimitUsage[] = [];\n for (const limit of policy.perPeriod) {\n const anchorMs = Date.parse(limit.anchor);\n // A hand-edited anchor must degrade to \"no window for this limit\", never\n // throw and take down every payment.\n if (Number.isNaN(anchorMs)) continue;\n const window = currentPeriodWindow({\n anchor: Math.floor(anchorMs / 1000),\n unit: limit.unit,\n multiplier: limit.multiplier,\n now: Math.floor(now.getTime() / 1000),\n permissionEnd: session.expiry,\n });\n const since = new Date(window.start * 1000).toISOString();\n usage.push({\n ...limit,\n spent: sumSpentSince(payerAddress, since),\n toppedUp: sumToppedUpSince(payerAddress, since),\n endsAt: new Date(window.end * 1000),\n source: 'ledger',\n });\n }\n return usage;\n}\n\n/**\n * The same list, asked of the chain first.\n *\n * The ledger undercounts by construction: it sees what went through\n * `payAndFetch` and nothing else, so a pull made by a `wallet_sendCalls` sent\n * through `jaw_rpc`, by a second machine, or by a write that never landed is\n * missing from it.\n *\n * Per limit, the two figures are combined rather than one replacing the other,\n * because each is ahead of the other in a different case. The chain is ahead\n * when something outside this CLI drew on the permission; the ledger is ahead\n * when our own top-up is signed and not yet mined. The larger is the only one\n * safe against both.\n *\n * A limit whose counter could not be read falls back to the ledger for that\n * limit alone. A node being down must not tighten a cap.\n */\nexport async function currentLimitUsageOnChain(\n policy: X402Policy,\n payerAddress: string,\n session: SessionConfig | null | undefined,\n now: Date = new Date(),\n deps: ReadDeps = {}\n): Promise<LimitUsage[]> {\n const local = currentLimitUsage(policy, payerAddress, session, now);\n if (!session || local.length === 0) return local;\n\n // The same token the policy was seeded from, resolved the same way.\n const asset = Object.values(USDC_BY_NETWORK).find((a) => a.chainId === session.chainId);\n if (!asset) return local;\n\n const onChain = await readCurrentPeriods(\n {\n chainId: session.chainId,\n permissionId: session.permissionId,\n permission: session.permission,\n token: asset.address,\n },\n deps\n );\n if (onChain.length === 0) return local;\n\n return local.map((limit) => {\n // Matched on the window and the allowance rather than on position, since\n // the policy skips limits it cannot normalise and the chain list does not.\n const match = onChain.find((candidate) => {\n const normalized = normalizePeriod(candidate.unit, candidate.multiplier);\n if (normalized?.unit !== limit.unit || normalized.multiplier !== limit.multiplier) return false;\n // Not `BigInt(...)` directly: these strings come off disk, and throwing\n // here rejects the whole call and takes down a payment the ledger figure\n // would have served.\n const a = parseBigInt(candidate.allowance);\n const b = parseBigInt(limit.allowance);\n return a !== null && b !== null && a === b;\n });\n if (!match || match.period.status !== 'ok') return limit;\n\n const since = new Date(match.period.start * 1000).toISOString();\n const fromLedger = sumToppedUpSince(payerAddress, since);\n const metered = match.period.spend >= fromLedger;\n return {\n ...limit,\n spent: sumSpentSince(payerAddress, since),\n toppedUp: metered ? match.period.spend : fromLedger,\n endsAt: new Date(match.period.end * 1000),\n source: metered ? 'chain' : 'ledger',\n };\n });\n}\n","import type { UsdcAsset } from './asset-registry.js';\n\n/**\n * The USDC a refill leaves behind so the next one can pay its own fee, in base\n * units of `asset`.\n *\n * Measured rather than guessed: a session's first operation, the most expensive\n * one it sends because it carries the EIP-7702 authorization and bootstraps the\n * permission manager as a co-owner, cost 0.0094 USDC on Base Sepolia. A tenth of\n * a token leaves ten times that.\n *\n * The grant seeds the session too, but it prices its own amount off the\n * paymaster's rate for the token (`account/spenderPrefund.ts` in @jaw.id/core),\n * so this is no longer a copy of that number and the two do not have to match.\n * All that is asked of either is that it covers an operation.\n *\n * It costs the granted allowance 0.10 once per session and not once per refill:\n * a refill fills the payer to price plus reserve, the payment takes the price,\n * and the reserve stays for the next one.\n *\n * An `upto` session asks for a second one on the payment that grants the\n * Permit2 allowance. That approval is an operation the payer pays for out of\n * its own balance, and a payer holding exactly the price cannot, so the bar for\n * skipping the refill rises by a reserve on top of the one every refill leaves.\n * The approval spends about a hundredth of it and the rest stays in the payer,\n * the same as the first.\n */\nexport function gasReserve(asset: UsdcAsset): bigint {\n return 10n ** BigInt(asset.decimals) / 10n;\n}\n\n/**\n * What one operation costs the session, in base units of `asset`.\n *\n * A cent, rounded up from the 0.0094 measured above so the figure stays a round\n * number rather than pretending to a precision a gas price does not have. It is\n * the floor a session has to clear to be able to send anything at all, which is\n * a different question from `gasReserve`: that one decides how much to leave\n * behind, this one decides whether there is enough to act.\n */\nexport function firstOperationCost(asset: UsdcAsset): bigint {\n return 10n ** BigInt(asset.decimals) / 100n;\n}\n","/**\n * Make server-controlled text safe to print.\n *\n * A paid endpoint controls its response body, and parts of its 402 challenge\n * reach our refusal messages, which are stored in the payment ledger and\n * reprinted by `jaw x402 log`. Printed raw, an escape sequence lets that server\n * erase the line the CLI just wrote and paint its own in place: a resource that\n * was never paid for can render a convincing green \"Paid. 5 USDC\" the CLI never\n * emitted, and once in the ledger it does so on every later read.\n *\n * Two shapes, because the danger differs. A newline inside a one-line field\n * forges an extra row that reads as a genuine record, while inside a response\n * body it is just a newline. Only the human renderers need any of this: JSON\n * output escapes control characters on its own.\n */\n\n/**\n * Characters that are invisible or reorder what follows them, in any context.\n *\n * Bidi overrides (U+202A-U+202E, U+2066-U+2069) are the Trojan Source trick:\n * they flip rendering direction, so an address can display as something other\n * than the bytes that were signed. The zero-width family hides text outright,\n * splitting an address to the eye while leaving it intact to a copy. Neither is\n * a control character in the C0 or C1 sense, so stripping those alone misses\n * both. U+2028 and U+2029 break lines the way a newline does.\n */\nconst INVISIBLE_AND_BIDI = /[\\u200B-\\u200F\\u2028\\u2029\\u202A-\\u202E\\u2066-\\u2069\\uFEFF]/g;\n\n/** C0 except tab and newline, DEL, and the C1 block. For multi-line text. */\n// eslint-disable-next-line no-control-regex\nconst BLOCK_CONTROLS = /[\\u0000-\\u0008\\u000B-\\u001F\\u007F-\\u009F]/g;\n\n/** Every C0 control, DEL and C1. For text that must stay on one line. */\n// eslint-disable-next-line no-control-regex\nconst LINE_CONTROLS = /[\\u0000-\\u001F\\u007F-\\u009F]/g;\n\n/** Left in place of a stripped character, so tampering shows rather than vanishing. */\nconst REPLACEMENT = '\\uFFFD';\n\nexport const DEFAULT_LINE_LENGTH = 200;\n\nfunction bound(text: string, maxLength: number): string {\n if (text.length <= maxLength) return text;\n // Say it was cut. A silently truncated error message reads as a complete one.\n return `${text.slice(0, maxLength)}\\u2026 (${text.length - maxLength} more characters)`;\n}\n\n/**\n * Disarm a value that has to render as a single line: a refusal reason, a\n * network id, a host, a transaction hash.\n *\n * Newlines go with everything else here. A reason carrying one would otherwise\n * open a second line under a log entry, indistinguishable from the real row\n * above it, which is the same forgery the escape sequences allow.\n */\nexport function sanitizeLine(value: unknown, maxLength: number = DEFAULT_LINE_LENGTH): string {\n const text = typeof value === 'string' ? value : String(value);\n return bound(text.replace(LINE_CONTROLS, REPLACEMENT).replace(INVISIBLE_AND_BIDI, REPLACEMENT), maxLength);\n}\n\n/**\n * Disarm a response body, keeping its shape.\n *\n * Newlines and tabs survive, since a body is legitimately multi-line and\n * neither can move the cursor back over text already written. Nothing is\n * truncated: the caller asked for this resource and may well have paid for it,\n * and the read is already bounded upstream by MAX_BODY_BYTES.\n */\nexport function sanitizeBlock(value: unknown): string {\n const text = typeof value === 'string' ? value : String(value);\n return text.replace(BLOCK_CONTROLS, REPLACEMENT).replace(INVISIBLE_AND_BIDI, REPLACEMENT);\n}\n","import { parseBigInt } from './amount.js';\nimport { sanitizeLine } from '../lib/terminal.js';\nimport type { PermissionLiveness } from './permission-onchain.js';\n\n/**\n * Presentation and diagnosis for `jaw x402 status`, kept apart from the command\n * so the rules can be tested without a session, a keystore or a network.\n */\n\n/** Base units to a readable amount, trailing zeros trimmed. */\nexport function formatUsdc(base: string | undefined, decimals: number): string {\n if (base === undefined) return 'unlimited';\n const value = parseBigInt(base);\n // Echoing the raw value put an unvalidated string on screen: amounts reach\n // here from the ledger and from config, both files that can be edited.\n if (value === null) return `${sanitizeLine(base, 32)} (invalid)`;\n const scale = 10n ** BigInt(decimals);\n const whole = value / scale;\n const frac = (value % scale).toString().padStart(decimals, '0').replace(/0+$/, '');\n return `${whole}${frac ? `.${frac}` : ''} USDC`;\n}\n\nexport function formatRemaining(seconds: number): string {\n const days = Math.floor(seconds / 86400);\n if (days > 0) return `${days} day${days === 1 ? '' : 's'} left`;\n const hours = Math.max(0, Math.floor(seconds / 3600));\n return `${hours}h left`;\n}\n\nexport interface StatusFacts {\n expired: boolean;\n /**\n * Defaults to `unknown`, which reports exactly what every session reported\n * before this could be read: the local file, and nothing more.\n */\n liveness?: PermissionLiveness;\n /**\n * True for a session an older CLI created, whose permission was granted to an\n * address separate from the session key. Auto mode refuses those, so a report\n * that stayed quiet about it would call a setup ready that cannot pay.\n */\n outdated?: boolean;\n ownerAddress: string;\n /** Formatted balance, or null when the read failed. */\n ownerBalance: string | null;\n payerBalance: string | null;\n /** False when the session's chain has no USDC in the registry. */\n hasAsset: boolean;\n spent: bigint;\n sessionCap: bigint | null;\n /**\n * The granted per-period cap and what has gone against it in the current\n * window: top-ups pulled through the permission, not payments, because the\n * cap mirrors the on-chain allowance and payments lag it by whatever float\n * the payer holds. Null when no grant seeded one. Reported separately from\n * the session cap because a grant-seeded policy usually has no session cap\n * at all, and checking only that one stayed quiet while the cap that\n * actually binds was exhausted.\n */\n periodCap?: bigint | null;\n periodSpent?: bigint | null;\n /** How the window reads in a sentence, e.g. \"day\" or \"2 weeks\". */\n periodLabel?: string | null;\n /**\n * The gas reserve refills leave in the payer, in the same formatted units as\n * the balances. A payer holding no more than this is holding what the CLI put\n * there to pay userOp fees, so it is not the misdirected-funds case below.\n */\n payerReserve?: number;\n}\n\n/**\n * What is stopping a payment, most likely cause first. Empty when nothing is.\n *\n * The interesting case is an empty owner next to a funded payer: payments still\n * succeed, so nothing looks wrong, but they are spending the payer's own balance\n * instead of pulling through the permission, which means the cap the user\n * granted is not being applied to anything.\n */\nexport function diagnose(facts: StatusFacts): string[] {\n const problems: string[] = [];\n\n if (facts.expired) {\n problems.push('The session expired. Run `jaw session setup --x402`.');\n }\n\n // Only the chain knows this one. Expiry is the same number the local file\n // carries, so it needs no read; a revoke made from keys.jaw.id or from\n // another machine leaves that file saying the session is fine.\n if (facts.liveness === 'revoked') {\n problems.push(\n 'The permission was revoked on chain, so nothing can be pulled through it any more. ' +\n 'Run `jaw session setup --x402` to grant a new one.'\n );\n }\n\n if (facts.liveness === 'unapproved') {\n problems.push(\n 'The chain has no record of this permission being approved. If the session was just created, ' +\n 'the grant may not have been mined yet; otherwise run `jaw session setup --x402`.'\n );\n }\n\n // `mismatch` is deliberately not here. It says the struct on disk does not\n // hash to the granted id, so the chain cannot be asked about this permission,\n // and nothing about the permission itself is wrong: the caps still apply and\n // payments still go through. Putting it in `problems` flipped `ready` to\n // false, which stops a script or an agent paying against a healthy session\n // over a local serialisation problem. It is reported on the permission line\n // instead.\n\n if (facts.outdated) {\n problems.push(\n 'This session was created by an older CLI and cannot pay: its permission belongs to an address ' +\n 'separate from the session key. Run `jaw session setup --x402` to recreate it.'\n );\n }\n\n if (!facts.hasAsset) {\n problems.push('This chain has no USDC configured, so x402 payments cannot be made on it.');\n }\n\n if (facts.hasAsset && facts.ownerBalance === null) {\n // Blaming the connection while the other balance rendered fine reads as a\n // contradiction, so only do it when both reads failed.\n problems.push(\n facts.payerBalance === null\n ? 'Could not read balances. Check the API key and network.'\n : `Could not read the owner balance for ${facts.ownerAddress}. The address may be malformed.`\n );\n }\n\n if (facts.ownerBalance !== null && Number(facts.ownerBalance) === 0) {\n // Above the reserve, because refills deliberately leave that much in the\n // payer to pay userOp fees with. Reading it back as funds sent to the wrong\n // address would tell the user to move money the CLI put there on purpose.\n const payerHoldsMoreThanItsGas =\n facts.payerBalance !== null && Number(facts.payerBalance) > (facts.payerReserve ?? 0);\n problems.push(\n payerHoldsMoreThanItsGas\n ? 'The owner account is empty but the payer holds USDC. Payments will work, but they bypass the ' +\n 'permission, so the cap you granted is not applying. Move the funds to the owner.'\n : 'The owner account holds no USDC, so there is nothing to pay with.'\n );\n }\n\n // Before the session cap: this is the one that mirrors the permission, so when\n // both are exhausted it is the more useful thing to say, and it frees up on its\n // own rather than needing a config change.\n if (facts.periodCap != null && facts.periodSpent != null && facts.periodSpent >= facts.periodCap) {\n problems.push(\n `The granted allowance for this ${facts.periodLabel ?? 'period'} is used up. It resets at the end of ` +\n 'the window, or grant a new permission with `jaw session setup --x402`.'\n );\n }\n\n if (facts.sessionCap !== null && facts.spent >= facts.sessionCap) {\n problems.push(\n 'The session cap is used up. Raise it with `jaw config set x402.maxTotalPerSession <base units>` ' +\n 'or start a new session.'\n );\n }\n\n return problems;\n}\n","import { saveRecoveredPermission, parseGrantedPermission, type SessionConfig } from '../lib/session-config.js';\nimport type { GrantedPermission } from '../lib/session-config.js';\n\n/**\n * The permission struct for a session that was created before the CLI kept one.\n *\n * Those sessions hold only the id, and no view on the permission manager takes\n * an id, so every on-chain read reports \"cannot tell\" and `session add` refuses\n * for want of a scope to merge against. Telling people to run setup again is a\n * poor answer when the grant is recoverable: the relay stores what was granted,\n * keyed by the same hash, and `getPermissionFromRelay` returns it.\n *\n * Recovered once and written back, so this costs one GET on the first command\n * that needs it and nothing afterwards. Anything that goes wrong leaves the\n * session exactly as it was, which is the behaviour every such session had\n * before this existed: a failed recovery must not turn a working command into\n * an error.\n */\n\nconst RECOVERY_TIMEOUT_MS = 5_000;\n\nexport interface RecoveryDeps {\n /** Injected for tests, and so the unit tests never import core. */\n fetchPermission?: (permissionId: string, apiKey: string) => Promise<unknown>;\n timeoutMs?: number;\n}\n\nexport async function recoverPermission(\n session: SessionConfig,\n apiKey: string | undefined,\n deps: RecoveryDeps = {}\n): Promise<GrantedPermission | undefined> {\n if (session.permission) return session.permission;\n if (!apiKey) return undefined;\n\n const fetchPermission =\n deps.fetchPermission ??\n (async (id: string, key: string) => {\n // Lazy, like every other core import in the CLI: a static one pulls the\n // whole SDK into startup for a command that may never need it.\n const { getPermissionFromRelay } = await import('@jaw.id/core');\n return getPermissionFromRelay(id as `0x${string}`, key);\n });\n\n let timer: ReturnType<typeof setTimeout> | undefined;\n try {\n const expired = new Promise<never>((_, reject) => {\n timer = setTimeout(() => reject(new Error('timed out')), deps.timeoutMs ?? RECOVERY_TIMEOUT_MS);\n });\n const relayed = await Promise.race([fetchPermission(session.permissionId, apiKey), expired]);\n const permission = parseGrantedPermission(relayed);\n if (!permission) return undefined;\n // Checked against the session before it is trusted. The on-chain `getHash`\n // gate covers the case where the chain can be reached, and `session add`\n // goes ahead on `unknown`, which is exactly what a chain outside the\n // client's registry returns: there the union would be built from an\n // unverified relay response with nothing local to corroborate it. These\n // three cost nothing and make it a response about this session or none.\n if (\n permission.account.toLowerCase() !== session.ownerAddress.toLowerCase() ||\n permission.spender.toLowerCase() !== session.sessionAddress.toLowerCase() ||\n permission.end !== session.expiry // safe now that nothing else rewrites expiry\n ) {\n return undefined;\n }\n // Whether it describes the permission that was granted is settled on chain,\n // by the `getHash` check every read here goes through. Storing one that does\n // not match costs a `mismatch` report, which is what the session already\n // showed as `unknown`, and never a wrong answer.\n // Undefined when it did not land, which means the session went away while\n // this was waiting on the relay. Handing the struct back anyway would let a\n // caller go on to merge against, or report on, a session that no longer\n // exists.\n return saveRecoveredPermission(session, permission) ? permission : undefined;\n } catch {\n return undefined;\n } finally {\n clearTimeout(timer);\n }\n}\n","import { BaseCommand } from '../../base-command.js';\nimport { keystoreExists } from '../../lib/keystore.js';\nimport { loadConfig } from '../../lib/config.js';\nimport { isLegacySession, liveOrphans, tryLoadSessionConfig } from '../../lib/session-config.js';\nimport { sessionPayerAddress } from '../../x402/payer.js';\nimport { usdcBalance } from '../../x402/balance.js';\nimport { sumSpentSince } from '../../x402/ledger.js';\nimport { resolveSessionX402Policy, sameLimit } from '../../x402/policy.js';\nimport { currentLimitUsageOnChain } from '../../x402/spend-window.js';\nimport { describePeriod } from '../../x402/period.js';\nimport { parseBigInt } from '../../x402/amount.js';\nimport { USDC_BY_NETWORK } from '../../x402/asset-registry.js';\nimport { gasReserve } from '../../x402/gas-reserve.js';\nimport { formatUsdc, formatRemaining, diagnose } from '../../x402/status-report.js';\nimport { readLiveness, type PermissionLiveness } from '../../x402/permission-onchain.js';\nimport { recoverPermission } from '../../x402/permission-recovery.js';\nimport type { OutputFormat } from '../../lib/types.js';\n\n/**\n * Answer \"is my x402 setup right?\" without spending anything.\n *\n * The three things that break a setup are invisible until a payment fails:\n * funding the payer instead of the owner (payments then work, but the permission\n * is never exercised and the cap never applies), a permission that does not\n * match what you thought you granted, and a session cap already used up. All\n * three are readable state, so read them.\n */\nexport default class X402Status extends BaseCommand {\n static override description =\n 'Show x402 payment readiness: which account holds the funds, the resolved caps, and what has been spent. Reads only, never pays.';\n\n static override examples = ['<%= config.bin %> x402 status', '<%= config.bin %> x402 status --output json'];\n\n static override flags = {\n ...BaseCommand.baseFlags,\n };\n\n async run(): Promise<void> {\n const { flags } = await this.parse(X402Status);\n const format = flags.output as OutputFormat;\n\n const session = keystoreExists() ? tryLoadSessionConfig() : null;\n if (!session) {\n if (format === 'json') {\n this.outputResult({ ready: false, reason: 'no session' }, format);\n return;\n }\n this.log('No session. Run `jaw session setup --x402` to create one.');\n return;\n }\n\n const config = loadConfig();\n const now = Date.now() / 1000;\n const expired = session.expiry <= now;\n\n const asset = Object.values(USDC_BY_NETWORK).find((a) => a.chainId === session.chainId);\n const payer = sessionPayerAddress();\n // Permissions from earlier sessions that are still live on chain and that\n // the caps below do not describe. Said in terms of the permission rather\n // than of this key: setup generates a fresh key whenever it is not reusing\n // one, and `--yes` always does, so the key that could exercise these is\n // usually gone from this machine. What is left is a grant on the account\n // that nothing here meters and that outlives the session that made it.\n const stillLive = liveOrphans(session.orphanedPermissions, now);\n\n // Network reads, all of them failing soft. The local half of the answer\n // (caps, spend, expiry) is still worth printing, and an unreachable RPC is\n // itself a useful thing to see.\n const [balances, recovered] = await Promise.all([\n Promise.all(\n [session.ownerAddress as `0x${string}`, payer].map(async (address) => {\n if (!asset) return null;\n try {\n return (await usdcBalance(asset.wireNetwork, address)).formatted;\n } catch {\n return null;\n }\n })\n ),\n // Recovered first for a session written before the struct was stored,\n // which is otherwise stuck reporting \"cannot tell\" forever.\n recoverPermission(session, config.apiKey),\n ]);\n const [ownerBalance, payerBalance] = balances;\n // Threaded through the rest of the command, not just the liveness read.\n // Without this the run that performed the recovery still reported no period\n // figure, because the on-chain read takes the struct off the session object\n // and that one was still the version loaded from disk.\n const current = recovered ? { ...session, permission: recovered } : session;\n const liveness = await readLiveness(current);\n\n // Resolved from the recovered session, not the one loaded off disk. Seeded\n // from the grant exactly as the paying paths do: resolving from config\n // alone reported the 10-USDC default that a grant-seeded policy deletes.\n // Resolving before the recovery had the same effect on the run that\n // performed it, which is the run where it matters most.\n const policy = resolveSessionX402Policy(config.x402, current);\n\n const spent = sumSpentSince(payer, session.createdAt);\n\n const sessionCap = parseBigInt(policy.maxTotalPerSession);\n const decimals = asset?.decimals ?? 6;\n\n // The cap that actually mirrors the permission, and what is left of it right\n // now. Without this the report was silent about the only cap a grant-seeded\n // session enforces. Asked of the chain first, which knows about pulls this\n // CLI's ledger never saw.\n // Every limit on the payment token, each with its own window and its own\n // usage. Reducing them to one was reporting a month's budget as a day's.\n const usage = await currentLimitUsageOnChain(policy, payer, current);\n // Joined onto the limits the policy holds, not read off the usage list. A\n // limit whose usage could not be computed is still enforced by\n // `checkPolicy`, and reporting only what has usage made it invisible here:\n // no line, no json entry, and a `ready: true` for a session whose grant is\n // the thing bounding it.\n const limits = (policy.perPeriod ?? []).map((limit) => {\n const measured = usage.find((entry) => sameLimit(entry, limit));\n return (\n measured ?? {\n ...limit,\n spent: 0n,\n toppedUp: 0n,\n endsAt: null,\n source: 'unmeasured' as const,\n }\n );\n });\n // The one with the least room left, which is what the verdict is about.\n // Picking the smallest allowance instead said `ready: true` for a session\n // whose month was drained, because today's counter was still at zero, right\n // under a printed line reading 100 of 100 USDC used this month.\n //\n // Unparseable allowances are skipped rather than thrown on: they reach here\n // from a config file someone can edit, and the rest of this command reports\n // a bad value instead of dying on it.\n const remaining = (limit: (typeof limits)[number]) => {\n const cap = parseBigInt(limit.allowance);\n if (cap === null) return null;\n return cap > limit.toppedUp ? cap - limit.toppedUp : 0n;\n };\n const tightest = limits.reduce<(typeof limits)[number] | null>((a, b) => {\n const left = remaining(b);\n if (left === null) return a;\n const best = a === null ? null : remaining(a);\n return best === null || left < best ? b : a;\n }, null);\n\n // One verdict for both renderers. `ready` used to be its own expression and\n // drifted from the warnings: a setup whose owner was empty printed a loud\n // \"the cap is not applying\" and still reported ready:true to a script.\n const problems = diagnose({\n expired,\n liveness,\n ownerAddress: session.ownerAddress,\n ownerBalance,\n payerBalance,\n hasAsset: asset !== undefined,\n spent,\n sessionCap,\n periodCap: tightest ? parseBigInt(tightest.allowance) : null,\n // Top-ups, not payments: the period cap mirrors the on-chain allowance\n // and the top-up is what draws it down, exactly as `topUpCeiling`\n // measures it. Payments lag by whatever float the payer still holds,\n // which kept this check quiet while the grant was already drained.\n // Null, not the zero an unmeasured limit carries: `diagnose` compares it\n // against the cap, and a figure nobody read is not a measurement of zero.\n periodSpent: tightest && tightest.endsAt !== null ? tightest.toppedUp : null,\n periodLabel: tightest ? describePeriod(tightest.unit, tightest.multiplier) : null,\n outdated: isLegacySession(session),\n // Same units as the formatted balances. Exact in a double: the reserve\n // is a tenth of a token, six decimals at most.\n payerReserve: asset ? Number(gasReserve(asset)) / 10 ** asset.decimals : 0,\n });\n\n if (format === 'json') {\n this.outputResult(\n {\n ready: problems.length === 0,\n problems,\n chainId: session.chainId,\n permission: { id: session.permissionId, onChain: liveness },\n ...(stillLive.length > 0 ? { stillLiveOnChain: stillLive } : {}),\n owner: { address: session.ownerAddress, usdc: ownerBalance },\n payer: { address: payer, usdc: payerBalance },\n policy: {\n maxAmountPerPayment: policy.maxAmountPerPayment,\n maxTotalPerSession: policy.maxTotalPerSession,\n // Every one of them: the contract charges all, and naming one as\n // the budget is what this set out to stop.\n perPeriod: limits.map((limit) => ({\n allowance: limit.allowance,\n unit: limit.unit,\n multiplier: limit.multiplier,\n used: limit.endsAt === null ? null : limit.toppedUp.toString(),\n usedFrom: limit.source,\n resetsAt: limit.endsAt === null ? null : limit.endsAt.toISOString(),\n })),\n topUpFloat: policy.topUpFloat,\n },\n spentThisSession: spent.toString(),\n\n expiry: session.expiry,\n expired,\n },\n format\n );\n return;\n }\n\n this.log(expired ? 'Session expired.\\n' : 'Session active.\\n');\n\n this.log(` owner ${session.ownerAddress} ${fmt(ownerBalance)} <- funds go here`);\n this.log(` payer ${payer} ${fmt(payerBalance)}`);\n this.log('');\n this.log(` chain ${session.chainId}${asset ? '' : ' (no USDC configured for this chain)'}`);\n // Only printed when the chain answered. A line saying \"unknown\" on every\n // run of an older session is noise about a question nobody asked.\n if (liveness !== 'unknown') {\n this.log(` perm ${session.permissionId} ${LIVENESS_LABEL[liveness]}`);\n }\n if (stillLive.length > 0) {\n this.log(\n ` ${stillLive.length} permission${stillLive.length === 1 ? '' : 's'} from earlier sessions ` +\n 'still live on this account; `jaw session revoke` removes them too'\n );\n }\n this.log(` caps ${formatUsdc(policy.maxAmountPerPayment, decimals)} per payment`);\n // Every limit, each with its own window and reset. One of them used to\n // stand for all, which is how a 100-a-month cap was reported as 50 a day.\n for (const limit of limits) {\n const floor = limit.source === 'chain' ? '' : 'at least ';\n // A limit with no window is one whose usage could not be computed. It\n // still binds, so it is reported, and the missing figure is named as\n // missing rather than printed as a zero.\n const window = limit.endsAt === null ? ' (usage unknown)' : ` (resets ${limit.endsAt.toISOString()})`;\n const used = limit.endsAt === null ? '?' : `${floor}${formatUsdc(limit.toppedUp.toString(), decimals)}`;\n this.log(\n ` ${used} of ${formatUsdc(limit.allowance, decimals)} used this ` +\n `${describePeriod(limit.unit, limit.multiplier)}${window}`\n );\n }\n if (limits.length > 1) {\n this.log(' all of them apply, so the tightest is what binds');\n }\n this.log(\n ` at least ${formatUsdc(spent.toString(), decimals)} of ${formatUsdc(policy.maxTotalPerSession, decimals)} spent this session`\n );\n // The session total has no on-chain counterpart, so it is always a floor:\n // the ledger sees what went through payAndFetch, and a `wallet_sendCalls`\n // sent through jaw_rpc spends the same permission without writing a row.\n this.log(\" the session figure is counted from this CLI's ledger, which a direct jaw_rpc send bypasses\");\n if (policy.topUpFloat) {\n this.log(` float tops the payer up to ${formatUsdc(policy.topUpFloat, decimals)} when it runs short`);\n }\n this.log(\n ` expires ${new Date(session.expiry * 1000).toISOString()}${expired ? '' : ` (${formatRemaining(session.expiry - now)})`}`\n );\n\n // Most likely blocker first.\n if (problems.length > 0) {\n this.log('');\n for (const problem of problems) this.log(` ! ${problem}`);\n }\n }\n}\n\nfunction fmt(balance: string | null): string {\n return balance === null ? ' ? ' : `${balance} USDC`;\n}\n\nconst LIVENESS_LABEL: Record<Exclude<PermissionLiveness, 'unknown'>, string> = {\n active: 'live on chain',\n revoked: 'REVOKED on chain',\n unapproved: 'not approved on chain',\n mismatch: 'does not match the stored permission',\n};\n"]}
|
package/dist/index.js
CHANGED
|
@@ -13,7 +13,9 @@ var PATHS = {
|
|
|
13
13
|
session: path.join(JAW_DIR, "session.json"),
|
|
14
14
|
relay: path.join(JAW_DIR, "relay.json"),
|
|
15
15
|
keystore: path.join(JAW_DIR, "keystore.json"),
|
|
16
|
-
sessionConfig: path.join(JAW_DIR, "session-config.json")
|
|
16
|
+
sessionConfig: path.join(JAW_DIR, "session-config.json"),
|
|
17
|
+
x402Log: path.join(JAW_DIR, "x402-log.jsonl"),
|
|
18
|
+
paymentLock: path.join(JAW_DIR, "x402-payment.lock")
|
|
17
19
|
};
|
|
18
20
|
|
|
19
21
|
// src/lib/validation.ts
|
|
@@ -223,7 +225,18 @@ function bufferToBase64(buf) {
|
|
|
223
225
|
}
|
|
224
226
|
|
|
225
227
|
// src/lib/ws-bridge.ts
|
|
228
|
+
function buildInitPayload(config) {
|
|
229
|
+
return {
|
|
230
|
+
type: "init",
|
|
231
|
+
apiKey: config.apiKey,
|
|
232
|
+
chainId: config.chainId,
|
|
233
|
+
ens: config.ens,
|
|
234
|
+
paymasterUrl: config.paymasterUrl,
|
|
235
|
+
...config.paymasterUrl && config.paymasterContext ? { paymasterContext: config.paymasterContext } : {}
|
|
236
|
+
};
|
|
237
|
+
}
|
|
226
238
|
var DEFAULT_TIMEOUT_MS = 12e4;
|
|
239
|
+
var DEFAULT_CONNECT_TIMEOUT_MS = 3e4;
|
|
227
240
|
var MAX_MESSAGE_BYTES = 5 * 1024 * 1024;
|
|
228
241
|
var BROWSER_REOPEN_COOLDOWN_MS = 5e3;
|
|
229
242
|
var MAX_RECONNECT_ATTEMPTS = 3;
|
|
@@ -232,6 +245,7 @@ var WSBridge = class {
|
|
|
232
245
|
relayUrl;
|
|
233
246
|
session;
|
|
234
247
|
timeout;
|
|
248
|
+
connectTimeout;
|
|
235
249
|
config;
|
|
236
250
|
privateKeyHex;
|
|
237
251
|
publicKeyHex;
|
|
@@ -253,6 +267,7 @@ var WSBridge = class {
|
|
|
253
267
|
this.relayUrl = options.relayUrl;
|
|
254
268
|
this.session = options.session;
|
|
255
269
|
this.timeout = options.timeout ?? DEFAULT_TIMEOUT_MS;
|
|
270
|
+
this.connectTimeout = options.connectTimeout ?? DEFAULT_CONNECT_TIMEOUT_MS;
|
|
256
271
|
this.config = options.config;
|
|
257
272
|
this.privateKeyHex = options.privateKeyHex;
|
|
258
273
|
this.publicKeyHex = options.publicKeyHex;
|
|
@@ -281,17 +296,16 @@ var WSBridge = class {
|
|
|
281
296
|
let expectingKeyExchange = !this.peerPublicKeyHex;
|
|
282
297
|
const timer = setTimeout(() => {
|
|
283
298
|
ws.close();
|
|
284
|
-
reject(
|
|
285
|
-
|
|
299
|
+
reject(
|
|
300
|
+
new Error(
|
|
301
|
+
`Browser did not connect within ${Math.round(this.connectTimeout / 1e3)}s.
|
|
302
|
+
Run \`jaw disconnect\` then try again, or raise JAW_BRIDGE_TIMEOUT_MS.`
|
|
303
|
+
)
|
|
304
|
+
);
|
|
305
|
+
}, this.connectTimeout);
|
|
286
306
|
const sendEncryptedInit = async () => {
|
|
287
307
|
if (!this.sharedSecret) return;
|
|
288
|
-
const envelope = await encryptMessage(this.sharedSecret,
|
|
289
|
-
type: "init",
|
|
290
|
-
apiKey: this.config.apiKey,
|
|
291
|
-
chainId: this.config.chainId,
|
|
292
|
-
ens: this.config.ens,
|
|
293
|
-
paymasterUrl: this.config.paymasterUrl
|
|
294
|
-
});
|
|
308
|
+
const envelope = await encryptMessage(this.sharedSecret, buildInitPayload(this.config));
|
|
295
309
|
this.sendRaw(ws, JSON.stringify({ type: "encrypted", ...envelope }));
|
|
296
310
|
};
|
|
297
311
|
const waitForReady = () => {
|
|
@@ -569,6 +583,10 @@ var DEFAULT_KEYS_URL = "https://keys.jaw.id";
|
|
|
569
583
|
var DEFAULT_RELAY_URL = "wss://relay.jaw.id";
|
|
570
584
|
async function getBridge(options) {
|
|
571
585
|
const config = loadConfig();
|
|
586
|
+
const envTimeout = Number(process.env["JAW_BRIDGE_TIMEOUT_MS"]);
|
|
587
|
+
const fromEnv = Number.isFinite(envTimeout) && envTimeout > 0 ? envTimeout : void 0;
|
|
588
|
+
const timeout = options.timeout ?? fromEnv;
|
|
589
|
+
const connectTimeout = options.connectTimeout ?? fromEnv;
|
|
572
590
|
const keysUrl = options.keysUrl ?? config.keysUrl ?? DEFAULT_KEYS_URL;
|
|
573
591
|
const relayUrl = options.relayUrl ?? config.relayUrl ?? DEFAULT_RELAY_URL;
|
|
574
592
|
const chainId = options.chainId ?? config.defaultChain ?? 1;
|
|
@@ -581,7 +599,7 @@ async function getBridge(options) {
|
|
|
581
599
|
let relaySession = loadRelaySession();
|
|
582
600
|
if (relaySession && relaySession.relayUrl === relayUrl && relaySession.peerPublicKey) {
|
|
583
601
|
try {
|
|
584
|
-
return await connectBridge(
|
|
602
|
+
return await connectBridge({ ...options, timeout }, relaySession, chainId, keysUrl, relayUrl, false);
|
|
585
603
|
} catch {
|
|
586
604
|
deleteRelaySession();
|
|
587
605
|
relaySession = null;
|
|
@@ -591,7 +609,7 @@ async function getBridge(options) {
|
|
|
591
609
|
}
|
|
592
610
|
const session = await createNewSession(relayUrl);
|
|
593
611
|
saveRelaySession(session);
|
|
594
|
-
return await connectBridge(
|
|
612
|
+
return await connectBridge({ ...options, timeout, connectTimeout }, session, chainId, keysUrl, relayUrl, true);
|
|
595
613
|
}
|
|
596
614
|
async function createNewSession(relayUrl) {
|
|
597
615
|
const kp = await generateKeyPair();
|
|
@@ -606,17 +624,20 @@ async function createNewSession(relayUrl) {
|
|
|
606
624
|
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
607
625
|
};
|
|
608
626
|
}
|
|
609
|
-
async function connectBridge(
|
|
627
|
+
async function connectBridge(options, relaySession, chainId, keysUrl, relayUrl, openBrowser) {
|
|
610
628
|
const config = loadConfig();
|
|
629
|
+
const paymaster = config.paymasters?.[chainId];
|
|
611
630
|
const bridge = new WSBridge({
|
|
612
631
|
relayUrl,
|
|
613
632
|
session: relaySession.session,
|
|
614
633
|
timeout: options.timeout,
|
|
634
|
+
connectTimeout: options.connectTimeout,
|
|
615
635
|
config: {
|
|
616
636
|
apiKey: options.apiKey,
|
|
617
637
|
chainId,
|
|
618
638
|
ens: options.ens ?? config.ens,
|
|
619
|
-
paymasterUrl:
|
|
639
|
+
paymasterUrl: paymaster?.url,
|
|
640
|
+
paymasterContext: paymaster?.context
|
|
620
641
|
},
|
|
621
642
|
privateKeyHex: relaySession.privateKey,
|
|
622
643
|
publicKeyHex: relaySession.publicKey,
|
|
@@ -626,6 +647,12 @@ async function connectBridge(relaySession, options, chainId, keysUrl, relayUrl,
|
|
|
626
647
|
// onBrowserNeeded — only open a browser for new sessions
|
|
627
648
|
openBrowser ? async () => {
|
|
628
649
|
const bridgeUrl = buildBridgeUrl(keysUrl, relaySession.session, relayUrl, relaySession.publicKey);
|
|
650
|
+
if (process.env["JAW_NO_BROWSER"]) {
|
|
651
|
+
process.stderr.write(`Open this URL to approve:
|
|
652
|
+
${bridgeUrl}
|
|
653
|
+
`);
|
|
654
|
+
return;
|
|
655
|
+
}
|
|
629
656
|
const { default: open } = await import('open');
|
|
630
657
|
await open(bridgeUrl);
|
|
631
658
|
} : void 0,
|