@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.
Files changed (3) hide show
  1. package/dist/index.js +207 -245
  2. package/package.json +3 -2
  3. 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(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,7 +75865,7 @@ 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
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: TREASURY2,
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 TREASURY3 = "FPiZAf3jEnNfCwfvbv46jdPfHbmSepD4Q6iHiLJDQXvb";
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: TREASURY3, amount: amount2, token: "AMIKO", chain: "solana" })
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 TREASURY4 = "FPiZAf3jEnNfCwfvbv46jdPfHbmSepD4Q6iHiLJDQXvb";
76121
+ var TREASURY5 = "FPiZAf3jEnNfCwfvbv46jdPfHbmSepD4Q6iHiLJDQXvb";
76168
76122
  var AMIKO_MINT = "AY1Ww6MxwC3cCiyrandHqLrp4FXvzgcwQTZpJ2FEpump";
76169
76123
  var USDC_MINT = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";
76170
- async function getAmikoPrice() {
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 usdToAmiko(usd2, pricePerAmiko) {
76131
+ function usdToAmiko2(usd3, pricePerAmiko) {
76178
76132
  if (pricePerAmiko <= 0)
76179
76133
  return "1";
76180
- const amount2 = usd2 / pricePerAmiko;
76134
+ const amount2 = usd3 / pricePerAmiko;
76181
76135
  return Math.ceil(amount2 * 100) / 100 + "";
76182
76136
  }
76183
- 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") {
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 getAmikoPrice();
76196
- amount2 = usdToAmiko(usdPrice, amikoPrice);
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: TREASURY4,
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 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) => {
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
- console.log(JSON.stringify(data, null, 2));
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.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",
@@ -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
+ ```