@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.
- package/dist/bin/codex-remote.mjs +4 -2
- package/dist/bin/manage-relay-admin.mjs +20 -20
- package/dist/bin/migrate-relay-db.mjs +109 -109
- package/dist/bin/publish-harmony-release.mjs +46 -46
- package/dist/bin/self-relay-admin.mjs +8 -8
- package/dist/bin/self-relay-demo.mjs +33 -33
- package/dist/bin/self-relay-server.mjs +66 -66
- package/dist/bin/set-relay-admin-password.mjs +11 -11
- package/dist/build-platform.json +1 -1
- package/dist/get-lan-networks.ps1 +40 -40
- package/dist/shared/app-server-events.cjs +511 -511
- package/dist/shared/p2p-data-channel.cjs +72 -72
- package/dist/shared/p2p-diagnostics.cjs +28 -28
- package/dist/shared/p2p-ice-policy.cjs +22 -22
- package/dist/shared/plan-pricing.cjs +32 -32
- package/dist/shared/question-attention.cjs +69 -69
- package/dist/shared/release-version.cjs +31 -31
- package/dist/src/archived-threads.mjs +22 -22
- package/dist/src/background-process.mjs +18 -18
- package/dist/src/code-browser.mjs +100 -100
- package/dist/src/desktop-launcher.mjs +62 -62
- package/dist/src/dots-task-files.mjs +52 -52
- package/dist/src/execution-health.mjs +26 -26
- package/dist/src/message-images.mjs +9 -9
- package/dist/src/plan-pricing.mjs +47 -47
- package/dist/src/platform/codex-runtime.mjs +3 -3
- package/dist/src/platform/credential-store.mjs +19 -19
- package/dist/src/platform/desktop-host.mjs +3 -3
- package/dist/src/platform/desktop-permission-mode.mjs +17 -17
- package/dist/src/platform/desktop-project-registration.mjs +3 -3
- package/dist/src/platform/file-opener.mjs +9 -9
- package/dist/src/platform/linux/README.md +3 -3
- package/dist/src/platform/linux/codex-runtime.mjs +56 -56
- package/dist/src/platform/linux/desktop-installation.mjs +36 -36
- package/dist/src/platform/linux/desktop-project-registration.mjs +11 -11
- package/dist/src/platform/linux/network-info.mjs +11 -11
- package/dist/src/platform/linux/project-locations.mjs +17 -17
- package/dist/src/platform/linux/resident-startup.mjs +52 -52
- package/dist/src/platform/linux/retained-runtime.mjs +76 -76
- package/dist/src/platform/linux/service-host.mjs +18 -18
- package/dist/src/platform/macos/README.md +3 -3
- package/dist/src/platform/macos/desktop-project-registration.mjs +11 -11
- package/dist/src/platform/macos/network-info.mjs +8 -8
- package/dist/src/platform/macos/project-locations.mjs +8 -8
- package/dist/src/platform/macos/resident-startup.mjs +84 -84
- package/dist/src/platform/macos/retained-runtime.mjs +127 -127
- package/dist/src/platform/macos/service-host.mjs +18 -18
- package/dist/src/platform/macos/system.mjs +29 -29
- package/dist/src/platform/network-info.mjs +3 -3
- package/dist/src/platform/path-identity.mjs +5 -5
- package/dist/src/platform/posix/network-candidates.mjs +23 -23
- package/dist/src/platform/posix/runtime-paths.mjs +25 -25
- package/dist/src/platform/power-inhibitor.mjs +22 -22
- package/dist/src/platform/project-locations.mjs +3 -3
- package/dist/src/platform/runtime-paths.mjs +8 -8
- package/dist/src/platform/select.mjs +5 -5
- package/dist/src/platform/service-host.mjs +3 -3
- package/dist/src/platform/user-environment.mjs +3 -3
- package/dist/src/platform/windows/IsolatedProcess.cs +79 -79
- package/dist/src/platform/windows/background-process.mjs +30 -30
- package/dist/src/platform/windows/desktop-helper.mjs +179 -179
- package/dist/src/platform/windows/desktop-runtime.ps1 +10 -10
- package/dist/src/platform/windows/desktop-shortcuts.ps1 +7 -7
- package/dist/src/platform/windows/keep-awake.ps1 +20 -20
- package/dist/src/platform/windows/network-info.mjs +34 -34
- package/dist/src/platform/windows/resident-start.js +20 -20
- package/dist/src/platform/windows/resident-start.ps1 +7 -7
- package/dist/src/platform/windows/resident-startup.ps1 +83 -83
- package/dist/src/platform/windows/resolve-state-root.ps1 +14 -14
- package/dist/src/platform/windows/runtime-paths.mjs +20 -20
- package/dist/src/platform/windows/rustdesk-video-probe.mjs +167 -167
- package/dist/src/platform/windows/start-hidden-console.ps1 +58 -58
- package/dist/src/platform/windows/stop-desktop.ps1 +22 -22
- package/dist/src/platform/windows/user-environment.ps1 +41 -41
- package/dist/src/project-create.mjs +121 -121
- package/dist/src/project-management.mjs +1 -1
- package/dist/src/project-remove.mjs +55 -55
- package/dist/src/project-update.mjs +59 -59
- package/dist/src/relay-config-import.mjs +45 -45
- package/dist/src/self-relay/account-connections.mjs +52 -52
- package/dist/src/self-relay/admin-auth.mjs +59 -59
- package/dist/src/self-relay/admin-cloud.mjs +101 -101
- package/dist/src/self-relay/admin-login-page.mjs +7 -7
- package/dist/src/self-relay/admin-server.mjs +105 -105
- package/dist/src/self-relay/admin-state.mjs +127 -127
- package/dist/src/self-relay/admin-workstations-page.mjs +95 -95
- package/dist/src/self-relay/admin-workstations.mjs +143 -143
- package/dist/src/self-relay/android-releases.mjs +61 -61
- package/dist/src/self-relay/client.mjs +61 -61
- package/dist/src/self-relay/connection-reporting.mjs +38 -38
- package/dist/src/self-relay/debug-allowlist.mjs +33 -33
- package/dist/src/self-relay/demo.mjs +6 -6
- package/dist/src/self-relay/device-info.mjs +27 -27
- package/dist/src/self-relay/device-store.mjs +92 -92
- package/dist/src/self-relay/lan-authorizations.mjs +71 -71
- package/dist/src/self-relay/lifecycle.mjs +278 -278
- package/dist/src/self-relay/long-pairing.mjs +40 -40
- package/dist/src/self-relay/mysql-state.mjs +118 -118
- package/dist/src/self-relay/phone-connections.mjs +55 -55
- package/dist/src/self-relay/server.mjs +1066 -1066
- package/dist/src/self-relay/usage-daily.mjs +55 -55
- package/dist/src/self-relay/windows-route-helper.mjs +75 -75
- package/dist/src/self-relay/wire.mjs +36 -36
- package/dist/src/server.mjs +1947 -1947
- package/dist/src/service-control.mjs +82 -82
- package/dist/src/service-health.mjs +33 -33
- package/dist/src/service-power.mjs +58 -58
- package/dist/src/service-restart.mjs +81 -81
- package/dist/src/service-update-worker.mjs +42 -42
- package/dist/src/service-update.mjs +80 -80
- package/dist/src/shared-recovery.mjs +152 -152
- package/dist/src/skill-install.mjs +51 -51
- package/dist/src/thread-title-generation.mjs +150 -150
- package/dist/src/turn-model-history.mjs +47 -47
- package/dist/tools/rtc-route-helper.ps1 +99 -99
- package/dist/tools/start-rtc-route-helper.ps1 +15 -15
- package/dist/web/vendor/markdown-it.LICENSE.txt +22 -22
- package/dist/web/vendor/markdown-it.umd.min.js +13 -13
- package/package.json +4 -4
- 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
|
+
}
|