@heyamiko/amiko-cli 0.6.5 → 0.6.7

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 +52 -6
  2. package/dist/index.js +208 -249
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Amiko CLI
1
+ # amiko-cli (v0.6.6)
2
2
 
3
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
 
@@ -147,6 +147,27 @@ amiko bridge routes # available bridge routes
147
147
  amiko bridge limits # min/max limits per route
148
148
  ```
149
149
 
150
+ ## Audio & Voice
151
+
152
+ All audio endpoints return binary audio streams. Use `amiko service call` for direct access:
153
+
154
+ ```bash
155
+ # Sound effects ($0.05)
156
+ amiko service call POST /v1/sfx '{"text":"Thunder with heavy rain","duration_seconds":5}'
157
+
158
+ # Music generation ($0.10)
159
+ amiko service call POST /v1/music '{"prompt":"Lo-fi chill beat","music_length_ms":30000}'
160
+
161
+ # Composition plan ($0.02) — get JSON plan before generating
162
+ amiko service call POST /v1/music/plan '{"prompt":"Epic orchestral piece"}'
163
+
164
+ # Text-to-speech ($0.50/1K chars)
165
+ amiko service call POST /v1/tts/21m00Tcm4TlvDq8ikWAM '{"text":"Hello world"}'
166
+
167
+ # List all available services + prices
168
+ amiko service list
169
+ ```
170
+
150
171
  ## Other Commands
151
172
 
152
173
  ```bash
@@ -158,24 +179,49 @@ amiko config set <key> <value> # set config value
158
179
  amiko update # update CLI to latest
159
180
  ```
160
181
 
182
+ ## X Search
183
+
184
+ ```bash
185
+ amiko search "AI agents" # search X (pays 1 AMIKO from Solana wallet)
186
+ amiko search "bitcoin" --token USDC # pay with USDC instead
187
+ amiko search "news" --raw # raw JSON output
188
+ ```
189
+
190
+ Pays via Crossmint server-signed transfer from the agent's wallet. Tries Solana wallet first, falls back to EVM (Base/Tempo).
191
+
192
+ ## Image Generation
193
+
194
+ ```bash
195
+ amiko image "a sunset over mountains" # gpt-image-1 (default, base64)
196
+ amiko image "logo" --background transparent # transparent background
197
+ amiko image "cat" --model dall-e-3 # DALL-E 3 (returns URL)
198
+ amiko image "portrait" --size 1024x1792 --quality high # portrait, high quality
199
+ amiko image "icon" --format webp # webp output
200
+ amiko image "banner" --raw # raw JSON (for piping)
201
+ ```
202
+
203
+ Costs 5 AMIKO per image. Default model is `gpt-image-1` which returns base64 data (permanent, no expiring URLs).
204
+
161
205
  ## Agent Integration (OpenClaw)
162
206
 
163
- Agents install the CLI and connect with a code from the user:
207
+ Agents auto-authenticate via `.amiko.json` — no manual connect needed. The platform writes this file during twin init with wallets, billing config, and twin token.
164
208
 
165
209
  ```bash
166
- npm install -g @heyamiko/amiko-cli
167
- amiko connect <CODE>
210
+ # Agent environment — just works
168
211
  amiko credits balance
212
+ amiko search "trending topics"
169
213
  amiko credits topup 10000 --token AMIKO --yes
