diff --git a/README.md b/README.md index 9bb5526..152baa3 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,8 @@ This monorepo contains: ### Demo Options - `--verbose, -v` - Show detailed logs - `--refresh` - Force refresh demo from GitHub +- `--port, -p` - Specify port number (default: 8016) +- `--dir, -d` - Specify demo directory (default: ~/.cache/atxp/demo) ## Examples @@ -44,6 +46,15 @@ npx atxp demo # Run with detailed output npx atxp demo --verbose +# Use custom port +npx atxp demo --port 3000 + +# Use custom directory +npx atxp demo --dir ./my-demo + +# Combine options +npx atxp demo --port 3000 --dir ./my-demo --verbose + # Force refresh from GitHub npx atxp demo --refresh ``` diff --git a/packages/atxp/README.md b/packages/atxp/README.md index 2bc42d6..ca2ef98 100644 --- a/packages/atxp/README.md +++ b/packages/atxp/README.md @@ -36,11 +36,16 @@ Runs the interactive ATXP demo application. **Options:** - `--verbose, -v` - Show detailed logs - `--refresh` - Force refresh demo from GitHub +- `--port, -p` - Specify port number (default: 8016) +- `--dir, -d` - Specify demo directory (default: ~/.cache/atxp/demo) **Examples:** ```bash npx atxp demo npx atxp demo --verbose +npx atxp demo --port 3000 +npx atxp demo --dir ./my-demo +npx atxp demo --port 3000 --dir ./my-demo --verbose npx atxp demo --refresh ``` diff --git a/packages/atxp/src/help.ts b/packages/atxp/src/help.ts index 3e99043..4e3a27c 100644 --- a/packages/atxp/src/help.ts +++ b/packages/atxp/src/help.ts @@ -18,12 +18,17 @@ export function showHelp(): void { console.log(chalk.bold('Demo Options:')); console.log(' ' + chalk.yellow('--verbose, -v') + ' ' + 'Show detailed logs'); console.log(' ' + chalk.yellow('--refresh') + ' ' + 'Force refresh the demo from GitHub'); + console.log(' ' + chalk.yellow('--port, -p') + ' ' + 'Specify port number (default: 8016)'); + console.log(' ' + chalk.yellow('--dir, -d') + ' ' + 'Specify demo directory (default: ~/.cache/atxp/demo)'); console.log(); console.log(chalk.bold('Examples:')); - console.log(' npx atxp demo # Run the demo'); - console.log(' npx atxp demo --verbose # Run demo with detailed logs'); - console.log(' npx atxp create # Create a new project'); + console.log(' npx atxp demo # Run the demo with defaults'); + console.log(' npx atxp demo --verbose # Run demo with detailed logs'); + console.log(' npx atxp demo --port 3000 # Run demo on port 3000'); + console.log(' npx atxp demo --dir ./my-demo # Use custom demo directory'); + console.log(' npx atxp demo --port 3000 --dir ./demo # Custom port and directory'); + console.log(' npx atxp create # Create a new project'); console.log(); console.log(chalk.bold('Learn more:')); diff --git a/packages/atxp/src/index.ts b/packages/atxp/src/index.ts index 8a57a1b..7be22ad 100644 --- a/packages/atxp/src/index.ts +++ b/packages/atxp/src/index.ts @@ -1,11 +1,52 @@ #!/usr/bin/env node +import path from 'path'; +import os from 'os'; import { createProject } from './create-project.js'; import { runDemo } from './run-demo.js'; import { showHelp } from './help.js'; -// Get the command from arguments -const command = process.argv[2]; +interface DemoOptions { + port: number; + dir: string; + verbose: boolean; + refresh: boolean; +} + +// Parse command line arguments +function parseArgs(): { command: string; demoOptions: DemoOptions } { + const command = process.argv[2]; + + // Parse demo options + const getArgValue = (flag: string, shortFlag: string): string | undefined => { + const index = process.argv.findIndex(arg => arg === flag || arg === shortFlag); + return index !== -1 ? process.argv[index + 1] : undefined; + }; + + const port = (() => { + const portValue = getArgValue('--port', '-p'); + if (portValue) { + const parsed = parseInt(portValue, 10); + if (parsed > 0 && parsed < 65536) return parsed; + } + return 8016; // default port + })(); + + const dir = (() => { + const dirValue = getArgValue('--dir', '-d'); + return dirValue ? path.resolve(dirValue) : path.join(os.homedir(), '.cache', 'atxp', 'demo'); + })(); + + const verbose = process.argv.includes('--verbose') || process.argv.includes('-v'); + const refresh = process.argv.includes('--refresh'); + + return { + command, + demoOptions: { port, dir, verbose, refresh } + }; +} + +const { command, demoOptions } = parseArgs(); // Detect if we're in create mode (npm create atxp or npx atxp create) const isCreateMode = process.env.npm_config_argv?.includes('create') || @@ -18,7 +59,7 @@ if (isCreateMode) { createProject(); } else if (command === 'demo') { console.log('Starting ATXP demo...'); - runDemo(); + runDemo(demoOptions); } else if (command === 'help' || command === '--help' || command === '-h') { showHelp(); } else if (!command) { diff --git a/packages/atxp/src/run-demo.ts b/packages/atxp/src/run-demo.ts index de0fb8c..dcd726f 100644 --- a/packages/atxp/src/run-demo.ts +++ b/packages/atxp/src/run-demo.ts @@ -3,37 +3,38 @@ import { spawn } from 'child_process'; import fs from 'fs-extra'; import path from 'path'; import open from 'open'; -import os from 'os'; -const DEMO_REPO_URL = 'https://github.com/atxp-dev/agent-demo.git'; -const DEMO_DIR = path.join(os.homedir(), '.cache', 'atxp', 'demo'); -const DEMO_PORT = 8016; +interface DemoOptions { + port: number; + dir: string; + verbose: boolean; + refresh: boolean; +} -const isVerbose = process.argv.includes('--verbose') || process.argv.includes('-v'); -const shouldRefresh = process.argv.includes('--refresh'); +const DEMO_REPO_URL = 'https://github.com/atxp-dev/agent-demo.git'; -export async function runDemo(): Promise { +export async function runDemo(options: DemoOptions): Promise { try { // Check if demo directory exists, if not clone it - if (!await fs.pathExists(DEMO_DIR)) { + if (!await fs.pathExists(options.dir)) { console.log(chalk.blue('Downloading demo from GitHub...')); - await cloneDemoRepo(); - } else if (shouldRefresh) { + await cloneDemoRepo(options.dir, options.verbose); + } else if (options.refresh) { // Force refresh if --refresh flag is used console.log(chalk.blue('Forcing demo refresh...')); - await fs.remove(DEMO_DIR); - await cloneDemoRepo(); + await fs.remove(options.dir); + await cloneDemoRepo(options.dir, options.verbose); } else { console.log(chalk.blue('Using existing demo...')); // Pull latest changes - await updateDemoRepo(); + await updateDemoRepo(options.dir, options.verbose); } // Install dependencies if needed - await installDependencies(); + await installDependencies(options.dir, options.verbose); // Start the demo and open browser - await startDemo(); + await startDemo(options.port, options.dir, options.verbose); } catch (error) { console.error(chalk.red('Error starting demo:'), (error as Error).message); @@ -41,9 +42,9 @@ export async function runDemo(): Promise { } } -async function cloneDemoRepo(): Promise { +async function cloneDemoRepo(demoDir: string, isVerbose: boolean): Promise { return new Promise((resolve, reject) => { - const git = spawn('git', ['clone', DEMO_REPO_URL, DEMO_DIR], { + const git = spawn('git', ['clone', DEMO_REPO_URL, demoDir], { stdio: isVerbose ? 'inherit' : 'pipe' }); @@ -62,10 +63,10 @@ async function cloneDemoRepo(): Promise { }); } -async function updateDemoRepo(): Promise { +async function updateDemoRepo(demoDir: string, isVerbose: boolean): Promise { return new Promise((resolve, _reject) => { const git = spawn('git', ['pull'], { - cwd: DEMO_DIR, + cwd: demoDir, stdio: isVerbose ? 'inherit' : 'pipe' }); @@ -86,7 +87,7 @@ async function updateDemoRepo(): Promise { }); } -async function installDependencies(): Promise { +async function installDependencies(demoDir: string, isVerbose: boolean): Promise { console.log(chalk.blue('Installing dependencies...')); return new Promise((resolve, reject) => { @@ -94,7 +95,7 @@ async function installDependencies(): Promise { const npmArgs = isVerbose ? ['run', 'install-all'] : ['run', 'install-all', '--silent']; const npm = spawn('npm', npmArgs, { - cwd: DEMO_DIR, + cwd: demoDir, stdio: isVerbose ? 'inherit' : 'pipe' }); @@ -113,9 +114,10 @@ async function installDependencies(): Promise { }); } -async function startDemo(): Promise { +async function startDemo(port: number, demoDir: string, isVerbose: boolean): Promise { console.log(chalk.blue('Starting demo application...')); - console.log(chalk.green(`Demo will be available at: http://localhost:${DEMO_PORT}`)); + console.log(chalk.green(`Demo will be available at: http://localhost:${port}`)); + console.log(chalk.gray(`Demo directory: ${demoDir}`)); console.log(chalk.yellow('Press Ctrl+C to stop the demo')); if (!isVerbose) { console.log(chalk.gray('Run with --verbose to see detailed logs')); @@ -125,7 +127,7 @@ async function startDemo(): Promise { // Set the port environment variable for the demo const env = { ...process.env, - PORT: DEMO_PORT.toString(), + PORT: port.toString(), // Suppress deprecation warnings NODE_NO_WARNINGS: '1', // Suppress React warnings in development @@ -133,7 +135,7 @@ async function startDemo(): Promise { }; const demo = spawn('npm', ['run', 'dev'], { - cwd: DEMO_DIR, + cwd: demoDir, stdio: 'pipe', // Always use pipe to capture output env }); @@ -178,10 +180,10 @@ async function startDemo(): Promise { if (!demoOpenedBrowser) { try { console.log(chalk.blue('Opening browser...')); - await open(`http://localhost:${DEMO_PORT}`); + await open(`http://localhost:${port}`); } catch { console.log(chalk.yellow('Could not open browser automatically')); - console.log(chalk.white(`Please open http://localhost:${DEMO_PORT} in your browser`)); + console.log(chalk.white(`Please open http://localhost:${port} in your browser`)); } } }, 2000);