Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/persistent-cli-sessions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'mppx': patch
---

Added persistent CLI payment sessions with automatic reuse, inspection, and explicit closing.
15 changes: 14 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,16 +97,29 @@ npx gitpick wevm/mppx/examples/charge

## CLI

`mppx` includes a basic CLI for making HTTP requests with automatic payment handling.
`mppx` includes a basic CLI for making HTTP requests with automatic payment handling. Tempo
session channels are retained and reused automatically until you close them.

```bash
# create account - stored in keychain, autofunded on testnet
mppx account create

# make request - automatic payment handling, curl-like api
mppx example.com

# open another session instead of reusing the preferred channel
mppx example.com --session new

# inspect and close retained sessions
mppx sessions list
mppx sessions view <channel-id>
mppx sessions close <channel-id>
mppx sessions close --all --yes
```

`--session auto` is the default. Pass `new` to open another channel or a channel ID to select one
explicitly.

You can also install globally to use the `mppx` CLI from anywhere:

```bash
Expand Down
55 changes: 45 additions & 10 deletions src/cli/account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import * as path from 'node:path'

import { Errors } from 'incur'

import { isTempoAccount } from './utils.js'

const SERVICE_NAME = 'mppx'
const defaultCommandTimeoutMs = 10_000

Expand Down Expand Up @@ -203,6 +205,48 @@ export function createKeychain(account = 'main') {
}
}

/** Resolves a local CLI signer together with its durable account reference. */
export async function resolveLocalAccount(name?: string) {
const { privateKeyToAccount } = await import('viem/accounts')

const envKey = process.env.MPPX_PRIVATE_KEY?.trim()
if (envKey)
return {
account: privateKeyToAccount(envKey as `0x${string}`),
source: 'environment' as const,
}

const accountName = resolveAccountName(name)
const key = await createKeychain(accountName).get()
if (key)
return {
account: privateKeyToAccount(key as `0x${string}`),
accountName,
source: 'keychain' as const,
}

throw new Error(`Account "${accountName}" not found.`)
}

/** Resolves an account supported by persistent CLI sessions. */
export async function resolvePersistentAccount(name?: string) {
const accountName = resolveAccountName(name)
if (!process.env.MPPX_PRIVATE_KEY?.trim() && isTempoAccount(accountName))
throw new Errors.IncurError({
code: 'UNSUPPORTED_ACCOUNT',
message: 'Persistent sessions require an mppx account or MPPX_PRIVATE_KEY.',
exitCode: 2,
})
return resolveLocalAccount(name).catch((cause: unknown) => {
throw new Errors.IncurError({
code: 'ACCOUNT_NOT_FOUND',
message: cause instanceof Error ? cause.message : 'No account found.',
exitCode: 69,
...(cause instanceof Error && { cause }),
})
})
}

/**
* Resolve a CLI account to a viem `LocalAccount`.
*
Expand All @@ -221,14 +265,5 @@ export function createKeychain(account = 'main') {
* ```
*/
export async function resolveAccount(name?: string) {
const { privateKeyToAccount } = await import('viem/accounts')

const envKey = process.env.MPPX_PRIVATE_KEY?.trim()
if (envKey) return privateKeyToAccount(envKey as `0x${string}`)

const accountName = resolveAccountName(name)
const key = await createKeychain(accountName).get()
if (key) return privateKeyToAccount(key as `0x${string}`)

throw new Error(`Account "${accountName}" not found.`)
return (await resolveLocalAccount(name)).account
}
Loading
Loading