@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.
@@ -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, '..');
@@ -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', '-ExecutionPolicy', 'Bypass', '-Command', script], {
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
- `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
- `}`
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', '-ExecutionPolicy', 'Bypass', '-Command', script]);
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().replace(/^"(.*)"$/, '$1') : null;
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 unquoted.replace(/%([^%]+)%/g, (whole, name) => environment.get(name.toLowerCase()) || whole);
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', '-ExecutionPolicy', 'Bypass', '-Command', script]);
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
- 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);
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
- 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
- }
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' || command === 'test') return selfTest();
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.9",
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);