@heyamiko/amiko-cli 0.12.0-beta.8 → 0.14.0-beta.2
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 +123 -80
- 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();
|
|
@@ -28610,7 +28644,9 @@ function printCompleted(final, title) {
|
|
|
28610
28644
|
console.log(label("Type", final.assetMime));
|
|
28611
28645
|
console.log("");
|
|
28612
28646
|
if (final.charged) {
|
|
28613
|
-
|
|
28647
|
+
const amt = final.chargeAmount ?? final.costAmiko ?? "?";
|
|
28648
|
+
const tok = final.chargeToken ?? "AMIKO";
|
|
28649
|
+
console.log(success(`Charged ${amt} ${tok}`) + (final.chargeTx ? dim(` tx: ${final.chargeTx.slice(0, 16)}…`) : ""));
|
|
28614
28650
|
} else {
|
|
28615
28651
|
console.log(dim("Not charged (settlement pending or waived)."));
|
|
28616
28652
|
}
|
|
@@ -28742,7 +28778,7 @@ function registerCreateCommand(create2) {
|
|
|
28742
28778
|
}
|
|
28743
28779
|
});
|
|
28744
28780
|
});
|
|
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) => {
|
|
28781
|
+
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
28782
|
if (!prompt && !opts.lyrics) {
|
|
28747
28783
|
console.error(error("Provide a prompt or --lyrics."));
|
|
28748
28784
|
process.exit(1);
|
|
@@ -28760,6 +28796,7 @@ function registerCreateCommand(create2) {
|
|
|
28760
28796
|
...opts.lyrics ? { lyrics: opts.lyrics } : {},
|
|
28761
28797
|
...opts.duration ? { durationMs: Number(opts.duration) } : {},
|
|
28762
28798
|
...opts.instrumental ? { isInstrumental: true } : {},
|
|
28799
|
+
...opts.lyricsOptimizer ? { lyricsOptimizer: true } : {},
|
|
28763
28800
|
...opts.token ? { preferredToken: opts.token } : {}
|
|
28764
28801
|
}
|
|
28765
28802
|
});
|
|
@@ -29407,24 +29444,29 @@ function registerCardCommand(card) {
|
|
|
29407
29444
|
}
|
|
29408
29445
|
|
|
29409
29446
|
// 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
|
-
}
|
|
29447
|
+
var AMAZON_SEARCH_USD = 0.01;
|
|
29420
29448
|
function registerAmazonCommand(program2) {
|
|
29421
29449
|
const cmd = program2.command("amazon").description("Search and buy products on Amazon — paid with AMIKO");
|
|
29422
|
-
cmd.command("search <query>").description("Search Amazon products —
|
|
29450
|
+
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) => {
|
|
29451
|
+
let payToken;
|
|
29452
|
+
try {
|
|
29453
|
+
payToken = normalizePaymentToken(opts.token);
|
|
29454
|
+
} catch (e5) {
|
|
29455
|
+
console.error(error(e5 instanceof Error ? e5.message : String(e5)));
|
|
29456
|
+
process.exit(1);
|
|
29457
|
+
}
|
|
29458
|
+
let quote;
|
|
29459
|
+
try {
|
|
29460
|
+
quote = await quoteTokenAmount(resolveAuth(), payToken, AMAZON_SEARCH_USD);
|
|
29461
|
+
} catch (e5) {
|
|
29462
|
+
console.error(error(e5 instanceof Error ? e5.message : String(e5)));
|
|
29463
|
+
process.exit(1);
|
|
29464
|
+
}
|
|
29423
29465
|
await requireApproval({
|
|
29424
|
-
cost:
|
|
29466
|
+
cost: `${quote.amount} ${payToken} (≈ $${AMAZON_SEARCH_USD.toFixed(2)})`,
|
|
29425
29467
|
summary: `Amazon product search: ${query.slice(0, 80)}${query.length > 80 ? "…" : ""}`,
|
|
29426
29468
|
yes: opts.yes,
|
|
29427
|
-
commandExample: `amiko markets amazon search ${JSON.stringify(query)}`
|
|
29469
|
+
commandExample: `amiko markets amazon search ${JSON.stringify(query)}${payToken !== "AMIKO" ? ` --token ${payToken}` : ""}`
|
|
29428
29470
|
});
|
|
29429
29471
|
const spinner = ora("Searching Amazon...").start();
|
|
29430
29472
|
try {
|
|
@@ -29446,14 +29488,14 @@ function registerAmazonCommand(program2) {
|
|
|
29446
29488
|
console.error(error(e5 instanceof Error ? e5.message : String(e5)));
|
|
29447
29489
|
process.exit(1);
|
|
29448
29490
|
}
|
|
29449
|
-
spinner.text =
|
|
29450
|
-
const txHash = await
|
|
29491
|
+
spinner.text = `Paying ${quote.amount} ${payToken}...`;
|
|
29492
|
+
const txHash = await payTreasury(auth, { wallet, amount: quote.amount, token: payToken });
|
|
29451
29493
|
spinner.text = "Searching Amazon...";
|
|
29452
29494
|
const params = new URLSearchParams({
|
|
29453
29495
|
q: `site:amazon.com ${query}`
|
|
29454
29496
|
});
|
|
29455
29497
|
const res = await fetch(`${mppBase2}/v1/search/google_shopping?${params}`, {
|
|
29456
|
-
headers: { "X-Payment-Tx": txHash, "X-Twin-Id": auth.twinId }
|
|
29498
|
+
headers: { "X-Payment-Tx": txHash, "X-Payment-Token": payToken, "X-Twin-Id": auth.twinId }
|
|
29457
29499
|
});
|
|
29458
29500
|
spinner.stop();
|
|
29459
29501
|
if (!res.ok) {
|
|
@@ -29550,52 +29592,52 @@ function registerAmazonCommand(program2) {
|
|
|
29550
29592
|
}
|
|
29551
29593
|
|
|
29552
29594
|
// src/commands/services.ts
|
|
29553
|
-
var TREASURY4 = "FPiZAf3jEnNfCwfvbv46jdPfHbmSepD4Q6iHiLJDQXvb";
|
|
29554
29595
|
function usdToAmiko(usd2, pricePerAmiko) {
|
|
29555
29596
|
if (pricePerAmiko <= 0)
|
|
29556
29597
|
return "1";
|
|
29557
29598
|
const amount2 = usd2 / pricePerAmiko;
|
|
29558
29599
|
return Math.ceil(amount2 * 100) / 100 + "";
|
|
29559
29600
|
}
|
|
29560
|
-
async function payAndCall(mppBase2, method, path2, auth, wallet, amountOrAuto, body, onProgress,
|
|
29601
|
+
async function payAndCall(mppBase2, method, path2, auth, wallet, amountOrAuto, body, onProgress, rawToken = "AMIKO") {
|
|
29602
|
+
const token = normalizePaymentToken(rawToken);
|
|
29561
29603
|
if (!path2.startsWith("/")) {
|
|
29562
29604
|
throw new Error(`path must start with "/" — got: ${path2}`);
|
|
29563
29605
|
}
|
|
29564
29606
|
let amount2 = amountOrAuto;
|
|
29565
29607
|
if (amount2 === "auto") {
|
|
29566
29608
|
onProgress?.("Fetching price...");
|
|
29609
|
+
let usdPrice = 0;
|
|
29567
29610
|
try {
|
|
29568
29611
|
const specRes = await fetchWithTimeout2(`${mppBase2}/openapi.json`, {}, 15000);
|
|
29569
29612
|
if (specRes.ok) {
|
|
29570
29613
|
const spec = await specRes.json();
|
|
29571
29614
|
const pathSpec = spec.paths?.[path2];
|
|
29572
29615
|
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";
|
|
29616
|
+
usdPrice = parseFloat(methodSpec?.["x-payment-info"]?.price || "0");
|
|
29583
29617
|
}
|
|
29584
29618
|
} catch {
|
|
29619
|
+
usdPrice = 0;
|
|
29620
|
+
}
|
|
29621
|
+
if (token === "USDC" || token === "USDT") {
|
|
29622
|
+
if (!(usdPrice > 0)) {
|
|
29623
|
+
throw new Error(`Could not auto-price ${path2} in USD — pass an explicit amount (e.g. --${token.toLowerCase()} 0.01).`);
|
|
29624
|
+
}
|
|
29625
|
+
amount2 = usdPrice.toFixed(6);
|
|
29626
|
+
onProgress?.(`Price: $${usdPrice.toFixed(4)} = ${amount2} ${token}`);
|
|
29627
|
+
} else if (usdPrice > 0) {
|
|
29628
|
+
const amikoPrice = await getTokenUsdPrice(auth, "AMIKO") ?? 0;
|
|
29629
|
+
amount2 = usdToAmiko(usdPrice, amikoPrice);
|
|
29630
|
+
onProgress?.(`Price: $${usdPrice.toFixed(4)} = ${amount2} AMIKO`);
|
|
29631
|
+
} else {
|
|
29585
29632
|
amount2 = "1";
|
|
29586
29633
|
}
|
|
29587
29634
|
}
|
|
29588
29635
|
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.");
|
|
29636
|
+
const txHash = await payTreasury(auth, { wallet, amount: amount2, token });
|
|
29596
29637
|
const headers = {
|
|
29597
29638
|
"Content-Type": "application/json",
|
|
29598
29639
|
"X-Payment-Tx": txHash,
|
|
29640
|
+
"X-Payment-Token": token,
|
|
29599
29641
|
"X-Twin-Id": auth.twinId
|
|
29600
29642
|
};
|
|
29601
29643
|
const fetchOpts = { method, headers };
|
|
@@ -29691,13 +29733,13 @@ function registerServicesCommand(program2) {
|
|
|
29691
29733
|
opts
|
|
29692
29734
|
});
|
|
29693
29735
|
});
|
|
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)";
|
|
29736
|
+
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) => {
|
|
29737
|
+
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
29738
|
await requireApproval({
|
|
29697
29739
|
cost: costLabel,
|
|
29698
29740
|
summary: `${method.toUpperCase()} ${path2}`,
|
|
29699
29741
|
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}` : ""}`
|
|
29742
|
+
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
29743
|
});
|
|
29702
29744
|
let body;
|
|
29703
29745
|
if (bodyStr) {
|
|
@@ -29712,8 +29754,9 @@ function registerServicesCommand(program2) {
|
|
|
29712
29754
|
method: method.toUpperCase(),
|
|
29713
29755
|
path: path2,
|
|
29714
29756
|
body,
|
|
29715
|
-
amiko: opts.amiko || (opts.usdc ? undefined : "auto"),
|
|
29757
|
+
amiko: opts.amiko || (opts.usdc || opts.usdt ? undefined : "auto"),
|
|
29716
29758
|
usdc: opts.usdc,
|
|
29759
|
+
usdt: opts.usdt,
|
|
29717
29760
|
opts
|
|
29718
29761
|
});
|
|
29719
29762
|
});
|
|
@@ -29764,8 +29807,8 @@ async function callService(params) {
|
|
|
29764
29807
|
console.error(error(e5 instanceof Error ? e5.message : String(e5)));
|
|
29765
29808
|
process.exit(1);
|
|
29766
29809
|
}
|
|
29767
|
-
const token = params.usdc ? "USDC" : "AMIKO";
|
|
29768
|
-
const amount2 = params.usdc || params.amiko || "auto";
|
|
29810
|
+
const token = params.usdt ? "USDT" : params.usdc ? "USDC" : "AMIKO";
|
|
29811
|
+
const amount2 = params.usdt || params.usdc || params.amiko || "auto";
|
|
29769
29812
|
const { res, txHash, amikoSpent } = await payAndCall(mppBase2, params.method, params.path, auth, wallet, amount2, params.body, (msg) => {
|
|
29770
29813
|
spinner.text = msg;
|
|
29771
29814
|
}, 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
|
|