Skip to content

Commit 1b35809

Browse files
committed
feat: create CLI tool for Aeternum Research Tool
- Add bin/cli.js script that bundles and serves the application - Configure package.json with bin field for npx execution - CLI automatically builds project if needed and starts local server - Opens browser automatically on supported platforms - Update files array to include bin and docs directories - Graceful shutdown handling with Ctrl+C
1 parent e48a16c commit 1b35809

10 files changed

Lines changed: 199 additions & 50 deletions

bin/cli.js

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
#!/usr/bin/env node
2+
3+
import { existsSync } from 'fs';
4+
import { execSync } from 'child_process';
5+
import { createServer } from 'http';
6+
import { readFile } from 'fs/promises';
7+
import { join, dirname } from 'path';
8+
import { fileURLToPath } from 'url';
9+
10+
const __filename = fileURLToPath(import.meta.url);
11+
const __dirname = dirname(__filename);
12+
const projectRoot = join(__dirname, '..');
13+
const distDir = join(projectRoot, 'docs');
14+
15+
console.log('🚀 Starting Aeternum Research Tool...\n');
16+
17+
// Check if dist directory exists, if not, build the project
18+
if (!existsSync(distDir)) {
19+
console.log('📦 Building project...');
20+
try {
21+
execSync('npm run build', {
22+
cwd: projectRoot,
23+
stdio: 'inherit'
24+
});
25+
console.log('✅ Build completed!\n');
26+
} catch (error) {
27+
console.error('❌ Build failed:', error.message);
28+
process.exit(1);
29+
}
30+
} else {
31+
console.log('📦 Using existing build...\n');
32+
}
33+
34+
// Create HTTP server to serve the built files
35+
const server = createServer(async (req, res) => {
36+
try {
37+
let filePath = join(distDir, req.url === '/' ? 'index.html' : req.url);
38+
39+
// Handle SPA routing - serve index.html for all non-file requests
40+
if (!existsSync(filePath) || !filePath.startsWith(distDir)) {
41+
filePath = join(distDir, 'index.html');
42+
}
43+
44+
const content = await readFile(filePath);
45+
const ext = filePath.split('.').pop();
46+
47+
// Set appropriate content types
48+
const contentTypes = {
49+
'html': 'text/html',
50+
'css': 'text/css',
51+
'js': 'application/javascript',
52+
'json': 'application/json',
53+
'png': 'image/png',
54+
'jpg': 'image/jpeg',
55+
'jpeg': 'image/jpeg',
56+
'gif': 'image/gif',
57+
'svg': 'image/svg+xml',
58+
'ico': 'image/x-icon'
59+
};
60+
61+
res.writeHead(200, {
62+
'Content-Type': contentTypes[ext] || 'text/plain',
63+
'Cache-Control': 'no-cache'
64+
});
65+
res.end(content);
66+
} catch (error) {
67+
res.writeHead(404);
68+
res.end('File not found');
69+
}
70+
});
71+
72+
const PORT = 3000;
73+
74+
// Start server
75+
server.listen(PORT, () => {
76+
const url = `http://localhost:${PORT}`;
77+
console.log('🌐 Server running at:', url);
78+
console.log('📖 Aeternum Research Tool is ready!\n');
79+
80+
// Open browser
81+
const { platform } = process;
82+
let openCommand;
83+
84+
if (platform === 'darwin') {
85+
openCommand = 'open';
86+
} else if (platform === 'win32') {
87+
openCommand = 'start';
88+
} else {
89+
openCommand = 'xdg-open';
90+
}
91+
92+
try {
93+
execSync(`${openCommand} ${url}`, { stdio: 'ignore' });
94+
console.log('🌍 Browser opened automatically');
95+
} catch (error) {
96+
console.log('ℹ️ Please open your browser and navigate to:', url);
97+
}
98+
99+
console.log('\n💡 Press Ctrl+C to stop the server');
100+
});
101+
102+
// Handle graceful shutdown
103+
process.on('SIGINT', () => {
104+
console.log('\n👋 Shutting down Aeternum Research Tool...');
105+
server.close(() => {
106+
console.log('✅ Server stopped');
107+
process.exit(0);
108+
});
109+
});
110+
111+
process.on('SIGTERM', () => {
112+
console.log('\n👋 Shutting down Aeternum Research Tool...');
113+
server.close(() => {
114+
console.log('✅ Server stopped');
115+
process.exit(0);
116+
});
117+
});

docs/assets/index-C65qo4lU.css

Lines changed: 0 additions & 1 deletion
This file was deleted.
Lines changed: 51 additions & 40 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/assets/index-Do8t6LRU.css

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/index.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@
1919
}
2020
}
2121
</script>
22-
<script type="module" crossorigin src="./assets/index-Dej8bAgS.js"></script>
23-
<link rel="stylesheet" crossorigin href="./assets/index-C65qo4lU.css">
22+
<script type="module" crossorigin src="./assets/index-DirBf-b_.js"></script>
23+
<link rel="stylesheet" crossorigin href="./assets/index-Do8t6LRU.css">
2424
</head>
2525
<body class="bg-slate-900">
2626
<div id="root"></div>

package.json

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
{
22
"name": "aeternum-research-tool",
3-
"private": true,
43
"version": "0.0.0",
54
"author": "Involvex",
65
"repository": {
@@ -11,6 +10,9 @@
1110
"description": "A research tool that uses AI to search the web for information about the New World Aeternum expansion",
1211
"keywords": [ "genai", "research", "aeternum", "new world", "expansion"],
1312
"type": "module",
13+
"bin": {
14+
"aeternum-research-tool": "./bin/cli.js"
15+
},
1416
"scripts": {
1517
"dev": "vite",
1618
"build": "tsc && vite build",
@@ -44,11 +46,7 @@
4446
"vite": "^7.1.6"
4547
},
4648
"files": [
47-
"node_modules",
48-
"package-lock.json",
49-
"vite.config.ts",
50-
"tailwind.config.js",
51-
"postcss.config.js",
52-
"dist"
49+
"bin",
50+
"docs"
5351
]
5452
}

tsconfig.node.tsbuildinfo

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.

tsconfig.tsbuildinfo

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"root":["./components/header.tsx","./components/historylog.tsx","./components/loadingspinner.tsx","./components/resultsdisplay.tsx","./components/searchbar.tsx","./components/icons.tsx","./services/geminiservice.ts","./types.ts","./index.tsx","./app.tsx"],"version":"5.9.2"}

vite.config.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
declare const _default: import("vite").UserConfig;
2+
export default _default;

vite.config.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import env from 'dotenv';
2+
import { defineConfig } from 'vite';
3+
import react from '@vitejs/plugin-react';
4+
import path from 'node:path';
5+
import process from 'node:process';
6+
env.config({
7+
path: path.resolve(process.cwd(), '.env'),
8+
debug: true
9+
});
10+
// https://vitejs.dev/config/
11+
export default defineConfig({
12+
plugins: [react()],
13+
build: {
14+
outDir: 'docs',
15+
emptyOutDir: true,
16+
},
17+
assetsInclude: ['**/*.md', 'node_modules'],
18+
base: './', // Use relative paths for assets for GitHub Pages compatibility
19+
});

0 commit comments

Comments
 (0)