win-nice 0.1.0 → 0.2.0

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.
Files changed (53) hide show
  1. package/CHANGELOG.md +141 -0
  2. package/README.md +309 -41
  3. package/bin/abovenormal +11 -11
  4. package/bin/abovenormal.bat +2 -2
  5. package/bin/abovenormal.ps1 +38 -10
  6. package/bin/admin +11 -11
  7. package/bin/admin.bat +2 -2
  8. package/bin/admin.ps1 +52 -13
  9. package/bin/belownormal +11 -11
  10. package/bin/belownormal.bat +2 -2
  11. package/bin/belownormal.ps1 +38 -10
  12. package/bin/{cap → capc} +11 -11
  13. package/bin/{cap.bat → capc.bat} +3 -3
  14. package/bin/capc.ps1 +432 -0
  15. package/bin/{pint → capm} +11 -11
  16. package/bin/capm.bat +12 -0
  17. package/bin/capm.ps1 +506 -0
  18. package/bin/capn +11 -0
  19. package/bin/capn.bat +8 -0
  20. package/bin/capn.ps1 +426 -0
  21. package/bin/caps +11 -0
  22. package/bin/caps.bat +10 -0
  23. package/bin/caps.ps1 +589 -0
  24. package/bin/capt +11 -0
  25. package/bin/capt.bat +8 -0
  26. package/bin/capt.ps1 +449 -0
  27. package/bin/cx +11 -11
  28. package/bin/cx.bat +1 -1
  29. package/bin/cx.ps1 +38 -10
  30. package/bin/cy +11 -11
  31. package/bin/cy.bat +1 -1
  32. package/bin/cy.ps1 +38 -10
  33. package/bin/high +11 -11
  34. package/bin/high.bat +2 -2
  35. package/bin/high.ps1 +38 -10
  36. package/bin/idle +11 -11
  37. package/bin/idle.bat +2 -2
  38. package/bin/idle.ps1 +38 -10
  39. package/bin/realtime +11 -11
  40. package/bin/realtime.bat +2 -2
  41. package/bin/realtime.ps1 +38 -10
  42. package/bin/uiup +11 -11
  43. package/bin/uiup.bat +1 -1
  44. package/bin/uiup.ps1 +2 -1
  45. package/install/install.js +30 -15
  46. package/install/paths.js +28 -2
  47. package/install/skill.js +25 -1
  48. package/install/uninstall.js +11 -0
  49. package/package.json +7 -3
  50. package/skills/win-nice/SKILL.md +107 -12
  51. package/bin/cap.ps1 +0 -269
  52. package/bin/pint.bat +0 -8
  53. package/bin/pint.ps1 +0 -270
