windows-development-sandbox 1.1.0 → 1.4.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 (187) hide show
  1. package/README.md +3 -2
  2. package/config.example.json +38 -5
  3. package/dist/cli/args.d.ts +3 -0
  4. package/dist/cli/args.js +3 -1
  5. package/dist/cli/commands/accounts.js +2 -2
  6. package/dist/cli/commands/agent-commands.js +1 -1
  7. package/dist/cli/commands/context.js +2 -1
  8. package/dist/cli/commands/data/clean.js +6 -1
  9. package/dist/cli/commands/data/collect.js +3 -3
  10. package/dist/cli/commands/index.d.ts +1 -1
  11. package/dist/cli/commands/index.js +1 -1
  12. package/dist/cli/commands/launch.js +36 -3
  13. package/dist/cli/commands/maintenance.d.ts +2 -0
  14. package/dist/cli/commands/maintenance.js +35 -2
  15. package/dist/cli/commands/sessions.js +3 -3
  16. package/dist/cli/commands/user-config.js +1 -1
  17. package/dist/cli/index.js +6 -4
  18. package/dist/cli/manifest.js +5 -0
  19. package/dist/cli/menu/apps.d.ts +21 -0
  20. package/dist/cli/menu/apps.js +122 -0
  21. package/dist/cli/menu/command-form.d.ts +28 -0
  22. package/dist/cli/menu/command-form.js +219 -0
  23. package/dist/cli/menu/index.d.ts +26 -0
  24. package/dist/cli/menu/index.js +180 -0
  25. package/dist/cli/menu/settings/categories.d.ts +9 -0
  26. package/dist/cli/menu/settings/categories.js +28 -0
  27. package/dist/cli/menu/settings/config-file.d.ts +40 -0
  28. package/dist/cli/menu/settings/config-file.js +71 -0
  29. package/dist/cli/menu/settings/editors.d.ts +30 -0
  30. package/dist/cli/menu/settings/editors.js +305 -0
  31. package/dist/cli/menu/settings/index.d.ts +32 -0
  32. package/dist/cli/menu/settings/index.js +136 -0
  33. package/dist/cli/menu/settings/schema-node.d.ts +62 -0
  34. package/dist/cli/menu/settings/schema-node.js +104 -0
  35. package/dist/cli/{menu-multi.d.ts → menu/several.d.ts} +10 -2
  36. package/dist/cli/menu/several.js +102 -0
  37. package/dist/cli/tui/app.d.ts +39 -0
  38. package/dist/cli/tui/app.js +52 -0
  39. package/dist/cli/tui/dialogs.d.ts +57 -0
  40. package/dist/cli/tui/dialogs.js +162 -0
  41. package/dist/cli/tui/frame.d.ts +18 -0
  42. package/dist/cli/tui/frame.js +27 -0
  43. package/dist/cli/tui/index.d.ts +7 -0
  44. package/dist/cli/tui/index.js +8 -0
  45. package/dist/cli/tui/list-screen.d.ts +101 -0
  46. package/dist/cli/tui/list-screen.js +299 -0
  47. package/dist/cli/tui/terminal.d.ts +37 -0
  48. package/dist/cli/tui/terminal.js +89 -0
  49. package/dist/cli/tui/text.d.ts +20 -0
  50. package/dist/cli/tui/text.js +105 -0
  51. package/dist/cli/tui/theme.d.ts +45 -0
  52. package/dist/cli/tui/theme.js +30 -0
  53. package/dist/core/config.js +9 -6
  54. package/dist/core/local-config.d.ts +15 -0
  55. package/dist/core/local-config.js +63 -4
  56. package/dist/core/schema/apps.d.ts +40 -0
  57. package/dist/core/schema/apps.js +27 -0
  58. package/dist/core/schema/config.d.ts +66 -1
  59. package/dist/core/schema/config.js +18 -6
  60. package/dist/core/schema/profile.d.ts +5 -0
  61. package/dist/core/schema/profile.js +4 -2
  62. package/dist/core/schema/sandbox.d.ts +28 -0
  63. package/dist/core/schema/sandbox.js +28 -0
  64. package/dist/core/schema/toolchains.d.ts +3 -0
  65. package/dist/core/schema/toolchains.js +6 -1
  66. package/dist/core/types.d.ts +6 -0
  67. package/dist/core/util.d.ts +2 -0
  68. package/dist/core/util.js +2 -0
  69. package/dist/core/workspace.js +22 -5
  70. package/dist/guest/apps/index.d.ts +10 -0
  71. package/dist/guest/apps/index.js +206 -0
  72. package/dist/guest/apps/resolvers.d.ts +8 -0
  73. package/dist/guest/apps/resolvers.js +90 -0
  74. package/dist/guest/audit-parse.d.ts +7 -0
  75. package/dist/guest/audit-parse.js +24 -0
  76. package/dist/guest/audit.d.ts +4 -0
  77. package/dist/guest/audit.js +47 -0
  78. package/dist/guest/daemon/relays.d.ts +1 -0
  79. package/dist/guest/daemon/relays.js +18 -0
  80. package/dist/guest/daemon.js +4 -0
  81. package/dist/guest/env.d.ts +1 -0
  82. package/dist/guest/env.js +3 -1
  83. package/dist/guest/helpers.d.ts +1 -1
  84. package/dist/guest/main.js +15 -2
  85. package/dist/guest/netproxy.d.ts +1 -0
  86. package/dist/guest/netproxy.js +100 -0
  87. package/dist/guest/ptyhost.js +14 -4
  88. package/dist/guest/setup/claude.d.ts +0 -4
  89. package/dist/guest/setup/claude.js +48 -2
  90. package/dist/guest/setup/network.d.ts +9 -0
  91. package/dist/guest/setup/network.js +97 -4
  92. package/dist/guest/setup/user.js +4 -0
  93. package/dist/guest/toolchains/native/mesa.js +13 -3
  94. package/dist/guest/toolchains/plan.d.ts +3 -1
  95. package/dist/guest/toolchains/plan.js +53 -9
  96. package/dist/guest/toolchains/python.js +26 -2
  97. package/dist/guest/toolchains/vscode-profile.d.ts +30 -0
  98. package/dist/guest/toolchains/vscode-profile.js +69 -0
  99. package/dist/guest/toolchains/vscode.d.ts +17 -0
  100. package/dist/guest/toolchains/vscode.js +87 -1
  101. package/dist/host/attach.js +2 -1
  102. package/dist/host/audit/collect.d.ts +34 -0
  103. package/dist/host/audit/collect.js +126 -0
  104. package/dist/host/audit/log.d.ts +46 -0
  105. package/dist/host/audit/log.js +143 -0
  106. package/dist/host/generate.d.ts +6 -0
  107. package/dist/host/generate.js +10 -0
  108. package/dist/host/launch/mappings.js +5 -1
  109. package/dist/host/launch/stage.js +1 -1
  110. package/dist/host/motw.d.ts +17 -0
  111. package/dist/host/motw.js +74 -0
  112. package/dist/host/platform.js +3 -0
  113. package/dist/host/port-forward.d.ts +8 -0
  114. package/dist/host/port-forward.js +37 -0
  115. package/dist/host/repo-guard.d.ts +10 -3
  116. package/dist/host/repo-guard.js +142 -14
  117. package/dist/host/stops.d.ts +67 -0
  118. package/dist/host/stops.js +126 -0
  119. package/dist/host/terminal.d.ts +8 -0
  120. package/dist/host/terminal.js +45 -1
  121. package/dist/host/workspace-view/index.d.ts +20 -0
  122. package/dist/host/workspace-view/index.js +53 -0
  123. package/dist/host/workspace-view/rules.d.ts +26 -0
  124. package/dist/host/workspace-view/rules.js +93 -0
  125. package/dist/host/workspace-view/view.d.ts +50 -0
  126. package/dist/host/workspace-view/view.js +281 -0
  127. package/dist/runtime/daemon.js +1319 -552
  128. package/dist/runtime/db.js +168 -13
  129. package/dist/runtime/main.js +2898 -608
  130. package/dist/runtime/netproxy.js +25936 -0
  131. package/dist/runtime/view.js +272 -23
  132. package/dist/selftest/audit.test.d.ts +2 -0
  133. package/dist/selftest/audit.test.js +57 -0
  134. package/dist/selftest/channel.test.js +7 -0
  135. package/dist/selftest/cli.test.js +47 -17
  136. package/dist/selftest/fixtures.d.ts +45 -0
  137. package/dist/selftest/fixtures.js +3 -1
  138. package/dist/selftest/generate.test.js +7 -2
  139. package/dist/selftest/index.js +5 -2
  140. package/dist/selftest/live-network.test.d.ts +2 -0
  141. package/dist/selftest/live-network.test.js +63 -0
  142. package/dist/selftest/local-config.test.js +16 -3
  143. package/dist/selftest/network.test.js +12 -0
  144. package/dist/selftest/projects.test.js +1 -1
  145. package/dist/selftest/repo-guard.test.js +59 -1
  146. package/dist/selftest/sync.test.js +50 -10
  147. package/dist/selftest/toolchains.test.js +30 -0
  148. package/dist/selftest/workspace-view.test.d.ts +2 -0
  149. package/dist/selftest/workspace-view.test.js +89 -0
  150. package/dist/shared/apps.d.ts +49 -0
  151. package/dist/shared/apps.js +114 -0
  152. package/dist/shared/netpolicy.d.ts +27 -0
  153. package/dist/shared/netpolicy.js +51 -0
  154. package/dist/shared/pty-client.d.ts +9 -4
  155. package/dist/shared/pty-client.js +139 -17
  156. package/dist/shared/rundir.d.ts +2 -0
  157. package/dist/shared/rundir.js +2 -0
  158. package/dist/shared/schema.d.ts +10 -0
  159. package/dist/shared/schema.js +2 -0
  160. package/dist/shared/system.d.ts +1 -0
  161. package/dist/shared/system.js +1 -0
  162. package/dist/sync/claude-mem.js +3 -2
  163. package/dist/sync/history.d.ts +6 -0
  164. package/dist/sync/history.js +26 -11
  165. package/dist/sync/watcher.js +78 -6
  166. package/package.json +2 -1
  167. package/sandbox/cargoshim.cs +56 -0
  168. package/sandbox/cpucap.cs +33 -0
  169. package/sandbox/cpucap.ps1 +4 -35
  170. package/sandbox/logon.cmd +6 -0
  171. package/sandbox/memlog.ps1 +34 -0
  172. package/sandbox/pagefile.cs +31 -0
  173. package/sandbox/pagefile.ps1 +21 -0
  174. package/sandbox/ptyhost.cs +105 -7
  175. package/schemas/config.schema.json +303 -2
  176. package/schemas/sandbox-config.schema.json +126 -12
  177. package/dist/cli/menu-multi.js +0 -82
  178. package/dist/cli/menu.d.ts +0 -14
  179. package/dist/cli/menu.js +0 -290
  180. package/dist/cli/settings/edit.d.ts +0 -13
  181. package/dist/cli/settings/edit.js +0 -19
  182. package/dist/cli/settings/index.d.ts +0 -8
  183. package/dist/cli/settings/index.js +0 -131
  184. package/dist/cli/settings/prompts.d.ts +0 -7
  185. package/dist/cli/settings/prompts.js +0 -48
  186. package/dist/cli/settings/schema.d.ts +0 -33
  187. package/dist/cli/settings/schema.js +0 -29
