dsh-cliworker-now 0.7.6 → 0.7.8

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 (181) hide show
  1. package/README.en.md +53 -11
  2. package/README.md +56 -11
  3. package/lib/account-models.mjs +49 -18
  4. package/lib/client.js +1221 -151
  5. package/lib/client.js.map +1 -1
  6. package/lib/grok-catalog.mjs +2 -2
  7. package/lib/hermes-native-entry.py +48 -13
  8. package/lib/hermes-nous-refresh.py +331 -0
  9. package/lib/index.js +9469 -6030
  10. package/lib/index.js.map +1 -1
  11. package/lib/native-launch.mjs +62 -7
  12. package/lib/native-spawn.mjs +32 -0
  13. package/lib/pi-installation.mjs +27 -3
  14. package/lib/pi-native-catalog.mjs +17 -7
  15. package/lib/pi-omp-bridge.mjs +2 -2
  16. package/lib/pi-omp-refresh.mjs +3 -3
  17. package/lib/private-launch.mjs +6 -2
  18. package/lib/terminal-bridge.mjs +2 -2
  19. package/lib/typert.host.js +265 -25
  20. package/lib/typert.remote-client.d.ts +8 -0
  21. package/lib/typert.remote-client.d.ts.map +1 -1
  22. package/lib/typert.remote-client.js +241 -25
  23. package/lib/types/client/account-query-queue.d.ts +16 -0
  24. package/lib/types/client/account-query-queue.js +61 -0
  25. package/lib/types/client/account-query-queue.js.map +1 -0
  26. package/lib/types/client/account-terminal.d.ts +6 -2
  27. package/lib/types/client/account-terminal.js +260 -31
  28. package/lib/types/client/account-terminal.js.map +1 -1
  29. package/lib/types/client/cli-executable-settings.d.ts +13 -0
  30. package/lib/types/client/cli-executable-settings.js +128 -0
  31. package/lib/types/client/cli-executable-settings.js.map +1 -0
  32. package/lib/types/client/management-styles.d.ts +1 -1
  33. package/lib/types/client/management-styles.js +4 -0
  34. package/lib/types/client/management-styles.js.map +1 -1
  35. package/lib/types/client/settings-dialog.js +196 -80
  36. package/lib/types/client/settings-dialog.js.map +1 -1
  37. package/lib/types/client/styles.d.ts +1 -1
  38. package/lib/types/client/styles.js +18 -16
  39. package/lib/types/client/styles.js.map +1 -1
  40. package/lib/types/client/zcode-login-link-provider.d.ts +9 -0
  41. package/lib/types/client/zcode-login-link-provider.js +105 -0
  42. package/lib/types/client/zcode-login-link-provider.js.map +1 -0
  43. package/lib/types/client/zcode-login-link.d.ts +16 -0
  44. package/lib/types/client/zcode-login-link.js +194 -0
  45. package/lib/types/client/zcode-login-link.js.map +1 -0
  46. package/lib/types/host/account-models.d.mts +10 -2
  47. package/lib/types/host/account-models.mjs +49 -18
  48. package/lib/types/host/accounts.d.ts +3 -2
  49. package/lib/types/host/accounts.js +168 -29
  50. package/lib/types/host/accounts.js.map +1 -1
  51. package/lib/types/host/adapters.d.ts +3 -1
  52. package/lib/types/host/adapters.js +138 -11
  53. package/lib/types/host/adapters.js.map +1 -1
  54. package/lib/types/host/agy-background-environment.d.ts +2 -0
  55. package/lib/types/host/agy-background-environment.js +7 -0
  56. package/lib/types/host/agy-background-environment.js.map +1 -0
  57. package/lib/types/host/agy-models.d.ts +2 -1
  58. package/lib/types/host/agy-models.js +6 -6
  59. package/lib/types/host/agy-models.js.map +1 -1
  60. package/lib/types/host/agy-windows-account.d.ts +14 -0
  61. package/lib/types/host/agy-windows-account.js +104 -0
  62. package/lib/types/host/agy-windows-account.js.map +1 -0
  63. package/lib/types/host/authorized-catalog.js +13 -4
  64. package/lib/types/host/authorized-catalog.js.map +1 -1
  65. package/lib/types/host/claude-native-oauth.d.ts +17 -0
  66. package/lib/types/host/claude-native-oauth.js +274 -0
  67. package/lib/types/host/claude-native-oauth.js.map +1 -0
  68. package/lib/types/host/claude-own-route.d.ts +45 -0
  69. package/lib/types/host/claude-own-route.js +375 -0
  70. package/lib/types/host/claude-own-route.js.map +1 -0
  71. package/lib/types/host/cli-account-binding.d.ts +2 -1
  72. package/lib/types/host/cli-account-binding.js +64 -21
  73. package/lib/types/host/cli-account-binding.js.map +1 -1
  74. package/lib/types/host/cli-executable-paths.d.ts +30 -0
  75. package/lib/types/host/cli-executable-paths.js +272 -0
  76. package/lib/types/host/cli-executable-paths.js.map +1 -0
  77. package/lib/types/host/codex-models.d.ts +3 -1
  78. package/lib/types/host/codex-models.js +41 -4
  79. package/lib/types/host/codex-models.js.map +1 -1
  80. package/lib/types/host/extended-adapters.d.ts +3 -2
  81. package/lib/types/host/extended-adapters.js +9 -7
  82. package/lib/types/host/extended-adapters.js.map +1 -1
  83. package/lib/types/host/first-party-models.d.ts +5 -0
  84. package/lib/types/host/first-party-models.js +173 -50
  85. package/lib/types/host/first-party-models.js.map +1 -1
  86. package/lib/types/host/hermes-accounts.js +68 -9
  87. package/lib/types/host/hermes-accounts.js.map +1 -1
  88. package/lib/types/host/hermes-adapter.d.ts +3 -0
  89. package/lib/types/host/hermes-adapter.js +13 -4
  90. package/lib/types/host/hermes-adapter.js.map +1 -1
  91. package/lib/types/host/hermes-installation.js +54 -35
  92. package/lib/types/host/hermes-installation.js.map +1 -1
  93. package/lib/types/host/hermes-models.js +21 -2
  94. package/lib/types/host/hermes-models.js.map +1 -1
  95. package/lib/types/host/hermes-native-entry.py +48 -13
  96. package/lib/types/host/hermes-nous-refresh.py +331 -0
  97. package/lib/types/host/hermes-nous.d.ts +5 -1
  98. package/lib/types/host/hermes-nous.js +5 -5
  99. package/lib/types/host/hermes-nous.js.map +1 -1
  100. package/lib/types/host/hermes-renewal.d.ts +27 -0
  101. package/lib/types/host/hermes-renewal.js +292 -0
  102. package/lib/types/host/hermes-renewal.js.map +1 -0
  103. package/lib/types/host/hermes-windows-installation.d.ts +5 -0
  104. package/lib/types/host/hermes-windows-installation.js +91 -0
  105. package/lib/types/host/hermes-windows-installation.js.map +1 -0
  106. package/lib/types/host/index.d.ts +12 -0
  107. package/lib/types/host/index.js +138 -8
  108. package/lib/types/host/index.js.map +1 -1
  109. package/lib/types/host/kimi-accounts.d.ts +2 -0
  110. package/lib/types/host/kimi-accounts.js +4 -2
  111. package/lib/types/host/kimi-accounts.js.map +1 -1
  112. package/lib/types/host/kimi-oauth-models.d.ts +14 -0
  113. package/lib/types/host/kimi-oauth-models.js +246 -0
  114. package/lib/types/host/kimi-oauth-models.js.map +1 -0
  115. package/lib/types/host/mimo-metadata-cache.d.ts +22 -0
  116. package/lib/types/host/mimo-metadata-cache.js +121 -0
  117. package/lib/types/host/mimo-metadata-cache.js.map +1 -0
  118. package/lib/types/host/native-capability-cache.d.ts +11 -0
  119. package/lib/types/host/native-capability-cache.js +94 -0
  120. package/lib/types/host/native-capability-cache.js.map +1 -0
  121. package/lib/types/host/native-launch.mjs +62 -7
  122. package/lib/types/host/native-process.d.ts +3 -1
  123. package/lib/types/host/native-process.js +30 -5
  124. package/lib/types/host/native-process.js.map +1 -1
  125. package/lib/types/host/native-spawn.mjs +32 -0
  126. package/lib/types/host/opencode-native.js +13 -0
  127. package/lib/types/host/opencode-native.js.map +1 -1
  128. package/lib/types/host/pi-installation.d.mts +1 -1
  129. package/lib/types/host/pi-installation.mjs +27 -3
  130. package/lib/types/host/pi-omp-adapter.js +17 -3
  131. package/lib/types/host/pi-omp-adapter.js.map +1 -1
  132. package/lib/types/host/pi-omp-native.js +5 -3
  133. package/lib/types/host/pi-omp-native.js.map +1 -1
  134. package/lib/types/host/pi-omp-refresh.mjs +3 -3
  135. package/lib/types/host/private-launch.mjs +23 -0
  136. package/lib/types/host/process.d.ts +3 -0
  137. package/lib/types/host/process.js +3 -1
  138. package/lib/types/host/process.js.map +1 -1
  139. package/lib/types/host/routing.js +25 -2
  140. package/lib/types/host/routing.js.map +1 -1
  141. package/lib/types/host/runtime.js +21 -5
  142. package/lib/types/host/runtime.js.map +1 -1
  143. package/lib/types/host/settings-catalog-cache.d.ts +28 -0
  144. package/lib/types/host/settings-catalog-cache.js +189 -0
  145. package/lib/types/host/settings-catalog-cache.js.map +1 -0
  146. package/lib/types/host/windows-account-bootstrap.ps1 +44 -0
  147. package/lib/types/host/windows-account-launch.mjs +47 -0
  148. package/lib/types/host/windows-account-owner.ps1 +137 -0
  149. package/lib/types/host/windows-account-stdio.mjs +19 -0
  150. package/lib/types/host/windows-account-terminal.d.ts +17 -0
  151. package/lib/types/host/windows-account-terminal.js +208 -0
  152. package/lib/types/host/windows-account-terminal.js.map +1 -0
  153. package/lib/types/host/windows-agy-account.ps1 +81 -0
  154. package/lib/types/host/windows-cli-installation.d.ts +35 -0
  155. package/lib/types/host/windows-cli-installation.js +164 -0
  156. package/lib/types/host/windows-cli-installation.js.map +1 -0
  157. package/lib/types/host/windows-codex-launch.d.ts +9 -0
  158. package/lib/types/host/windows-codex-launch.js +216 -0
  159. package/lib/types/host/windows-codex-launch.js.map +1 -0
  160. package/lib/types/host/windows-desktop-cli-installation.d.ts +20 -0
  161. package/lib/types/host/windows-desktop-cli-installation.js +420 -0
  162. package/lib/types/host/windows-desktop-cli-installation.js.map +1 -0
  163. package/lib/types/host/windows-hidden-launch.mjs +51 -0
  164. package/lib/types/host/windows-hidden-process.d.ts +12 -0
  165. package/lib/types/host/windows-hidden-process.js +116 -0
  166. package/lib/types/host/windows-hidden-process.js.map +1 -0
  167. package/lib/types/host/zcode-adapter.js +5 -1
  168. package/lib/types/host/zcode-adapter.js.map +1 -1
  169. package/lib/types/host/zcode-grok-accounts.d.ts +3 -1
  170. package/lib/types/host/zcode-grok-accounts.js +60 -11
  171. package/lib/types/host/zcode-grok-accounts.js.map +1 -1
  172. package/lib/types/shared/accounts.d.ts +4 -0
  173. package/lib/types/shared/cli-settings.d.ts +8 -0
  174. package/lib/windows-account-bootstrap.ps1 +44 -0
  175. package/lib/windows-account-launch.mjs +47 -0
  176. package/lib/windows-account-owner.ps1 +137 -0
  177. package/lib/windows-account-stdio.mjs +19 -0
  178. package/lib/windows-agy-account.ps1 +81 -0
  179. package/lib/windows-hidden-launch.mjs +51 -0
  180. package/lib/zcode-resume.mjs +2 -2
  181. package/package.json +2 -2
