@caypo/canton-cli 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +14 -0
- package/.turbo/turbo-test.log +12 -0
- package/README.md +101 -0
- package/SPEC.md +41 -0
- package/dist/index.js +468 -0
- package/dist/index.js.map +1 -0
- package/package.json +59 -0
- package/src/commands/address.ts +23 -0
- package/src/commands/balance.ts +32 -0
- package/src/commands/init.ts +141 -0
- package/src/commands/mcp.ts +105 -0
- package/src/commands/pay.ts +53 -0
- package/src/commands/safeguards.ts +107 -0
- package/src/commands/send.ts +36 -0
- package/src/commands/traffic.ts +49 -0
- package/src/helpers/format.ts +29 -0
- package/src/helpers/load-agent.ts +28 -0
- package/src/index.ts +35 -0
- package/tsconfig.json +8 -0
- package/tsup.config.ts +12 -0
- package/vitest.config.ts +8 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/commands/init.ts","../src/helpers/format.ts","../src/commands/balance.ts","../src/helpers/load-agent.ts","../src/commands/send.ts","../src/commands/pay.ts","../src/commands/address.ts","../src/commands/safeguards.ts","../src/commands/traffic.ts","../src/commands/mcp.ts"],"sourcesContent":["/**\n * @caypo/canton-cli — CLI for Canton Network agent.\n * Binary: caypo (aliases: canton-agent, ca)\n */\n\nimport { Command } from \"commander\";\nimport { CANTON_SDK_VERSION } from \"@caypo/canton-sdk\";\n\nimport { initCommand } from \"./commands/init.js\";\nimport { balanceCommand } from \"./commands/balance.js\";\nimport { sendCommand } from \"./commands/send.js\";\nimport { payCommand } from \"./commands/pay.js\";\nimport { addressCommand } from \"./commands/address.js\";\nimport { safeguardsCommand } from \"./commands/safeguards.js\";\nimport { trafficCommand } from \"./commands/traffic.js\";\nimport { mcpCommand } from \"./commands/mcp.js\";\n\nexport const CANTON_CLI_VERSION = \"0.1.0\";\n\nconst program = new Command();\n\nprogram\n .name(\"caypo\")\n .description(\"CAYPO — A bank account for AI agents on Canton Network\")\n .version(CANTON_CLI_VERSION, \"-v, --version\")\n .addCommand(initCommand)\n .addCommand(balanceCommand)\n .addCommand(sendCommand)\n .addCommand(payCommand)\n .addCommand(addressCommand)\n .addCommand(safeguardsCommand)\n .addCommand(trafficCommand)\n .addCommand(mcpCommand);\n\nprogram.parse(process.argv);\n","/**\n * caypo init — Interactive wallet setup.\n */\n\nimport { Command } from \"commander\";\nimport inquirer from \"inquirer\";\nimport ora from \"ora\";\nimport chalk from \"chalk\";\nimport {\n CantonClient,\n Keystore,\n saveConfig,\n DEFAULT_CONFIG,\n SafeguardManager,\n type AgentConfig,\n} from \"@caypo/canton-sdk\";\nimport { banner, keyValue, successMessage, errorMessage, accent } from \"../helpers/format.js\";\n\nexport const initCommand = new Command(\"init\")\n .description(\"Interactive wallet setup — create keystore, allocate party\")\n .action(async () => {\n banner();\n console.log(chalk.gray(\" Setting up your Canton agent wallet...\\n\"));\n\n try {\n // 1. Prompt for PIN\n const { pin } = await inquirer.prompt([\n {\n type: \"password\",\n name: \"pin\",\n message: \"Choose a PIN for your wallet:\",\n mask: \"*\",\n validate: (v: string) => v.length >= 4 || \"PIN must be at least 4 characters\",\n },\n ]);\n\n const { confirmPin } = await inquirer.prompt([\n {\n type: \"password\",\n name: \"confirmPin\",\n message: \"Confirm PIN:\",\n mask: \"*\",\n validate: (v: string) => v === pin || \"PINs do not match\",\n },\n ]);\n\n // 2. Prompt for ledger URL\n const { ledgerUrl } = await inquirer.prompt([\n {\n type: \"input\",\n name: \"ledgerUrl\",\n message: \"Canton Ledger URL:\",\n default: \"http://localhost:7575\",\n },\n ]);\n\n // 3. Prompt for JWT\n const { jwt } = await inquirer.prompt([\n {\n type: \"password\",\n name: \"jwt\",\n message: \"JWT bearer token:\",\n mask: \"*\",\n validate: (v: string) => v.length > 0 || \"JWT is required\",\n },\n ]);\n\n // 4. Prompt for user ID\n const { userId } = await inquirer.prompt([\n {\n type: \"input\",\n name: \"userId\",\n message: \"Ledger API user ID:\",\n default: \"ledger-api-user\",\n },\n ]);\n\n // 5. Prompt for display name (party hint)\n const { displayName } = await inquirer.prompt([\n {\n type: \"input\",\n name: \"displayName\",\n message: \"Agent display name:\",\n default: \"Agent\",\n },\n ]);\n\n // 6. Allocate party\n const spinner = ora(\"Allocating party on Canton ledger...\").start();\n\n const client = new CantonClient({ ledgerUrl, token: jwt, userId });\n\n let partyId: string;\n try {\n const party = await client.allocateParty(displayName);\n partyId = party.party;\n spinner.succeed(\"Party allocated\");\n } catch (err) {\n spinner.fail(\"Failed to allocate party\");\n errorMessage((err as Error).message);\n process.exit(1);\n }\n\n // 7. Create keystore\n const keystoreSpinner = ora(\"Creating encrypted keystore...\").start();\n await Keystore.create(pin, { partyId, jwt, userId });\n keystoreSpinner.succeed(\"Keystore created\");\n\n // 8. Save config\n const config: AgentConfig = {\n ...DEFAULT_CONFIG,\n ledgerUrl,\n partyId,\n userId,\n };\n await saveConfig(config);\n\n // 9. Initialize safeguards\n const safeguards = new SafeguardManager();\n safeguards.setTxLimit(DEFAULT_CONFIG.safeguards.txLimit);\n safeguards.setDailyLimit(DEFAULT_CONFIG.safeguards.dailyLimit);\n\n // 10. Show success\n console.log(\"\");\n successMessage(\"Canton agent wallet created successfully!\");\n console.log(\"\");\n keyValue(\"Party ID\", accent(partyId));\n keyValue(\"Ledger URL\", ledgerUrl);\n keyValue(\"Config\", \"~/.caypo/config.json\");\n keyValue(\"Keystore\", \"~/.caypo/wallet.key\");\n console.log(\"\");\n console.log(chalk.gray(\" Next steps:\"));\n console.log(chalk.gray(\" caypo balance — Check your balance\"));\n console.log(chalk.gray(\" caypo mcp install — Install MCP server for AI tools\"));\n console.log(chalk.gray(\" caypo send 1 to <party> — Send USDCx\"));\n console.log(\"\");\n } catch (err) {\n errorMessage(`Setup failed: ${(err as Error).message}`);\n process.exit(1);\n }\n });\n","/**\n * CLI formatting helpers.\n */\n\nimport chalk from \"chalk\";\n\nexport const label = (text: string) => chalk.gray(text);\nexport const value = (text: string) => chalk.white.bold(text);\nexport const success = (text: string) => chalk.green(text);\nexport const warn = (text: string) => chalk.yellow(text);\nexport const fail = (text: string) => chalk.red(text);\nexport const accent = (text: string) => chalk.cyan(text);\nexport const dim = (text: string) => chalk.dim(text);\n\nexport function banner(): void {\n console.log(chalk.cyan.bold(\"\\n CAYPO\") + chalk.gray(\" — A bank account for AI agents on Canton Network\\n\"));\n}\n\nexport function keyValue(key: string, val: string): void {\n console.log(` ${label(key + \":\")} ${value(val)}`);\n}\n\nexport function errorMessage(msg: string): void {\n console.log(`\\n ${fail(\"✗\")} ${msg}\\n`);\n}\n\nexport function successMessage(msg: string): void {\n console.log(`\\n ${success(\"✓\")} ${msg}\\n`);\n}\n","/**\n * caypo balance — Show USDCx balance.\n */\n\nimport { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport ora from \"ora\";\nimport { banner, keyValue, accent } from \"../helpers/format.js\";\nimport { loadAgent } from \"../helpers/load-agent.js\";\n\nexport const balanceCommand = new Command(\"balance\")\n .description(\"Show USDCx checking balance\")\n .action(async () => {\n banner();\n\n const spinner = ora(\"Fetching balance...\").start();\n try {\n const agent = await loadAgent();\n const bal = await agent.checking.balance();\n spinner.stop();\n\n console.log(chalk.gray(\" Checking Account\\n\"));\n keyValue(\"Balance\", accent(`${bal.available} USDCx`));\n keyValue(\"Holdings\", `${bal.holdingCount} UTXO${bal.holdingCount !== 1 ? \"s\" : \"\"}`);\n keyValue(\"Address\", agent.wallet.address);\n keyValue(\"Network\", agent.wallet.network);\n console.log(\"\");\n } catch (err) {\n spinner.fail(`Failed to fetch balance: ${(err as Error).message}`);\n process.exit(1);\n }\n });\n","/**\n * Load CantonAgent from config for CLI commands.\n */\n\nimport { CantonAgent, loadConfig } from \"@caypo/canton-sdk\";\nimport { errorMessage } from \"./format.js\";\n\nexport async function loadAgent(): Promise<CantonAgent> {\n try {\n const config = await loadConfig();\n\n if (!config.partyId) {\n errorMessage(\"No wallet configured. Run 'caypo init' first.\");\n process.exit(1);\n }\n\n return CantonAgent.create({\n ledgerUrl: config.ledgerUrl,\n partyId: config.partyId,\n userId: config.userId,\n network: config.network,\n token: process.env.CANTON_JWT ?? \"\",\n });\n } catch (err) {\n errorMessage(`Failed to load agent: ${(err as Error).message}`);\n process.exit(1);\n }\n}\n","/**\n * caypo send <amount> to <recipient> — Send USDCx.\n */\n\nimport { Command } from \"commander\";\nimport ora from \"ora\";\nimport { banner, keyValue, successMessage, errorMessage, accent } from \"../helpers/format.js\";\nimport { loadAgent } from \"../helpers/load-agent.js\";\n\nexport const sendCommand = new Command(\"send\")\n .description(\"Send USDCx to a recipient\")\n .argument(\"<amount>\", \"Amount of USDCx to send\")\n .argument(\"to\", \"Literal 'to' keyword\")\n .argument(\"<recipient>\", \"Recipient party ID\")\n .option(\"--memo <memo>\", \"Optional memo\")\n .action(async (amount: string, _to: string, recipient: string, opts: { memo?: string }) => {\n banner();\n\n const spinner = ora(`Sending ${amount} USDCx to ${recipient}...`).start();\n try {\n const agent = await loadAgent();\n const result = await agent.checking.send(recipient, amount, { memo: opts.memo });\n spinner.stop();\n\n successMessage(`Sent ${accent(amount + \" USDCx\")} successfully!`);\n keyValue(\"Recipient\", recipient);\n keyValue(\"Update ID\", result.updateId);\n keyValue(\"Offset\", String(result.completionOffset));\n keyValue(\"Command ID\", result.commandId);\n console.log(\"\");\n } catch (err) {\n spinner.fail(\"Transfer failed\");\n errorMessage((err as Error).message);\n process.exit(1);\n }\n });\n","/**\n * caypo pay <url> — Pay for API via MPP 402 flow.\n */\n\nimport { Command } from \"commander\";\nimport ora from \"ora\";\nimport { banner, keyValue, successMessage, errorMessage, accent, dim } from \"../helpers/format.js\";\nimport { loadAgent } from \"../helpers/load-agent.js\";\n\nexport const payCommand = new Command(\"pay\")\n .description(\"Pay for an API call via MPP (402 auto-pay)\")\n .argument(\"<url>\", \"URL to fetch\")\n .option(\"-d, --data <json>\", \"Request body (JSON)\")\n .option(\"-X, --method <method>\", \"HTTP method\", \"GET\")\n .option(\"--max-price <amount>\", \"Maximum price to pay\")\n .action(async (url: string, opts: { data?: string; method: string; maxPrice?: string }) => {\n banner();\n\n const spinner = ora(`Fetching ${url}...`).start();\n try {\n const agent = await loadAgent();\n const result = await agent.mpp.pay(url, {\n method: opts.method,\n body: opts.data,\n maxPrice: opts.maxPrice,\n headers: opts.data ? { \"Content-Type\": \"application/json\" } : undefined,\n });\n\n spinner.stop();\n\n if (result.paid) {\n successMessage(`Paid ${accent(result.receipt!.amount + \" USDCx\")} for API access`);\n keyValue(\"Update ID\", result.receipt!.updateId);\n keyValue(\"Command ID\", result.receipt!.commandId);\n } else {\n console.log(dim(\" No payment required (non-402 response)\"));\n }\n\n keyValue(\"Status\", String(result.response.status));\n\n const body = await result.response.text();\n if (body) {\n console.log(dim(\"\\n Response:\"));\n console.log(dim(\" \" + body.slice(0, 500)));\n if (body.length > 500) console.log(dim(\" ... (truncated)\"));\n }\n console.log(\"\");\n } catch (err) {\n spinner.fail(\"Payment failed\");\n errorMessage((err as Error).message);\n process.exit(1);\n }\n });\n","/**\n * caypo address — Show party ID.\n */\n\nimport { Command } from \"commander\";\nimport { banner, keyValue } from \"../helpers/format.js\";\nimport { loadAgent } from \"../helpers/load-agent.js\";\n\nexport const addressCommand = new Command(\"address\")\n .description(\"Show your Canton party ID (address)\")\n .action(async () => {\n banner();\n\n try {\n const agent = await loadAgent();\n keyValue(\"Party ID\", agent.wallet.address);\n keyValue(\"Network\", agent.wallet.network);\n console.log(\"\");\n } catch (err) {\n console.error((err as Error).message);\n process.exit(1);\n }\n });\n","/**\n * caypo safeguards — View and manage safeguard settings.\n */\n\nimport { Command } from \"commander\";\nimport inquirer from \"inquirer\";\nimport chalk from \"chalk\";\nimport { SafeguardManager } from \"@caypo/canton-sdk\";\nimport { banner, keyValue, successMessage, errorMessage, accent, warn } from \"../helpers/format.js\";\n\nexport const safeguardsCommand = new Command(\"safeguards\")\n .description(\"View and manage safeguard settings\")\n .action(async () => {\n banner();\n\n try {\n const mgr = await SafeguardManager.load();\n const s = mgr.settings();\n\n console.log(chalk.gray(\" Safeguard Settings\\n\"));\n keyValue(\"Per-tx limit\", accent(s.txLimit + \" USDCx\"));\n keyValue(\"Daily limit\", accent(s.dailyLimit + \" USDCx\"));\n keyValue(\"Daily spent\", s.dailySpent + \" USDCx\");\n keyValue(\"Locked\", s.locked ? warn(\"YES\") : \"no\");\n keyValue(\"Last reset\", s.lastResetDate);\n console.log(\"\");\n } catch (err) {\n errorMessage((err as Error).message);\n process.exit(1);\n }\n });\n\nsafeguardsCommand\n .command(\"set-tx-limit\")\n .argument(\"<amount>\", \"Per-transaction limit in USDCx\")\n .description(\"Set per-transaction spending limit\")\n .action(async (amount: string) => {\n try {\n const mgr = await SafeguardManager.load();\n mgr.setTxLimit(amount);\n successMessage(`Per-transaction limit set to ${accent(amount + \" USDCx\")}`);\n } catch (err) {\n errorMessage((err as Error).message);\n process.exit(1);\n }\n });\n\nsafeguardsCommand\n .command(\"set-daily-limit\")\n .argument(\"<amount>\", \"Daily spending limit in USDCx\")\n .description(\"Set daily spending limit\")\n .action(async (amount: string) => {\n try {\n const mgr = await SafeguardManager.load();\n mgr.setDailyLimit(amount);\n successMessage(`Daily limit set to ${accent(amount + \" USDCx\")}`);\n } catch (err) {\n errorMessage((err as Error).message);\n process.exit(1);\n }\n });\n\nsafeguardsCommand\n .command(\"lock\")\n .description(\"Lock wallet — all transactions will be rejected\")\n .action(async () => {\n try {\n const { pin } = await inquirer.prompt([\n {\n type: \"password\",\n name: \"pin\",\n message: \"Set lock PIN:\",\n mask: \"*\",\n },\n ]);\n\n const mgr = await SafeguardManager.load();\n mgr.lock(pin);\n successMessage(\"Wallet locked\");\n } catch (err) {\n errorMessage((err as Error).message);\n process.exit(1);\n }\n });\n\nsafeguardsCommand\n .command(\"unlock\")\n .description(\"Unlock wallet\")\n .action(async () => {\n try {\n const { pin } = await inquirer.prompt([\n {\n type: \"password\",\n name: \"pin\",\n message: \"Enter lock PIN:\",\n mask: \"*\",\n },\n ]);\n\n const mgr = await SafeguardManager.load();\n mgr.unlock(pin);\n successMessage(\"Wallet unlocked\");\n } catch (err) {\n errorMessage((err as Error).message);\n process.exit(1);\n }\n });\n","/**\n * caypo traffic — Show validator traffic balance.\n */\n\nimport { Command } from \"commander\";\nimport ora from \"ora\";\nimport chalk from \"chalk\";\nimport { banner, keyValue, accent, warn } from \"../helpers/format.js\";\nimport { loadAgent } from \"../helpers/load-agent.js\";\n\nexport const trafficCommand = new Command(\"traffic\")\n .description(\"Show validator traffic balance\")\n .action(async () => {\n banner();\n\n const spinner = ora(\"Checking traffic balance...\").start();\n try {\n const agent = await loadAgent();\n const balance = await agent.traffic.trafficBalance();\n const sufficient = balance.remaining > 1000;\n spinner.stop();\n\n console.log(chalk.gray(\" Validator Traffic\\n\"));\n keyValue(\"Total purchased\", accent(String(balance.totalPurchased)));\n keyValue(\"Consumed\", String(balance.consumed));\n keyValue(\"Remaining\", sufficient ? accent(String(balance.remaining)) : warn(String(balance.remaining)));\n keyValue(\"Status\", sufficient ? \"Sufficient\" : warn(\"Low — consider purchasing more\"));\n console.log(\"\");\n } catch (err) {\n spinner.fail(`Failed: ${(err as Error).message}`);\n process.exit(1);\n }\n });\n\ntrafficCommand\n .command(\"purchase\")\n .argument(\"<cc-amount>\", \"Canton Coin amount to burn for traffic\")\n .description(\"Purchase additional traffic by burning CC\")\n .action(async (ccAmount: string) => {\n const spinner = ora(`Purchasing traffic with ${ccAmount} CC...`).start();\n try {\n const agent = await loadAgent();\n await agent.traffic.purchaseTraffic(ccAmount);\n spinner.succeed(\"Traffic purchased\");\n } catch (err) {\n spinner.fail(`Purchase failed: ${(err as Error).message}`);\n process.exit(1);\n }\n });\n","/**\n * caypo mcp install — Install MCP server config for AI tools.\n */\n\nimport { Command } from \"commander\";\nimport { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { homedir, platform } from \"node:os\";\nimport chalk from \"chalk\";\nimport { successMessage, errorMessage, keyValue, dim } from \"../helpers/format.js\";\n\ninterface McpConfig {\n mcpServers: Record<string, { command: string; args: string[]; env?: Record<string, string> }>;\n}\n\nconst MCP_ENTRY = {\n command: \"npx\",\n args: [\"@caypo/canton-mcp\"],\n env: { CANTON_AGENT_CONFIG: join(homedir(), \".caypo\", \"config.json\") },\n};\n\nfunction getConfigPaths(): { name: string; path: string }[] {\n const home = homedir();\n const os = platform();\n\n const paths: { name: string; path: string }[] = [];\n\n if (os === \"darwin\") {\n paths.push(\n { name: \"Claude Desktop\", path: join(home, \"Library\", \"Application Support\", \"Claude\", \"claude_desktop_config.json\") },\n { name: \"Cursor\", path: join(home, \".cursor\", \"mcp.json\") },\n { name: \"Windsurf\", path: join(home, \".windsurf\", \"mcp.json\") },\n );\n } else if (os === \"linux\") {\n paths.push(\n { name: \"Claude Desktop\", path: join(home, \".config\", \"Claude\", \"claude_desktop_config.json\") },\n { name: \"Cursor\", path: join(home, \".cursor\", \"mcp.json\") },\n { name: \"Windsurf\", path: join(home, \".windsurf\", \"mcp.json\") },\n );\n } else {\n // Windows\n const appData = process.env.APPDATA ?? join(home, \"AppData\", \"Roaming\");\n paths.push(\n { name: \"Claude Desktop\", path: join(appData, \"Claude\", \"claude_desktop_config.json\") },\n { name: \"Cursor\", path: join(home, \".cursor\", \"mcp.json\") },\n { name: \"Windsurf\", path: join(home, \".windsurf\", \"mcp.json\") },\n );\n }\n\n return paths;\n}\n\nasync function installToConfig(configPath: string): Promise<boolean> {\n let config: McpConfig;\n\n try {\n const raw = await readFile(configPath, \"utf8\");\n config = JSON.parse(raw) as McpConfig;\n } catch {\n config = { mcpServers: {} };\n }\n\n if (!config.mcpServers) {\n config.mcpServers = {};\n }\n\n config.mcpServers[\"caypo\"] = MCP_ENTRY;\n\n await mkdir(join(configPath, \"..\"), { recursive: true });\n await writeFile(configPath, JSON.stringify(config, null, 2), \"utf8\");\n return true;\n}\n\nexport const mcpCommand = new Command(\"mcp\").description(\"MCP server management\");\n\nmcpCommand\n .command(\"install\")\n .description(\"Install MCP server config for Claude Desktop, Cursor, Windsurf\")\n .action(async () => {\n console.log(chalk.gray(\"\\n Installing CAYPO MCP server configuration...\\n\"));\n\n const configs = getConfigPaths();\n let installed = 0;\n\n for (const { name, path } of configs) {\n try {\n await installToConfig(path);\n console.log(` ${chalk.green(\"✓\")} ${name} ${dim(path)}`);\n installed++;\n } catch (err) {\n console.log(` ${chalk.yellow(\"⚠\")} ${name} — ${dim((err as Error).message)}`);\n }\n }\n\n if (installed > 0) {\n successMessage(`MCP server installed for ${installed} tool${installed > 1 ? \"s\" : \"\"}`);\n console.log(chalk.gray(\" Restart your AI tool to activate the MCP server.\"));\n console.log(chalk.gray(\" The server provides 33 tools and 20 prompts for Canton banking.\\n\"));\n } else {\n errorMessage(\"No AI tool configs found. Install manually.\");\n console.log(chalk.gray(\" Add this to your MCP config:\\n\"));\n console.log(chalk.gray(\" \" + JSON.stringify({ caypo: MCP_ENTRY }, null, 2).replace(/\\n/g, \"\\n \")));\n console.log(\"\");\n }\n });\n"],"mappings":"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","names":["Command","chalk","chalk","Command","chalk","ora","Command","ora","chalk","Command","ora","Command","ora","Command","ora","Command","ora","Command","Command","Command","inquirer","chalk","SafeguardManager","Command","SafeguardManager","chalk","inquirer","Command","ora","chalk","Command","ora","chalk","Command","chalk","Command","chalk","Command"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@caypo/canton-cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "CLI for AI agent banking on Canton Network — send, receive, and pay for APIs with USDCx",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"canton",
|
|
7
|
+
"cli",
|
|
8
|
+
"payments",
|
|
9
|
+
"ai-agents",
|
|
10
|
+
"usdcx",
|
|
11
|
+
"blockchain",
|
|
12
|
+
"machine-payments",
|
|
13
|
+
"wallet",
|
|
14
|
+
"mpp"
|
|
15
|
+
],
|
|
16
|
+
"author": "Cayvox Labs <anil@cayvox.com>",
|
|
17
|
+
"homepage": "https://github.com/anilkaracay/Caypo/tree/main/packages/cli",
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "https://github.com/anilkaracay/Caypo.git",
|
|
21
|
+
"directory": "packages/cli"
|
|
22
|
+
},
|
|
23
|
+
"type": "module",
|
|
24
|
+
"main": "./dist/index.js",
|
|
25
|
+
"types": "./dist/index.d.ts",
|
|
26
|
+
"bin": {
|
|
27
|
+
"caypo": "./dist/index.js",
|
|
28
|
+
"canton-agent": "./dist/index.js",
|
|
29
|
+
"ca": "./dist/index.js"
|
|
30
|
+
},
|
|
31
|
+
"exports": {
|
|
32
|
+
".": {
|
|
33
|
+
"types": "./dist/index.d.ts",
|
|
34
|
+
"import": "./dist/index.js",
|
|
35
|
+
"require": "./dist/index.cjs"
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"commander": "^13.1.0",
|
|
40
|
+
"chalk": "^5.4.0",
|
|
41
|
+
"ora": "^8.2.0",
|
|
42
|
+
"inquirer": "^12.6.0",
|
|
43
|
+
"@caypo/canton-sdk": "0.1.0"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@types/inquirer": "^9.0.7",
|
|
47
|
+
"tsup": "^8.4.0",
|
|
48
|
+
"typescript": "^5.8.0",
|
|
49
|
+
"vitest": "^3.1.0"
|
|
50
|
+
},
|
|
51
|
+
"license": "(Apache-2.0 OR MIT)",
|
|
52
|
+
"scripts": {
|
|
53
|
+
"build": "tsup",
|
|
54
|
+
"test": "vitest run",
|
|
55
|
+
"lint": "eslint src/",
|
|
56
|
+
"typecheck": "tsc --noEmit",
|
|
57
|
+
"clean": "rm -rf dist"
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* caypo address — Show party ID.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { Command } from "commander";
|
|
6
|
+
import { banner, keyValue } from "../helpers/format.js";
|
|
7
|
+
import { loadAgent } from "../helpers/load-agent.js";
|
|
8
|
+
|
|
9
|
+
export const addressCommand = new Command("address")
|
|
10
|
+
.description("Show your Canton party ID (address)")
|
|
11
|
+
.action(async () => {
|
|
12
|
+
banner();
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
const agent = await loadAgent();
|
|
16
|
+
keyValue("Party ID", agent.wallet.address);
|
|
17
|
+
keyValue("Network", agent.wallet.network);
|
|
18
|
+
console.log("");
|
|
19
|
+
} catch (err) {
|
|
20
|
+
console.error((err as Error).message);
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* caypo balance — Show USDCx balance.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { Command } from "commander";
|
|
6
|
+
import chalk from "chalk";
|
|
7
|
+
import ora from "ora";
|
|
8
|
+
import { banner, keyValue, accent } from "../helpers/format.js";
|
|
9
|
+
import { loadAgent } from "../helpers/load-agent.js";
|
|
10
|
+
|
|
11
|
+
export const balanceCommand = new Command("balance")
|
|
12
|
+
.description("Show USDCx checking balance")
|
|
13
|
+
.action(async () => {
|
|
14
|
+
banner();
|
|
15
|
+
|
|
16
|
+
const spinner = ora("Fetching balance...").start();
|
|
17
|
+
try {
|
|
18
|
+
const agent = await loadAgent();
|
|
19
|
+
const bal = await agent.checking.balance();
|
|
20
|
+
spinner.stop();
|
|
21
|
+
|
|
22
|
+
console.log(chalk.gray(" Checking Account\n"));
|
|
23
|
+
keyValue("Balance", accent(`${bal.available} USDCx`));
|
|
24
|
+
keyValue("Holdings", `${bal.holdingCount} UTXO${bal.holdingCount !== 1 ? "s" : ""}`);
|
|
25
|
+
keyValue("Address", agent.wallet.address);
|
|
26
|
+
keyValue("Network", agent.wallet.network);
|
|
27
|
+
console.log("");
|
|
28
|
+
} catch (err) {
|
|
29
|
+
spinner.fail(`Failed to fetch balance: ${(err as Error).message}`);
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* caypo init — Interactive wallet setup.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { Command } from "commander";
|
|
6
|
+
import inquirer from "inquirer";
|
|
7
|
+
import ora from "ora";
|
|
8
|
+
import chalk from "chalk";
|
|
9
|
+
import {
|
|
10
|
+
CantonClient,
|
|
11
|
+
Keystore,
|
|
12
|
+
saveConfig,
|
|
13
|
+
DEFAULT_CONFIG,
|
|
14
|
+
SafeguardManager,
|
|
15
|
+
type AgentConfig,
|
|
16
|
+
} from "@caypo/canton-sdk";
|
|
17
|
+
import { banner, keyValue, successMessage, errorMessage, accent } from "../helpers/format.js";
|
|
18
|
+
|
|
19
|
+
export const initCommand = new Command("init")
|
|
20
|
+
.description("Interactive wallet setup — create keystore, allocate party")
|
|
21
|
+
.action(async () => {
|
|
22
|
+
banner();
|
|
23
|
+
console.log(chalk.gray(" Setting up your Canton agent wallet...\n"));
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
// 1. Prompt for PIN
|
|
27
|
+
const { pin } = await inquirer.prompt([
|
|
28
|
+
{
|
|
29
|
+
type: "password",
|
|
30
|
+
name: "pin",
|
|
31
|
+
message: "Choose a PIN for your wallet:",
|
|
32
|
+
mask: "*",
|
|
33
|
+
validate: (v: string) => v.length >= 4 || "PIN must be at least 4 characters",
|
|
34
|
+
},
|
|
35
|
+
]);
|
|
36
|
+
|
|
37
|
+
const { confirmPin } = await inquirer.prompt([
|
|
38
|
+
{
|
|
39
|
+
type: "password",
|
|
40
|
+
name: "confirmPin",
|
|
41
|
+
message: "Confirm PIN:",
|
|
42
|
+
mask: "*",
|
|
43
|
+
validate: (v: string) => v === pin || "PINs do not match",
|
|
44
|
+
},
|
|
45
|
+
]);
|
|
46
|
+
|
|
47
|
+
// 2. Prompt for ledger URL
|
|
48
|
+
const { ledgerUrl } = await inquirer.prompt([
|
|
49
|
+
{
|
|
50
|
+
type: "input",
|
|
51
|
+
name: "ledgerUrl",
|
|
52
|
+
message: "Canton Ledger URL:",
|
|
53
|
+
default: "http://localhost:7575",
|
|
54
|
+
},
|
|
55
|
+
]);
|
|
56
|
+
|
|
57
|
+
// 3. Prompt for JWT
|
|
58
|
+
const { jwt } = await inquirer.prompt([
|
|
59
|
+
{
|
|
60
|
+
type: "password",
|
|
61
|
+
name: "jwt",
|
|
62
|
+
message: "JWT bearer token:",
|
|
63
|
+
mask: "*",
|
|
64
|
+
validate: (v: string) => v.length > 0 || "JWT is required",
|
|
65
|
+
},
|
|
66
|
+
]);
|
|
67
|
+
|
|
68
|
+
// 4. Prompt for user ID
|
|
69
|
+
const { userId } = await inquirer.prompt([
|
|
70
|
+
{
|
|
71
|
+
type: "input",
|
|
72
|
+
name: "userId",
|
|
73
|
+
message: "Ledger API user ID:",
|
|
74
|
+
default: "ledger-api-user",
|
|
75
|
+
},
|
|
76
|
+
]);
|
|
77
|
+
|
|
78
|
+
// 5. Prompt for display name (party hint)
|
|
79
|
+
const { displayName } = await inquirer.prompt([
|
|
80
|
+
{
|
|
81
|
+
type: "input",
|
|
82
|
+
name: "displayName",
|
|
83
|
+
message: "Agent display name:",
|
|
84
|
+
default: "Agent",
|
|
85
|
+
},
|
|
86
|
+
]);
|
|
87
|
+
|
|
88
|
+
// 6. Allocate party
|
|
89
|
+
const spinner = ora("Allocating party on Canton ledger...").start();
|
|
90
|
+
|
|
91
|
+
const client = new CantonClient({ ledgerUrl, token: jwt, userId });
|
|
92
|
+
|
|
93
|
+
let partyId: string;
|
|
94
|
+
try {
|
|
95
|
+
const party = await client.allocateParty(displayName);
|
|
96
|
+
partyId = party.party;
|
|
97
|
+
spinner.succeed("Party allocated");
|
|
98
|
+
} catch (err) {
|
|
99
|
+
spinner.fail("Failed to allocate party");
|
|
100
|
+
errorMessage((err as Error).message);
|
|
101
|
+
process.exit(1);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// 7. Create keystore
|
|
105
|
+
const keystoreSpinner = ora("Creating encrypted keystore...").start();
|
|
106
|
+
await Keystore.create(pin, { partyId, jwt, userId });
|
|
107
|
+
keystoreSpinner.succeed("Keystore created");
|
|
108
|
+
|
|
109
|
+
// 8. Save config
|
|
110
|
+
const config: AgentConfig = {
|
|
111
|
+
...DEFAULT_CONFIG,
|
|
112
|
+
ledgerUrl,
|
|
113
|
+
partyId,
|
|
114
|
+
userId,
|
|
115
|
+
};
|
|
116
|
+
await saveConfig(config);
|
|
117
|
+
|
|
118
|
+
// 9. Initialize safeguards
|
|
119
|
+
const safeguards = new SafeguardManager();
|
|
120
|
+
safeguards.setTxLimit(DEFAULT_CONFIG.safeguards.txLimit);
|
|
121
|
+
safeguards.setDailyLimit(DEFAULT_CONFIG.safeguards.dailyLimit);
|
|
122
|
+
|
|
123
|
+
// 10. Show success
|
|
124
|
+
console.log("");
|
|
125
|
+
successMessage("Canton agent wallet created successfully!");
|
|
126
|
+
console.log("");
|
|
127
|
+
keyValue("Party ID", accent(partyId));
|
|
128
|
+
keyValue("Ledger URL", ledgerUrl);
|
|
129
|
+
keyValue("Config", "~/.caypo/config.json");
|
|
130
|
+
keyValue("Keystore", "~/.caypo/wallet.key");
|
|
131
|
+
console.log("");
|
|
132
|
+
console.log(chalk.gray(" Next steps:"));
|
|
133
|
+
console.log(chalk.gray(" caypo balance — Check your balance"));
|
|
134
|
+
console.log(chalk.gray(" caypo mcp install — Install MCP server for AI tools"));
|
|
135
|
+
console.log(chalk.gray(" caypo send 1 to <party> — Send USDCx"));
|
|
136
|
+
console.log("");
|
|
137
|
+
} catch (err) {
|
|
138
|
+
errorMessage(`Setup failed: ${(err as Error).message}`);
|
|
139
|
+
process.exit(1);
|
|
140
|
+
}
|
|
141
|
+
});
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* caypo mcp install — Install MCP server config for AI tools.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { Command } from "commander";
|
|
6
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
7
|
+
import { join } from "node:path";
|
|
8
|
+
import { homedir, platform } from "node:os";
|
|
9
|
+
import chalk from "chalk";
|
|
10
|
+
import { successMessage, errorMessage, keyValue, dim } from "../helpers/format.js";
|
|
11
|
+
|
|
12
|
+
interface McpConfig {
|
|
13
|
+
mcpServers: Record<string, { command: string; args: string[]; env?: Record<string, string> }>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const MCP_ENTRY = {
|
|
17
|
+
command: "npx",
|
|
18
|
+
args: ["@caypo/canton-mcp"],
|
|
19
|
+
env: { CANTON_AGENT_CONFIG: join(homedir(), ".caypo", "config.json") },
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
function getConfigPaths(): { name: string; path: string }[] {
|
|
23
|
+
const home = homedir();
|
|
24
|
+
const os = platform();
|
|
25
|
+
|
|
26
|
+
const paths: { name: string; path: string }[] = [];
|
|
27
|
+
|
|
28
|
+
if (os === "darwin") {
|
|
29
|
+
paths.push(
|
|
30
|
+
{ name: "Claude Desktop", path: join(home, "Library", "Application Support", "Claude", "claude_desktop_config.json") },
|
|
31
|
+
{ name: "Cursor", path: join(home, ".cursor", "mcp.json") },
|
|
32
|
+
{ name: "Windsurf", path: join(home, ".windsurf", "mcp.json") },
|
|
33
|
+
);
|
|
34
|
+
} else if (os === "linux") {
|
|
35
|
+
paths.push(
|
|
36
|
+
{ name: "Claude Desktop", path: join(home, ".config", "Claude", "claude_desktop_config.json") },
|
|
37
|
+
{ name: "Cursor", path: join(home, ".cursor", "mcp.json") },
|
|
38
|
+
{ name: "Windsurf", path: join(home, ".windsurf", "mcp.json") },
|
|
39
|
+
);
|
|
40
|
+
} else {
|
|
41
|
+
// Windows
|
|
42
|
+
const appData = process.env.APPDATA ?? join(home, "AppData", "Roaming");
|
|
43
|
+
paths.push(
|
|
44
|
+
{ name: "Claude Desktop", path: join(appData, "Claude", "claude_desktop_config.json") },
|
|
45
|
+
{ name: "Cursor", path: join(home, ".cursor", "mcp.json") },
|
|
46
|
+
{ name: "Windsurf", path: join(home, ".windsurf", "mcp.json") },
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return paths;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async function installToConfig(configPath: string): Promise<boolean> {
|
|
54
|
+
let config: McpConfig;
|
|
55
|
+
|
|
56
|
+
try {
|
|
57
|
+
const raw = await readFile(configPath, "utf8");
|
|
58
|
+
config = JSON.parse(raw) as McpConfig;
|
|
59
|
+
} catch {
|
|
60
|
+
config = { mcpServers: {} };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (!config.mcpServers) {
|
|
64
|
+
config.mcpServers = {};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
config.mcpServers["caypo"] = MCP_ENTRY;
|
|
68
|
+
|
|
69
|
+
await mkdir(join(configPath, ".."), { recursive: true });
|
|
70
|
+
await writeFile(configPath, JSON.stringify(config, null, 2), "utf8");
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export const mcpCommand = new Command("mcp").description("MCP server management");
|
|
75
|
+
|
|
76
|
+
mcpCommand
|
|
77
|
+
.command("install")
|
|
78
|
+
.description("Install MCP server config for Claude Desktop, Cursor, Windsurf")
|
|
79
|
+
.action(async () => {
|
|
80
|
+
console.log(chalk.gray("\n Installing CAYPO MCP server configuration...\n"));
|
|
81
|
+
|
|
82
|
+
const configs = getConfigPaths();
|
|
83
|
+
let installed = 0;
|
|
84
|
+
|
|
85
|
+
for (const { name, path } of configs) {
|
|
86
|
+
try {
|
|
87
|
+
await installToConfig(path);
|
|
88
|
+
console.log(` ${chalk.green("✓")} ${name} ${dim(path)}`);
|
|
89
|
+
installed++;
|
|
90
|
+
} catch (err) {
|
|
91
|
+
console.log(` ${chalk.yellow("⚠")} ${name} — ${dim((err as Error).message)}`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (installed > 0) {
|
|
96
|
+
successMessage(`MCP server installed for ${installed} tool${installed > 1 ? "s" : ""}`);
|
|
97
|
+
console.log(chalk.gray(" Restart your AI tool to activate the MCP server."));
|
|
98
|
+
console.log(chalk.gray(" The server provides 33 tools and 20 prompts for Canton banking.\n"));
|
|
99
|
+
} else {
|
|
100
|
+
errorMessage("No AI tool configs found. Install manually.");
|
|
101
|
+
console.log(chalk.gray(" Add this to your MCP config:\n"));
|
|
102
|
+
console.log(chalk.gray(" " + JSON.stringify({ caypo: MCP_ENTRY }, null, 2).replace(/\n/g, "\n ")));
|
|
103
|
+
console.log("");
|
|
104
|
+
}
|
|
105
|
+
});
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* caypo pay <url> — Pay for API via MPP 402 flow.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { Command } from "commander";
|
|
6
|
+
import ora from "ora";
|
|
7
|
+
import { banner, keyValue, successMessage, errorMessage, accent, dim } from "../helpers/format.js";
|
|
8
|
+
import { loadAgent } from "../helpers/load-agent.js";
|
|
9
|
+
|
|
10
|
+
export const payCommand = new Command("pay")
|
|
11
|
+
.description("Pay for an API call via MPP (402 auto-pay)")
|
|
12
|
+
.argument("<url>", "URL to fetch")
|
|
13
|
+
.option("-d, --data <json>", "Request body (JSON)")
|
|
14
|
+
.option("-X, --method <method>", "HTTP method", "GET")
|
|
15
|
+
.option("--max-price <amount>", "Maximum price to pay")
|
|
16
|
+
.action(async (url: string, opts: { data?: string; method: string; maxPrice?: string }) => {
|
|
17
|
+
banner();
|
|
18
|
+
|
|
19
|
+
const spinner = ora(`Fetching ${url}...`).start();
|
|
20
|
+
try {
|
|
21
|
+
const agent = await loadAgent();
|
|
22
|
+
const result = await agent.mpp.pay(url, {
|
|
23
|
+
method: opts.method,
|
|
24
|
+
body: opts.data,
|
|
25
|
+
maxPrice: opts.maxPrice,
|
|
26
|
+
headers: opts.data ? { "Content-Type": "application/json" } : undefined,
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
spinner.stop();
|
|
30
|
+
|
|
31
|
+
if (result.paid) {
|
|
32
|
+
successMessage(`Paid ${accent(result.receipt!.amount + " USDCx")} for API access`);
|
|
33
|
+
keyValue("Update ID", result.receipt!.updateId);
|
|
34
|
+
keyValue("Command ID", result.receipt!.commandId);
|
|
35
|
+
} else {
|
|
36
|
+
console.log(dim(" No payment required (non-402 response)"));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
keyValue("Status", String(result.response.status));
|
|
40
|
+
|
|
41
|
+
const body = await result.response.text();
|
|
42
|
+
if (body) {
|
|
43
|
+
console.log(dim("\n Response:"));
|
|
44
|
+
console.log(dim(" " + body.slice(0, 500)));
|
|
45
|
+
if (body.length > 500) console.log(dim(" ... (truncated)"));
|
|
46
|
+
}
|
|
47
|
+
console.log("");
|
|
48
|
+
} catch (err) {
|
|
49
|
+
spinner.fail("Payment failed");
|
|
50
|
+
errorMessage((err as Error).message);
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* caypo safeguards — View and manage safeguard settings.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { Command } from "commander";
|
|
6
|
+
import inquirer from "inquirer";
|
|
7
|
+
import chalk from "chalk";
|
|
8
|
+
import { SafeguardManager } from "@caypo/canton-sdk";
|
|
9
|
+
import { banner, keyValue, successMessage, errorMessage, accent, warn } from "../helpers/format.js";
|
|
10
|
+
|
|
11
|
+
export const safeguardsCommand = new Command("safeguards")
|
|
12
|
+
.description("View and manage safeguard settings")
|
|
13
|
+
.action(async () => {
|
|
14
|
+
banner();
|
|
15
|
+
|
|
16
|
+
try {
|
|
17
|
+
const mgr = await SafeguardManager.load();
|
|
18
|
+
const s = mgr.settings();
|
|
19
|
+
|
|
20
|
+
console.log(chalk.gray(" Safeguard Settings\n"));
|
|
21
|
+
keyValue("Per-tx limit", accent(s.txLimit + " USDCx"));
|
|
22
|
+
keyValue("Daily limit", accent(s.dailyLimit + " USDCx"));
|
|
23
|
+
keyValue("Daily spent", s.dailySpent + " USDCx");
|
|
24
|
+
keyValue("Locked", s.locked ? warn("YES") : "no");
|
|
25
|
+
keyValue("Last reset", s.lastResetDate);
|
|
26
|
+
console.log("");
|
|
27
|
+
} catch (err) {
|
|
28
|
+
errorMessage((err as Error).message);
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
safeguardsCommand
|
|
34
|
+
.command("set-tx-limit")
|
|
35
|
+
.argument("<amount>", "Per-transaction limit in USDCx")
|
|
36
|
+
.description("Set per-transaction spending limit")
|
|
37
|
+
.action(async (amount: string) => {
|
|
38
|
+
try {
|
|
39
|
+
const mgr = await SafeguardManager.load();
|
|
40
|
+
mgr.setTxLimit(amount);
|
|
41
|
+
successMessage(`Per-transaction limit set to ${accent(amount + " USDCx")}`);
|
|
42
|
+
} catch (err) {
|
|
43
|
+
errorMessage((err as Error).message);
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
safeguardsCommand
|
|
49
|
+
.command("set-daily-limit")
|
|
50
|
+
.argument("<amount>", "Daily spending limit in USDCx")
|
|
51
|
+
.description("Set daily spending limit")
|
|
52
|
+
.action(async (amount: string) => {
|
|
53
|
+
try {
|
|
54
|
+
const mgr = await SafeguardManager.load();
|
|
55
|
+
mgr.setDailyLimit(amount);
|
|
56
|
+
successMessage(`Daily limit set to ${accent(amount + " USDCx")}`);
|
|
57
|
+
} catch (err) {
|
|
58
|
+
errorMessage((err as Error).message);
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
safeguardsCommand
|
|
64
|
+
.command("lock")
|
|
65
|
+
.description("Lock wallet — all transactions will be rejected")
|
|
66
|
+
.action(async () => {
|
|
67
|
+
try {
|
|
68
|
+
const { pin } = await inquirer.prompt([
|
|
69
|
+
{
|
|
70
|
+
type: "password",
|
|
71
|
+
name: "pin",
|
|
72
|
+
message: "Set lock PIN:",
|
|
73
|
+
mask: "*",
|
|
74
|
+
},
|
|
75
|
+
]);
|
|
76
|
+
|
|
77
|
+
const mgr = await SafeguardManager.load();
|
|
78
|
+
mgr.lock(pin);
|
|
79
|
+
successMessage("Wallet locked");
|
|
80
|
+
} catch (err) {
|
|
81
|
+
errorMessage((err as Error).message);
|
|
82
|
+
process.exit(1);
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
safeguardsCommand
|
|
87
|
+
.command("unlock")
|
|
88
|
+
.description("Unlock wallet")
|
|
89
|
+
.action(async () => {
|
|
90
|
+
try {
|
|
91
|
+
const { pin } = await inquirer.prompt([
|
|
92
|
+
{
|
|
93
|
+
type: "password",
|
|
94
|
+
name: "pin",
|
|
95
|
+
message: "Enter lock PIN:",
|
|
96
|
+
mask: "*",
|
|
97
|
+
},
|
|
98
|
+
]);
|
|
99
|
+
|
|
100
|
+
const mgr = await SafeguardManager.load();
|
|
101
|
+
mgr.unlock(pin);
|
|
102
|
+
successMessage("Wallet unlocked");
|
|
103
|
+
} catch (err) {
|
|
104
|
+
errorMessage((err as Error).message);
|
|
105
|
+
process.exit(1);
|
|
106
|
+
}
|
|
107
|
+
});
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* caypo send <amount> to <recipient> — Send USDCx.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { Command } from "commander";
|
|
6
|
+
import ora from "ora";
|
|
7
|
+
import { banner, keyValue, successMessage, errorMessage, accent } from "../helpers/format.js";
|
|
8
|
+
import { loadAgent } from "../helpers/load-agent.js";
|
|
9
|
+
|
|
10
|
+
export const sendCommand = new Command("send")
|
|
11
|
+
.description("Send USDCx to a recipient")
|
|
12
|
+
.argument("<amount>", "Amount of USDCx to send")
|
|
13
|
+
.argument("to", "Literal 'to' keyword")
|
|
14
|
+
.argument("<recipient>", "Recipient party ID")
|
|
15
|
+
.option("--memo <memo>", "Optional memo")
|
|
16
|
+
.action(async (amount: string, _to: string, recipient: string, opts: { memo?: string }) => {
|
|
17
|
+
banner();
|
|
18
|
+
|
|
19
|
+
const spinner = ora(`Sending ${amount} USDCx to ${recipient}...`).start();
|
|
20
|
+
try {
|
|
21
|
+
const agent = await loadAgent();
|
|
22
|
+
const result = await agent.checking.send(recipient, amount, { memo: opts.memo });
|
|
23
|
+
spinner.stop();
|
|
24
|
+
|
|
25
|
+
successMessage(`Sent ${accent(amount + " USDCx")} successfully!`);
|
|
26
|
+
keyValue("Recipient", recipient);
|
|
27
|
+
keyValue("Update ID", result.updateId);
|
|
28
|
+
keyValue("Offset", String(result.completionOffset));
|
|
29
|
+
keyValue("Command ID", result.commandId);
|
|
30
|
+
console.log("");
|
|
31
|
+
} catch (err) {
|
|
32
|
+
spinner.fail("Transfer failed");
|
|
33
|
+
errorMessage((err as Error).message);
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
36
|
+
});
|