@heyamiko/amiko-cli 0.12.0-beta.7 → 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 +125 -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)));
|
|
28502
|
+
process.exit(1);
|
|
28503
|
+
}
|
|
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)));
|
|
28467
28509
|
process.exit(1);
|
|
28468
28510
|
}
|
|
28469
|
-
const
|
|
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
|
});
|
|
@@ -28958,6 +28993,10 @@ function detectMime(buffer, name) {
|
|
|
28958
28993
|
|
|
28959
28994
|
// src/commands/chat.ts
|
|
28960
28995
|
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
28996
|
+
var CUID_RE = /^c[a-z0-9]{20,}$/i;
|
|
28997
|
+
function looksLikeConversationId(t) {
|
|
28998
|
+
return UUID_RE.test(t) || CUID_RE.test(t);
|
|
28999
|
+
}
|
|
28961
29000
|
function authOrExit() {
|
|
28962
29001
|
try {
|
|
28963
29002
|
return requireAuth();
|
|
@@ -28989,7 +29028,7 @@ async function findOrCreateDm(auth, userId) {
|
|
|
28989
29028
|
}
|
|
28990
29029
|
async function resolveConversation(auth, target) {
|
|
28991
29030
|
const t = target.trim();
|
|
28992
|
-
if (
|
|
29031
|
+
if (looksLikeConversationId(t))
|
|
28993
29032
|
return t;
|
|
28994
29033
|
if (t.startsWith("@")) {
|
|
28995
29034
|
const handle = t.slice(1);
|
|
@@ -29403,24 +29442,29 @@ function registerCardCommand(card) {
|
|
|
29403
29442
|
}
|
|
29404
29443
|
|
|
29405
29444
|
// src/commands/amazon.ts
|
|
29406
|
-
var
|
|
29407
|
-
async function payAmiko(auth, wallet, amount2) {
|
|
29408
|
-
const data = await amikoWebFetch(auth, `/api/agents/${encodeURIComponent(auth.twinId)}/wallets/${encodeURIComponent(wallet)}/transfer`, {
|
|
29409
|
-
method: "POST",
|
|
29410
|
-
body: { to: TREASURY3, amount: amount2, token: "AMIKO", chain: "solana" }
|
|
29411
|
-
});
|
|
29412
|
-
if (!data.txHash)
|
|
29413
|
-
throw new Error("No tx hash returned.");
|
|
29414
|
-
return data.txHash;
|
|
29415
|
-
}
|
|
29445
|
+
var AMAZON_SEARCH_USD = 0.01;
|
|
29416
29446
|
function registerAmazonCommand(program2) {
|
|
29417
29447
|
const cmd = program2.command("amazon").description("Search and buy products on Amazon — paid with AMIKO");
|
|
29418
|
-
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
|
+
}
|
|
29419
29463
|
await requireApproval({
|
|
29420
|
-
cost:
|
|
29464
|
+
cost: `${quote.amount} ${payToken} (≈ $${AMAZON_SEARCH_USD.toFixed(2)})`,
|
|
29421
29465
|
summary: `Amazon product search: ${query.slice(0, 80)}${query.length > 80 ? "…" : ""}`,
|
|
29422
29466
|
yes: opts.yes,
|
|
29423
|
-
commandExample: `amiko markets amazon search ${JSON.stringify(query)}`
|
|
29467
|
+
commandExample: `amiko markets amazon search ${JSON.stringify(query)}${payToken !== "AMIKO" ? ` --token ${payToken}` : ""}`
|
|
29424
29468
|
});
|
|
29425
29469
|
const spinner = ora("Searching Amazon...").start();
|
|
29426
29470
|
try {
|
|
@@ -29442,14 +29486,14 @@ function registerAmazonCommand(program2) {
|
|
|
29442
29486
|
console.error(error(e5 instanceof Error ? e5.message : String(e5)));
|
|
29443
29487
|
process.exit(1);
|
|
29444
29488
|
}
|
|
29445
|
-
spinner.text =
|
|
29446
|
-
const txHash = await
|
|
29489
|
+
spinner.text = `Paying ${quote.amount} ${payToken}...`;
|
|
29490
|
+
const txHash = await payTreasury(auth, { wallet, amount: quote.amount, token: payToken });
|
|
29447
29491
|
spinner.text = "Searching Amazon...";
|
|
29448
29492
|
const params = new URLSearchParams({
|
|
29449
29493
|
q: `site:amazon.com ${query}`
|
|
29450
29494
|
});
|
|
29451
29495
|
const res = await fetch(`${mppBase2}/v1/search/google_shopping?${params}`, {
|
|
29452
|
-
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 }
|
|
29453
29497
|
});
|
|
29454
29498
|
spinner.stop();
|
|
29455
29499
|
if (!res.ok) {
|
|
@@ -29546,52 +29590,52 @@ function registerAmazonCommand(program2) {
|
|
|
29546
29590
|
}
|
|
29547
29591
|
|
|
29548
29592
|
// src/commands/services.ts
|
|
29549
|
-
var TREASURY4 = "FPiZAf3jEnNfCwfvbv46jdPfHbmSepD4Q6iHiLJDQXvb";
|
|
29550
29593
|
function usdToAmiko(usd2, pricePerAmiko) {
|
|
29551
29594
|
if (pricePerAmiko <= 0)
|
|
29552
29595
|
return "1";
|
|
29553
29596
|
const amount2 = usd2 / pricePerAmiko;
|
|
29554
29597
|
return Math.ceil(amount2 * 100) / 100 + "";
|
|
29555
29598
|
}
|
|
29556
|
-
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);
|
|
29557
29601
|
if (!path2.startsWith("/")) {
|
|
29558
29602
|
throw new Error(`path must start with "/" — got: ${path2}`);
|
|
29559
29603
|
}
|
|
29560
29604
|
let amount2 = amountOrAuto;
|
|
29561
29605
|
if (amount2 === "auto") {
|
|
29562
29606
|
onProgress?.("Fetching price...");
|
|
29607
|
+
let usdPrice = 0;
|
|
29563
29608
|
try {
|
|
29564
29609
|
const specRes = await fetchWithTimeout2(`${mppBase2}/openapi.json`, {}, 15000);
|
|
29565
29610
|
if (specRes.ok) {
|
|
29566
29611
|
const spec = await specRes.json();
|
|
29567
29612
|
const pathSpec = spec.paths?.[path2];
|
|
29568
29613
|
const methodSpec = pathSpec?.[method.toLowerCase()];
|
|
29569
|
-
|
|
29570
|
-
if (usdPrice > 0) {
|
|
29571
|
-
const amikoPrice = await getTokenUsdPrice(auth, "AMIKO") ?? 0;
|
|
29572
|
-
amount2 = usdToAmiko(usdPrice, amikoPrice);
|
|
29573
|
-
onProgress?.(`Price: $${usdPrice.toFixed(4)} = ${amount2} AMIKO`);
|
|
29574
|
-
} else {
|
|
29575
|
-
amount2 = "1";
|
|
29576
|
-
}
|
|
29577
|
-
} else {
|
|
29578
|
-
amount2 = "1";
|
|
29614
|
+
usdPrice = parseFloat(methodSpec?.["x-payment-info"]?.price || "0");
|
|
29579
29615
|
}
|
|
29580
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 {
|
|
29581
29630
|
amount2 = "1";
|
|
29582
29631
|
}
|
|
29583
29632
|
}
|
|
29584
29633
|
onProgress?.(`Paying ${amount2} ${token}...`);
|
|
29585
|
-
const
|
|
29586
|
-
method: "POST",
|
|
29587
|
-
body: { to: TREASURY4, amount: amount2, token, chain: "solana" }
|
|
29588
|
-
});
|
|
29589
|
-
const txHash = transferData.txHash;
|
|
29590
|
-
if (!txHash)
|
|
29591
|
-
throw new Error("Payment sent but no tx hash returned.");
|
|
29634
|
+
const txHash = await payTreasury(auth, { wallet, amount: amount2, token });
|
|
29592
29635
|
const headers = {
|
|
29593
29636
|
"Content-Type": "application/json",
|
|
29594
29637
|
"X-Payment-Tx": txHash,
|
|
29638
|
+
"X-Payment-Token": token,
|
|
29595
29639
|
"X-Twin-Id": auth.twinId
|
|
29596
29640
|
};
|
|
29597
29641
|
const fetchOpts = { method, headers };
|
|
@@ -29687,13 +29731,13 @@ function registerServicesCommand(program2) {
|
|
|
29687
29731
|
opts
|
|
29688
29732
|
});
|
|
29689
29733
|
});
|
|
29690
|
-
svc.command("call <method> <path> [body]").description("Call any MPP endpoint — pays in AMIKO or
|
|
29691
|
-
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)";
|
|
29692
29736
|
await requireApproval({
|
|
29693
29737
|
cost: costLabel,
|
|
29694
29738
|
summary: `${method.toUpperCase()} ${path2}`,
|
|
29695
29739
|
yes: opts.yes,
|
|
29696
|
-
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}` : ""}`
|
|
29697
29741
|
});
|
|
29698
29742
|
let body;
|
|
29699
29743
|
if (bodyStr) {
|
|
@@ -29708,8 +29752,9 @@ function registerServicesCommand(program2) {
|
|
|
29708
29752
|
method: method.toUpperCase(),
|
|
29709
29753
|
path: path2,
|
|
29710
29754
|
body,
|
|
29711
|
-
amiko: opts.amiko || (opts.usdc ? undefined : "auto"),
|
|
29755
|
+
amiko: opts.amiko || (opts.usdc || opts.usdt ? undefined : "auto"),
|
|
29712
29756
|
usdc: opts.usdc,
|
|
29757
|
+
usdt: opts.usdt,
|
|
29713
29758
|
opts
|
|
29714
29759
|
});
|
|
29715
29760
|
});
|
|
@@ -29760,8 +29805,8 @@ async function callService(params) {
|
|
|
29760
29805
|
console.error(error(e5 instanceof Error ? e5.message : String(e5)));
|
|
29761
29806
|
process.exit(1);
|
|
29762
29807
|
}
|
|
29763
|
-
const token = params.usdc ? "USDC" : "AMIKO";
|
|
29764
|
-
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";
|
|
29765
29810
|
const { res, txHash, amikoSpent } = await payAndCall(mppBase2, params.method, params.path, auth, wallet, amount2, params.body, (msg) => {
|
|
29766
29811
|
spinner.text = msg;
|
|
29767
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
|
|