@tensorify.io/cli
Version:
Official CLI for Tensorify.io - Build, test, and deploy machine learning plugins
461 lines (433 loc) • 20.9 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.authService = void 0;
const chalk_1 = __importDefault(require("chalk"));
const session_storage_1 = require("./session-storage");
const url_generator_1 = require("../utils/url-generator");
const http_1 = require("http");
const url_1 = require("url");
const open_1 = __importDefault(require("open"));
class AuthService {
constructor() {
// Future: Initialize Clerk client when needed
}
async login(isDev = false) {
try {
console.log(chalk_1.default.yellow("🌐 Setting up authentication flow..."));
// Create a callback server to handle the redirect
const callbackServer = await this.createCallbackServer();
const { port, server } = callbackServer;
// Generate the callback URL
const callbackUrl = url_generator_1.urlGenerator.getCallbackUrl(isDev, port);
// Generate the sign-in URL with redirect
const signInUrl = url_generator_1.urlGenerator.getSignInUrl(isDev, callbackUrl);
console.log(chalk_1.default.blue("📋 Authentication URL:"), signInUrl);
console.log(chalk_1.default.yellow("🔗 If the browser doesn't open automatically, copy and paste the URL above."));
// Try to open the browser
try {
await (0, open_1.default)(signInUrl);
console.log(chalk_1.default.green("🌍 Opening browser for authentication..."));
}
catch (error) {
console.log(chalk_1.default.yellow("⚠️ Could not open browser automatically. Please open the URL manually."));
}
// Wait for the callback
return new Promise((resolve, reject) => {
const timeout = setTimeout(() => {
server.close();
reject(new Error("Authentication timeout. Please try again."));
}, 300000); // 5 minute timeout
server.on("request", async (req, res) => {
try {
const url = new url_1.URL(req.url, `http://localhost:${port}`);
if (url.pathname === "/callback") {
const sessionToken = url.searchParams.get("session");
if (sessionToken) {
// Store the session
await session_storage_1.sessionStorage.storeSession(sessionToken);
// Send success response
res.writeHead(200, { "Content-Type": "text/html" });
res.end(`
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Tensorify CLI - Authentication Success</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: linear-gradient(135deg, #0f0f23 0%, #1a1a2e 50%, #16213e 100%);
color: #ffffff;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
position: relative;
}
/* Animated background particles */
.particles {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
overflow: hidden;
z-index: 1;
}
.particle {
position: absolute;
width: 4px;
height: 4px;
background: linear-gradient(45deg, #8b5cf6, #06b6d4);
border-radius: 50%;
animation: float 6s ease-in-out infinite;
opacity: 0.7;
}
@keyframes float {
0%, 100% { transform: translateY(0px) rotate(0deg); opacity: 0.7; }
50% { transform: translateY(-20px) rotate(180deg); opacity: 1; }
}
.container {
text-align: center;
z-index: 10;
position: relative;
max-width: 500px;
padding: 40px;
background: rgba(255, 255, 255, 0.05);
backdrop-filter: blur(20px);
border-radius: 24px;
border: 1px solid rgba(255, 255, 255, 0.1);
box-shadow: 0 25px 50px rgba(0, 0, 0, 0.3);
animation: slideIn 0.6s ease-out;
}
@keyframes slideIn {
from {
opacity: 0;
transform: translateY(30px) scale(0.95);
}
to {
opacity: 1;
transform: translateY(0) scale(1);
}
}
.logo {
width: 80px;
height: 80px;
margin: 0 auto 30px;
background: linear-gradient(135deg, #8b5cf6 0%, #06b6d4 100%);
border-radius: 20px;
display: flex;
align-items: center;
justify-content: center;
font-size: 36px;
font-weight: bold;
color: white;
box-shadow: 0 10px 30px rgba(139, 92, 246, 0.3);
animation: pulse 2s ease-in-out infinite;
}
@keyframes pulse {
0%, 100% { transform: scale(1); box-shadow: 0 10px 30px rgba(139, 92, 246, 0.3); }
50% { transform: scale(1.05); box-shadow: 0 15px 40px rgba(139, 92, 246, 0.5); }
}
.success-icon {
font-size: 64px;
margin-bottom: 20px;
animation: checkmark 0.8s ease-in-out 0.3s both;
}
@keyframes checkmark {
0% { transform: scale(0) rotate(-45deg); opacity: 0; }
50% { transform: scale(1.2) rotate(-45deg); opacity: 1; }
100% { transform: scale(1) rotate(0deg); opacity: 1; }
}
h1 {
font-size: 32px;
font-weight: 700;
margin-bottom: 16px;
background: linear-gradient(135deg, #ffffff 0%, #e2e8f0 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
animation: fadeInUp 0.8s ease-out 0.2s both;
}
.subtitle {
font-size: 18px;
color: #94a3b8;
margin-bottom: 30px;
line-height: 1.6;
animation: fadeInUp 0.8s ease-out 0.4s both;
}
.status-badge {
display: inline-flex;
align-items: center;
gap: 8px;
background: rgba(34, 197, 94, 0.1);
color: #22c55e;
padding: 12px 24px;
border-radius: 50px;
font-weight: 600;
font-size: 16px;
border: 1px solid rgba(34, 197, 94, 0.2);
animation: fadeInUp 0.8s ease-out 0.6s both;
}
.closing-notice {
margin-top: 30px;
font-size: 14px;
color: #64748b;
animation: fadeInUp 0.8s ease-out 0.8s both;
}
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.progress-bar {
width: 100%;
height: 4px;
background: rgba(255, 255, 255, 0.1);
border-radius: 2px;
margin-top: 30px;
overflow: hidden;
}
.progress-fill {
height: 100%;
background: linear-gradient(90deg, #8b5cf6, #06b6d4);
border-radius: 2px;
width: 0%;
animation: fillProgress 3s ease-out forwards;
}
@keyframes fillProgress {
to { width: 100%; }
}
</style>
</head>
<body>
<div class="particles">
<div class="particle" style="left: 10%; animation-delay: 0s;"></div>
<div class="particle" style="left: 20%; animation-delay: 1s;"></div>
<div class="particle" style="left: 30%; animation-delay: 2s;"></div>
<div class="particle" style="left: 40%; animation-delay: 0.5s;"></div>
<div class="particle" style="left: 50%; animation-delay: 1.5s;"></div>
<div class="particle" style="left: 60%; animation-delay: 2.5s;"></div>
<div class="particle" style="left: 70%; animation-delay: 0.8s;"></div>
<div class="particle" style="left: 80%; animation-delay: 1.8s;"></div>
<div class="particle" style="left: 90%; animation-delay: 2.8s;"></div>
</div>
<div class="container">
<div class="logo">T</div>
<div class="success-icon">✅</div>
<h1>Authentication Successful!</h1>
<p class="subtitle">
Welcome to Tensorify CLI! Your authentication has been completed successfully.
</p>
<div class="status-badge">
<span>🔐</span>
<span>Securely Connected</span>
</div>
<p class="closing-notice">
This window will close automatically in a few seconds.<br>
You can now return to your terminal and start using Tensorify CLI.
</p>
<div class="progress-bar">
<div class="progress-fill"></div>
</div>
</div>
<script>
// Add more particles dynamically
function createParticle() {
const particle = document.createElement('div');
particle.className = 'particle';
particle.style.left = Math.random() * 100 + '%';
particle.style.top = Math.random() * 100 + '%';
particle.style.animationDelay = Math.random() * 3 + 's';
particle.style.animationDuration = (Math.random() * 3 + 3) + 's';
document.querySelector('.particles').appendChild(particle);
}
// Create additional particles
for (let i = 0; i < 15; i++) {
setTimeout(createParticle, i * 200);
}
// Close window after 3 seconds
setTimeout(() => {
try {
window.close();
} catch (e) {
// Fallback if window.close() fails
document.body.innerHTML = '<div style="text-align: center; padding: 50px; color: #64748b;">You can safely close this tab now.</div>';
}
}, 3000);
</script>
</body>
</html>
`);
clearTimeout(timeout);
server.close();
resolve();
}
else {
throw new Error("No session token received");
}
}
else {
res.writeHead(404);
res.end("Not found");
}
}
catch (error) {
console.error("Callback error:", error);
res.writeHead(500);
res.end("Internal server error");
clearTimeout(timeout);
server.close();
reject(error);
}
});
});
}
catch (error) {
throw new Error(`Authentication failed: ${error instanceof Error ? error.message : "Unknown error"}`);
}
}
async logout() {
try {
await session_storage_1.sessionStorage.clearSession();
}
catch (error) {
throw new Error(`Logout failed: ${error instanceof Error ? error.message : "Unknown error"}`);
}
}
async isAuthenticated() {
try {
const session = await session_storage_1.sessionStorage.getSession();
return !!session;
}
catch (error) {
return false;
}
}
async getSession() {
try {
return await session_storage_1.sessionStorage.getSession();
}
catch (error) {
return null;
}
}
async getUserProfile(isDev = false) {
try {
// Check for test token first in development
let sessionToken = null;
if (process.env.NODE_ENV === "development" &&
process.env.TENSORIFY_TEST_TOKEN) {
sessionToken = process.env.TENSORIFY_TEST_TOKEN;
}
else {
sessionToken = await this.getSession();
}
if (!sessionToken) {
throw new Error("No session token found. Please login first.");
}
const profileUrl = url_generator_1.urlGenerator.getUserProfileUrl(isDev);
// Use dynamic import for node-fetch to handle ESM compatibility
let fetchResponse;
try {
// Try to use built-in fetch first (Node.js 18+)
if (typeof globalThis.fetch !== "undefined") {
fetchResponse = await globalThis.fetch(profileUrl, {
method: "GET",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${sessionToken}`,
},
});
}
else {
// Fallback to node-fetch with dynamic import
const { default: fetch } = await Promise.resolve().then(() => __importStar(require("node-fetch")));
fetchResponse = await fetch(profileUrl, {
method: "GET",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${sessionToken}`,
},
});
}
}
catch (importError) {
throw new Error("Failed to load HTTP client. Please ensure you have internet connectivity.");
}
if (!fetchResponse.ok) {
throw new Error(`Failed to fetch user profile: ${fetchResponse.statusText}`);
}
const data = (await fetchResponse.json());
return data.data; // Return the user profile data
}
catch (error) {
throw new Error(`Failed to get user profile: ${error instanceof Error ? error.message : "Unknown error"}`);
}
}
async createCallbackServer() {
return new Promise((resolve, reject) => {
const server = (0, http_1.createServer)();
server.listen(0, "localhost", () => {
const address = server.address();
if (address && typeof address === "object") {
resolve({ port: address.port, server });
}
else {
reject(new Error("Failed to start callback server"));
}
});
server.on("error", reject);
});
}
}
exports.authService = new AuthService();
//# sourceMappingURL=auth-service.js.map