@heyamiko/amiko-cli 0.8.0 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # @heyamiko/amiko-cli (v0.8.0)
1
+ # @heyamiko/amiko-cli (v0.8.1)
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
 
@@ -148,19 +148,20 @@ amiko swap send 0.5 USDC AMIKO --yes
148
148
  amiko swap send 100 AMIKO SOL --slippage 100
149
149
  ```
150
150
 
151
- Swaps execute via Jupiter on Solana. Supports SOL, USDC, USDT, AMIKO, PYUSD, BONK, JUP, RAY, JitoSOL, mSOL, or any mint address.
151
+ Swaps execute via Jupiter on Solana. Supports SOL, USDC, USDT, AMIKO, PYUSD, BONK, JUP, RAY, JitoSOL, mSOL, or any mint address. `swap send` is hard-gated: in a non-interactive shell you must pass `--yes` after quoting the cost to the user (`Solana gas + 10bps platform fee`).
152
152
 
153
153
  ## Bridge
154
154
 
155
155
  ```bash
156
156
  amiko bridge quote <amount> # cross-chain USDC quote via Across Protocol
157
- amiko bridge send <amount> # execute bridge transfer
157
+ amiko bridge send <amount> # execute bridge transfer (gated — requires --yes in non-TTY)
158
+ amiko bridge send <amount> --raw # print transaction JSON without sending (no gate)
158
159
  amiko bridge status <txHash> # check bridge status
159
160
  amiko bridge routes # available bridge routes
160
161
  amiko bridge limits # min/max limits per route
161
162
  ```
162
163
 
163
- Default route: Solana → Tempo. Use `--from` and `--to` flags for other chains.
164
+ Default route: Solana → Tempo. Use `--from` and `--to` flags for other chains. Like `swap send`, `bridge send` refuses to run in a non-interactive shell without `--yes` so agents must quote the Across fee + gas to the user before executing.
164
165
 
165
166
  ## MPP Services
166
167
 
@@ -191,7 +192,7 @@ Audio and image endpoints upload to Supabase Storage and return permanent URLs.
191
192
 
192
193
  ## Confirmation Gate for Paid Commands
193
194
 
194
- Every paid command (`image`, `search`, `amazon search`, `service tts`, `service stt`, `service chat`, `service call`, `credits topup`) requires explicit approval before spending:
195
+ Every paid / value-moving command (`image`, `search`, `amazon search`, `service tts`, `service stt`, `service chat`, `service call`, `credits topup`, `swap send`, `bridge send`) requires explicit approval before spending:
195
196
 
196
197
  - **Human, interactive shell** — the command prints the cost and prompts `Proceed? [y/N]`. Answer `y` to run.
197
198
  - **Agent / non-interactive shell** — the command *refuses* to run without `--yes`, with a message telling the agent what to do:
@@ -206,7 +207,7 @@ Every paid command (`image`, `search`, `amazon search`, `service tts`, `service
206
207
  amiko image "a sunset over mountains" --yes
