@heyamiko/amiko-cli 0.9.0-beta.6 → 0.9.0
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 +82 -56
- package/dist/index.js +728 -177
- package/package.json +2 -1
- package/skills/SKILL.md +151 -69
package/dist/index.js
CHANGED
|
@@ -18484,6 +18484,104 @@ async function requireApproval(args) {
|
|
|
18484
18484
|
process.exit(2);
|
|
18485
18485
|
}
|
|
18486
18486
|
|
|
18487
|
+
// src/lib/amiko-web.ts
|
|
18488
|
+
var DEFAULT_PLATFORM_URL = "https://platform.heyamiko.com";
|
|
18489
|
+
function baseUrl(auth) {
|
|
18490
|
+
const url = auth.platformUrl || DEFAULT_PLATFORM_URL;
|
|
18491
|
+
return url.replace(/\/$/, "");
|
|
18492
|
+
}
|
|
18493
|
+
function authHeader(auth) {
|
|
18494
|
+
if (auth.twinToken)
|
|
18495
|
+
return `Bearer ${auth.twinToken}`;
|
|
18496
|
+
throw new Error("No twin token found. Place an .amiko.json file with amikoTwinToken in the current directory or any parent.");
|
|
18497
|
+
}
|
|
18498
|
+
async function amikoWebFetch(auth, path, opts = {}) {
|
|
18499
|
+
const url = new URL(`${baseUrl(auth)}${path}`);
|
|
18500
|
+
if (opts.query) {
|
|
18501
|
+
for (const [k, v] of Object.entries(opts.query)) {
|
|
18502
|
+
if (v !== undefined && v !== null)
|
|
18503
|
+
url.searchParams.set(k, String(v));
|
|
18504
|
+
}
|
|
18505
|
+
}
|
|
18506
|
+
const headers = {
|
|
18507
|
+
Authorization: authHeader(auth),
|
|
18508
|
+
...opts.headers
|
|
18509
|
+
};
|
|
18510
|
+
let body;
|
|
18511
|
+
if (opts.multipart) {
|
|
18512
|
+
body = opts.multipart;
|
|
18513
|
+
} else if (opts.rawBody) {
|
|
18514
|
+
body = opts.rawBody;
|
|
18515
|
+
if (opts.rawContentType)
|
|
18516
|
+
headers["Content-Type"] = opts.rawContentType;
|
|
18517
|
+
} else if (opts.body) {
|
|
18518
|
+
body = JSON.stringify(opts.body);
|
|
18519
|
+
headers["Content-Type"] = "application/json";
|
|
18520
|
+
}
|
|
18521
|
+
const controller = new AbortController;
|
|
18522
|
+
const timeoutMs = opts.timeoutMs ?? 30000;
|
|
18523
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
18524
|
+
let res;
|
|
18525
|
+
try {
|
|
18526
|
+
res = await fetch(url, {
|
|
18527
|
+
method: opts.method ?? "GET",
|
|
18528
|
+
headers,
|
|
18529
|
+
body,
|
|
18530
|
+
signal: controller.signal
|
|
18531
|
+
});
|
|
18532
|
+
} finally {
|
|
18533
|
+
clearTimeout(timer);
|
|
18534
|
+
}
|
|
18535
|
+
if (!res.ok) {
|
|
18536
|
+
const text = await res.text().catch(() => "");
|
|
18537
|
+
const err = new Error(formatError(res.status, text, path));
|
|
18538
|
+
err.status = res.status;
|
|
18539
|
+
err.body = text;
|
|
18540
|
+
throw err;
|
|
18541
|
+
}
|
|
18542
|
+
const ctype = res.headers.get("content-type") || "";
|
|
18543
|
+
if (ctype.includes("application/json")) {
|
|
18544
|
+
return await res.json();
|
|
18545
|
+
}
|
|
18546
|
+
return await res.text();
|
|
18547
|
+
}
|
|
18548
|
+
function formatError(status, body, path) {
|
|
18549
|
+
const prefix = `${status} ${path}`;
|
|
18550
|
+
if (status === 401) {
|
|
18551
|
+
return `${prefix}: unauthorized. The stored token is invalid or expired; re-link this device to Amiko.`;
|
|
18552
|
+
}
|
|
18553
|
+
if (status === 403) {
|
|
18554
|
+
return `${prefix}: forbidden. The current auth context does not have access to this resource.`;
|
|
18555
|
+
}
|
|
18556
|
+
if (!body)
|
|
18557
|
+
return prefix;
|
|
18558
|
+
const snippet = body.length > 500 ? `${body.slice(0, 500)}...` : body;
|
|
18559
|
+
return `${prefix}: ${snippet}`;
|
|
18560
|
+
}
|
|
18561
|
+
|
|
18562
|
+
// src/lib/wallet-default.ts
|
|
18563
|
+
var cache = null;
|
|
18564
|
+
async function loadWallets(auth) {
|
|
18565
|
+
if (cache)
|
|
18566
|
+
return cache;
|
|
18567
|
+
const data = await amikoWebFetch(auth, `/api/agents/${encodeURIComponent(auth.twinId)}/wallets`);
|
|
18568
|
+
cache = data.wallets ?? [];
|
|
18569
|
+
return cache;
|
|
18570
|
+
}
|
|
18571
|
+
async function resolveWalletAddress(auth, explicit, preferredChain = "solana") {
|
|
18572
|
+
if (explicit)
|
|
18573
|
+
return explicit;
|
|
18574
|
+
const wallets = await loadWallets(auth);
|
|
18575
|
+
const match = wallets.find((w) => w.status === "active" && w.chain === preferredChain);
|
|
18576
|
+
if (match)
|
|
18577
|
+
return match.wallet_address;
|
|
18578
|
+
const anyActive = wallets.find((w) => w.status === "active");
|
|
18579
|
+
if (anyActive && !explicit) {
|
|
18580
|
+
throw new Error(`No active ${preferredChain} wallet found. Run \`amiko wallets create --chain ${preferredChain}\` or pass --wallet <address> explicitly.`);
|
|
18581
|
+
}
|
|
18582
|
+
throw new Error(`No wallets found for this agent. Run \`amiko wallets create --chain ${preferredChain}\` first.`);
|
|
18583
|
+
}
|
|
18584
|
+
|
|
18487
18585
|
// src/lib/across.ts
|
|
18488
18586
|
function getBridgeBase() {
|
|
18489
18587
|
const config = loadConfig();
|
|
@@ -18635,7 +18733,7 @@ var DEFAULT_ORIGIN = CHAINS.solana;
|
|
|
18635
18733
|
var DEFAULT_DESTINATION = CHAINS.tempo;
|
|
18636
18734
|
function registerBridgeCommand(program2) {
|
|
18637
18735
|
const cmd = program2.command("bridge").description("Bridge USDC across chains via Across Protocol");
|
|
18638
|
-
cmd.command("quote").description("Get a bridge quote for USDC transfer").argument("<amount>", "Amount of USDC to bridge (e.g., 10, 5.50)").option("--from <chain>", "Origin chain (default: solana)", "solana").option("--to <chain>", "Destination chain (default: tempo)", "tempo").option("--recipient <address>", "Recipient address on destination chain").option("--depositor <address>", "Depositor address on origin chain").action(async (amountStr, opts) => {
|
|
18736
|
+
cmd.command("quote").description("Get a bridge quote for USDC transfer").argument("<amount>", "Amount of USDC to bridge (e.g., 10, 5.50)").option("--from <chain>", "Origin chain (default: solana)", "solana").option("--to <chain>", "Destination chain (default: tempo)", "tempo").option("--recipient <address>", "Recipient address on destination chain (required for Solana ↔ EVM)").option("--depositor <address>", "Depositor address on origin chain (defaults to your wallet on that chain)").action(async (amountStr, opts) => {
|
|
18639
18737
|
const spinner = ora("Fetching bridge quote...").start();
|
|
18640
18738
|
try {
|
|
18641
18739
|
const originChainId = resolveChainId(opts.from);
|
|
@@ -18643,10 +18741,14 @@ function registerBridgeCommand(program2) {
|
|
|
18643
18741
|
const inputToken = getUsdcAddress(originChainId);
|
|
18644
18742
|
const outputToken = getUsdcAddress(destChainId);
|
|
18645
18743
|
const amount = parseUsdcAmount(amountStr);
|
|
18646
|
-
const
|
|
18647
|
-
|
|
18744
|
+
const preferredChain = originChainId === CHAINS.solana ? "solana" : "base";
|
|
18745
|
+
let depositor;
|
|
18746
|
+
try {
|
|
18747
|
+
const auth = requireAuth();
|
|
18748
|
+
depositor = await resolveWalletAddress(auth, opts.depositor, preferredChain);
|
|
18749
|
+
} catch (e) {
|
|
18648
18750
|
spinner.stop();
|
|
18649
|
-
console.error(error(
|
|
18751
|
+
console.error(error(e instanceof Error ? e.message : String(e)));
|
|
18650
18752
|
process.exit(1);
|
|
18651
18753
|
}
|
|
18652
18754
|
const isCrossEcosystem = originChainId === CHAINS.solana !== (destChainId === CHAINS.solana);
|
|
@@ -18695,7 +18797,7 @@ function registerBridgeCommand(program2) {
|
|
|
18695
18797
|
if (quote.swapTx) {
|
|
18696
18798
|
console.log("");
|
|
18697
18799
|
console.log(dim('Transaction ready. Run "amiko wallets bridge send" to execute.'));
|
|
18698
|
-
console.log(dim(` amiko wallets bridge send ${amountStr} --from ${opts.from} --to ${opts.to}`));
|
|
18800
|
+
console.log(dim(` amiko wallets bridge send ${amountStr} --from ${opts.from} --to ${opts.to}${opts.depositor ? ` --depositor ${opts.depositor}` : ""}${opts.recipient ? ` --recipient ${opts.recipient}` : ""}`));
|
|
18699
18801
|
}
|
|
18700
18802
|
} catch (err) {
|
|
18701
18803
|
spinner.stop();
|
|
@@ -18703,7 +18805,7 @@ function registerBridgeCommand(program2) {
|
|
|
18703
18805
|
process.exit(1);
|
|
18704
18806
|
}
|
|
18705
18807
|
});
|
|
18706
|
-
cmd.command("send").description("Execute a USDC bridge transfer via Across").argument("<amount>", "Amount of USDC to bridge (e.g., 10, 5.50)").option("--from <chain>", "Origin chain (default: solana)", "solana").option("--to <chain>", "Destination chain (default: tempo)", "tempo").option("--recipient <address>", "Recipient address on destination chain").option("--depositor <address>", "Depositor address on origin chain").option("--raw", "Output raw transaction JSON for manual signing").option("--yes", "Skip confirmation (only after the user has approved)").action(async (amountStr, opts) => {
|
|
18808
|
+
cmd.command("send").description("Execute a USDC bridge transfer via Across").argument("<amount>", "Amount of USDC to bridge (e.g., 10, 5.50)").option("--from <chain>", "Origin chain (default: solana)", "solana").option("--to <chain>", "Destination chain (default: tempo)", "tempo").option("--recipient <address>", "Recipient address on destination chain (required for Solana ↔ EVM)").option("--depositor <address>", "Depositor address on origin chain (defaults to your wallet on that chain)").option("--raw", "Output raw transaction JSON for manual signing").option("--yes", "Skip confirmation (only after the user has approved)").action(async (amountStr, opts) => {
|
|
18707
18809
|
const spinner = ora("Preparing bridge transaction...").start();
|
|
18708
18810
|
try {
|
|
18709
18811
|
const originChainId = resolveChainId(opts.from);
|
|
@@ -18711,10 +18813,14 @@ function registerBridgeCommand(program2) {
|
|
|
18711
18813
|
const inputToken = getUsdcAddress(originChainId);
|
|
18712
18814
|
const outputToken = getUsdcAddress(destChainId);
|
|
18713
18815
|
const amount = parseUsdcAmount(amountStr);
|
|
18714
|
-
const
|
|
18715
|
-
|
|
18816
|
+
const preferredChain = originChainId === CHAINS.solana ? "solana" : "base";
|
|
18817
|
+
let depositor;
|
|
18818
|
+
try {
|
|
18819
|
+
const auth = requireAuth();
|
|
18820
|
+
depositor = await resolveWalletAddress(auth, opts.depositor, preferredChain);
|
|
18821
|
+
} catch (e) {
|
|
18716
18822
|
spinner.stop();
|
|
18717
|
-
console.error(error(
|
|
18823
|
+
console.error(error(e instanceof Error ? e.message : String(e)));
|
|
18718
18824
|
process.exit(1);
|
|
18719
18825
|
}
|
|
18720
18826
|
const isCrossEcosystem = originChainId === CHAINS.solana !== (destChainId === CHAINS.solana);
|
|
@@ -18974,81 +19080,6 @@ function registerBridgeCommand(program2) {
|
|
|
18974
19080
|
// src/commands/credits.ts
|
|
18975
19081
|
import readline2 from "node:readline";
|
|
18976
19082
|
|
|
18977
|
-
// src/lib/amiko-web.ts
|
|
18978
|
-
var DEFAULT_PLATFORM_URL = "https://platform.heyamiko.com";
|
|
18979
|
-
function baseUrl(auth) {
|
|
18980
|
-
const url = auth.platformUrl || DEFAULT_PLATFORM_URL;
|
|
18981
|
-
return url.replace(/\/$/, "");
|
|
18982
|
-
}
|
|
18983
|
-
function authHeader(auth) {
|
|
18984
|
-
if (auth.twinToken)
|
|
18985
|
-
return `Bearer ${auth.twinToken}`;
|
|
18986
|
-
throw new Error("No twin token found. Place an .amiko.json file with amikoTwinToken in the current directory or any parent.");
|
|
18987
|
-
}
|
|
18988
|
-
async function amikoWebFetch(auth, path, opts = {}) {
|
|
18989
|
-
const url = new URL(`${baseUrl(auth)}${path}`);
|
|
18990
|
-
if (opts.query) {
|
|
18991
|
-
for (const [k, v] of Object.entries(opts.query)) {
|
|
18992
|
-
if (v !== undefined && v !== null)
|
|
18993
|
-
url.searchParams.set(k, String(v));
|
|
18994
|
-
}
|
|
18995
|
-
}
|
|
18996
|
-
const headers = {
|
|
18997
|
-
Authorization: authHeader(auth),
|
|
18998
|
-
...opts.headers
|
|
18999
|
-
};
|
|
19000
|
-
let body;
|
|
19001
|
-
if (opts.multipart) {
|
|
19002
|
-
body = opts.multipart;
|
|
19003
|
-
} else if (opts.rawBody) {
|
|
19004
|
-
body = opts.rawBody;
|
|
19005
|
-
if (opts.rawContentType)
|
|
19006
|
-
headers["Content-Type"] = opts.rawContentType;
|
|
19007
|
-
} else if (opts.body) {
|
|
19008
|
-
body = JSON.stringify(opts.body);
|
|
19009
|
-
headers["Content-Type"] = "application/json";
|
|
19010
|
-
}
|
|
19011
|
-
const controller = new AbortController;
|
|
19012
|
-
const timeoutMs = opts.timeoutMs ?? 30000;
|
|
19013
|
-
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
19014
|
-
let res;
|
|
19015
|
-
try {
|
|
19016
|
-
res = await fetch(url, {
|
|
19017
|
-
method: opts.method ?? "GET",
|
|
19018
|
-
headers,
|
|
19019
|
-
body,
|
|
19020
|
-
signal: controller.signal
|
|
19021
|
-
});
|
|
19022
|
-
} finally {
|
|
19023
|
-
clearTimeout(timer);
|
|
19024
|
-
}
|
|
19025
|
-
if (!res.ok) {
|
|
19026
|
-
const text = await res.text().catch(() => "");
|
|
19027
|
-
const err = new Error(formatError(res.status, text, path));
|
|
19028
|
-
err.status = res.status;
|
|
19029
|
-
err.body = text;
|
|
19030
|
-
throw err;
|
|
19031
|
-
}
|
|
19032
|
-
const ctype = res.headers.get("content-type") || "";
|
|
19033
|
-
if (ctype.includes("application/json")) {
|
|
19034
|
-
return await res.json();
|
|
19035
|
-
}
|
|
19036
|
-
return await res.text();
|
|
19037
|
-
}
|
|
19038
|
-
function formatError(status, body, path) {
|
|
19039
|
-
const prefix = `${status} ${path}`;
|
|
19040
|
-
if (status === 401) {
|
|
19041
|
-
return `${prefix}: unauthorized. The stored token is invalid or expired; re-link this device to Amiko.`;
|
|
19042
|
-
}
|
|
19043
|
-
if (status === 403) {
|
|
19044
|
-
return `${prefix}: forbidden. The current auth context does not have access to this resource.`;
|
|
19045
|
-
}
|
|
19046
|
-
if (!body)
|
|
19047
|
-
return prefix;
|
|
19048
|
-
const snippet = body.length > 500 ? `${body.slice(0, 500)}...` : body;
|
|
19049
|
-
return `${prefix}: ${snippet}`;
|
|
19050
|
-
}
|
|
19051
|
-
|
|
19052
19083
|
// src/lib/balance.ts
|
|
19053
19084
|
async function fetchCreditsBalance(auth) {
|
|
19054
19085
|
if (!auth.twinToken)
|
|
@@ -19295,6 +19326,7 @@ function registerCreditsCommand(program2) {
|
|
|
19295
19326
|
if (mppResult.credits?.balance)
|
|
19296
19327
|
console.log(label("Balance", String(mppResult.credits.balance)));
|
|
19297
19328
|
console.log(success("Credits topped up via MPP."));
|
|
19329
|
+
console.log(dim("Balance may take a moment to reflect on-chain confirmation — re-run `amiko credits balance` if it looks stale."));
|
|
19298
19330
|
return;
|
|
19299
19331
|
}
|
|
19300
19332
|
console.log(heading("Top-up Summary"));
|
|
@@ -19339,6 +19371,7 @@ function registerCreditsCommand(program2) {
|
|
|
19339
19371
|
}
|
|
19340
19372
|
console.log("");
|
|
19341
19373
|
console.log(success("Credits topped up."));
|
|
19374
|
+
console.log(dim("Balance may take a moment to reflect on-chain confirmation — re-run `amiko credits balance` if it looks stale."));
|
|
19342
19375
|
} catch (e) {
|
|
19343
19376
|
spinner.stop();
|
|
19344
19377
|
console.error(error(e instanceof Error ? e.message : "Top-up failed"));
|
|
@@ -19453,14 +19486,15 @@ function registerSwapCommand(program2) {
|
|
|
19453
19486
|
console.log(label("Route", dim(route)));
|
|
19454
19487
|
}
|
|
19455
19488
|
console.log("");
|
|
19456
|
-
console.log(dim(`To execute: amiko wallets swap send ${amountStr} ${fromToken} ${toToken}`
|
|
19489
|
+
console.log(dim(`To execute: amiko wallets swap send ${amountStr} ${fromToken} ${toToken}` + `
|
|
19490
|
+
(uses your default Solana wallet; pass --wallet <address> to override)`));
|
|
19457
19491
|
} catch (err) {
|
|
19458
19492
|
spinner.stop();
|
|
19459
19493
|
console.error(error(err instanceof Error ? err.message : "Failed to get quote"));
|
|
19460
19494
|
process.exit(1);
|
|
19461
19495
|
}
|
|
19462
19496
|
});
|
|
19463
|
-
cmd.command("send").description("Execute a token swap on Solana via Crossmint wallet").argument("<amount>", "Amount to swap (e.g., 5, 0.1)").argument("<from>", "Input token (sol, usdc, usdt, amiko, bonk, or mint address)").argument("<to>", "Output token (sol, usdc, usdt, amiko, bonk, or mint address)").option("--wallet <address>", "
|
|
19497
|
+
cmd.command("send").description("Execute a token swap on Solana via Crossmint wallet").argument("<amount>", "Amount to swap (e.g., 5, 0.1)").argument("<from>", "Input token (sol, usdc, usdt, amiko, bonk, or mint address)").argument("<to>", "Output token (sol, usdc, usdt, amiko, bonk, or mint address)").option("--wallet <address>", "Solana wallet to swap from (defaults to your Solana wallet — run `amiko wallets create --chain solana` if none)").option("--slippage <bps>", "Slippage tolerance in basis points", "50").option("--yes", "Skip confirmation").action(async (amountStr, fromToken, toToken, opts) => {
|
|
19464
19498
|
try {
|
|
19465
19499
|
let auth;
|
|
19466
19500
|
try {
|
|
@@ -19469,9 +19503,11 @@ function registerSwapCommand(program2) {
|
|
|
19469
19503
|
console.error(error(e instanceof Error ? e.message : "auth failed"));
|
|
19470
19504
|
process.exit(1);
|
|
19471
19505
|
}
|
|
19472
|
-
|
|
19473
|
-
|
|
19474
|
-
|
|
19506
|
+
let wallet;
|
|
19507
|
+
try {
|
|
19508
|
+
wallet = await resolveWalletAddress(auth, opts.wallet, "solana");
|
|
19509
|
+
} catch (e) {
|
|
19510
|
+
console.error(error(e instanceof Error ? e.message : String(e)));
|
|
19475
19511
|
process.exit(1);
|
|
19476
19512
|
}
|
|
19477
19513
|
const input = resolveToken(fromToken);
|
|
@@ -19748,12 +19784,12 @@ async function reportHttpError(res, label2) {
|
|
|
19748
19784
|
// src/commands/search.ts
|
|
19749
19785
|
var TREASURY = "FPiZAf3jEnNfCwfvbv46jdPfHbmSepD4Q6iHiLJDQXvb";
|
|
19750
19786
|
function registerSearchCommand(program2) {
|
|
19751
|
-
program2.command("search <query>").description("X Search — pays 1 AMIKO token per search").option("--raw", "Output raw JSON").option("--token <token>", "Payment token (AMIKO, USDC, SOL)", "AMIKO").option("--wallet <address>", "Solana wallet
|
|
19787
|
+
program2.command("search <query>").description("X Search — pays 1 AMIKO token per search").option("--raw", "Output raw JSON").option("--token <token>", "Payment token (AMIKO, USDC, SOL)", "AMIKO").option("--wallet <address>", "Wallet to pay from (defaults to your Solana wallet — run `amiko wallets create --chain solana` if you don't have one)").option("--yes", "Skip the pre-spend confirmation (required in non-interactive shells)").action(async (query, opts) => {
|
|
19752
19788
|
await requireApproval({
|
|
19753
19789
|
cost: `1 ${opts.token || "AMIKO"}`,
|
|
19754
19790
|
summary: `X/Twitter search: ${query.slice(0, 80)}${query.length > 80 ? "…" : ""}`,
|
|
19755
19791
|
yes: opts.yes,
|
|
19756
|
-
commandExample: `amiko
|
|
19792
|
+
commandExample: `amiko markets search ${JSON.stringify(query)}${opts.token && opts.token !== "AMIKO" ? ` --token ${opts.token}` : ""}`
|
|
19757
19793
|
});
|
|
19758
19794
|
const spinner = ora("X Search...").start();
|
|
19759
19795
|
try {
|
|
@@ -19770,10 +19806,12 @@ function registerSearchCommand(program2) {
|
|
|
19770
19806
|
const token = opts.token || "AMIKO";
|
|
19771
19807
|
const chain = "solana";
|
|
19772
19808
|
const amount2 = token === "AMIKO" ? "1" : "0.01";
|
|
19773
|
-
|
|
19774
|
-
|
|
19809
|
+
let wallet;
|
|
19810
|
+
try {
|
|
19811
|
+
wallet = await resolveWalletAddress(auth, opts.wallet, "solana");
|
|
19812
|
+
} catch (e) {
|
|
19775
19813
|
spinner.stop();
|
|
19776
|
-
console.error(error(
|
|
19814
|
+
console.error(error(e instanceof Error ? e.message : String(e)));
|
|
19777
19815
|
process.exit(1);
|
|
19778
19816
|
}
|
|
19779
19817
|
spinner.text = `Paying ${amount2} ${token} (${chain})...`;
|
|
@@ -19801,7 +19839,8 @@ function registerSearchCommand(program2) {
|
|
|
19801
19839
|
"Content-Type": "application/json",
|
|
19802
19840
|
"X-Payment-Tx": txHash,
|
|
19803
19841
|
"X-Payment-Amount": amount2,
|
|
19804
|
-
"X-Payment-Token": token
|
|
19842
|
+
"X-Payment-Token": token,
|
|
19843
|
+
"X-Twin-Id": auth.twinId
|
|
19805
19844
|
}
|
|
19806
19845
|
});
|
|
19807
19846
|
spinner.stop();
|
|
@@ -19847,12 +19886,12 @@ function registerSearchCommand(program2) {
|
|
|
19847
19886
|
// src/commands/image.ts
|
|
19848
19887
|
var TREASURY2 = "FPiZAf3jEnNfCwfvbv46jdPfHbmSepD4Q6iHiLJDQXvb";
|
|
19849
19888
|
function registerImageCommand(program2) {
|
|
19850
|
-
program2.command("image <prompt>").alias("img").description("Generate an image — costs 5 AMIKO").option("--size <size>", "Image size: 1024x1024, 1024x1792, 1792x1024", "1024x1024").option("--quality <q>", "Quality: auto, low, medium, high (gpt-image-1) or standard, hd (dall-e-3)", "auto").option("--model <model>", "Model: gpt-image-1 or dall-e-3", "gpt-image-1").option("--background <bg>", "Background: transparent, opaque, auto (gpt-image-1 only)").option("--format <fmt>", "Output: png, webp, jpeg (gpt-image-1 only)", "png").option("--wallet <address>", "Solana wallet
|
|
19889
|
+
program2.command("image <prompt>").alias("img").description("Generate an image — costs 5 AMIKO").option("--size <size>", "Image size: 1024x1024, 1024x1792, 1792x1024", "1024x1024").option("--quality <q>", "Quality: auto, low, medium, high (gpt-image-1) or standard, hd (dall-e-3)", "auto").option("--model <model>", "Model: gpt-image-1 or dall-e-3", "gpt-image-1").option("--background <bg>", "Background: transparent, opaque, auto (gpt-image-1 only)").option("--format <fmt>", "Output: png, webp, jpeg (gpt-image-1 only)", "png").option("--wallet <address>", "Wallet to pay from (defaults to your Solana wallet — run `amiko wallets create --chain solana` if you don't have one)").option("--raw", "Output raw JSON").option("--yes", "Skip the pre-spend confirmation (required in non-interactive shells)").action(async (prompt, opts) => {
|
|
19851
19890
|
await requireApproval({
|
|
19852
19891
|
cost: "5 AMIKO",
|
|
19853
19892
|
summary: `Generate image: ${prompt.slice(0, 80)}${prompt.length > 80 ? "…" : ""}`,
|
|
19854
19893
|
yes: opts.yes,
|
|
19855
|
-
commandExample: `amiko
|
|
19894
|
+
commandExample: `amiko markets image ${JSON.stringify(prompt)}`
|
|
19856
19895
|
});
|
|
19857
19896
|
const isTTY = process.stderr.isTTY;
|
|
19858
19897
|
const spinner = isTTY ? ora("Generating image...").start() : null;
|
|
@@ -19893,10 +19932,12 @@ function registerImageCommand(program2) {
|
|
|
19893
19932
|
process.exit(1);
|
|
19894
19933
|
}
|
|
19895
19934
|
const mppBase2 = (config2.apiBase || "https://mpp.amikomarkets.com").replace(/\/$/, "");
|
|
19896
|
-
|
|
19897
|
-
|
|
19935
|
+
let wallet;
|
|
19936
|
+
try {
|
|
19937
|
+
wallet = await resolveWalletAddress(auth, opts.wallet, "solana");
|
|
19938
|
+
} catch (e) {
|
|
19898
19939
|
stopProgress();
|
|
19899
|
-
console.error(error(
|
|
19940
|
+
console.error(error(e instanceof Error ? e.message : String(e)));
|
|
19900
19941
|
process.exit(1);
|
|
19901
19942
|
}
|
|
19902
19943
|
setStatus("Paying 5 AMIKO...");
|
|
@@ -19922,7 +19963,8 @@ function registerImageCommand(program2) {
|
|
|
19922
19963
|
method: "POST",
|
|
19923
19964
|
headers: {
|
|
19924
19965
|
"Content-Type": "application/json",
|
|
19925
|
-
"X-Payment-Tx": txHash
|
|
19966
|
+
"X-Payment-Tx": txHash,
|
|
19967
|
+
"X-Twin-Id": auth.twinId
|
|
19926
19968
|
},
|
|
19927
19969
|
body: JSON.stringify({
|
|
19928
19970
|
prompt,
|
|
@@ -19956,7 +19998,7 @@ function registerImageCommand(program2) {
|
|
|
19956
19998
|
console.log(label("URL", img.url));
|
|
19957
19999
|
} else if (img.b64_json) {
|
|
19958
20000
|
console.log(label("Format", "base64 (inline image data)"));
|
|
19959
|
-
console.log(dim(` ${img.b64_json.length.toLocaleString()} chars — save with: amiko
|
|
20001
|
+
console.log(dim(` ${img.b64_json.length.toLocaleString()} chars — save with: amiko markets image "..." --raw | jq -r '.images[0].b64_json' | base64 -d > image.png`));
|
|
19960
20002
|
}
|
|
19961
20003
|
if (img.revised_prompt && img.revised_prompt !== prompt) {
|
|
19962
20004
|
console.log(label("Revised", dim(img.revised_prompt)));
|
|
@@ -19986,12 +20028,12 @@ async function payAmiko(auth, wallet, amount2) {
|
|
|
19986
20028
|
}
|
|
19987
20029
|
function registerAmazonCommand(program2) {
|
|
19988
20030
|
const cmd = program2.command("amazon").description("Search and buy products on Amazon — paid with AMIKO");
|
|
19989
|
-
cmd.command("search <query>").description("Search Amazon products — 1 AMIKO").option("--limit <n>", "Number of results", "5").option("--wallet <address>", "Solana wallet
|
|
20031
|
+
cmd.command("search <query>").description("Search Amazon products — 1 AMIKO").option("--limit <n>", "Number of results", "5").option("--wallet <address>", "Wallet to pay from (defaults to your Solana wallet — run `amiko wallets create --chain solana` if you don't have one)").option("--raw", "Output raw JSON").option("--yes", "Skip the pre-spend confirmation (required in non-interactive shells)").action(async (query, opts) => {
|
|
19990
20032
|
await requireApproval({
|
|
19991
20033
|
cost: "1 AMIKO",
|
|
19992
20034
|
summary: `Amazon product search: ${query.slice(0, 80)}${query.length > 80 ? "…" : ""}`,
|
|
19993
20035
|
yes: opts.yes,
|
|
19994
|
-
commandExample: `amiko
|
|
20036
|
+
commandExample: `amiko markets amazon search ${JSON.stringify(query)}`
|
|
19995
20037
|
});
|
|
19996
20038
|
const spinner = ora("Searching Amazon...").start();
|
|
19997
20039
|
try {
|
|
@@ -20005,10 +20047,12 @@ function registerAmazonCommand(program2) {
|
|
|
20005
20047
|
process.exit(1);
|
|
20006
20048
|
}
|
|
20007
20049
|
const mppBase2 = (config2.apiBase || "https://mpp.amikomarkets.com").replace(/\/$/, "");
|
|
20008
|
-
|
|
20009
|
-
|
|
20050
|
+
let wallet;
|
|
20051
|
+
try {
|
|
20052
|
+
wallet = await resolveWalletAddress(auth, opts.wallet, "solana");
|
|
20053
|
+
} catch (e) {
|
|
20010
20054
|
spinner.stop();
|
|
20011
|
-
console.error(error(
|
|
20055
|
+
console.error(error(e instanceof Error ? e.message : String(e)));
|
|
20012
20056
|
process.exit(1);
|
|
20013
20057
|
}
|
|
20014
20058
|
spinner.text = "Paying 1 AMIKO...";
|
|
@@ -20018,7 +20062,7 @@ function registerAmazonCommand(program2) {
|
|
|
20018
20062
|
q: `site:amazon.com ${query}`
|
|
20019
20063
|
});
|
|
20020
20064
|
const res = await fetch(`${mppBase2}/v1/search/google_shopping?${params}`, {
|
|
20021
|
-
headers: { "X-Payment-Tx": txHash }
|
|
20065
|
+
headers: { "X-Payment-Tx": txHash, "X-Twin-Id": auth.twinId }
|
|
20022
20066
|
});
|
|
20023
20067
|
spinner.stop();
|
|
20024
20068
|
if (!res.ok) {
|
|
@@ -20166,7 +20210,8 @@ async function payAndCall(mppBase2, method, path2, auth, wallet, amountOrAuto, b
|
|
|
20166
20210
|
throw new Error("Payment sent but no tx hash returned.");
|
|
20167
20211
|
const headers = {
|
|
20168
20212
|
"Content-Type": "application/json",
|
|
20169
|
-
"X-Payment-Tx": txHash
|
|
20213
|
+
"X-Payment-Tx": txHash,
|
|
20214
|
+
"X-Twin-Id": auth.twinId
|
|
20170
20215
|
};
|
|
20171
20216
|
const fetchOpts = { method, headers };
|
|
20172
20217
|
if (body && method !== "GET") {
|
|
@@ -20178,12 +20223,12 @@ async function payAndCall(mppBase2, method, path2, auth, wallet, amountOrAuto, b
|
|
|
20178
20223
|
}
|
|
20179
20224
|
function registerServicesCommand(program2) {
|
|
20180
20225
|
const svc = program2.command("service").alias("svc").description("Call any MPP service — pays with AMIKO tokens");
|
|
20181
|
-
svc.command("tts <voiceId> <text>").description("Text-to-speech — 1 AMIKO").option("--raw", "Output raw response").option("--wallet <address>", "Solana wallet
|
|
20226
|
+
svc.command("tts <voiceId> <text>").description("Text-to-speech — 1 AMIKO").option("--raw", "Output raw response").option("--wallet <address>", "Wallet to pay from (defaults to your Solana wallet — run `amiko wallets create --chain solana` if none)").option("--yes", "Skip the pre-spend confirmation (required in non-interactive shells)").action(async (voiceId, text, opts) => {
|
|
20182
20227
|
await requireApproval({
|
|
20183
20228
|
cost: "1 AMIKO",
|
|
20184
20229
|
summary: `Text-to-speech (voice ${voiceId}): "${text.slice(0, 60)}${text.length > 60 ? "…" : ""}"`,
|
|
20185
20230
|
yes: opts.yes,
|
|
20186
|
-
commandExample: `amiko
|
|
20231
|
+
commandExample: `amiko markets service tts ${voiceId} ${JSON.stringify(text)}`
|
|
20187
20232
|
});
|
|
20188
20233
|
await callService({
|
|
20189
20234
|
name: "Text-to-Speech",
|
|
@@ -20194,12 +20239,12 @@ function registerServicesCommand(program2) {
|
|
|
20194
20239
|
opts
|
|
20195
20240
|
});
|
|
20196
20241
|
});
|
|
20197
|
-
svc.command("stt <audioUrl>").description("Speech-to-text — 1 AMIKO").option("--raw", "Output raw response").option("--wallet <address>", "Solana wallet
|
|
20242
|
+
svc.command("stt <audioUrl>").description("Speech-to-text — 1 AMIKO").option("--raw", "Output raw response").option("--wallet <address>", "Wallet to pay from (defaults to your Solana wallet — run `amiko wallets create --chain solana` if none)").option("--yes", "Skip the pre-spend confirmation (required in non-interactive shells)").action(async (audioUrl, opts) => {
|
|
20198
20243
|
await requireApproval({
|
|
20199
20244
|
cost: "1 AMIKO",
|
|
20200
20245
|
summary: `Speech-to-text on ${audioUrl}`,
|
|
20201
20246
|
yes: opts.yes,
|
|
20202
|
-
commandExample: `amiko
|
|
20247
|
+
commandExample: `amiko markets service stt ${JSON.stringify(audioUrl)}`
|
|
20203
20248
|
});
|
|
20204
20249
|
await callService({
|
|
20205
20250
|
name: "Speech-to-Text",
|
|
@@ -20228,12 +20273,12 @@ function registerServicesCommand(program2) {
|
|
|
20228
20273
|
console.log(JSON.stringify(data, null, 2));
|
|
20229
20274
|
}
|
|
20230
20275
|
});
|
|
20231
|
-
svc.command("chat <message>").description("AI chat — 1 AMIKO").option("--model <model>", "Model to use").option("--raw", "Output raw response").option("--wallet <address>", "Solana wallet
|
|
20276
|
+
svc.command("chat <message>").description("AI chat — 1 AMIKO").option("--model <model>", "Model to use").option("--raw", "Output raw response").option("--wallet <address>", "Wallet to pay from (defaults to your Solana wallet — run `amiko wallets create --chain solana` if none)").option("--yes", "Skip the pre-spend confirmation (required in non-interactive shells)").action(async (message, opts) => {
|
|
20232
20277
|
await requireApproval({
|
|
20233
20278
|
cost: "1 AMIKO",
|
|
20234
20279
|
summary: `AI chat${opts.model ? ` (model ${opts.model})` : ""}: "${message.slice(0, 60)}${message.length > 60 ? "…" : ""}"`,
|
|
20235
20280
|
yes: opts.yes,
|
|
20236
|
-
commandExample: `amiko
|
|
20281
|
+
commandExample: `amiko markets service chat ${JSON.stringify(message)}${opts.model ? ` --model ${opts.model}` : ""}`
|
|
20237
20282
|
});
|
|
20238
20283
|
const body = { message };
|
|
20239
20284
|
if (opts.model)
|
|
@@ -20247,13 +20292,13 @@ function registerServicesCommand(program2) {
|
|
|
20247
20292
|
opts
|
|
20248
20293
|
});
|
|
20249
20294
|
});
|
|
20250
|
-
svc.command("call <method> <path> [body]").description("Call any MPP endpoint — pays in AMIKO or USDC on Solana").option("--amiko <amount>", "AMIKO tokens to pay (default: auto-price from USD)").option("--usdc <amount>", "Pay in USDC instead of AMIKO").option("--raw", "Output raw response").option("--wallet <address>", "Solana wallet
|
|
20295
|
+
svc.command("call <method> <path> [body]").description("Call any MPP endpoint — pays in AMIKO or USDC on Solana").option("--amiko <amount>", "AMIKO tokens to pay (default: auto-price from USD)").option("--usdc <amount>", "Pay in USDC instead of AMIKO").option("--raw", "Output raw response").option("--wallet <address>", "Wallet to pay from (defaults to your Solana wallet — run `amiko wallets create --chain solana` if none)").option("--yes", "Skip the pre-spend confirmation (required in non-interactive shells)").action(async (method, path2, bodyStr, opts) => {
|
|
20251
20296
|
const costLabel = opts.usdc ? `${opts.usdc} USDC` : opts.amiko ? `${opts.amiko} AMIKO` : "auto-priced AMIKO (check /v1/ list for endpoint price first)";
|
|
20252
20297
|
await requireApproval({
|
|
20253
20298
|
cost: costLabel,
|
|
20254
20299
|
summary: `${method.toUpperCase()} ${path2}`,
|
|
20255
20300
|
yes: opts.yes,
|
|
20256
|
-
commandExample: `amiko
|
|
20301
|
+
commandExample: `amiko markets service call ${method.toUpperCase()} ${path2}${bodyStr ? ` ${JSON.stringify(bodyStr)}` : ""}${opts.amiko ? ` --amiko ${opts.amiko}` : ""}${opts.usdc ? ` --usdc ${opts.usdc}` : ""}`
|
|
20257
20302
|
});
|
|
20258
20303
|
let body;
|
|
20259
20304
|
if (bodyStr) {
|
|
@@ -20312,10 +20357,12 @@ async function callService(params) {
|
|
|
20312
20357
|
process.exit(1);
|
|
20313
20358
|
}
|
|
20314
20359
|
const mppBase2 = (config2.apiBase || "https://mpp.amikomarkets.com").replace(/\/$/, "");
|
|
20315
|
-
|
|
20316
|
-
|
|
20360
|
+
let wallet;
|
|
20361
|
+
try {
|
|
20362
|
+
wallet = await resolveWalletAddress(auth, params.opts.wallet, "solana");
|
|
20363
|
+
} catch (e) {
|
|
20317
20364
|
spinner.stop();
|
|
20318
|
-
console.error(error(
|
|
20365
|
+
console.error(error(e instanceof Error ? e.message : String(e)));
|
|
20319
20366
|
process.exit(1);
|
|
20320
20367
|
}
|
|
20321
20368
|
const token = params.usdc ? "USDC" : "AMIKO";
|
|
@@ -20448,7 +20495,7 @@ function parseBool(raw, flag) {
|
|
|
20448
20495
|
throw new Error(`${flag} must be true or false, got "${raw}"`);
|
|
20449
20496
|
}
|
|
20450
20497
|
function registerTwinCommand(program2) {
|
|
20451
|
-
program2.command("
|
|
20498
|
+
program2.command("update").description("Update twin name, description, or public visibility").option("--name <name>", "Display name").option("--description <text>", "Description").option("--public <bool>", "Public visibility (true/false)").option("--twin <idOrName>", "Target twin id or name").option("--yes", "Skip confirmation when changing visibility").option("--json", "Output as JSON").action(async (opts) => {
|
|
20452
20499
|
if (opts.name === undefined && opts.description === undefined && opts.public === undefined) {
|
|
20453
20500
|
console.error(error("Pass at least one of --name, --description, --public"));
|
|
20454
20501
|
process.exit(1);
|
|
@@ -20483,7 +20530,7 @@ function registerTwinCommand(program2) {
|
|
|
20483
20530
|
action: `Set twin visibility to ${next ? "PUBLIC" : "PRIVATE"}`,
|
|
20484
20531
|
detail: next ? "Anyone on amiko.com will be able to find and chat with this twin." : "The twin will no longer be discoverable.",
|
|
20485
20532
|
yes: opts.yes,
|
|
20486
|
-
commandExample: `amiko twin
|
|
20533
|
+
commandExample: `amiko twin update --public ${next}`
|
|
20487
20534
|
});
|
|
20488
20535
|
}
|
|
20489
20536
|
const spinner = opts.json ? null : ora("Updating twin...").start();
|
|
@@ -20605,6 +20652,18 @@ function detectMime(buffer, name) {
|
|
|
20605
20652
|
}
|
|
20606
20653
|
|
|
20607
20654
|
// src/commands/docs.ts
|
|
20655
|
+
function fmtBytes(n) {
|
|
20656
|
+
if (!n || n <= 0)
|
|
20657
|
+
return "-";
|
|
20658
|
+
const units = ["B", "KB", "MB", "GB"];
|
|
20659
|
+
let v = n;
|
|
20660
|
+
let i = 0;
|
|
20661
|
+
while (v >= 1024 && i < units.length - 1) {
|
|
20662
|
+
v /= 1024;
|
|
20663
|
+
i++;
|
|
20664
|
+
}
|
|
20665
|
+
return `${v.toFixed(v >= 10 ? 0 : 1)} ${units[i]}`;
|
|
20666
|
+
}
|
|
20608
20667
|
function twinFromFlag(opts) {
|
|
20609
20668
|
const config2 = loadConfig();
|
|
20610
20669
|
const auth = resolveAuth();
|
|
@@ -20618,20 +20677,8 @@ function twinFromFlag(opts) {
|
|
|
20618
20677
|
throw e;
|
|
20619
20678
|
}
|
|
20620
20679
|
}
|
|
20621
|
-
function fmtBytes(n) {
|
|
20622
|
-
if (!n || n <= 0)
|
|
20623
|
-
return "-";
|
|
20624
|
-
const units = ["B", "KB", "MB", "GB"];
|
|
20625
|
-
let v = n;
|
|
20626
|
-
let i = 0;
|
|
20627
|
-
while (v >= 1024 && i < units.length - 1) {
|
|
20628
|
-
v /= 1024;
|
|
20629
|
-
i++;
|
|
20630
|
-
}
|
|
20631
|
-
return `${v.toFixed(v >= 10 ? 0 : 1)} ${units[i]}`;
|
|
20632
|
-
}
|
|
20633
20680
|
function registerDocsCommand(program2) {
|
|
20634
|
-
program2.command("
|
|
20681
|
+
program2.command("list").description("List uploaded documents for a twin").option("--twin <idOrName>", "Target twin id or name").option("--limit <n>", "Max results", "50").option("--json", "Output as JSON").action(async (opts) => {
|
|
20635
20682
|
const { twinId, auth } = twinFromFlag(opts);
|
|
20636
20683
|
const spinner = opts.json ? null : ora("Loading documents...").start();
|
|
20637
20684
|
try {
|
|
@@ -20647,15 +20694,15 @@ function registerDocsCommand(program2) {
|
|
|
20647
20694
|
[
|
|
20648
20695
|
dim("ID"),
|
|
20649
20696
|
dim("NAME"),
|
|
20650
|
-
dim("
|
|
20697
|
+
dim("TYPE"),
|
|
20651
20698
|
dim("STATUS"),
|
|
20652
20699
|
dim("CREATED")
|
|
20653
20700
|
],
|
|
20654
20701
|
...d.docs.map((doc2) => [
|
|
20655
|
-
doc2.id
|
|
20702
|
+
doc2.id,
|
|
20656
20703
|
doc2.title || doc2.filename,
|
|
20657
|
-
|
|
20658
|
-
doc2.
|
|
20704
|
+
doc2.file_type || "-",
|
|
20705
|
+
doc2.is_processed ? "processed" : doc2.is_parsed ? "parsed" : "pending",
|
|
20659
20706
|
doc2.created_at?.slice(0, 10) || "-"
|
|
20660
20707
|
])
|
|
20661
20708
|
];
|
|
@@ -20667,7 +20714,7 @@ function registerDocsCommand(program2) {
|
|
|
20667
20714
|
process.exit(1);
|
|
20668
20715
|
}
|
|
20669
20716
|
});
|
|
20670
|
-
program2.command("
|
|
20717
|
+
program2.command("upload <source>").description("Upload a document (path | https URL | - for stdin)").option("--twin <idOrName>", "Target twin id or name").option("--name <filename>", "Override filename").option("--title <title>", "Document title (defaults to filename)").option("--description <text>", "Document description").option("--doc-type <type>", "Document type (e.g. 'other', 'memory')", "other").option("--stdin", "Read from stdin (source must be -)").option("--json", "Output as JSON").action(async (source, opts) => {
|
|
20671
20718
|
const { twinId, auth } = twinFromFlag(opts);
|
|
20672
20719
|
const spinner = opts.json ? null : ora("Reading input...").start();
|
|
20673
20720
|
try {
|
|
@@ -20718,13 +20765,13 @@ function registerDocsCommand(program2) {
|
|
|
20718
20765
|
process.exit(1);
|
|
20719
20766
|
}
|
|
20720
20767
|
});
|
|
20721
|
-
program2.command("
|
|
20768
|
+
program2.command("delete <docId>").description("Delete a document").option("--twin <idOrName>", "Target twin id or name").option("--yes", "Skip confirmation").option("--json", "Output as JSON").action(async (docId, opts) => {
|
|
20722
20769
|
const { twinId, auth } = twinFromFlag(opts);
|
|
20723
20770
|
await confirmDestructive({
|
|
20724
20771
|
action: `Delete document ${docId}`,
|
|
20725
20772
|
detail: "This will remove the document and its embeddings from the twin's RAG.",
|
|
20726
20773
|
yes: opts.yes,
|
|
20727
|
-
commandExample: `amiko docs
|
|
20774
|
+
commandExample: `amiko docs delete ${docId}`
|
|
20728
20775
|
});
|
|
20729
20776
|
const spinner = opts.json ? null : ora("Deleting document...").start();
|
|
20730
20777
|
try {
|
|
@@ -20737,7 +20784,7 @@ function registerDocsCommand(program2) {
|
|
|
20737
20784
|
process.exit(1);
|
|
20738
20785
|
}
|
|
20739
20786
|
});
|
|
20740
|
-
program2.command("
|
|
20787
|
+
program2.command("presign <source>").description("Get a presigned upload URL for a document (no upload)").option("--twin <idOrName>", "Target twin id or name").option("--name <filename>", "Override filename used in the request").option("--json", "Output as JSON").action(async (source, opts) => {
|
|
20741
20788
|
const { twinId, auth } = twinFromFlag(opts);
|
|
20742
20789
|
const filename = opts.name ?? (source === "-" ? "stdin" : source.split("/").pop() ?? source);
|
|
20743
20790
|
try {
|
|
@@ -20765,7 +20812,7 @@ function twinFromFlag2(opts) {
|
|
|
20765
20812
|
}
|
|
20766
20813
|
}
|
|
20767
20814
|
function registerVoiceCommand(program2) {
|
|
20768
|
-
program2.command("
|
|
20815
|
+
program2.command("design").description("Design a voice from a text description").requiredOption("--description <text>", "Voice description (min 20 chars)").option("--twin <idOrName>", "Target twin id or name").option("--json", "Output as JSON").action(async (opts) => {
|
|
20769
20816
|
if (opts.description.trim().length < 20) {
|
|
20770
20817
|
console.error(error("--description must be at least 20 characters"));
|
|
20771
20818
|
process.exit(1);
|
|
@@ -20785,7 +20832,7 @@ function registerVoiceCommand(program2) {
|
|
|
20785
20832
|
console.log(label(`#${i + 1} id`, p.generated_voice_id ?? "?"));
|
|
20786
20833
|
}
|
|
20787
20834
|
console.log("");
|
|
20788
|
-
console.log(dim("Pick one with: amiko voice
|
|
20835
|
+
console.log(dim("Pick one with: amiko voice create --sample <id>"));
|
|
20789
20836
|
}, { json: opts.json });
|
|
20790
20837
|
} catch (e) {
|
|
20791
20838
|
spinner?.stop();
|
|
@@ -20793,7 +20840,7 @@ function registerVoiceCommand(program2) {
|
|
|
20793
20840
|
process.exit(1);
|
|
20794
20841
|
}
|
|
20795
20842
|
});
|
|
20796
|
-
program2.command("
|
|
20843
|
+
program2.command("create").description("Create a voice from a previously designed preview").requiredOption("--sample <id>", "generated_voice_id from voice design").option("--description <text>", "Optional voice description").option("--twin <idOrName>", "Target twin id or name").option("--json", "Output as JSON").action(async (opts) => {
|
|
20797
20844
|
const { twinId, auth } = twinFromFlag2(opts);
|
|
20798
20845
|
const spinner = opts.json ? null : ora("Creating voice...").start();
|
|
20799
20846
|
try {
|
|
@@ -20819,7 +20866,7 @@ function registerVoiceCommand(program2) {
|
|
|
20819
20866
|
process.exit(1);
|
|
20820
20867
|
}
|
|
20821
20868
|
});
|
|
20822
|
-
program2.command("
|
|
20869
|
+
program2.command("clone <source>").description("Clone a voice from an audio file (path | https URL | -)").option("--twin <idOrName>", "Target twin id or name").option("--name <filename>", "Override filename").option("--stdin", "Read from stdin (source must be -)").option("--json", "Output as JSON").action(async (source, opts) => {
|
|
20823
20870
|
const { twinId, auth } = twinFromFlag2(opts);
|
|
20824
20871
|
const spinner = opts.json ? null : ora("Reading audio...").start();
|
|
20825
20872
|
try {
|
|
@@ -20847,13 +20894,13 @@ function registerVoiceCommand(program2) {
|
|
|
20847
20894
|
process.exit(1);
|
|
20848
20895
|
}
|
|
20849
20896
|
});
|
|
20850
|
-
program2.command("
|
|
20897
|
+
program2.command("reset").description("Reset the twin's voice to default").option("--twin <idOrName>", "Target twin id or name").option("--yes", "Skip confirmation").option("--json", "Output as JSON").action(async (opts) => {
|
|
20851
20898
|
const { twinId, auth } = twinFromFlag2(opts);
|
|
20852
20899
|
await confirmDestructive({
|
|
20853
20900
|
action: "Reset voice",
|
|
20854
20901
|
detail: "This removes the custom voice. You can redesign or clone a new one later.",
|
|
20855
20902
|
yes: opts.yes,
|
|
20856
|
-
commandExample: "amiko voice
|
|
20903
|
+
commandExample: "amiko voice reset"
|
|
20857
20904
|
});
|
|
20858
20905
|
const spinner = opts.json ? null : ora("Resetting voice...").start();
|
|
20859
20906
|
try {
|
|
@@ -20870,7 +20917,7 @@ function registerVoiceCommand(program2) {
|
|
|
20870
20917
|
|
|
20871
20918
|
// src/commands/avatar.ts
|
|
20872
20919
|
function registerAvatarCommand(program2) {
|
|
20873
|
-
program2.command("
|
|
20920
|
+
program2.command("update").description("Update twin avatar image (path | https URL | - for stdin)").requiredOption("--file <source>", "Image source").option("--twin <idOrName>", "Target twin id or name").option("--stdin", "Read from stdin (file must be -)").option("--name <filename>", "Override filename").option("--yes", "Skip confirmation").option("--json", "Output as JSON").action(async (opts) => {
|
|
20874
20921
|
const config2 = loadConfig();
|
|
20875
20922
|
const auth = resolveAuth();
|
|
20876
20923
|
let twinId;
|
|
@@ -20887,7 +20934,7 @@ function registerAvatarCommand(program2) {
|
|
|
20887
20934
|
action: "Replace twin avatar",
|
|
20888
20935
|
detail: "The twin's public avatar will change immediately.",
|
|
20889
20936
|
yes: opts.yes,
|
|
20890
|
-
commandExample: `amiko avatar
|
|
20937
|
+
commandExample: `amiko avatar update --file ${opts.file}`
|
|
20891
20938
|
});
|
|
20892
20939
|
const spinner = opts.json ? null : ora("Reading image...").start();
|
|
20893
20940
|
try {
|
|
@@ -20929,27 +20976,27 @@ function registerAvatarCommand(program2) {
|
|
|
20929
20976
|
|
|
20930
20977
|
// src/commands/friends.ts
|
|
20931
20978
|
function registerFriendsCommand(program2) {
|
|
20932
|
-
program2.command("
|
|
20979
|
+
program2.command("list").description("List friends").option("--type <user|twin>", "Filter by friend type").option("--json", "Output as JSON").action(async (opts) => {
|
|
20933
20980
|
const auth = resolveAuth();
|
|
20934
20981
|
const spinner = opts.json ? null : ora("Loading friends...").start();
|
|
20935
20982
|
try {
|
|
20936
20983
|
const data = await amikoWebFetch(auth, "/api/friends", { query: { type: opts.type } });
|
|
20937
20984
|
spinner?.stop();
|
|
20938
20985
|
renderOutput(data, (d) => {
|
|
20939
|
-
const items = d.
|
|
20986
|
+
const items = d.friends ?? [];
|
|
20940
20987
|
if (items.length === 0) {
|
|
20941
20988
|
console.log(dim("No friends."));
|
|
20942
20989
|
return;
|
|
20943
20990
|
}
|
|
20944
20991
|
console.log(heading(`Friends (${items.length})`));
|
|
20945
20992
|
const rows = [
|
|
20946
|
-
[dim("ID"), dim("NAME"), dim("HANDLE"), dim("TYPE"), dim("
|
|
20993
|
+
[dim("ID"), dim("NAME"), dim("HANDLE"), dim("TYPE"), dim("SINCE")],
|
|
20947
20994
|
...items.map((f) => [
|
|
20948
|
-
f.
|
|
20995
|
+
f.friendship_id,
|
|
20949
20996
|
f.friend.name ?? "-",
|
|
20950
20997
|
f.friend.handle ?? "-",
|
|
20951
20998
|
f.friend.type,
|
|
20952
|
-
f.
|
|
20999
|
+
f.since?.slice(0, 10) ?? "-"
|
|
20953
21000
|
])
|
|
20954
21001
|
];
|
|
20955
21002
|
console.log(table(rows));
|
|
@@ -20960,7 +21007,7 @@ function registerFriendsCommand(program2) {
|
|
|
20960
21007
|
process.exit(1);
|
|
20961
21008
|
}
|
|
20962
21009
|
});
|
|
20963
|
-
program2.command("
|
|
21010
|
+
program2.command("add").description("Send a friend request to a user by id").requiredOption("--id <userId>", "Target user id").option("--json", "Output as JSON").action(async (opts) => {
|
|
20964
21011
|
const auth = resolveAuth();
|
|
20965
21012
|
const spinner = opts.json ? null : ora("Sending friend request...").start();
|
|
20966
21013
|
try {
|
|
@@ -20980,7 +21027,7 @@ function registerFriendsCommand(program2) {
|
|
|
20980
21027
|
process.exit(1);
|
|
20981
21028
|
}
|
|
20982
21029
|
});
|
|
20983
|
-
program2.command("
|
|
21030
|
+
program2.command("accept <friendshipId>").description("Accept a pending friend request").option("--json", "Output as JSON").action(async (friendshipId, opts) => {
|
|
20984
21031
|
const auth = resolveAuth();
|
|
20985
21032
|
const spinner = opts.json ? null : ora("Accepting...").start();
|
|
20986
21033
|
try {
|
|
@@ -20993,11 +21040,380 @@ function registerFriendsCommand(program2) {
|
|
|
20993
21040
|
process.exit(1);
|
|
20994
21041
|
}
|
|
20995
21042
|
});
|
|
20996
|
-
program2.command("
|
|
21043
|
+
program2.command("requests").description("List pending friend requests (incoming and outgoing)").option("--direction <incoming|outgoing|all>", "Filter direction", "all").option("--json", "Output as JSON").action(async (opts) => {
|
|
21044
|
+
const auth = resolveAuth();
|
|
21045
|
+
const spinner = opts.json ? null : ora("Loading requests...").start();
|
|
21046
|
+
try {
|
|
21047
|
+
const data = await amikoWebFetch(auth, "/api/friends/requests");
|
|
21048
|
+
spinner?.stop();
|
|
21049
|
+
renderOutput(data, (d) => {
|
|
21050
|
+
const dir = opts.direction;
|
|
21051
|
+
const showIn = dir === "all" || dir === "incoming";
|
|
21052
|
+
const showOut = dir === "all" || dir === "outgoing";
|
|
21053
|
+
const incoming = d.incoming ?? [];
|
|
21054
|
+
const outgoing = d.outgoing ?? [];
|
|
21055
|
+
if (showIn) {
|
|
21056
|
+
console.log(heading(`Incoming (${incoming.length})`));
|
|
21057
|
+
if (incoming.length === 0) {
|
|
21058
|
+
console.log(dim(" none"));
|
|
21059
|
+
} else {
|
|
21060
|
+
const rows = [
|
|
21061
|
+
[dim("FRIENDSHIP"), dim("FROM"), dim("HANDLE"), dim("SENT")],
|
|
21062
|
+
...incoming.map((r) => [
|
|
21063
|
+
r.friendship_id,
|
|
21064
|
+
r.user.name ?? "-",
|
|
21065
|
+
r.user.handle ?? "-",
|
|
21066
|
+
r.sent_at?.slice(0, 10) ?? "-"
|
|
21067
|
+
])
|
|
21068
|
+
];
|
|
21069
|
+
console.log(table(rows));
|
|
21070
|
+
console.log(dim(`
|
|
21071
|
+
Accept with: amiko friends accept <friendshipId>`));
|
|
21072
|
+
}
|
|
21073
|
+
}
|
|
21074
|
+
if (showOut) {
|
|
21075
|
+
if (showIn)
|
|
21076
|
+
console.log();
|
|
21077
|
+
console.log(heading(`Outgoing (${outgoing.length})`));
|
|
21078
|
+
if (outgoing.length === 0) {
|
|
21079
|
+
console.log(dim(" none"));
|
|
21080
|
+
} else {
|
|
21081
|
+
const rows = [
|
|
21082
|
+
[dim("FRIENDSHIP"), dim("TO"), dim("HANDLE"), dim("SENT")],
|
|
21083
|
+
...outgoing.map((r) => [
|
|
21084
|
+
r.friendship_id,
|
|
21085
|
+
r.user.name ?? "-",
|
|
21086
|
+
r.user.handle ?? "-",
|
|
21087
|
+
r.sent_at?.slice(0, 10) ?? "-"
|
|
21088
|
+
])
|
|
21089
|
+
];
|
|
21090
|
+
console.log(table(rows));
|
|
21091
|
+
}
|
|
21092
|
+
}
|
|
21093
|
+
}, { json: opts.json });
|
|
21094
|
+
} catch (e) {
|
|
21095
|
+
spinner?.stop();
|
|
21096
|
+
console.error(error(e instanceof Error ? e.message : String(e)));
|
|
21097
|
+
process.exit(1);
|
|
21098
|
+
}
|
|
21099
|
+
});
|
|
21100
|
+
const reports = program2.command("reports").description("Friend matching reports");
|
|
21101
|
+
reports.command("list").description("List friend matching reports").option("--page <n>", "Page number", "1").option("--limit <n>", "Page size (1-50)", "20").option("--json", "Output as JSON").action(async (opts) => {
|
|
21102
|
+
const auth = resolveAuth();
|
|
21103
|
+
const spinner = opts.json ? null : ora("Loading reports...").start();
|
|
21104
|
+
try {
|
|
21105
|
+
const data = await amikoWebFetch(auth, "/api/friends/reports", {
|
|
21106
|
+
query: { page: opts.page, limit: opts.limit }
|
|
21107
|
+
});
|
|
21108
|
+
spinner?.stop();
|
|
21109
|
+
renderOutput(data, (d) => {
|
|
21110
|
+
const items = d.items ?? [];
|
|
21111
|
+
if (items.length === 0) {
|
|
21112
|
+
console.log(dim("No reports."));
|
|
21113
|
+
return;
|
|
21114
|
+
}
|
|
21115
|
+
console.log(heading(`Reports (${items.length}/${d.total ?? items.length})`));
|
|
21116
|
+
const rows = [
|
|
21117
|
+
[
|
|
21118
|
+
dim("ID"),
|
|
21119
|
+
dim("TYPE"),
|
|
21120
|
+
dim("STATUS"),
|
|
21121
|
+
dim("SCORE"),
|
|
21122
|
+
dim("WITH"),
|
|
21123
|
+
dim("CREATED")
|
|
21124
|
+
],
|
|
21125
|
+
...items.map((r) => {
|
|
21126
|
+
const other = r.respondent ?? r.initiator;
|
|
21127
|
+
return [
|
|
21128
|
+
r.id,
|
|
21129
|
+
r.report_type ?? "-",
|
|
21130
|
+
r.status ?? "-",
|
|
21131
|
+
r.score_preview != null ? String(r.score_preview) : "-",
|
|
21132
|
+
other?.name ?? other?.handle ?? "-",
|
|
21133
|
+
r.created_at?.slice(0, 10) ?? "-"
|
|
21134
|
+
];
|
|
21135
|
+
})
|
|
21136
|
+
];
|
|
21137
|
+
console.log(table(rows));
|
|
21138
|
+
}, { json: opts.json });
|
|
21139
|
+
} catch (e) {
|
|
21140
|
+
spinner?.stop();
|
|
21141
|
+
console.error(error(e instanceof Error ? e.message : String(e)));
|
|
21142
|
+
process.exit(1);
|
|
21143
|
+
}
|
|
21144
|
+
});
|
|
21145
|
+
reports.command("view <id>").description("View a friend matching report").option("--json", "Output as JSON").action(async (id, opts) => {
|
|
21146
|
+
const auth = resolveAuth();
|
|
21147
|
+
const spinner = opts.json ? null : ora("Loading report...").start();
|
|
21148
|
+
try {
|
|
21149
|
+
const data = await amikoWebFetch(auth, `/api/friends/reports/${encodeURIComponent(id)}`);
|
|
21150
|
+
spinner?.stop();
|
|
21151
|
+
renderOutput(data, (d) => {
|
|
21152
|
+
const r = d.report;
|
|
21153
|
+
console.log(heading(`Report ${r.id}`));
|
|
21154
|
+
if (r.report_type)
|
|
21155
|
+
console.log(label("Type", r.report_type));
|
|
21156
|
+
if (r.status)
|
|
21157
|
+
console.log(label("Status", r.status));
|
|
21158
|
+
const other = r.respondent ?? r.initiator;
|
|
21159
|
+
if (other)
|
|
21160
|
+
console.log(label("With", other.name ?? other.handle ?? other.id));
|
|
21161
|
+
if (r.created_at)
|
|
21162
|
+
console.log(label("Created", r.created_at.slice(0, 10)));
|
|
21163
|
+
if (r.teaser_text) {
|
|
21164
|
+
console.log();
|
|
21165
|
+
console.log(r.teaser_text);
|
|
21166
|
+
}
|
|
21167
|
+
if (r.report_json) {
|
|
21168
|
+
console.log();
|
|
21169
|
+
console.log(heading("Report"));
|
|
21170
|
+
console.log(typeof r.report_json === "string" ? r.report_json : JSON.stringify(r.report_json, null, 2));
|
|
21171
|
+
}
|
|
21172
|
+
}, { json: opts.json });
|
|
21173
|
+
} catch (e) {
|
|
21174
|
+
spinner?.stop();
|
|
21175
|
+
console.error(error(e instanceof Error ? e.message : String(e)));
|
|
21176
|
+
process.exit(1);
|
|
21177
|
+
}
|
|
21178
|
+
});
|
|
21179
|
+
reports.command("pending").description("List pending-consent report requests (awaiting my consent + awaiting theirs)").option("--limit <n>", "Page size (1-50)", "50").option("--json", "Output as JSON").action(async (opts) => {
|
|
21180
|
+
const auth = resolveAuth();
|
|
21181
|
+
const me = auth.userId;
|
|
21182
|
+
if (!me && !opts.json) {
|
|
21183
|
+
console.error(error("Current user id unknown — cannot split requests. Run `amiko accounts` to check."));
|
|
21184
|
+
process.exit(1);
|
|
21185
|
+
}
|
|
21186
|
+
const spinner = opts.json ? null : ora("Loading...").start();
|
|
21187
|
+
try {
|
|
21188
|
+
const data = await amikoWebFetch(auth, "/api/friends/reports", {
|
|
21189
|
+
query: { page: "1", limit: opts.limit }
|
|
21190
|
+
});
|
|
21191
|
+
spinner?.stop();
|
|
21192
|
+
const items = (data.items ?? []).filter((r) => r.status === "pending_consent");
|
|
21193
|
+
const incoming = items.filter((r) => r.respondent_id === me);
|
|
21194
|
+
const outgoing = items.filter((r) => r.initiator_id === me);
|
|
21195
|
+
renderOutput({ incoming, outgoing }, () => {
|
|
21196
|
+
console.log(heading(`Awaiting my consent (${incoming.length})`));
|
|
21197
|
+
if (incoming.length === 0) {
|
|
21198
|
+
console.log(dim(" none"));
|
|
21199
|
+
} else {
|
|
21200
|
+
const rows = [
|
|
21201
|
+
[
|
|
21202
|
+
dim("REPORT ID"),
|
|
21203
|
+
dim("TYPE"),
|
|
21204
|
+
dim("FROM"),
|
|
21205
|
+
dim("HANDLE"),
|
|
21206
|
+
dim("SENT")
|
|
21207
|
+
],
|
|
21208
|
+
...incoming.map((r) => [
|
|
21209
|
+
r.id,
|
|
21210
|
+
r.report_type ?? "-",
|
|
21211
|
+
r.initiator?.name ?? "-",
|
|
21212
|
+
r.initiator?.handle ?? "-",
|
|
21213
|
+
r.created_at?.slice(0, 10) ?? "-"
|
|
21214
|
+
])
|
|
21215
|
+
];
|
|
21216
|
+
console.log(table(rows));
|
|
21217
|
+
console.log(dim(`
|
|
21218
|
+
Consent with: amiko friends reports consent <reportId>`));
|
|
21219
|
+
}
|
|
21220
|
+
console.log();
|
|
21221
|
+
console.log(heading(`Awaiting their consent (${outgoing.length})`));
|
|
21222
|
+
if (outgoing.length === 0) {
|
|
21223
|
+
console.log(dim(" none"));
|
|
21224
|
+
} else {
|
|
21225
|
+
const rows = [
|
|
21226
|
+
[
|
|
21227
|
+
dim("REPORT ID"),
|
|
21228
|
+
dim("TYPE"),
|
|
21229
|
+
dim("TO"),
|
|
21230
|
+
dim("HANDLE"),
|
|
21231
|
+
dim("SENT")
|
|
21232
|
+
],
|
|
21233
|
+
...outgoing.map((r) => [
|
|
21234
|
+
r.id,
|
|
21235
|
+
r.report_type ?? "-",
|
|
21236
|
+
r.respondent?.name ?? "-",
|
|
21237
|
+
r.respondent?.handle ?? "-",
|
|
21238
|
+
r.created_at?.slice(0, 10) ?? "-"
|
|
21239
|
+
])
|
|
21240
|
+
];
|
|
21241
|
+
console.log(table(rows));
|
|
21242
|
+
console.log(dim(`
|
|
21243
|
+
Cancel with: amiko friends reports cancel <reportId>`));
|
|
21244
|
+
}
|
|
21245
|
+
}, { json: opts.json });
|
|
21246
|
+
} catch (e) {
|
|
21247
|
+
spinner?.stop();
|
|
21248
|
+
console.error(error(e instanceof Error ? e.message : String(e)));
|
|
21249
|
+
process.exit(1);
|
|
21250
|
+
}
|
|
21251
|
+
});
|
|
21252
|
+
reports.command("request").description("Request a friend matching report (notifies the other user to consent)").requiredOption("--id <userId>", "Target user id (must be an accepted friend)").requiredOption("--type <friend|romantic|career>", "Report type").option("--yes", "Skip confirmation").option("--json", "Output as JSON").action(async (opts) => {
|
|
21253
|
+
const allowed = ["friend", "romantic", "career"];
|
|
21254
|
+
if (!allowed.includes(opts.type)) {
|
|
21255
|
+
console.error(error(`--type must be one of: ${allowed.join(", ")}`));
|
|
21256
|
+
process.exit(1);
|
|
21257
|
+
}
|
|
21258
|
+
await confirmDestructive({
|
|
21259
|
+
action: `Request a ${opts.type} matching report with user ${opts.id} (they will be notified)`,
|
|
21260
|
+
yes: opts.yes,
|
|
21261
|
+
commandExample: `amiko friends reports request --id ${opts.id} --type ${opts.type}`
|
|
21262
|
+
});
|
|
21263
|
+
const auth = resolveAuth();
|
|
21264
|
+
const spinner = opts.json ? null : ora("Requesting report...").start();
|
|
21265
|
+
try {
|
|
21266
|
+
const data = await amikoWebFetch(auth, "/api/friends/reports", {
|
|
21267
|
+
method: "POST",
|
|
21268
|
+
body: { friend_id: opts.id, report_type: opts.type }
|
|
21269
|
+
});
|
|
21270
|
+
spinner?.stop();
|
|
21271
|
+
renderOutput(data, (d) => {
|
|
21272
|
+
if (d.report) {
|
|
21273
|
+
console.log(success(`Report requested (${d.report.report_type})`));
|
|
21274
|
+
console.log(label("Report ID", d.report.id));
|
|
21275
|
+
console.log(label("Status", d.report.status));
|
|
21276
|
+
console.log(dim(`
|
|
21277
|
+
The other user will be notified and must consent before the report is generated.`));
|
|
21278
|
+
}
|
|
21279
|
+
}, { json: opts.json });
|
|
21280
|
+
} catch (e) {
|
|
21281
|
+
spinner?.stop();
|
|
21282
|
+
console.error(error(e instanceof Error ? e.message : String(e)));
|
|
21283
|
+
process.exit(1);
|
|
21284
|
+
}
|
|
21285
|
+
});
|
|
21286
|
+
for (const action of ["consent", "cancel", "retry"]) {
|
|
21287
|
+
const desc = {
|
|
21288
|
+
consent: "Consent to a report request (respondent only)",
|
|
21289
|
+
cancel: "Cancel a pending report",
|
|
21290
|
+
retry: "Retry a failed report generation"
|
|
21291
|
+
};
|
|
21292
|
+
reports.command(`${action} <id>`).description(desc[action]).option("--json", "Output as JSON").action(async (id, opts) => {
|
|
21293
|
+
const auth = resolveAuth();
|
|
21294
|
+
const spinner = opts.json ? null : ora(`${action}...`).start();
|
|
21295
|
+
try {
|
|
21296
|
+
const data = await amikoWebFetch(auth, `/api/friends/reports/${encodeURIComponent(id)}`, { method: "PATCH", body: { action } });
|
|
21297
|
+
spinner?.stop();
|
|
21298
|
+
renderOutput(data, (d) => {
|
|
21299
|
+
console.log(success(`${action} ok`));
|
|
21300
|
+
if (d.status)
|
|
21301
|
+
console.log(label("Status", d.status));
|
|
21302
|
+
}, { json: opts.json });
|
|
21303
|
+
} catch (e) {
|
|
21304
|
+
spinner?.stop();
|
|
21305
|
+
console.error(error(e instanceof Error ? e.message : String(e)));
|
|
21306
|
+
process.exit(1);
|
|
21307
|
+
}
|
|
21308
|
+
});
|
|
21309
|
+
}
|
|
21310
|
+
program2.command("find").description("Find matches for a custom relationship description (LLM-generated, on-demand)").requiredOption("--relationship <text>", 'Relationship description, e.g. "cofounder who is good at design"').option("--rationale <text>", "Optional extra context for the matcher").option("--limit <n>", "Max results (1-20)", "10").option("--json", "Output as JSON").action(async (opts) => {
|
|
21311
|
+
const limitNum = Math.max(1, Math.min(20, Number(opts.limit) || 10));
|
|
21312
|
+
const auth = resolveAuth();
|
|
21313
|
+
const spinner = opts.json ? null : ora("Finding matches (may take ~10s)...").start();
|
|
21314
|
+
try {
|
|
21315
|
+
const data = await amikoWebFetch(auth, "/api/user/personality-profile/matches", {
|
|
21316
|
+
method: "POST",
|
|
21317
|
+
body: {
|
|
21318
|
+
relationship_type: opts.relationship,
|
|
21319
|
+
rationale: opts.rationale,
|
|
21320
|
+
limit: limitNum
|
|
21321
|
+
}
|
|
21322
|
+
});
|
|
21323
|
+
spinner?.stop();
|
|
21324
|
+
renderOutput(data, (d) => {
|
|
21325
|
+
const matches = d.matches ?? [];
|
|
21326
|
+
if (matches.length === 0) {
|
|
21327
|
+
console.log(dim("No matches."));
|
|
21328
|
+
if (d.no_match_reason)
|
|
21329
|
+
console.log(dim(d.no_match_reason));
|
|
21330
|
+
return;
|
|
21331
|
+
}
|
|
21332
|
+
console.log(heading(`Matches for "${opts.relationship}" (${matches.length})`));
|
|
21333
|
+
const rows = [
|
|
21334
|
+
[
|
|
21335
|
+
dim("USER"),
|
|
21336
|
+
dim("NAME"),
|
|
21337
|
+
dim("SCORE"),
|
|
21338
|
+
dim("STATUS"),
|
|
21339
|
+
dim("PAIRING")
|
|
21340
|
+
],
|
|
21341
|
+
...matches.map((m) => [
|
|
21342
|
+
m.user_id,
|
|
21343
|
+
m.display_name ?? "-",
|
|
21344
|
+
m.score.toFixed(3),
|
|
21345
|
+
m.friendship_status ?? "none",
|
|
21346
|
+
m.pairing_label ?? "-"
|
|
21347
|
+
])
|
|
21348
|
+
];
|
|
21349
|
+
console.log(table(rows));
|
|
21350
|
+
for (const m of matches) {
|
|
21351
|
+
if (m.compatibility_highlights?.length) {
|
|
21352
|
+
console.log();
|
|
21353
|
+
console.log(label(m.display_name ?? m.user_id, m.compatibility_highlights.join("; ")));
|
|
21354
|
+
}
|
|
21355
|
+
}
|
|
21356
|
+
}, { json: opts.json });
|
|
21357
|
+
} catch (e) {
|
|
21358
|
+
spinner?.stop();
|
|
21359
|
+
console.error(error(e instanceof Error ? e.message : String(e)));
|
|
21360
|
+
process.exit(1);
|
|
21361
|
+
}
|
|
21362
|
+
});
|
|
21363
|
+
program2.command("matches").description("List friend matches").option("--limit <n>", "Max results (1-50)", "10").option("--offset <n>", "Offset", "0").option("--relationship-type <type>", "Filter by relationship type").option("--dimension <dimension>", "Filter by match dimension").option("--json", "Output as JSON").action(async (opts) => {
|
|
21364
|
+
const auth = resolveAuth();
|
|
21365
|
+
const spinner = opts.json ? null : ora("Loading matches...").start();
|
|
21366
|
+
try {
|
|
21367
|
+
const data = await amikoWebFetch(auth, "/api/friends/matches", {
|
|
21368
|
+
query: {
|
|
21369
|
+
limit: opts.limit,
|
|
21370
|
+
offset: opts.offset,
|
|
21371
|
+
relationship_type: opts.relationshipType,
|
|
21372
|
+
dimension: opts.dimension
|
|
21373
|
+
}
|
|
21374
|
+
});
|
|
21375
|
+
spinner?.stop();
|
|
21376
|
+
renderOutput(data, (d) => {
|
|
21377
|
+
const items = d.items ?? [];
|
|
21378
|
+
if (items.length === 0) {
|
|
21379
|
+
console.log(dim("No matches."));
|
|
21380
|
+
return;
|
|
21381
|
+
}
|
|
21382
|
+
console.log(heading(`Matches (${items.length}/${d.total ?? items.length})`));
|
|
21383
|
+
const rows = [
|
|
21384
|
+
[
|
|
21385
|
+
dim("USER"),
|
|
21386
|
+
dim("NAME"),
|
|
21387
|
+
dim("REL"),
|
|
21388
|
+
dim("DIM"),
|
|
21389
|
+
dim("SCORE"),
|
|
21390
|
+
dim("STATUS")
|
|
21391
|
+
],
|
|
21392
|
+
...items.map((m) => [
|
|
21393
|
+
m.matched_user_id,
|
|
21394
|
+
m.display_name ?? m.handle ?? "-",
|
|
21395
|
+
m.relationship_type ?? "-",
|
|
21396
|
+
m.match_dimension ?? "-",
|
|
21397
|
+
m.score != null ? String(m.score) : "-",
|
|
21398
|
+
m.friendship_status ?? "-"
|
|
21399
|
+
])
|
|
21400
|
+
];
|
|
21401
|
+
console.log(table(rows));
|
|
21402
|
+
if (d.has_more)
|
|
21403
|
+
console.log(dim(`
|
|
21404
|
+
(more available)`));
|
|
21405
|
+
}, { json: opts.json });
|
|
21406
|
+
} catch (e) {
|
|
21407
|
+
spinner?.stop();
|
|
21408
|
+
console.error(error(e instanceof Error ? e.message : String(e)));
|
|
21409
|
+
process.exit(1);
|
|
21410
|
+
}
|
|
21411
|
+
});
|
|
21412
|
+
program2.command("remove <friendshipId>").description("Remove a friend").option("--yes", "Skip confirmation").option("--json", "Output as JSON").action(async (friendshipId, opts) => {
|
|
20997
21413
|
await confirmDestructive({
|
|
20998
21414
|
action: `Remove friend ${friendshipId}`,
|
|
20999
21415
|
yes: opts.yes,
|
|
21000
|
-
commandExample: `amiko friends
|
|
21416
|
+
commandExample: `amiko friends remove ${friendshipId}`
|
|
21001
21417
|
});
|
|
21002
21418
|
const auth = resolveAuth();
|
|
21003
21419
|
const spinner = opts.json ? null : ora("Removing...").start();
|
|
@@ -21013,6 +21429,85 @@ function registerFriendsCommand(program2) {
|
|
|
21013
21429
|
});
|
|
21014
21430
|
}
|
|
21015
21431
|
|
|
21432
|
+
// src/commands/users.ts
|
|
21433
|
+
function registerUsersCommand(program2) {
|
|
21434
|
+
program2.command("search <query>").description("Search for users by name or handle").option("--limit <n>", "Max results (1-20)", "10").option("--cursor <cursor>", "Pagination cursor").option("--json", "Output as JSON").action(async (query, opts) => {
|
|
21435
|
+
const auth = resolveAuth();
|
|
21436
|
+
const spinner = opts.json ? null : ora("Searching users...").start();
|
|
21437
|
+
try {
|
|
21438
|
+
const data = await amikoWebFetch(auth, "/api/search", {
|
|
21439
|
+
query: {
|
|
21440
|
+
q: query,
|
|
21441
|
+
type: "people",
|
|
21442
|
+
limit: opts.limit,
|
|
21443
|
+
cursor: opts.cursor
|
|
21444
|
+
}
|
|
21445
|
+
});
|
|
21446
|
+
spinner?.stop();
|
|
21447
|
+
renderOutput(data, (d) => {
|
|
21448
|
+
const items = d.users ?? [];
|
|
21449
|
+
if (items.length === 0) {
|
|
21450
|
+
console.log(dim("No users found."));
|
|
21451
|
+
return;
|
|
21452
|
+
}
|
|
21453
|
+
console.log(heading(`Users (${items.length})`));
|
|
21454
|
+
const rows = [
|
|
21455
|
+
[dim("ID"), dim("NAME"), dim("HANDLE")],
|
|
21456
|
+
...items.map((u) => [
|
|
21457
|
+
u.id,
|
|
21458
|
+
u.name ?? "-",
|
|
21459
|
+
u.handle ?? "-"
|
|
21460
|
+
])
|
|
21461
|
+
];
|
|
21462
|
+
console.log(table(rows));
|
|
21463
|
+
if (d.hasMore && d.nextCursor) {
|
|
21464
|
+
console.log(dim(`
|
|
21465
|
+
Next cursor: ${d.nextCursor}`));
|
|
21466
|
+
}
|
|
21467
|
+
}, { json: opts.json });
|
|
21468
|
+
} catch (e) {
|
|
21469
|
+
spinner?.stop();
|
|
21470
|
+
console.error(error(e instanceof Error ? e.message : String(e)));
|
|
21471
|
+
process.exit(1);
|
|
21472
|
+
}
|
|
21473
|
+
});
|
|
21474
|
+
program2.command("profile <handle>").description("View a user's public profile").option("--json", "Output as JSON").action(async (handle, opts) => {
|
|
21475
|
+
const auth = resolveAuth();
|
|
21476
|
+
const spinner = opts.json ? null : ora("Loading profile...").start();
|
|
21477
|
+
try {
|
|
21478
|
+
const data = await amikoWebFetch(auth, `/api/users/${encodeURIComponent(handle)}`);
|
|
21479
|
+
spinner?.stop();
|
|
21480
|
+
renderOutput(data, (d) => {
|
|
21481
|
+
const u = d.user;
|
|
21482
|
+
console.log(heading(u.name ?? u.handle ?? u.id));
|
|
21483
|
+
console.log(label("ID", u.id));
|
|
21484
|
+
if (u.handle)
|
|
21485
|
+
console.log(label("Handle", u.handle));
|
|
21486
|
+
if (u.created_at)
|
|
21487
|
+
console.log(label("Joined", u.created_at.slice(0, 10)));
|
|
21488
|
+
if (u.last_seen_at)
|
|
21489
|
+
console.log(label("Last seen", u.last_seen_at.slice(0, 10)));
|
|
21490
|
+
if (u.twins && u.twins.length > 0) {
|
|
21491
|
+
console.log(heading(`
|
|
21492
|
+
Twins (${u.twins.length})`));
|
|
21493
|
+
for (const t of u.twins) {
|
|
21494
|
+
console.log(` ${t.name ?? "-"} ${dim(t.handle ?? t.id.slice(0, 8))}`);
|
|
21495
|
+
}
|
|
21496
|
+
}
|
|
21497
|
+
if (Array.isArray(u.mutual_friends) && u.mutual_friends.length > 0) {
|
|
21498
|
+
console.log(label("Mutual friends", String(u.mutual_friends.length)));
|
|
21499
|
+
} else if (typeof u.mutual_friends === "number") {
|
|
21500
|
+
console.log(label("Mutual friends", String(u.mutual_friends)));
|
|
21501
|
+
}
|
|
21502
|
+
}, { json: opts.json });
|
|
21503
|
+
} catch (e) {
|
|
21504
|
+
spinner?.stop();
|
|
21505
|
+
console.error(error(e instanceof Error ? e.message : String(e)));
|
|
21506
|
+
process.exit(1);
|
|
21507
|
+
}
|
|
21508
|
+
});
|
|
21509
|
+
}
|
|
21510
|
+
|
|
21016
21511
|
// src/commands/feed.ts
|
|
21017
21512
|
function registerFeedCommand(program2) {
|
|
21018
21513
|
program2.command("feed").description("Get feed posts").option("--type <for_you|friends>", "Feed type", "friends").option("--hashtag <tag>", "Filter by hashtag").option("--limit <n>", "Max results (default 10, max 100)").option("--cursor <id>", "Pagination cursor").option("--json", "Output as JSON").action(async (opts) => {
|
|
@@ -21056,7 +21551,9 @@ function registerFeedCommand(program2) {
|
|
|
21056
21551
|
process.exit(1);
|
|
21057
21552
|
}
|
|
21058
21553
|
});
|
|
21059
|
-
|
|
21554
|
+
}
|
|
21555
|
+
function registerPostCommand(program2) {
|
|
21556
|
+
program2.command("create").description("Create a post on your feed").requiredOption("--content <text>", "Post body (required, non-empty)").option("--visibility <public|private>", "Post visibility", "public").option("--media <url...>", "One or more media URLs").option("--json", "Output as JSON").action(async (opts) => {
|
|
21060
21557
|
const auth = resolveAuth();
|
|
21061
21558
|
const visibility = opts.visibility === "private" ? "private" : "public";
|
|
21062
21559
|
const spinner = opts.json ? null : ora("Publishing post...").start();
|
|
@@ -21084,7 +21581,7 @@ function registerFeedCommand(program2) {
|
|
|
21084
21581
|
process.exit(1);
|
|
21085
21582
|
}
|
|
21086
21583
|
});
|
|
21087
|
-
program2.command("
|
|
21584
|
+
program2.command("comment").description("Comment on a post").requiredOption("--id <postId>", "Target post id").requiredOption("--comment <text>", "Comment body").option("--media <url...>", "One or more media URLs to attach").option("--twin <idOrName>", "Post as this twin (agent mode)").option("--json", "Output as JSON").action(async (opts) => {
|
|
21088
21585
|
const config2 = loadConfig();
|
|
21089
21586
|
const auth = resolveAuth();
|
|
21090
21587
|
let twinId;
|
|
@@ -21136,7 +21633,7 @@ function twinFromFlag3(opts) {
|
|
|
21136
21633
|
}
|
|
21137
21634
|
}
|
|
21138
21635
|
function registerComposioCommand(program2) {
|
|
21139
|
-
program2.command("
|
|
21636
|
+
program2.command("connect").description("Start Composio OAuth for an app (gmail, github, ...)").requiredOption("--app <slug>", "App name (e.g. gmail)").option("--twin <idOrName>", "Target twin id or name").option("--redirect <url>", "Override OAuth redirect URL").option("--force", "Force reconnect if already connected").option("--json", "Output as JSON").action(async (opts) => {
|
|
21140
21637
|
const { twinId, auth } = twinFromFlag3(opts);
|
|
21141
21638
|
const spinner = opts.json ? null : ora(`Connecting ${opts.app}...`).start();
|
|
21142
21639
|
try {
|
|
@@ -21172,13 +21669,13 @@ function registerComposioCommand(program2) {
|
|
|
21172
21669
|
process.exit(1);
|
|
21173
21670
|
}
|
|
21174
21671
|
});
|
|
21175
|
-
program2.command("
|
|
21672
|
+
program2.command("disconnect").description("Disconnect a Composio app for the twin").requiredOption("--app <slug>", "App name (e.g. gmail)").option("--twin <idOrName>", "Target twin id or name").option("--yes", "Skip confirmation").option("--json", "Output as JSON").action(async (opts) => {
|
|
21176
21673
|
const { twinId, auth } = twinFromFlag3(opts);
|
|
21177
21674
|
await confirmDestructive({
|
|
21178
21675
|
action: `Disconnect ${opts.app}`,
|
|
21179
21676
|
detail: "The twin will lose access to the app's tools until reconnected.",
|
|
21180
21677
|
yes: opts.yes,
|
|
21181
|
-
commandExample: `amiko composio
|
|
21678
|
+
commandExample: `amiko composio disconnect --app ${opts.app}`
|
|
21182
21679
|
});
|
|
21183
21680
|
const spinner = opts.json ? null : ora("Finding connection...").start();
|
|
21184
21681
|
try {
|
|
@@ -21248,6 +21745,49 @@ function registerWalletsBalanceCommand(program2) {
|
|
|
21248
21745
|
}
|
|
21249
21746
|
});
|
|
21250
21747
|
}
|
|
21748
|
+
function registerWalletsCreateCommand(program2) {
|
|
21749
|
+
program2.command("create").description("Create a new wallet for the twin on solana or base").requiredOption("--chain <chain>", "Chain to create the wallet on (solana | base)").option("--custodian <custodian>", "Custodian (crossmint | amiko). Defaults to crossmint; amiko is solana-only.", "crossmint").option("--json", "Output raw JSON").action(async (opts) => {
|
|
21750
|
+
const chain = opts.chain.toLowerCase();
|
|
21751
|
+
if (!["solana", "base"].includes(chain)) {
|
|
21752
|
+
console.error(error(`Unsupported chain '${opts.chain}'. Use solana or base.`));
|
|
21753
|
+
process.exit(1);
|
|
21754
|
+
}
|
|
21755
|
+
let auth;
|
|
21756
|
+
try {
|
|
21757
|
+
auth = requireAuth();
|
|
21758
|
+
} catch (e) {
|
|
21759
|
+
console.error(error(e instanceof Error ? e.message : String(e)));
|
|
21760
|
+
process.exit(1);
|
|
21761
|
+
}
|
|
21762
|
+
const spinner = opts.json ? null : ora(`Creating ${chain} wallet...`).start();
|
|
21763
|
+
try {
|
|
21764
|
+
const wallet = await amikoWebFetch(auth, `/api/agents/${encodeURIComponent(auth.twinId)}/wallets`, { method: "POST", body: { chain, custodian: opts.custodian } });
|
|
21765
|
+
spinner?.stop();
|
|
21766
|
+
if (opts.json) {
|
|
21767
|
+
console.log(JSON.stringify(wallet, null, 2));
|
|
21768
|
+
return;
|
|
21769
|
+
}
|
|
21770
|
+
console.log(success(`${chain} wallet created`));
|
|
21771
|
+
console.log("");
|
|
21772
|
+
console.log(label("Chain", `${wallet.chain} (${wallet.chain_type})`));
|
|
21773
|
+
if (wallet.name)
|
|
21774
|
+
console.log(label("Name", wallet.name));
|
|
21775
|
+
console.log(label("Address", wallet.wallet_address));
|
|
21776
|
+
console.log(label("Custodian", wallet.custodian ?? "-"));
|
|
21777
|
+
console.log("");
|
|
21778
|
+
console.log(dim(`Tip: paid commands (swap, markets search, image, amazon, services) will now use this wallet by default.`));
|
|
21779
|
+
} catch (e) {
|
|
21780
|
+
spinner?.stop();
|
|
21781
|
+
const msg = e instanceof Error ? e.message : "Failed to create wallet";
|
|
21782
|
+
if (/409|already/i.test(msg)) {
|
|
21783
|
+
console.error(error(`An active ${chain} wallet already exists. Run \`amiko wallets list\` to see it.`));
|
|
21784
|
+
} else {
|
|
21785
|
+
console.error(error(msg));
|
|
21786
|
+
}
|
|
21787
|
+
process.exit(1);
|
|
21788
|
+
}
|
|
21789
|
+
});
|
|
21790
|
+
}
|
|
21251
21791
|
function registerWalletsListCommand(program2) {
|
|
21252
21792
|
program2.command("list").alias("ls").description("List wallets for the authenticated twin").option("--json", "Output raw JSON").action(async (opts) => {
|
|
21253
21793
|
let auth;
|
|
@@ -21302,26 +21842,37 @@ if (didOnboard)
|
|
|
21302
21842
|
process.exit(0);
|
|
21303
21843
|
var program2 = new Command;
|
|
21304
21844
|
program2.name("amiko").description("Amiko CLI — swap tokens, manage credits, bridge cross-chain, and call marketplace services").version(PKG_VERSION);
|
|
21305
|
-
var
|
|
21845
|
+
var markets = program2.command("markets").description("MPP marketplace — discover and call paid AMIKO services");
|
|
21306
21846
|
var wallets = program2.command("wallets").description("Manage twin wallets — list, swap, bridge");
|
|
21847
|
+
var twin = program2.command("twin").description("Update twin identity (name, description, visibility)");
|
|
21848
|
+
var docs = program2.command("docs").description("Manage twin RAG documents — list, upload, delete, presign");
|
|
21849
|
+
var voice = program2.command("voice").description("Manage twin voice — design, create, clone, reset");
|
|
21850
|
+
var avatar = program2.command("avatar").description("Manage twin avatar image");
|
|
21851
|
+
var friends = program2.command("friends").description("Manage friendships — list, requests, add, accept, remove, reports, matches");
|
|
21852
|
+
var users = program2.command("users").description("Search users and view public profiles");
|
|
21853
|
+
var post = program2.command("post").description("Create posts and comments on the feed");
|
|
21854
|
+
var composio = program2.command("composio").description("Manage Composio app connections");
|
|
21307
21855
|
registerCreditsCommand(program2);
|
|
21308
|
-
registerSearchCommand(
|
|
21309
|
-
registerImageCommand(
|
|
21310
|
-
registerAmazonCommand(
|
|
21856
|
+
registerSearchCommand(markets);
|
|
21857
|
+
registerImageCommand(markets);
|
|
21858
|
+
registerAmazonCommand(markets);
|
|
21859
|
+
registerWalletsCreateCommand(wallets);
|
|
21311
21860
|
registerWalletsListCommand(wallets);
|
|
21312
21861
|
registerWalletsBalanceCommand(wallets);
|
|
21313
21862
|
registerSwapCommand(wallets);
|
|
21314
21863
|
registerBridgeCommand(wallets);
|
|
21315
21864
|
registerConfigCommand(program2);
|
|
21316
|
-
registerServicesCommand(
|
|
21317
|
-
registerDiscoveryCommand(
|
|
21865
|
+
registerServicesCommand(markets);
|
|
21866
|
+
registerDiscoveryCommand(markets);
|
|
21318
21867
|
registerUpdateCommand(program2);
|
|
21319
21868
|
registerAccountsCommand(program2);
|
|
21320
|
-
registerTwinCommand(
|
|
21321
|
-
registerDocsCommand(
|
|
21322
|
-
registerVoiceCommand(
|
|
21323
|
-
registerAvatarCommand(
|
|
21324
|
-
registerFriendsCommand(
|
|
21869
|
+
registerTwinCommand(twin);
|
|
21870
|
+
registerDocsCommand(docs);
|
|
21871
|
+
registerVoiceCommand(voice);
|
|
21872
|
+
registerAvatarCommand(avatar);
|
|
21873
|
+
registerFriendsCommand(friends);
|
|
21874
|
+
registerUsersCommand(users);
|
|
21325
21875
|
registerFeedCommand(program2);
|
|
21326
|
-
|
|
21876
|
+
registerPostCommand(post);
|
|
21877
|
+
registerComposioCommand(composio);
|
|
21327
21878
|
program2.parse();
|