@@ -16,6 +16,9 @@
16
16
  // 18 ERROR utf8 message
17
17
  // 19 RESTARTING
18
18
  // 20 STATUS [viewers:2][remoteViewers:2] viewers heard from recently (remote = not loopback, i.e. the PC)
19
+ // 21 CONSOLE utf8 "conpty.dll" or "windows", sent after HELLO. With conpty.dll the output is passed
20
+ // through (real scrollback, no repaint on resize): a viewer that joins later is sent the
21
+ // output since the last screen clear instead, and must not clear its screen on a resize.
19
22
  using System;
20
23
  using System.Collections.Concurrent;
21
24
  using System.Collections.Generic;
@@ -75,6 +78,7 @@ sealed class SessionHost
75
78
  using (var console = PseudoConsole.Start(command, hub.Cols, hub.Rows, hub.Broadcast))
76
79
  {
77
80
  hub.Attach(console);
81
+ log.Write("pseudo console: " + Native.ConsoleKind);
78
82
  int exitCode = console.WaitForExit();
79
83
  if (!restartRequested)
80
84
  {
@@ -92,6 +96,7 @@ sealed class SessionHost
92
96
  public void Restart()
93
97
  {
94
98
  restartRequested = true;
99
+ hub.ResetReplay();
95
100
  hub.Notify(Frame.Restarting);
96
101
  hub.KillProgram();
97
102
  }
@@ -225,6 +230,9 @@ sealed class ClientHub
225
230
  readonly byte[] token;
226
231
  readonly byte[] tail = new byte[8192];
227
232
  int tailLength;
233
+ readonly object replayGate = new object();
234
+ readonly MemoryStream replay = new MemoryStream();
235
+ const int MaxReplay = 4 << 20;
228
236
  PseudoConsole console;
229
237
  Client sizeOwner;
230
238
  short cols, rows;
@@ -251,8 +259,18 @@ sealed class ClientHub
251
259
  client.ControlOnly = (hello[4] & 3) != 0;
252
260
  client.Query = (hello[4] & 2) != 0;
253
261
  if (client.Query) return true;
254
- lock (gate) clients.Add(client);
255
- if (!client.ControlOnly) TakeOwnership(client);
262
+ if (!client.ControlOnly)
263
+ {
264
+ Send(client, Frame.Console, Encoding.UTF8.GetBytes(Native.ConsoleKind.StartsWith("conpty") ? "conpty.dll" : "windows"));
265
+ // Taken and queued under the replay lock, so no output falls between the replay and the live stream.
266
+ lock (replayGate)
267
+ {
268
+ if (Native.ConsoleKind.StartsWith("conpty") && replay.Length > 0) Send(client, Frame.Output, replay.ToArray());
269
+ lock (gate) clients.Add(client);
270
+ }
271
+ TakeOwnership(client);
272
+ }
273
+ else lock (gate) clients.Add(client);
256
274
  return true;
257
275
  }
258
276
 
@@ -287,7 +305,41 @@ sealed class ClientHub
287
305
  public void Broadcast(byte[] output)
288
306
  {
289
307
  Remember(output);
290
- foreach (Client c in Snapshot()) if (!c.ControlOnly) Send(c, Frame.Output, output);
308
+ lock (replayGate)
309
+ {
310
+ Record(output);
311
+ foreach (Client c in Snapshot()) if (!c.ControlOnly) Send(c, Frame.Output, output);
312
+ }
313
+ }
314
+
315
+ /**
316
+ * The output a viewer joining now needs to see the screen (conpty.dll does not repaint for it): everything since the
317
+ * last "clear screen", capped. Cut at a line start when capped, so the replay never begins inside an escape sequence.
318
+ */
319
+ void Record(byte[] output)
320
+ {
321
+ if (!Native.ConsoleKind.StartsWith("conpty")) return;
322
+ int clear = LastClear(output);
323
+ if (clear >= 0) { replay.SetLength(0); replay.Write(output, clear, output.Length - clear); }
324
+ else replay.Write(output, 0, output.Length);
325
+ if (replay.Length <= MaxReplay) return;
326
+ byte[] all = replay.ToArray();
327
+ int from = all.Length - MaxReplay / 2;
328
+ while (from < all.Length && all[from] != (byte)'\n') from++;
329
+ replay.SetLength(0);
330
+ if (from + 1 < all.Length) replay.Write(all, from + 1, all.Length - from - 1);
331
+ }
332
+
333
+ /** Where the last ESC[2J (clear screen) or ESC c (reset) starts in `data`, or -1. */
334
+ static int LastClear(byte[] data)
335
+ {
336
+ for (int i = data.Length - 2; i >= 0; i--)
337
+ {
338
+ if (data[i] != 0x1b) continue;
339
+ if (data[i + 1] == (byte)'c') return i;
340
+ if (i + 3 < data.Length && data[i + 1] == (byte)'[' && data[i + 2] == (byte)'2' && data[i + 3] == (byte)'J') return i;
341
+ }
342
+ return -1;
291
343
  }
292
344
 
293
345
  /** The program's last output as plain text (escape sequences removed), for the log when it exits. */
@@ -311,6 +363,9 @@ sealed class ClientHub
311
363
  }
