@walkhi/code-relax 0.1.0-beta.50 → 0.1.0-beta.59

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 (101) hide show
  1. package/README.md +105 -105
  2. package/dist/bin/cleanup-accounts.mjs +63 -63
  3. package/dist/bin/codex-remote.mjs +403 -403
  4. package/dist/bin/publish-android-release.mjs +63 -63
  5. package/dist/bin/release-upload-server.mjs +190 -190
  6. package/dist/bin/set-relay-admin-password.mjs +11 -0
  7. package/dist/build-platform.json +1 -1
  8. package/dist/get-shared-service-url.ps1 +14 -14
  9. package/dist/shared/code-browser.cjs +159 -159
  10. package/dist/shared/file-changes.cjs +31 -31
  11. package/dist/shared/file-citations.cjs +117 -117
  12. package/dist/shared/file-mentions.cjs +92 -92
  13. package/dist/shared/message-delivery.cjs +115 -115
  14. package/dist/shared/p2p-file-channel.cjs +87 -87
  15. package/dist/shared/release-version.cjs +31 -0
  16. package/dist/shared/server-requests.cjs +311 -311
  17. package/dist/shared/timeline-sync.cjs +173 -173
  18. package/dist/skills/code-relax/SKILL.md +22 -22
  19. package/dist/src/bounded-process-log.mjs +63 -63
  20. package/dist/src/celsius-client.mjs +107 -107
  21. package/dist/src/dots-approval-timeline.mjs +99 -99
  22. package/dist/src/dots-avatar.mjs +42 -42
  23. package/dist/src/dots-chat-projection.mjs +50 -50
  24. package/dist/src/dots-chat-server.mjs +483 -483
  25. package/dist/src/dots-message-images.mjs +319 -319
  26. package/dist/src/dots-profile.mjs +77 -77
  27. package/dist/src/dots-room-activity.mjs +172 -172
  28. package/dist/src/dots-room-client.mjs +137 -135
  29. package/dist/src/dots-room-projection.mjs +94 -94
  30. package/dist/src/dots-schedules.mjs +66 -66
  31. package/dist/src/dots-summary-tasks.mjs +32 -32
  32. package/dist/src/dots-task-catalog.mjs +430 -430
  33. package/dist/src/file-transfer-range.mjs +22 -22
  34. package/dist/src/file-upload-session.mjs +146 -130
  35. package/dist/src/hosted-history-app-server.mjs +103 -103
  36. package/dist/src/hosted-history-cache.mjs +113 -113
  37. package/dist/src/lan-socket-server.mjs +338 -266
  38. package/dist/src/managed-app-server.mjs +373 -373
  39. package/dist/src/managed-endpoint.mjs +25 -25
  40. package/dist/src/message-upload-files.mjs +109 -109
  41. package/dist/src/onboarding.mjs +12 -12
  42. package/dist/src/platform/README.md +1 -1
  43. package/dist/src/platform/codex-protocol.mjs +46 -46
  44. package/dist/src/platform/desktop-project-store.mjs +219 -208
  45. package/dist/src/platform/linux/desktop-host.mjs +87 -87
  46. package/dist/src/platform/linux/process.mjs +53 -53
  47. package/dist/src/platform/linux/system.mjs +50 -50
  48. package/dist/src/platform/linux/user-environment.mjs +9 -9
  49. package/dist/src/platform/macos/codex-runtime.mjs +125 -125
  50. package/dist/src/platform/macos/desktop-host.mjs +86 -86
  51. package/dist/src/platform/macos/user-environment.mjs +9 -9
  52. package/dist/src/platform/native-package.mjs +58 -58
  53. package/dist/src/platform/windows/activate-desktop.ps1 +49 -49
  54. package/dist/src/platform/windows/codex-runtime.mjs +215 -215
  55. package/dist/src/platform/windows/credential-store.mjs +27 -27
  56. package/dist/src/platform/windows/credential-store.ps1 +49 -49
  57. package/dist/src/platform/windows/desktop-host.mjs +65 -65
  58. package/dist/src/platform/windows/desktop-info.ps1 +38 -38
  59. package/dist/src/platform/windows/desktop-project-registration.mjs +4 -4
  60. package/dist/src/platform/windows/desktop-shortcuts.ps1 +14 -14
  61. package/dist/src/platform/windows/dots-profile.mjs +49 -49
  62. package/dist/src/platform/windows/launch-worker.mjs +41 -41
  63. package/dist/src/platform/windows/project-locations.mjs +16 -16
  64. package/dist/src/platform/windows/service-host.mjs +35 -35
  65. package/dist/src/platform/windows/user-environment.mjs +52 -52
  66. package/dist/src/project-management.mjs +58 -58
  67. package/dist/src/runtime-discovery.mjs +47 -47
  68. package/dist/src/self-relay/account-cleanup.mjs +30 -30
  69. package/dist/src/self-relay/admin-auth.mjs +59 -0
  70. package/dist/src/self-relay/admin-login-page.mjs +7 -0
  71. package/dist/src/self-relay/admin-page.mjs +87 -80
  72. package/dist/src/self-relay/admin-server.mjs +49 -18
  73. package/dist/src/self-relay/admin-style.mjs +58 -14
  74. package/dist/src/self-relay/admin-workstations-page.mjs +95 -0
  75. package/dist/src/self-relay/admin-workstations.mjs +143 -0
  76. package/dist/src/self-relay/connection-reporting.mjs +38 -0
  77. package/dist/src/self-relay/connector.mjs +444 -439
  78. package/dist/src/self-relay/demo.mjs +31 -31
  79. package/dist/src/self-relay/diagnostics.mjs +54 -54
  80. package/dist/src/self-relay/email-accounts.mjs +183 -183
  81. package/dist/src/self-relay/file-wire.mjs +13 -13
  82. package/dist/src/self-relay/lifecycle.mjs +11 -0
  83. package/dist/src/self-relay/p2p-probe.mjs +168 -168
  84. package/dist/src/self-relay/phone-connections.mjs +55 -0
  85. package/dist/src/self-relay/privacy-page.mjs +26 -26
  86. package/dist/src/self-relay/public-releases.mjs +67 -67
  87. package/dist/src/self-relay/rtc-file-bridge.mjs +46 -41
  88. package/dist/src/self-relay/server.mjs +63 -3
  89. package/dist/src/self-relay/usage-meter.mjs +141 -141
  90. package/dist/src/server.mjs +23 -9
  91. package/dist/src/service-doctor.mjs +148 -148
  92. package/dist/src/service-install.mjs +97 -97
  93. package/dist/src/service-lifecycle.mjs +139 -139
  94. package/dist/src/service-update.mjs +6 -3
  95. package/dist/src/shared-app-server.mjs +683 -666
  96. package/dist/src/shared-catalog.mjs +132 -132
  97. package/dist/src/shared-thread-stream.mjs +165 -165
  98. package/dist/src/usage-reset.mjs +17 -17
  99. package/dist/src/workstation-downloads.mjs +120 -120
  100. package/package.json +4 -4
  101. package/tools/postinstall.mjs +186 -186
