Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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
```
Expand Down
5 changes: 5 additions & 0 deletions packages/atxp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
```

Expand Down
11 changes: 8 additions & 3 deletions packages/atxp/src/help.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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:'));
Expand Down
47 changes: 44 additions & 3 deletions packages/atxp/src/index.ts
Original file line number Diff line number Diff line change
@@ -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') ||
Expand All @@ -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) {
Expand Down
56 changes: 29 additions & 27 deletions packages/atxp/src/run-demo.ts
Original file line number Diff line number Diff line change
@@ -1,49 +1,50 @@
import chalk from 'chalk';
import { spawn } from 'child_process';
import fs from 'fs-extra';
import path from 'path';

Check warning on line 4 in packages/atxp/src/run-demo.ts

View workflow job for this annotation

GitHub Actions / test

'path' is defined but never used
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<void> {
export async function runDemo(options: DemoOptions): Promise<void> {
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);
process.exit(1);
}
}

async function cloneDemoRepo(): Promise<void> {
async function cloneDemoRepo(demoDir: string, isVerbose: boolean): Promise<void> {
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'
});

Expand All @@ -62,10 +63,10 @@
});
}

async function updateDemoRepo(): Promise<void> {
async function updateDemoRepo(demoDir: string, isVerbose: boolean): Promise<void> {
return new Promise((resolve, _reject) => {
const git = spawn('git', ['pull'], {
cwd: DEMO_DIR,
cwd: demoDir,
stdio: isVerbose ? 'inherit' : 'pipe'
});

Expand All @@ -86,15 +87,15 @@
});
}

async function installDependencies(): Promise<void> {
async function installDependencies(demoDir: string, isVerbose: boolean): Promise<void> {
console.log(chalk.blue('Installing dependencies...'));

return new Promise((resolve, reject) => {
// Use --silent flag to reduce npm output
const npmArgs = isVerbose ? ['run', 'install-all'] : ['run', 'install-all', '--silent'];

const npm = spawn('npm', npmArgs, {
cwd: DEMO_DIR,
cwd: demoDir,
stdio: isVerbose ? 'inherit' : 'pipe'
});

Expand All @@ -113,9 +114,10 @@
});
}

async function startDemo(): Promise<void> {
async function startDemo(port: number, demoDir: string, isVerbose: boolean): Promise<void> {
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'));
Expand All @@ -125,15 +127,15 @@
// 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
CI: 'false'
};

const demo = spawn('npm', ['run', 'dev'], {
cwd: DEMO_DIR,
cwd: demoDir,
stdio: 'pipe', // Always use pipe to capture output
env
});
Expand Down Expand Up @@ -178,10 +180,10 @@
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);
Expand Down