@walkhi/code-relax 0.1.0-beta.66 → 0.1.0-beta.68

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.
Files changed (120) hide show
  1. package/dist/bin/codex-remote.mjs +4 -2
  2. package/dist/bin/manage-relay-admin.mjs +20 -20
  3. package/dist/bin/migrate-relay-db.mjs +109 -109
  4. package/dist/bin/publish-harmony-release.mjs +46 -46
  5. package/dist/bin/self-relay-admin.mjs +8 -8
  6. package/dist/bin/self-relay-demo.mjs +33 -33
  7. package/dist/bin/self-relay-server.mjs +66 -66
  8. package/dist/bin/set-relay-admin-password.mjs +11 -11
  9. package/dist/build-platform.json +1 -1
  10. package/dist/get-lan-networks.ps1 +40 -40
  11. package/dist/shared/app-server-events.cjs +511 -511
  12. package/dist/shared/p2p-data-channel.cjs +72 -72
  13. package/dist/shared/p2p-diagnostics.cjs +28 -28
  14. package/dist/shared/p2p-ice-policy.cjs +22 -22
  15. package/dist/shared/plan-pricing.cjs +32 -32
  16. package/dist/shared/question-attention.cjs +69 -69
  17. package/dist/shared/release-version.cjs +31 -31
  18. package/dist/src/archived-threads.mjs +22 -22
  19. package/dist/src/background-process.mjs +18 -18
  20. package/dist/src/code-browser.mjs +100 -100
  21. package/dist/src/desktop-launcher.mjs +62 -62
  22. package/dist/src/dots-task-files.mjs +52 -52
  23. package/dist/src/execution-health.mjs +26 -26
  24. package/dist/src/message-images.mjs +9 -9
  25. package/dist/src/plan-pricing.mjs +47 -47
  26. package/dist/src/platform/codex-runtime.mjs +3 -3
  27. package/dist/src/platform/credential-store.mjs +19 -19
  28. package/dist/src/platform/desktop-host.mjs +3 -3
  29. package/dist/src/platform/desktop-permission-mode.mjs +17 -17
  30. package/dist/src/platform/desktop-project-registration.mjs +3 -3
  31. package/dist/src/platform/file-opener.mjs +9 -9
  32. package/dist/src/platform/linux/README.md +3 -3
  33. package/dist/src/platform/linux/codex-runtime.mjs +56 -56
  34. package/dist/src/platform/linux/desktop-installation.mjs +36 -36
  35. package/dist/src/platform/linux/desktop-project-registration.mjs +11 -11
  36. package/dist/src/platform/linux/network-info.mjs +11 -11
  37. package/dist/src/platform/linux/project-locations.mjs +17 -17
  38. package/dist/src/platform/linux/resident-startup.mjs +52 -52
  39. package/dist/src/platform/linux/retained-runtime.mjs +76 -76
  40. package/dist/src/platform/linux/service-host.mjs +18 -18
  41. package/dist/src/platform/macos/README.md +3 -3
  42. package/dist/src/platform/macos/desktop-project-registration.mjs +11 -11
  43. package/dist/src/platform/macos/network-info.mjs +8 -8
  44. package/dist/src/platform/macos/project-locations.mjs +8 -8
  45. package/dist/src/platform/macos/resident-startup.mjs +84 -84
  46. package/dist/src/platform/macos/retained-runtime.mjs +127 -127
  47. package/dist/src/platform/macos/service-host.mjs +18 -18
  48. package/dist/src/platform/macos/system.mjs +29 -29
  49. package/dist/src/platform/network-info.mjs +3 -3
  50. package/dist/src/platform/path-identity.mjs +5 -5
  51. package/dist/src/platform/posix/network-candidates.mjs +23 -23
  52. package/dist/src/platform/posix/runtime-paths.mjs +25 -25
  53. package/dist/src/platform/power-inhibitor.mjs +22 -22
  54. package/dist/src/platform/project-locations.mjs +3 -3
  55. package/dist/src/platform/runtime-paths.mjs +8 -8
  56. package/dist/src/platform/select.mjs +5 -5
  57. package/dist/src/platform/service-host.mjs +3 -3
  58. package/dist/src/platform/user-environment.mjs +3 -3
  59. package/dist/src/platform/windows/IsolatedProcess.cs +79 -79
  60. package/dist/src/platform/windows/background-process.mjs +30 -30
  61. package/dist/src/platform/windows/desktop-helper.mjs +179 -179
  62. package/dist/src/platform/windows/desktop-runtime.ps1 +10 -10
  63. package/dist/src/platform/windows/desktop-shortcuts.ps1 +7 -7
  64. package/dist/src/platform/windows/keep-awake.ps1 +20 -20
  65. package/dist/src/platform/windows/network-info.mjs +34 -34
  66. package/dist/src/platform/windows/resident-start.js +20 -20
  67. package/dist/src/platform/windows/resident-start.ps1 +7 -7
  68. package/dist/src/platform/windows/resident-startup.ps1 +83 -83
  69. package/dist/src/platform/windows/resolve-state-root.ps1 +14 -14
  70. package/dist/src/platform/windows/runtime-paths.mjs +20 -20
  71. package/dist/src/platform/windows/rustdesk-video-probe.mjs +167 -167
  72. package/dist/src/platform/windows/start-hidden-console.ps1 +58 -58
  73. package/dist/src/platform/windows/stop-desktop.ps1 +22 -22
  74. package/dist/src/platform/windows/user-environment.ps1 +41 -41
  75. package/dist/src/project-create.mjs +121 -121
  76. package/dist/src/project-management.mjs +1 -1
  77. package/dist/src/project-remove.mjs +55 -55
  78. package/dist/src/project-update.mjs +59 -59
  79. package/dist/src/relay-config-import.mjs +45 -45
  80. package/dist/src/self-relay/account-connections.mjs +52 -52
  81. package/dist/src/self-relay/admin-auth.mjs +59 -59
  82. package/dist/src/self-relay/admin-cloud.mjs +101 -101
  83. package/dist/src/self-relay/admin-login-page.mjs +7 -7
  84. package/dist/src/self-relay/admin-server.mjs +105 -105
  85. package/dist/src/self-relay/admin-state.mjs +127 -127
  86. package/dist/src/self-relay/admin-workstations-page.mjs +95 -95
  87. package/dist/src/self-relay/admin-workstations.mjs +143 -143
  88. package/dist/src/self-relay/android-releases.mjs +61 -61
  89. package/dist/src/self-relay/client.mjs +61 -61
  90. package/dist/src/self-relay/connection-reporting.mjs +38 -38
  91. package/dist/src/self-relay/debug-allowlist.mjs +33 -33
  92. package/dist/src/self-relay/demo.mjs +6 -6
  93. package/dist/src/self-relay/device-info.mjs +27 -27
  94. package/dist/src/self-relay/device-store.mjs +92 -92
  95. package/dist/src/self-relay/lan-authorizations.mjs +71 -71
  96. package/dist/src/self-relay/lifecycle.mjs +278 -278
  97. package/dist/src/self-relay/long-pairing.mjs +40 -40
  98. package/dist/src/self-relay/mysql-state.mjs +118 -118
  99. package/dist/src/self-relay/phone-connections.mjs +55 -55
  100. package/dist/src/self-relay/server.mjs +1066 -1066
  101. package/dist/src/self-relay/usage-daily.mjs +55 -55
  102. package/dist/src/self-relay/windows-route-helper.mjs +75 -75
  103. package/dist/src/self-relay/wire.mjs +36 -36
  104. package/dist/src/server.mjs +1947 -1947
  105. package/dist/src/service-control.mjs +82 -82
  106. package/dist/src/service-health.mjs +33 -33
  107. package/dist/src/service-power.mjs +58 -58
  108. package/dist/src/service-restart.mjs +81 -81
  109. package/dist/src/service-update-worker.mjs +42 -42
  110. package/dist/src/service-update.mjs +80 -80
  111. package/dist/src/shared-recovery.mjs +152 -152
  112. package/dist/src/skill-install.mjs +51 -51
  113. package/dist/src/thread-title-generation.mjs +150 -150
  114. package/dist/src/turn-model-history.mjs +47 -47
  115. package/dist/tools/rtc-route-helper.ps1 +99 -99
  116. package/dist/tools/start-rtc-route-helper.ps1 +15 -15
  117. package/dist/web/vendor/markdown-it.LICENSE.txt +22 -22
  118. package/dist/web/vendor/markdown-it.umd.min.js +13 -13
  119. package/package.json +4 -4
  120. package/tools/postinstall.mjs +1 -1
