@heyamiko/amiko-cli 0.6.6 → 0.6.8
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/dist/index.js +207 -245
- package/package.json +3 -2
- package/skills/SKILL.md +358 -0
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,7 +75865,7 @@ 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
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();
|
|
@@ -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"
|
|
@@ -76017,7 +75971,7 @@ function registerImageCommand(program2) {
|
|
|
76017
75971
|
}
|
|
76018
75972
|
|
|
76019
75973
|
// src/commands/amazon.ts
|
|
76020
|
-
var
|
|
75974
|
+
var TREASURY4 = "FPiZAf3jEnNfCwfvbv46jdPfHbmSepD4Q6iHiLJDQXvb";
|
|
76021
75975
|
async function payAmiko(billingBase2, billingToken, solanaWallet, amount2) {
|
|
76022
75976
|
const res = await fetch(`${billingBase2}/api/crossmint/wallets/${encodeURIComponent(solanaWallet)}/transfer`, {
|
|
76023
75977
|
method: "POST",
|
|
@@ -76025,7 +75979,7 @@ async function payAmiko(billingBase2, billingToken, solanaWallet, amount2) {
|
|
|
76025
75979
|
"Content-Type": "application/json",
|
|
76026
75980
|
Authorization: `Bearer ${billingToken}`
|
|
76027
75981
|
},
|
|
76028
|
-
body: JSON.stringify({ to:
|
|
75982
|
+
body: JSON.stringify({ to: TREASURY4, amount: amount2, token: "AMIKO", chain: "solana" })
|
|
76029
75983
|
});
|
|
76030
75984
|
if (!res.ok) {
|
|
76031
75985
|
const err = await res.json().catch(() => ({ error: "Transfer failed" }));
|
|
@@ -76164,23 +76118,23 @@ function registerAmazonCommand(program2) {
|
|
|
76164
76118
|
}
|
|
76165
76119
|
|
|
76166
76120
|
// src/commands/services.ts
|
|
76167
|
-
var
|
|
76121
|
+
var TREASURY5 = "FPiZAf3jEnNfCwfvbv46jdPfHbmSepD4Q6iHiLJDQXvb";
|
|
76168
76122
|
var AMIKO_MINT = "AY1Ww6MxwC3cCiyrandHqLrp4FXvzgcwQTZpJ2FEpump";
|
|
76169
76123
|
var USDC_MINT = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";
|
|
76170
|
-
async function
|
|
76124
|
+
async function getAmikoPrice2() {
|
|
76171
76125
|
const res = await fetch(`https://lite-api.jup.ag/swap/v1/quote?inputMint=${AMIKO_MINT}&outputMint=${USDC_MINT}&amount=${1e6}&slippageBps=100`);
|
|
76172
76126
|
if (!res.ok)
|
|
76173
76127
|
return 0;
|
|
76174
76128
|
const data = await res.json();
|
|
76175
76129
|
return parseFloat(data.outAmount) / 1e6;
|
|
76176
76130
|
}
|
|
76177
|
-
function
|
|
76131
|
+
function usdToAmiko2(usd3, pricePerAmiko) {
|
|
76178
76132
|
if (pricePerAmiko <= 0)
|
|
76179
76133
|
return "1";
|
|
76180
|
-
const amount2 =
|
|
76134
|
+
const amount2 = usd3 / pricePerAmiko;
|
|
76181
76135
|
return Math.ceil(amount2 * 100) / 100 + "";
|
|
76182
76136
|
}
|
|
76183
|
-
async function
|
|
76137
|
+
async function payAndCall2(mppBase2, method, path, billingBase2, billingToken, solanaWallet, amountOrAuto, body, onProgress, token = "AMIKO") {
|
|
76184
76138
|
let amount2 = amountOrAuto;
|
|
76185
76139
|
if (amount2 === "auto") {
|
|
76186
76140
|
onProgress?.("Fetching price...");
|
|
@@ -76192,8 +76146,8 @@ async function payAndCall(mppBase2, method, path, billingBase2, billingToken, so
|
|
|
76192
76146
|
const methodSpec = pathSpec?.[method.toLowerCase()];
|
|
76193
76147
|
const usdPrice = parseFloat(methodSpec?.["x-payment-info"]?.price || "0");
|
|
76194
76148
|
if (usdPrice > 0) {
|
|
76195
|
-
const amikoPrice = await
|
|
76196
|
-
amount2 =
|
|
76149
|
+
const amikoPrice = await getAmikoPrice2();
|
|
76150
|
+
amount2 = usdToAmiko2(usdPrice, amikoPrice);
|
|
76197
76151
|
onProgress?.(`Price: $${usdPrice.toFixed(4)} = ${amount2} AMIKO`);
|
|
76198
76152
|
} else {
|
|
76199
76153
|
amount2 = "1";
|
|
@@ -76213,7 +76167,7 @@ async function payAndCall(mppBase2, method, path, billingBase2, billingToken, so
|
|
|
76213
76167
|
Authorization: `Bearer ${billingToken}`
|
|
76214
76168
|
},
|
|
76215
76169
|
body: JSON.stringify({
|
|
76216
|
-
to:
|
|
76170
|
+
to: TREASURY5,
|
|
76217
76171
|
amount: amount2,
|
|
76218
76172
|
token,
|
|
76219
76173
|
chain: "solana"
|
|
@@ -76359,7 +76313,7 @@ async function callService(params) {
|
|
|
76359
76313
|
}
|
|
76360
76314
|
const token = params.usdc ? "USDC" : "AMIKO";
|
|
76361
76315
|
const amount2 = params.usdc || params.amiko || "auto";
|
|
76362
|
-
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) => {
|
|
76363
76317
|
spinner.text = msg;
|
|
76364
76318
|
});
|
|
76365
76319
|
if (params.usdc) {}
|
|
@@ -76376,7 +76330,15 @@ async function callService(params) {
|
|
|
76376
76330
|
console.log(text);
|
|
76377
76331
|
} else {
|
|
76378
76332
|
const data = await res.json();
|
|
76379
|
-
|
|
76333
|
+
if (data.url && data.service) {
|
|
76334
|
+
console.log(heading(`${data.service.toUpperCase()} Generated
|
|
76335
|
+
`));
|
|
76336
|
+
console.log(label("URL", data.url));
|
|
76337
|
+
if (data.format)
|
|
76338
|
+
console.log(label("Format", data.format));
|
|
76339
|
+
} else {
|
|
76340
|
+
console.log(JSON.stringify(data, null, 2));
|
|
76341
|
+
}
|
|
76380
76342
|
}
|
|
76381
76343
|
console.log("");
|
|
76382
76344
|
console.log(success(`Paid ${amikoSpent} ${token}`) + dim(` tx: ${txHash.slice(0, 16)}...`));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@heyamiko/amiko-cli",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.8",
|
|
4
4
|
"description": "Amiko CLI — swap tokens, manage credits, bridge cross-chain, and call marketplace agents",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -13,7 +13,8 @@
|
|
|
13
13
|
"test": "bun test"
|
|
14
14
|
},
|
|
15
15
|
"files": [
|
|
16
|
-
"dist"
|
|
16
|
+
"dist",
|
|
17
|
+
"skills"
|
|
17
18
|
],
|
|
18
19
|
"keywords": [
|
|
19
20
|
"amiko",
|
package/skills/SKILL.md
ADDED
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: amiko
|
|
3
|
+
description: Interact with amiko-new APIs using the twin token already configured in OpenClaw
|
|
4
|
+
homepage: https://platform.heyamiko.com
|
|
5
|
+
metadata: {"openclaw":{"emoji":"🤖","requires":{"bins":["node"]}}}
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Amiko Skill
|
|
9
|
+
|
|
10
|
+
This skill talks to the current `amiko-new` API surface using the twin token already stored in OpenClaw.
|
|
11
|
+
|
|
12
|
+
It does not use `workspace/.amiko.json` or `.amiko.json`.
|
|
13
|
+
|
|
14
|
+
## Configuration
|
|
15
|
+
|
|
16
|
+
Config is read from:
|
|
17
|
+
|
|
18
|
+
```text
|
|
19
|
+
1. $OPENCLAW_CONFIG_PATH
|
|
20
|
+
2. $OPENCLAW_STATE_DIR/openclaw.json
|
|
21
|
+
3. /data/.openclaw/openclaw.json
|
|
22
|
+
4. ~/.openclaw/openclaw.json
|
|
23
|
+
|
|
24
|
+
Then read: channels.amiko
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Installed CLI path:
|
|
28
|
+
|
|
29
|
+
```text
|
|
30
|
+
/openclaw/extensions/amiko/skills/amiko/cli.js
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
If you are editing the plugin from a source checkout, the same file is:
|
|
34
|
+
|
|
35
|
+
```text
|
|
36
|
+
./skills/amiko/cli.js
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Recommended multi-account shape:
|
|
40
|
+
|
|
41
|
+
```json5
|
|
42
|
+
{
|
|
43
|
+
"channels": {
|
|
44
|
+
"amiko": {
|
|
45
|
+
"defaultAccount": "main",
|
|
46
|
+
"accounts": {
|
|
47
|
+
"main": {
|
|
48
|
+
"twinId": "<primaryTwinId>",
|
|
49
|
+
"token": "clawd-...",
|
|
50
|
+
"platformApiBaseUrl": "https://platform.heyamiko.com",
|
|
51
|
+
"chatApiBaseUrl": "https://api.amiko.app"
|
|
52
|
+
},
|
|
53
|
+
"agent-foo": {
|
|
54
|
+
"twinId": "<fooTwinId>",
|
|
55
|
+
"token": "clawd-...",
|
|
56
|
+
"platformApiBaseUrl": "https://platform.heyamiko.com",
|
|
57
|
+
"chatApiBaseUrl": "https://api.amiko.app"
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Notes:
|
|
66
|
+
|
|
67
|
+
- The account key is the OpenClaw agent ID, such as `main` or `agent-foo`.
|
|
68
|
+
- `twinId` lives inside each account entry.
|
|
69
|
+
- The skill uses `platformApiBaseUrl` for `amiko-new` routes.
|
|
70
|
+
- The plugin channel uses `chatApiBaseUrl` for send/health routes.
|
|
71
|
+
- When invoking the CLI directly, prefer `node /openclaw/extensions/amiko/skills/amiko/cli.js ...` in an installed OpenClaw environment, or `node ./skills/amiko/cli.js ...` from the plugin source checkout, instead of relying on the file being executable from the current shell.
|
|
72
|
+
- `--account` is optional when the skill is called from an agent workspace like `/data/.openclaw/workspace` or `/data/.openclaw/workspace-<agentId>`. The CLI auto-detects `main` or `<agentId>` from the current working directory, and also respects `OPENCLAW_AGENT_ID` when present.
|
|
73
|
+
- If auto-detection does not work, use `--account <agentId>`.
|
|
74
|
+
- In the standard container layout, the default persisted config is `/data/.openclaw/openclaw.json`.
|
|
75
|
+
- If the config file is missing, check `OPENCLAW_CONFIG_PATH`, then `OPENCLAW_STATE_DIR`, then `/data/.openclaw/openclaw.json`, before assuming `~/.openclaw/openclaw.json` is the active location.
|
|
76
|
+
|
|
77
|
+
## Scope
|
|
78
|
+
|
|
79
|
+
This skill only keeps APIs that still exist in `amiko-platform/amiko-new` and that make sense with the plugin's twin token config.
|
|
80
|
+
|
|
81
|
+
Removed from the old template:
|
|
82
|
+
|
|
83
|
+
- `.amiko.json`, `--agent`, `--workspace`
|
|
84
|
+
- Old/nonexistent APIs such as stats, personality, social, wallets, training, notifications, user info, twins list
|
|
85
|
+
- `voice:generate`, because `amiko-new` no longer exposes that route
|
|
86
|
+
|
|
87
|
+
## CRITICAL RULES — Read Before Any CLI Call
|
|
88
|
+
|
|
89
|
+
1. **NEVER retry a failed command.** If a command returns an error (400, 401, 402, 404, 500), report the error to the user and STOP. Do not try variations, do not loop. Every paid call costs tokens even if it fails.
|
|
90
|
+
2. **NEVER suggest `amiko login`, `amiko connect`, or generating connection codes.** Auth comes from `.amiko.json` automatically. If auth fails, tell the user to re-init from the Amiko dashboard.
|
|
91
|
+
3. **NEVER use `--task` flag.** Use chat endpoints only.
|
|
92
|
+
4. **Check balance before expensive operations.** Run `amiko credits balance` first if unsure about funds.
|
|
93
|
+
|
|
94
|
+
## Amiko CLI — Payments, Search, Image, Audio, Marketplace
|
|
95
|
+
|
|
96
|
+
The `amiko` CLI handles payments, search, image generation, audio, and marketplace interactions. All services are paid with AMIKO tokens on Solana automatically. Auth is read from `.amiko.json` in the workspace — NOT from the skill's `channels.amiko` config.
|
|
97
|
+
|
|
98
|
+
### When to use the Amiko CLI
|
|
99
|
+
|
|
100
|
+
Use the CLI when the user asks about:
|
|
101
|
+
- **Wallet addresses** or **balances** → `amiko credits balance`
|
|
102
|
+
- **Top up credits** → `amiko credits topup <amount> --token AMIKO --yes`
|
|
103
|
+
- **Search X/Twitter** → `amiko search "<query>"` (1 AMIKO)
|
|
104
|
+
- **Generate images** → `amiko image "<prompt>"` (5 AMIKO)
|
|
105
|
+
- **Sound effects** → `amiko service call POST /v1/sfx '{"text":"...","duration_seconds":5}'` ($0.05) — returns `{ url, format, service }`
|
|
106
|
+
- **Music generation** → `amiko service call POST /v1/music '{"prompt":"...","music_length_ms":30000}'` ($0.10) — returns `{ url, format, service }`
|
|
107
|
+
- **Call marketplace agents** → `amiko call <agent> "message"`
|
|
108
|
+
- **Amazon search** → `amiko amazon search "<query>"` (1 AMIKO)
|
|
109
|
+
- **Amazon quote** → `amiko amazon quote <ASIN>` (free)
|
|
110
|
+
- **Swap tokens** → `amiko swap quote <amount> <from> <to>`
|
|
111
|
+
- **Who am I / identity** → `amiko whoami`
|
|
112
|
+
- **Marketplace** → `amiko browse`
|
|
113
|
+
- **Any MPP service** → `amiko service call <METHOD> <path> [body]`
|
|
114
|
+
- **List all services** → `amiko service list`
|
|
115
|
+
|
|
116
|
+
### When NOT to use the CLI — use the filesystem instead
|
|
117
|
+
|
|
118
|
+
- **"Check my docs"** / **"what documents do I have"** → `ls /data/.openclaw/workspace/amiko-docs/`
|
|
119
|
+
- **"Read this doc"** / **"summarize my doc"** → `cat /data/.openclaw/workspace/amiko-docs/<file>.md`
|
|
120
|
+
- Documents are automatically synced to `/data/.openclaw/workspace/amiko-docs/` as markdown files. Always check there first — it's free and instant. Only fall back to the CLI if the files aren't there.
|
|
121
|
+
|
|
122
|
+
### Quick Reference
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
amiko whoami # identity + wallets
|
|
126
|
+
amiko credits balance # credits + wallet balances
|
|
127
|
+
amiko credits topup 10000 --token AMIKO --yes # top up credits
|
|
128
|
+
amiko search "AI agents" # X search (1 AMIKO)
|
|
129
|
+
amiko image "a sunset over mountains" # image gen (5 AMIKO)
|
|
130
|
+
amiko image "logo" --background transparent # transparent bg (gpt-image-1)
|
|
131
|
+
amiko service call POST /v1/sfx '{"text":"thunder","duration_seconds":5}' # SFX ($0.05)
|
|
132
|
+
amiko service call POST /v1/music '{"prompt":"lo-fi beat","music_length_ms":30000}' # music ($0.10)
|
|
133
|
+
amiko service call POST /v1/ai/openai/chat '{"message":"hi"}' # any service
|
|
134
|
+
amiko amazon search "usb c cable" # Amazon search (1 AMIKO)
|
|
135
|
+
amiko amazon quote B01GGKYKQM # Amazon price quote (free)
|
|
136
|
+
amiko swap quote 0.01 SOL USDC # swap quote
|
|
137
|
+
amiko swap send 0.01 SOL USDC --yes # execute swap
|
|
138
|
+
amiko browse # marketplace
|
|
139
|
+
amiko service list # all MPP services + prices
|
|
140
|
+
amiko --help # all commands
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Quick Commands
|
|
144
|
+
|
|
145
|
+
### Accounts
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
node /openclaw/extensions/amiko/skills/amiko/cli.js accounts
|
|
149
|
+
node /openclaw/extensions/amiko/skills/amiko/cli.js info
|
|
150
|
+
node /openclaw/extensions/amiko/skills/amiko/cli.js --account main info
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### Twin
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
node ./skills/amiko/cli.js info
|
|
157
|
+
node ./skills/amiko/cli.js twin:update --name "New Name"
|
|
158
|
+
node ./skills/amiko/cli.js twin:update --description "Updated profile"
|
|
159
|
+
node ./skills/amiko/cli.js twin:update --public
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### Documents
|
|
163
|
+
|
|
164
|
+
**Reading documents:** Documents uploaded by the user are automatically synced to the workspace. Always check the local filesystem first before using the CLI:
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
# PREFERRED: Read docs directly from workspace (free, instant, no auth needed)
|
|
168
|
+
ls /data/.openclaw/workspace/amiko-docs/
|
|
169
|
+
cat /data/.openclaw/workspace/amiko-docs/<docId>.md
|
|
170
|
+
|
|
171
|
+
# When asked "check my docs", "what docs do I have", etc:
|
|
172
|
+
# 1. First list local docs: ls /data/.openclaw/workspace/amiko-docs/
|
|
173
|
+
# 2. Read any doc: cat /data/.openclaw/workspace/amiko-docs/<filename>.md
|
|
174
|
+
# 3. Only use CLI if local files are missing or you need metadata
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
**Managing documents via CLI** (for listing metadata, uploading, deleting):
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
node ./skills/amiko/cli.js docs
|
|
181
|
+
node ./skills/amiko/cli.js docs --search handbook
|
|
182
|
+
node ./skills/amiko/cli.js docs:get --id <docId>
|
|
183
|
+
|
|
184
|
+
# High-level upload: upload file, then create the doc record
|
|
185
|
+
node ./skills/amiko/cli.js docs:upload --file ./notes.md --title "Notes"
|
|
186
|
+
|
|
187
|
+
# Raw file upload only
|
|
188
|
+
node ./skills/amiko/cli.js docs:upload-file --file ./notes.md
|
|
189
|
+
|
|
190
|
+
# Create doc record manually
|
|
191
|
+
node ./skills/amiko/cli.js docs:create \
|
|
192
|
+
--title "Notes" \
|
|
193
|
+
--filename "notes.md" \
|
|
194
|
+
--file-url "https://..." \
|
|
195
|
+
--file-type "text/markdown"
|
|
196
|
+
|
|
197
|
+
node ./skills/amiko/cli.js docs:presign --filename notes.md --content-type text/markdown
|
|
198
|
+
node ./skills/amiko/cli.js docs:check --id <docId>
|
|
199
|
+
node ./skills/amiko/cli.js docs:delete --id <docId>
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### Voice
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
node ./skills/amiko/cli.js voice
|
|
206
|
+
node ./skills/amiko/cli.js voice:update --description "Warm and natural"
|
|
207
|
+
node ./skills/amiko/cli.js voice:design --description "A calm, warm, natural speaking voice with gentle pacing"
|
|
208
|
+
node ./skills/amiko/cli.js voice:create --generated-voice-id <generatedVoiceId>
|
|
209
|
+
node ./skills/amiko/cli.js voice:clone --file ./sample.mp3
|
|
210
|
+
node ./skills/amiko/cli.js voice:reset
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### Avatar
|
|
214
|
+
|
|
215
|
+
```bash
|
|
216
|
+
node ./skills/amiko/cli.js avatar:update --file ./avatar.png
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### Friends
|
|
220
|
+
|
|
221
|
+
```bash
|
|
222
|
+
node ./skills/amiko/cli.js friends
|
|
223
|
+
node ./skills/amiko/cli.js friends --favorites
|
|
224
|
+
node ./skills/amiko/cli.js friends:search --query alice
|
|
225
|
+
node ./skills/amiko/cli.js friends:requests
|
|
226
|
+
node ./skills/amiko/cli.js friends:add --id <userId>
|
|
227
|
+
node ./skills/amiko/cli.js friends:accept --id <friendshipId>
|
|
228
|
+
node ./skills/amiko/cli.js friends:decline --id <friendshipId>
|
|
229
|
+
node ./skills/amiko/cli.js friends:remove --id <friendshipId>
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### Feed
|
|
233
|
+
|
|
234
|
+
```bash
|
|
235
|
+
node ./skills/amiko/cli.js feed
|
|
236
|
+
node ./skills/amiko/cli.js post --id <postId>
|
|
237
|
+
node ./skills/amiko/cli.js post:comment --id <postId> --comment "Nice post"
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### Composio
|
|
241
|
+
|
|
242
|
+
```bash
|
|
243
|
+
node ./skills/amiko/cli.js composio:connections
|
|
244
|
+
node ./skills/amiko/cli.js composio:connect --app gmail
|
|
245
|
+
node ./skills/amiko/cli.js composio:disconnect --id <connectionId>
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
## API Map
|
|
249
|
+
|
|
250
|
+
Twin:
|
|
251
|
+
|
|
252
|
+
- `GET /api/agents/:id`
|
|
253
|
+
- `PATCH /api/agents/:id`
|
|
254
|
+
|
|
255
|
+
Docs:
|
|
256
|
+
|
|
257
|
+
- `GET /api/agents/:id/docs`
|
|
258
|
+
- `POST /api/agents/:id/docs`
|
|
259
|
+
- `POST /api/agents/:id/docs/upload`
|
|
260
|
+
- `POST /api/agents/:id/docs/presigned-url`
|
|
261
|
+
- `POST /api/agents/:id/docs/check-processing`
|
|
262
|
+
- `GET /api/agents/:id/docs/:docId`
|
|
263
|
+
- `DELETE /api/agents/:id/docs/:docId`
|
|
264
|
+
|
|
265
|
+
Voice:
|
|
266
|
+
|
|
267
|
+
- `GET /api/agents/:id/voice`
|
|
268
|
+
- `POST /api/agents/:id/voice`
|
|
269
|
+
- `POST /api/agents/:id/voice/design`
|
|
270
|
+
- `POST /api/agents/:id/voice/create`
|
|
271
|
+
- `POST /api/agents/:id/voice/clone`
|
|
272
|
+
- `POST /api/agents/:id/voice/reset`
|
|
273
|
+
|
|
274
|
+
Avatar:
|
|
275
|
+
|
|
276
|
+
- `POST /api/agents/:id/avatar`
|
|
277
|
+
|
|
278
|
+
Friends:
|
|
279
|
+
|
|
280
|
+
- `GET /api/friends`
|
|
281
|
+
- `POST /api/friends`
|
|
282
|
+
- `GET /api/friends/search`
|
|
283
|
+
- `GET /api/friends/requests`
|
|
284
|
+
- `PATCH /api/friends/:id`
|
|
285
|
+
- `DELETE /api/friends/:id`
|
|
286
|
+
|
|
287
|
+
Feed:
|
|
288
|
+
|
|
289
|
+
- `GET /api/feed`
|
|
290
|
+
- `GET /api/posts/:id`
|
|
291
|
+
- `POST /api/posts/:id/comments`
|
|
292
|
+
|
|
293
|
+
Composio:
|
|
294
|
+
|
|
295
|
+
- `GET /api/agents/:id/composio/connections`
|
|
296
|
+
- `DELETE /api/agents/:id/composio/connections`
|
|
297
|
+
- `POST /api/agents/:id/composio/connect`
|
|
298
|
+
|
|
299
|
+
## Library
|
|
300
|
+
|
|
301
|
+
```js
|
|
302
|
+
import {
|
|
303
|
+
setAccountId,
|
|
304
|
+
detectCurrentAccountId,
|
|
305
|
+
getConfig,
|
|
306
|
+
listConfiguredAccounts,
|
|
307
|
+
getTwinInfo,
|
|
308
|
+
updateTwin,
|
|
309
|
+
listDocs,
|
|
310
|
+
getDoc,
|
|
311
|
+
createDoc,
|
|
312
|
+
createDocUploadUrl,
|
|
313
|
+
uploadDocFile,
|
|
314
|
+
uploadDoc,
|
|
315
|
+
uploadDocFromFile,
|
|
316
|
+
checkDocsProcessing,
|
|
317
|
+
deleteDoc,
|
|
318
|
+
getVoice,
|
|
319
|
+
updateVoice,
|
|
320
|
+
designVoice,
|
|
321
|
+
createVoice,
|
|
322
|
+
cloneVoice,
|
|
323
|
+
cloneVoiceFromFile,
|
|
324
|
+
resetVoice,
|
|
325
|
+
updateAvatar,
|
|
326
|
+
searchFriends,
|
|
327
|
+
listFriends,
|
|
328
|
+
listFriendRequests,
|
|
329
|
+
sendFriendRequest,
|
|
330
|
+
acceptFriendRequest,
|
|
331
|
+
declineFriendRequest,
|
|
332
|
+
removeFriendship,
|
|
333
|
+
getFeed,
|
|
334
|
+
getPost,
|
|
335
|
+
commentOnPost,
|
|
336
|
+
connectComposioApp,
|
|
337
|
+
listComposioConnections,
|
|
338
|
+
disconnectComposioConnection,
|
|
339
|
+
} from "./lib.js";
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
Examples:
|
|
343
|
+
|
|
344
|
+
```js
|
|
345
|
+
import { setAccountId, getTwinInfo, uploadDocFromFile } from "./lib.js";
|
|
346
|
+
|
|
347
|
+
setAccountId("main");
|
|
348
|
+
// or rely on auto-detection from the current workspace / OPENCLAW_AGENT_ID
|
|
349
|
+
|
|
350
|
+
const twin = await getTwinInfo();
|
|
351
|
+
console.log(twin.name);
|
|
352
|
+
|
|
353
|
+
const doc = await uploadDocFromFile("./notes.md", {
|
|
354
|
+
title: "Notes",
|
|
355
|
+
docType: "other",
|
|
356
|
+
});
|
|
357
|
+
console.log(doc.id);
|
|
358
|
+
```
|