@heyamiko/amiko-cli 0.2.3 → 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 +87 -137
  2. package/dist/index.js +264 -82
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Amiko CLI
2
2
 
3
- Manage agents, wallets, credits, and marketplace on the MPP payment rail. Works for both human users and AI agents running on OpenClaw.
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
 
@@ -19,38 +19,34 @@ amiko update
19
19
  ## Quick Start
20
20
 
21
21
  ```bash
22
- # Connect your account (opens browser)
23
- amiko connect
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
24
26
 
25
- # Browse the marketplace
26
- amiko browse
27
-
28
- # Call an agent (auto-pays via Tempo or API key)
29
- amiko call titan-research "Summarize the latest news on AI agents"
30
-
31
- # Check credits and wallet balances
27
+ # Check your balance
32
28
  amiko credits balance
33
29
 
34
- # Top up credits from agent wallet
35
- amiko credits topup 10000 --token AMIKO
30
+ # Top up credits from your agent's wallet
31
+ amiko credits topup 10000 --token AMIKO --yes
32
+
33
+ # Browse and call marketplace agents
34
+ amiko browse
35
+ amiko call titan-research "Summarize the latest AI news"
36
36
  ```
37
37
 
38
- ## Authentication
38
+ ## Connect
39
39
 
40
- Two ways to connect:
40
+ Three ways to authenticate:
41
41
 
42
42
  ```bash
43
- # Browser flow (for humans)
44
- amiko connect # opens browser, authorize with one click
45
-
46
- # URL flow (for agents on OpenClaw)
47
- amiko connect <url> # paste connect link from Amiko settings
48
-
49
- # Legacy Privy login (marketplace features)
50
- amiko login # sign in via browser
51
- amiko login --no-browser # print login URL instead
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
52
46
  ```
53
47
 
48
+ The connect code is the simplest — generate one from your Amiko settings page, paste it to the agent or terminal.
49
+
54
50
  ```bash
55
51
  amiko whoami # show current auth status
56
52
  amiko logout # clear stored credentials
@@ -58,177 +54,131 @@ amiko logout # clear stored credentials
58
54
 
59
55
  ## Credits
60
56
 
61
- Manage platform credits — check balance and top up from agent wallets.
62
-
63
57
  ```bash
64
- amiko credits balance # show credit balance + wallet balances
65
- amiko credits topup <amount> # top up credits (amount in credits)
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)
66
60
  ```
67
61
 
68
62
  **Top-up options:**
69
63
 
70
64
  ```bash
71
- amiko credits topup 10000 # auto-select best token/wallet
72
- amiko credits topup 10000 --token AMIKO # use AMIKO token (Solana)
73
- amiko credits topup 10000 --token USDC --chain base # use USDC on Base
74
- amiko credits topup 5000 --method mpp # use MPP 402 payment flow
75
- amiko credits topup 10000 --yes # skip confirmation
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
76
71
  ```
77
72
 
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
78
+
78
79
  **Supported tokens:** AMIKO, SOL, USDC, USDT, ETH
79
80
  **Supported chains:** Solana, Base, Ethereum, Tempo
80
81
 
81
- **Payment methods:**
82
- - `crossmint` — transfer from agent Crossmint wallet to treasury (default)
83
- - `mpp` — auto-pay via `@solana/mpp` SDK (402 challenge flow)
84
- - `auto` — try crossmint first, fall back to mpp
85
-
86
- ## Wallet Management
82
+ ## Wallet
87
83
 
88
84
  ```bash
89
85
  amiko wallet create # create MPP wallet on Tempo
90
86
  amiko wallet balance [address] # check USDC.e balance
91
- amiko wallet balance --watch # poll balance every 5s
92
- amiko wallet topup <amount> # top up via Stripe ($1-$100)
93
- amiko wallet topup 25 --open # auto-open Stripe checkout
94
- amiko wallet deposit [address] # show USDC.e deposit instructions
95
- amiko wallet login # log in to Tempo wallet
96
- amiko wallet whoami # show Tempo wallet identity
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
97
92
  amiko wallet fund # fund via Tempo dashboard
