windows-development-sandbox 1.9.0 → 1.10.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 (52) hide show
  1. package/config.example.json +2 -1
  2. package/dist/cli/commands/launch/launch.js +2 -0
  3. package/dist/cli/commands/vpn.js +1 -0
  4. package/dist/cli/commands/web.d.ts +5 -0
  5. package/dist/cli/commands/web.js +38 -0
  6. package/dist/core/config/defaults.js +1 -1
  7. package/dist/core/schema/config.d.ts +1 -0
  8. package/dist/core/schema/vpn.js +1 -1
  9. package/dist/core/schema/web.d.ts +1 -0
  10. package/dist/core/schema/web.js +4 -1
  11. package/dist/guest/boot/boot.js +3 -1
  12. package/dist/guest/governor/decide.d.ts +11 -1
  13. package/dist/guest/governor/decide.js +30 -1
  14. package/dist/guest/governor/governor.d.ts +3 -0
  15. package/dist/guest/governor/governor.js +18 -2
  16. package/dist/guest/network/firewall.d.ts +1 -0
  17. package/dist/guest/network/firewall.js +65 -23
  18. package/dist/guest/runtime/process.js +53 -5
  19. package/dist/guest/setup/claude-mem.js +12 -4
  20. package/dist/guest/toolchains/languages/node.js +29 -3
  21. package/dist/host/life/host-memory.d.ts +1 -0
  22. package/dist/host/life/host-memory.js +4 -1
  23. package/dist/host/life/watchdog.d.ts +2 -0
  24. package/dist/host/life/watchdog.js +19 -3
  25. package/dist/runtime/agent.js +14 -10
  26. package/dist/runtime/daemon.js +861 -711
  27. package/dist/runtime/db.js +14 -10
  28. package/dist/runtime/main.js +159 -41
  29. package/dist/runtime/netproxy.js +4 -3
  30. package/dist/runtime/view.js +4 -3
  31. package/dist/selftest/guest-boot.test.js +47 -1
  32. package/dist/selftest/heavy-load.test.js +27 -1
  33. package/dist/selftest/network.test.js +20 -1
  34. package/dist/selftest/support/fixtures.d.ts +1 -0
  35. package/dist/shared/io/copy-tree.d.ts +7 -0
  36. package/dist/shared/io/copy-tree.js +45 -0
  37. package/dist/shared/io/index.d.ts +2 -0
  38. package/dist/shared/io/index.js +2 -0
  39. package/dist/shared/io/robocopy-result.d.ts +6 -0
  40. package/dist/shared/io/robocopy-result.js +23 -0
  41. package/dist/shared/memory/policy.d.ts +5 -0
  42. package/dist/shared/memory/policy.js +8 -0
  43. package/dist/shared/network/firewall-rules.d.ts +8 -0
  44. package/dist/shared/network/firewall-rules.js +28 -0
  45. package/native/csharp/HcsHost/AssemblyInfo.cs +3 -3
  46. package/native/csharp/HostTools/AssemblyInfo.cs +3 -3
  47. package/native/csharp/HostTools/MemoryStatus.cs +128 -107
  48. package/native/scripts/memory-status.ps1 +10 -2
  49. package/package.json +1 -1
  50. package/sandbox/csharp/MemGov.cs +466 -398
  51. package/sandbox/scripts/new-shortcuts.ps1 +3 -1
  52. package/schemas/config.schema.json +6 -2
@@ -10,6 +10,7 @@ export declare const HostMemorySchema: z.ZodObject<{
10
10
  fixed: "fixed";
11
11
  }>>;
12
12
  pageFileMaxMb: z.ZodCatch<z.ZodNumber>;
13
+ diskQueue: z.ZodCatch<z.ZodOptional<z.ZodNumber>>;
13
14
  }, z.core.$strip>;
14
15
  export type HostMemory = z.output<typeof HostMemorySchema>;
15
16
  /** The parsed answer of memory-status, or null (not Windows, unreadable, nonsense). */
@@ -19,6 +19,8 @@ export const HostMemorySchema = z.object({
19
19
  pageFile: z.enum(['auto', 'fixed', 'none']).catch('auto'),
20
20
  /** Fixed: the total of its maximum sizes (MB). */
21
21
  pageFileMaxMb: z.number().int().nonnegative().catch(0),
22
+ /** The disks' queue length now (all disks together); absent when this PC has no such counter. */
23
+ diskQueue: z.number().nonnegative().max(100000).optional().catch(undefined),
22
24
  });
