-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathcli.js
More file actions
210 lines (183 loc) · 5.42 KB
/
cli.js
File metadata and controls
210 lines (183 loc) · 5.42 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
#!/usr/bin/env node
import http from 'http';
import path from 'path';
import fs from 'fs';
import url from 'url';
import { fileURLToPath } from 'url';
import { exec } from 'child_process';
import os from 'os';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const PORT = process.env.PORT || 3000;
const HOST = process.env.HOST || 'localhost';
function checkPort(port, callback) {
const server = http.createServer();
server.once('error', (err) => {
if (err.code === 'EADDRINUSE') {
callback(false);
} else {
callback(true);
}
});
server.once('listening', () => {
server.close();
callback(true);
});
server.listen(port);
}
function findAvailablePort(startPort, callback) {
checkPort(startPort, (isAvailable) => {
if (isAvailable) {
callback(startPort);
} else {
findAvailablePort(startPort + 1, callback);
}
});
}
function getMimeType(filePath) {
const ext = path.extname(filePath).toLowerCase();
const mimeTypes = {
'.html': 'text/html',
'.js': 'application/javascript',
'.mjs': 'application/javascript',
'.css': 'text/css',
'.json': 'application/json',
'.png': 'image/png',
'.jpg': 'image/jpeg',
'.jpeg': 'image/jpeg',
'.gif': 'image/gif',
'.svg': 'image/svg+xml',
'.ico': 'image/x-icon',
'.webp': 'image/webp',
'.woff': 'font/woff',
'.woff2': 'font/woff2',
'.ttf': 'font/ttf',
'.otf': 'font/otf',
'.wasm': 'application/wasm'
};
return mimeTypes[ext] || 'application/octet-stream';
}
function openBrowser(url) {
const platform = os.platform();
let command;
switch (platform) {
case 'darwin':
command = `open "${url}"`;
break;
case 'win32':
command = `start "${url}"`;
break;
default:
// Linux and others
command = `xdg-open "${url}" || sensible-browser "${url}" || x-www-browser "${url}" || gnome-open "${url}"`;
}
exec(command, (err) => {
if (err) {
console.log('📌 Could not open browser automatically. Please open the URL manually.');
}
});
}
function startServer() {
const distPath = path.join(__dirname, 'dist');
if (!fs.existsSync(distPath)) {
console.error('Error: Built files not found. Please ensure the package was installed correctly.');
console.error('Expected path:', distPath);
process.exit(1);
}
console.log('🚀 Starting ContextMax...');
console.log(`📁 Serving from: ${distPath}`);
findAvailablePort(PORT, (availablePort) => {
const server = http.createServer((req, res) => {
const parsedUrl = url.parse(req.url);
let pathname = parsedUrl.pathname;
// Default to index.html for root path
if (pathname === '/') {
pathname = '/index.html';
}
// Security: prevent directory traversal
pathname = pathname.replace(/\.\./g, '');
let filePath = path.join(distPath, pathname);
// Try to serve the file
fs.readFile(filePath, (err, data) => {
if (err) {
// If file not found, try with .html extension
if (!pathname.endsWith('.html')) {
filePath = path.join(distPath, pathname + '.html');
fs.readFile(filePath, (err2, data2) => {
if (err2) {
// For client-side routing, serve index.html
fs.readFile(path.join(distPath, 'index.html'), (err3, data3) => {
if (err3) {
res.writeHead(404, { 'Content-Type': 'text/plain' });
res.end('404 Not Found');
} else {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end(data3);
}
});
} else {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end(data2);
}
});
} else {
// Serve 404
res.writeHead(404, { 'Content-Type': 'text/plain' });
res.end('404 Not Found');
}
} else {
// Success - serve the file with appropriate content type
const mimeType = getMimeType(filePath);
res.writeHead(200, { 'Content-Type': mimeType });
res.end(data);
}
});
});
server.listen(availablePort, HOST, () => {
const serverUrl = `http://${HOST}:${availablePort}`;
console.log(`\n✨ ContextMax is running at: ${serverUrl}`);
console.log('📋 Press Ctrl+C to stop the server\n');
// Open browser
openBrowser(serverUrl);
});
process.on('SIGINT', () => {
console.log('\n👋 Shutting down ContextMax...');
server.close();
process.exit(0);
});
});
}
function showHelp() {
console.log(`
ContextMax - Privacy-first context sets for LLMs
Usage:
npx contextmax [command]
Commands:
run, start Start the ContextMax server (default)
help Show this help message
Environment Variables:
PORT Server port (default: 3000)
HOST Server host (default: localhost)
Examples:
npx contextmax
npx contextmax run
PORT=8080 npx contextmax
`);
}
const command = process.argv[2];
switch (command) {
case undefined:
case 'run':
case 'start':
startServer();
break;
case 'help':
case '--help':
case '-h':
showHelp();
break;
default:
console.error(`Unknown command: ${command}`);
showHelp();
process.exit(1);
}