312
364
  }
313
365
 
366
+ /** A restarted program starts on a clean screen (viewers clear theirs on RESTARTING). */
367
+ public void ResetReplay() { lock (replayGate) replay.SetLength(0); }
368
+
314
369
  public void Notify(byte type)
315
370
  {
316
371
  foreach (Client c in Snapshot()) if (!c.ControlOnly) Send(c, type, new byte[0]);
@@ -503,7 +558,7 @@ sealed class PseudoConsole : IDisposable
503
558
  static class Frame
504
559
  {
505
560
  public const byte Hello = 1, Input = 2, Resize = 3, Restart = 4, Ping = 5;
506
- public const byte Output = 16, Exit = 17, Error = 18, Restarting = 19, Status = 20;
561
+ public const byte Output = 16, Exit = 17, Error = 18, Restarting = 19, Status = 20, Console = 21;
507
562
  const int MaxPayload = 1 << 20;
508
563
 
509
564
  public static bool Read(Stream stream, out byte type, out byte[] payload)
@@ -598,9 +653,52 @@ static class Native
598
653
  }
599
654
  }
600
655
 
601
- [DllImport("kernel32.dll")] public static extern int CreatePseudoConsole(Coord size, SafeFileHandle hInput, SafeFileHandle hOutput, uint dwFlags, out IntPtr phPC);
602
- [DllImport("kernel32.dll")] public static extern int ResizePseudoConsole(IntPtr hPC, Coord size);
603
- [DllImport("kernel32.dll")] public static extern void ClosePseudoConsole(IntPtr hPC);
656
+ // The pseudo console: the newer conpty.dll (+ OpenConsole.exe next to it) when PTYHOST_CONPTY names one, else the one
657
+ // built into Windows. Windows 10/11 before 24H2 ship a ConPTY that re-renders the screen instead of passing the
658
+ // program's output through, which garbles wide characters after a resize and loses mouse input and scrollback.
659
+ [UnmanagedFunctionPointer(CallingConvention.Winapi)] delegate int CreateFn(Coord size, SafeFileHandle hInput, SafeFileHandle hOutput, uint dwFlags, out IntPtr phPC);
660
+ [UnmanagedFunctionPointer(CallingConvention.Winapi)] delegate int ResizeFn(IntPtr hPC, Coord size);
661
+ [UnmanagedFunctionPointer(CallingConvention.Winapi)] delegate void CloseFn(IntPtr hPC);
662
+ static CreateFn create;
663
+ static ResizeFn resize;
664
+ static CloseFn close;
665
+ /** "conpty.dll" or "windows": which pseudo console is in use (logged, and told to viewers). */
666
+ public static string ConsoleKind = "windows";
667
+
668
+ static Native()
669
+ {
670
+ string dll = Environment.GetEnvironmentVariable("PTYHOST_CONPTY");
671
+ if (string.IsNullOrEmpty(dll) || !File.Exists(dll) || !File.Exists(Path.Combine(Path.GetDirectoryName(dll), "OpenConsole.exe"))) return;
672
+ IntPtr module = LoadLibraryW(dll);
673
+ if (module == IntPtr.Zero) return;
674
+ IntPtr c = GetProcAddress(module, "ConptyCreatePseudoConsole"), r = GetProcAddress(module, "ConptyResizePseudoConsole"), x = GetProcAddress(module, "ConptyClosePseudoConsole");
675
+ if (c == IntPtr.Zero || r == IntPtr.Zero || x == IntPtr.Zero) return;
676
+ create = (CreateFn)Marshal.GetDelegateForFunctionPointer(c, typeof(CreateFn));
677
+ resize = (ResizeFn)Marshal.GetDelegateForFunctionPointer(r, typeof(ResizeFn));
678
+ close = (CloseFn)Marshal.GetDelegateForFunctionPointer(x, typeof(CloseFn));
679
+ ConsoleKind = "conpty.dll";
680
+ }
681
+
682
+ /** conpty.dll first; if it cannot start one (OpenConsole.exe blocked or broken), the built-in one for good. */
683
+ public static int CreatePseudoConsole(Coord size, SafeFileHandle hInput, SafeFileHandle hOutput, uint dwFlags, out IntPtr phPC)
684
+ {
685
+ if (create != null)
686
+ {
687
+ int hr = create(size, hInput, hOutput, dwFlags, out phPC);
688
+ if (hr == 0) return 0;
689
+ create = null; resize = null; close = null;
690
+ ConsoleKind = "windows (conpty.dll failed: 0x" + hr.ToString("X8") + ")";
691
+ }
692
+ return KernelCreatePseudoConsole(size, hInput, hOutput, dwFlags, out phPC);
693
+ }
694
+ public static int ResizePseudoConsole(IntPtr hPC, Coord size) { return resize != null ? resize(hPC, size) : KernelResizePseudoConsole(hPC, size); }
695
+ public static void ClosePseudoConsole(IntPtr hPC) { if (close != null) close(hPC); else KernelClosePseudoConsole(hPC); }
696
+
697
+ [DllImport("kernel32.dll", EntryPoint = "CreatePseudoConsole")] static extern int KernelCreatePseudoConsole(Coord size, SafeFileHandle hInput, SafeFileHandle hOutput, uint dwFlags, out IntPtr phPC);
698
+ [DllImport("kernel32.dll", EntryPoint = "ResizePseudoConsole")] static extern int KernelResizePseudoConsole(IntPtr hPC, Coord size);
699
+ [DllImport("kernel32.dll", EntryPoint = "ClosePseudoConsole")] static extern void KernelClosePseudoConsole(IntPtr hPC);
700
+ [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] static extern IntPtr LoadLibraryW(string path);
701
+ [DllImport("kernel32.dll", CharSet = CharSet.Ansi, BestFitMapping = false)] static extern IntPtr GetProcAddress(IntPtr module, string name);
604
702
  [DllImport("kernel32.dll", SetLastError = true)] public static extern bool CreatePipe(out SafeFileHandle hReadPipe, out SafeFileHandle hWritePipe, IntPtr lpPipeAttributes, int nSize);