23
25
  const TIMEOUT_MS = 20000;
24
26
  /** The parsed answer of memory-status, or null (not Windows, unreadable, nonsense). */
@@ -91,7 +93,8 @@ export function hostMemoryLine(memory, at = new Date()) {
91
93
  '/' +
92
94
  memory.commitLimitMb +
93
95
  ' | page file ' +
94
- memory.pageFile);
96
+ memory.pageFile +
97
+ (memory.diskQueue === undefined ? '' : ' | disk queue ' + memory.diskQueue));
95
98
  }
96
99
  /**
97
100
  * Whether Windows Sandbox could push this PC past its commit limit (pure): the paging file is fixed or off and the
@@ -6,6 +6,8 @@ export interface WatchdogState {
6
6
  readonly calm: number;
7
7
  /** Seconds the CPU has been above the limit. */
8
8
  readonly cpuHighSeconds: number;
9
+ /** Seconds the disk queue has been above the limit. */
10
+ readonly diskHighSeconds: number;
9
11
  /** Time (ms) the last request went out. */
10
12
  readonly lastSentAt: number;
11
13
  }
@@ -3,7 +3,13 @@ import fs from 'node:fs';
3
3
  import path from 'node:path';
4
4
  import { HOST_THRESHOLDS } from '../../shared/memory/index.js';
5
5
  import { hostMemoryLine } from './host-memory.js';
6
- export const WATCHDOG_START = { low: false, calm: 0, cpuHighSeconds: 0, lastSentAt: 0 };
6
+ export const WATCHDOG_START = {
7
+ low: false,
8
+ calm: 0,
9
+ cpuHighSeconds: 0,
10
+ diskHighSeconds: 0,
11
+ lastSentAt: 0,
12
+ };
7
13
  /** The request is repeated this often while the PC stays low, and each one lasts a bit longer than that. */
8
14
  export const RESEND_MS = 30000;
9
15
  export const REQUEST_SECONDS = 90;