98
93
  ```
99
94
 
100
95
  ## Marketplace
101
96
 
102
97
  ```bash
103
- amiko browse # browse marketplace agents
104
- amiko browse -c research # filter by category
105
- amiko browse -s newest -l 10 # sort by newest, limit 10
106
- amiko browse -q "image gen" # search by keyword
107
- amiko info <agent> # show agent details + pricing
98
+ amiko browse # browse agents
99
+ amiko browse -c research -l 10 # filter by category
100
+ amiko info <agent> # agent details + pricing
108
101
  amiko popular # trending agents
109
- amiko popular -p day # trending today
110
- amiko activity # recent payment activity
111
- amiko leaderboard # top creators by revenue
102
+ amiko activity # recent payments
103
+ amiko leaderboard # top creators
112
104
  ```
113
105
 
114
106
  ## Calling Agents
115
107
 
116
108
  ```bash
117
- amiko call titan-research "What is quantum computing?" # single message
118
- echo "Explain transformers" | amiko call titan-research # pipe input
119
- amiko call titan-research -i # interactive REPL
120
- amiko call titan-research "Generate a report" --task # task endpoint
121
- amiko call titan-research "hello" --dry-run # preview cost
122
- amiko call titan-research "hello" --raw # raw JSON output
123
- amiko call titan-research "hello" --no-tempo # force API key
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
124
114
  ```
125
115
 
126
116
  ## Creator Dashboard
127
117
 
128
- Requires authentication (`amiko login`).
129
-
130
118
  ```bash
131
- amiko creator summary # earnings, listings, payout status
132
- amiko creator listings # list your agent listings
133
- amiko creator publish <id> # publish a draft listing
119
+ amiko creator summary # earnings + listings
120
+ amiko creator listings # your agent listings
121
+ amiko creator publish <id> # publish a draft
134
122
  amiko creator payout # request payout
135
123
  ```
136
124
 
137
- ## Reputation
125
+ ## Other Commands
138
126
 
139
127
  ```bash
140
- amiko reputation # your TARS score
141
- amiko rep 0xabc...def # look up any address
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
142
135
  ```
143
136
 
144
- ## Service Discovery
145
-
146
- ```bash
147
- amiko discover # MPP service info + endpoints
148
- amiko ping # health check with latency
149
- ```
137
+ ## Agent Integration (OpenClaw)
150
138
 
151
- ## Cross-Chain Bridge
139
+ Agents install the CLI and connect with a code from the user:
152
140
 
153
141
  ```bash
154
- amiko bridge quote 5 # get bridge quote (USDC, $5)
155
- amiko bridge status <depositId> # check transfer status
156
- amiko bridge routes # list available routes
142
+ npm install -g @heyamiko/amiko-cli
143
+ amiko connect <CODE>
144
+ amiko credits balance
145
+ amiko credits topup 10000 --token AMIKO --yes
157
146
  ```
158
147
 
159
- ## Configuration
148
+ The CLI auto-detects `.amiko.json` (synced by the platform) when running on an agent.
160
149
 
161
- Config stored at `~/.amiko/config.json`. Agent config read from `.amiko.json` when running on OpenClaw.
150
+ ## Configuration
162
151
 
163
- ```bash
164
- amiko config show # display configuration
165
- amiko config set <key> <value> # set a value
166
- amiko config reset # reset to defaults
167
- amiko config path # print config file path
168
- ```
152
+ Config: `~/.amiko/config.json`
169
153
 
170
154
  | Key | Default |
171
155
  |-----|---------|
172
- | `apiBase` | `https://mpp.amikomarkets.com` |
173
156
  | `billingBase` | `https://billing.heyamiko.com` |