605
703
  [DllImport("kernel32.dll", SetLastError = true)] static extern bool InitializeProcThreadAttributeList(IntPtr lpAttributeList, int dwAttributeCount, int dwFlags, ref IntPtr lpSize);
606
704
  [DllImport("kernel32.dll", SetLastError = true)] static extern bool UpdateProcThreadAttribute(IntPtr lpAttributeList, uint dwFlags, IntPtr attribute, IntPtr lpValue, IntPtr cbSize, IntPtr lpPreviousValue, IntPtr lpReturnSize);
@@ -32,6 +32,62 @@
32
32
  "type": "boolean",
33
33
  "description": "Map the workspace read-only."
34
34
  },
35
+ "workspaceFilter": {
36
+ "type": "object",
37
+ "properties": {
38
+ "enabled": {
39
+ "type": "boolean",
40
+ "description": "Keep what your .gitignore lists (.env files, node_modules, build output ...) out of the sandbox. The sandbox works on a copy of the project without those files, kept in step with it both ways within about a second; what it creates that is ignored (its own node_modules, builds) stays in the sandbox. Git works as usual. Off = the project folder itself is mapped."
41
+ },
42
+ "exclude": {
43
+ "type": "array",
44
+ "items": {
45
+ "type": "string",
46
+ "maxLength": 500
47
+ },
48
+ "description": "Also keep these out, as .gitignore patterns, e.g. secrets/, *.pem, data/private/."
49
+ },
50
+ "include": {
51
+ "type": "array",
52
+ "items": {
53
+ "type": "string",
54
+ "maxLength": 500
55
+ },
56
+ "description": "Let these in although .gitignore lists them, e.g. .env.example, data/fixtures/."
57
+ }
58
+ },
59
+ "additionalProperties": false
60
+ },
61
+ "audit": {
62
+ "type": "object",
63
+ "properties": {
64
+ "enabled": {
65
+ "type": "boolean",
66
+ "description": "Keep an audit log on this PC of what happened in your sandboxes: programs started (with their command lines), files changed in the project, sessions, and what the repo guard caught. The sandbox cannot change it. Read it with sandbox audit."
67
+ },
68
+ "days": {
69
+ "type": "integer",
70
+ "minimum": 1,
71
+ "maximum": 365,
72
+ "description": "Days the audit log is kept."
73
+ },
74
+ "maxMb": {
75
+ "type": "integer",
76
+ "minimum": 5,
77
+ "maximum": 5000,
78
+ "description": "Most space the audit log may take, in MB (older days go first). Days before today are compressed."
79
+ },
80
+ "processes": {
81
+ "type": "boolean",
82
+ "description": "Record every program started in the sandbox with its command line (Windows process auditing; repeated runs are counted, not listed)."
83
+ },
84
+ "files": {
85
+ "type": "boolean",
86
+ "description": "Record the files changed in the project while the sandbox runs."
87
+ }
88
+ },
89
+ "additionalProperties": false
90
+ },
35
91
  "mappings": {
36
92
  "type": "array",
37
93
  "items": {
@@ -102,6 +158,12 @@
102
158
  ],
103
159
  "description": "Most of this PC's CPU the sandbox may use, in percent (5-100), e.g. 50 keeps half free for you; \"none\" = no cap (Windows Sandbox would take every processor). Covers everything this tool starts in the sandbox (agents, installs, builds, its terminal)."
104
160
  },
