@heyamiko/amiko-cli 0.14.0-beta.12 → 0.14.0-beta.13
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 +29 -4
- package/dist/index.js +159 -30
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# @heyamiko/amiko-cli (v0.14.0-beta.
|
|
1
|
+
# @heyamiko/amiko-cli (v0.14.0-beta.13)
|
|
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,12 @@ npm publish
|
|
|
564
583
|
|
|
565
584
|
## Changelog
|
|
566
585
|
|
|
586
|
+
### 0.14.0-beta.13
|
|
587
|
+
|
|
588
|
+
- **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`.
|
|
589
|
+
- **`amiko voice clone`** defaults to MiniMax (mpp rapid clone + ownership registry); `--provider elevenlabs` keeps legacy IVC. MiniMax-only flags: `--voice-id`, `--model`, `--preview-text`.
|
|
590
|
+
- **`amiko markets service tts`** gains `--provider minimax|elevenlabs` and `--model` for MiniMax speech routing on `/v1/tts/:voiceId`.
|
|
591
|
+
|
|
567
592
|
### 0.14.0-beta.7
|
|
568
593
|
|
|
569
594
|
- **`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");
|
|
@@ -23661,21 +23661,34 @@ function readFromFile(path) {
|
|
|
23661
23661
|
return null;
|
|
23662
23662
|
}
|
|
23663
23663
|
}
|
|
23664
|
+
function readRoutingIdsFromEnv() {
|
|
23665
|
+
return {
|
|
23666
|
+
agentId: asString(process.env.AMIKO_AGENT_ID),
|
|
23667
|
+
sessionId: asString(process.env.AMIKO_SESSION_ID)
|
|
23668
|
+
};
|
|
23669
|
+
}
|
|
23664
23670
|
function readFromEnv() {
|
|
23665
23671
|
return {
|
|
23666
23672
|
twinToken: asString(process.env.AMIKO_TWIN_TOKEN) ?? asString(process.env.AMIKO_USER_TOKEN),
|
|
23667
23673
|
twinId: asString(process.env.AMIKO_TWIN_ID),
|
|
23668
23674
|
userId: asString(process.env.AMIKO_USER_ID),
|
|
23669
23675
|
platformUrl: asString(process.env.AMIKO_PLATFORM_URL),
|
|
23670
|
-
apiBase: asString(process.env.AMIKO_API_BASE)
|
|
23676
|
+
apiBase: asString(process.env.AMIKO_API_BASE),
|
|
23677
|
+
...readRoutingIdsFromEnv()
|
|
23671
23678
|
};
|
|
23672
23679
|
}
|
|
23680
|
+
function resolveFileAuth(path) {
|
|
23681
|
+
const fromFile = readFromFile(path);
|
|
23682
|
+
if (!fromFile)
|
|
23683
|
+
return null;
|
|
23684
|
+
return { ...fromFile, ...readRoutingIdsFromEnv() };
|
|
23685
|
+
}
|
|
23673
23686
|
function resolveAuth() {
|
|
23674
23687
|
const filePath = findAmikoJsonPath();
|
|
23675
23688
|
if (filePath) {
|
|
23676
|
-
const
|
|
23677
|
-
if (
|
|
23678
|
-
return
|
|
23689
|
+
const merged = resolveFileAuth(filePath);
|
|
23690
|
+
if (merged)
|
|
23691
|
+
return merged;
|
|
23679
23692
|
}
|
|
23680
23693
|
return readFromEnv();
|
|
23681
23694
|
}
|
|
@@ -26442,6 +26455,13 @@ var ETA = {
|
|
|
26442
26455
|
function statusOf(err) {
|
|
26443
26456
|
return err?.status;
|
|
26444
26457
|
}
|
|
26458
|
+
function autoApproveLimit() {
|
|
26459
|
+
const raw = process.env.AMIKO_AUTO_APPROVE_LIMIT;
|
|
26460
|
+
if (typeof raw !== "string" || raw.trim() === "")
|
|
26461
|
+
return;
|
|
26462
|
+
const limit = Number(raw);
|
|
26463
|
+
return Number.isFinite(limit) ? limit : undefined;
|
|
26464
|
+
}
|
|
26445
26465
|
function collectOption(value, previous) {
|
|
26446
26466
|
return [...previous ?? [], value];
|
|
26447
26467
|
}
|
|
@@ -26451,7 +26471,7 @@ function assertMaxUrls(resourceName, urls, max) {
|
|
|
26451
26471
|
process.exit(1);
|
|
26452
26472
|
}
|
|
26453
26473
|
}
|
|
26454
|
-
function printCompleted(final, title) {
|
|
26474
|
+
function printCompleted(final, title, deliveredToThisSession = false) {
|
|
26455
26475
|
console.log(heading(`${title}
|
|
26456
26476
|
`));
|
|
26457
26477
|
if (final.prompt)
|
|
@@ -26460,6 +26480,9 @@ function printCompleted(final, title) {
|
|
|
26460
26480
|
console.log(label("URL", final.assetUrl));
|
|
26461
26481
|
if (final.assetMime)
|
|
26462
26482
|
console.log(label("Type", final.assetMime));
|
|
26483
|
+
if (deliveredToThisSession) {
|
|
26484
|
+
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."));
|
|
26485
|
+
}
|
|
26463
26486
|
console.log("");
|
|
26464
26487
|
if (final.charged) {
|
|
26465
26488
|
const amt = final.chargeAmount ?? final.costAmiko ?? "?";
|
|
@@ -26488,6 +26511,7 @@ async function submitCreateJob(opts) {
|
|
|
26488
26511
|
console.error(dim("--token is ignored with --pay credits (credits come from your Amiko account, not a wallet token)."));
|
|
26489
26512
|
}
|
|
26490
26513
|
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)";
|
|
26514
|
+
let quotedCostAmiko;
|
|
26491
26515
|
try {
|
|
26492
26516
|
const q = await amikoWebFetch(auth, "/api/create/quote", {
|
|
26493
26517
|
method: "POST",
|
|
@@ -26497,11 +26521,29 @@ async function submitCreateJob(opts) {
|
|
|
26497
26521
|
if (q.cost_usd != null) {
|
|
26498
26522
|
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
26523
|
}
|
|
26524
|
+
if (typeof q.cost_amiko === "string" && q.cost_amiko.trim() !== "") {
|
|
26525
|
+
const n = Number(q.cost_amiko);
|
|
26526
|
+
if (Number.isFinite(n))
|
|
26527
|
+
quotedCostAmiko = n;
|
|
26528
|
+
}
|
|
26500
26529
|
} catch {}
|
|
26530
|
+
const preferredToken = typeof opts.jobBody.preferredToken === "string" ? opts.jobBody.preferredToken.toUpperCase() : undefined;
|
|
26531
|
+
const chargeIsAmiko = !payCredits && (preferredToken === undefined || preferredToken === "AMIKO");
|
|
26532
|
+
const effectiveChargeToken = preferredToken ?? "AMIKO";
|
|
26533
|
+
let yes = opts.yes;
|
|
26534
|
+
let autoApproved = false;
|
|
26535
|
+
if (!yes) {
|
|
26536
|
+
const limit = autoApproveLimit();
|
|
26537
|
+
if (limit !== undefined && quotedCostAmiko !== undefined && quotedCostAmiko > 0 && chargeIsAmiko && quotedCostAmiko <= limit) {
|
|
26538
|
+
yes = true;
|
|
26539
|
+
autoApproved = true;
|
|
26540
|
+
console.log(dim(`Auto-approved (cost ${quotedCostAmiko} ≤ limit ${limit} AMIKO)`));
|
|
26541
|
+
}
|
|
26542
|
+
}
|
|
26501
26543
|
await requireApproval({
|
|
26502
26544
|
cost: costLine,
|
|
26503
26545
|
summary: opts.summary,
|
|
26504
|
-
yes
|
|
26546
|
+
yes,
|
|
26505
26547
|
commandExample: `amiko create ${opts.mode.toLowerCase()} ...`
|
|
26506
26548
|
});
|
|
26507
26549
|
const isTTY = process.stderr.isTTY;
|
|
@@ -26511,16 +26553,21 @@ async function submitCreateJob(opts) {
|
|
|
26511
26553
|
spinner.stop();
|
|
26512
26554
|
};
|
|
26513
26555
|
let jobId;
|
|
26556
|
+
let routedAgentId;
|
|
26557
|
+
let routedSessionId;
|
|
26514
26558
|
try {
|
|
26515
26559
|
const submit = await amikoWebFetch(auth, "/api/create/jobs", {
|
|
26516
26560
|
method: "POST",
|
|
26517
26561
|
body: {
|
|
26518
26562
|
...opts.jobBody,
|
|
26519
26563
|
twinId: auth.twinId,
|
|
26564
|
+
...autoApproved ? { preferredToken: effectiveChargeToken, strictToken: true } : {},
|
|
26520
26565
|
...payCredits ? {
|
|
26521
26566
|
paymentMethod: { kind: "account_credits" },
|
|
26522
26567
|
requestId: randomUUID()
|
|
26523
|
-
} : {}
|
|
26568
|
+
} : {},
|
|
26569
|
+
...auth.agentId ? { agentId: auth.agentId } : {},
|
|
26570
|
+
...auth.sessionId ? { sessionId: auth.sessionId } : {}
|
|
26524
26571
|
},
|
|
26525
26572
|
timeoutMs: 30000
|
|
26526
26573
|
});
|
|
@@ -26530,6 +26577,8 @@ async function submitCreateJob(opts) {
|
|
|
26530
26577
|
process.exit(1);
|
|
26531
26578
|
}
|
|
26532
26579
|
jobId = submit.jobId;
|
|
26580
|
+
routedAgentId = submit.agentId;
|
|
26581
|
+
routedSessionId = submit.sessionId;
|
|
26533
26582
|
} catch (e5) {
|
|
26534
26583
|
stop();
|
|
26535
26584
|
const webErr = e5;
|
|
@@ -26550,19 +26599,24 @@ async function submitCreateJob(opts) {
|
|
|
26550
26599
|
}
|
|
26551
26600
|
process.exit(1);
|
|
26552
26601
|
}
|
|
26602
|
+
const sessionRouted = !!(routedAgentId && routedSessionId);
|
|
26553
26603
|
stop();
|
|
26554
26604
|
if (opts.raw) {
|
|
26555
26605
|
console.log(JSON.stringify({
|
|
26556
26606
|
jobId,
|
|
26557
26607
|
status: "PENDING",
|
|
26558
26608
|
check: `amiko create status ${jobId}`,
|
|
26559
|
-
eta: ETA[opts.mode]
|
|
26609
|
+
eta: ETA[opts.mode],
|
|
26610
|
+
...sessionRouted ? { sessionRouted: true, deliveredToSession: routedSessionId } : {}
|
|
26560
26611
|
}, null, 2));
|
|
26561
26612
|
return;
|
|
26562
26613
|
}
|
|
26563
26614
|
console.log(success(`Submitted — job ${jobId}`));
|
|
26564
26615
|
console.log(label("Status", "PENDING — generating in the background"));
|
|
26565
26616
|
console.log(label("Check result", `amiko create status ${jobId}`));
|
|
26617
|
+
if (sessionRouted) {
|
|
26618
|
+
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."));
|
|
26619
|
+
}
|
|
26566
26620
|
console.log(dim(`Usually ready in ${ETA[opts.mode]} — charged only on success. (Also: amiko create media)`));
|
|
26567
26621
|
if (opts.mode === "VIDEO") {
|
|
26568
26622
|
console.log(dim("Async video: HTTP 202 / PENDING means the job was accepted — not a failure. Poll status after ~2–9 min."));
|
|
@@ -26610,7 +26664,8 @@ function registerCreateCommand(create2) {
|
|
|
26610
26664
|
mode: "VIDEO",
|
|
26611
26665
|
model,
|
|
26612
26666
|
size: opts.resolution,
|
|
26613
|
-
seconds
|
|
26667
|
+
seconds,
|
|
26668
|
+
...referenceVideoUrls.length ? { referenceVideoUrls } : {}
|
|
26614
26669
|
},
|
|
26615
26670
|
jobBody: {
|
|
26616
26671
|
mode: "VIDEO",
|
|
@@ -26637,19 +26692,54 @@ function registerCreateCommand(create2) {
|
|
|
26637
26692
|
}
|
|
26638
26693
|
});
|
|
26639
26694
|
});
|
|
26640
|
-
|
|
26695
|
+
const DEFAULT_EL_TTS_MODEL = "eleven_multilingual_v2";
|
|
26696
|
+
const DEFAULT_EL_TTS_VOICE = "21m00Tcm4TlvDq8ikWAM";
|
|
26697
|
+
const DEFAULT_MM_TTS_MODEL = "speech-2.8-turbo";
|
|
26698
|
+
const DEFAULT_MM_TTS_VOICE = "English_expressive_narrator";
|
|
26699
|
+
const MINIMAX_TTS_MODELS = new Set([
|
|
26700
|
+
"speech-2.8-turbo",
|
|
26701
|
+
"speech-2.8-hd",
|
|
26702
|
+
"speech-2.6-turbo",
|
|
26703
|
+
"speech-2.6-hd"
|
|
26704
|
+
]);
|
|
26705
|
+
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) => {
|
|
26706
|
+
const providerRaw = typeof opts.provider === "string" ? opts.provider.trim().toLowerCase() : "";
|
|
26707
|
+
if (providerRaw && providerRaw !== "minimax" && providerRaw !== "elevenlabs") {
|
|
26708
|
+
console.error(error("--provider must be minimax or elevenlabs"));
|
|
26709
|
+
process.exit(1);
|
|
26710
|
+
}
|
|
26711
|
+
let model = typeof opts.model === "string" && opts.model.trim().length > 0 ? opts.model.trim() : undefined;
|
|
26712
|
+
let voice = typeof opts.voice === "string" && opts.voice.trim().length > 0 ? opts.voice.trim() : undefined;
|
|
26713
|
+
let provider = providerRaw === "minimax" || providerRaw === "elevenlabs" ? providerRaw : model && MINIMAX_TTS_MODELS.has(model) ? "minimax" : model && model.startsWith("eleven") ? "elevenlabs" : "elevenlabs";
|
|
26714
|
+
if (!model) {
|
|
26715
|
+
model = provider === "minimax" ? DEFAULT_MM_TTS_MODEL : DEFAULT_EL_TTS_MODEL;
|
|
26716
|
+
}
|
|
26717
|
+
if (!voice) {
|
|
26718
|
+
voice = provider === "minimax" ? DEFAULT_MM_TTS_VOICE : DEFAULT_EL_TTS_VOICE;
|
|
26719
|
+
}
|
|
26720
|
+
if (MINIMAX_TTS_MODELS.has(model) && providerRaw !== "elevenlabs") {
|
|
26721
|
+
provider = "minimax";
|
|
26722
|
+
}
|
|
26723
|
+
if (provider === "minimax" && !MINIMAX_TTS_MODELS.has(model)) {
|
|
26724
|
+
console.error(error(`--provider minimax requires a MiniMax speech model (${[...MINIMAX_TTS_MODELS].join(", ")})`));
|
|
26725
|
+
process.exit(1);
|
|
26726
|
+
}
|
|
26727
|
+
if (provider === "elevenlabs" && !model.startsWith("eleven")) {
|
|
26728
|
+
console.error(error("--provider elevenlabs requires an eleven_* model"));
|
|
26729
|
+
process.exit(1);
|
|
26730
|
+
}
|
|
26641
26731
|
await submitCreateJob({
|
|
26642
26732
|
mode: "TTS",
|
|
26643
|
-
summary: `Create speech: ${text.slice(0, 80)}${text.length > 80 ? "…" : ""}`,
|
|
26733
|
+
summary: `Create speech (${provider}/${model}): ${text.slice(0, 80)}${text.length > 80 ? "…" : ""}`,
|
|
26644
26734
|
raw: opts.raw,
|
|
26645
26735
|
pay: opts.pay,
|
|
26646
26736
|
yes: opts.yes,
|
|
26647
|
-
quoteBody: { mode: "TTS", model
|
|
26737
|
+
quoteBody: { mode: "TTS", model, prompt: text, voiceId: voice },
|
|
26648
26738
|
jobBody: {
|
|
26649
26739
|
mode: "TTS",
|
|
26650
26740
|
prompt: text,
|
|
26651
|
-
model
|
|
26652
|
-
voiceId:
|
|
26741
|
+
model,
|
|
26742
|
+
voiceId: voice,
|
|
26653
26743
|
...opts.token ? { preferredToken: opts.token } : {}
|
|
26654
26744
|
}
|
|
26655
26745
|
});
|
|
@@ -26713,8 +26803,9 @@ function registerCreateCommand(create2) {
|
|
|
26713
26803
|
}
|
|
26714
26804
|
process.exit(1);
|
|
26715
26805
|
}
|
|
26806
|
+
const deliveredToThisSession = !!(p.agentId && p.sessionId && auth.sessionId && p.sessionId === auth.sessionId);
|
|
26716
26807
|
if (opts.raw) {
|
|
26717
|
-
console.log(JSON.stringify(p, null, 2));
|
|
26808
|
+
console.log(JSON.stringify(deliveredToThisSession ? { ...p, sessionRouted: true, deliveredToSession: p.sessionId } : p, null, 2));
|
|
26718
26809
|
return;
|
|
26719
26810
|
}
|
|
26720
26811
|
if (p.status === "FAILED") {
|
|
@@ -26726,7 +26817,7 @@ function registerCreateCommand(create2) {
|
|
|
26726
26817
|
console.log(dim(`Job id: ${jobId}`));
|
|
26727
26818
|
return;
|
|
26728
26819
|
}
|
|
26729
|
-
printCompleted(p, "Job complete");
|
|
26820
|
+
printCompleted(p, "Job complete", deliveredToThisSession);
|
|
26730
26821
|
});
|
|
26731
26822
|
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
26823
|
let auth;
|
|
@@ -28311,18 +28402,32 @@ async function fetchWithTimeout2(url2, init = {}, timeoutMs) {
|
|
|
28311
28402
|
}
|
|
28312
28403
|
function registerServicesCommand(program2) {
|
|
28313
28404
|
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
|
|
28405
|
+
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) => {
|
|
28406
|
+
const provider = typeof opts.provider === "string" ? opts.provider.trim().toLowerCase() : "";
|
|
28407
|
+
const body = { text };
|
|
28408
|
+
if (provider === "minimax") {
|
|
28409
|
+
body.provider = "minimax";
|
|
28410
|
+
if (opts.model)
|
|
28411
|
+
body.model_id = opts.model;
|
|
28412
|
+
else
|
|
28413
|
+
body.model_id = "speech-2.8-turbo";
|
|
28414
|
+
} else if (provider && provider !== "elevenlabs") {
|
|
28415
|
+
console.error(error("--provider must be minimax or elevenlabs"));
|
|
28416
|
+
process.exit(1);
|
|
28417
|
+
}
|
|
28418
|
+
const providerArgs = provider ? ` --provider ${provider}` : "";
|
|
28419
|
+
const modelArgs = provider === "minimax" ? ` --model ${JSON.stringify(typeof opts.model === "string" && opts.model ? opts.model : "speech-2.8-turbo")}` : "";
|
|
28315
28420
|
await requireApproval({
|
|
28316
|
-
cost: "
|
|
28317
|
-
summary: `Text-to-speech (voice ${voiceId}): "${text.slice(0, 60)}${text.length > 60 ? "…" : ""}"`,
|
|
28421
|
+
cost: "variable (see mpp pricing)",
|
|
28422
|
+
summary: `Text-to-speech (voice ${voiceId}${provider ? `, ${provider}` : ""}): "${text.slice(0, 60)}${text.length > 60 ? "…" : ""}"`,
|
|
28318
28423
|
yes: opts.yes,
|
|
28319
|
-
commandExample: `amiko markets service tts ${voiceId} ${JSON.stringify(text)}`
|
|
28424
|
+
commandExample: `amiko markets service tts ${voiceId} ${JSON.stringify(text)}${providerArgs}${modelArgs}`
|
|
28320
28425
|
});
|
|
28321
28426
|
await callService({
|
|
28322
28427
|
name: "Text-to-Speech",
|
|
28323
28428
|
method: "POST",
|
|
28324
28429
|
path: `/v1/tts/${encodeURIComponent(voiceId)}`,
|
|
28325
|
-
body
|
|
28430
|
+
body,
|
|
28326
28431
|
amiko: "1",
|
|
28327
28432
|
opts
|
|
28328
28433
|
});
|
|
@@ -29201,8 +29306,18 @@ function twinFromFlag2(opts) {
|
|
|
29201
29306
|
throw e5;
|
|
29202
29307
|
}
|
|
29203
29308
|
}
|
|
29309
|
+
function parseProvider(raw, fallback2) {
|
|
29310
|
+
if (raw == null || raw.trim() === "")
|
|
29311
|
+
return fallback2;
|
|
29312
|
+
const p = raw.trim().toLowerCase();
|
|
29313
|
+
if (p !== "minimax" && p !== "elevenlabs") {
|
|
29314
|
+
console.error(error("--provider must be minimax or elevenlabs"));
|
|
29315
|
+
process.exit(1);
|
|
29316
|
+
}
|
|
29317
|
+
return p;
|
|
29318
|
+
}
|
|
29204
29319
|
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) => {
|
|
29320
|
+
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
29321
|
if (opts.description.trim().length < 20) {
|
|
29207
29322
|
console.error(error("--description must be at least 20 characters"));
|
|
29208
29323
|
process.exit(1);
|
|
@@ -29230,7 +29345,7 @@ function registerVoiceCommand(program2) {
|
|
|
29230
29345
|
process.exit(1);
|
|
29231
29346
|
}
|
|
29232
29347
|
});
|
|
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) => {
|
|
29348
|
+
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
29349
|
const { twinId, auth } = twinFromFlag2(opts);
|
|
29235
29350
|
const spinner = opts.json ? null : ora("Creating voice...").start();
|
|
29236
29351
|
try {
|
|
@@ -29256,7 +29371,8 @@ function registerVoiceCommand(program2) {
|
|
|
29256
29371
|
process.exit(1);
|
|
29257
29372
|
}
|
|
29258
29373
|
});
|
|
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) => {
|
|
29374
|
+
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) => {
|
|
29375
|
+
const provider = parseProvider(opts.provider, "minimax");
|
|
29260
29376
|
const { twinId, auth } = twinFromFlag2(opts);
|
|
29261
29377
|
const spinner = opts.json ? null : ora("Reading audio...").start();
|
|
29262
29378
|
try {
|
|
@@ -29266,17 +29382,30 @@ function registerVoiceCommand(program2) {
|
|
|
29266
29382
|
hintMime: "audio/mpeg"
|
|
29267
29383
|
});
|
|
29268
29384
|
if (spinner)
|
|
29269
|
-
spinner.text = `Cloning voice from ${file2.name}...`;
|
|
29385
|
+
spinner.text = `Cloning voice (${provider}) from ${file2.name}...`;
|
|
29270
29386
|
const form = new FormData;
|
|
29271
29387
|
form.set("audio", new Blob([new Uint8Array(file2.buffer)], { type: file2.mime }), file2.name);
|
|
29388
|
+
form.set("provider", provider);
|
|
29389
|
+
if (provider === "minimax") {
|
|
29390
|
+
if (opts.voiceId)
|
|
29391
|
+
form.set("voice_id", opts.voiceId);
|
|
29392
|
+
if (opts.model)
|
|
29393
|
+
form.set("model_id", opts.model);
|
|
29394
|
+
if (opts.previewText !== undefined)
|
|
29395
|
+
form.set("text", opts.previewText);
|
|
29396
|
+
}
|
|
29272
29397
|
const result = await amikoWebFetch(auth, `/api/agents/${twinId}/voice/clone`, { method: "POST", multipart: form, timeoutMs: 180000 });
|
|
29273
29398
|
spinner?.stop();
|
|
29274
29399
|
renderOutput(result, (r) => {
|
|
29275
|
-
console.log(success(
|
|
29400
|
+
console.log(success(`Voice cloned (${String(r.provider ?? provider)})`));
|
|
29276
29401
|
if (r.voice_id)
|
|
29277
29402
|
console.log(label("Voice ID", String(r.voice_id)));
|
|
29278
29403
|
if (r.voice_status)
|
|
29279
29404
|
console.log(label("Status", String(r.voice_status)));
|
|
29405
|
+
if (r.demo_audio)
|
|
29406
|
+
console.log(label("Demo", String(r.demo_audio)));
|
|
29407
|
+
console.log("");
|
|
29408
|
+
console.log(dim(`Synthesize: amiko create tts "Hello" --provider ${provider} --voice ${String(r.voice_id ?? "<id>")} --yes`));
|
|
29280
29409
|
}, { json: opts.json });
|
|
29281
29410
|
} catch (e5) {
|
|
29282
29411
|
spinner?.stop();
|