@@ -30,6 +36,10 @@ export function watchdogStep(prev, input) {
30
36
  const cpuHigh = input.cpuPercent !== null && input.cpuPercent >= t.cpuPercent ? prev.cpuHighSeconds + input.intervalSeconds : 0;
31
37
  if (!reason && cpuHigh >= t.cpuSeconds)
32
38
  reason = "this PC's CPU has been above " + t.cpuPercent + '% for ' + cpuHigh + ' seconds';
39
+ const diskQueue = input.memory?.diskQueue;
40
+ const diskHigh = diskQueue !== undefined && diskQueue >= t.diskQueue ? prev.diskHighSeconds + input.intervalSeconds : 0;
41
+ if (!reason && diskHigh >= t.diskSeconds)
42
+ reason = "this PC's disks have had a queue of " + diskQueue + ' for ' + diskHigh + ' seconds';
33
43
  const calmNow = input.memory ? lowReason(input.memory, input.reserveHostMb, t, 1.25) === '' : true;
34
44
  let low = prev.low;
35
45
  let calm = prev.calm;
@@ -38,7 +48,7 @@ export function watchdogStep(prev, input) {
38
48
  calm = 0;
39
49
  }
40
50
  else if (prev.low) {
41
- calm = calmNow && cpuHigh === 0 ? prev.calm + 1 : 0;
51
+ calm = calmNow && cpuHigh === 0 && diskHigh === 0 ? prev.calm + 1 : 0;
42
52
  if (calm >= t.calmSamples) {
43
53
  low = false;
44
54
  calm = 0;
@@ -46,7 +56,13 @@ export function watchdogStep(prev, input) {
46
56
  }
47
57
  const send = low && input.nowMs - prev.lastSentAt >= RESEND_MS;
48
58
  return {
49
- state: { low, calm, cpuHighSeconds: cpuHigh, lastSentAt: send ? input.nowMs : prev.lastSentAt },
59
+ state: {
60
+ low,
61
+ calm,
62
+ cpuHighSeconds: cpuHigh,
63
+ diskHighSeconds: diskHigh,
64
+ lastSentAt: send ? input.nowMs : prev.lastSentAt,
65
+ },
50
66
  send,
51
67
  reason: reason || (low ? 'this PC is still low' : ''),
52
68
  changed: low !== prev.low,
@@ -26562,16 +26562,17 @@ var ResumeSchema = external_exports.strictObject({
26562
26562
  // src/core/schema/web.ts
26563
26563
  var WebSchema = external_exports.strictObject({
26564
26564
  enabled: external_exports.boolean().describe(
26565
- "(Beta) Allow the web UI: your sandbox sessions in a browser on this PC. Off by default, and nothing starts it by itself yet: sandbox web start works either way and runs it until sandbox web stop (or a restart of the PC)."
26565
+ "(Beta) The web UI: your sandbox sessions in a browser on this PC (http://127.0.0.1:<port>, this PC only). On: every sandbox start also starts it in the background (it keeps running until sandbox web stop or a restart of the PC). Off: only sandbox web start runs it. Open it signed in with sandbox web open."
26566
26566
  ),
26567
- port: external_exports.number().int().min(1024).max(65535).describe("Port of the web UI on 127.0.0.1 (this PC only). Pick another when it is taken.")
26567
+ port: external_exports.number().int().min(1024).max(65535).describe("Port of the web UI on 127.0.0.1 (this PC only). Pick another when it is taken."),
26568
+ openOnLaunch: external_exports.boolean().describe("(Beta) With the web UI on: open it in your browser, already signed in, every time a sandbox starts.")
26568
26569
  });
26569
26570
 
26570
26571
  // src/core/schema/vpn.ts
26571
26572
  var VPN_PROVIDERS = ["nordvpn", "openvpn-file", "wireguard-file"];
26572
26573
  var VpnSchema = external_exports.strictObject({
26573
26574
  enabled: external_exports.boolean().describe(
26574
- "Send the sandbox's traffic through a VPN. Only the sandbox: your PC's own connection is untouched. Needs a saved login first (sandbox vpn login). The agent in the sandbox can read that login and can turn the tunnel off (it is the administrator there)."
26575
+ "(Alpha: not yet tried on a real PC) Send the sandbox's traffic through a VPN. Only the sandbox: your PC's own connection is untouched. Needs a saved login first (sandbox vpn login). The agent in the sandbox can read that login and can turn the tunnel off (it is the administrator there)."
26575
26576
  ),
26576
26577
  provider: external_exports.enum(VPN_PROVIDERS).describe(
26577
26578
  "nordvpn: a recommended NordVPN server is picked at every launch. openvpn-file: your own .ovpn file (configFile). wireguard-file: your own WireGuard .conf (configFile), e.g. from your company."
@@ -27359,12 +27360,6 @@ var githubAssetFromPage = async (repo, pattern, searchOlder) => {
27359
27360
  };
27360
27361
 
27361
27362
  // src/guest/runtime/process.ts
27362
- function exec(file2, args, options = {}) {
27363
- const result = spawnSync(file2, args, { stdio: "inherit", windowsHide: true, ...options });
27364
- if (result.error) throw result.error;
27365
- if (result.status !== 0)
27366
- throw new Error(path11.basename(file2) + " " + args.slice(0, 3).join(" ") + " failed with exit code " + result.status);
27367
- }
27368
27363
  async function execAsync(file2, args, options = {}) {
27369
27364
  const code = await spawnAsync(file2, args, { stdio: "inherit", ...options });
27370
27365
  if (code !== 0)
@@ -27375,7 +27370,16 @@ function execOutput(file2, args, options = {}) {
27375
27370
  return result.status === 0 ? String(result.stdout ?? "").trim() : "";
27376
27371
  }
27377
27372
  function runScript(name, args = []) {
27378
- exec(SYSTEM.powershell(), scriptArgs(name, args));
27373
+ const result = spawnSync(SYSTEM.powershell(), scriptArgs(name, args), {
27374
+ stdio: ["inherit", "inherit", "pipe"],
27375
+ encoding: "utf8",
27376
+ windowsHide: true
27377
+ });
27378
+ if (result.error) throw result.error;
27379
+ const errors = String(result.stderr ?? "").trim();
27380
+ if (errors) process.stderr.write(errors + "\n");
27381
+ if (result.status !== 0)
27382
+ throw new Error(name + " failed with exit code " + result.status + (errors ? ": " + errors.slice(0, 400) : ""));
27379
27383
  }
27380
27384
  function runScriptWithJson(name, param, data, args = []) {
27381
27385
  const dir = fs10.mkdtempSync(path11.join(os3.tmpdir(), "sbx-"));