@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.
- package/README.md +52 -6
- package/dist/index.js +208 -249
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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(
|
|
73205
|
+
function printPaymentFooter(txHash, amount, token, opts) {
|
|
73272
73206
|
if (opts.raw)
|
|
73273
73207
|
return;
|
|
73274
|
-
|
|
73275
|
-
|
|
73276
|
-
|
|
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
|
|
73303
|
-
|
|
73304
|
-
|
|
73305
|
-
|
|
73306
|
-
|
|
73307
|
-
|
|
73308
|
-
|
|
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
|
-
|
|
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
|
|
73336
|
-
|
|
73337
|
-
|
|
73338
|
-
|
|
73339
|
-
|
|
73340
|
-
|
|
73341
|
-
|
|
73342
|
-
|
|
73343
|
-
|
|
73344
|
-
|
|
73345
|
-
|
|
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
|
|
73354
|
-
|
|
73355
|
-
|
|
73356
|
-
|
|
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
|
|
73293
|
+
async function callAgent(agentId, message, opts) {
|
|
73360
73294
|
const config = loadConfig();
|
|
73361
|
-
const
|
|
73362
|
-
const
|
|
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
|
|
73366
|
-
|
|
73367
|
-
|
|
73368
|
-
|
|
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
|
-
|
|
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} ${
|
|
73387
|
-
|
|
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(
|
|
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
|
|
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: ${
|
|
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
|
-
|
|
73432
|
-
|
|
73433
|
-
|
|
73434
|
-
|
|
73435
|
-
|
|
73436
|
-
|
|
73437
|
-
|
|
73438
|
-
|
|
73439
|
-
|
|
73440
|
-
|
|
73441
|
-
|
|
73442
|
-
|
|
73443
|
-
|
|
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
|
|
73449
|
-
|
|
73450
|
-
|
|
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
|
-
|
|
73463
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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:
|
|
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
|
|
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:
|
|
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
|
|
75868
|
+
var TREASURY3 = "FPiZAf3jEnNfCwfvbv46jdPfHbmSepD4Q6iHiLJDQXvb";
|
|
75915
75869
|
function registerImageCommand(program2) {
|
|
75916
|
-
program2.command("image <prompt>").alias("img").description("Generate an image
|
|
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:
|
|
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
|
-
|
|
75998
|
-
|
|
75999
|
-
|
|
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
|
|
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:
|
|
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
|
|
76121
|
+
var TREASURY5 = "FPiZAf3jEnNfCwfvbv46jdPfHbmSepD4Q6iHiLJDQXvb";
|
|
76163
76122
|
var AMIKO_MINT = "AY1Ww6MxwC3cCiyrandHqLrp4FXvzgcwQTZpJ2FEpump";
|
|
76164
76123
|
var USDC_MINT = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";
|
|
76165
|
-
async function
|
|
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
|
|
76131
|
+
function usdToAmiko2(usd3, pricePerAmiko) {
|
|
76173
76132
|
if (pricePerAmiko <= 0)
|
|
76174
76133
|
return "1";
|
|
76175
|
-
const amount2 =
|
|
76134
|
+
const amount2 = usd3 / pricePerAmiko;
|
|
76176
76135
|
return Math.ceil(amount2 * 100) / 100 + "";
|
|
76177
76136
|
}
|
|
76178
|
-
async function
|
|
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
|
|
76191
|
-
amount2 =
|
|
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:
|
|
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
|
|
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) {}
|