windows-development-sandbox 1.7.0 → 1.9.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.
- package/config.example.json +10 -0
- package/dist/app/context.d.ts +8 -0
- package/dist/app/context.js +12 -0
- package/dist/app/features/defs.d.ts +322 -0
- package/dist/app/features/defs.js +28 -0
- package/dist/app/features/sandbox/index.d.ts +2 -0
- package/dist/app/features/sandbox/index.js +3 -0
- package/dist/app/features/sandbox/sandbox.def.d.ts +193 -0
- package/dist/app/features/sandbox/sandbox.def.js +245 -0
- package/dist/app/features/sandbox/sandbox.run.d.ts +72 -0
- package/dist/app/features/sandbox/sandbox.run.js +98 -0
- package/dist/app/features/sessions/index.d.ts +2 -0
- package/dist/app/features/sessions/index.js +3 -0
- package/dist/app/features/sessions/sessions.def.d.ts +141 -0
- package/dist/app/features/sessions/sessions.def.js +176 -0
- package/dist/app/features/sessions/sessions.run.d.ts +39 -0
- package/dist/app/features/sessions/sessions.run.js +58 -0
- package/dist/{cli/menu/settings/categories.d.ts → app/features/settings/categories.def.d.ts} +2 -0
- package/dist/{cli/menu/settings/categories.js → app/features/settings/categories.def.js} +24 -5
- package/dist/app/features/settings/index.d.ts +5 -0
- package/dist/app/features/settings/index.js +6 -0
- package/dist/{cli/menu/settings/config-file.d.ts → app/features/settings/settings-file.d.ts} +1 -1
- package/dist/{cli/menu/settings/config-file.js → app/features/settings/settings-file.js} +1 -1
- package/dist/app/features/settings/settings-rows.d.ts +35 -0
- package/dist/app/features/settings/settings-rows.js +94 -0
- package/dist/app/features/settings/settings.def.d.ts +157 -0
- package/dist/app/features/settings/settings.def.js +179 -0
- package/dist/app/features/settings/settings.run.d.ts +67 -0
- package/dist/app/features/settings/settings.run.js +84 -0
- package/dist/app/kernel/define.d.ts +143 -0
- package/dist/app/kernel/define.js +42 -0
- package/dist/app/kernel/errors.d.ts +34 -0
- package/dist/app/kernel/errors.js +45 -0
- package/dist/app/kernel/fields.d.ts +70 -0
- package/dist/app/kernel/fields.js +243 -0
- package/dist/app/kernel/index.d.ts +8 -0
- package/dist/app/kernel/index.js +10 -0
- package/dist/app/kernel/io.d.ts +193 -0
- package/dist/app/kernel/io.js +126 -0
- package/dist/app/kernel/rpc.d.ts +156 -0
- package/dist/app/kernel/rpc.js +47 -0
- package/dist/app/kernel/text.d.ts +6 -0
- package/dist/app/kernel/text.js +57 -0
- package/dist/app/kernel/view.d.ts +146 -0
- package/dist/app/kernel/view.js +28 -0
- package/dist/app/registry.d.ts +10 -0
- package/dist/app/registry.js +50 -0
- package/dist/cli/args.d.ts +5 -0
- package/dist/cli/args.js +6 -0
- package/dist/cli/commands/engine.d.ts +2 -0
- package/dist/cli/commands/engine.js +60 -0
- package/dist/cli/commands/index.d.ts +1 -1
- package/dist/cli/commands/index.js +1 -1
- package/dist/cli/commands/launch/launch.js +47 -7
- package/dist/cli/commands/launch/prepare.js +3 -3
- package/dist/cli/commands/launch/running.js +2 -1
- package/dist/cli/commands/launch/summary.d.ts +2 -0
- package/dist/cli/commands/maintenance/config-share.d.ts +12 -0
- package/dist/cli/commands/maintenance/config-share.js +80 -0
- package/dist/cli/commands/maintenance/config.js +8 -82
- package/dist/cli/commands/maintenance/logs.js +14 -0
- package/dist/cli/commands/registry.js +3 -7
- package/dist/cli/commands/repair/checks/contained.d.ts +2 -0
- package/dist/cli/commands/repair/checks/contained.js +139 -0
- package/dist/cli/commands/repair/checks/hcs.d.ts +7 -0
- package/dist/cli/commands/repair/checks/hcs.js +28 -0
- package/dist/cli/commands/repair/checks/index.js +4 -0
- package/dist/cli/commands/repair/checks/pc.js +32 -1
- package/dist/cli/commands/repair/checks/sandbox.js +16 -1
- package/dist/cli/commands/repair/checks/vpn.js +34 -9
- package/dist/cli/commands/repair/env.js +2 -1
- package/dist/cli/commands/sessions/index.d.ts +0 -4
- package/dist/cli/commands/sessions/index.js +3 -7
- package/dist/cli/commands/shared-use.d.ts +5 -2
- package/dist/cli/commands/shared-use.js +11 -8
- package/dist/cli/commands/storage/contained.d.ts +3 -0
- package/dist/cli/commands/storage/contained.js +179 -0
- package/dist/cli/commands/storage/offer.js +7 -1
- package/dist/cli/commands/storage/storage.js +10 -2
- package/dist/cli/commands/user-config.js +1 -1
- package/dist/cli/commands/vpn.js +19 -3
- package/dist/cli/commands/web.js +2 -0
- package/dist/cli/index.js +2 -1
- package/dist/cli/manifest.d.ts +72 -104
- package/dist/cli/manifest.js +64 -39
- package/dist/cli/menu/add-agent.d.ts +1 -1
- package/dist/cli/menu/add-agent.js +1 -1
- package/dist/cli/menu/apps.d.ts +7 -1
- package/dist/cli/menu/apps.js +36 -0
- package/dist/cli/menu/command-fields.d.ts +1 -1
- package/dist/cli/menu/command-form.js +2 -2
- package/dist/cli/menu/io-menu.d.ts +29 -0
- package/dist/cli/menu/io-menu.js +126 -0
- package/dist/cli/menu/live.d.ts +27 -0
- package/dist/cli/menu/live.js +69 -0
- package/dist/cli/menu/memory.d.ts +19 -0
- package/dist/cli/menu/memory.js +53 -0
- package/dist/cli/menu/menu.d.ts +10 -1
- package/dist/cli/menu/menu.js +27 -9
- package/dist/cli/menu/pages/agents-page.d.ts +3 -1
- package/dist/cli/menu/pages/agents-page.js +12 -5
- package/dist/cli/menu/pages/apps-page.d.ts +3 -1
- package/dist/cli/menu/pages/apps-page.js +19 -2
- package/dist/cli/menu/pages/list-page.d.ts +27 -0
- package/dist/cli/menu/pages/list-page.js +109 -7
- package/dist/cli/menu/pages/maintenance-page.d.ts +0 -5
- package/dist/cli/menu/pages/maintenance-page.js +17 -19
- package/dist/cli/menu/pages/sandbox-page.d.ts +4 -6
- package/dist/cli/menu/pages/sandbox-page.js +26 -118
- package/dist/cli/menu/pages/scopes.d.ts +1 -1
- package/dist/cli/menu/pages/sessions-page.d.ts +5 -11
- package/dist/cli/menu/pages/sessions-page.js +19 -88
- package/dist/cli/menu/pages/settings-page.d.ts +2 -10
- package/dist/cli/menu/pages/settings-page.js +64 -94
- package/dist/cli/menu/render-view.d.ts +28 -0
- package/dist/cli/menu/render-view.js +112 -0
- package/dist/cli/menu/settings/{path-kind.d.ts → ask-path.d.ts} +1 -7
- package/dist/cli/menu/settings/ask-path.js +14 -0
- package/dist/cli/menu/settings/collections/collection-editor.d.ts +1 -1
- package/dist/cli/menu/settings/collections/string-list-editor.d.ts +1 -2
- package/dist/cli/menu/settings/collections/string-list-editor.js +1 -1
- package/dist/cli/menu/settings/draft.d.ts +1 -1
- package/dist/cli/menu/settings/format.d.ts +1 -3
- package/dist/cli/menu/settings/format.js +6 -36
- package/dist/cli/menu/settings/index.d.ts +1 -3
- package/dist/cli/menu/settings/index.js +3 -3
- package/dist/cli/menu/settings/object-editor.d.ts +1 -1
- package/dist/cli/menu/settings/share.d.ts +36 -0
- package/dist/cli/menu/settings/share.js +131 -0
- package/dist/cli/menu/settings/value-editor.d.ts +5 -1
- package/dist/cli/menu/settings/value-editor.js +57 -54
- package/dist/cli/menu/several.js +1 -1
- package/dist/cli/menu/share-settings.d.ts +21 -0
- package/dist/cli/menu/share-settings.js +112 -0
- package/dist/cli/menu/shell.d.ts +21 -5
- package/dist/cli/menu/shell.js +31 -6
- package/dist/cli/menu/views.d.ts +7 -0
- package/dist/cli/menu/views.js +11 -0
- package/dist/cli/menu/vpn-login.js +8 -0
- package/dist/cli/operations/handler.d.ts +6 -0
- package/dist/cli/operations/handler.js +61 -0
- package/dist/cli/operations/index.d.ts +4 -0
- package/dist/cli/operations/index.js +5 -0
- package/dist/cli/operations/input.d.ts +6 -0
- package/dist/cli/operations/input.js +37 -0
- package/dist/cli/operations/io-cli.d.ts +7 -0
- package/dist/cli/operations/io-cli.js +62 -0
- package/dist/cli/operations/spec.d.ts +18 -0
- package/dist/cli/operations/spec.js +108 -0
- package/dist/cli/output/colors.d.ts +2 -2
- package/dist/cli/output/colors.js +2 -11
- package/dist/cli/output/index.d.ts +1 -0
- package/dist/cli/output/index.js +1 -0
- package/dist/cli/output/print.d.ts +3 -1
- package/dist/cli/output/print.js +7 -3
- package/dist/cli/output/progress.d.ts +15 -0
- package/dist/cli/output/progress.js +44 -0
- package/dist/cli/output/render-view.d.ts +4 -0
- package/dist/cli/output/render-view.js +63 -0
- package/dist/cli/services/agent-commands-service.d.ts +2 -0
- package/dist/cli/services/agent-commands-service.js +11 -2
- package/dist/cli/services/logs-service.js +4 -3
- package/dist/cli/tui/app.d.ts +6 -1
- package/dist/cli/tui/app.js +26 -4
- package/dist/cli/tui/list/list-screen.d.ts +14 -0
- package/dist/cli/tui/list/list-screen.js +64 -36
- package/dist/cli/tui/list/list-types.d.ts +2 -0
- package/dist/cli/tui/terminal.d.ts +7 -0
- package/dist/cli/tui/terminal.js +30 -10
- package/dist/core/config/defaults.js +8 -0
- package/dist/core/schema/config.d.ts +13 -0
- package/dist/core/schema/config.js +16 -1
- package/dist/core/schema/harness.d.ts +1 -0
- package/dist/core/schema/harness.js +5 -0
- package/dist/core/schema/sandbox.d.ts +6 -0
- package/dist/core/schema/sandbox.js +20 -3
- package/dist/core/schema/vpn.d.ts +2 -1
- package/dist/core/schema/vpn.js +3 -3
- package/dist/core/types.d.ts +23 -5
- package/dist/core/util/fs.d.ts +2 -0
- package/dist/core/util/fs.js +9 -0
- package/dist/core/util/index.d.ts +1 -1
- package/dist/core/util/index.js +1 -1
- package/dist/guest/agents/agent-files.d.ts +5 -0
- package/dist/guest/agents/agent-files.js +4 -0
- package/dist/guest/agents/local-state.d.ts +2 -0
- package/dist/guest/agents/local-state.js +21 -1
- package/dist/guest/boot/boot.js +2 -1
- package/dist/guest/daemon/daemon.js +4 -0
- package/dist/guest/daemon/sessions.d.ts +5 -0
- package/dist/guest/daemon/sessions.js +17 -3
- package/dist/guest/entries/daemon.js +3 -0
- package/dist/guest/governor/decide.d.ts +61 -0
- package/dist/guest/governor/decide.js +182 -0
- package/dist/guest/governor/governor.d.ts +42 -0
- package/dist/guest/governor/governor.js +214 -0
- package/dist/guest/governor/index.d.ts +2 -0
- package/dist/guest/governor/index.js +2 -0
- package/dist/guest/runtime/csc.d.ts +1 -0
- package/dist/guest/runtime/csc.js +1 -0
- package/dist/guest/sessions/viewer.js +3 -1
- package/dist/guest/toolchains/native/rust-extras.js +4 -2
- package/dist/guest/vpn/vpn.js +69 -21
- package/dist/guest/vpn/wireguard.d.ts +32 -0
- package/dist/guest/vpn/wireguard.js +132 -0
- package/dist/host/agent-commands/commands.d.ts +4 -0
- package/dist/host/agent-commands/commands.js +31 -7
- package/dist/host/audit/log.d.ts +1 -1
- package/dist/host/channel/attach.d.ts +3 -1
- package/dist/host/channel/attach.js +5 -2
- package/dist/host/channel/channel.d.ts +2 -0
- package/dist/host/channel/channel.js +4 -0
- package/dist/host/engines/engine.d.ts +12 -0
- package/dist/host/engines/engine.js +1 -0
- package/dist/host/engines/hcs/check.d.ts +25 -0
- package/dist/host/engines/hcs/check.js +95 -0
- package/dist/host/engines/hcs/documents.d.ts +44 -0
- package/dist/host/engines/hcs/documents.js +155 -0
- package/dist/host/engines/hcs/engine.d.ts +39 -0
- package/dist/host/engines/hcs/engine.js +171 -0
- package/dist/host/engines/hcs/helper.d.ts +43 -0
- package/dist/host/engines/hcs/helper.js +173 -0
- package/dist/host/engines/hcs/images.d.ts +37 -0
- package/dist/host/engines/hcs/images.js +152 -0
- package/dist/host/engines/index.d.ts +7 -0
- package/dist/host/engines/index.js +5 -0
- package/dist/host/engines/registry.d.ts +16 -0
- package/dist/host/engines/registry.js +30 -0
- package/dist/host/engines/types.d.ts +58 -0
- package/dist/host/engines/types.js +1 -0
- package/dist/host/engines/windows-sandbox/engine.d.ts +16 -0
- package/dist/host/engines/windows-sandbox/engine.js +63 -0
- package/dist/host/engines/windows-sandbox/wsb-text.d.ts +2 -0
- package/dist/host/engines/windows-sandbox/wsb-text.js +29 -0
- package/dist/host/launch/generate.d.ts +5 -1
- package/dist/host/launch/generate.js +50 -8
- package/dist/host/launch/index.d.ts +3 -1
- package/dist/host/launch/index.js +3 -1
- package/dist/host/launch/launch-plan.d.ts +13 -0
- package/dist/host/launch/launch-plan.js +26 -0
- package/dist/host/launch/mappings.js +2 -1
- package/dist/host/launch/risk-notes.d.ts +10 -0
- package/dist/host/launch/risk-notes.js +18 -0
- package/dist/host/launch/session-file.d.ts +2 -0
- package/dist/host/launch/session-file.js +7 -1
- package/dist/host/launch/wsb-file.d.ts +28 -4
- package/dist/host/launch/wsb-file.js +61 -42
- package/dist/host/life/crash-report.d.ts +27 -1
- package/dist/host/life/crash-report.js +173 -7
- package/dist/host/life/host-memory.d.ts +40 -0
- package/dist/host/life/host-memory.js +137 -0
- package/dist/host/life/index.d.ts +2 -0
- package/dist/host/life/index.js +2 -0
- package/dist/host/life/stop-events.d.ts +2 -0
- package/dist/host/life/stop-events.js +20 -3
- package/dist/host/life/stop-warning.js +1 -1
- package/dist/host/life/watchdog.d.ts +54 -0
- package/dist/host/life/watchdog.js +108 -0
- package/dist/host/native.d.ts +3 -2
- package/dist/host/native.js +22 -3
- package/dist/host/profile/claude-login.js +3 -2
- package/dist/host/sandbox/contained-deps.d.ts +2 -0
- package/dist/host/sandbox/contained-deps.js +17 -0
- package/dist/host/sandbox/index.d.ts +2 -0
- package/dist/host/sandbox/index.js +2 -0
- package/dist/host/sandbox/platform.d.ts +2 -0
- package/dist/host/sandbox/platform.js +7 -7
- package/dist/host/sandbox/tasklist.d.ts +13 -0
- package/dist/host/sandbox/tasklist.js +47 -0
- package/dist/host/secrets/launch-secrets.js +2 -1
- package/dist/host/sharing/current.js +2 -6
- package/dist/host/storage/contained/check.d.ts +9 -0
- package/dist/host/storage/contained/check.js +37 -0
- package/dist/host/storage/contained/contained-storage.d.ts +66 -0
- package/dist/host/storage/contained/contained-storage.js +168 -0
- package/dist/host/storage/contained/disk-port.d.ts +43 -0
- package/dist/host/storage/contained/disk-port.js +122 -0
- package/dist/host/storage/contained/index.d.ts +9 -0
- package/dist/host/storage/contained/index.js +8 -0
- package/dist/host/storage/contained/layout.d.ts +28 -0
- package/dist/host/storage/contained/layout.js +45 -0
- package/dist/host/storage/contained/resolve.d.ts +4 -0
- package/dist/host/storage/contained/resolve.js +30 -0
- package/dist/host/storage/contained/setting.d.ts +1 -0
- package/dist/host/storage/contained/setting.js +5 -0
- package/dist/host/storage/contained/state.d.ts +89 -0
- package/dist/host/storage/contained/state.js +166 -0
- package/dist/host/storage/contained/transfer.d.ts +24 -0
- package/dist/host/storage/contained/transfer.js +121 -0
- package/dist/host/storage/index.d.ts +2 -0
- package/dist/host/storage/index.js +2 -0
- package/dist/host/storage/providers/contained.d.ts +15 -0
- package/dist/host/storage/providers/contained.js +39 -0
- package/dist/host/storage/providers/folder.d.ts +12 -0
- package/dist/host/storage/providers/folder.js +20 -0
- package/dist/host/storage/providers/index.d.ts +4 -0
- package/dist/host/storage/providers/index.js +3 -0
- package/dist/host/storage/providers/registry.d.ts +9 -0
- package/dist/host/storage/providers/registry.js +14 -0
- package/dist/host/storage/providers/types.d.ts +33 -0
- package/dist/host/storage/providers/types.js +1 -0
- package/dist/host/tools/conpty.js +3 -2
- package/dist/host/tools/windows-terminal.js +8 -7
- package/dist/host/vpn/index.d.ts +1 -1
- package/dist/host/vpn/index.js +1 -1
- package/dist/host/vpn/prepare.d.ts +2 -0
- package/dist/host/vpn/prepare.js +63 -2
- package/dist/host/web/browser.d.ts +2 -0
- package/dist/host/web/browser.js +14 -1
- package/dist/host/web/index.d.ts +3 -2
- package/dist/host/web/index.js +3 -2
- package/dist/host/web/rpc.d.ts +24 -0
- package/dist/host/web/rpc.js +149 -0
- package/dist/host/web/runner.d.ts +2 -0
- package/dist/host/web/runner.js +1 -2
- package/dist/host/web/server.d.ts +7 -0
- package/dist/host/web/server.js +23 -3
- package/dist/runtime/agent.js +69 -8
- package/dist/runtime/daemon.js +1058 -624
- package/dist/runtime/db.js +69 -8
- package/dist/runtime/main.js +624 -182
- package/dist/runtime/netproxy.js +69 -8
- package/dist/runtime/view.js +87 -13
- package/dist/selftest/agent-paths.test.js +11 -1
- package/dist/selftest/app.test.d.ts +2 -0
- package/dist/selftest/app.test.js +324 -0
- package/dist/selftest/channel.test.js +13 -0
- package/dist/selftest/cli.test.js +2 -1
- package/dist/selftest/config.test.js +59 -0
- package/dist/selftest/contained.test.d.ts +2 -0
- package/dist/selftest/contained.test.js +544 -0
- package/dist/selftest/credentials.test.js +1 -1
- package/dist/selftest/engine.test.d.ts +2 -0
- package/dist/selftest/engine.test.js +127 -0
- package/dist/selftest/hcs.test.d.ts +2 -0
- package/dist/selftest/hcs.test.js +482 -0
- package/dist/selftest/heavy-load.test.d.ts +2 -0
- package/dist/selftest/heavy-load.test.js +329 -0
- package/dist/selftest/index.js +12 -0
- package/dist/selftest/layout.test.js +12 -0
- package/dist/selftest/menu-perf.test.d.ts +2 -0
- package/dist/selftest/menu-perf.test.js +248 -0
- package/dist/selftest/menu-screens.js +4 -2
- package/dist/selftest/menu.test.js +36 -8
- package/dist/selftest/support/engine-cases.d.ts +8 -0
- package/dist/selftest/support/engine-cases.js +65 -0
- package/dist/selftest/support/engine-golden.d.ts +3 -0
- package/dist/selftest/support/engine-golden.js +66 -0
- package/dist/selftest/support/fixtures.d.ts +7 -1
- package/dist/selftest/support/hcs-probes.d.ts +17 -0
- package/dist/selftest/support/hcs-probes.js +41 -0
- package/dist/selftest/vpn.test.js +136 -3
- package/dist/selftest/web.test.js +101 -3
- package/dist/shared/channel/run-dir.d.ts +4 -1
- package/dist/shared/channel/run-dir.js +7 -1
- package/dist/shared/channel/schema.d.ts +35 -1
- package/dist/shared/channel/schema.js +27 -0
- package/dist/shared/channel/secrets.d.ts +1 -0
- package/dist/shared/channel/secrets.js +6 -0
- package/dist/shared/memory/index.d.ts +1 -0
- package/dist/shared/memory/index.js +1 -0
- package/dist/shared/memory/policy.d.ts +33 -0
- package/dist/shared/memory/policy.js +55 -0
- package/dist/shared/pty/output-filter.d.ts +14 -1
- package/dist/shared/pty/output-filter.js +22 -4
- package/dist/shared/pty/terminal.d.ts +4 -1
- package/dist/shared/pty/terminal.js +4 -2
- package/dist/shared/system.d.ts +1 -0
- package/dist/shared/system.js +1 -0
- package/dist/shared/vpn/download.d.ts +16 -0
- package/dist/shared/vpn/download.js +16 -0
- package/dist/shared/vpn/wireguard.d.ts +51 -0
- package/dist/shared/vpn/wireguard.js +227 -0
- package/dist/sync/sync-cycle.js +5 -5
- package/dist/sync/watcher.d.ts +13 -0
- package/dist/sync/watcher.js +92 -3
- package/dist/web/index.html +4 -3
- package/dist/web/main-NRRS5O6Z.js +157 -0
- package/dist/web/main-T5DMCHYV.css +1 -0
- package/native/csharp/HcsHost/AssemblyInfo.cs +14 -0
- package/native/csharp/HcsHost/Guard.cs +100 -0
- package/native/csharp/HcsHost/Hcs.cs +256 -0
- package/native/csharp/HcsHost/Json.cs +78 -0
- package/native/csharp/HcsHost/Program.cs +185 -0
- package/native/csharp/HcsHost/VirtualDisk.cs +95 -0
- package/native/csharp/HostTools/AssemblyInfo.cs +14 -0
- package/native/csharp/HostTools/MemoryStatus.cs +107 -0
- package/native/csharp/HostTools/Program.cs +58 -17
- package/native/scripts/check-contained.ps1 +28 -0
- package/native/scripts/contained-common.ps1 +241 -0
- package/native/scripts/dismount-contained.ps1 +66 -0
- package/native/scripts/memory-status.ps1 +34 -0
- package/native/scripts/mount-contained.ps1 +139 -0
- package/native/scripts/run-elevated.ps1 +17 -4
- package/native/scripts/stop-events.ps1 +23 -8
- package/package.json +9 -2
- package/sandbox/csharp/MemGov.cs +398 -0
- package/sandbox/csharp/PtyHost.cs +111 -5
- package/schemas/config.schema.json +60 -6
- package/schemas/sandbox-config.schema.json +3 -3
- package/dist/cli/commands/sessions/control.d.ts +0 -4
- package/dist/cli/commands/sessions/control.js +0 -66
- package/dist/cli/commands/sessions/list.d.ts +0 -2
- package/dist/cli/commands/sessions/list.js +0 -48
- package/dist/cli/commands/sessions/status.d.ts +0 -4
- package/dist/cli/commands/sessions/status.js +0 -114
- package/dist/cli/commands/sessions/window.d.ts +0 -3
- package/dist/cli/commands/sessions/window.js +0 -18
- package/dist/cli/menu/settings/path-kind.js +0 -44
- package/dist/web/main-CXESL347.js +0 -45
- package/dist/web/main-VLR7DVQ4.css +0 -1
- /package/dist/{cli/menu/settings → app/kernel}/schema-node.d.ts +0 -0
- /package/dist/{cli/menu/settings → app/kernel}/schema-node.js +0 -0
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
#Requires -Version 5.1
|
|
2
|
+
# Mounts the contained-storage disk image of one data set at its mount folder (no drive letter), creating and
|
|
3
|
+
# formatting the image the very first time. Run elevated (host/storage/contained starts it through run-elevated.ps1,
|
|
4
|
+
# one Windows permission prompt). The request is a JSON file: { set, storageRoot, image, mount, userSid, maxGb, create }.
|
|
5
|
+
# Safety, all checked again here: the image is <storage>\images\<set>.vhdx and the mount folder <storage>\mounts\<set>;
|
|
6
|
+
# no folder on the way is a link; both are yours; the image has no parent file and is not bigger than the limit; an
|
|
7
|
+
# existing image is only attached (never formatted, never deleted), and a new one is only made where no file is.
|
|
8
|
+
# Every diskpart command names this one image. -DryRun checks everything and prints the diskpart lines it would run
|
|
9
|
+
# (and does nothing else); the result of a real run goes to result.json next to the request file.
|
|
10
|
+
param(
|
|
11
|
+
[Parameter(Mandatory = $true)][string]$Json,
|
|
12
|
+
[string]$Sha256 = '',
|
|
13
|
+
[switch]$DryRun
|
|
14
|
+
)
|
|
15
|
+
$ErrorActionPreference = 'Stop'
|
|
16
|
+
. (Join-Path $PSScriptRoot 'contained-common.ps1')
|
|
17
|
+
|
|
18
|
+
$result = @{ ok = $false; message = ''; action = 'mount'; created = $false; alreadyMounted = $false }
|
|
19
|
+
|
|
20
|
+
function Invoke-Mount($Req, [bool]$Dry, [string]$TempDir) {
|
|
21
|
+
foreach ($name in @('maxGb', 'create')) {
|
|
22
|
+
if (-not ($Req.PSObject.Properties.Name -contains $name)) { throw "The request has no $name." }
|
|
23
|
+
}
|
|
24
|
+
if ($Req.maxGb -lt 8 -or $Req.maxGb -gt 4096) { throw 'The size limit must be between 8 and 4096 GB.' }
|
|
25
|
+
$create = [bool]$Req.create
|
|
26
|
+
Assert-NoLinksOnTheWay $Req $true
|
|
27
|
+
$root = $Req.storageRoot.TrimEnd('\')
|
|
28
|
+
$mountIsLink = Test-ReparsePoint $Req.mount
|
|
29
|
+
Assert-OwnedByUser $Req @($root, (Join-Path $root 'images'), (Join-Path $root 'mounts'))
|
|
30
|
+
# A mounted folder shows the image's own root, whose owner is not ours to judge; a plain folder is.
|
|
31
|
+
if (-not $mountIsLink) { Assert-OwnedByUser $Req @($Req.mount) }
|
|
32
|
+
$exists = Test-Path -LiteralPath $Req.image -PathType Leaf
|
|
33
|
+
if ($create -and $exists) { throw "An image exists already; refusing to create over it: $($Req.image)" }
|
|
34
|
+
if ((-not $create) -and (-not $exists)) { throw "The image is missing: $($Req.image)" }
|
|
35
|
+
$image = $Req.image
|
|
36
|
+
$mount = $Req.mount.TrimEnd('\')
|
|
37
|
+
|
|
38
|
+
if ($exists) {
|
|
39
|
+
Assert-OwnedByUser $Req @($image)
|
|
40
|
+
$state = Get-ImageState $image $Dry
|
|
41
|
+
if ($state.Attached) {
|
|
42
|
+
if ((Test-MountedAt $state $mount) -and $mountIsLink) {
|
|
43
|
+
$script:result.ok = $true
|
|
44
|
+
$script:result.alreadyMounted = $true
|
|
45
|
+
$script:result.message = 'The image is mounted already.'
|
|
46
|
+
Add-Note 'already mounted at the mount folder'
|
|
47
|
+
return
|
|
48
|
+
}
|
|
49
|
+
throw 'The image is attached somewhere else (Disk Management or another tool). Detach it there first.'
|
|
50
|
+
}
|
|
51
|
+
# Not attached, so its header can be read.
|
|
52
|
+
[void](Assert-PlainImage $Req $Dry)
|
|
53
|
+
}
|
|
54
|
+
if ($mountIsLink) { throw "The mount folder is a mount point already: $mount" }
|
|
55
|
+
if (@(Get-ChildItem -LiteralPath $mount -Force).Count -ne 0) { throw "The mount folder is not empty: $mount" }
|
|
56
|
+
$format = (New-Object IO.DriveInfo ([IO.Path]::GetPathRoot($mount))).DriveFormat
|
|
57
|
+
if ($format -ne 'NTFS') { throw "The mount folder must be on an NTFS drive (this one is $format): $mount" }
|
|
58
|
+
|
|
59
|
+
if ($exists) {
|
|
60
|
+
# Attach and mount the first partition of this image; nothing is created and nothing is formatted.
|
|
61
|
+
$lines = @(
|
|
62
|
+
"select vdisk file=""$image""",
|
|
63
|
+
'attach vdisk',
|
|
64
|
+
'select partition 1',
|
|
65
|
+
"assign mount=""$mount"""
|
|
66
|
+
)
|
|
67
|
+
} else {
|
|
68
|
+
if (-not $Dry) {
|
|
69
|
+
$drive = New-Object IO.DriveInfo ([IO.Path]::GetPathRoot($image))
|
|
70
|
+
if ($drive.AvailableFreeSpace -lt 1GB) { throw 'There is less than 1 GB free where the image would be made.' }
|
|
71
|
+
}
|
|
72
|
+
$megabytes = [int]$Req.maxGb * 1024
|
|
73
|
+
$lines = @(
|
|
74
|
+
"create vdisk file=""$image"" maximum=$megabytes type=expandable",
|
|
75
|
+
"select vdisk file=""$image""",
|
|
76
|
+
'attach vdisk',
|
|
77
|
+
'create partition primary',
|
|
78
|
+
'format fs=ntfs quick label=AgentData',
|
|
79
|
+
"assign mount=""$mount"""
|
|
80
|
+
)
|
|
81
|
+
}
|
|
82
|
+
try {
|
|
83
|
+
[void](Invoke-DiskPart $lines $TempDir $Dry)
|
|
84
|
+
if (-not $Dry) {
|
|
85
|
+
$after = Get-ImageState $image $false
|
|
86
|
+
if (-not $after.Attached -or -not (Test-MountedAt $after $mount)) { throw 'The image is attached but not mounted at the mount folder.' }
|
|
87
|
+
if (-not (Test-ReparsePoint $mount)) { throw 'The mount folder is not a mount point after mounting.' }
|
|
88
|
+
if (-not $exists) {
|
|
89
|
+
# The new volume's root is open to Users by default: only you and the system may use it.
|
|
90
|
+
$acl = New-Object Security.AccessControl.DirectorySecurity
|
|
91
|
+
$acl.SetAccessRuleProtection($true, $false)
|
|
92
|
+
$inherit = [Security.AccessControl.InheritanceFlags]'ContainerInherit, ObjectInherit'
|
|
93
|
+
foreach ($sid in @($Req.userSid, 'S-1-5-18')) {
|
|
94
|
+
$who = New-Object Security.Principal.SecurityIdentifier($sid)
|
|
95
|
+
$acl.AddAccessRule((New-Object Security.AccessControl.FileSystemAccessRule($who, 'FullControl', $inherit, 'None', 'Allow')))
|
|
96
|
+
}
|
|
97
|
+
Set-Acl -LiteralPath $mount -AclObject $acl
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
$script:result.ok = $true
|
|
101
|
+
$script:result.created = (-not $exists)
|
|
102
|
+
$script:result.message = if ($exists) { 'The image was mounted.' } else { 'The image was created and mounted.' }
|
|
103
|
+
} catch {
|
|
104
|
+
$problem = $_.Exception.Message
|
|
105
|
+
if (-not $Dry) {
|
|
106
|
+
# Leave the PC as it was: detach this image (only this one); a half-made new image is renamed, never deleted.
|
|
107
|
+
$now = $null
|
|
108
|
+
try { $now = Get-ImageState $image $false } catch { $now = $null }
|
|
109
|
+
if ((Test-Path -LiteralPath $image) -and ((-not $now) -or $now.Attached)) {
|
|
110
|
+
try { [void](Invoke-DiskPart @("select vdisk file=""$image""", 'detach vdisk') $TempDir $false) } catch { $problem += ' (it could not be detached again: ' + $_.Exception.Message + ')' }
|
|
111
|
+
}
|
|
112
|
+
if ((-not $exists) -and (Test-Path -LiteralPath $image)) {
|
|
113
|
+
$kept = $image + '.failed-' + (Get-Date -Format 'yyyyMMddHHmmss')
|
|
114
|
+
try { Rename-Item -LiteralPath $image -NewName (Split-Path -Leaf $kept); $problem += " (the unfinished image was kept as $kept)" } catch { $problem += ' (the unfinished image is still at ' + $image + ')' }
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
throw $problem
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
$mutex = $null
|
|
122
|
+
try {
|
|
123
|
+
$request = Read-ContainedRequest -JsonPath $Json -Sha256 $Sha256 -Dry $DryRun.IsPresent
|
|
124
|
+
Assert-ContainedRequest $request
|
|
125
|
+
if (-not $DryRun) {
|
|
126
|
+
if (-not (Test-IsAdministrator)) { throw 'Mounting a disk image needs administrator rights.' }
|
|
127
|
+
$mutex = Enter-ContainedLock
|
|
128
|
+
}
|
|
129
|
+
Invoke-Mount $request $DryRun.IsPresent (Split-Path -Parent $Json)
|
|
130
|
+
} catch {
|
|
131
|
+
$result.ok = $false
|
|
132
|
+
$result.message = $_.Exception.Message
|
|
133
|
+
} finally {
|
|
134
|
+
if ($mutex) { $mutex.ReleaseMutex(); $mutex.Dispose() }
|
|
135
|
+
}
|
|
136
|
+
Write-ContainedResult $result $Json $DryRun.IsPresent
|
|
137
|
+
if ($result.ok) { exit 0 }
|
|
138
|
+
if ($DryRun) { [Console]::Error.WriteLine($result.message) }
|
|
139
|
+
exit 1
|
|
@@ -1,11 +1,24 @@
|
|
|
1
1
|
#Requires -Version 5.1
|
|
2
|
-
# Runs one fixed PowerShell command in an elevated PowerShell (Windows asks for
|
|
3
|
-
# `sandbox setup` to turn on the Windows Sandbox feature.
|
|
4
|
-
|
|
2
|
+
# Runs one fixed PowerShell command, or one script of this package, in an elevated PowerShell (Windows asks for
|
|
3
|
+
# permission) and waits for it. -Command is used by `sandbox setup` to turn on the Windows Sandbox feature. -Script is
|
|
4
|
+
# used for contained storage: the script (a file of this package) gets -Json <request file> -Sha256 <its checksum>.
|
|
5
|
+
# Exits with the exit code of what it ran, or 1 when the permission was declined.
|
|
6
|
+
[CmdletBinding(DefaultParameterSetName = 'Command')]
|
|
7
|
+
param(
|
|
8
|
+
[Parameter(Mandatory = $true, ParameterSetName = 'Command')][string]$Command,
|
|
9
|
+
[Parameter(Mandatory = $true, ParameterSetName = 'Script')][string]$Script,
|
|
10
|
+
[Parameter(Mandatory = $true, ParameterSetName = 'Script')][string]$Json,
|
|
11
|
+
[Parameter(Mandatory = $true, ParameterSetName = 'Script')][string]$Sha256
|
|
12
|
+
)
|
|
5
13
|
$ErrorActionPreference = 'Stop'
|
|
6
14
|
try {
|
|
7
15
|
$exe = Join-Path $PSHOME 'powershell.exe'
|
|
8
|
-
|
|
16
|
+
if ($PSCmdlet.ParameterSetName -eq 'Script') {
|
|
17
|
+
$arguments = @('-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Bypass', '-File', ('"' + $Script + '"'), '-Json', ('"' + $Json + '"'), '-Sha256', $Sha256)
|
|
18
|
+
} else {
|
|
19
|
+
$arguments = @('-NoProfile', '-Command', $Command)
|
|
20
|
+
}
|
|
21
|
+
$p = Start-Process -FilePath $exe -Verb RunAs -Wait -PassThru -WindowStyle Hidden -ArgumentList $arguments
|
|
9
22
|
exit $p.ExitCode
|
|
10
23
|
} catch {
|
|
11
24
|
exit 1
|
|
@@ -7,28 +7,43 @@ $ErrorActionPreference = 'Stop'
|
|
|
7
7
|
$filters = @(
|
|
8
8
|
@{ LogName = 'System'; Id = 1074, 6008, 41, 42, 2004; StartTime = $Since },
|
|
9
9
|
@{ LogName = 'System'; Id = 1001; ProviderName = 'Microsoft-Windows-WER-SystemErrorReporting'; StartTime = $Since },
|
|
10
|
-
@{ LogName = 'Microsoft-Windows-Hyper-V-Worker-Admin'; Id = 18590, 40001; StartTime = $Since }
|
|
10
|
+
@{ LogName = 'Microsoft-Windows-Hyper-V-Worker-Admin'; Id = 18590, 40001; StartTime = $Since },
|
|
11
|
+
# Hardware errors Windows reported (WHEA: CPU, memory, bus, disk).
|
|
12
|
+
@{ LogName = 'System'; ProviderName = 'Microsoft-Windows-WHEA-Logger'; Id = 17, 18, 19, 47; StartTime = $Since },
|
|
13
|
+
# Any error or critical event of the Hyper-V worker and compute services (the VM's memory, start and stop).
|
|
14
|
+
@{ LogName = 'Microsoft-Windows-Hyper-V-Worker-Admin'; Level = 1, 2; StartTime = $Since },
|
|
15
|
+
@{ LogName = 'Microsoft-Windows-Hyper-V-Compute-Admin'; Level = 1, 2; StartTime = $Since },
|
|
16
|
+
# A crash report of the programs that hold the VM (kept only when it names one of them).
|
|
17
|
+
@{ LogName = 'Application'; Id = 1000, 1001; StartTime = $Since; OnlyVmPrograms = $true }
|
|
11
18
|
)
|
|
12
19
|
$maxMessage = 390
|
|
20
|
+
$maxResourceMessage = 1500
|
|
13
21
|
|
|
14
22
|
# What each event says beyond its id: the program that asked for a restart (1074), else the start of the message.
|
|
15
23
|
function Get-EventDetail($record) {
|
|
16
24
|
if ($record.Id -eq 1074) { return [string]$record.Properties[0].Value }
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
21
|
-
|
|
25
|
+
# The plain power events say nothing more; the resource-exhaustion event (2004) lists the biggest memory users.
|
|
26
|
+
# Kernel-Power 41 carries the stop code of a blue screen (0 when there was none: a freeze or power loss).
|
|
27
|
+
if ($record.Id -eq 41) { return 'BugcheckCode ' + [string]$record.Properties[0].Value }
|
|
28
|
+
if ($record.Id -in 42, 6008) { return '' }
|
|
29
|
+
$message = [string]$record.Message
|
|
30
|
+
$max = if ($record.Id -eq 2004) { $maxResourceMessage } else { $maxMessage }
|
|
31
|
+
return $message.Substring(0, [math]::Min($max, $message.Length))
|
|
22
32
|
}
|
|
23
33
|
|
|
24
34
|
$events = foreach ($filter in $filters) {
|
|
25
35
|
try {
|
|
26
|
-
|
|
36
|
+
$onlyVm = $filter.ContainsKey('OnlyVmPrograms')
|
|
37
|
+
$query = @{} + $filter
|
|
38
|
+
$query.Remove('OnlyVmPrograms')
|
|
39
|
+
Get-WinEvent -FilterHashtable $query -MaxEvents 100 | ForEach-Object {
|
|
40
|
+
$detail = Get-EventDetail $_
|
|
41
|
+
if ($onlyVm -and $detail -notmatch 'vmwp\.exe|vmmem|vmcompute|windowssandbox') { return }
|
|
27
42
|
[pscustomobject]@{
|
|
28
43
|
at = $_.TimeCreated.ToUniversalTime().ToString('o')
|
|
29
44
|
id = $_.Id
|
|
30
45
|
provider = $_.ProviderName
|
|
31
|
-
process =
|
|
46
|
+
process = $detail
|
|
32
47
|
}
|
|
33
48
|
}
|
|
34
49
|
} catch {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "windows-development-sandbox",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.0",
|
|
4
4
|
"description": "A disposable Windows Sandbox for development, with or without an AI agent (Claude Code, Codex, Gemini, opencode, ...): the folder you run it from is the workspace, toolchains and databases for your project install themselves, and nothing else on your PC is reachable.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -93,6 +93,13 @@
|
|
|
93
93
|
],
|
|
94
94
|
"devDependencies": {
|
|
95
95
|
"@eslint/js": "^9.39.5",
|
|
96
|
+
"@mantine/core": "^7.17.8",
|
|
97
|
+
"@mantine/form": "^7.17.8",
|
|
98
|
+
"@mantine/hooks": "^7.17.8",
|
|
99
|
+
"@mantine/modals": "^7.17.8",
|
|
100
|
+
"@mantine/notifications": "^7.17.8",
|
|
101
|
+
"@tabler/icons-react": "^3.49.0",
|
|
102
|
+
"@tanstack/react-query": "^5.104.3",
|
|
96
103
|
"@types/lodash": "^4.17.26",
|
|
97
104
|
"@types/node": "^22.0.0",
|
|
98
105
|
"@types/react": "^18.3.31",
|
|
@@ -100,9 +107,9 @@
|
|
|
100
107
|
"@types/ws": "^8.18.2",
|
|
101
108
|
"@xterm/addon-fit": "^0.11.0",
|
|
102
109
|
"@xterm/xterm": "^6.0.0",
|
|
103
|
-
"bootstrap": "^5.3.8",
|
|
104
110
|
"esbuild": "^0.28.2",
|
|
105
111
|
"eslint": "^9.39.5",
|
|
112
|
+
"mantine-form-zod-resolver": "^1.3.0",
|
|
106
113
|
"prettier": "^3.9.10",
|
|
107
114
|
"react": "^18.3.1",
|
|
108
115
|
"react-dom": "^18.3.1",
|
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
// memgov.exe: the Windows side of the sandbox's memory governor (src/guest/governor). One long-running process the
|
|
2
|
+
// daemon talks to over stdin/stdout, so the decisions stay in TypeScript and only the Win32 calls live here. C# 5.
|
|
3
|
+
//
|
|
4
|
+
// stdout, a JSON line every --interval seconds (default 2):
|
|
5
|
+
// {"ramTotalMb":..,"ramAvailMb":..,"commitLimitMb":..,"commitAvailMb":..,"procs":[{"pid":..,"name":"rustc","mb":..,"prio":"normal"}]}
|
|
6
|
+
// procs = the processes in the agents' job (the job ptyhost makes for everything the agents start), nothing else.
|
|
7
|
+
// stdin, one command per line:
|
|
8
|
+
// priority <pid> <idle|below|normal> lowers (or restores) the priority of a process in that job
|
|
9
|
+
// suspend <pid> <ms> pauses a process in that job, resumes it after ms (at most 30000)
|
|
10
|
+
// exit
|
|
11
|
+
//
|
|
12
|
+
// It only ever touches processes that are in the agents' job, never itself, and resumes everything it paused when it
|
|
13
|
+
// ends or when the daemon goes away (stdin closes).
|
|
14
|
+
using System;
|
|
15
|
+
using System.Collections.Generic;
|
|
16
|
+
using System.ComponentModel;
|
|
17
|
+
using System.Diagnostics;
|
|
18
|
+
using System.Globalization;
|
|
19
|
+
using System.IO;
|
|
20
|
+
using System.Runtime.InteropServices;
|
|
21
|
+
using System.Text;
|
|
22
|
+
using System.Threading;
|
|
23
|
+
|
|
24
|
+
static class MemGov
|
|
25
|
+
{
|
|
26
|
+
private const string AgentJobName = "Local\\AgentSandboxAgentsJob";
|
|
27
|
+
private const double BytesPerMb = 1024.0 * 1024.0;
|
|
28
|
+
private const int MaxProcesses = 1024;
|
|
29
|
+
private const int MaxSuspendMs = 30000;
|
|
30
|
+
private const uint JobObjectQuery = 0x0004;
|
|
31
|
+
private const int JobObjectBasicProcessIdList = 3;
|
|
32
|
+
private const uint ProcessQueryLimited = 0x1000;
|
|
33
|
+
private const uint ProcessSetInformation = 0x0200;
|
|
34
|
+
private const uint ProcessSuspendResume = 0x0800;
|
|
35
|
+
private const uint IdleClass = 0x00000040;
|
|
36
|
+
private const uint BelowNormalClass = 0x00004000;
|
|
37
|
+
private const uint NormalClass = 0x00000020;
|
|
38
|
+
|
|
39
|
+
// MEMORYSTATUSEX.
|
|
40
|
+
[StructLayout(LayoutKind.Sequential)]
|
|
41
|
+
private struct MemoryStatus
|
|
42
|
+
{
|
|
43
|
+
public uint Length;
|
|
44
|
+
public uint MemoryLoad;
|
|
45
|
+
public ulong TotalPhys;
|
|
46
|
+
public ulong AvailPhys;
|
|
47
|
+
public ulong TotalPageFile;
|
|
48
|
+
public ulong AvailPageFile;
|
|
49
|
+
public ulong TotalVirtual;
|
|
50
|
+
public ulong AvailVirtual;
|
|
51
|
+
public ulong AvailExtendedVirtual;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
private static class NativeMethods
|
|
55
|
+
{
|
|
56
|
+
[DllImport("kernel32.dll", SetLastError = true)]
|
|
57
|
+
public static extern bool GlobalMemoryStatusEx(ref MemoryStatus status);
|
|
58
|
+
|
|
59
|
+
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
|
|
60
|
+
public static extern IntPtr OpenJobObjectW(uint access, bool inherit, string name);
|
|
61
|
+
|
|
62
|
+
[DllImport("kernel32.dll", SetLastError = true)]
|
|
63
|
+
public static extern bool QueryInformationJobObject(IntPtr job, int infoClass, IntPtr info, int length, IntPtr returnLength);
|
|
64
|
+
|
|
65
|
+
[DllImport("kernel32.dll", SetLastError = true)]
|
|
66
|
+
public static extern bool IsProcessInJob(IntPtr process, IntPtr job, out bool result);
|
|
67
|
+
|
|
68
|
+
[DllImport("kernel32.dll", SetLastError = true)]
|
|
69
|
+
public static extern IntPtr OpenProcess(uint access, bool inherit, int pid);
|
|
70
|
+
|
|
71
|
+
[DllImport("kernel32.dll", SetLastError = true)]
|
|
72
|
+
public static extern bool SetPriorityClass(IntPtr process, uint priorityClass);
|
|
73
|
+
|
|
74
|
+
[DllImport("kernel32.dll", SetLastError = true)]
|
|
75
|
+
public static extern uint GetPriorityClass(IntPtr process);
|
|
76
|
+
|
|
77
|
+
[DllImport("kernel32.dll", SetLastError = true)]
|
|
78
|
+
public static extern bool CloseHandle(IntPtr handle);
|
|
79
|
+
|
|
80
|
+
[DllImport("ntdll.dll")]
|
|
81
|
+
public static extern int NtSuspendProcess(IntPtr process);
|
|
82
|
+
|
|
83
|
+
[DllImport("ntdll.dll")]
|
|
84
|
+
public static extern int NtResumeProcess(IntPtr process);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
private static readonly object Gate = new object();
|
|
88
|
+
private static readonly Dictionary<int, DateTime> Paused = new Dictionary<int, DateTime>();
|
|
89
|
+
private static IntPtr job = IntPtr.Zero;
|
|
90
|
+
private static volatile bool stop;
|
|
91
|
+
|
|
92
|
+
static int Main(string[] args)
|
|
93
|
+
{
|
|
94
|
+
if (args.Length == 0 || args[0] != "serve")
|
|
95
|
+
{
|
|
96
|
+
Console.Error.WriteLine("usage: memgov serve [--interval seconds]");
|
|
97
|
+
return 2;
|
|
98
|
+
}
|
|
99
|
+
int interval = 2;
|
|
100
|
+
for (int i = 1; i + 1 < args.Length; i++)
|
|
101
|
+
{
|
|
102
|
+
if (args[i] == "--interval")
|
|
103
|
+
{
|
|
104
|
+
int.TryParse(args[i + 1], NumberStyles.Integer, CultureInfo.InvariantCulture, out interval);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
if (interval < 1)
|
|
108
|
+
{
|
|
109
|
+
interval = 2;
|
|
110
|
+
}
|
|
111
|
+
Thread commands = new Thread(ReadCommands);
|
|
112
|
+
commands.IsBackground = true;
|
|
113
|
+
commands.Start();
|
|
114
|
+
Thread resumer = new Thread(ResumeWhenDue);
|
|
115
|
+
resumer.IsBackground = true;
|
|
116
|
+
resumer.Start();
|
|
117
|
+
while (!stop)
|
|
118
|
+
{
|
|
119
|
+
try
|
|
120
|
+
{
|
|
121
|
+
Console.Out.WriteLine(Sample());
|
|
122
|
+
Console.Out.Flush();
|
|
123
|
+
}
|
|
124
|
+
catch (Exception)
|
|
125
|
+
{
|
|
126
|
+
// A sample that fails (a process ended while it was read) is skipped; the next one tries again.
|
|
127
|
+
}
|
|
128
|
+
for (int waited = 0; waited < interval * 10 && !stop; waited++)
|
|
129
|
+
{
|
|
130
|
+
Thread.Sleep(100);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
ResumeAll();
|
|
134
|
+
return 0;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/// <summary>The job, opened once it exists (ptyhost makes it when the first agent starts).</summary>
|
|
138
|
+
private static IntPtr Job()
|
|
139
|
+
{
|
|
140
|
+
if (job == IntPtr.Zero)
|
|
141
|
+
{
|
|
142
|
+
job = NativeMethods.OpenJobObjectW(JobObjectQuery, false, AgentJobName);
|
|
143
|
+
}
|
|
144
|
+
return job;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
private static string Sample()
|
|
148
|
+
{
|
|
149
|
+
MemoryStatus status = new MemoryStatus();
|
|
150
|
+
status.Length = (uint)Marshal.SizeOf(typeof(MemoryStatus));
|
|
151
|
+
if (!NativeMethods.GlobalMemoryStatusEx(ref status))
|
|
152
|
+
{
|
|
153
|
+
throw new Win32Exception(Marshal.GetLastWin32Error());
|
|
154
|
+
}
|
|
155
|
+
StringBuilder sb = new StringBuilder();
|
|
156
|
+
sb.Append("{\"ramTotalMb\":").Append(Mb(status.TotalPhys));
|
|
157
|
+
sb.Append(",\"ramAvailMb\":").Append(Mb(status.AvailPhys));
|
|
158
|
+
sb.Append(",\"commitLimitMb\":").Append(Mb(status.TotalPageFile));
|
|
159
|
+
sb.Append(",\"commitAvailMb\":").Append(Mb(status.AvailPageFile));
|
|
160
|
+
sb.Append(",\"procs\":[");
|
|
161
|
+
bool first = true;
|
|
162
|
+
int self = Process.GetCurrentProcess().Id;
|
|
163
|
+
foreach (int pid in JobProcessIds())
|
|
164
|
+
{
|
|
165
|
+
if (pid == self)
|
|
166
|
+
{
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
try
|
|
170
|
+
{
|
|
171
|
+
using (Process p = Process.GetProcessById(pid))
|
|
172
|
+
{
|
|
173
|
+
if (!first)
|
|
174
|
+
{
|
|
175
|
+
sb.Append(',');
|
|
176
|
+
}
|
|
177
|
+
first = false;
|
|
178
|
+
sb.Append("{\"pid\":").Append(pid.ToString(CultureInfo.InvariantCulture));
|
|
179
|
+
sb.Append(",\"name\":").Append(Json(p.ProcessName));
|
|
180
|
+
sb.Append(",\"mb\":").Append(Mb((ulong)p.PrivateMemorySize64));
|
|
181
|
+
sb.Append(",\"prio\":\"").Append(PriorityName(pid)).Append("\"}");
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
catch (Exception)
|
|
185
|
+
{
|
|
186
|
+
// The process ended while it was read.
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return sb.Append("]}").ToString();
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
private static List<int> JobProcessIds()
|
|
193
|
+
{
|
|
194
|
+
List<int> ids = new List<int>();
|
|
195
|
+
IntPtr handle = Job();
|
|
196
|
+
if (handle == IntPtr.Zero)
|
|
197
|
+
{
|
|
198
|
+
return ids;
|
|
199
|
+
}
|
|
200
|
+
// JOBOBJECT_BASIC_PROCESS_ID_LIST: two counts, then the ids (pointer-sized).
|
|
201
|
+
int length = 8 + IntPtr.Size * MaxProcesses;
|
|
202
|
+
IntPtr buffer = Marshal.AllocHGlobal(length);
|
|
203
|
+
try
|
|
204
|
+
{
|
|
205
|
+
if (!NativeMethods.QueryInformationJobObject(handle, JobObjectBasicProcessIdList, buffer, length, IntPtr.Zero))
|
|
206
|
+
{
|
|
207
|
+
return ids;
|
|
208
|
+
}
|
|
209
|
+
int count = Marshal.ReadInt32(buffer, 4);
|
|
210
|
+
for (int i = 0; i < count && i < MaxProcesses; i++)
|
|
211
|
+
{
|
|
212
|
+
ids.Add((int)Marshal.ReadIntPtr(buffer, 8 + i * IntPtr.Size).ToInt64());
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
finally
|
|
216
|
+
{
|
|
217
|
+
Marshal.FreeHGlobal(buffer);
|
|
218
|
+
}
|
|
219
|
+
return ids;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
private static string PriorityName(int pid)
|
|
223
|
+
{
|
|
224
|
+
IntPtr process = NativeMethods.OpenProcess(ProcessQueryLimited, false, pid);
|
|
225
|
+
if (process == IntPtr.Zero)
|
|
226
|
+
{
|
|
227
|
+
return "normal";
|
|
228
|
+
}
|
|
229
|
+
try
|
|
230
|
+
{
|
|
231
|
+
uint c = NativeMethods.GetPriorityClass(process);
|
|
232
|
+
return c == IdleClass ? "idle" : c == BelowNormalClass ? "below" : "normal";
|
|
233
|
+
}
|
|
234
|
+
finally
|
|
235
|
+
{
|
|
236
|
+
NativeMethods.CloseHandle(process);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/// <summary>Whether pid is a process of the agents' job and not this one: the only ones this program touches.</summary>
|
|
241
|
+
private static bool Allowed(int pid)
|
|
242
|
+
{
|
|
243
|
+
return pid != Process.GetCurrentProcess().Id && JobProcessIds().Contains(pid);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
private static void ReadCommands()
|
|
247
|
+
{
|
|
248
|
+
string line;
|
|
249
|
+
while ((line = Console.In.ReadLine()) != null)
|
|
250
|
+
{
|
|
251
|
+
try
|
|
252
|
+
{
|
|
253
|
+
Run(line.Trim().Split(' '));
|
|
254
|
+
}
|
|
255
|
+
catch (Exception)
|
|
256
|
+
{
|
|
257
|
+
// A bad command is ignored.
|
|
258
|
+
}
|
|
259
|
+
if (stop)
|
|
260
|
+
{
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
// The daemon is gone: put everything back and end.
|
|
265
|
+
stop = true;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
private static void Run(string[] parts)
|
|
269
|
+
{
|
|
270
|
+
if (parts.Length == 1 && parts[0] == "exit")
|
|
271
|
+
{
|
|
272
|
+
stop = true;
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
int pid;
|
|
276
|
+
if (parts.Length < 3 || !int.TryParse(parts[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out pid) || !Allowed(pid))
|
|
277
|
+
{
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
if (parts[0] == "priority")
|
|
281
|
+
{
|
|
282
|
+
uint cls = parts[2] == "idle" ? IdleClass : parts[2] == "below" ? BelowNormalClass : NormalClass;
|
|
283
|
+
IntPtr process = NativeMethods.OpenProcess(ProcessSetInformation, false, pid);
|
|
284
|
+
if (process != IntPtr.Zero)
|
|
285
|
+
{
|
|
286
|
+
NativeMethods.SetPriorityClass(process, cls);
|
|
287
|
+
NativeMethods.CloseHandle(process);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
else if (parts[0] == "suspend")
|
|
291
|
+
{
|
|
292
|
+
int ms;
|
|
293
|
+
if (!int.TryParse(parts[2], NumberStyles.Integer, CultureInfo.InvariantCulture, out ms) || ms < 1)
|
|
294
|
+
{
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
Suspend(pid, Math.Min(ms, MaxSuspendMs));
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
private static void Suspend(int pid, int ms)
|
|
302
|
+
{
|
|
303
|
+
lock (Gate)
|
|
304
|
+
{
|
|
305
|
+
if (Paused.ContainsKey(pid))
|
|
306
|
+
{
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
IntPtr process = NativeMethods.OpenProcess(ProcessSuspendResume, false, pid);
|
|
310
|
+
if (process == IntPtr.Zero)
|
|
311
|
+
{
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
try
|
|
315
|
+
{
|
|
316
|
+
NativeMethods.NtSuspendProcess(process);
|
|
317
|
+
Paused[pid] = DateTime.UtcNow.AddMilliseconds(ms);
|
|
318
|
+
}
|
|
319
|
+
finally
|
|
320
|
+
{
|
|
321
|
+
NativeMethods.CloseHandle(process);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
private static void Resume(int pid)
|
|
327
|
+
{
|
|
328
|
+
IntPtr process = NativeMethods.OpenProcess(ProcessSuspendResume, false, pid);
|
|
329
|
+
if (process != IntPtr.Zero)
|
|
330
|
+
{
|
|
331
|
+
NativeMethods.NtResumeProcess(process);
|
|
332
|
+
NativeMethods.CloseHandle(process);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/// <summary>A paused process always comes back: when its time is up (checked every 100 ms).</summary>
|
|
337
|
+
private static void ResumeWhenDue()
|
|
338
|
+
{
|
|
339
|
+
while (true)
|
|
340
|
+
{
|
|
341
|
+
Thread.Sleep(100);
|
|
342
|
+
lock (Gate)
|
|
343
|
+
{
|
|
344
|
+
List<int> due = new List<int>();
|
|
345
|
+
foreach (KeyValuePair<int, DateTime> p in Paused)
|
|
346
|
+
{
|
|
347
|
+
if (p.Value <= DateTime.UtcNow)
|
|
348
|
+
{
|
|
349
|
+
due.Add(p.Key);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
foreach (int pid in due)
|
|
353
|
+
{
|
|
354
|
+
Resume(pid);
|
|
355
|
+
Paused.Remove(pid);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
private static void ResumeAll()
|
|
362
|
+
{
|
|
363
|
+
lock (Gate)
|
|
364
|
+
{
|
|
365
|
+
foreach (int pid in new List<int>(Paused.Keys))
|
|
366
|
+
{
|
|
367
|
+
Resume(pid);
|
|
368
|
+
}
|
|
369
|
+
Paused.Clear();
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
private static string Mb(ulong bytes)
|
|
374
|
+
{
|
|
375
|
+
return Math.Floor(bytes / BytesPerMb).ToString("0", CultureInfo.InvariantCulture);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
private static string Json(string value)
|
|
379
|
+
{
|
|
380
|
+
StringBuilder sb = new StringBuilder("\"");
|
|
381
|
+
foreach (char c in value)
|
|
382
|
+
{
|
|
383
|
+
if (c == '"' || c == '\\')
|
|
384
|
+
{
|
|
385
|
+
sb.Append('\\').Append(c);
|
|
386
|
+
}
|
|
387
|
+
else if (c < 0x20)
|
|
388
|
+
{
|
|
389
|
+
sb.Append("\\u").Append(((int)c).ToString("x4", CultureInfo.InvariantCulture));
|
|
390
|
+
}
|
|
391
|
+
else
|
|
392
|
+
{
|
|
393
|
+
sb.Append(c);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
return sb.Append('"').ToString();
|
|
397
|
+
}
|
|
398
|
+
}
|