plusui-native 0.2.0
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/README.md +162 -0
- package/package.json +36 -0
- package/src/assets/icon-generator.js +251 -0
- package/src/assets/resource-embedder.js +351 -0
- package/src/doctor/detectors/cmake.js +84 -0
- package/src/doctor/detectors/compiler.js +145 -0
- package/src/doctor/detectors/just.js +45 -0
- package/src/doctor/detectors/nodejs.js +57 -0
- package/src/doctor/detectors/webview2.js +66 -0
- package/src/doctor/index.js +184 -0
- package/src/doctor/installers/linux.js +121 -0
- package/src/doctor/installers/macos.js +123 -0
- package/src/doctor/installers/windows.js +117 -0
- package/src/doctor/reporter.js +219 -0
- package/src/index.js +904 -0
- package/templates/base/Justfile +115 -0
- package/templates/base/README.md.template +168 -0
- package/templates/base/assets/README.md +88 -0
- package/templates/base/assets/icon.png +0 -0
- package/templates/manager.js +217 -0
- package/templates/react/.vscode/c_cpp_properties.json +24 -0
- package/templates/react/CMakeLists.txt.template +151 -0
- package/templates/react/frontend/index.html +12 -0
- package/templates/react/frontend/package.json.template +24 -0
- package/templates/react/frontend/src/App.tsx +134 -0
- package/templates/react/frontend/src/main.tsx +10 -0
- package/templates/react/frontend/src/styles/app.css +140 -0
- package/templates/react/frontend/tsconfig.json +21 -0
- package/templates/react/frontend/tsconfig.node.json +11 -0
- package/templates/react/frontend/vite.config.ts +14 -0
- package/templates/react/main.cpp.template +201 -0
- package/templates/react/package.json.template +23 -0
- package/templates/solid/.vscode/c_cpp_properties.json +24 -0
- package/templates/solid/CMakeLists.txt.template +151 -0
- package/templates/solid/frontend/index.html +12 -0
- package/templates/solid/frontend/package.json.template +21 -0
- package/templates/solid/frontend/src/App.tsx +133 -0
- package/templates/solid/frontend/src/main.tsx +5 -0
- package/templates/solid/frontend/src/styles/app.css +140 -0
- package/templates/solid/frontend/tsconfig.json +22 -0
- package/templates/solid/frontend/tsconfig.node.json +11 -0
- package/templates/solid/frontend/vite.config.ts +14 -0
- package/templates/solid/main.cpp.template +192 -0
- package/templates/solid/package.json.template +23 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { existsSync } from 'fs';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
|
|
4
|
+
// This is a check for WebView2 headers in the expected locations
|
|
5
|
+
// Currently focused on Windows as WebView2 is Windows-specific
|
|
6
|
+
export async function detectWebView2() {
|
|
7
|
+
if (process.platform !== 'win32') {
|
|
8
|
+
return {
|
|
9
|
+
found: true,
|
|
10
|
+
valid: true,
|
|
11
|
+
platform: process.platform,
|
|
12
|
+
message: 'Not applicable (Windows only)'
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Check common locations
|
|
17
|
+
// By default, PlusUI looks for vendor/webview.h and friends in Core/vendor
|
|
18
|
+
// But we can also check for the SDK installation if possible, though that's harder without a project context.
|
|
19
|
+
// Instead, we can check if we can find the NuGet package installed globally or if the headers are present in a known location.
|
|
20
|
+
// Since this is a CLI tool, we might not know where the user's project is.
|
|
21
|
+
// However, for a general environment check, we might just skip deep validation or check for the Windows SDK.
|
|
22
|
+
|
|
23
|
+
// For now, we will return a soft pass but with a note if we can't easily verify.
|
|
24
|
+
// But the request is to add it to doctor.
|
|
25
|
+
|
|
26
|
+
// Let's check if the generic 'webview2.h' is available in a common include path if environment variables are set.
|
|
27
|
+
// Or check for a directory where we usually download dependencies.
|
|
28
|
+
|
|
29
|
+
// Realistically, for PlusUI CLI usage, we care if the user can build.
|
|
30
|
+
// The downloading happens at the project level (or SDK level).
|
|
31
|
+
|
|
32
|
+
// Let's return true for now but signal that we are checking "WebView2 Runtime" mostly associated with the OS.
|
|
33
|
+
const runtimeCheck = await checkWebView2Runtime();
|
|
34
|
+
|
|
35
|
+
return {
|
|
36
|
+
found: runtimeCheck.installed,
|
|
37
|
+
version: runtimeCheck.version || 'unknown',
|
|
38
|
+
valid: runtimeCheck.installed,
|
|
39
|
+
requiredVersion: 'Any'
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async function checkWebView2Runtime() {
|
|
44
|
+
// Simple check for the registry key often present
|
|
45
|
+
try {
|
|
46
|
+
const { execSync } = await import('child_process');
|
|
47
|
+
// PowerShell command to check registry
|
|
48
|
+
const cmd = 'powershell -Command "Get-ItemProperty -Path \'HKLM:\\SOFTWARE\\WOW6432Node\\Microsoft\\EdgeUpdate\\Clients\\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}\' -Name pv -ErrorAction SilentlyContinue | Select-Object -ExpandProperty pv"';
|
|
49
|
+
const output = execSync(cmd, { encoding: 'utf8' }).trim();
|
|
50
|
+
|
|
51
|
+
if (output) {
|
|
52
|
+
return { installed: true, version: output };
|
|
53
|
+
}
|
|
54
|
+
// Check current user
|
|
55
|
+
const cmdUser = 'powershell -Command "Get-ItemProperty -Path \'HKCU:\\Software\\Microsoft\\EdgeUpdate\\Clients\\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}\' -Name pv -ErrorAction SilentlyContinue | Select-Object -ExpandProperty pv"';
|
|
56
|
+
const outputUser = execSync(cmdUser, { encoding: 'utf8' }).trim();
|
|
57
|
+
|
|
58
|
+
if (outputUser) {
|
|
59
|
+
return { installed: true, version: outputUser };
|
|
60
|
+
}
|
|
61
|
+
} catch (e) {
|
|
62
|
+
// Ignore
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return { installed: false }; // Conservative
|
|
66
|
+
}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { detectCMake } from './detectors/cmake.js';
|
|
2
|
+
import { detectNodeJS } from './detectors/nodejs.js';
|
|
3
|
+
import { detectCompiler } from './detectors/compiler.js';
|
|
4
|
+
import { detectJust } from './detectors/just.js';
|
|
5
|
+
import { detectWebView2 } from './detectors/webview2.js';
|
|
6
|
+
import { DoctorReporter } from './reporter.js';
|
|
7
|
+
import * as windowsInstaller from './installers/windows.js';
|
|
8
|
+
import * as macosInstaller from './installers/macos.js';
|
|
9
|
+
import * as linuxInstaller from './installers/linux.js';
|
|
10
|
+
import chalk from 'chalk';
|
|
11
|
+
|
|
12
|
+
export class EnvironmentDoctor {
|
|
13
|
+
constructor() {
|
|
14
|
+
this.platform = process.platform;
|
|
15
|
+
this.installer = this.getInstaller();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
getInstaller() {
|
|
19
|
+
switch (this.platform) {
|
|
20
|
+
case 'win32':
|
|
21
|
+
return windowsInstaller;
|
|
22
|
+
case 'darwin':
|
|
23
|
+
return macosInstaller;
|
|
24
|
+
default:
|
|
25
|
+
return linuxInstaller;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async diagnose(options = {}) {
|
|
30
|
+
const { quick = false } = options;
|
|
31
|
+
|
|
32
|
+
console.log(chalk.bold('\nPlusUI Environment Doctor\n'));
|
|
33
|
+
console.log('Checking your development environment...\n');
|
|
34
|
+
|
|
35
|
+
// Run all detections in parallel
|
|
36
|
+
const [nodejs, cmake, compiler, just, webview2] = await Promise.all([
|
|
37
|
+
detectNodeJS(),
|
|
38
|
+
detectCMake(),
|
|
39
|
+
detectCompiler(),
|
|
40
|
+
detectJust(),
|
|
41
|
+
detectWebView2()
|
|
42
|
+
]);
|
|
43
|
+
|
|
44
|
+
const results = {
|
|
45
|
+
platform: this.platform,
|
|
46
|
+
nodejs,
|
|
47
|
+
cmake,
|
|
48
|
+
compiler,
|
|
49
|
+
compiler,
|
|
50
|
+
just,
|
|
51
|
+
webview2,
|
|
52
|
+
hasIssues: false
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
// Check if there are any issues
|
|
56
|
+
results.hasIssues =
|
|
57
|
+
!nodejs.found || !nodejs.valid ||
|
|
58
|
+
!cmake.found || !cmake.valid ||
|
|
59
|
+
!compiler.found || !compiler.valid ||
|
|
60
|
+
!compiler.found || !compiler.valid ||
|
|
61
|
+
!just.found || !just.valid ||
|
|
62
|
+
!webview2.found;
|
|
63
|
+
|
|
64
|
+
return results;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async autoFix(results) {
|
|
68
|
+
console.log(chalk.bold('\nAttempting automatic installation...\n'));
|
|
69
|
+
|
|
70
|
+
const fixes = [];
|
|
71
|
+
|
|
72
|
+
// Fix Node.js if needed
|
|
73
|
+
if (!results.nodejs.found || !results.nodejs.valid) {
|
|
74
|
+
fixes.push(this.fixTool('nodejs', 'Node.js'));
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Fix CMake if needed
|
|
78
|
+
if (!results.cmake.found || !results.cmake.valid) {
|
|
79
|
+
fixes.push(this.fixTool('cmake', 'CMake'));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Fix compiler if needed
|
|
83
|
+
if (!results.compiler.found || !results.compiler.valid) {
|
|
84
|
+
const compilerTool = this.platform === 'win32' ? 'visualstudio' :
|
|
85
|
+
this.platform === 'darwin' ? 'xcode' : 'buildtools';
|
|
86
|
+
fixes.push(this.fixTool(compilerTool, results.compiler.name));
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Fix just if needed
|
|
90
|
+
if (!results.just.found || !results.just.valid) {
|
|
91
|
+
fixes.push(this.fixTool('just', 'Just Command Runner'));
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (!results.webview2.found && results.webview2.platform === 'win32') {
|
|
95
|
+
fixes.push(this.fixTool('webview2', 'WebView2 Runtime'));
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const fixResults = await Promise.all(fixes);
|
|
99
|
+
|
|
100
|
+
// Print results
|
|
101
|
+
console.log(chalk.bold('\nInstallation Results\n'));
|
|
102
|
+
console.log('====================\n');
|
|
103
|
+
|
|
104
|
+
for (const result of fixResults) {
|
|
105
|
+
if (result.success) {
|
|
106
|
+
console.log(chalk.green(`✓ ${result.name} installed successfully`));
|
|
107
|
+
} else {
|
|
108
|
+
console.log(chalk.red(`✗ ${result.name} installation failed`));
|
|
109
|
+
if (result.message) {
|
|
110
|
+
console.log(chalk.gray(` ${result.message}`));
|
|
111
|
+
}
|
|
112
|
+
if (result.instructions) {
|
|
113
|
+
console.log(chalk.yellow('\n Manual installation required:'));
|
|
114
|
+
result.instructions.forEach(line => {
|
|
115
|
+
console.log(chalk.gray(` ${line}`));
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Re-run diagnosis to verify
|
|
122
|
+
console.log(chalk.bold('\n\nVerifying installation...\n'));
|
|
123
|
+
const newResults = await this.diagnose();
|
|
124
|
+
|
|
125
|
+
return newResults;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async fixTool(toolName, displayName) {
|
|
129
|
+
try {
|
|
130
|
+
const result = await this.installer.installTool(toolName);
|
|
131
|
+
return {
|
|
132
|
+
name: displayName,
|
|
133
|
+
...result
|
|
134
|
+
};
|
|
135
|
+
} catch (error) {
|
|
136
|
+
return {
|
|
137
|
+
name: displayName,
|
|
138
|
+
success: false,
|
|
139
|
+
error: error.message
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async quickCheck() {
|
|
145
|
+
// Lightweight check for use in `plusui create`
|
|
146
|
+
const [cmake, compiler, just] = await Promise.all([
|
|
147
|
+
detectCMake(),
|
|
148
|
+
detectCompiler(),
|
|
149
|
+
detectJust()
|
|
150
|
+
]);
|
|
151
|
+
|
|
152
|
+
const ready = cmake.found && cmake.valid && compiler.found && compiler.valid && just.found && just.valid;
|
|
153
|
+
|
|
154
|
+
return {
|
|
155
|
+
ready,
|
|
156
|
+
cmake,
|
|
157
|
+
compiler,
|
|
158
|
+
just
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Export for use in CLI
|
|
164
|
+
export async function runDoctor(options = {}) {
|
|
165
|
+
const { fix = false, json = false, quick = false } = options;
|
|
166
|
+
|
|
167
|
+
const doctor = new EnvironmentDoctor();
|
|
168
|
+
const results = await doctor.diagnose({ quick });
|
|
169
|
+
|
|
170
|
+
if (json) {
|
|
171
|
+
console.log(JSON.stringify(results, null, 2));
|
|
172
|
+
return results;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const reporter = new DoctorReporter();
|
|
176
|
+
const allGood = reporter.formatResults(results);
|
|
177
|
+
|
|
178
|
+
if (fix && results.hasIssues) {
|
|
179
|
+
const fixedResults = await doctor.autoFix(results);
|
|
180
|
+
return fixedResults;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return results;
|
|
184
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { execSync } from 'child_process';
|
|
2
|
+
|
|
3
|
+
async function tryCommand(command) {
|
|
4
|
+
try {
|
|
5
|
+
const output = execSync(command, {
|
|
6
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
7
|
+
encoding: 'utf8',
|
|
8
|
+
timeout: 30000
|
|
9
|
+
}).trim();
|
|
10
|
+
return { success: true, output };
|
|
11
|
+
} catch (error) {
|
|
12
|
+
return { success: false, error };
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async function detectPackageManager() {
|
|
17
|
+
const managers = [
|
|
18
|
+
{ name: 'apt', check: 'apt --version' },
|
|
19
|
+
{ name: 'dnf', check: 'dnf --version' },
|
|
20
|
+
{ name: 'pacman', check: 'pacman --version' },
|
|
21
|
+
{ name: 'zypper', check: 'zypper --version' }
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
for (const pm of managers) {
|
|
25
|
+
const result = await tryCommand(pm.check);
|
|
26
|
+
if (result.success) {
|
|
27
|
+
return pm.name;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const INSTALL_COMMANDS = {
|
|
35
|
+
apt: {
|
|
36
|
+
cmake: 'sudo apt install -y cmake',
|
|
37
|
+
buildtools: 'sudo apt install -y build-essential',
|
|
38
|
+
nodejs: 'sudo apt install -y nodejs npm'
|
|
39
|
+
},
|
|
40
|
+
dnf: {
|
|
41
|
+
cmake: 'sudo dnf install -y cmake',
|
|
42
|
+
buildtools: 'sudo dnf groupinstall -y "Development Tools"',
|
|
43
|
+
nodejs: 'sudo dnf install -y nodejs'
|
|
44
|
+
},
|
|
45
|
+
pacman: {
|
|
46
|
+
cmake: 'sudo pacman -S --noconfirm cmake',
|
|
47
|
+
buildtools: 'sudo pacman -S --noconfirm base-devel',
|
|
48
|
+
nodejs: 'sudo pacman -S --noconfirm nodejs npm'
|
|
49
|
+
},
|
|
50
|
+
zypper: {
|
|
51
|
+
cmake: 'sudo zypper install -y cmake',
|
|
52
|
+
buildtools: 'sudo zypper install -y -t pattern devel_basis',
|
|
53
|
+
nodejs: 'sudo zypper install -y nodejs npm'
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const TOOL_NAMES = {
|
|
58
|
+
cmake: 'CMake',
|
|
59
|
+
buildtools: 'Build Tools (gcc/g++)',
|
|
60
|
+
nodejs: 'Node.js'
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export async function installTool(toolName) {
|
|
64
|
+
const name = TOOL_NAMES[toolName] || toolName;
|
|
65
|
+
const pm = await detectPackageManager();
|
|
66
|
+
|
|
67
|
+
if (!pm) {
|
|
68
|
+
return {
|
|
69
|
+
success: false,
|
|
70
|
+
autoInstallAvailable: false,
|
|
71
|
+
message: 'Could not detect package manager (apt, dnf, pacman, zypper)',
|
|
72
|
+
instructions: [
|
|
73
|
+
`Please install ${name} manually using your distribution's package manager`
|
|
74
|
+
]
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const command = INSTALL_COMMANDS[pm]?.[toolName];
|
|
79
|
+
|
|
80
|
+
if (!command) {
|
|
81
|
+
return {
|
|
82
|
+
success: false,
|
|
83
|
+
error: `No install command for ${toolName} on ${pm}`
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
console.log(`Installing ${name} via ${pm}...`);
|
|
88
|
+
console.log(`Running: ${command}`);
|
|
89
|
+
|
|
90
|
+
const result = await tryCommand(command);
|
|
91
|
+
|
|
92
|
+
if (result.success) {
|
|
93
|
+
return {
|
|
94
|
+
success: true,
|
|
95
|
+
message: `${name} installed successfully`,
|
|
96
|
+
output: result.output
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return {
|
|
101
|
+
success: false,
|
|
102
|
+
autoInstallAvailable: true,
|
|
103
|
+
error: result.error,
|
|
104
|
+
message: `Failed to install ${name} automatically`,
|
|
105
|
+
manualCommand: command
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function getInstallInstructions(toolName) {
|
|
110
|
+
const name = TOOL_NAMES[toolName] || toolName;
|
|
111
|
+
|
|
112
|
+
return {
|
|
113
|
+
name,
|
|
114
|
+
instructions: [
|
|
115
|
+
'Debian/Ubuntu: ' + (INSTALL_COMMANDS.apt[toolName] || 'N/A'),
|
|
116
|
+
'Fedora/RHEL: ' + (INSTALL_COMMANDS.dnf[toolName] || 'N/A'),
|
|
117
|
+
'Arch Linux: ' + (INSTALL_COMMANDS.pacman[toolName] || 'N/A'),
|
|
118
|
+
'openSUSE: ' + (INSTALL_COMMANDS.zypper[toolName] || 'N/A')
|
|
119
|
+
]
|
|
120
|
+
};
|
|
121
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { execSync } from 'child_process';
|
|
2
|
+
|
|
3
|
+
async function tryCommand(command) {
|
|
4
|
+
try {
|
|
5
|
+
const output = execSync(command, {
|
|
6
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
7
|
+
encoding: 'utf8',
|
|
8
|
+
timeout: 30000
|
|
9
|
+
}).trim();
|
|
10
|
+
return { success: true, output };
|
|
11
|
+
} catch (error) {
|
|
12
|
+
return { success: false, error };
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async function checkHomebrew() {
|
|
17
|
+
const result = await tryCommand('brew --version');
|
|
18
|
+
return result.success;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const INSTALL_COMMANDS = {
|
|
22
|
+
cmake: {
|
|
23
|
+
command: 'brew install cmake',
|
|
24
|
+
manual: 'https://cmake.org/download/',
|
|
25
|
+
name: 'CMake'
|
|
26
|
+
},
|
|
27
|
+
nodejs: {
|
|
28
|
+
command: 'brew install node',
|
|
29
|
+
manual: 'https://nodejs.org/',
|
|
30
|
+
name: 'Node.js'
|
|
31
|
+
},
|
|
32
|
+
xcode: {
|
|
33
|
+
command: 'xcode-select --install',
|
|
34
|
+
manual: 'https://developer.apple.com/xcode/',
|
|
35
|
+
name: 'Xcode Command Line Tools'
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export async function installTool(toolName) {
|
|
40
|
+
const tool = INSTALL_COMMANDS[toolName];
|
|
41
|
+
|
|
42
|
+
if (!tool) {
|
|
43
|
+
return {
|
|
44
|
+
success: false,
|
|
45
|
+
error: `Unknown tool: ${toolName}`
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// For Xcode, we can always try xcode-select --install
|
|
50
|
+
if (toolName === 'xcode') {
|
|
51
|
+
console.log(`Installing ${tool.name}...`);
|
|
52
|
+
const result = await tryCommand(tool.command);
|
|
53
|
+
|
|
54
|
+
if (result.success) {
|
|
55
|
+
return {
|
|
56
|
+
success: true,
|
|
57
|
+
message: `${tool.name} installation started. Follow the prompts.`,
|
|
58
|
+
output: result.output
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return {
|
|
63
|
+
success: false,
|
|
64
|
+
error: result.error,
|
|
65
|
+
message: `Failed to install ${tool.name}`,
|
|
66
|
+
manual: tool.manual
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Check if Homebrew is available
|
|
71
|
+
const hasHomebrew = await checkHomebrew();
|
|
72
|
+
|
|
73
|
+
if (!hasHomebrew) {
|
|
74
|
+
return {
|
|
75
|
+
success: false,
|
|
76
|
+
autoInstallAvailable: false,
|
|
77
|
+
manual: tool.manual,
|
|
78
|
+
message: 'Homebrew is not installed. Install Homebrew first from: https://brew.sh',
|
|
79
|
+
instructions: [
|
|
80
|
+
'Install Homebrew:',
|
|
81
|
+
'/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"',
|
|
82
|
+
'',
|
|
83
|
+
`Then install ${tool.name}:`,
|
|
84
|
+
tool.command
|
|
85
|
+
]
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Attempt auto-installation
|
|
90
|
+
console.log(`Installing ${tool.name} via Homebrew...`);
|
|
91
|
+
const result = await tryCommand(tool.command);
|
|
92
|
+
|
|
93
|
+
if (result.success) {
|
|
94
|
+
return {
|
|
95
|
+
success: true,
|
|
96
|
+
message: `${tool.name} installed successfully`,
|
|
97
|
+
output: result.output
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return {
|
|
102
|
+
success: false,
|
|
103
|
+
autoInstallAvailable: true,
|
|
104
|
+
error: result.error,
|
|
105
|
+
message: `Failed to install ${tool.name} automatically`,
|
|
106
|
+
manual: tool.manual,
|
|
107
|
+
downloadUrl: tool.manual
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export function getInstallInstructions(toolName) {
|
|
112
|
+
const tool = INSTALL_COMMANDS[toolName];
|
|
113
|
+
if (!tool) return null;
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
name: tool.name,
|
|
117
|
+
manual: tool.manual,
|
|
118
|
+
instructions: [
|
|
119
|
+
`Install via Homebrew: ${tool.command}`,
|
|
120
|
+
`Or download from: ${tool.manual}`
|
|
121
|
+
]
|
|
122
|
+
};
|
|
123
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { execSync } from 'child_process';
|
|
2
|
+
|
|
3
|
+
async function tryCommand(command) {
|
|
4
|
+
try {
|
|
5
|
+
const output = execSync(command, {
|
|
6
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
7
|
+
encoding: 'utf8',
|
|
8
|
+
timeout: 30000
|
|
9
|
+
}).trim();
|
|
10
|
+
return { success: true, output };
|
|
11
|
+
} catch (error) {
|
|
12
|
+
return { success: false, error };
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async function checkWinget() {
|
|
17
|
+
const result = await tryCommand('winget --version');
|
|
18
|
+
return result.success;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const INSTALL_COMMANDS = {
|
|
22
|
+
cmake: {
|
|
23
|
+
command: 'winget install -e --id Kitware.CMake',
|
|
24
|
+
manual: 'https://cmake.org/download/',
|
|
25
|
+
name: 'CMake'
|
|
26
|
+
},
|
|
27
|
+
nodejs: {
|
|
28
|
+
command: 'winget install -e --id OpenJS.NodeJS',
|
|
29
|
+
manual: 'https://nodejs.org/',
|
|
30
|
+
name: 'Node.js'
|
|
31
|
+
},
|
|
32
|
+
visualstudio: {
|
|
33
|
+
command: null, // Too complex for auto-install
|
|
34
|
+
manual: 'https://visualstudio.microsoft.com/downloads/',
|
|
35
|
+
name: 'Visual Studio 2022',
|
|
36
|
+
instructions: [
|
|
37
|
+
'1. Download Visual Studio 2022 Community (free)',
|
|
38
|
+
'2. Run the installer',
|
|
39
|
+
'3. Select "Desktop development with C++" workload',
|
|
40
|
+
'4. Ensure these components are included:',
|
|
41
|
+
' - MSVC v143 - VS 2022 C++ x64/x86 build tools',
|
|
42
|
+
' - Windows 11 SDK (10.0.22621.0 or later)',
|
|
43
|
+
' - CMake tools for Windows'
|
|
44
|
+
]
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export async function installTool(toolName) {
|
|
49
|
+
const tool = INSTALL_COMMANDS[toolName];
|
|
50
|
+
|
|
51
|
+
if (!tool) {
|
|
52
|
+
return {
|
|
53
|
+
success: false,
|
|
54
|
+
error: `Unknown tool: ${toolName}`
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Check if winget is available
|
|
59
|
+
const hasWinget = await checkWinget();
|
|
60
|
+
|
|
61
|
+
if (!hasWinget && tool.command) {
|
|
62
|
+
return {
|
|
63
|
+
success: false,
|
|
64
|
+
autoInstallAvailable: false,
|
|
65
|
+
manual: tool.manual,
|
|
66
|
+
message: 'winget is not available. Please install manually.',
|
|
67
|
+
downloadUrl: tool.manual
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// If no auto-install command, provide manual instructions
|
|
72
|
+
if (!tool.command) {
|
|
73
|
+
return {
|
|
74
|
+
success: false,
|
|
75
|
+
autoInstallAvailable: false,
|
|
76
|
+
manual: tool.manual,
|
|
77
|
+
instructions: tool.instructions,
|
|
78
|
+
message: `${tool.name} requires manual installation`,
|
|
79
|
+
downloadUrl: tool.manual
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Attempt auto-installation
|
|
84
|
+
console.log(`Installing ${tool.name} via winget...`);
|
|
85
|
+
const result = await tryCommand(tool.command);
|
|
86
|
+
|
|
87
|
+
if (result.success) {
|
|
88
|
+
return {
|
|
89
|
+
success: true,
|
|
90
|
+
message: `${tool.name} installed successfully`,
|
|
91
|
+
output: result.output
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return {
|
|
96
|
+
success: false,
|
|
97
|
+
autoInstallAvailable: true,
|
|
98
|
+
error: result.error,
|
|
99
|
+
message: `Failed to install ${tool.name} automatically`,
|
|
100
|
+
manual: tool.manual,
|
|
101
|
+
downloadUrl: tool.manual
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function getInstallInstructions(toolName) {
|
|
106
|
+
const tool = INSTALL_COMMANDS[toolName];
|
|
107
|
+
if (!tool) return null;
|
|
108
|
+
|
|
109
|
+
return {
|
|
110
|
+
name: tool.name,
|
|
111
|
+
manual: tool.manual,
|
|
112
|
+
instructions: tool.instructions || [
|
|
113
|
+
`Install via winget: ${tool.command}`,
|
|
114
|
+
`Or download from: ${tool.manual}`
|
|
115
|
+
]
|
|
116
|
+
};
|
|
117
|
+
}
|