@heyamiko/amiko-cli 0.9.9-beta.5 → 0.9.9-beta.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +27 -1
  2. package/dist/index.js +67 -12
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # @heyamiko/amiko-cli (v0.9.7)
1
+ # @heyamiko/amiko-cli (v0.9.9-beta.7)
2
2
 
3
3
  Manage wallets, credits, swaps, MPP marketplace services, and your Amiko twin (identity, documents, voice, avatar, friends, feed, Composio apps) from the terminal. Works for both human users and AI agents running on OpenClaw.
4
4
 
@@ -392,6 +392,32 @@ npm publish
392
392
 
393
393
  ## Changelog
394
394
 
395
+ ### 0.9.9-beta.7
396
+
397
+ - `amiko friends list` table now shows the friend's `User.id` as the primary `USER ID` column (what `friends reports request`, `matches`, etc. all need) and surfaces `friendship_id` as a separate `FRIENDSHIP` column (used by `friends accept/remove`). Mirrors how amiko-web's UI separates the two ids: person actions take `actor.id`, row actions take `friendship_id`. JSON output unchanged. Fixes a 404 from `/api/internal/friend-reports/regenerate` when an agent fed the friendship row id back as `--id` for a paid report.
398
+
399
+ ### 0.9.9-beta.6
400
+
401
+ - Code-review followups across `friends`, `image`, `memory`, and the friend-report payment lib: explicit allowlists for `--size`/`--quality`/`--format`/`--background` on `markets image` (rejects unknown values before charging), `auto` quality maps to `high` for billing so we never under-bill, `memory` uses `lstatSync` + `path.relative` to skip symlinks and stop relying on string `startsWith` for sandboxing, and the friends-report transfer body now sets `chain: "solana"` explicitly.
402
+
403
+ ### 0.9.9-beta.5
404
+
405
+ - `amiko friends reports request --pay` — pays the AMIKO friend-report fee from the agent's Crossmint-custodied Solana wallet (no keys held in CLI). Two-step flow: `amiko-web /transfer` signs the AMIKO charge to treasury, then `mpp-service /v1/friend-reports/generate` is called with `X-Payment-Tx`. Confirm prompt shows the exact AMIKO amount quoted from the live spot price.
406
+ - `--json` output for friends-report includes `txHash` + `amikoAmount` payment metadata.
407
+ - `did:privy:` prefix is stripped from user ids before the mpp call so amiko-web's `User` table lookup matches.
408
+ - `markets image` pricing is now dynamic: charges OpenAI pass-through × 1.30 markup per model/quality/size, quoted in AMIKO from live `AMIKO/USD`. Confirm prompt shows the exact amount before any transfer.
409
+ - Imports `Mppx` + Solana helpers from `@solana/mpp/client` subpath (root barrel only re-exports `charge`).
410
+
411
+ ### 0.9.9-beta.1 → 0.9.9-beta.4
412
+
413
+ - `amiko memory add | list | search | rm | status | sync` — manage the twin's persistent memory store from the CLI.
414
+ - `memory sync` now also walks `<workspace>/memory/` so memory written next to your project files gets picked up.
415
+ - `SKILL.md` documents the memory commands and biases agents toward calling `memory search` before falling back to other context-discovery flows.
416
+
417
+ ### 0.9.8
418
+
419
+ - `SKILL.md` defaults `platform=Amiko` and steers agents away from `sessions_*` lookups.
420
+
395
421
  ### 0.9.7
396
422
 
397
423
  - `composio connect` is now idempotent by default. Previously the CLI sent `forceReconnect: true` whenever `--force` was omitted, which caused every status probe to tear down the active Composio connection. The new default is `forceReconnect: false`: if the toolkit is already connected, the call returns `{ alreadyConnected: true }` without touching the connection. Pass `--force` explicitly to opt into a destructive reconnect.