207
208
  ```
208
209
 
209
- This makes cost confirmation effectively mandatory regardless of which model drives the agent — weak models can no longer silently burn tokens by ignoring the skill docs.
210
+ This makes cost confirmation effectively mandatory regardless of which model drives the agent — weak models can no longer silently burn tokens by ignoring the skill docs. The same refusal fires for `swap send` (Solana gas + 10 bps platform fee) and `bridge send` (Across fee + gas), so an agent cannot move tokens without a round-trip through user approval.
210
211
 
211
212
  ## Error Output
212
213
 
package/dist/index.js CHANGED
@@ -73750,6 +73750,69 @@ Run "amiko login" again to re-authenticate, or "amiko logout" to sign out.
73750
73750
  });
73751
73751
  }
73752
73752
 
73753
+ // src/lib/confirm.ts
73754
+ import readline from "node:readline";
73755
+ function promptYesNo(question) {
73756
+ const rl = readline.createInterface({
73757
+ input: process.stdin,
73758
+ output: process.stdout
73759
+ });
73760
+ return new Promise((resolve2) => {
73761
+ rl.question(question, (answer) => {
73762
+ rl.close();
73763
+ resolve2(/^y(es)?$/i.test(answer.trim()));
73764
+ });
73765
+ });
73766
+ }
73767
+ async function confirmDestructive(args) {
73768
+ if (args.yes)
73769
+ return;
73770
+ if (process.stdin.isTTY) {
73771
+ console.log("");
73772
+ console.log(warn(args.action));
73773
+ if (args.detail)
73774
+ console.log(dim(args.detail));
73775
+ const ok = await promptYesNo("Proceed? [y/N] ");
73776
+ if (!ok) {
73777
+ console.log(dim("Cancelled."));
73778
+ process.exit(0);
73779
+ }
73780
+ return;
73781
+ }
73782
+ console.error(error(`Refusing to run without --yes (non-interactive shell).`));
73783
+ console.error("");
73784
+ console.error(`Action: ${args.action}`);
73785
+ if (args.detail)
73786
+ console.error(dim(args.detail));
73787
+ console.error("");
73788
+ console.error(dim("Re-run with --yes to confirm:"));
73789
+ console.error(dim(` ${args.commandExample} --yes`));
73790
+ process.exit(2);
73791
+ }
73792
+ async function requireApproval(args) {
73793
+ if (args.yes)
73794
+ return;
73795
+ if (process.stdin.isTTY) {
73796
+ console.log("");
73797
+ console.log(`About to run: ${args.summary}`);
73798
+ console.log(`Cost: ${args.cost}`);
73799
+ const ok = await promptYesNo("Proceed? [y/N] ");
73800
+ if (!ok) {
73801
+ console.log(dim("Cancelled."));
73802
+ process.exit(0);
73803
+ }
73804
+ return;
73805
+ }
73806
+ console.error(error(`Refusing to run without --yes (non-interactive shell detected).`));
73807
+ console.error("");
73808
+ console.error(`This command will cost ${args.cost}.`);
73809
+ console.error(`Action: ${args.summary}`);
73810
+ console.error("");
73811
+ console.error(dim(`Quote the cost to the user, get their explicit approval, then re-run with --yes:`));
73812
+ console.error(dim(` ${args.commandExample} --yes`));
73813
+ process.exit(2);
73814
+ }
73815
+
73753
73816
  // src/lib/across.ts
73754
73817
  function getBridgeBase() {
73755
73818
  const config = loadConfig();
@@ -73969,7 +74032,7 @@ function registerBridgeCommand(program2) {
73969
74032
  process.exit(1);
73970
74033
  }
73971
74034
  });
73972
- cmd.command("send").description("Execute a USDC bridge transfer via Across").argument("<amount>", "Amount of USDC to bridge (e.g., 10, 5.50)").option("--from <chain>", "Origin chain (default: solana)", "solana").option("--to <chain>", "Destination chain (default: tempo)", "tempo").option("--recipient <address>", "Recipient address on destination chain").option("--depositor <address>", "Depositor address on origin chain").option("--raw", "Output raw transaction JSON for manual signing").action(async (amountStr, opts) => {
74035
+ cmd.command("send").description("Execute a USDC bridge transfer via Across").argument("<amount>", "Amount of USDC to bridge (e.g., 10, 5.50)").option("--from <chain>", "Origin chain (default: solana)", "solana").option("--to <chain>", "Destination chain (default: tempo)", "tempo").option("--recipient <address>", "Recipient address on destination chain").option("--depositor <address>", "Depositor address on origin chain").option("--raw", "Output raw transaction JSON for manual signing").option("--yes", "Skip confirmation (only after the user has approved)").action(async (amountStr, opts) => {
73973
74036
  const spinner = ora("Preparing bridge transaction...").start();
73974
74037
  try {
73975
74038
  const originChainId = resolveChainId(opts.from);
@@ -74009,6 +74072,14 @@ function registerBridgeCommand(program2) {
74009
74072
  if (quote.fees?.total) {
74010
74073
  console.log(label("Fee", `${formatUsdcAmount(quote.fees.total.amount)} USDC`));
74011
74074
  }
74075
+ if (!opts.raw) {
74076
+ await requireApproval({
74077
+ cost: quote.fees?.total ? `${formatUsdcAmount(quote.fees.total.amount)} USDC bridge fee + gas` : "gas + Across fee",
74078
+ summary: `bridge ${formatUsdcAmount(quote.inputAmount)} USDC ${originName} → ${destName}`,
74079
+ yes: opts.yes,
74080
+ commandExample: `amiko bridge send ${amountStr} --from ${opts.from} --to ${opts.to}`
74081
+ });
74082
+ }
74012
74083
  if (opts.raw) {
74013
74084
  console.log("");
74014
74085
  console.log(heading("Transaction Data"));
@@ -74412,7 +74483,7 @@ Tip: you can also generate a connect link from your Amiko settings`));
74412
74483
  }
74413
74484
 
74414
74485
  // src/commands/credits.ts