@@ -1,65 +1,65 @@
1
- import { startWindowsProcess } from './background-process.mjs';
2
- import { execFile } from 'node:child_process';
3
- import { promisify } from 'node:util';
4
- import { fileURLToPath } from 'node:url';
5
- import path from 'node:path';
6
- import { managedEndpointUrl, readManagedEndpoint } from '../../managed-endpoint.mjs';
7
- import { runtimePaths } from './runtime-paths.mjs';
8
- import { sharedServiceUrl } from './service-host.mjs';
9
- const exec = promisify(execFile);
10
- const directory = path.dirname(fileURLToPath(import.meta.url));
11
- export const powershell = path.join(process.env.SystemRoot || 'C:\\Windows', 'System32/WindowsPowerShell/v1.0/powershell.exe');
12
- export async function runScript(name, args = []) {
13
- const { stdout } = await exec(powershell, ['-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Bypass', '-File', path.join(directory, name), ...args], { windowsHide: true, encoding: 'utf8', timeout: 30000 });
14
- return stdout.trim();
15
- }
16
- export const desktopHost = {
17
- inspect: async () => JSON.parse(await runScript('desktop-info.ps1')),
18
- process: async pid => JSON.parse(await runScript('desktop-info.ps1', ['-ProcessId', String(pid)])),
19
- sharedServiceUrl,
20
- ownsListener: async (expected, url) => JSON.parse(await runScript('desktop-info.ps1', ['-ProcessId', String(expected.pid), '-Expected', JSON.stringify(expected), '-AppServerUrl', url, '-CheckListener'])),
21
- start: startWindowsProcess,
22
- startDesktop: (executable, environment) => {
23
- const url = managedEndpointUrl(environment.CODEX_APP_SERVER_WS_URL);
24
- if (!url || url !== readManagedEndpoint(runtimePaths().stateRoot)) throw new Error('Desktop 内核地址与工作站不一致。');
25
- return runScript('activate-desktop.ps1', [
26
- '-Executable', executable, '-AppServerUrl', url,
27
- ]);
28
- },
29
- async desktopReady() {
30
- return (await this.inspect()).hasWindow === true && Boolean(await this.sharedServiceUrl());
31
- },
32
- closeDesktop: executable => runScript('stop-desktop.ps1', ['-Executable', executable]),
33
- stop: (pid, expected) => runScript('desktop-info.ps1', ['-ProcessId', String(pid), '-Expected', JSON.stringify(expected), '-StopManagedProcess']),
34
- async prepareKernelRestart() {
35
- const info = await this.inspect();
36
- if (!info.running || !await this.sharedServiceUrl()) return null;
37
- await this.closeDesktop(info.desktop);
38
- return info.desktop;
39
- },
40
- async resumeAfterKernelRestart(executable, environment) {
41
- if (!executable) return;
42
- await this.startDesktop(executable, environment);
43
- const deadline = Date.now() + 30000;
44
- while (!await this.desktopReady()) {
45
- if (Date.now() >= deadline) throw new Error('Codex 内核已就绪,但 Desktop 未完成重新接入。');
46
- await new Promise(resolve => setTimeout(resolve, 500));
47
- }
48
- },
49
- ready: async url => { try { return (await fetch(url, { signal: AbortSignal.timeout(1000) })).ok; } catch { return false; } },
50
- };
51
- export const resolveStateRoot = stateRoot => runScript('resolve-state-root.ps1', ['-StateRoot', stateRoot]);
52
-
53
- export async function removeDesktopShortcut({ serviceRoot, nodeExecutable }, destination) {
54
- const entry = path.join(serviceRoot, 'dist/bin/codex-remote.mjs');
55
- const args = ['-NodePath', nodeExecutable, '-Entry', entry];
56
- if (destination) args.push('-Destination', destination);
57
- return JSON.parse(await runScript('desktop-shortcuts.ps1', args));
58
- }
59
-
60
- export async function residentStartup({ serviceRoot, nodeExecutable }, action = 'status') {
61
- const { stdout } = await exec(powershell, ['-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Bypass', '-File',
62
- path.join(serviceRoot, 'dist/src/platform/windows/resident-startup.ps1'), '-NodePath', nodeExecutable,
63
- '-Entry', path.join(serviceRoot, 'dist/bin/codex-remote.mjs'), '-Action', action], { windowsHide: true, encoding: 'utf8' });
64
- return JSON.parse(stdout.trim());
65
- }
1
+ import { startWindowsProcess } from './background-process.mjs';
2
+ import { execFile } from 'node:child_process';
3
+ import { promisify } from 'node:util';
4
+ import { fileURLToPath } from 'node:url';
5
+ import path from 'node:path';
6
+ import { managedEndpointUrl, readManagedEndpoint } from '../../managed-endpoint.mjs';
7
+ import { runtimePaths } from './runtime-paths.mjs';
8
+ import { sharedServiceUrl } from './service-host.mjs';
9
+ const exec = promisify(execFile);
10
+ const directory = path.dirname(fileURLToPath(import.meta.url));
11
+ export const powershell = path.join(process.env.SystemRoot || 'C:\\Windows', 'System32/WindowsPowerShell/v1.0/powershell.exe');
12
+ export async function runScript(name, args = []) {
13
+ const { stdout } = await exec(powershell, ['-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Bypass', '-File', path.join(directory, name), ...args], { windowsHide: true, encoding: 'utf8', timeout: 30000 });
14
+ return stdout.trim();
15
+ }
16
+ export const desktopHost = {
17
+ inspect: async () => JSON.parse(await runScript('desktop-info.ps1')),
18
+ process: async pid => JSON.parse(await runScript('desktop-info.ps1', ['-ProcessId', String(pid)])),
19
+ sharedServiceUrl,
20
+ ownsListener: async (expected, url) => JSON.parse(await runScript('desktop-info.ps1', ['-ProcessId', String(expected.pid), '-Expected', JSON.stringify(expected), '-AppServerUrl', url, '-CheckListener'])),
21
+ start: startWindowsProcess,
22
+ startDesktop: (executable, environment) => {
23
+ const url = managedEndpointUrl(environment.CODEX_APP_SERVER_WS_URL);
24
+ if (!url || url !== readManagedEndpoint(runtimePaths().stateRoot)) throw new Error('Desktop 内核地址与工作站不一致。');
25
+ return runScript('activate-desktop.ps1', [
26
+ '-Executable', executable, '-AppServerUrl', url,
27
+ ]);
28
+ },
29
+ async desktopReady() {
30
+ return (await this.inspect()).hasWindow === true && Boolean(await this.sharedServiceUrl());
31
+ },
32
+ closeDesktop: executable => runScript('stop-desktop.ps1', ['-Executable', executable]),
33
+ stop: (pid, expected) => runScript('desktop-info.ps1', ['-ProcessId', String(pid), '-Expected', JSON.stringify(expected), '-StopManagedProcess']),
34
+ async prepareKernelRestart() {
35
+ const info = await this.inspect();
36
+ if (!info.running || !await this.sharedServiceUrl()) return null;
37
+ await this.closeDesktop(info.desktop);
38
+ return info.desktop;
39
+ },
40
+ async resumeAfterKernelRestart(executable, environment) {
41
+ if (!executable) return;
42
+ await this.startDesktop(executable, environment);
43
+ const deadline = Date.now() + 30000;
44
+ while (!await this.desktopReady()) {
45
+ if (Date.now() >= deadline) throw new Error('Codex 内核已就绪,但 Desktop 未完成重新接入。');
46
+ await new Promise(resolve => setTimeout(resolve, 500));
47
+ }
48
+ },
49
+ ready: async url => { try { return (await fetch(url, { signal: AbortSignal.timeout(1000) })).ok; } catch { return false; } },
50
+ };
51
+ export const resolveStateRoot = stateRoot => runScript('resolve-state-root.ps1', ['-StateRoot', stateRoot]);
52
+
53
+ export async function removeDesktopShortcut({ serviceRoot, nodeExecutable }, destination) {
54
+ const entry = path.join(serviceRoot, 'dist/bin/codex-remote.mjs');
55
+ const args = ['-NodePath', nodeExecutable, '-Entry', entry];
56
+ if (destination) args.push('-Destination', destination);
57
+ return JSON.parse(await runScript('desktop-shortcuts.ps1', args));
58
+ }
59
+
60
+ export async function residentStartup({ serviceRoot, nodeExecutable }, action = 'status') {
61
+ const { stdout } = await exec(powershell, ['-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Bypass', '-File',
62
+ path.join(serviceRoot, 'dist/src/platform/windows/resident-startup.ps1'), '-NodePath', nodeExecutable,
63
+ '-Entry', path.join(serviceRoot, 'dist/bin/codex-remote.mjs'), '-Action', action], { windowsHide: true, encoding: 'utf8' });
64
+ return JSON.parse(stdout.trim());
65
+ }
@@ -1,38 +1,38 @@
1
- param([int]$ProcessId = 0, [string]$Expected, [string]$AppServerUrl, [switch]$CheckListener, [switch]$StopManagedProcess)
2
- $ErrorActionPreference = 'Stop'
3
- [Console]::OutputEncoding = New-Object System.Text.UTF8Encoding($false)
4
- function Read-Identity([int]$Id) {
5
- $item = Get-CimInstance Win32_Process -Filter "ProcessId=$Id"
6
- if ($item) { return @{ pid=$item.ProcessId; executable=$item.ExecutablePath; command=$item.CommandLine; created=$item.CreationDate.ToUniversalTime().ToString('o') } }
7
- return $null
8
- }
9
- function Same-Identity($a, $b) {
10
- return $null -ne $a -and $null -ne $b -and $a.pid -eq $b.pid -and $a.executable -ceq $b.executable -and $a.command -ceq $b.command -and $a.created -ceq $b.created
11
- }
12
- if ($CheckListener -or $StopManagedProcess) {
13
- if ($ProcessId -le 0 -or -not $Expected) { throw 'Missing managed process identity.' }
14
- $identity = $Expected | ConvertFrom-Json
15
- if ($identity.pid -ne $ProcessId) { throw 'Process identity mismatch.' }
16
- $actual = Read-Identity $ProcessId
17
- if (-not (Same-Identity $identity $actual)) {
18
- if ($StopManagedProcess) { throw 'Process identity changed; nothing stopped.' }
19
- 'false'; exit
20
- }
21
- if ($StopManagedProcess) { Stop-Process -Id $ProcessId -ErrorAction Stop; '{"stopped":true}'; exit }
22
- if ($AppServerUrl -cnotmatch '^ws://127\.0\.0\.1:([1-9][0-9]{0,4})\z' -or [int]$Matches[1] -gt 65535) { throw 'Invalid managed app-server URL.' }
23
- $port = [int]$Matches[1]
24
- $listeners = @(Get-NetTCPConnection -LocalPort $port -State Listen -ErrorAction SilentlyContinue | Where-Object { $_.LocalAddress -eq '127.0.0.1' -and $_.OwningProcess -eq $ProcessId })
25
- [bool]($listeners.Count -gt 0 -and (Same-Identity $identity (Read-Identity $ProcessId))) | ConvertTo-Json -Compress
26
- exit
27
- }
28
- if ($ProcessId -gt 0) {
29
- $item = Get-CimInstance Win32_Process -Filter "ProcessId=$ProcessId"
30
- if ($item) { @{ pid=$item.ProcessId; executable=$item.ExecutablePath; command=$item.CommandLine; created=$item.CreationDate.ToUniversalTime().ToString('o') } | ConvertTo-Json -Compress }
31
- else { 'null' }
32
- exit
33
- }
34
- $package = Get-AppxPackage OpenAI.Codex | Select-Object -First 1
35
- if (-not $package) { @{ desktop=$null; running=$false } | ConvertTo-Json -Compress; exit }
36
- $desktop = Join-Path $package.InstallLocation 'app/ChatGPT.exe'
37
- $running = @(Get-Process ChatGPT -ErrorAction SilentlyContinue | Where-Object { $_.Path -eq $desktop })
38
- @{ desktop=$desktop; version=$package.Version.ToString(); running=($running.Count -gt 0); hasWindow=(@($running | Where-Object { $_.MainWindowHandle -ne 0 }).Count -gt 0) } | ConvertTo-Json -Compress
1
+ param([int]$ProcessId = 0, [string]$Expected, [string]$AppServerUrl, [switch]$CheckListener, [switch]$StopManagedProcess)
2
+ $ErrorActionPreference = 'Stop'
3
+ [Console]::OutputEncoding = New-Object System.Text.UTF8Encoding($false)
4
+ function Read-Identity([int]$Id) {
5
+ $item = Get-CimInstance Win32_Process -Filter "ProcessId=$Id"
6
+ if ($item) { return @{ pid=$item.ProcessId; executable=$item.ExecutablePath; command=$item.CommandLine; created=$item.CreationDate.ToUniversalTime().ToString('o') } }
7
+ return $null
8
+ }
9
+ function Same-Identity($a, $b) {
10
+ return $null -ne $a -and $null -ne $b -and $a.pid -eq $b.pid -and $a.executable -ceq $b.executable -and $a.command -ceq $b.command -and $a.created -ceq $b.created
11
+ }
12
+ if ($CheckListener -or $StopManagedProcess) {
13
+ if ($ProcessId -le 0 -or -not $Expected) { throw 'Missing managed process identity.' }
14
+ $identity = $Expected | ConvertFrom-Json
15
+ if ($identity.pid -ne $ProcessId) { throw 'Process identity mismatch.' }
16
+ $actual = Read-Identity $ProcessId
17
+ if (-not (Same-Identity $identity $actual)) {
18
+ if ($StopManagedProcess) { throw 'Process identity changed; nothing stopped.' }
19
+ 'false'; exit
20
+ }
21
+ if ($StopManagedProcess) { Stop-Process -Id $ProcessId -ErrorAction Stop; '{"stopped":true}'; exit }
22
+ if ($AppServerUrl -cnotmatch '^ws://127\.0\.0\.1:([1-9][0-9]{0,4})\z' -or [int]$Matches[1] -gt 65535) { throw 'Invalid managed app-server URL.' }
23
+ $port = [int]$Matches[1]
24
+ $listeners = @(Get-NetTCPConnection -LocalPort $port -State Listen -ErrorAction SilentlyContinue | Where-Object { $_.LocalAddress -eq '127.0.0.1' -and $_.OwningProcess -eq $ProcessId })
25
+ [bool]($listeners.Count -gt 0 -and (Same-Identity $identity (Read-Identity $ProcessId))) | ConvertTo-Json -Compress
26
+ exit
27
+ }
28
+ if ($ProcessId -gt 0) {
29
+ $item = Get-CimInstance Win32_Process -Filter "ProcessId=$ProcessId"
30
+ if ($item) { @{ pid=$item.ProcessId; executable=$item.ExecutablePath; command=$item.CommandLine; created=$item.CreationDate.ToUniversalTime().ToString('o') } | ConvertTo-Json -Compress }
31
+ else { 'null' }
32
+ exit
33
+ }
34
+ $package = Get-AppxPackage OpenAI.Codex | Select-Object -First 1
35
+ if (-not $package) { @{ desktop=$null; running=$false } | ConvertTo-Json -Compress; exit }
36
+ $desktop = Join-Path $package.InstallLocation 'app/ChatGPT.exe'
37
+ $running = @(Get-Process ChatGPT -ErrorAction SilentlyContinue | Where-Object { $_.Path -eq $desktop })
38
+ @{ desktop=$desktop; version=$package.Version.ToString(); running=($running.Count -gt 0); hasWindow=(@($running | Where-Object { $_.MainWindowHandle -ne 0 }).Count -gt 0) } | ConvertTo-Json -Compress
@@ -1,4 +1,4 @@
1
- import { desktopHost } from './desktop-host.mjs';
2
- import { withDesktopProjectRegistration as register } from '../desktop-project-store.mjs';
3
- export { registerProjectInState, desktopProjectRestartWarning } from '../desktop-project-store.mjs';
4
- export const withDesktopProjectRegistration = (options, action) => register({ host: desktopHost, ...options }, action);
1
+ import { desktopHost } from './desktop-host.mjs';
2
+ import { withDesktopProjectRegistration as register } from '../desktop-project-store.mjs';
3
+ export { registerProjectInState, desktopProjectRestartWarning } from '../desktop-project-store.mjs';
4
+ export const withDesktopProjectRegistration = (options, action) => register({ host: desktopHost, ...options }, action);
@@ -1,21 +1,21 @@
1
1
  param([string]$NodePath, [string]$Entry, [string]$Destination)