161
+ "pageFileMb": {
162
+ "type": "integer",
163
+ "minimum": 0,
164
+ "maximum": 262144,
165
+ "description": "Size of the sandbox's paging file in MB, created at full size when it starts so it never grows while builds run (8 GB of its disk always stays free). Windows Sandbox starts with about 1 GB: parallel builds then fail with \"The paging file is too small\", and Windows grows the file under load. 0 = leave Windows' own."
166
+ },
105
167
  "reserveHostMb": {
106
168
  "type": "integer",
107
169
  "minimum": 0,
@@ -139,6 +201,10 @@
139
201
  "headless": {
140
202
  "type": "boolean",
141
203
  "description": "Hide the sandbox window (sbx-show brings it back). A headless sandbox shuts down when its last session ends."
204
+ },
205
+ "keepAwake": {
206
+ "type": "boolean",
207
+ "description": "Keep this PC from sleeping while a sandbox runs, so agents left working overnight keep going (the screen may still turn off; closing a laptop lid still sleeps it)."
142
208
  }
143
209
  },
144
210
  "additionalProperties": false
@@ -159,6 +225,15 @@
159
225
  "type": "string",
160
226
  "pattern": "^[A-Za-z0-9_-]+$",
161
227
  "description": "Name of the persistent data set (C:\\AgentData) shared by all workspaces."