package/dist/index.js CHANGED
@@ -26283,8 +26283,29 @@ function registerImageCommand(program2) {
26283
26283
  console.error(error(`Unknown model "${opts.model}". Supported: ${SUPPORTED_MODELS.join(", ")}.`));
26284
26284
  process.exit(1);
26285
26285
  }
26286
+ const ALLOWED_SIZES = ["1024x1024", "1024x1536", "1536x1024"];
26287
+ const ALLOWED_QUALITIES = ["low", "medium", "high", "auto"];
26288
+ const ALLOWED_FORMATS = ["png", "webp", "jpeg"];
26289
+ const ALLOWED_BACKGROUNDS = ["transparent", "opaque", "auto"];
26290
+ if (!ALLOWED_SIZES.includes(opts.size)) {
26291
+ console.error(error(`Unknown --size "${opts.size}". Allowed: ${ALLOWED_SIZES.join(", ")}.`));
26292
+ process.exit(1);
26293
+ }
26294
+ if (!ALLOWED_QUALITIES.includes(opts.quality)) {
26295
+ console.error(error(`Unknown --quality "${opts.quality}". Allowed: ${ALLOWED_QUALITIES.join(", ")}.`));
26296
+ process.exit(1);
26297
+ }
26298
+ if (opts.format && !ALLOWED_FORMATS.includes(opts.format)) {
26299
+ console.error(error(`Unknown --format "${opts.format}". Allowed: ${ALLOWED_FORMATS.join(", ")}.`));
26300
+ process.exit(1);
26301
+ }
26302
+ if (opts.background && !ALLOWED_BACKGROUNDS.includes(opts.background)) {
26303
+ console.error(error(`Unknown --background "${opts.background}". Allowed: ${ALLOWED_BACKGROUNDS.join(", ")}.`));
26304
+ process.exit(1);
26305
+ }
26306
+ const billedQuality = opts.quality === "auto" ? "high" : opts.quality;
26286
26307
  const n = Math.max(1, Math.min(Math.floor(Number(opts.n) || 1), 4));
26287
- const usdCost = computeImageUsd(opts.model, opts.quality, opts.size, n);
26308
+ const usdCost = computeImageUsd(opts.model, billedQuality, opts.size, n);
26288
26309
  let auth;
