windows-development-sandbox 1.23.2 → 1.25.1

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 (211) hide show
  1. package/config.example.json +5 -3
  2. package/dist/app/features/data/data.def.d.ts +4 -4
  3. package/dist/app/features/data/data.run.d.ts +1 -1
  4. package/dist/app/features/defs.d.ts +161 -6
  5. package/dist/app/features/defs.js +7 -0
  6. package/dist/app/features/projects/projects.def.d.ts +1 -1
  7. package/dist/app/features/projects/projects.run.d.ts +1 -1
  8. package/dist/app/features/unreal/index.d.ts +2 -0
  9. package/dist/app/features/unreal/index.js +3 -0
  10. package/dist/app/features/unreal/unreal.def.d.ts +236 -0
  11. package/dist/app/features/unreal/unreal.def.js +379 -0
  12. package/dist/app/features/unreal/unreal.run.d.ts +96 -0
  13. package/dist/app/features/unreal/unreal.run.js +217 -0
  14. package/dist/app/registry.js +5 -0
  15. package/dist/bin/agent-sandbox.js +2 -2
  16. package/dist/bin/{chunk-7YPEDAJD.js → chunk-3GAXLATK.js} +58 -5
  17. package/dist/bin/{chunk-USTRSHIE.js → chunk-F4PVBN23.js} +2615 -1515
  18. package/dist/bin/{chunk-QFSJX2KW.js → chunk-G3TG4ZT7.js} +1 -1
  19. package/dist/bin/claude-host.js +2 -2
  20. package/dist/bin/claude-sbx.js +2 -2
  21. package/dist/bin/codex-sbx.js +2 -2
  22. package/dist/bin/gemini-sbx.js +2 -2
  23. package/dist/bin/sandbox.js +2 -2
  24. package/dist/bin/sbx-hide.js +2 -2
  25. package/dist/bin/sbx-show.js +2 -2
  26. package/dist/cli/commands/launch/launch.js +5 -2
  27. package/dist/cli/commands/registry.js +3 -0
  28. package/dist/cli/manifest.d.ts +18 -0
  29. package/dist/cli/manifest.js +26 -0
  30. package/dist/cli/menu/coverage-exempt.js +1 -0
  31. package/dist/cli/menu/pages/sandbox-page.d.ts +3 -0
  32. package/dist/cli/menu/pages/sandbox-page.js +34 -4
  33. package/dist/cli/menu/views.js +2 -0
  34. package/dist/core/config/defaults.js +18 -5
  35. package/dist/core/detect/toolchains.js +0 -6
  36. package/dist/core/schema/config.d.ts +10 -9
  37. package/dist/core/schema/pc-installs.d.ts +3 -1
  38. package/dist/core/schema/pc-installs.js +10 -1
  39. package/dist/core/schema/toolchains.d.ts +1 -2
  40. package/dist/core/schema/toolchains.js +4 -7
  41. package/dist/guest/accounts/kinds/epic.js +18 -13
  42. package/dist/guest/agents/agent-files.d.ts +11 -7
  43. package/dist/guest/agents/agent-files.js +37 -11
  44. package/dist/guest/agents/local-state.d.ts +2 -1
  45. package/dist/guest/agents/local-state.js +2 -1
  46. package/dist/guest/apps/after-install.d.ts +1 -1
  47. package/dist/guest/apps/after-install.js +6 -1
  48. package/dist/guest/apps/app-installer.d.ts +1 -0
  49. package/dist/guest/apps/app-installer.js +13 -3
  50. package/dist/guest/apps/comet-unpack.js +3 -2
  51. package/dist/guest/apps/resolvers-gamedev.js +55 -1
  52. package/dist/guest/boot/boot-console.d.ts +28 -0
  53. package/dist/guest/boot/boot-console.js +98 -0
  54. package/dist/guest/boot/boot.d.ts +8 -1
  55. package/dist/guest/boot/boot.js +80 -43
  56. package/dist/guest/boot/graph.d.ts +9 -5
  57. package/dist/guest/boot/graph.js +8 -2
  58. package/dist/guest/boot/guide.js +24 -0
  59. package/dist/guest/boot/progress-view.d.ts +116 -0
  60. package/dist/guest/boot/progress-view.js +456 -0
  61. package/dist/guest/boot/runner.d.ts +20 -10
  62. package/dist/guest/boot/runner.js +39 -47
  63. package/dist/guest/daemon/daemon.js +3 -0
  64. package/dist/guest/runtime/log-line.d.ts +7 -0
  65. package/dist/guest/runtime/log-line.js +18 -0
  66. package/dist/guest/runtime/log.d.ts +17 -4
  67. package/dist/guest/runtime/log.js +40 -17
  68. package/dist/guest/runtime/process.d.ts +9 -0
  69. package/dist/guest/runtime/process.js +69 -8
  70. package/dist/guest/setup/addon-mcp.d.ts +7 -0
  71. package/dist/guest/setup/addon-mcp.js +19 -4
  72. package/dist/guest/setup/browser-gpu.js +14 -11
  73. package/dist/guest/setup/environment.js +9 -4
  74. package/dist/guest/setup/headroom.js +4 -0
  75. package/dist/guest/setup/hooks.d.ts +2 -0
  76. package/dist/guest/setup/hooks.js +4 -0
  77. package/dist/guest/setup/path-plan.d.ts +12 -0
  78. package/dist/guest/setup/path-plan.js +18 -0
  79. package/dist/guest/toolchains/engines/epic-launcher.d.ts +26 -0
  80. package/dist/guest/toolchains/engines/epic-launcher.js +127 -0
  81. package/dist/guest/toolchains/engines/unreal-engines.d.ts +21 -1
  82. package/dist/guest/toolchains/engines/unreal-engines.js +163 -3
  83. package/dist/guest/toolchains/engines/unreal-status.d.ts +13 -0
  84. package/dist/guest/toolchains/engines/unreal-status.js +41 -0
  85. package/dist/guest/toolchains/native/debuggers.d.ts +6 -0
  86. package/dist/guest/toolchains/native/debuggers.js +57 -0
  87. package/dist/guest/toolchains/native/mesa.js +15 -8
  88. package/dist/guest/toolchains/native/rust-extras.d.ts +7 -0
  89. package/dist/guest/toolchains/native/rust-extras.js +23 -1
  90. package/dist/guest/toolchains/native/unreal.d.ts +2 -2
  91. package/dist/guest/toolchains/native/unreal.js +96 -20
  92. package/dist/guest/toolchains/native/visual-studio.d.ts +14 -14
  93. package/dist/guest/toolchains/native/visual-studio.js +145 -174
  94. package/dist/guest/toolchains/native/vs-persist.d.ts +189 -0
  95. package/dist/guest/toolchains/native/vs-persist.js +256 -0
  96. package/dist/guest/toolchains/native/vs-request.d.ts +6 -26
  97. package/dist/guest/toolchains/native/vs-request.js +16 -50
  98. package/dist/guest/toolchains/plan.js +23 -15
  99. package/dist/guest/toolchains/tools/vscode-profile.js +2 -0
  100. package/dist/host/accounts/kinds/epic.js +11 -5
  101. package/dist/host/accounts/source.d.ts +2 -0
  102. package/dist/host/accounts/source.js +1 -0
  103. package/dist/host/migrate/migrations/drop-vs-install-files.d.ts +5 -0
  104. package/dist/host/migrate/migrations/drop-vs-install-files.js +34 -0
  105. package/dist/host/migrate/registry.js +7 -1
  106. package/dist/host/pc-installs/discover.d.ts +2 -0
  107. package/dist/host/pc-installs/discover.js +25 -5
  108. package/dist/host/pc-installs/epic.d.ts +3 -0
  109. package/dist/host/pc-installs/epic.js +33 -2
  110. package/dist/host/pc-installs/source.d.ts +1 -1
  111. package/dist/host/pc-installs/unreal-vault.d.ts +11 -0
  112. package/dist/host/pc-installs/unreal-vault.js +78 -0
  113. package/dist/host/pc-installs/unreal.d.ts +36 -0
  114. package/dist/host/pc-installs/unreal.js +145 -2
  115. package/dist/host/profile/claude-profile.d.ts +8 -0
  116. package/dist/host/profile/claude-profile.js +43 -4
  117. package/dist/host/profile/index.d.ts +1 -1
  118. package/dist/host/profile/index.js +1 -1
  119. package/dist/runtime/agent.js +148 -33
  120. package/dist/runtime/daemon.js +3146 -1546
  121. package/dist/runtime/db.js +148 -33
  122. package/dist/runtime/main.js +3982 -2049
  123. package/dist/runtime/netproxy.js +84 -13
  124. package/dist/runtime/tool.js +125 -31
  125. package/dist/runtime/view.js +85 -14
  126. package/dist/selftest/agent-paths.test.js +18 -1
  127. package/dist/selftest/agent-settings.test.js +36 -5
  128. package/dist/selftest/agents-web.test.js +12 -0
  129. package/dist/selftest/boot-console.test.d.ts +2 -0
  130. package/dist/selftest/boot-console.test.js +291 -0
  131. package/dist/selftest/boot-runner.test.js +10 -12
  132. package/dist/selftest/engines.test.js +602 -3
  133. package/dist/selftest/guest-boot.test.js +7 -1
  134. package/dist/selftest/heavy-load.test.js +3 -1
  135. package/dist/selftest/hostile-input.test.js +11 -1
  136. package/dist/selftest/index.js +4 -0
  137. package/dist/selftest/layout.test.js +4 -0
  138. package/dist/selftest/menu-screens.js +3 -1
  139. package/dist/selftest/migrate.test.js +21 -0
  140. package/dist/selftest/mixamo.test.d.ts +2 -0
  141. package/dist/selftest/mixamo.test.js +60 -0
  142. package/dist/selftest/persisted-installs.test.js +169 -33
  143. package/dist/selftest/secrets.test.js +36 -1
  144. package/dist/selftest/support/engine-golden.js +5 -0
  145. package/dist/selftest/support/fixtures.d.ts +4 -3
  146. package/dist/selftest/support/unreal-sample.d.ts +2 -0
  147. package/dist/selftest/support/unreal-sample.js +83 -0
  148. package/dist/selftest/toolchains.test.js +32 -2
  149. package/dist/shared/accounts/index.d.ts +1 -1
  150. package/dist/shared/accounts/index.js +1 -1
  151. package/dist/shared/accounts/kinds/epic.d.ts +6 -1
  152. package/dist/shared/accounts/kinds/epic.js +11 -1
  153. package/dist/shared/apps/associations.d.ts +7 -0
  154. package/dist/shared/apps/associations.js +26 -0
  155. package/dist/shared/apps/catalogue-agents-web.d.ts +4 -1
  156. package/dist/shared/apps/catalogue-agents-web.js +5 -2
  157. package/dist/shared/apps/catalogue-gamedev.js +71 -1
  158. package/dist/shared/apps/index.d.ts +2 -0
  159. package/dist/shared/apps/index.js +2 -0
  160. package/dist/shared/apps/preview.d.ts +2 -0
  161. package/dist/shared/apps/preview.js +10 -0
  162. package/dist/shared/browser-policy.d.ts +14 -0
  163. package/dist/shared/browser-policy.js +22 -0
  164. package/dist/shared/channel/schema.d.ts +3 -6
  165. package/dist/shared/io/index.d.ts +1 -0
  166. package/dist/shared/io/index.js +1 -0
  167. package/dist/shared/io/rotate-log.d.ts +2 -0
  168. package/dist/shared/io/rotate-log.js +16 -0
  169. package/dist/shared/memory/policy.d.ts +5 -0
  170. package/dist/shared/memory/policy.js +7 -0
  171. package/dist/shared/paths/pc-installs.d.ts +13 -2
  172. package/dist/shared/paths/pc-installs.js +1 -0
  173. package/dist/shared/pty/terminal.js +2 -1
  174. package/dist/shared/readiness.d.ts +0 -1
  175. package/dist/shared/readiness.js +0 -1
  176. package/dist/shared/signer.d.ts +4 -0
  177. package/dist/shared/signer.js +2 -0
  178. package/dist/shared/unreal/engines.d.ts +161 -0
  179. package/dist/shared/unreal/engines.js +308 -0
  180. package/dist/shared/unreal/guide.d.ts +19 -0
  181. package/dist/shared/unreal/guide.js +105 -0
  182. package/dist/shared/unreal/index.d.ts +7 -0
  183. package/dist/shared/unreal/index.js +8 -0
  184. package/dist/shared/unreal/launcher-files.d.ts +88 -0
  185. package/dist/shared/unreal/launcher-files.js +292 -0
  186. package/dist/shared/unreal/mixamo.d.ts +5 -0
  187. package/dist/shared/unreal/mixamo.js +16 -0
  188. package/dist/shared/unreal/room.d.ts +17 -0
  189. package/dist/shared/unreal/room.js +34 -0
  190. package/dist/shared/unreal/status.d.ts +54 -0
  191. package/dist/shared/unreal/status.js +57 -0
  192. package/dist/shared/unreal/vault.d.ts +21 -0
  193. package/dist/shared/unreal/vault.js +67 -0
  194. package/dist/shared/unreal/vs-check.d.ts +39 -0
  195. package/dist/shared/unreal/vs-check.js +79 -0
  196. package/dist/sync/watcher.js +5 -1
  197. package/dist/web/index.html +1 -1
  198. package/dist/web/{main-CCILLX2M.js → main-FNE6XEHA.js} +84 -84
  199. package/native/csharp/HcsHost/AssemblyInfo.cs +3 -3
  200. package/native/csharp/HostTools/AssemblyInfo.cs +3 -3
  201. package/native/csharp/StorageHelper/AssemblyInfo.cs +3 -3
  202. package/package.json +1 -1
  203. package/sandbox/csharp/VsSetup.cs +210 -0
  204. package/sandbox/scripts/get-vs-setup.ps1 +93 -0
  205. package/sandbox/scripts/restore-visual-studio.ps1 +78 -0
  206. package/sandbox/scripts/save-visual-studio.ps1 +106 -0
  207. package/sandbox/scripts/set-registry-values.ps1 +35 -0
  208. package/schemas/config.schema.json +38 -9
  209. package/schemas/sandbox-config.schema.json +11 -8
  210. package/dist/guest/toolchains/native/autosdk.d.ts +0 -3
  211. package/dist/guest/toolchains/native/autosdk.js +0 -50