74415
- import readline from "node:readline";
74486
+ import readline2 from "node:readline";
74416
74487
 
74417
74488
  // src/lib/mpp-topup.ts
74418
74489
  async function mppCreditsTopup(params) {
@@ -74475,11 +74546,11 @@ async function mppCreditsTopup(params) {
74475
74546
  }
74476
74547
 
74477
74548
  // src/commands/credits.ts
74478
- function promptYesNo(question) {
74549
+ function promptYesNo2(question) {
74479
74550
  if (!process.stdin.isTTY) {
74480
74551
  return Promise.resolve(false);
74481
74552
  }
74482
- const rl = readline.createInterface({
74553
+ const rl = readline2.createInterface({
74483
74554
  input: process.stdin,
74484
74555
  output: process.stdout
74485
74556
  });
@@ -74717,7 +74788,7 @@ function registerCreditsCommand(program2) {
74717
74788
  console.log(label("USD", `$${usdAmount.toFixed(2)}`));
74718
74789
  console.log(dim("Charged via MPP connected key."));
74719
74790
  console.log("");
74720
- const ok = await promptYesNo("Proceed? [y/N] ");
74791
+ const ok = await promptYesNo2("Proceed? [y/N] ");
74721
74792
  if (!ok) {
74722
74793
  console.log(dim("Top-up cancelled."));
74723
74794
  process.exit(0);
@@ -74761,7 +74832,7 @@ function registerCreditsCommand(program2) {
74761
74832
  console.log(dim("Tokens will be transferred from your agent wallet to the treasury."));
74762
74833
  console.log("");
74763
74834
  if (!options.yes) {
74764
- const confirmed = await promptYesNo("Proceed? [y/N] ");
74835
+ const confirmed = await promptYesNo2("Proceed? [y/N] ");
74765
74836
  if (!confirmed) {
74766
74837
  console.log(dim("Top-up cancelled."));
74767
74838
  process.exit(0);
@@ -75006,20 +75077,12 @@ function registerSwapCommand(program2) {
75006
75077
  console.log(label("Route", dim(route)));
75007
75078
  console.log(label("Wallet", truncAddr(wallet)));
75008
75079
  console.log("");
75009
- const isInteractive2 = process.stdin.isTTY && !opts.yes;
75010
- if (isInteractive2) {
75011
- const readline2 = await import("readline");
75012
- const rl = readline2.createInterface({
75013
- input: process.stdin,
75014
- output: process.stdout
75015
- });
75016
- const answer = await new Promise((resolve2) => rl.question("Confirm swap? (y/N) ", resolve2));
75017
- rl.close();
75018
- if (answer.toLowerCase() !== "y") {
75019
- console.log(dim("Cancelled."));
75020
- process.exit(0);
75021
- }
75022
- }
75080
+ await requireApproval({
75081
+ cost: "Solana gas + 10bps platform fee",
75082
+ summary: `swap ${amountStr} ${fromToken.toUpperCase()} → ~${(Number(quote.outAmount) / 10 ** output.decimals).toFixed(6)} ${output.symbol}`,
75083
+ yes: opts.yes,
75084
+ commandExample: `amiko swap send ${amountStr} ${fromToken} ${toToken}`
75085
+ });
75023
75086
  const swapSpinner = ora("Executing swap via Crossmint...").start();
75024
75087
  const billingToken = auth.platformKey || auth.twinToken || auth.privyToken;
75025
75088
  const billingBase = auth.billingBase || "https://billing.heyamiko.com";
@@ -75236,69 +75299,6 @@ async function reportHttpError(res, label2) {
75236
75299
  console.error(String(pretty).slice(0, 2000));
75237
75300
  }
75238
75301
 
75239
- // src/lib/confirm.ts
75240
- import readline2 from "node:readline";
75241
- function promptYesNo2(question) {
75242
- const rl = readline2.createInterface({
75243
- input: process.stdin,
75244
- output: process.stdout
75245
- });
75246
- return new Promise((resolve2) => {
75247
- rl.question(question, (answer) => {
75248
- rl.close();
75249
- resolve2(/^y(es)?$/i.test(answer.trim()));
75250
- });
75251
- });
75252
- }
75253
- async function confirmDestructive(args) {
75254
- if (args.yes)
75255
- return;
75256
- if (process.stdin.isTTY) {
75257
- console.log("");
75258
- console.log(warn(args.action));
75259
- if (args.detail)
75260
- console.log(dim(args.detail));
75261
- const ok = await promptYesNo2("Proceed? [y/N] ");
75262
- if (!ok) {
75263
- console.log(dim("Cancelled."));
75264
- process.exit(0);
75265
- }
75266
- return;
75267
- }
75268
- console.error(error(`Refusing to run without --yes (non-interactive shell).`));
75269
- console.error("");
75270
- console.error(`Action: ${args.action}`);
75271
- if (args.detail)
75272
- console.error(dim(args.detail));
75273
- console.error("");
75274
- console.error(dim("Re-run with --yes to confirm:"));
75275
- console.error(dim(` ${args.commandExample} --yes`));
75276
- process.exit(2);
75277
- }
75278
- async function requireApproval(args) {
75279
- if (args.yes)
75280
- return;
75281
- if (process.stdin.isTTY) {
75282
- console.log("");
75283
- console.log(`About to run: ${args.summary}`);
75284
- console.log(`Cost: ${args.cost}`);
75285
- const ok = await promptYesNo2("Proceed? [y/N] ");
75286
- if (!ok) {
75287
- console.log(dim("Cancelled."));
75288
- process.exit(0);
75289
- }
75290
- return;
75291
- }
75292
- console.error(error(`Refusing to run without --yes (non-interactive shell detected).`));
75293
- console.error("");
75294
- console.error(`This command will cost ${args.cost}.`);
75295
- console.error(`Action: ${args.summary}`);
75296
- console.error("");
75297
- console.error(dim(`Quote the cost to the user, get their explicit approval, then re-run with --yes:`));
75298
- console.error(dim(` ${args.commandExample} --yes`));
75299
- process.exit(2);
75300
- }
75301
-
75302
75302
  // src/commands/search.ts
75303
75303
  var TREASURY = "FPiZAf3jEnNfCwfvbv46jdPfHbmSepD4Q6iHiLJDQXvb";
75304
75304
  function registerSearchCommand(program2) {
@@ -76025,10 +76025,10 @@ async function amikoWebFetch(auth, path2, opts = {}) {
76025
76025
  function formatError2(status, body, path2) {
76026
76026
  const prefix = `${status} ${path2}`;
76027
76027
  if (status === 401) {
76028
- return `${prefix}: unauthorized. Run \`amiko connect\` to refresh your session.`;
76028
+ return `${prefix}: unauthorized. The stored token is invalid or expired; re-link this device to Amiko.`;
76029
76029
  }
76030
76030
  if (status === 403) {
76031
- return `${prefix}: forbidden. The twin token does not have access to this resource.`;
76031
+ return `${prefix}: forbidden. The current auth context does not have access to this resource.`;
76032
76032
  }
76033
76033
  if (!body)
76034
76034
  return prefix;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@heyamiko/amiko-cli",
3
- "version": "0.8.0",
3
+ "version": "0.8.1",
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
@@ -15,7 +15,7 @@ The `amiko` CLI is installed globally. Auth and payments are automatic via `.ami
15
15
 
16
16
  ## Critical Rules
17
17
 
18
- 1. **Every paid command is hard-gated on `--yes` in a non-interactive shell.** The CLI will *refuse* to run `image`, `search`, `amazon search`, `service call`, `service tts`, `service stt`, `service chat`, or `credits topup` unless you pass `--yes`. The refusal is loud: it prints the cost and the command you should re-run with `--yes` appended. This means: **you must first quote the cost to the user, get their explicit approval, and only then append `--yes`**. If the user didn't approve, do not add `--yes`.
18
+ 1. **Every paid / value-moving command is hard-gated on `--yes` in a non-interactive shell.** The CLI will *refuse* to run `image`, `search`, `amazon search`, `service call`, `service tts`, `service stt`, `service chat`, `credits topup`, `swap send`, or `bridge send` unless you pass `--yes`. The refusal is loud: it prints the cost and the command you should re-run with `--yes` appended. This means: **you must first quote the cost to the user, get their explicit approval, and only then append `--yes`**. If the user didn't approve, do not add `--yes`. The same applies to destructive non-paid commands (`twin:update --public`, `docs:delete`, `friends:remove`, `avatar:update`, `voice:reset`, `composio:disconnect`).
19
19
  2. **After every paid command, report the remaining balance to the user.** The CLI prints a `Balance: N credits (~$X.XX)` line at the end of every paid command — include that figure in your reply so the user knows what they have left.
20
20
  3. **Never retry a failed command.** If a command returns an error, report it and stop. Every paid call costs tokens even on failure.
21
21
  4. **Never suggest `amiko login` or `amiko connect`.** These commands don't exist. Auth is automatic via `.amiko.json`.