@xaidenlabs/uso 1.1.1 → 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 +147 -13
- package/templates/default/programs/my-project/Cargo.lock +0 -1471
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
|
@@ -1,7 +1,20 @@
|
|
|
1
1
|
const shell = require('shelljs');
|
|
2
2
|
const os = require('os');
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
3
5
|
const { log, spinner } = require('../utils/logger');
|
|
4
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
|
+
|
|
5
18
|
const runProxyCommand = async (command, args = []) => {
|
|
6
19
|
// Check if anchor is available
|
|
7
20
|
if (!shell.which('anchor')) {
|
|
@@ -20,27 +33,148 @@ const runProxyCommand = async (command, args = []) => {
|
|
|
20
33
|
return;
|
|
21
34
|
}
|
|
22
35
|
|
|
23
|
-
// Check for Windows privilege error (os error 1314 — symlink creation requires elevation)
|
|
24
36
|
const output = (execution.stderr || '') + (execution.stdout || '');
|
|
25
|
-
const isPrivilegeError = output.includes('os error 1314') || output.includes('A required privilege is not held by the client');
|
|
26
37
|
|
|
27
|
-
|
|
28
|
-
log.warn("⚠️ Windows requires Administrator privileges for this operation.");
|
|
29
|
-
log.info("🔑 Requesting elevated access...");
|
|
38
|
+
// --- Error Detection & Auto-Fix ---
|
|
30
39
|
|
|
31
|
-
|
|
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");
|
|
32
45
|
|
|
33
|
-
const
|
|
46
|
+
const cwd = process.cwd();
|
|
47
|
+
const cargoHome = path.join(os.homedir(), '.cargo');
|
|
34
48
|
|
|
35
|
-
|
|
36
|
-
|
|
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
|
+
}
|
|
37
66
|
} else {
|
|
38
|
-
log.error(
|
|
39
|
-
log.
|
|
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}`);
|
|
40
71
|
}
|
|
41
|
-
|
|
42
|
-
log.error(`❌ '${command}' failed.`);
|
|
72
|
+
return;
|
|
43
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');
|
|
77
|
+
if (isPrivilegeError && os.platform() === 'win32') {
|
|
78
|
+
log.warn("⚠️ Windows requires Administrator privileges for this operation.");
|
|
79
|
+
log.info("🔑 Requesting elevated access...\n");
|
|
80
|
+
|
|
81
|
+
await runElevatedWithProgress(command, args);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
log.error(`❌ '${command}' failed.`);
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Runs the anchor command in a HIDDEN elevated PowerShell window,
|
|
90
|
+
* redirecting all output to a temp log file. The user's terminal
|
|
91
|
+
* shows a spinner that tails the log file in real-time.
|
|
92
|
+
*/
|
|
93
|
+
const runElevatedWithProgress = (command, args = []) => {
|
|
94
|
+
return new Promise((resolve) => {
|
|
95
|
+
const tmpDir = os.tmpdir();
|
|
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`);
|
|
99
|
+
const cwd = process.cwd();
|
|
100
|
+
|
|
101
|
+
// Write an empty log file so we can start reading immediately
|
|
102
|
+
fs.writeFileSync(logFile, '', 'utf8');
|
|
103
|
+
|
|
104
|
+
const anchorCmd = `anchor ${command} ${args.join(' ')}`;
|
|
105
|
+
const innerScript = `
|
|
106
|
+
Set-Location '${cwd.replace(/'/g, "''")}';
|
|
107
|
+
$output = & cmd /c '${anchorCmd} 2>&1';
|
|
108
|
+
$output | Out-File -FilePath '${logFile.replace(/\\/g, '\\\\')}' -Encoding utf8 -Append;
|
|
109
|
+
$LASTEXITCODE | Out-File -FilePath '${doneFile.replace(/\\/g, '\\\\')}' -Encoding utf8;
|
|
110
|
+
`.replace(/\n/g, ' ').trim();
|
|
111
|
+
|
|
112
|
+
// Launch hidden elevated process
|
|
113
|
+
const elevateCmd = `powershell -Command "Start-Process powershell -ArgumentList '-NoProfile', '-ExecutionPolicy', 'Bypass', '-Command', '${innerScript.replace(/'/g, "''")}' -Verb RunAs -WindowStyle Hidden"`;
|
|
114
|
+
|
|
115
|
+
const elevateResult = shell.exec(elevateCmd, { silent: true });
|
|
116
|
+
|
|
117
|
+
if (elevateResult.code !== 0) {
|
|
118
|
+
log.error("❌ Failed to launch elevated process. Was the UAC prompt declined?");
|
|
119
|
+
resolve();
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Now poll the log file and show progress
|
|
124
|
+
const spin = spinner('Building with elevated privileges...').start();
|
|
125
|
+
let lastSize = 0;
|
|
126
|
+
|
|
127
|
+
const pollInterval = setInterval(() => {
|
|
128
|
+
try {
|
|
129
|
+
if (fs.existsSync(doneFile)) {
|
|
130
|
+
clearInterval(pollInterval);
|
|
131
|
+
|
|
132
|
+
const exitCode = fs.readFileSync(doneFile, 'utf8').trim();
|
|
133
|
+
const fullLog = fs.readFileSync(logFile, 'utf8');
|
|
134
|
+
|
|
135
|
+
try { fs.unlinkSync(logFile); } catch (e) { /* ignore */ }
|
|
136
|
+
try { fs.unlinkSync(doneFile); } catch (e) { /* ignore */ }
|
|
137
|
+
|
|
138
|
+
if (exitCode === '0') {
|
|
139
|
+
spin.succeed(`'${command}' completed successfully (elevated).`);
|
|
140
|
+
} else {
|
|
141
|
+
spin.fail(`'${command}' failed (elevated, exit code: ${exitCode}).`);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (fullLog.trim()) {
|
|
145
|
+
console.log('\n' + fullLog.trim());
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
resolve();
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Read new content for live progress
|
|
153
|
+
const stats = fs.statSync(logFile);
|
|
154
|
+
if (stats.size > lastSize) {
|
|
155
|
+
const content = fs.readFileSync(logFile, 'utf8');
|
|
156
|
+
const lines = content.trim().split('\n').filter(l => l.trim());
|
|
157
|
+
if (lines.length > 0) {
|
|
158
|
+
const lastLine = lines[lines.length - 1].trim();
|
|
159
|
+
const displayLine = lastLine.length > 70 ? lastLine.substring(0, 67) + '...' : lastLine;
|
|
160
|
+
spin.text = displayLine;
|
|
161
|
+
}
|
|
162
|
+
lastSize = stats.size;
|
|
163
|
+
}
|
|
164
|
+
} catch (e) {
|
|
165
|
+
// File might not exist yet or be locked
|
|
166
|
+
}
|
|
167
|
+
}, 500);
|
|
168
|
+
|
|
169
|
+
// Safety timeout: 10 minutes
|
|
170
|
+
setTimeout(() => {
|
|
171
|
+
if (!fs.existsSync(doneFile)) {
|
|
172
|
+
clearInterval(pollInterval);
|
|
173
|
+
spin.fail("Timed out waiting for elevated process (10 min).");
|
|
174
|
+
resolve();
|
|
175
|
+
}
|
|
176
|
+
}, 10 * 60 * 1000);
|
|
177
|
+
});
|
|
44
178
|
};
|
|
45
179
|
|
|
46
180
|
const build = () => runProxyCommand('build');
|