@heyamiko/amiko-cli 0.14.0-beta.12 → 0.14.0-beta.14
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 +33 -4
- package/dist/index.js +214 -34
- package/package.json +1 -1
- package/skills/SKILL.md +17 -0
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# @heyamiko/amiko-cli (v0.14.0-beta.
|
|
1
|
+
# @heyamiko/amiko-cli (v0.14.0-beta.14)
|
|
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
|
|
|
@@ -131,6 +131,10 @@ amiko create video "animate cover" --first-frame https://.../cover.png --yes
|
|
|
131
131
|
amiko create video "dance clip" --model dreamina-seedance-2-0-fast-260128 \
|
|
132
132
|
--reference-image https://.../ref1.png --reference-audio https://.../beat.mp3 --generate-audio --yes
|
|
133
133
|
amiko create tts "Hello world" --voice 21m00Tcm4TlvDq8ikWAM
|
|
134
|
+
amiko create tts "Hello world" --provider minimax --model speech-2.8-turbo
|
|
135
|
+
amiko create tts "Hello" --provider minimax --voice English_expressive_narrator --yes
|
|
136
|
+
amiko voice clone ./me.mp3 --provider minimax
|
|
137
|
+
amiko voice clone ./me.mp3 --provider elevenlabs
|
|
134
138
|
amiko create music "lo-fi chill beat" --duration 30000
|
|
135
139
|
amiko create music --lyrics "..." --instrumental # instrumental
|
|
136
140
|
amiko create sfx "thunder and heavy rain" --duration 5
|
|
@@ -178,8 +182,20 @@ Reference flags accept URLs or data URIs (not local paths). Typical I2V flow: `c
|
|
|
178
182
|
|
|
179
183
|
| Flag | Default | Description |
|
|
180
184
|
|------|---------|-------------|
|
|
181
|
-
| `--
|
|
182
|
-
| `--
|
|
185
|
+
| `--provider <name>` | inferred / `elevenlabs` | `minimax` or `elevenlabs` — sets model/voice defaults when those flags are omitted |
|
|
186
|
+
| `--model <model>` | EL: `eleven_multilingual_v2`; MiniMax: `speech-2.8-turbo` | MiniMax: `speech-2.8-turbo\|hd`, `speech-2.6-turbo\|hd`. ElevenLabs: `eleven_*` |
|
|
187
|
+
| `--voice <voiceId>` | EL default hash; MiniMax: `English_expressive_narrator` | MiniMax system or cloned `voice_id`; ElevenLabs voice id |
|
|
188
|
+
|
|
189
|
+
Examples: `amiko create tts "Hi" --provider minimax --yes` · `amiko create tts "Hi" --provider minimax --model speech-2.8-turbo --voice <clonedId> --yes`
|
|
190
|
+
|
|
191
|
+
**`amiko voice clone <source>`** — audio path, https URL, or `-` (stdin)
|
|
192
|
+
|
|
193
|
+
| Flag | Default | Description |
|
|
194
|
+
|------|---------|-------------|
|
|
195
|
+
| `--provider <name>` | `minimax` | `minimax` (mpp rapid clone + ownership registry) or `elevenlabs` (legacy IVC) |
|
|
196
|
+
| `--voice-id <id>` | auto | **MiniMax only** — optional custom voice_id (ignored for elevenlabs) |
|
|
197
|
+
| `--model <model>` | `speech-2.8-turbo` | **MiniMax only** — preview speech model (ignored for elevenlabs) |
|
|
198
|
+
| `--preview-text <text>` | provider default | **MiniMax only** — preview line; empty string skips preview (ignored for elevenlabs) |
|
|
183
199
|
|
|
184
200
|
**`amiko create music [prompt]`** — pass a prompt and/or `--lyrics`
|
|
185
201
|
|
|
@@ -345,8 +361,11 @@ amiko markets service call POST /v1/sfx '{"text":"Thunder with heavy rain","dura
|
|
|
345
361
|
# Music generation ($0.10) — returns { url, format, service }
|
|
346
362
|
amiko markets service call POST /v1/music '{"prompt":"Lo-fi chill beat","music_length_ms":30000}'
|
|
347
363
|
|
|
348
|
-
# Text-to-speech
|
|
364
|
+
# Text-to-speech — ElevenLabs voice id (default)
|
|
349
365
|
amiko markets service tts 21m00Tcm4TlvDq8ikWAM "Hello world"
|
|
366
|
+
# MiniMax system or cloned voice_id
|
|
367
|
+
amiko markets service tts English_expressive_narrator "Hello" --provider minimax --yes
|
|
368
|
+
amiko markets service tts <clonedId> "Hello" --provider minimax --model speech-2.8-turbo --yes
|
|
350
369
|
|
|
351
370
|
# Composition plan ($0.02)
|
|
352
371
|
amiko markets service call POST /v1/music/plan '{"prompt":"Epic orchestral piece"}'
|
|
@@ -564,6 +583,16 @@ npm publish
|
|
|
564
583
|
|
|
565
584
|
## Changelog
|
|
566
585
|
|
|
586
|
+
### 0.14.0-beta.14
|
|
587
|
+
|
|
588
|
+
- `credits balance` prefers `AMIKO_API_KEY` (`amk_`) → `billing.heyamiko.com/api/credits` directly; falls back to twin-token platform proxy. Override base with `AMIKO_BILLING_URL`.
|
|
589
|
+
|
|
590
|
+
### 0.14.0-beta.13
|
|
591
|
+
|
|
592
|
+
- **Create Studio TTS + voice clone: MiniMax.** `amiko create tts` accepts `--provider minimax|elevenlabs` (infers from `speech-*` / `eleven_*` models when omitted), with provider-specific defaults (`speech-2.8-turbo` + `English_expressive_narrator` vs `eleven_multilingual_v2` + Rachel). Quote body includes `voiceId`. Cloned-voice examples require `--provider minimax` when using a MiniMax `voice_id`.
|
|
593
|
+
- **`amiko voice clone`** defaults to MiniMax (mpp rapid clone + ownership registry); `--provider elevenlabs` keeps legacy IVC. MiniMax-only flags: `--voice-id`, `--model`, `--preview-text`.
|
|
594
|
+
- **`amiko markets service tts`** gains `--provider minimax|elevenlabs` and `--model` for MiniMax speech routing on `/v1/tts/:voiceId`.
|
|
595
|
+
|
|
567
596
|
### 0.14.0-beta.7
|
|
568
597
|
|
|
569
598
|
- **`amiko chat group invite <group>` — share a group's invite/join link + QR.** New read-only subcommand that fetches `GET /api/conversations/{id}/invite` (twin token) and prints the join link plus a scannable QR image URL. Admins-only: SKILL.md instructs the agent to confirm the asker holds `admin`/`owner` (via `amiko chat group info`) before sharing, and the endpoint is admin-gated server-side (403 → admin tip). Fails loudly if the response is missing its link/QR instead of printing a placeholder. Requires the companion amiko-web change (adds `qrUrl` to the invite response + a public `GET /api/conversations/join/[token]/qr` PNG route).
|
package/dist/index.js
CHANGED
|
@@ -861,7 +861,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
861
861
|
this._exitCallback = (err) => {
|
|
862
862
|
if (err.code !== "commander.executeSubCommandAsync") {
|
|
863
863
|
throw err;
|
|
864
|
-
}
|
|
864
|
+
} else {}
|
|
865
865
|
};
|
|
866
866
|
}
|
|
867
867
|
return this;
|
|
@@ -3596,7 +3596,7 @@ function getHumanReadableErrorMessage(code, context = {}) {
|
|
|
3596
3596
|
function getErrorMessage(code, context = {}) {
|
|
3597
3597
|
if (true) {
|
|
3598
3598
|
return getHumanReadableErrorMessage(code, context);
|
|
3599
|
-
}
|
|
3599
|
+
} else {}
|
|
3600
3600
|
}
|
|
3601
3601
|
function isSolanaError(e, code) {
|
|
3602
3602
|
const isSolanaError2 = e instanceof Error && e.name === "SolanaError";
|
|
@@ -19992,7 +19992,7 @@ function finalize(ctx, schema) {
|
|
|
19992
19992
|
result.$schema = "http://json-schema.org/draft-07/schema#";
|
|
19993
19993
|
} else if (ctx.target === "draft-04") {
|
|
19994
19994
|
result.$schema = "http://json-schema.org/draft-04/schema#";
|
|
19995
|
-
} else if (ctx.target === "openapi-3.0") {}
|
|
19995
|
+
} else if (ctx.target === "openapi-3.0") {} else {}
|
|
19996
19996
|
if (ctx.external?.uri) {
|
|
19997
19997
|
const id = ctx.external.registry.get(schema)?.id;
|
|
19998
19998
|
if (!id)
|
|
@@ -20257,7 +20257,7 @@ var formatMap, stringProcessor = (schema, ctx, _json, _params) => {
|
|
|
20257
20257
|
if (val === undefined) {
|
|
20258
20258
|
if (ctx.unrepresentable === "throw") {
|
|
20259
20259
|
throw new Error("Literal `undefined` cannot be represented in JSON Schema");
|
|
20260
|
-
}
|
|
20260
|
+
} else {}
|
|
20261
20261
|
} else if (typeof val === "bigint") {
|
|
20262
20262
|
if (ctx.unrepresentable === "throw") {
|
|
20263
20263
|
throw new Error("BigInt literals cannot be represented in JSON Schema");
|
|
@@ -23655,27 +23655,50 @@ function readFromFile(path) {
|
|
|
23655
23655
|
twinId: asString(parsed.amikoTwinId) ?? asString(parsed.AMIKO_TWIN_ID),
|
|
23656
23656
|
userId: asString(parsed.amikoUserId) ?? asString(parsed.AMIKO_USER_ID),
|
|
23657
23657
|
platformUrl: asString(parsed.amikoPlatformUrl) ?? asString(parsed.AMIKO_PLATFORM_URL),
|
|
23658
|
-
apiBase: asString(parsed.amikoApiBase) ?? asString(parsed.AMIKO_API_BASE) ?? asString(parsed.apiBase)
|
|
23658
|
+
apiBase: asString(parsed.amikoApiBase) ?? asString(parsed.AMIKO_API_BASE) ?? asString(parsed.apiBase),
|
|
23659
|
+
billingApiKey: asString(parsed.amikoApiKey) ?? asString(parsed.AMIKO_API_KEY) ?? asString(parsed.billingApiKey),
|
|
23660
|
+
billingUrl: asString(parsed.amikoBillingUrl) ?? asString(parsed.AMIKO_BILLING_URL) ?? asString(parsed.billingUrl)
|
|
23659
23661
|
};
|
|
23660
23662
|
} catch {
|
|
23661
23663
|
return null;
|
|
23662
23664
|
}
|
|
23663
23665
|
}
|
|
23666
|
+
function readRoutingIdsFromEnv() {
|
|
23667
|
+
return {
|
|
23668
|
+
agentId: asString(process.env.AMIKO_AGENT_ID),
|
|
23669
|
+
sessionId: asString(process.env.AMIKO_SESSION_ID)
|
|
23670
|
+
};
|
|
23671
|
+
}
|
|
23664
23672
|
function readFromEnv() {
|
|
23665
23673
|
return {
|
|
23666
23674
|
twinToken: asString(process.env.AMIKO_TWIN_TOKEN) ?? asString(process.env.AMIKO_USER_TOKEN),
|
|
23667
23675
|
twinId: asString(process.env.AMIKO_TWIN_ID),
|
|
23668
23676
|
userId: asString(process.env.AMIKO_USER_ID),
|
|
23669
23677
|
platformUrl: asString(process.env.AMIKO_PLATFORM_URL),
|
|
23670
|
-
apiBase: asString(process.env.AMIKO_API_BASE)
|
|
23678
|
+
apiBase: asString(process.env.AMIKO_API_BASE),
|
|
23679
|
+
billingApiKey: asString(process.env.AMIKO_API_KEY),
|
|
23680
|
+
billingUrl: asString(process.env.AMIKO_BILLING_URL),
|
|
23681
|
+
...readRoutingIdsFromEnv()
|
|
23682
|
+
};
|
|
23683
|
+
}
|
|
23684
|
+
function resolveFileAuth(path) {
|
|
23685
|
+
const fromFile = readFromFile(path);
|
|
23686
|
+
if (!fromFile)
|
|
23687
|
+
return null;
|
|
23688
|
+
const fromEnv = readFromEnv();
|
|
23689
|
+
return {
|
|
23690
|
+
...fromFile,
|
|
23691
|
+
...readRoutingIdsFromEnv(),
|
|
23692
|
+
billingApiKey: fromEnv.billingApiKey ?? fromFile.billingApiKey,
|
|
23693
|
+
billingUrl: fromEnv.billingUrl ?? fromFile.billingUrl
|
|
23671
23694
|
};
|
|
23672
23695
|
}
|
|
23673
23696
|
function resolveAuth() {
|
|
23674
23697
|
const filePath = findAmikoJsonPath();
|
|
23675
23698
|
if (filePath) {
|
|
23676
|
-
const
|
|
23677
|
-
if (
|
|
23678
|
-
return
|
|
23699
|
+
const merged = resolveFileAuth(filePath);
|
|
23700
|
+
if (merged)
|
|
23701
|
+
return merged;
|
|
23679
23702
|
}
|
|
23680
23703
|
return readFromEnv();
|
|
23681
23704
|
}
|
|
@@ -25405,7 +25428,31 @@ function registerBridgeCommand(program2) {
|
|
|
25405
25428
|
import readline2 from "node:readline";
|
|
25406
25429
|
|
|
25407
25430
|
// src/lib/balance.ts
|
|
25408
|
-
|
|
25431
|
+
var DEFAULT_BILLING_URL = "https://billing.heyamiko.com";
|
|
25432
|
+
function billingBase(auth) {
|
|
25433
|
+
const url = auth.billingUrl || DEFAULT_BILLING_URL;
|
|
25434
|
+
return url.replace(/\/$/, "");
|
|
25435
|
+
}
|
|
25436
|
+
async function fetchCreditsBalanceViaAmk(auth) {
|
|
25437
|
+
const apiKey = auth.billingApiKey;
|
|
25438
|
+
if (!apiKey || !apiKey.startsWith("amk_"))
|
|
25439
|
+
return null;
|
|
25440
|
+
try {
|
|
25441
|
+
const res = await fetch(`${billingBase(auth)}/api/credits`, {
|
|
25442
|
+
method: "GET",
|
|
25443
|
+
headers: { Authorization: `Bearer ${apiKey}` },
|
|
25444
|
+
signal: AbortSignal.timeout(15000)
|
|
25445
|
+
});
|
|
25446
|
+
if (!res.ok)
|
|
25447
|
+
return null;
|
|
25448
|
+
const data = await res.json();
|
|
25449
|
+
const raw = data.displayBalance;
|
|
25450
|
+
return typeof raw === "number" && isFinite(raw) ? Math.round(raw) : null;
|
|
25451
|
+
} catch {
|
|
25452
|
+
return null;
|
|
25453
|
+
}
|
|
25454
|
+
}
|
|
25455
|
+
async function fetchCreditsBalanceViaPlatformProxy(auth) {
|
|
25409
25456
|
if (!auth.twinToken)
|
|
25410
25457
|
return null;
|
|
25411
25458
|
try {
|
|
@@ -25416,6 +25463,12 @@ async function fetchCreditsBalance(auth) {
|
|
|
25416
25463
|
return null;
|
|
25417
25464
|
}
|
|
25418
25465
|
}
|
|
25466
|
+
async function fetchCreditsBalance(auth) {
|
|
25467
|
+
const viaAmk = await fetchCreditsBalanceViaAmk(auth);
|
|
25468
|
+
if (viaAmk != null)
|
|
25469
|
+
return viaAmk;
|
|
25470
|
+
return fetchCreditsBalanceViaPlatformProxy(auth);
|
|
25471
|
+
}
|
|
25419
25472
|
async function printBalanceRemaining(auth) {
|
|
25420
25473
|
const balance = await fetchCreditsBalance(auth);
|
|
25421
25474
|
if (balance == null) {
|
|
@@ -26442,6 +26495,13 @@ var ETA = {
|
|
|
26442
26495
|
function statusOf(err) {
|
|
26443
26496
|
return err?.status;
|
|
26444
26497
|
}
|
|
26498
|
+
function autoApproveLimit() {
|
|
26499
|
+
const raw = process.env.AMIKO_AUTO_APPROVE_LIMIT;
|
|
26500
|
+
if (typeof raw !== "string" || raw.trim() === "")
|
|
26501
|
+
return;
|
|
26502
|
+
const limit = Number(raw);
|
|
26503
|
+
return Number.isFinite(limit) ? limit : undefined;
|
|
26504
|
+
}
|
|
26445
26505
|
function collectOption(value, previous) {
|
|
26446
26506
|
return [...previous ?? [], value];
|
|
26447
26507
|
}
|
|
@@ -26451,7 +26511,7 @@ function assertMaxUrls(resourceName, urls, max) {
|
|
|
26451
26511
|
process.exit(1);
|
|
26452
26512
|
}
|
|
26453
26513
|
}
|
|
26454
|
-
function printCompleted(final, title) {
|
|
26514
|
+
function printCompleted(final, title, deliveredToThisSession = false) {
|
|
26455
26515
|
console.log(heading(`${title}
|
|
26456
26516
|
`));
|
|
26457
26517
|
if (final.prompt)
|
|
@@ -26460,6 +26520,9 @@ function printCompleted(final, title) {
|
|
|
26460
26520
|
console.log(label("URL", final.assetUrl));
|
|
26461
26521
|
if (final.assetMime)
|
|
26462
26522
|
console.log(label("Type", final.assetMime));
|
|
26523
|
+
if (deliveredToThisSession) {
|
|
26524
|
+
console.log(success("Already delivered to the user in this chat automatically. Do NOT send this file again and do NOT paste the URL — you would duplicate it."));
|
|
26525
|
+
}
|
|
26463
26526
|
console.log("");
|
|
26464
26527
|
if (final.charged) {
|
|
26465
26528
|
const amt = final.chargeAmount ?? final.costAmiko ?? "?";
|
|
@@ -26488,6 +26551,7 @@ async function submitCreateJob(opts) {
|
|
|
26488
26551
|
console.error(dim("--token is ignored with --pay credits (credits come from your Amiko account, not a wallet token)."));
|
|
26489
26552
|
}
|
|
26490
26553
|
let costLine = payCredits ? "reserved from your Amiko account credits, captured on success (released on failure)" : "charged from your twin wallet on success (nothing on failure)";
|
|
26554
|
+
let quotedCostAmiko;
|
|
26491
26555
|
try {
|
|
26492
26556
|
const q = await amikoWebFetch(auth, "/api/create/quote", {
|
|
26493
26557
|
method: "POST",
|
|
@@ -26497,11 +26561,29 @@ async function submitCreateJob(opts) {
|
|
|
26497
26561
|
if (q.cost_usd != null) {
|
|
26498
26562
|
costLine = payCredits ? `≈ ${Math.ceil(q.cost_usd * 1e4)} Amiko credits (≈ $${q.cost_usd.toFixed(4)}) — reserved on submit, captured only on success` : `${q.cost_amiko ?? "?"} AMIKO (≈ $${q.cost_usd.toFixed(4)}) — charged only on success`;
|
|
26499
26563
|
}
|
|
26564
|
+
if (typeof q.cost_amiko === "string" && q.cost_amiko.trim() !== "") {
|
|
26565
|
+
const n = Number(q.cost_amiko);
|
|
26566
|
+
if (Number.isFinite(n))
|
|
26567
|
+
quotedCostAmiko = n;
|
|
26568
|
+
}
|
|
26500
26569
|
} catch {}
|
|
26570
|
+
const preferredToken = typeof opts.jobBody.preferredToken === "string" ? opts.jobBody.preferredToken.toUpperCase() : undefined;
|
|
26571
|
+
const chargeIsAmiko = !payCredits && (preferredToken === undefined || preferredToken === "AMIKO");
|
|
26572
|
+
const effectiveChargeToken = preferredToken ?? "AMIKO";
|
|
26573
|
+
let yes = opts.yes;
|
|
26574
|
+
let autoApproved = false;
|
|
26575
|
+
if (!yes) {
|
|
26576
|
+
const limit = autoApproveLimit();
|
|
26577
|
+
if (limit !== undefined && quotedCostAmiko !== undefined && quotedCostAmiko > 0 && chargeIsAmiko && quotedCostAmiko <= limit) {
|
|
26578
|
+
yes = true;
|
|
26579
|
+
autoApproved = true;
|
|
26580
|
+
console.log(dim(`Auto-approved (cost ${quotedCostAmiko} ≤ limit ${limit} AMIKO)`));
|
|
26581
|
+
}
|
|
26582
|
+
}
|
|
26501
26583
|
await requireApproval({
|
|
26502
26584
|
cost: costLine,
|
|
26503
26585
|
summary: opts.summary,
|
|
26504
|
-
yes
|
|
26586
|
+
yes,
|
|
26505
26587
|
commandExample: `amiko create ${opts.mode.toLowerCase()} ...`
|
|
26506
26588
|
});
|
|
26507
26589
|
const isTTY = process.stderr.isTTY;
|
|
@@ -26511,16 +26593,21 @@ async function submitCreateJob(opts) {
|
|
|
26511
26593
|
spinner.stop();
|
|
26512
26594
|
};
|
|
26513
26595
|
let jobId;
|
|
26596
|
+
let routedAgentId;
|
|
26597
|
+
let routedSessionId;
|
|
26514
26598
|
try {
|
|
26515
26599
|
const submit = await amikoWebFetch(auth, "/api/create/jobs", {
|
|
26516
26600
|
method: "POST",
|
|
26517
26601
|
body: {
|
|
26518
26602
|
...opts.jobBody,
|
|
26519
26603
|
twinId: auth.twinId,
|
|
26604
|
+
...autoApproved ? { preferredToken: effectiveChargeToken, strictToken: true } : {},
|
|
26520
26605
|
...payCredits ? {
|
|
26521
26606
|
paymentMethod: { kind: "account_credits" },
|
|
26522
26607
|
requestId: randomUUID()
|
|
26523
|
-
} : {}
|
|
26608
|
+
} : {},
|
|
26609
|
+
...auth.agentId ? { agentId: auth.agentId } : {},
|
|
26610
|
+
...auth.sessionId ? { sessionId: auth.sessionId } : {}
|
|
26524
26611
|
},
|
|
26525
26612
|
timeoutMs: 30000
|
|
26526
26613
|
});
|
|
@@ -26530,6 +26617,8 @@ async function submitCreateJob(opts) {
|
|
|
26530
26617
|
process.exit(1);
|
|
26531
26618
|
}
|
|
26532
26619
|
jobId = submit.jobId;
|
|
26620
|
+
routedAgentId = submit.agentId;
|
|
26621
|
+
routedSessionId = submit.sessionId;
|
|
26533
26622
|
} catch (e5) {
|
|
26534
26623
|
stop();
|
|
26535
26624
|
const webErr = e5;
|
|
@@ -26550,19 +26639,24 @@ async function submitCreateJob(opts) {
|
|
|
26550
26639
|
}
|
|
26551
26640
|
process.exit(1);
|
|
26552
26641
|
}
|
|
26642
|
+
const sessionRouted = !!(routedAgentId && routedSessionId);
|
|
26553
26643
|
stop();
|
|
26554
26644
|
if (opts.raw) {
|
|
26555
26645
|
console.log(JSON.stringify({
|
|
26556
26646
|
jobId,
|
|
26557
26647
|
status: "PENDING",
|
|
26558
26648
|
check: `amiko create status ${jobId}`,
|
|
26559
|
-
eta: ETA[opts.mode]
|
|
26649
|
+
eta: ETA[opts.mode],
|
|
26650
|
+
...sessionRouted ? { sessionRouted: true, deliveredToSession: routedSessionId } : {}
|
|
26560
26651
|
}, null, 2));
|
|
26561
26652
|
return;
|
|
26562
26653
|
}
|
|
26563
26654
|
console.log(success(`Submitted — job ${jobId}`));
|
|
26564
26655
|
console.log(label("Status", "PENDING — generating in the background"));
|
|
26565
26656
|
console.log(label("Check result", `amiko create status ${jobId}`));
|
|
26657
|
+
if (sessionRouted) {
|
|
26658
|
+
console.log(dim("A generating-placeholder is already showing in the chat and the finished media will be delivered there automatically — do not send the file or URL yourself."));
|
|
26659
|
+
}
|
|
26566
26660
|
console.log(dim(`Usually ready in ${ETA[opts.mode]} — charged only on success. (Also: amiko create media)`));
|
|
26567
26661
|
if (opts.mode === "VIDEO") {
|
|
26568
26662
|
console.log(dim("Async video: HTTP 202 / PENDING means the job was accepted — not a failure. Poll status after ~2–9 min."));
|
|
@@ -26610,7 +26704,8 @@ function registerCreateCommand(create2) {
|
|
|
26610
26704
|
mode: "VIDEO",
|
|
26611
26705
|
model,
|
|
26612
26706
|
size: opts.resolution,
|
|
26613
|
-
seconds
|
|
26707
|
+
seconds,
|
|
26708
|
+
...referenceVideoUrls.length ? { referenceVideoUrls } : {}
|
|
26614
26709
|
},
|
|
26615
26710
|
jobBody: {
|
|
26616
26711
|
mode: "VIDEO",
|
|
@@ -26637,19 +26732,54 @@ function registerCreateCommand(create2) {
|
|
|
26637
26732
|
}
|
|
26638
26733
|
});
|
|
26639
26734
|
});
|
|
26640
|
-
|
|
26735
|
+
const DEFAULT_EL_TTS_MODEL = "eleven_multilingual_v2";
|
|
26736
|
+
const DEFAULT_EL_TTS_VOICE = "21m00Tcm4TlvDq8ikWAM";
|
|
26737
|
+
const DEFAULT_MM_TTS_MODEL = "speech-2.8-turbo";
|
|
26738
|
+
const DEFAULT_MM_TTS_VOICE = "English_expressive_narrator";
|
|
26739
|
+
const MINIMAX_TTS_MODELS = new Set([
|
|
26740
|
+
"speech-2.8-turbo",
|
|
26741
|
+
"speech-2.8-hd",
|
|
26742
|
+
"speech-2.6-turbo",
|
|
26743
|
+
"speech-2.6-hd"
|
|
26744
|
+
]);
|
|
26745
|
+
create2.command("tts <text>").description("Generate speech from text (Create Studio)").option("--provider <name>", "TTS provider: minimax | elevenlabs (sets defaults when --model/--voice omitted)").option("--model <model>", `TTS model id (MiniMax: speech-2.8-turbo|hd, speech-2.6-turbo|hd; ElevenLabs: eleven_*)`).option("--voice <voiceId>", "Voice id (MiniMax system e.g. English_expressive_narrator, or a cloned voice_id; ElevenLabs voice hash)").option("--token <symbol>", "Preferred charge token: AMIKO, USDC, USDT, SOL").option("--pay <method>", "Payment method: wallet (twin wallet, default) or credits (Amiko account credits — unified accounts only)", "wallet").option("--raw", "Output raw JSON").option("--yes", "Skip the pre-spend confirmation (required in non-interactive shells)").action(async (text, opts) => {
|
|
26746
|
+
const providerRaw = typeof opts.provider === "string" ? opts.provider.trim().toLowerCase() : "";
|
|
26747
|
+
if (providerRaw && providerRaw !== "minimax" && providerRaw !== "elevenlabs") {
|
|
26748
|
+
console.error(error("--provider must be minimax or elevenlabs"));
|
|
26749
|
+
process.exit(1);
|
|
26750
|
+
}
|
|
26751
|
+
let model = typeof opts.model === "string" && opts.model.trim().length > 0 ? opts.model.trim() : undefined;
|
|
26752
|
+
let voice = typeof opts.voice === "string" && opts.voice.trim().length > 0 ? opts.voice.trim() : undefined;
|
|
26753
|
+
let provider = providerRaw === "minimax" || providerRaw === "elevenlabs" ? providerRaw : model && MINIMAX_TTS_MODELS.has(model) ? "minimax" : model && model.startsWith("eleven") ? "elevenlabs" : "elevenlabs";
|
|
26754
|
+
if (!model) {
|
|
26755
|
+
model = provider === "minimax" ? DEFAULT_MM_TTS_MODEL : DEFAULT_EL_TTS_MODEL;
|
|
26756
|
+
}
|
|
26757
|
+
if (!voice) {
|
|
26758
|
+
voice = provider === "minimax" ? DEFAULT_MM_TTS_VOICE : DEFAULT_EL_TTS_VOICE;
|
|
26759
|
+
}
|
|
26760
|
+
if (MINIMAX_TTS_MODELS.has(model) && providerRaw !== "elevenlabs") {
|
|
26761
|
+
provider = "minimax";
|
|
26762
|
+
}
|
|
26763
|
+
if (provider === "minimax" && !MINIMAX_TTS_MODELS.has(model)) {
|
|
26764
|
+
console.error(error(`--provider minimax requires a MiniMax speech model (${[...MINIMAX_TTS_MODELS].join(", ")})`));
|
|
26765
|
+
process.exit(1);
|
|
26766
|
+
}
|
|
26767
|
+
if (provider === "elevenlabs" && !model.startsWith("eleven")) {
|
|
26768
|
+
console.error(error("--provider elevenlabs requires an eleven_* model"));
|
|
26769
|
+
process.exit(1);
|
|
26770
|
+
}
|
|
26641
26771
|
await submitCreateJob({
|
|
26642
26772
|
mode: "TTS",
|
|
26643
|
-
summary: `Create speech: ${text.slice(0, 80)}${text.length > 80 ? "…" : ""}`,
|
|
26773
|
+
summary: `Create speech (${provider}/${model}): ${text.slice(0, 80)}${text.length > 80 ? "…" : ""}`,
|
|
26644
26774
|
raw: opts.raw,
|
|
26645
26775
|
pay: opts.pay,
|
|
26646
26776
|
yes: opts.yes,
|
|
26647
|
-
quoteBody: { mode: "TTS", model
|
|
26777
|
+
quoteBody: { mode: "TTS", model, prompt: text, voiceId: voice },
|
|
26648
26778
|
jobBody: {
|
|
26649
26779
|
mode: "TTS",
|
|
26650
26780
|
prompt: text,
|
|
26651
|
-
model
|
|
26652
|
-
voiceId:
|
|
26781
|
+
model,
|
|
26782
|
+
voiceId: voice,
|
|
26653
26783
|
...opts.token ? { preferredToken: opts.token } : {}
|
|
26654
26784
|
}
|
|
26655
26785
|
});
|
|
@@ -26713,8 +26843,9 @@ function registerCreateCommand(create2) {
|
|
|
26713
26843
|
}
|
|
26714
26844
|
process.exit(1);
|
|
26715
26845
|
}
|
|
26846
|
+
const deliveredToThisSession = !!(p.agentId && p.sessionId && auth.sessionId && p.sessionId === auth.sessionId);
|
|
26716
26847
|
if (opts.raw) {
|
|
26717
|
-
console.log(JSON.stringify(p, null, 2));
|
|
26848
|
+
console.log(JSON.stringify(deliveredToThisSession ? { ...p, sessionRouted: true, deliveredToSession: p.sessionId } : p, null, 2));
|
|
26718
26849
|
return;
|
|
26719
26850
|
}
|
|
26720
26851
|
if (p.status === "FAILED") {
|
|
@@ -26726,7 +26857,7 @@ function registerCreateCommand(create2) {
|
|
|
26726
26857
|
console.log(dim(`Job id: ${jobId}`));
|
|
26727
26858
|
return;
|
|
26728
26859
|
}
|
|
26729
|
-
printCompleted(p, "Job complete");
|
|
26860
|
+
printCompleted(p, "Job complete", deliveredToThisSession);
|
|
26730
26861
|
});
|
|
26731
26862
|
create2.command("media").description("List your recent Create Studio generations").option("--limit <n>", "How many to show (max 50)", "20").option("--service <type>", "Filter by type: image, video, tts, music, sfx").option("--raw", "Output raw JSON").action(async (opts) => {
|
|
26732
26863
|
let auth;
|
|
@@ -28311,18 +28442,32 @@ async function fetchWithTimeout2(url2, init = {}, timeoutMs) {
|
|
|
28311
28442
|
}
|
|
28312
28443
|
function registerServicesCommand(program2) {
|
|
28313
28444
|
const svc = program2.command("service").alias("svc").description("Call any MPP service — pay in AMIKO, USDC, or USDT");
|
|
28314
|
-
svc.command("tts <voiceId> <text>").description("Text-to-speech
|
|
28445
|
+
svc.command("tts <voiceId> <text>").description("Text-to-speech via mpp /v1/tts (pay-per-call)").option("--provider <name>", "elevenlabs (default) | minimax").option("--model <model>", "MiniMax speech-* model when provider=minimax").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 (voiceId, text, opts) => {
|
|
28446
|
+
const provider = typeof opts.provider === "string" ? opts.provider.trim().toLowerCase() : "";
|
|
28447
|
+
const body = { text };
|
|
28448
|
+
if (provider === "minimax") {
|
|
28449
|
+
body.provider = "minimax";
|
|
28450
|
+
if (opts.model)
|
|
28451
|
+
body.model_id = opts.model;
|
|
28452
|
+
else
|
|
28453
|
+
body.model_id = "speech-2.8-turbo";
|
|
28454
|
+
} else if (provider && provider !== "elevenlabs") {
|
|
28455
|
+
console.error(error("--provider must be minimax or elevenlabs"));
|
|
28456
|
+
process.exit(1);
|
|
28457
|
+
}
|
|
28458
|
+
const providerArgs = provider ? ` --provider ${provider}` : "";
|
|
28459
|
+
const modelArgs = provider === "minimax" ? ` --model ${JSON.stringify(typeof opts.model === "string" && opts.model ? opts.model : "speech-2.8-turbo")}` : "";
|
|
28315
28460
|
await requireApproval({
|
|
28316
|
-
cost: "
|
|
28317
|
-
summary: `Text-to-speech (voice ${voiceId}): "${text.slice(0, 60)}${text.length > 60 ? "…" : ""}"`,
|
|
28461
|
+
cost: "variable (see mpp pricing)",
|
|
28462
|
+
summary: `Text-to-speech (voice ${voiceId}${provider ? `, ${provider}` : ""}): "${text.slice(0, 60)}${text.length > 60 ? "…" : ""}"`,
|
|
28318
28463
|
yes: opts.yes,
|
|
28319
|
-
commandExample: `amiko markets service tts ${voiceId} ${JSON.stringify(text)}`
|
|
28464
|
+
commandExample: `amiko markets service tts ${voiceId} ${JSON.stringify(text)}${providerArgs}${modelArgs}`
|
|
28320
28465
|
});
|
|
28321
28466
|
await callService({
|
|
28322
28467
|
name: "Text-to-Speech",
|
|
28323
28468
|
method: "POST",
|
|
28324
28469
|
path: `/v1/tts/${encodeURIComponent(voiceId)}`,
|
|
28325
|
-
body
|
|
28470
|
+
body,
|
|
28326
28471
|
amiko: "1",
|
|
28327
28472
|
opts
|
|
28328
28473
|
});
|
|
@@ -28534,7 +28679,7 @@ function registerAccountsCommand(program2) {
|
|
|
28534
28679
|
}
|
|
28535
28680
|
}, { json: opts.json });
|
|
28536
28681
|
});
|
|
28537
|
-
program2.command("info").description("Get twin info (name, description, voice, avatar)").option("--twin <idOrName>", "Target twin id or name").option("--json", "Output as JSON").action(async (opts) => {
|
|
28682
|
+
program2.command("info").description("Get twin info (name, description, voice ids, avatar)").option("--twin <idOrName>", "Target twin id or name").option("--json", "Output as JSON").action(async (opts) => {
|
|
28538
28683
|
const config2 = loadConfig();
|
|
28539
28684
|
const auth = resolveAuth();
|
|
28540
28685
|
let twinId;
|
|
@@ -28558,13 +28703,24 @@ function registerAccountsCommand(program2) {
|
|
|
28558
28703
|
console.log(label("Description", t.description));
|
|
28559
28704
|
console.log(label("Public", t.is_public ? "yes" : "no"));
|
|
28560
28705
|
if (t.voice_status)
|
|
28561
|
-
console.log(label("Voice", t.voice_status));
|
|
28706
|
+
console.log(label("Voice status", t.voice_status));
|
|
28707
|
+
if (t.voice_id)
|
|
28708
|
+
console.log(label("ElevenLabs voice_id", t.voice_id));
|
|
28709
|
+
else
|
|
28710
|
+
console.log(label("ElevenLabs voice_id", dim("(none)")));
|
|
28711
|
+
if (t.minimax_voice_id)
|
|
28712
|
+
console.log(label("MiniMax voice_id", t.minimax_voice_id));
|
|
28713
|
+
else
|
|
28714
|
+
console.log(label("MiniMax voice_id", dim("(none — clone with amiko voice clone)")));
|
|
28562
28715
|
if (t.avatar_url)
|
|
28563
28716
|
console.log(label("Avatar", t.avatar_url));
|
|
28564
28717
|
if (t.shipped_at)
|
|
28565
28718
|
console.log(label("Shipped", t.shipped_at));
|
|
28566
28719
|
if (t.updated_at)
|
|
28567
28720
|
console.log(dim(`Updated ${t.updated_at}`));
|
|
28721
|
+
console.log("");
|
|
28722
|
+
console.log(dim('MiniMax TTS: amiko create tts "…" --provider minimax --voice <MiniMax voice_id> --yes'));
|
|
28723
|
+
console.log(dim('ElevenLabs TTS: amiko create tts "…" --provider elevenlabs --voice <ElevenLabs voice_id> --yes'));
|
|
28568
28724
|
}, { json: opts.json });
|
|
28569
28725
|
} catch (e5) {
|
|
28570
28726
|
spinner?.stop();
|
|
@@ -29201,8 +29357,18 @@ function twinFromFlag2(opts) {
|
|
|
29201
29357
|
throw e5;
|
|
29202
29358
|
}
|
|
29203
29359
|
}
|
|
29360
|
+
function parseProvider(raw, fallback2) {
|
|
29361
|
+
if (raw == null || raw.trim() === "")
|
|
29362
|
+
return fallback2;
|
|
29363
|
+
const p = raw.trim().toLowerCase();
|
|
29364
|
+
if (p !== "minimax" && p !== "elevenlabs") {
|
|
29365
|
+
console.error(error("--provider must be minimax or elevenlabs"));
|
|
29366
|
+
process.exit(1);
|
|
29367
|
+
}
|
|
29368
|
+
return p;
|
|
29369
|
+
}
|
|
29204
29370
|
function registerVoiceCommand(program2) {
|
|
29205
|
-
program2.command("design").description("Design a voice from a text description").requiredOption("--description <text>", "Voice description (min 20 chars)").option("--twin <idOrName>", "Target twin id or name").option("--json", "Output as JSON").action(async (opts) => {
|
|
29371
|
+
program2.command("design").description("Design a voice from a text description (ElevenLabs)").requiredOption("--description <text>", "Voice description (min 20 chars)").option("--twin <idOrName>", "Target twin id or name").option("--json", "Output as JSON").action(async (opts) => {
|
|
29206
29372
|
if (opts.description.trim().length < 20) {
|
|
29207
29373
|
console.error(error("--description must be at least 20 characters"));
|
|
29208
29374
|
process.exit(1);
|
|
@@ -29230,7 +29396,7 @@ function registerVoiceCommand(program2) {
|
|
|
29230
29396
|
process.exit(1);
|
|
29231
29397
|
}
|
|
29232
29398
|
});
|
|
29233
|
-
program2.command("create").description("Create a voice from a previously designed preview").requiredOption("--sample <id>", "generated_voice_id from voice design").option("--description <text>", "Optional voice description").option("--twin <idOrName>", "Target twin id or name").option("--json", "Output as JSON").action(async (opts) => {
|
|
29399
|
+
program2.command("create").description("Create a voice from a previously designed preview (ElevenLabs)").requiredOption("--sample <id>", "generated_voice_id from voice design").option("--description <text>", "Optional voice description").option("--twin <idOrName>", "Target twin id or name").option("--json", "Output as JSON").action(async (opts) => {
|
|
29234
29400
|
const { twinId, auth } = twinFromFlag2(opts);
|
|
29235
29401
|
const spinner = opts.json ? null : ora("Creating voice...").start();
|
|
29236
29402
|
try {
|
|
@@ -29256,7 +29422,8 @@ function registerVoiceCommand(program2) {
|
|
|
29256
29422
|
process.exit(1);
|
|
29257
29423
|
}
|
|
29258
29424
|
});
|
|
29259
|
-
program2.command("clone <source>").description("Clone a voice from an audio file (path | https URL | -)").option("--twin <idOrName>", "Target twin id or name").option("--name <filename>", "Override filename").option("--stdin", "Read from stdin (source must be -)").option("--json", "Output as JSON").action(async (source, opts) => {
|
|
29425
|
+
program2.command("clone <source>").description("Clone a voice from an audio file (path | https URL | -). Default provider: minimax").option("--provider <name>", "minimax (default, mpp rapid clone) | elevenlabs (platform IVC)", "minimax").option("--voice-id <id>", "MiniMax: optional custom voice_id (8–256 chars)").option("--model <model>", "MiniMax preview TTS model", "speech-2.8-turbo").option("--preview-text <text>", "MiniMax: preview line (empty string skips preview)").option("--twin <idOrName>", "Target twin id or name").option("--name <filename>", "Override filename").option("--stdin", "Read from stdin (source must be -)").option("--json", "Output as JSON").action(async (source, opts) => {
|
|
29426
|
+
const provider = parseProvider(opts.provider, "minimax");
|
|
29260
29427
|
const { twinId, auth } = twinFromFlag2(opts);
|
|
29261
29428
|
const spinner = opts.json ? null : ora("Reading audio...").start();
|
|
29262
29429
|
try {
|
|
@@ -29266,17 +29433,30 @@ function registerVoiceCommand(program2) {
|
|
|
29266
29433
|
hintMime: "audio/mpeg"
|
|
29267
29434
|
});
|
|
29268
29435
|
if (spinner)
|
|
29269
|
-
spinner.text = `Cloning voice from ${file2.name}...`;
|
|
29436
|
+
spinner.text = `Cloning voice (${provider}) from ${file2.name}...`;
|
|
29270
29437
|
const form = new FormData;
|
|
29271
29438
|
form.set("audio", new Blob([new Uint8Array(file2.buffer)], { type: file2.mime }), file2.name);
|
|
29439
|
+
form.set("provider", provider);
|
|
29440
|
+
if (provider === "minimax") {
|
|
29441
|
+
if (opts.voiceId)
|
|
29442
|
+
form.set("voice_id", opts.voiceId);
|
|
29443
|
+
if (opts.model)
|
|
29444
|
+
form.set("model_id", opts.model);
|
|
29445
|
+
if (opts.previewText !== undefined)
|
|
29446
|
+
form.set("text", opts.previewText);
|
|
29447
|
+
}
|
|
29272
29448
|
const result = await amikoWebFetch(auth, `/api/agents/${twinId}/voice/clone`, { method: "POST", multipart: form, timeoutMs: 180000 });
|
|
29273
29449
|
spinner?.stop();
|
|
29274
29450
|
renderOutput(result, (r) => {
|
|
29275
|
-
console.log(success(
|
|
29451
|
+
console.log(success(`Voice cloned (${String(r.provider ?? provider)})`));
|
|
29276
29452
|
if (r.voice_id)
|
|
29277
29453
|
console.log(label("Voice ID", String(r.voice_id)));
|
|
29278
29454
|
if (r.voice_status)
|
|
29279
29455
|
console.log(label("Status", String(r.voice_status)));
|
|
29456
|
+
if (r.demo_audio)
|
|
29457
|
+
console.log(label("Demo", String(r.demo_audio)));
|
|
29458
|
+
console.log("");
|
|
29459
|
+
console.log(dim(`Synthesize: amiko create tts "Hello" --provider ${provider} --voice ${String(r.voice_id ?? "<id>")} --yes`));
|
|
29280
29460
|
}, { json: opts.json });
|
|
29281
29461
|
} catch (e5) {
|
|
29282
29462
|
spinner?.stop();
|
package/package.json
CHANGED
package/skills/SKILL.md
CHANGED
|
@@ -35,6 +35,8 @@ Call your shell tool (your runtime calls it `bash`, `shell`, `run`, or similar)
|
|
|
35
35
|
| "share the group's invite link" (asked by an admin) | shell → `amiko chat group info "<group>"` (confirm they're admin) → `amiko chat group invite "<group>"` |
|
|
36
36
|
| "react to the latest message from Ava about the team with a heart" | shell → `amiko chat read "Ava"` (find that message, copy its `id`) → `amiko chat react <messageId> ❤️ --yes` |
|
|
37
37
|
| "what can amiko do?" | shell → `amiko --help` |
|
|
38
|
+
| "what's my MiniMax / ElevenLabs voice id?" | shell → `amiko info` |
|
|
39
|
+
| "speak with my cloned MiniMax voice" | shell → `amiko info`, quote the cost, obtain explicit approval, then run `amiko create tts "…" --provider minimax --voice <minimax_voice_id> --yes` |
|
|
38
40
|
|
|
39
41
|
The CLI is installed globally and is pre-authenticated when you're inside your workspace folder. Never suggest `amiko login` or `amiko connect` — they don't exist.
|
|
40
42
|
|
|
@@ -96,6 +98,21 @@ All paid markets commands auto-select the twin's active Solana wallet (see `--wa
|
|
|
96
98
|
|
|
97
99
|
**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`.
|
|
98
100
|
|
|
101
|
+
### Voice IDs — never guess
|
|
102
|
+
|
|
103
|
+
This twin has **two** optional TTS ids. They are **not** the same:
|
|
104
|
+
|
|
105
|
+
| Field | Provider | How to get it | How to use |
|
|
106
|
+
|---|---|---|---|
|
|
107
|
+
| `voice_id` | ElevenLabs | shown by `amiko info` | quote cost + approval, then `amiko create tts "…" --provider elevenlabs --voice <voice_id> --yes` |
|
|
108
|
+
| `minimax_voice_id` | MiniMax | shown by `amiko info` | quote cost + approval, then `amiko create tts "…" --provider minimax --voice <minimax_voice_id> --yes` |
|
|
109
|
+
|
|
110
|
+
- **Before any TTS or clone follow-up**, run `amiko info` (or `amiko info --json`) and read the printed ids. Do **not** invent a `voice_id`, ask the owner to paste one you already have on the twin, or claim MiniMax TTS is blocked because you "don't know" the id.
|
|
111
|
+
- Paid voice ops still need the Critical Rules gate: quote live cost, get explicit approval, only then append `--yes`.
|
|
112
|
+
- If MiniMax voice_id is `(none)`, quote the clone cost, obtain explicit approval, then run `amiko voice clone <audio> --provider minimax --yes` and `amiko info` again.
|
|
113
|
+
- System MiniMax voices (no clone) also work: e.g. `--voice English_expressive_narrator --provider minimax` (same approval gate before `--yes`).
|
|
114
|
+
- `markets service tts <voiceId> "…" --provider minimax --yes` needs the same MiniMax id and approval gate.
|
|
115
|
+
|
|
99
116
|
**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.
|
|
100
117
|
|
|
101
118
|
## Create Studio — behavior notes
|