174
- | `siteBase` | `https://amikomarkets.com` |
175
- | `platformUrl` | `https://app.heyamiko.com` |
176
-
177
- ## Agent Integration (OpenClaw)
178
-
179
- Agents running on OpenClaw can install and use the CLI to manage credits:
180
-
181
- ```bash
182
- # 1. User generates connect link from Amiko settings
183
- # 2. Agent runs:
184
- amiko connect https://app.heyamiko.com/api/cli/connect/redeem?code=abc123
185
-
186
- # 3. Agent can now top up credits:
187
- amiko credits topup 10000 --token AMIKO --yes
188
-
189
- # Or check balances:
190
- amiko credits balance
191
- ```
192
-
193
- The CLI auto-detects `.amiko.json` (synced by the platform) for twin token auth when running on an agent.
194
-
195
- ## Development
196
-
197
- ```bash
198
- bun install # install dependencies
199
- bun run dev # run without building
200
- bun run build # compile to dist/
201
- bun run typecheck # type-check
202
- bun test # run tests
203
- ```
157
+ | `apiBase` | `https://mpp.amikomarkets.com` |
158
+ | `siteBase` | `https://platform.heyamiko.com` |
204
159
 
205
160
  ## Architecture
206
161
 
207
162
  ```
208
163
  src/
209
- ├── index.ts # entry point — registers all commands
210
- ├── onboarding.ts # first-run setup flow
164
+ ├── index.ts # entry point
165
+ ├── onboarding.ts # first-run setup
211
166
  ├── commands/
212
- │ ├── browse.ts # browse, info
213
- │ ├── call.ts # call (Tempo + API key + interactive)
214
- │ ├── connect.ts # connect (browser + URL modes)
215
- │ ├── credits.ts # credits balance + topup
216
- │ ├── wallet.ts # wallet subcommands
217
- │ ├── creator.ts # creator dashboard
218
- │ ├── reputation.ts # TARS reputation lookup
219
- │ ├── config.ts # config management
220
- │ ├── discovery.ts # discover, ping
221
- │ ├── marketplace.ts # activity, leaderboard, popular
222
- │ ├── login.ts # login, logout, whoami
223
- │ ├── bridge.ts # cross-chain bridge
224
- │ └── update.ts # self-update
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
172
+ │ ├── creator.ts # creator dashboard
173
+ │ ├── login.ts # Privy browser login
174
+ │ ├── update.ts # self-update
175
+ │ └── ...
225
176
  └── lib/
226
- ├── api.ts # HTTP client (MPP + billing)
227
- ├── config.ts # ~/.amiko/config.json I/O
228
- ├── agent-config.ts # .amiko.json reader + auth resolver
229
- ├── mpp-topup.ts # MPP SDK payment helper
230
- ├── format.ts # terminal formatting
231
- └── tempo.ts # Tempo CLI detection
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
+ └── ...
232
182
  ```
233
183
 
234
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).
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,28 +26402,84 @@ 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
26473
  let code;
26402
26474
  let redeemOrigin;
26403
26475
  try {
26404
- const url = new URL(urlStr);
26405
- code = url.searchParams.get("code") || urlStr;
26476
+ const url = new URL(input);
26477
+ code = url.searchParams.get("code") || input;
26406
26478
  redeemOrigin = url.origin;
26407
26479
  } catch {
26408
- code = urlStr.trim();
26480
+ code = input;
26409
26481
  const config2 = loadConfig();
26410
- redeemOrigin = config2.platformUrl || process.env.AMIKO_PLATFORM_URL || "https://app.heyamiko.com";
26482
+ redeemOrigin = config2.billingBase || "https://billing.heyamiko.com";
26411
26483
  }