228
+ },
229
+ "markSandboxFiles": {
230
+ "type": "boolean",
231
+ "description": "Programs and scripts (.exe, .ps1, .bat, .cmd, .msi, .lnk ...) that appear or change in your project while a sandbox runs get Windows' \"downloaded from the internet\" mark, so SmartScreen and PowerShell ask before they run on this PC."
232
+ },
233
+ "warnDataGb": {
234
+ "type": "number",
235
+ "minimum": 0,
236
+ "description": "Say at launch when the persisted data (tools, databases, logins) is bigger than this many GB; 0 = never. sandbox data shows what takes the space."
162
237
  }
163
238
  },
164
239
  "additionalProperties": false
@@ -169,6 +244,59 @@
169
244
  "blockPrivateRanges": {
170
245
  "type": "boolean",
171
246
  "description": "Block your LAN (10/8, 172.16/12, 192.168/16, SMB) from inside the sandbox."
247
+ },
248
+ "allowOnly": {
249
+ "type": "array",
250
+ "items": {
251
+ "type": "string",
252
+ "maxLength": 253,
253
+ "pattern": "^(@developer|(\\*\\.)?([a-zA-Z0-9-]+\\.)*[a-zA-Z0-9-]+)$"
254
+ },
255
+ "description": "Only these hosts can be reached once the sandbox is set up (tools still download first). *.example.com covers its subdomains; @developer adds package registries, GitHub, toolchain downloads and the AI providers. Programs go through a proxy in the sandbox (HTTP_PROXY); anything else is blocked by its firewall. Empty = no limit."
256
+ },
257
+ "blockHosts": {
258
+ "type": "array",
259
+ "items": {
260
+ "type": "string",
261
+ "maxLength": 253,
262
+ "pattern": "^([a-zA-Z0-9-]+\\.)*[a-zA-Z0-9-]+$"
263
+ },
264
+ "description": "Never reachable from the sandbox, e.g. production servers or telemetry (each exact name; the sandbox's hosts file sends them nowhere)."
265
+ },
266
+ "forwardPorts": {
267
+ "type": "array",
268
+ "items": {
269
+ "type": "object",
270
+ "properties": {
271
+ "pc": {
272
+ "type": "integer",
273
+ "minimum": 1,
274
+ "maximum": 65535,
275
+ "description": "The port on this PC (localhost only)."
276
+ },
277
+ "sandbox": {
278
+ "type": "integer",
279
+ "minimum": 1,
280
+ "maximum": 65535,
281
+ "description": "The port inside the sandbox (a server there may listen on localhost only)."
282
+ }
283
+ },
284
+ "additionalProperties": false
285
+ },
286
+ "description": "Reach servers in the sandbox from this PC: http://localhost:<pc> here goes to <sandbox> inside, while it runs. Nothing else on your network can use them."
287
+ },
288
+ "dns": {
289
+ "type": "array",
290
+ "items": {
291
+ "type": "string",
292
+ "pattern": "^\\d{1,3}(\\.\\d{1,3}){3}$"
293
+ },
294
+ "description": "DNS servers for the sandbox instead of your network's, e.g. 1.1.1.1 and 9.9.9.9."
295
+ },
296
+ "proxy": {
297
+ "type": "string",
298
+ "pattern": "^(https?:\\/\\/[^\\s]+)?$",
299
+ "description": "An HTTP proxy the sandbox uses to reach the internet (HTTP_PROXY / HTTPS_PROXY for everything in it), e.g. http://proxy.corp:8080. Empty = none."
172
300
  }