@@ -1,21 +1,21 @@
1
- param([string]$NodePath, [string]$Entry, [string]$Destination)
1
+ param([string]$NodePath, [string]$Entry, [string]$Destination)
2
2
  $ErrorActionPreference = 'Stop'
3
3
  [Console]::OutputEncoding = New-Object System.Text.UTF8Encoding($false)
4
4
  $shell = New-Object -ComObject WScript.Shell
5
5
  if (-not $Destination) { $Destination = $shell.SpecialFolders.Item('Desktop') }
6
- # Only identify and remove the retired shortcut; never create a new launcher.
7
- $launcher = Join-Path $PSScriptRoot 'desktop-shortcut-run.ps1'
8
- $items = @(@{Name='Code Relax - Codex 内核'; Command='shared-start'})
6
+ # Only identify and remove the retired shortcut; never create a new launcher.
7
+ $launcher = Join-Path $PSScriptRoot 'desktop-shortcut-run.ps1'
8
+ $items = @(@{Name='Code Relax - Codex 内核'; Command='shared-start'})
9
9
  $results = @()
10
10
  foreach ($item in $items) {
11
11
  $target = Join-Path $Destination ($item.Name + '.lnk')
12
12
  $arguments = '-NoProfile -NoExit -ExecutionPolicy Bypass -File "' + $launcher + '" -NodePath "' + $NodePath + '" -Entry "' + $Entry + '" -Action ' + $item.Command
13
13
  if (Test-Path -LiteralPath $target) {
14
14
  $existing = $shell.CreateShortcut($target)
15
- $powershell = Join-Path $env:SystemRoot 'System32/WindowsPowerShell/v1.0/powershell.exe'
16
- if ($existing.Arguments -ne $arguments -or $existing.TargetPath -ne $powershell) { continue }
15
+ $powershell = Join-Path $env:SystemRoot 'System32/WindowsPowerShell/v1.0/powershell.exe'
16
+ if ($existing.Arguments -ne $arguments -or $existing.TargetPath -ne $powershell) { continue }
17
17
  }
18
- if (Test-Path -LiteralPath $target) { Remove-Item -LiteralPath $target }
18
+ if (Test-Path -LiteralPath $target) { Remove-Item -LiteralPath $target }
19
19
  $results += $target
20
20
  }
