Initial commit

This commit is contained in:
decolua
2026-01-05 09:58:59 +07:00
commit 3857598de4
159 changed files with 14537 additions and 0 deletions

View File

@@ -0,0 +1,63 @@
import figlet from "figlet";
import gradient from "gradient-string";
import chalkAnimation from "chalk-animation";
/**
* Display banner
*/
export function showBanner() {
const banner = figlet.textSync("LLM Proxy", {
font: "ANSI Shadow",
horizontalLayout: "default",
verticalLayout: "default",
});
console.log("\n" + gradient.pastel.multiline(banner));
console.log(gradient.cristal(" 🚀 OAuth CLI for AI Providers\n"));
}
/**
* Display simple banner (no animation)
*/
export function showSimpleBanner() {
const banner = figlet.textSync("EP CLI", {
font: "Standard",
horizontalLayout: "default",
});
console.log(gradient.pastel.multiline(banner));
console.log(gradient.cristal(" OAuth CLI for AI Providers\n"));
}
/**
* Display success animation
*/
export async function showSuccess(message) {
return new Promise((resolve) => {
const animation = chalkAnimation.rainbow(`\n✨ ${message}\n`);
setTimeout(() => {
animation.stop();
resolve();
}, 1000);
});
}
/**
* Display loading animation
*/
export function showLoading(text) {
const frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
let i = 0;
const interval = setInterval(() => {
process.stdout.write(`\r${frames[i]} ${text}`);
i = (i + 1) % frames.length;
}, 80);
return {
stop: () => {
clearInterval(interval);
process.stdout.write("\r");
},
};
}

View File

@@ -0,0 +1,38 @@
import crypto from "crypto";
/**
* Generate PKCE code verifier (43-128 characters)
*/
export function generateCodeVerifier() {
return crypto.randomBytes(32).toString("base64url");
}
/**
* Generate PKCE code challenge from verifier (S256 method)
*/
export function generateCodeChallenge(verifier) {
return crypto.createHash("sha256").update(verifier).digest("base64url");
}
/**
* Generate random state for CSRF protection
*/
export function generateState() {
return crypto.randomBytes(32).toString("base64url");
}
/**
* Generate complete PKCE pair
*/
export function generatePKCE() {
const codeVerifier = generateCodeVerifier();
const codeChallenge = generateCodeChallenge(codeVerifier);
const state = generateState();
return {
codeVerifier,
codeChallenge,
state,
};
}

View File

@@ -0,0 +1,116 @@
import http from "http";
import { URL } from "url";
/**
* Start a local HTTP server to receive OAuth callback
* @param {Function} onCallback - Called with query params when callback received
* @param {number} fixedPort - Optional fixed port number (default: random)
* @returns {Promise<{server: http.Server, port: number, close: Function}>}
*/
export function startLocalServer(onCallback, fixedPort = null) {
return new Promise((resolve, reject) => {
const server = http.createServer((req, res) => {
const url = new URL(req.url, `http://localhost`);
if (url.pathname === "/callback" || url.pathname === "/auth/callback") {
const params = Object.fromEntries(url.searchParams);
// Send success response to browser with auto-close attempt
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
res.end(`<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Authentication Successful</title>
<style>
body { font-family: system-ui; display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; background: #f5f5f5; }
.container { text-align: center; padding: 2rem; background: white; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }
.success { color: #22c55e; font-size: 3rem; }
h1 { margin: 1rem 0; }
p { color: #666; }
#countdown { font-weight: bold; }
</style>
</head>
<body>
<div class="container">
<div class="success">&#10003;</div>
<h1>Authentication Successful</h1>
<p id="message">Closing in <span id="countdown">3</span> seconds...</p>
</div>
<script>
let count = 3;
const countdown = document.getElementById("countdown");
const message = document.getElementById("message");
const timer = setInterval(() => {
count--;
countdown.textContent = count;
if (count <= 0) {
clearInterval(timer);
window.close();
setTimeout(() => {
message.textContent = "Please close this tab manually.";
}, 500);
}
}, 1000);
</script>
</body>
</html>`);
// Call callback with params
onCallback(params);
} else {
res.writeHead(404);
res.end("Not found");
}
});
// Listen on fixed port or find available port
const portToUse = fixedPort || 0;
server.listen(portToUse, "127.0.0.1", () => {
const { port } = server.address();
resolve({
server,
port,
close: () => server.close(),
});
});
server.on("error", (err) => {
if (err.code === "EADDRINUSE" && fixedPort) {
reject(new Error(`Port ${fixedPort} is already in use. Please close other applications using this port.`));
} else {
reject(err);
}
});
});
}
/**
* Wait for callback with timeout
* @param {number} timeoutMs - Timeout in milliseconds
* @returns {Promise<Object>} - Callback params
*/
export function waitForCallback(timeoutMs = 300000) {
return new Promise((resolve, reject) => {
let resolved = false;
const timeout = setTimeout(() => {
if (!resolved) {
resolved = true;
reject(new Error("Authentication timeout"));
}
}, timeoutMs);
const onCallback = (params) => {
if (!resolved) {
resolved = true;
clearTimeout(timeout);
resolve(params);
}
};
// Return the callback function
resolve.__onCallback = onCallback;
});
}

48
src/lib/oauth/utils/ui.js Normal file
View File

@@ -0,0 +1,48 @@
import chalk from "chalk";
import ora from "ora";
/**
* UI Helper Functions
*/
export function success(message) {
console.log(chalk.green(`\n✓ ${message}\n`));
}
export function error(message) {
console.log(chalk.red(`\n✗ ${message}\n`));
}
export function info(message) {
console.log(chalk.blue(`\n${message}\n`));
}
export function warn(message) {
console.log(chalk.yellow(`\n⚠ ${message}\n`));
}
export function gray(message) {
console.log(chalk.gray(message));
}
export function spinner(text) {
return ora(text);
}
export function printSection(title) {
console.log(chalk.blue(`\n${title}\n`));
}
export function printKeyValue(key, value, isSuccess = false) {
const color = isSuccess ? chalk.green : chalk.gray;
console.log(color(` ${key}: ${value}`));
}
export function printList(items, isSuccess = false) {
const symbol = isSuccess ? "✓" : "✗";
const color = isSuccess ? chalk.green : chalk.gray;
items.forEach((item) => {
console.log(color(` ${symbol} ${item}`));
});
}