2
- $ErrorActionPreference = 'Stop'
3
- [Console]::OutputEncoding = New-Object System.Text.UTF8Encoding($false)
4
- $shell = New-Object -ComObject WScript.Shell
5
- if (-not $Destination) { $Destination = $shell.SpecialFolders.Item('Desktop') }
2
+ $ErrorActionPreference = 'Stop'
3
+ [Console]::OutputEncoding = New-Object System.Text.UTF8Encoding($false)
4
+ $shell = New-Object -ComObject WScript.Shell
5
+ if (-not $Destination) { $Destination = $shell.SpecialFolders.Item('Desktop') }
6
6
  # Only identify and remove the retired shortcut; never create a new launcher.
7
7
  $launcher = Join-Path $PSScriptRoot 'desktop-shortcut-run.ps1'
8
8
  $items = @(@{Name='Code Relax - Codex 内核'; Command='shared-start'})
9
- $results = @()
10
- foreach ($item in $items) {
11
- $target = Join-Path $Destination ($item.Name + '.lnk')
12
- $arguments = '-NoProfile -NoExit -ExecutionPolicy Bypass -File "' + $launcher + '" -NodePath "' + $NodePath + '" -Entry "' + $Entry + '" -Action ' + $item.Command
13
- if (Test-Path -LiteralPath $target) {
14
- $existing = $shell.CreateShortcut($target)
9
+ $results = @()
10
+ foreach ($item in $items) {
11
+ $target = Join-Path $Destination ($item.Name + '.lnk')
12
+ $arguments = '-NoProfile -NoExit -ExecutionPolicy Bypass -File "' + $launcher + '" -NodePath "' + $NodePath + '" -Entry "' + $Entry + '" -Action ' + $item.Command
13
+ if (Test-Path -LiteralPath $target) {
14
+ $existing = $shell.CreateShortcut($target)
15
15
  $powershell = Join-Path $env:SystemRoot 'System32/WindowsPowerShell/v1.0/powershell.exe'
16
16
  if ($existing.Arguments -ne $arguments -or $existing.TargetPath -ne $powershell) { continue }
17
- }
17
+ }
18
18
  if (Test-Path -LiteralPath $target) { Remove-Item -LiteralPath $target }
19
- $results += $target
20
- }
21
- ConvertTo-Json -InputObject $results -Compress
19
+ $results += $target
20
+ }
21
+ ConvertTo-Json -InputObject $results -Compress
@@ -1,49 +1,49 @@
1
- import fs from 'node:fs/promises';
2
- import path from 'node:path';
3
- import { createHash } from 'node:crypto';
4
- import { dotsProfile } from '../../dots-profile.mjs';
5
-
6
- // Desktop 26.928.2636.0 persists its current TBO sidebar profiles here.
7
- // Read display names, appearance colors and snapshot references; identity comes from the authenticated kernel.
8
- export function desktopDotsProfiles(codexDataRoot, stateRoot) {
9
- const snapshotPath = ({ accountId, userId }) => stateRoot && path.join(stateRoot, 'dots-profiles',
10
- createHash('sha256').update(JSON.stringify([accountId, userId])).digest('hex') + '.json');
11
- const read = async ({ accountId, userId }) => {
12
- let state;
13
- try {
14
- state = JSON.parse(await fs.readFile(path.join(codexDataRoot, '.codex-global-state.json'), 'utf8'));
15
- } catch (error) {
16
- if (error.code !== 'ENOENT') throw error;
17
- }
18
- const cache = state?.['electron-persisted-atom-state']?.['cloud-aeon-sidebar-cache-v1'];
19
- const names = new Map();
20
- if (!accountId || !userId) return names;
21
- const matching = cache?.hostId === 'durable' && cache.accountId === accountId && cache.userId === userId;
22
- for (const [threadId, profile] of Object.entries(matching ? cache.profilesByThreadId || {} : {})) {
23
- const value = dotsProfile(profile);
24
- for (const id of [threadId, profile.active_root_thread_id, profile.root_thread_id]) {
25
- if (typeof id === 'string' && id) names.set(id, value);
26
- }
27
- }
28
- const file = snapshotPath({ accountId, userId });
29
- if (file) {
30
- try {
31
- const saved = JSON.parse(await fs.readFile(file, 'utf8'));
32
- if (saved.accountId !== accountId || saved.userId !== userId || !Array.isArray(saved.profiles)) throw new Error('点点资料缓存身份无效。');
33
- const profiles = new Map(saved.profiles);
34
- const byId = new Map([...profiles.values()].map(profile => [profile.profileId, profile]));
35
- for (const [id, profile] of names) if (byId.has(profile.profileId)) names.set(id, byId.get(profile.profileId));
36
- for (const [id, profile] of profiles) names.set(id, profile);
37
- } catch (error) { if (error.code !== 'ENOENT') console.error('读取点点资料缓存失败:', error.message); }
38
- }
39
- return names;
40
- };
41
- read.save = async (identity, profiles) => {
42
- const file = snapshotPath(identity);
43
- if (!file) return;
44
- await fs.mkdir(path.dirname(file), { recursive: true });
45
- await fs.writeFile(file + '.tmp', JSON.stringify({ ...identity, profiles: [...profiles] }), 'utf8');
46
- await fs.rename(file + '.tmp', file);
47
- };
48
- return read;
49
- }
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { createHash } from 'node:crypto';
4
+ import { dotsProfile } from '../../dots-profile.mjs';
5
+
6
+ // Desktop 26.928.2636.0 persists its current TBO sidebar profiles here.
7
+ // Read display names, appearance colors and snapshot references; identity comes from the authenticated kernel.
8
+ export function desktopDotsProfiles(codexDataRoot, stateRoot) {
9
+ const snapshotPath = ({ accountId, userId }) => stateRoot && path.join(stateRoot, 'dots-profiles',
10
+ createHash('sha256').update(JSON.stringify([accountId, userId])).digest('hex') + '.json');
11
+ const read = async ({ accountId, userId }) => {
12
+ let state;
13
+ try {
14
+ state = JSON.parse(await fs.readFile(path.join(codexDataRoot, '.codex-global-state.json'), 'utf8'));
15
+ } catch (error) {
16
+ if (error.code !== 'ENOENT') throw error;
17
+ }
18
+ const cache = state?.['electron-persisted-atom-state']?.['cloud-aeon-sidebar-cache-v1'];
19
+ const names = new Map();
20
+ if (!accountId || !userId) return names;
21
+ const matching = cache?.hostId === 'durable' && cache.accountId === accountId && cache.userId === userId;
22
+ for (const [threadId, profile] of Object.entries(matching ? cache.profilesByThreadId || {} : {})) {
23
+ const value = dotsProfile(profile);
24
+ for (const id of [threadId, profile.active_root_thread_id, profile.root_thread_id]) {
25
+ if (typeof id === 'string' && id) names.set(id, value);
26
+ }
27
+ }
28
+ const file = snapshotPath({ accountId, userId });
29
+ if (file) {
30
+ try {
31
+ const saved = JSON.parse(await fs.readFile(file, 'utf8'));
32
+ if (saved.accountId !== accountId || saved.userId !== userId || !Array.isArray(saved.profiles)) throw new Error('点点资料缓存身份无效。');
33
+ const profiles = new Map(saved.profiles);
34
+ const byId = new Map([...profiles.values()].map(profile => [profile.profileId, profile]));
35
+ for (const [id, profile] of names) if (byId.has(profile.profileId)) names.set(id, byId.get(profile.profileId));
36
+ for (const [id, profile] of profiles) names.set(id, profile);
37
+ } catch (error) { if (error.code !== 'ENOENT') console.error('读取点点资料缓存失败:', error.message); }
38
+ }
39
+ return names;
40
+ };
41
+ read.save = async (identity, profiles) => {
42
+ const file = snapshotPath(identity);
43
+ if (!file) return;
44
+ await fs.mkdir(path.dirname(file), { recursive: true });
45
+ await fs.writeFile(file + '.tmp', JSON.stringify({ ...identity, profiles: [...profiles] }), 'utf8');
46
+ await fs.rename(file + '.tmp', file);
47
+ };
48
+ return read;
49
+ }
@@ -1,41 +1,41 @@
1
- import fs from 'node:fs';
2
- import { spawn } from 'node:child_process';
3
- import { attachProcessLog } from '../../bounded-process-log.mjs';
4
-
5
- const [requestPath, readyPath, instance, diagnosticPath] = process.argv.slice(2);
6
- let result = { instance, brokerPid: process.pid };
7
-
8
- function diagnostic(message) {
9
- if (!diagnosticPath) return;
10
- fs.appendFileSync(diagnosticPath, `${new Date().toISOString()} ${message}\n`, 'utf8');
11
- }
12
-
13
- try {
14
- const spec = JSON.parse(fs.readFileSync(requestPath, 'utf8').replace(/^\uFEFF/u, ''));
15
- diagnostic('request loaded');
16
- if (spec.instance !== instance) throw new Error('Windows process broker request identity mismatch.');
17
- fs.rmSync(requestPath, { force: true });
18
- const logs = spec.logs || [];
19
- const child = spawn(spec.executable, spec.args || [], {
20
- windowsHide: true,
21
- env: spec.environment,
22
- stdio: ['ignore', logs[0] ? 'pipe' : 'ignore', logs[1] ? 'pipe' : 'ignore'],
23
- });
24
- if (child.stdout) attachProcessLog(child.stdout, logs[0]);
25
- if (child.stderr) attachProcessLog(child.stderr, logs[1]);
26
- await new Promise((resolve, reject) => {
27
- child.once('spawn', resolve);
28
- child.once('error', reject);
29
- });
30
- // Keep this WMI-owned broker alive with the target so Codex tools inherit
31
- // its hidden console instead of creating a visible console per command.
32
- result.pid = child.pid;
33
- diagnostic(`target started (PID ${child.pid})`);
34
- } catch (error) {
35
- result.error = error?.stack || error?.message || String(error);
36
- try { diagnostic(`failed: ${result.error}`); } catch {}
37
- }
38
-
39
- const temporary = `${readyPath}.tmp`;
40
- fs.writeFileSync(temporary, JSON.stringify(result), 'utf8');
41
- fs.renameSync(temporary, readyPath);
1
+ import fs from 'node:fs';
2
+ import { spawn } from 'node:child_process';
3
+ import { attachProcessLog } from '../../bounded-process-log.mjs';
4
+
5
+ const [requestPath, readyPath, instance, diagnosticPath] = process.argv.slice(2);
6
+ let result = { instance, brokerPid: process.pid };
7
+
8
+ function diagnostic(message) {
9
+ if (!diagnosticPath) return;
10
+ fs.appendFileSync(diagnosticPath, `${new Date().toISOString()} ${message}\n`, 'utf8');
11
+ }
12
+
13
+ try {
14
+ const spec = JSON.parse(fs.readFileSync(requestPath, 'utf8').replace(/^\uFEFF/u, ''));
15
+ diagnostic('request loaded');
16
+ if (spec.instance !== instance) throw new Error('Windows process broker request identity mismatch.');
17
+ fs.rmSync(requestPath, { force: true });
18
+ const logs = spec.logs || [];
19
+ const child = spawn(spec.executable, spec.args || [], {
20
+ windowsHide: true,
21
+ env: spec.environment,
22
+ stdio: ['ignore', logs[0] ? 'pipe' : 'ignore', logs[1] ? 'pipe' : 'ignore'],
23
+ });
24
+ if (child.stdout) attachProcessLog(child.stdout, logs[0]);
25
+ if (child.stderr) attachProcessLog(child.stderr, logs[1]);
26
+ await new Promise((resolve, reject) => {
27
+ child.once('spawn', resolve);
28
+ child.once('error', reject);
29
+ });
30
+ // Keep this WMI-owned broker alive with the target so Codex tools inherit
31
+ // its hidden console instead of creating a visible console per command.
32
+ result.pid = child.pid;
33
+ diagnostic(`target started (PID ${child.pid})`);
34
+ } catch (error) {
35
+ result.error = error?.stack || error?.message || String(error);
36
+ try { diagnostic(`failed: ${result.error}`); } catch {}
37
+ }
38
+
39
+ const temporary = `${readyPath}.tmp`;
40
+ fs.writeFileSync(temporary, JSON.stringify(result), 'utf8');
41
+ fs.renameSync(temporary, readyPath);
@@ -1,16 +1,16 @@
1
- import { execFile } from 'node:child_process';
2
- import { promisify } from 'node:util';
3
- import path from 'node:path';
4
- import { powershell } from './desktop-host.mjs';
5
-
6
- const exec = promisify(execFile);
7
-
8
- export async function projectLocations() {
9
- const command = "[Console]::OutputEncoding = New-Object System.Text.UTF8Encoding($false); $projectDocuments = [Environment]::GetFolderPath('MyDocuments'); $projectUserDirectory = [Environment]::GetFolderPath('UserProfile'); $projectDownloads = [Environment]::ExpandEnvironmentVariables((Get-ItemPropertyValue -LiteralPath 'HKCU:\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\User Shell Folders' -Name '{374DE290-123F-4565-9164-39C4925E467B}' -ErrorAction Stop)); $projectDriveRoots = @(Get-PSDrive -PSProvider FileSystem | ForEach-Object { $_.Root }); @{ documents = $projectDocuments; home = $projectUserDirectory; downloads = $projectDownloads; roots = $projectDriveRoots } | ConvertTo-Json -Compress";
10
- const { stdout } = await exec(powershell, ['-NoProfile', '-NonInteractive', '-Command', command],
11
- { windowsHide: true, encoding: 'utf8', timeout: 10000 });
12
- const locations = JSON.parse(stdout);
13
- if (!path.isAbsolute(locations.documents)) throw new Error('无法取得电脑的文档目录。');
14
- if (!path.isAbsolute(locations.downloads)) throw new Error('无法取得电脑的下载目录。');
15
- return { defaultParent: path.join(locations.documents, 'ChatGPT'), home: locations.home, downloads: locations.downloads, roots: locations.roots };
16
- }
1
+ import { execFile } from 'node:child_process';
2
+ import { promisify } from 'node:util';
3
+ import path from 'node:path';
4
+ import { powershell } from './desktop-host.mjs';
5
+
6
+ const exec = promisify(execFile);
7
+
8
+ export async function projectLocations() {
9
+ const command = "[Console]::OutputEncoding = New-Object System.Text.UTF8Encoding($false); $projectDocuments = [Environment]::GetFolderPath('MyDocuments'); $projectUserDirectory = [Environment]::GetFolderPath('UserProfile'); $projectDownloads = [Environment]::ExpandEnvironmentVariables((Get-ItemPropertyValue -LiteralPath 'HKCU:\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\User Shell Folders' -Name '{374DE290-123F-4565-9164-39C4925E467B}' -ErrorAction Stop)); $projectDriveRoots = @(Get-PSDrive -PSProvider FileSystem | ForEach-Object { $_.Root }); @{ documents = $projectDocuments; home = $projectUserDirectory; downloads = $projectDownloads; roots = $projectDriveRoots } | ConvertTo-Json -Compress";
10
+ const { stdout } = await exec(powershell, ['-NoProfile', '-NonInteractive', '-Command', command],
11
+ { windowsHide: true, encoding: 'utf8', timeout: 10000 });
12
+ const locations = JSON.parse(stdout);
13
+ if (!path.isAbsolute(locations.documents)) throw new Error('无法取得电脑的文档目录。');
14
+ if (!path.isAbsolute(locations.downloads)) throw new Error('无法取得电脑的下载目录。');
15
+ return { defaultParent: path.join(locations.documents, 'ChatGPT'), home: locations.home, downloads: locations.downloads, roots: locations.roots };
16
+ }
@@ -1,35 +1,35 @@
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
- import fs from 'node:fs';
6
- import { runtimePaths } from './runtime-paths.mjs';
7
- import { readRecord } from '../../service-lifecycle.mjs';
8
- import { readManagedEndpoint } from '../../managed-endpoint.mjs';
9
- import { lanNetworks } from './network-info.mjs';
10
-
11
- const exec = promisify(execFile);
12
- const bridgeRoot = fileURLToPath(new URL('../../../', import.meta.url));
13
- async function detect(script, args = []) {
14
- const executable = path.join(process.env.SystemRoot, 'System32/WindowsPowerShell/v1.0/powershell.exe');
15
- const { stdout } = await exec(executable, ['-NoProfile', '-NonInteractive', '-File',
16
- path.join(bridgeRoot, script), ...args], { windowsHide: true, encoding: 'utf8' });
17
- return stdout.trim();
18
- }
19
-
20
- // Windows-only network metadata stays here, outside the lifecycle implementation.
21
- export const sharedServiceUrl = () => {
22
- const url = readManagedEndpoint(runtimePaths().stateRoot);
23
- return url ? detect('get-shared-service-url.ps1', ['-AppServerUrl', url]) : '';
24
- };
25
- export const lanAddress = async () => (await lanNetworks())[0]?.address || '';
26
- export { lanNetworks };
27
- export async function bridgeEnvironment() {
28
- const environment = { ...process.env };
29
- const installation = readRecord(path.join(runtimePaths(environment).serviceRoot, 'installation.json'));
30
- const executable = installation?.nodeExecutable;
31
- if (!executable || !fs.existsSync(executable)) throw new Error('安装时选用的 Node 不存在,请重新安装 Code Relax。');
32
- environment.CODEX_MCP_NODE_PATH = executable;
33
- delete environment.CODEX_APP_TOOLS_PIPE_PATH;
34
- return { executable, environment };
35
- }
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
+ import fs from 'node:fs';
6
+ import { runtimePaths } from './runtime-paths.mjs';
7
+ import { readRecord } from '../../service-lifecycle.mjs';
8
+ import { readManagedEndpoint } from '../../managed-endpoint.mjs';
9
+ import { lanNetworks } from './network-info.mjs';
10
+
11
+ const exec = promisify(execFile);
12
+ const bridgeRoot = fileURLToPath(new URL('../../../', import.meta.url));
13
+ async function detect(script, args = []) {
14
+ const executable = path.join(process.env.SystemRoot, 'System32/WindowsPowerShell/v1.0/powershell.exe');
15
+ const { stdout } = await exec(executable, ['-NoProfile', '-NonInteractive', '-File',
16
+ path.join(bridgeRoot, script), ...args], { windowsHide: true, encoding: 'utf8' });
17
+ return stdout.trim();
18
+ }
19
+
20
+ // Windows-only network metadata stays here, outside the lifecycle implementation.
21
+ export const sharedServiceUrl = () => {
22
+ const url = readManagedEndpoint(runtimePaths().stateRoot);
23
+ return url ? detect('get-shared-service-url.ps1', ['-AppServerUrl', url]) : '';
24
+ };
25
+ export const lanAddress = async () => (await lanNetworks())[0]?.address || '';
26
+ export { lanNetworks };
27
+ export async function bridgeEnvironment() {
28
+ const environment = { ...process.env };
29
+ const installation = readRecord(path.join(runtimePaths(environment).serviceRoot, 'installation.json'));
30
+ const executable = installation?.nodeExecutable;
31
+ if (!executable || !fs.existsSync(executable)) throw new Error('安装时选用的 Node 不存在,请重新安装 Code Relax。');
32
+ environment.CODEX_MCP_NODE_PATH = executable;
33
+ delete environment.CODEX_APP_TOOLS_PIPE_PATH;
34
+ return { executable, environment };
35
+ }