21
21
  ConvertTo-Json -InputObject $results -Compress
@@ -1,20 +1,20 @@
1
- $ErrorActionPreference = 'Stop'
2
- Import-Module "$env:SystemRoot\System32\WindowsPowerShell\v1.0\Modules\Microsoft.PowerShell.Utility\Microsoft.PowerShell.Utility.psd1"
3
- Add-Type -TypeDefinition @'
4
- using System;
5
- using System.Runtime.InteropServices;
6
- public static class CodeRelaxPowerLease {
7
- [DllImport("kernel32.dll", SetLastError = true)]
8
- static extern uint SetThreadExecutionState(uint flags);
9
- public static void Hold() {
10
- if (SetThreadExecutionState(0x80000001u) == 0)
11
- throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
12
- try {
13
- Console.WriteLine("ready");
14
- Console.Out.Flush();
15
- Console.In.ReadLine();
16
- } finally { SetThreadExecutionState(0x80000000u); }
17
- }
18
- }
19
- '@
20
- [CodeRelaxPowerLease]::Hold()
1
+ $ErrorActionPreference = 'Stop'
2
+ Import-Module "$env:SystemRoot\System32\WindowsPowerShell\v1.0\Modules\Microsoft.PowerShell.Utility\Microsoft.PowerShell.Utility.psd1"
3
+ Add-Type -TypeDefinition @'
4
+ using System;
5
+ using System.Runtime.InteropServices;
6
+ public static class CodeRelaxPowerLease {
7
+ [DllImport("kernel32.dll", SetLastError = true)]
8
+ static extern uint SetThreadExecutionState(uint flags);
9
+ public static void Hold() {
10
+ if (SetThreadExecutionState(0x80000001u) == 0)
11
+ throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
12
+ try {
13
+ Console.WriteLine("ready");
14
+ Console.Out.Flush();
15
+ Console.In.ReadLine();
16
+ } finally { SetThreadExecutionState(0x80000000u); }
17
+ }
18
+ }
19
+ '@
20
+ [CodeRelaxPowerLease]::Hold()
@@ -1,34 +1,34 @@
1
- import { execFile } from 'node:child_process';
2
- import { promisify } from 'node:util';
3
- import path from 'node:path';
4
- import { fileURLToPath } from 'node:url';
5
-
6
- const exec = promisify(execFile);
7
- const bridgeRoot = fileURLToPath(new URL('../../../', import.meta.url));
8
-
9
- export async function lanNetworks() {
10
- const executable = path.join(process.env.SystemRoot, 'System32/WindowsPowerShell/v1.0/powershell.exe');
11
- const { stdout } = await exec(executable, ['-NoProfile', '-NonInteractive', '-File', path.join(bridgeRoot, 'get-lan-networks.ps1')],
12
- { windowsHide: true, encoding: 'utf8' });
13
- const records = JSON.parse(stdout.trim() || '[]');
14
- if (!Array.isArray(records) || records.length > 8) throw new Error('Windows 返回了无效的局域网信息');
15
- return records.map(record => {
16
- if (!privateIpv4(record.address) || !['wifi', 'ethernet'].includes(record.networkType) ||
17
- !plainLabel(record.networkName) || !plainLabel(record.interfaceName)) throw new Error('Windows 返回了无效的局域网信息');
18
- return { address: record.address, networkType: record.networkType,
19
- networkName: record.networkName.trim(), interfaceName: record.interfaceName.trim() };
20
- });
21
- }
22
-
23
- function privateIpv4(value) {
24
- if (typeof value !== 'string') return false;
25
- const parts = value.split('.').map(Number);
26
- if (parts.length !== 4 || parts.some(part => !Number.isInteger(part) || part < 0 || part > 255)) return false;
27
- return parts[0] === 10 || (parts[0] === 192 && parts[1] === 168) ||
28
- (parts[0] === 172 && parts[1] >= 16 && parts[1] <= 31) ||
29
- (parts[0] === 100 && parts[1] >= 64 && parts[1] <= 127);
30
- }
31
-
32
- function plainLabel(value) {
33
- return typeof value === 'string' && value.trim().length > 0 && value.trim().length <= 80 && !/[\u0000-\u001f\u007f]/.test(value);
34
- }
1
+ import { execFile } from 'node:child_process';
2
+ import { promisify } from 'node:util';
3
+ import path from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+
6
+ const exec = promisify(execFile);
7
+ const bridgeRoot = fileURLToPath(new URL('../../../', import.meta.url));
8
+
9
+ export async function lanNetworks() {
10
+ const executable = path.join(process.env.SystemRoot, 'System32/WindowsPowerShell/v1.0/powershell.exe');
11
+ const { stdout } = await exec(executable, ['-NoProfile', '-NonInteractive', '-File', path.join(bridgeRoot, 'get-lan-networks.ps1')],
12
+ { windowsHide: true, encoding: 'utf8' });
13
+ const records = JSON.parse(stdout.trim() || '[]');
14
+ if (!Array.isArray(records) || records.length > 8) throw new Error('Windows 返回了无效的局域网信息');
15
+ return records.map(record => {
16
+ if (!privateIpv4(record.address) || !['wifi', 'ethernet'].includes(record.networkType) ||
17
+ !plainLabel(record.networkName) || !plainLabel(record.interfaceName)) throw new Error('Windows 返回了无效的局域网信息');
18
+ return { address: record.address, networkType: record.networkType,
19
+ networkName: record.networkName.trim(), interfaceName: record.interfaceName.trim() };
20
+ });
21
+ }
22
+
23
+ function privateIpv4(value) {
24
+ if (typeof value !== 'string') return false;
25
+ const parts = value.split('.').map(Number);
26
+ if (parts.length !== 4 || parts.some(part => !Number.isInteger(part) || part < 0 || part > 255)) return false;
27
+ return parts[0] === 10 || (parts[0] === 192 && parts[1] === 168) ||
28
+ (parts[0] === 172 && parts[1] >= 16 && parts[1] <= 31) ||
29
+ (parts[0] === 100 && parts[1] >= 64 && parts[1] <= 127);
30
+ }
31
+
32
+ function plainLabel(value) {
33
+ return typeof value === 'string' && value.trim().length > 0 && value.trim().length <= 80 && !/[\u0000-\u001f\u007f]/.test(value);
34
+ }
@@ -1,20 +1,20 @@
1
- // Windows Script Host has no console. Keep the login task alive until the
2
- // short-lived CLI has handed the resident service to the background broker.
3
- if (WScript.Arguments.length !== 2) WScript.Quit(2);
4
- var shell = new ActiveXObject('WScript.Shell');
5
- var files = new ActiveXObject('Scripting.FileSystemObject');
6
- var node = WScript.Arguments.Item(0);
7
- var entry = WScript.Arguments.Item(1);
8
- if (!files.FileExists(node)) {
9
- var directories = shell.Environment('PROCESS').Item('PATH').split(';');
10
- node = '';
11
- for (var i = 0; i < directories.length; i++) {
12
- var directory = directories[i].replace(/^"|"$/g, '');
13
- if (!directory) continue;
14
- var candidate = files.BuildPath(directory, 'node.exe');
15
- if (files.FileExists(candidate)) { node = candidate; break; }
16
- }
17
- if (!node) WScript.Quit(2);
18
- }
19
- var command = '"' + node + '" "' + entry + '" start --json';
20
- WScript.Quit(shell.Run(command, 0, true));
1
+ // Windows Script Host has no console. Keep the login task alive until the
2
+ // short-lived CLI has handed the resident service to the background broker.
3
+ if (WScript.Arguments.length !== 2) WScript.Quit(2);
4
+ var shell = new ActiveXObject('WScript.Shell');
5
+ var files = new ActiveXObject('Scripting.FileSystemObject');
6
+ var node = WScript.Arguments.Item(0);
7
+ var entry = WScript.Arguments.Item(1);
8
+ if (!files.FileExists(node)) {
9
+ var directories = shell.Environment('PROCESS').Item('PATH').split(';');
10
+ node = '';
11
+ for (var i = 0; i < directories.length; i++) {
12
+ var directory = directories[i].replace(/^"|"$/g, '');
13
+ if (!directory) continue;
14
+ var candidate = files.BuildPath(directory, 'node.exe');
15
+ if (files.FileExists(candidate)) { node = candidate; break; }
16
+ }
17
+ if (!node) WScript.Quit(2);
18
+ }
19
+ var command = '"' + node + '" "' + entry + '" start --json';
20
+ WScript.Quit(shell.Run(command, 0, true));
@@ -1,7 +1,7 @@
1
- param([Parameter(Mandatory=$true)][string]$NodePath, [Parameter(Mandatory=$true)][string]$Entry)
2
- $ErrorActionPreference = 'Stop'
3
- [Console]::OutputEncoding = New-Object System.Text.UTF8Encoding($false)
4
- $state = Join-Path $env:LOCALAPPDATA 'CodexRemote'
5
- New-Item -ItemType Directory -Path $state -Force | Out-Null
6
- & $NodePath $Entry start --json >> (Join-Path $state 'resident-start.log') 2>&1
7
- exit $LASTEXITCODE
1
+ param([Parameter(Mandatory=$true)][string]$NodePath, [Parameter(Mandatory=$true)][string]$Entry)
2
+ $ErrorActionPreference = 'Stop'
3
+ [Console]::OutputEncoding = New-Object System.Text.UTF8Encoding($false)
4
+ $state = Join-Path $env:LOCALAPPDATA 'CodexRemote'
5
+ New-Item -ItemType Directory -Path $state -Force | Out-Null
6
+ & $NodePath $Entry start --json >> (Join-Path $state 'resident-start.log') 2>&1
7
+ exit $LASTEXITCODE
@@ -1,83 +1,83 @@
1
- param([string]$NodePath, [string]$Entry, [ValidateSet('enable','disable','status')][string]$Action='status')
2
- $ErrorActionPreference = 'Stop'
3
- [Console]::OutputEncoding = New-Object System.Text.UTF8Encoding($false)
4
-
5
- $taskName = 'Code Relax 桌面服务'
6
- $legacyTaskName = 'Code Relax Bridge'
7
- $taskSource = '@walkhi/code-relax'
8
- $taskPath = '\' + $taskName
9
- $launcher = Join-Path $PSScriptRoot 'resident-start.js'
10
- $wscript = Join-Path $env:WINDIR 'System32\wscript.exe'
11
- $arguments = '//B //Nologo "' + $launcher + '" "' + $NodePath + '" "' + $Entry + '"'
12
- $currentUser = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name
13
-
14
- $scheduler = New-Object -ComObject 'Schedule.Service'
15
- $scheduler.Connect()
16
- $root = $scheduler.GetFolder('\')
17
- $registered = @($root.GetTasks(1) | Where-Object { $_.Name -eq $taskName } | Select-Object -First 1)
18
- $legacyRegistered = @($root.GetTasks(1) | Where-Object { $_.Name -eq $legacyTaskName } | Select-Object -First 1)
19
- if ($registered.Count -gt 0 -and $registered[0].Definition.RegistrationInfo.Source -ne $taskSource) {
20
- throw 'Scheduled task is not owned by this installation.'
21
- }
22
- if ($legacyRegistered.Count -gt 0 -and $legacyRegistered[0].Definition.RegistrationInfo.Source -ne $taskSource) {
23
- throw 'Legacy scheduled task is not owned by this installation.'
24
- }
25
-
26
- # Remove only the legacy shortcut created by this package. A Startup LNK that
27
- # launches hidden PowerShell with ExecutionPolicy Bypass triggers AV heuristics.
28
- $shell = New-Object -ComObject WScript.Shell
29
- $legacyPath = Join-Path $shell.SpecialFolders.Item('Startup') 'Code Relax Bridge.lnk'
30
- $legacyLauncher = Join-Path $PSScriptRoot 'resident-start.ps1'
31
- $legacyExists = Test-Path -LiteralPath $legacyPath
32
- if ($Action -ne 'status' -and $legacyExists) {
33
- $legacy = $shell.CreateShortcut($legacyPath)
34
- if (-not $legacy.Arguments.Contains('"' + $legacyLauncher + '"')) {
35
- throw 'Legacy startup shortcut is not owned by this installation.'
36
- }
37
- }
38
-
39
- if ($Action -eq 'enable') {
40
- if (-not (Test-Path -LiteralPath $NodePath) -or -not (Test-Path -LiteralPath $Entry) -or
41
- -not (Test-Path -LiteralPath $launcher) -or -not (Test-Path -LiteralPath $wscript)) { throw 'Installed runtime is missing.' }
42
- $definition = $scheduler.NewTask(0)
43
- $definition.RegistrationInfo.Description = 'Start the Code Relax desktop service when this user signs in.'
44
- $definition.RegistrationInfo.Source = $taskSource
45
- $definition.Principal.UserId = $currentUser
46
- $definition.Principal.LogonType = 3 # TASK_LOGON_INTERACTIVE_TOKEN
47
- $definition.Principal.RunLevel = 0 # TASK_RUNLEVEL_LUA
48
- $definition.Settings.Enabled = $true
49
- $definition.Settings.Hidden = $true
50
- $definition.Settings.StartWhenAvailable = $true
51
- $definition.Settings.DisallowStartIfOnBatteries = $false
52
- $definition.Settings.StopIfGoingOnBatteries = $false
53
- $definition.Settings.ExecutionTimeLimit = 'PT5M'
54
- $definition.Settings.MultipleInstances = 2 # TASK_INSTANCES_IGNORE_NEW
55
- $trigger = $definition.Triggers.Create(9) # TASK_TRIGGER_LOGON
56
- $trigger.UserId = $currentUser
57
- $trigger.Enabled = $true
58
- $exec = $definition.Actions.Create(0) # TASK_ACTION_EXEC
59
- $exec.Path = $wscript
60
- $exec.Arguments = $arguments
61
- $exec.WorkingDirectory = $env:USERPROFILE
62
- [void]$root.RegisterTaskDefinition($taskName, $definition, 6, $null, $null, 3, $null) # TASK_CREATE_OR_UPDATE
63
- if ($legacyRegistered.Count -gt 0) { $root.DeleteTask($legacyTaskName, 0) }
64
- } elseif ($Action -eq 'disable') {
65
- if ($registered.Count -gt 0) { $root.DeleteTask($taskName, 0) }
66
- if ($legacyRegistered.Count -gt 0) { $root.DeleteTask($legacyTaskName, 0) }
67
- }
68
- if ($Action -ne 'status' -and $legacyExists) { Remove-Item -LiteralPath $legacyPath }
69
-
70
- $active = @($root.GetTasks(1) | Where-Object { $_.Name -eq $taskName } | Select-Object -First 1)
71
- $taskEnabled = $active.Count -gt 0 -and $active[0].Enabled
72
- $taskCurrent = $false
73
- if ($taskEnabled) {
74
- $taskAction = $active[0].Definition.Actions.Item(1)
75
- $taskCurrent = $taskAction.Path -ieq $wscript -and $taskAction.Arguments -ieq $arguments
76
- }
77
- @{
78
- enabled=($taskCurrent -and $legacyRegistered.Count -eq 0)
79
- taskPresent=$taskEnabled
80
- path=$(if ($taskEnabled) { 'Task Scheduler:' + $taskPath } elseif ($legacyRegistered.Count -gt 0) { 'Task Scheduler:\' + $legacyTaskName } elseif ($legacyExists) { $legacyPath } else { 'Task Scheduler:' + $taskPath })
81
- scope='current-user-logon'
82
- mechanism=$(if ($taskCurrent -and $legacyRegistered.Count -eq 0) { 'task-scheduler' } elseif ($taskEnabled -or $legacyRegistered.Count -gt 0) { 'stale-task-scheduler' } elseif ($legacyExists) { 'legacy-startup-shortcut' } else { 'task-scheduler' })
83
- } | ConvertTo-Json -Compress
1
+ param([string]$NodePath, [string]$Entry, [ValidateSet('enable','disable','status')][string]$Action='status')
2
+ $ErrorActionPreference = 'Stop'
3
+ [Console]::OutputEncoding = New-Object System.Text.UTF8Encoding($false)
4
+
5
+ $taskName = 'Code Relax 桌面服务'
6
+ $legacyTaskName = 'Code Relax Bridge'
7
+ $taskSource = '@walkhi/code-relax'
8
+ $taskPath = '\' + $taskName
9
+ $launcher = Join-Path $PSScriptRoot 'resident-start.js'
10
+ $wscript = Join-Path $env:WINDIR 'System32\wscript.exe'
11
+ $arguments = '//B //Nologo "' + $launcher + '" "' + $NodePath + '" "' + $Entry + '"'
12
+ $currentUser = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name
13
+
14
+ $scheduler = New-Object -ComObject 'Schedule.Service'
15
+ $scheduler.Connect()
16
+ $root = $scheduler.GetFolder('\')
17
+ $registered = @($root.GetTasks(1) | Where-Object { $_.Name -eq $taskName } | Select-Object -First 1)
18
+ $legacyRegistered = @($root.GetTasks(1) | Where-Object { $_.Name -eq $legacyTaskName } | Select-Object -First 1)
19
+ if ($registered.Count -gt 0 -and $registered[0].Definition.RegistrationInfo.Source -ne $taskSource) {
20
+ throw 'Scheduled task is not owned by this installation.'
21
+ }
22
+ if ($legacyRegistered.Count -gt 0 -and $legacyRegistered[0].Definition.RegistrationInfo.Source -ne $taskSource) {
23
+ throw 'Legacy scheduled task is not owned by this installation.'
24
+ }
25
+
26
+ # Remove only the legacy shortcut created by this package. A Startup LNK that
27
+ # launches hidden PowerShell with ExecutionPolicy Bypass triggers AV heuristics.
28
+ $shell = New-Object -ComObject WScript.Shell
29
+ $legacyPath = Join-Path $shell.SpecialFolders.Item('Startup') 'Code Relax Bridge.lnk'
30
+ $legacyLauncher = Join-Path $PSScriptRoot 'resident-start.ps1'
31
+ $legacyExists = Test-Path -LiteralPath $legacyPath
32
+ if ($Action -ne 'status' -and $legacyExists) {
33
+ $legacy = $shell.CreateShortcut($legacyPath)
34
+ if (-not $legacy.Arguments.Contains('"' + $legacyLauncher + '"')) {
35
+ throw 'Legacy startup shortcut is not owned by this installation.'
36
+ }
37
+ }
38
+
39
+ if ($Action -eq 'enable') {
40
+ if (-not (Test-Path -LiteralPath $NodePath) -or -not (Test-Path -LiteralPath $Entry) -or
41
+ -not (Test-Path -LiteralPath $launcher) -or -not (Test-Path -LiteralPath $wscript)) { throw 'Installed runtime is missing.' }
42
+ $definition = $scheduler.NewTask(0)
43
+ $definition.RegistrationInfo.Description = 'Start the Code Relax desktop service when this user signs in.'
44
+ $definition.RegistrationInfo.Source = $taskSource
45
+ $definition.Principal.UserId = $currentUser
46
+ $definition.Principal.LogonType = 3 # TASK_LOGON_INTERACTIVE_TOKEN
47
+ $definition.Principal.RunLevel = 0 # TASK_RUNLEVEL_LUA
48
+ $definition.Settings.Enabled = $true
49
+ $definition.Settings.Hidden = $true
50
+ $definition.Settings.StartWhenAvailable = $true
51
+ $definition.Settings.DisallowStartIfOnBatteries = $false
52
+ $definition.Settings.StopIfGoingOnBatteries = $false
53
+ $definition.Settings.ExecutionTimeLimit = 'PT5M'
54
+ $definition.Settings.MultipleInstances = 2 # TASK_INSTANCES_IGNORE_NEW
55
+ $trigger = $definition.Triggers.Create(9) # TASK_TRIGGER_LOGON
56
+ $trigger.UserId = $currentUser
57
+ $trigger.Enabled = $true
58
+ $exec = $definition.Actions.Create(0) # TASK_ACTION_EXEC
59
+ $exec.Path = $wscript
60
+ $exec.Arguments = $arguments
61
+ $exec.WorkingDirectory = $env:USERPROFILE
62
+ [void]$root.RegisterTaskDefinition($taskName, $definition, 6, $null, $null, 3, $null) # TASK_CREATE_OR_UPDATE
63
+ if ($legacyRegistered.Count -gt 0) { $root.DeleteTask($legacyTaskName, 0) }
64
+ } elseif ($Action -eq 'disable') {
65
+ if ($registered.Count -gt 0) { $root.DeleteTask($taskName, 0) }
66
+ if ($legacyRegistered.Count -gt 0) { $root.DeleteTask($legacyTaskName, 0) }
67
+ }
68
+ if ($Action -ne 'status' -and $legacyExists) { Remove-Item -LiteralPath $legacyPath }
69
+
70
+ $active = @($root.GetTasks(1) | Where-Object { $_.Name -eq $taskName } | Select-Object -First 1)
71
+ $taskEnabled = $active.Count -gt 0 -and $active[0].Enabled
72
+ $taskCurrent = $false
73
+ if ($taskEnabled) {
74
+ $taskAction = $active[0].Definition.Actions.Item(1)
75
+ $taskCurrent = $taskAction.Path -ieq $wscript -and $taskAction.Arguments -ieq $arguments
76
+ }
77
+ @{
78
+ enabled=($taskCurrent -and $legacyRegistered.Count -eq 0)
79
+ taskPresent=$taskEnabled
80
+ path=$(if ($taskEnabled) { 'Task Scheduler:' + $taskPath } elseif ($legacyRegistered.Count -gt 0) { 'Task Scheduler:\' + $legacyTaskName } elseif ($legacyExists) { $legacyPath } else { 'Task Scheduler:' + $taskPath })
81
+ scope='current-user-logon'
82
+ mechanism=$(if ($taskCurrent -and $legacyRegistered.Count -eq 0) { 'task-scheduler' } elseif ($taskEnabled -or $legacyRegistered.Count -gt 0) { 'stale-task-scheduler' } elseif ($legacyExists) { 'legacy-startup-shortcut' } else { 'task-scheduler' })
83
+ } | ConvertTo-Json -Compress
@@ -1,14 +1,14 @@
1
- param([Parameter(Mandatory=$true)][string]$StateRoot)
2
- $ErrorActionPreference='Stop'
3
- [Console]::OutputEncoding=New-Object Text.UTF8Encoding($false)
4
- Add-Type -Path (Join-Path $PSScriptRoot 'IsolatedProcess.cs')
5
- New-Item -ItemType Directory -Path $StateRoot -Force | Out-Null
6
- $anchor=Join-Path $StateRoot 'bridge-config.json'
7
- $temporary=$false
8
- if(-not(Test-Path -LiteralPath $anchor)) {
9
- $anchor=Join-Path $StateRoot ('.state-path-'+[Guid]::NewGuid().ToString()+'.tmp')
10
- Set-Content -LiteralPath $anchor -Value 'path-resolution' -Encoding ASCII
11
- $temporary=$true
12
- }
13
- try { [IO.Path]::GetDirectoryName([IsolatedProcess]::ResolvePath($anchor)) }
14
- finally { if($temporary){Remove-Item -LiteralPath $anchor} }
1
+ param([Parameter(Mandatory=$true)][string]$StateRoot)
2
+ $ErrorActionPreference='Stop'
3
+ [Console]::OutputEncoding=New-Object Text.UTF8Encoding($false)
4
+ Add-Type -Path (Join-Path $PSScriptRoot 'IsolatedProcess.cs')
5
+ New-Item -ItemType Directory -Path $StateRoot -Force | Out-Null
6
+ $anchor=Join-Path $StateRoot 'bridge-config.json'
7
+ $temporary=$false
8
+ if(-not(Test-Path -LiteralPath $anchor)) {
9
+ $anchor=Join-Path $StateRoot ('.state-path-'+[Guid]::NewGuid().ToString()+'.tmp')
10
+ Set-Content -LiteralPath $anchor -Value 'path-resolution' -Encoding ASCII
11
+ $temporary=$true
12
+ }
13
+ try { [IO.Path]::GetDirectoryName([IsolatedProcess]::ResolvePath($anchor)) }
14
+ finally { if($temporary){Remove-Item -LiteralPath $anchor} }
@@ -1,20 +1,20 @@
1
- import os from 'node:os';
2
- import path from 'node:path';
3
- import fs from 'node:fs';
4
-
5
- // Windows host data locations. Keep existing paths and historical attachments.
6
- export function runtimePaths(environment = process.env, home = os.homedir()) {
7
- const codexDataRoot = environment.CODEX_HOME?.trim() || path.join(home, '.codex');
8
- const serviceRoot = path.join(environment.LOCALAPPDATA || path.join(home, 'AppData', 'Local'), 'Programs', 'CodexRemote', 'service');
9
- let installed;
10
- try { installed = JSON.parse(fs.readFileSync(path.join(serviceRoot, 'installation.json'), 'utf8').replace(/^\uFEFF/, '')); }
11
- catch (error) { if (error.code !== 'ENOENT') throw error; }
12
- return {
13
- codexDataRoot,
14
- sessionsRoot: path.join(codexDataRoot, 'sessions'),
15
- stateRoot: installed?.stateRoot || path.join(environment.LOCALAPPDATA || path.join(home, 'AppData', 'Local'), 'CodexRemote'),
16
- serviceRoot,
17
- uploadsRoot: path.join(environment.ProgramData || 'C:\\ProgramData', 'CodexRemote', 'attachments'),
18
- credentialsRoot: path.join(environment.ProgramData || 'C:\\ProgramData', 'CodexRemote', 'credentials'),
19
- };
20
- }
1
+ import os from 'node:os';
2
+ import path from 'node:path';
3
+ import fs from 'node:fs';
4
+
5
+ // Windows host data locations. Keep existing paths and historical attachments.
6
+ export function runtimePaths(environment = process.env, home = os.homedir()) {
7
+ const codexDataRoot = environment.CODEX_HOME?.trim() || path.join(home, '.codex');
8
+ const serviceRoot = path.join(environment.LOCALAPPDATA || path.join(home, 'AppData', 'Local'), 'Programs', 'CodexRemote', 'service');
9
+ let installed;
10
+ try { installed = JSON.parse(fs.readFileSync(path.join(serviceRoot, 'installation.json'), 'utf8').replace(/^\uFEFF/, '')); }
11
+ catch (error) { if (error.code !== 'ENOENT') throw error; }
12
+ return {
13
+ codexDataRoot,
14
+ sessionsRoot: path.join(codexDataRoot, 'sessions'),
15
+ stateRoot: installed?.stateRoot || path.join(environment.LOCALAPPDATA || path.join(home, 'AppData', 'Local'), 'CodexRemote'),
16
+ serviceRoot,
17
+ uploadsRoot: path.join(environment.ProgramData || 'C:\\ProgramData', 'CodexRemote', 'attachments'),
18
+ credentialsRoot: path.join(environment.ProgramData || 'C:\\ProgramData', 'CodexRemote', 'credentials'),
19
+ };
20
+ }