@heyamiko/amiko-cli 0.9.2 → 0.9.4

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 CHANGED
@@ -19158,6 +19158,22 @@ async function mppCreditsTopup(params) {
19158
19158
  }
19159
19159
  }
19160
19160
 
19161
+ // src/lib/price.ts
19162
+ var DEFAULT_PLATFORM_URL2 = "https://platform.heyamiko.com";
19163
+ async function getTokenUsdPrice(auth, symbolOrMint) {
19164
+ const base = (auth.platformUrl || DEFAULT_PLATFORM_URL2).replace(/\/$/, "");
19165
+ const param = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/.test(symbolOrMint) ? `mint=${encodeURIComponent(symbolOrMint)}` : `symbol=${encodeURIComponent(symbolOrMint)}`;
19166
+ try {
19167
+ const res = await fetch(`${base}/api/prices/token?${param}`);
19168
+ if (!res.ok)
19169
+ return null;
19170
+ const data = await res.json();
19171
+ return typeof data.price === "number" ? data.price : null;
19172
+ } catch {
19173
+ return null;
19174
+ }
19175
+ }
19176
+
19161
19177
  // src/commands/credits.ts
19162
19178
  function promptYesNo2(question) {
19163
19179
  if (!process.stdin.isTTY)
@@ -19242,34 +19258,19 @@ function registerCreditsCommand(program2) {
19242
19258
  pricePerToken = 1;
19243
19259
  } else {
19244
19260
  const spinner2 = ora(`Fetching ${token} price...`).start();
19245
- try {
19246
- const MINTS = {
19247
- AMIKO: "AY1Ww6MxwC3cCiyrandHqLrp4FXvzgcwQTZpJ2FEpump",
19248
- SOL: "So11111111111111111111111111111111111111112"
19249
- };
19250
- const USDC_MINT = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";
19251
- const mint = MINTS[token.toUpperCase()];
19252
- if (!mint) {
19253
- spinner2.stop();
19254
- console.error(error(`Unknown token: ${token}`));
19255
- process.exit(1);
19256
- }
19257
- const decimals = token.toUpperCase() === "SOL" ? 9 : 6;
19258
- const oneToken = Math.pow(10, decimals).toString();
19259
- const jupRes = await fetch(`https://lite-api.jup.ag/swap/v1/quote?inputMint=${mint}&outputMint=${USDC_MINT}&amount=${oneToken}&slippageBps=100`);
19260
- if (!jupRes.ok) {
19261
- spinner2.stop();
19262
- console.error(error("Failed to fetch token price"));
19263
- process.exit(1);
19264
- }
19265
- const jupData = await jupRes.json();
19266
- pricePerToken = parseFloat(jupData.outAmount) / 1e6;
19267
- spinner2.stop();
19268
- } catch {
19261
+ const supported = ["AMIKO", "SOL"];
19262
+ if (!supported.includes(token.toUpperCase())) {
19269
19263
  spinner2.stop();
19264
+ console.error(error(`Unknown token: ${token}`));
19265
+ process.exit(1);
19266
+ }
19267
+ const price = await getTokenUsdPrice(auth, token.toUpperCase());
19268
+ spinner2.stop();
19269
+ if (price == null || price <= 0) {
19270
19270
  console.error(error("Failed to fetch token price"));
19271
19271
  process.exit(1);
19272
19272
  }
19273
+ pricePerToken = price;
19273
19274
  }
19274
19275
  let creditsAmount;
19275
19276
  let usdAmount;
@@ -20160,15 +20161,6 @@ function registerAmazonCommand(program2) {
20160
20161
 
20161
20162
  // src/commands/services.ts
20162
20163
  var TREASURY4 = "FPiZAf3jEnNfCwfvbv46jdPfHbmSepD4Q6iHiLJDQXvb";
20163
- var AMIKO_MINT = "AY1Ww6MxwC3cCiyrandHqLrp4FXvzgcwQTZpJ2FEpump";
20164
- var USDC_MINT = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";
20165
- async function getAmikoPrice() {
20166
- const res = await fetch(`https://lite-api.jup.ag/swap/v1/quote?inputMint=${AMIKO_MINT}&outputMint=${USDC_MINT}&amount=${1e6}&slippageBps=100`);
20167
- if (!res.ok)
20168
- return 0;
20169
- const data = await res.json();
20170
- return parseFloat(data.outAmount) / 1e6;
20171
- }
20172
20164
  function usdToAmiko(usd2, pricePerAmiko) {
20173
20165
  if (pricePerAmiko <= 0)
20174
20166
  return "1";
@@ -20187,7 +20179,7 @@ async function payAndCall(mppBase2, method, path2, auth, wallet, amountOrAuto, b
20187
20179
  const methodSpec = pathSpec?.[method.toLowerCase()];
20188
20180
  const usdPrice = parseFloat(methodSpec?.["x-payment-info"]?.price || "0");
20189
20181
  if (usdPrice > 0) {
20190
- const amikoPrice = await getAmikoPrice();
20182
+ const amikoPrice = await getTokenUsdPrice(auth, "AMIKO") ?? 0;
20191
20183
  amount2 = usdToAmiko(usdPrice, amikoPrice);
20192
20184
  onProgress?.(`Price: $${usdPrice.toFixed(4)} = ${amount2} AMIKO`);
20193
20185
  } else {
@@ -21617,6 +21609,77 @@ function registerPostCommand(program2) {
21617
21609
  }
21618
21610
  });
21619
21611
  }
21612
+ function registerReviewCommand(program2) {
21613
+ program2.command("list").description("List twin-drafted comments awaiting your approval").option("--json", "Output as JSON").action(async (opts) => {
21614
+ const auth = resolveAuth();
21615
+ const spinner = opts.json ? null : ora("Loading review queue...").start();
21616
+ try {
21617
+ const data = await amikoWebFetch(auth, "/api/comments/review");
21618
+ spinner?.stop();
21619
+ renderOutput(data, (d) => {
21620
+ if (!d.comments?.length) {
21621
+ console.log(dim("No comments awaiting review."));
21622
+ return;
21623
+ }
21624
+ console.log(heading(`Review queue — ${d.comments.length} comment(s)`));
21625
+ for (const c of d.comments) {
21626
+ const author = c.post?.user?.name ?? c.post?.user?.handle ?? "unknown";
21627
+ console.log("");
21628
+ console.log(label("Comment ID", c.id));
21629
+ console.log(label("Twin", c.twin?.name ?? c.twin?.id ?? "-"));
21630
+ console.log(label("On post", `${author} · ${(c.post?.content ?? "").slice(0, 80)}`));
21631
+ if (c.post?.id)
21632
+ console.log(label("Post ID", c.post.id));
21633
+ console.log(label("Draft", c.comment));
21634
+ console.log(dim(c.created_at));
21635
+ }
21636
+ }, { json: opts.json });
21637
+ } catch (e) {
21638
+ spinner?.stop();
21639
+ console.error(error(e instanceof Error ? e.message : String(e)));
21640
+ process.exit(1);
21641
+ }
21642
+ });
21643
+ program2.command("approve <commentId>").description("Approve and publish a twin-drafted comment from the review queue").option("--json", "Output as JSON").action(async (commentId, opts) => {
21644
+ const auth = resolveAuth();
21645
+ const spinner = opts.json ? null : ora("Approving comment...").start();
21646
+ try {
21647
+ const data = await amikoWebFetch(auth, `/api/comments/${encodeURIComponent(commentId)}`, { method: "PATCH", body: { action: "approve" } });
21648
+ spinner?.stop();
21649
+ renderOutput(data, () => {
21650
+ console.log(success("Comment approved and published"));
21651
+ console.log(label("Comment ID", commentId));
21652
+ }, { json: opts.json });
21653
+ } catch (e) {
21654
+ spinner?.stop();
21655
+ console.error(error(e instanceof Error ? e.message : String(e)));
21656
+ process.exit(1);
21657
+ }
21658
+ });
21659
+ program2.command("reject <commentId>").description("Reject (delete) a twin-drafted comment from the review queue").option("--yes", "Skip confirmation").option("--json", "Output as JSON").action(async (commentId, opts) => {
21660
+ await confirmDestructive({
21661
+ action: `Reject and delete comment ${commentId}`,
21662
+ yes: opts.yes,
21663
+ commandExample: `amiko review reject ${commentId}`
21664
+ });
21665
+ const auth = resolveAuth();
21666
+ const spinner = opts.json ? null : ora("Rejecting comment...").start();
21667
+ try {
21668
+ await amikoWebFetch(auth, `/api/comments/${encodeURIComponent(commentId)}`, { method: "DELETE" });
21669
+ spinner?.stop();
21670
+ if (opts.json) {
21671
+ console.log(JSON.stringify({ ok: true, id: commentId }, null, 2));
21672
+ } else {
21673
+ console.log(success("Comment rejected"));
21674
+ console.log(label("Comment ID", commentId));
21675
+ }
21676
+ } catch (e) {
21677
+ spinner?.stop();
21678
+ console.error(error(e instanceof Error ? e.message : String(e)));
21679
+ process.exit(1);
21680
+ }
21681
+ });
21682
+ }
21620
21683
 
21621
21684
  // src/commands/composio.ts
21622
21685
  function twinFromFlag3(opts) {
@@ -21911,6 +21974,7 @@ var avatar = program2.command("avatar").description("Manage twin avatar image");
21911
21974
  var friends = program2.command("friends").description("Manage friendships — list, requests, add, accept, remove, reports, matches");
21912
21975
  var users = program2.command("users").description("Search users and view public profiles");
21913
21976
  var post = program2.command("post").description("Create posts and comments on the feed");
21977
+ var review = program2.command("review").description("Review queue — list, approve, or reject twin-drafted comments");
21914
21978
  var composio = program2.command("composio").description("Manage Composio app connections");
21915
21979
  registerCreditsCommand(program2);
21916
21980
  registerSearchCommand(markets);
@@ -21935,5 +21999,6 @@ registerFriendsCommand(friends);
21935
21999
  registerUsersCommand(users);
21936
22000
  registerFeedCommand(program2);
21937
22001
  registerPostCommand(post);
22002
+ registerReviewCommand(review);
21938
22003
  registerComposioCommand(composio);
21939
22004
  program2.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@heyamiko/amiko-cli",
3
- "version": "0.9.2",
3
+ "version": "0.9.4",
4
4
  "description": "Amiko CLI — swap tokens, manage credits, bridge cross-chain, and call marketplace agents",
5
5
  "type": "module",
6
6
  "bin": {
package/skills/SKILL.md CHANGED
@@ -29,6 +29,7 @@ Each row is a first-level command group or command. Start here when the user ask
29
29
  | `amiko friends <cmd>` | Social graph — list, requests, add, accept, remove, matches, reports |
30
30
  | `amiko users <cmd>` | Search users, view public profile |
31
31
  | `amiko post <cmd>` | Create posts and comments on the feed (optionally with media) |
32
+ | `amiko review <cmd>` | Review queue — list, approve, or reject twin-drafted comments |
32
33
  | `amiko feed` | Read the friends feed, for-you feed, or filter by hashtag |
33
34
  | `amiko composio <cmd>` | Connect / disconnect third-party OAuth apps (Gmail, GitHub, …) |
34
35
  | `amiko accounts` | Show the resolved identity (authenticated, userId, twinId, platform) |
@@ -51,7 +52,7 @@ Every paid / value-moving command (`markets search`, `markets image`, `markets a
51
52
 
52
53
  ## Critical Rules
53
54
 
54
- 1. **Every paid / value-moving command is hard-gated on `--yes` in a non-interactive shell.** The CLI *refuses* to run `markets image`, `markets search`, `markets amazon search`, `markets service call`, `markets service tts`, `markets service stt`, `markets service chat`, `credits topup`, `wallets swap send`, `wallets bridge send`, or `wallets transfer` unless `--yes` is passed. The refusal is loud: it prints the cost and the command you should re-run. **Quote the cost to the user, get explicit approval, THEN append `--yes`**. Same for destructive non-paid commands (`twin update --public`, `docs delete`, `friends remove`, `friends reports request`, `avatar update`, `voice reset`, `composio disconnect`).
55
+ 1. **Every paid / value-moving command is hard-gated on `--yes` in a non-interactive shell.** The CLI *refuses* to run `markets image`, `markets search`, `markets amazon search`, `markets service call`, `markets service tts`, `markets service stt`, `markets service chat`, `credits topup`, `wallets swap send`, `wallets bridge send`, or `wallets transfer` unless `--yes` is passed. The refusal is loud: it prints the cost and the command you should re-run. **Quote the cost to the user, get explicit approval, THEN append `--yes`**. Same for destructive non-paid commands (`twin update --public`, `docs delete`, `friends remove`, `friends reports request`, `avatar update`, `voice reset`, `composio disconnect`, `review reject`).
55
56
  2. **After every paid command, report the remaining balance.** The CLI prints a `Balance: N credits` line at the end of every paid command — include that figure in your reply.
56
57
  3. **Never retry a failed command.** Report the error and stop. Every paid call costs tokens even on failure.
57
58
  4. **Never suggest `amiko login` or `amiko connect`.** These don't exist. Auth is automatic when you run from your workspace folder.
@@ -252,8 +253,13 @@ amiko post create --content "look" --media https://...jpg
252
253
  amiko post comment --id <postId> --comment "great post"
253
254
  amiko post comment --id <postId> --comment "look" --media https://...jpg
254
255
  amiko post comment --id <postId> --comment "..." --twin <id>
256
+ amiko review list # list twin-drafted comments awaiting your approval
257
+ amiko review approve <commentId> # publish a draft comment from the review queue
258
+ amiko review reject <commentId> --yes # destructive — delete a draft comment
255
259
  ```
256
260
 
261
+ **Comments authored by a twin always go through review.** Any comment created with `amiko post comment ... --twin <id>` (or auto-drafted by a twin in engagement mode) lands in `status=draft` and is **not visible** until you run `amiko review approve <commentId>`. Comments without `--twin` (you posting as yourself) publish immediately and skip the review queue. Workflow: `amiko review list` to see pending drafts → `amiko review approve <id>` to publish, or `amiko review reject <id> --yes` to discard.
262
+
257
263
  ## Composio
258
264
 
259
265
  ```bash