UNPKG

logggai

Version:

AI-powered CLI for transforming your development work into professional content

306 lines 11.7 kB
#!/usr/bin/env node "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const commander_1 = require("commander"); const login_1 = require("./commands/login"); const register_1 = require("./commands/register"); const logout_1 = require("./commands/logout"); const post_1 = require("./commands/post"); const list_1 = require("./commands/list"); const { configCommand } = require('./commands/config'); const context_1 = require("./commands/context"); const package_json_1 = require("../package.json"); const project_1 = require("./commands/project"); const sync_1 = require("./commands/sync"); const fs = require("fs"); const path = require("path"); const child_process_1 = require("child_process"); const token_1 = require("./commands/token"); const chalk_1 = require("chalk"); const auth_1 = require("./lib/auth"); const program = new commander_1.Command(); program .name('logggai') .description('AI-powered CLI for transforming your development work into professional content') .version(package_json_1.version); // === Auto-launch daemon if needed === function isDaemonRunning() { const pidFile = path.join(process.cwd(), '.logggai-daemon.pid'); if (!fs.existsSync(pidFile)) return false; try { const pid = parseInt(fs.readFileSync(pidFile, 'utf-8')); process.kill(pid, 0); // throws if not running return true; } catch { return false; } } function launchDaemonIfNeeded() { const projectFile = path.join(process.cwd(), '.logggai-project.json'); if (!fs.existsSync(projectFile)) return; if (isDaemonRunning()) return; // Launch daemon in detached/background mode const daemonPath = path.resolve(__dirname, 'autosync-daemon.js'); const child = (0, child_process_1.spawn)('node', [daemonPath], { cwd: process.cwd(), detached: true, stdio: 'ignore', }); child.unref(); console.log('Logggai auto-sync daemon started in background.'); } // Only auto-launch if not running the daemon command itself if (process.argv[2] !== 'daemon') { launchDaemonIfNeeded(); } // Helper to centralize automatic relogin and command relaunch after login function withSessionCheck(fn) { return async (...args) => { // If ensureValidSession relaunches the command, stop current execution const proceed = await (0, auth_1.ensureValidSession)(() => fn(...args)); if (!proceed) return; // Command already executed in ensureValidSession if proceed is true }; } // Available commands program .command('register') .description('Create a new account') .action(register_1.registerCommand); program .command('login') .description('Login to your account') .action(login_1.loginCommand); program .command('logout') .description('Logout from your account') .action(logout_1.logoutCommand); program .command('post <title>') .description('Create a new article with AI enhancement') .option('-c, --content <content>', 'Article content') .option('-t, --tags <tags>', 'Tags separated by commas') .option('--no-ai', 'Disable AI processing') .option('-p, --prompt <promptId>', 'AI prompt ID to use') .option('-o, --org <organization>', 'Create in specific organization (name, slug or ID)') .action(post_1.postCommand); program .command('list') .description('List your recent articles (current context)') .option('-l, --limit <number>', 'Number of articles to display', '10') .option('-o, --org <organization>', 'List from specific organization (name, slug or ID)') .action(list_1.listCommand); program .command('config') .description('Configure the CLI') .option('-s, --set <key=value>', 'Set a configuration value') .option('-g, --get <key>', 'Get a configuration value') .option('-l, --list', 'List all configurations') .action(configCommand); // Context management commands program .command('context') .description('Manage contexts (Personal/Organization)') .option('-l, --list', 'List available contexts') .option('-s, --switch <org>', 'Switch to organization') .option('-p, --personal', 'Switch to personal context') .action(context_1.contextCommand); program .command('switch [organization]') .description('Quick context switch (personal if no argument)') .action(context_1.switchCommand); program .command('use <context>') .description('Use a context (alias for switch)') .action(context_1.useCommand); program .command('project') .description('Create or initialize a Logggai project (local/cloud mapping)') .action(project_1.projectCommand); program .command('sync') .description('Sync git commits with Logggai (AI, publishing, mapping)') // TODO: Add options if needed .action(sync_1.syncCommand); program .command('start') .description('Start the Logggai auto-sync daemon in the current project directory.') .action(() => { // No session check needed here, the daemon will check itself const fs = require('fs'); const path = require('path'); const { spawn } = require('child_process'); function isDaemonRunning() { const pidFile = path.join(process.cwd(), '.logggai-daemon.pid'); if (!fs.existsSync(pidFile)) return false; try { const pid = parseInt(fs.readFileSync(pidFile, 'utf-8')); process.kill(pid, 0); return true; } catch { return false; } } if (isDaemonRunning()) { console.log('\x1b[33mLogggai auto-sync daemon is already running in this project.\x1b[0m'); return; } const daemonPath = path.resolve(__dirname, 'autosync-daemon.js'); const child = spawn('node', [daemonPath], { cwd: process.cwd(), detached: true, stdio: 'ignore', }); child.unref(); console.log('\x1b[32mLogggai auto-sync daemon started in background.\x1b[0m'); }); program .command('status') .description('Show the status of the Logggai auto-sync daemon in the current project directory.') .action(() => { const fs = require('fs'); const path = require('path'); const chalk = require('chalk'); const pidFile = path.join(process.cwd(), '.logggai-daemon.pid'); const logFile = path.join(process.cwd(), '.logggai-daemon.log'); const projectFile = path.join(process.cwd(), '.logggai-project.json'); if (!fs.existsSync(projectFile)) { console.log(chalk.yellow('No Logggai project found in this directory.')); console.log(chalk.gray('This folder is not a Logggai project. Move to a mapped project folder or run: npx logggai project to initialize.')); return; } if (fs.existsSync(pidFile)) { const pid = parseInt(fs.readFileSync(pidFile, 'utf-8')); let running = false; try { process.kill(pid, 0); // throws if not running running = true; } catch { running = false; } if (running) { console.log(chalk.green('Logggai auto-sync daemon is running.')); console.log(chalk.gray(' PID:'), pid); } else { console.log(chalk.red('Logggai auto-sync daemon PID file found, but process is not running.')); console.log(chalk.gray(' PID:'), pid); } // Show last 10 lines of log if exists if (fs.existsSync(logFile)) { console.log(chalk.cyan('\nLast 10 lines of .logggai-daemon.log:')); const lines = fs.readFileSync(logFile, 'utf-8').split('\n').filter(Boolean); const lastLines = lines.slice(-10); lastLines.forEach((line) => console.log(chalk.gray(' ' + line))); } } else { console.log(chalk.yellow('Logggai auto-sync daemon is not running in this project.')); console.log(chalk.gray('Run: logggai start')); } }); program .command('token') .description('Show your Clerk session token for MCP/agent usage') .action(withSessionCheck(token_1.tokenCommand)); program .command('whoami') .description('Show the current user, context, and token expiration') .action(withSessionCheck(async () => { const { getConfig, isLoggedIn, getCurrentContext, getCurrentOrganizationId } = require('./lib/config'); if (!isLoggedIn()) { console.log(chalk_1.default.red('You are not logged in. Use: logggai login')); return; } const email = getConfig('userEmail'); const context = getCurrentContext() || 'personal'; const orgId = getCurrentOrganizationId(); const token = getConfig('sessionToken'); let exp = null; try { if (token) { const payload = token.split('.')[1]; const decoded = JSON.parse(Buffer.from(payload, 'base64').toString('utf8')); if (decoded.exp) { exp = new Date(decoded.exp * 1000); } } } catch { } console.log(chalk_1.default.green('Logged in user:')); if (email) console.log(chalk_1.default.cyan(' Email:'), email); console.log(chalk_1.default.cyan(' Context:'), context === 'organization' ? 'Organization' : 'Personal'); if (context === 'organization' && orgId) console.log(chalk_1.default.cyan(' Organization ID:'), orgId); if (exp) { const now = new Date(); const diff = (exp.getTime() - now.getTime()) / (1000 * 60 * 60); console.log(chalk_1.default.cyan(' Token expiration:'), exp.toISOString()); if (diff < 24 && diff > 0) { console.log(chalk_1.default.yellow.bold('Warning: your token expires in less than 24h. Run logggai login soon.')); } else if (diff <= 0) { console.log(chalk_1.default.red.bold('Token expired. Please run logggai login.')); } } else { console.log(chalk_1.default.gray(' (Token expiration unknown)')); } })); program .command('stop') .description('Stop the Logggai auto-sync daemon in the current project directory.') .action(() => { const fs = require('fs'); const path = require('path'); const chalk = require('chalk'); const pidFile = path.join(process.cwd(), '.logggai-daemon.pid'); const projectFile = path.join(process.cwd(), '.logggai-project.json'); if (!fs.existsSync(projectFile)) { console.log(chalk.yellow('No Logggai project found in this directory.')); console.log(chalk.gray('Run: logggai project')); return; } if (!fs.existsSync(pidFile)) { console.log(chalk.yellow('No daemon is running in this project.')); return; } const pid = parseInt(fs.readFileSync(pidFile, 'utf-8')); let running = false; try { process.kill(pid, 0); // throws if not running running = true; } catch { running = false; } if (running) { try { process.kill(pid, 'SIGTERM'); fs.unlinkSync(pidFile); console.log(chalk.green('Logggai auto-sync daemon stopped successfully.')); console.log(chalk.gray(' PID:'), pid); } catch (err) { const error = err; console.log(chalk.red('Failed to stop daemon.')); console.log(chalk.gray(' PID:'), pid); console.log(chalk.gray('Error:'), error.message || error); } } else { fs.unlinkSync(pidFile); console.log(chalk.yellow('Daemon process was not running, but PID file has been removed.')); } }); program.parse(); //# sourceMappingURL=index.js.map