UNPKG

@stackmemoryai/stackmemory

Version:

Project-scoped memory for AI coding tools. Durable context across sessions with MCP integration, frames, smart retrieval, Claude Code skills, and automatic hooks.

173 lines (172 loc) 6.63 kB
import { fileURLToPath as __fileURLToPath } from 'url'; import { dirname as __pathDirname } from 'path'; const __filename = __fileURLToPath(import.meta.url); const __dirname = __pathDirname(__filename); import inquirer from "inquirer"; import chalk from "chalk"; import { homedir } from "os"; import { join } from "path"; import { existsSync, mkdirSync, writeFileSync, readFileSync } from "fs"; import open from "open"; import { IntegrationError, ErrorCode } from "../../core/errors/index.js"; function registerLoginCommand(program) { program.command("login").description("Login to hosted StackMemory service").option("--api-url <url>", "Custom API URL", "https://api.stackmemory.ai").option("--email <email>", "Email address for login").option("--password <password>", "Password (not recommended in CLI)").action(async (options) => { const cfgDir = join(homedir(), ".stackmemory"); if (!existsSync(cfgDir)) mkdirSync(cfgDir, { recursive: true }); console.log(chalk.cyan("\u{1F510} StackMemory Hosted Service Login\n")); const credentials = await inquirer.prompt([ { type: "input", name: "email", message: "Email:", default: options.email, validate: (input) => { const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; return emailRegex.test(input) ? true : "Please enter a valid email"; } }, { type: "password", name: "password", message: "Password:", default: options.password, mask: "*", validate: (input) => input.length >= 6 ? true : "Password must be at least 6 characters" } ]); console.log(chalk.gray("\nAuthenticating with StackMemory API...")); try { const apiUrl = options.apiUrl || process.env.STACKMEMORY_API_URL || "https://api.stackmemory.ai"; const response = await fetch(`${apiUrl}/auth/login`, { method: "POST", headers: { "Content-Type": "application/json", "User-Agent": "StackMemory-CLI/0.3.19" }, body: JSON.stringify({ email: credentials.email, password: credentials.password }) }); const data = await response.json(); if (!response.ok || !data.success) { if (response.status === 404) { console.log( chalk.yellow("\n\u26A0\uFE0F Hosted API not available. Would you like to:") ); const { choice } = await inquirer.prompt([ { type: "list", name: "choice", message: "Select an option:", choices: [ { name: "Open signup page in browser", value: "signup" }, { name: "Configure database URL manually", value: "manual" }, { name: "Use local database", value: "local" }, { name: "Cancel", value: "cancel" } ] } ]); if (choice === "signup") { await open("https://stackmemory.ai/signup"); console.log(chalk.cyan("Opening signup page in browser...")); return; } else if (choice === "manual") { const { databaseUrl } = await inquirer.prompt([ { type: "password", name: "databaseUrl", message: "Enter your DATABASE_URL (postgres://...):", validate: (input) => input.startsWith("postgres://") || input.startsWith("postgresql://") ? true : "Must start with postgres:// or postgresql://" } ]); const cfgPath2 = join(cfgDir, "config.json"); let cfg2 = {}; try { if (existsSync(cfgPath2)) cfg2 = JSON.parse(readFileSync(cfgPath2, "utf-8")); } catch { } cfg2.database = { mode: "hosted", url: databaseUrl }; cfg2.auth = { email: credentials.email }; writeFileSync(cfgPath2, JSON.stringify(cfg2, null, 2)); console.log(chalk.green("\u2713 Database configured successfully")); return; } else if (choice === "local") { const cfgPath2 = join(cfgDir, "config.json"); let cfg2 = {}; try { if (existsSync(cfgPath2)) cfg2 = JSON.parse(readFileSync(cfgPath2, "utf-8")); } catch { } cfg2.database = { mode: "local" }; writeFileSync(cfgPath2, JSON.stringify(cfg2, null, 2)); console.log(chalk.green("\u2713 Switched to local database mode")); return; } else { console.log(chalk.gray("Login cancelled")); return; } } throw new IntegrationError( data.error || "Authentication failed", ErrorCode.LINEAR_AUTH_FAILED, { email: credentials.email, apiUrl } ); } const cfgPath = join(cfgDir, "config.json"); let cfg = {}; try { if (existsSync(cfgPath)) cfg = JSON.parse(readFileSync(cfgPath, "utf-8")); } catch { } cfg.auth = { apiKey: data.apiKey, apiUrl, email: credentials.email }; if (data.databaseUrl) { cfg.database = { mode: "hosted", url: data.databaseUrl }; } writeFileSync(cfgPath, JSON.stringify(cfg, null, 2)); const envFile = join(cfgDir, "stackmemory.env"); const envContent = `# StackMemory Authentication STACKMEMORY_API_KEY=${data.apiKey} STACKMEMORY_API_URL=${apiUrl} ${data.databaseUrl ? `DATABASE_URL=${data.databaseUrl}` : ""} `; writeFileSync(envFile, envContent); console.log(chalk.green("\n\u2705 Successfully logged in to StackMemory")); console.log( chalk.green(`\u2713 Configuration saved to ~/.stackmemory/config.json`) ); console.log(chalk.gray("\nYou can now use:")); console.log( chalk.cyan(" stackmemory sync ") + chalk.gray("- Sync your context to the cloud") ); console.log( chalk.cyan(" stackmemory db status") + chalk.gray("- Check database connection") ); console.log( chalk.cyan(" stackmemory context ") + chalk.gray("- Manage your contexts") ); } catch (error) { console.error(chalk.red("\n\u274C Login failed:"), error.message); console.log( chalk.yellow( "\nTip: Visit https://stackmemory.ai/signup to create an account" ) ); process.exit(1); } }); } export { registerLoginCommand }; //# sourceMappingURL=login.js.map