@@ -0,0 +1,189 @@
1
+ import { z } from 'zod';
2
+ import type { VsRequest } from './vs-request.js';
3
+ /** The marker file in the saved copy's folder, written last. */
4
+ export declare const MARKER_FILE = ".complete.json";
5
+ /** The Setup Configuration COM class (vswhere and UnrealBuildTool find Visual Studio through it). */
6
+ export declare const SETUP_CLSID = "{177F0C4A-1CD3-4DE7-A32C-71DBBB9FA36D}";
7
+ /** The Windows folders an install uses (from the environment; the tests pass their own). */
8
+ export interface WinPlaces {
9
+ readonly programFiles: string;
10
+ readonly programFilesX86: string;
11
+ readonly programData: string;
12
+ }
13
+ export declare function winPlaces(env: NodeJS.ProcessEnv): WinPlaces;
14
+ export declare const FOLDER_NAMES: readonly ["Community", "BuildTools", "WindowsKits10", "NETFXSDK", "MicrosoftSDKs-v10.0A", "ReferenceAssemblies", "Installer", "Shared", "Setup", "instance"];
15
+ export type FolderName = (typeof FOLDER_NAMES)[number];
16
+ /**
17
+ * What a boot does with a saved folder: 'stay' on the data disk (registered there), 'copy' back to where it came from
18
+ * (programs look for it at a fixed path), or 'copy-missing': only the versions not there yet (the reference assemblies,
19
+ * where the .NET step may have put some).
20
+ */
21
+ export type RestoreMode = 'stay' | 'copy' | 'copy-missing';
22
+ export interface PersistFolder {
23
+ readonly name: FolderName;
24
+ /** Where the install put it. */
25
+ readonly source: string;
26
+ readonly restore: RestoreMode;
27
+ /** Not every install has it (the Build Tools without .NET parts): skipped when missing. */
28
+ readonly optional: boolean;
29
+ }
30
+ export declare function productFolder(product: VsRequest['product']): 'Community' | 'BuildTools';
31
+ /** The instances folder of the Visual Studio installer (one subfolder per instance ID). */
32
+ export declare function instancesDir(places: WinPlaces): string;
33
+ /** Every folder a Visual Studio install spreads over, and what a boot does with each. */
34
+ export declare function persistFolders(vs: {
35
+ readonly product: VsRequest['product'];
36
+ readonly installationPath: string;
37
+ readonly instanceId: string;
38
+ }, places: WinPlaces): PersistFolder[];
39
+ export declare const RegEntrySchema: z.ZodObject<{
40
+ view: z.ZodEnum<{
41
+ 32: "32";
42
+ 64: "64";
43
+ }>;
44
+ key: z.ZodString;
45
+ name: z.ZodString;
46
+ kind: z.ZodEnum<{
47
+ string: "string";
48
+ expand: "expand";
49
+ dword: "dword";
50
+ }>;
51
+ value: z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>;
52
+ }, z.core.$strict>;
53
+ export type RegEntry = z.infer<typeof RegEntrySchema>;
54
+ export interface RegRead {
55
+ readonly view: '64' | '32';
56
+ readonly key: string;
57
+ /** Only these values (null: every string and DWORD value). */
58
+ readonly names: readonly string[] | null;
59
+ readonly recurse: boolean;
60
+ }
61
+ /** The registry values that point at the saved folders, read in both views at capture time. */
62
+ export declare const REGISTRY_READS: readonly Omit<RegRead, 'view'>[];
63
+ export declare const VsPersistMarkerSchema: z.ZodObject<{
64
+ product: z.ZodEnum<{
65
+ buildtools: "buildtools";
66
+ community: "community";
67
+ }>;
68
+ version: z.ZodString;
69
+ components: z.ZodArray<z.ZodString>;
70
+ instanceId: z.ZodString;
71
+ installationPath: z.ZodString;
72
+ sdkVersion: z.ZodString;
73
+ folders: z.ZodArray<z.ZodEnum<{
74
+ Community: "Community";
75
+ BuildTools: "BuildTools";
76
+ WindowsKits10: "WindowsKits10";
77
+ NETFXSDK: "NETFXSDK";
78
+ "MicrosoftSDKs-v10.0A": "MicrosoftSDKs-v10.0A";
79
+ ReferenceAssemblies: "ReferenceAssemblies";
80
+ Installer: "Installer";
81
+ Shared: "Shared";
82
+ Setup: "Setup";
83
+ instance: "instance";
84
+ }>>;
85
+ registry: z.ZodArray<z.ZodObject<{
86
+ view: z.ZodEnum<{
87
+ 32: "32";
88
+ 64: "64";
89
+ }>;
90
+ key: z.ZodString;
91
+ name: z.ZodString;
92
+ kind: z.ZodEnum<{
93
+ string: "string";
94
+ expand: "expand";
95
+ dword: "dword";
96
+ }>;
97
+ value: z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>;
98
+ }, z.core.$strict>>;
99
+ capturedAt: z.ZodString;
100
+ }, z.core.$strict>;
101
+ export type VsPersistMarker = z.infer<typeof VsPersistMarkerSchema>;
102
+ /** What save-visual-studio.ps1 writes to its result file. */
103
+ export declare const SaveResultSchema: z.ZodObject<{
104
+ copied: z.ZodArray<z.ZodEnum<{
105
+ Community: "Community";
106
+ BuildTools: "BuildTools";
107
+ WindowsKits10: "WindowsKits10";
108
+ NETFXSDK: "NETFXSDK";
109
+ "MicrosoftSDKs-v10.0A": "MicrosoftSDKs-v10.0A";
110
+ ReferenceAssemblies: "ReferenceAssemblies";
111
+ Installer: "Installer";
112
+ Shared: "Shared";
113
+ Setup: "Setup";
114
+ instance: "instance";
115
+ }>>;
116
+ registry: z.ZodArray<z.ZodObject<{
117
+ view: z.ZodEnum<{
118
+ 32: "32";
119
+ 64: "64";
120
+ }>;
121
+ key: z.ZodString;
122
+ name: z.ZodString;
123
+ kind: z.ZodEnum<{
124
+ string: "string";
125
+ expand: "expand";
126
+ dword: "dword";
127
+ }>;
128
+ value: z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>;
129
+ }, z.core.$strict>>;
130
+ }, z.core.$strict>;
131
+ export type SaveResult = z.infer<typeof SaveResultSchema>;
132
+ /**
133
+ * Whether the saved copy serves this request: the same product with every component, or Community for a Build Tools
134
+ * request (it holds the same C++ parts; only the workload IDs differ).
135
+ */
136
+ export declare function persistCovers(marker: VsPersistMarker, req: Pick<VsRequest, 'product' | 'components'>): boolean;
137
+ /**
138
+ * The instance's state.json with every mention of the install folder (JSON-escaped, any case) moved to `to`. A longer
139
+ * folder name that only starts the same (Community2) is left alone.
140
+ */
141
+ export declare function rewriteInstanceState(text: string, from: string, to: string): string;
142
+ /** `value` with a saved folder's old place swapped for its place on the data disk (case-insensitive prefix). */
143
+ export declare function rebase(value: string, moves: readonly {
144
+ from: string;
145
+ to: string;
146
+ }[]): string;
147
+ /** What the boot writes under HKLM: the saved values (paths moved to the data disk) in both views, the COM class. */
148
+ export declare function registryPlan(marker: VsPersistMarker, root: string, places: WinPlaces): RegEntry[];
149
+ /**
150
+ * The newest of the instances vswhere lists. Chosen here, not with vswhere's -latest, which fails outright (0x8000FFFF)
151
+ * when two instances have the same install date, as a copy of an instance does.
152
+ */
153
+ export declare function newestInstance<T extends {
154
+ readonly installationVersion?: string;
155
+ }>(instances: readonly T[]): T | null;
156
+ export interface SaveCopy {
157
+ readonly name: FolderName;
158
+ readonly from: string;
159
+ readonly to: string;
160
+ readonly optional: boolean;
161
+ }
162
+ /** The input of save-visual-studio.ps1: the folders to mirror onto the data disk and the registry values to read. */
163
+ export declare function saveInput(vs: {
164
+ readonly product: VsRequest['product'];
165
+ readonly installationPath: string;
166
+ readonly instanceId: string;
167
+ }, root: string, places: WinPlaces, resultFile: string): {
168
+ copies: SaveCopy[];
169
+ registry: RegRead[];
170
+ resultFile: string;
171
+ };
172
+ /** The marker for a finished capture (written after the instance's state is rewritten). */
173
+ export declare function markerFrom(req: Pick<VsRequest, 'product' | 'components'>, vs: {
174
+ readonly installationPath: string;
175
+ readonly instanceId: string;
176
+ readonly version: string;
177
+ }, result: SaveResult, capturedAt: string): VsPersistMarker;
178
+ export interface RestoreCopy {
179
+ readonly name: FolderName;
180
+ readonly from: string;
181
+ readonly to: string;
182
+ /** 'all': what differs is copied; 'missing': each subfolder only when the target has none. */
183
+ readonly mode: 'all' | 'missing';
184
+ }
185
+ /** The input of restore-visual-studio.ps1: the folders copied back to their fixed places, then the registry values. */
186
+ export declare function restoreInput(marker: VsPersistMarker, root: string, places: WinPlaces): {
187
+ copies: RestoreCopy[];
188
+ registry: RegEntry[];
189
+ };
@@ -0,0 +1,256 @@
1
+ // Visual Studio kept on the data disk (visual-studio.ts runs it): what is saved once after the real install, the marker
2
+ // that says the saved copy is complete, and what a later boot puts back (a few copies and registry values) so the Setup
3
+ // API, vswhere and vcvars find it again in seconds. The big folders (Visual Studio, the Windows SDK) stay on the data
4
+ // disk and are registered where they are; the small ones that programs look for at fixed paths are copied back. Pure:
5
+ // no runtime, so the self-test loads it.
6
+ import path from 'node:path';
7
+ import { z } from 'zod';
8
+ const win = path.win32;
9
+ /** The marker file in the saved copy's folder, written last. */
10
+ export const MARKER_FILE = '.complete.json';
11
+ /** The Setup Configuration COM class (vswhere and UnrealBuildTool find Visual Studio through it). */
12
+ export const SETUP_CLSID = '{177F0C4A-1CD3-4DE7-A32C-71DBBB9FA36D}';
13
+ const SETUP_DLL = 'Microsoft.VisualStudio.Setup.Configuration.Native.dll';
14
+ /** A Visual Studio instance ID (8 hex digits in practice); never a path. */
15
+ const INSTANCE_ID = /^[A-Za-z0-9]{1,64}$/;
16
+ export function winPlaces(env) {
17
+ return {
18
+ programFiles: env.ProgramFiles ?? 'C:\\Program Files',
19
+ programFilesX86: env['ProgramFiles(x86)'] ?? 'C:\\Program Files (x86)',
20
+ programData: env.ProgramData ?? 'C:\\ProgramData',
21
+ };
22
+ }
23
+ export const FOLDER_NAMES = [
24
+ 'Community',
25
+ 'BuildTools',
26
+ 'WindowsKits10',
27
+ 'NETFXSDK',
28
+ 'MicrosoftSDKs-v10.0A',
29
+ 'ReferenceAssemblies',
30
+ 'Installer',
31
+ 'Shared',
32
+ 'Setup',
33
+ 'instance',
34
+ ];
35
+ export function productFolder(product) {
36
+ return product === 'community' ? 'Community' : 'BuildTools';
37
+ }
38
+ /** The instances folder of the Visual Studio installer (one subfolder per instance ID). */
39
+ export function instancesDir(places) {
40
+ return win.join(places.programData, 'Microsoft', 'VisualStudio', 'Packages', '_Instances');
41
+ }
42
+ /** Every folder a Visual Studio install spreads over, and what a boot does with each. */
43
+ export function persistFolders(vs, places) {
44
+ if (!INSTANCE_ID.test(vs.instanceId))
45
+ throw new Error('"' + vs.instanceId + '" is not a Visual Studio instance ID');
46
+ const x86 = places.programFilesX86;
47
+ const one = (name, source, restore, optional = false) => ({
48
+ name,
49
+ source,
50
+ restore,
51
+ optional,
52
+ });
53
+ return [
54
+ one(productFolder(vs.product), vs.installationPath, 'stay'),
55
+ one('WindowsKits10', win.join(x86, 'Windows Kits', '10'), 'stay'),
56
+ one('NETFXSDK', win.join(x86, 'Windows Kits', 'NETFXSDK'), 'stay', true),
57
+ one('MicrosoftSDKs-v10.0A', win.join(x86, 'Microsoft SDKs', 'Windows', 'v10.0A'), 'stay', true),
58
+ // MSBuild looks for the .NET Framework reference assemblies at this fixed path.
59
+ one('ReferenceAssemblies', win.join(x86, 'Reference Assemblies', 'Microsoft', 'Framework', '.NETFramework'), 'copy-missing', true),
60
+ // Scripts and build tools look for vswhere here; the instance's state names setup.exe here too (144 MB).
61
+ one('Installer', win.join(x86, 'Microsoft Visual Studio', 'Installer'), 'copy'),
62
+ one('Shared', win.join(x86, 'Microsoft Visual Studio', 'Shared'), 'copy', true),
63
+ one('Setup', win.join(places.programData, 'Microsoft', 'VisualStudio', 'Setup'), 'copy'),
64
+ one('instance', win.join(instancesDir(places), vs.instanceId), 'copy'),
65
+ ];
66
+ }
67
+ export const RegEntrySchema = z.strictObject({
68
+ view: z.enum(['64', '32']),
69
+ /** Under HKLM, e.g. SOFTWARE\Microsoft\Microsoft SDKs\Windows\v10.0 (the view picks WOW6432Node). */
70
+ key: z.string().min(1).max(400),
71
+ /** '' is the default value. */
72
+ name: z.string().max(200),
73
+ kind: z.enum(['string', 'expand', 'dword']),
74
+ value: z.union([z.string().max(2000), z.number().int()]),
75
+ });
76
+ /** The registry values that point at the saved folders, read in both views at capture time. */
77
+ export const REGISTRY_READS = [
78
+ { key: 'SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots', names: ['KitsRoot10'], recurse: false },
79
+ { key: 'SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows\\v10.0', names: null, recurse: false },
80
+ { key: 'SOFTWARE\\Microsoft\\Microsoft SDKs\\NETFXSDK', names: null, recurse: true },
81
+ { key: 'SOFTWARE\\Microsoft\\VisualStudio\\Setup', names: null, recurse: false },
82
+ ];
83
+ const VIEWS = ['64', '32'];
84
+ export const VsPersistMarkerSchema = z.strictObject({
85
+ product: z.enum(['community', 'buildtools']),
86
+ /** The installed version (17.14.37827.9). */
87
+ version: z.string().min(1).max(100),
88
+ /** The component IDs it was installed with. */
89
+ components: z.array(z.string().max(200)).max(500),
90
+ instanceId: z.string().regex(INSTANCE_ID),
91
+ /** Where the installer put it (the paths in its state and the registry are rewritten from this). */
92
+ installationPath: z.string().min(3).max(400),
93
+ /** The Windows SDK's ProductVersion (10.0.22621), '' when there is none. */
94
+ sdkVersion: z.string().max(50),
95
+ folders: z.array(z.enum(FOLDER_NAMES)).max(FOLDER_NAMES.length),
96
+ /** The registry values as the installer wrote them (paths on C:). */
97
+ registry: z.array(RegEntrySchema).max(500),
98
+ capturedAt: z.string().max(50),
99
+ });
100
+ /** What save-visual-studio.ps1 writes to its result file. */
101
+ export const SaveResultSchema = z.strictObject({
102
+ copied: z.array(z.enum(FOLDER_NAMES)).max(FOLDER_NAMES.length),
103
+ registry: z.array(RegEntrySchema).max(500),
104
+ });
105
+ /** Without these a saved copy is not usable (the product, its instance, the Setup API, vswhere, the SDK). */
106
+ const needed = (product) => [
107
+ productFolder(product),
108
+ 'instance',
109
+ 'Setup',
110
+ 'Installer',
111
+ 'WindowsKits10',
112
+ ];
113
+ /**
114
+ * Whether the saved copy serves this request: the same product with every component, or Community for a Build Tools
115
+ * request (it holds the same C++ parts; only the workload IDs differ).
116
+ */
117
+ export function persistCovers(marker, req) {
118
+ const same = marker.product === req.product;
119
+ if (!same && !(marker.product === 'community' && req.product === 'buildtools'))
120
+ return false;
121
+ if (!needed(marker.product).every((f) => marker.folders.includes(f)))
122
+ return false;
123
+ const have = new Set(marker.components.map((c) => c.toLowerCase()));
124
+ return req.components.filter((id) => same || !/\.workload\./i.test(id)).every((id) => have.has(id.toLowerCase()));
125
+ }
126
+ /** A string as it appears inside a JSON string (backslashes doubled). */
127
+ const jsonEscaped = (s) => JSON.stringify(s).slice(1, -1);
128
+ /**
129
+ * The instance's state.json with every mention of the install folder (JSON-escaped, any case) moved to `to`. A longer
130
+ * folder name that only starts the same (Community2) is left alone.
131
+ */
132
+ export function rewriteInstanceState(text, from, to) {
133
+ const needle = jsonEscaped(win.normalize(from).replace(/\\+$/, ''));
134
+ const replacement = jsonEscaped(win.normalize(to).replace(/\\+$/, ''));
135
+ const lower = text.toLowerCase();
136
+ const find = needle.toLowerCase();
137
+ let out = '';
138
+ let at = 0;
139
+ for (let i = lower.indexOf(find); i !== -1; i = lower.indexOf(find, i + find.length)) {
140
+ const next = text.slice(i + find.length, i + find.length + 2);
141
+ if (next !== '\\\\' && !next.startsWith('"'))
142
+ continue;
143
+ out += text.slice(at, i) + replacement;
144
+ at = i + find.length;
145
+ }
146
+ return out + text.slice(at);
147
+ }
148
+ /** `value` with a saved folder's old place swapped for its place on the data disk (case-insensitive prefix). */
149
+ export function rebase(value, moves) {
150
+ for (const move of moves) {
151
+ const from = move.from.replace(/\\+$/, '');
152
+ const lower = value.toLowerCase();
153
+ if (lower === from.toLowerCase() || lower.startsWith(from.toLowerCase() + '\\'))
154
+ return move.to.replace(/\\+$/, '') + value.slice(from.length);
155
+ }
156
+ return value;
157
+ }
158
+ /** Whether a registry value from the marker is one the capture reads (a tampered marker writes nothing else). */
159
+ const allowed = (entry) => REGISTRY_READS.some((read) => {
160
+ const key = entry.key.toLowerCase();
161
+ const root = read.key.toLowerCase();
162
+ const under = key === root || (read.recurse && key.startsWith(root + '\\'));
163
+ return under && (read.names === null || read.names.some((n) => n.toLowerCase() === entry.name.toLowerCase()));
164
+ });
165
+ /** What the boot writes under HKLM: the saved values (paths moved to the data disk) in both views, the COM class. */
166
+ export function registryPlan(marker, root, places) {
167
+ const folders = persistFolders(marker, places).filter((f) => marker.folders.includes(f.name));
168
+ const moves = folders
169
+ .filter((f) => f.restore === 'stay')
170
+ .map((f) => ({ from: f.source, to: win.join(root, f.name) }));
171
+ const values = new Map();
172
+ const put = (e) => {
173
+ values.set(e.key.toLowerCase() + '|' + e.name.toLowerCase(), e);
174
+ };
175
+ for (const e of marker.registry.filter(allowed))
176
+ put({
177
+ key: e.key,
178
+ name: e.name,
179
+ kind: e.kind,
180
+ value: typeof e.value === 'string' ? rebase(e.value, moves) : e.value,
181
+ });
182
+ if (marker.folders.includes('WindowsKits10')) {
183
+ const kits = win.join(root, 'WindowsKits10') + '\\';
184
+ const sdk = 'SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows\\v10.0';
185
+ put({ key: 'SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots', name: 'KitsRoot10', kind: 'string', value: kits });
186
+ put({ key: sdk, name: 'InstallationFolder', kind: 'string', value: kits });
187
+ if (marker.sdkVersion)
188
+ put({ key: sdk, name: 'ProductVersion', kind: 'string', value: marker.sdkVersion });
189
+ }
190
+ const both = [...values.values()].flatMap((e) => VIEWS.map((view) => ({ view, ...e })));
191
+ const setup = win.join(places.programData, 'Microsoft', 'VisualStudio', 'Setup');
192
+ const com = (view, arch) => {
193
+ const key = 'SOFTWARE\\Classes\\CLSID\\' + SETUP_CLSID + '\\InprocServer32';
194
+ return [
195
+ { view, key, name: '', kind: 'string', value: win.join(setup, arch, SETUP_DLL) },
196
+ { view, key, name: 'ThreadingModel', kind: 'string', value: 'Both' },
197
+ ];
198
+ };
199
+ return [...both, ...com('64', 'x64'), ...com('32', 'x86')];
200
+ }
201
+ /** Compares dotted versions (17.14.37827.9) part by part, as numbers. */
202
+ function compareVersions(a, b) {
203
+ const x = a.split('.').map((p) => parseInt(p, 10) || 0);
204
+ const y = b.split('.').map((p) => parseInt(p, 10) || 0);
205
+ for (let i = 0; i < Math.max(x.length, y.length); i++)
206
+ if ((x[i] ?? 0) !== (y[i] ?? 0))
207
+ return (x[i] ?? 0) - (y[i] ?? 0);
208
+ return 0;
209
+ }
210
+ /**
211
+ * The newest of the instances vswhere lists. Chosen here, not with vswhere's -latest, which fails outright (0x8000FFFF)
212
+ * when two instances have the same install date, as a copy of an instance does.
213
+ */
214
+ export function newestInstance(instances) {
215
+ return ([...instances].sort((a, b) => compareVersions(b.installationVersion ?? '', a.installationVersion ?? ''))[0] ?? null);
216
+ }
217
+ /** The input of save-visual-studio.ps1: the folders to mirror onto the data disk and the registry values to read. */
218
+ export function saveInput(vs, root, places, resultFile) {
219
+ return {
220
+ copies: persistFolders(vs, places).map((f) => ({
221
+ name: f.name,
222
+ from: f.source,
223
+ to: win.join(root, f.name),
224
+ optional: f.optional,
225
+ })),
226
+ registry: VIEWS.flatMap((view) => REGISTRY_READS.map((r) => ({ view, ...r }))),
227
+ resultFile,
228
+ };
229
+ }
230
+ /** The marker for a finished capture (written after the instance's state is rewritten). */
231
+ export function markerFrom(req, vs, result, capturedAt) {
232
+ const sdk = result.registry.find((e) => /\\Microsoft SDKs\\Windows\\v10\.0$/i.test(e.key) && e.name.toLowerCase() === 'productversion');
233
+ return {
234
+ product: req.product,
235
+ version: vs.version,
236
+ components: [...req.components],
237
+ instanceId: vs.instanceId,
238
+ installationPath: vs.installationPath,
239
+ sdkVersion: typeof sdk?.value === 'string' ? sdk.value : '',
240
+ folders: [...result.copied],
241
+ registry: [...result.registry],
242
+ capturedAt,
243
+ };
244
+ }
245
+ /** The input of restore-visual-studio.ps1: the folders copied back to their fixed places, then the registry values. */
246
+ export function restoreInput(marker, root, places) {
247
+ const copies = persistFolders(marker, places)
248
+ .filter((f) => f.restore !== 'stay' && marker.folders.includes(f.name))
249
+ .map((f) => ({
250
+ name: f.name,
251
+ from: win.join(root, f.name),
252
+ to: f.source,
253
+ mode: f.restore === 'copy-missing' ? 'missing' : 'all',
254
+ }));
255
+ return { copies, registry: registryPlan(marker, root, places) };
256
+ }
@@ -1,12 +1,17 @@
1
1
  import type { Toolchains } from '../../../core/types.js';
