@kaifronsdal/transcript-viewer
Version:
A web-based viewer for AI conversation transcripts with rollback support
190 lines (158 loc) ⢠6.05 kB
JavaScript
import { exec } from 'child_process';
import { fileURLToPath } from 'url';
import { dirname, join, resolve } from 'path';
import { existsSync } from 'fs';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const packageRoot = join(__dirname, '..');
const buildDir = join(packageRoot, 'build');
// Parse command line arguments
const args = process.argv.slice(2);
const forceRebuild = args.includes('--force-rebuild') || args.includes('-f');
const showHelp = args.includes('--help') || args.includes('-h');
// Parse transcript directory argument
let transcriptDir = null;
const dirIndex = args.findIndex(arg => arg === '--dir' || arg === '-d');
if (dirIndex !== -1 && args[dirIndex + 1]) {
transcriptDir = resolve(args[dirIndex + 1]);
} else {
// Check for --dir=path format
const dirArg = args.find(arg => arg.startsWith('--dir='));
if (dirArg) {
transcriptDir = resolve(dirArg.split('=')[1]);
}
}
// Parse host argument
let host = null;
const hostIndex = args.findIndex(arg => arg === '--host');
if (hostIndex !== -1 && args[hostIndex + 1]) {
host = args[hostIndex + 1];
} else {
// Check for --host=value format
const hostArg = args.find(arg => arg.startsWith('--host='));
if (hostArg) {
host = hostArg.split('=')[1];
}
}
// Parse port argument
let port = null;
const portIndex = args.findIndex(arg => arg === '--port' || arg === '-p');
if (portIndex !== -1 && args[portIndex + 1]) {
port = parseInt(args[portIndex + 1]);
} else {
// Check for --port=value format
const portArg = args.find(arg => arg.startsWith('--port='));
if (portArg) {
port = parseInt(portArg.split('=')[1]);
}
}
if (showHelp) {
console.log(`transcript-viewer - A web-based viewer for AI conversation transcripts
USAGE:
transcript-viewer [OPTIONS]
OPTIONS:
-d, --dir <PATH> Specify the transcript directory to load
[default: ./transcripts]
--host <HOST> Specify the host to bind to [default: localhost]
Use 0.0.0.0 for RunPod/Docker environments
-p, --port <PORT> Specify the port to bind to [default: 3000]
-f, --force-rebuild Force rebuild the application even if build exists
-h, --help Print help information
EXAMPLES:
transcript-viewer
Start with default directory (./transcripts)
transcript-viewer --dir ./my-transcripts
Start with custom directory
transcript-viewer -d /path/to/transcripts
Start with custom directory (short form)
transcript-viewer --host 0.0.0.0 --port 8080
Start accessible from all interfaces on port 8080 (RunPod compatible)
transcript-viewer --force-rebuild
Force rebuild and start
RUNPOD USAGE:
transcript-viewer --host 0.0.0.0 --port 8080 --dir /workspace/transcripts
Recommended settings for RunPod deployment
`);
process.exit(0);
}
console.log('š Starting Transcript Viewer...');
if (transcriptDir) {
if (!existsSync(transcriptDir)) {
console.error(`ā Error: Transcript directory does not exist: ${transcriptDir}`);
process.exit(1);
}
console.log(`š Using transcript directory: ${transcriptDir}`);
} else {
const defaultPath = resolve(process.cwd(), 'transcripts');
console.log(`š Using default transcript directory: ${defaultPath}`);
console.log(`š” Tip: Use --dir <path> to specify a different directory`);
}
// Configure host and port
const finalHost = host || process.env.HOST || 'localhost';
const finalPort = port || parseInt(process.env.PORT || '3000');
console.log(`š Server will bind to: ${finalHost}:${finalPort}`);
if (finalHost === '0.0.0.0') {
console.log(`š RunPod/Docker mode: Server will be accessible from all interfaces`);
}
// Check if build directory exists and if we should force rebuild
if (!existsSync(buildDir) || forceRebuild) {
if (forceRebuild) {
console.log('š Force rebuilding application...');
} else {
console.log('š¦ Building application...');
}
exec('npm run build', { cwd: packageRoot }, (error, stdout, stderr) => {
if (error) {
console.error('ā Build failed:', error);
process.exit(1);
}
// console.log(stdout);
// console.log(stderr);
console.log('šļø Build completed');
startServer();
});
} else {
startServer();
}
function startServer() {
console.log('š Starting server...');
// Set up environment variables
const env = { ...process.env };
if (transcriptDir) {
env.TRANSCRIPT_DIR = transcriptDir;
} else {
// If no directory specified, use ./transcripts relative to where the user ran the command
env.TRANSCRIPT_DIR = resolve(process.cwd(), 'transcripts');
}
// Build the start command with host and port
const startCommand = `vite preview --host ${finalHost} --port ${finalPort}`;
const server = exec(startCommand, { cwd: packageRoot, env });
server.stdout.on('data', (data) => {
console.log(data.toString());
// Look for the local URL in the output
const urlMatch = data.toString().match(/Listening on (http:\/\/[^\s]+)/);
if (urlMatch) {
console.log('\nā
Transcript Viewer is running!');
console.log(`š Open your browser to: ${urlMatch[1]}`);
// Show RunPod-specific instructions if binding to all interfaces
if (finalHost === '0.0.0.0') {
console.log(`š RunPod: Your service should be accessible via the RunPod public URL`);
console.log(`š Make sure port ${finalPort} is exposed in your RunPod configuration`);
}
console.log('\nš” To stop the server, press Ctrl+C');
}
});
server.stderr.on('data', (data) => {
console.error(data.toString());
});
server.on('close', (code) => {
console.log(`\nš Server stopped with code ${code}`);
});
// Handle Ctrl+C gracefully
process.on('SIGINT', () => {
console.log('\nš Stopping server...');
server.kill('SIGINT');
process.exit(0);
});
}