cardcli-demo / web /src /lib /server /cardcli.ts
CardCLI Bot
Deploy CardCLI Hugging Face Space
d2948d0
import { execFile } from "node:child_process";
import { promisify } from "node:util";
import { mkdirSync } from "node:fs";
import path from "node:path";
import crypto from "node:crypto";
import type { NextRequest } from "next/server";
import { NextResponse } from "next/server";
const execFileAsync = promisify(execFile);
const SESSION_COOKIE = "cardcli_demo_session";
const DEMO_HOME_ROOT = process.env.CARDCLI_DEMO_HOME_ROOT ?? "/tmp/cardcli-demo";
const SHARED_DEMO_SESSION = (process.env.CARDCLI_DEMO_SHARED_SESSION ?? "true") === "true";
function cardCliBinPath() {
return (
process.env.CARDCLI_BIN ??
path.resolve(process.cwd(), "..", "target", "release", process.platform === "win32" ? "card-cli.exe" : "card-cli")
);
}
function sessionIdFromRequest(request: NextRequest) {
if (SHARED_DEMO_SESSION) {
return "shared-demo";
}
return request.cookies.get(SESSION_COOKIE)?.value ?? crypto.randomUUID();
}
function cardCliHome(sessionId: string) {
const dir = path.join(DEMO_HOME_ROOT, "sessions", sessionId);
mkdirSync(dir, { recursive: true });
return dir;
}
function cardCliEnv(sessionId: string): NodeJS.ProcessEnv {
return {
...process.env,
CARDCLI_HOME: cardCliHome(sessionId),
CARDCLI_SOLANA_PRIVATE_KEY_BASE58:
process.env.CARDCLI_DEMO_SOLANA_PRIVATE_KEY ?? process.env.CARDCLI_SOLANA_PRIVATE_KEY_BASE58 ?? "",
CARDCLI_FEE_PROGRAM_ID:
process.env.CARDCLI_DEMO_FEE_PROGRAM_ID ?? process.env.CARDCLI_FEE_PROGRAM_ID ?? "EpB6hUZUf1vvvTVAYvEN57pjUWfYswaAuKGGQDHP5iH",
CARDCLI_ONCHAIN_FEE_COLLECTION_ENABLED:
process.env.CARDCLI_ONCHAIN_FEE_COLLECTION_ENABLED ?? "true",
CARDCLI_FEE_FIXED_CENTS: process.env.CARDCLI_FEE_FIXED_CENTS ?? "10",
CARDCLI_FEE_VARIABLE_BPS: process.env.CARDCLI_FEE_VARIABLE_BPS ?? "20",
CARDCLI_RPC_URL: process.env.CARDCLI_RPC_URL ?? "https://api.devnet.solana.com",
CARDCLI_COINBASE_BASE_URL: process.env.CARDCLI_COINBASE_BASE_URL ?? "https://api.coinbase.com",
};
}
export async function runCardCli(
request: NextRequest,
args: string[],
): Promise<{ sessionId: string; payload: unknown; stderr?: string }> {
const sessionId = sessionIdFromRequest(request);
const env = cardCliEnv(sessionId);
const bin = cardCliBinPath();
try {
const { stdout, stderr } = await execFileAsync(bin, [...args, "--format", "json"], {
env,
maxBuffer: 10 * 1024 * 1024,
});
return {
sessionId,
payload: JSON.parse(stdout),
stderr,
};
} catch (error) {
const err = error as { stdout?: string; stderr?: string; message?: string };
const raw = err.stderr || err.stdout || JSON.stringify({ ok: false, error: { message: err.message } });
try {
return {
sessionId,
payload: JSON.parse(raw),
stderr: err.stderr,
};
} catch {
return {
sessionId,
payload: {
ok: false,
error: {
code: "WEB_EXEC_ERROR",
message: err.message ?? "Failed to run card-cli",
raw,
},
},
stderr: err.stderr,
};
}
}
}
export function jsonWithSession(sessionId: string, payload: unknown, init?: ResponseInit) {
const response = NextResponse.json(payload, init);
if (!SHARED_DEMO_SESSION) {
response.cookies.set(SESSION_COOKIE, sessionId, {
httpOnly: true,
sameSite: "lax",
path: "/",
maxAge: 60 * 60 * 24,
});
}
return response;
}