@heyamiko/amiko-cli 0.2.2 → 0.3.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.
Files changed (3) hide show
  1. package/README.md +105 -140
  2. package/dist/index.js +273 -82
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -1,222 +1,187 @@
1
1
  # Amiko CLI
2
2
 
3
- Command-line interface for the Amiko AI Agent Marketplace. Browse, call, and pay for AI agent services directly from your terminal.
3
+ Manage agents, wallets, credits, and the MPP marketplace from your terminal. Works for both human users and AI agents running on OpenClaw.
4
4
 
5
5
  ## Install
6
6
 
7
7
  ```bash
8
8
  npm install -g @heyamiko/amiko-cli
9
+ # or
10
+ bun install -g @heyamiko/amiko-cli
9
11
  ```
10
12
 
11
- Or run from source:
13
+ Update to latest:
12
14
 
13
15
  ```bash
14
- git clone <repo> && cd amiko-cli
15
- npm install
16
- npm run build
17
- npm link # makes `amiko` available globally
16
+ amiko update
18
17
  ```
19
18
 
20
19
  ## Quick Start
21
20
 
22
21
  ```bash
23
- # Sign in
24
- amiko login
22
+ # Connect your account
23
+ amiko connect <CODE> # paste code from Amiko settings
24
+ amiko connect amk_<key> # or use your API key directly
25
+ amiko connect # or open browser to sign in
25
26
 
26
- # Browse the marketplace
27
- amiko browse
27
+ # Check your balance
28
+ amiko credits balance
28
29
 
29
- # Call an agent (auto-pays via Tempo or API key)
30
- amiko call titan-research "Summarize the latest news on AI agents"
30
+ # Top up credits from your agent's wallet
31
+ amiko credits topup 10000 --token AMIKO --yes
31
32
 
32
- # Check your balance
33
- amiko wallet balance
33
+ # Browse and call marketplace agents
34
+ amiko browse
35
+ amiko call titan-research "Summarize the latest AI news"
34
36
  ```
35
37
 
36
- ## Payment
38
+ ## Connect
37
39
 