26289
26310
  try {
26290
26311
  auth = requireAuth();
@@ -27372,6 +27393,17 @@ function registerAvatarCommand(program2) {
27372
27393
  // src/lib/mpp-friend-report.ts
27373
27394
  var AMIKO_TREASURY = "FPiZAf3jEnNfCwfvbv46jdPfHbmSepD4Q6iHiLJDQXvb";
27374
27395
  var PRICE_USD = "0.10";
27396
+ async function quoteFriendReport(auth) {
27397
+ const amikoUsd = await getTokenUsdPrice(auth, "AMIKO");
27398
+ if (!amikoUsd || amikoUsd <= 0) {
27399
+ return { error: "Failed to fetch AMIKO/USD price" };
27400
+ }
27401
+ return {
27402
+ amikoAmount: (Number(PRICE_USD) / amikoUsd).toFixed(6),
27403
+ amikoUsd,
27404
+ usdAnchor: PRICE_USD
27405
+ };
27406
+ }
27375
27407
  async function mppFriendReportGenerate(auth, body, opts = {}) {
27376
27408
  const config2 = loadConfig();
27377
27409
  const mppBase2 = config2.apiBase.replace(/\/$/, "");
@@ -27407,7 +27439,8 @@ async function mppFriendReportGenerate(auth, body, opts = {}) {
27407
27439
  body: {
27408
27440
  to: AMIKO_TREASURY,
27409
27441
  amount: amikoAmount,
27410
- token: "AMIKO"
27442
+ token: "AMIKO",
27443
+ chain: "solana"
27411
27444
  }
27412
27445
  });
27413
27446
  const candidate = transferData.txHash ?? transferData.tx_hash ?? transferData.signature;
@@ -27480,8 +27513,10 @@ async function runPaidRegenerate(args) {
27480
27513
  console.error(error("Paid regenerate requires your user id. Add 'amikoUserId' to your .amiko.json (alongside amikoTwinToken/amikoTwinId)."));
27481
27514
  process.exit(1);
27482
27515
  }
27516
+ const quote = await quoteFriendReport(auth);
27517
+ const priceLine = "error" in quote ? "(price oracle down — actual amount computed at server)" : `${quote.amikoAmount} AMIKO ≈ $${quote.usdAnchor} (1 AMIKO = $${quote.amikoUsd.toFixed(4)})`;
27483
27518
  await confirmDestructive({
27484
- action: `Pay AMIKO to regenerate a ${args.type} matching report with user ${args.id} (~$0.10 USD-equivalent in AMIKO; signed by your agent's wallet via amiko-web)`,
27519
+ action: `Pay ${priceLine} to regenerate a ${args.type} matching report with user ${args.id} (signed by your agent's wallet via amiko-web)`,
27485
27520
  yes: args.yes,
27486
27521
  commandExample: `amiko friends reports request --id ${args.id} --type ${args.type} --pay`
27487
27522
  });
@@ -27561,12 +27596,20 @@ function registerFriendsCommand(program2) {
27561
27596
  }
27562
27597
  console.log(heading(`Friends (${items.length})`));
27563
27598
  const rows = [
27564
- [dim("ID"), dim("NAME"), dim("HANDLE"), dim("TYPE"), dim("SINCE")],
27599
+ [
27600
+ dim("USER ID"),
27601
+ dim("NAME"),
27602
+ dim("HANDLE"),
27603
+ dim("TYPE"),
27604
+ dim("FRIENDSHIP"),
27605
+ dim("SINCE")
27606
+ ],
27565
27607
  ...items.map((f) => [
27566
- f.friendship_id,
27608
+ f.friend.id,
27567
27609
  f.friend.name ?? "-",
27568
27610
  f.friend.handle ?? "-",
27569
27611
  f.friend.type,
27612
+ f.friendship_id,
27570
27613
  f.since?.slice(0, 10) ?? "-"
27571
27614
  ])
27572
27615
  ];
@@ -27671,6 +27714,7 @@ Accept with: amiko friends accept <friendshipId>`));
27671
27714
  const reports = program2.command("reports").description("Friend matching reports");
27672
27715
  reports.command("list").description("List friend matching reports").option("--page <n>", "Page number", "1").option("--limit <n>", "Page size (1-50)", "20").option("--json", "Output as JSON").action(async (opts) => {
27673
27716
  const auth = resolveAuth();
27717
+ const meId = (auth.userId ?? "").replace(/^did:privy:/, "");
27674
27718
  const spinner = opts.json ? null : ora("Loading reports...").start();
27675
27719
  try {
27676
27720
  const data = await amikoWebFetch(auth, "/api/friends/reports", {
@@ -27694,7 +27738,7 @@ Accept with: amiko friends accept <friendshipId>`));
27694
27738
  dim("CREATED")
27695
27739
  ],