2
2
  /**
3
3
  * What Epic lists for Unreal Engine 5 with Visual Studio 2022: the game workload, MSVC v143, a Windows SDK of
4
- * 10.0.18362 or newer, the C++ profiling tools, the .NET Framework targeting packs some engine tools build against,
4
+ * 10.0.22621 or newer, the C++ profiling tools, the .NET Framework targeting packs some engine tools build against,
5
5
  * Visual Studio's Unreal support and the Unreal Engine installer (the Epic Games Launcher).
6
6
  */
7
7
  export declare const UNREAL_COMPONENTS: readonly ["Microsoft.VisualStudio.Workload.NativeGame", "Microsoft.VisualStudio.Component.VC.Tools.x86.x64", "Microsoft.VisualStudio.Component.Windows11SDK.22621", "Microsoft.VisualStudio.Component.VC.DiagnosticTools", "Microsoft.Net.Component.4.6.2.TargetingPack", "Microsoft.Net.Component.4.8.SDK", "Component.Unreal.Ide", "Component.Unreal"];
8
8
  /** The Build Tools' C++ desktop parts: MSVC v143 and a Windows 11 SDK (Flutter desktop, Unity IL2CPP). */
9
9
  export declare const BUILD_TOOLS_COMPONENTS: readonly ["Microsoft.VisualStudio.Workload.VCTools", "Microsoft.VisualStudio.Component.VC.Tools.x86.x64", "Microsoft.VisualStudio.Component.Windows11SDK.22621"];
