fontana-setup 0.1.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/index.js +160 -0
- package/package.json +16 -0
package/bin/index.js
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import os from 'os';
|
|
3
|
+
import { execSync, spawn } from 'child_process';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
import ora from 'ora';
|
|
6
|
+
import enquirer from 'enquirer';
|
|
7
|
+
import path from 'path';
|
|
8
|
+
import fs from 'fs';
|
|
9
|
+
|
|
10
|
+
const { prompt } = enquirer;
|
|
11
|
+
|
|
12
|
+
async function checkSystem() {
|
|
13
|
+
console.log(chalk.cyan.bold('\n🖥️ System Check'));
|
|
14
|
+
const spinner = ora('Checking system requirements...').start();
|
|
15
|
+
|
|
16
|
+
// 1. Node.js version
|
|
17
|
+
const nodeVersion = process.version;
|
|
18
|
+
const majorVersion = parseInt(nodeVersion.replace('v', '').split('.')[0], 10);
|
|
19
|
+
|
|
20
|
+
// 2. OS detect
|
|
21
|
+
const platform = os.platform();
|
|
22
|
+
const cpus = os.cpus().length;
|
|
23
|
+
|
|
24
|
+
// 3. RAM (converted from bytes to GB)
|
|
25
|
+
const totalRAM = Math.round(os.totalmem() / 1024 / 1024 / 1024);
|
|
26
|
+
const freeRAM = Math.round(os.freemem() / 1024 / 1024 / 1024);
|
|
27
|
+
|
|
28
|
+
spinner.succeed('System checks passed!');
|
|
29
|
+
console.log(`- ${chalk.bold('Node.js')}: ${nodeVersion} ${majorVersion < 18 ? chalk.red('(Requires v18+)') : chalk.green('OK')}`);
|
|
30
|
+
console.log(`- ${chalk.bold('OS')}: ${platform} (${cpus} cores)`);
|
|
31
|
+
console.log(`- ${chalk.bold('RAM')}: ${totalRAM}GB Total, ${freeRAM}GB Free ${freeRAM < 4 ? chalk.yellow('(Warning: Low memory)') : chalk.green('OK')}\n`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function checkCommand(command) {
|
|
35
|
+
try {
|
|
36
|
+
execSync(`which ${command}`, { stdio: 'ignore' });
|
|
37
|
+
return true;
|
|
38
|
+
} catch {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async function installDependencies() {
|
|
44
|
+
console.log(chalk.cyan.bold('📦 Installing Dependencies'));
|
|
45
|
+
|
|
46
|
+
let spinner = ora('Checking Ollama installation...').start();
|
|
47
|
+
const hasOllama = checkCommand('ollama');
|
|
48
|
+
if (hasOllama) {
|
|
49
|
+
spinner.succeed('Ollama is already installed.');
|
|
50
|
+
} else {
|
|
51
|
+
spinner.warn('Ollama is missing. For your system, please install it securely from https://ollama.ai/download');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
spinner = ora('Setting up the backend...').start();
|
|
55
|
+
|
|
56
|
+
// Resolves backend directory from caller's CWD
|
|
57
|
+
const backendPath = path.resolve(process.cwd(), 'backend');
|
|
58
|
+
if (fs.existsSync(backendPath)) {
|
|
59
|
+
try {
|
|
60
|
+
execSync('npm install', { cwd: backendPath, stdio: 'ignore' });
|
|
61
|
+
spinner.succeed('Backend dependencies installed locally.');
|
|
62
|
+
} catch (e) {
|
|
63
|
+
spinner.fail(`Failed to install backend dependencies: ${e.message}`);
|
|
64
|
+
}
|
|
65
|
+
} else {
|
|
66
|
+
spinner.info('No backend directory found natively. Assuming global or standalone context.');
|
|
67
|
+
}
|
|
68
|
+
console.log();
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async function selectModel() {
|
|
72
|
+
const { model } = await prompt({
|
|
73
|
+
type: 'select',
|
|
74
|
+
name: 'model',
|
|
75
|
+
message: 'Select the primary model for your assistant:',
|
|
76
|
+
choices: [
|
|
77
|
+
{ name: '1. Qwen 7B (Fast, req ~4GB+ RAM)', value: 'qwen' },
|
|
78
|
+
{ name: '2. Qwen 14B (Balanced, req ~8GB+ RAM)', value: 'qwen:14b' },
|
|
79
|
+
{ name: '3. Qwen 32B (Advanced, req ~16GB+ RAM)', value: 'qwen:32b' }
|
|
80
|
+
],
|
|
81
|
+
result(name) {
|
|
82
|
+
if (name.includes('7B')) return 'qwen';
|
|
83
|
+
if (name.includes('14B')) return 'qwen:14b';
|
|
84
|
+
if (name.includes('32B')) return 'qwen:32b';
|
|
85
|
+
return 'qwen';
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
console.log(chalk.magenta.bold(`\n🧠 Pulling requested LLM: ${model}`));
|
|
90
|
+
const spinner = ora(`Running ollama pull ${model}...`).start();
|
|
91
|
+
|
|
92
|
+
try {
|
|
93
|
+
execSync(`ollama pull ${model}`, { stdio: 'pipe' });
|
|
94
|
+
spinner.succeed(`${chalk.green.bold(model)} acquired successfully and ready to inference.\n`);
|
|
95
|
+
} catch (e) {
|
|
96
|
+
spinner.fail(`Failed to pull ${model}. Ensure your Ollama server is running natively! Error: ${e.message}\n`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function getIps() {
|
|
101
|
+
const interfaces = os.networkInterfaces();
|
|
102
|
+
const addresses = [];
|
|
103
|
+
for (const k in interfaces) {
|
|
104
|
+
for (const k2 in interfaces[k]) {
|
|
105
|
+
const address = interfaces[k][k2];
|
|
106
|
+
if (address.family === 'IPv4' && !address.internal) {
|
|
107
|
+
addresses.push(address.address);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return addresses;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
async function startServices() {
|
|
115
|
+
console.log(chalk.cyan.bold('🚀 Starting Backend Server'));
|
|
116
|
+
|
|
117
|
+
const spinner = ora('Initializing Services...').start();
|
|
118
|
+
const backendPath = path.resolve(process.cwd(), 'backend');
|
|
119
|
+
spinner.succeed('Node.js Backend Initialized');
|
|
120
|
+
|
|
121
|
+
const ips = getIps();
|
|
122
|
+
const localUrl = `http://localhost:3000`;
|
|
123
|
+
const networkUrls = ips.map(ip => `http://${ip}:3000`).join('\n ');
|
|
124
|
+
|
|
125
|
+
console.log(chalk.green.bold('\n✨ Fontana Backend interface is live!'));
|
|
126
|
+
console.log(``);
|
|
127
|
+
console.log(`${chalk.bold('Local UI:')} ${chalk.blue(localUrl)}`);
|
|
128
|
+
console.log(`${chalk.bold('LAN / VPN:')} ${chalk.blue(networkUrls)}`);
|
|
129
|
+
console.log(``);
|
|
130
|
+
console.log(chalk.dim('Booting backend subprocess asynchronously...'));
|
|
131
|
+
|
|
132
|
+
if (fs.existsSync(backendPath)) {
|
|
133
|
+
const child = spawn(/^win/.test(process.platform) ? 'npm.cmd' : 'npm', ['run', 'start'], {
|
|
134
|
+
cwd: backendPath,
|
|
135
|
+
stdio: 'inherit'
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
child.on('error', (e) => console.log(chalk.red('\nCould not start backend: ' + e.message)));
|
|
139
|
+
} else {
|
|
140
|
+
console.log(chalk.yellow('\n(Standalone Mode) Ready for external requests via the designated IPs.'));
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async function runInstaller() {
|
|
145
|
+
console.clear();
|
|
146
|
+
console.log(chalk.blue.bold('=============================================='));
|
|
147
|
+
console.log(chalk.white.bold(' Welcome to the Fontana Setup '));
|
|
148
|
+
console.log(chalk.blue.bold('=============================================='));
|
|
149
|
+
|
|
150
|
+
try {
|
|
151
|
+
await checkSystem();
|
|
152
|
+
await installDependencies();
|
|
153
|
+
await selectModel();
|
|
154
|
+
await startServices();
|
|
155
|
+
} catch (error) {
|
|
156
|
+
console.error(chalk.red.bold('\nInstaller Error:'), error);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
runInstaller();
|
package/package.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "fontana-setup",
|
|
3
|
+
"version": "0.1.0-alpha.1",
|
|
4
|
+
"description": "CLI Installer for Fontana",
|
|
5
|
+
"main": "bin/index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": "bin/index.js",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"start": "node ./bin/index.js"
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"chalk": "^5.3.0",
|
|
13
|
+
"enquirer": "^2.4.1",
|
|
14
|
+
"ora": "^7.0.1"
|
|
15
|
+
}
|
|
16
|
+
}
|