package/bin/pint.ps1 DELETED
@@ -1,270 +0,0 @@
1
- # SPDX-License-Identifier: MIT OR Apache-2.0
2
- # win-nice: managed-file
3
- # Deliberately no param()/[CmdletBinding()]: a declared parameter name (even
4
- # without a [Parameter()] attribute) can still be ambiguously prefix-matched by
5
- # flags meant for the wrapped command (e.g. "-c" matching "-Count"). Reading
6
- # everything from $args sidesteps PowerShell's parameter binder entirely.
7
- $processorCount = [Environment]::ProcessorCount
8
- $maxCount = [Math]::Min($processorCount, 63)
9
- if ($args.Count -lt 2) {
10
- Write-Error "usage: pint <thread-count 1-$maxCount> <command> [args...]"
11
- exit 1
12
- }
13
- $countValue = 0
14
- if (-not [int]::TryParse($args[0], [ref]$countValue) -or $countValue -lt 1 -or $countValue -gt $maxCount) {
15
- Write-Error "usage: pint <thread-count 1-$maxCount> <command> [args...]"
16
- exit 1
17
- }
18
- $Command = @($args[1..($args.Count - 1)])
19
-
20
- # Fallback command line for when the target isn't a directly-launchable .exe (see
21
- # PintLauncher.Run below) - re-parsed by cmd.exe (via "cmd.exe /c"), so quoting must
22
- # neutralize its operators (&|<>^) and not just whitespace - see cap.ps1 for the
23
- # same logic and its documented "%" limitation.
24
- $commandLine = ($Command | ForEach-Object {
25
- $escaped = $_ -replace '"', '\"'
26
- if ($escaped -eq '' -or $escaped -match '[\s"&|<>^]') { '"' + $escaped + '"' } else { $escaped }
27
- }) -join ' '
28
-
29
- $source = @"
30
- using System;
31
- using System.Runtime.InteropServices;
32
- using System.Text;
33
-
34
- public static class PintLauncher
35
- {
36
- [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
37
- struct STARTUPINFO
38
- {
39
- public int cb;
40
- public string lpReserved;
41
- public string lpDesktop;
42
- public string lpTitle;
43
- public int dwX;
44
- public int dwY;
45
- public int dwXSize;
46
- public int dwYSize;
47
- public int dwXCountChars;
48
- public int dwYCountChars;
49
- public int dwFillAttribute;
50
- public int dwFlags;
51
- public short wShowWindow;
52
- public short cbReserved2;
53
- public IntPtr lpReserved2;
54
- public IntPtr hStdInput;
55
- public IntPtr hStdOutput;
56
- public IntPtr hStdError;
57
- }
58
-
59
- [StructLayout(LayoutKind.Sequential)]
60
- struct PROCESS_INFORMATION
61
- {
62
- public IntPtr hProcess;
63
- public IntPtr hThread;
64
- public int dwProcessId;
65
- public int dwThreadId;
66
- }
67
-
68
- [StructLayout(LayoutKind.Sequential)]
69
- struct JOBOBJECT_BASIC_LIMIT_INFORMATION
70
- {
71
- public long PerProcessUserTimeLimit;
72
- public long PerJobUserTimeLimit;
73
- public uint LimitFlags;
74
- public UIntPtr MinimumWorkingSetSize;
75
- public UIntPtr MaximumWorkingSetSize;
76
- public uint ActiveProcessLimit;
77
- public UIntPtr Affinity;
78
- public uint PriorityClass;
79
- public uint SchedulingClass;
80
- }
81
-
82
- [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
83
- static extern bool CreateProcess(string lpApplicationName, StringBuilder lpCommandLine,
84
- IntPtr lpProcessAttributes, IntPtr lpThreadAttributes, bool bInheritHandles,
85
- uint dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory,
86
- ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation);
87
-
88
- [DllImport("kernel32.dll", SetLastError = true)]
89
- static extern IntPtr CreateJobObject(IntPtr lpJobAttributes, string lpName);
90
-
91
- [DllImport("kernel32.dll", SetLastError = true)]
92
- static extern bool SetInformationJobObject(IntPtr hJob, int JobObjectInfoClass, IntPtr lpJobObjectInfo, uint cbJobObjectInfoLength);
93
-
94
- [DllImport("kernel32.dll", SetLastError = true)]
95
- static extern bool AssignProcessToJobObject(IntPtr hJob, IntPtr hProcess);
96
-
97
- [DllImport("kernel32.dll", SetLastError = true)]
98
- static extern uint ResumeThread(IntPtr hThread);
99
-
100
- [DllImport("kernel32.dll", SetLastError = true)]
101
- static extern uint WaitForSingleObject(IntPtr hHandle, uint dwMilliseconds);
102
-
103
- [DllImport("kernel32.dll", SetLastError = true)]
104
- static extern bool GetExitCodeProcess(IntPtr hProcess, out uint lpExitCode);
105
-
106
- [DllImport("kernel32.dll", SetLastError = true)]
107
- static extern bool TerminateProcess(IntPtr hProcess, uint uExitCode);
108
-
109
- [DllImport("kernel32.dll")]
110
- static extern bool CloseHandle(IntPtr hObject);
111
-
112
- const uint CREATE_SUSPENDED = 0x00000004;
113
- const int JobObjectBasicLimitInformation = 2;
114
- const uint JOB_OBJECT_LIMIT_AFFINITY = 0x00000010;
115
-
116
- // Standard MSVCRT/CommandLineToArgvW quoting: safe for a directly-launched .exe's
117
- // own argv parsing. No cmd.exe involved on this path, so none of its operator or
118
- // "%" expansion semantics apply - this is the safe path, used whenever possible.
119
- static string ArgvQuote(string arg)
120
- {
121
- if (arg.Length > 0 && arg.IndexOfAny(new char[] { ' ', '\t', '\n', '\v', '"' }) < 0)
122
- return arg;
123
-
124
- var result = new StringBuilder();
125
- result.Append('"');
126
- int backslashes = 0;
127
- foreach (char c in arg)
128
- {
129
- if (c == '\\')
130
- {
131
- backslashes++;
132
- }
133
- else if (c == '"')
134
- {
135
- result.Append('\\', backslashes * 2 + 1);
136
- result.Append('"');
137
- backslashes = 0;
138
- }
139
- else
140
- {
141
- if (backslashes > 0) { result.Append('\\', backslashes); backslashes = 0; }
142
- result.Append(c);
143
- }
144
- }
145
- if (backslashes > 0) result.Append('\\', backslashes * 2);
146
- result.Append('"');
147
- return result.ToString();
148
- }
149
-
150
- static string BuildArgvCommandLine(string[] argv)
151
- {
152
- var parts = new string[argv.Length];
153
- for (int i = 0; i < argv.Length; i++) parts[i] = ArgvQuote(argv[i]);
154
- return string.Join(" ", parts);
155
- }
156
-
157
- public static int Run(ulong affinityMask, string[] argv, string cmdExeCommandLine)
158
- {
159
- IntPtr hJob = CreateJobObject(IntPtr.Zero, null);
160
- if (hJob == IntPtr.Zero)
161
- throw new InvalidOperationException("CreateJobObject failed: " + Marshal.GetLastWin32Error());
162
-
163
- var limitInfo = new JOBOBJECT_BASIC_LIMIT_INFORMATION
164
- {
165
- LimitFlags = JOB_OBJECT_LIMIT_AFFINITY,
166
- Affinity = (UIntPtr)affinityMask
167
- };
168
- int size = Marshal.SizeOf(limitInfo);
169
- IntPtr ptr = Marshal.AllocHGlobal(size);
170
- Marshal.StructureToPtr(limitInfo, ptr, false);
171
- bool ok = SetInformationJobObject(hJob, JobObjectBasicLimitInformation, ptr, (uint)size);
172
- Marshal.FreeHGlobal(ptr);
173
- if (!ok)
174
- {
175
- CloseHandle(hJob);
176
- throw new InvalidOperationException("SetInformationJobObject failed: " + Marshal.GetLastWin32Error());
177
- }
178
-
179
- var si = new STARTUPINFO();
180
- si.cb = Marshal.SizeOf(si);
181
- PROCESS_INFORMATION pi = new PROCESS_INFORMATION();
182
-
183
- // See cap.ps1 for why .bat/.cmd targets skip the direct attempt entirely:
184
- // CreateProcess silently re-invokes them through cmd.exe on its own, using
185
- // unescaped text, instead of failing the way a genuinely missing exe would.
186
- bool isBatOrCmd = argv.Length > 0 && (
187
- argv[0].EndsWith(".bat", StringComparison.OrdinalIgnoreCase) ||
188
- argv[0].EndsWith(".cmd", StringComparison.OrdinalIgnoreCase));
189
-
190
- bool created = false;
191
- if (!isBatOrCmd)
192
- {
193
- var directCommandLine = new StringBuilder(BuildArgvCommandLine(argv));
194
- created = CreateProcess(null, directCommandLine, IntPtr.Zero, IntPtr.Zero, true,
195
- CREATE_SUSPENDED, IntPtr.Zero, null, ref si, out pi);
196
- }
197
-
198
- if (!created)
199
- {
200
- // Falling back to cmd.exe /c: a literal "%" in any argument could now
201
- // trigger environment-variable expansion (cmd.exe pairs up "%" characters
202
- // across the whole command line, even across separate arguments) and
203
- // change what actually runs. Fail loudly here instead of silently risking
204
- // that - there's no reliable per-character escape for "%" at this level.
205
- foreach (var a in argv)
206
- {
207
- if (a.IndexOf('%') >= 0)
208
- {
209
- CloseHandle(hJob);
210
- throw new InvalidOperationException(
211
- "Refusing to run: argument contains '%' and the target needs the cmd.exe " +
212
- "fallback (not a directly-launchable .exe), where '%' can trigger unintended " +
213
- "environment-variable expansion. See README's Argument handling section.");
214
- }
215
- }
216
-
217
- string cmdExe = Environment.SystemDirectory + "\\cmd.exe";
218
- // /d: skip HKCU AutoRun (user-writable registry key). /v:off: disable delayed
219
- // expansion so "!var!" in an argument can't be expanded. /s plus the extra outer
220
- // quote pair: cmd's /S rule strips exactly that outer pair and leaves the rest of
221
- // the string untouched - without /S, cmd strips the first and last quote of the
222
- // whole line instead, which breaks quoting whenever the target path itself needs
223
- // quotes AND another argument is also quoted.
224
- var shellCommandLine = new StringBuilder("\"" + cmdExe + "\" /d /v:off /s /c \"" + cmdExeCommandLine + "\"");
225
- created = CreateProcess(null, shellCommandLine, IntPtr.Zero, IntPtr.Zero, true,
226
- CREATE_SUSPENDED, IntPtr.Zero, null, ref si, out pi);
227
- if (!created)
228
- {
229
- CloseHandle(hJob);
230
- throw new InvalidOperationException("CreateProcess failed: " + Marshal.GetLastWin32Error());
231
- }
232
- }
233
-
234
- if (!AssignProcessToJobObject(hJob, pi.hProcess))
235
- {
236
- // Can't guarantee the pin - kill instead of letting it run unpinned and orphaned.
237
- int err = Marshal.GetLastWin32Error();
238
- TerminateProcess(pi.hProcess, 1);
239
- CloseHandle(pi.hThread);
240
- CloseHandle(pi.hProcess);
241
- CloseHandle(hJob);
242
- throw new InvalidOperationException("AssignProcessToJobObject failed: " + err);
243
- }
244
-
245
- ResumeThread(pi.hThread);
246
- WaitForSingleObject(pi.hProcess, 0xFFFFFFFF);
247
-
248
- uint exitCode;
249
- GetExitCodeProcess(pi.hProcess, out exitCode);
250
-
251
- CloseHandle(pi.hThread);
252
- CloseHandle(pi.hProcess);
253
- CloseHandle(hJob);
254
-
255
- return (int)exitCode;
256
- }
257
- }
258
- "@
259
-
260
- Add-Type -TypeDefinition $source -Language CSharp
261
-
262
- # First $countValue logical processors, i.e. threads - not physical cores. See
263
- # README. Bit-shift, not [Math]::Pow: doubles can't exactly represent 2^63.
264
- $affinityMask = ([uint64]1 -shl $countValue) - [uint64]1
265
- try {
266
- exit ([PintLauncher]::Run($affinityMask, [string[]]$Command, $commandLine))
267
- } catch {
268
- Write-Error $_.Exception.InnerException.Message
269
- exit 1
270
- }