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

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 (89) 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/build-platform.json +1 -1
  7. package/dist/get-shared-service-url.ps1 +14 -14
  8. package/dist/shared/code-browser.cjs +159 -159
  9. package/dist/shared/file-changes.cjs +31 -31
  10. package/dist/shared/file-citations.cjs +117 -117
  11. package/dist/shared/file-mentions.cjs +92 -92
  12. package/dist/shared/message-delivery.cjs +115 -115
  13. package/dist/shared/p2p-file-channel.cjs +87 -87
  14. package/dist/shared/server-requests.cjs +311 -311
  15. package/dist/shared/timeline-sync.cjs +173 -173
  16. package/dist/skills/code-relax/SKILL.md +22 -22
  17. package/dist/src/bounded-process-log.mjs +63 -63
  18. package/dist/src/celsius-client.mjs +107 -107
  19. package/dist/src/dots-approval-timeline.mjs +99 -99
  20. package/dist/src/dots-avatar.mjs +42 -42
  21. package/dist/src/dots-chat-projection.mjs +50 -50
  22. package/dist/src/dots-chat-server.mjs +483 -483
  23. package/dist/src/dots-message-images.mjs +319 -319
  24. package/dist/src/dots-profile.mjs +77 -77
  25. package/dist/src/dots-room-activity.mjs +172 -172
  26. package/dist/src/dots-room-client.mjs +135 -135
  27. package/dist/src/dots-room-projection.mjs +94 -94
  28. package/dist/src/dots-schedules.mjs +66 -66
  29. package/dist/src/dots-summary-tasks.mjs +32 -32
  30. package/dist/src/dots-task-catalog.mjs +430 -430
  31. package/dist/src/file-transfer-range.mjs +22 -22
  32. package/dist/src/file-upload-session.mjs +130 -130
  33. package/dist/src/hosted-history-app-server.mjs +103 -103
  34. package/dist/src/hosted-history-cache.mjs +113 -113
  35. package/dist/src/lan-socket-server.mjs +266 -266
  36. package/dist/src/managed-app-server.mjs +373 -373
  37. package/dist/src/managed-endpoint.mjs +25 -25
  38. package/dist/src/message-upload-files.mjs +109 -109
  39. package/dist/src/onboarding.mjs +12 -12
  40. package/dist/src/platform/README.md +1 -1
  41. package/dist/src/platform/codex-protocol.mjs +46 -46
  42. package/dist/src/platform/desktop-project-store.mjs +208 -208
  43. package/dist/src/platform/linux/desktop-host.mjs +87 -87
  44. package/dist/src/platform/linux/process.mjs +53 -53
  45. package/dist/src/platform/linux/system.mjs +50 -50
  46. package/dist/src/platform/linux/user-environment.mjs +9 -9
  47. package/dist/src/platform/macos/codex-runtime.mjs +125 -125
  48. package/dist/src/platform/macos/desktop-host.mjs +86 -86
  49. package/dist/src/platform/macos/user-environment.mjs +9 -9
  50. package/dist/src/platform/native-package.mjs +58 -58
  51. package/dist/src/platform/windows/activate-desktop.ps1 +49 -49
  52. package/dist/src/platform/windows/codex-runtime.mjs +215 -215
  53. package/dist/src/platform/windows/credential-store.mjs +27 -27
  54. package/dist/src/platform/windows/credential-store.ps1 +49 -49
  55. package/dist/src/platform/windows/desktop-host.mjs +65 -65
  56. package/dist/src/platform/windows/desktop-info.ps1 +38 -38
  57. package/dist/src/platform/windows/desktop-project-registration.mjs +4 -4
  58. package/dist/src/platform/windows/desktop-shortcuts.ps1 +14 -14
  59. package/dist/src/platform/windows/dots-profile.mjs +49 -49
  60. package/dist/src/platform/windows/launch-worker.mjs +41 -41
  61. package/dist/src/platform/windows/project-locations.mjs +16 -16
  62. package/dist/src/platform/windows/service-host.mjs +35 -35
  63. package/dist/src/platform/windows/user-environment.mjs +52 -52
  64. package/dist/src/project-management.mjs +58 -58
  65. package/dist/src/runtime-discovery.mjs +47 -47
  66. package/dist/src/self-relay/account-cleanup.mjs +30 -30
  67. package/dist/src/self-relay/admin-page.mjs +80 -80
  68. package/dist/src/self-relay/admin-style.mjs +14 -14
  69. package/dist/src/self-relay/connector.mjs +439 -439
  70. package/dist/src/self-relay/demo.mjs +31 -31
  71. package/dist/src/self-relay/diagnostics.mjs +54 -54
  72. package/dist/src/self-relay/email-accounts.mjs +183 -183
  73. package/dist/src/self-relay/file-wire.mjs +13 -13
  74. package/dist/src/self-relay/p2p-probe.mjs +168 -168
  75. package/dist/src/self-relay/privacy-page.mjs +26 -26
  76. package/dist/src/self-relay/public-releases.mjs +67 -67
  77. package/dist/src/self-relay/rtc-file-bridge.mjs +41 -41
  78. package/dist/src/self-relay/usage-meter.mjs +141 -141
  79. package/dist/src/server.mjs +5 -4
  80. package/dist/src/service-doctor.mjs +148 -148
  81. package/dist/src/service-install.mjs +97 -97
  82. package/dist/src/service-lifecycle.mjs +139 -139
  83. package/dist/src/shared-app-server.mjs +666 -666
  84. package/dist/src/shared-catalog.mjs +132 -132
  85. package/dist/src/shared-thread-stream.mjs +165 -165
  86. package/dist/src/usage-reset.mjs +17 -17
  87. package/dist/src/workstation-downloads.mjs +120 -120
  88. package/package.json +4 -4
  89. 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
+ }