170
214
  ```
171
215
 
172
- The CLI auto-detects `.amiko.json` (synced by the platform) when running on an agent. It checks:
216
+ The CLI checks for `.amiko.json` in order:
173
217
  1. `$OPENCLAW_WORKSPACE_DIR/.amiko.json`
174
218
  2. `$OPENCLAW_HOME/.openclaw/workspace/.amiko.json`
175
219
  3. `/data/.openclaw/workspace/.amiko.json`
176
220
  4. Walking up from `cwd`
177
221
 
178
- When `.amiko.json` is found, the CLI automatically authenticates using the twin token -- no manual connect needed.
222
+ The `.amiko.json` provides: `AMIKO_TWIN_TOKEN` (auth), `agentWallets` (Solana + Base addresses), `billingBase`, and optionally `platformKey`.
223
+
224
+ **Important:** If `~/.amiko/config.json` exists with a stale `apiKey`, it takes priority over `.amiko.json` twin token. Remove stale keys or let the platform manage auth.
179
225
 
180
226
  ## Configuration
181
227
 
package/dist/index.js CHANGED
@@ -73193,73 +73193,7 @@ function registerBrowseCommand(program2) {
73193
73193
 
73194
73194
  // src/commands/call.ts
73195
73195
  import { createInterface as createInterface2 } from "node:readline";
73196
-
73197
- // src/lib/tempo.ts
73198
- import { execFile, execFileSync as execFileSync2 } from "node:child_process";
73199
- import { existsSync as existsSync3 } from "node:fs";
73200
- import { homedir as homedir2 } from "node:os";
73201
- import { join as join3 } from "node:path";
73202
- var TEMPO_PATHS = [join3(homedir2(), ".tempo", "bin", "tempo"), "tempo"];
73203
- function findTempo() {
73204
- for (const p of TEMPO_PATHS) {
73205
- if (p === "tempo") {
73206
- try {
73207
- execFileSync2(p, ["--version"], { stdio: "pipe", timeout: 3000 });
73208
- return p;
73209
- } catch {
73210
- continue;
73211
- }
73212
- } else if (existsSync3(p)) {
73213
- return p;
73214
- }
73215
- }
73216
- return null;
73217
- }
73218
- function hasTempo() {
73219
- return findTempo() !== null;
73220
- }
73221
- function runTempo(args, timeoutMs = 60000) {
73222
- const bin = findTempo();
73223
- if (!bin)
73224
- throw new Error("Tempo CLI not installed. Install: curl -fsSL https://tempo.xyz/install | bash");
73225
- return new Promise((resolve2, reject) => {
73226
- execFile(bin, args, { timeout: timeoutMs, maxBuffer: 1024 * 1024 * 10 }, (err, stdout, stderr) => {
73227
- if (err) {
73228
- const msg = stderr?.trim() || stdout?.trim() || err.message;
73229
- reject(new Error(msg));
73230
- return;
73231
- }
73232
- resolve2(stdout.trim() || stderr.trim());
73233
- });
73234
- });
73235
- }
73236
- async function tempoRequest(opts) {
73237
- const args = ["request", "-t"];
73238
- if (opts.dryRun)
73239
- args.push("--dry-run");
73240
- args.push("-X", opts.method.toUpperCase());
73241
- if (opts.body) {
73242
- args.push("--json", opts.body);
73243
- }
73244
- args.push(opts.url);
73245
- const output = await runTempo(args, opts.timeoutMs ?? 120000);
73246
- return { status: 200, body: output };
73247
- }
73248
- async function tempoWhoami() {
73249
- return runTempo(["wallet", "-t", "whoami"], 1e4);
73250
- }
73251
- async function tempoFund() {
73252
- return runTempo(["wallet", "fund"], 30000);
73253
- }
73254
-
73255
- // src/commands/call.ts
73256
- function buildHeaders() {
73257
- const config = loadConfig();
73258
- const headers = { "Content-Type": "application/json" };
73259
- if (config.apiKey)
73260
- headers["Authorization"] = `Bearer ${config.apiKey}`;
73261
- return headers;
73262
- }
73196
+ var TREASURY = "FPiZAf3jEnNfCwfvbv46jdPfHbmSepD4Q6iHiLJDQXvb";
73263
73197
  function extractResponse(data) {
73264
73198
  const text = data.response ?? data.result ?? data.content ?? data.message ?? data.text ?? data.output;
73265
73199
  if (typeof text === "string")
@@ -73268,26 +73202,13 @@ function extractResponse(data) {
73268
73202
  return JSON.stringify(text, null, 2);
73269
73203
  return JSON.stringify(data, null, 2);
73270
73204
  }
73271
- function printPaymentFooter(res, data, opts) {
73205
+ function printPaymentFooter(txHash, amount, token, opts) {
73272
73206
  if (opts.raw)
73273
73207
  return;
73274
- const paymentMethod = res.headers.get("x-payment-method");
73275
- const paymentBalance = res.headers.get("x-payment-balance");
73276
- const paymentTx = res.headers.get("x-payment-tx");
73277
- const paid = data.paid !== false;
73278
- if (!paid)
73279
- return;
73280
- const cost = typeof data.cost === "number" ? data.cost : null;
73281
- let footer = success("Paid");
73282
- if (cost != null)
73283
- footer += ` ${usd(cost)}`;
73284
- if (paymentMethod)
73285
- footer += dim(` via ${paymentMethod}`);
73286
- if (paymentBalance)
73287
- footer += dim(` (balance: $${paymentBalance})`);
73208
+ let footer = success(`Paid ${amount} ${token}`);
73209
+ if (txHash)
73210
+ footer += dim(` tx: ${txHash.slice(0, 16)}...`);
73288
73211
  console.log(footer);
73289
- if (paymentTx)
73290
- console.log(dim(`tx: ${paymentTx}`));
73291
73212
  }
73292
73213
  async function readStdin() {
73293
73214
  if (process.stdin.isTTY)
@@ -73299,113 +73220,140 @@ async function readStdin() {
73299
73220
  const text = Buffer.concat(chunks).toString("utf-8").trim();
73300
73221
  return text || null;
73301
73222
  }
73302
- function getPaymentMode() {
73303
- const config = loadConfig();
73304
- if (hasTempo())
73305
- return "tempo";
73306
- if (config.apiKey)
73307
- return "apikey";
73308
- return "none";
73309
- }
73310
- async function callViaTempo(agentId, message, opts) {
73311
- const config = loadConfig();
73312
- const base = config.apiBase.replace(/\/$/, "");
73313
- const endpoint = opts.task ? "tasks" : "chat";
73314
- const url = `${base}/v1/agents/${agentId}/${endpoint}`;
73315
- if (opts.dryRun) {
73316
- const spinner2 = ora(`Dry run: ${agentId}...`).start();
73317
- try {
73318
- const result = await tempoRequest({
73319
- method: "POST",
73320
- url,
73321
- body: JSON.stringify({ message }),
73322
- dryRun: true
73323
- });
73324
- spinner2.stop();
73325
- console.log(result.body);
73326
- } catch (err) {
73327
- spinner2.stop();
73328
- console.error(error(err instanceof Error ? err.message : "Dry run failed"));
73329
- process.exit(1);
73330
- }
73331
- return;
73223
+ async function getAmikoPrice() {
73224
+ try {
73225
+ const res = await fetch("https://api.jup.ag/price/v2?ids=AY1Ww6MxwC3cCiyrandHqLrp4FXvzgcwQTZpJ2FEpump");
73226
+ const data = await res.json();
73227
+ return parseFloat(data.data?.["AY1Ww6MxwC3cCiyrandHqLrp4FXvzgcwQTZpJ2FEpump"]?.price || "0");
73228
+ } catch {
73229
+ return 0;
73332
73230
  }
73333
- const spinner = ora(`Calling ${agentId} via Tempo...`).start();
73231
+ }
73232
+ function usdToAmiko(usdAmount, amikoPrice) {
73233
+ if (amikoPrice <= 0)
73234
+ return "1";
73235
+ const tokens = Math.ceil(usdAmount / amikoPrice);
73236
+ return String(Math.max(tokens, 1));
73237
+ }
73238
+ async function payAndCall(params) {
73239
+ const { mppBase: mppBase2, billingBase: billingBase2, billingToken, solanaWallet, endpoint, message, onProgress } = params;
73240
+ onProgress?.("Fetching price...");
73241
+ let amount = "1";
73242
+ let token = "AMIKO";
73243
+ let chain = "solana";
73244
+ let wallet = solanaWallet;
73334
73245
  try {
73335
- const result = await tempoRequest({
73336
- method: "POST",
73337
- url,
73338
- body: JSON.stringify({ message })
73339
- });
73340
- spinner.stop();
73341
- if (opts.raw) {
73342
- console.log(result.body);
73343
- return;
73344
- }
73345
- try {
73346
- const data = JSON.parse(result.body);
73347
- console.log(extractResponse(data));
73348
- console.log("");
73349
- console.log(success("Paid via Tempo wallet"));
73350
- } catch {
73351
- console.log(result.body);
73246
+ const specRes = await fetch(`${mppBase2}/openapi.json`);
73247
+ if (specRes.ok) {
73248
+ const spec = await specRes.json();
73249
+ const pathSpec = spec.paths?.[`/v1/agents/{agentId}/${endpoint}`];
73250
+ const methodSpec = pathSpec?.post;
73251
+ const usdPrice = parseFloat(methodSpec?.["x-payment-info"]?.price || methodSpec?.["x-payment-info"]?.minPrice || "0.01");
73252
+ if (usdPrice > 0) {
73253
+ const amikoPrice = await getAmikoPrice();
73254
+ amount = usdToAmiko(usdPrice, amikoPrice);
73255
+ onProgress?.(`Price: $${usdPrice.toFixed(4)} = ${amount} AMIKO`);
73256
+ }
73352
73257
  }
73353
- } catch (err) {
73354
- spinner.stop();
73355
- console.error(error(err instanceof Error ? err.message : "Call failed"));
73356
- process.exit(1);
73258
+ } catch {}
73259
+ if (!solanaWallet && params.evmWallet) {
73260
+ wallet = params.evmWallet;
73261
+ token = "USDC";
73262
+ chain = "base";
73263
+ amount = "0.01";
73264
+ }
73265
+ onProgress?.(`Paying ${amount} ${token}...`);
73266
+ const transferRes = await fetch(`${billingBase2}/api/crossmint/wallets/${encodeURIComponent(wallet)}/transfer`, {
73267
+ method: "POST",
73268
+ headers: {
73269
+ "Content-Type": "application/json",
73270
+ Authorization: `Bearer ${billingToken}`
73271
+ },
73272
+ body: JSON.stringify({ to: TREASURY, amount, token, chain })
73273
+ });
73274
+ if (!transferRes.ok) {
73275
+ const err = await transferRes.json().catch(() => ({ error: "Transfer failed" }));
73276
+ throw new Error(err.error || `Payment failed: ${transferRes.status}`);
73357
73277
  }
73278
+ const transferData = await transferRes.json();
73279
+ const txHash = transferData.txHash;
73280
+ if (!txHash)
73281
+ throw new Error("Payment sent but no tx hash returned.");
73282
+ onProgress?.(`Calling agent...`);
73283
+ const res = await fetch(`${mppBase2}/v1/agents/${endpoint}`, {
73284
+ method: "POST",
73285
+ headers: {
73286
+ "Content-Type": "application/json",
73287
+ "X-Payment-Tx": txHash
73288
+ },
73289
+ body: JSON.stringify({ message })
73290
+ });
73291
+ return { res, txHash, amount, token };
73358
73292
  }
73359
- async function callViaApiKey(agentId, message, opts) {
73293
+ async function callAgent(agentId, message, opts) {
73360
73294
  const config = loadConfig();
73361
- const base = config.apiBase.replace(/\/$/, "");
73362
- const endpoint = opts.task ? "tasks" : "chat";
73295
+ const auth = resolveAuth(config);
73296
+ const mppBase2 = (config.apiBase || "https://mpp.amikomarkets.com").replace(/\/$/, "");
73297
+ const billingBase2 = auth.billingBase || "https://billing.heyamiko.com";
73298
+ const billingToken = auth.platformKey || auth.twinToken || auth.privyToken;
73299
+ if (!billingToken) {
73300
+ console.error(error("Not authenticated. Check .amiko.json credentials."));
73301
+ process.exit(1);
73302
+ }
73303
+ const allWallets = auth.agentWallets || config.agentWallets || [];
73304
+ const solanaWallet = allWallets.find((w) => w.chain === "solana")?.address || "";
73305
+ const evmWallet = allWallets.find((w) => w.chain === "base" || w.chain === "evm" || w.chain === "tempo")?.address;
73306
+ if (!solanaWallet && !evmWallet) {
73307
+ console.error(error("No wallet found. Your twin needs a Solana or EVM wallet."));
73308
+ process.exit(1);
73309
+ }
73310
+ const endpoint = opts.task ? `${agentId}/tasks` : `${agentId}/chat`;
73363
73311
  const spinner = ora(`Calling ${agentId}...`).start();
73364
73312
  try {
73365
- const res = await fetch(`${base}/v1/agents/${agentId}/${endpoint}`, {
73366
- method: "POST",
73367
- headers: buildHeaders(),
73368
- body: JSON.stringify({ message })
73313
+ const { res, txHash, amount, token } = await payAndCall({
73314
+ mppBase: mppBase2,
73315
+ billingBase: billingBase2,
73316
+ billingToken,
73317
+ solanaWallet,
73318
+ evmWallet,
73319
+ endpoint,
73320
+ message,
73321
+ onProgress: (msg) => {
73322
+ spinner.text = msg;
73323
+ }
73369
73324
  });
73370
- if (res.status === 402) {
73371
- spinner.stop();
73372
- const body = await res.json().catch(() => ({}));
73373
- const challengeId = body.challengeId ?? body.challenge_id ?? "unknown";
73374
- const price = body.price ?? body.amount;
73375
- console.log(warn("Insufficient balance"));
73376
- if (price)
73377
- console.log(dim(` Price: $${price}`));
73378
- console.log(dim(" Top up: amiko wallet topup <amount>"));
73379
- console.log(dim(`
73380
- Challenge: ${challengeId}`));
73381
- return;
73382
- }
73325
+ spinner.stop();
73383
73326
  if (!res.ok) {
73384
- spinner.stop();
73385
73327
  const text = await res.text().catch(() => "");
73386
- console.error(error(`${res.status} ${res.statusText}`));
73387
- if (text)
73388
- console.error(dim(text));
73328
+ console.error(error(`${res.status} ${text.slice(0, 300)}`));
73329
+ console.log(dim(`Payment tx: https://solscan.io/tx/${txHash}`));
73389
73330
  process.exit(1);
