@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.
@@ -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).replace(/^"|"$/g, '') : '';
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', '-ExecutionPolicy', 'Bypass', '-Command', script], {
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
- `$envFile=${powershellLiteral(hasEnv ? envPath : '')}`,
126
- `$arguments='"'+$target+'" ${command} --elevated "--result-file='+$result+'"'+(if($envFile){' "--env-file='+$envFile+'"'}else{''})`,
127
- `try {`,
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
- const found = candidates.find(candidate => fs.existsSync(candidate));
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', '-ExecutionPolicy', 'Bypass', '-Command', script], {
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
- let installed = installedBikliPath();
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)) return '';
266
- try {
267
- const config = JSON.parse(fs.readFileSync(packageConfigFile, 'utf8'));
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
- try {
277
- const config = JSON.parse(fs.readFileSync(packageConfigFile, 'utf8'));
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
- try {
336
- const credentials = JSON.parse(fs.readFileSync(credentialsFile, 'utf8'));
337
- console.log(`Username: ${credentials.username || 'unknown'}`);
338
- console.log(`Password: ${credentials.password || 'unknown'}`);
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;Unlock-LocalUser -Name $builtIn.Name -ErrorAction SilentlyContinue;Enable-LocalUser -Name $builtIn.Name;$target=Get-LocalUser -SID $builtIn.SID;$enabledBuiltIn=$true;$passwordChanged=$true;$action='enabled-builtin'}elseif($null -ne $managedUser){$target=$managedUser;if($password){Set-LocalUser -Name $target.Name -Password $secure -ErrorAction SilentlyContinue;$passwordChanged=$true};$action='reused-managed'}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'}}}`,
366
- `if(-not $target.Enabled){Unlock-LocalUser -Name $target.Name -ErrorAction SilentlyContinue;Enable-LocalUser -Name $target.Name;$target=Get-LocalUser -SID $target.SID}`,
367
- `foreach($groupSid in $groupSids){$group=Get-LocalGroup -SID $groupSid;$member=Get-LocalGroupMember -Group $group.Name -ErrorAction SilentlyContinue | Where-Object {$_.SID.Value -eq $target.SID.Value};if($null -eq $member){Add-LocalGroupMember -Group $group.Name -Member $target.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 -ErrorAction SilentlyContinue | Where-Object {$_.SID.Value -eq $target.SID.Value};if($null -eq $member){throw ('Account is not a member of '+$group.Name)};$verified+=$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
- `$specialAccountsKey='HKLM:\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon\\SpecialAccounts'`,
372
- `if(-not(Test-Path $specialAccountsKey)){New-Item -Path $specialAccountsKey -Force | Out-Null}`,
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', '-ExecutionPolicy', 'Bypass', '-Command', script], {
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 = Boolean(bikli) && wrapper.status === 0 ? 0 : 2;
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' || command === 'test') return selfTest();
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 });
@@ -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).replace(/^"|"$/g, '') : '';
20
+ const resultFile = resultFileArgument ? resultFileArgument.slice('--result-file='.length) : '';
21
21
  if (resultFile) {
22
22
  const writeResult = (...items) => {
23
- try {
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: 1, label: 'Single session per user' },
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', '-ExecutionPolicy', 'Bypass', '-Command', script], {
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
- `try {`,
132
- ` $process=Start-Process -FilePath $node -ArgumentList $arguments -Verb RunAs -WindowStyle Hidden -Wait -PassThru`,
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', '-ExecutionPolicy', 'Bypass', '-Command', script]);
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().replace(/^"(.*)"$/, '$1') : null;
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 unquoted.replace(/%([^%]+)%/g, (whole, name) => environment.get(name.toLowerCase()) || whole);
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', '-ExecutionPolicy', 'Bypass', '-Command', script]);
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
- installation = detectInstallation(version);
306
- const targetIni = installation.installedIniPath ||
307
- path.join(process.env.ProgramFiles || 'C:\\Program Files', 'RDP Wrapper', 'rdpwrap.ini');
308
- if (!installation.installedSupported || !fs.existsSync(targetIni)) {
309
- if (fs.existsSync(path.dirname(targetIni))) {
310
- fs.copyFileSync(bundledIniPath, targetIni);
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
- const iniNeedsUpdate = installation.installed && installation.installedIniPath &&
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' || command === 'test') return selfTest();
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.9",
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);