27696
27740
  ...items.map((r) => {
27697
- const other = r.respondent ?? r.initiator;
27741
+ const other = r.initiator?.id === meId ? r.respondent : r.respondent?.id === meId ? r.initiator : r.respondent ?? r.initiator;
27698
27742
  return [
27699
27743
  r.id,
27700
27744
  r.report_type ?? "-",
@@ -27715,6 +27759,7 @@ Accept with: amiko friends accept <friendshipId>`));
27715
27759
  });
27716
27760
  reports.command("view <id>").description("View a friend matching report").option("--json", "Output as JSON").action(async (id, opts) => {
27717
27761
  const auth = resolveAuth();
27762
+ const meId = (auth.userId ?? "").replace(/^did:privy:/, "");
27718
27763
  const spinner = opts.json ? null : ora("Loading report...").start();
27719
27764
  try {
27720
27765
  const data = await amikoWebFetch(auth, `/api/friends/reports/${encodeURIComponent(id)}`);
@@ -27726,7 +27771,7 @@ Accept with: amiko friends accept <friendshipId>`));
27726
27771
  console.log(label("Type", r.report_type));
27727
27772
  if (r.status)
27728
27773
  console.log(label("Status", r.status));
27729
- const other = r.respondent ?? r.initiator;
27774
+ const other = r.initiator?.id === meId ? r.respondent : r.respondent?.id === meId ? r.initiator : r.respondent ?? r.initiator;
27730
27775
  if (other)
27731
27776
  console.log(label("With", other.name ?? other.handle ?? other.id));
27732
27777
  if (r.created_at)
@@ -28599,8 +28644,12 @@ function registerNotificationsCommand(program2) {
28599
28644
  }
28600
28645
 
28601
28646
  // src/commands/memory.ts
28602
- import { existsSync as existsSync3, readdirSync, readFileSync as readFileSync2, statSync } from "node:fs";
28603
- import { dirname, isAbsolute, normalize, resolve as resolve2 } from "node:path";
28647
+ import { existsSync as existsSync3, lstatSync, readdirSync, readFileSync as readFileSync2, statSync } from "node:fs";
28648
+ import { dirname, isAbsolute, normalize, relative, resolve as resolve2 } from "node:path";
28649
+ function isInsideRoot(abs, root) {
28650
+ const rel = relative(root, abs);
28651
+ return rel !== "" && !rel.startsWith("..") && !isAbsolute(rel);
28652
+ }
28604
28653
  var VALID_CATEGORIES = ["fact", "preference", "pattern", "decision", "context"];
28605
28654
  var VALID_SOURCES = ["chat", "file", "manual"];
28606
28655
  var DEFAULT_WORKSPACE = "/data/.openclaw/workspace";
@@ -28878,7 +28927,7 @@ function collectMemoryFiles(root, indexPaths, memoryDir) {
28878
28927
  if (isExternal(ref))
28879
28928
  continue;
28880
28929
  const abs = normalize(isAbsolute(ref) ? ref : resolve2(indexDir, ref));
28881
- if (!abs.startsWith(root)) {
28930
+ if (!isInsideRoot(abs, root)) {
28882
28931
  errors3.push(`Skipping ${ref}: outside workspace ${root}`);
28883
28932
  continue;
28884
28933
  }
@@ -28886,7 +28935,11 @@ function collectMemoryFiles(root, indexPaths, memoryDir) {
28886
28935
  errors3.push(`Referenced file missing: ${ref}`);
28887
28936
  continue;
28888
28937
  }
28889
- const st = statSync(abs);
28938
+ const st = lstatSync(abs);
28939
+ if (st.isSymbolicLink()) {
28940
+ errors3.push(`Skipping ${ref}: symlinks not followed`);
28941
+ continue;
28942
+ }
28890
28943
  if (!st.isFile())
28891
28944
  continue;
28892
28945
  const content = safeRead(abs, errors3);
@@ -28918,10 +28971,12 @@ function walkMarkdown(dir, errors3) {
28918
28971
  const abs = resolve2(dir, name);
28919
28972
  let st;
28920
28973
  try {
28921
- st = statSync(abs);
28974
+ st = lstatSync(abs);
28922
28975
  } catch {
28923
28976
  continue;
28924
28977
  }
28978
+ if (st.isSymbolicLink())
28979
+ continue;
28925
28980
  if (st.isDirectory()) {
28926
28981
  out.push(...walkMarkdown(abs, errors3));
28927
28982
  } else if (st.isFile() && name.toLowerCase().endsWith(".md")) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@heyamiko/amiko-cli",
3
- "version": "0.9.9-beta.5",
3
+ "version": "0.9.9-beta.7",
4
4
  "description": "Amiko CLI — swap tokens, manage credits, bridge cross-chain, and call marketplace agents",
5
5
  "type": "module",
6
6
  "bin": {