10
+ /**
11
+ * Unreal on the Build Tools: what UnrealBuildTool needs (it finds them through the Setup API like Visual Studio):
12
+ * MSVC v143, the Windows SDK and the .NET Framework parts its tools build against. No IDE and no debugger.
13
+ */
14
+ export declare const UNREAL_BUILD_TOOLS_COMPONENTS: readonly ["Microsoft.VisualStudio.Workload.VCTools", "Microsoft.VisualStudio.Component.VC.Tools.x86.x64", "Microsoft.VisualStudio.Component.Windows11SDK.22621", "Microsoft.Net.Component.4.6.2.TargetingPack", "Microsoft.Net.Component.4.8.SDK"];
10
15
  export interface VsRequest {
11
16
  readonly product: 'community' | 'buildtools';
12
17
  readonly components: readonly string[];
@@ -17,30 +22,5 @@ export interface VsRequest {
17
22
  readonly accepted: boolean;
18
23
  }
19
24
  export declare function visualStudioWanted(t: Toolchains, on?: (setting: boolean | 'auto', name: string) => boolean): VsRequest | null;
20
- /** Whether Unreal is on and builds from the portable MSVC through AutoSDK instead of Visual Studio. */
21
- export declare function unrealUsesAutosdk(t: Toolchains, on?: (setting: boolean | 'auto', name: string) => boolean): boolean;
22
25
  /** The Visual Studio installer's exit code in plain words (it is a uint32: -1 and 4294967295 are the same). */
23
26
  export declare function vsExitText(code: number): string;
24
- /**
25
- * Room the staging copy of the layout needs on the sandbox's own disk, on top of the reserve. The layout of the Unreal
26
- * components is about the size of their download (5-8 GB); the sandbox's ~40 GB disk has about 14 GB free at boot
27
- * (a fixed paging file takes 16 GB), so the estimate must stay under that. A layout that outgrows it fails cleanly.
28
- */
29
- export declare const LAYOUT_STAGING_BYTES: number;
30
- /** Spare room on the sandbox's disk while the layout is built there (before Visual Studio is installed). */
31
- export declare const STAGING_RESERVE_BYTES: number;
32
- /**
33
- * What to do after the layout attempt in the data disk: 'done' when it worked, 'stage' (build it on the sandbox's
34
- * own disk and copy it over) when it failed and that disk has room, 'skip' (download this boot) otherwise.
35
- */
36
- export declare function layoutNextStep(exit: number | null, localFreeBytes: number, reserveBytes: number): 'done' | 'stage' | 'skip';
37
- export interface CopyPair {
38
- readonly from: string;
39
- readonly to: string;
40
- }
41
- /**
42
- * The copies that make an Unreal AutoSDK tree from the portable MSVC (copies, not links: mapped folders refuse
43
- * junctions): MSVC's tools into HostWin64\Win64\VS2022\<msvc> and the Windows SDK's Include, Lib and bin folders into
44
- * HostWin64\Win64\Windows Kits\10. Pure.
45
- */
46
- export declare function autosdkLayout(msvcRoot: string, msvc: string, sdk: string, autosdkRoot: string): CopyPair[];
@@ -1,11 +1,6 @@
1
- // Which Visual Studio this sandbox installs (a real installer: it runs every boot), from every toolchain that needs
2
- // one: Unreal (Community with the C++ game workload), Flutter's Windows desktop builds and Unity's Windows IL2CPP
3
- // builds (the Build Tools' C++ parts). Pure: no runtime, so the self-test loads it. Community holds everything the
4
- // Build Tools would add, so one install serves all of them.
5
- import path from 'node:path';
6
1
  /**
7
2
  * What Epic lists for Unreal Engine 5 with Visual Studio 2022: the game workload, MSVC v143, a Windows SDK of
8
- * 10.0.18362 or newer, the C++ profiling tools, the .NET Framework targeting packs some engine tools build against,
3
+ * 10.0.22621 or newer, the C++ profiling tools, the .NET Framework targeting packs some engine tools build against,
9
4
  * Visual Studio's Unreal support and the Unreal Engine installer (the Epic Games Launcher).
10
5
  */
11
6
  export const UNREAL_COMPONENTS = [
@@ -24,24 +19,34 @@ export const BUILD_TOOLS_COMPONENTS = [
24
19
  'Microsoft.VisualStudio.Component.VC.Tools.x86.x64',
25
20
  'Microsoft.VisualStudio.Component.Windows11SDK.22621',
26
21
  ];
22
+ /**
23
+ * Unreal on the Build Tools: what UnrealBuildTool needs (it finds them through the Setup API like Visual Studio):
24
+ * MSVC v143, the Windows SDK and the .NET Framework parts its tools build against. No IDE and no debugger.
25
+ */
26
+ export const UNREAL_BUILD_TOOLS_COMPONENTS = [
27
+ ...BUILD_TOOLS_COMPONENTS,
28
+ 'Microsoft.Net.Component.4.6.2.TargetingPack',
29
+ 'Microsoft.Net.Component.4.8.SDK',
30
+ ];
27
31
  /** Whether a toolchain is on (the host resolved "auto"; a session file that still says "auto" counts as off). */
28
32
  const isOn = (setting) => setting === true;
29
33
  export function visualStudioWanted(t, on = isOn) {
30
- // Unreal with visualStudio "autosdk" needs no Visual Studio: it builds from the portable MSVC (autosdk.ts).
31
- const unreal = on(t.unreal.enabled, 'unreal') && t.unreal.visualStudio === 'full';
34
+ const unreal = on(t.unreal.enabled, 'unreal');
32
35
  const flutter = on(t.flutter.enabled, 'flutter') && t.flutter.windowsDesktop;
33
36
  const unity = on(t.unity.enabled, 'unity') && t.unity.modules.includes('windows-il2cpp');
34
37
  const users = [...(unreal ? ['unreal'] : []), ...(flutter ? ['flutter'] : []), ...(unity ? ['unity'] : [])];
35
38
  if (!users.length)
36
39
  return null;
37
- if (unreal)
40
+ if (unreal) {
41
+ const full = t.unreal.visualStudio === 'full';
38
42
  return {
39
- product: 'community',
40
- components: [...new Set([...UNREAL_COMPONENTS, ...t.unreal.components])],
43
+ product: full ? 'community' : 'buildtools',
44
+ components: [...new Set([...(full ? UNREAL_COMPONENTS : UNREAL_BUILD_TOOLS_COMPONENTS), ...t.unreal.components])],
41
45
  for: users,
42
46
  licence: 'toolchains.unreal.acceptLicense',
43
47
  accepted: t.unreal.acceptLicense,
44
48
  };
49
+ }
45
50
  return {
46
51
  product: 'buildtools',
47
52
  components: [...BUILD_TOOLS_COMPONENTS],
@@ -50,10 +55,6 @@ export function visualStudioWanted(t, on = isOn) {
50
55
  accepted: t.msvc.acceptLicense,
51
56
  };
52
57
  }
53
- /** Whether Unreal is on and builds from the portable MSVC through AutoSDK instead of Visual Studio. */
54
- export function unrealUsesAutosdk(t, on = isOn) {
55
- return on(t.unreal.enabled, 'unreal') && t.unreal.visualStudio === 'autosdk';
56
- }
57
58
  /** The Visual Studio installer's exit code in plain words (it is a uint32: -1 and 4294967295 are the same). */
58
59
  export function vsExitText(code) {
59
60
  const c = code >>> 0;
@@ -74,38 +75,3 @@ export function vsExitText(code) {
74
75
  return 'the installer failed with exit ' + c + ' (0x' + c.toString(16).toUpperCase() + ')';
75
76
  }
76
77
  }
77
- /**
78
- * Room the staging copy of the layout needs on the sandbox's own disk, on top of the reserve. The layout of the Unreal
79
- * components is about the size of their download (5-8 GB); the sandbox's ~40 GB disk has about 14 GB free at boot
80
- * (a fixed paging file takes 16 GB), so the estimate must stay under that. A layout that outgrows it fails cleanly.
81
- */
82
- export const LAYOUT_STAGING_BYTES = 8 * 1024 ** 3;
83
- /** Spare room on the sandbox's disk while the layout is built there (before Visual Studio is installed). */
84
- export const STAGING_RESERVE_BYTES = 1.5 * 1024 ** 3;
85
- /**
86
- * What to do after the layout attempt in the data disk: 'done' when it worked, 'stage' (build it on the sandbox's
87
- * own disk and copy it over) when it failed and that disk has room, 'skip' (download this boot) otherwise.
88
- */
89
- export function layoutNextStep(exit, localFreeBytes, reserveBytes) {
90
- if (exit !== null && (exit === 0 || exit === 3010))
91
- return 'done';
92
- return localFreeBytes >= LAYOUT_STAGING_BYTES + reserveBytes ? 'stage' : 'skip';
93
- }
94
- /**
95
- * The copies that make an Unreal AutoSDK tree from the portable MSVC (copies, not links: mapped folders refuse
96
- * junctions): MSVC's tools into HostWin64\Win64\VS2022\<msvc> and the Windows SDK's Include, Lib and bin folders into
97
- * HostWin64\Win64\Windows Kits\10. Pure.
98
- */
99
- export function autosdkLayout(msvcRoot, msvc, sdk, autosdkRoot) {
100
- const win64 = path.win32.join(autosdkRoot, 'HostWin64', 'Win64');
101
- return [
102
- {
103
- from: path.win32.join(msvcRoot, 'VC', 'Tools', 'MSVC', msvc),
104
- to: path.win32.join(win64, 'VS2022', msvc),
105
- },
106
- ...['Include', 'Lib', 'bin'].map((part) => ({
107
- from: path.win32.join(msvcRoot, 'Windows Kits', '10', part, sdk),
108
- to: path.win32.join(win64, 'Windows Kits', '10', part, sdk),
109
- })),
110
- ];
111
- }