173
301
  },
174
302
  "additionalProperties": false
@@ -216,6 +344,16 @@
216
344
  }
217
345
  },
218
346
  "additionalProperties": false
347
+ },
348
+ "conpty": {
349
+ "type": "object",
350
+ "properties": {
351
+ "enabled": {
352
+ "type": "boolean",
353
+ "description": "Agent sessions use Microsoft's newer pseudo console (conpty.dll, downloaded once) instead of the one built into Windows: no garbled screen after resizing, real scrollback for the mouse wheel. Off = the one built into Windows."
354
+ }
355
+ },
356
+ "additionalProperties": false
219
357
  }
220
358
  },
221
359
  "additionalProperties": false
@@ -725,6 +863,22 @@
725
863
  "enabled": {
726
864
  "type": "boolean",
727
865
  "description": "Portable VS Code inside the sandbox (code on PATH, desktop shortcut); its settings and extensions persist."
866
+ },
867
+ "setup": {
868
+ "type": "boolean",
869
+ "description": "Point VS Code at the sandbox's own tools (JDK, Python, Go, compilers, .NET, Git, PowerShell 7 and Git Bash terminals) and install the language extensions for the toolchains in use."
870
+ },
871
+ "recommended": {
872
+ "type": "boolean",
873
+ "description": "Also install the extensions a project recommends in its .vscode/extensions.json."
874
+ },
875
+ "extensions": {
876
+ "type": "array",
877
+ "items": {
878
+ "type": "string",
879
+ "pattern": "^[\\w-]+\\.[\\w-]+$"
880
+ },
881
+ "description": "More extensions to install in every sandbox, e.g. eamodio.gitlens, ms-azuretools.vscode-docker."
728
882
  }
729
883
  },
730
884
  "additionalProperties": false
@@ -999,6 +1153,141 @@
999
1153
  },
1000
1154
  "additionalProperties": false
1001
1155
  },
