windows-development-sandbox 1.15.0 → 1.17.0

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 (71) hide show
  1. package/config.example.json +6 -0
  2. package/dist/app/features/access/access.run.js +1 -1
  3. package/dist/app/features/accounts/accounts.def.js +4 -3
  4. package/dist/app/features/defs.d.ts +3 -1
  5. package/dist/app/features/defs.js +26 -12
  6. package/dist/app/features/sandbox/sandbox.def.js +9 -5
  7. package/dist/app/features/sessions/sessions.def.js +1 -1
  8. package/dist/bin/agent-sandbox.js +2 -2
  9. package/dist/bin/{chunk-HTQT5SBY.js → chunk-QLGGAG3D.js} +98 -79
  10. package/dist/bin/{chunk-D7CM63BS.js → chunk-REE3EMHD.js} +105 -41
  11. package/dist/bin/{chunk-RQ4BWBYX.js → chunk-XYUVIVJT.js} +1 -1
  12. package/dist/bin/claude-host.js +2 -2
  13. package/dist/bin/claude-sbx.js +2 -2
  14. package/dist/bin/codex-sbx.js +2 -2
  15. package/dist/bin/gemini-sbx.js +2 -2
  16. package/dist/bin/sandbox.js +2 -2
  17. package/dist/bin/sbx-hide.js +2 -2
  18. package/dist/bin/sbx-show.js +2 -2
  19. package/dist/cli/commands/web.js +1 -1
  20. package/dist/cli/index.js +4 -1
  21. package/dist/cli/report.d.ts +7 -0
  22. package/dist/cli/report.js +31 -0
  23. package/dist/core/config/defaults.js +1 -0
  24. package/dist/core/detect/toolchains.d.ts +1 -0
  25. package/dist/core/detect/toolchains.js +7 -0
  26. package/dist/core/schema/config.d.ts +21 -0
  27. package/dist/core/schema/sandbox.js +1 -1
  28. package/dist/core/schema/toolchains.d.ts +6 -0
  29. package/dist/core/schema/toolchains.js +10 -0
  30. package/dist/guest/boot/boot.js +2 -0
  31. package/dist/guest/setup/browser-gpu.d.ts +1 -0
  32. package/dist/guest/setup/browser-gpu.js +18 -0
  33. package/dist/guest/toolchains/native/rust-extras.js +3 -1
  34. package/dist/guest/toolchains/native/unreal.d.ts +9 -0
  35. package/dist/guest/toolchains/native/unreal.js +186 -0
  36. package/dist/guest/toolchains/native/vcruntime.d.ts +3 -0
  37. package/dist/guest/toolchains/native/vcruntime.js +2 -2
  38. package/dist/guest/toolchains/plan.js +10 -2
  39. package/dist/host/engines/hcs/live-folders.js +1 -1
  40. package/dist/host/github/token.js +1 -1
  41. package/dist/host/profile/accounts.js +1 -1
  42. package/dist/host/profile/agent-accounts.js +1 -1
  43. package/dist/host/profile/plugin-share.d.ts +5 -0
  44. package/dist/host/profile/plugin-share.js +22 -1
  45. package/dist/host/secrets/secret-store.d.ts +3 -3
  46. package/dist/host/secrets/secret-store.js +37 -10
  47. package/dist/host/vpn/login.js +1 -1
  48. package/dist/runtime/agent.js +16 -1
  49. package/dist/runtime/daemon.js +522 -337
  50. package/dist/runtime/db.js +17 -1
  51. package/dist/runtime/main.js +669 -472
  52. package/dist/runtime/netproxy.js +16 -1
  53. package/dist/runtime/view.js +16 -1
  54. package/dist/selftest/accounts.test.js +29 -0
  55. package/dist/selftest/app.test.js +2 -2
  56. package/dist/selftest/detect.test.js +7 -0
  57. package/dist/selftest/live-network.test.js +5 -0
  58. package/dist/selftest/support/fixtures.d.ts +6 -0
  59. package/dist/shared/channel/schema.d.ts +18 -0
  60. package/dist/web/index.html +2 -2
  61. package/dist/web/main-JTH3FT4O.js +221 -0
  62. package/dist/web/{main-UAQPI5XD.css → main-XT7T6ODM.css} +1 -1
  63. package/native/csharp/HcsHost/AssemblyInfo.cs +3 -3
  64. package/native/csharp/HostTools/AssemblyInfo.cs +3 -3
  65. package/native/csharp/HostTools/Program.cs +6 -1
  66. package/native/csharp/HostTools/Protect.cs +41 -0
  67. package/native/csharp/StorageHelper/AssemblyInfo.cs +3 -3
  68. package/package.json +1 -1
  69. package/schemas/config.schema.json +34 -1
  70. package/schemas/sandbox-config.schema.json +34 -1
  71. package/dist/web/main-7DJZ2M27.js +0 -221
