alnilam-cli
Version:
Git-native AI career coach that converts multi-year ambitions into weekly execution
634 lines (633 loc) • 23.7 kB
JavaScript
;
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;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.getCurrentAuthToken = exports.authCommand = void 0;
const commander_1 = require("commander");
const readline = __importStar(require("readline"));
const readModule = __importStar(require("read"));
const auth_js_1 = require("../lib/auth.js");
// Constants
const MIN_PASSWORD_LENGTH = 6;
const authCommand = new commander_1.Command('auth');
exports.authCommand = authCommand;
authCommand.description('Authentication commands - login with email/password or service role key');
// Create auth manager instance
const authManager = new auth_js_1.AuthManager();
// Helper function to prompt for password securely (hidden input)
async function promptForPassword(message = 'Enter your password: ') {
try {
const password = await readModule.read({
prompt: message,
silent: true,
replace: '*'
});
return password.trim();
}
catch (error) {
throw new Error(`Failed to read password: ${error}`);
}
}
// Helper function to prompt for email
async function promptForEmail() {
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
return new Promise((resolve) => {
rl.question('Enter your email address: ', (answer) => {
rl.close();
resolve(answer.trim());
});
});
}
// Helper function to validate email format
function isValidEmail(email) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
}
// Register command
const registerCommand = new commander_1.Command('register');
registerCommand
.description('Register a new account with email and password')
.option('--email <email>', 'Email address for registration')
.option('--password [password]', 'Password for registration (will prompt if not provided)')
.option('--json', 'Output as JSON')
.action(async (options) => {
try {
let email = options.email;
let password = options.password;
// Get email if not provided
if (!email) {
email = await promptForEmail();
}
// Validate email format
if (!isValidEmail(email)) {
if (options.json) {
console.log(JSON.stringify({ error: 'Invalid email format' }));
}
else {
console.error('❌ Invalid email format');
}
process.exit(1);
}
// Get password if not provided
if (password === undefined) {
password = await promptForPassword(`Enter your password (min ${MIN_PASSWORD_LENGTH} characters): `);
}
else if (password === true) {
password = await promptForPassword(`Enter your password (min ${MIN_PASSWORD_LENGTH} characters): `);
}
// Validate password length
if (password.length < MIN_PASSWORD_LENGTH) {
if (options.json) {
console.log(JSON.stringify({ error: `Password must be at least ${MIN_PASSWORD_LENGTH} characters long` }));
}
else {
console.error(`❌ Password must be at least ${MIN_PASSWORD_LENGTH} characters long`);
}
process.exit(1);
}
// Register with Supabase
const { data, error } = await authManager.register(email, password);
if (error) {
if (options.json) {
console.log(JSON.stringify({ error: error.message }));
}
else {
console.error(`❌ Registration failed: ${error.message}`);
if (error.message.includes('already registered')) {
console.log('💡 User already exists. Try: alnl auth login');
}
}
process.exit(1);
}
if (!data.user) {
if (options.json) {
console.log(JSON.stringify({ error: 'Registration failed - no user data returned' }));
}
else {
console.error('❌ Registration failed - no user data returned');
}
process.exit(1);
}
// Success - Supabase automatically handles session storage
if (options.json) {
console.log(JSON.stringify({
success: true,
message: data.session ? 'Registration successful and logged in' : 'Registration successful - please check email for confirmation',
user: {
id: data.user.id,
email: data.user.email
},
session: !!data.session
}));
}
else {
console.log('✅ Registration successful!');
if (data.session) {
console.log(`👤 Logged in as: ${data.user.email}`);
console.log('🚀 You can now use all Alnilam commands');
}
else {
console.log('📧 Please check your email for a confirmation link');
console.log('💡 After confirming, run: alnl auth login');
}
}
}
catch (error) {
if (options.json) {
console.log(JSON.stringify({ error: error.message }));
}
else {
console.error(`❌ Registration error: ${error.message}`);
}
process.exit(1);
}
});
// Login command
const loginCommand = new commander_1.Command('login');
loginCommand
.description('Authenticate with Alnilam using email/password or service role key')
.option('--email <email>', 'Email address for authentication')
.option('--password [password]', 'Password for authentication (will prompt if not provided)')
.option('--service-role-key <key>', 'Service role key for automation/CI')
.option('--json', 'Output as JSON')
.action(async (options) => {
try {
// Service role key authentication (for automation/CI)
if (options.serviceRoleKey) {
const { success } = await authManager.setServiceRoleSession(options.serviceRoleKey);
if (options.json) {
console.log(JSON.stringify({
success: true,
message: 'Service role authentication successful',
auth_type: 'service_role'
}));
}
else {
console.log('✅ Service role authentication successful!');
console.log('🔑 Using service role permissions');
}
return;
}
// Email/password authentication
let email = options.email;
let password = options.password;
// Get email if not provided
if (!email) {
email = await promptForEmail();
}
// Validate email format
if (!isValidEmail(email)) {
if (options.json) {
console.log(JSON.stringify({ error: 'Invalid email format' }));
}
else {
console.error('❌ Invalid email format');
}
process.exit(1);
}
// Get password if not provided
if (password === undefined) {
password = await promptForPassword();
}
else if (password === true) {
password = await promptForPassword();
}
// Login with Supabase
const { data, error } = await authManager.login(email, password);
if (error || !data.session) {
if (options.json) {
console.log(JSON.stringify({
error: error?.message || 'Authentication failed',
suggestion: 'Check your email and password, or try registering: alnl auth register'
}));
}
else {
console.error(`❌ Authentication failed: ${error?.message || 'Invalid credentials'}`);
console.log('💡 Try registering first with: alnl auth register');
console.log('💡 Or check your email and password');
}
process.exit(1);
}
// Success - Supabase automatically stores the session
if (options.json) {
console.log(JSON.stringify({
success: true,
message: 'Authentication successful',
user: {
id: data.user.id,
email: data.user.email
},
expires_at: data.session.expires_at
}));
}
else {
console.log('✅ Authentication successful!');
console.log(`👤 Logged in as: ${data.user.email}`);
if (data.session.expires_at) {
const expiresDate = new Date(data.session.expires_at * 1000);
console.log(`⏰ Session expires: ${expiresDate.toLocaleString()}`);
}
}
}
catch (error) {
if (options.json) {
console.log(JSON.stringify({ error: error.message }));
}
else {
console.error(`❌ Login error: ${error.message}`);
}
process.exit(1);
}
});
// Verify command (check current authentication status)
const verifyCommand = new commander_1.Command('verify');
verifyCommand
.description('Verify current authentication status')
.option('--json', 'Output as JSON')
.action(async (options) => {
try {
const { user, error } = await authManager.getCurrentUser();
if (error || !user) {
if (options.json) {
console.log(JSON.stringify({
authenticated: false,
error: error?.message || 'Not authenticated',
suggestion: 'Run "alnl auth login" or "alnl auth register" to authenticate'
}));
}
else {
console.log('❌ Not authenticated');
console.log('💡 Run "alnl auth login" or "alnl auth register" to authenticate');
}
process.exit(1);
}
// Get session info
const { session } = await authManager.getSession();
if (options.json) {
console.log(JSON.stringify({
authenticated: true,
user: {
id: user.id,
email: user.email
},
expires_at: session?.expires_at,
expires_in: session?.expires_at ? session.expires_at - Math.floor(Date.now() / 1000) : null
}));
}
else {
console.log('✅ Authentication verified');
console.log(`👤 Logged in as: ${user.email}`);
if (session?.expires_at) {
const expiresDate = new Date(session.expires_at * 1000);
const expiresIn = session.expires_at - Math.floor(Date.now() / 1000);
const expiresInHours = Math.floor(expiresIn / 3600);
console.log(`⏰ Token expires: ${expiresDate.toLocaleString()} (in ${expiresInHours} hours)`);
}
}
}
catch (error) {
if (options.json) {
console.log(JSON.stringify({
authenticated: false,
error: error.message
}));
}
else {
console.error(`❌ Verification error: ${error.message}`);
}
process.exit(1);
}
});
// Status command (alias for verify)
const statusCommand = new commander_1.Command('status');
statusCommand
.description('Check authentication status')
.option('--json', 'Output as JSON')
.action(async (options) => {
// Reuse verify command logic
await verifyCommand.parseAsync(['verify', ...(options.json ? ['--json'] : [])], { from: 'user' });
});
// Refresh command
const refreshCommand = new commander_1.Command('refresh');
refreshCommand
.description('Refresh authentication session')
.option('--json', 'Output as JSON')
.action(async (options) => {
try {
const { data, error } = await authManager.refreshSession();
if (error || !data.session) {
if (options.json) {
console.log(JSON.stringify({
error: error?.message || 'Session refresh failed',
suggestion: 'Run "alnl auth login" to re-authenticate'
}));
}
else {
console.log('❌ Session refresh failed');
console.log('💡 Run "alnl auth login" to re-authenticate');
if (error?.message) {
console.log(`🔍 Details: ${error.message}`);
}
}
process.exit(1);
}
if (options.json) {
console.log(JSON.stringify({
success: true,
message: 'Session refreshed successfully',
expires_at: data.session.expires_at
}));
}
else {
console.log('✅ Session refreshed successfully');
if (data.session.expires_at) {
const expiresDate = new Date(data.session.expires_at * 1000);
console.log(`⏰ New expiration: ${expiresDate.toLocaleString()}`);
}
}
}
catch (error) {
if (options.json) {
console.log(JSON.stringify({
error: error.message,
suggestion: 'Run "alnl auth login" to re-authenticate'
}));
}
else {
console.error(`❌ Refresh error: ${error.message}`);
console.log('💡 Run "alnl auth login" to re-authenticate');
}
process.exit(1);
}
});
// Update password command
const updatePasswordCommand = new commander_1.Command('update-password');
updatePasswordCommand
.description('Update password for currently authenticated user')
.option('--password [password]', 'New password (will prompt if not provided)')
.option('--json', 'Output as JSON')
.action(async (options) => {
try {
// Check if user is authenticated
const { user } = await authManager.getCurrentUser();
if (!user) {
if (options.json) {
console.log(JSON.stringify({ error: 'Not authenticated' }));
}
else {
console.error('❌ Not authenticated');
console.log('💡 Run "alnl auth login" to authenticate first');
}
process.exit(1);
}
let password = options.password;
// Get password if not provided
if (password === undefined) {
password = await promptForPassword('Enter your new password (min 6 characters): ');
}
else if (password === true) {
password = await promptForPassword('Enter your new password (min 6 characters): ');
}
// Validate password length
if (password.length < 6) {
if (options.json) {
console.log(JSON.stringify({ error: 'Password must be at least 6 characters long' }));
}
else {
console.error('❌ Password must be at least 6 characters long');
}
process.exit(1);
}
// Update password
const { data, error } = await authManager.updatePassword(password);
if (error) {
if (options.json) {
console.log(JSON.stringify({ error: error.message }));
}
else {
console.error(`❌ Password update failed: ${error.message}`);
}
process.exit(1);
}
if (options.json) {
console.log(JSON.stringify({
success: true,
message: 'Password updated successfully',
user: {
id: data.user?.id,
email: data.user?.email
}
}));
}
else {
console.log('✅ Password updated successfully!');
console.log(`👤 Account: ${data.user?.email}`);
console.log('🔒 Your new password is now active');
console.log('💡 You can now login with: alnl auth login');
}
}
catch (error) {
if (options.json) {
console.log(JSON.stringify({ error: error.message }));
}
else {
console.error(`❌ Update password error: ${error.message}`);
}
process.exit(1);
}
});
// Set session command (for handling password reset callbacks)
const setSessionCommand = new commander_1.Command('set-session');
setSessionCommand
.description('Set authentication session from tokens (used after password reset)')
.requiredOption('--access-token <token>', 'Access token from password reset callback')
.requiredOption('--refresh-token <token>', 'Refresh token from password reset callback')
.option('--json', 'Output as JSON')
.action(async (options) => {
try {
// Set session from tokens
const { data, error } = await authManager.setSession(options.accessToken, options.refreshToken);
if (error) {
if (options.json) {
console.log(JSON.stringify({ error: error.message }));
}
else {
console.error(`❌ Failed to set session: ${error.message}`);
}
process.exit(1);
}
if (!data.user) {
if (options.json) {
console.log(JSON.stringify({ error: 'No user data in session' }));
}
else {
console.error('❌ Failed to set session: No user data');
}
process.exit(1);
}
if (options.json) {
console.log(JSON.stringify({
success: true,
message: 'Session set successfully',
user: {
id: data.user.id,
email: data.user.email
}
}));
}
else {
console.log('✅ Session set successfully!');
console.log(`👤 Logged in as: ${data.user.email}`);
console.log('🔄 You can now set a new password with: alnl auth update-password');
}
}
catch (error) {
if (options.json) {
console.log(JSON.stringify({ error: error.message }));
}
else {
console.error(`❌ Set session error: ${error.message}`);
}
process.exit(1);
}
});
// Reset password command
const resetPasswordCommand = new commander_1.Command('reset-password');
resetPasswordCommand
.description('Send a password reset email')
.option('--email <email>', 'Email address to send reset link to')
.option('--json', 'Output as JSON')
.action(async (options) => {
try {
let email = options.email;
// Get email if not provided
if (!email) {
email = await promptForEmail();
}
// Validate email format
if (!isValidEmail(email)) {
if (options.json) {
console.log(JSON.stringify({ error: 'Invalid email format' }));
}
else {
console.error('❌ Invalid email format');
}
process.exit(1);
}
// Send password reset email with CLI-friendly redirect
const { data, error } = await authManager.resetPassword(email);
if (error) {
if (options.json) {
console.log(JSON.stringify({ error: error.message }));
}
else {
console.error(`❌ Password reset failed: ${error.message}`);
}
process.exit(1);
}
if (options.json) {
console.log(JSON.stringify({
success: true,
message: 'Password reset email sent',
email: email
}));
}
else {
console.log('✅ Password reset email sent!');
console.log(`📧 Check your email at: ${email}`);
console.log('🔗 Click the reset link to set a new password');
console.log('💡 After resetting, use: alnl auth login');
}
}
catch (error) {
if (options.json) {
console.log(JSON.stringify({ error: error.message }));
}
else {
console.error(`❌ Reset password error: ${error.message}`);
}
process.exit(1);
}
});
// Logout command
const logoutCommand = new commander_1.Command('logout');
logoutCommand
.description('Log out and clear stored credentials')
.option('--json', 'Output as JSON')
.action(async (options) => {
try {
const { error } = await authManager.logout();
if (error) {
if (options.json) {
console.log(JSON.stringify({ error: error.message }));
}
else {
console.error(`❌ Logout error: ${error.message}`);
}
process.exit(1);
}
if (options.json) {
console.log(JSON.stringify({ success: true, message: 'Logged out successfully' }));
}
else {
console.log('✅ Logged out successfully');
console.log('🔐 All stored credentials have been cleared');
}
}
catch (error) {
if (options.json) {
console.log(JSON.stringify({ error: error.message }));
}
else {
console.error(`❌ Logout error: ${error.message}`);
}
process.exit(1);
}
});
// Add subcommands
authCommand.addCommand(loginCommand);
authCommand.addCommand(registerCommand);
authCommand.addCommand(verifyCommand);
authCommand.addCommand(statusCommand);
authCommand.addCommand(refreshCommand);
authCommand.addCommand(resetPasswordCommand);
authCommand.addCommand(setSessionCommand);
authCommand.addCommand(updatePasswordCommand);
authCommand.addCommand(logoutCommand);
// Export the getCurrentAuthToken function for backward compatibility
var auth_js_2 = require("../lib/auth.js");
Object.defineProperty(exports, "getCurrentAuthToken", { enumerable: true, get: function () { return auth_js_2.getCurrentAuthToken; } });