@heyamiko/amiko-cli 0.9.0-beta.7 → 0.9.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 +49 -27
- package/dist/index.js +749 -147
- package/package.json +2 -1
- package/skills/SKILL.md +126 -42
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";
|
|
@@ -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,18 +20677,6 @@ 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
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);
|
|
@@ -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
|
];
|
|
@@ -20936,20 +20983,20 @@ function registerFriendsCommand(program2) {
|
|
|
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));
|
|
@@ -20993,6 +21040,375 @@ function registerFriendsCommand(program2) {
|
|
|
20993
21040
|
process.exit(1);
|
|
20994
21041
|
}
|
|
20995
21042
|
});
|
|
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
|
+
});
|
|
20996
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}`,
|
|
@@ -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) => {
|
|
@@ -21250,6 +21745,109 @@ function registerWalletsBalanceCommand(program2) {
|
|
|
21250
21745
|
}
|
|
21251
21746
|
});
|
|
21252
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
|
+
}
|
|
21791
|
+
function registerWalletsTransferCommand(program2) {
|
|
21792
|
+
program2.command("transfer").description("Transfer tokens from a twin wallet to another address").requiredOption("--to <address>", "Recipient address").requiredOption("--amount <amount>", "Amount to send (e.g., 0.5)").requiredOption("--token <symbol>", "Token symbol (sol, usdc, usdt, amiko, eth) or mint/contract address").option("--wallet <address>", "Source wallet (defaults to your Solana wallet)").option("--chain <chain>", "Preferred chain when resolving default wallet (solana | base)", "solana").option("--yes", "Skip confirmation").option("--json", "Output raw JSON").action(async (opts) => {
|
|
21793
|
+
let auth;
|
|
21794
|
+
try {
|
|
21795
|
+
auth = requireAuth();
|
|
21796
|
+
} catch (e) {
|
|
21797
|
+
console.error(error(e instanceof Error ? e.message : String(e)));
|
|
21798
|
+
process.exit(1);
|
|
21799
|
+
}
|
|
21800
|
+
const chain = opts.chain.toLowerCase();
|
|
21801
|
+
if (chain !== "solana" && chain !== "base") {
|
|
21802
|
+
console.error(error(`Unsupported chain '${opts.chain}'. Use solana or base.`));
|
|
21803
|
+
process.exit(1);
|
|
21804
|
+
}
|
|
21805
|
+
let wallet;
|
|
21806
|
+
try {
|
|
21807
|
+
wallet = await resolveWalletAddress(auth, opts.wallet, chain);
|
|
21808
|
+
} catch (e) {
|
|
21809
|
+
console.error(error(e instanceof Error ? e.message : String(e)));
|
|
21810
|
+
process.exit(1);
|
|
21811
|
+
}
|
|
21812
|
+
if (!opts.json) {
|
|
21813
|
+
console.log(heading("Transfer"));
|
|
21814
|
+
console.log("");
|
|
21815
|
+
console.log(label("From", wallet));
|
|
21816
|
+
console.log(label("To", opts.to));
|
|
21817
|
+
console.log(label("Amount", `${opts.amount} ${opts.token.toUpperCase()}`));
|
|
21818
|
+
console.log("");
|
|
21819
|
+
}
|
|
21820
|
+
await requireApproval({
|
|
21821
|
+
cost: `${chain} gas`,
|
|
21822
|
+
summary: `transfer ${opts.amount} ${opts.token.toUpperCase()} from ${truncAddr(wallet)} to ${truncAddr(opts.to)}`,
|
|
21823
|
+
yes: opts.yes,
|
|
21824
|
+
commandExample: `amiko wallets transfer --to ${opts.to} --amount ${opts.amount} --token ${opts.token}`
|
|
21825
|
+
});
|
|
21826
|
+
const spinner = opts.json ? null : ora("Sending transfer...").start();
|
|
21827
|
+
try {
|
|
21828
|
+
const data = await amikoWebFetch(auth, `/api/agents/${encodeURIComponent(auth.twinId)}/wallets/${encodeURIComponent(wallet)}/transfer`, {
|
|
21829
|
+
method: "POST",
|
|
21830
|
+
body: { to: opts.to, amount: opts.amount, token: opts.token }
|
|
21831
|
+
});
|
|
21832
|
+
spinner?.stop();
|
|
21833
|
+
if (opts.json) {
|
|
21834
|
+
console.log(JSON.stringify(data, null, 2));
|
|
21835
|
+
return;
|
|
21836
|
+
}
|
|
21837
|
+
console.log(success("Transfer submitted"));
|
|
21838
|
+
console.log("");
|
|
21839
|
+
console.log(label("Status", data.crossmint.status));
|
|
21840
|
+
if (data.txHash)
|
|
21841
|
+
console.log(label("Tx", data.txHash));
|
|
21842
|
+
if (data.transaction?.id)
|
|
21843
|
+
console.log(label("Transaction", data.transaction.id));
|
|
21844
|
+
} catch (e) {
|
|
21845
|
+
spinner?.stop();
|
|
21846
|
+
console.error(error(e instanceof Error ? e.message : "Failed to transfer"));
|
|
21847
|
+
process.exit(1);
|
|
21848
|
+
}
|
|
21849
|
+
});
|
|
21850
|
+
}
|
|
21253
21851
|
function registerWalletsListCommand(program2) {
|
|
21254
21852
|
program2.command("list").alias("ls").description("List wallets for the authenticated twin").option("--json", "Output raw JSON").action(async (opts) => {
|
|
21255
21853
|
let auth;
|
|
@@ -21304,26 +21902,29 @@ if (didOnboard)
|
|
|
21304
21902
|
process.exit(0);
|
|
21305
21903
|
var program2 = new Command;
|
|
21306
21904
|
program2.name("amiko").description("Amiko CLI — swap tokens, manage credits, bridge cross-chain, and call marketplace services").version(PKG_VERSION);
|
|
21307
|
-
var
|
|
21905
|
+
var markets = program2.command("markets").description("MPP marketplace — discover and call paid AMIKO services");
|
|
21308
21906
|
var wallets = program2.command("wallets").description("Manage twin wallets — list, swap, bridge");
|
|
21309
21907
|
var twin = program2.command("twin").description("Update twin identity (name, description, visibility)");
|
|
21310
21908
|
var docs = program2.command("docs").description("Manage twin RAG documents — list, upload, delete, presign");
|
|
21311
21909
|
var voice = program2.command("voice").description("Manage twin voice — design, create, clone, reset");
|
|
21312
21910
|
var avatar = program2.command("avatar").description("Manage twin avatar image");
|
|
21313
|
-
var friends = program2.command("friends").description("Manage friendships — list, add, accept, remove");
|
|
21911
|
+
var friends = program2.command("friends").description("Manage friendships — list, requests, add, accept, remove, reports, matches");
|
|
21912
|
+
var users = program2.command("users").description("Search users and view public profiles");
|
|
21314
21913
|
var post = program2.command("post").description("Create posts and comments on the feed");
|
|
21315
21914
|
var composio = program2.command("composio").description("Manage Composio app connections");
|
|
21316
21915
|
registerCreditsCommand(program2);
|
|
21317
|
-
registerSearchCommand(
|
|
21318
|
-
registerImageCommand(
|
|
21319
|
-
registerAmazonCommand(
|
|
21916
|
+
registerSearchCommand(markets);
|
|
21917
|
+
registerImageCommand(markets);
|
|
21918
|
+
registerAmazonCommand(markets);
|
|
21919
|
+
registerWalletsCreateCommand(wallets);
|
|
21320
21920
|
registerWalletsListCommand(wallets);
|
|
21321
21921
|
registerWalletsBalanceCommand(wallets);
|
|
21922
|
+
registerWalletsTransferCommand(wallets);
|
|
21322
21923
|
registerSwapCommand(wallets);
|
|
21323
21924
|
registerBridgeCommand(wallets);
|
|
21324
21925
|
registerConfigCommand(program2);
|
|
21325
|
-
registerServicesCommand(
|
|
21326
|
-
registerDiscoveryCommand(
|
|
21926
|
+
registerServicesCommand(markets);
|
|
21927
|
+
registerDiscoveryCommand(markets);
|
|
21327
21928
|
registerUpdateCommand(program2);
|
|
21328
21929
|
registerAccountsCommand(program2);
|
|
21329
21930
|
registerTwinCommand(twin);
|
|
@@ -21331,6 +21932,7 @@ registerDocsCommand(docs);
|
|
|
21331
21932
|
registerVoiceCommand(voice);
|
|
21332
21933
|
registerAvatarCommand(avatar);
|
|
21333
21934
|
registerFriendsCommand(friends);
|
|
21935
|
+
registerUsersCommand(users);
|
|
21334
21936
|
registerFeedCommand(program2);
|
|
21335
21937
|
registerPostCommand(post);
|
|
21336
21938
|
registerComposioCommand(composio);
|