@xaidenlabs/uso 1.1.0 → 1.1.2

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/bin/index.js CHANGED
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
  const { program } = require('commander');
3
3
  const { init } = require('../src/commands/init');
4
4
  const { doctor } = require('../src/commands/doctor');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xaidenlabs/uso",
3
- "version": "1.1.0",
3
+ "version": "1.1.2",
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,4 +1,7 @@
1
1
  const shell = require('shelljs');
2
+ const os = require('os');
3
+ const fs = require('fs');
4
+ const path = require('path');
2
5
  const { log, spinner } = require('../utils/logger');
3
6
 
4
7
  const runProxyCommand = async (command, args = []) => {
@@ -16,20 +19,130 @@ const runProxyCommand = async (command, args = []) => {
16
19
 
17
20
  if (execution.code === 0) {
18
21
  log.success(`✅ '${command}' completed successfully.`);
22
+ return;
23
+ }
24
+
25
+ // Check for Windows privilege error (os error 1314 — symlink creation requires elevation)
26
+ 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
+
29
+ if (isPrivilegeError && os.platform() === 'win32') {
30
+ log.warn("⚠️ Windows requires Administrator privileges for this operation.");
31
+ log.info("🔑 Requesting elevated access...\n");
32
+
33
+ await runElevatedWithProgress(command, args);
19
34
  } else {
20
35
  log.error(`❌ '${command}' failed.`);
21
- // We don't exit process here strictly, but let the user know
22
36
  }
23
37
  };
24
38
 
39
+ /**
40
+ * Runs the anchor command in a HIDDEN elevated PowerShell window,
41
+ * redirecting all output to a temp log file. The user's terminal
42
+ * shows a spinner that tails the log file in real-time.
43
+ */
44
+ const runElevatedWithProgress = (command, args = []) => {
45
+ return new Promise((resolve) => {
46
+ 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`);
49
+ const cwd = process.cwd();
50
+
51
+ // Write an empty log file so we can start reading immediately
52
+ fs.writeFileSync(logFile, '', 'utf8');
53
+
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
+ const anchorCmd = `anchor ${command} ${args.join(' ')}`;
60
+ const innerScript = `
61
+ Set-Location '${cwd.replace(/'/g, "''")}';
62
+ $output = & cmd /c '${anchorCmd} 2>&1';
63
+ $output | Out-File -FilePath '${logFile.replace(/\\/g, '\\\\')}' -Encoding utf8 -Append;
64
+ $LASTEXITCODE | Out-File -FilePath '${doneFile.replace(/\\/g, '\\\\')}' -Encoding utf8;
65
+ `.replace(/\n/g, ' ').trim();
66
+
67
+ // Launch hidden elevated process
68
+ const elevateCmd = `powershell -Command "Start-Process powershell -ArgumentList '-NoProfile', '-ExecutionPolicy', 'Bypass', '-Command', '${innerScript.replace(/'/g, "''")}' -Verb RunAs -WindowStyle Hidden"`;
69
+
70
+ // This triggers the UAC prompt (that one still shows — it's Windows security, can't bypass it)
71
+ const elevateResult = shell.exec(elevateCmd, { silent: true });
72
+
73
+ if (elevateResult.code !== 0) {
74
+ log.error("❌ Failed to launch elevated process. Was the UAC prompt declined?");
75
+ resolve();
76
+ return;
77
+ }
78
+
79
+ // Now poll the log file and show progress
80
+ const spin = spinner('Building with elevated privileges...').start();
81
+ let lastSize = 0;
82
+ let lastLine = '';
83
+
84
+ const pollInterval = setInterval(() => {
85
+ try {
86
+ // Check if process finished
87
+ if (fs.existsSync(doneFile)) {
88
+ clearInterval(pollInterval);
89
+
90
+ const exitCode = fs.readFileSync(doneFile, 'utf8').trim();
91
+ const fullLog = fs.readFileSync(logFile, 'utf8');
92
+
93
+ // Cleanup temp files
94
+ try { fs.unlinkSync(logFile); } catch (e) { /* ignore */ }
95
+ try { fs.unlinkSync(doneFile); } catch (e) { /* ignore */ }
96
+
97
+ if (exitCode === '0') {
98
+ spin.succeed(`'${command}' completed successfully (elevated).`);
99
+ } else {
100
+ spin.fail(`'${command}' failed (elevated, exit code: ${exitCode}).`);
101
+ }
102
+
103
+ // Print the captured output
104
+ if (fullLog.trim()) {
105
+ console.log('\n' + fullLog.trim());
106
+ }
107
+
108
+ resolve();
109
+ return;
110
+ }
111
+
112
+ // Read new content from log file for progress
113
+ const stats = fs.statSync(logFile);
114
+ if (stats.size > lastSize) {
115
+ const content = fs.readFileSync(logFile, 'utf8');
116
+ const lines = content.trim().split('\n').filter(l => l.trim());
117
+ if (lines.length > 0) {
118
+ lastLine = lines[lines.length - 1].trim();
119
+ // Show the last meaningful line as spinner text
120
+ const displayLine = lastLine.length > 70 ? lastLine.substring(0, 67) + '...' : lastLine;
121
+ spin.text = displayLine;
122
+ }
123
+ lastSize = stats.size;
124
+ }
125
+ } catch (e) {
126
+ // File might not exist yet or be locked, just keep polling
127
+ }
128
+ }, 500);
129
+
130
+ // Safety timeout: 10 minutes max
131
+ setTimeout(() => {
132
+ if (!fs.existsSync(doneFile)) {
133
+ clearInterval(pollInterval);
134
+ spin.fail("Timed out waiting for elevated process (10 min).");
135
+ resolve();
136
+ }
137
+ }, 10 * 60 * 1000);
138
+ });
139
+ };
140
+
25
141
  const build = () => runProxyCommand('build');
26
142
  const test = () => runProxyCommand('test');
27
143
  const deploy = () => runProxyCommand('deploy');
28
144
  const clean = () => runProxyCommand('clean');
29
145
 
30
- // Generic run command for other anchor commands?
31
- // For now, explicit functions are safer for help generation.
32
-
33
146
  module.exports = {
34
147
  build,
35
148
  test,
@@ -1,18 +0,0 @@
1
- [toolchain]
2
-
3
- [features]
4
- seeds = false
5
- skip-lint = false
6
-
7
- [programs.localnet]
8
- my_test_project = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"
9
-
10
- [registry]
11
- url = "https://api.apr.dev"
12
-
13
- [provider]
14
- cluster = "Localnet"
15
- wallet = "~/.config/solana/id.json"
16
-
17
- [scripts]
18
- test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts"
@@ -1,36 +0,0 @@
1
- # Welcome to Your USO Project 🚀
2
-
3
- Congratulations! You've just scaffolded a professional Solana workspace using the **Universal Solana Orchestrator (USO)**.
4
-
5
- This isn't just a folder with files. It's a battle-tested setup ready for serious development.
6
-
7
- ## 📂 Structure
8
- - `programs/`: Your Rust smart contracts live here.
9
- - `tests/`: Robust TypeScript tests using Anchor.
10
- - `Anchor.toml`: Your project configuration.
11
-
12
- ## ⚡ Quick Start
13
-
14
- ### 1. Build
15
- Compile your smart contract:
16
- ```bash
17
- anchor build
18
- ```
19
-
20
- ### 2. Test
21
- Run your test suite against a local validator:
22
- ```bash
23
- anchor test
24
- ```
25
- *Note: This automatically spins up a local validator, deploys your program, runs tests, and shuts down.*
26
-
27
- ### 3. Deploy (Devnet)
28
- When you're ready to go public:
29
- 1. Switch to devnet: `solana config set --url devnet`
30
- 2. Airdrop SOL: `solana airdrop 2`
31
- 3. Deploy: `anchor deploy`
32
-
33
- ## 🧠 Need Help?
34
- Run `uso doctor` if your environment feels weird. Usage instructions are also available via `uso help`.
35
-
36
- Happy coding!