@@ -0,0 +1,47 @@
1
+ // Windows account PTY bootstrap. No native CLI runs until its process has been
2
+ // assigned to the separate lifecycle Job owner; no task sandbox is applied.
3
+ import { accountInput } from './windows-account-stdio.mjs'
4
+ import { fileURLToPath } from 'node:url'
5
+ import { spawnNativeProcess } from './native-spawn.mjs'
6
+ import { writeFileSync, existsSync } from 'node:fs'
7
+ import { join } from 'node:path'
8
+ import { setTimeout as delay } from 'node:timers/promises'
9
+ import { nativeLaunchArgv } from './native-launch.mjs'
10
+ process.umask(0o077)
11
+ const [directory, encoded] = process.argv.slice(2)
12
+ let requested
13
+ try { requested = JSON.parse(Buffer.from(encoded, 'base64').toString('utf8')) } catch { process.exit(1) }
14
+ if (!directory || !Array.isArray(requested) || !requested.length || requested.some(value => typeof value !== 'string')) process.exit(1)
15
+ // This exact plugin wrapper only sets umask and delegates an argv array. This
16
+ // helper already does both; avoid another GUI runtime losing console stdin.
17
+ if (requested[0] === process.execPath && requested[1] === fileURLToPath(new URL('./private-launch.mjs', import.meta.url)))
18
+ requested = requested.slice(2)
19
+ const until = Date.now() + 15_000
20
+ while (!existsSync(join(directory, 'ready'))) {
21
+ if (Date.now() >= until) process.exit(1)
22
+ await delay(20)
23
+ }
24
+ // Keep ConPTY alive after the native command exits so Host can dispose its
25
+ // native worker before closing the entire Job. Only the exit code is exposed.
26
+ setInterval(() => {}, 1000)
27
+ const recordExit = (code, signal) => {
28
+ const exitCode = Number.isInteger(code) ? code : 1
29
+ writeFileSync(join(directory, 'exit'), JSON.stringify({ exitCode, signal: signal ?? null }), { mode: 0o600 })
30
+ }
31
+ if (!existsSync(join(directory, 'cancel')) && !existsSync(join(directory, 'stop'))) {
32
+ try {
33
+ const [executable, ...argv] = nativeLaunchArgv(requested)
34
+ // Node-based CLI account UIs also need their own Electron stdin restored.
35
+ // This fixed preload is scoped to that child, never NODE_OPTIONS or Host.
36
+ if (executable === process.execPath && process.versions.electron)
37
+ argv.unshift('--import', new URL('./windows-account-stdio.mjs', import.meta.url).href)
38
+ // Keep the existing ConPTY handles; a GUI-subsystem parent must not cause
39
+ // the native CLI to present an additional Windows console window.
40
+ const child = spawnNativeProcess(executable, argv, { env: process.env, stdio: [accountInput, 1, 2] })
41
+ child.once('error', () => { process.stderr.write('CLI launch failed\r\n') })
42
+ child.once('close', recordExit)
43
+ } catch {
44
+ process.stderr.write('CLI launch failed\r\n')
45
+ recordExit(1)
46
+ }
47
+ }
@@ -0,0 +1,137 @@
1
+ param([Parameter(Mandatory=$true)][string]$Directory, [Parameter(Mandatory=$true)][int]$HostProcessId)
2
+ $ErrorActionPreference = 'Stop'
3
+ # Fixed process ownership only: no resource, privilege, network, or filesystem limits.
4
+ Add-Type -TypeDefinition @'
5
+ using System;
6
+ using System.Diagnostics;
7
+ using System.ComponentModel;
8
+ using System.Runtime.InteropServices;
9
+ public sealed class CwnAccountJob : IDisposable {
10
+ [StructLayout(LayoutKind.Sequential)] struct BasicLimits { public long ProcessTime, JobTime; public uint Flags; public UIntPtr MinWorking, MaxWorking; public uint ActiveLimit; public UIntPtr Affinity; public uint Priority, Scheduling; }
11
+ [StructLayout(LayoutKind.Sequential)] struct Io { public ulong ReadOps, WriteOps, OtherOps, ReadBytes, WriteBytes, OtherBytes; }
12
+ [StructLayout(LayoutKind.Sequential)] struct ExtendedLimits { public BasicLimits Basic; public Io Io; public UIntPtr ProcessMemory, JobMemory, PeakProcessMemory, PeakJobMemory; }
13
+ [StructLayout(LayoutKind.Sequential)] struct Accounting { public long User, Kernel, PeriodUser, PeriodKernel; public uint PageFaults, TotalProcesses, ActiveProcesses, TerminatedProcesses; }
14
+ [DllImport("kernel32.dll", CharSet=CharSet.Unicode, SetLastError=true)] static extern IntPtr CreateJobObject(IntPtr attr, string name);
15
+ [DllImport("kernel32.dll", SetLastError=true)] static extern bool SetInformationJobObject(IntPtr job, int kind, ref ExtendedLimits limits, uint size);
16
+ [DllImport("kernel32.dll", SetLastError=true)] static extern bool QueryInformationJobObject(IntPtr job, int kind, out Accounting info, uint size, IntPtr length);
17
+ [DllImport("kernel32.dll", SetLastError=true)] static extern bool AssignProcessToJobObject(IntPtr job, IntPtr process);
18
+ [DllImport("kernel32.dll", SetLastError=true)] static extern bool TerminateJobObject(IntPtr job, uint code);
19
+ [DllImport("kernel32.dll")] static extern bool CloseHandle(IntPtr handle);
20
+ [DllImport("kernel32.dll")] static extern bool FreeConsole();
21
+ [DllImport("kernel32.dll", SetLastError=true)] static extern bool AttachConsole(uint pid);
22
+ [DllImport("kernel32.dll")] static extern IntPtr GetConsoleWindow();
23
+ [DllImport("user32.dll", SetLastError=true)] static extern uint GetWindowThreadProcessId(IntPtr window, out uint pid);
24
+ [DllImport("kernel32.dll", CharSet=CharSet.Unicode, SetLastError=true)] static extern IntPtr GetModuleHandle(string name);
25
+ [DllImport("kernel32.dll", SetLastError=true)] static extern IntPtr GetProcAddress(IntPtr module, IntPtr ordinal);
26
+ [UnmanagedFunctionPointer(CallingConvention.Winapi)] delegate uint RealWindowOwner(IntPtr window);
27
+ IntPtr job; Process root;
28
+ public CwnAccountJob() { job=CreateJobObject(IntPtr.Zero,null); if(job==IntPtr.Zero) throw new Win32Exception(Marshal.GetLastWin32Error()); var l=new ExtendedLimits(); l.Basic.Flags=0x2000; if(!SetInformationJobObject(job,9,ref l,(uint)Marshal.SizeOf(l))) { var error=Marshal.GetLastWin32Error(); Dispose(); throw new Win32Exception(error); } }
29
+ public void Attach(int pid, DateTime receiptTime, DateTime earliest) { root=Process.GetProcessById(pid); var handle=root.Handle; var started=root.StartTime.ToUniversalTime(); if(started>receiptTime || started<earliest) throw new Exception("process identity changed"); if(!AssignProcessToJobObject(job,handle)) throw new Win32Exception(Marshal.GetLastWin32Error()); }
30
+ public static int ConsoleOwner(int pid) {
31
+ FreeConsole(); if(!AttachConsole((uint)pid)) throw new Win32Exception(Marshal.GetLastWin32Error());
32
+ try {
33
+ var window=GetConsoleWindow(); uint owner;
34
+ if(window==IntPtr.Zero || GetWindowThreadProcessId(window,out owner)==0 || owner==0) throw new Exception("console owner unavailable");
35
+ // Modern ConPTY gives its message-only window to the root client, not
36
+ // conhost: microsoft/terminal#14196. Resolve this same HWND's real owner
37
+ // using the maintainer's dynamic API from discussion #16447. The ordinal
38
+ // is not a stable public API; absence or an invalid result fails closed.
39
+ if(owner==(uint)pid) {
40
+ var module=GetModuleHandle("user32.dll");
41
+ var address=module==IntPtr.Zero?IntPtr.Zero:GetProcAddress(module,new IntPtr(2712));
42
+ if(address==IntPtr.Zero) throw new Exception("real console owner unavailable");
43
+ var getOwner=(RealWindowOwner)Marshal.GetDelegateForFunctionPointer(address,typeof(RealWindowOwner));
44
+ owner=getOwner(window);
45
+ }
46
+ if(owner==0 || owner>(uint)Int32.MaxValue || owner==(uint)pid) throw new Exception("console owner unavailable");
47
+ return (int)owner;
48
+ } finally { FreeConsole(); }
49
+ }
50
+ public string ConsoleIdentityReason { get; private set; }
51
+ public string ConsoleImageKind { get; private set; }
52
+ public bool ConsoleImageDevicePrefix { get; private set; }
53
+ public void AttachConsoleHost(int pid, DateTime latest, DateTime earliest) {
54
+ using(var p=Process.GetProcessById(pid)) {
55
+ var handle=p.Handle; var started=p.StartTime.ToUniversalTime();
56
+ var expected=System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.System),"conhost.exe");
57
+ if(started>latest) { ConsoleIdentityReason="after-receipt"; throw new Exception("console identity changed"); }
58
+ if(started<earliest) { ConsoleIdentityReason="before-directory"; throw new Exception("console identity changed"); }
59
+ var image=p.MainModule.FileName; var leaf=System.IO.Path.GetFileName(image);
60
+ ConsoleImageKind=String.Equals(leaf,"conhost.exe",StringComparison.OrdinalIgnoreCase)?"conhost.exe":String.Equals(leaf,"openconsole.exe",StringComparison.OrdinalIgnoreCase)?"openconsole.exe":"other";
61
+ ConsoleImageDevicePrefix=image!=null && (image.StartsWith(@"\\?\") || image.StartsWith(@"\??\"));
62
+ if(!String.Equals(image,expected,StringComparison.OrdinalIgnoreCase)) { ConsoleIdentityReason="wrong-image"; throw new Exception("console identity changed"); }
63
+ if(!AssignProcessToJobObject(job,handle)) throw new Win32Exception(Marshal.GetLastWin32Error());
64
+ }
65
+ }
66
+ public bool RootExited { get { return root.HasExited; } }
67
+ public void Stop() { if(!TerminateJobObject(job,143)) throw new Win32Exception(Marshal.GetLastWin32Error()); }
68
+ public bool Empty { get { Accounting a; if(!QueryInformationJobObject(job,1,out a,(uint)Marshal.SizeOf(typeof(Accounting)),IntPtr.Zero)) throw new Win32Exception(Marshal.GetLastWin32Error()); return a.ActiveProcesses==0; } }
69
+ public void Dispose() { if(job!=IntPtr.Zero) { CloseHandle(job); job=IntPtr.Zero; } if(root!=null) root.Dispose(); }
70
+ }
71
+ '@
72
+ $job=$null
73
+ $stage='await-bootstrap'
74
+ $consolePid=$null
75
+ $consoleParentPid=$null
76
+ try {
77
+ $earliest=[System.IO.Directory]::GetCreationTimeUtc($Directory).AddSeconds(-1)
78
+ $pidFile=Join-Path $Directory 'pid'
79
+ $stopFile=Join-Path $Directory 'stop'
80
+ [System.IO.File]::WriteAllText((Join-Path $Directory 'owner-ready'),'owner-ready')
81
+ $until=[DateTime]::UtcNow.AddSeconds(15)
82
+ while (!(Test-Path -LiteralPath $pidFile)) {
83
+ if ([DateTime]::UtcNow -ge $until) { throw 'bootstrap timeout' }
84
+ Start-Sleep -Milliseconds 20
85
+ }
86
+ $childPid=0
87
+ if (![int]::TryParse([System.IO.File]::ReadAllText($pidFile),[ref]$childPid) -or $childPid -le 0) { throw 'invalid bootstrap receipt' }
88
+ $stage='create-job'
89
+ $job=New-Object CwnAccountJob
90
+ $stage='attach-root'
91
+ $job.Attach($childPid,[System.IO.File]::GetLastWriteTimeUtc($pidFile),$earliest)
92
+ # The ConPTY console is a sibling of the bootstrap, created by the Host.
93
+ # Resolve it through this exact console, then validate parent and creation
94
+ # identity before adding it; never enumerate/kill processes by image name.
95
+ $stage='resolve-console'
96
+ $consolePid=[CwnAccountJob]::ConsoleOwner($childPid)
97
+ $stage='validate-console-parent'
98
+ $console=Get-CimInstance Win32_Process -Filter "ProcessId=$consolePid"
99
+ if ($null -ne $console) { $consoleParentPid=$console.ParentProcessId }
100
+ if ($null -eq $console -or $console.ParentProcessId -ne $HostProcessId) { throw 'console owner mismatch' }
101
+ $stage='attach-console'
102
+ $job.AttachConsoleHost($consolePid,[System.IO.File]::GetLastWriteTimeUtc($pidFile),$earliest)
103
+ $stage='await-stop'
104
+ [System.IO.File]::WriteAllText((Join-Path $Directory 'ready'),'ready')
105
+ while (!$job.RootExited -and !(Test-Path -LiteralPath $stopFile)) { Start-Sleep -Milliseconds 50 }
106
+ $stage='stop-job'
107
+ $job.Stop()
108
+ $stage='confirm-empty'
109
+ $until=[DateTime]::UtcNow.AddSeconds(8)
110
+ while (!$job.Empty) {
111
+ if ([DateTime]::UtcNow -ge $until) { throw 'empty receipt timeout' }
112
+ Start-Sleep -Milliseconds 20
113
+ }
114
+ [System.IO.File]::WriteAllText((Join-Path $Directory 'closed'),'closed')
115
+ exit 0
116
+ } catch {
117
+ # Fixed stage and numeric metadata only; no exception text/argv/environment.
118
+ $failure=@{ stage=$stage; hostPid=$HostProcessId; ownerPid=$PID }
119
+ if ($null -ne $consolePid) { $failure.consolePid=$consolePid }
120
+ if ($null -ne $consoleParentPid) { $failure.consoleParentPid=$consoleParentPid }
121
+ if ($null -ne $job -and $job.ConsoleIdentityReason -in @('before-directory','after-receipt','wrong-image')) {
122
+ $failure.identityReason=$job.ConsoleIdentityReason
123
+ if ($job.ConsoleImageKind -in @('conhost.exe','openconsole.exe','other')) {
124
+ $failure.imageKind=$job.ConsoleImageKind
125
+ $failure.imageDevicePrefix=$job.ConsoleImageDevicePrefix
126
+ }
127
+ }
128
+ $errorObject=$_.Exception
129
+ while ($null -ne $errorObject) {
130
+ if ($errorObject -is [System.ComponentModel.Win32Exception]) { $failure.win32Code=$errorObject.NativeErrorCode; break }
131
+ $errorObject=$errorObject.InnerException
132
+ }
133
+ try { [System.IO.File]::WriteAllText((Join-Path $Directory 'failure'),($failure | ConvertTo-Json -Compress)) } catch {}
134
+ # Fixed classification only: never emit native exception/context/environment.
135
+ [Console]::Error.WriteLine('Windows account process ownership failed')
136
+ exit 2
137
+ } finally { if ($null -ne $job) { $job.Dispose() } }
@@ -0,0 +1,19 @@
1
+ // Imported only by the Windows account-terminal runtime and its Electron CLI
2
+ // child. Electron attaches stdout/stderr to the parent console, but its GUI
3
+ // executable can leave fd 0 mapped to NUL. Reopen this already-owned console;
4
+ // do not redirect stdin for task processes or change the shared Host.
5
+ import { openSync, closeSync } from 'node:fs'
6
+ import { isatty, ReadStream } from 'node:tty'
7
+ export let accountInput = 0
8
+ if (process.platform === 'win32' && !isatty(0)) {
9
+ try {
10
+ accountInput = openSync('\\\\.\\CONIN$', 'r+')
11
+ if (!isatty(accountInput)) throw new Error('not a console')
12
+ const input = new ReadStream(accountInput)
13
+ Object.defineProperty(process, 'stdin', { value: input, configurable: true })
14
+ process.once('exit', () => { try { closeSync(accountInput) } catch {} })
15
+ } catch {
16
+ if (accountInput) { try { closeSync(accountInput) } catch {} }
17
+ throw new Error('Windows account console input unavailable')
18
+ }
19
+ }
@@ -0,0 +1,17 @@
1
+ import type { SubprocessTerminalHandle, SubprocessTerminalSpawnSpec } from '@deepseek-ai/dsh-subprocess';
2
+ import { type ProcessBackend } from './process.ts';
3
+ /** A fixed diagnostic; never derived from PowerShell or native output. */
4
+ export declare class WindowsAccountTerminalError extends Error {
5
+ constructor();
6
+ }
7
+ /** ConPTY publishes pid=0 during async allocation in the pinned Windows SDK.
8
+ * Attach a waiting bootstrap to a lifecycle-only Job before allowing the CLI to
9
+ * spawn. The separate owner is itself held by the Host's ordinary subprocess
10
+ * Job, so closing/crashing the Host also closes the account process range.
11
+ */
12
+ export declare function spawnAccountTerminal(backend: ProcessBackend, spec: SubprocessTerminalSpawnSpec, options?: {
13
+ platform?: NodeJS.Platform;
14
+ handshakeMs?: number;
15
+ cleanupMs?: number;
16
+ terminalExitMs?: number;
17
+ }): Promise<SubprocessTerminalHandle>;
@@ -0,0 +1,208 @@
1
+ import { mkdtemp, readFile, writeFile, rm } from 'node:fs/promises';
2
+ import { tmpdir } from 'node:os';
3
+ import { join } from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+ import { setTimeout as delay } from 'node:timers/promises';
6
+ import { ProcessCleanupUnconfirmedError } from "./process.js";
7
+ /** A fixed diagnostic; never derived from PowerShell or native output. */
8
+ export class WindowsAccountTerminalError extends Error {
9
+ constructor() {
10
+ super('Windows 账号终端托管不可用,请检查 PowerShell 后重试');
11
+ }
12
+ }
13
+ /** ConPTY publishes pid=0 during async allocation in the pinned Windows SDK.
14
+ * Attach a waiting bootstrap to a lifecycle-only Job before allowing the CLI to
15
+ * spawn. The separate owner is itself held by the Host's ordinary subprocess
16
+ * Job, so closing/crashing the Host also closes the account process range.
17
+ */
18
+ export async function spawnAccountTerminal(backend, spec, options = {}) {
19
+ if ((options.platform ?? process.platform) !== 'win32')
20
+ return backend.spawnTerminal(spec);
21
+ spec.signal?.throwIfAborted();
22
+ const directory = await mkdtemp(join(tmpdir(), 'cwn-account-job-'));
23
+ const windows = process.env.SystemRoot ?? 'C:\\Windows';
24
+ let terminal;
25
+ const owner = await (async () => {
26
+ try {
27
+ return backend.spawn({
28
+ argv: [
29
+ join(windows, 'System32/WindowsPowerShell/v1.0/powershell.exe'),
30
+ '-NoLogo',
31
+ '-NoProfile',
32
+ '-NonInteractive',
33
+ '-File',
34
+ fileURLToPath(new URL('./windows-account-owner.ps1', import.meta.url)),
35
+ '-HostProcessId',
36
+ String(process.pid),
37
+ '-Directory',
38
+ directory,
39
+ ],
40
+ cwd: spec.cwd,
41
+ stdio: { stdin: 'ignore', stdout: 'pipe', stderr: 'pipe' },
42
+ graceMs: 1000,
43
+ });
44
+ }
45
+ catch (error) {
46
+ await rm(directory, { recursive: true, force: true });
47
+ throw error;
48
+ }
49
+ })();
50
+ void owner.done.catch(() => undefined);
51
+ owner.stdout?.resume();
52
+ owner.stderr?.resume();
53
+ let cleanup;
54
+ const exists = async (name) => {
55
+ try {
56
+ return (await readFile(join(directory, name), 'utf8')).trim() === name;
57
+ }
58
+ catch {
59
+ return false;
60
+ }
61
+ };
62
+ const terminate = () => (cleanup ??= (async () => {
63
+ let stage = 'receipt';
64
+ try {
65
+ // Cancellation before the handshake must still let the owner adopt the
66
+ // bootstrap/console, while preventing it from launching the native CLI.
67
+ await writeFile(join(directory, 'cancel'), 'cancel', { mode: 0o600 });
68
+ const adoptedUntil = Date.now() + (options.cleanupMs ?? 9000);
69
+ while (!(await exists('ready'))) {
70
+ if (Date.now() >= adoptedUntil)
71
+ throw new ProcessCleanupUnconfirmedError();
72
+ await delay(25);
73
+ }
74
+ // Dispose ConPTY while the bootstrap is still alive. With the pinned
75
+ // node-pty provider, killing the Job first bypasses its worker disposal.
76
+ // Native exit receipts keep the bootstrap alive for this same reason.
77
+ const disposal = terminal?.terminate().then(() => true, () => false);
78
+ if (disposal)
79
+ await Promise.race([disposal, delay(options.terminalExitMs ?? 2500)]);
80
+ await writeFile(join(directory, 'stop'), 'stop', { mode: 0o600 });
81
+ const deadline = Date.now() + (options.cleanupMs ?? 9000);
82
+ while (!(await exists('closed'))) {
83
+ if (Date.now() >= deadline)
84
+ throw new ProcessCleanupUnconfirmedError();
85
+ await delay(25);
86
+ }
87
+ // The receipt follows ActiveProcesses == 0, not just the root's exit.
88
+ stage = 'terminal';
89
+ if (disposal && !(await Promise.race([disposal, delay(options.terminalExitMs ?? 2500, false)])))
90
+ throw new ProcessCleanupUnconfirmedError();
91
+ stage = 'owner';
92
+ if (!(await owner.waitForExit()))
93
+ throw new ProcessCleanupUnconfirmedError();
94
+ await owner.done;
95
+ stage = 'directory';
96
+ await rm(directory, { recursive: true, force: true });
97
+ }
98
+ catch {
99
+ cleanup = undefined;
100
+ throw Object.assign(new ProcessCleanupUnconfirmedError(), { stage });
101
+ }
102
+ })());
103
+ try {
104
+ // Compile/validate the fixed owner before allocating any ConPTY resources.
105
+ const ownerUntil = Date.now() + (options.handshakeMs ?? 15_000);
106
+ while (!(await exists('owner-ready'))) {
107
+ spec.signal?.throwIfAborted();
108
+ if (Date.now() >= ownerUntil)
109
+ throw new Error('Windows account owner handshake timed out');
110
+ await Promise.race([
111
+ delay(25),
112
+ owner.done.then(() => {
113
+ throw new Error('Windows account owner ended');
114
+ }),
115
+ ]);
116
+ }
117
+ spec.signal?.throwIfAborted();
118
+ terminal = await backend.spawnTerminal({
119
+ ...spec,
120
+ signal: undefined,
121
+ argv: [
122
+ join(windows, 'System32/WindowsPowerShell/v1.0/powershell.exe'),
123
+ '-NoLogo',
124
+ '-NoProfile',
125
+ '-NonInteractive',
126
+ '-File',
127
+ fileURLToPath(new URL('./windows-account-bootstrap.ps1', import.meta.url)),
128
+ '-Directory',
129
+ directory,
130
+ '-RuntimePath',
131
+ process.execPath,
132
+ '-LaunchPath',
133
+ fileURLToPath(new URL('./windows-account-launch.mjs', import.meta.url)),
134
+ '-ArgumentsBase64',
135
+ Buffer.from(JSON.stringify(spec.argv), 'utf8').toString('base64'),
136
+ ],
137
+ env: { ...spec.env, ELECTRON_RUN_AS_NODE: '1' },
138
+ });
139
+ void terminal.done.catch(() => undefined);
140
+ const until = Date.now() + (options.handshakeMs ?? 15_000);
141
+ while (!(await exists('ready'))) {
142
+ spec.signal?.throwIfAborted();
143
+ if (Date.now() >= until)
144
+ throw new Error('Windows account terminal handshake timed out');
145
+ await Promise.race([
146
+ delay(25),
147
+ owner.done.then(() => {
148
+ throw new Error('Windows account owner ended');
149
+ }),
150
+ ]);
151
+ }
152
+ const current = terminal;
153
+ const done = (async () => {
154
+ for (;;) {
155
+ try {
156
+ const receipt = JSON.parse(await readFile(join(directory, 'exit'), 'utf8'));
157
+ if (Number.isInteger(receipt.exitCode))
158
+ return { exitCode: receipt.exitCode, signal: null };
159
+ }
160
+ catch {
161
+ /* An interrupted bootstrap retains the SDK outcome. */
162
+ }
163
+ const outcome = await Promise.race([current.done, delay(50, undefined)]);
164
+ if (outcome)
165
+ return outcome;
166
+ }
167
+ })();
168
+ void done.catch(() => undefined);
169
+ return {
170
+ pid: Number(await readFile(join(directory, 'pid'), 'utf8')),
171
+ output: current.output,
172
+ done,
173
+ write: (data) => current.write(data),
174
+ resize: (cols, rows) => current.resize(cols, rows),
175
+ inspectForeground: () => current.inspectForeground(),
176
+ inspectActivity: () => current.inspectActivity(),
177
+ signalForeground: (signal) => current.signalForeground(signal),
178
+ terminate,
179
+ };
180
+ }
181
+ catch (error) {
182
+ if (terminal) {
183
+ // A cancellation can precede ready: the bootstrap keeps waiting until
184
+ // the owner adopts its console/Job, then observes stop without running
185
+ // the CLI. Require the same empty-range receipt as a normal close.
186
+ try {
187
+ await terminate();
188
+ }
189
+ catch (failure) {
190
+ owner.terminate();
191
+ await terminal.terminate().catch(() => undefined);
192
+ throw failure;
193
+ }
194
+ }
195
+ else {
196
+ // No terminal was allocated; only the ordinary managed owner exists.
197
+ await writeFile(join(directory, 'stop'), 'stop', { mode: 0o600 });
198
+ owner.terminate();
199
+ if (!(await owner.waitForExit()))
200
+ throw new ProcessCleanupUnconfirmedError();
201
+ await rm(directory, { recursive: true, force: true });
202
+ }
203
+ if (spec.signal?.aborted)
204
+ throw error;
205
+ throw new WindowsAccountTerminalError();
206
+ }
207
+ }
208
+ //# sourceMappingURL=windows-account-terminal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"windows-account-terminal.js","sourceRoot":"","sources":["../../../src/host/windows-account-terminal.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,81 @@
1
+ # Read only Antigravity's own native Windows credential. Never emit its blob.
2
+ $ErrorActionPreference = 'Stop'
3
+ [Console]::OutputEncoding = [System.Text.UTF8Encoding]::new($false)
4
+ try {
5
+ Add-Type @'
6
+ using System;
7
+ using System.Runtime.InteropServices;
8
+ using System.Text;
9
+ public static class CwnAgyCredential {
10
+ [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)] struct Credential {
11
+ public uint Flags, Type;
12
+ public string TargetName, Comment;
13
+ public System.Runtime.InteropServices.ComTypes.FILETIME LastWritten;
14
+ public uint CredentialBlobSize;
15
+ public IntPtr CredentialBlob;
16
+ public uint Persist, AttributeCount;
17
+ public IntPtr Attributes;
18
+ public string TargetAlias, UserName;
19
+ }
20
+ [DllImport("advapi32.dll", CharSet=CharSet.Unicode, SetLastError=true)]
21
+ static extern bool CredRead(string target, uint type, uint flags, out IntPtr value);
22
+ [DllImport("advapi32.dll")] static extern void CredFree(IntPtr value);
23
+ public static string Read() {
24
+ IntPtr value;
25
+ if (!CredRead("gemini:antigravity", 1, 0, out value)) {
26
+ if (Marshal.GetLastWin32Error() == 1168) return null;
27
+ throw new Exception("Credential store unavailable");
28
+ }
29
+ byte[] bytes = null;
30
+ try {
31
+ var credential = (Credential)Marshal.PtrToStructure(value, typeof(Credential));
32
+ if (credential.CredentialBlobSize > 65536) throw new Exception("Invalid credential");
33
+ bytes = new byte[credential.CredentialBlobSize];
34
+ Marshal.Copy(credential.CredentialBlob, bytes, 0, bytes.Length);
35
+ return bytes.Length > 1 && bytes[1] == 0
36
+ ? new UnicodeEncoding(false, false, true).GetString(bytes)
37
+ : new UTF8Encoding(false, true).GetString(bytes);
38
+ } finally {
39
+ if (bytes != null) Array.Clear(bytes, 0, bytes.Length);
40
+ CredFree(value);
41
+ }
42
+ }
43
+ }
44
+ '@
45
+ $raw = [CwnAgyCredential]::Read()
46
+ if ($null -eq $raw) { @{ state = 'unconfigured' } | ConvertTo-Json -Compress; exit 0 }
47
+ $account = $raw | ConvertFrom-Json
48
+ $raw = $null
49
+ if ($account.auth_method -ne 'consumer' -or -not $account.token) {
50
+ @{ state = 'unknown' } | ConvertTo-Json -Compress; exit 0
51
+ }
52
+ $active = $false
53
+ if ($account.token.access_token -is [string] -and $account.token.access_token.Trim()) {
54
+ try { $active = [DateTimeOffset]::Parse($account.token.expiry) -gt [DateTimeOffset]::UtcNow } catch {}
55
+ }
56
+ $refreshable = $account.token.refresh_token -is [string] -and $account.token.refresh_token.Trim().Length -gt 0
57
+ if (-not $active -and -not $refreshable) {
58
+ @{ state = 'unauthenticated' } | ConvertTo-Json -Compress; exit 0
59
+ }
60
+ $result = @{ state = 'authenticated' }
61
+ if ($account.id_token -is [string] -and $account.id_token.Length -le 32768 -and $account.id_token -match '^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$') {
62
+ $encoded = $account.id_token.Split('.')[1].Replace('-','+').Replace('_','/')
63
+ $encoded = $encoded.PadRight($encoded.Length + ((4 - $encoded.Length % 4) % 4), '=')
64
+ $bytes = [Convert]::FromBase64String($encoded)
65
+ try { $claims = [Text.Encoding]::UTF8.GetString($bytes) | ConvertFrom-Json }
66
+ finally { [Array]::Clear($bytes, 0, $bytes.Length) }
67
+ if ($claims.iss -in @('accounts.google.com', 'https://accounts.google.com') -and $claims.sub -is [string] -and $claims.sub -match '^[A-Za-z0-9_-]{6,128}$') {
68
+ $hash = [Security.Cryptography.SHA256]::Create()
69
+ $principal = [Text.Encoding]::UTF8.GetBytes('google:' + $claims.sub)
70
+ try { $result.principalHash = ([BitConverter]::ToString($hash.ComputeHash($principal))).Replace('-','').ToLowerInvariant() }
71
+ finally { [Array]::Clear($principal, 0, $principal.Length); $hash.Dispose() }
72
+ if ($claims.email -is [string] -and $claims.email.Length -le 254 -and $claims.email -match '^[^\s<>@]+@[^\s<>@]+\.[^\s<>@]+$') { $result.email = $claims.email }
73
+ }
74
+ }
75
+ $account = $null; $claims = $null
76
+ $result | ConvertTo-Json -Compress
77
+ } catch {
78
+ # Neither exception strings nor credential fragments cross the process boundary.
79
+ @{ state = 'unavailable' } | ConvertTo-Json -Compress
80
+ exit 1
81
+ }
@@ -0,0 +1,35 @@
1
+ export declare class WindowsCliInstallationError extends Error {
2
+ readonly installed: boolean;
3
+ readonly code = "CLI_INSTALLATION_UNAVAILABLE";
4
+ constructor(message: string, installed?: boolean);
5
+ }
6
+ /** A completed desktop/PATH search found no CLI; absence is not a broken installation. */
7
+ export declare class WindowsCliNotFoundError extends WindowsCliInstallationError {
8
+ constructor(cli: 'zcode' | 'opencode');
9
+ }
10
+ type WindowsEnvironment = {
11
+ home?: string;
12
+ env?: NodeJS.ProcessEnv;
13
+ };
14
+ /** Fixed installer locations only; never scan arbitrary drives or select a venv by recency. */
15
+ export declare function windowsCliCandidates(cli: 'codex' | 'hermes', options?: WindowsEnvironment): string[];
16
+ /** A residue is not a runnable installation and is not evidence of an account. */
17
+ export declare function hasWindowsHermesInstallDirectory(options?: WindowsEnvironment): boolean;
18
+ /** Validate an automatically discovered npm shim against Codex's own package, not just its name. */
19
+ export declare function validateWindowsCodexNpmEntry(path: string): string;
20
+ export declare function presentWindowsCliCandidate(cli: 'codex' | 'hermes', options?: WindowsEnvironment): string | undefined;
21
+ /** Query current-user registered packages, never guess the newest WindowsApps directory. */
22
+ export declare const CODEX_APPX_QUERY = "$ErrorActionPreference='Stop'; [Console]::OutputEncoding=[Text.UTF8Encoding]::new($false); $p=@(Get-AppxPackage -Name 'OpenAI.Codex'); if($p.Count -eq 0){'null';exit 0}; if($p.Count -ne 1){throw 'Ambiguous Codex package'}; $v=$p[0]; [ordered]@{name=$v.Name;family=$v.PackageFamilyName;fullName=$v.PackageFullName;publisher=$v.Publisher;signature=[string]$v.SignatureKind;status=[string]$v.Status;location=$v.InstallLocation}|ConvertTo-Json -Compress";
23
+ export interface CodexAppxRecord {
24
+ name: string;
25
+ family: string;
26
+ fullName: string;
27
+ publisher: string;
28
+ signature: string;
29
+ status: string;
30
+ location: string;
31
+ }
32
+ /** Exported separately for synthetic identity/relocation tests; no process or account access. */
33
+ export declare function codexAppxExecutable(raw: string, validateFile?: (path: string) => string): string | undefined;
34
+ export declare function findWindowsCodexAppx(capture: (argv: string[], cwd: string) => Promise<string>, signal: AbortSignal, env?: NodeJS.ProcessEnv): Promise<string | undefined>;
35
+ export {};