@@ -25515,7 +25515,7 @@ var SandboxSchema = external_exports.strictObject({
25515
25515
  "When the sandbox (or this PC) runs low on memory, slow builds down in steps, like a PC that gets slower instead of failing: build programs get lower CPU and memory priority, then less CPU, then compilers take turns (the waiting ones briefly suspended and trimmed to the paging file). Nothing is stopped or killed, and the agents themselves are never touched. Yours only: a project file cannot change it."
25516
25516
  ),
25517
25517
  vGpu: external_exports.boolean().describe(
25518
- "Virtual GPU. Off by default: Microsoft notes it widens the sandbox attack surface. The sandbox then uses this PC's graphics card, and with some graphics drivers heavy use can freeze the whole PC: turn it on only when a program in the sandbox needs the GPU (the mesa toolchain gives software OpenGL without it)."
25518
+ "Virtual GPU. Off by default: Microsoft notes it widens the sandbox attack surface. The sandbox then uses this PC's graphics card, and with some graphics drivers heavy use can freeze the whole PC: turn it on only when a program in the sandbox needs the GPU (the mesa toolchain gives software OpenGL without it). Browsers in the sandbox never use it (they draw in software)."
25519
25519
  ),
25520
25520
  networking: external_exports.boolean().describe("Internet access. false is the only hard network isolation Windows Sandbox offers."),
25521
25521
  clipboard: external_exports.boolean().describe(
@@ -25677,6 +25677,21 @@ var ToolchainsSchema = external_exports.strictObject({
25677
25677
  msvcVersion: external_exports.string().min(1).describe('"latest" or an MSVC tools version prefix, e.g. "14.44".'),
25678
25678
  sdkVersion: external_exports.string().min(1).describe('"latest" or a Windows SDK build, e.g. "26100" or "10.0.22621".')
25679
25679
  }),
25680
+ unreal: external_exports.strictObject({
25681
+ enabled: enabled(
25682
+ "Unreal Engine development: Visual Studio Community 2022 with the C++ game workload (MSVC, a Windows 11 SDK, .NET Framework targeting packs, Unreal integration and the Epic Games Launcher) and the DirectX End-User Runtime. A real installer: it runs every boot (~10 GB on the sandbox disk). The engine itself comes from the Epic Games Launcher or its source.",
25683
+ "a .uproject or .uplugin"
25684
+ ),
25685
+ acceptLicense: external_exports.boolean().describe(
25686
+ "Required to install: you accept the Visual Studio Community licence terms (https://visualstudio.microsoft.com/license-terms/) and the DirectX End-User Runtime licence."
25687
+ ),
25688
+ components: StringsSchema.describe(
25689
+ 'More Visual Studio workload or component IDs to add, e.g. ["Microsoft.VisualStudio.Component.VC.Llvm.Clang"].'
25690
+ ),
25691
+ keepInstallFiles: external_exports.boolean().describe(
25692
+ "Keep an offline copy of the Visual Studio install files in tools\\visualstudio (~10 GB, made once), so later boots install without downloading them again. Skipped while that disk has too little room."
25693
+ )
25694
+ }),
25680
25695
  cpp: external_exports.strictObject({
25681
25696
  enabled: enabled(
25682
25697
  "MinGW-w64 GCC with CMake, Ninja, premake5 and mingw32-make (also Rust's linker).",
@@ -26090,7 +26090,7 @@ var SandboxSchema = external_exports.strictObject({
26090
26090
  "When the sandbox (or this PC) runs low on memory, slow builds down in steps, like a PC that gets slower instead of failing: build programs get lower CPU and memory priority, then less CPU, then compilers take turns (the waiting ones briefly suspended and trimmed to the paging file). Nothing is stopped or killed, and the agents themselves are never touched. Yours only: a project file cannot change it."
26091
26091
  ),
26092
26092
  vGpu: external_exports.boolean().describe(
26093
- "Virtual GPU. Off by default: Microsoft notes it widens the sandbox attack surface. The sandbox then uses this PC's graphics card, and with some graphics drivers heavy use can freeze the whole PC: turn it on only when a program in the sandbox needs the GPU (the mesa toolchain gives software OpenGL without it)."
26093
+ "Virtual GPU. Off by default: Microsoft notes it widens the sandbox attack surface. The sandbox then uses this PC's graphics card, and with some graphics drivers heavy use can freeze the whole PC: turn it on only when a program in the sandbox needs the GPU (the mesa toolchain gives software OpenGL without it). Browsers in the sandbox never use it (they draw in software)."
26094
26094
  ),
26095
26095
  networking: external_exports.boolean().describe("Internet access. false is the only hard network isolation Windows Sandbox offers."),
26096
26096
  clipboard: external_exports.boolean().describe(
@@ -26252,6 +26252,21 @@ var ToolchainsSchema = external_exports.strictObject({
26252
26252
  msvcVersion: external_exports.string().min(1).describe('"latest" or an MSVC tools version prefix, e.g. "14.44".'),
26253
26253
  sdkVersion: external_exports.string().min(1).describe('"latest" or a Windows SDK build, e.g. "26100" or "10.0.22621".')
26254
26254
  }),
26255
+ unreal: external_exports.strictObject({
26256
+ enabled: enabled(
26257
+ "Unreal Engine development: Visual Studio Community 2022 with the C++ game workload (MSVC, a Windows 11 SDK, .NET Framework targeting packs, Unreal integration and the Epic Games Launcher) and the DirectX End-User Runtime. A real installer: it runs every boot (~10 GB on the sandbox disk). The engine itself comes from the Epic Games Launcher or its source.",
26258
+ "a .uproject or .uplugin"
26259
+ ),
26260
+ acceptLicense: external_exports.boolean().describe(
26261
+ "Required to install: you accept the Visual Studio Community licence terms (https://visualstudio.microsoft.com/license-terms/) and the DirectX End-User Runtime licence."
26262
+ ),
26263
+ components: StringsSchema.describe(
26264
+ 'More Visual Studio workload or component IDs to add, e.g. ["Microsoft.VisualStudio.Component.VC.Llvm.Clang"].'
26265
+ ),
26266
+ keepInstallFiles: external_exports.boolean().describe(
26267
+ "Keep an offline copy of the Visual Studio install files in tools\\visualstudio (~10 GB, made once), so later boots install without downloading them again. Skipped while that disk has too little room."
26268
+ )
26269
+ }),
26255
26270
  cpp: external_exports.strictObject({
26256
26271
  enabled: enabled(
26257
26272
  "MinGW-w64 GCC with CMake, Ninja, premake5 and mingw32-make (also Rust's linker).",
@@ -2,6 +2,9 @@
2
2
  import fs from 'node:fs';
3
3
  import path from 'node:path';
4
4
  import { AccountStore } from '../host/profile/index.js';
5
+ import { DEFAULTS } from '../core/config/index.js';
6
+ import { defaultAccount } from '../app/features/accounts/index.js';
7
+ import { MenuOperations } from '../cli/menu/io-menu.js';
5
8
  export const accountsSuite = {
6
9
  name: 'accounts',
7
10
  title: 'Claude accounts (save, switch, remove, safe names)',
@@ -32,6 +35,25 @@ export const accountsSuite = {
32
35
  stateAfter.other === 1 &&
33
36
  new AccountStore(storage).activeFor(dataA) === 'work', 'switching swaps in the other credentials and account, keeps the rest of the sandbox state, and marks it active');
34
37
  t.includes(JSON.stringify(new AccountStore(storage).read('personal')?.credentials), 'personal-2', "the outgoing account's refreshed tokens are saved back before switching");
38
+ t.section('make default, as the menu runs it');
39
+ const menuCtx = {
40
+ packageRoot: t.dir('acct-menu', 'package'),
41
+ options: {},
42
+ loaded: { config: structuredClone(DEFAULTS), configPath: null, base: t.temp },
43
+ storageRoot: storage,
44
+ dataPath: dataA,
45
+ };
46
+ let said;
47
+ let error = null;
48
+ try {
49
+ said = await new MenuOperations({}, menuCtx).run(defaultAccount, { agent: 'claude', name: 'personal', noRestart: false }, []);
50
+ }
51
+ catch (e) {
52
+ error = e;
53
+ }
54
+ t.check(error === null &&
55
+ new AccountStore(storage).defaultName() === 'personal' &&
56
+ new AccountStore(storage).activeFor(dataA) === 'personal', 'make default from the menu sets the default and switches the sandbox to it', String(error instanceof Error ? error.stack : (error ?? said)));
35
57
  t.section('sharing plugins between data sets');
36
58
  const plugA = path.join(storage, 'data', 'p1', 'state', 'claude');
37
59
  const plugB = path.join(storage, 'data', 'p2', 'state', 'claude');
@@ -57,6 +79,13 @@ export const accountsSuite = {
57
79
  settingsB.enabledPlugins['p@m'] === true &&
58
80
  settingsB.enabledPlugins['q@m'] === false &&
59
81
  !fs.existsSync(path.join(storage, 'accounts', 'shared-plugins', '.credentials.json')), 'on: plugins and enabledPlugins reach the other data set; its own settings win and no login travels');
82
+ const sentinel = path.join(storage, 'accounts', 'shared-plugins', 'plugins', 'cache', 'kept.js');
83
+ t.write(sentinel, 'k');
84
+ on.activate(dataP('p1'), 'one');
85
+ t.check(fs.existsSync(sentinel), 'switching with the same plugins does not copy the plugins folder again');
86
+ t.write(path.join(plugA, 'plugins', 'installed_plugins.json'), '{"version":2,"plugins":{"p@m":[]}}');
87
+ on.activate(dataP('p1'), 'one');
88
+ t.check(!fs.existsSync(sentinel), 'once a plugin changed, the switch refreshes the shared set');
60
89
  t.write(path.join(plugC, 'plugins', 'installed_plugins.json'), '{}');
61
90
  const outside = t.dir('plugin-victim');
62
91
  t.write(path.join(outside, 'secret.txt'), 's');
@@ -319,9 +319,9 @@ export const appSuite = {
319
319
  !sessionLines.some((l) => l.includes('--wide')), 'sessions on the command line: the table with the folder column (--wide), the marker and its note', sessionLines.join('\n'));
320
320
  const statusLines = pageLines(statusPage);
321
321
  t.check(statusLines.some((l) => /^State\s+\S/.test(l)) &&
322
- statusLines.some((l) => /^Helper\s+responding, 2 sessions running$/.test(l)) &&
322
+ statusLines.some((l) => /^Helper\s+Responding$/.test(l)) &&
323
323
  statusLines.includes('* api C:\\Projects\\api -> C:\\Projects\\api (started from here)') &&
324
- statusLines.some((l) => l.startsWith('Repo guard 1 folder(s) guarded')), 'status on the command line: facts as rows, projects with their details, the repo guard', statusLines.join('\n'));
324
+ statusLines.some((l) => l.startsWith('Repo guard Watching 1 folder.')), 'status on the command line: facts as rows, projects with their details, the repo guard', statusLines.join('\n'));
325
325
  t.equal(pageLines(sessionsPage({ ...list, sessions: [] })).at(-1), 'No sessions. Start one with: sandbox claude', 'an empty page prints its command-line text');
326
326
  const menu = menuView(sessions);
327
327
  const chosen = menu.resolve({ kind: 'pick', value: 'new' });
@@ -66,6 +66,13 @@ export const detectSuite = {
66
66
  t.check(fresh.python.on && fresh.dotnet.on && !fresh.go.on && fresh.python.reason.includes('this PC'), 'a folder that is no project yet gets the languages this PC has, and the reason names this PC');
67
67
  const rusty = decideToolchains({ ...tc, python: { ...tc.python, enabled: true } }, ['Cargo.toml'], []);
68
68
  t.check(rusty.rust.on && rusty.cpp.on && rusty.python.on && rusty.python.reason === 'enabled in config', 'Rust brings its C/C++ linker; true in config always wins');
69
+ const game = ['ZombieGame.uproject', 'Source/ZombieGame.Target.cs'];
70
+ const accepted = { ...tc, unreal: { ...tc.unreal, acceptLicense: true } };
71
+ t.check(decideToolchains(accepted, game, []).unreal.on &&
72
+ decideToolchains(accepted, ['Plugins/Kit.uplugin'], []).unreal.on &&
73
+ !decideToolchains(accepted, ['go.mod'], []).unreal.on, 'Unreal: "auto" follows a .uproject or .uplugin');
74
+ const unaccepted = decideToolchains(tc, game, []).unreal;
75
+ t.check(!unaccepted.on && unaccepted.reason.includes('toolchains.unreal.acceptLicense'), 'Unreal is not installed until its licence is accepted, and the reason says how');
69
76
  const resolved = resolveToolchains(tc, goProject);
70
77
  t.check(resolved.go.enabled === true && resolved.python.enabled === false, 'the sandbox gets plain decisions instead of "auto"');
71
78
  t.section('optional tools: Bun, uv, VS Code');
@@ -122,6 +122,11 @@ export const liveNetworkSuite = {
122
122
  typeof e.id === 'number' &&
123
123
  typeof e.provider === 'string' &&
124
124
  typeof e.process === 'string'), 'HostTools stop-events prints a JSON list of { at, id, provider, process }', events.stdout.slice(0, 500) + events.stderr);
125
+ const plain = Buffer.from('a saved login').toString('base64');
126
+ const sealed = spawnSync(hostTools, ['protect'], { encoding: 'utf8', input: plain });
127
+ const opened = spawnSync(hostTools, ['unprotect'], { encoding: 'utf8', input: sealed.stdout });
128
+ t.check(sealed.status === 0 && sealed.stdout !== plain && opened.status === 0 && opened.stdout === plain, 'HostTools protect and unprotect round-trip through DPAPI', sealed.stderr + opened.stderr);
129
+ t.equal(spawnSync(hostTools, ['unprotect'], { encoding: 'utf8', input: 'not base64!' }).status, 1, 'HostTools unprotect refuses bad input');
125
130
  t.equal(spawnSync(hostTools, ['stop-events'], { encoding: 'utf8' }).status, 2, 'HostTools without --since prints its usage');
126
131
  },
127
132
  };
@@ -157,6 +157,12 @@ export declare function createFixtures(t: Harness): {
157
157
  msvcVersion: string;
158
158
  sdkVersion: string;
159
159
  };
160
+ unreal: {
161
+ enabled: boolean | "auto";
162
+ acceptLicense: boolean;
163
+ components: string[];
164
+ keepInstallFiles: boolean;
165
+ };
160
166
  cpp: {
161
167
  enabled: boolean | "auto";
162
168
  flexBison: boolean;
@@ -88,6 +88,12 @@ export declare const ProjectSpecSchema: z.ZodObject<{
88
88
  msvcVersion: z.ZodString;
89
89
  sdkVersion: z.ZodString;
90
90
  }, z.core.$strict>;
91
+ unreal: z.ZodObject<{
92
+ enabled: z.ZodUnion<readonly [z.ZodBoolean, z.ZodLiteral<"auto">]>;
93
+ acceptLicense: z.ZodBoolean;
94
+ components: z.ZodArray<z.ZodString>;
95
+ keepInstallFiles: z.ZodBoolean;
96
+ }, z.core.$strict>;
91
97
  cpp: z.ZodObject<{
92
98
  enabled: z.ZodUnion<readonly [z.ZodBoolean, z.ZodLiteral<"auto">]>;
93
99
  flexBison: z.ZodBoolean;
@@ -254,6 +260,12 @@ export declare const GuestRequestSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
254
260
  msvcVersion: z.ZodString;
255
261
  sdkVersion: z.ZodString;
256
262
  }, z.core.$strict>;
263
+ unreal: z.ZodObject<{
264
+ enabled: z.ZodUnion<readonly [z.ZodBoolean, z.ZodLiteral<"auto">]>;
265
+ acceptLicense: z.ZodBoolean;
266
+ components: z.ZodArray<z.ZodString>;
267
+ keepInstallFiles: z.ZodBoolean;
268
+ }, z.core.$strict>;
257
269
  cpp: z.ZodObject<{
258
270
  enabled: z.ZodUnion<readonly [z.ZodBoolean, z.ZodLiteral<"auto">]>;
259
271
  flexBison: z.ZodBoolean;
@@ -440,6 +452,12 @@ export declare const GuestRequestSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
440
452
  msvcVersion: z.ZodString;
441
453
  sdkVersion: z.ZodString;
442
454
  }, z.core.$strict>;
455
+ unreal: z.ZodObject<{
456
+ enabled: z.ZodUnion<readonly [z.ZodBoolean, z.ZodLiteral<"auto">]>;
457
+ acceptLicense: z.ZodBoolean;
458
+ components: z.ZodArray<z.ZodString>;
459
+ keepInstallFiles: z.ZodBoolean;
460
+ }, z.core.$strict>;
443
461
  cpp: z.ZodObject<{
444
462
  enabled: z.ZodUnion<readonly [z.ZodBoolean, z.ZodLiteral<"auto">]>;
445
463
  flexBison: z.ZodBoolean;
@@ -5,8 +5,8 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1" />
6
6
  <title>Sandbox</title>
7
7
  <link rel="icon" href="data:," />
8
- <link rel="stylesheet" href="/main-UAQPI5XD.css" />
9
- <script type="module" src="/main-7DJZ2M27.js"></script>
8
+ <link rel="stylesheet" href="/main-XT7T6ODM.css" />
9
+ <script type="module" src="/main-JTH3FT4O.js"></script>
10
10
  </head>
11
11
  <body>
12
12
  <div id="root"></div>