@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 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 levelsfrom beginners setting up their first environment to senior engineers managing multiple workstations.
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xaidenlabs/uso",
3
- "version": "1.1.2",
3
+ "version": "1.1.3",
4
4
  "description": "Universal Solana Orchestrator - A one-command setup tool for Solana and Anchor development environments on Windows, macOS, and Linux.",
5
5
  "bin": {
6
6
  "uso": "bin/index.js"
@@ -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
 
@@ -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
- } else {
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 logFile = path.join(tmpDir, `uso-elevated-${Date.now()}.log`);
48
- const doneFile = path.join(tmpDir, `uso-elevated-${Date.now()}.done`);
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 from log file for progress
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, just keep polling
165
+ // File might not exist yet or be locked
127
166
  }
128
167
  }, 500);
129
168
 
130
- // Safety timeout: 10 minutes max
169
+ // Safety timeout: 10 minutes
131
170
  setTimeout(() => {
132
171
  if (!fs.existsSync(doneFile)) {
133
172
  clearInterval(pollInterval);