73390
73331
  }
73391
73332
  const data = await res.json();
73392
- spinner.stop();
73393
73333
  if (opts.raw) {
73394
73334
  console.log(JSON.stringify(data, null, 2));
73395
73335
  return;
73396
73336
  }
73397
73337
  console.log(extractResponse(data));
73398
73338
  console.log("");
73399
- printPaymentFooter(res, data, opts);
73339
+ printPaymentFooter(txHash, amount, token, opts);
73400
73340
  } catch (err) {
73401
73341
  spinner.stop();
73402
73342
  console.error(error(err instanceof Error ? err.message : "Call failed"));
73403
73343
  process.exit(1);
73404
73344
  }
73405
73345
  }
73406
- async function interactiveMode(agentId, opts, paymentMode) {
73346
+ async function interactiveMode(agentId, opts) {
73347
+ const config = loadConfig();
73348
+ const auth = resolveAuth(config);
73349
+ const mppBase2 = (config.apiBase || "https://mpp.amikomarkets.com").replace(/\/$/, "");
73350
+ const billingBase2 = auth.billingBase || "https://billing.heyamiko.com";
73351
+ const billingToken = auth.platformKey || auth.twinToken || auth.privyToken;
73352
+ const allWallets = auth.agentWallets || config.agentWallets || [];
73353
+ const solanaWallet = allWallets.find((w) => w.chain === "solana")?.address || "";
73354
+ const evmWallet = allWallets.find((w) => w.chain === "base" || w.chain === "evm")?.address;
73407
73355
  console.log(heading(`Interactive session with ${agentId}`));
73408
- console.log(dim(`Payment: ${paymentMode === "tempo" ? "Tempo wallet" : "API key"}`));
73356
+ console.log(dim(`Payment: Crossmint wallet (${solanaWallet ? "Solana" : "EVM"})`));
73409
73357
  console.log(dim(`Type your messages. Ctrl+C or "exit" to quit.
73410
73358
  `));
73411
73359
  const rl = createInterface2({
@@ -73428,51 +73376,26 @@ Session ended.`));
73428
73376
  }
73429
73377
  process.stdout.write(source_default.dim(" thinking..."));
73430
73378
  try {
73431
- if (paymentMode === "tempo") {
73432
- const config = loadConfig();
73433
- const base = config.apiBase.replace(/\/$/, "");
73434
- const endpoint = opts.task ? "tasks" : "chat";
73435
- const result = await tempoRequest({
73436
- method: "POST",
73437
- url: `${base}/v1/agents/${agentId}/${endpoint}`,
73438
- body: JSON.stringify({ message: msg })
73439
- });
73440
- process.stdout.write("\r" + " ".repeat(40) + "\r");
73441
- try {
73442
- const data = JSON.parse(result.body);
73443
- console.log(source_default.green("agent › ") + extractResponse(data));
73444
- } catch {
73445
- console.log(source_default.green("agent › ") + result.body);
73446
- }
73379
+ const endpoint = opts.task ? `${agentId}/tasks` : `${agentId}/chat`;
73380
+ const { res, txHash, amount, token } = await payAndCall({
73381
+ mppBase: mppBase2,
73382
+ billingBase: billingBase2,
73383
+ billingToken: billingToken || "",
73384
+ solanaWallet,
73385
+ evmWallet,
73386
+ endpoint,
73387
+ message: msg
73388
+ });
73389
+ process.stdout.write("\r" + " ".repeat(40) + "\r");
73390
+ if (!res.ok) {
73391
+ console.log(source_default.red(` Error: ${res.status} ${res.statusText}`));
73447
73392
  } else {
73448
- const config = loadConfig();
73449
- const base = config.apiBase.replace(/\/$/, "");
73450
- const endpoint = opts.task ? "tasks" : "chat";
73451
- const res = await fetch(`${base}/v1/agents/${agentId}/${endpoint}`, {
73452
- method: "POST",
73453
- headers: buildHeaders(),
73454
- body: JSON.stringify({ message: msg })
73455
- });
73456
- process.stdout.write("\r" + " ".repeat(40) + "\r");
73457
- if (res.status === 402) {
73458
- console.log(warn("Payment required — insufficient balance."));
73459
- } else if (!res.ok) {
73460
- console.log(source_default.red(` Error: ${res.status} ${res.statusText}`));
73393
+ const data = await res.json();
73394
+ if (opts.raw) {
73395
+ console.log(JSON.stringify(data, null, 2));
73461
73396
  } else {
73462
- const data = await res.json();
73463
- if (opts.raw) {
73464
- console.log(JSON.stringify(data, null, 2));
73465
- } else {
73466
- console.log(source_default.green("agent › ") + extractResponse(data));
73467
- const cost = typeof data.cost === "number" ? data.cost : null;
73468
- if (cost != null) {
73469
- const balance = res.headers.get("x-payment-balance");
73470
- let meta = dim(` ${usd(cost)}`);
73471
- if (balance)
73472
- meta += dim(` · bal $${balance}`);
73473
- console.log(meta);
73474
- }
73475
- }
73397
+ console.log(source_default.green("agent › ") + extractResponse(data));
73398
+ console.log(dim(` ${amount} ${token} · tx: ${txHash.slice(0, 12)}...`));
73476
73399
  }
73477
73400
  }
73478
73401
  } catch (err) {
@@ -73487,43 +73410,74 @@ Session ended.`));
73487
73410
  });