38
- The CLI supports two payment methods, checked in this order:
39
-
40
- 1. **Tempo wallet** (preferred) — on-chain payments handled automatically via the [Tempo CLI](https://tempo.xyz)
41
- 2. **API key** — pre-funded account balance, topped up via Stripe
40
+ Three ways to authenticate:
42
41
 
43
42
  ```bash
44
- # Option 1: Tempo
45
- curl -fsSL https://tempo.xyz/install | bash
46
- tempo wallet login
47
-
48
- # Option 2: API key
49
- amiko config set apiKey amk_<your-key>
50
- amiko wallet topup 10
43
+ amiko connect <CODE> # 7-char code from platform settings (recommended)
44
+ amiko connect amk_<key> # API key (shown once at creation)
45
+ amiko connect # browser sign-in flow
51
46
  ```
52
47
 
53
- ## Commands
54
-
55
- ### Marketplace
56
-
57
- | Command | Description |
58
- |---------|-------------|
59
- | `amiko browse` | Browse marketplace agents |
60
- | `amiko info <agent>` | Show agent details, pricing, and stats |
61
- | `amiko popular` | Trending agents by period |
62
- | `amiko activity` | Recent payment activity |
63
- | `amiko leaderboard` | Top creators by revenue/usage |
64
-
65
- **Browse options:**
48
+ The connect code is the simplest — generate one from your Amiko settings page, paste it to the agent or terminal.
66
49
 
67
50
  ```bash
68
- amiko browse -c research # filter by category
69
- amiko browse -s newest -l 10 # sort by newest, limit 10
70
- amiko browse -q "image generation" # search by keyword
51
+ amiko whoami # show current auth status
52
+ amiko logout # clear stored credentials
71
53
  ```
72
54
 
73
- **Popular options:**
55
+ ## Credits
74
56
 
75
57
  ```bash
76
- amiko popular -p day # trending today
77
- amiko popular -p month -l 20 # top 20 this month
58
+ amiko credits balance # credit balance + wallet balances across all chains
59
+ amiko credits topup <amount> # top up credits (amount in credits, e.g. 10000)
78
60
  ```
79
61
 
80
- ### Calling Agents
62
+ **Top-up options:**
81
63
 
82
64
  ```bash
83
- # Single message
84
- amiko call titan-research "What is quantum computing?"
85
-
86
- # Pipe input
87
- echo "Explain transformer architecture" | amiko call titan-research
88
-
89
- # Interactive REPL
90
- amiko call titan-research -i
65
+ amiko credits topup 10000 # auto-select best wallet + token
66
+ amiko credits topup 10000 --token AMIKO # use AMIKO (Solana)
67
+ amiko credits topup 10000 --token USDC --chain base # use USDC on Base
68
+ amiko credits topup 5000 --method mpp # use MPP 402 payment
69
+ amiko credits topup 10000 --wallet Gu2b...QVya # specific wallet address
70
+ amiko credits topup 10000 --yes # skip confirmation
71
+ ```
91
72
 
92
- # Use task endpoint instead of chat
93
- amiko call titan-research "Generate a report" --task
73
+ **How it works:**
74
+ 1. CLI fetches live token price from Jupiter
75
+ 2. Transfers tokens from your Crossmint agent wallet to the treasury via billing API
76
+ 3. Verifies the on-chain transfer and credits your account
77
+ 4. All calls go through `billing.heyamiko.com` — no other dependencies
94
78
 
95
- # Preview cost without executing (Tempo only)
96
- amiko call titan-research "hello" --dry-run
79
+ **Supported tokens:** AMIKO, SOL, USDC, USDT, ETH
80
+ **Supported chains:** Solana, Base, Ethereum, Tempo
97
81
 
98
- # Raw JSON output
99
- amiko call titan-research "hello" --raw
82
+ ## Wallet
100
83
 
101
- # Force API key payment (skip Tempo)
102
- amiko call titan-research "hello" --no-tempo
84
+ ```bash
85
+ amiko wallet create # create MPP wallet on Tempo
86
+ amiko wallet balance [address] # check USDC.e balance
87
+ amiko wallet balance --watch # poll every 5s
88
+ amiko wallet topup <amount> # Stripe checkout ($1-$100)
89
+ amiko wallet deposit [address] # USDC.e deposit instructions
90
+ amiko wallet login # Tempo wallet login
91
+ amiko wallet whoami # Tempo wallet identity
92
+ amiko wallet fund # fund via Tempo dashboard
103
93
  ```
104
94
 
105
- ### Wallet Management
106
-
107
- | Command | Description |
108
- |---------|-------------|
109
- | `amiko wallet create` | Create a new MPP wallet on Tempo |
110
- | `amiko wallet balance [address]` | Check USDC.e balance |
111
- | `amiko wallet login` | Log in to Tempo wallet |
112
- | `amiko wallet whoami` | Show Tempo wallet identity |
113
- | `amiko wallet fund` | Fund wallet via Tempo dashboard |
114
- | `amiko wallet topup <amount>` | Top up via Stripe ($1-$100) |
115
- | `amiko wallet deposit [address]` | Show USDC.e deposit instructions |
95
+ ## Marketplace
116
96
 
117
97
  ```bash
118
- # Watch balance in real time
119
- amiko wallet balance --watch
120
- amiko wallet balance --watch --interval 10
121
-
122
- # Create wallet and save to config
123
- amiko wallet create --save
124
-
125
- # Top up and open Stripe in browser
126
- amiko wallet topup 25 --open
98
+ amiko browse # browse agents
99
+ amiko browse -c research -l 10 # filter by category
100
+ amiko info <agent> # agent details + pricing
101
+ amiko popular # trending agents
102
+ amiko activity # recent payments
103
+ amiko leaderboard # top creators
127
104
  ```
128
105
 
129
- ### Authentication
106
+ ## Calling Agents
130
107
 
131
108
  ```bash
132
- amiko login # Sign in via browser (Privy)
133
- amiko login --no-browser # Print login URL instead
134
- amiko logout # Clear stored credentials
135
- amiko whoami # Show current auth status
109
+ amiko call <agent> "message" # single message
110
+ amiko call <agent> -i # interactive REPL
111
+ echo "input" | amiko call <agent> # pipe input
112
+ amiko call <agent> "msg" --task # task endpoint
113
+ amiko call <agent> "msg" --dry-run # preview cost
136
114
  ```
137
115
 
138
- ### Creator Dashboard
139
-
140
- Requires authentication (`amiko login`).
116
+ ## Creator Dashboard
141
117
 
142
118
  ```bash
143
- amiko creator summary # Earnings, listings, and payout status
144
- amiko creator listings # List all your agent listings
145
- amiko creator publish <id> # Publish a draft listing
146
- amiko creator payout # Request payout of available earnings
119
+ amiko creator summary # earnings + listings
120
+ amiko creator listings # your agent listings
121
+ amiko creator publish <id> # publish a draft
122
+ amiko creator payout # request payout
147
123
  ```
148
124
 
149
- ### Reputation
125
+ ## Other Commands
150
126
 
151
127
  ```bash
152
- amiko reputation # Look up your TARS score
153
- amiko reputation 0xabc...def # Look up any address
154
- amiko rep 0xabc...def # Short alias
128
+ amiko reputation [address] # TARS score
129
+ amiko discover # MPP service info
130
+ amiko ping # health check
131
+ amiko bridge quote 5 # cross-chain bridge quote
132
+ amiko config show # view config
133
+ amiko config set <key> <value> # set config value
134
+ amiko update # update CLI to latest
155
135
  ```
156
136
 
157
- Displays a 1-5 star rating with total jobs, volume, feedback count, confidence, and on-chain verification status.
137
+ ## Agent Integration (OpenClaw)
158
138
 
159
- ### Service Discovery
139
+ Agents install the CLI and connect with a code from the user:
160
140
 
161
141
  ```bash
162
- amiko discover # MPP service info, endpoints, and pricing
163
- amiko status # Alias for discover
164
- amiko ping # Health check with latency
142
+ npm install -g @heyamiko/amiko-cli
143
+ amiko connect <CODE>
144
+ amiko credits balance
145
+ amiko credits topup 10000 --token AMIKO --yes
165
146
  ```
166
147
 
167
- ### Configuration
148
+ The CLI auto-detects `.amiko.json` (synced by the platform) when running on an agent.
168
149
 
169
- Config is stored at `~/.amiko/config.json`.
150
+ ## Configuration
170
151
 
171
- ```bash
172
- amiko config show # Display current configuration
173
- amiko config set <key> <v> # Set a value
174
- amiko config reset # Reset to defaults
175
- amiko config path # Print config file path
176
- ```
177
-
178
- **Default endpoints:**
152
+ Config: `~/.amiko/config.json`
179
153
 
180
154
  | Key | Default |
181
155
  |-----|---------|
182
- | `apiBase` | `https://mpp.heyamiko.com` |
183
156
  | `billingBase` | `https://billing.heyamiko.com` |
184
- | `siteBase` | `https://amikomarkets.com` |
185
-
186
- **Configurable keys:** `apiBase`, `billingBase`, `siteBase`, `wallet`, `apiKey`, `privyToken`, `userId`, `connectedKeyId`, `connectedKeyName`, `connectedKeyPreview`
187
-
188
- ## Development
189
-
190
- ```bash
191
- npm run dev -- browse # Run commands without building
192
- npm run build # Compile to dist/
193
- npm run typecheck # Type-check without emitting
194
- npm test # Run tests
195
- ```
157
+ | `apiBase` | `https://mpp.amikomarkets.com` |
158
+ | `siteBase` | `https://platform.heyamiko.com` |
196
159
 
197
160
  ## Architecture
198
161
 
199
162
  ```
200
163
  src/
201
- ├── index.ts # Entry point — registers all commands
164
+ ├── index.ts # entry point
165
+ ├── onboarding.ts # first-run setup
202
166
  ├── commands/
203
- │ ├── browse.ts # browse, info
204
- │ ├── call.ts # call (Tempo + API key + interactive)
205
- │ ├── wallet.ts # wallet subcommands
167
+ │ ├── connect.ts # connect (code, API key, browser)
168
+ │ ├── credits.ts # credits balance + topup
169
+ │ ├── browse.ts # marketplace browsing
170
+ │ ├── call.ts # call agents
171
+ │ ├── wallet.ts # MPP wallet management
206
172
  │ ├── creator.ts # creator dashboard
207
- │ ├── reputation.ts # TARS reputation lookup
208
- │ ├── config.ts # config management
209
- │ ├── discovery.ts # discover, ping
210
- │ ├── marketplace.ts # activity, leaderboard, popular
211
- │ └── login.ts # login, logout, whoami
173
+ │ ├── login.ts # Privy browser login
174
+ │ ├── update.ts # self-update
175
+ │ └── ...
212
176
  └── lib/
213
- ├── api.ts # HTTP client (MPP + billing services)
214
- ├── config.ts # ~/.amiko/config.json I/O
215
- ├── format.ts # Terminal formatting (tables, colors)
216
- └── tempo.ts # Tempo CLI detection and invocation
177
+ ├── config.ts # ~/.amiko/config.json
178
+ ├── agent-config.ts # .amiko.json reader
179
+ ├── api.ts # HTTP client
180
+ ├── mpp-topup.ts # MPP SDK helper
181
+ └── ...
217
182
  ```
218
183
 
219
- Built with [Commander](https://github.com/tj/commander.js), [chalk](https://github.com/chalk/chalk), and [ora](https://github.com/sindresorhus/ora). Bundled with [tsup](https://github.com/egoist/tsup).
184
+ Built with [Commander](https://github.com/tj/commander.js), [chalk](https://github.com/chalk/chalk), [ora](https://github.com/sindresorhus/ora), and [@solana/mpp](https://github.com/solana-foundation/mpp-sdk). Bundled with [Bun](https://bun.sh).
220
185
 
221
186
  ## License
222
187
 
package/dist/index.js CHANGED
@@ -23311,7 +23311,7 @@ var CONFIG_FILE = join(CONFIG_DIR, "config.json");
23311
23311
  var DEFAULTS = {
23312
23312
  apiBase: "https://mpp.amikomarkets.com",
23313
23313
  billingBase: "https://billing.heyamiko.com",
23314
- siteBase: "https://amikomarkets.com"
23314
+ siteBase: "https://platform.heyamiko.com"
23315
23315
  };
23316
23316
  var LEGACY_SITE_BASES = new Set([
23317
23317
  "https://amiko-markets.vercel.app",
@@ -23418,6 +23418,7 @@ var SKIP_COMMANDS = new Set([
23418
23418
  "config",
23419
23419
  "login",
23420
23420
  "logout",
23421
+ "whoami",
23421
23422
  "connect",
23422
23423
  "credits",
23423
23424
  "update"
@@ -23436,7 +23437,7 @@ async function runOnboarding(argv) {
23436
23437
  if (cmd && SKIP_COMMANDS.has(cmd))
23437
23438
  return false;
23438
23439
  const config = loadConfig();
23439
- if (config.wallet || config.privyToken || config.apiKey)
23440
+ if (config.wallet || config.privyToken || config.apiKey || config.twinToken || config.agentWallets?.length)
23440
23441
  return false;
23441
23442
  console.log("");
23442
23443
  console.log(heading("Welcome to Amiko CLI"));
@@ -25336,11 +25337,14 @@ var VALID_KEYS = [
25336
25337
  "apiBase",
25337
25338
  "billingBase",
25338
25339
  "siteBase",
25340
+ "platformUrl",
25339
25341
  "wallet",
25340
25342
  "apiKey",
25341
25343
  "privyToken",
25342
25344
  "creatorToken",
25343
25345
  "userId",
25346
+ "twinId",
25347
+ "twinToken",
25344
25348
  "connectedKeyId",
25345
25349
  "connectedKeyName",
25346
25350
  "connectedKeyPreview"
@@ -25657,6 +25661,18 @@ function registerLoginCommand(program2) {
25657
25661
  console.log(heading("Amiko Login"));
25658
25662
  console.log(dim(`Authenticate your CLI with your Amiko account.
25659
25663
  `));
25664
+ const isHeadless = !process.stdout.isTTY || !process.env.DISPLAY && process.platform === "linux";
25665
+ if (isHeadless) {
25666
+ console.log(source_default.yellow("⚠ No browser available in this environment."));
25667
+ console.log("");
25668
+ console.log(dim("For agents and headless environments, use connect instead:"));
25669
+ console.log("");
25670
+ console.log(source_default.cyan(" amiko connect <CODE>"));
25671
+ console.log("");
25672
+ console.log(dim("Generate a connect code from your Amiko settings page,"));
25673
+ console.log(dim("then run the command above with the code."));
25674
+ process.exit(0);
25675
+ }
25660
25676
  if (existingToken) {
25661
25677
  console.log(dim("Already signed in."));
25662
25678
  if (config.userId)
@@ -26386,26 +26402,91 @@ function printSuccess(data) {
26386
26402
  console.log(success("Account connected. You can now run: amiko credits topup <amount>"));
26387
26403
  }
26388
26404
  function saveCredentials(data) {
26389
- saveConfig({
26390
- userId: data.userId,
26391
- platformUrl: data.platformUrl,
26392
- billingBase: data.billingBase,
26393
- agentWallets: data.wallets
26394
- });
26405
+ const update = {};
26406
+ if (data.userId)
26407
+ update.userId = data.userId;
26408
+ if (data.platformUrl)
26409
+ update.platformUrl = data.platformUrl;
26410
+ if (data.billingBase)
26411
+ update.billingBase = data.billingBase;
26412
+ if (data.twinId)
26413
+ update.twinId = data.twinId;
26414
+ if (data.twinToken)
26415
+ update.twinToken = data.twinToken;
26416
+ if (data.privyToken)
26417
+ update.privyToken = data.privyToken;
26418
+ if (data.connectedKeyId)
26419
+ update.connectedKeyId = data.connectedKeyId;
26420
+ if (data.connectedKeyPreview)
26421
+ update.connectedKeyPreview = data.connectedKeyPreview;
26422
+ if (data.connectedKeyName)
26423
+ update.connectedKeyName = data.connectedKeyName;
26424
+ if (data.wallets)
26425
+ update.agentWallets = data.wallets;
26426
+ saveConfig(update);
26395
26427
  }
26396
26428
  function registerConnectCommand(program2) {
26397
26429
  program2.command("connect").description("Connect to an Amiko account (opens browser, or pass a connect URL)").argument("[url]", "Connect URL from Amiko settings (optional — opens browser if omitted)").option("--no-browser", "Print the connect URL instead of opening a browser").action(async (urlStr, opts) => {
26398
26430
  if (urlStr) {
26431
+ const input = urlStr.trim();
26432
+ if (input.startsWith("amk_")) {
26433
+ const spinner3 = ora("Connecting with API key...").start();
26434
+ try {
26435
+ const config2 = loadConfig();
26436
+ const billingBase2 = config2.billingBase || "https://billing.heyamiko.com";
26437
+ const res = await fetch(`${billingBase2}/api/auth/bootstrap`, {
26438
+ headers: { Authorization: `Bearer ${input}` }
26439
+ });
26440
+ if (!res.ok) {
26441
+ spinner3.stop();
26442
+ const err = await res.json().catch(() => ({ error: "Invalid API key" }));
26443
+ console.error(error(err.error || `Authentication failed: ${res.status}`));
26444
+ process.exit(1);
26445
+ }
26446
+ const data = await res.json();
26447
+ spinner3.stop();
26448
+ saveConfig({
26449
+ apiKey: input,
26450
+ userId: data.userId,
26451
+ connectedKeyId: data.connectedKey?.id,
26452
+ connectedKeyName: data.connectedKey?.name,
26453
+ connectedKeyPreview: data.connectedKey?.keyPreview
26454
+ });
26455
+ console.log(heading("Connected"));
26456
+ console.log("");
26457
+ console.log(label("User", data.userId));
26458
+ if (data.connectedKey) {
26459
+ console.log(label("Key", `${data.connectedKey.name} (${data.connectedKey.keyPreview})`));
26460
+ }
26461
+ console.log(label("Credits", Number(data.credits?.balance || 0).toLocaleString()));
26462
+ console.log("");
26463
+ console.log(success("Account connected via API key."));
26464
+ } catch (err) {
26465
+ spinner3.stop();
26466
+ console.error(error(err instanceof Error ? err.message : "Failed to connect"));
26467
+ process.exit(1);
26468
+ }
26469
+ return;
26470
+ }
26399
26471
  const spinner2 = ora("Connecting...").start();
26400
26472
  try {
26401
- const url = new URL(urlStr);
26402
- const code = url.searchParams.get("code");
26473
+ let code;
26474
+ let redeemOrigin;
26475
+ try {
26476
+ const url = new URL(input);
26477
+ code = url.searchParams.get("code") || input;
26478
+ redeemOrigin = url.origin;
26479
+ } catch {
26480
+ code = input;
26481
+ const config2 = loadConfig();
26482
+ redeemOrigin = config2.billingBase || "https://billing.heyamiko.com";
26483
+ }
26403
26484
  if (!code) {
26404
26485
  spinner2.stop();
26405
- console.error(error("Invalid connect URL — missing code parameter."));
26486
+ console.error(error("Invalid code or URL."));
26406
26487
  process.exit(1);
26407
26488
  }
26408
- const redeemUrl = `${url.origin}/api/cli/connect/redeem`;
26489
+ const redeemUrl = `${redeemOrigin}/api/cli/connect/redeem`;
26409
26490
  const res = await fetch(redeemUrl, {
26410
26491
  method: "POST",
26411
26492
  headers: { "Content-Type": "application/json" },
@@ -26430,7 +26511,7 @@ function registerConnectCommand(program2) {
26430
26511
  }
26431
26512
  const config = loadConfig();
26432
26513
  const state = randomBytes2(16).toString("hex");
26433
- const platformUrl = config.platformUrl || process.env.AMIKO_PLATFORM_URL || "https://app.heyamiko.com";
26514
+ const platformUrl = config.platformUrl || process.env.AMIKO_PLATFORM_URL || "https://platform.heyamiko.com";
26434
26515
  console.log(heading("Amiko Connect"));
26435
26516
  console.log(dim(`Connect your CLI to your Amiko account.
26436
26517
  `));
@@ -26499,7 +26580,7 @@ function loadAgentConfig() {
26499
26580
  return null;
26500
26581
  }
26501
26582
  function resolveAuth(cliConfig) {
26502
- if (cliConfig.platformUrl && cliConfig.agentWallets?.length) {
26583
+ if (cliConfig.agentWallets?.length) {
26503
26584
  return {
26504
26585
  mode: "connected",
26505
26586
  platformUrl: cliConfig.platformUrl,
@@ -26507,6 +26588,7 @@ function resolveAuth(cliConfig) {
26507
26588
  twinId: cliConfig.twinId,
26508
26589
  twinToken: cliConfig.twinToken,
26509
26590
  platformKey: cliConfig.apiKey,
26591
+ privyToken: cliConfig.privyToken,
26510
26592
  connectedKeyId: cliConfig.connectedKeyId,
26511
26593
  userId: cliConfig.userId,
26512
26594
  agentWallets: cliConfig.agentWallets
@@ -26517,6 +26599,7 @@ function resolveAuth(cliConfig) {
26517
26599
  mode: "connected",
26518
26600
  billingBase: cliConfig.billingBase,
26519
26601
  platformKey: cliConfig.privyToken,
26602
+ privyToken: cliConfig.privyToken,
26520
26603
  connectedKeyId: cliConfig.connectedKeyId,
26521
26604
  userId: cliConfig.userId
26522
26605
  };
@@ -26612,23 +26695,32 @@ function registerCreditsCommand(program2) {
26612
26695
  spinner.stop();
26613
26696
  console.log(heading("Credits"));
26614
26697
  console.log("");
26615
- if (auth.billingBase && auth.platformKey) {
26698
+ {
26616
26699
  const spinner2 = ora("Fetching credit balance...").start();
26617
26700
  try {
26618
- const res = await fetch(`${auth.billingBase}/api/credits`, {
26619
- headers: { Authorization: `Bearer ${auth.platformKey}` }
26620
- });
26621
- if (!res.ok) {
26622
- spinner2.stop();
26623
- const err = await res.json().catch(() => ({ error: `HTTP ${res.status}` }));
26624
- console.error(error(err.error || `Failed to fetch credit balance: ${res.status}`));
26625
- } else {
26626
- const data = await res.json();
26627
- spinner2.stop();
26628
- console.log(label("Balance", `${data.balance ?? 0} credits`));
26629
- if (data.spent !== undefined) {
26630
- console.log(label("Spent", `${data.spent} credits`));
26701
+ let creditsData = null;
26702
+ if (auth.billingBase && auth.platformKey) {
26703
+ const res = await fetch(`${auth.billingBase}/api/credits`, {
26704
+ headers: { Authorization: `Bearer ${auth.platformKey}` }
26705
+ });
26706
+ if (res.ok)
26707
+ creditsData = await res.json();
26708
+ }
26709
+ if (!creditsData && auth.platformUrl && auth.twinId && auth.twinToken) {
26710
+ const res = await fetch(`${auth.platformUrl}/api/agents/${auth.twinId}/credits/balance`, {
26711
+ headers: { Authorization: `Bearer ${auth.twinToken}` }
26712
+ });
26713
+ if (res.ok) {
26714
+ const data = await res.json();
26715
+ creditsData = { balance: data.balance };
26631
26716
  }
26717
+ }
26718
+ spinner2.stop();
26719
+ if (creditsData) {
26720
+ console.log(label("Balance", source_default.bold(Number(creditsData.balance || 0).toLocaleString())));
26721
+ console.log("");
26722
+ } else {
26723
+ console.log(dim(" Could not fetch credit balance"));
26632
26724
  console.log("");
26633
26725
  }
26634
26726
  } catch (err) {
@@ -26655,10 +26747,44 @@ function registerCreditsCommand(program2) {
26655
26747
  } else {
26656
26748
  console.log(label("Agent Wallets", ""));
26657
26749
  for (const w of wallets) {
26658
- const chain = w.chain ?? "?";
26659
- const token = w.token ?? "";
26660
- const balance = w.balance ?? 0;
26661
- console.log(dim(` ${chain}${token ? ` (${token})` : ""}: ${balance}`));
26750
+ const rawChain = w.chain ?? "?";
26751
+ const isEvm = ["base", "ethereum", "tempo"].includes(rawChain);
26752
+ const chainLabel = isEvm ? "EVM (Ethereum · Base · Tempo)" : rawChain.charAt(0).toUpperCase() + rawChain.slice(1);
26753
+ const addr = w.wallet_address || "";
26754
+ const shortAddr = addr ? `${addr.slice(0, 6)}...${addr.slice(-4)}` : "";
26755
+ console.log(` ${source_default.bold(chainLabel)} ${dim(shortAddr)}`);
26756
+ let bals = w.balance && typeof w.balance === "object" ? w.balance : null;
26757
+ if (!bals) {
26758
+ try {
26759
+ const balRes = await fetch(`${auth.platformUrl}/api/agents/${auth.twinId}/wallets/${addr}/balance`, { headers: { Authorization: `Bearer ${auth.twinToken}` } });
26760
+ if (balRes.ok) {
26761
+ const balData = await balRes.json();
26762
+ if (balData.chains) {
26763
+ for (const [chainName, chainBals] of Object.entries(balData.chains)) {
26764
+ const chainLabel2 = chainName.charAt(0).toUpperCase() + chainName.slice(1);
26765
+ console.log(dim(` ${chainLabel2}:`));
26766
+ for (const [token, tData] of Object.entries(chainBals)) {
26767
+ const human = parseFloat(tData.amount) / Math.pow(10, tData.decimals);
26768
+ console.log(dim(` ${token.padEnd(8)} ${human.toFixed(6)}`));
26769
+ }
26770
+ }
26771
+ continue;
26772
+ }
26773
+ bals = balData.balances || {};
26774
+ }
26775
+ } catch {
26776
+ console.log(dim(" (balance unavailable)"));
26777
+ continue;
26778
+ }
26779
+ }
26780
+ if (bals) {
26781
+ for (const [token, data2] of Object.entries(bals)) {
26782
+ const human = parseFloat(data2.amount) / Math.pow(10, data2.decimals);
26783
+ if (human > 0 || ["sol", "eth", "usdc", "amiko"].includes(token.toLowerCase())) {
26784
+ console.log(dim(` ${token.padEnd(10)} ${human.toFixed(6)}`));
26785
+ }
26786
+ }
26787
+ }
26662
26788
  }
26663
26789
  }
26664
26790
  console.log("");
@@ -26688,11 +26814,18 @@ function registerCreditsCommand(program2) {
26688
26814
  console.error(error("Not connected"));
26689
26815
  process.exit(1);
26690
26816
  }
26691
- if (!auth.platformUrl || !auth.twinId) {
26817
+ if (!auth.billingBase) {
26818
+ spinner.stop();
26819
+ console.error(error("Billing service not configured. Run: amiko connect"));
26820
+ process.exit(1);
26821
+ }
26822
+ const authToken = auth.platformKey || auth.privyToken;
26823
+ if (!authToken) {
26692
26824
  spinner.stop();
26693
- console.error(error("Platform URL and twin ID required for top-up. Run: amiko connect <url>"));
26825
+ console.error(error("No API key or auth token. Run: amiko connect"));
26694
26826
  process.exit(1);
26695
26827
  }
26828
+ const authHeaders2 = { Authorization: `Bearer ${authToken}`, "Content-Type": "application/json" };
26696
26829
  spinner.stop();
26697
26830
  if (!options2.yes) {
26698
26831
  console.log(warn(`About to top up ${credits2} credits.`));
@@ -26700,74 +26833,132 @@ function registerCreditsCommand(program2) {
26700
26833
  console.log("");
26701
26834
  }
26702
26835
  if (options2.method === "mpp" && auth.userId && auth.connectedKeyId) {
26703
- const spinner22 = ora("Topping up credits via MPP...").start();
26836
+ const spinner2 = ora("Topping up credits via MPP...").start();
26704
26837
  const mppResult = await mppCreditsTopup({
26705
26838
  userId: auth.userId,
26706
26839
  keyId: auth.connectedKeyId,
26707
26840
  amount: credits2 / 1e4
26708
26841
  });
26709
- spinner22.stop();
26842
+ spinner2.stop();
26710
26843
  if (!mppResult.success) {
26711
26844
  console.error(error(mppResult.error || "MPP top-up failed"));
26712
26845
  process.exit(1);
26713
26846
  }
26714
- console.log(heading("Top-up Complete"));
26847
+ console.log(heading("Top-up Complete (MPP)"));
26715
26848
  console.log("");
26716
- if (mppResult.credits?.added !== undefined) {
26717
- console.log(label("Credits Added", `${mppResult.credits.added}`));
26718
- }
26719
- if (mppResult.credits?.balance !== undefined) {
26720
- console.log(label("New Balance", `${mppResult.credits.balance} credits`));
26721
- }
26722
- console.log("");
26723
- console.log(success("Credits topped up successfully via MPP."));
26849
+ if (mppResult.credits?.balance)
26850
+ console.log(label("Balance", mppResult.credits.balance));
26851
+ console.log(success("Credits topped up via MPP."));
26724
26852
  return;
26725
26853
  }
26726
- const spinner2 = ora("Topping up credits...").start();
26727
- const body = { credits: credits2 };
26728
- if (options2.token)
26729
- body.token = options2.token;
26730
- if (options2.chain)
26731
- body.chain = options2.chain;
26732
- if (options2.wallet)
26733
- body.walletAddress = options2.wallet;
26734
- if (auth.platformKey && auth.connectedKeyId) {
26735
- body.platformKey = { key: auth.platformKey, keyId: auth.connectedKeyId };
26736
- }
26737
- const res = await fetch(`${auth.platformUrl}/api/agents/${auth.twinId}/credits/topup`, {
26854
+ const agentWallet = auth.agentWallets?.find((w) => w.source === "agent");
26855
+ const walletAddr = options2.wallet || agentWallet?.address || auth.agentWallets?.[0]?.address;
26856
+ const walletChain = options2.chain || auth.agentWallets?.find((w) => w.address === walletAddr)?.chain || "solana";
26857
+ const token = options2.token || "AMIKO";
26858
+ if (!walletAddr) {
26859
+ console.error(error("No wallet found. Specify --wallet <address>"));
26860
+ process.exit(1);
26861
+ }
26862
+ const usdAmount = credits2 / 1e4;
26863
+ let tokenAmount;
26864
+ const stablecoins = ["usdc", "usdt", "usdc.e", "usdt0"];
26865
+ if (stablecoins.includes(token.toLowerCase())) {
26866
+ tokenAmount = usdAmount;
26867
+ } else {
26868
+ const spinner2 = ora(`Fetching ${token} price...`).start();
26869
+ try {
26870
+ const MINTS = {
26871
+ AMIKO: "AY1Ww6MxwC3cCiyrandHqLrp4FXvzgcwQTZpJ2FEpump",
26872
+ SOL: "So11111111111111111111111111111111111111112"
26873
+ };
26874
+ const USDC_MINT = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";
26875
+ const mint = MINTS[token.toUpperCase()];
26876
+ if (!mint) {
26877
+ spinner2.stop();
26878
+ console.error(error(`Unknown token: ${token}`));
26879
+ process.exit(1);
26880
+ }
26881
+ const decimals = token.toUpperCase() === "SOL" ? 9 : 6;
26882
+ const oneToken = Math.pow(10, decimals).toString();
26883
+ const jupRes = await fetch(`https://lite-api.jup.ag/swap/v1/quote?inputMint=${mint}&outputMint=${USDC_MINT}&amount=${oneToken}&slippageBps=100`);
26884
+ if (!jupRes.ok) {
26885
+ spinner2.stop();
26886
+ console.error(error("Failed to fetch token price"));
26887
+ process.exit(1);
26888
+ }
26889
+ const jupData = await jupRes.json();
26890
+ const pricePerToken = parseFloat(jupData.outAmount) / 1e6;
26891
+ tokenAmount = usdAmount / pricePerToken;
26892
+ spinner2.stop();
26893
+ console.log(label("Price", `1 ${token} = $${pricePerToken.toFixed(4)}`));
26894
+ console.log(label("Cost", `${tokenAmount.toFixed(6)} ${token} (~$${usdAmount.toFixed(2)})`));
26895
+ console.log("");
26896
+ } catch {
26897
+ spinner2.stop();
26898
+ console.error(error("Failed to fetch token price"));
26899
+ process.exit(1);
26900
+ }
26901
+ }
26902
+ const TREASURY = "FPiZAf3jEnNfCwfvbv46jdPfHbmSepD4Q6iHiLJDQXvb";
26903
+ const spinner3 = ora("Transferring tokens to treasury...").start();
26904
+ const transferRes = await fetch(`${auth.billingBase}/api/crossmint/wallets/${encodeURIComponent(walletAddr)}/transfer`, {
26738
26905
  method: "POST",
26739
- headers: {
26740
- "Content-Type": "application/json",
26741
- ...auth.twinToken ? { Authorization: `Bearer ${auth.twinToken}` } : {}
26742
- },
26743
- body: JSON.stringify(body)
26906
+ headers: authHeaders2,
26907
+ body: JSON.stringify({
26908
+ to: TREASURY,
26909
+ amount: tokenAmount.toFixed(6),
26910
+ token,
26911
+ chain: walletChain
26912
+ })
26744
26913
  });
26745
- spinner2.stop();
26746
- if (!res.ok) {
26747
- const err = await res.json().catch(() => ({ error: `HTTP ${res.status}` }));
26748
- console.error(error(err.error || `Top-up failed: ${res.status}`));
26914
+ if (!transferRes.ok) {
26915
+ spinner3.stop();
26916
+ const err = await transferRes.json().catch(() => ({ error: "Transfer failed" }));
26917
+ console.error(error(err.error || `Transfer failed: ${transferRes.status}`));
26749
26918
  process.exit(1);
26750
26919
  }
26751
- const data = await res.json();
26752
- console.log(heading("Top-up Complete"));
26753
- console.log("");
26754
- if (data.creditsAdded !== undefined) {
26755
- console.log(label("Credits Added", `${data.creditsAdded}`));
26756
- }
26757
- if (data.tokenAmount !== undefined) {
26758
- console.log(label("Token Amount", `${data.tokenAmount}${data.token ? ` ${data.token}` : ""}`));
26920
+ const transferData = await transferRes.json();
26921
+ const txHash = transferData.txHash;
26922
+ spinner3.stop();
26923
+ if (!txHash) {
26924
+ console.error(error("Transfer sent but no transaction hash returned."));
26925
+ process.exit(1);
26759
26926
  }
26760
- if (data.usdValue !== undefined) {
26761
- console.log(label("USD Value", usd(data.usdValue)));
26927
+ console.log(label("Tx Hash", dim(txHash.slice(0, 16) + "...")));
26928
+ const keyId = auth.connectedKeyId;
26929
+ if (!keyId) {
26930
+ console.log(warn("No connected API key — transfer sent but credits not verified."));
26931
+ console.log(dim(`Tx: ${txHash}`));
26932
+ process.exit(0);
26762
26933
  }
26763
- if (data.txHash) {
26764
- console.log(label("Tx Hash", dim(data.txHash)));
26934
+ const spinner4 = ora("Verifying and crediting...").start();
26935
+ const verifyRes = await fetch(`${auth.billingBase}/api/user/keys/${keyId}/topup/wallet`, {
26936
+ method: "POST",
26937
+ headers: authHeaders2,
26938
+ body: JSON.stringify({
26939
+ txSignature: txHash,
26940
+ amount: tokenAmount,
26941
+ token: token.toUpperCase()
26942
+ })
26943
+ });
26944
+ spinner4.stop();
26945
+ if (!verifyRes.ok) {
26946
+ const err = await verifyRes.json().catch(() => ({ error: "Verify failed" }));
26947
+ console.error(error(err.error || "Transfer sent but credit verification failed."));
26948
+ console.log(dim(`Tx: ${txHash}`));
26949
+ process.exit(1);
26765
26950
  }
26766
- if (data.newBalance !== undefined) {
26767
- console.log(label("New Balance", `${data.newBalance} credits`));
26951
+ const verifyData = await verifyRes.json();
26952
+ console.log(heading("Top-up Complete"));
26953
+ console.log("");
26954
+ console.log(label("Credits", source_default.bold(credits2.toLocaleString())));
26955
+ console.log(label("Token", `${tokenAmount.toFixed(6)} ${token}`));
26956
+ console.log(label("USD", `$${usdAmount.toFixed(2)}`));
26957
+ if (verifyData.credits?.balance) {
26958
+ console.log(label("Balance", source_default.bold(Number(verifyData.credits.balance).toLocaleString())));
26768
26959
  }
26769
26960
  console.log("");
26770
- console.log(success("Credits topped up successfully."));
26961
+ console.log(success("Credits topped up."));
26771
26962
  } catch (err) {
26772
26963
  spinner.stop();
26773
26964
  console.error(error(err instanceof Error ? err.message : "Failed to top up credits"));
@@ -26828,7 +27019,7 @@ var didOnboard = await runOnboarding(process.argv);
26828
27019
  if (didOnboard)
26829
27020
  process.exit(0);
26830
27021
  var program2 = new Command;
26831
- program2.name("amiko").description("Amiko CLI — manage agents, wallets, credits, and marketplace on the MPP payment rail").version("0.2.2");
27022
+ program2.name("amiko").description("Amiko CLI — manage agents, wallets, credits, and the MPP marketplace").version("0.3.0");
26832
27023
  registerBrowseCommand(program2);
26833
27024
  registerCallCommand(program2);
26834
27025
  registerWalletCommand(program2);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@heyamiko/amiko-cli",
3
- "version": "0.2.2",
3
+ "version": "0.3.0",
4
4
  "description": "Amiko CLI — manage agents, wallets, credits, and marketplace on the MPP payment rail",
5
5
  "type": "module",
6
6
  "bin": {