@xaidenlabs/uso 1.1.2 → 1.1.3
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 +1 -1
- package/package.json +1 -1
- package/src/commands/create.js +14 -1
- package/src/commands/workflow.js +60 -21
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
The Universal Solana Orchestrator (USO) is a command-line interface designed to streamline the initialization and management of Solana development environments. By automating the installation of the Rust toolchain, Solana CLI, and Anchor framework, USO eliminates the complexity often associated with setting up a Web3 development workspace on Windows, macOS, and Linux.
|
|
4
4
|
|
|
5
|
-
This tool is engineered to support developers at all levels
|
|
5
|
+
This tool is engineered to support developers at all levels, from beginners setting up their first environment to senior engineers managing multiple workstations.
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
package/package.json
CHANGED
package/src/commands/create.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const path = require('path');
|
|
3
|
+
const os = require('os');
|
|
3
4
|
const shell = require('shelljs');
|
|
4
5
|
const { log, spinner } = require('../utils/logger');
|
|
5
|
-
const { getCargoBinPath } = require('../utils/paths');
|
|
6
6
|
|
|
7
7
|
const create = async (projectName) => {
|
|
8
8
|
if (!projectName) {
|
|
@@ -37,6 +37,19 @@ const create = async (projectName) => {
|
|
|
37
37
|
return;
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
// 1b. Add Windows Defender exclusion for the project directory (prevents build blocks)
|
|
41
|
+
if (os.platform() === 'win32') {
|
|
42
|
+
const defSpin = spinner('Adding Windows Defender exclusion...').start();
|
|
43
|
+
const cargoHome = path.join(os.homedir(), '.cargo');
|
|
44
|
+
const cmd = `powershell -Command "Start-Process powershell -ArgumentList '-NoProfile', '-Command', 'Add-MpPreference -ExclusionPath \\\"${targetDir.replace(/\\/g, '\\\\')}\\\"; Add-MpPreference -ExclusionPath \\\"${cargoHome.replace(/\\/g, '\\\\')}\\\"' -Verb RunAs -WindowStyle Hidden -Wait"`;
|
|
45
|
+
const defResult = shell.exec(cmd, { silent: true });
|
|
46
|
+
if (defResult.code === 0) {
|
|
47
|
+
defSpin.succeed('Defender exclusion added (builds won\'t be blocked).');
|
|
48
|
+
} else {
|
|
49
|
+
defSpin.warn('Could not add Defender exclusion. Builds may be blocked by antivirus.');
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
40
53
|
// 2. Customize Project (Rename files and content)
|
|
41
54
|
log.info("✏️ Customizing project details...");
|
|
42
55
|
|
package/src/commands/workflow.js
CHANGED
|
@@ -4,6 +4,17 @@ const fs = require('fs');
|
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const { log, spinner } = require('../utils/logger');
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Adds a Windows Defender exclusion for a given path (requires elevation).
|
|
9
|
+
* Returns true if successful.
|
|
10
|
+
*/
|
|
11
|
+
const addDefenderExclusion = (dirPath) => {
|
|
12
|
+
const normalized = dirPath.replace(/\//g, '\\');
|
|
13
|
+
const cmd = `powershell -Command "Start-Process powershell -ArgumentList '-NoProfile', '-Command', 'Add-MpPreference -ExclusionPath \\\"${normalized}\\\"' -Verb RunAs -WindowStyle Hidden -Wait"`;
|
|
14
|
+
const result = shell.exec(cmd, { silent: true });
|
|
15
|
+
return result.code === 0;
|
|
16
|
+
};
|
|
17
|
+
|
|
7
18
|
const runProxyCommand = async (command, args = []) => {
|
|
8
19
|
// Check if anchor is available
|
|
9
20
|
if (!shell.which('anchor')) {
|
|
@@ -22,18 +33,56 @@ const runProxyCommand = async (command, args = []) => {
|
|
|
22
33
|
return;
|
|
23
34
|
}
|
|
24
35
|
|
|
25
|
-
// Check for Windows privilege error (os error 1314 — symlink creation requires elevation)
|
|
26
36
|
const output = (execution.stderr || '') + (execution.stdout || '');
|
|
27
|
-
const isPrivilegeError = output.includes('os error 1314') || output.includes('A required privilege is not held by the client');
|
|
28
37
|
|
|
38
|
+
// --- Error Detection & Auto-Fix ---
|
|
39
|
+
|
|
40
|
+
// 1. Windows Defender / Application Control blocking build scripts (os error 4551)
|
|
41
|
+
const isDefenderBlock = output.includes('os error 4551') || output.includes('Application Control policy has blocked');
|
|
42
|
+
if (isDefenderBlock && os.platform() === 'win32') {
|
|
43
|
+
log.warn("⚠️ Windows Defender is blocking Cargo build scripts.");
|
|
44
|
+
log.info("🛡️ Adding project directory to Defender exclusions...\n");
|
|
45
|
+
|
|
46
|
+
const cwd = process.cwd();
|
|
47
|
+
const cargoHome = path.join(os.homedir(), '.cargo');
|
|
48
|
+
|
|
49
|
+
const success = addDefenderExclusion(cwd) && addDefenderExclusion(cargoHome);
|
|
50
|
+
|
|
51
|
+
if (success) {
|
|
52
|
+
log.success("✅ Defender exclusions added.");
|
|
53
|
+
log.info("🔄 Cleaning build cache and retrying...\n");
|
|
54
|
+
|
|
55
|
+
// Clean stale blocked artifacts
|
|
56
|
+
shell.exec('cargo clean', { silent: true });
|
|
57
|
+
|
|
58
|
+
// Retry the command
|
|
59
|
+
const retry = shell.exec(fullCommand);
|
|
60
|
+
if (retry.code === 0) {
|
|
61
|
+
log.success(`✅ '${command}' completed successfully.`);
|
|
62
|
+
} else {
|
|
63
|
+
log.error(`❌ '${command}' still failed after adding exclusions.`);
|
|
64
|
+
log.warn("👉 Try running your terminal as Administrator.");
|
|
65
|
+
}
|
|
66
|
+
} else {
|
|
67
|
+
log.error("❌ Could not add Defender exclusions. Was the UAC prompt declined?");
|
|
68
|
+
log.info("👉 Manually add exclusions in Windows Security > Virus & threat protection > Exclusions:");
|
|
69
|
+
log.info(` - ${cwd}`);
|
|
70
|
+
log.info(` - ${cargoHome}`);
|
|
71
|
+
}
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// 2. Privilege error (os error 1314 — symlink creation requires elevation)
|
|
76
|
+
const isPrivilegeError = output.includes('os error 1314') || output.includes('A required privilege is not held by the client');
|
|
29
77
|
if (isPrivilegeError && os.platform() === 'win32') {
|
|
30
78
|
log.warn("⚠️ Windows requires Administrator privileges for this operation.");
|
|
31
79
|
log.info("🔑 Requesting elevated access...\n");
|
|
32
80
|
|
|
33
81
|
await runElevatedWithProgress(command, args);
|
|
34
|
-
|
|
35
|
-
log.error(`❌ '${command}' failed.`);
|
|
82
|
+
return;
|
|
36
83
|
}
|
|
84
|
+
|
|
85
|
+
log.error(`❌ '${command}' failed.`);
|
|
37
86
|
};
|
|
38
87
|
|
|
39
88
|
/**
|
|
@@ -44,18 +93,14 @@ const runProxyCommand = async (command, args = []) => {
|
|
|
44
93
|
const runElevatedWithProgress = (command, args = []) => {
|
|
45
94
|
return new Promise((resolve) => {
|
|
46
95
|
const tmpDir = os.tmpdir();
|
|
47
|
-
const
|
|
48
|
-
const
|
|
96
|
+
const ts = Date.now();
|
|
97
|
+
const logFile = path.join(tmpDir, `uso-elevated-${ts}.log`);
|
|
98
|
+
const doneFile = path.join(tmpDir, `uso-elevated-${ts}.done`);
|
|
49
99
|
const cwd = process.cwd();
|
|
50
100
|
|
|
51
101
|
// Write an empty log file so we can start reading immediately
|
|
52
102
|
fs.writeFileSync(logFile, '', 'utf8');
|
|
53
103
|
|
|
54
|
-
// Build the elevated command:
|
|
55
|
-
// 1. cd into the user's project directory
|
|
56
|
-
// 2. Run anchor <command>
|
|
57
|
-
// 3. Write exit code to a .done file so we know it finished
|
|
58
|
-
// 4. All stdout/stderr piped to the log file
|
|
59
104
|
const anchorCmd = `anchor ${command} ${args.join(' ')}`;
|
|
60
105
|
const innerScript = `
|
|
61
106
|
Set-Location '${cwd.replace(/'/g, "''")}';
|
|
@@ -67,7 +112,6 @@ const runElevatedWithProgress = (command, args = []) => {
|
|
|
67
112
|
// Launch hidden elevated process
|
|
68
113
|
const elevateCmd = `powershell -Command "Start-Process powershell -ArgumentList '-NoProfile', '-ExecutionPolicy', 'Bypass', '-Command', '${innerScript.replace(/'/g, "''")}' -Verb RunAs -WindowStyle Hidden"`;
|
|
69
114
|
|
|
70
|
-
// This triggers the UAC prompt (that one still shows — it's Windows security, can't bypass it)
|
|
71
115
|
const elevateResult = shell.exec(elevateCmd, { silent: true });
|
|
72
116
|
|
|
73
117
|
if (elevateResult.code !== 0) {
|
|
@@ -79,18 +123,15 @@ const runElevatedWithProgress = (command, args = []) => {
|
|
|
79
123
|
// Now poll the log file and show progress
|
|
80
124
|
const spin = spinner('Building with elevated privileges...').start();
|
|
81
125
|
let lastSize = 0;
|
|
82
|
-
let lastLine = '';
|
|
83
126
|
|
|
84
127
|
const pollInterval = setInterval(() => {
|
|
85
128
|
try {
|
|
86
|
-
// Check if process finished
|
|
87
129
|
if (fs.existsSync(doneFile)) {
|
|
88
130
|
clearInterval(pollInterval);
|
|
89
131
|
|
|
90
132
|
const exitCode = fs.readFileSync(doneFile, 'utf8').trim();
|
|
91
133
|
const fullLog = fs.readFileSync(logFile, 'utf8');
|
|
92
134
|
|
|
93
|
-
// Cleanup temp files
|
|
94
135
|
try { fs.unlinkSync(logFile); } catch (e) { /* ignore */ }
|
|
95
136
|
try { fs.unlinkSync(doneFile); } catch (e) { /* ignore */ }
|
|
96
137
|
|
|
@@ -100,7 +141,6 @@ const runElevatedWithProgress = (command, args = []) => {
|
|
|
100
141
|
spin.fail(`'${command}' failed (elevated, exit code: ${exitCode}).`);
|
|
101
142
|
}
|
|
102
143
|
|
|
103
|
-
// Print the captured output
|
|
104
144
|
if (fullLog.trim()) {
|
|
105
145
|
console.log('\n' + fullLog.trim());
|
|
106
146
|
}
|
|
@@ -109,25 +149,24 @@ const runElevatedWithProgress = (command, args = []) => {
|
|
|
109
149
|
return;
|
|
110
150
|
}
|
|
111
151
|
|
|
112
|
-
// Read new content
|
|
152
|
+
// Read new content for live progress
|
|
113
153
|
const stats = fs.statSync(logFile);
|
|
114
154
|
if (stats.size > lastSize) {
|
|
115
155
|
const content = fs.readFileSync(logFile, 'utf8');
|
|
116
156
|
const lines = content.trim().split('\n').filter(l => l.trim());
|
|
117
157
|
if (lines.length > 0) {
|
|
118
|
-
lastLine = lines[lines.length - 1].trim();
|
|
119
|
-
// Show the last meaningful line as spinner text
|
|
158
|
+
const lastLine = lines[lines.length - 1].trim();
|
|
120
159
|
const displayLine = lastLine.length > 70 ? lastLine.substring(0, 67) + '...' : lastLine;
|
|
121
160
|
spin.text = displayLine;
|
|
122
161
|
}
|
|
123
162
|
lastSize = stats.size;
|
|
124
163
|
}
|
|
125
164
|
} catch (e) {
|
|
126
|
-
// File might not exist yet or be locked
|
|
165
|
+
// File might not exist yet or be locked
|
|
127
166
|
}
|
|
128
167
|
}, 500);
|
|
129
168
|
|
|
130
|
-
// Safety timeout: 10 minutes
|
|
169
|
+
// Safety timeout: 10 minutes
|
|
131
170
|
setTimeout(() => {
|
|
132
171
|
if (!fs.existsSync(doneFile)) {
|
|
133
172
|
clearInterval(pollInterval);
|