73488
73411
  }
73489
73412
  function registerCallCommand(program2) {
73490
- program2.command("call").description("Call a paid agent service (supports piping, interactive mode, and Tempo wallet)").argument("<agent>", "Agent ID (e.g. titan-research)").argument("[message]", "Message to send (omit for interactive mode, or pipe from stdin)").option("--task", "Use task endpoint instead of chat").option("--raw", "Output raw JSON response").option("-i, --interactive", "Start interactive conversation mode").option("--dry-run", "Preview cost without executing (Tempo wallet only)").option("--no-tempo", "Skip Tempo wallet, use API key only").action(async (agentId, message, opts) => {
73491
- const paymentMode = opts.tempo === false ? loadConfig().apiKey ? "apikey" : "none" : getPaymentMode();
73492
- if (paymentMode === "none") {
73493
- console.error(error("No payment method available."));
73494
- console.log("");
73495
- console.log(dim("Option 1 — Install the Tempo wallet (handles payments automatically):"));
73496
- console.log(dim(" curl -fsSL https://tempo.xyz/install | bash"));
73497
- console.log(dim(" tempo wallet login"));
73498
- console.log("");
73499
- console.log(dim("Option 2 — Use an API key:"));
73500
- console.log(dim(" amiko config set apiKey amk_<your-key>"));
73501
- console.log(dim(" amiko wallet topup 10"));
73502
- process.exit(1);
73503
- }
73413
+ program2.command("call").description("Call a paid agent service via Crossmint wallet payment").argument("<agent>", "Agent ID (e.g. titan-research)").argument("[message]", "Message to send (omit for interactive mode, or pipe from stdin)").option("--task", "Use task endpoint instead of chat").option("--raw", "Output raw JSON response").option("-i, --interactive", "Start interactive conversation mode").action(async (agentId, message, opts) => {
73504
73414
  if (!message && !opts.interactive) {
73505
73415
  const piped = await readStdin();
73506
73416
  if (piped)
73507
73417
  message = piped;
73508
73418
  }
73509
73419
  if (opts.interactive || !message && process.stdin.isTTY) {
73510
- await interactiveMode(agentId, opts, paymentMode);
73420
+ await interactiveMode(agentId, opts);
73511
73421
  return;
73512
73422
  }
73513
73423
  if (!message) {
73514
73424
  console.error(error("No message provided. Pass a message, pipe stdin, or use --interactive."));
73515
73425
  process.exit(1);
73516
73426
  }
73517
- if (paymentMode === "tempo") {
73518
- await callViaTempo(agentId, message, opts);
73519
- } else {
73520
- await callViaApiKey(agentId, message, opts);
73521
- }
73427
+ await callAgent(agentId, message, opts);
73522
73428
  });
73523
73429
  }
73524
73430
 
73525
73431
  // src/commands/wallet.ts
73526
73432
  import { execFileSync as execFileSync3 } from "node:child_process";
73433
+
73434
+ // src/lib/tempo.ts
73435
+ import { execFile, execFileSync as execFileSync2 } from "node:child_process";
73436
+ import { existsSync as existsSync3 } from "node:fs";
73437
+ import { homedir as homedir2 } from "node:os";
73438
+ import { join as join3 } from "node:path";
73439
+ var TEMPO_PATHS = [join3(homedir2(), ".tempo", "bin", "tempo"), "tempo"];
73440
+ function findTempo() {
73441
+ for (const p of TEMPO_PATHS) {
73442
+ if (p === "tempo") {
73443
+ try {
73444
+ execFileSync2(p, ["--version"], { stdio: "pipe", timeout: 3000 });
73445
+ return p;
73446
+ } catch {
73447
+ continue;
73448
+ }
73449
+ } else if (existsSync3(p)) {
73450
+ return p;
73451
+ }
73452
+ }
73453
+ return null;
73454
+ }
73455
+ function hasTempo() {
73456
+ return findTempo() !== null;
73457
+ }
73458
+ function runTempo(args, timeoutMs = 60000) {
73459
+ const bin = findTempo();
73460
+ if (!bin)
73461
+ throw new Error("Tempo CLI not installed. Install: curl -fsSL https://tempo.xyz/install | bash");
73462
+ return new Promise((resolve2, reject) => {
73463
+ execFile(bin, args, { timeout: timeoutMs, maxBuffer: 1024 * 1024 * 10 }, (err, stdout, stderr) => {
73464
+ if (err) {
73465
+ const msg = stderr?.trim() || stdout?.trim() || err.message;
73466
+ reject(new Error(msg));
73467
+ return;
73468
+ }
73469
+ resolve2(stdout.trim() || stderr.trim());
73470
+ });
73471
+ });
73472
+ }
73473
+ async function tempoWhoami() {
73474
+ return runTempo(["wallet", "-t", "whoami"], 1e4);
73475
+ }
73476
+ async function tempoFund() {
73477
+ return runTempo(["wallet", "fund"], 30000);
73478
+ }
73479
+
73480
+ // src/commands/wallet.ts
73527
73481
  function sanitizeTempoWhoamiOutput(output) {
73528
73482
  return output.replace(/^(\s*key:\s*)"[^"\n]*"$/m, '$1"[redacted]"');
73529
73483
  }
@@ -75434,13 +75388,13 @@ function registerCreditsCommand(program2) {
75434
75388
  console.error(error("EVM topup not supported yet. Use a Solana wallet with --token AMIKO, USDC, USDT, or SOL."));
75435
75389
  process.exit(1);
75436
75390
  }
75437
- const TREASURY = "FPiZAf3jEnNfCwfvbv46jdPfHbmSepD4Q6iHiLJDQXvb";
75391
+ const TREASURY2 = "FPiZAf3jEnNfCwfvbv46jdPfHbmSepD4Q6iHiLJDQXvb";
75438
75392
  const spinner3 = ora("Transferring tokens to treasury...").start();
75439
75393
  const transferRes = await fetch(`${auth.billingBase || "https://billing.heyamiko.com"}/api/crossmint/wallets/${encodeURIComponent(walletAddr)}/transfer`, {
75440
75394
  method: "POST",
75441
75395
  headers: authHeaders2,
75442
75396
  body: JSON.stringify({
75443
- to: TREASURY,
75397
+ to: TREASURY2,
75444
75398
  amount: tokenAmount.toFixed(6),
75445
75399
  token,
75446
75400
  chain: walletChain
@@ -75804,7 +75758,7 @@ function registerUpdateCommand(program2) {
75804
75758
  }
75805
75759
 
75806
75760
  // src/commands/search.ts
75807
- var TREASURY = "FPiZAf3jEnNfCwfvbv46jdPfHbmSepD4Q6iHiLJDQXvb";
75761
+ var TREASURY2 = "FPiZAf3jEnNfCwfvbv46jdPfHbmSepD4Q6iHiLJDQXvb";
75808
75762
  function registerSearchCommand(program2) {
75809
75763
  program2.command("search <query>").description("X Search — pays 1 AMIKO token per search").option("--raw", "Output raw JSON").option("--token <token>", "Payment token (AMIKO, USDC, SOL)", "AMIKO").action(async (query, opts) => {
75810
75764
  const spinner = ora("X Search...").start();
@@ -75841,7 +75795,7 @@ function registerSearchCommand(program2) {
75841
75795
  Authorization: `Bearer ${billingToken}`
75842
75796
  },
75843
75797
  body: JSON.stringify({
75844
- to: TREASURY,
75798
+ to: TREASURY2,
75845
75799
  amount: amount2,
75846
75800
  token,
75847
75801
  chain
@@ -75911,9 +75865,9 @@ function registerSearchCommand(program2) {
75911
75865
  }
75912
75866
 
75913
75867
  // src/commands/image.ts
75914
- var TREASURY2 = "FPiZAf3jEnNfCwfvbv46jdPfHbmSepD4Q6iHiLJDQXvb";
75868
+ var TREASURY3 = "FPiZAf3jEnNfCwfvbv46jdPfHbmSepD4Q6iHiLJDQXvb";
75915
75869
  function registerImageCommand(program2) {
75916
- program2.command("image <prompt>").alias("img").description("Generate an image with DALL-E — costs 5 AMIKO").option("--size <size>", "Image size: 1024x1024, 1024x1792, 1792x1024", "1024x1024").option("--quality <q>", "Quality: standard or hd", "standard").option("--model <model>", "Model: dall-e-3 or dall-e-2", "dall-e-3").option("--raw", "Output raw JSON").action(async (prompt2, opts) => {
75870
+ program2.command("image <prompt>").alias("img").description("Generate an image — costs 5 AMIKO").option("--size <size>", "Image size: 1024x1024, 1024x1792, 1792x1024", "1024x1024").option("--quality <q>", "Quality: auto, low, medium, high (gpt-image-1) or standard, hd (dall-e-3)", "auto").option("--model <model>", "Model: gpt-image-1 or dall-e-3", "gpt-image-1").option("--background <bg>", "Background: transparent, opaque, auto (gpt-image-1 only)").option("--format <fmt>", "Output: png, webp, jpeg (gpt-image-1 only)", "png").option("--raw", "Output raw JSON").action(async (prompt2, opts) => {
75917
75871
  const spinner = ora("Generating image...").start();
75918
75872
  try {
75919
75873
  const config2 = loadConfig();
@@ -75941,7 +75895,7 @@ function registerImageCommand(program2) {
75941
75895
  Authorization: `Bearer ${billingToken}`
75942
75896
  },
75943
75897
  body: JSON.stringify({
75944
- to: TREASURY2,
75898
+ to: TREASURY3,
75945
75899
  amount: "5",
75946
75900
  token: "AMIKO",
75947
75901
  chain: "solana"
@@ -75971,7 +75925,9 @@ function registerImageCommand(program2) {
75971
75925
  prompt: prompt2,
75972
75926
  size: opts.size,
75973
75927
  quality: opts.quality,
75974
- model: opts.model
75928
+ model: opts.model,
75929
+ ...opts.background && { background: opts.background },
75930
+ ...opts.model === "gpt-image-1" && { output_format: opts.format || "png" }
75975
75931
  })
75976
75932
  });
75977
75933
  spinner.stop();
@@ -75994,9 +75950,12 @@ function registerImageCommand(program2) {
75994
75950
  console.log("");
75995
75951
  const images = data.images || data.data || [];
75996
75952
  for (const img of images) {
75997
- const url2 = img.url || img.revised_prompt || "";
75998
- if (url2)
75999
- console.log(label("URL", url2));
75953
+ if (img.url) {
75954
+ console.log(label("URL", img.url));
75955
+ } else if (img.b64_json) {
75956
+ console.log(label("Format", "base64 (inline image data)"));
75957
+ console.log(dim(` ${img.b64_json.length.toLocaleString()} chars — save with: amiko image "..." --raw | jq -r '.images[0].b64_json' | base64 -d > image.png`));
75958
+ }
76000
75959
  if (img.revised_prompt && img.revised_prompt !== prompt2) {
76001
75960
  console.log(label("Revised", dim(img.revised_prompt)));
76002
75961
  }
@@ -76012,7 +75971,7 @@ function registerImageCommand(program2) {
76012
75971
  }
76013
75972
 
76014
75973
  // src/commands/amazon.ts
76015
- var TREASURY3 = "FPiZAf3jEnNfCwfvbv46jdPfHbmSepD4Q6iHiLJDQXvb";
75974
+ var TREASURY4 = "FPiZAf3jEnNfCwfvbv46jdPfHbmSepD4Q6iHiLJDQXvb";
76016
75975
  async function payAmiko(billingBase2, billingToken, solanaWallet, amount2) {
76017
75976
  const res = await fetch(`${billingBase2}/api/crossmint/wallets/${encodeURIComponent(solanaWallet)}/transfer`, {
76018
75977
  method: "POST",
@@ -76020,7 +75979,7 @@ async function payAmiko(billingBase2, billingToken, solanaWallet, amount2) {
76020
75979
  "Content-Type": "application/json",
76021
75980
  Authorization: `Bearer ${billingToken}`
76022
75981
  },
76023
- body: JSON.stringify({ to: TREASURY3, amount: amount2, token: "AMIKO", chain: "solana" })
75982
+ body: JSON.stringify({ to: TREASURY4, amount: amount2, token: "AMIKO", chain: "solana" })
76024
75983
  });
76025
75984
  if (!res.ok) {
76026
75985
  const err = await res.json().catch(() => ({ error: "Transfer failed" }));
@@ -76159,23 +76118,23 @@ function registerAmazonCommand(program2) {
76159
76118
  }
76160
76119
 
76161
76120
  // src/commands/services.ts
76162
- var TREASURY4 = "FPiZAf3jEnNfCwfvbv46jdPfHbmSepD4Q6iHiLJDQXvb";
76121
+ var TREASURY5 = "FPiZAf3jEnNfCwfvbv46jdPfHbmSepD4Q6iHiLJDQXvb";
76163
76122
  var AMIKO_MINT = "AY1Ww6MxwC3cCiyrandHqLrp4FXvzgcwQTZpJ2FEpump";
76164
76123
  var USDC_MINT = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";
76165
- async function getAmikoPrice() {
76124
+ async function getAmikoPrice2() {
76166
76125
  const res = await fetch(`https://lite-api.jup.ag/swap/v1/quote?inputMint=${AMIKO_MINT}&outputMint=${USDC_MINT}&amount=${1e6}&slippageBps=100`);
76167
76126
  if (!res.ok)
76168
76127
  return 0;
76169
76128
  const data = await res.json();
76170
76129
  return parseFloat(data.outAmount) / 1e6;
76171
76130
  }
76172
- function usdToAmiko(usd2, pricePerAmiko) {
76131
+ function usdToAmiko2(usd3, pricePerAmiko) {
76173
76132
  if (pricePerAmiko <= 0)
76174
76133
  return "1";
76175
- const amount2 = usd2 / pricePerAmiko;
76134
+ const amount2 = usd3 / pricePerAmiko;
76176
76135
  return Math.ceil(amount2 * 100) / 100 + "";
76177
76136
  }
76178
- async function payAndCall(mppBase2, method, path, billingBase2, billingToken, solanaWallet, amountOrAuto, body, onProgress, token = "AMIKO") {
76137
+ async function payAndCall2(mppBase2, method, path, billingBase2, billingToken, solanaWallet, amountOrAuto, body, onProgress, token = "AMIKO") {
76179
76138
  let amount2 = amountOrAuto;
76180
76139
  if (amount2 === "auto") {
76181
76140
  onProgress?.("Fetching price...");
@@ -76187,8 +76146,8 @@ async function payAndCall(mppBase2, method, path, billingBase2, billingToken, so
76187
76146
  const methodSpec = pathSpec?.[method.toLowerCase()];
76188
76147
  const usdPrice = parseFloat(methodSpec?.["x-payment-info"]?.price || "0");
76189
76148
  if (usdPrice > 0) {
76190
- const amikoPrice = await getAmikoPrice();
76191
- amount2 = usdToAmiko(usdPrice, amikoPrice);
76149
+ const amikoPrice = await getAmikoPrice2();
76150
+ amount2 = usdToAmiko2(usdPrice, amikoPrice);
76192
76151
  onProgress?.(`Price: $${usdPrice.toFixed(4)} = ${amount2} AMIKO`);
76193
76152
  } else {
76194
76153
  amount2 = "1";
@@ -76208,7 +76167,7 @@ async function payAndCall(mppBase2, method, path, billingBase2, billingToken, so
76208
76167
  Authorization: `Bearer ${billingToken}`
76209
76168
  },
76210
76169
  body: JSON.stringify({
76211
- to: TREASURY4,
76170
+ to: TREASURY5,
76212
76171
  amount: amount2,
76213
76172
  token,
76214
76173
  chain: "solana"
@@ -76354,7 +76313,7 @@ async function callService(params) {
76354
76313
  }
76355
76314
  const token = params.usdc ? "USDC" : "AMIKO";
76356
76315
  const amount2 = params.usdc || params.amiko || "auto";
76357
- const { res, txHash, amikoSpent } = await payAndCall(mppBase2, params.method, params.path, billingBase2, billingToken, solanaWallet, token === "USDC" ? amount2 : amount2, params.body, (msg) => {
76316
+ const { res, txHash, amikoSpent } = await payAndCall2(mppBase2, params.method, params.path, billingBase2, billingToken, solanaWallet, token === "USDC" ? amount2 : amount2, params.body, (msg) => {
76358
76317
  spinner.text = msg;
76359
76318
  });
76360
76319
  if (params.usdc) {}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@heyamiko/amiko-cli",
3
- "version": "0.6.5",
3
+ "version": "0.6.7",
4
4
  "description": "Amiko CLI — swap tokens, manage credits, bridge cross-chain, and call marketplace agents",
5
5
  "type": "module",
6
6
  "bin": {