1156
+ "apps": {
1157
+ "type": "object",
1158
+ "properties": {
1159
+ "install": {
1160
+ "type": "array",
1161
+ "items": {
1162
+ "type": "string",
1163
+ "enum": [
1164
+ "notepadpp",
1165
+ "sublime",
1166
+ "neovim",
1167
+ "dbeaver",
1168
+ "pgadmin",
1169
+ "heidisql",
1170
+ "sqlitebrowser",
1171
+ "compass",
1172
+ "redisinsight",
1173
+ "dbgate",
1174
+ "beekeeper",
1175
+ "dynamodb-admin",
1176
+ "bruno",
1177
+ "insomnia",
1178
+ "sourcegit",
1179
+ "githubdesktop",
1180
+ "lazygit",
1181
+ "7zip",
1182
+ "winmerge",
1183
+ "everything",
1184
+ "sysinternals",
1185
+ "imhex",
1186
+ "chrome",
1187
+ "firefox",
1188
+ "godot",
1189
+ "blender",
1190
+ "love",
1191
+ "defold",
1192
+ "pixelorama",
1193
+ "tiled",
1194
+ "kubectl",
1195
+ "helm",
1196
+ "k9s",
1197
+ "terraform",
1198
+ "azure-cli",
1199
+ "gcloud",
1200
+ "aws-cli",
1201
+ "drawio",
1202
+ "pandoc",
1203
+ "mitmproxy",
1204
+ "xh",
1205
+ "grpcurl",
1206
+ "k6",
1207
+ "jq",
1208
+ "yq",
1209
+ "ripgrep",
1210
+ "fd",
1211
+ "bat",
1212
+ "fzf",
1213
+ "delta",
1214
+ "just",
1215
+ "hyperfine",
1216
+ "watchexec",
1217
+ "protoc",
1218
+ "ninja",
1219
+ "ffmpeg",
1220
+ "sqlite",
1221
+ "shellcheck",
1222
+ "gitleaks"
1223
+ ]
1224
+ },
1225
+ "description": "Apps from the catalogue to install in every sandbox (the Apps screen in the menu lists them by category). Each is downloaded once and kept."
1226
+ },
1227
+ "custom": {
1228
+ "type": "object",
1229
+ "propertyNames": {
1230
+ "type": "string",
1231
+ "pattern": "^[a-z0-9][a-z0-9-]{0,40}$"
1232
+ },
1233
+ "additionalProperties": {
1234
+ "type": "object",
1235
+ "properties": {
1236
+ "name": {
1237
+ "type": "string",
1238
+ "minLength": 1,
1239
+ "maxLength": 80,
1240
+ "description": "The name on the desktop shortcut, e.g. \"My Tool\"."
1241
+ },
1242
+ "github": {
1243
+ "description": "GitHub repository (owner/repo): the file comes from its latest release.",
1244
+ "type": "string",
1245
+ "pattern": "^[\\w.-]+\\/[\\w.-]+$"
1246
+ },
1247
+ "asset": {
1248
+ "description": "With github: a pattern (regular expression) matching the release file, e.g. windows.*x64\\.zip$",
1249
+ "type": "string",
1250
+ "maxLength": 200
1251
+ },
1252
+ "url": {
1253
+ "description": "Or a direct https:// download link (zip, 7z, exe or msi).",
1254
+ "type": "string",
1255
+ "format": "starts_with",
1256
+ "pattern": "^https:\\/\\/.*"
1257
+ },
1258
+ "sha256": {
1259
+ "description": "SHA-256 of the download (GitHub releases usually publish one; then this is not needed).",
1260
+ "type": "string",
1261
+ "pattern": "^[0-9a-fA-F]{64}$"
1262
+ },
1263
+ "unpack": {
1264
+ "description": "archive (zip, 7z, tar: the default), file (a single .exe), nsis (an installer 7-Zip can open) or msi.",
1265
+ "type": "string",
1266
+ "enum": [
1267
+ "archive",
1268
+ "file",
1269
+ "nsis",
1270
+ "msi"
1271
+ ]
1272
+ },
1273
+ "exe": {
1274
+ "type": "string",
1275
+ "minLength": 1,
1276
+ "maxLength": 200,
1277
+ "description": "The program to start (found anywhere in the download), e.g. mytool.exe."
1278
+ },
1279
+ "cli": {
1280
+ "description": "A command-line tool: its folder goes on PATH instead of getting a desktop shortcut.",
1281
+ "type": "boolean"
1282
+ }
1283
+ },
1284
+ "additionalProperties": false
1285
+ },
1286
+ "description": "Programs of your own: a GitHub release or a download link, and the program to start. Installed like the catalogue apps."
1287
+ }
1288
+ },
1289
+ "additionalProperties": false
1290
+ },
1002
1291
  "claudeProfile": {
1003
1292
  "type": "object",
1004
1293
  "properties": {
@@ -1032,11 +1321,23 @@
1032
1321
  },
1033
1322
  "syncHistory": {
1034
1323
  "type": "boolean",
1035
- "description": "Bring all conversation history in, and sync new history back (path-translated)."
1324
+ "description": "Bring conversation history in, and sync new history back (path-translated)."
1325
+ },
1326
+ "historyScope": {
1327
+ "type": "string",
1328
+ "enum": [
1329
+ "project",
1330
+ "all"
1331
+ ],
1332
+ "description": "Which history comes in: \"project\" = only the projects in this sandbox (other projects' conversations, file backups and prompts stay on this PC), \"all\" = everything. Only this sandbox's projects ever sync back."
1036
1333
  },
1037
1334
  "syncClaudeMem": {
1038
1335
  "type": "boolean",
1039
- "description": "Bring claude-mem data in, and its database back when the sandbox closes."
1336
+ "description": "Bring claude-mem data into the sandbox (its settings and backups stay on this PC)."
1337
+ },
1338
+ "syncClaudeMemBack": {
1339
+ "type": "boolean",
1340
+ "description": "Also copy claude-mem's database back to this PC when the sandbox closes. Off by default: what is in it is loaded into every Claude session on this PC, so a compromised agent could plant instructions there."
1040
1341
  }
1041
1342
  },
1042
1343
  "additionalProperties": false