windows-development-sandbox 1.0.5 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +25 -8
- package/config.example.json +55 -5
- package/dist/api.js +2 -1
- package/dist/cli/args.d.ts +23 -0
- package/dist/cli/args.js +38 -4
- package/dist/cli/colors.d.ts +24 -0
- package/dist/cli/colors.js +42 -0
- package/dist/cli/commands/accounts.js +2 -2
- package/dist/cli/commands/agent-commands.d.ts +6 -0
- package/dist/cli/commands/agent-commands.js +61 -0
- package/dist/cli/commands/context.d.ts +2 -0
- package/dist/cli/commands/context.js +5 -1
- package/dist/cli/commands/data/clean.d.ts +15 -0
- package/dist/cli/commands/data/clean.js +106 -0
- package/dist/cli/commands/data/collect.d.ts +11 -0
- package/dist/cli/commands/data/collect.js +54 -0
- package/dist/cli/commands/data/index.d.ts +4 -0
- package/dist/cli/commands/data/index.js +45 -0
- package/dist/cli/commands/data/remove.d.ts +2 -0
- package/dist/cli/commands/data/remove.js +30 -0
- package/dist/cli/commands/index.d.ts +6 -4
- package/dist/cli/commands/index.js +6 -4
- package/dist/cli/commands/launch.d.ts +26 -2
- package/dist/cli/commands/launch.js +256 -42
- package/dist/cli/commands/maintenance.d.ts +2 -0
- package/dist/cli/commands/maintenance.js +67 -5
- package/dist/cli/commands/project.d.ts +29 -0
- package/dist/cli/commands/project.js +63 -0
- package/dist/cli/commands/projects.d.ts +14 -0
- package/dist/cli/commands/projects.js +146 -0
- package/dist/cli/commands/repair/checks.d.ts +2 -0
- package/dist/cli/commands/{repair.js → repair/checks.js} +19 -181
- package/dist/cli/commands/{repair.d.ts → repair/finders.d.ts} +0 -29
- package/dist/cli/commands/repair/finders.js +108 -0
- package/dist/cli/commands/repair/index.d.ts +12 -0
- package/dist/cli/commands/repair/index.js +61 -0
- package/dist/cli/commands/repair/types.d.ts +26 -0
- package/dist/cli/commands/repair/types.js +3 -0
- package/dist/cli/commands/restart.d.ts +6 -0
- package/dist/cli/commands/restart.js +85 -0
- package/dist/cli/commands/sessions.d.ts +2 -1
- package/dist/cli/commands/sessions.js +189 -45
- package/dist/cli/commands/setup/checks.d.ts +13 -0
- package/dist/cli/commands/setup/checks.js +71 -0
- package/dist/cli/commands/{setup.d.ts → setup/facts.d.ts} +1 -15
- package/dist/cli/commands/setup/facts.js +60 -0
- package/dist/cli/commands/setup/index.d.ts +2 -0
- package/dist/cli/commands/setup/index.js +43 -0
- package/dist/cli/commands/user-config.d.ts +8 -0
- package/dist/cli/commands/user-config.js +41 -0
- package/dist/cli/history.d.ts +11 -0
- package/dist/cli/history.js +21 -0
- package/dist/cli/index.d.ts +7 -1
- package/dist/cli/index.js +42 -15
- package/dist/cli/manifest.d.ts +8 -0
- package/dist/cli/manifest.js +40 -15
- package/dist/cli/menu/apps.d.ts +21 -0
- package/dist/cli/menu/apps.js +122 -0
- package/dist/cli/menu/command-form.d.ts +28 -0
- package/dist/cli/menu/command-form.js +219 -0
- package/dist/cli/menu/index.d.ts +26 -0
- package/dist/cli/menu/index.js +180 -0
- package/dist/cli/menu/settings/categories.d.ts +9 -0
- package/dist/cli/menu/settings/categories.js +28 -0
- package/dist/cli/menu/settings/config-file.d.ts +40 -0
- package/dist/cli/menu/settings/config-file.js +71 -0
- package/dist/cli/menu/settings/editors.d.ts +30 -0
- package/dist/cli/menu/settings/editors.js +305 -0
- package/dist/cli/menu/settings/index.d.ts +32 -0
- package/dist/cli/menu/settings/index.js +136 -0
- package/dist/cli/menu/settings/schema-node.d.ts +62 -0
- package/dist/cli/menu/settings/schema-node.js +104 -0
- package/dist/cli/menu/several.d.ts +20 -0
- package/dist/cli/menu/several.js +102 -0
- package/dist/cli/progress.d.ts +9 -0
- package/dist/cli/progress.js +49 -0
- package/dist/cli/tui/app.d.ts +39 -0
- package/dist/cli/tui/app.js +52 -0
- package/dist/cli/tui/dialogs.d.ts +57 -0
- package/dist/cli/tui/dialogs.js +162 -0
- package/dist/cli/tui/frame.d.ts +18 -0
- package/dist/cli/tui/frame.js +27 -0
- package/dist/cli/tui/index.d.ts +7 -0
- package/dist/cli/tui/index.js +8 -0
- package/dist/cli/tui/list-screen.d.ts +101 -0
- package/dist/cli/tui/list-screen.js +299 -0
- package/dist/cli/tui/terminal.d.ts +37 -0
- package/dist/cli/tui/terminal.js +89 -0
- package/dist/cli/tui/text.d.ts +20 -0
- package/dist/cli/tui/text.js +105 -0
- package/dist/cli/tui/theme.d.ts +45 -0
- package/dist/cli/tui/theme.js +30 -0
- package/dist/cli/validate.d.ts +17 -0
- package/dist/cli/validate.js +89 -0
- package/dist/core/config.js +12 -6
- package/dist/core/disk.d.ts +16 -0
- package/dist/core/disk.js +55 -0
- package/dist/core/local-config.d.ts +23 -0
- package/dist/core/local-config.js +79 -7
- package/dist/core/project-policy.d.ts +15 -0
- package/dist/core/project-policy.js +35 -0
- package/dist/core/prompt.d.ts +4 -0
- package/dist/core/prompt.js +15 -0
- package/dist/core/safe-paths.d.ts +4 -0
- package/dist/core/safe-paths.js +37 -0
- package/dist/core/schema/apps.d.ts +40 -0
- package/dist/core/schema/apps.js +27 -0
- package/dist/core/schema/common.d.ts +17 -0
- package/dist/core/schema/common.js +15 -0
- package/dist/core/schema/config.d.ts +659 -0
- package/dist/core/schema/config.js +75 -0
- package/dist/core/schema/databases.d.ts +33 -0
- package/dist/core/schema/databases.js +35 -0
- package/dist/core/schema/harness.d.ts +12 -0
- package/dist/core/schema/harness.js +17 -0
- package/dist/core/schema/json-schema.d.ts +5 -0
- package/dist/core/schema/json-schema.js +25 -0
- package/dist/core/schema/profile.d.ts +32 -0
- package/dist/core/schema/profile.js +31 -0
- package/dist/core/schema/projects.d.ts +9 -0
- package/dist/core/schema/projects.js +11 -0
- package/dist/core/schema/sandbox.d.ts +78 -0
- package/dist/core/schema/sandbox.js +64 -0
- package/dist/core/schema/toolchains.d.ts +96 -0
- package/dist/core/schema/toolchains.js +88 -0
- package/dist/core/schema.d.ts +6 -604
- package/dist/core/schema.js +8 -264
- package/dist/core/types.d.ts +35 -1
- package/dist/core/util.d.ts +9 -0
- package/dist/core/util.js +14 -3
- package/dist/core/workspace.d.ts +0 -5
- package/dist/core/workspace.js +33 -50
- package/dist/guest/apps/index.d.ts +10 -0
- package/dist/guest/apps/index.js +206 -0
- package/dist/guest/apps/resolvers.d.ts +8 -0
- package/dist/guest/apps/resolvers.js +90 -0
- package/dist/guest/audit-parse.d.ts +7 -0
- package/dist/guest/audit-parse.js +24 -0
- package/dist/guest/audit.d.ts +4 -0
- package/dist/guest/audit.js +47 -0
- package/dist/guest/boot-env.d.ts +4 -0
- package/dist/guest/boot-env.js +25 -0
- package/dist/guest/cmdline.d.ts +12 -0
- package/dist/guest/cmdline.js +23 -0
- package/dist/guest/daemon/exec.d.ts +5 -0
- package/dist/guest/daemon/exec.js +32 -0
- package/dist/guest/daemon/project.d.ts +14 -0
- package/dist/guest/daemon/project.js +132 -0
- package/dist/guest/daemon/relays.d.ts +1 -0
- package/dist/guest/daemon/relays.js +18 -0
- package/dist/guest/daemon/session.d.ts +13 -0
- package/dist/guest/daemon/session.js +139 -0
- package/dist/guest/daemon/shutdown.d.ts +1 -0
- package/dist/guest/daemon/shutdown.js +21 -0
- package/dist/guest/daemon/state.d.ts +17 -0
- package/dist/guest/daemon/state.js +31 -0
- package/dist/guest/daemon.js +12 -141
- package/dist/guest/db.js +18 -3
- package/dist/guest/env.d.ts +3 -0
- package/dist/guest/env.js +7 -3
- package/dist/guest/guide.d.ts +6 -2
- package/dist/guest/guide.js +5 -4
- package/dist/guest/helpers.d.ts +3 -0
- package/dist/guest/helpers.js +26 -0
- package/dist/guest/main.js +31 -8
- package/dist/guest/netproxy.d.ts +1 -0
- package/dist/guest/netproxy.js +100 -0
- package/dist/guest/ports.d.ts +6 -0
- package/dist/guest/ports.js +22 -0
- package/dist/guest/project-context.d.ts +7 -0
- package/dist/guest/project-context.js +10 -0
- package/dist/guest/project-env.d.ts +20 -0
- package/dist/guest/project-env.js +20 -0
- package/dist/guest/project-status.d.ts +5 -0
- package/dist/guest/project-status.js +17 -0
- package/dist/guest/ptyhost.d.ts +0 -18
- package/dist/guest/ptyhost.js +17 -65
- package/dist/guest/setup/agents.d.ts +5 -2
- package/dist/guest/setup/agents.js +22 -8
- package/dist/guest/setup/claude.d.ts +0 -4
- package/dist/guest/setup/claude.js +59 -5
- package/dist/guest/setup/environment.d.ts +13 -3
- package/dist/guest/setup/environment.js +14 -10
- package/dist/guest/setup/hooks.d.ts +9 -1
- package/dist/guest/setup/hooks.js +28 -14
- package/dist/guest/setup/network.d.ts +9 -0
- package/dist/guest/setup/network.js +97 -4
- package/dist/guest/setup/user.js +4 -0
- package/dist/guest/toolchains/catalogue.d.ts +4 -0
- package/dist/guest/toolchains/catalogue.js +2 -2
- package/dist/guest/toolchains/cli.js +25 -17
- package/dist/guest/toolchains/common.d.ts +18 -1
- package/dist/guest/toolchains/common.js +34 -11
- package/dist/guest/toolchains/databases/dynamodb.d.ts +8 -0
- package/dist/guest/toolchains/databases/dynamodb.js +52 -0
- package/dist/guest/toolchains/{databases.d.ts → databases/index.d.ts} +2 -3
- package/dist/guest/toolchains/databases/index.js +62 -0
- package/dist/guest/toolchains/databases/mongodb.d.ts +3 -0
- package/dist/guest/toolchains/databases/mongodb.js +31 -0
- package/dist/guest/toolchains/databases/mysql.d.ts +3 -0
- package/dist/guest/toolchains/databases/mysql.js +38 -0
- package/dist/guest/toolchains/databases/postgres.d.ts +5 -0
- package/dist/guest/toolchains/databases/postgres.js +38 -0
- package/dist/guest/toolchains/databases/redis.d.ts +3 -0
- package/dist/guest/toolchains/databases/redis.js +16 -0
- package/dist/guest/toolchains/databases/server.d.ts +14 -0
- package/dist/guest/toolchains/databases/server.js +52 -0
- package/dist/guest/toolchains/git.js +25 -16
- package/dist/guest/toolchains/go.js +1 -1
- package/dist/guest/toolchains/index.d.ts +13 -9
- package/dist/guest/toolchains/index.js +40 -82
- package/dist/guest/toolchains/java.js +5 -5
- package/dist/guest/toolchains/leftovers.d.ts +2 -0
- package/dist/guest/toolchains/leftovers.js +26 -0
- package/dist/guest/toolchains/managers.js +12 -10
- package/dist/guest/toolchains/{binstall.js → native/binstall.js} +6 -9
- package/dist/guest/toolchains/native/cpp.d.ts +4 -0
- package/dist/guest/toolchains/native/cpp.js +38 -0
- package/dist/guest/toolchains/native/dotnet.d.ts +4 -0
- package/dist/guest/toolchains/native/dotnet.js +103 -0
- package/dist/guest/toolchains/native/mesa.d.ts +7 -0
- package/dist/guest/toolchains/native/mesa.js +56 -0
- package/dist/guest/toolchains/native/msvc/environment.d.ts +16 -0
- package/dist/guest/toolchains/native/msvc/environment.js +57 -0
- package/dist/guest/toolchains/native/msvc/index.d.ts +6 -0
- package/dist/guest/toolchains/native/msvc/index.js +48 -0
- package/dist/guest/toolchains/native/msvc/packages.d.ts +6 -0
- package/dist/guest/toolchains/native/msvc/packages.js +156 -0
- package/dist/guest/toolchains/native/msvc/tree.d.ts +15 -0
- package/dist/guest/toolchains/native/msvc/tree.js +21 -0
- package/dist/guest/toolchains/native/rust.d.ts +3 -0
- package/dist/guest/toolchains/native/rust.js +45 -0
- package/dist/guest/toolchains/node.js +26 -6
- package/dist/guest/toolchains/plan.d.ts +24 -0
- package/dist/guest/toolchains/plan.js +140 -0
- package/dist/guest/toolchains/pwsh.js +1 -1
- package/dist/guest/toolchains/python.js +43 -12
- package/dist/guest/toolchains/runtimes.d.ts +2 -0
- package/dist/guest/toolchains/runtimes.js +6 -8
- package/dist/guest/toolchains/scripting/php.d.ts +3 -0
- package/dist/guest/toolchains/scripting/php.js +71 -0
- package/dist/guest/toolchains/scripting/ruby.d.ts +3 -0
- package/dist/guest/toolchains/scripting/ruby.js +47 -0
- package/dist/guest/toolchains/scripting/vcruntime.d.ts +7 -0
- package/dist/guest/toolchains/scripting/vcruntime.js +45 -0
- package/dist/guest/toolchains/vscode-profile.d.ts +30 -0
- package/dist/guest/toolchains/vscode-profile.js +69 -0
- package/dist/guest/toolchains/vscode.d.ts +17 -0
- package/dist/guest/toolchains/vscode.js +87 -1
- package/dist/guest/view.js +21 -1
- package/dist/host/accounts.js +14 -5
- package/dist/host/agent-commands.d.ts +25 -0
- package/dist/host/agent-commands.js +145 -0
- package/dist/host/attach.d.ts +17 -0
- package/dist/host/attach.js +70 -0
- package/dist/host/audit/collect.d.ts +34 -0
- package/dist/host/audit/collect.js +126 -0
- package/dist/host/audit/log.d.ts +46 -0
- package/dist/host/audit/log.js +143 -0
- package/dist/host/channel.d.ts +23 -22
- package/dist/host/channel.js +66 -84
- package/dist/host/claude-profile.js +7 -3
- package/dist/host/cleanup.js +2 -11
- package/dist/host/database-plan.d.ts +12 -0
- package/dist/host/database-plan.js +52 -0
- package/dist/host/generate.d.ts +10 -12
- package/dist/host/generate.js +58 -135
- package/dist/host/join.d.ts +28 -0
- package/dist/host/join.js +76 -0
- package/dist/host/launch/mappings.d.ts +3 -0
- package/dist/host/launch/mappings.js +54 -0
- package/dist/host/launch/projects.d.ts +15 -0
- package/dist/host/launch/projects.js +56 -0
- package/dist/host/launch/stage.d.ts +11 -0
- package/dist/host/launch/stage.js +66 -0
- package/dist/host/launch/wsb.d.ts +10 -0
- package/dist/host/launch/wsb.js +34 -0
- package/dist/host/motw.d.ts +17 -0
- package/dist/host/motw.js +74 -0
- package/dist/host/pc-scan.d.ts +6 -0
- package/dist/host/pc-scan.js +37 -0
- package/dist/host/platform.d.ts +2 -0
- package/dist/host/platform.js +16 -1
- package/dist/host/port-forward.d.ts +8 -0
- package/dist/host/port-forward.js +37 -0
- package/dist/host/project-scan.d.ts +1 -17
- package/dist/host/project-scan.js +2 -86
- package/dist/host/projects.d.ts +100 -0
- package/dist/host/projects.js +147 -0
- package/dist/host/repo-guard.d.ts +25 -5
- package/dist/host/repo-guard.js +242 -23
- package/dist/host/stops.d.ts +67 -0
- package/dist/host/stops.js +126 -0
- package/dist/host/terminal.d.ts +8 -0
- package/dist/host/terminal.js +48 -1
- package/dist/host/test-guard.d.ts +6 -0
- package/dist/host/test-guard.js +15 -0
- package/dist/host/window.js +7 -3
- package/dist/host/workspace-view/index.d.ts +20 -0
- package/dist/host/workspace-view/index.js +53 -0
- package/dist/host/workspace-view/rules.d.ts +26 -0
- package/dist/host/workspace-view/rules.js +93 -0
- package/dist/host/workspace-view/view.d.ts +50 -0
- package/dist/host/workspace-view/view.js +281 -0
- package/dist/host/wsb.d.ts +37 -0
- package/dist/host/wsb.js +101 -0
- package/dist/runtime/daemon.js +11791 -2821
- package/dist/runtime/db.js +6585 -466
- package/dist/runtime/main.js +10238 -1724
- package/dist/runtime/netproxy.js +25936 -0
- package/dist/runtime/view.js +6183 -59
- package/dist/selftest/accounts.test.d.ts +2 -0
- package/dist/selftest/accounts.test.js +44 -0
- package/dist/selftest/agent-commands.test.d.ts +2 -0
- package/dist/selftest/agent-commands.test.js +47 -0
- package/dist/selftest/audit.test.d.ts +2 -0
- package/dist/selftest/audit.test.js +57 -0
- package/dist/selftest/channel.test.d.ts +2 -0
- package/dist/selftest/channel.test.js +44 -0
- package/dist/selftest/cli.test.d.ts +2 -2
- package/dist/selftest/cli.test.js +145 -27
- package/dist/selftest/cmdline.test.d.ts +2 -0
- package/dist/selftest/cmdline.test.js +45 -0
- package/dist/selftest/config.test.d.ts +2 -3
- package/dist/selftest/config.test.js +36 -72
- package/dist/selftest/data.test.d.ts +2 -2
- package/dist/selftest/data.test.js +77 -70
- package/dist/selftest/detect.test.d.ts +2 -2
- package/dist/selftest/detect.test.js +54 -38
- package/dist/selftest/fixtures.d.ts +89 -4
- package/dist/selftest/fixtures.js +43 -24
- package/dist/selftest/generate.test.d.ts +2 -0
- package/dist/selftest/generate.test.js +68 -0
- package/dist/selftest/git-access.test.d.ts +2 -0
- package/dist/selftest/git-access.test.js +26 -0
- package/dist/selftest/guest-boot.test.d.ts +2 -0
- package/dist/selftest/guest-boot.test.js +76 -0
- package/dist/selftest/harness.d.ts +44 -7
- package/dist/selftest/harness.js +62 -21
- package/dist/selftest/host-sim.test.d.ts +2 -0
- package/dist/selftest/host-sim.test.js +93 -0
- package/dist/selftest/hostile-input.test.d.ts +2 -0
- package/dist/selftest/hostile-input.test.js +54 -0
- package/dist/selftest/index.d.ts +8 -1
- package/dist/selftest/index.js +137 -35
- package/dist/selftest/links.test.d.ts +2 -0
- package/dist/selftest/links.test.js +50 -0
- package/dist/selftest/live-daemon.test.d.ts +2 -0
- package/dist/selftest/live-daemon.test.js +91 -0
- package/dist/selftest/live-git.test.d.ts +2 -0
- package/dist/selftest/live-git.test.js +60 -0
- package/dist/selftest/live-network.test.d.ts +2 -0
- package/dist/selftest/live-network.test.js +63 -0
- package/dist/selftest/live-projects.test.d.ts +2 -0
- package/dist/selftest/live-projects.test.js +127 -0
- package/dist/selftest/live.d.ts +65 -0
- package/dist/selftest/live.js +109 -0
- package/dist/selftest/local-config.test.d.ts +2 -0
- package/dist/selftest/local-config.test.js +120 -0
- package/dist/selftest/mirror.test.d.ts +2 -3
- package/dist/selftest/mirror.test.js +92 -65
- package/dist/selftest/network.test.d.ts +2 -0
- package/dist/selftest/network.test.js +40 -0
- package/dist/selftest/personalization.test.d.ts +2 -0
- package/dist/selftest/personalization.test.js +53 -0
- package/dist/selftest/projects.test.d.ts +2 -0
- package/dist/selftest/projects.test.js +254 -0
- package/dist/selftest/repair.test.d.ts +2 -0
- package/dist/selftest/repair.test.js +42 -0
- package/dist/selftest/repo-guard.test.d.ts +2 -0
- package/dist/selftest/repo-guard.test.js +121 -0
- package/dist/selftest/secrets.test.d.ts +2 -0
- package/dist/selftest/secrets.test.js +71 -0
- package/dist/selftest/setup.test.d.ts +2 -2
- package/dist/selftest/setup.test.js +45 -22
- package/dist/selftest/suite.d.ts +13 -0
- package/dist/selftest/suite.js +1 -0
- package/dist/selftest/sync.test.d.ts +2 -3
- package/dist/selftest/sync.test.js +130 -78
- package/dist/selftest/test-guard.test.d.ts +2 -0
- package/dist/selftest/test-guard.test.js +49 -0
- package/dist/selftest/toolchains.test.d.ts +2 -0
- package/dist/selftest/toolchains.test.js +133 -0
- package/dist/selftest/workspace-view.test.d.ts +2 -0
- package/dist/selftest/workspace-view.test.js +89 -0
- package/dist/selftest/workspace.test.d.ts +2 -0
- package/dist/selftest/workspace.test.js +53 -0
- package/dist/shared/apps.d.ts +49 -0
- package/dist/shared/apps.js +114 -0
- package/dist/shared/download.d.ts +3 -1
- package/dist/shared/download.js +19 -4
- package/dist/shared/netpolicy.d.ts +27 -0
- package/dist/shared/netpolicy.js +51 -0
- package/dist/shared/paths.d.ts +5 -0
- package/dist/shared/paths.js +6 -0
- package/dist/shared/projects.d.ts +53 -0
- package/dist/shared/projects.js +67 -0
- package/dist/shared/pty-client.d.ts +9 -4
- package/dist/shared/pty-client.js +139 -17
- package/dist/shared/rundir.d.ts +12 -1
- package/dist/shared/rundir.js +14 -4
- package/dist/shared/safe-remove.js +11 -0
- package/dist/shared/schema.d.ts +453 -0
- package/dist/shared/schema.js +43 -2
- package/dist/shared/system.d.ts +1 -0
- package/dist/shared/system.js +1 -0
- package/dist/shared/test-guard.d.ts +4 -0
- package/dist/shared/test-guard.js +37 -0
- package/dist/sync/claude-mem.js +6 -3
- package/dist/sync/history.d.ts +29 -12
- package/dist/sync/history.js +72 -25
- package/dist/sync/watcher.d.ts +8 -0
- package/dist/sync/watcher.js +99 -8
- package/package.json +7 -6
- package/sandbox/bootstrap.ps1 +258 -258
- package/sandbox/cargoshim.cs +56 -0
- package/sandbox/cpucap.cs +33 -0
- package/sandbox/cpucap.ps1 +20 -0
- package/sandbox/logon.cmd +24 -16
- package/sandbox/memlog.ps1 +34 -0
- package/sandbox/openssl.cs +42 -0
- package/sandbox/pagefile.cs +31 -0
- package/sandbox/pagefile.ps1 +21 -0
- package/sandbox/ptyhost.cs +120 -11
- package/sandbox/trust.cmd +35 -35
- package/schemas/config.schema.json +403 -3
- package/schemas/sandbox-config.schema.json +143 -13
- package/dist/cli/commands/data.d.ts +0 -46
- package/dist/cli/commands/data.js +0 -249
- package/dist/cli/commands/setup.js +0 -152
- package/dist/cli/menu.d.ts +0 -22
- package/dist/cli/menu.js +0 -187
- package/dist/cli/settings.d.ts +0 -11
- package/dist/cli/settings.js +0 -156
- package/dist/guest/toolchains/databases.js +0 -226
- package/dist/guest/toolchains/msvc.d.ts +0 -10
- package/dist/guest/toolchains/msvc.js +0 -216
- package/dist/guest/toolchains/native.d.ts +0 -12
- package/dist/guest/toolchains/native.js +0 -220
- package/dist/guest/toolchains/scripting.d.ts +0 -10
- package/dist/guest/toolchains/scripting.js +0 -137
- package/dist/selftest/guest.test.d.ts +0 -3
- package/dist/selftest/guest.test.js +0 -61
- package/dist/selftest/helpers.test.d.ts +0 -2
- package/dist/selftest/helpers.test.js +0 -125
- package/dist/selftest/host.test.d.ts +0 -2
- package/dist/selftest/host.test.js +0 -71
- package/dist/selftest/live.test.d.ts +0 -7
- package/dist/selftest/live.test.js +0 -127
- package/dist/selftest/security.test.d.ts +0 -3
- package/dist/selftest/security.test.js +0 -143
- /package/dist/guest/toolchains/{binstall.d.ts → native/binstall.d.ts} +0 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
// "Start several agents": projects, then agents for each, then where to attach. It only builds a command line
|
|
2
|
+
// (sandbox launch --session <agent>@<folder> ...), so "repeat last" works and typing it does the same.
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { assertSafeWorkspace } from '../../core/safe-paths.js';
|
|
6
|
+
import { errMsg, expandPath } from '../../core/util.js';
|
|
7
|
+
import { readKnown } from '../../host/projects.js';
|
|
8
|
+
import { isWithin } from '../../shared/projects.js';
|
|
9
|
+
import { commandLine } from '../history.js';
|
|
10
|
+
import { Text, Theme } from '../tui/index.js';
|
|
11
|
+
/** Folders worth offering: this one, the projects started before, and the folders inside your roots. */
|
|
12
|
+
export const candidateFolders = (ctx, cwd = process.cwd()) => {
|
|
13
|
+
const roots = ctx.loaded.config.projects.roots.map((r) => expandPath(r, ctx.loaded.base));
|
|
14
|
+
const inRoots = roots.flatMap((r) => { try {
|
|
15
|
+
return fs.readdirSync(r, { withFileTypes: true }).filter((e) => e.isDirectory() && !e.isSymbolicLink() && !e.name.startsWith('.')).slice(0, 200).map((e) => path.join(r, e.name));
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return [];
|
|
19
|
+
} });
|
|
20
|
+
const all = [path.resolve(cwd), ...readKnown(ctx.storageRoot).map((k) => k.host), ...inRoots];
|
|
21
|
+
return all.filter((f, i) => all.findIndex((g) => g.toLowerCase() === f.toLowerCase()) === i && fs.existsSync(f));
|
|
22
|
+
};
|
|
23
|
+
/** The command line for several sessions: one --session per (agent, folder), with the shared options. */
|
|
24
|
+
export const severalAgentsArgv = (sessions, o) => ['launch', ...sessions.flatMap((s) => ['--session', s.harness + '@' + s.folder]), ...(o.headless ? ['--headless'] : []), ...(o.attachFirst ? ['-t'] : [])];
|
|
25
|
+
const ADD = '@add';
|
|
26
|
+
export class SeveralAgents {
|
|
27
|
+
app;
|
|
28
|
+
ctx;
|
|
29
|
+
path = ['Start several agents'];
|
|
30
|
+
constructor(app, ctx) {
|
|
31
|
+
this.app = app;
|
|
32
|
+
this.ctx = ctx;
|
|
33
|
+
}
|
|
34
|
+
async run() {
|
|
35
|
+
const folders = await this.folders();
|
|
36
|
+
if (!folders?.length)
|
|
37
|
+
return null;
|
|
38
|
+
const agents = Object.keys(this.ctx.loaded.config.harnesses);
|
|
39
|
+
const sessions = [];
|
|
40
|
+
for (const folder of folders) {
|
|
41
|
+
const picked = await this.app.list({
|
|
42
|
+
path: [...this.path, path.basename(folder)], intro: 'Agents for ' + folder + ' (one session each). Space ticks, Enter continues.', multi: true,
|
|
43
|
+
items: agents.map((a) => ({ value: a, label: a, checked: a === this.ctx.loaded.config.defaultHarness })),
|
|
44
|
+
});
|
|
45
|
+
if (picked.kind !== 'multi')
|
|
46
|
+
return null;
|
|
47
|
+
sessions.push(...picked.values.map((harness) => ({ harness, folder })));
|
|
48
|
+
}
|
|
49
|
+
if (!sessions.length)
|
|
50
|
+
return null;
|
|
51
|
+
const headless = await this.app.confirm({ path: this.path, question: 'Hide the sandbox window (headless)?', about: ['sandbox show brings it back.'], initial: this.ctx.loaded.config.sandbox.headless });
|
|
52
|
+
const attach = await this.app.choose(this.path, 'Attach this terminal to one of them? Each session also has a tab inside the sandbox; sandbox attach shows any of them later.', [
|
|
53
|
+
{ value: -1, label: 'None' },
|
|
54
|
+
...sessions.map((s, i) => ({ value: i, label: s.harness + ' in ' + Text.middle(s.folder, 60) })),
|
|
55
|
+
], 0);
|
|
56
|
+
if (attach === undefined)
|
|
57
|
+
return null;
|
|
58
|
+
// The one to attach goes first: -t attaches the first session.
|
|
59
|
+
const ordered = attach >= 0 ? [sessions[attach], ...sessions.filter((_s, i) => i !== attach)] : sessions;
|
|
60
|
+
const argv = severalAgentsArgv(ordered, { headless, attachFirst: attach >= 0 });
|
|
61
|
+
const projects = [...new Set(sessions.map((s) => path.basename(s.folder)))];
|
|
62
|
+
const go = await this.app.confirm({
|
|
63
|
+
path: this.path, question: 'Start them?',
|
|
64
|
+
about: ['One sandbox with ' + sessions.length + ' session' + (sessions.length === 1 ? '' : 's') + ': ' + ordered.map((s) => s.harness + ' in ' + path.basename(s.folder)).join(', ') + '. Maps ' + projects.join(' and ') + '.', '', '$ ' + commandLine(argv)],
|
|
65
|
+
});
|
|
66
|
+
return go ? argv : null;
|
|
67
|
+
}
|
|
68
|
+
async folders() {
|
|
69
|
+
const here = path.resolve(process.cwd());
|
|
70
|
+
const candidates = candidateFolders(this.ctx);
|
|
71
|
+
const picked = await this.app.list({
|
|
72
|
+
path: this.path, intro: 'Which projects? Each gets its own sessions; all of them are mapped into one sandbox. Space ticks, Enter continues.', multi: true,
|
|
73
|
+
items: [
|
|
74
|
+
...candidates.map((f) => ({ value: f, label: Text.middle(f, 90), hint: isWithin(f, here) && isWithin(here, f) ? Theme.muted('this folder') : undefined, checked: f.toLowerCase() === here.toLowerCase() })),
|
|
75
|
+
{ value: ADD, label: '+ Another folder…' },
|
|
76
|
+
],
|
|
77
|
+
});
|
|
78
|
+
if (picked.kind !== 'multi')
|
|
79
|
+
return null;
|
|
80
|
+
const folders = picked.values.filter((f) => f !== ADD);
|
|
81
|
+
if (picked.values.includes(ADD)) {
|
|
82
|
+
const extra = await this.app.text({
|
|
83
|
+
path: this.path, label: 'Folder to add',
|
|
84
|
+
validate: (v) => {
|
|
85
|
+
const dir = path.resolve(v.trim());
|
|
86
|
+
if (!v.trim() || !fs.existsSync(dir) || !fs.statSync(dir).isDirectory())
|
|
87
|
+
return 'Not a folder: ' + dir;
|
|
88
|
+
try {
|
|
89
|
+
assertSafeWorkspace(dir, this.ctx.packageRoot, this.ctx.storageRoot);
|
|
90
|
+
return true;
|
|
91
|
+
}
|
|
92
|
+
catch (e) {
|
|
93
|
+
return errMsg(e);
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
if (extra)
|
|
98
|
+
folders.push(path.resolve(extra.trim()));
|
|
99
|
+
}
|
|
100
|
+
return folders;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** Can a line be redrawn in place? */
|
|
2
|
+
export declare const canAnimate: (env: NodeJS.ProcessEnv, isTTY: boolean) => boolean;
|
|
3
|
+
/** "12 s", "2 min 5 s". */
|
|
4
|
+
export declare const elapsed: (ms: number) => string;
|
|
5
|
+
/**
|
|
6
|
+
* Shows `message` while work runs and removes the line afterwards. The spinner only moves while the event loop is free
|
|
7
|
+
* (waiting on the sandbox); for long synchronous work (measuring folders) the static line is what you see.
|
|
8
|
+
*/
|
|
9
|
+
export declare const working: <T>(message: string | (() => string), work: () => Promise<T> | T, enabled?: boolean) => Promise<T>;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// Feedback for slow work: a spinner line on stderr (so --json and piped output stay clean). Without a terminal (pipe, CI,
|
|
2
|
+
// TERM=dumb) it prints one plain line instead of animating.
|
|
3
|
+
import { colorEnabled, shorten, termWidth } from './colors.js';
|
|
4
|
+
const FRAMES = ['|', '/', '-', '\\'];
|
|
5
|
+
/** Can a line be redrawn in place? */
|
|
6
|
+
export const canAnimate = (env, isTTY) => isTTY && !env.CI && env.TERM !== 'dumb';
|
|
7
|
+
/** "12 s", "2 min 5 s". */
|
|
8
|
+
export const elapsed = (ms) => {
|
|
9
|
+
const s = Math.round(ms / 1000);
|
|
10
|
+
return s < 60 ? s + ' s' : Math.floor(s / 60) + ' min ' + (s % 60) + ' s';
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Shows `message` while work runs and removes the line afterwards. The spinner only moves while the event loop is free
|
|
14
|
+
* (waiting on the sandbox); for long synchronous work (measuring folders) the static line is what you see.
|
|
15
|
+
*/
|
|
16
|
+
export const working = async (message, work, enabled = true) => {
|
|
17
|
+
if (!enabled)
|
|
18
|
+
return work();
|
|
19
|
+
const err = process.stderr;
|
|
20
|
+
const start = Date.now();
|
|
21
|
+
// A function: the message follows the work (e.g. the sandbox's boot step); without a terminal each new one is a line.
|
|
22
|
+
const text = () => (typeof message === 'function' ? message() : message);
|
|
23
|
+
if (!canAnimate(process.env, Boolean(err.isTTY))) {
|
|
24
|
+
let last = text();
|
|
25
|
+
err.write(last + '...\n');
|
|
26
|
+
const follow = typeof message === 'function' ? setInterval(() => { const now = text(); if (now !== last) {
|
|
27
|
+
last = now;
|
|
28
|
+
err.write(now + '\n');
|
|
29
|
+
} }, 500) : null;
|
|
30
|
+
try {
|
|
31
|
+
return await work();
|
|
32
|
+
}
|
|
33
|
+
finally {
|
|
34
|
+
if (follow)
|
|
35
|
+
clearInterval(follow);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
let frame = 0;
|
|
39
|
+
const draw = () => { err.write('\r\x1b[2K' + (colorEnabled(process.env, true) ? '\x1b[36m' : '') + FRAMES[frame++ % FRAMES.length] + (colorEnabled(process.env, true) ? '\x1b[0m' : '') + ' ' + shorten(text(), termWidth() - 14) + (Date.now() - start > 3000 ? ' (' + elapsed(Date.now() - start) + ')' : '')); };
|
|
40
|
+
draw();
|
|
41
|
+
const timer = setInterval(draw, 120);
|
|
42
|
+
try {
|
|
43
|
+
return await work();
|
|
44
|
+
}
|
|
45
|
+
finally {
|
|
46
|
+
clearInterval(timer);
|
|
47
|
+
err.write('\r\x1b[2K');
|
|
48
|
+
}
|
|
49
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { MessageScreen, type ConfirmOptions, type TextOptions } from './dialogs.js';
|
|
2
|
+
import { type FrameParts } from './frame.js';
|
|
3
|
+
import { type ListOptions, type ListResult } from './list-screen.js';
|
|
4
|
+
import { Terminal, type Key } from './terminal.js';
|
|
5
|
+
export interface Size {
|
|
6
|
+
width: number;
|
|
7
|
+
height: number;
|
|
8
|
+
}
|
|
9
|
+
/** What a screen's key handler returns: `{ done: value }` ends the screen, nothing keeps it open (and redraws it). */
|
|
10
|
+
export type Step<T> = {
|
|
11
|
+
done: T;
|
|
12
|
+
} | undefined | void;
|
|
13
|
+
export interface Screen<T> {
|
|
14
|
+
render(size: Size): FrameParts;
|
|
15
|
+
handle(key: Key): Step<T> | Promise<Step<T>>;
|
|
16
|
+
}
|
|
17
|
+
export declare class App {
|
|
18
|
+
readonly terminal: Terminal;
|
|
19
|
+
/** The status line for screens that do not set their own (refreshed on every draw). */
|
|
20
|
+
status: () => string;
|
|
21
|
+
constructor(terminal?: Terminal);
|
|
22
|
+
/** Shows `screen` until its handler says it is done, and returns what it returned. */
|
|
23
|
+
show<T>(screen: Screen<T>): Promise<T>;
|
|
24
|
+
list<T>(o: ListOptions<T>): Promise<ListResult<T>>;
|
|
25
|
+
text(o: TextOptions): Promise<string | undefined>;
|
|
26
|
+
confirm(o: ConfirmOptions): Promise<boolean>;
|
|
27
|
+
message(o: ConstructorParameters<typeof MessageScreen>[0]): Promise<void>;
|
|
28
|
+
/** One of a few values (undefined = back). */
|
|
29
|
+
choose<T>(path: string[], intro: string, choices: {
|
|
30
|
+
value: T;
|
|
31
|
+
label: string;
|
|
32
|
+
description?: string;
|
|
33
|
+
hint?: string;
|
|
34
|
+
}[], current?: T): Promise<T | undefined>;
|
|
35
|
+
/** Runs `work` on the normal screen (command output, interactive commands); the next show() takes the screen back. */
|
|
36
|
+
suspend<T>(work: () => Promise<T>): Promise<T>;
|
|
37
|
+
/** Leaves the alternate screen for good. */
|
|
38
|
+
close(): void;
|
|
39
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// The menu as an application: screens are shown one at a time in the frame, each until it is done. Commands that print
|
|
2
|
+
// (or take over the terminal) run with the terminal handed back, and the next screen takes it again.
|
|
3
|
+
import { ConfirmScreen, MessageScreen, TextScreen } from './dialogs.js';
|
|
4
|
+
import { Frame } from './frame.js';
|
|
5
|
+
import { ListScreen } from './list-screen.js';
|
|
6
|
+
import { Terminal } from './terminal.js';
|
|
7
|
+
import { Theme } from './theme.js';
|
|
8
|
+
export class App {
|
|
9
|
+
terminal;
|
|
10
|
+
/** The status line for screens that do not set their own (refreshed on every draw). */
|
|
11
|
+
status = () => '';
|
|
12
|
+
constructor(terminal = new Terminal()) {
|
|
13
|
+
this.terminal = terminal;
|
|
14
|
+
}
|
|
15
|
+
/** Shows `screen` until its handler says it is done, and returns what it returned. */
|
|
16
|
+
async show(screen) {
|
|
17
|
+
this.terminal.start();
|
|
18
|
+
const draw = () => {
|
|
19
|
+
const { width, height } = this.terminal;
|
|
20
|
+
const parts = screen.render({ width, height: Frame.bodyHeight(height) });
|
|
21
|
+
this.terminal.render(Frame.render({ ...parts, status: parts.status ?? this.status() }, width, height));
|
|
22
|
+
};
|
|
23
|
+
this.terminal.onResized(draw);
|
|
24
|
+
try {
|
|
25
|
+
for (;;) {
|
|
26
|
+
draw();
|
|
27
|
+
const step = await screen.handle(await this.terminal.key());
|
|
28
|
+
if (step)
|
|
29
|
+
return step.done;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
finally {
|
|
33
|
+
this.terminal.onResized(null);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
list(o) { return this.show(new ListScreen(o)); }
|
|
37
|
+
text(o) { return this.show(new TextScreen(o)); }
|
|
38
|
+
confirm(o) { return this.show(new ConfirmScreen(o)); }
|
|
39
|
+
message(o) { return this.show(new MessageScreen(o)); }
|
|
40
|
+
/** One of a few values (undefined = back). */
|
|
41
|
+
async choose(path, intro, choices, current) {
|
|
42
|
+
const picked = await this.list({ path, intro, items: choices.map((c) => ({ ...c, icon: c.value === current ? Theme.ok(Theme.glyph.on) : Theme.muted(Theme.glyph.off) })), startAt: current });
|
|
43
|
+
return picked.kind === 'pick' ? picked.value : undefined;
|
|
44
|
+
}
|
|
45
|
+
/** Runs `work` on the normal screen (command output, interactive commands); the next show() takes the screen back. */
|
|
46
|
+
async suspend(work) {
|
|
47
|
+
this.terminal.stop();
|
|
48
|
+
return work();
|
|
49
|
+
}
|
|
50
|
+
/** Leaves the alternate screen for good. */
|
|
51
|
+
close() { this.terminal.stop(); }
|
|
52
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { FrameParts } from './frame.js';
|
|
2
|
+
import type { Screen, Size, Step } from './app.js';
|
|
3
|
+
import type { Key } from './terminal.js';
|
|
4
|
+
export interface TextOptions {
|
|
5
|
+
path: string[];
|
|
6
|
+
label: string;
|
|
7
|
+
/** Explanation above the field (plain lines). */
|
|
8
|
+
about?: string[];
|
|
9
|
+
initial?: string;
|
|
10
|
+
placeholder?: string;
|
|
11
|
+
/** true = fine, a string = what is wrong (shown under the field). */
|
|
12
|
+
validate?: (value: string) => true | string;
|
|
13
|
+
/** Allow an empty answer (otherwise Enter on an empty field cancels). */
|
|
14
|
+
allowEmpty?: boolean;
|
|
15
|
+
}
|
|
16
|
+
/** One line of text. Resolves to the text, or undefined when you press Esc. */
|
|
17
|
+
export declare class TextScreen implements Screen<string | undefined> {
|
|
18
|
+
private readonly o;
|
|
19
|
+
private value;
|
|
20
|
+
private cursor;
|
|
21
|
+
private problem;
|
|
22
|
+
constructor(o: TextOptions);
|
|
23
|
+
render(size: Size): FrameParts;
|
|
24
|
+
handle(key: Key): Step<string | undefined>;
|
|
25
|
+
private check;
|
|
26
|
+
}
|
|
27
|
+
export interface ConfirmOptions {
|
|
28
|
+
path: string[];
|
|
29
|
+
question: string;
|
|
30
|
+
about?: string[];
|
|
31
|
+
yes?: string;
|
|
32
|
+
no?: string;
|
|
33
|
+
initial?: boolean;
|
|
34
|
+
danger?: boolean;
|
|
35
|
+
}
|
|
36
|
+
/** Yes or no. Esc answers no. */
|
|
37
|
+
export declare class ConfirmScreen implements Screen<boolean> {
|
|
38
|
+
private readonly o;
|
|
39
|
+
private yes;
|
|
40
|
+
constructor(o: ConfirmOptions);
|
|
41
|
+
render(size: Size): FrameParts;
|
|
42
|
+
handle(key: Key): Step<boolean>;
|
|
43
|
+
}
|
|
44
|
+
/** Text to read (scrolls); any key but the arrows closes it. */
|
|
45
|
+
export declare class MessageScreen implements Screen<void> {
|
|
46
|
+
private readonly o;
|
|
47
|
+
private offset;
|
|
48
|
+
private height;
|
|
49
|
+
constructor(o: {
|
|
50
|
+
path: string[];
|
|
51
|
+
title?: string;
|
|
52
|
+
lines: string[];
|
|
53
|
+
tone?: 'ok' | 'warn' | 'error';
|
|
54
|
+
});
|
|
55
|
+
render(size: Size): FrameParts;
|
|
56
|
+
handle(key: Key): Step<void>;
|
|
57
|
+
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { Text } from './text.js';
|
|
2
|
+
import { Theme } from './theme.js';
|
|
3
|
+
/** Plain lines are wrapped to the width; coloured ones are kept as they are (the frame cuts them). */
|
|
4
|
+
const paragraphs = (lines, width) => lines.flatMap((l) => (Text.strip(l) === l ? Text.wrap(l, width - 4) : [l]).map((x) => ' ' + x));
|
|
5
|
+
/** One line of text. Resolves to the text, or undefined when you press Esc. */
|
|
6
|
+
export class TextScreen {
|
|
7
|
+
o;
|
|
8
|
+
value;
|
|
9
|
+
cursor;
|
|
10
|
+
problem;
|
|
11
|
+
constructor(o) {
|
|
12
|
+
this.o = o;
|
|
13
|
+
this.value = o.initial ?? '';
|
|
14
|
+
this.cursor = [...this.value].length;
|
|
15
|
+
}
|
|
16
|
+
render(size) {
|
|
17
|
+
const width = Math.min(size.width - 4, 100);
|
|
18
|
+
const chars = [...this.value];
|
|
19
|
+
// Scroll the field so the cursor stays visible.
|
|
20
|
+
const inner = width - 4;
|
|
21
|
+
const start = Math.max(0, this.cursor - inner + 1);
|
|
22
|
+
const shown = chars.slice(start, start + inner);
|
|
23
|
+
const before = shown.slice(0, this.cursor - start).join('');
|
|
24
|
+
const at = shown[this.cursor - start] ?? ' ';
|
|
25
|
+
const after = shown.slice(this.cursor - start + 1).join('');
|
|
26
|
+
const text = this.value === '' && this.o.placeholder ? Theme.muted(this.o.placeholder) : before + (Theme.colour ? '\x1b[7m' + at + '\x1b[27m' : '_') + after;
|
|
27
|
+
const body = [
|
|
28
|
+
'',
|
|
29
|
+
' ' + Theme.bold(this.o.label),
|
|
30
|
+
...(this.o.about?.length ? paragraphs(this.o.about, size.width).map((l) => Theme.muted(l)) : []),
|
|
31
|
+
'',
|
|
32
|
+
' ' + Theme.accent('╭' + '─'.repeat(width - 2) + '╮'),
|
|
33
|
+
' ' + Theme.accent('│') + ' ' + Text.fit(text, inner) + ' ' + Theme.accent('│'),
|
|
34
|
+
' ' + Theme.accent('╰' + '─'.repeat(width - 2) + '╯'),
|
|
35
|
+
this.problem ? ' ' + Theme.error(Theme.glyph.cross + ' ' + this.problem) : '',
|
|
36
|
+
];
|
|
37
|
+
return { path: this.o.path, body, keys: [['↵', 'save'], ['esc', 'cancel'], ['ctrl+u', 'clear'], ['←→', 'move']] };
|
|
38
|
+
}
|
|
39
|
+
handle(key) {
|
|
40
|
+
const chars = [...this.value];
|
|
41
|
+
const set = (next, cursor) => { this.value = next.join(''); this.cursor = Math.max(0, Math.min(next.length, cursor)); this.check(); };
|
|
42
|
+
if (key.name === 'escape')
|
|
43
|
+
return { done: undefined };
|
|
44
|
+
if (key.name === 'return') {
|
|
45
|
+
if (this.value === '' && !this.o.allowEmpty)
|
|
46
|
+
return { done: undefined };
|
|
47
|
+
const verdict = this.o.validate?.(this.value) ?? true;
|
|
48
|
+
if (verdict !== true) {
|
|
49
|
+
this.problem = verdict;
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
return { done: this.value };
|
|
53
|
+
}
|
|
54
|
+
if (key.name === 'left') {
|
|
55
|
+
this.cursor = Math.max(0, this.cursor - 1);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
if (key.name === 'right') {
|
|
59
|
+
this.cursor = Math.min(chars.length, this.cursor + 1);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
if (key.name === 'home') {
|
|
63
|
+
this.cursor = 0;
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
if (key.name === 'end') {
|
|
67
|
+
this.cursor = chars.length;
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
if (key.name === 'backspace') {
|
|
71
|
+
if (this.cursor > 0)
|
|
72
|
+
set([...chars.slice(0, this.cursor - 1), ...chars.slice(this.cursor)], this.cursor - 1);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
if (key.name === 'delete') {
|
|
76
|
+
set([...chars.slice(0, this.cursor), ...chars.slice(this.cursor + 1)], this.cursor);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
if (key.ctrl && key.name === 'u') {
|
|
80
|
+
set([], 0);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
if (key.text) {
|
|
84
|
+
const add = [...key.text.replace(/[\r\n]/g, '')];
|
|
85
|
+
set([...chars.slice(0, this.cursor), ...add, ...chars.slice(this.cursor)], this.cursor + add.length);
|
|
86
|
+
}
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
check() {
|
|
90
|
+
const verdict = this.value === '' ? true : this.o.validate?.(this.value) ?? true;
|
|
91
|
+
this.problem = verdict === true ? undefined : verdict;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/** Yes or no. Esc answers no. */
|
|
95
|
+
export class ConfirmScreen {
|
|
96
|
+
o;
|
|
97
|
+
yes;
|
|
98
|
+
constructor(o) {
|
|
99
|
+
this.o = o;
|
|
100
|
+
this.yes = o.initial ?? true;
|
|
101
|
+
}
|
|
102
|
+
render(size) {
|
|
103
|
+
const button = (label, on, danger = false) => (on ? (danger ? Theme.colour ? '\x1b[48;5;160m\x1b[97m ' + label + ' \x1b[0m' : '[' + label + ']' : Theme.selected(' ' + label + ' ')) : Theme.muted(' ' + label + ' '));
|
|
104
|
+
const body = [
|
|
105
|
+
'',
|
|
106
|
+
' ' + Theme.bold(this.o.question),
|
|
107
|
+
...(this.o.about?.length ? ['', ...paragraphs(this.o.about, size.width)] : []),
|
|
108
|
+
'',
|
|
109
|
+
' ' + button(this.o.yes ?? 'Yes', this.yes, this.o.danger) + ' ' + button(this.o.no ?? 'No', !this.yes),
|
|
110
|
+
];
|
|
111
|
+
return { path: this.o.path, body, keys: [['←→', 'choose'], ['↵', 'confirm'], ['y/n', 'answer'], ['esc', 'no']] };
|
|
112
|
+
}
|
|
113
|
+
handle(key) {
|
|
114
|
+
if (key.name === 'left' || key.name === 'right' || key.name === 'tab') {
|
|
115
|
+
this.yes = !this.yes;
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
if (key.text?.toLowerCase() === 'y')
|
|
119
|
+
return { done: true };
|
|
120
|
+
if (key.text?.toLowerCase() === 'n' || key.name === 'escape')
|
|
121
|
+
return { done: false };
|
|
122
|
+
if (key.name === 'return')
|
|
123
|
+
return { done: this.yes };
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
/** Text to read (scrolls); any key but the arrows closes it. */
|
|
128
|
+
export class MessageScreen {
|
|
129
|
+
o;
|
|
130
|
+
offset = 0;
|
|
131
|
+
height = 10;
|
|
132
|
+
constructor(o) {
|
|
133
|
+
this.o = o;
|
|
134
|
+
}
|
|
135
|
+
render(size) {
|
|
136
|
+
const paint = this.o.tone === 'ok' ? Theme.ok : this.o.tone === 'warn' ? Theme.warn : this.o.tone === 'error' ? Theme.error : Theme.bold;
|
|
137
|
+
const head = this.o.title ? ['', ' ' + paint(this.o.title), ''] : [''];
|
|
138
|
+
const lines = this.o.lines.flatMap((l) => (Text.width(l) > size.width - 4 && Text.strip(l) === l ? Text.wrap(l, size.width - 4) : [l])).map((l) => ' ' + l);
|
|
139
|
+
this.height = size.height - head.length;
|
|
140
|
+
this.offset = Math.max(0, Math.min(this.offset, lines.length - this.height));
|
|
141
|
+
return { path: this.o.path, body: [...head, ...lines.slice(this.offset, this.offset + this.height)], keys: lines.length > this.height ? [['↑↓', 'scroll'], ['↵', 'close']] : [['any key', 'close']] };
|
|
142
|
+
}
|
|
143
|
+
handle(key) {
|
|
144
|
+
if (key.name === 'up') {
|
|
145
|
+
this.offset--;
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
if (key.name === 'down') {
|
|
149
|
+
this.offset++;
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
if (key.name === 'pageup') {
|
|
153
|
+
this.offset -= this.height;
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
if (key.name === 'pagedown') {
|
|
157
|
+
this.offset += this.height;
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
return { done: undefined };
|
|
161
|
+
}
|
|
162
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface FrameParts {
|
|
2
|
+
/** Where you are, e.g. ['Settings', 'Languages and tools']. */
|
|
3
|
+
path: string[];
|
|
4
|
+
/** Short line under the title (what is running, which folder). */
|
|
5
|
+
status?: string;
|
|
6
|
+
body: string[];
|
|
7
|
+
/** [key, what it does] pairs, e.g. [['↵', 'open'], ['esc', 'back']]. */
|
|
8
|
+
keys: [string, string][];
|
|
9
|
+
/** A message for the last action (saved, invalid ...), shown above the footer. */
|
|
10
|
+
notice?: string;
|
|
11
|
+
}
|
|
12
|
+
export declare class Frame {
|
|
13
|
+
static title: string;
|
|
14
|
+
static version: string;
|
|
15
|
+
/** Rows the body may use on a terminal `height` rows high (title, status, two rules, notice and keys take 6). */
|
|
16
|
+
static bodyHeight(height: number): number;
|
|
17
|
+
static render(parts: FrameParts, width: number, height: number): string[];
|
|
18
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// The frame every screen is drawn in: a title bar with where you are, a status line, the screen's own body and a footer
|
|
2
|
+
// with the keys that work here.
|
|
3
|
+
import { Text } from './text.js';
|
|
4
|
+
import { Theme } from './theme.js';
|
|
5
|
+
export class Frame {
|
|
6
|
+
static title = 'Windows Development Sandbox';
|
|
7
|
+
static version = '';
|
|
8
|
+
/** Rows the body may use on a terminal `height` rows high (title, status, two rules, notice and keys take 6). */
|
|
9
|
+
static bodyHeight(height) {
|
|
10
|
+
return Math.max(3, height - 6);
|
|
11
|
+
}
|
|
12
|
+
static render(parts, width, height) {
|
|
13
|
+
const w = width - 1;
|
|
14
|
+
const crumbs = [Frame.title, ...parts.path].map((p, i, all) => (i === all.length - 1 ? Theme.bold(p) : p)).join(Theme.dim(' › '));
|
|
15
|
+
const right = Frame.version ? 'v' + Frame.version + ' ' : '';
|
|
16
|
+
const title = Theme.bar(Text.fit(' ' + Theme.accent('◆') + ' ' + crumbs, w - Text.width(right)) + right);
|
|
17
|
+
const status = ' ' + Text.truncate(parts.status ?? '', w - 2);
|
|
18
|
+
const rule = Theme.dim(Theme.glyph.line.repeat(w));
|
|
19
|
+
const bodyRows = Frame.bodyHeight(height);
|
|
20
|
+
const body = parts.body.slice(0, bodyRows);
|
|
21
|
+
while (body.length < bodyRows)
|
|
22
|
+
body.push('');
|
|
23
|
+
const notice = parts.notice ? ' ' + Text.truncate(parts.notice, w - 2) : '';
|
|
24
|
+
const keys = ' ' + parts.keys.map(([k, what]) => Theme.key(k) + ' ' + Theme.muted(what)).join(' ');
|
|
25
|
+
return [title, status, rule, ...body, notice, rule, Text.truncate(keys, w)];
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { App, type Screen, type Size, type Step } from './app.js';
|
|
2
|
+
export { ConfirmScreen, MessageScreen, TextScreen } from './dialogs.js';
|
|
3
|
+
export { Frame } from './frame.js';
|
|
4
|
+
export { ListScreen, type Category, type ListItem, type ListOptions, type ListResult, type Tab } from './list-screen.js';
|
|
5
|
+
export { ExitRequested, Terminal, type Key } from './terminal.js';
|
|
6
|
+
export { Text } from './text.js';
|
|
7
|
+
export { Theme } from './theme.js';
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// The menu's screen toolkit: a full-screen app (App) showing one screen at a time in a common frame.
|
|
2
|
+
export { App } from './app.js';
|
|
3
|
+
export { ConfirmScreen, MessageScreen, TextScreen } from './dialogs.js';
|
|
4
|
+
export { Frame } from './frame.js';
|
|
5
|
+
export { ListScreen } from './list-screen.js';
|
|
6
|
+
export { ExitRequested, Terminal } from './terminal.js';
|
|
7
|
+
export { Text } from './text.js';
|
|
8
|
+
export { Theme } from './theme.js';
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import type { FrameParts } from './frame.js';
|
|
2
|
+
import type { Screen, Size, Step } from './app.js';
|
|
3
|
+
import type { Key } from './terminal.js';
|
|
4
|
+
export interface ListItem<T> {
|
|
5
|
+
value: T;
|
|
6
|
+
label: string;
|
|
7
|
+
/** Right-hand column: a setting's value, a size, a state. */
|
|
8
|
+
hint?: string;
|
|
9
|
+
description?: string;
|
|
10
|
+
/** Extra lines for the details pane (already formatted). */
|
|
11
|
+
details?: string[];
|
|
12
|
+
/** Heading the entry is listed under (entries with the same section should be next to each other). */
|
|
13
|
+
section?: string;
|
|
14
|
+
/** Sidebar category id. */
|
|
15
|
+
category?: string;
|
|
16
|
+
/** More words search should find it by. */
|
|
17
|
+
keywords?: string;
|
|
18
|
+
/** Why it cannot be picked (shown dim). */
|
|
19
|
+
disabled?: string;
|
|
20
|
+
/** Multi-select: ticked. */
|
|
21
|
+
checked?: boolean;
|
|
22
|
+
/** A glyph before the label. */
|
|
23
|
+
icon?: string;
|
|
24
|
+
}
|
|
25
|
+
export interface Category {
|
|
26
|
+
id: string;
|
|
27
|
+
label: string;
|
|
28
|
+
description?: string;
|
|
29
|
+
}
|
|
30
|
+
export interface ListAction {
|
|
31
|
+
key: string;
|
|
32
|
+
label: string;
|
|
33
|
+
}
|
|
34
|
+
/** Tabs above the list (e.g. which settings file): Tab switches, the screen reopens on the other one. */
|
|
35
|
+
export interface Tab {
|
|
36
|
+
label: string;
|
|
37
|
+
badge: string;
|
|
38
|
+
paint: (text: string) => string;
|
|
39
|
+
}
|
|
40
|
+
export type ListResult<T> = {
|
|
41
|
+
kind: 'pick';
|
|
42
|
+
value: T;
|
|
43
|
+
} | {
|
|
44
|
+
kind: 'back';
|
|
45
|
+
} | {
|
|
46
|
+
kind: 'action';
|
|
47
|
+
key: string;
|
|
48
|
+
value: T | undefined;
|
|
49
|
+
} | {
|
|
50
|
+
kind: 'multi';
|
|
51
|
+
values: T[];
|
|
52
|
+
} | {
|
|
53
|
+
kind: 'tab';
|
|
54
|
+
index: number;
|
|
55
|
+
};
|
|
56
|
+
export interface ListOptions<T> {
|
|
57
|
+
path: string[];
|
|
58
|
+
status?: string;
|
|
59
|
+
/** A line or two above the list. */
|
|
60
|
+
intro?: string | string[];
|
|
61
|
+
items: ListItem<T>[];
|
|
62
|
+
categories?: Category[];
|
|
63
|
+
multi?: boolean;
|
|
64
|
+
/** Start on this entry (and its category). */
|
|
65
|
+
startAt?: T;
|
|
66
|
+
startCategory?: string;
|
|
67
|
+
actions?: ListAction[];
|
|
68
|
+
notice?: string;
|
|
69
|
+
empty?: string;
|
|
70
|
+
tabs?: {
|
|
71
|
+
items: Tab[];
|
|
72
|
+
active: number;
|
|
73
|
+
};
|
|
74
|
+
/** What Esc does, for the footer ("back", "quit"). */
|
|
75
|
+
escapeLabel?: string;
|
|
76
|
+
}
|
|
77
|
+
export declare class ListScreen<T> implements Screen<ListResult<T>> {
|
|
78
|
+
private readonly o;
|
|
79
|
+
private category;
|
|
80
|
+
private focus;
|
|
81
|
+
private cursor;
|
|
82
|
+
private offset;
|
|
83
|
+
private query;
|
|
84
|
+
private searching;
|
|
85
|
+
private notice;
|
|
86
|
+
private readonly checked;
|
|
87
|
+
constructor(o: ListOptions<T>);
|
|
88
|
+
/** Indexes of the entries shown now: all matches of the search, else the current category's, else all. */
|
|
89
|
+
private visible;
|
|
90
|
+
private rows;
|
|
91
|
+
render(size: Size): FrameParts;
|
|
92
|
+
private rowLine;
|
|
93
|
+
private sideLines;
|
|
94
|
+
private detailsLines;
|
|
95
|
+
private keys;
|
|
96
|
+
/** Programmatic notice (e.g. "Saved") shown until the next key. */
|
|
97
|
+
setNotice(text: string | undefined): void;
|
|
98
|
+
handle(key: Key): Step<ListResult<T>>;
|
|
99
|
+
private toggle;
|
|
100
|
+
private switchCategory;
|
|
101
|
+
}
|