windows-development-sandbox 1.18.3 → 1.18.4
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.
- package/dist/app/features/defs.d.ts +18 -0
- package/dist/app/features/defs.js +2 -1
- package/dist/app/features/sandbox/sandbox.def.d.ts +22 -0
- package/dist/app/features/sandbox/sandbox.def.js +41 -14
- package/dist/app/features/sandbox/sandbox.run.d.ts +20 -0
- package/dist/app/features/sandbox/sandbox.run.js +41 -1
- package/dist/app/registry.js +2 -1
- package/dist/bin/agent-sandbox.js +2 -2
- package/dist/bin/{chunk-FQWYCF6L.js → chunk-FRPLQLSY.js} +3453 -3145
- package/dist/bin/{chunk-M6XUQK53.js → chunk-MY7NLQQY.js} +1 -1
- package/dist/bin/{chunk-3G53NNZP.js → chunk-N4GXYTLV.js} +1 -1
- package/dist/bin/claude-host.js +2 -2
- package/dist/bin/claude-sbx.js +2 -2
- package/dist/bin/codex-sbx.js +2 -2
- package/dist/bin/gemini-sbx.js +2 -2
- package/dist/bin/sandbox.js +2 -2
- package/dist/bin/sbx-hide.js +2 -2
- package/dist/bin/sbx-show.js +2 -2
- package/dist/cli/commands/launch/launch.js +4 -1
- package/dist/cli/commands/launch/leftover-offer.d.ts +3 -0
- package/dist/cli/commands/launch/leftover-offer.js +26 -0
- package/dist/cli/commands/repair/checks/sandbox.js +29 -2
- package/dist/guest/daemon/heartbeat.d.ts +7 -2
- package/dist/guest/daemon/heartbeat.js +34 -15
- package/dist/guest/daemon/idle-shutdown.js +2 -0
- package/dist/guest/entries/daemon.js +1 -1
- package/dist/host/health/index.d.ts +1 -0
- package/dist/host/health/index.js +1 -0
- package/dist/host/health/leftovers.d.ts +3 -0
- package/dist/host/health/leftovers.js +40 -0
- package/dist/host/health/stale.d.ts +48 -0
- package/dist/host/health/stale.js +40 -0
- package/dist/host/life/crash-report.d.ts +1 -1
- package/dist/host/life/crash-report.js +5 -3
- package/dist/host/life/last-end.d.ts +7 -0
- package/dist/host/life/last-end.js +21 -0
- package/dist/host/life/life-record.d.ts +3 -0
- package/dist/host/life/life-record.js +2 -2
- package/dist/host/life/stop-events.d.ts +3 -1
- package/dist/host/life/stop-events.js +59 -0
- package/dist/host/life/stop-warning.js +3 -0
- package/dist/host/sandbox/cleanup.d.ts +37 -0
- package/dist/host/sandbox/cleanup.js +82 -0
- package/dist/host/sandbox/index.d.ts +1 -0
- package/dist/host/sandbox/index.js +1 -0
- package/dist/host/sandbox/platform.js +1 -1
- package/dist/runtime/agent.js +3 -1
- package/dist/runtime/daemon.js +368 -351
- package/dist/runtime/db.js +3 -1
- package/dist/runtime/main.js +3 -1
- package/dist/runtime/netproxy.js +3 -1
- package/dist/runtime/tool.js +3 -1
- package/dist/runtime/view.js +3 -1
- package/dist/selftest/heavy-load.test.js +119 -2
- package/dist/shared/channel/schema.d.ts +1 -0
- package/dist/shared/channel/schema.js +2 -1
- package/dist/shared/system.d.ts +1 -0
- package/dist/shared/system.js +1 -0
- package/dist/sync/watcher.js +9 -4
- package/dist/web/index.html +1 -1
- package/dist/web/{main-6XYO5FZC.js → main-GVZWWP4I.js} +62 -62
- package/native/csharp/HcsHost/AssemblyInfo.cs +3 -3
- package/native/csharp/HostTools/AssemblyInfo.cs +3 -3
- package/native/csharp/HostTools/Program.cs +5 -1
- package/native/csharp/StorageHelper/AssemblyInfo.cs +3 -3
- package/native/scripts/stop-events.ps1 +7 -3
- package/package.json +1 -1
|
@@ -17,6 +17,24 @@ const StopEventsSchema = z
|
|
|
17
17
|
.transform((p) => p ?? ''),
|
|
18
18
|
}))
|
|
19
19
|
.max(100);
|
|
20
|
+
/** The ways a sandbox can end that nobody asked for (the life record stores one of these). */
|
|
21
|
+
export const STOP_KINDS = [
|
|
22
|
+
'updates',
|
|
23
|
+
'restart',
|
|
24
|
+
'crash',
|
|
25
|
+
'sleep',
|
|
26
|
+
'memory',
|
|
27
|
+
'sandboxCrash',
|
|
28
|
+
/** The graphics driver reset (display 4101, a GPU LiveKernelEvent): vGPU and the GPU's shared memory. */
|
|
29
|
+
'gpu',
|
|
30
|
+
/** Hyper-V reported an error, or a program that holds the VM (vmwp.exe, vmmem) crashed. */
|
|
31
|
+
'hyperv',
|
|
32
|
+
/** Nothing was logged, but the sandbox was working and left no word of leaving. */
|
|
33
|
+
'vanished',
|
|
34
|
+
];
|
|
35
|
+
/** Windows live kernel events that are a graphics driver (GPU) reset or timeout. */
|
|
36
|
+
const GPU_LIVE_KERNEL = ['117', '141', '1a8'];
|
|
37
|
+
const VM_PROGRAMS = /vmwp\.exe|vmmem\w*|vmcompute|windowssandbox\w*/i;
|
|
20
38
|
const UPDATERS = /^(trustedinstaller|mousocoreworker|musnotification(ux)?|usoclient|wuauclt|tiworker|updateorchestrator)\.exe$/i;
|
|
21
39
|
/** The stop code in Kernel-Power 41's detail ("BugcheckCode 209" -> "0xD1"); null for 0 (no blue screen). */
|
|
22
40
|
export function bugcheckCode(detail) {
|
|
@@ -85,6 +103,47 @@ export function explainStop(events) {
|
|
|
85
103
|
},
|
|
86
104
|
];
|
|
87
105
|
}
|
|
106
|
+
// The graphics driver stopped responding and was reset: with vGPU the sandbox shares the GPU, so the VM goes too.
|
|
107
|
+
if (e.id === 4101 && p === 'display')
|
|
108
|
+
return [
|
|
109
|
+
{
|
|
110
|
+
kind: 'gpu',
|
|
111
|
+
at: e.at,
|
|
112
|
+
text: 'the graphics driver stopped responding and was reset while the sandbox used the GPU (vGPU)',
|
|
113
|
+
},
|
|
114
|
+
];
|
|
115
|
+
const programs = VM_PROGRAMS.exec(e.process)?.[0];
|
|
116
|
+
if (e.id === 1001 && p.includes('windows error reporting') && /LiveKernelEvent/i.test(e.process)) {
|
|
117
|
+
const code = /\bP1:\s*([0-9a-f]{2,4})\b/i.exec(e.process)?.[1]?.toLowerCase();
|
|
118
|
+
if (code && GPU_LIVE_KERNEL.includes(code))
|
|
119
|
+
return [
|
|
120
|
+
{
|
|
121
|
+
kind: 'gpu',
|
|
122
|
+
at: e.at,
|
|
123
|
+
text: 'the graphics driver reset (Windows live kernel event ' +
|
|
124
|
+
code +
|
|
125
|
+
') while the sandbox used the GPU (vGPU)',
|
|
126
|
+
},
|
|
127
|
+
];
|
|
128
|
+
}
|
|
129
|
+
if ((e.id === 1000 || e.id === 1001) &&
|
|
130
|
+
programs &&
|
|
131
|
+
/^(application error|windows error reporting)$/i.test(e.provider))
|
|
132
|
+
return [
|
|
133
|
+
{
|
|
134
|
+
kind: 'hyperv',
|
|
135
|
+
at: e.at,
|
|
136
|
+
text: 'a program that runs the sandbox VM (' + programs.toLowerCase() + ') crashed',
|
|
137
|
+
},
|
|
138
|
+
];
|
|
139
|
+
if (/^microsoft-windows-hyper-v-(worker|vmswitch|compute)/.test(p) && e.id !== 18590 && e.id !== 40001)
|
|
140
|
+
return [
|
|
141
|
+
{
|
|
142
|
+
kind: 'hyperv',
|
|
143
|
+
at: e.at,
|
|
144
|
+
text: 'Hyper-V, which runs the sandbox VM, reported an error (event ' + e.id + ')',
|
|
145
|
+
},
|
|
146
|
+
];
|
|
88
147
|
return [];
|
|
89
148
|
});
|
|
90
149
|
return causes.sort((a, b) => Date.parse(a.at) - Date.parse(b.at))[0] ?? null;
|
|
@@ -4,6 +4,9 @@ const ADVICE = {
|
|
|
4
4
|
crash: 'sandbox logs --crash prints what Windows logged and how much memory this PC had; Event Viewer > Windows Logs > System has the rest. A laptop also needs to stay plugged in.',
|
|
5
5
|
sleep: 'sandbox.keepAwake keeps this PC awake while a sandbox runs (closing a laptop lid still sleeps it). Check it is on, and that sleep is not forced by a policy.',
|
|
6
6
|
sandboxCrash: 'C:\\AgentData\\logs\\memory-*.log shows what used memory just before. Fewer builds at once inside the sandbox (e.g. cargo build.jobs) makes it less likely.',
|
|
7
|
+
gpu: 'The graphics driver reset while the sandbox used the GPU (vGPU). Turn vGPU off (sandbox config set sandbox.vGpu false) or lower the GPU load, and keep heavy builds and a GPU game apart.',
|
|
8
|
+
hyperv: 'Hyper-V, which runs the sandbox, reported an error. Closing other virtual machines, updating the graphics driver, or turning vGPU off usually helps; sandbox logs --crash has the events.',
|
|
9
|
+
vanished: 'If you did not close its window, this is the known pattern of heavy builds plus a GPU game with vGPU on: turn vGPU off or run fewer builds at once. sandbox logs --crash says what it was doing in its last seconds.',
|
|
7
10
|
memory: 'Give the sandbox less RAM (sandbox.maxMemoryMb, sandbox.reserveHostMb) or run fewer builds at once inside it.',
|
|
8
11
|
};
|
|
9
12
|
const when = (iso) => new Date(iso).toLocaleString(undefined, { weekday: 'short', hour: '2-digit', minute: '2-digit' });
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { LEFTOVER_IMAGES, type SandboxFacts, type SandboxJudgement } from '../health/stale.js';
|
|
2
|
+
/** taskkill arguments that end exactly these programs (pure). */
|
|
3
|
+
export declare function killArgs(pids: readonly number[]): string[];
|
|
4
|
+
/** Ends these Windows Sandbox programs (taskkill by absolute path). Returns false when taskkill failed. */
|
|
5
|
+
export declare function endSandboxPrograms(pids: readonly number[]): boolean;
|
|
6
|
+
/** Restarts the Windows Sandbox service (CmService) in an elevated PowerShell: Windows asks for permission. */
|
|
7
|
+
export declare function restartSandboxService(): boolean;
|
|
8
|
+
/** What the person is asked, and what is done about it: everything outside the decision is passed in. */
|
|
9
|
+
export interface CleanupDeps {
|
|
10
|
+
/** The facts now (a fresh process list each time). */
|
|
11
|
+
readonly facts: () => Promise<SandboxFacts>;
|
|
12
|
+
readonly end: (pids: readonly number[]) => boolean;
|
|
13
|
+
readonly restartService: () => boolean;
|
|
14
|
+
/** An interactive question: true for yes. */
|
|
15
|
+
readonly confirm: (q: {
|
|
16
|
+
title: string;
|
|
17
|
+
lines: string[];
|
|
18
|
+
yes: string;
|
|
19
|
+
}) => Promise<boolean>;
|
|
20
|
+
readonly say: (line: string) => void;
|
|
21
|
+
/** Also restart the service even when the programs ended (the caller said Windows still refuses). */
|
|
22
|
+
readonly alsoService?: boolean;
|
|
23
|
+
}
|
|
24
|
+
export type CleanupOutcome =
|
|
25
|
+
/** Nothing of the sandbox is on this PC. */
|
|
26
|
+
'nothing'
|
|
27
|
+
/** A live sandbox (or another user's): nothing was ended. */
|
|
28
|
+
| 'alive' | 'declined' | 'cleaned' | 'service-restarted' | 'failed';
|
|
29
|
+
/** The question for a judgement of "leftover" (pure). */
|
|
30
|
+
export declare function cleanupQuestion(j: SandboxJudgement): {
|
|
31
|
+
title: string;
|
|
32
|
+
lines: string[];
|
|
33
|
+
yes: string;
|
|
34
|
+
};
|
|
35
|
+
/** The whole clean-up: judge, ask, end, check, and (when needed and allowed) restart the service. */
|
|
36
|
+
export declare function cleanUpLeftovers(deps: CleanupDeps): Promise<CleanupOutcome>;
|
|
37
|
+
export { LEFTOVER_IMAGES };
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
// Windows Sandbox leaves its programs behind when the VM dies (a crash, a kill from the host side): the next start then
|
|
2
|
+
// fails ("the sandbox was signed out", or "already running"). This ends those leftovers, after asking, and when Windows
|
|
3
|
+
// still refuses, restarts the Windows Sandbox service (CmService), which needs administrator permission. The decisions
|
|
4
|
+
// are pure and the actions are injected, so tests never end a process.
|
|
5
|
+
import { spawnSync } from 'node:child_process';
|
|
6
|
+
import { SYSTEM } from '../../shared/system.js';
|
|
7
|
+
import { refuseInSelfTest } from '../test-guard.js';
|
|
8
|
+
import { nativeScript } from '../native.js';
|
|
9
|
+
import { judgeSandbox, LEFTOVER_IMAGES } from '../health/stale.js';
|
|
10
|
+
const HELPER_TIMEOUT_MS = 15000;
|
|
11
|
+
const SERVICE_TIMEOUT_MS = 120000;
|
|
12
|
+
/** taskkill arguments that end exactly these programs (pure). */
|
|
13
|
+
export function killArgs(pids) {
|
|
14
|
+
const safe = pids.filter((pid) => Number.isInteger(pid) && pid > 4);
|
|
15
|
+
return ['/F', ...safe.flatMap((pid) => ['/PID', String(pid)])];
|
|
16
|
+
}
|
|
17
|
+
/** Ends these Windows Sandbox programs (taskkill by absolute path). Returns false when taskkill failed. */
|
|
18
|
+
export function endSandboxPrograms(pids) {
|
|
19
|
+
refuseInSelfTest('end Windows Sandbox programs');
|
|
20
|
+
if (killArgs(pids).length <= 1)
|
|
21
|
+
return true;
|
|
22
|
+
const result = spawnSync(SYSTEM.taskkill(), killArgs(pids), { windowsHide: true, timeout: HELPER_TIMEOUT_MS });
|
|
23
|
+
return result.status === 0;
|
|
24
|
+
}
|
|
25
|
+
/** Restarts the Windows Sandbox service (CmService) in an elevated PowerShell: Windows asks for permission. */
|
|
26
|
+
export function restartSandboxService() {
|
|
27
|
+
refuseInSelfTest('restart the Windows Sandbox service');
|
|
28
|
+
const result = spawnSync(SYSTEM.powershell(), nativeScript('run-elevated.ps1', ['-Command', 'Restart-Service -Name CmService -Force']), { windowsHide: true, timeout: SERVICE_TIMEOUT_MS });
|
|
29
|
+
return result.status === 0;
|
|
30
|
+
}
|
|
31
|
+
const NAMES = (j) => j.images.join(', ');
|
|
32
|
+
/** The question for a judgement of "leftover" (pure). */
|
|
33
|
+
export function cleanupQuestion(j) {
|
|
34
|
+
return {
|
|
35
|
+
title: 'Close the leftover Windows Sandbox programs?',
|
|
36
|
+
lines: [
|
|
37
|
+
j.why,
|
|
38
|
+
'Windows Sandbox leaves its programs behind when the sandbox dies, and then refuses to start again.',
|
|
39
|
+
'Only these programs of your Windows session are closed: ' + NAMES(j) + '. Nothing else.',
|
|
40
|
+
],
|
|
41
|
+
yes: 'Close them',
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/** The whole clean-up: judge, ask, end, check, and (when needed and allowed) restart the service. */
|
|
45
|
+
export async function cleanUpLeftovers(deps) {
|
|
46
|
+
const first = judgeSandbox(await deps.facts());
|
|
47
|
+
if (first.state === 'none')
|
|
48
|
+
return 'nothing';
|
|
49
|
+
if (first.state !== 'leftover') {
|
|
50
|
+
deps.say(first.why + ' Nothing was closed.');
|
|
51
|
+
return 'alive';
|
|
52
|
+
}
|
|
53
|
+
if (!(await deps.confirm(cleanupQuestion(first))))
|
|
54
|
+
return 'declined';
|
|
55
|
+
const ended = deps.end(first.pids);
|
|
56
|
+
const after = judgeSandbox(await deps.facts());
|
|
57
|
+
const gone = after.state === 'none' || after.state === 'foreign';
|
|
58
|
+
if (gone && !deps.alsoService) {
|
|
59
|
+
deps.say('Closed ' + first.pids.length + ' leftover program(s). The sandbox can start again now.');
|
|
60
|
+
return 'cleaned';
|
|
61
|
+
}
|
|
62
|
+
if (!gone)
|
|
63
|
+
deps.say(ended ? 'Some programs are still there.' : 'Windows would not let them be closed.');
|
|
64
|
+
const restart = deps.alsoService === true ||
|
|
65
|
+
(await deps.confirm({
|
|
66
|
+
title: 'Restart the Windows Sandbox service?',
|
|
67
|
+
lines: [
|
|
68
|
+
'Windows still holds the old sandbox. Restarting its service (CmService) clears that.',
|
|
69
|
+
'Windows asks for administrator permission once.',
|
|
70
|
+
],
|
|
71
|
+
yes: 'Restart it',
|
|
72
|
+
}));
|
|
73
|
+
if (!restart)
|
|
74
|
+
return gone ? 'cleaned' : 'declined';
|
|
75
|
+
if (!deps.restartService()) {
|
|
76
|
+
deps.say('The service was not restarted (the permission was declined, or Windows refused).');
|
|
77
|
+
return 'failed';
|
|
78
|
+
}
|
|
79
|
+
deps.say('The Windows Sandbox service was restarted.');
|
|
80
|
+
return 'service-restarted';
|
|
81
|
+
}
|
|
82
|
+
export { LEFTOVER_IMAGES };
|
|
@@ -89,7 +89,7 @@ export async function confirmSandboxStarted(wsbPath, waitMs = START_WAIT_MS) {
|
|
|
89
89
|
if (Date.now() >= deadline)
|
|
90
90
|
fail('Windows Sandbox did not start within ' + Math.round(waitMs / 1000) + ' seconds.', 'Open ' +
|
|
91
91
|
wsbPath +
|
|
92
|
-
' yourself to see the message Windows gives, or run sandbox doctor to check the Windows Sandbox feature.');
|
|
92
|
+
' yourself to see the message Windows gives, or run sandbox doctor to check the Windows Sandbox feature. If Windows says the sandbox was signed out, a sandbox that died left its programs behind: run sandbox cleanup.');
|
|
93
93
|
await sleep(1000);
|
|
94
94
|
}
|
|
95
95
|
}
|
package/dist/runtime/agent.js
CHANGED
|
@@ -5541,6 +5541,7 @@ var SYSTEM = {
|
|
|
5541
5541
|
cmd: () => system32("cmd.exe"),
|
|
5542
5542
|
powershell: () => system32("WindowsPowerShell\\v1.0\\powershell.exe"),
|
|
5543
5543
|
tasklist: () => system32("tasklist.exe"),
|
|
5544
|
+
taskkill: () => system32("taskkill.exe"),
|
|
5544
5545
|
whoami: () => system32("whoami.exe"),
|
|
5545
5546
|
icacls: () => system32("icacls.exe"),
|
|
5546
5547
|
tar: () => system32("tar.exe"),
|
|
@@ -27319,7 +27320,8 @@ var HeartbeatSchema = external_exports.object({
|
|
|
27319
27320
|
topProcesses: external_exports.array(external_exports.object({ name: Text(64), privateMb: external_exports.number().int().min(0).max(1e7) })).max(5),
|
|
27320
27321
|
/** The disk queue this PC last reported while asking the sandbox to slow down. */
|
|
27321
27322
|
diskQueueReported: external_exports.number().min(0).max(1e5).optional(),
|
|
27322
|
-
|
|
27323
|
+
/** "ending": the sandbox is shutting down in order (idle shutdown, sandbox stop, Windows shutting down). */
|
|
27324
|
+
phase: external_exports.enum(["ready", "working", "ending"])
|
|
27323
27325
|
});
|
|
27324
27326
|
var GuestResponseSchema = external_exports.object({
|
|
27325
27327
|
id: IdSchema,
|