26412
26484
  if (!code) {
26413
26485
  spinner2.stop();
@@ -26439,7 +26511,7 @@ function registerConnectCommand(program2) {
26439
26511
  }
26440
26512
  const config = loadConfig();
26441
26513
  const state = randomBytes2(16).toString("hex");
26442
- 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";
26443
26515
  console.log(heading("Amiko Connect"));
26444
26516
  console.log(dim(`Connect your CLI to your Amiko account.
26445
26517
  `));
@@ -26508,7 +26580,7 @@ function loadAgentConfig() {
26508
26580
  return null;
26509
26581
  }
26510
26582
  function resolveAuth(cliConfig) {
26511
- if (cliConfig.platformUrl && cliConfig.agentWallets?.length) {
26583
+ if (cliConfig.agentWallets?.length) {
26512
26584
  return {
26513
26585
  mode: "connected",
26514
26586
  platformUrl: cliConfig.platformUrl,
@@ -26516,6 +26588,7 @@ function resolveAuth(cliConfig) {
26516
26588
  twinId: cliConfig.twinId,
26517
26589
  twinToken: cliConfig.twinToken,
26518
26590
  platformKey: cliConfig.apiKey,
26591
+ privyToken: cliConfig.privyToken,
26519
26592
  connectedKeyId: cliConfig.connectedKeyId,
26520
26593
  userId: cliConfig.userId,
26521
26594
  agentWallets: cliConfig.agentWallets
@@ -26526,6 +26599,7 @@ function resolveAuth(cliConfig) {
26526
26599
  mode: "connected",
26527
26600
  billingBase: cliConfig.billingBase,
26528
26601
  platformKey: cliConfig.privyToken,
26602
+ privyToken: cliConfig.privyToken,
26529
26603
  connectedKeyId: cliConfig.connectedKeyId,
26530
26604
  userId: cliConfig.userId
26531
26605
  };
@@ -26621,23 +26695,32 @@ function registerCreditsCommand(program2) {
26621
26695
  spinner.stop();
26622
26696
  console.log(heading("Credits"));
26623
26697
  console.log("");
26624
- if (auth.billingBase && auth.platformKey) {
26698
+ {
26625
26699
  const spinner2 = ora("Fetching credit balance...").start();
26626
26700
  try {
26627
- const res = await fetch(`${auth.billingBase}/api/credits`, {
26628
- headers: { Authorization: `Bearer ${auth.platformKey}` }
26629
- });
26630
- if (!res.ok) {
26631
- spinner2.stop();
26632
- const err = await res.json().catch(() => ({ error: `HTTP ${res.status}` }));
26633
- console.error(error(err.error || `Failed to fetch credit balance: ${res.status}`));
26634
- } else {
26635
- const data = await res.json();
26636
- spinner2.stop();
26637
- console.log(label("Balance", `${data.balance ?? 0} credits`));
26638
- if (data.spent !== undefined) {
26639
- 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 };
26640
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"));
26641
26724
  console.log("");
26642
26725
  }
26643
26726
  } catch (err) {
@@ -26664,10 +26747,44 @@ function registerCreditsCommand(program2) {
26664
26747
  } else {
26665
26748
  console.log(label("Agent Wallets", ""));
26666
26749
  for (const w of wallets) {
26667
- const chain = w.chain ?? "?";
26668
- const token = w.token ?? "";
26669
- const balance = w.balance ?? 0;
26670
- 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
+ }
26671
26788
  }
26672
26789
  }
26673
26790
  console.log("");
@@ -26697,11 +26814,18 @@ function registerCreditsCommand(program2) {
26697
26814
  console.error(error("Not connected"));
26698
26815
  process.exit(1);
26699
26816
  }
26700
- if (!auth.platformUrl || !auth.twinId) {
26817
+ if (!auth.billingBase) {
26701
26818
  spinner.stop();
26702
- console.error(error("Platform URL and twin ID required for top-up. Run: amiko connect <url>"));
26819
+ console.error(error("Billing service not configured. Run: amiko connect"));
26703
26820
  process.exit(1);
26704
26821
  }
26822
+ const authToken = auth.platformKey || auth.privyToken;
26823
+ if (!authToken) {
26824
+ spinner.stop();
26825
+ console.error(error("No API key or auth token. Run: amiko connect"));
26826
+ process.exit(1);
26827
+ }
26828
+ const authHeaders2 = { Authorization: `Bearer ${authToken}`, "Content-Type": "application/json" };
26705
26829
  spinner.stop();
26706
26830
  if (!options2.yes) {
26707
26831
  console.log(warn(`About to top up ${credits2} credits.`));
@@ -26709,74 +26833,132 @@ function registerCreditsCommand(program2) {
26709
26833
  console.log("");
26710
26834
  }
26711
26835
  if (options2.method === "mpp" && auth.userId && auth.connectedKeyId) {
26712
- const spinner22 = ora("Topping up credits via MPP...").start();
26836
+ const spinner2 = ora("Topping up credits via MPP...").start();
26713
26837
  const mppResult = await mppCreditsTopup({
26714
26838
  userId: auth.userId,
26715
26839
  keyId: auth.connectedKeyId,
26716
26840
  amount: credits2 / 1e4
26717
26841
  });
26718
- spinner22.stop();
26842
+ spinner2.stop();
26719
26843
  if (!mppResult.success) {
26720
26844
  console.error(error(mppResult.error || "MPP top-up failed"));
26721
26845
  process.exit(1);
26722
26846
  }
26723
- console.log(heading("Top-up Complete"));
26847
+ console.log(heading("Top-up Complete (MPP)"));
26724
26848
  console.log("");
26725
- if (mppResult.credits?.added !== undefined) {
26726
- console.log(label("Credits Added", `${mppResult.credits.added}`));
26727
- }
26728
- if (mppResult.credits?.balance !== undefined) {
26729
- console.log(label("New Balance", `${mppResult.credits.balance} credits`));
26730
- }
26731
- console.log("");
26732
- 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."));
26733
26852
  return;
26734
26853
  }
26735
- const spinner2 = ora("Topping up credits...").start();
26736
- const body = { credits: credits2 };
26737
- if (options2.token)
26738
- body.token = options2.token;
26739
- if (options2.chain)
26740
- body.chain = options2.chain;
26741
- if (options2.wallet)
26742
- body.walletAddress = options2.wallet;
26743
- if (auth.platformKey && auth.connectedKeyId) {
26744
- body.platformKey = { key: auth.platformKey, keyId: auth.connectedKeyId };
26745
- }
26746
- 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`, {
26747
26905
  method: "POST",
26748
- headers: {
26749
- "Content-Type": "application/json",
26750
- ...auth.twinToken ? { Authorization: `Bearer ${auth.twinToken}` } : {}
26751
- },
26752
- 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
+ })
26753
26913
  });
26754
- spinner2.stop();
26755
- if (!res.ok) {
26756
- const err = await res.json().catch(() => ({ error: `HTTP ${res.status}` }));
26757
- 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}`));
26758
26918
  process.exit(1);
26759
26919
  }
26760
- const data = await res.json();
26761
- console.log(heading("Top-up Complete"));
26762
- console.log("");
26763
- if (data.creditsAdded !== undefined) {
26764
- console.log(label("Credits Added", `${data.creditsAdded}`));
26765
- }
26766
- if (data.tokenAmount !== undefined) {
26767
- 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);
26768
26926
  }
26769
- if (data.usdValue !== undefined) {
26770
- 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);
26771
26933
  }
26772
- if (data.txHash) {
26773
- 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);
26774
26950
  }
26775
- if (data.newBalance !== undefined) {
26776
- 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())));
26777
26959
  }
26778
26960
  console.log("");
26779
- console.log(success("Credits topped up successfully."));
26961
+ console.log(success("Credits topped up."));
26780
26962
  } catch (err) {
26781
26963
  spinner.stop();
26782
26964
  console.error(error(err instanceof Error ? err.message : "Failed to top up credits"));
@@ -26837,7 +27019,7 @@ var didOnboard = await runOnboarding(process.argv);
26837
27019
  if (didOnboard)
26838
27020
  process.exit(0);
26839
27021
  var program2 = new Command;
26840
- program2.name("amiko").description("Amiko CLI — manage agents, wallets, credits, and marketplace on the MPP payment rail").version("0.2.3");
27022
+ program2.name("amiko").description("Amiko CLI — manage agents, wallets, credits, and the MPP marketplace").version("0.3.0");
26841
27023
  registerBrowseCommand(program2);
26842
27024
  registerCallCommand(program2);
26843
27025
  registerWalletCommand(program2);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@heyamiko/amiko-cli",
3
- "version": "0.2.3",
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": {