@heyamiko/amiko-cli 0.12.0-beta.8 → 0.14.0-beta.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 +12 -5
- package/dist/index.js +120 -79
- package/package.json +1 -1
- package/skills/SKILL.md +3 -1
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# @heyamiko/amiko-cli (v0.
|
|
1
|
+
# @heyamiko/amiko-cli (v0.14.0-beta.0)
|
|
2
2
|
|
|
3
3
|
Manage wallets, credits, swaps, MPP marketplace services, and your Amiko twin (identity, documents, voice, avatar, friends, feed) from the terminal. Works for both human users and AI agents running on OpenClaw.
|
|
4
4
|
|
|
@@ -47,6 +47,7 @@ amiko wallets swap send 1.0 SOL USDC --yes
|
|
|
47
47
|
# Call any MPP service
|
|
48
48
|
amiko markets service list
|
|
49
49
|
amiko markets service call POST /v1/sfx '{"text":"thunder","duration_seconds":5}'
|
|
50
|
+
amiko markets service call GET /v1/x/search?q=ai --usdt auto # pay any endpoint in USDT
|
|
50
51
|
```
|
|
51
52
|
|
|
52
53
|
## Credits
|
|
@@ -93,12 +94,13 @@ amiko credits topup 10000 --wallet Gu2b...QVya # specific wallet (else de
|
|
|
93
94
|
## Search
|
|
94
95
|
|
|
95
96
|
```bash
|
|
96
|
-
amiko markets search "AI agents" # search X/Twitter (
|
|
97
|
-
amiko markets search "bitcoin" --token
|
|
97
|
+
amiko markets search "AI agents" # search X/Twitter ($0.01 in AMIKO at spot)
|
|
98
|
+
amiko markets search "bitcoin" --token USDT # pay 0.01 USDT directly (no swap)
|
|
99
|
+
amiko markets search "solana" --token USDC # or USDC
|
|
98
100
|
amiko markets search "news" --raw # raw JSON output
|
|
99
101
|
```
|
|
100
102
|
|
|
101
|
-
|
|
103
|
+
Each search is anchored at $0.01. The CLI quotes the real token amount before the approval gate — AMIKO converts at the live platform spot; USDC/USDT are pinned at $1.00 so the amount equals the USD price. Payment is a Crossmint wallet transfer from your Solana wallet; the tx rides to MPP as `X-Payment-Tx` + `X-Payment-Token`.
|
|
102
104
|
|
|
103
105
|
## Image Generation
|
|
104
106
|
|
|
@@ -253,7 +255,8 @@ amiko card mint <slug> --status # check an existing mint
|
|
|
253
255
|
## Amazon
|
|
254
256
|
|
|
255
257
|
```bash
|
|
256
|
-
amiko markets amazon search "usb c cable" # product search (
|
|
258
|
+
amiko markets amazon search "usb c cable" # product search ($0.01)
|
|
259
|
+
amiko markets amazon search "ssd" --token USDT # pay in USDT directly
|
|
257
260
|
amiko markets amazon quote B01GGKYKQM # price quote (free)
|
|
258
261
|
```
|
|
259
262
|
|
|
@@ -525,6 +528,10 @@ npm publish
|
|
|
525
528
|
|
|
526
529
|
## Changelog
|
|
527
530
|
|
|
531
|
+
### 0.14.0-beta.0
|
|
532
|
+
|
|
533
|
+
- **Direct USDC/USDT payment for MPP marketplace services (Bug #9).** `markets search`, `markets image`, and `markets amazon search` gain `--token <AMIKO|USDC|USDT>`; `markets service call` completes its `--usdc` path and adds `--usdt` (both accept `auto` to price from the endpoint's USD anchor). Stables are pinned at $1.00 — no forced swap to AMIKO and no credits conversion. Requires the matching mpp-service deploy: `charge()`/`chargeAmiko()` now read `X-Payment-Token` and verify the on-chain transfer against that token's mint (`verifySplTransfer`), with a ±0.5% tolerance for stables (±5% stays for AMIKO spot drift). Also fixes stale hardcoded amounts: `markets search` / `amazon search` paid a flat "1 AMIKO" regardless of spot — under current prices that under-pays the $0.01 anchor and fails verification; amounts are now quoted live before the approval gate, which shows the real cost instead of "1 AMIKO". Shared payment plumbing extracted to `src/lib/treasury-payment.ts` (one TREASURY constant, one transfer path).
|
|
534
|
+
|
|
528
535
|
### 0.11.0-beta.3
|
|
529
536
|
|
|
530
537
|
- **`amiko composio` removed (breaking).** The Composio OAuth-connection commands (`composio connect`, `composio disconnect`) are gone — `src/commands/composio.ts` deleted, `composio` command group dropped from `src/index.ts`. SKILL.md frontmatter, command-groups list, `--yes`-gated list, and the dedicated `## Composio` block are all removed; README's lead paragraph, architecture tree, destructive-ops list, Platform-table row, and Composio code-block example are updated to match. Scripts that called `amiko composio ...` will now error with "unknown command".
|
package/dist/index.js
CHANGED
|
@@ -28306,15 +28306,60 @@ async function reportHttpError(res, label2) {
|
|
|
28306
28306
|
console.error(String(pretty).slice(0, 2000));
|
|
28307
28307
|
}
|
|
28308
28308
|
|
|
28309
|
-
// src/
|
|
28309
|
+
// src/lib/treasury-payment.ts
|
|
28310
28310
|
var TREASURY = "FPiZAf3jEnNfCwfvbv46jdPfHbmSepD4Q6iHiLJDQXvb";
|
|
28311
|
+
var PAYMENT_TOKENS = ["AMIKO", "USDC", "USDT"];
|
|
28312
|
+
function normalizePaymentToken(raw) {
|
|
28313
|
+
const sym = (raw ?? "AMIKO").trim().toUpperCase();
|
|
28314
|
+
if (!PAYMENT_TOKENS.includes(sym)) {
|
|
28315
|
+
throw new Error(`Unsupported payment token "${raw}". Supported: ${PAYMENT_TOKENS.join(", ")}.`);
|
|
28316
|
+
}
|
|
28317
|
+
return sym;
|
|
28318
|
+
}
|
|
28319
|
+
async function quoteTokenAmount(auth, token, usd2) {
|
|
28320
|
+
if (token === "USDC" || token === "USDT") {
|
|
28321
|
+
return { amount: usd2.toFixed(6), priceUsd: 1 };
|
|
28322
|
+
}
|
|
28323
|
+
const priceUsd = await getTokenUsdPrice(auth, "AMIKO");
|
|
28324
|
+
if (!priceUsd || priceUsd <= 0) {
|
|
28325
|
+
throw new Error("Could not fetch AMIKO/USD price from the Amiko platform. Try again in a moment, or pay with --token USDT/USDC.");
|
|
28326
|
+
}
|
|
28327
|
+
const amount2 = (usd2 / priceUsd * 1.01).toFixed(6);
|
|
28328
|
+
return { amount: amount2, priceUsd };
|
|
28329
|
+
}
|
|
28330
|
+
async function payTreasury(auth, args) {
|
|
28331
|
+
const transferData = await amikoWebFetch(auth, `/api/agents/${encodeURIComponent(auth.twinId)}/wallets/${encodeURIComponent(args.wallet)}/transfer`, {
|
|
28332
|
+
method: "POST",
|
|
28333
|
+
body: { to: TREASURY, amount: args.amount, token: args.token, chain: "solana" }
|
|
28334
|
+
});
|
|
28335
|
+
if (!transferData.txHash)
|
|
28336
|
+
throw new Error("Payment sent but no tx hash returned.");
|
|
28337
|
+
return transferData.txHash;
|
|
28338
|
+
}
|
|
28339
|
+
|
|
28340
|
+
// src/commands/search.ts
|
|
28341
|
+
var SEARCH_USD = 0.01;
|
|
28311
28342
|
function registerSearchCommand(program2) {
|
|
28312
|
-
program2.command("search <query>").description("X Search —
|
|
28343
|
+
program2.command("search <query>").description("X Search — $0.01 per search, paid in AMIKO, USDC, or USDT").option("--raw", "Output raw JSON").option("--token <token>", "Payment token (AMIKO, USDC, USDT)", "AMIKO").option("--wallet <address>", "Wallet to pay from (defaults to your Solana wallet — run `amiko wallets create --chain solana` if you don't have one)").option("--yes", "Skip the pre-spend confirmation (required in non-interactive shells)").action(async (query, opts) => {
|
|
28344
|
+
let token;
|
|
28345
|
+
try {
|
|
28346
|
+
token = normalizePaymentToken(opts.token);
|
|
28347
|
+
} catch (e5) {
|
|
28348
|
+
console.error(error(e5 instanceof Error ? e5.message : String(e5)));
|
|
28349
|
+
process.exit(1);
|
|
28350
|
+
}
|
|
28351
|
+
let quote;
|
|
28352
|
+
try {
|
|
28353
|
+
quote = await quoteTokenAmount(resolveAuth(), token, SEARCH_USD);
|
|
28354
|
+
} catch (e5) {
|
|
28355
|
+
console.error(error(e5 instanceof Error ? e5.message : String(e5)));
|
|
28356
|
+
process.exit(1);
|
|
28357
|
+
}
|
|
28313
28358
|
await requireApproval({
|
|
28314
|
-
cost:
|
|
28359
|
+
cost: `${quote.amount} ${token} (≈ $${SEARCH_USD.toFixed(2)})`,
|
|
28315
28360
|
summary: `X/Twitter search: ${query.slice(0, 80)}${query.length > 80 ? "…" : ""}`,
|
|
28316
28361
|
yes: opts.yes,
|
|
28317
|
-
commandExample: `amiko markets search ${JSON.stringify(query)}${
|
|
28362
|
+
commandExample: `amiko markets search ${JSON.stringify(query)}${token !== "AMIKO" ? ` --token ${token}` : ""}`
|
|
28318
28363
|
});
|
|
28319
28364
|
const spinner = ora("X Search...").start();
|
|
28320
28365
|
try {
|
|
@@ -28328,9 +28373,7 @@ function registerSearchCommand(program2) {
|
|
|
28328
28373
|
process.exit(1);
|
|
28329
28374
|
}
|
|
28330
28375
|
const mppBase2 = (config2.apiBase || "https://mpp.amikomarkets.com").replace(/\/$/, "");
|
|
28331
|
-
const
|
|
28332
|
-
const chain = "solana";
|
|
28333
|
-
const amount2 = token === "AMIKO" ? "1" : "0.01";
|
|
28376
|
+
const amount2 = quote.amount;
|
|
28334
28377
|
let wallet;
|
|
28335
28378
|
try {
|
|
28336
28379
|
wallet = await resolveWalletAddress(auth, opts.wallet, "solana");
|
|
@@ -28339,19 +28382,10 @@ function registerSearchCommand(program2) {
|
|
|
28339
28382
|
console.error(error(e5 instanceof Error ? e5.message : String(e5)));
|
|
28340
28383
|
process.exit(1);
|
|
28341
28384
|
}
|
|
28342
|
-
spinner.text = `Paying ${amount2} ${token} (
|
|
28385
|
+
spinner.text = `Paying ${amount2} ${token} (solana)...`;
|
|
28343
28386
|
let txHash;
|
|
28344
28387
|
try {
|
|
28345
|
-
|
|
28346
|
-
method: "POST",
|
|
28347
|
-
body: { to: TREASURY, amount: amount2, token, chain }
|
|
28348
|
-
});
|
|
28349
|
-
if (!transferData.txHash) {
|
|
28350
|
-
spinner.stop();
|
|
28351
|
-
console.error(error("Payment sent but no tx hash returned."));
|
|
28352
|
-
process.exit(1);
|
|
28353
|
-
}
|
|
28354
|
-
txHash = transferData.txHash;
|
|
28388
|
+
txHash = await payTreasury(auth, { wallet, amount: amount2, token });
|
|
28355
28389
|
} catch (e5) {
|
|
28356
28390
|
spinner.stop();
|
|
28357
28391
|
console.error(error(e5 instanceof Error ? e5.message : `${token} payment failed`));
|
|
@@ -28408,7 +28442,6 @@ function registerSearchCommand(program2) {
|
|
|
28408
28442
|
}
|
|
28409
28443
|
|
|
28410
28444
|
// src/commands/image.ts
|
|
28411
|
-
var TREASURY2 = "FPiZAf3jEnNfCwfvbv46jdPfHbmSepD4Q6iHiLJDQXvb";
|
|
28412
28445
|
var IMAGE_MARKUP = 1.3;
|
|
28413
28446
|
var IMAGE_PRICING = {
|
|
28414
28447
|
"gpt-image-2": { low: { sq: 0.006, lg: 0.005 }, medium: { sq: 0.053, lg: 0.041 }, high: { sq: 0.211, lg: 0.165 } },
|
|
@@ -28426,7 +28459,7 @@ function computeImageUsd(model, quality, size3, n) {
|
|
|
28426
28459
|
return Number((pricing[q][tier] * clamped * IMAGE_MARKUP).toFixed(6));
|
|
28427
28460
|
}
|
|
28428
28461
|
function registerImageCommand(program2) {
|
|
28429
|
-
program2.command("image <prompt>").alias("img").description("Generate an image — pays in AMIKO at OpenAI pass-through pricing × 1.30 (cost varies by model/quality/size)").option("--size <size>", "Image size: 1024x1024, 1024x1536, 1536x1024", "1024x1024").option("--quality <q>", "Quality: low, medium, high, auto", "high").option("--model <model>", `Model: ${SUPPORTED_MODELS.join(", ")}`, DEFAULT_IMAGE_MODEL).option("--n <count>", "Number of images (1–4)", "1").option("--background <bg>", "Background: transparent, opaque, auto").option("--format <fmt>", "Output: png, webp, jpeg", "png").option("--wallet <address>", "Wallet to pay from (defaults to your Solana wallet — run `amiko wallets create --chain solana` if you don't have one)").option("--raw", "Output raw JSON").option("--yes", "Skip the pre-spend confirmation (required in non-interactive shells)").action(async (prompt, opts) => {
|
|
28462
|
+
program2.command("image <prompt>").alias("img").description("Generate an image — pays in AMIKO at OpenAI pass-through pricing × 1.30 (cost varies by model/quality/size)").option("--size <size>", "Image size: 1024x1024, 1024x1536, 1536x1024", "1024x1024").option("--quality <q>", "Quality: low, medium, high, auto", "high").option("--model <model>", `Model: ${SUPPORTED_MODELS.join(", ")}`, DEFAULT_IMAGE_MODEL).option("--n <count>", "Number of images (1–4)", "1").option("--background <bg>", "Background: transparent, opaque, auto").option("--format <fmt>", "Output: png, webp, jpeg", "png").option("--wallet <address>", "Wallet to pay from (defaults to your Solana wallet — run `amiko wallets create --chain solana` if you don't have one)").option("--token <token>", "Payment token (AMIKO, USDC, USDT)", "AMIKO").option("--raw", "Output raw JSON").option("--yes", "Skip the pre-spend confirmation (required in non-interactive shells)").action(async (prompt, opts) => {
|
|
28430
28463
|
if (!SUPPORTED_MODELS.includes(opts.model)) {
|
|
28431
28464
|
console.error(error(`Unknown model "${opts.model}". Supported: ${SUPPORTED_MODELS.join(", ")}.`));
|
|
28432
28465
|
process.exit(1);
|
|
@@ -28461,14 +28494,23 @@ function registerImageCommand(program2) {
|
|
|
28461
28494
|
console.error(error(e5 instanceof Error ? e5.message : "auth failed"));
|
|
28462
28495
|
process.exit(1);
|
|
28463
28496
|
}
|
|
28464
|
-
|
|
28465
|
-
|
|
28466
|
-
|
|
28497
|
+
let payToken;
|
|
28498
|
+
try {
|
|
28499
|
+
payToken = normalizePaymentToken(opts.token);
|
|
28500
|
+
} catch (e5) {
|
|
28501
|
+
console.error(error(e5 instanceof Error ? e5.message : String(e5)));
|
|
28467
28502
|
process.exit(1);
|
|
28468
28503
|
}
|
|
28469
|
-
|
|
28504
|
+
let quote;
|
|
28505
|
+
try {
|
|
28506
|
+
quote = await quoteTokenAmount(auth, payToken, usdCost);
|
|
28507
|
+
} catch (e5) {
|
|
28508
|
+
console.error(error(e5 instanceof Error ? e5.message : String(e5)));
|
|
28509
|
+
process.exit(1);
|
|
28510
|
+
}
|
|
28511
|
+
const payAmount = quote.amount;
|
|
28470
28512
|
await requireApproval({
|
|
28471
|
-
cost: `${
|
|
28513
|
+
cost: `${payAmount} ${payToken} (≈ $${usdCost.toFixed(4)} @ $${quote.priceUsd.toFixed(6)}/${payToken}, ${opts.model} ${opts.quality} ${opts.size}${n > 1 ? ` ×${n}` : ""})`,
|
|
28472
28514
|
summary: `Generate image: ${prompt.slice(0, 80)}${prompt.length > 80 ? "…" : ""}`,
|
|
28473
28515
|
yes: opts.yes,
|
|
28474
28516
|
commandExample: `amiko markets image ${JSON.stringify(prompt)}`
|
|
@@ -28512,19 +28554,10 @@ function registerImageCommand(program2) {
|
|
|
28512
28554
|
console.error(error(e5 instanceof Error ? e5.message : String(e5)));
|
|
28513
28555
|
process.exit(1);
|
|
28514
28556
|
}
|
|
28515
|
-
setStatus(`Paying ${
|
|
28557
|
+
setStatus(`Paying ${payAmount} ${payToken}...`);
|
|
28516
28558
|
let txHash;
|
|
28517
28559
|
try {
|
|
28518
|
-
|
|
28519
|
-
method: "POST",
|
|
28520
|
-
body: { to: TREASURY2, amount: amikoAmount, token: "AMIKO", chain: "solana" }
|
|
28521
|
-
});
|
|
28522
|
-
if (!transferData.txHash) {
|
|
28523
|
-
stopProgress();
|
|
28524
|
-
console.error(error("Payment sent but no tx hash returned."));
|
|
28525
|
-
process.exit(1);
|
|
28526
|
-
}
|
|
28527
|
-
txHash = transferData.txHash;
|
|
28560
|
+
txHash = await payTreasury(auth, { wallet, amount: payAmount, token: payToken });
|
|
28528
28561
|
} catch (e5) {
|
|
28529
28562
|
stopProgress();
|
|
28530
28563
|
console.error(error(e5 instanceof Error ? e5.message : "Payment failed"));
|
|
@@ -28536,6 +28569,7 @@ function registerImageCommand(program2) {
|
|
|
28536
28569
|
headers: {
|
|
28537
28570
|
"Content-Type": "application/json",
|
|
28538
28571
|
"X-Payment-Tx": txHash,
|
|
28572
|
+
"X-Payment-Token": payToken,
|
|
28539
28573
|
"X-Twin-Id": auth.twinId
|
|
28540
28574
|
},
|
|
28541
28575
|
body: JSON.stringify({
|
|
@@ -28578,7 +28612,7 @@ function registerImageCommand(program2) {
|
|
|
28578
28612
|
}
|
|
28579
28613
|
}
|
|
28580
28614
|
console.log("");
|
|
28581
|
-
console.log(success(`Paid ${
|
|
28615
|
+
console.log(success(`Paid ${payAmount} ${payToken} (≈ $${usdCost.toFixed(4)})`) + dim(` tx: ${txHash.slice(0, 16)}...`));
|
|
28582
28616
|
await printBalanceRemaining(auth);
|
|
28583
28617
|
} catch (err) {
|
|
28584
28618
|
stopProgress();
|
|
@@ -28742,7 +28776,7 @@ function registerCreateCommand(create2) {
|
|
|
28742
28776
|
}
|
|
28743
28777
|
});
|
|
28744
28778
|
});
|
|
28745
|
-
create2.command("music [prompt]").description("Generate music (Create Studio)").option("--model <model>", "music_v1, music-2.6, music-cover", "music-2.6").option("--lyrics <lyrics>", "Lyrics (optional)").option("--duration <ms>", "Length in ms").option("--instrumental", "Instrumental (no vocals)").option("--token <symbol>", "Preferred charge token: AMIKO, USDC, USDT, SOL").option("--raw", "Output raw JSON").option("--yes", "Skip the pre-spend confirmation (required in non-interactive shells)").action(async (prompt, opts) => {
|
|
28779
|
+
create2.command("music [prompt]").description("Generate music (Create Studio)").option("--model <model>", "music_v1, music-2.6, music-cover", "music-2.6").option("--lyrics <lyrics>", "Lyrics (optional)").option("--duration <ms>", "Length in ms").option("--instrumental", "Instrumental (no vocals)").option("--lyrics-optimizer", "Auto-write lyrics from the prompt (music_v1 only; on by default when no --lyrics/--instrumental)").option("--token <symbol>", "Preferred charge token: AMIKO, USDC, USDT, SOL").option("--raw", "Output raw JSON").option("--yes", "Skip the pre-spend confirmation (required in non-interactive shells)").action(async (prompt, opts) => {
|
|
28746
28780
|
if (!prompt && !opts.lyrics) {
|
|
28747
28781
|
console.error(error("Provide a prompt or --lyrics."));
|
|
28748
28782
|
process.exit(1);
|
|
@@ -28760,6 +28794,7 @@ function registerCreateCommand(create2) {
|
|
|
28760
28794
|
...opts.lyrics ? { lyrics: opts.lyrics } : {},
|
|
28761
28795
|
...opts.duration ? { durationMs: Number(opts.duration) } : {},
|
|
28762
28796
|
...opts.instrumental ? { isInstrumental: true } : {},
|
|
28797
|
+
...opts.lyricsOptimizer ? { lyricsOptimizer: true } : {},
|
|
28763
28798
|
...opts.token ? { preferredToken: opts.token } : {}
|
|
28764
28799
|
}
|
|
28765
28800
|
});
|
|
@@ -29407,24 +29442,29 @@ function registerCardCommand(card) {
|
|
|
29407
29442
|
}
|
|
29408
29443
|
|
|
29409
29444
|
// src/commands/amazon.ts
|
|
29410
|
-
var
|
|
29411
|
-
async function payAmiko(auth, wallet, amount2) {
|
|
29412
|
-
const data = await amikoWebFetch(auth, `/api/agents/${encodeURIComponent(auth.twinId)}/wallets/${encodeURIComponent(wallet)}/transfer`, {
|
|
29413
|
-
method: "POST",
|
|
29414
|
-
body: { to: TREASURY3, amount: amount2, token: "AMIKO", chain: "solana" }
|
|
29415
|
-
});
|
|
29416
|
-
if (!data.txHash)
|
|
29417
|
-
throw new Error("No tx hash returned.");
|
|
29418
|
-
return data.txHash;
|
|
29419
|
-
}
|
|
29445
|
+
var AMAZON_SEARCH_USD = 0.01;
|
|
29420
29446
|
function registerAmazonCommand(program2) {
|
|
29421
29447
|
const cmd = program2.command("amazon").description("Search and buy products on Amazon — paid with AMIKO");
|
|
29422
|
-
cmd.command("search <query>").description("Search Amazon products —
|
|
29448
|
+
cmd.command("search <query>").description("Search Amazon products — $0.01, paid in AMIKO, USDC, or USDT").option("--limit <n>", "Number of results", "5").option("--wallet <address>", "Wallet to pay from (defaults to your Solana wallet — run `amiko wallets create --chain solana` if you don't have one)").option("--token <token>", "Payment token (AMIKO, USDC, USDT)", "AMIKO").option("--raw", "Output raw JSON").option("--yes", "Skip the pre-spend confirmation (required in non-interactive shells)").action(async (query, opts) => {
|
|
29449
|
+
let payToken;
|
|
29450
|
+
try {
|
|
29451
|
+
payToken = normalizePaymentToken(opts.token);
|
|
29452
|
+
} catch (e5) {
|
|
29453
|
+
console.error(error(e5 instanceof Error ? e5.message : String(e5)));
|
|
29454
|
+
process.exit(1);
|
|
29455
|
+
}
|
|
29456
|
+
let quote;
|
|
29457
|
+
try {
|
|
29458
|
+
quote = await quoteTokenAmount(resolveAuth(), payToken, AMAZON_SEARCH_USD);
|
|
29459
|
+
} catch (e5) {
|
|
29460
|
+
console.error(error(e5 instanceof Error ? e5.message : String(e5)));
|
|
29461
|
+
process.exit(1);
|
|
29462
|
+
}
|
|
29423
29463
|
await requireApproval({
|
|
29424
|
-
cost:
|
|
29464
|
+
cost: `${quote.amount} ${payToken} (≈ $${AMAZON_SEARCH_USD.toFixed(2)})`,
|
|
29425
29465
|
summary: `Amazon product search: ${query.slice(0, 80)}${query.length > 80 ? "…" : ""}`,
|
|
29426
29466
|
yes: opts.yes,
|
|
29427
|
-
commandExample: `amiko markets amazon search ${JSON.stringify(query)}`
|
|
29467
|
+
commandExample: `amiko markets amazon search ${JSON.stringify(query)}${payToken !== "AMIKO" ? ` --token ${payToken}` : ""}`
|
|
29428
29468
|
});
|
|
29429
29469
|
const spinner = ora("Searching Amazon...").start();
|
|
29430
29470
|
try {
|
|
@@ -29446,14 +29486,14 @@ function registerAmazonCommand(program2) {
|
|
|
29446
29486
|
console.error(error(e5 instanceof Error ? e5.message : String(e5)));
|
|
29447
29487
|
process.exit(1);
|
|
29448
29488
|
}
|
|
29449
|
-
spinner.text =
|
|
29450
|
-
const txHash = await
|
|
29489
|
+
spinner.text = `Paying ${quote.amount} ${payToken}...`;
|
|
29490
|
+
const txHash = await payTreasury(auth, { wallet, amount: quote.amount, token: payToken });
|
|
29451
29491
|
spinner.text = "Searching Amazon...";
|
|
29452
29492
|
const params = new URLSearchParams({
|
|
29453
29493
|
q: `site:amazon.com ${query}`
|
|
29454
29494
|
});
|
|
29455
29495
|
const res = await fetch(`${mppBase2}/v1/search/google_shopping?${params}`, {
|
|
29456
|
-
headers: { "X-Payment-Tx": txHash, "X-Twin-Id": auth.twinId }
|
|
29496
|
+
headers: { "X-Payment-Tx": txHash, "X-Payment-Token": payToken, "X-Twin-Id": auth.twinId }
|
|
29457
29497
|
});
|
|
29458
29498
|
spinner.stop();
|
|
29459
29499
|
if (!res.ok) {
|
|
@@ -29550,52 +29590,52 @@ function registerAmazonCommand(program2) {
|
|
|
29550
29590
|
}
|
|
29551
29591
|
|
|
29552
29592
|
// src/commands/services.ts
|
|
29553
|
-
var TREASURY4 = "FPiZAf3jEnNfCwfvbv46jdPfHbmSepD4Q6iHiLJDQXvb";
|
|
29554
29593
|
function usdToAmiko(usd2, pricePerAmiko) {
|
|
29555
29594
|
if (pricePerAmiko <= 0)
|
|
29556
29595
|
return "1";
|
|
29557
29596
|
const amount2 = usd2 / pricePerAmiko;
|
|
29558
29597
|
return Math.ceil(amount2 * 100) / 100 + "";
|
|
29559
29598
|
}
|
|
29560
|
-
async function payAndCall(mppBase2, method, path2, auth, wallet, amountOrAuto, body, onProgress,
|
|
29599
|
+
async function payAndCall(mppBase2, method, path2, auth, wallet, amountOrAuto, body, onProgress, rawToken = "AMIKO") {
|
|
29600
|
+
const token = normalizePaymentToken(rawToken);
|
|
29561
29601
|
if (!path2.startsWith("/")) {
|
|
29562
29602
|
throw new Error(`path must start with "/" — got: ${path2}`);
|
|
29563
29603
|
}
|
|
29564
29604
|
let amount2 = amountOrAuto;
|
|
29565
29605
|
if (amount2 === "auto") {
|
|
29566
29606
|
onProgress?.("Fetching price...");
|
|
29607
|
+
let usdPrice = 0;
|
|
29567
29608
|
try {
|
|
29568
29609
|
const specRes = await fetchWithTimeout2(`${mppBase2}/openapi.json`, {}, 15000);
|
|
29569
29610
|
if (specRes.ok) {
|
|
29570
29611
|
const spec = await specRes.json();
|
|
29571
29612
|
const pathSpec = spec.paths?.[path2];
|
|
29572
29613
|
const methodSpec = pathSpec?.[method.toLowerCase()];
|
|
29573
|
-
|
|
29574
|
-
if (usdPrice > 0) {
|
|
29575
|
-
const amikoPrice = await getTokenUsdPrice(auth, "AMIKO") ?? 0;
|
|
29576
|
-
amount2 = usdToAmiko(usdPrice, amikoPrice);
|
|
29577
|
-
onProgress?.(`Price: $${usdPrice.toFixed(4)} = ${amount2} AMIKO`);
|
|
29578
|
-
} else {
|
|
29579
|
-
amount2 = "1";
|
|
29580
|
-
}
|
|
29581
|
-
} else {
|
|
29582
|
-
amount2 = "1";
|
|
29614
|
+
usdPrice = parseFloat(methodSpec?.["x-payment-info"]?.price || "0");
|
|
29583
29615
|
}
|
|
29584
29616
|
} catch {
|
|
29617
|
+
usdPrice = 0;
|
|
29618
|
+
}
|
|
29619
|
+
if (token === "USDC" || token === "USDT") {
|
|
29620
|
+
if (!(usdPrice > 0)) {
|
|
29621
|
+
throw new Error(`Could not auto-price ${path2} in USD — pass an explicit amount (e.g. --${token.toLowerCase()} 0.01).`);
|
|
29622
|
+
}
|
|
29623
|
+
amount2 = usdPrice.toFixed(6);
|
|
29624
|
+
onProgress?.(`Price: $${usdPrice.toFixed(4)} = ${amount2} ${token}`);
|
|
29625
|
+
} else if (usdPrice > 0) {
|
|
29626
|
+
const amikoPrice = await getTokenUsdPrice(auth, "AMIKO") ?? 0;
|
|
29627
|
+
amount2 = usdToAmiko(usdPrice, amikoPrice);
|
|
29628
|
+
onProgress?.(`Price: $${usdPrice.toFixed(4)} = ${amount2} AMIKO`);
|
|
29629
|
+
} else {
|
|
29585
29630
|
amount2 = "1";
|
|
29586
29631
|
}
|
|
29587
29632
|
}
|
|
29588
29633
|
onProgress?.(`Paying ${amount2} ${token}...`);
|
|
29589
|
-
const
|
|
29590
|
-
method: "POST",
|
|
29591
|
-
body: { to: TREASURY4, amount: amount2, token, chain: "solana" }
|
|
29592
|
-
});
|
|
29593
|
-
const txHash = transferData.txHash;
|
|
29594
|
-
if (!txHash)
|
|
29595
|
-
throw new Error("Payment sent but no tx hash returned.");
|
|
29634
|
+
const txHash = await payTreasury(auth, { wallet, amount: amount2, token });
|
|
29596
29635
|
const headers = {
|
|
29597
29636
|
"Content-Type": "application/json",
|
|
29598
29637
|
"X-Payment-Tx": txHash,
|
|
29638
|
+
"X-Payment-Token": token,
|
|
29599
29639
|
"X-Twin-Id": auth.twinId
|
|
29600
29640
|
};
|
|
29601
29641
|
const fetchOpts = { method, headers };
|
|
@@ -29691,13 +29731,13 @@ function registerServicesCommand(program2) {
|
|
|
29691
29731
|
opts
|
|
29692
29732
|
});
|
|
29693
29733
|
});
|
|
29694
|
-
svc.command("call <method> <path> [body]").description("Call any MPP endpoint — pays in AMIKO or
|
|
29695
|
-
const costLabel = opts.usdc ? `${opts.usdc} USDC` : opts.amiko ? `${opts.amiko} AMIKO` : "auto-priced AMIKO (check /v1/ list for endpoint price first)";
|
|
29734
|
+
svc.command("call <method> <path> [body]").description("Call any MPP endpoint — pays in AMIKO, USDC, or USDT on Solana").option("--amiko <amount>", "AMIKO tokens to pay (default: auto-price from USD)").option("--usdc <amount>", 'Pay in USDC instead of AMIKO ("auto" to price from USD)').option("--usdt <amount>", 'Pay in USDT instead of AMIKO ("auto" to price from USD)').option("--raw", "Output raw response").option("--wallet <address>", "Wallet to pay from (defaults to your Solana wallet — run `amiko wallets create --chain solana` if none)").option("--yes", "Skip the pre-spend confirmation (required in non-interactive shells)").action(async (method, path2, bodyStr, opts) => {
|
|
29735
|
+
const costLabel = opts.usdt ? `${opts.usdt} USDT` : opts.usdc ? `${opts.usdc} USDC` : opts.amiko ? `${opts.amiko} AMIKO` : "auto-priced AMIKO (check /v1/ list for endpoint price first)";
|
|
29696
29736
|
await requireApproval({
|
|
29697
29737
|
cost: costLabel,
|
|
29698
29738
|
summary: `${method.toUpperCase()} ${path2}`,
|
|
29699
29739
|
yes: opts.yes,
|
|
29700
|
-
commandExample: `amiko markets service call ${method.toUpperCase()} ${path2}${bodyStr ? ` ${JSON.stringify(bodyStr)}` : ""}${opts.amiko ? ` --amiko ${opts.amiko}` : ""}${opts.usdc ? ` --usdc ${opts.usdc}` : ""}`
|
|
29740
|
+
commandExample: `amiko markets service call ${method.toUpperCase()} ${path2}${bodyStr ? ` ${JSON.stringify(bodyStr)}` : ""}${opts.amiko ? ` --amiko ${opts.amiko}` : ""}${opts.usdc ? ` --usdc ${opts.usdc}` : ""}${opts.usdt ? ` --usdt ${opts.usdt}` : ""}`
|
|
29701
29741
|
});
|
|
29702
29742
|
let body;
|
|
29703
29743
|
if (bodyStr) {
|
|
@@ -29712,8 +29752,9 @@ function registerServicesCommand(program2) {
|
|
|
29712
29752
|
method: method.toUpperCase(),
|
|
29713
29753
|
path: path2,
|
|
29714
29754
|
body,
|
|
29715
|
-
amiko: opts.amiko || (opts.usdc ? undefined : "auto"),
|
|
29755
|
+
amiko: opts.amiko || (opts.usdc || opts.usdt ? undefined : "auto"),
|
|
29716
29756
|
usdc: opts.usdc,
|
|
29757
|
+
usdt: opts.usdt,
|
|
29717
29758
|
opts
|
|
29718
29759
|
});
|
|
29719
29760
|
});
|
|
@@ -29764,8 +29805,8 @@ async function callService(params) {
|
|
|
29764
29805
|
console.error(error(e5 instanceof Error ? e5.message : String(e5)));
|
|
29765
29806
|
process.exit(1);
|
|
29766
29807
|
}
|
|
29767
|
-
const token = params.usdc ? "USDC" : "AMIKO";
|
|
29768
|
-
const amount2 = params.usdc || params.amiko || "auto";
|
|
29808
|
+
const token = params.usdt ? "USDT" : params.usdc ? "USDC" : "AMIKO";
|
|
29809
|
+
const amount2 = params.usdt || params.usdc || params.amiko || "auto";
|
|
29769
29810
|
const { res, txHash, amikoSpent } = await payAndCall(mppBase2, params.method, params.path, auth, wallet, amount2, params.body, (msg) => {
|
|
29770
29811
|
spinner.text = msg;
|
|
29771
29812
|
}, token);
|
package/package.json
CHANGED
package/skills/SKILL.md
CHANGED
|
@@ -92,9 +92,11 @@ All paid markets commands auto-select the twin's active Solana wallet (see `--wa
|
|
|
92
92
|
|
|
93
93
|
**Prefer `amiko create` for anything it supports.** For media generation — image, video, speech (TTS), music, SFX — use `amiko create` (async, charge-on-success, never loses money on a timeout), NOT `markets`. Reach for `markets` **only for what `create` does not cover**: X/Twitter search, Amazon, AI chat, speech-to-text/transcription, and arbitrary MPP endpoints via `markets service call`.
|
|
94
94
|
|
|
95
|
+
**Payment token:** `markets search`, `markets image`, and `markets amazon search` accept `--token <AMIKO|USDC|USDT>` (default AMIKO); `markets service call` uses `--usdc`/`--usdt` (pass `auto` to price from USD). Stables are pinned at $1.00 — the amount equals the endpoint's USD price — so there is no forced swap through AMIKO. When the owner asks to pay in USDT/USDC, pass the flag; do NOT swap first.
|
|
96
|
+
|
|
95
97
|
## Create Studio — behavior notes
|
|
96
98
|
|
|
97
|
-
`amiko create <image|video|tts|music|sfx>` is the CLI half of the platform Create Studio. It generates through Amiko's own authenticated endpoints (not raw MPP), runs **async**, and is **charged on success** from the twin's custodial wallet — a failed or timed-out generation is **never billed**, and there is no pre-pay. **The command returns immediately** with a `jobId` and `status: PENDING` — it does NOT block for the whole generation (so you're not held for 60s–9min). It prints how to check + a rough ETA. **Do NOT sit in a tight polling loop** (it burns turns/tokens). After submitting, **tell the user roughly how long to wait and stop** — image ~15–60s, video ~2–9min, music ~30–120s, tts/sfx ~5–20s (e.g. "your video's generating — check back in a few minutes"). Then retrieve it **once** later — when the user next asks, or after the ETA — with `amiko create status <jobId>` (re-query that job, ~24h) or `amiko create media` (list recent generations; `--service`/`--limit`/`--raw`). Result is a permanent Supabase Storage URL. **Do NOT use `--wait`** — there's no such option; `create` is always non-blocking, so submit then check later with `amiko create status <jobId>`. `--token <AMIKO|USDC|USDT|SOL>` selects the charge token (default auto, AMIKO-first). Prefer `create` over `markets image` for media generation — it doesn't lose money on timeouts. `markets image` remains the raw MPP pre-pay path.
|
|
99
|
+
`amiko create <image|video|tts|music|sfx>` is the CLI half of the platform Create Studio. It generates through Amiko's own authenticated endpoints (not raw MPP), runs **async**, and is **charged on success** from the twin's custodial wallet — a failed or timed-out generation is **never billed**, and there is no pre-pay. **The command returns immediately** with a `jobId` and `status: PENDING` — it does NOT block for the whole generation (so you're not held for 60s–9min). It prints how to check + a rough ETA. **Do NOT sit in a tight polling loop** (it burns turns/tokens). After submitting, **tell the user roughly how long to wait and stop** — image ~15–60s, video ~2–9min, music ~30–120s, tts/sfx ~5–20s (e.g. "your video's generating — check back in a few minutes"). Then retrieve it **once** later — when the user next asks, or after the ETA — with `amiko create status <jobId>` (re-query that job, ~24h) or `amiko create media` (list recent generations; `--service`/`--limit`/`--raw`). Result is a permanent Supabase Storage URL. **Do NOT use `--wait`** — there's no such option; `create` is always non-blocking, so submit then check later with `amiko create status <jobId>`. `--token <AMIKO|USDC|USDT|SOL>` selects the charge token (default auto, AMIKO-first). Prefer `create` over `markets image` for media generation — it doesn't lose money on timeouts. `markets image` remains the raw MPP pre-pay path. For **music**, a plain prompt is enough — `create music "a triumphant orchestral ballad"` sings from the prompt (lyrics auto-written); add `--lyrics "…"` to set exact words, or `--instrumental` for no vocals. Don't paste long lyrics into the prompt itself.
|
|
98
100
|
|
|
99
101
|
## Chat — behavior notes
|
|
100
102
|
|