@heyamiko/amiko-cli 0.15.2 → 0.16.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 +2044 -36
- package/dist/index.js +1367 -427
- package/package.json +3 -2
- package/skills/SKILL.md +47 -9
package/dist/index.js
CHANGED
|
@@ -25320,6 +25320,11 @@ var init_client2 = __esm(() => {
|
|
|
25320
25320
|
// src/index.ts
|
|
25321
25321
|
import { createRequire as createRequire2 } from "node:module";
|
|
25322
25322
|
|
|
25323
|
+
// src/onboarding.ts
|
|
25324
|
+
async function runOnboarding(_argv) {
|
|
25325
|
+
return false;
|
|
25326
|
+
}
|
|
25327
|
+
|
|
25323
25328
|
// node_modules/commander/esm.mjs
|
|
25324
25329
|
var import__ = __toESM(require_commander(), 1);
|
|
25325
25330
|
var {
|
|
@@ -25336,11 +25341,6 @@ var {
|
|
|
25336
25341
|
Help
|
|
25337
25342
|
} = import__.default;
|
|
25338
25343
|
|
|
25339
|
-
// src/onboarding.ts
|
|
25340
|
-
async function runOnboarding(_argv) {
|
|
25341
|
-
return false;
|
|
25342
|
-
}
|
|
25343
|
-
|
|
25344
25344
|
// node_modules/chalk/source/vendor/ansi-styles/index.js
|
|
25345
25345
|
var ANSI_BACKGROUND_OFFSET = 10;
|
|
25346
25346
|
var wrapAnsi16 = (offset = 0) => (code) => `\x1B[${code + offset}m`;
|
|
@@ -27165,6 +27165,17 @@ function formatError(status, body, path) {
|
|
|
27165
27165
|
const snippet = body.length > 500 ? `${body.slice(0, 500)}...` : body;
|
|
27166
27166
|
return `${prefix}: ${snippet}`;
|
|
27167
27167
|
}
|
|
27168
|
+
async function presignedPut(uploadUrl, body, contentType) {
|
|
27169
|
+
const res = await fetch(uploadUrl, {
|
|
27170
|
+
method: "PUT",
|
|
27171
|
+
headers: { "Content-Type": contentType },
|
|
27172
|
+
body
|
|
27173
|
+
});
|
|
27174
|
+
if (!res.ok) {
|
|
27175
|
+
const text = await res.text().catch(() => "");
|
|
27176
|
+
throw new Error(`Presigned PUT failed: ${res.status} ${text.slice(0, 200)}`);
|
|
27177
|
+
}
|
|
27178
|
+
}
|
|
27168
27179
|
|
|
27169
27180
|
// src/lib/wallet-default.ts
|
|
27170
27181
|
var cache = null;
|
|
@@ -28404,107 +28415,606 @@ async function payTreasury(auth, args) {
|
|
|
28404
28415
|
return transferData.txHash;
|
|
28405
28416
|
}
|
|
28406
28417
|
|
|
28418
|
+
// src/lib/paid-search.ts
|
|
28419
|
+
function abort(spinner, e5, fallback2) {
|
|
28420
|
+
spinner?.stop();
|
|
28421
|
+
console.error(error(e5 instanceof Error ? e5.message : fallback2));
|
|
28422
|
+
return process.exit(1);
|
|
28423
|
+
}
|
|
28424
|
+
function formatUsd(usd2) {
|
|
28425
|
+
return `$${usd2.toFixed(3).replace(/0$/, "")}`;
|
|
28426
|
+
}
|
|
28427
|
+
function buildQuery(params) {
|
|
28428
|
+
const query = new URLSearchParams;
|
|
28429
|
+
for (const [key, value] of Object.entries(params)) {
|
|
28430
|
+
if (value !== undefined && value !== "")
|
|
28431
|
+
query.set(key, value);
|
|
28432
|
+
}
|
|
28433
|
+
return query;
|
|
28434
|
+
}
|
|
28435
|
+
async function paidSearch(req, opts, render) {
|
|
28436
|
+
let token;
|
|
28437
|
+
try {
|
|
28438
|
+
token = normalizePaymentToken(opts.token);
|
|
28439
|
+
} catch (e5) {
|
|
28440
|
+
abort(null, e5, "invalid payment token");
|
|
28441
|
+
}
|
|
28442
|
+
let quote;
|
|
28443
|
+
try {
|
|
28444
|
+
quote = await quoteTokenAmount(resolveAuth(), token, req.usd);
|
|
28445
|
+
} catch (e5) {
|
|
28446
|
+
abort(null, e5, "could not price the payment");
|
|
28447
|
+
}
|
|
28448
|
+
const amount2 = quote.amount;
|
|
28449
|
+
await requireApproval({
|
|
28450
|
+
cost: `${amount2} ${token} (≈ ${formatUsd(req.usd)})`,
|
|
28451
|
+
summary: req.label,
|
|
28452
|
+
yes: opts.yes,
|
|
28453
|
+
commandExample: req.commandExample
|
|
28454
|
+
});
|
|
28455
|
+
const spinner = ora(`${req.label}...`).start();
|
|
28456
|
+
const config2 = loadConfig();
|
|
28457
|
+
const mppBase2 = (config2.apiBase || "https://mpp.amikomarkets.com").replace(/\/$/, "");
|
|
28458
|
+
let auth;
|
|
28459
|
+
try {
|
|
28460
|
+
auth = requireAuth();
|
|
28461
|
+
} catch (e5) {
|
|
28462
|
+
abort(spinner, e5, "auth failed");
|
|
28463
|
+
}
|
|
28464
|
+
let wallet;
|
|
28465
|
+
try {
|
|
28466
|
+
wallet = await resolveWalletAddress(auth, opts.wallet, "solana");
|
|
28467
|
+
} catch (e5) {
|
|
28468
|
+
abort(spinner, e5, "could not resolve a wallet to pay from");
|
|
28469
|
+
}
|
|
28470
|
+
spinner.text = `Paying ${amount2} ${token} (solana)...`;
|
|
28471
|
+
let txHash;
|
|
28472
|
+
try {
|
|
28473
|
+
txHash = await payTreasury(auth, { wallet, amount: amount2, token });
|
|
28474
|
+
} catch (e5) {
|
|
28475
|
+
abort(spinner, e5, `${token} payment failed`);
|
|
28476
|
+
}
|
|
28477
|
+
const charged = dim(`Charged ${amount2} ${token} before this failed — tx: https://solscan.io/tx/${txHash}`);
|
|
28478
|
+
spinner.text = `Paid ${amount2} ${token} — ${req.label}...`;
|
|
28479
|
+
let res;
|
|
28480
|
+
try {
|
|
28481
|
+
res = await fetch(`${mppBase2}${req.path}?${buildQuery(req.params)}`, {
|
|
28482
|
+
headers: {
|
|
28483
|
+
"X-Payment-Tx": txHash,
|
|
28484
|
+
"X-Payment-Amount": amount2,
|
|
28485
|
+
"X-Payment-Token": token,
|
|
28486
|
+
"X-Twin-Id": auth.twinId
|
|
28487
|
+
}
|
|
28488
|
+
});
|
|
28489
|
+
} catch (e5) {
|
|
28490
|
+
spinner.stop();
|
|
28491
|
+
console.error(error(e5 instanceof Error ? e5.message : `${req.label} failed`));
|
|
28492
|
+
console.error(charged);
|
|
28493
|
+
process.exit(1);
|
|
28494
|
+
}
|
|
28495
|
+
spinner.stop();
|
|
28496
|
+
if (!res.ok) {
|
|
28497
|
+
await reportHttpError(res, req.label);
|
|
28498
|
+
console.error(charged);
|
|
28499
|
+
process.exit(1);
|
|
28500
|
+
}
|
|
28501
|
+
let data;
|
|
28502
|
+
try {
|
|
28503
|
+
data = await res.json();
|
|
28504
|
+
} catch (e5) {
|
|
28505
|
+
console.error(error("The service returned a response that could not be parsed as JSON."));
|
|
28506
|
+
console.error(charged);
|
|
28507
|
+
process.exit(1);
|
|
28508
|
+
}
|
|
28509
|
+
if (opts.raw) {
|
|
28510
|
+
console.log(JSON.stringify(data, null, 2));
|
|
28511
|
+
return;
|
|
28512
|
+
}
|
|
28513
|
+
render(data);
|
|
28514
|
+
console.log(success(`Paid ${amount2} ${token}`) + dim(` tx: ${txHash.slice(0, 16)}...`));
|
|
28515
|
+
await printBalanceRemaining(auth);
|
|
28516
|
+
}
|
|
28517
|
+
|
|
28407
28518
|
// src/commands/search.ts
|
|
28408
|
-
var
|
|
28519
|
+
var COUNTRY = { spec: "--country <code>", desc: "Country code (gl), e.g. us, gb, jp", param: "gl" };
|
|
28520
|
+
var LANG = { spec: "--lang <code>", desc: "Language code (hl), e.g. en, zh-cn", param: "hl" };
|
|
28521
|
+
var SINCE = { spec: "--since <period>", desc: "Time period, e.g. last_hour, last_day, last_week, last_year", param: "time_period" };
|
|
28522
|
+
function stars(rating, reviews) {
|
|
28523
|
+
if (!rating)
|
|
28524
|
+
return false;
|
|
28525
|
+
return reviews ? `${rating}★ (${reviews})` : `${rating}★`;
|
|
28526
|
+
}
|
|
28527
|
+
function clip(text, max = 160) {
|
|
28528
|
+
if (typeof text !== "string" || !text.trim())
|
|
28529
|
+
return;
|
|
28530
|
+
const flat = text.replace(/\s+/g, " ").trim();
|
|
28531
|
+
return flat.length > max ? `${flat.slice(0, max)}…` : flat;
|
|
28532
|
+
}
|
|
28533
|
+
var VERTICALS = [
|
|
28534
|
+
{
|
|
28535
|
+
name: "news",
|
|
28536
|
+
path: "/v1/search/news",
|
|
28537
|
+
usd: 0.01,
|
|
28538
|
+
summary: "Google News",
|
|
28539
|
+
flags: [COUNTRY, LANG, SINCE, { spec: "--sort <by>", desc: "Sort order, e.g. date", param: "sort_by" }],
|
|
28540
|
+
item: (r) => ({ title: r.title, body: clip(r.snippet), facts: [r.source, r.date], link: r.link })
|
|
28541
|
+
},
|
|
28542
|
+
{
|
|
28543
|
+
name: "images",
|
|
28544
|
+
path: "/v1/search/images",
|
|
28545
|
+
usd: 0.01,
|
|
28546
|
+
summary: "Google Images",
|
|
28547
|
+
flags: [
|
|
28548
|
+
COUNTRY,
|
|
28549
|
+
LANG,
|
|
28550
|
+
{ spec: "--size <size>", desc: "Image size, e.g. large, medium, icon", param: "size" },
|
|
28551
|
+
{ spec: "--color <color>", desc: "Dominant colour filter", param: "color" },
|
|
28552
|
+
{ spec: "--image-type <type>", desc: "e.g. photo, clipart, lineart, face", param: "image_type" }
|
|
28553
|
+
],
|
|
28554
|
+
item: (r) => ({ title: r.title, facts: [r.source], link: r.original || r.link })
|
|
28555
|
+
},
|
|
28556
|
+
{
|
|
28557
|
+
name: "videos",
|
|
28558
|
+
path: "/v1/search/videos",
|
|
28559
|
+
usd: 0.01,
|
|
28560
|
+
summary: "Google Videos",
|
|
28561
|
+
serverLimit: true,
|
|
28562
|
+
flags: [COUNTRY, LANG],
|
|
28563
|
+
item: (r) => ({ title: r.title, facts: [r.channel || r.source, r.duration, r.date], link: r.link })
|
|
28564
|
+
},
|
|
28565
|
+
{
|
|
28566
|
+
name: "youtube",
|
|
28567
|
+
path: "/v1/search/youtube",
|
|
28568
|
+
usd: 0.01,
|
|
28569
|
+
summary: "YouTube",
|
|
28570
|
+
flags: [COUNTRY, LANG, { spec: "--sp <token>", desc: "YouTube filter token (sp)", param: "sp" }],
|
|
28571
|
+
item: (r) => ({
|
|
28572
|
+
title: r.title,
|
|
28573
|
+
body: clip(r.description),
|
|
28574
|
+
facts: [r.channel, r.views && `${r.views} views`, r.duration, r.date],
|
|
28575
|
+
link: r.link
|
|
28576
|
+
})
|
|
28577
|
+
},
|
|
28578
|
+
{
|
|
28579
|
+
name: "scholar",
|
|
28580
|
+
path: "/v1/search/scholar",
|
|
28581
|
+
usd: 0.01,
|
|
28582
|
+
summary: "Google Scholar",
|
|
28583
|
+
serverLimit: true,
|
|
28584
|
+
flags: [
|
|
28585
|
+
LANG,
|
|
28586
|
+
{ spec: "--from <year>", desc: "Earliest publication year", param: "as_ylo" },
|
|
28587
|
+
{ spec: "--to <year>", desc: "Latest publication year", param: "as_yhi" }
|
|
28588
|
+
],
|
|
28589
|
+
item: (r) => ({
|
|
28590
|
+
title: r.title,
|
|
28591
|
+
body: clip(r.snippet),
|
|
28592
|
+
facts: [r.publication_info, r.year, r.cited_by && `cited by ${r.cited_by}`],
|
|
28593
|
+
link: r.link
|
|
28594
|
+
})
|
|
28595
|
+
},
|
|
28596
|
+
{
|
|
28597
|
+
name: "patents",
|
|
28598
|
+
path: "/v1/search/patents",
|
|
28599
|
+
usd: 0.01,
|
|
28600
|
+
summary: "Google Patents",
|
|
28601
|
+
serverLimit: true,
|
|
28602
|
+
flags: [{ spec: "--page <n>", desc: "Result page", param: "page" }],
|
|
28603
|
+
item: (r) => ({
|
|
28604
|
+
title: r.title,
|
|
28605
|
+
body: clip(r.snippet),
|
|
28606
|
+
facts: [r.patent_id, r.assignee || r.inventor, r.date],
|
|
28607
|
+
link: r.link
|
|
28608
|
+
})
|
|
28609
|
+
},
|
|
28610
|
+
{
|
|
28611
|
+
name: "maps",
|
|
28612
|
+
path: "/v1/search/maps",
|
|
28613
|
+
usd: 0.01,
|
|
28614
|
+
summary: "Google Maps places",
|
|
28615
|
+
flags: [COUNTRY, LANG, { spec: "--ll <coords>", desc: "Centre point, e.g. @40.71,-74.00,14z", param: "ll" }],
|
|
28616
|
+
item: (r) => ({
|
|
28617
|
+
title: r.title,
|
|
28618
|
+
body: r.address,
|
|
28619
|
+
facts: [stars(r.rating, r.reviews), r.type, r.phone],
|
|
28620
|
+
link: r.website
|
|
28621
|
+
})
|
|
28622
|
+
},
|
|
28623
|
+
{
|
|
28624
|
+
name: "jobs",
|
|
28625
|
+
path: "/v1/search/jobs",
|
|
28626
|
+
usd: 0.01,
|
|
28627
|
+
summary: "Google Jobs",
|
|
28628
|
+
flags: [
|
|
28629
|
+
COUNTRY,
|
|
28630
|
+
LANG,
|
|
28631
|
+
{ spec: "--location <where>", desc: 'Where to search, e.g. "Berlin, Germany"', param: "location" },
|
|
28632
|
+
{ spec: "--page-token <token>", desc: "next_page_token from a previous page", param: "next_page_token" }
|
|
28633
|
+
],
|
|
28634
|
+
item: (r) => ({
|
|
28635
|
+
title: r.title,
|
|
28636
|
+
body: clip(r.description),
|
|
28637
|
+
facts: [r.company, r.location, r.salary, r.schedule, r.date, r.via],
|
|
28638
|
+
link: r.link
|
|
28639
|
+
})
|
|
28640
|
+
},
|
|
28641
|
+
{
|
|
28642
|
+
name: "shopping",
|
|
28643
|
+
path: "/v1/search/shopping",
|
|
28644
|
+
usd: 0.01,
|
|
28645
|
+
summary: "Google Shopping",
|
|
28646
|
+
flags: [
|
|
28647
|
+
COUNTRY,
|
|
28648
|
+
LANG,
|
|
28649
|
+
{ spec: "--min <price>", desc: "Minimum price", param: "price_min" },
|
|
28650
|
+
{ spec: "--max <price>", desc: "Maximum price", param: "price_max" },
|
|
28651
|
+
{ spec: "--sort <by>", desc: "Sort order, e.g. price_low_to_high", param: "sort_by" },
|
|
28652
|
+
{ spec: "--condition <c>", desc: "e.g. new, used", param: "condition" }
|
|
28653
|
+
],
|
|
28654
|
+
item: (r) => ({
|
|
28655
|
+
title: r.title,
|
|
28656
|
+
price: r.price || r.extracted_price && `$${r.extracted_price}` || undefined,
|
|
28657
|
+
facts: [r.source, stars(r.rating, r.reviews), r.delivery],
|
|
28658
|
+
link: r.link
|
|
28659
|
+
})
|
|
28660
|
+
},
|
|
28661
|
+
{
|
|
28662
|
+
name: "amazon",
|
|
28663
|
+
path: "/v1/search/amazon",
|
|
28664
|
+
usd: 0.01,
|
|
28665
|
+
summary: "Amazon products",
|
|
28666
|
+
flags: [
|
|
28667
|
+
{ spec: "--domain <domain>", desc: "Marketplace, e.g. amazon.co.uk", param: "amazon_domain" },
|
|
28668
|
+
{ spec: "--min <price>", desc: "Minimum price", param: "price_min" },
|
|
28669
|
+
{ spec: "--max <price>", desc: "Maximum price", param: "price_max" },
|
|
28670
|
+
{ spec: "--sort <by>", desc: "Sort order", param: "sort_by" },
|
|
28671
|
+
{ spec: "--page <n>", desc: "Result page", param: "page" }
|
|
28672
|
+
],
|
|
28673
|
+
item: (r) => ({
|
|
28674
|
+
title: r.title,
|
|
28675
|
+
price: typeof r.price === "string" ? r.price : r.price ? `$${r.price}` : undefined,
|
|
28676
|
+
facts: [
|
|
28677
|
+
r.asin && `ASIN: ${r.asin}`,
|
|
28678
|
+
r.rating && `${r.rating}★`,
|
|
28679
|
+
r.reviews_count && `${r.reviews_count} reviews`,
|
|
28680
|
+
r.is_prime && "Prime"
|
|
28681
|
+
],
|
|
28682
|
+
link: r.link
|
|
28683
|
+
})
|
|
28684
|
+
},
|
|
28685
|
+
{
|
|
28686
|
+
name: "walmart",
|
|
28687
|
+
path: "/v1/search/walmart",
|
|
28688
|
+
usd: 0.01,
|
|
28689
|
+
summary: "Walmart products",
|
|
28690
|
+
flags: [
|
|
28691
|
+
{ spec: "--min <price>", desc: "Minimum price", param: "min_price" },
|
|
28692
|
+
{ spec: "--max <price>", desc: "Maximum price", param: "max_price" },
|
|
28693
|
+
{ spec: "--sort <by>", desc: "Sort order", param: "sort_by" },
|
|
28694
|
+
{ spec: "--page <n>", desc: "Result page", param: "page" }
|
|
28695
|
+
],
|
|
28696
|
+
item: (r) => ({
|
|
28697
|
+
title: r.title,
|
|
28698
|
+
price: typeof r.price === "string" ? r.price : r.price ? `$${r.price}` : undefined,
|
|
28699
|
+
facts: [r.seller, stars(r.rating, r.reviews_count)],
|
|
28700
|
+
link: r.link
|
|
28701
|
+
})
|
|
28702
|
+
},
|
|
28703
|
+
{
|
|
28704
|
+
name: "finance",
|
|
28705
|
+
path: "/v1/search/finance",
|
|
28706
|
+
usd: 0.01,
|
|
28707
|
+
summary: "Google Finance quote for a ticker",
|
|
28708
|
+
render: renderFinance
|
|
28709
|
+
},
|
|
28710
|
+
{
|
|
28711
|
+
name: "trends",
|
|
28712
|
+
path: "/v1/search/trends",
|
|
28713
|
+
usd: 0.01,
|
|
28714
|
+
summary: "Google Trends interest over time",
|
|
28715
|
+
flags: [
|
|
28716
|
+
{ spec: "--geo <code>", desc: "Region, e.g. US, GB, US-CA", param: "geo" },
|
|
28717
|
+
{ spec: "--date <range>", desc: "Range, e.g. today 12-m, 2024-01-01 2024-06-01", param: "date" },
|
|
28718
|
+
{ spec: "--data-type <type>", desc: "e.g. TIMESERIES, GEO_MAP, RELATED_QUERIES", param: "data_type" },
|
|
28719
|
+
{ spec: "--cat <id>", desc: "Category id", param: "cat" }
|
|
28720
|
+
],
|
|
28721
|
+
render: renderTrends
|
|
28722
|
+
}
|
|
28723
|
+
];
|
|
28724
|
+
var WEB_ENGINES = {
|
|
28725
|
+
google: "/v1/search/google",
|
|
28726
|
+
bing: "/v1/search/bing",
|
|
28727
|
+
duckduckgo: "/v1/search/duckduckgo"
|
|
28728
|
+
};
|
|
28729
|
+
function printRendered(r) {
|
|
28730
|
+
console.log(` ${source_default.cyan(r.title || "Untitled")}`);
|
|
28731
|
+
if (r.price)
|
|
28732
|
+
console.log(` ${source_default.yellow(r.price)}`);
|
|
28733
|
+
if (r.body)
|
|
28734
|
+
console.log(` ${r.body}`);
|
|
28735
|
+
const facts = (r.facts ?? []).filter(Boolean);
|
|
28736
|
+
if (facts.length)
|
|
28737
|
+
console.log(dim(` ${facts.join(" · ")}`));
|
|
28738
|
+
if (r.link)
|
|
28739
|
+
console.log(dim(` ${String(r.link).slice(0, 100)}`));
|
|
28740
|
+
console.log("");
|
|
28741
|
+
}
|
|
28742
|
+
function renderResults(data, limit, item) {
|
|
28743
|
+
const results = Array.isArray(data?.results) ? data.results : [];
|
|
28744
|
+
if (results.length === 0) {
|
|
28745
|
+
console.log(dim(" No results found."));
|
|
28746
|
+
console.log("");
|
|
28747
|
+
return;
|
|
28748
|
+
}
|
|
28749
|
+
for (const row of results.slice(0, limit))
|
|
28750
|
+
printRendered(item(row));
|
|
28751
|
+
if (results.length > limit) {
|
|
28752
|
+
console.log(dim(` …${results.length - limit} more — raise --limit or use --raw.`));
|
|
28753
|
+
console.log("");
|
|
28754
|
+
}
|
|
28755
|
+
}
|
|
28756
|
+
function renderWeb(data, limit) {
|
|
28757
|
+
if (data?.answer_box) {
|
|
28758
|
+
const box = data.answer_box;
|
|
28759
|
+
const text = box.answer || box.snippet || box.result || clip(JSON.stringify(box), 200);
|
|
28760
|
+
if (text) {
|
|
28761
|
+
console.log(source_default.bold(" Answer"));
|
|
28762
|
+
console.log(` ${text}`);
|
|
28763
|
+
console.log("");
|
|
28764
|
+
}
|
|
28765
|
+
}
|
|
28766
|
+
if (data?.knowledge_graph?.title) {
|
|
28767
|
+
const kg = data.knowledge_graph;
|
|
28768
|
+
console.log(source_default.bold(` ${kg.title}`));
|
|
28769
|
+
if (kg.description)
|
|
28770
|
+
console.log(` ${clip(kg.description)}`);
|
|
28771
|
+
console.log("");
|
|
28772
|
+
}
|
|
28773
|
+
renderResults(data, limit, (r) => ({
|
|
28774
|
+
title: r.title,
|
|
28775
|
+
body: clip(r.snippet),
|
|
28776
|
+
facts: [r.date],
|
|
28777
|
+
link: r.link
|
|
28778
|
+
}));
|
|
28779
|
+
const related = data?.related_questions ?? [];
|
|
28780
|
+
if (related.length) {
|
|
28781
|
+
console.log(dim(" Related questions:"));
|
|
28782
|
+
for (const q of related.slice(0, 5)) {
|
|
28783
|
+
console.log(dim(` ${typeof q === "string" ? q : q.question || q.title}`));
|
|
28784
|
+
}
|
|
28785
|
+
console.log("");
|
|
28786
|
+
}
|
|
28787
|
+
}
|
|
28788
|
+
function renderFinance(data, limit) {
|
|
28789
|
+
const s = data?.summary;
|
|
28790
|
+
if (s) {
|
|
28791
|
+
const name = [s.title, s.stock && `(${s.stock})`].filter(Boolean).join(" ");
|
|
28792
|
+
if (name)
|
|
28793
|
+
console.log(` ${source_default.cyan(name)}`);
|
|
28794
|
+
const price = s.price ?? s.extracted_price;
|
|
28795
|
+
if (price !== undefined) {
|
|
28796
|
+
const move = s.price_movement;
|
|
28797
|
+
const arrow = move?.movement === "Down" ? source_default.red("▼") : move?.movement === "Up" ? source_default.green("▲") : "";
|
|
28798
|
+
const pct = move?.percentage !== undefined ? ` ${arrow} ${move.percentage}%` : "";
|
|
28799
|
+
console.log(` ${source_default.yellow(`${price}${s.currency ? ` ${s.currency}` : ""}`)}${pct}`);
|
|
28800
|
+
}
|
|
28801
|
+
if (s.exchange)
|
|
28802
|
+
console.log(dim(` ${s.exchange}`));
|
|
28803
|
+
console.log("");
|
|
28804
|
+
} else {
|
|
28805
|
+
console.log(dim(" No quote summary returned."));
|
|
28806
|
+
console.log("");
|
|
28807
|
+
}
|
|
28808
|
+
const news = data?.news ?? [];
|
|
28809
|
+
if (news.length) {
|
|
28810
|
+
console.log(dim(" News"));
|
|
28811
|
+
for (const n of news.slice(0, limit)) {
|
|
28812
|
+
console.log(` ${source_default.cyan(n.title)}`);
|
|
28813
|
+
console.log(dim(` ${[n.source, n.date].filter(Boolean).join(" · ")}`));
|
|
28814
|
+
if (n.link)
|
|
28815
|
+
console.log(dim(` ${String(n.link).slice(0, 100)}`));
|
|
28816
|
+
console.log("");
|
|
28817
|
+
}
|
|
28818
|
+
}
|
|
28819
|
+
const extra = ["graph", "knowledge_graph", "financials"].filter((k) => data?.[k]);
|
|
28820
|
+
if (extra.length)
|
|
28821
|
+
console.log(dim(` Also returned (use --raw): ${extra.join(", ")}`));
|
|
28822
|
+
}
|
|
28823
|
+
function renderTrends(data, limit) {
|
|
28824
|
+
const timeline = data?.interest_over_time?.timeline_data ?? [];
|
|
28825
|
+
if (timeline.length) {
|
|
28826
|
+
console.log(dim(" Interest over time"));
|
|
28827
|
+
for (const point of timeline.slice(-limit)) {
|
|
28828
|
+
const value = point.values?.[0]?.value ?? point.values?.[0]?.extracted_value;
|
|
28829
|
+
console.log(` ${dim(point.date ?? "")} ${value ?? "-"}`);
|
|
28830
|
+
}
|
|
28831
|
+
console.log("");
|
|
28832
|
+
} else {
|
|
28833
|
+
console.log(dim(" No timeline returned — try --data-type TIMESERIES."));
|
|
28834
|
+
console.log("");
|
|
28835
|
+
}
|
|
28836
|
+
const queries = data?.related_queries?.rising ?? data?.related_queries?.top ?? [];
|
|
28837
|
+
if (Array.isArray(queries) && queries.length) {
|
|
28838
|
+
console.log(dim(" Related queries"));
|
|
28839
|
+
for (const q of queries.slice(0, limit)) {
|
|
28840
|
+
console.log(` ${q.query ?? q.topic?.title ?? JSON.stringify(q)}`);
|
|
28841
|
+
}
|
|
28842
|
+
console.log("");
|
|
28843
|
+
}
|
|
28844
|
+
const extra = ["compared_breakdown_by_region", "related_topics"].filter((k) => data?.[k]);
|
|
28845
|
+
if (extra.length)
|
|
28846
|
+
console.log(dim(` Also returned (use --raw): ${extra.join(", ")}`));
|
|
28847
|
+
}
|
|
28848
|
+
function collectParam(value, previous) {
|
|
28849
|
+
return [...previous, value];
|
|
28850
|
+
}
|
|
28851
|
+
function parseParams(pairs) {
|
|
28852
|
+
const out = {};
|
|
28853
|
+
for (const pair of pairs ?? []) {
|
|
28854
|
+
const eq = pair.indexOf("=");
|
|
28855
|
+
if (eq <= 0) {
|
|
28856
|
+
console.error(error(`--param expects key=value, got: ${pair}`));
|
|
28857
|
+
process.exit(1);
|
|
28858
|
+
}
|
|
28859
|
+
out[pair.slice(0, eq)] = pair.slice(eq + 1);
|
|
28860
|
+
}
|
|
28861
|
+
return out;
|
|
28862
|
+
}
|
|
28863
|
+
function limitOf(opts) {
|
|
28864
|
+
const n = parseInt(opts.limit ?? "", 10);
|
|
28865
|
+
return Number.isFinite(n) && n > 0 ? n : 5;
|
|
28866
|
+
}
|
|
28867
|
+
function paramsFor(v, query, opts) {
|
|
28868
|
+
const params = { q: query };
|
|
28869
|
+
for (const flag of v.flags ?? []) {
|
|
28870
|
+
const key = flag.spec.replace(/^--/, "").replace(/ .*$/, "").replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
28871
|
+
const value = opts[key];
|
|
28872
|
+
if (value !== undefined)
|
|
28873
|
+
params[flag.param] = String(value);
|
|
28874
|
+
}
|
|
28875
|
+
if (v.serverLimit)
|
|
28876
|
+
params.num = String(limitOf(opts));
|
|
28877
|
+
return { ...params, ...parseParams(opts.param) };
|
|
28878
|
+
}
|
|
28879
|
+
function attachCommonOptions(cmd, flags = []) {
|
|
28880
|
+
for (const flag of flags)
|
|
28881
|
+
cmd.option(flag.spec, flag.desc);
|
|
28882
|
+
return cmd.option("--limit <n>", "Max results to print", "5").option("--param <key=value>", "Extra upstream parameter (repeat for more)", collectParam, []).option("--raw", "Output raw JSON — the full response, nothing else on stdout").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)");
|
|
28883
|
+
}
|
|
28884
|
+
function quoteArg(s) {
|
|
28885
|
+
return JSON.stringify(s);
|
|
28886
|
+
}
|
|
28887
|
+
function oneQuery(parts, subcommand) {
|
|
28888
|
+
if (parts.length > 1) {
|
|
28889
|
+
console.error(error(`Quote the query — "${parts.join(" ")}" was read as ${parts.length} arguments.`));
|
|
28890
|
+
console.error(dim(` amiko search ${subcommand} ${quoteArg(parts.join(" "))}`));
|
|
28891
|
+
process.exit(1);
|
|
28892
|
+
}
|
|
28893
|
+
return parts[0];
|
|
28894
|
+
}
|
|
28409
28895
|
function registerSearchCommand(program2) {
|
|
28410
|
-
program2.command("search
|
|
28411
|
-
|
|
28412
|
-
|
|
28413
|
-
|
|
28414
|
-
|
|
28415
|
-
|
|
28896
|
+
const search = program2.command("search").description("Paid web search across Google, Bing, X, Amazon, news, images, scholar and more — $0.01 per search");
|
|
28897
|
+
attachCommonOptions(search.command("web <query...>").description("General web search — $0.01 (default engine: google)").option("--engine <engine>", "google, bing, or duckduckgo", "google"), [COUNTRY, LANG, SINCE]).action(async (parts, opts) => {
|
|
28898
|
+
const query = oneQuery(parts, "web");
|
|
28899
|
+
const engine = String(opts.engine).toLowerCase();
|
|
28900
|
+
const path2 = WEB_ENGINES[engine];
|
|
28901
|
+
if (!path2) {
|
|
28902
|
+
console.error(error(`Unknown --engine "${opts.engine}". Choose google, bing, or duckduckgo.`));
|
|
28416
28903
|
process.exit(1);
|
|
28417
28904
|
}
|
|
28418
|
-
|
|
28905
|
+
const params = engine === "google" ? paramsFor({ flags: [COUNTRY, LANG, SINCE], serverLimit: true }, query, opts) : { q: query, ...parseParams(opts.param) };
|
|
28906
|
+
await paidSearch({
|
|
28907
|
+
path: path2,
|
|
28908
|
+
params,
|
|
28909
|
+
usd: 0.01,
|
|
28910
|
+
label: `${engine} search: ${clip(query, 60)}`,
|
|
28911
|
+
commandExample: `amiko search web ${quoteArg(query)}${engine !== "google" ? ` --engine ${engine}` : ""}`
|
|
28912
|
+
}, opts, (data) => {
|
|
28913
|
+
console.log(heading(`${engine} — "${query}"
|
|
28914
|
+
`));
|
|
28915
|
+
renderWeb(data, limitOf(opts));
|
|
28916
|
+
});
|
|
28917
|
+
});
|
|
28918
|
+
attachCommonOptions(search.command("x <query...>").description("X / Twitter search — $0.01")).action(async (parts, opts) => {
|
|
28919
|
+
const query = oneQuery(parts, "x");
|
|
28920
|
+
await runXSearch(query, opts, `amiko search x ${quoteArg(query)}`);
|
|
28921
|
+
});
|
|
28922
|
+
for (const v of VERTICALS) {
|
|
28923
|
+
attachCommonOptions(search.command(`${v.name} <query...>`).description(`${v.summary} — $${v.usd}`), v.flags).action(async (parts, opts) => {
|
|
28924
|
+
const query = oneQuery(parts, v.name);
|
|
28925
|
+
await runVertical(v.name, query, opts, `amiko search ${v.name} ${quoteArg(query)}`);
|
|
28926
|
+
});
|
|
28927
|
+
}
|
|
28928
|
+
search.command("engines").description("List available search engines and their parameters — free").option("--raw", "Output raw JSON").action(async (opts) => {
|
|
28929
|
+
const config2 = loadConfig();
|
|
28930
|
+
const mppBase2 = (config2.apiBase || "https://mpp.amikomarkets.com").replace(/\/$/, "");
|
|
28931
|
+
let auth;
|
|
28419
28932
|
try {
|
|
28420
|
-
|
|
28933
|
+
auth = requireAuth();
|
|
28421
28934
|
} catch (e5) {
|
|
28422
|
-
console.error(error(e5 instanceof Error ? e5.message :
|
|
28935
|
+
console.error(error(e5 instanceof Error ? e5.message : "auth failed"));
|
|
28423
28936
|
process.exit(1);
|
|
28424
28937
|
}
|
|
28425
|
-
await
|
|
28426
|
-
|
|
28427
|
-
summary: `X/Twitter search: ${query.slice(0, 80)}${query.length > 80 ? "…" : ""}`,
|
|
28428
|
-
yes: opts.yes,
|
|
28429
|
-
commandExample: `amiko markets search ${JSON.stringify(query)}${token !== "AMIKO" ? ` --token ${token}` : ""}`
|
|
28938
|
+
const res = await fetch(`${mppBase2}/v1/search/engines`, {
|
|
28939
|
+
headers: { "X-Twin-Id": auth.twinId }
|
|
28430
28940
|
});
|
|
28431
|
-
|
|
28432
|
-
|
|
28433
|
-
const config2 = loadConfig();
|
|
28434
|
-
let auth;
|
|
28435
|
-
try {
|
|
28436
|
-
auth = requireAuth();
|
|
28437
|
-
} catch (e5) {
|
|
28438
|
-
spinner.stop();
|
|
28439
|
-
console.error(error(e5 instanceof Error ? e5.message : "auth failed"));
|
|
28440
|
-
process.exit(1);
|
|
28441
|
-
}
|
|
28442
|
-
const mppBase2 = (config2.apiBase || "https://mpp.amikomarkets.com").replace(/\/$/, "");
|
|
28443
|
-
const amount2 = quote.amount;
|
|
28444
|
-
let wallet;
|
|
28445
|
-
try {
|
|
28446
|
-
wallet = await resolveWalletAddress(auth, opts.wallet, "solana");
|
|
28447
|
-
} catch (e5) {
|
|
28448
|
-
spinner.stop();
|
|
28449
|
-
console.error(error(e5 instanceof Error ? e5.message : String(e5)));
|
|
28450
|
-
process.exit(1);
|
|
28451
|
-
}
|
|
28452
|
-
spinner.text = `Paying ${amount2} ${token} (solana)...`;
|
|
28453
|
-
let txHash;
|
|
28454
|
-
try {
|
|
28455
|
-
txHash = await payTreasury(auth, { wallet, amount: amount2, token });
|
|
28456
|
-
} catch (e5) {
|
|
28457
|
-
spinner.stop();
|
|
28458
|
-
console.error(error(e5 instanceof Error ? e5.message : `${token} payment failed`));
|
|
28459
|
-
process.exit(1);
|
|
28460
|
-
}
|
|
28461
|
-
spinner.text = `Paid ${amount2} ${token} — searching X...`;
|
|
28462
|
-
const params = new URLSearchParams({ q: query });
|
|
28463
|
-
const res = await fetch(`${mppBase2}/v1/x/search?${params}`, {
|
|
28464
|
-
headers: {
|
|
28465
|
-
"Content-Type": "application/json",
|
|
28466
|
-
"X-Payment-Tx": txHash,
|
|
28467
|
-
"X-Payment-Amount": amount2,
|
|
28468
|
-
"X-Payment-Token": token,
|
|
28469
|
-
"X-Twin-Id": auth.twinId
|
|
28470
|
-
}
|
|
28471
|
-
});
|
|
28472
|
-
spinner.stop();
|
|
28473
|
-
if (!res.ok) {
|
|
28474
|
-
await reportHttpError(res, "Search");
|
|
28475
|
-
console.log(dim(`Payment tx: https://solscan.io/tx/${txHash}`));
|
|
28476
|
-
process.exit(1);
|
|
28477
|
-
}
|
|
28478
|
-
const data = await res.json();
|
|
28479
|
-
if (opts.raw) {
|
|
28480
|
-
console.log(JSON.stringify(data, null, 2));
|
|
28481
|
-
return;
|
|
28482
|
-
}
|
|
28483
|
-
console.log(heading(`X Search — "${query}"
|
|
28484
|
-
`));
|
|
28485
|
-
const tweets = data.data || [];
|
|
28486
|
-
if (tweets.length === 0) {
|
|
28487
|
-
console.log(dim(" No results found."));
|
|
28488
|
-
} else {
|
|
28489
|
-
for (const tweet of tweets) {
|
|
28490
|
-
const author = tweet.author_id ? dim(`@${tweet.author_id}`) : "";
|
|
28491
|
-
const date5 = tweet.created_at ? dim(new Date(tweet.created_at).toLocaleDateString()) : "";
|
|
28492
|
-
const metrics = tweet.public_metrics;
|
|
28493
|
-
const stats = metrics ? dim(` ${metrics.like_count || 0} likes · ${metrics.retweet_count || 0} RTs · ${metrics.reply_count || 0} replies`) : "";
|
|
28494
|
-
console.log(` ${source_default.cyan(tweet.id)} ${author} ${date5}`);
|
|
28495
|
-
console.log(` ${tweet.text}`);
|
|
28496
|
-
if (stats)
|
|
28497
|
-
console.log(stats);
|
|
28498
|
-
console.log("");
|
|
28499
|
-
}
|
|
28500
|
-
}
|
|
28501
|
-
console.log(success(`Paid ${amount2} ${token}`) + dim(` tx: ${txHash.slice(0, 16)}...`));
|
|
28502
|
-
await printBalanceRemaining(auth);
|
|
28503
|
-
} catch (err) {
|
|
28504
|
-
spinner.stop();
|
|
28505
|
-
console.error(error(err instanceof Error ? err.message : "Search failed"));
|
|
28941
|
+
if (!res.ok) {
|
|
28942
|
+
await reportHttpError(res, "search engines");
|
|
28506
28943
|
process.exit(1);
|
|
28507
28944
|
}
|
|
28945
|
+
const data = await res.json();
|
|
28946
|
+
if (opts.raw) {
|
|
28947
|
+
console.log(JSON.stringify(data, null, 2));
|
|
28948
|
+
return;
|
|
28949
|
+
}
|
|
28950
|
+
console.log(heading(`Search engines (${data.total ?? 0}) — provider: ${data.provider ?? "unknown"}
|
|
28951
|
+
`));
|
|
28952
|
+
for (const e5 of data.engines ?? []) {
|
|
28953
|
+
console.log(` ${source_default.cyan(e5.id)} ${source_default.yellow(e5.price)}`);
|
|
28954
|
+
console.log(dim(` ${e5.path}`));
|
|
28955
|
+
console.log(dim(` params: ${e5.params}`));
|
|
28956
|
+
console.log("");
|
|
28957
|
+
}
|
|
28958
|
+
console.log(dim("Anything without an `amiko search` subcommand is reachable via `amiko markets service call GET <path>`."));
|
|
28959
|
+
});
|
|
28960
|
+
}
|
|
28961
|
+
async function runVertical(name, query, opts, commandExample) {
|
|
28962
|
+
const v = VERTICALS.find((entry) => entry.name === name);
|
|
28963
|
+
if (!v)
|
|
28964
|
+
throw new Error(`unknown search vertical: ${name}`);
|
|
28965
|
+
await paidSearch({
|
|
28966
|
+
path: v.path,
|
|
28967
|
+
params: paramsFor(v, query, opts),
|
|
28968
|
+
usd: v.usd,
|
|
28969
|
+
label: `${v.name} search: ${clip(query, 60)}`,
|
|
28970
|
+
commandExample
|
|
28971
|
+
}, opts, (data) => {
|
|
28972
|
+
console.log(heading(`${v.summary} — "${query}"
|
|
28973
|
+
`));
|
|
28974
|
+
if (v.render)
|
|
28975
|
+
v.render(data, limitOf(opts));
|
|
28976
|
+
else
|
|
28977
|
+
renderResults(data, limitOf(opts), v.item);
|
|
28978
|
+
});
|
|
28979
|
+
}
|
|
28980
|
+
function attachSearchOptions(cmd, vertical) {
|
|
28981
|
+
return attachCommonOptions(cmd, VERTICALS.find((v) => v.name === vertical)?.flags);
|
|
28982
|
+
}
|
|
28983
|
+
async function runXSearch(query, opts, commandExample) {
|
|
28984
|
+
await paidSearch({
|
|
28985
|
+
path: "/v1/x/search",
|
|
28986
|
+
params: { q: query, ...parseParams(opts.param) },
|
|
28987
|
+
usd: 0.01,
|
|
28988
|
+
label: `X search: ${clip(query, 60)}`,
|
|
28989
|
+
commandExample
|
|
28990
|
+
}, opts, (data) => {
|
|
28991
|
+
console.log(heading(`X — "${query}"
|
|
28992
|
+
`));
|
|
28993
|
+
const tweets = data?.data ?? [];
|
|
28994
|
+
if (tweets.length === 0) {
|
|
28995
|
+
console.log(dim(" No results found."));
|
|
28996
|
+
console.log("");
|
|
28997
|
+
return;
|
|
28998
|
+
}
|
|
28999
|
+
for (const tweet of tweets.slice(0, limitOf(opts))) {
|
|
29000
|
+
const m = tweet.public_metrics;
|
|
29001
|
+
printRendered({
|
|
29002
|
+
title: tweet.id,
|
|
29003
|
+
body: tweet.text,
|
|
29004
|
+
facts: [
|
|
29005
|
+
tweet.author_id && `@${tweet.author_id}`,
|
|
29006
|
+
tweet.created_at && new Date(tweet.created_at).toLocaleDateString(),
|
|
29007
|
+
m && `${m.like_count ?? 0} likes · ${m.retweet_count ?? 0} RTs · ${m.reply_count ?? 0} replies`
|
|
29008
|
+
]
|
|
29009
|
+
});
|
|
29010
|
+
}
|
|
29011
|
+
});
|
|
29012
|
+
}
|
|
29013
|
+
function registerDeprecatedMarketsSearch(markets) {
|
|
29014
|
+
attachCommonOptions(markets.command("search <query...>").description("[deprecated — use `amiko search x`] X / Twitter search — $0.01")).action(async (parts, opts) => {
|
|
29015
|
+
const query = oneQuery(parts, "x");
|
|
29016
|
+
console.error(warn("`amiko markets search` is deprecated — use `amiko search x` (or `amiko search web` for Google)."));
|
|
29017
|
+
await runXSearch(query, opts, `amiko markets search ${quoteArg(query)}`);
|
|
28508
29018
|
});
|
|
28509
29019
|
}
|
|
28510
29020
|
|
|
@@ -28690,7 +29200,521 @@ function registerImageCommand(program2) {
|
|
|
28690
29200
|
}
|
|
28691
29201
|
|
|
28692
29202
|
// src/commands/create.ts
|
|
28693
|
-
import { randomUUID } from "node:crypto";
|
|
29203
|
+
import { randomUUID } from "node:crypto";
|
|
29204
|
+
|
|
29205
|
+
// src/lib/media-duration.ts
|
|
29206
|
+
var H3_REFERENCE_AUDIO_MIN_MS = 2000;
|
|
29207
|
+
var H3_REFERENCE_AUDIO_MAX_MS = 15000;
|
|
29208
|
+
var REFERENCE_TOTAL_MAX_MS = 15000;
|
|
29209
|
+
function readMediaDurationMs(buffer, mime) {
|
|
29210
|
+
if (mime === "audio/wav")
|
|
29211
|
+
return wavDurationMs(buffer);
|
|
29212
|
+
if (mime === "video/mp4" || mime === "video/quicktime")
|
|
29213
|
+
return isoDurationMs(buffer);
|
|
29214
|
+
return null;
|
|
29215
|
+
}
|
|
29216
|
+
function wavDurationMs(buffer) {
|
|
29217
|
+
if (buffer.length < 12)
|
|
29218
|
+
return null;
|
|
29219
|
+
if (buffer.toString("ascii", 0, 4) !== "RIFF")
|
|
29220
|
+
return null;
|
|
29221
|
+
if (buffer.toString("ascii", 8, 12) !== "WAVE")
|
|
29222
|
+
return null;
|
|
29223
|
+
let byteRate = 0;
|
|
29224
|
+
let dataSize = 0;
|
|
29225
|
+
let offset = 12;
|
|
29226
|
+
while (offset + 8 <= buffer.length) {
|
|
29227
|
+
const id = buffer.toString("ascii", offset, offset + 4);
|
|
29228
|
+
const size3 = buffer.readUInt32LE(offset + 4);
|
|
29229
|
+
const body = offset + 8;
|
|
29230
|
+
if (id === "fmt " && body + 16 <= buffer.length) {
|
|
29231
|
+
byteRate = buffer.readUInt32LE(body + 8);
|
|
29232
|
+
} else if (id === "data") {
|
|
29233
|
+
dataSize = size3 > 0 && body + size3 <= buffer.length ? size3 : buffer.length - body;
|
|
29234
|
+
}
|
|
29235
|
+
if (size3 <= 0)
|
|
29236
|
+
break;
|
|
29237
|
+
offset = body + size3 + size3 % 2;
|
|
29238
|
+
}
|
|
29239
|
+
if (byteRate <= 0 || dataSize <= 0)
|
|
29240
|
+
return null;
|
|
29241
|
+
return Math.round(dataSize / byteRate * 1000);
|
|
29242
|
+
}
|
|
29243
|
+
function isoDurationMs(buffer) {
|
|
29244
|
+
const moov = findBox(buffer, 0, buffer.length, "moov");
|
|
29245
|
+
if (!moov)
|
|
29246
|
+
return null;
|
|
29247
|
+
const mvhd = findBox(buffer, moov.start, moov.end, "mvhd");
|
|
29248
|
+
if (!mvhd || mvhd.start + 4 > buffer.length)
|
|
29249
|
+
return null;
|
|
29250
|
+
const version3 = buffer[mvhd.start];
|
|
29251
|
+
const head = mvhd.start + 4 + (version3 === 1 ? 16 : 8);
|
|
29252
|
+
let timescale;
|
|
29253
|
+
let duration3;
|
|
29254
|
+
if (version3 === 1) {
|
|
29255
|
+
if (head + 12 > mvhd.end)
|
|
29256
|
+
return null;
|
|
29257
|
+
timescale = buffer.readUInt32BE(head);
|
|
29258
|
+
const raw = buffer.readBigUInt64BE(head + 4);
|
|
29259
|
+
if (raw > BigInt(Number.MAX_SAFE_INTEGER))
|
|
29260
|
+
return null;
|
|
29261
|
+
duration3 = Number(raw);
|
|
29262
|
+
} else {
|
|
29263
|
+
if (head + 8 > mvhd.end)
|
|
29264
|
+
return null;
|
|
29265
|
+
timescale = buffer.readUInt32BE(head);
|
|
29266
|
+
duration3 = buffer.readUInt32BE(head + 4);
|
|
29267
|
+
}
|
|
29268
|
+
if (timescale <= 0 || duration3 <= 0 || duration3 === 4294967295)
|
|
29269
|
+
return null;
|
|
29270
|
+
return Math.round(duration3 / timescale * 1000);
|
|
29271
|
+
}
|
|
29272
|
+
function findBox(buffer, from7, until, type) {
|
|
29273
|
+
let offset = from7;
|
|
29274
|
+
while (offset + 8 <= until) {
|
|
29275
|
+
let size3 = buffer.readUInt32BE(offset);
|
|
29276
|
+
const boxType = buffer.toString("ascii", offset + 4, offset + 8);
|
|
29277
|
+
let header = 8;
|
|
29278
|
+
if (size3 === 1) {
|
|
29279
|
+
if (offset + 16 > until)
|
|
29280
|
+
return null;
|
|
29281
|
+
const large = buffer.readBigUInt64BE(offset + 8);
|
|
29282
|
+
if (large > BigInt(Number.MAX_SAFE_INTEGER))
|
|
29283
|
+
return null;
|
|
29284
|
+
size3 = Number(large);
|
|
29285
|
+
header = 16;
|
|
29286
|
+
} else if (size3 === 0) {
|
|
29287
|
+
size3 = until - offset;
|
|
29288
|
+
}
|
|
29289
|
+
if (size3 < header)
|
|
29290
|
+
return null;
|
|
29291
|
+
const end = Math.min(offset + size3, until);
|
|
29292
|
+
if (boxType === type)
|
|
29293
|
+
return { start: offset + header, end };
|
|
29294
|
+
offset += size3;
|
|
29295
|
+
}
|
|
29296
|
+
return null;
|
|
29297
|
+
}
|
|
29298
|
+
|
|
29299
|
+
// src/lib/create-capabilities.ts
|
|
29300
|
+
var H3_VIDEO_MODEL = /^(minimax-h3|h3|hailuo-0?3)$/i;
|
|
29301
|
+
var H3_MIN_SECONDS = 4;
|
|
29302
|
+
var H3_MAX_SECONDS = 15;
|
|
29303
|
+
var H3_RESOLUTIONS = new Set(["768P", "2K"]);
|
|
29304
|
+
var H3_CONCRETE_RATIOS = new Set(["21:9", "16:9", "4:3", "1:1", "3:4", "9:16"]);
|
|
29305
|
+
var H3_MAX_PROMPT_CHARS = 7000;
|
|
29306
|
+
var NON_H3_MAX_PROMPT_CHARS = 2000;
|
|
29307
|
+
var H3_MAX_REFERENCE_FILES = 12;
|
|
29308
|
+
var H3_DEFAULT_T2V_ASPECT = "16:9";
|
|
29309
|
+
var NON_H3_VIDEO_SECONDS = new Set([4, 6, 8, 10]);
|
|
29310
|
+
function isH3VideoModel(model) {
|
|
29311
|
+
return H3_VIDEO_MODEL.test(model);
|
|
29312
|
+
}
|
|
29313
|
+
function videoFamily(model) {
|
|
29314
|
+
if (isH3VideoModel(model))
|
|
29315
|
+
return "h3";
|
|
29316
|
+
if (model === "grok-imagine-video")
|
|
29317
|
+
return "grok";
|
|
29318
|
+
if (/^(dreamina-seedance-|seedance-|ep-\d{14}-)/i.test(model))
|
|
29319
|
+
return "seedance";
|
|
29320
|
+
if (/^veo-/i.test(model))
|
|
29321
|
+
return "veo";
|
|
29322
|
+
return "hailuo";
|
|
29323
|
+
}
|
|
29324
|
+
var VIDEO_CAPS = {
|
|
29325
|
+
h3: {
|
|
29326
|
+
referenceImages: 9,
|
|
29327
|
+
referenceVideos: 3,
|
|
29328
|
+
referenceAudio: 3,
|
|
29329
|
+
referenceFiles: H3_MAX_REFERENCE_FILES,
|
|
29330
|
+
firstFrame: true,
|
|
29331
|
+
lastFrame: true,
|
|
29332
|
+
lastFrameNeedsFirst: false,
|
|
29333
|
+
frameExcludesReference: true,
|
|
29334
|
+
frameExclusionCountsAudio: true,
|
|
29335
|
+
audioNeedsVisual: true,
|
|
29336
|
+
subjectReferenceNative: false,
|
|
29337
|
+
label: "MiniMax H3"
|
|
29338
|
+
},
|
|
29339
|
+
grok: {
|
|
29340
|
+
referenceImages: 9,
|
|
29341
|
+
referenceVideos: 0,
|
|
29342
|
+
referenceAudio: 0,
|
|
29343
|
+
firstFrame: true,
|
|
29344
|
+
lastFrame: false,
|
|
29345
|
+
lastFrameNeedsFirst: false,
|
|
29346
|
+
frameExcludesReference: true,
|
|
29347
|
+
frameExclusionCountsAudio: false,
|
|
29348
|
+
audioNeedsVisual: false,
|
|
29349
|
+
subjectReferenceNative: false,
|
|
29350
|
+
label: "Grok Imagine Video"
|
|
29351
|
+
},
|
|
29352
|
+
seedance: {
|
|
29353
|
+
referenceImages: 9,
|
|
29354
|
+
referenceVideos: 3,
|
|
29355
|
+
referenceAudio: 3,
|
|
29356
|
+
firstFrame: true,
|
|
29357
|
+
lastFrame: true,
|
|
29358
|
+
lastFrameNeedsFirst: true,
|
|
29359
|
+
frameExcludesReference: true,
|
|
29360
|
+
frameExclusionCountsAudio: false,
|
|
29361
|
+
audioNeedsVisual: true,
|
|
29362
|
+
subjectReferenceNative: false,
|
|
29363
|
+
label: "Seedance"
|
|
29364
|
+
},
|
|
29365
|
+
veo: {
|
|
29366
|
+
referenceImages: 3,
|
|
29367
|
+
referenceVideos: 0,
|
|
29368
|
+
referenceAudio: 0,
|
|
29369
|
+
firstFrame: true,
|
|
29370
|
+
lastFrame: true,
|
|
29371
|
+
lastFrameNeedsFirst: true,
|
|
29372
|
+
frameExcludesReference: true,
|
|
29373
|
+
frameExclusionCountsAudio: false,
|
|
29374
|
+
audioNeedsVisual: false,
|
|
29375
|
+
subjectReferenceNative: false,
|
|
29376
|
+
label: "Veo"
|
|
29377
|
+
},
|
|
29378
|
+
hailuo: {
|
|
29379
|
+
referenceImages: 0,
|
|
29380
|
+
referenceVideos: 0,
|
|
29381
|
+
referenceAudio: 0,
|
|
29382
|
+
firstFrame: true,
|
|
29383
|
+
lastFrame: true,
|
|
29384
|
+
lastFrameNeedsFirst: false,
|
|
29385
|
+
frameExcludesReference: false,
|
|
29386
|
+
frameExclusionCountsAudio: false,
|
|
29387
|
+
audioNeedsVisual: false,
|
|
29388
|
+
subjectReferenceNative: true,
|
|
29389
|
+
label: "MiniMax Hailuo"
|
|
29390
|
+
}
|
|
29391
|
+
};
|
|
29392
|
+
function effectiveReferenceImageCount(input) {
|
|
29393
|
+
if (VIDEO_CAPS[videoFamily(input.model)].subjectReferenceNative) {
|
|
29394
|
+
return input.referenceImageUrls.length;
|
|
29395
|
+
}
|
|
29396
|
+
return input.subjectReference ? 1 : input.referenceImageUrls.length;
|
|
29397
|
+
}
|
|
29398
|
+
function validateVideoReferences(input) {
|
|
29399
|
+
const family = videoFamily(input.model);
|
|
29400
|
+
const caps = VIDEO_CAPS[family];
|
|
29401
|
+
const images = effectiveReferenceImageCount(input);
|
|
29402
|
+
const videos = input.referenceVideoUrls.length;
|
|
29403
|
+
const audio = input.referenceAudioUrls.length;
|
|
29404
|
+
if (input.referenceImageUrls.length > 0 && caps.referenceImages === 0) {
|
|
29405
|
+
return `${caps.label} ignores --reference-image — mpp's ${family} branch never reads it, so the video would be generated and billed without it. Use --subject-reference for a single character image, or --model MiniMax-H3 for true reference-to-video.`;
|
|
29406
|
+
}
|
|
29407
|
+
if (videos > 0 && caps.referenceVideos === 0) {
|
|
29408
|
+
return `${caps.label} ignores --reference-video — it would be dropped and the job billed anyway. Only MiniMax-H3 and Seedance read reference clips.`;
|
|
29409
|
+
}
|
|
29410
|
+
if (audio > 0 && caps.referenceAudio === 0) {
|
|
29411
|
+
return `${caps.label} ignores --reference-audio — it would be dropped and the job billed anyway. Only MiniMax-H3 and Seedance read reference audio.`;
|
|
29412
|
+
}
|
|
29413
|
+
if (input.lastFrame && !caps.lastFrame) {
|
|
29414
|
+
return `${caps.label} ignores --last-frame — it would be dropped and the job billed anyway. Drop the flag, or use a model with first-and-last-frame support (MiniMax-H3, Seedance, Veo).`;
|
|
29415
|
+
}
|
|
29416
|
+
if (input.lastFrame && !input.firstFrame && caps.lastFrameNeedsFirst) {
|
|
29417
|
+
return `${caps.label} needs --first-frame alongside --last-frame.`;
|
|
29418
|
+
}
|
|
29419
|
+
const hasFrame = Boolean(input.firstFrame || input.lastFrame);
|
|
29420
|
+
const hasReference = images > 0 || videos > 0 || caps.frameExclusionCountsAudio && audio > 0;
|
|
29421
|
+
if (hasFrame && hasReference && caps.frameExcludesReference) {
|
|
29422
|
+
const kinds = caps.frameExclusionCountsAudio ? "--reference-image/-video/-audio, --subject-reference" : "--reference-image/-video, --subject-reference";
|
|
29423
|
+
return `${caps.label} cannot combine --first-frame/--last-frame with reference inputs (${kinds}). Use one mode or the other.`;
|
|
29424
|
+
}
|
|
29425
|
+
if (audio > 0 && images === 0 && videos === 0 && caps.audioNeedsVisual) {
|
|
29426
|
+
return `${caps.label} reference audio needs at least one --reference-image or --reference-video alongside it.`;
|
|
29427
|
+
}
|
|
29428
|
+
if (images > caps.referenceImages) {
|
|
29429
|
+
return `${caps.label} accepts at most ${caps.referenceImages} reference image${caps.referenceImages === 1 ? "" : "s"} (got ${images}).`;
|
|
29430
|
+
}
|
|
29431
|
+
if (videos > caps.referenceVideos) {
|
|
29432
|
+
return `${caps.label} accepts at most ${caps.referenceVideos} reference video${caps.referenceVideos === 1 ? "" : "s"} (got ${videos}).`;
|
|
29433
|
+
}
|
|
29434
|
+
if (audio > caps.referenceAudio) {
|
|
29435
|
+
return `${caps.label} accepts at most ${caps.referenceAudio} reference audio clip${caps.referenceAudio === 1 ? "" : "s"} (got ${audio}).`;
|
|
29436
|
+
}
|
|
29437
|
+
if (caps.referenceFiles !== undefined && images + videos + audio > caps.referenceFiles) {
|
|
29438
|
+
return `${caps.label} accepts at most ${caps.referenceFiles} reference files in total (got ${images + videos + audio}).`;
|
|
29439
|
+
}
|
|
29440
|
+
return null;
|
|
29441
|
+
}
|
|
29442
|
+
function validateReferenceDurations(model, videoMs, audioMs) {
|
|
29443
|
+
const family = videoFamily(model);
|
|
29444
|
+
if (family !== "h3" && family !== "seedance")
|
|
29445
|
+
return null;
|
|
29446
|
+
const caps = VIDEO_CAPS[family];
|
|
29447
|
+
if (family === "h3") {
|
|
29448
|
+
for (const [index, ms] of audioMs.entries()) {
|
|
29449
|
+
if (ms === null)
|
|
29450
|
+
continue;
|
|
29451
|
+
if (ms < H3_REFERENCE_AUDIO_MIN_MS || ms > H3_REFERENCE_AUDIO_MAX_MS) {
|
|
29452
|
+
return `--reference-audio #${index + 1} is ${(ms / 1000).toFixed(1)}s; ${caps.label} accepts ${H3_REFERENCE_AUDIO_MIN_MS / 1000}–${H3_REFERENCE_AUDIO_MAX_MS / 1000}s per clip. Trim it before attaching.`;
|
|
29453
|
+
}
|
|
29454
|
+
}
|
|
29455
|
+
}
|
|
29456
|
+
const all = [...videoMs, ...audioMs];
|
|
29457
|
+
if (all.length === 0 || all.some((ms) => ms === null))
|
|
29458
|
+
return null;
|
|
29459
|
+
const total = all.reduce((sum, ms) => sum + (ms ?? 0), 0);
|
|
29460
|
+
if (total > REFERENCE_TOTAL_MAX_MS) {
|
|
29461
|
+
return `Reference video + audio total ${(total / 1000).toFixed(1)}s; ${caps.label} caps the combined reference material at ${REFERENCE_TOTAL_MAX_MS / 1000}s.`;
|
|
29462
|
+
}
|
|
29463
|
+
return null;
|
|
29464
|
+
}
|
|
29465
|
+
function imageReferenceSupport(model) {
|
|
29466
|
+
if (model.startsWith("nano-banana"))
|
|
29467
|
+
return "multi";
|
|
29468
|
+
if (model.startsWith("gpt-image"))
|
|
29469
|
+
return "multi";
|
|
29470
|
+
if (model === "image-01")
|
|
29471
|
+
return "character";
|
|
29472
|
+
return null;
|
|
29473
|
+
}
|
|
29474
|
+
var IMAGE_REFERENCE_MAX = {
|
|
29475
|
+
multi: 4,
|
|
29476
|
+
character: 1
|
|
29477
|
+
};
|
|
29478
|
+
function validateImageReferences(model, referenceImageUrls) {
|
|
29479
|
+
if (referenceImageUrls.length === 0)
|
|
29480
|
+
return null;
|
|
29481
|
+
const support = imageReferenceSupport(model);
|
|
29482
|
+
if (!support) {
|
|
29483
|
+
return `${model} does not take reference images. Use nano-banana / nano-banana-pro / nano-banana-lite / nano-banana-2 or gpt-image-* (up to ${IMAGE_REFERENCE_MAX.multi}), or image-01 for a single character portrait.`;
|
|
29484
|
+
}
|
|
29485
|
+
const max = IMAGE_REFERENCE_MAX[support];
|
|
29486
|
+
if (referenceImageUrls.length > max) {
|
|
29487
|
+
return support === "character" ? `${model} takes exactly one reference image — a front-facing character portrait (got ${referenceImageUrls.length}).` : `${model} accepts at most ${max} reference images (got ${referenceImageUrls.length}).`;
|
|
29488
|
+
}
|
|
29489
|
+
return null;
|
|
29490
|
+
}
|
|
29491
|
+
|
|
29492
|
+
// src/lib/create-input.ts
|
|
29493
|
+
import { basename as basename2, extname as extname2 } from "node:path";
|
|
29494
|
+
|
|
29495
|
+
// src/lib/file-input.ts
|
|
29496
|
+
import { readFile } from "node:fs/promises";
|
|
29497
|
+
import { basename, extname } from "node:path";
|
|
29498
|
+
var MAX_BYTES = 100 * 1024 * 1024;
|
|
29499
|
+
var FETCH_TIMEOUT_MS = 30000;
|
|
29500
|
+
async function resolveFileInput(src, opts = {}) {
|
|
29501
|
+
const maxBytes = opts.maxBytes ?? MAX_BYTES;
|
|
29502
|
+
if (src === "-" || opts.stdin) {
|
|
29503
|
+
const buffer2 = await readStdin(maxBytes);
|
|
29504
|
+
const name2 = opts.hintName ?? "stdin";
|
|
29505
|
+
const mime2 = opts.hintMime ?? detectMime(buffer2, name2);
|
|
29506
|
+
return { buffer: buffer2, name: name2, mime: mime2, size: buffer2.length };
|
|
29507
|
+
}
|
|
29508
|
+
if (/^https?:\/\//i.test(src)) {
|
|
29509
|
+
const buffer2 = await fetchRemote(src, maxBytes);
|
|
29510
|
+
const urlPath = new URL(src).pathname;
|
|
29511
|
+
const name2 = opts.hintName ?? (basename(urlPath) || "download");
|
|
29512
|
+
const mime2 = opts.hintMime ?? detectMime(buffer2, name2);
|
|
29513
|
+
return { buffer: buffer2, name: name2, mime: mime2, size: buffer2.length };
|
|
29514
|
+
}
|
|
29515
|
+
const buffer = await readFile(src);
|
|
29516
|
+
if (buffer.length > maxBytes) {
|
|
29517
|
+
throw new Error(`File too large: ${buffer.length} bytes exceeds ${maxBytes} byte cap.`);
|
|
29518
|
+
}
|
|
29519
|
+
const name = opts.hintName ?? basename(src);
|
|
29520
|
+
const mime = opts.hintMime ?? detectMime(buffer, name);
|
|
29521
|
+
return { buffer, name, mime, size: buffer.length };
|
|
29522
|
+
}
|
|
29523
|
+
async function readStdin(maxBytes) {
|
|
29524
|
+
const chunks = [];
|
|
29525
|
+
let total = 0;
|
|
29526
|
+
for await (const chunk of process.stdin) {
|
|
29527
|
+
const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
29528
|
+
total += buf.length;
|
|
29529
|
+
if (total > maxBytes) {
|
|
29530
|
+
throw new Error(`stdin exceeded ${maxBytes} byte cap.`);
|
|
29531
|
+
}
|
|
29532
|
+
chunks.push(buf);
|
|
29533
|
+
}
|
|
29534
|
+
return Buffer.concat(chunks);
|
|
29535
|
+
}
|
|
29536
|
+
async function fetchRemote(url2, maxBytes) {
|
|
29537
|
+
const controller = new AbortController;
|
|
29538
|
+
const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
|
|
29539
|
+
try {
|
|
29540
|
+
const res = await fetch(url2, { signal: controller.signal });
|
|
29541
|
+
if (!res.ok) {
|
|
29542
|
+
throw new Error(`Fetch ${url2} failed: ${res.status} ${res.statusText}`);
|
|
29543
|
+
}
|
|
29544
|
+
const len = Number(res.headers.get("content-length") || 0);
|
|
29545
|
+
if (len > maxBytes) {
|
|
29546
|
+
throw new Error(`Remote file too large: ${len} bytes exceeds ${maxBytes} byte cap.`);
|
|
29547
|
+
}
|
|
29548
|
+
const buf = Buffer.from(await res.arrayBuffer());
|
|
29549
|
+
if (buf.length > maxBytes) {
|
|
29550
|
+
throw new Error(`Remote file too large: ${buf.length} bytes exceeds ${maxBytes} byte cap.`);
|
|
29551
|
+
}
|
|
29552
|
+
return buf;
|
|
29553
|
+
} finally {
|
|
29554
|
+
clearTimeout(timer);
|
|
29555
|
+
}
|
|
29556
|
+
}
|
|
29557
|
+
var MAGIC = [
|
|
29558
|
+
[Buffer.from([37, 80, 68, 70]), "application/pdf"],
|
|
29559
|
+
[Buffer.from([137, 80, 78, 71]), "image/png"],
|
|
29560
|
+
[Buffer.from([255, 216, 255]), "image/jpeg"],
|
|
29561
|
+
[Buffer.from([71, 73, 70, 56]), "image/gif"],
|
|
29562
|
+
[Buffer.from([82, 73, 70, 70]), "audio/wav"],
|
|
29563
|
+
[Buffer.from([73, 68, 51]), "audio/mpeg"],
|
|
29564
|
+
[Buffer.from([255, 251]), "audio/mpeg"],
|
|
29565
|
+
[Buffer.from([80, 75, 3, 4]), "application/zip"]
|
|
29566
|
+
];
|
|
29567
|
+
var EXT_MIME = {
|
|
29568
|
+
".pdf": "application/pdf",
|
|
29569
|
+
".png": "image/png",
|
|
29570
|
+
".jpg": "image/jpeg",
|
|
29571
|
+
".jpeg": "image/jpeg",
|
|
29572
|
+
".gif": "image/gif",
|
|
29573
|
+
".webp": "image/webp",
|
|
29574
|
+
".txt": "text/plain",
|
|
29575
|
+
".md": "text/markdown",
|
|
29576
|
+
".json": "application/json",
|
|
29577
|
+
".wav": "audio/wav",
|
|
29578
|
+
".mp3": "audio/mpeg",
|
|
29579
|
+
".m4a": "audio/mp4",
|
|
29580
|
+
".ogg": "audio/ogg",
|
|
29581
|
+
".aac": "audio/aac",
|
|
29582
|
+
".flac": "audio/flac",
|
|
29583
|
+
".mp4": "video/mp4",
|
|
29584
|
+
".m4v": "video/x-m4v",
|
|
29585
|
+
".webm": "video/webm",
|
|
29586
|
+
".mov": "video/quicktime",
|
|
29587
|
+
".mkv": "video/x-matroska",
|
|
29588
|
+
".zip": "application/zip"
|
|
29589
|
+
};
|
|
29590
|
+
function detectMime(buffer, name) {
|
|
29591
|
+
for (const [sig, mime] of MAGIC) {
|
|
29592
|
+
if (buffer.length >= sig.length && buffer.subarray(0, sig.length).equals(sig)) {
|
|
29593
|
+
return mime;
|
|
29594
|
+
}
|
|
29595
|
+
}
|
|
29596
|
+
const ext = extname(name).toLowerCase();
|
|
29597
|
+
return EXT_MIME[ext] ?? "application/octet-stream";
|
|
29598
|
+
}
|
|
29599
|
+
|
|
29600
|
+
// src/lib/create-input.ts
|
|
29601
|
+
var IMAGE_MAX_BYTES = 30 * 1024 * 1024;
|
|
29602
|
+
var VIDEO_MAX_BYTES = 50 * 1024 * 1024;
|
|
29603
|
+
var AUDIO_MAX_BYTES = 15 * 1024 * 1024;
|
|
29604
|
+
var CREATE_INPUT_MEDIA = {
|
|
29605
|
+
"image/jpeg": { extension: "jpg", maxBytes: IMAGE_MAX_BYTES },
|
|
29606
|
+
"image/png": { extension: "png", maxBytes: IMAGE_MAX_BYTES },
|
|
29607
|
+
"image/webp": { extension: "webp", maxBytes: IMAGE_MAX_BYTES },
|
|
29608
|
+
"image/heic": { extension: "heic", maxBytes: IMAGE_MAX_BYTES },
|
|
29609
|
+
"image/heif": { extension: "heif", maxBytes: IMAGE_MAX_BYTES },
|
|
29610
|
+
"video/mp4": { extension: "mp4", maxBytes: VIDEO_MAX_BYTES },
|
|
29611
|
+
"video/quicktime": { extension: "mov", maxBytes: VIDEO_MAX_BYTES },
|
|
29612
|
+
"audio/mpeg": { extension: "mp3", maxBytes: AUDIO_MAX_BYTES },
|
|
29613
|
+
"audio/wav": { extension: "wav", maxBytes: AUDIO_MAX_BYTES }
|
|
29614
|
+
};
|
|
29615
|
+
var MIME_ALIASES = {
|
|
29616
|
+
"image/jpg": "image/jpeg",
|
|
29617
|
+
"image/pjpeg": "image/jpeg",
|
|
29618
|
+
"audio/mp3": "audio/mpeg",
|
|
29619
|
+
"audio/x-wav": "audio/wav"
|
|
29620
|
+
};
|
|
29621
|
+
var EXT_TO_MIME = {
|
|
29622
|
+
".jpg": "image/jpeg",
|
|
29623
|
+
".jpeg": "image/jpeg",
|
|
29624
|
+
".png": "image/png",
|
|
29625
|
+
".webp": "image/webp",
|
|
29626
|
+
".heic": "image/heic",
|
|
29627
|
+
".heif": "image/heif",
|
|
29628
|
+
".mp4": "video/mp4",
|
|
29629
|
+
".m4v": "video/mp4",
|
|
29630
|
+
".mov": "video/quicktime",
|
|
29631
|
+
".mp3": "audio/mpeg",
|
|
29632
|
+
".wav": "audio/wav"
|
|
29633
|
+
};
|
|
29634
|
+
var KIND_PREFIX = {
|
|
29635
|
+
image: "image/",
|
|
29636
|
+
video: "video/",
|
|
29637
|
+
audio: "audio/"
|
|
29638
|
+
};
|
|
29639
|
+
var KIND_ACCEPTS = {
|
|
29640
|
+
image: "jpg, png, webp, heic, heif (max 30 MB)",
|
|
29641
|
+
video: "mp4, mov (max 50 MB)",
|
|
29642
|
+
audio: "mp3, wav (max 15 MB)"
|
|
29643
|
+
};
|
|
29644
|
+
function canonicalMime(rawMime, filename) {
|
|
29645
|
+
const base = rawMime.split(";")[0]?.trim().toLowerCase() ?? "";
|
|
29646
|
+
if (base) {
|
|
29647
|
+
const aliased = MIME_ALIASES[base] ?? base;
|
|
29648
|
+
if (CREATE_INPUT_MEDIA[aliased])
|
|
29649
|
+
return aliased;
|
|
29650
|
+
}
|
|
29651
|
+
const fromExt = EXT_TO_MIME[extname2(filename).toLowerCase()];
|
|
29652
|
+
return fromExt && CREATE_INPUT_MEDIA[fromExt] ? fromExt : undefined;
|
|
29653
|
+
}
|
|
29654
|
+
async function uploadCreateInput(auth, source, kind) {
|
|
29655
|
+
const file2 = await resolveFileInput(source, {
|
|
29656
|
+
maxBytes: VIDEO_MAX_BYTES
|
|
29657
|
+
});
|
|
29658
|
+
const mime = canonicalMime(file2.mime, file2.name || basename2(source));
|
|
29659
|
+
if (!mime) {
|
|
29660
|
+
throw new Error(`"${source}" is not a Create Studio input type (detected ${file2.mime || "unknown"}). Accepted: ${KIND_ACCEPTS[kind]}.`);
|
|
29661
|
+
}
|
|
29662
|
+
if (!mime.startsWith(KIND_PREFIX[kind])) {
|
|
29663
|
+
throw new Error(`"${source}" is ${mime}, not ${kind === "audio" ? "an" : "a"} ${kind}. Accepted here: ${KIND_ACCEPTS[kind]}.`);
|
|
29664
|
+
}
|
|
29665
|
+
const config2 = CREATE_INPUT_MEDIA[mime];
|
|
29666
|
+
if (file2.size > config2.maxBytes) {
|
|
29667
|
+
throw new Error(`"${source}" is ${file2.size} bytes; the ${kind} limit is ${config2.maxBytes} bytes (${KIND_ACCEPTS[kind]}).`);
|
|
29668
|
+
}
|
|
29669
|
+
const session = await amikoWebFetch(auth, "/api/create/uploads", {
|
|
29670
|
+
method: "POST",
|
|
29671
|
+
body: { contentType: mime, sizeBytes: file2.size },
|
|
29672
|
+
timeoutMs: 30000
|
|
29673
|
+
});
|
|
29674
|
+
if (!session.uploadUrl || !session.path) {
|
|
29675
|
+
throw new Error(`Could not start the upload for "${source}".`);
|
|
29676
|
+
}
|
|
29677
|
+
await presignedPut(session.uploadUrl, file2.buffer, mime);
|
|
29678
|
+
const finalized = await amikoWebFetch(auth, "/api/create/uploads", {
|
|
29679
|
+
method: "PUT",
|
|
29680
|
+
body: { path: session.path },
|
|
29681
|
+
timeoutMs: 30000
|
|
29682
|
+
});
|
|
29683
|
+
if (!finalized.url) {
|
|
29684
|
+
throw new Error(`Upload of "${source}" completed but returned no URL.`);
|
|
29685
|
+
}
|
|
29686
|
+
return { url: finalized.url, durationMs: readMediaDurationMs(file2.buffer, mime) };
|
|
29687
|
+
}
|
|
29688
|
+
async function resolveCreateInput(auth, value, kind, opts = {}) {
|
|
29689
|
+
const trimmed = value.trim();
|
|
29690
|
+
if (trimmed === "") {
|
|
29691
|
+
throw new Error("Empty media argument.");
|
|
29692
|
+
}
|
|
29693
|
+
if (/^data:/i.test(trimmed)) {
|
|
29694
|
+
if (!opts.allowDataUri) {
|
|
29695
|
+
throw new Error("A data: URI only works for --first-frame/--last-frame. Reference media is fetched by the provider, so pass a local file path or an https URL.");
|
|
29696
|
+
}
|
|
29697
|
+
return { url: trimmed, durationMs: null };
|
|
29698
|
+
}
|
|
29699
|
+
if (/^https:\/\//i.test(trimmed))
|
|
29700
|
+
return { url: trimmed, durationMs: null };
|
|
29701
|
+
if (/^http:\/\//i.test(trimmed)) {
|
|
29702
|
+
throw new Error(`"${trimmed}" is plain http. The provider fetches this URL server-side and amiko-web requires https — use an https URL, or pass the local file and let the CLI upload it.`);
|
|
29703
|
+
}
|
|
29704
|
+
return uploadCreateInput(auth, trimmed, kind);
|
|
29705
|
+
}
|
|
29706
|
+
async function resolveCreateInputs(auth, values, kind) {
|
|
29707
|
+
const out = [];
|
|
29708
|
+
for (const value of values) {
|
|
29709
|
+
out.push(await resolveCreateInput(auth, value, kind));
|
|
29710
|
+
}
|
|
29711
|
+
return out;
|
|
29712
|
+
}
|
|
29713
|
+
function mediaUrls(items) {
|
|
29714
|
+
return items.map((item) => item.url);
|
|
29715
|
+
}
|
|
29716
|
+
|
|
29717
|
+
// src/commands/create.ts
|
|
28694
29718
|
var MUSIC_MAX_SECONDS = 300;
|
|
28695
29719
|
var ETA = {
|
|
28696
29720
|
IMAGE: "~15–60s",
|
|
@@ -28712,11 +29736,9 @@ function autoApproveLimit() {
|
|
|
28712
29736
|
function collectOption(value, previous) {
|
|
28713
29737
|
return [...previous ?? [], value];
|
|
28714
29738
|
}
|
|
28715
|
-
function
|
|
28716
|
-
|
|
28717
|
-
|
|
28718
|
-
process.exit(1);
|
|
28719
|
-
}
|
|
29739
|
+
function failInput(message) {
|
|
29740
|
+
console.error(error(message));
|
|
29741
|
+
process.exit(1);
|
|
28720
29742
|
}
|
|
28721
29743
|
function printCompleted(final, title, deliveredToThisSession = false) {
|
|
28722
29744
|
console.log(heading(`${title}
|
|
@@ -28751,13 +29773,28 @@ async function submitCreateJob(opts) {
|
|
|
28751
29773
|
console.error(error(e5 instanceof Error ? e5.message : "auth failed"));
|
|
28752
29774
|
process.exit(1);
|
|
28753
29775
|
}
|
|
29776
|
+
let bodies;
|
|
29777
|
+
if (typeof opts.bodies === "function") {
|
|
29778
|
+
const uploadSpinner = process.stderr.isTTY ? ora("Preparing media…").start() : null;
|
|
29779
|
+
try {
|
|
29780
|
+
bodies = await opts.bodies(auth);
|
|
29781
|
+
} catch (e5) {
|
|
29782
|
+
uploadSpinner?.stop();
|
|
29783
|
+
console.error(error(e5 instanceof Error ? e5.message : "Could not prepare the media inputs."));
|
|
29784
|
+
process.exit(1);
|
|
29785
|
+
}
|
|
29786
|
+
uploadSpinner?.stop();
|
|
29787
|
+
} else {
|
|
29788
|
+
bodies = opts.bodies;
|
|
29789
|
+
}
|
|
29790
|
+
const { jobBody, quoteBody } = bodies;
|
|
28754
29791
|
const payMethod = (opts.pay ?? "credits").toLowerCase();
|
|
28755
29792
|
if (payMethod !== "wallet" && payMethod !== "credits") {
|
|
28756
29793
|
console.error(error(`Unknown --pay "${opts.pay}". Allowed: wallet, credits.`));
|
|
28757
29794
|
process.exit(1);
|
|
28758
29795
|
}
|
|
28759
29796
|
const payCredits = payMethod === "credits";
|
|
28760
|
-
if (payCredits &&
|
|
29797
|
+
if (payCredits && jobBody.preferredToken) {
|
|
28761
29798
|
console.error(dim("--token is ignored with --pay credits (credits come from your Amiko account, not a wallet token)."));
|
|
28762
29799
|
}
|
|
28763
29800
|
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)";
|
|
@@ -28765,7 +29802,7 @@ async function submitCreateJob(opts) {
|
|
|
28765
29802
|
try {
|
|
28766
29803
|
const q = await amikoWebFetch(auth, "/api/create/quote", {
|
|
28767
29804
|
method: "POST",
|
|
28768
|
-
body:
|
|
29805
|
+
body: quoteBody,
|
|
28769
29806
|
timeoutMs: 15000
|
|
28770
29807
|
});
|
|
28771
29808
|
if (q.cost_usd != null) {
|
|
@@ -28777,7 +29814,7 @@ async function submitCreateJob(opts) {
|
|
|
28777
29814
|
quotedCostAmiko = n;
|
|
28778
29815
|
}
|
|
28779
29816
|
} catch {}
|
|
28780
|
-
const preferredToken = typeof
|
|
29817
|
+
const preferredToken = typeof jobBody.preferredToken === "string" ? jobBody.preferredToken.toUpperCase() : undefined;
|
|
28781
29818
|
const chargeIsAmiko = !payCredits && (preferredToken === undefined || preferredToken === "AMIKO");
|
|
28782
29819
|
const autoApprovable = payCredits || chargeIsAmiko;
|
|
28783
29820
|
const effectiveChargeToken = preferredToken ?? "AMIKO";
|
|
@@ -28810,7 +29847,7 @@ async function submitCreateJob(opts) {
|
|
|
28810
29847
|
const submit = await amikoWebFetch(auth, "/api/create/jobs", {
|
|
28811
29848
|
method: "POST",
|
|
28812
29849
|
body: {
|
|
28813
|
-
...
|
|
29850
|
+
...jobBody,
|
|
28814
29851
|
twinId: auth.twinId,
|
|
28815
29852
|
...autoApproved && !payCredits ? { preferredToken: effectiveChargeToken, strictToken: true } : {},
|
|
28816
29853
|
...payCredits ? {
|
|
@@ -28874,72 +29911,150 @@ async function submitCreateJob(opts) {
|
|
|
28874
29911
|
}
|
|
28875
29912
|
}
|
|
28876
29913
|
function registerCreateCommand(create2) {
|
|
28877
|
-
create2.command("image <prompt>").description("Generate an image (Create Studio)").option("--model <model>", "Image model: nano-banana-2 (default), nano-banana[-pro|-lite] (Google), gpt-image-2/-1.5/-1/-1-mini (OpenAI), grok-imagine-image-quality (xAI), image-01 (MiniMax)", "nano-banana-2").option("--aspect <ratio>", "Aspect ratio: 1:1, 16:9, 9:16, 4:3, 3:4", "1:1").option("--token <symbol>", "Preferred charge token: AMIKO, USDC, USDT, SOL").option("--pay <method>", "Payment method: credits (Amiko account credits, default) or wallet (twin wallet)", "credits").option("--raw", "Output raw JSON").option("--yes", "Skip the pre-spend confirmation (required in non-interactive shells)").action(async (prompt, opts) => {
|
|
29914
|
+
create2.command("image <prompt>").description("Generate an image (Create Studio)").option("--model <model>", "Image model: nano-banana-2 (default), nano-banana[-pro|-lite] (Google), gpt-image-2/-1.5/-1/-1-mini (OpenAI), grok-imagine-image-quality (xAI), image-01 (MiniMax)", "nano-banana-2").option("--aspect <ratio>", "Aspect ratio: 1:1, 16:9, 9:16, 4:3, 3:4", "1:1").option("--reference-image <file|url>", `Image-to-image reference: local file path or https URL (repeatable). nano-banana* / gpt-image*: up to ${IMAGE_REFERENCE_MAX.multi}; image-01: exactly 1 front-facing character portrait; grok-imagine-image-quality: not supported`, collectOption, []).option("--token <symbol>", "Preferred charge token: AMIKO, USDC, USDT, SOL").option("--pay <method>", "Payment method: credits (Amiko account credits, default) or wallet (twin wallet)", "credits").option("--raw", "Output raw JSON").option("--yes", "Skip the pre-spend confirmation (required in non-interactive shells)").action(async (prompt, opts) => {
|
|
29915
|
+
if (prompt.length > NON_H3_MAX_PROMPT_CHARS) {
|
|
29916
|
+
failInput(`Image prompts are limited to ${NON_H3_MAX_PROMPT_CHARS} characters (got ${prompt.length}).`);
|
|
29917
|
+
}
|
|
29918
|
+
const referenceImages = opts.referenceImage ?? [];
|
|
29919
|
+
const refProblem = validateImageReferences(opts.model, referenceImages);
|
|
29920
|
+
if (refProblem)
|
|
29921
|
+
failInput(refProblem);
|
|
28878
29922
|
await submitCreateJob({
|
|
28879
29923
|
mode: "IMAGE",
|
|
28880
29924
|
summary: `Create image: ${prompt.slice(0, 80)}${prompt.length > 80 ? "…" : ""}`,
|
|
28881
29925
|
raw: opts.raw,
|
|
28882
29926
|
pay: opts.pay,
|
|
28883
29927
|
yes: opts.yes,
|
|
28884
|
-
|
|
28885
|
-
|
|
28886
|
-
|
|
28887
|
-
|
|
28888
|
-
|
|
28889
|
-
|
|
28890
|
-
|
|
29928
|
+
bodies: async (auth) => {
|
|
29929
|
+
const resolved = mediaUrls(await resolveCreateInputs(auth, referenceImages, "image"));
|
|
29930
|
+
return {
|
|
29931
|
+
quoteBody: {
|
|
29932
|
+
mode: "IMAGE",
|
|
29933
|
+
model: opts.model,
|
|
29934
|
+
size: opts.aspect,
|
|
29935
|
+
...resolved.length ? { referenceImageUrls: resolved } : {}
|
|
29936
|
+
},
|
|
29937
|
+
jobBody: {
|
|
29938
|
+
mode: "IMAGE",
|
|
29939
|
+
prompt,
|
|
29940
|
+
model: opts.model,
|
|
29941
|
+
size: opts.aspect,
|
|
29942
|
+
...resolved.length ? { referenceImageUrls: resolved } : {},
|
|
29943
|
+
...opts.token ? { preferredToken: opts.token } : {}
|
|
29944
|
+
}
|
|
29945
|
+
};
|
|
28891
29946
|
}
|
|
28892
29947
|
});
|
|
28893
29948
|
});
|
|
28894
|
-
create2.command("video <prompt>").description("Generate a video (Create Studio)").option("--model <model>", "Video model: MiniMax-Hailuo-02 (default for prompt-only; T2V+I2V), MiniMax-Hailuo-2.3-Fast (default with --first-frame; I2V only), MiniMax-Hailuo-2.3 (T2V+I2V), veo-3.1[-fast|-lite]-generate-preview (Google; durations clamp to 4/6/8s), grok-imagine-video (xAI), dreamina-seedance-* (BytePlus). ONLY these — kling and other ids are not supported").option("--resolution <res>", "512P, 720P, 768P, 1080P", "768P").option("--seconds <n>", "Clip length: 6 or 10", "6").option("--aspect <ratio>", "Aspect ratio (provider-dependent)").option("--first-frame <url>", "Image-to-video: first frame URL
|
|
29949
|
+
create2.command("video <prompt>").description("Generate a video (Create Studio)").option("--model <model>", "Video model: MiniMax-Hailuo-02 (default for prompt-only; T2V+I2V), MiniMax-Hailuo-2.3-Fast (default with --first-frame; I2V only), MiniMax-Hailuo-2.3 (T2V+I2V), MiniMax-H3 (aliases h3, hailuo-03; T2V+I2V+reference-to-video, 768P|2K, any 4–15s, billed per second), veo-3.1[-fast|-lite]-generate-preview (Google; durations clamp to 4/6/8s), grok-imagine-video (xAI), dreamina-seedance-* (BytePlus). ONLY these — kling and other ids are not supported").option("--resolution <res>", "512P, 720P, 768P, 1080P (MiniMax, Grok, Seedance); 720p, 1080p, 4k (Veo); 768P or 2K (MiniMax-H3)", "768P").option("--seconds <n>", "Clip length: 6 or 10 (Hailuo), 4/6/8 (Veo), 4/6/8/10 (Grok, Seedance), any whole number 4–15 (MiniMax-H3)", "6").option("--aspect <ratio>", "Aspect ratio (provider-dependent). MiniMax-H3 text-to-video needs a concrete ratio (21:9, 16:9, 4:3, 1:1, 3:4, 9:16); defaults to 16:9 when omitted").option("--first-frame <file|url>", "Image-to-video: first frame — local file path, https URL, or data URI").option("--last-frame <file|url>", "First-and-last-frame video: ending frame — local file path, https URL, or data URI. MiniMax-H3, Seedance and Veo only (Seedance/Veo also need --first-frame)").option("--reference-image <file|url>", "Reference-to-video: local file path or https URL (repeatable). MiniMax-H3 / Seedance / Grok: max 9; Veo: max 3; Hailuo v1: NOT supported — use --subject-reference", collectOption, []).option("--reference-video <file|url>", "Reference-to-video: local file path or https URL (repeatable, max 3). MiniMax-H3 and Seedance only; combined reference video+audio must be ≤15s, and H3 bills 15 input-seconds whenever any is attached", collectOption, []).option("--reference-audio <file|url>", "Reference-to-video: local file path or https URL (repeatable, max 3). MiniMax-H3 and Seedance only; needs an image or video reference alongside, H3 wants 2–15s per clip", collectOption, []).option("--subject-reference <file|url>", "MiniMax S2V / H3: subject/character reference image — local file path or https URL. Takes the place of --reference-image rather than adding to it").option("--generate-audio", "Seedance: generate audio in the output video").option("--no-generate-audio", "Seedance: output silent video").option("--watermark", "Seedance: burn in a watermark").option("--no-watermark", "Seedance: no watermark").option("--camera-fixed", "Seedance: hold the camera still").option("--no-camera-fixed", "Seedance: allow camera movement").option("--prompt-optimizer", "MiniMax (H3 and Hailuo v1): let the model rewrite the prompt before generating").option("--no-prompt-optimizer", "MiniMax: use the prompt verbatim").option("--fast-pretreatment", "MiniMax Hailuo v1: faster input pre-processing (ignored by H3, Veo, Grok and Seedance)").option("--no-fast-pretreatment", "MiniMax Hailuo v1: standard input pre-processing").option("--token <symbol>", "Preferred charge token: AMIKO, USDC, USDT, SOL").option("--pay <method>", "Payment method: credits (Amiko account credits, default) or wallet (twin wallet)", "credits").option("--raw", "Output raw JSON").option("--yes", "Skip the pre-spend confirmation (required in non-interactive shells)").action(async (prompt, opts) => {
|
|
29950
|
+
const model = opts.model ?? (opts.firstFrame ? "MiniMax-Hailuo-2.3-Fast" : "MiniMax-Hailuo-02");
|
|
29951
|
+
const isH3 = isH3VideoModel(model);
|
|
28895
29952
|
const seconds = Number(opts.seconds);
|
|
28896
|
-
if (
|
|
28897
|
-
|
|
28898
|
-
|
|
29953
|
+
if (isH3) {
|
|
29954
|
+
if (!Number.isInteger(seconds) || seconds < H3_MIN_SECONDS || seconds > H3_MAX_SECONDS) {
|
|
29955
|
+
failInput(`--seconds must be a whole number from ${H3_MIN_SECONDS} to ${H3_MAX_SECONDS} for MiniMax-H3.`);
|
|
29956
|
+
}
|
|
29957
|
+
} else if (!NON_H3_VIDEO_SECONDS.has(seconds)) {
|
|
29958
|
+
failInput("--seconds must be 4, 6, 8, or 10 (Hailuo accepts 6 or 10; use --model MiniMax-H3 for any 4–15s).");
|
|
28899
29959
|
}
|
|
28900
|
-
|
|
28901
|
-
|
|
28902
|
-
|
|
28903
|
-
|
|
28904
|
-
|
|
28905
|
-
|
|
29960
|
+
let resolution = opts.resolution;
|
|
29961
|
+
if (isH3) {
|
|
29962
|
+
const upper = resolution.toUpperCase();
|
|
29963
|
+
if (!H3_RESOLUTIONS.has(upper)) {
|
|
29964
|
+
failInput(`MiniMax-H3 only supports 768P and 2K, not ${resolution}.`);
|
|
29965
|
+
}
|
|
29966
|
+
resolution = upper;
|
|
29967
|
+
}
|
|
29968
|
+
const maxPromptChars = isH3 ? H3_MAX_PROMPT_CHARS : NON_H3_MAX_PROMPT_CHARS;
|
|
29969
|
+
if (prompt.length > maxPromptChars) {
|
|
29970
|
+
failInput(`${isH3 ? "MiniMax-H3" : model} prompts are limited to ${maxPromptChars} characters (got ${prompt.length}).` + (isH3 ? "" : " MiniMax-H3 accepts up to 7000."));
|
|
29971
|
+
}
|
|
29972
|
+
const referenceImageArgs = opts.referenceImage ?? [];
|
|
29973
|
+
const referenceVideoArgs = opts.referenceVideo ?? [];
|
|
29974
|
+
const referenceAudioArgs = opts.referenceAudio ?? [];
|
|
28906
29975
|
const generateAudio = opts.generateAudio;
|
|
28907
|
-
const
|
|
29976
|
+
const promptOptimizer = opts.promptOptimizer;
|
|
29977
|
+
const watermark = opts.watermark;
|
|
29978
|
+
const cameraFixed = opts.cameraFixed;
|
|
29979
|
+
const fastPretreatment = opts.fastPretreatment;
|
|
29980
|
+
const referenceProblem = validateVideoReferences({
|
|
29981
|
+
model,
|
|
29982
|
+
firstFrame: opts.firstFrame,
|
|
29983
|
+
lastFrame: opts.lastFrame,
|
|
29984
|
+
subjectReference: opts.subjectReference,
|
|
29985
|
+
referenceImageUrls: referenceImageArgs,
|
|
29986
|
+
referenceVideoUrls: referenceVideoArgs,
|
|
29987
|
+
referenceAudioUrls: referenceAudioArgs
|
|
29988
|
+
});
|
|
29989
|
+
if (referenceProblem)
|
|
29990
|
+
failInput(referenceProblem);
|
|
29991
|
+
const hasFrame = Boolean(opts.firstFrame) || Boolean(opts.lastFrame);
|
|
29992
|
+
const hasReference = Boolean(opts.subjectReference) || referenceImageArgs.length > 0 || referenceVideoArgs.length > 0 || referenceAudioArgs.length > 0;
|
|
29993
|
+
const hasVisualInput = hasFrame || hasReference;
|
|
29994
|
+
let aspect = opts.aspect;
|
|
29995
|
+
if (isH3 && !hasVisualInput) {
|
|
29996
|
+
if (!aspect) {
|
|
29997
|
+
aspect = H3_DEFAULT_T2V_ASPECT;
|
|
29998
|
+
if (!opts.raw) {
|
|
29999
|
+
console.error(dim(`MiniMax-H3 text-to-video needs an aspect ratio; using ${aspect}.`));
|
|
30000
|
+
}
|
|
30001
|
+
} else if (!H3_CONCRETE_RATIOS.has(aspect)) {
|
|
30002
|
+
failInput(`MiniMax-H3 text-to-video needs a specific aspect ratio (${[...H3_CONCRETE_RATIOS].join(", ")}), not ${aspect}.`);
|
|
30003
|
+
}
|
|
30004
|
+
}
|
|
28908
30005
|
await submitCreateJob({
|
|
28909
30006
|
mode: "VIDEO",
|
|
28910
30007
|
summary: `Create video: ${prompt.slice(0, 80)}${prompt.length > 80 ? "…" : ""}`,
|
|
28911
30008
|
raw: opts.raw,
|
|
28912
30009
|
pay: opts.pay,
|
|
28913
30010
|
yes: opts.yes,
|
|
28914
|
-
|
|
28915
|
-
|
|
28916
|
-
|
|
28917
|
-
|
|
28918
|
-
|
|
28919
|
-
|
|
28920
|
-
|
|
28921
|
-
|
|
28922
|
-
|
|
28923
|
-
|
|
28924
|
-
|
|
28925
|
-
|
|
28926
|
-
|
|
28927
|
-
|
|
28928
|
-
|
|
28929
|
-
|
|
28930
|
-
|
|
28931
|
-
|
|
28932
|
-
|
|
28933
|
-
|
|
28934
|
-
|
|
28935
|
-
|
|
28936
|
-
|
|
28937
|
-
|
|
28938
|
-
|
|
28939
|
-
|
|
28940
|
-
|
|
28941
|
-
|
|
28942
|
-
|
|
30011
|
+
bodies: async (auth) => {
|
|
30012
|
+
const firstFrame = opts.firstFrame ? (await resolveCreateInput(auth, opts.firstFrame, "image", { allowDataUri: true })).url : undefined;
|
|
30013
|
+
const lastFrame = opts.lastFrame ? (await resolveCreateInput(auth, opts.lastFrame, "image", { allowDataUri: true })).url : undefined;
|
|
30014
|
+
const subjectReference = opts.subjectReference ? (await resolveCreateInput(auth, opts.subjectReference, "image")).url : undefined;
|
|
30015
|
+
const referenceImages = await resolveCreateInputs(auth, referenceImageArgs, "image");
|
|
30016
|
+
const referenceVideos = await resolveCreateInputs(auth, referenceVideoArgs, "video");
|
|
30017
|
+
const referenceAudio = await resolveCreateInputs(auth, referenceAudioArgs, "audio");
|
|
30018
|
+
const durationProblem = validateReferenceDurations(model, referenceVideos.map((m) => m.durationMs), referenceAudio.map((m) => m.durationMs));
|
|
30019
|
+
if (durationProblem)
|
|
30020
|
+
failInput(durationProblem);
|
|
30021
|
+
const referenceImageUrls = mediaUrls(referenceImages);
|
|
30022
|
+
const referenceVideoUrls = mediaUrls(referenceVideos);
|
|
30023
|
+
const referenceAudioUrls = mediaUrls(referenceAudio);
|
|
30024
|
+
return {
|
|
30025
|
+
quoteBody: {
|
|
30026
|
+
mode: "VIDEO",
|
|
30027
|
+
model,
|
|
30028
|
+
size: resolution,
|
|
30029
|
+
seconds,
|
|
30030
|
+
...referenceVideoUrls.length ? { referenceVideoUrls } : {},
|
|
30031
|
+
...referenceImageUrls.length ? { referenceImageUrls } : {}
|
|
30032
|
+
},
|
|
30033
|
+
jobBody: {
|
|
30034
|
+
mode: "VIDEO",
|
|
30035
|
+
prompt,
|
|
30036
|
+
model,
|
|
30037
|
+
resolution,
|
|
30038
|
+
seconds,
|
|
30039
|
+
...aspect ? { aspectRatio: aspect } : {},
|
|
30040
|
+
...firstFrame ? { firstFrameImage: firstFrame } : {},
|
|
30041
|
+
...lastFrame ? { lastFrameImage: lastFrame } : {},
|
|
30042
|
+
...referenceImageUrls.length ? { referenceImageUrls } : {},
|
|
30043
|
+
...referenceVideoUrls.length ? { referenceVideoUrls } : {},
|
|
30044
|
+
...referenceAudioUrls.length ? { referenceAudioUrls } : {},
|
|
30045
|
+
...subjectReference ? {
|
|
30046
|
+
subjectReference: [
|
|
30047
|
+
{ type: "character", image: [subjectReference] }
|
|
30048
|
+
]
|
|
30049
|
+
} : {},
|
|
30050
|
+
...generateAudio !== undefined ? { generateAudio } : {},
|
|
30051
|
+
...promptOptimizer !== undefined ? { promptOptimizer } : {},
|
|
30052
|
+
...fastPretreatment !== undefined ? { fastPretreatment } : {},
|
|
30053
|
+
...watermark !== undefined ? { watermark } : {},
|
|
30054
|
+
...cameraFixed !== undefined ? { cameraFixed } : {},
|
|
30055
|
+
...opts.token ? { preferredToken: opts.token } : {}
|
|
30056
|
+
}
|
|
30057
|
+
};
|
|
28943
30058
|
}
|
|
28944
30059
|
});
|
|
28945
30060
|
});
|
|
@@ -28985,17 +30100,19 @@ function registerCreateCommand(create2) {
|
|
|
28985
30100
|
raw: opts.raw,
|
|
28986
30101
|
pay: opts.pay,
|
|
28987
30102
|
yes: opts.yes,
|
|
28988
|
-
|
|
28989
|
-
|
|
28990
|
-
|
|
28991
|
-
|
|
28992
|
-
|
|
28993
|
-
|
|
28994
|
-
|
|
30103
|
+
bodies: {
|
|
30104
|
+
quoteBody: { mode: "TTS", model, prompt: text, voiceId: voice },
|
|
30105
|
+
jobBody: {
|
|
30106
|
+
mode: "TTS",
|
|
30107
|
+
prompt: text,
|
|
30108
|
+
model,
|
|
30109
|
+
voiceId: voice,
|
|
30110
|
+
...opts.token ? { preferredToken: opts.token } : {}
|
|
30111
|
+
}
|
|
28995
30112
|
}
|
|
28996
30113
|
});
|
|
28997
30114
|
});
|
|
28998
|
-
create2.command("music [prompt]").description("Generate music (Create Studio)").option("--model <model>", "music-3.0 (default), music-cover, music_v1. The retired MiniMax ids (music-2.0/2.5/2.6, -free) still resolve to music-3.0", "music-3.0").option("--lyrics <lyrics>", "Lyrics (optional; a prompt is still required for the style). Needs section tags on their own lines, e.g. [verse] / [chorus] — plain lyrics are rejected").option("--duration <seconds>", "Track length in seconds, 1-300 — music-3.0 bills per second, so this sets the price").option("--audio-url <url>", "
|
|
30115
|
+
create2.command("music [prompt]").description("Generate music (Create Studio)").option("--model <model>", "music-3.0 (default), music-cover, music_v1. The retired MiniMax ids (music-2.0/2.5/2.6, -free) still resolve to music-3.0", "music-3.0").option("--lyrics <lyrics>", "Lyrics (optional; a prompt is still required for the style). Needs section tags on their own lines, e.g. [verse] / [chorus] — plain lyrics are rejected").option("--duration <seconds>", "Track length in seconds, 1-300 — music-3.0 bills per second, so this sets the price").option("--audio-url <file|url>", "Audio to cover — local file path or https URL (required by --model music-cover)").option("--instrumental", "Instrumental (no vocals)").option("--lyrics-optimizer", "Auto-write lyrics from the prompt. Only affects music_v1 — the fal models always write their own when no --lyrics is given").option("--token <symbol>", "Preferred charge token: AMIKO, USDC, USDT, SOL").option("--pay <method>", "Payment method: credits (Amiko account credits, default) or wallet (twin wallet)", "credits").option("--raw", "Output raw JSON").option("--yes", "Skip the pre-spend confirmation (required in non-interactive shells)").action(async (prompt, opts) => {
|
|
28999
30116
|
const isCover = String(opts.model ?? "").startsWith("music-cover");
|
|
29000
30117
|
if (isCover) {
|
|
29001
30118
|
if (!opts.audioUrl) {
|
|
@@ -29026,21 +30143,26 @@ function registerCreateCommand(create2) {
|
|
|
29026
30143
|
raw: opts.raw,
|
|
29027
30144
|
pay: opts.pay,
|
|
29028
30145
|
yes: opts.yes,
|
|
29029
|
-
|
|
29030
|
-
|
|
29031
|
-
|
|
29032
|
-
|
|
29033
|
-
|
|
29034
|
-
|
|
29035
|
-
|
|
29036
|
-
|
|
29037
|
-
|
|
29038
|
-
|
|
29039
|
-
|
|
29040
|
-
|
|
29041
|
-
|
|
29042
|
-
|
|
29043
|
-
|
|
30146
|
+
bodies: async (auth) => {
|
|
30147
|
+
const audioUrl = opts.audioUrl ? (await resolveCreateInput(auth, opts.audioUrl, "audio")).url : undefined;
|
|
30148
|
+
return {
|
|
30149
|
+
quoteBody: {
|
|
30150
|
+
mode: "MUSIC",
|
|
30151
|
+
model: opts.model,
|
|
30152
|
+
...durationMs ? { durationMs } : {}
|
|
30153
|
+
},
|
|
30154
|
+
jobBody: {
|
|
30155
|
+
mode: "MUSIC",
|
|
30156
|
+
model: opts.model,
|
|
30157
|
+
...prompt ? { prompt } : {},
|
|
30158
|
+
...opts.lyrics ? { lyrics: opts.lyrics } : {},
|
|
30159
|
+
...durationMs ? { durationMs } : {},
|
|
30160
|
+
...audioUrl ? { audioUrl } : {},
|
|
30161
|
+
...opts.instrumental ? { isInstrumental: true } : {},
|
|
30162
|
+
...opts.lyricsOptimizer ? { lyricsOptimizer: true } : {},
|
|
30163
|
+
...opts.token ? { preferredToken: opts.token } : {}
|
|
30164
|
+
}
|
|
30165
|
+
};
|
|
29044
30166
|
}
|
|
29045
30167
|
});
|
|
29046
30168
|
});
|
|
@@ -29051,12 +30173,14 @@ function registerCreateCommand(create2) {
|
|
|
29051
30173
|
raw: opts.raw,
|
|
29052
30174
|
pay: opts.pay,
|
|
29053
30175
|
yes: opts.yes,
|
|
29054
|
-
|
|
29055
|
-
|
|
29056
|
-
|
|
29057
|
-
|
|
29058
|
-
|
|
29059
|
-
|
|
30176
|
+
bodies: {
|
|
30177
|
+
quoteBody: { mode: "SFX" },
|
|
30178
|
+
jobBody: {
|
|
30179
|
+
mode: "SFX",
|
|
30180
|
+
prompt: text,
|
|
30181
|
+
...opts.duration ? { durationSeconds: Number(opts.duration) } : {},
|
|
30182
|
+
...opts.token ? { preferredToken: opts.token } : {}
|
|
30183
|
+
}
|
|
29060
30184
|
}
|
|
29061
30185
|
});
|
|
29062
30186
|
});
|
|
@@ -29141,111 +30265,6 @@ More available — raise --limit to see more.`));
|
|
|
29141
30265
|
});
|
|
29142
30266
|
}
|
|
29143
30267
|
|
|
29144
|
-
// src/lib/file-input.ts
|
|
29145
|
-
import { readFile } from "node:fs/promises";
|
|
29146
|
-
import { basename, extname } from "node:path";
|
|
29147
|
-
var MAX_BYTES = 100 * 1024 * 1024;
|
|
29148
|
-
var FETCH_TIMEOUT_MS = 30000;
|
|
29149
|
-
async function resolveFileInput(src, opts = {}) {
|
|
29150
|
-
const maxBytes = opts.maxBytes ?? MAX_BYTES;
|
|
29151
|
-
if (src === "-" || opts.stdin) {
|
|
29152
|
-
const buffer2 = await readStdin(maxBytes);
|
|
29153
|
-
const name2 = opts.hintName ?? "stdin";
|
|
29154
|
-
const mime2 = opts.hintMime ?? detectMime(buffer2, name2);
|
|
29155
|
-
return { buffer: buffer2, name: name2, mime: mime2, size: buffer2.length };
|
|
29156
|
-
}
|
|
29157
|
-
if (/^https?:\/\//i.test(src)) {
|
|
29158
|
-
const buffer2 = await fetchRemote(src, maxBytes);
|
|
29159
|
-
const urlPath = new URL(src).pathname;
|
|
29160
|
-
const name2 = opts.hintName ?? (basename(urlPath) || "download");
|
|
29161
|
-
const mime2 = opts.hintMime ?? detectMime(buffer2, name2);
|
|
29162
|
-
return { buffer: buffer2, name: name2, mime: mime2, size: buffer2.length };
|
|
29163
|
-
}
|
|
29164
|
-
const buffer = await readFile(src);
|
|
29165
|
-
if (buffer.length > maxBytes) {
|
|
29166
|
-
throw new Error(`File too large: ${buffer.length} bytes exceeds ${maxBytes} byte cap.`);
|
|
29167
|
-
}
|
|
29168
|
-
const name = opts.hintName ?? basename(src);
|
|
29169
|
-
const mime = opts.hintMime ?? detectMime(buffer, name);
|
|
29170
|
-
return { buffer, name, mime, size: buffer.length };
|
|
29171
|
-
}
|
|
29172
|
-
async function readStdin(maxBytes) {
|
|
29173
|
-
const chunks = [];
|
|
29174
|
-
let total = 0;
|
|
29175
|
-
for await (const chunk of process.stdin) {
|
|
29176
|
-
const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
29177
|
-
total += buf.length;
|
|
29178
|
-
if (total > maxBytes) {
|
|
29179
|
-
throw new Error(`stdin exceeded ${maxBytes} byte cap.`);
|
|
29180
|
-
}
|
|
29181
|
-
chunks.push(buf);
|
|
29182
|
-
}
|
|
29183
|
-
return Buffer.concat(chunks);
|
|
29184
|
-
}
|
|
29185
|
-
async function fetchRemote(url2, maxBytes) {
|
|
29186
|
-
const controller = new AbortController;
|
|
29187
|
-
const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
|
|
29188
|
-
try {
|
|
29189
|
-
const res = await fetch(url2, { signal: controller.signal });
|
|
29190
|
-
if (!res.ok) {
|
|
29191
|
-
throw new Error(`Fetch ${url2} failed: ${res.status} ${res.statusText}`);
|
|
29192
|
-
}
|
|
29193
|
-
const len = Number(res.headers.get("content-length") || 0);
|
|
29194
|
-
if (len > maxBytes) {
|
|
29195
|
-
throw new Error(`Remote file too large: ${len} bytes exceeds ${maxBytes} byte cap.`);
|
|
29196
|
-
}
|
|
29197
|
-
const buf = Buffer.from(await res.arrayBuffer());
|
|
29198
|
-
if (buf.length > maxBytes) {
|
|
29199
|
-
throw new Error(`Remote file too large: ${buf.length} bytes exceeds ${maxBytes} byte cap.`);
|
|
29200
|
-
}
|
|
29201
|
-
return buf;
|
|
29202
|
-
} finally {
|
|
29203
|
-
clearTimeout(timer);
|
|
29204
|
-
}
|
|
29205
|
-
}
|
|
29206
|
-
var MAGIC = [
|
|
29207
|
-
[Buffer.from([37, 80, 68, 70]), "application/pdf"],
|
|
29208
|
-
[Buffer.from([137, 80, 78, 71]), "image/png"],
|
|
29209
|
-
[Buffer.from([255, 216, 255]), "image/jpeg"],
|
|
29210
|
-
[Buffer.from([71, 73, 70, 56]), "image/gif"],
|
|
29211
|
-
[Buffer.from([82, 73, 70, 70]), "audio/wav"],
|
|
29212
|
-
[Buffer.from([73, 68, 51]), "audio/mpeg"],
|
|
29213
|
-
[Buffer.from([255, 251]), "audio/mpeg"],
|
|
29214
|
-
[Buffer.from([80, 75, 3, 4]), "application/zip"]
|
|
29215
|
-
];
|
|
29216
|
-
var EXT_MIME = {
|
|
29217
|
-
".pdf": "application/pdf",
|
|
29218
|
-
".png": "image/png",
|
|
29219
|
-
".jpg": "image/jpeg",
|
|
29220
|
-
".jpeg": "image/jpeg",
|
|
29221
|
-
".gif": "image/gif",
|
|
29222
|
-
".webp": "image/webp",
|
|
29223
|
-
".txt": "text/plain",
|
|
29224
|
-
".md": "text/markdown",
|
|
29225
|
-
".json": "application/json",
|
|
29226
|
-
".wav": "audio/wav",
|
|
29227
|
-
".mp3": "audio/mpeg",
|
|
29228
|
-
".m4a": "audio/mp4",
|
|
29229
|
-
".ogg": "audio/ogg",
|
|
29230
|
-
".aac": "audio/aac",
|
|
29231
|
-
".flac": "audio/flac",
|
|
29232
|
-
".mp4": "video/mp4",
|
|
29233
|
-
".m4v": "video/x-m4v",
|
|
29234
|
-
".webm": "video/webm",
|
|
29235
|
-
".mov": "video/quicktime",
|
|
29236
|
-
".mkv": "video/x-matroska",
|
|
29237
|
-
".zip": "application/zip"
|
|
29238
|
-
};
|
|
29239
|
-
function detectMime(buffer, name) {
|
|
29240
|
-
for (const [sig, mime] of MAGIC) {
|
|
29241
|
-
if (buffer.length >= sig.length && buffer.subarray(0, sig.length).equals(sig)) {
|
|
29242
|
-
return mime;
|
|
29243
|
-
}
|
|
29244
|
-
}
|
|
29245
|
-
const ext = extname(name).toLowerCase();
|
|
29246
|
-
return EXT_MIME[ext] ?? "application/octet-stream";
|
|
29247
|
-
}
|
|
29248
|
-
|
|
29249
30268
|
// src/lib/conversations.ts
|
|
29250
30269
|
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
29251
30270
|
var CUID_RE = /^c[a-z0-9]{20,}$/i;
|
|
@@ -31215,97 +32234,12 @@ function registerCardCommand(card) {
|
|
|
31215
32234
|
}
|
|
31216
32235
|
|
|
31217
32236
|
// src/commands/amazon.ts
|
|
31218
|
-
var AMAZON_SEARCH_USD = 0.01;
|
|
31219
32237
|
function registerAmazonCommand(program2) {
|
|
31220
|
-
const cmd = program2.command("amazon").description("
|
|
31221
|
-
cmd.command("search <query
|
|
31222
|
-
|
|
31223
|
-
|
|
31224
|
-
|
|
31225
|
-
} catch (e5) {
|
|
31226
|
-
console.error(error(e5 instanceof Error ? e5.message : String(e5)));
|
|
31227
|
-
process.exit(1);
|
|
31228
|
-
}
|
|
31229
|
-
let quote;
|
|
31230
|
-
try {
|
|
31231
|
-
quote = await quoteTokenAmount(resolveAuth(), payToken, AMAZON_SEARCH_USD);
|
|
31232
|
-
} catch (e5) {
|
|
31233
|
-
console.error(error(e5 instanceof Error ? e5.message : String(e5)));
|
|
31234
|
-
process.exit(1);
|
|
31235
|
-
}
|
|
31236
|
-
await requireApproval({
|
|
31237
|
-
cost: `${quote.amount} ${payToken} (≈ $${AMAZON_SEARCH_USD.toFixed(2)})`,
|
|
31238
|
-
summary: `Amazon product search: ${query.slice(0, 80)}${query.length > 80 ? "…" : ""}`,
|
|
31239
|
-
yes: opts.yes,
|
|
31240
|
-
commandExample: `amiko markets amazon search ${JSON.stringify(query)}${payToken !== "AMIKO" ? ` --token ${payToken}` : ""}`
|
|
31241
|
-
});
|
|
31242
|
-
const spinner = ora("Searching Amazon...").start();
|
|
31243
|
-
try {
|
|
31244
|
-
const config2 = loadConfig();
|
|
31245
|
-
let auth;
|
|
31246
|
-
try {
|
|
31247
|
-
auth = requireAuth();
|
|
31248
|
-
} catch (e5) {
|
|
31249
|
-
spinner.stop();
|
|
31250
|
-
console.error(error(e5 instanceof Error ? e5.message : "auth failed"));
|
|
31251
|
-
process.exit(1);
|
|
31252
|
-
}
|
|
31253
|
-
const mppBase2 = (config2.apiBase || "https://mpp.amikomarkets.com").replace(/\/$/, "");
|
|
31254
|
-
let wallet;
|
|
31255
|
-
try {
|
|
31256
|
-
wallet = await resolveWalletAddress(auth, opts.wallet, "solana");
|
|
31257
|
-
} catch (e5) {
|
|
31258
|
-
spinner.stop();
|
|
31259
|
-
console.error(error(e5 instanceof Error ? e5.message : String(e5)));
|
|
31260
|
-
process.exit(1);
|
|
31261
|
-
}
|
|
31262
|
-
spinner.text = `Paying ${quote.amount} ${payToken}...`;
|
|
31263
|
-
const txHash = await payTreasury(auth, { wallet, amount: quote.amount, token: payToken });
|
|
31264
|
-
spinner.text = "Searching Amazon...";
|
|
31265
|
-
const params = new URLSearchParams({
|
|
31266
|
-
q: `site:amazon.com ${query}`
|
|
31267
|
-
});
|
|
31268
|
-
const res = await fetch(`${mppBase2}/v1/search/google_shopping?${params}`, {
|
|
31269
|
-
headers: { "X-Payment-Tx": txHash, "X-Payment-Token": payToken, "X-Twin-Id": auth.twinId }
|
|
31270
|
-
});
|
|
31271
|
-
spinner.stop();
|
|
31272
|
-
if (!res.ok) {
|
|
31273
|
-
await reportHttpError(res, "Amazon search");
|
|
31274
|
-
process.exit(1);
|
|
31275
|
-
}
|
|
31276
|
-
const data = await res.json();
|
|
31277
|
-
if (opts.raw) {
|
|
31278
|
-
console.log(JSON.stringify(data, null, 2));
|
|
31279
|
-
return;
|
|
31280
|
-
}
|
|
31281
|
-
console.log(heading(`Amazon: "${query}"
|
|
31282
|
-
`));
|
|
31283
|
-
const results = data.shopping_results || data.organic_results || [];
|
|
31284
|
-
const limit = parseInt(opts.limit) || 5;
|
|
31285
|
-
for (const item of results.slice(0, limit)) {
|
|
31286
|
-
const title = item.title || "Unknown";
|
|
31287
|
-
const price = item.price || item.extracted_price || "";
|
|
31288
|
-
const link = item.link || item.product_link || "";
|
|
31289
|
-
const asin = link.match(/\/dp\/([A-Z0-9]{10})/)?.[1] || "";
|
|
31290
|
-
console.log(` ${source_default.cyan(title)}`);
|
|
31291
|
-
if (price)
|
|
31292
|
-
console.log(` ${source_default.yellow(typeof price === "string" ? price : `$${price}`)}`);
|
|
31293
|
-
if (asin)
|
|
31294
|
-
console.log(dim(` ASIN: ${asin}`));
|
|
31295
|
-
if (link)
|
|
31296
|
-
console.log(dim(` ${link.slice(0, 80)}`));
|
|
31297
|
-
console.log("");
|
|
31298
|
-
}
|
|
31299
|
-
if (results.length === 0) {
|
|
31300
|
-
console.log(dim(" No results found."));
|
|
31301
|
-
}
|
|
31302
|
-
console.log(success("Paid 1 AMIKO") + dim(` tx: ${txHash.slice(0, 16)}...`));
|
|
31303
|
-
await printBalanceRemaining(auth);
|
|
31304
|
-
} catch (err) {
|
|
31305
|
-
spinner.stop();
|
|
31306
|
-
console.error(error(err instanceof Error ? err.message : "Search failed"));
|
|
31307
|
-
process.exit(1);
|
|
31308
|
-
}
|
|
32238
|
+
const cmd = program2.command("amazon").description("Buy products on Amazon — quote and order, paid in AMIKO, USDC, or USDT");
|
|
32239
|
+
attachSearchOptions(cmd.command("search <query...>").description("[deprecated — use `amiko search amazon`] Search Amazon products — $0.01"), "amazon").action(async (parts, opts) => {
|
|
32240
|
+
const query = oneQuery(parts, "amazon");
|
|
32241
|
+
console.error(warn("`amiko markets amazon search` is deprecated — use `amiko search amazon`."));
|
|
32242
|
+
await runVertical("amazon", query, opts, `amiko markets amazon search ${JSON.stringify(query)}`);
|
|
31309
32243
|
});
|
|
31310
32244
|
cmd.command("quote <product>").description("Get price quote for an Amazon product (ASIN or URL) — free").option("--raw", "Output raw JSON").action(async (product, opts) => {
|
|
31311
32245
|
const spinner = ora("Getting quote...").start();
|
|
@@ -31794,7 +32728,7 @@ function registerTwinCommand(program2) {
|
|
|
31794
32728
|
|
|
31795
32729
|
// src/commands/drive.ts
|
|
31796
32730
|
import { writeFile as writeFile2 } from "node:fs/promises";
|
|
31797
|
-
import { basename as
|
|
32731
|
+
import { basename as basename3, resolve as resolvePath } from "node:path";
|
|
31798
32732
|
var DIRECT_UPLOAD_MAX_BYTES = 4 * 1024 * 1024;
|
|
31799
32733
|
var MAX_DRIVE_UPLOAD_BYTES = 200 * 1024 * 1024;
|
|
31800
32734
|
var PRESIGNED_PUT_TIMEOUT_MS = 10 * 60 * 1000;
|
|
@@ -32047,7 +32981,7 @@ Search hits filename, title, description, and parsed RAG content (once processed
|
|
|
32047
32981
|
process.stdout.write(buf);
|
|
32048
32982
|
return;
|
|
32049
32983
|
}
|
|
32050
|
-
const outPath = resolvePath(opts.out ??
|
|
32984
|
+
const outPath = resolvePath(opts.out ?? basename3(meta3.doc.filename));
|
|
32051
32985
|
await writeFile2(outPath, buf);
|
|
32052
32986
|
spinner?.stop();
|
|
32053
32987
|
console.log(success(`Saved ${meta3.doc.filename} → ${outPath}`));
|
|
@@ -34389,7 +35323,7 @@ function registerNotificationsCommand(program2) {
|
|
|
34389
35323
|
|
|
34390
35324
|
// src/commands/memory.ts
|
|
34391
35325
|
import { existsSync as existsSync2, readFileSync as readFileSync2, statSync as statSync2 } from "node:fs";
|
|
34392
|
-
import { basename as
|
|
35326
|
+
import { basename as basename4, resolve } from "node:path";
|
|
34393
35327
|
var VALID_CATEGORIES = ["fact", "preference", "pattern", "decision", "context"];
|
|
34394
35328
|
var VALID_SOURCES = ["chat", "file", "manual"];
|
|
34395
35329
|
var UPLOAD_BATCH_SIZE = 100;
|
|
@@ -34587,7 +35521,7 @@ function registerMemoryCommand(program2) {
|
|
|
34587
35521
|
} catch (e5) {
|
|
34588
35522
|
fail(`Failed to read ${f}: ${e5 instanceof Error ? e5.message : String(e5)}`);
|
|
34589
35523
|
}
|
|
34590
|
-
const key = keyOverride && files.length === 1 ? keyOverride :
|
|
35524
|
+
const key = keyOverride && files.length === 1 ? keyOverride : basename4(abs);
|
|
34591
35525
|
if (seenKeys.has(key)) {
|
|
34592
35526
|
fail(`Duplicate path key "${key}" within this upload. Run separately or pass --key per file.`);
|
|
34593
35527
|
}
|
|
@@ -34853,55 +35787,61 @@ function registerWalletsListCommand(program2) {
|
|
|
34853
35787
|
});
|
|
34854
35788
|
}
|
|
34855
35789
|
|
|
35790
|
+
// src/program.ts
|
|
35791
|
+
function buildProgram(version3) {
|
|
35792
|
+
const program2 = new Command;
|
|
35793
|
+
program2.name("amiko").description("Amiko CLI — Create Studio media generation, chats, drive, credits & wallets, social graph, and paid MPP marketplace services").version(version3);
|
|
35794
|
+
const markets = program2.command("markets").description("MPP marketplace — discover and call paid AMIKO services");
|
|
35795
|
+
const create2 = program2.command("create").description("Create Studio — generate images, video, speech, music, and SFX (charged on success)");
|
|
35796
|
+
const chat = program2.command("chat").description("Your conversations (DMs + group chats) as the owner — list, read, send, manage groups");
|
|
35797
|
+
const card = program2.command("card").description("Twin Cards (work/play/love) — generate, view, download, mint");
|
|
35798
|
+
const wallets = program2.command("wallets").description("Manage twin wallets — list, swap, bridge");
|
|
35799
|
+
const twin = program2.command("twin").description("Update twin identity (name, description, visibility)");
|
|
35800
|
+
const drive = program2.command("drive").alias("docs").description("Manage twin drive — upload/download/search files, organise into folders");
|
|
35801
|
+
const voice = program2.command("voice").description("Manage twin voice — design, create, clone, reset");
|
|
35802
|
+
const avatar = program2.command("avatar").description("Manage twin avatar image");
|
|
35803
|
+
const friends = program2.command("friends").description("Manage friendships — list, requests, add, accept, remove, nicknames, reports, matches");
|
|
35804
|
+
const users = program2.command("users").description("Search users, view public profiles, and manage follows (follow, unfollow, followers, following)");
|
|
35805
|
+
const post = program2.command("post").description("Create posts (notes) and comments on the feed — text, images, and documents");
|
|
35806
|
+
const review = program2.command("review").description("Review queue — list, approve, or reject twin-drafted comments");
|
|
35807
|
+
const notifications = program2.command("notifications").description("Platform notifications — list, mark as read");
|
|
35808
|
+
registerCreditsCommand(program2);
|
|
35809
|
+
registerSearchCommand(program2);
|
|
35810
|
+
registerDeprecatedMarketsSearch(markets);
|
|
35811
|
+
registerImageCommand(markets);
|
|
35812
|
+
registerCreateCommand(create2);
|
|
35813
|
+
registerChatCommand(chat);
|
|
35814
|
+
registerCardCommand(card);
|
|
35815
|
+
registerAmazonCommand(markets);
|
|
35816
|
+
registerWalletsCreateCommand(wallets);
|
|
35817
|
+
registerWalletsListCommand(wallets);
|
|
35818
|
+
registerWalletsBalanceCommand(wallets);
|
|
35819
|
+
registerWalletsTransferCommand(wallets);
|
|
35820
|
+
registerSwapCommand(wallets);
|
|
35821
|
+
registerBridgeCommand(wallets);
|
|
35822
|
+
registerConfigCommand(program2);
|
|
35823
|
+
registerServicesCommand(markets);
|
|
35824
|
+
registerDiscoveryCommand(markets);
|
|
35825
|
+
registerUpdateCommand(program2);
|
|
35826
|
+
registerAccountsCommand(program2);
|
|
35827
|
+
registerTwinCommand(twin);
|
|
35828
|
+
registerDriveCommand(drive);
|
|
35829
|
+
registerVoiceCommand(voice);
|
|
35830
|
+
registerAvatarCommand(avatar);
|
|
35831
|
+
registerFriendsCommand(friends);
|
|
35832
|
+
registerUsersCommand(users);
|
|
35833
|
+
registerFeedCommand(program2);
|
|
35834
|
+
registerPostCommand(post);
|
|
35835
|
+
registerReviewCommand(review);
|
|
35836
|
+
registerNotificationsCommand(notifications);
|
|
35837
|
+
registerMemoryCommand(program2);
|
|
35838
|
+
return program2;
|
|
35839
|
+
}
|
|
35840
|
+
|
|
34856
35841
|
// src/index.ts
|
|
34857
35842
|
var require2 = createRequire2(import.meta.url);
|
|
34858
35843
|
var { version: PKG_VERSION } = require2("../package.json");
|
|
34859
35844
|
var didOnboard = await runOnboarding(process.argv);
|
|
34860
35845
|
if (didOnboard)
|
|
34861
35846
|
process.exit(0);
|
|
34862
|
-
|
|
34863
|
-
program2.name("amiko").description("Amiko CLI — Create Studio media generation, chats, drive, credits & wallets, social graph, and paid MPP marketplace services").version(PKG_VERSION);
|
|
34864
|
-
var markets = program2.command("markets").description("MPP marketplace — discover and call paid AMIKO services");
|
|
34865
|
-
var create2 = program2.command("create").description("Create Studio — generate images, video, speech, music, and SFX (charged on success)");
|
|
34866
|
-
var chat = program2.command("chat").description("Your conversations (DMs + group chats) as the owner — list, read, send, manage groups");
|
|
34867
|
-
var card = program2.command("card").description("Twin Cards (work/play/love) — generate, view, download, mint");
|
|
34868
|
-
var wallets = program2.command("wallets").description("Manage twin wallets — list, swap, bridge");
|
|
34869
|
-
var twin = program2.command("twin").description("Update twin identity (name, description, visibility)");
|
|
34870
|
-
var drive = program2.command("drive").alias("docs").description("Manage twin drive — upload/download/search files, organise into folders");
|
|
34871
|
-
var voice = program2.command("voice").description("Manage twin voice — design, create, clone, reset");
|
|
34872
|
-
var avatar = program2.command("avatar").description("Manage twin avatar image");
|
|
34873
|
-
var friends = program2.command("friends").description("Manage friendships — list, requests, add, accept, remove, nicknames, reports, matches");
|
|
34874
|
-
var users = program2.command("users").description("Search users, view public profiles, and manage follows (follow, unfollow, followers, following)");
|
|
34875
|
-
var post = program2.command("post").description("Create posts (notes) and comments on the feed — text, images, and documents");
|
|
34876
|
-
var review = program2.command("review").description("Review queue — list, approve, or reject twin-drafted comments");
|
|
34877
|
-
var notifications = program2.command("notifications").description("Platform notifications — list, mark as read");
|
|
34878
|
-
registerCreditsCommand(program2);
|
|
34879
|
-
registerSearchCommand(markets);
|
|
34880
|
-
registerImageCommand(markets);
|
|
34881
|
-
registerCreateCommand(create2);
|
|
34882
|
-
registerChatCommand(chat);
|
|
34883
|
-
registerCardCommand(card);
|
|
34884
|
-
registerAmazonCommand(markets);
|
|
34885
|
-
registerWalletsCreateCommand(wallets);
|
|
34886
|
-
registerWalletsListCommand(wallets);
|
|
34887
|
-
registerWalletsBalanceCommand(wallets);
|
|
34888
|
-
registerWalletsTransferCommand(wallets);
|
|
34889
|
-
registerSwapCommand(wallets);
|
|
34890
|
-
registerBridgeCommand(wallets);
|
|
34891
|
-
registerConfigCommand(program2);
|
|
34892
|
-
registerServicesCommand(markets);
|
|
34893
|
-
registerDiscoveryCommand(markets);
|
|
34894
|
-
registerUpdateCommand(program2);
|
|
34895
|
-
registerAccountsCommand(program2);
|
|
34896
|
-
registerTwinCommand(twin);
|
|
34897
|
-
registerDriveCommand(drive);
|
|
34898
|
-
registerVoiceCommand(voice);
|
|
34899
|
-
registerAvatarCommand(avatar);
|
|
34900
|
-
registerFriendsCommand(friends);
|
|
34901
|
-
registerUsersCommand(users);
|
|
34902
|
-
registerFeedCommand(program2);
|
|
34903
|
-
registerPostCommand(post);
|
|
34904
|
-
registerReviewCommand(review);
|
|
34905
|
-
registerNotificationsCommand(notifications);
|
|
34906
|
-
registerMemoryCommand(program2);
|
|
34907
|
-
program2.parse();
|
|
35847
|
+
buildProgram(PKG_VERSION).parse();
|