@xaidenlabs/uso 1.1.67 → 1.1.68

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.
@@ -1,70 +1,129 @@
1
- const shell = require('shelljs');
2
- const os = require('os');
3
- const fs = require('fs');
4
- const path = require('path');
5
- const { log, spinner } = require('../utils/logger');
6
- const { isStealthMode } = require('../utils/stealth');
7
- const { runWsl, toWslPath } = require('../utils/wsl-bridge');
8
-
9
- const runProxyCommand = async (command, args = [], binary = 'anchor') => {
1
+ const shell = require("shelljs");
2
+ const os = require("os");
3
+ const fs = require("fs");
4
+ const path = require("path");
5
+ const { log, spinner } = require("../utils/logger");
6
+ const { isStealthMode } = require("../utils/stealth");
7
+ const { runWsl, toWslPath } = require("../utils/wsl-bridge");
8
+
9
+ const isUsoCoreBridgeEnabled = () =>
10
+ process.env.USE_INTENT_SDK === "1" || process.env.USO_CORE_BRIDGE === "1";
11
+
12
+ const runViaUsoCoreBridge = async (command, args = [], binary = "anchor") => {
13
+ try {
14
+ const {
15
+ runCliIntentAdapter,
16
+ } = require("../../packages/uso-core/dist/cjs/adapters/cli.js");
10
17
  const stealth = isStealthMode();
11
18
 
12
- // --- STEALTH WSL MODE ---
13
- // Smart routing: prefer native binary if available (e.g. native Anchor on Windows
14
- // connecting to WSL validator via localhost). Only WSL-route if native binary is missing.
15
- const nativeAvailable = shell.which(binary);
16
- if (stealth.enabled && !nativeAvailable) {
17
- const fullCommand = `${binary} ${command} ${args.join(' ')}`;
19
+ const result = await runCliIntentAdapter({
20
+ command,
21
+ args,
22
+ binary,
23
+ cwd: process.cwd(),
24
+ preferWsl: !!stealth.enabled,
25
+ });
18
26
 
19
- // Source cargo/solana paths inside WSL before running
20
- const envSetup = 'source $HOME/.cargo/env 2>/dev/null; export PATH="$HOME/.local/share/solana/install/active_release/bin:$PATH"';
21
- const wslCwd = toWslPath(process.cwd());
22
- const execution = runWsl(`${envSetup} && ${fullCommand}`, { distro: stealth.distro, cwd: process.cwd() });
27
+ if (!result.handled) {
28
+ return false;
29
+ }
23
30
 
24
- return;
31
+ if (result.ok) {
32
+ return true;
25
33
  }
26
34
 
27
- // --- NATIVE MODE ---
28
- // Check if binary is available
29
- if (!shell.which(binary)) {
30
- log.error(`❌ ${binary} is not found in PATH.`);
31
- log.warn("👉 Run 'uso init' (or 'uso install') to set up your environment.");
32
- return;
35
+ log.warn(
36
+ `⚠️ uso-core bridge returned status '${result.status || "failed"}'. Falling back to legacy workflow.`,
37
+ );
38
+ if (result.reason) {
39
+ log.warn(`uso-core reason: ${result.reason}`);
33
40
  }
41
+ return false;
42
+ } catch (err) {
43
+ log.warn(
44
+ "⚠️ uso-core bridge unavailable. Falling back to legacy workflow.",
45
+ );
46
+ return false;
47
+ }
48
+ };
34
49
 
35
- const fullCommand = `${binary} ${command} ${args.join(' ')}`;
50
+ const runProxyCommand = async (command, args = [], binary = "anchor") => {
51
+ if (isUsoCoreBridgeEnabled()) {
52
+ const bridged = await runViaUsoCoreBridge(command, args, binary);
53
+ if (bridged) {
54
+ return;
55
+ }
56
+ }
57
+
58
+ const stealth = isStealthMode();
59
+
60
+ // --- STEALTH WSL MODE ---
61
+ // Smart routing: prefer native binary if available (e.g. native Anchor on Windows
62
+ // connecting to WSL validator via localhost). Only WSL-route if native binary is missing.
63
+ const nativeAvailable = shell.which(binary);
64
+ if (stealth.enabled && !nativeAvailable) {
65
+ const fullCommand = `${binary} ${command} ${args.join(" ")}`;
66
+
67
+ // Source cargo/solana paths inside WSL before running
68
+ const envSetup =
69
+ 'source $HOME/.cargo/env 2>/dev/null; export PATH="$HOME/.local/share/solana/install/active_release/bin:$PATH"';
70
+ const wslCwd = toWslPath(process.cwd());
71
+ const execution = runWsl(`${envSetup} && ${fullCommand}`, {
72
+ distro: stealth.distro,
73
+ cwd: process.cwd(),
74
+ });
36
75
 
37
- const execution = shell.exec(fullCommand);
76
+ return;
77
+ }
38
78
 
39
- if (execution.code === 0) {
40
- return;
41
- }
79
+ // --- NATIVE MODE ---
80
+ // Check if binary is available
81
+ if (!shell.which(binary)) {
82
+ log.error(`❌ ${binary} is not found in PATH.`);
83
+ log.warn(
84
+ "👉 Run 'uso init' (or 'uso install') to set up your environment.",
85
+ );
86
+ return;
87
+ }
42
88
 
43
- const output = (execution.stderr || '') + (execution.stdout || '');
89
+ const fullCommand = `${binary} ${command} ${args.join(" ")}`;
44
90
 
45
- // Detect Windows-specific errors that require elevation
46
- const isPrivilegeError = output.includes('os error 1314') || output.includes('A required privilege is not held by the client');
47
- const isAppControlBlock = output.includes('os error 4551') || output.includes('Application Control policy has blocked');
91
+ const execution = shell.exec(fullCommand);
48
92
 
49
- if ((isPrivilegeError || isAppControlBlock) && os.platform() === 'win32') {
50
- if (isAppControlBlock) {
51
- log.warn("⚠️ Windows Application Control is blocking build scripts.");
52
- } else {
53
- log.warn("⚠️ Windows requires Administrator privileges for this operation.");
54
- }
93
+ if (execution.code === 0) {
94
+ return;
95
+ }
55
96
 
97
+ const output = (execution.stderr || "") + (execution.stdout || "");
56
98
 
57
- // Clean stale blocked artifacts before elevated retry (only for cargo/anchor builds)
58
- if (isAppControlBlock && binary === 'anchor') {
59
- const cleanSpin = spinner('Cleaning blocked build artifacts...').start();
60
- shell.exec('cargo clean', { silent: true, cwd: process.cwd() });
61
- cleanSpin.succeed('Build cache cleaned.');
62
- }
99
+ // Detect Windows-specific errors that require elevation
100
+ const isPrivilegeError =
101
+ output.includes("os error 1314") ||
102
+ output.includes("A required privilege is not held by the client");
103
+ const isAppControlBlock =
104
+ output.includes("os error 4551") ||
105
+ output.includes("Application Control policy has blocked");
63
106
 
64
- await runElevatedWithProgress(command, args, binary);
107
+ if ((isPrivilegeError || isAppControlBlock) && os.platform() === "win32") {
108
+ if (isAppControlBlock) {
109
+ log.warn("⚠️ Windows Application Control is blocking build scripts.");
65
110
  } else {
66
- // Command failed (non-elevated)
111
+ log.warn(
112
+ "⚠️ Windows requires Administrator privileges for this operation.",
113
+ );
67
114
  }
115
+
116
+ // Clean stale blocked artifacts before elevated retry (only for cargo/anchor builds)
117
+ if (isAppControlBlock && binary === "anchor") {
118
+ const cleanSpin = spinner("Cleaning blocked build artifacts...").start();
119
+ shell.exec("cargo clean", { silent: true, cwd: process.cwd() });
120
+ cleanSpin.succeed("Build cache cleaned.");
121
+ }
122
+
123
+ await runElevatedWithProgress(command, args, binary);
124
+ } else {
125
+ // Command failed (non-elevated)
126
+ }
68
127
  };
69
128
 
70
129
  /**
@@ -72,20 +131,22 @@ const runProxyCommand = async (command, args = [], binary = 'anchor') => {
72
131
  * This is the robust fallback for Smart App Control / WDAC errors.
73
132
  * We rely on the user to see the output in the new window.
74
133
  */
75
- const runElevatedWithProgress = (command, args = [], binary = 'anchor') => {
76
- return new Promise((resolve, reject) => {
77
- const cwd = process.cwd().replace(/\\/g, '\\\\');
78
- const cargoBin = path.join(os.homedir(), '.cargo', 'bin').replace(/\\/g, '\\\\');
79
- const progressFile = path.join(process.cwd(), 'uso-elevation.log');
80
- const progressFileEscaped = progressFile.replace(/\\/g, '\\\\');
81
-
82
- // 1. Prepare progress file
83
- try { fs.writeFileSync(progressFile, ''); } catch (e) { }
84
-
85
-
86
-
87
- // 2. Construct Elevated Command
88
- const innerCmd = `
134
+ const runElevatedWithProgress = (command, args = [], binary = "anchor") => {
135
+ return new Promise((resolve, reject) => {
136
+ const cwd = process.cwd().replace(/\\/g, "\\\\");
137
+ const cargoBin = path
138
+ .join(os.homedir(), ".cargo", "bin")
139
+ .replace(/\\/g, "\\\\");
140
+ const progressFile = path.join(process.cwd(), "uso-elevation.log");
141
+ const progressFileEscaped = progressFile.replace(/\\/g, "\\\\");
142
+
143
+ // 1. Prepare progress file
144
+ try {
145
+ fs.writeFileSync(progressFile, "");
146
+ } catch (e) {}
147
+
148
+ // 2. Construct Elevated Command
149
+ const innerCmd = `
89
150
  $ErrorActionPreference = "Stop";
90
151
  Start-Transcript -Path "${progressFileEscaped}" -Append -Force | Out-Null;
91
152
 
@@ -104,7 +165,7 @@ const runElevatedWithProgress = (command, args = [], binary = 'anchor') => {
104
165
 
105
166
  try {
106
167
  # Run command and let Transcript capture output
107
- & ${binary} ${command} ${args.join(' ')};
168
+ & ${binary} ${command} ${args.join(" ")};
108
169
 
109
170
  if ($LASTEXITCODE -eq 0) {
110
171
  Write-Host "✅ Success!" -ForegroundColor Green;
@@ -121,207 +182,229 @@ const runElevatedWithProgress = (command, args = [], binary = 'anchor') => {
121
182
  Stop-Transcript | Out-Null;
122
183
  Write-Host "Press any key to close..." -ForegroundColor Gray;
123
184
  $null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown");
124
- `.replace(/\n/g, ' ').replace(/\s+/g, ' ').trim();
125
-
126
- // 3. Spawn Window (-NoExit to prevent immediate close on crash)
127
- const innerCmdBytes = Buffer.from(innerCmd, 'utf16le');
128
- const innerCmdBase64 = innerCmdBytes.toString('base64');
129
- const psCmd = `powershell -Command "Start-Process powershell -ArgumentList '-NoExit', '-EncodedCommand', '${innerCmdBase64}' -Verb RunAs"`;
130
- shell.exec(psCmd);
131
-
132
- // 4. Stream Log to Main Console
133
- let lastSize = 0;
134
- const spin = spinner('Waiting for output from elevated terminal...').start();
135
-
136
- const checkInterval = setInterval(() => {
137
- try {
138
- if (!fs.existsSync(progressFile)) return;
139
-
140
- const stats = fs.statSync(progressFile);
141
- if (stats.size > lastSize) {
142
- const fd = fs.openSync(progressFile, 'r');
143
- const buffer = Buffer.alloc(stats.size - lastSize);
144
- fs.readSync(fd, buffer, 0, buffer.length, lastSize);
145
- fs.closeSync(fd);
146
-
147
- const rawText = buffer.toString('utf8');
148
- const displayText = rawText
149
- .replace(/USO_AC_SUCCESS/g, '')
150
- .replace(/USO_AC_FAILURE/g, '');
151
-
152
- // Stop spinner to print logs cleanly
153
- spin.stop();
154
- process.stdout.write(displayText);
155
-
156
- lastSize = stats.size;
157
-
158
- if (rawText.includes("USO_AC_SUCCESS")) {
159
- clearInterval(checkInterval);
160
- spin.succeed('Elevated process completed successfully.');
161
- resolve();
162
- } else if (rawText.includes("USO_AC_FAILURE")) {
163
- clearInterval(checkInterval);
164
- spin.fail('Elevated process reported failure.');
165
- resolve();
166
- } else {
167
- // Restart spinner with "Building..." status since we are receiving data
168
- spin.start('Building...');
169
- }
170
- }
171
- } catch (e) {
172
- // Ignore busy/lock errors during polling
173
- }
174
- }, 500);
175
- });
185
+ `
186
+ .replace(/\n/g, " ")
187
+ .replace(/\s+/g, " ")
188
+ .trim();
189
+
190
+ // 3. Spawn Window (-NoExit to prevent immediate close on crash)
191
+ const innerCmdBytes = Buffer.from(innerCmd, "utf16le");
192
+ const innerCmdBase64 = innerCmdBytes.toString("base64");
193
+ const psCmd = `powershell -Command "Start-Process powershell -ArgumentList '-NoExit', '-EncodedCommand', '${innerCmdBase64}' -Verb RunAs"`;
194
+ shell.exec(psCmd);
195
+
196
+ // 4. Stream Log to Main Console
197
+ let lastSize = 0;
198
+ const spin = spinner(
199
+ "Waiting for output from elevated terminal...",
200
+ ).start();
201
+
202
+ const checkInterval = setInterval(() => {
203
+ try {
204
+ if (!fs.existsSync(progressFile)) return;
205
+
206
+ const stats = fs.statSync(progressFile);
207
+ if (stats.size > lastSize) {
208
+ const fd = fs.openSync(progressFile, "r");
209
+ const buffer = Buffer.alloc(stats.size - lastSize);
210
+ fs.readSync(fd, buffer, 0, buffer.length, lastSize);
211
+ fs.closeSync(fd);
212
+
213
+ const rawText = buffer.toString("utf8");
214
+ const displayText = rawText
215
+ .replace(/USO_AC_SUCCESS/g, "")
216
+ .replace(/USO_AC_FAILURE/g, "");
217
+
218
+ // Stop spinner to print logs cleanly
219
+ spin.stop();
220
+ process.stdout.write(displayText);
221
+
222
+ lastSize = stats.size;
223
+
224
+ if (rawText.includes("USO_AC_SUCCESS")) {
225
+ clearInterval(checkInterval);
226
+ spin.succeed("Elevated process completed successfully.");
227
+ resolve();
228
+ } else if (rawText.includes("USO_AC_FAILURE")) {
229
+ clearInterval(checkInterval);
230
+ spin.fail("Elevated process reported failure.");
231
+ resolve();
232
+ } else {
233
+ // Restart spinner with "Building..." status since we are receiving data
234
+ spin.start("Building...");
235
+ }
236
+ }
237
+ } catch (e) {
238
+ // Ignore busy/lock errors during polling
239
+ }
240
+ }, 500);
241
+ });
176
242
  };
177
243
 
178
- const build = () => runProxyCommand('build', [], 'anchor');
179
- const deploy = () => runProxyCommand('deploy', [], 'anchor');
180
- const clean = () => runProxyCommand('clean', [], 'anchor');
244
+ const build = () => runProxyCommand("build", [], "anchor");
245
+ const deploy = () => runProxyCommand("deploy", [], "anchor");
246
+ const clean = () => runProxyCommand("clean", [], "anchor");
181
247
 
182
248
  const test = async (args = []) => {
183
- const userArgs = Array.isArray(args) ? args : [];
184
-
185
- // On Windows, auto-detect validator to prevent anchor test from hanging
186
- if (os.platform() === 'win32' && !userArgs.includes('--skip-local-validator')) {
187
- const isValidatorUp = () => {
188
- const res = shell.exec('netstat -an | findstr 8899', { silent: true });
189
- return res.code === 0 && res.stdout.length > 0;
190
- };
191
-
192
- if (isValidatorUp()) {
193
- log.info("✅ Validator detected on port 8899. Running tests against it.");
194
- userArgs.push('--skip-local-validator');
195
- } else {
196
- log.warn("⚠️ No validator detected. Starting one first...");
197
- log.info("👉 Run 'uso val' in a separate terminal, then re-run 'uso test'.");
198
- log.info(" Or use 'uso dev' to do both automatically.");
199
- return;
200
- }
249
+ const userArgs = Array.isArray(args) ? args : [];
250
+
251
+ // On Windows, auto-detect validator to prevent anchor test from hanging
252
+ if (
253
+ os.platform() === "win32" &&
254
+ !userArgs.includes("--skip-local-validator")
255
+ ) {
256
+ const isValidatorUp = () => {
257
+ const res = shell.exec("netstat -an | findstr 8899", { silent: true });
258
+ return res.code === 0 && res.stdout.length > 0;
259
+ };
260
+
261
+ if (isValidatorUp()) {
262
+ log.info(" Validator detected on port 8899. Running tests against it.");
263
+ userArgs.push("--skip-local-validator");
264
+ } else {
265
+ log.warn("⚠️ No validator detected. Starting one first...");
266
+ log.info(
267
+ "👉 Run 'uso val' in a separate terminal, then re-run 'uso test'.",
268
+ );
269
+ log.info(" Or use 'uso dev' to do both automatically.");
270
+ return;
201
271
  }
272
+ }
202
273
 
203
- return runProxyCommand('test', userArgs, 'anchor');
274
+ return runProxyCommand("test", userArgs, "anchor");
204
275
  };
205
- const address = () => runProxyCommand('address', [], 'solana');
276
+ const address = () => runProxyCommand("address", [], "solana");
206
277
  const balance = (addrArg) => {
207
- const args = addrArg ? [addrArg] : [];
208
- return runProxyCommand('balance', args, 'solana');
278
+ const args = addrArg ? [addrArg] : [];
279
+ return runProxyCommand("balance", args, "solana");
209
280
  };
210
281
  const airdrop = (amount, recipient) => {
211
- const args = [amount];
212
- if (recipient) args.push(recipient);
213
- return runProxyCommand('airdrop', args, 'solana');
282
+ const args = [amount];
283
+ if (recipient) args.push(recipient);
284
+ return runProxyCommand("airdrop", args, "solana");
214
285
  };
215
286
 
216
287
  const validator = async (args = []) => {
217
- const stealth = isStealthMode();
288
+ const stealth = isStealthMode();
218
289
 
219
- // --- STEALTH WSL MODE ---
220
- if (stealth.enabled) {
221
- const flags = Array.isArray(args) ? args : [];
222
-
223
- // Unix sockets (admin.rpc) don't work on NTFS-mounted paths (/mnt/c/).
224
- // Force ledger onto native Linux filesystem.
225
- if (!flags.some(f => f.startsWith('--ledger'))) {
226
- flags.push('--ledger', '/tmp/test-ledger');
227
- }
228
-
229
- const cmdArgs = flags.join(' ');
230
- const fullCmd = `solana-test-validator ${cmdArgs}`;
231
-
232
- log.info("👉 Press Ctrl+C to stop it.");
290
+ // --- STEALTH WSL MODE ---
291
+ if (stealth.enabled) {
292
+ const flags = Array.isArray(args) ? args : [];
233
293
 
234
- const envSetup = 'source $HOME/.cargo/env 2>/dev/null; export PATH="$HOME/.local/share/solana/install/active_release/bin:$PATH"';
235
- runWsl(`${envSetup} && ${fullCmd}`, {
236
- distro: stealth.distro,
237
- execOpts: { async: false }
238
- });
239
- return;
294
+ // Unix sockets (admin.rpc) don't work on NTFS-mounted paths (/mnt/c/).
295
+ // Force ledger onto native Linux filesystem.
296
+ if (!flags.some((f) => f.startsWith("--ledger"))) {
297
+ flags.push("--ledger", "/tmp/test-ledger");
240
298
  }
241
299
 
242
- // --- NATIVE MODE ---
243
- if (!shell.which('solana-test-validator')) {
244
- log.error("❌ 'solana-test-validator' is not found in PATH.");
245
- log.warn("👉 Run 'uso init' to install it.");
246
- return;
247
- }
300
+ const cmdArgs = flags.join(" ");
301
+ const fullCmd = `solana-test-validator ${cmdArgs}`;
248
302
 
249
- // Check if args is the commander object (happens if no args defined in command)
250
- // or if it's an array of strings (if [args...] is defined)
251
- // Commander passes (args, options, command) if using [args...]
252
- // If we change bin/index.js to .command('validator [args...]'), args will be array.
303
+ log.info("👉 Press Ctrl+C to stop it.");
253
304
 
254
- const flags = Array.isArray(args) ? args : [];
305
+ const envSetup =
306
+ 'source $HOME/.cargo/env 2>/dev/null; export PATH="$HOME/.local/share/solana/install/active_release/bin:$PATH"';
307
+ runWsl(`${envSetup} && ${fullCmd}`, {
308
+ distro: stealth.distro,
309
+ execOpts: { async: false },
310
+ });
311
+ return;
312
+ }
313
+
314
+ // --- NATIVE MODE ---
315
+ if (!shell.which("solana-test-validator")) {
316
+ log.error("❌ 'solana-test-validator' is not found in PATH.");
317
+ log.warn("👉 Run 'uso init' to install it.");
318
+ return;
319
+ }
320
+
321
+ // Check if args is the commander object (happens if no args defined in command)
322
+ // or if it's an array of strings (if [args...] is defined)
323
+ // Commander passes (args, options, command) if using [args...]
324
+ // If we change bin/index.js to .command('validator [args...]'), args will be array.
325
+
326
+ const flags = Array.isArray(args) ? args : [];
327
+
328
+ // Robust Manual Cleanup for --reset on Windows
329
+ // solana-test-validator --reset often fails with "Access Denied" on Windows due to file locks
330
+ if (flags.includes("--reset") || flags.includes("-r")) {
331
+ const ledgerPath = path.join(process.cwd(), "test-ledger");
332
+ if (fs.existsSync(ledgerPath)) {
333
+ const spin = spinner("🧹 Manually cleaning test-ledger...").start();
334
+ try {
335
+ // Retry loop for Windows file locking
336
+ let retries = 5;
337
+ while (retries > 0) {
338
+ try {
339
+ fs.rmSync(ledgerPath, { recursive: true, force: true });
340
+ if (!fs.existsSync(ledgerPath)) break;
341
+ } catch (e) {
342
+ // wait 500ms
343
+ shell.exec('powershell -Command "Start-Sleep -Milliseconds 500"');
344
+ }
345
+ retries--;
346
+ }
255
347
 
256
- // Robust Manual Cleanup for --reset on Windows
257
- // solana-test-validator --reset often fails with "Access Denied" on Windows due to file locks
258
- if (flags.includes('--reset') || flags.includes('-r')) {
259
- const ledgerPath = path.join(process.cwd(), 'test-ledger');
260
348
  if (fs.existsSync(ledgerPath)) {
261
- const spin = spinner('🧹 Manually cleaning test-ledger...').start();
262
- try {
263
- // Retry loop for Windows file locking
264
- let retries = 5;
265
- while (retries > 0) {
266
- try {
267
- fs.rmSync(ledgerPath, { recursive: true, force: true });
268
- if (!fs.existsSync(ledgerPath)) break;
269
- } catch (e) {
270
- // wait 500ms
271
- shell.exec('powershell -Command "Start-Sleep -Milliseconds 500"');
272
- }
273
- retries--;
274
- }
275
-
276
- if (fs.existsSync(ledgerPath)) {
277
- // Last resort: ShellJS
278
- shell.rm('-rf', ledgerPath);
279
- }
349
+ // Last resort: ShellJS
350
+ shell.rm("-rf", ledgerPath);
351
+ }
280
352
 
281
- if (fs.existsSync(ledgerPath)) {
282
- spin.warn('⚠️ Could not fully remove test-ledger. Validator might complain.');
283
- } else {
284
- spin.succeed('Ledger reset successfully.');
285
- }
286
- } catch (e) {
287
- spin.warn(`⚠️ Manual cleanup failed: ${e.message}`);
288
- }
353
+ if (fs.existsSync(ledgerPath)) {
354
+ spin.warn(
355
+ "⚠️ Could not fully remove test-ledger. Validator might complain.",
356
+ );
357
+ } else {
358
+ spin.succeed("Ledger reset successfully.");
289
359
  }
360
+ } catch (e) {
361
+ spin.warn(`⚠️ Manual cleanup failed: ${e.message}`);
362
+ }
290
363
  }
291
-
292
- const cmdArgs = flags.join(' ');
293
- const fullCmd = cmdArgs ? `solana-test-validator ${cmdArgs}` : 'solana-test-validator';
294
-
295
- log.info("👉 Press Ctrl+C to stop it.");
296
-
297
- // Run and capture exit code
298
- // We use shell.exec, which blocks. If it runs successfully, it blocks until user Ctrl+C.
299
- // If it fails immediately (like Access Denied), it returns execution object.
300
- const execution = shell.exec(fullCmd);
301
-
302
- if (execution.code !== 0) {
303
- const output = (execution.stderr || '') + (execution.stdout || '');
304
- // On Windows, exit code 1 with "Access is denied" is common.
305
- // Also check for "os error 5" or "os error 1314"
306
- if (output.includes('Access is denied') || output.includes('os error 5') || execution.code === 1) {
307
- log.warn("⚠️ Validator failed/crashed. Retrying in specialized Administrator terminal...");
308
-
309
- // Use Fire-and-Forget for validator (interactive/long-running)
310
- // We don't want to capture logs, we want the user to see the new window.
311
- const flagStr = flags.join(' ');
312
- const targetCmd = `solana-test-validator ${flagStr}`;
313
-
314
-
315
- log.warn("👉 A new window will appear. Keep it open to run the validator!");
316
-
317
- // Robust Launch using EncodedCommand to avoid quoting hell and set CWD correctly
318
- // 1. Set CWD (Critical: RunAs defaults to System32)
319
- // 2. Add Exclusions (Critical: Fixes Access Denied on ledger files)
320
- // 3. Run Validator
321
- // 4. Pause on Error
322
-
323
- const cwd = process.cwd();
324
- const psScript = `
364
+ }
365
+
366
+ const cmdArgs = flags.join(" ");
367
+ const fullCmd = cmdArgs
368
+ ? `solana-test-validator ${cmdArgs}`
369
+ : "solana-test-validator";
370
+
371
+ log.info("👉 Press Ctrl+C to stop it.");
372
+
373
+ // Run and capture exit code
374
+ // We use shell.exec, which blocks. If it runs successfully, it blocks until user Ctrl+C.
375
+ // If it fails immediately (like Access Denied), it returns execution object.
376
+ const execution = shell.exec(fullCmd);
377
+
378
+ if (execution.code !== 0) {
379
+ const output = (execution.stderr || "") + (execution.stdout || "");
380
+ // On Windows, exit code 1 with "Access is denied" is common.
381
+ // Also check for "os error 5" or "os error 1314"
382
+ if (
383
+ output.includes("Access is denied") ||
384
+ output.includes("os error 5") ||
385
+ execution.code === 1
386
+ ) {
387
+ log.warn(
388
+ "⚠️ Validator failed/crashed. Retrying in specialized Administrator terminal...",
389
+ );
390
+
391
+ // Use Fire-and-Forget for validator (interactive/long-running)
392
+ // We don't want to capture logs, we want the user to see the new window.
393
+ const flagStr = flags.join(" ");
394
+ const targetCmd = `solana-test-validator ${flagStr}`;
395
+
396
+ log.warn(
397
+ "👉 A new window will appear. Keep it open to run the validator!",
398
+ );
399
+
400
+ // Robust Launch using EncodedCommand to avoid quoting hell and set CWD correctly
401
+ // 1. Set CWD (Critical: RunAs defaults to System32)
402
+ // 2. Add Exclusions (Critical: Fixes Access Denied on ledger files)
403
+ // 3. Run Validator
404
+ // 4. Pause on Error
405
+
406
+ const cwd = process.cwd();
407
+ const psScript = `
325
408
  $ErrorActionPreference = 'Stop';
326
409
  try { Set-Location -Path '${cwd}'; } catch { Write-Host "⚠️ Could not set CWD. Keeping default." -ForegroundColor Yellow; }
327
410
 
@@ -358,144 +441,148 @@ const validator = async (args = []) => {
358
441
  Write-Host "❌ Validator Crashed (Exit Code: $LASTEXITCODE). Press Enter to exit..." -ForegroundColor Red;
359
442
  Read-Host;
360
443
  }
361
- `.replace(/\n/g, ' ').replace(/\s+/g, ' ').trim();
444
+ `
445
+ .replace(/\n/g, " ")
446
+ .replace(/\s+/g, " ")
447
+ .trim();
362
448
 
363
- const encoded = Buffer.from(psScript, 'utf16le').toString('base64');
364
- const psCmd = `powershell -Command "Start-Process powershell -ArgumentList '-NoExit', '-EncodedCommand', '${encoded}' -Verb RunAs"`;
449
+ const encoded = Buffer.from(psScript, "utf16le").toString("base64");
450
+ const psCmd = `powershell -Command "Start-Process powershell -ArgumentList '-NoExit', '-EncodedCommand', '${encoded}' -Verb RunAs"`;
365
451
 
366
- shell.exec(psCmd);
452
+ shell.exec(psCmd);
367
453
 
368
- log.success("✅ Validator launch sequence initiated.");
369
- }
454
+ log.success("✅ Validator launch sequence initiated.");
370
455
  }
456
+ }
371
457
  };
372
458
 
373
459
  const unblock = () => {
374
- const cwd = process.cwd();
375
-
376
-
377
- if (os.platform() !== 'win32') {
378
- log.success("✅ Not on Windows, nothing to unblock.");
379
- return;
380
- }
381
-
382
- const spin = spinner('Removing Mark-of-the-Web...').start();
383
- const cargoBin = path.join(os.homedir(), '.cargo', 'bin').replace(/'/g, "''");
384
- const targetCwd = cwd.replace(/'/g, "''");
385
-
386
- // Use resolved paths to avoid $env variable expansion issues in single quotes
387
- // And use Where-Object for older PowerShell compatibility
388
- const cmd = `powershell -Command "Get-ChildItem -Path '${targetCwd}' -Recurse | Where-Object { !$\_.PSIsContainer } | Unblock-File; Get-ChildItem -Path '${cargoBin}' -Recurse | Where-Object { !$\_.PSIsContainer } | Unblock-File"`;
389
-
390
- const result = shell.exec(cmd, { silent: true });
391
-
392
- if (result.code === 0) {
393
- spin.succeed('Files unblocked successfully.');
394
- log.info("👉 Try running 'uso test' now.");
395
- } else {
396
- spin.fail('Failed to unblock files.');
397
- log.error(result.stderr);
398
- log.warn("👉 Try running this command as Administrator.");
399
- }
460
+ const cwd = process.cwd();
461
+
462
+ if (os.platform() !== "win32") {
463
+ log.success("✅ Not on Windows, nothing to unblock.");
464
+ return;
465
+ }
466
+
467
+ const spin = spinner("Removing Mark-of-the-Web...").start();
468
+ const cargoBin = path.join(os.homedir(), ".cargo", "bin").replace(/'/g, "''");
469
+ const targetCwd = cwd.replace(/'/g, "''");
470
+
471
+ // Use resolved paths to avoid $env variable expansion issues in single quotes
472
+ // And use Where-Object for older PowerShell compatibility
473
+ const cmd = `powershell -Command "Get-ChildItem -Path '${targetCwd}' -Recurse | Where-Object { !$\_.PSIsContainer } | Unblock-File; Get-ChildItem -Path '${cargoBin}' -Recurse | Where-Object { !$\_.PSIsContainer } | Unblock-File"`;
474
+
475
+ const result = shell.exec(cmd, { silent: true });
476
+
477
+ if (result.code === 0) {
478
+ spin.succeed("Files unblocked successfully.");
479
+ log.info("👉 Try running 'uso test' now.");
480
+ } else {
481
+ spin.fail("Failed to unblock files.");
482
+ log.error(result.stderr);
483
+ log.warn("👉 Try running this command as Administrator.");
484
+ }
400
485
  };
401
486
 
402
487
  module.exports = {
403
- build,
404
- test,
405
- deploy,
406
- clean,
407
- unblock,
408
- airdrop,
409
- validator
488
+ build,
489
+ test,
490
+ deploy,
491
+ clean,
492
+ unblock,
493
+ airdrop,
494
+ validator,
410
495
  };
411
496
 
412
497
  const dev = async () => {
413
- const stealth = isStealthMode();
414
-
415
-
416
- // 1. Check if validator is running
417
- const isValidatorRunning = () => {
418
- if (stealth.enabled) {
419
- // Check from Windows side — validator in WSL still binds to host port
420
- const res = shell.exec('netstat -an | findstr 8899', { silent: true });
421
- return res.code === 0 && res.stdout.length > 0;
422
- }
423
- const res = shell.exec('netstat -an | findstr 8899', { silent: true });
424
- return res.code === 0 && res.stdout.length > 0;
425
- };
426
-
427
- if (isValidatorRunning()) {
428
- log.success("✅ Validator is already running.");
429
- } else {
498
+ const stealth = isStealthMode();
430
499
 
431
- // Spawn validator (this will open the Admin window)
432
- // We use [] args to start cleanly but persistently.
433
- // If it fails, the user will see it in the new window.
434
- await validator([]);
435
-
436
- // Wait for validator to be ready
437
- const spin = spinner('Waiting for Validator to respond...').start();
438
- let retries = 60; // 60 seconds (Increased for Windows genesis unpacking)
439
- while (retries > 0) {
440
- if (isValidatorRunning()) {
441
- spin.succeed("✅ Validator is online.");
442
- break;
443
- }
444
- await new Promise(r => setTimeout(r, 1000));
445
- retries--;
446
- }
500
+ // 1. Check if validator is running
501
+ const isValidatorRunning = () => {
502
+ if (stealth.enabled) {
503
+ // Check from Windows side — validator in WSL still binds to host port
504
+ const res = shell.exec("netstat -an | findstr 8899", { silent: true });
505
+ return res.code === 0 && res.stdout.length > 0;
506
+ }
507
+ const res = shell.exec("netstat -an | findstr 8899", { silent: true });
508
+ return res.code === 0 && res.stdout.length > 0;
509
+ };
510
+
511
+ if (isValidatorRunning()) {
512
+ log.success("✅ Validator is already running.");
513
+ } else {
514
+ // Spawn validator (this will open the Admin window)
515
+ // We use [] args to start cleanly but persistently.
516
+ // If it fails, the user will see it in the new window.
517
+ await validator([]);
518
+
519
+ // Wait for validator to be ready
520
+ const spin = spinner("Waiting for Validator to respond...").start();
521
+ let retries = 60; // 60 seconds (Increased for Windows genesis unpacking)
522
+ while (retries > 0) {
523
+ if (isValidatorRunning()) {
524
+ spin.succeed("✅ Validator is online.");
525
+ break;
526
+ }
527
+ await new Promise((r) => setTimeout(r, 1000));
528
+ retries--;
529
+ }
447
530
 
448
- if (retries === 0) {
449
- spin.warn("⚠️ Validator might be taking a while to start. Check the blue window.");
450
- }
531
+ if (retries === 0) {
532
+ spin.warn(
533
+ "⚠️ Validator might be taking a while to start. Check the blue window.",
534
+ );
451
535
  }
536
+ }
452
537
 
538
+ // Fix: Pass --skip-local-validator directly (without --) so Anchor consumes it
539
+ test(["--skip-local-validator"]);
453
540
 
454
- // Fix: Pass --skip-local-validator directly (without --) so Anchor consumes it
455
- test(['--skip-local-validator']);
541
+ log.header("👀 Watching for changes...");
542
+ log.info("👉 Change any .rs or .ts file to re-run tests.");
543
+ log.info("👉 Press Ctrl+C to exit.");
456
544
 
457
- log.header("👀 Watching for changes...");
458
- log.info("👉 Change any .rs or .ts file to re-run tests.");
459
- log.info("👉 Press Ctrl+C to exit.");
460
-
461
- let debounce = false;
462
- const runTests = () => {
463
- if (debounce) return;
464
- debounce = true;
465
- setTimeout(() => debounce = false, 2000); // 2s debounce
466
-
467
- console.clear();
468
- log.header("🔄 Detected change. Re-running tests...");
469
- test(['--skip-local-validator']);
470
- log.header("👀 Watching for changes...");
471
- };
545
+ let debounce = false;
546
+ const runTests = () => {
547
+ if (debounce) return;
548
+ debounce = true;
549
+ setTimeout(() => (debounce = false), 2000); // 2s debounce
472
550
 
473
- // Simple Watcher using fs.watch
474
- const watchDirs = ['programs', 'tests'];
475
- watchDirs.forEach(dir => {
476
- const p = path.join(process.cwd(), dir);
477
- if (fs.existsSync(p)) {
478
- fs.watch(p, { recursive: true }, (eventType, filename) => {
479
- if (filename && (filename.endsWith('.rs') || filename.endsWith('.ts'))) {
480
- runTests();
481
- }
482
- });
551
+ console.clear();
552
+ log.header("🔄 Detected change. Re-running tests...");
553
+ test(["--skip-local-validator"]);
554
+ log.header("👀 Watching for changes...");
555
+ };
556
+
557
+ // Simple Watcher using fs.watch
558
+ const watchDirs = ["programs", "tests"];
559
+ watchDirs.forEach((dir) => {
560
+ const p = path.join(process.cwd(), dir);
561
+ if (fs.existsSync(p)) {
562
+ fs.watch(p, { recursive: true }, (eventType, filename) => {
563
+ if (
564
+ filename &&
565
+ (filename.endsWith(".rs") || filename.endsWith(".ts"))
566
+ ) {
567
+ runTests();
483
568
  }
484
- });
569
+ });
570
+ }
571
+ });
485
572
 
486
- // Keep process alive
487
- setInterval(() => { }, 1000);
573
+ // Keep process alive
574
+ setInterval(() => {}, 1000);
488
575
  };
489
576
 
490
577
  module.exports = {
491
- build,
492
- test,
493
- deploy,
494
- clean,
495
- unblock,
496
- airdrop,
497
- address,
498
- balance,
499
- validator,
500
- dev
578
+ build,
579
+ test,
580
+ deploy,
581
+ clean,
582
+ unblock,
583
+ airdrop,
584
+ address,
585
+ balance,
586
+ validator,
587
+ dev,
501
588
  };