windows-development-sandbox 1.5.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/README.md +1 -0
- package/config.example.json +27 -1
- 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} +4 -0
- package/dist/{cli/menu/settings/categories.js → app/features/settings/categories.def.js} +40 -46
- 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/{cli/menu/settings → app/kernel}/schema-node.d.ts +9 -0
- package/dist/{cli/menu/settings → app/kernel}/schema-node.js +26 -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 +18 -2
- package/dist/cli/args.js +101 -24
- package/dist/cli/commands/accounts.js +45 -17
- package/dist/cli/commands/context.d.ts +2 -0
- package/dist/cli/commands/context.js +9 -2
- package/dist/cli/commands/data/clean.js +32 -9
- package/dist/cli/commands/engine.d.ts +2 -0
- package/dist/cli/commands/engine.js +60 -0
- package/dist/cli/commands/index.d.ts +2 -1
- package/dist/cli/commands/index.js +2 -1
- package/dist/cli/commands/launch/add-root.d.ts +3 -2
- package/dist/cli/commands/launch/add-root.js +11 -10
- package/dist/cli/commands/launch/launch.js +102 -9
- package/dist/cli/commands/launch/prepare.js +6 -4
- package/dist/cli/commands/launch/running.js +2 -1
- package/dist/cli/commands/launch/summary.d.ts +2 -0
- package/dist/cli/commands/maintenance/audit.js +1 -1
- package/dist/cli/commands/maintenance/clean.d.ts +1 -1
- package/dist/cli/commands/maintenance/clean.js +17 -3
- 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.d.ts +1 -1
- package/dist/cli/commands/maintenance/config.js +38 -10
- package/dist/cli/commands/maintenance/doctor.d.ts +1 -1
- package/dist/cli/commands/maintenance/doctor.js +2 -4
- package/dist/cli/commands/maintenance/logs.js +14 -0
- package/dist/cli/commands/maintenance/migrate.d.ts +2 -1
- package/dist/cli/commands/maintenance/migrate.js +19 -4
- package/dist/cli/commands/maintenance/sync.js +2 -0
- package/dist/cli/commands/maintenance/update.d.ts +2 -0
- package/dist/cli/commands/maintenance/update.js +27 -4
- package/dist/cli/commands/projects/here.js +12 -7
- package/dist/cli/commands/projects/projects.js +26 -17
- package/dist/cli/commands/registry.js +10 -7
- package/dist/cli/commands/repair/checks/agents.d.ts +2 -0
- package/dist/cli/commands/repair/checks/agents.js +70 -0
- package/dist/cli/commands/repair/checks/common.d.ts +8 -0
- package/dist/cli/commands/repair/checks/common.js +22 -0
- 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/data.d.ts +2 -0
- package/dist/cli/commands/repair/checks/data.js +241 -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.d.ts +2 -0
- package/dist/cli/commands/repair/checks/index.js +24 -0
- package/dist/cli/commands/repair/checks/install.d.ts +5 -0
- package/dist/cli/commands/repair/checks/install.js +318 -0
- package/dist/cli/commands/repair/checks/machine.d.ts +2 -0
- package/dist/cli/commands/repair/checks/machine.js +232 -0
- package/dist/cli/commands/repair/checks/network.d.ts +6 -0
- package/dist/cli/commands/repair/checks/network.js +60 -0
- package/dist/cli/commands/repair/checks/pc.d.ts +4 -0
- package/dist/cli/commands/repair/checks/pc.js +86 -0
- package/dist/cli/commands/repair/checks/sandbox.d.ts +2 -0
- package/dist/cli/commands/repair/checks/sandbox.js +282 -0
- package/dist/cli/commands/repair/checks/settings.d.ts +2 -0
- package/dist/cli/commands/repair/checks/settings.js +169 -0
- package/dist/cli/commands/repair/checks/vpn.d.ts +2 -0
- package/dist/cli/commands/repair/checks/vpn.js +78 -0
- package/dist/cli/commands/repair/engine.d.ts +32 -0
- package/dist/cli/commands/repair/engine.js +127 -0
- package/dist/cli/commands/repair/env.d.ts +7 -0
- package/dist/cli/commands/repair/env.js +50 -0
- package/dist/cli/commands/repair/index.d.ts +1 -9
- package/dist/cli/commands/repair/index.js +2 -54
- package/dist/cli/commands/repair/log.d.ts +17 -0
- package/dist/cli/commands/repair/log.js +82 -0
- package/dist/cli/commands/repair/printer.d.ts +32 -0
- package/dist/cli/commands/repair/printer.js +132 -0
- package/dist/cli/commands/repair/run.d.ts +33 -0
- package/dist/cli/commands/repair/run.js +133 -0
- package/dist/cli/commands/repair/types.d.ts +97 -9
- package/dist/cli/commands/repair/types.js +11 -7
- package/dist/cli/commands/resume.js +2 -2
- package/dist/cli/commands/sessions/index.d.ts +0 -4
- package/dist/cli/commands/sessions/index.js +3 -7
- package/dist/cli/commands/setup/index.d.ts +5 -0
- package/dist/cli/commands/setup/index.js +25 -1
- 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/index.d.ts +2 -0
- package/dist/cli/commands/storage/index.js +2 -0
- package/dist/cli/commands/storage/offer.d.ts +3 -0
- package/dist/cli/commands/storage/offer.js +91 -0
- package/dist/cli/commands/storage/storage.d.ts +8 -0
- package/dist/cli/commands/storage/storage.js +179 -0
- package/dist/cli/commands/user-config.js +2 -2
- package/dist/cli/commands/vpn.d.ts +4 -0
- package/dist/cli/commands/vpn.js +91 -0
- package/dist/cli/commands/web.d.ts +4 -0
- package/dist/cli/commands/web.js +97 -0
- package/dist/cli/history.js +1 -1
- package/dist/cli/index.js +48 -4
- package/dist/cli/manifest.d.ts +339 -97
- package/dist/cli/manifest.js +222 -67
- package/dist/cli/menu/add-agent.d.ts +47 -0
- package/dist/cli/menu/add-agent.js +168 -0
- package/dist/cli/menu/apps.d.ts +38 -3
- package/dist/cli/menu/apps.js +80 -5
- package/dist/cli/menu/command-fields.d.ts +18 -0
- package/dist/cli/menu/command-fields.js +61 -0
- package/dist/cli/menu/command-form.d.ts +22 -0
- package/dist/cli/menu/command-form.js +169 -46
- package/dist/cli/menu/data-folder.d.ts +18 -0
- package/dist/cli/menu/data-folder.js +99 -0
- 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 +11 -2
- package/dist/cli/menu/menu.js +36 -15
- package/dist/cli/menu/pages/accounts-page.js +55 -24
- package/dist/cli/menu/pages/agents-page.d.ts +68 -0
- package/dist/cli/menu/pages/agents-page.js +345 -0
- package/dist/cli/menu/pages/apps-page.d.ts +3 -1
- package/dist/cli/menu/pages/apps-page.js +20 -4
- package/dist/cli/menu/pages/list-page.d.ts +43 -3
- package/dist/cli/menu/pages/list-page.js +146 -12
- package/dist/cli/menu/pages/maintenance-page.d.ts +9 -5
- package/dist/cli/menu/pages/maintenance-page.js +172 -85
- package/dist/cli/menu/pages/sandbox-page.d.ts +7 -6
- package/dist/cli/menu/pages/sandbox-page.js +117 -120
- package/dist/cli/menu/pages/scopes.d.ts +1 -1
- package/dist/cli/menu/pages/sessions-page.d.ts +6 -8
- package/dist/cli/menu/pages/sessions-page.js +22 -75
- package/dist/cli/menu/pages/settings-page.d.ts +10 -14
- package/dist/cli/menu/pages/settings-page.js +95 -102
- package/dist/cli/menu/render-view.d.ts +28 -0
- package/dist/cli/menu/render-view.js +112 -0
- package/dist/cli/menu/settings/ask-path.d.ts +14 -0
- package/dist/cli/menu/settings/ask-path.js +14 -0
- package/dist/cli/menu/settings/collections/collection-editor.d.ts +8 -2
- package/dist/cli/menu/settings/collections/collection-editor.js +12 -8
- package/dist/cli/menu/settings/collections/map-editor.js +5 -1
- package/dist/cli/menu/settings/collections/object-list-editor.js +2 -2
- package/dist/cli/menu/settings/collections/object-map-editor.js +2 -2
- package/dist/cli/menu/settings/collections/string-list-editor.d.ts +10 -1
- package/dist/cli/menu/settings/collections/string-list-editor.js +23 -0
- 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 +6 -2
- package/dist/cli/menu/settings/object-editor.js +15 -4
- 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 +10 -1
- package/dist/cli/menu/settings/value-editor.js +99 -63
- package/dist/cli/menu/several.js +7 -5
- 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 +53 -10
- package/dist/cli/menu/shell.js +115 -10
- package/dist/cli/menu/views.d.ts +7 -0
- package/dist/cli/menu/views.js +11 -0
- package/dist/cli/menu/vpn-login.d.ts +16 -0
- package/dist/cli/menu/vpn-login.js +74 -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 +5 -1
- package/dist/cli/output/print.js +15 -4
- 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/accounts-service.d.ts +2 -0
- package/dist/cli/services/accounts-service.js +2 -2
- 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/health-service.d.ts +0 -6
- package/dist/cli/services/health-service.js +5 -42
- package/dist/cli/services/index.d.ts +3 -1
- package/dist/cli/services/index.js +3 -1
- package/dist/cli/services/logs-service.js +4 -3
- package/dist/cli/services/migrate-service.js +1 -1
- package/dist/cli/services/update-service.d.ts +2 -0
- package/dist/cli/services/update-service.js +13 -6
- package/dist/cli/services/vpn-service.d.ts +28 -0
- package/dist/cli/services/vpn-service.js +46 -0
- package/dist/cli/services/web-service.d.ts +22 -0
- package/dist/cli/services/web-service.js +111 -0
- package/dist/cli/tui/app.d.ts +12 -4
- package/dist/cli/tui/app.js +38 -9
- package/dist/cli/tui/dialogs/path-listing.d.ts +43 -0
- package/dist/cli/tui/dialogs/path-listing.js +127 -0
- package/dist/cli/tui/dialogs/path-picker.d.ts +67 -0
- package/dist/cli/tui/dialogs/path-picker.js +423 -0
- package/dist/cli/tui/dialogs/text-screen.d.ts +6 -0
- package/dist/cli/tui/dialogs/text-screen.js +10 -3
- package/dist/cli/tui/frame.d.ts +7 -10
- package/dist/cli/tui/frame.js +19 -10
- package/dist/cli/tui/index.d.ts +4 -3
- package/dist/cli/tui/index.js +2 -1
- package/dist/cli/tui/list/list-screen.d.ts +28 -2
- package/dist/cli/tui/list/list-screen.js +130 -66
- package/dist/cli/tui/list/list-types.d.ts +10 -4
- package/dist/cli/tui/nav.d.ts +29 -0
- package/dist/cli/tui/nav.js +67 -0
- package/dist/cli/tui/terminal.d.ts +7 -0
- package/dist/cli/tui/terminal.js +30 -10
- package/dist/cli/validate.d.ts +1 -1
- package/dist/cli/validate.js +3 -3
- package/dist/cli/version.d.ts +2 -0
- package/dist/cli/version.js +11 -0
- package/dist/core/config/defaults.js +13 -1
- package/dist/core/config/loader.js +12 -2
- package/dist/core/local-config/overrides.js +1 -0
- package/dist/core/prompt.d.ts +19 -0
- package/dist/core/prompt.js +43 -3
- package/dist/core/schema/common.d.ts +6 -0
- package/dist/core/schema/common.js +8 -0
- package/dist/core/schema/config.d.ts +36 -0
- package/dist/core/schema/config.js +26 -2
- package/dist/core/schema/harness.d.ts +2 -0
- package/dist/core/schema/harness.js +13 -2
- package/dist/core/schema/index.d.ts +3 -1
- package/dist/core/schema/index.js +3 -1
- package/dist/core/schema/profile.d.ts +1 -0
- package/dist/core/schema/profile.js +3 -0
- package/dist/core/schema/redact.d.ts +13 -0
- package/dist/core/schema/redact.js +75 -0
- package/dist/core/schema/sandbox.d.ts +6 -0
- package/dist/core/schema/sandbox.js +21 -3
- package/dist/core/schema/vpn.d.ts +18 -0
- package/dist/core/schema/vpn.js +22 -0
- package/dist/core/schema/web.d.ts +6 -0
- package/dist/core/schema/web.js +14 -0
- package/dist/core/types.d.ts +44 -0
- 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 +54 -0
- package/dist/guest/agents/agent-files.js +67 -0
- package/dist/guest/agents/agent-installer.js +2 -9
- package/dist/guest/agents/local-state.d.ts +14 -0
- package/dist/guest/agents/local-state.js +218 -0
- package/dist/guest/agents/sbx-agent.d.ts +2 -0
- package/dist/guest/agents/sbx-agent.js +35 -0
- package/dist/guest/boot/boot.d.ts +4 -0
- package/dist/guest/boot/boot.js +51 -11
- package/dist/guest/daemon/daemon.js +8 -1
- package/dist/guest/daemon/exec.js +2 -1
- package/dist/guest/daemon/idle-shutdown.js +2 -0
- package/dist/guest/daemon/sessions.d.ts +5 -0
- package/dist/guest/daemon/sessions.js +20 -3
- package/dist/guest/entries/agent.d.ts +1 -0
- package/dist/guest/entries/agent.js +10 -0
- package/dist/guest/entries/daemon.js +5 -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 +2 -0
- package/dist/guest/runtime/csc.js +2 -0
- package/dist/guest/runtime/secrets.d.ts +12 -0
- package/dist/guest/runtime/secrets.js +63 -0
- package/dist/guest/sessions/viewer.js +3 -1
- package/dist/guest/setup/claude-profile.d.ts +2 -1
- package/dist/guest/setup/claude-profile.js +12 -1
- package/dist/guest/setup/environment.d.ts +6 -0
- package/dist/guest/setup/environment.js +56 -6
- package/dist/guest/setup/path-plan.d.ts +32 -0
- package/dist/guest/setup/path-plan.js +67 -0
- package/dist/guest/setup/shell-path.d.ts +1 -0
- package/dist/guest/setup/shell-path.js +22 -0
- package/dist/guest/toolchains/languages/python.js +1 -1
- package/dist/guest/toolchains/native/rust-extras.js +4 -2
- package/dist/guest/toolchains/runtimes.js +2 -2
- package/dist/guest/vpn/index.d.ts +1 -0
- package/dist/guest/vpn/index.js +1 -0
- package/dist/guest/vpn/openvpn.d.ts +9 -0
- package/dist/guest/vpn/openvpn.js +47 -0
- package/dist/guest/vpn/tunnel.d.ts +17 -0
- package/dist/guest/vpn/tunnel.js +78 -0
- package/dist/guest/vpn/vpn.d.ts +6 -0
- package/dist/guest/vpn/vpn.js +174 -0
- 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 +6 -2
- package/dist/host/channel/attach.js +6 -3
- package/dist/host/channel/channel.d.ts +9 -0
- package/dist/host/channel/channel.js +30 -2
- 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/health/acl-read.d.ts +17 -0
- package/dist/host/health/acl-read.js +45 -0
- package/dist/host/health/config-backup.d.ts +11 -0
- package/dist/host/health/config-backup.js +73 -0
- package/dist/host/health/index.d.ts +6 -0
- package/dist/host/health/index.js +7 -0
- package/dist/host/health/install-state.d.ts +33 -0
- package/dist/host/health/install-state.js +110 -0
- package/dist/host/health/path-copies.d.ts +32 -0
- package/dist/host/health/path-copies.js +82 -0
- package/dist/host/health/stale.d.ts +45 -0
- package/dist/host/health/stale.js +120 -0
- package/dist/host/health/system.d.ts +37 -0
- package/dist/host/health/system.js +87 -0
- package/dist/host/install/copies.d.ts +13 -0
- package/dist/host/install/copies.js +63 -0
- package/dist/host/install/index.d.ts +1 -0
- package/dist/host/install/index.js +1 -0
- package/dist/host/launch/generate.d.ts +5 -1
- package/dist/host/launch/generate.js +73 -7
- 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 +11 -2
- package/dist/host/launch/stage.js +1 -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/migrate/backup.js +3 -0
- package/dist/host/migrate/migrations/config-file.d.ts +2 -0
- package/dist/host/migrate/migrations/config-file.js +7 -0
- package/dist/host/migrate/migrations/follow-machine-storage.js +9 -1
- package/dist/host/migrate/migrations/pin-storage-root.d.ts +4 -0
- package/dist/host/migrate/migrations/pin-storage-root.js +46 -0
- package/dist/host/migrate/migrations/unpin-config-defaults.js +4 -1
- package/dist/host/migrate/registry.js +2 -1
- package/dist/host/native.d.ts +3 -2
- package/dist/host/native.js +22 -3
- package/dist/host/profile/accounts.d.ts +25 -1
- package/dist/host/profile/accounts.js +96 -13
- package/dist/host/profile/agent-accounts.d.ts +5 -1
- package/dist/host/profile/agent-accounts.js +35 -5
- package/dist/host/profile/claude-login.js +3 -2
- package/dist/host/profile/index.d.ts +2 -1
- package/dist/host/profile/index.js +1 -0
- package/dist/host/profile/plugin-share.d.ts +26 -0
- package/dist/host/profile/plugin-share.js +146 -0
- package/dist/host/projects/join.d.ts +7 -1
- package/dist/host/projects/join.js +21 -4
- package/dist/host/projects/registry.d.ts +16 -10
- package/dist/host/projects/registry.js +74 -6
- 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 +4 -0
- package/dist/host/sandbox/platform.js +23 -4
- package/dist/host/sandbox/tasklist.d.ts +13 -0
- package/dist/host/sandbox/tasklist.js +47 -0
- package/dist/host/secrets/index.d.ts +2 -0
- package/dist/host/secrets/index.js +2 -0
- package/dist/host/secrets/launch-secrets.d.ts +17 -0
- package/dist/host/secrets/launch-secrets.js +94 -0
- package/dist/host/secrets/secret-store.d.ts +25 -0
- package/dist/host/secrets/secret-store.js +89 -0
- package/dist/host/sharing/acl.d.ts +16 -2
- package/dist/host/sharing/acl.js +35 -10
- package/dist/host/sharing/current.js +8 -3
- package/dist/host/sharing/doctor.d.ts +2 -0
- package/dist/host/sharing/doctor.js +44 -0
- package/dist/host/sharing/group-access.d.ts +6 -0
- package/dist/host/sharing/group-access.js +24 -0
- package/dist/host/sharing/index.d.ts +2 -1
- package/dist/host/sharing/index.js +2 -1
- package/dist/host/sharing/lifecycle.d.ts +8 -2
- package/dist/host/sharing/lifecycle.js +16 -7
- package/dist/host/storage/cleanup.d.ts +2 -0
- package/dist/host/storage/cleanup.js +2 -1
- 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/history.d.ts +21 -0
- package/dist/host/storage/history.js +49 -0
- package/dist/host/storage/index.d.ts +6 -0
- package/dist/host/storage/index.js +6 -0
- package/dist/host/storage/move.d.ts +95 -0
- package/dist/host/storage/move.js +384 -0
- package/dist/host/storage/offer.d.ts +11 -0
- package/dist/host/storage/offer.js +36 -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/storage/root.d.ts +18 -0
- package/dist/host/storage/root.js +77 -0
- package/dist/host/tools/conpty.js +3 -2
- package/dist/host/tools/windows-terminal.d.ts +2 -0
- package/dist/host/tools/windows-terminal.js +31 -12
- package/dist/host/update/index.d.ts +1 -1
- package/dist/host/update/index.js +1 -1
- package/dist/host/update/notice.d.ts +2 -0
- package/dist/host/update/notice.js +13 -1
- package/dist/host/update/update-state.d.ts +6 -0
- package/dist/host/update/update-state.js +2 -0
- package/dist/host/update/updater.d.ts +19 -0
- package/dist/host/update/updater.js +88 -12
- package/dist/host/vpn/index.d.ts +4 -0
- package/dist/host/vpn/index.js +4 -0
- package/dist/host/vpn/login.d.ts +24 -0
- package/dist/host/vpn/login.js +79 -0
- package/dist/host/vpn/nordvpn.d.ts +21 -0
- package/dist/host/vpn/nordvpn.js +77 -0
- package/dist/host/vpn/prepare.d.ts +19 -0
- package/dist/host/vpn/prepare.js +138 -0
- package/dist/host/vpn/status.d.ts +12 -0
- package/dist/host/vpn/status.js +53 -0
- package/dist/host/web/auth.d.ts +38 -0
- package/dist/host/web/auth.js +96 -0
- package/dist/host/web/backend.d.ts +14 -0
- package/dist/host/web/backend.js +1 -0
- package/dist/host/web/bridge.d.ts +27 -0
- package/dist/host/web/bridge.js +144 -0
- package/dist/host/web/browser.d.ts +3 -0
- package/dist/host/web/browser.js +23 -0
- package/dist/host/web/control.d.ts +17 -0
- package/dist/host/web/control.js +67 -0
- package/dist/host/web/index.d.ts +10 -0
- package/dist/host/web/index.js +9 -0
- package/dist/host/web/rpc.d.ts +24 -0
- package/dist/host/web/rpc.js +149 -0
- package/dist/host/web/runner.d.ts +16 -0
- package/dist/host/web/runner.js +44 -0
- package/dist/host/web/server.d.ts +47 -0
- package/dist/host/web/server.js +238 -0
- package/dist/host/web/state.d.ts +20 -0
- package/dist/host/web/state.js +41 -0
- package/dist/host/web/static-files.d.ts +13 -0
- package/dist/host/web/static-files.js +57 -0
- package/dist/runtime/agent.js +27675 -0
- package/dist/runtime/daemon.js +1950 -1122
- package/dist/runtime/db.js +302 -111
- package/dist/runtime/main.js +1560 -275
- package/dist/runtime/netproxy.js +168 -8
- package/dist/runtime/view.js +186 -13
- package/dist/selftest/accounts.test.js +40 -0
- package/dist/selftest/agent-paths.test.d.ts +2 -0
- package/dist/selftest/agent-paths.test.js +125 -0
- package/dist/selftest/app.test.d.ts +2 -0
- package/dist/selftest/app.test.js +324 -0
- package/dist/selftest/channel.test.js +18 -0
- package/dist/selftest/cli.test.js +35 -4
- 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.d.ts +2 -0
- package/dist/selftest/credentials.test.js +198 -0
- package/dist/selftest/engine.test.d.ts +2 -0
- package/dist/selftest/engine.test.js +127 -0
- package/dist/selftest/generate.test.js +1 -1
- package/dist/selftest/hcs.test.d.ts +2 -0
- package/dist/selftest/hcs.test.js +482 -0
- package/dist/selftest/health.test.d.ts +2 -0
- package/dist/selftest/health.test.js +470 -0
- package/dist/selftest/heavy-load.test.d.ts +2 -0
- package/dist/selftest/heavy-load.test.js +329 -0
- package/dist/selftest/host-sim.test.js +2 -0
- package/dist/selftest/index.js +31 -0
- package/dist/selftest/layout.test.js +14 -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.d.ts +7 -0
- package/dist/selftest/menu-screens.js +68 -17
- package/dist/selftest/menu.test.js +603 -22
- package/dist/selftest/migrate.test.js +1 -1
- package/dist/selftest/personalization.test.js +3 -1
- package/dist/selftest/projects.test.js +39 -1
- package/dist/selftest/shared-use.test.js +64 -2
- package/dist/selftest/sharing.test.js +50 -1
- package/dist/selftest/storage.test.d.ts +2 -0
- package/dist/selftest/storage.test.js +363 -0
- 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 +20 -0
- package/dist/selftest/support/hcs-probes.d.ts +17 -0
- package/dist/selftest/support/hcs-probes.js +41 -0
- package/dist/selftest/support/releases.js +1 -1
- package/dist/selftest/toolchains.test.js +21 -0
- package/dist/selftest/update-service.test.js +87 -3
- package/dist/selftest/vpn.test.d.ts +2 -0
- package/dist/selftest/vpn.test.js +412 -0
- package/dist/selftest/web.test.d.ts +2 -0
- package/dist/selftest/web.test.js +402 -0
- package/dist/shared/apps/catalogue.js +31 -0
- package/dist/shared/channel/index.d.ts +1 -0
- package/dist/shared/channel/index.js +1 -0
- package/dist/shared/channel/run-dir.d.ts +9 -1
- package/dist/shared/channel/run-dir.js +18 -1
- package/dist/shared/channel/schema.d.ts +53 -0
- package/dist/shared/channel/schema.js +38 -0
- package/dist/shared/channel/secrets.d.ts +29 -0
- package/dist/shared/channel/secrets.js +50 -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 +29 -0
- package/dist/shared/vpn/download.js +30 -0
- package/dist/shared/vpn/kill-switch.d.ts +18 -0
- package/dist/shared/vpn/kill-switch.js +81 -0
- package/dist/shared/vpn/ovpn.d.ts +23 -0
- package/dist/shared/vpn/ovpn.js +244 -0
- package/dist/shared/vpn/wireguard.d.ts +51 -0
- package/dist/shared/vpn/wireguard.js +227 -0
- package/dist/shared/web/index.d.ts +1 -0
- package/dist/shared/web/index.js +1 -0
- package/dist/shared/web/protocol.d.ts +61 -0
- package/dist/shared/web/protocol.js +35 -0
- package/dist/sync/sync-cycle.js +9 -5
- package/dist/sync/sync-state.d.ts +1 -0
- package/dist/sync/sync-state.js +1 -0
- package/dist/sync/watcher.d.ts +13 -0
- package/dist/sync/watcher.js +102 -3
- package/dist/web/index.html +14 -0
- package/dist/web/main-NRRS5O6Z.js +157 -0
- package/dist/web/main-T5DMCHYV.css +1 -0
- package/native/csharp/FolderSecurity.cs +29 -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/protect-data.ps1 +29 -0
- package/native/scripts/read-folder-sddl.ps1 +10 -0
- package/native/scripts/run-elevated.ps1 +17 -4
- package/native/scripts/set-folder-access.ps1 +49 -7
- package/native/scripts/stop-events.ps1 +23 -8
- package/node_modules/ws/LICENSE +20 -0
- package/node_modules/ws/README.md +548 -0
- package/node_modules/ws/browser.js +8 -0
- package/node_modules/ws/index.js +22 -0
- package/node_modules/ws/lib/buffer-util.js +131 -0
- package/node_modules/ws/lib/constants.js +19 -0
- package/node_modules/ws/lib/event-target.js +292 -0
- package/node_modules/ws/lib/extension.js +203 -0
- package/node_modules/ws/lib/limiter.js +55 -0
- package/node_modules/ws/lib/permessage-deflate.js +530 -0
- package/node_modules/ws/lib/receiver.js +743 -0
- package/node_modules/ws/lib/sender.js +607 -0
- package/node_modules/ws/lib/stream.js +161 -0
- package/node_modules/ws/lib/subprotocol.js +62 -0
- package/node_modules/ws/lib/validation.js +152 -0
- package/node_modules/ws/lib/websocket-server.js +562 -0
- package/node_modules/ws/lib/websocket.js +1421 -0
- package/node_modules/ws/package.json +74 -0
- package/node_modules/ws/wrapper.mjs +21 -0
- package/package.json +22 -5
- package/sandbox/csharp/EnvBroadcast.cs +33 -0
- package/sandbox/csharp/MemGov.cs +398 -0
- package/sandbox/csharp/PathSync.cs +90 -0
- package/sandbox/csharp/PtyHost.cs +111 -5
- package/sandbox/scripts/install-shell-path.ps1 +73 -0
- package/sandbox/scripts/set-machine-environment.ps1 +37 -4
- package/schemas/config.schema.json +136 -7
- package/schemas/sandbox-config.schema.json +17 -4
- package/dist/cli/commands/repair/checks.d.ts +0 -2
- package/dist/cli/commands/repair/checks.js +0 -246
- 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 -45
- package/dist/cli/commands/sessions/status.d.ts +0 -2
- package/dist/cli/commands/sessions/status.js +0 -93
- package/dist/cli/commands/sessions/window.d.ts +0 -3
- package/dist/cli/commands/sessions/window.js +0 -18
- package/dist/cli/menu/pages/start-page.d.ts +0 -43
- package/dist/cli/menu/pages/start-page.js +0 -192
- package/dist/cli/tui/tab-bar.d.ts +0 -9
- package/dist/cli/tui/tab-bar.js +0 -33
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// Doctor rows for an all-users install: can this user read the settings for the PC and write the data folder.
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { machineConfigPath } from '../../core/config/index.js';
|
|
5
|
+
import { errMsg } from '../../core/util/index.js';
|
|
6
|
+
import { sharingDeps } from './lifecycle.js';
|
|
7
|
+
/** "readable", "not there" or the reason it cannot be read. */
|
|
8
|
+
function readableText(file) {
|
|
9
|
+
try {
|
|
10
|
+
fs.accessSync(file, fs.constants.R_OK);
|
|
11
|
+
return 'readable';
|
|
12
|
+
}
|
|
13
|
+
catch (e) {
|
|
14
|
+
const code = e.code;
|
|
15
|
+
if (code === 'ENOENT' || code === 'ENOTDIR')
|
|
16
|
+
return 'none (built-in defaults apply)';
|
|
17
|
+
return 'NOT readable (' + errMsg(e) + '). Sign out of Windows and in again after being added to the group';
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/** "writable" or why not: a small file is created and removed in the folder (made first when it is missing). */
|
|
21
|
+
function writableText(dir) {
|
|
22
|
+
const probe = path.join(dir, '.write-test-' + process.pid);
|
|
23
|
+
try {
|
|
24
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
25
|
+
fs.writeFileSync(probe, '');
|
|
26
|
+
return 'writable';
|
|
27
|
+
}
|
|
28
|
+
catch (e) {
|
|
29
|
+
return 'NOT writable (' + errMsg(e) + ')';
|
|
30
|
+
}
|
|
31
|
+
finally {
|
|
32
|
+
fs.rmSync(probe, { force: true });
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/** The extra doctor rows of an all-users install; none for any other install. */
|
|
36
|
+
export function machineDoctorRows(packageRoot, dataPath) {
|
|
37
|
+
if (!sharingDeps(packageRoot))
|
|
38
|
+
return [];
|
|
39
|
+
const file = machineConfigPath();
|
|
40
|
+
return [
|
|
41
|
+
['Settings for this PC', file + ': ' + readableText(file)],
|
|
42
|
+
['Data folder access', dataPath + ': ' + writableText(dataPath)],
|
|
43
|
+
];
|
|
44
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* true when the group has an entry on any of the existing folders, or when that cannot be read (fail closed: the
|
|
3
|
+
* caller then removes the entries and refuses the launch if that fails). false only when every folder was read and
|
|
4
|
+
* none has a group entry.
|
|
5
|
+
*/
|
|
6
|
+
export declare function groupHasAccess(dirs: readonly string[]): boolean;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// Does the sharing group still have an entry on one of the folders a sharing plan touches? Read-only (icacls), so a
|
|
2
|
+
// launch that shares nothing can skip the permission script when the answer is no.
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import { spawnSync } from 'node:child_process';
|
|
5
|
+
import { SYSTEM } from '../../shared/system.js';
|
|
6
|
+
// acl-read.ts is pure; the health barrel would make a cycle (health reads the process list from sharing).
|
|
7
|
+
import { SHARING_GROUP, groupEntries, parseIcacls } from '../health/acl-read.js';
|
|
8
|
+
/**
|
|
9
|
+
* true when the group has an entry on any of the existing folders, or when that cannot be read (fail closed: the
|
|
10
|
+
* caller then removes the entries and refuses the launch if that fails). false only when every folder was read and
|
|
11
|
+
* none has a group entry.
|
|
12
|
+
*/
|
|
13
|
+
export function groupHasAccess(dirs) {
|
|
14
|
+
for (const dir of dirs) {
|
|
15
|
+
if (!fs.existsSync(dir))
|
|
16
|
+
continue;
|
|
17
|
+
const run = spawnSync(SYSTEM.icacls(), [dir], { encoding: 'utf8', windowsHide: true, timeout: 15000 });
|
|
18
|
+
if (run.status !== 0 || run.error)
|
|
19
|
+
return true;
|
|
20
|
+
if (groupEntries(parseIcacls(run.stdout ?? '', dir), SHARING_GROUP).length > 0)
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
@@ -5,4 +5,5 @@ export { currentUserName, currentUserSid, listProcesses, runningOwner, sessionOf
|
|
|
5
5
|
export { ownerLine, ownerName, ownerRule, launchBlocked, type OwnerAction, type OwnerRule } from './rules.js';
|
|
6
6
|
export { sharedDataPath, type Borrowed } from './borrow.js';
|
|
7
7
|
export { beginSharing, endSharing, sharingDeps, takeSharingNotice, type SharingDeps, type SharingStart, } from './lifecycle.js';
|
|
8
|
-
export { sharingAclPlan, applyAcl, runFolderAccess, type AclChange } from './acl.js';
|
|
8
|
+
export { sharingAclPlan, applyAcl, protectPrivateFolder, runFolderAccess, runPrivateFolder, type AclChange, } from './acl.js';
|
|
9
|
+
export { machineDoctorRows } from './doctor.js';
|
|
@@ -7,4 +7,5 @@ export { currentUserName, currentUserSid, listProcesses, runningOwner, sessionOf
|
|
|
7
7
|
export { ownerLine, ownerName, ownerRule, launchBlocked } from './rules.js';
|
|
8
8
|
export { sharedDataPath } from './borrow.js';
|
|
9
9
|
export { beginSharing, endSharing, sharingDeps, takeSharingNotice, } from './lifecycle.js';
|
|
10
|
-
export { sharingAclPlan, applyAcl, runFolderAccess } from './acl.js';
|
|
10
|
+
export { sharingAclPlan, applyAcl, protectPrivateFolder, runFolderAccess, runPrivateFolder, } from './acl.js';
|
|
11
|
+
export { machineDoctorRows } from './doctor.js';
|
|
@@ -8,14 +8,20 @@ export interface SharingDeps {
|
|
|
8
8
|
readonly groupSid: string | null;
|
|
9
9
|
/** The shared tree (machine\users): permissions change only for data folders inside it. */
|
|
10
10
|
readonly usersDir: string;
|
|
11
|
+
/**
|
|
12
|
+
* Does the group have an entry on any of these folders (true also when that cannot be read)? When it says no, a
|
|
13
|
+
* launch that shares nothing has no entries to remove and skips the permission script. Left out: always apply.
|
|
14
|
+
*/
|
|
15
|
+
readonly groupHasAccess?: (dirs: readonly string[]) => boolean;
|
|
11
16
|
}
|
|
12
17
|
/** What the launch knows about the sandbox it is about to start. */
|
|
13
18
|
export type SharingStart = Omit<ActiveSandbox, 'startedAt'> & {
|
|
14
19
|
readonly startedAt?: string;
|
|
15
20
|
};
|
|
16
21
|
/**
|
|
17
|
-
* The machinery of
|
|
18
|
-
* storage folder).
|
|
22
|
+
* The machinery of the all-users install on this PC, or null when there is none (nothing is shared then: one user, one
|
|
23
|
+
* storage folder). It is found from any copy of the program: an npm copy or a checkout that starts a sandbox shares it
|
|
24
|
+
* the same way the installed one does, with the group and the settings the install recorded.
|
|
19
25
|
*/
|
|
20
26
|
export declare function sharingDeps(packageRoot: string): SharingDeps | null;
|
|
21
27
|
/** Writes the record, and opens (shared) or closes (not shared) the owner's folders for the group. */
|
|
@@ -7,24 +7,28 @@ import { z } from 'zod';
|
|
|
7
7
|
import { RunDir } from '../../shared/channel/index.js';
|
|
8
8
|
import { isWithin } from '../../shared/paths/index.js';
|
|
9
9
|
import { JsonFile } from '../../shared/io/index.js';
|
|
10
|
-
import { InstallLayout, InstallRecordFile } from '../install/index.js';
|
|
10
|
+
import { InstallLayout, InstallRecordFile, machineInstall } from '../install/index.js';
|
|
11
11
|
import { storageOf } from '../storage/index.js';
|
|
12
12
|
import { sharingAclPlan, applyAcl } from './acl.js';
|
|
13
13
|
import { ActiveRecords } from './active-record.js';
|
|
14
|
+
import { groupHasAccess } from './group-access.js';
|
|
14
15
|
import { activeDir, usersDir } from './machine-paths.js';
|
|
15
16
|
/**
|
|
16
|
-
* The machinery of
|
|
17
|
-
* storage folder).
|
|
17
|
+
* The machinery of the all-users install on this PC, or null when there is none (nothing is shared then: one user, one
|
|
18
|
+
* storage folder). It is found from any copy of the program: an npm copy or a checkout that starts a sandbox shares it
|
|
19
|
+
* the same way the installed one does, with the group and the settings the install recorded.
|
|
18
20
|
*/
|
|
19
21
|
export function sharingDeps(packageRoot) {
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
+
const detected = InstallLayout.detect(packageRoot);
|
|
23
|
+
const layout = detected?.scope === 'machine' ? detected : machineInstall();
|
|
24
|
+
if (!layout)
|
|
22
25
|
return null;
|
|
23
26
|
return {
|
|
24
27
|
records: new ActiveRecords(activeDir()),
|
|
25
28
|
apply: applyAcl,
|
|
26
29
|
groupSid: new InstallRecordFile(layout.root).read()?.groupSid ?? null,
|
|
27
30
|
usersDir: usersDir(),
|
|
31
|
+
groupHasAccess,
|
|
28
32
|
};
|
|
29
33
|
}
|
|
30
34
|
/** Folder permissions change only with a group, and only inside the shared tree (never your own storage folder). */
|
|
@@ -41,12 +45,17 @@ export function beginSharing(deps, start, now = new Date()) {
|
|
|
41
45
|
const run = new RunDir(start.dataPath);
|
|
42
46
|
for (const dir of [run.root, run.requestsDir, run.responsesDir, run.sessionsDir, run.projectsDir, run.auditDir])
|
|
43
47
|
fs.mkdirSync(dir, { recursive: true });
|
|
44
|
-
|
|
48
|
+
const plan = sharingAclPlan({
|
|
45
49
|
storageUserDir: storageOf(start.dataPath),
|
|
46
50
|
dataPath: start.dataPath,
|
|
47
51
|
groupSid: deps.groupSid,
|
|
48
52
|
on: start.shared,
|
|
49
|
-
})
|
|
53
|
+
});
|
|
54
|
+
// Not shared and nothing of the group left to take away: nothing to write, so a slow folder cannot block the launch.
|
|
55
|
+
// When the entries cannot be read, or one is there, the removal runs and a failure of it refuses the launch.
|
|
56
|
+
if (!start.shared && deps.groupHasAccess && !deps.groupHasAccess(plan.map((c) => c.path)))
|
|
57
|
+
return;
|
|
58
|
+
deps.apply(plan);
|
|
50
59
|
}
|
|
51
60
|
/** The sandbox of this data folder ended: its record goes and the group loses access again. Never throws. */
|
|
52
61
|
export function endSharing(deps, dataPath) {
|
|
@@ -31,7 +31,8 @@ export function pruneStorage(storageRoot, options = {}) {
|
|
|
31
31
|
const result = { launches: 0, logs: 0, leftovers: 0, bytes: 0 };
|
|
32
32
|
const remove = (p, counter) => {
|
|
33
33
|
result.bytes += sizeOf(p);
|
|
34
|
-
|
|
34
|
+
if (!o.dryRun)
|
|
35
|
+
safeRemove(p, storageRoot);
|
|
35
36
|
result[counter]++;
|
|
36
37
|
};
|
|
37
38
|
for (const launch of children(path.join(storageRoot, 'launches'))) {
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ProviderCheck } from '../providers/types.js';
|
|
2
|
+
export interface ContainedFacts {
|
|
3
|
+
readonly windows: boolean;
|
|
4
|
+
readonly insideSandbox: boolean;
|
|
5
|
+
readonly diskpartPresent: boolean;
|
|
6
|
+
readonly powershellPresent: boolean;
|
|
7
|
+
}
|
|
8
|
+
export declare function containedFacts(): ContainedFacts;
|
|
9
|
+
export declare function containedAvailability(facts?: ContainedFacts): ProviderCheck;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// Can contained storage run on this PC? Windows, diskpart present, and not inside a Windows Sandbox (a sandbox has no
|
|
2
|
+
// Virtual Disk Service, and a nested one would only mount inside itself).
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import { isWin } from '../../../core/util/index.js';
|
|
5
|
+
import { SYSTEM, system32 } from '../../../shared/system.js';
|
|
6
|
+
import { insideSandbox } from '../../sandbox/platform.js';
|
|
7
|
+
export function containedFacts() {
|
|
8
|
+
return {
|
|
9
|
+
windows: isWin,
|
|
10
|
+
insideSandbox: insideSandbox(),
|
|
11
|
+
diskpartPresent: isWin && fs.existsSync(system32('diskpart.exe')),
|
|
12
|
+
powershellPresent: isWin && fs.existsSync(SYSTEM.powershell()),
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
export function containedAvailability(facts = containedFacts()) {
|
|
16
|
+
const reasons = [];
|
|
17
|
+
const fixes = [];
|
|
18
|
+
if (!facts.windows) {
|
|
19
|
+
reasons.push('Contained storage needs Windows (it uses disk images).');
|
|
20
|
+
fixes.push('Set storage.contained to "off".');
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
if (facts.insideSandbox) {
|
|
24
|
+
reasons.push('This is inside a Windows Sandbox: disk images cannot be mounted here.');
|
|
25
|
+
fixes.push('Run the command on your PC, not in a sandbox.');
|
|
26
|
+
}
|
|
27
|
+
if (!facts.diskpartPresent) {
|
|
28
|
+
reasons.push('diskpart.exe was not found in Windows\\System32.');
|
|
29
|
+
fixes.push('Repair Windows (sfc /scannow) or set storage.contained to "off".');
|
|
30
|
+
}
|
|
31
|
+
if (!facts.powershellPresent) {
|
|
32
|
+
reasons.push('Windows PowerShell 5.1 was not found.');
|
|
33
|
+
fixes.push('Repair Windows or set storage.contained to "off".');
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return { available: reasons.length === 0, reasons, fixes };
|
|
37
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { type ContainedPaths } from './layout.js';
|
|
2
|
+
import type { DiskPort } from './disk-port.js';
|
|
3
|
+
import { type ContainedDiagnosis, type MountReality } from './state.js';
|
|
4
|
+
export interface ContainedDeps {
|
|
5
|
+
readonly storageRoot: string;
|
|
6
|
+
readonly maxGb: number;
|
|
7
|
+
/** Makes a NEW, empty folder private (protectPrivateFolder). Never called on a folder that holds files. */
|
|
8
|
+
readonly protect: (dir: string) => void;
|
|
9
|
+
readonly userSid: () => string;
|
|
10
|
+
readonly port: DiskPort;
|
|
11
|
+
readonly sandboxRunning: () => boolean;
|
|
12
|
+
readonly alive?: (pid: number) => boolean;
|
|
13
|
+
/** The volume of a folder (stat.dev): a mounted image is another volume than the folder it is mounted in. */
|
|
14
|
+
readonly deviceOf?: (dir: string) => number;
|
|
15
|
+
}
|
|
16
|
+
export interface HolderId {
|
|
17
|
+
readonly id: string;
|
|
18
|
+
readonly pid: number;
|
|
19
|
+
}
|
|
20
|
+
export interface ReleaseResult {
|
|
21
|
+
readonly dismounted: boolean;
|
|
22
|
+
/** Why it stayed mounted (or could not be dismounted), when it did. */
|
|
23
|
+
readonly note?: string;
|
|
24
|
+
}
|
|
25
|
+
export declare class ContainedStorage {
|
|
26
|
+
private readonly deps;
|
|
27
|
+
readonly set: string;
|
|
28
|
+
readonly paths: ContainedPaths;
|
|
29
|
+
private readonly stateFile;
|
|
30
|
+
private readonly alive;
|
|
31
|
+
constructor(deps: ContainedDeps, set: string);
|
|
32
|
+
imageExists(): boolean;
|
|
33
|
+
/** Holders of this set that still run. */
|
|
34
|
+
liveHolders(): number;
|
|
35
|
+
recordedMounted(): boolean;
|
|
36
|
+
reality(): Promise<MountReality>;
|
|
37
|
+
diagnose(): Promise<ContainedDiagnosis>;
|
|
38
|
+
/**
|
|
39
|
+
* Makes sure the image is mounted at its folder (creating it when `create` is set and no image exists yet), records
|
|
40
|
+
* the holder and returns the mount folder, ready with the folders of the data set inside it.
|
|
41
|
+
*/
|
|
42
|
+
acquire(holder: HolderId, o?: {
|
|
43
|
+
readonly create?: boolean;
|
|
44
|
+
}): Promise<string>;
|
|
45
|
+
private mountForHolder;
|
|
46
|
+
private withHolder;
|
|
47
|
+
/** The record knows this set as mounted (the watcher only holds a mount that exists). */
|
|
48
|
+
isRecorded(): boolean;
|
|
49
|
+
/** Adds a holder to a mount that is recorded as mounted (the background watcher); false when nothing is mounted. */
|
|
50
|
+
hold(holder: HolderId): boolean;
|
|
51
|
+
/** Lets go; dismounts when nobody else holds it and no sandbox runs. Never throws: a stuck mount is for doctor. */
|
|
52
|
+
release(id: string): Promise<ReleaseResult>;
|
|
53
|
+
dismountIfIdle(): Promise<ReleaseResult>;
|
|
54
|
+
/**
|
|
55
|
+
* Deletes the image file (what `storage contained off` offers last, after the files were moved out). Only an image
|
|
56
|
+
* that is not mounted and not attached; never through a link. The folders around it stay.
|
|
57
|
+
*/
|
|
58
|
+
deleteImage(): Promise<void>;
|
|
59
|
+
/** Doctor/repair: the record says mounted, nothing is (clears the record; no permission prompt needed). */
|
|
60
|
+
clearRecord(): void;
|
|
61
|
+
private requireOk;
|
|
62
|
+
/** A mounted image is another volume than its folder; the same id would mean writes land on this PC's own disk. */
|
|
63
|
+
private assertReallyMounted;
|
|
64
|
+
/** The folders the image needs on this PC. Only folders this call creates are made private, never one with files. */
|
|
65
|
+
private ensureFolders;
|
|
66
|
+
}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
// One data set's contained storage: make sure its image is mounted for a launch (creating it the first time), keep the
|
|
2
|
+
// record of who holds the mount, and dismount when the last holder lets go and no sandbox runs. Disks are only touched
|
|
3
|
+
// through the DiskPort; folders only in the places the layout names.
|
|
4
|
+
import fs from 'node:fs';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { assertNoLinks, fail } from '../../../core/util/index.js';
|
|
7
|
+
import { assertTestWritable } from '../../test-guard.js';
|
|
8
|
+
import { CONTAINED_FOLDERS, containedPaths } from './layout.js';
|
|
9
|
+
import { addHolder, diagnose, markMounted, processAlive, pruneHolders, removeHolder, setStateOf, StateFile, } from './state.js';
|
|
10
|
+
const deviceOfDefault = (dir) => fs.statSync(dir).dev;
|
|
11
|
+
export class ContainedStorage {
|
|
12
|
+
deps;
|
|
13
|
+
set;
|
|
14
|
+
paths;
|
|
15
|
+
stateFile;
|
|
16
|
+
alive;
|
|
17
|
+
constructor(deps, set) {
|
|
18
|
+
this.deps = deps;
|
|
19
|
+
this.set = set;
|
|
20
|
+
this.paths = containedPaths(deps.storageRoot, set);
|
|
21
|
+
this.alive = deps.alive ?? processAlive;
|
|
22
|
+
this.stateFile = new StateFile(this.paths, this.alive);
|
|
23
|
+
}
|
|
24
|
+
imageExists() {
|
|
25
|
+
return fs.existsSync(this.paths.image);
|
|
26
|
+
}
|
|
27
|
+
/** Holders of this set that still run. */
|
|
28
|
+
liveHolders() {
|
|
29
|
+
return setStateOf(this.stateFile.read(), this.set).holders.filter((h) => this.alive(h.pid)).length;
|
|
30
|
+
}
|
|
31
|
+
recordedMounted() {
|
|
32
|
+
return setStateOf(this.stateFile.read(), this.set).mounted;
|
|
33
|
+
}
|
|
34
|
+
reality() {
|
|
35
|
+
return this.deps.port.reality(this.paths);
|
|
36
|
+
}
|
|
37
|
+
async diagnose() {
|
|
38
|
+
const reality = await this.reality();
|
|
39
|
+
return diagnose({
|
|
40
|
+
state: setStateOf(this.stateFile.read(), this.set),
|
|
41
|
+
reality,
|
|
42
|
+
live: this.liveHolders(),
|
|
43
|
+
sandboxRunning: this.deps.sandboxRunning(),
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Makes sure the image is mounted at its folder (creating it when `create` is set and no image exists yet), records
|
|
48
|
+
* the holder and returns the mount folder, ready with the folders of the data set inside it.
|
|
49
|
+
*/
|
|
50
|
+
async acquire(holder, o = {}) {
|
|
51
|
+
this.ensureFolders();
|
|
52
|
+
// Others that run now vouch for a mount that is there; this holder is recorded first, so a watcher that is just
|
|
53
|
+
// finishing cannot dismount between the check and the use.
|
|
54
|
+
const others = this.liveHolders();
|
|
55
|
+
this.stateFile.update((s) => this.withHolder(s, holder));
|
|
56
|
+
try {
|
|
57
|
+
return await this.mountForHolder(others > 0, o.create === true);
|
|
58
|
+
}
|
|
59
|
+
catch (e) {
|
|
60
|
+
this.stateFile.update((s) => pruneHolders(removeHolder(s, this.set, holder.id), this.set, this.alive));
|
|
61
|
+
throw e;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
async mountForHolder(vouched, create) {
|
|
65
|
+
const { paths } = this;
|
|
66
|
+
const before = await this.reality();
|
|
67
|
+
const mountedAlready = vouched && this.recordedMounted() && before.mountIsMountPoint;
|
|
68
|
+
if (!mountedAlready) {
|
|
69
|
+
if (!before.imageExists && !create)
|
|
70
|
+
fail('The contained storage image is missing: ' + paths.image, 'Run sandbox storage contained on to make it again, or sandbox storage contained off to go back to folders.');
|
|
71
|
+
const outcome = await this.deps.port.mount({
|
|
72
|
+
paths,
|
|
73
|
+
maxGb: this.deps.maxGb,
|
|
74
|
+
create: !before.imageExists,
|
|
75
|
+
userSid: this.deps.userSid(),
|
|
76
|
+
});
|
|
77
|
+
this.requireOk(outcome, 'mount');
|
|
78
|
+
}
|
|
79
|
+
this.assertReallyMounted();
|
|
80
|
+
for (const name of CONTAINED_FOLDERS)
|
|
81
|
+
fs.mkdirSync(path.join(paths.mount, name), { recursive: true });
|
|
82
|
+
this.stateFile.update((s) => markMounted(s, this.set, true));
|
|
83
|
+
return paths.mount;
|
|
84
|
+
}
|
|
85
|
+
withHolder(state, holder) {
|
|
86
|
+
return addHolder(pruneHolders(state, this.set, this.alive), this.set, {
|
|
87
|
+
...holder,
|
|
88
|
+
since: new Date().toISOString(),
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
/** The record knows this set as mounted (the watcher only holds a mount that exists). */
|
|
92
|
+
isRecorded() {
|
|
93
|
+
return this.recordedMounted();
|
|
94
|
+
}
|
|
95
|
+
/** Adds a holder to a mount that is recorded as mounted (the background watcher); false when nothing is mounted. */
|
|
96
|
+
hold(holder) {
|
|
97
|
+
if (!this.recordedMounted())
|
|
98
|
+
return false;
|
|
99
|
+
this.stateFile.update((s) => this.withHolder(s, holder));
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
/** Lets go; dismounts when nobody else holds it and no sandbox runs. Never throws: a stuck mount is for doctor. */
|
|
103
|
+
async release(id) {
|
|
104
|
+
this.stateFile.update((s) => pruneHolders(removeHolder(s, this.set, id), this.set, this.alive));
|
|
105
|
+
return this.dismountIfIdle();
|
|
106
|
+
}
|
|
107
|
+
async dismountIfIdle() {
|
|
108
|
+
if (this.liveHolders() > 0)
|
|
109
|
+
return { dismounted: false, note: 'another command still uses it' };
|
|
110
|
+
if (this.deps.sandboxRunning())
|
|
111
|
+
return { dismounted: false, note: 'a sandbox is running' };
|
|
112
|
+
try {
|
|
113
|
+
// Nothing mounted: no permission prompt for nothing.
|
|
114
|
+
if (!(await this.reality()).mountIsMountPoint) {
|
|
115
|
+
if (this.recordedMounted())
|
|
116
|
+
this.clearRecord();
|
|
117
|
+
return { dismounted: false, note: 'it was not mounted' };
|
|
118
|
+
}
|
|
119
|
+
const outcome = await this.deps.port.dismount({ paths: this.paths, userSid: this.deps.userSid() });
|
|
120
|
+
if (!outcome.ok)
|
|
121
|
+
return { dismounted: false, note: outcome.message };
|
|
122
|
+
this.stateFile.update((s) => markMounted(s, this.set, false));
|
|
123
|
+
return { dismounted: true };
|
|
124
|
+
}
|
|
125
|
+
catch (e) {
|
|
126
|
+
return { dismounted: false, note: e instanceof Error ? e.message : String(e) };
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Deletes the image file (what `storage contained off` offers last, after the files were moved out). Only an image
|
|
131
|
+
* that is not mounted and not attached; never through a link. The folders around it stay.
|
|
132
|
+
*/
|
|
133
|
+
async deleteImage() {
|
|
134
|
+
assertTestWritable(this.paths.image);
|
|
135
|
+
const reality = await this.reality();
|
|
136
|
+
if (reality.mountIsMountPoint || reality.attached === true)
|
|
137
|
+
fail('The image is still mounted.', 'Dismount it first (sandbox repair), then try again.');
|
|
138
|
+
assertNoLinks(this.paths.storageRoot, this.paths.image);
|
|
139
|
+
fs.rmSync(this.paths.image, { force: true });
|
|
140
|
+
if (fs.existsSync(this.paths.image))
|
|
141
|
+
fail('The image could not be deleted: ' + this.paths.image, 'Close programs that use it.');
|
|
142
|
+
}
|
|
143
|
+
/** Doctor/repair: the record says mounted, nothing is (clears the record; no permission prompt needed). */
|
|
144
|
+
clearRecord() {
|
|
145
|
+
this.stateFile.update((s) => markMounted(pruneHolders(s, this.set, this.alive), this.set, false));
|
|
146
|
+
}
|
|
147
|
+
requireOk(outcome, what) {
|
|
148
|
+
if (outcome.ok)
|
|
149
|
+
return;
|
|
150
|
+
fail('Could not ' + what + ' the contained storage: ' + outcome.message, 'Run sandbox doctor (it checks the image and the mount folder), or turn contained storage off with: sandbox storage contained off');
|
|
151
|
+
}
|
|
152
|
+
/** A mounted image is another volume than its folder; the same id would mean writes land on this PC's own disk. */
|
|
153
|
+
assertReallyMounted() {
|
|
154
|
+
const deviceOf = this.deps.deviceOf ?? deviceOfDefault;
|
|
155
|
+
if (deviceOf(this.paths.mount) === deviceOf(this.paths.mountsDir))
|
|
156
|
+
fail('The disk image is not mounted at ' + this.paths.mount + ', so nothing was written there.', 'Run sandbox doctor.');
|
|
157
|
+
}
|
|
158
|
+
/** The folders the image needs on this PC. Only folders this call creates are made private, never one with files. */
|
|
159
|
+
ensureFolders() {
|
|
160
|
+
const { paths } = this;
|
|
161
|
+
for (const dir of [paths.imagesDir, paths.mountsDir, paths.mount]) {
|
|
162
|
+
if (fs.existsSync(dir))
|
|
163
|
+
continue;
|
|
164
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
165
|
+
this.deps.protect(dir);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { ContainedPaths } from './layout.js';
|
|
2
|
+
import type { MountReality } from './state.js';
|
|
3
|
+
export interface ScriptOutcome {
|
|
4
|
+
readonly ok: boolean;
|
|
5
|
+
readonly message: string;
|
|
6
|
+
/** mount only: the image did not exist and was made (and formatted) by this run. */
|
|
7
|
+
readonly created: boolean;
|
|
8
|
+
/** mount only: it was mounted before this run. */
|
|
9
|
+
readonly alreadyMounted: boolean;
|
|
10
|
+
readonly notes: readonly string[];
|
|
11
|
+
}
|
|
12
|
+
export interface MountRequest {
|
|
13
|
+
readonly paths: ContainedPaths;
|
|
14
|
+
readonly maxGb: number;
|
|
15
|
+
/** true only the first time: no image file exists yet. The script refuses to create over a file. */
|
|
16
|
+
readonly create: boolean;
|
|
17
|
+
readonly userSid: string;
|
|
18
|
+
}
|
|
19
|
+
export interface DismountRequest {
|
|
20
|
+
readonly paths: ContainedPaths;
|
|
21
|
+
readonly userSid: string;
|
|
22
|
+
}
|
|
23
|
+
export interface DiskPort {
|
|
24
|
+
mount(request: MountRequest): Promise<ScriptOutcome>;
|
|
25
|
+
dismount(request: DismountRequest): Promise<ScriptOutcome>;
|
|
26
|
+
reality(paths: ContainedPaths): Promise<MountReality>;
|
|
27
|
+
}
|
|
28
|
+
export interface RunResult {
|
|
29
|
+
readonly status: number | null;
|
|
30
|
+
readonly stdout: string;
|
|
31
|
+
readonly stderr: string;
|
|
32
|
+
}
|
|
33
|
+
export type Runner = (file: string, args: readonly string[], timeoutMs: number) => Promise<RunResult>;
|
|
34
|
+
export declare const spawnRunner: Runner;
|
|
35
|
+
export declare class ScriptDiskPort implements DiskPort {
|
|
36
|
+
private readonly run;
|
|
37
|
+
constructor(run?: Runner);
|
|
38
|
+
mount(request: MountRequest): Promise<ScriptOutcome>;
|
|
39
|
+
dismount(request: DismountRequest): Promise<ScriptOutcome>;
|
|
40
|
+
reality(paths: ContainedPaths): Promise<MountReality>;
|
|
41
|
+
/** One run of an elevated script: the request goes in a file in a private temp folder, the answer comes back so. */
|
|
42
|
+
private elevated;
|
|
43
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
// The only place that touches disks: mounting and dismounting go through native\scripts\mount-contained.ps1 and
|
|
2
|
+
// dismount-contained.ps1 (run elevated through run-elevated.ps1: one Windows permission prompt each), looking goes
|
|
3
|
+
// through check-contained.ps1 (no elevation). Everything above this file works against the `DiskPort` interface, so
|
|
4
|
+
// the tests use a fake and the real scripts are only ever started on a PC.
|
|
5
|
+
import crypto from 'node:crypto';
|
|
6
|
+
import fs from 'node:fs';
|
|
7
|
+
import os from 'node:os';
|
|
8
|
+
import path from 'node:path';
|
|
9
|
+
import { spawn } from 'node:child_process';
|
|
10
|
+
import { z } from 'zod';
|
|
11
|
+
import { fail } from '../../../core/util/index.js';
|
|
12
|
+
import { readValid } from '../../../shared/io/index.js';
|
|
13
|
+
import { SYSTEM } from '../../../shared/system.js';
|
|
14
|
+
import { nativeFile, nativeScript } from '../../native.js';
|
|
15
|
+
import { refuseInSelfTest } from '../../test-guard.js';
|
|
16
|
+
const OutcomeSchema = z.object({
|
|
17
|
+
ok: z.boolean(),
|
|
18
|
+
message: z.string().max(4000),
|
|
19
|
+
created: z.boolean().optional(),
|
|
20
|
+
alreadyMounted: z.boolean().optional(),
|
|
21
|
+
notes: z
|
|
22
|
+
.union([z.array(z.string().max(2000)).max(100), z.string().max(2000)])
|
|
23
|
+
.optional()
|
|
24
|
+
.transform((v) => (v === undefined ? [] : typeof v === 'string' ? [v] : v)),
|
|
25
|
+
});
|
|
26
|
+
const RealitySchema = z.object({
|
|
27
|
+
imageExists: z.boolean(),
|
|
28
|
+
imageBytes: z.number().nonnegative(),
|
|
29
|
+
mountExists: z.boolean(),
|
|
30
|
+
mountIsMountPoint: z.boolean(),
|
|
31
|
+
attached: z.boolean().nullable(),
|
|
32
|
+
});
|
|
33
|
+
const MOUNT_TIMEOUT_MS = 10 * 60_000;
|
|
34
|
+
const LOOK_TIMEOUT_MS = 30_000;
|
|
35
|
+
export const spawnRunner = (file, args, timeoutMs) => new Promise((resolve) => {
|
|
36
|
+
const child = spawn(file, [...args], { windowsHide: true, stdio: ['ignore', 'pipe', 'pipe'] });
|
|
37
|
+
let stdout = '';
|
|
38
|
+
let stderr = '';
|
|
39
|
+
const timer = setTimeout(() => child.kill(), timeoutMs);
|
|
40
|
+
child.stdout.on('data', (d) => (stdout += d.toString('utf8')));
|
|
41
|
+
child.stderr.on('data', (d) => (stderr += d.toString('utf8')));
|
|
42
|
+
child.on('error', (e) => {
|
|
43
|
+
clearTimeout(timer);
|
|
44
|
+
resolve({ status: null, stdout, stderr: stderr + e.message });
|
|
45
|
+
});
|
|
46
|
+
child.on('close', (code) => {
|
|
47
|
+
clearTimeout(timer);
|
|
48
|
+
resolve({ status: code, stdout, stderr });
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
export class ScriptDiskPort {
|
|
52
|
+
run;
|
|
53
|
+
constructor(run = spawnRunner) {
|
|
54
|
+
this.run = run;
|
|
55
|
+
}
|
|
56
|
+
mount(request) {
|
|
57
|
+
const { paths } = request;
|
|
58
|
+
return this.elevated('mount-contained.ps1', {
|
|
59
|
+
set: paths.set,
|
|
60
|
+
storageRoot: paths.storageRoot,
|
|
61
|
+
image: paths.image,
|
|
62
|
+
mount: paths.mount,
|
|
63
|
+
userSid: request.userSid,
|
|
64
|
+
maxGb: request.maxGb,
|
|
65
|
+
create: request.create,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
dismount(request) {
|
|
69
|
+
const { paths } = request;
|
|
70
|
+
return this.elevated('dismount-contained.ps1', {
|
|
71
|
+
set: paths.set,
|
|
72
|
+
storageRoot: paths.storageRoot,
|
|
73
|
+
image: paths.image,
|
|
74
|
+
mount: paths.mount,
|
|
75
|
+
userSid: request.userSid,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
async reality(paths) {
|
|
79
|
+
const out = await this.run(SYSTEM.powershell(), nativeScript('check-contained.ps1', ['-Image', paths.image, '-Mount', paths.mount]), LOOK_TIMEOUT_MS);
|
|
80
|
+
const parsed = RealitySchema.safeParse(safeJson(out.stdout.trim()));
|
|
81
|
+
if (out.status !== 0 || !parsed.success)
|
|
82
|
+
fail('Could not look at the contained storage: ' + (out.stderr.trim() || out.stdout.trim() || 'no answer') + '.', 'Run sandbox doctor.');
|
|
83
|
+
return parsed.data;
|
|
84
|
+
}
|
|
85
|
+
/** One run of an elevated script: the request goes in a file in a private temp folder, the answer comes back so. */
|
|
86
|
+
async elevated(script, request) {
|
|
87
|
+
// A test hands in a fake runner; only the real one reaches a disk.
|
|
88
|
+
if (this.run === spawnRunner)
|
|
89
|
+
refuseInSelfTest('mount or dismount a disk image');
|
|
90
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'agent-sandbox-contained-'));
|
|
91
|
+
try {
|
|
92
|
+
const file = path.join(dir, 'request.json');
|
|
93
|
+
const text = JSON.stringify(request, null, 2);
|
|
94
|
+
fs.writeFileSync(file, text, 'utf8');
|
|
95
|
+
const sha = crypto.createHash('sha256').update(fs.readFileSync(file)).digest('hex');
|
|
96
|
+
const out = await this.run(SYSTEM.powershell(), nativeScript('run-elevated.ps1', ['-Script', nativeFile('scripts', script), '-Json', file, '-Sha256', sha]), MOUNT_TIMEOUT_MS);
|
|
97
|
+
const answer = readValid(OutcomeSchema, path.join(dir, 'result.json'));
|
|
98
|
+
if (!answer)
|
|
99
|
+
return fail(out.status === 1
|
|
100
|
+
? 'Windows did not give permission (the prompt was declined or closed).'
|
|
101
|
+
: 'The disk image tool gave no answer' + (out.stderr.trim() ? ': ' + out.stderr.trim() : '.'), 'Run the command again and choose Yes in the Windows prompt.');
|
|
102
|
+
return {
|
|
103
|
+
ok: answer.ok,
|
|
104
|
+
message: answer.message,
|
|
105
|
+
created: answer.created ?? false,
|
|
106
|
+
alreadyMounted: answer.alreadyMounted ?? false,
|
|
107
|
+
notes: answer.notes,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
finally {
|
|
111
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
function safeJson(text) {
|
|
116
|
+
try {
|
|
117
|
+
return JSON.parse(text);
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { CONTAINED_FOLDERS, containedMappings, containedPaths, setNameOf, type ContainedFolder } from './layout.js';
|
|
2
|
+
export type { ContainedPaths } from './layout.js';
|
|
3
|
+
export { ContainedStorage, type ContainedDeps, type HolderId, type ReleaseResult } from './contained-storage.js';
|
|
4
|
+
export { ScriptDiskPort, spawnRunner, type DiskPort, type MountRequest, type ScriptOutcome } from './disk-port.js';
|
|
5
|
+
export { containedAvailability, containedFacts, type ContainedFacts } from './check.js';
|
|
6
|
+
export { hostDataFolder, hostDataRoot } from './resolve.js';
|
|
7
|
+
export { writeContainedSetting } from './setting.js';
|
|
8
|
+
export { addHolder, diagnose, markMounted, mountedSets, staleMountNote, pruneHolders, removeHolder, setStateOf, StateFile, type ContainedDiagnosis, type MountReality, } from './state.js';
|
|
9
|
+
export { assertTargetsFree, planTransfer, runTransfer, type TransferPlan, type TransferResult } from './transfer.js';
|