@crystalplatforms/adm 0.2.3 → 0.3.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 -21
- package/README.md +154 -161
- package/bin/adm +18 -21
- package/package.json +9 -22
- package/scripts/completion/adm.bash +12 -55
- package/scripts/completion/adm.zsh +21 -58
- package/scripts/install.js +8 -8
- package/src/commands/config.js +66 -0
- package/src/commands/dotfiles.js +49 -32
- package/src/commands/feedback.js +12 -0
- package/src/commands/help.js +33 -0
- package/src/commands/uninstall.js +73 -54
- package/src/commands/version.js +7 -0
- package/src/commands.js +26 -0
- package/src/config/index.js +32 -32
- package/src/data/extensions.json +358 -358
- package/src/engines.js +38 -0
- package/src/install/executor.js +263 -0
- package/src/install/hints.js +94 -0
- package/src/install/npm-prefix.js +110 -0
- package/src/install/plan.js +75 -0
- package/src/install/report.js +46 -0
- package/src/install/run.js +88 -0
- package/src/install/shell-paths.js +129 -0
- package/src/legacy-cleanup.js +96 -0
- package/src/pkg/builders.js +220 -0
- package/src/pkg/managers.js +59 -0
- package/src/pkg/package-names.js +229 -0
- package/src/pkg/script-urls.js +100 -0
- package/src/platform/index.js +129 -0
- package/src/platform/os-release.js +27 -0
- package/src/platform/shell.js +111 -0
- package/src/reports.js +37 -0
- package/src/router.js +157 -0
- package/src/setup/catalog.js +13 -13
- package/src/setup/git-step.js +128 -0
- package/src/setup/loader.js +108 -0
- package/src/setup/prompts.js +259 -0
- package/src/setup/registry.js +48 -0
- package/src/setup/spinner.js +67 -0
- package/src/setup/wizard.js +319 -195
- package/src/upgrade/index.js +499 -0
- package/src/utils/closest-match.js +36 -0
- package/src/utils/dotfiles-sync.js +89 -89
- package/src/utils/file-conflict-handler.js +30 -30
- package/bin/adm-tui +0 -13
- package/src/ai/knowledge.js +0 -61
- package/src/commands/connect.js +0 -76
- package/src/commands/issue.js +0 -80
- package/src/commands/pr.js +0 -55
- package/src/commands/theme.js +0 -103
- package/src/commands/upgrade.js +0 -114
- package/src/installer/git-config.js +0 -34
- package/src/installer/node-installer.js +0 -50
- package/src/installer/package-manager-installer.js +0 -31
- package/src/installer/ssh-setup.js +0 -31
- package/src/installer/system-packages.js +0 -49
- package/src/installers/index.js +0 -66
- package/src/integrations/ai-backend.js +0 -60
- package/src/integrations/ai-providers/registry.js +0 -261
- package/src/integrations/github.js +0 -107
- package/src/integrations/gitlab.js +0 -121
- package/src/plugins/context.js +0 -64
- package/src/plugins/loader.js +0 -50
- package/src/plugins/runner.js +0 -25
- package/src/setup/installer.js +0 -192
- package/src/shell/detector.js +0 -10
- package/src/tui/app-state.js +0 -374
- package/src/tui/app.js +0 -442
- package/src/tui/autocomplete.js +0 -57
- package/src/tui/commands/registry.js +0 -823
- package/src/tui/components/SetupScreen.js +0 -292
- package/src/ui/animations.js +0 -24
- package/src/ui/spinner.js +0 -34
- package/src/ui/theme/cyberpunk.js +0 -14
- package/src/ui/theme/dark.js +0 -14
- package/src/ui/theme/forest.js +0 -14
- package/src/ui/theme/light.js +0 -14
- package/src/ui/theme/monokai.js +0 -14
- package/src/ui/theme/nord.js +0 -14
- package/src/ui/theme.js +0 -49
- package/src/utils/command-history.js +0 -43
- package/src/utils/error-handler.js +0 -69
- package/src/utils/fuzzy-search.js +0 -20
- package/src/utils/github-error-handler.js +0 -26
- package/src/utils/keychain.js +0 -97
- package/src/utils/open-url.js +0 -15
- package/src/utils/terminal-detection.js +0 -26
- package/src/utils/token-encryption.js +0 -96
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
// Per-distro package-name mapping: tool id → package name per manager.
|
|
2
|
+
//
|
|
3
|
+
// PACKAGE_NAMES[id][manager] = 'package-name' explicit mapping
|
|
4
|
+
// PACKAGE_NAMES[id][manager] = { manual: 'reason'} not packaged for that manager
|
|
5
|
+
//
|
|
6
|
+
// Managers with no entry for a tool default to the tool id itself
|
|
7
|
+
// (most package names equal the tool id). resolvePackageName therefore ALWAYS
|
|
8
|
+
// returns { name } or { manual } — never undefined (completeness contract).
|
|
9
|
+
//
|
|
10
|
+
// Only deviations from the tool id are listed here; the identity default keeps
|
|
11
|
+
// the table maintainable. When a tool is genuinely absent from a manager's
|
|
12
|
+
// official repos, an explicit { manual } entry explains how to get it.
|
|
13
|
+
//
|
|
14
|
+
// ── winget ids (HITL finding, 0.3.0) ────────────────────────────────────
|
|
15
|
+
// winget requires Publisher.Package ids (Git.Git, not "git") — the identity
|
|
16
|
+
// default is essentially ALWAYS wrong on winget, so every winget-route tool
|
|
17
|
+
// carries an explicit entry below. All ids verified against the live winget
|
|
18
|
+
// catalog (winget show --id <id> --exact) on winget 1.29.
|
|
19
|
+
const PACKAGE_NAMES = {
|
|
20
|
+
// ── language runtimes ─────────────────────────────────────────
|
|
21
|
+
python3: { brew: 'python', pacman: 'python', winget: 'Python.Python.3.13' },
|
|
22
|
+
pyenv: {
|
|
23
|
+
apt: { manual: 'pyenv is not in Debian/Ubuntu repos — install via script: curl -fsSL https://pyenv.run | sh' },
|
|
24
|
+
dnf: { manual: 'pyenv is not in Fedora repos — install via script: curl -fsSL https://pyenv.run | sh' },
|
|
25
|
+
pacman: { manual: 'pyenv lives in the AUR — install manually: https://aur.archlinux.org/packages/pyenv' },
|
|
26
|
+
zypper: { manual: 'pyenv is not in openSUSE repos — install via script: curl -fsSL https://pyenv.run | sh' },
|
|
27
|
+
apk: { manual: 'pyenv is not in Alpine repos — install via script: curl -fsSL https://pyenv.run | sh' },
|
|
28
|
+
winget: 'pyenv-win.pyenv-win',
|
|
29
|
+
},
|
|
30
|
+
go: { apt: 'golang-go', winget: 'GoLang.Go' },
|
|
31
|
+
jdk: {
|
|
32
|
+
brew: 'openjdk',
|
|
33
|
+
apt: 'default-jdk',
|
|
34
|
+
dnf: 'java-21-openjdk-devel',
|
|
35
|
+
pacman: 'jdk-openjdk',
|
|
36
|
+
zypper: 'java-21-openjdk-devel',
|
|
37
|
+
apk: 'openjdk21-jdk',
|
|
38
|
+
winget: 'EclipseAdoptium.Temurin.21.JDK',
|
|
39
|
+
},
|
|
40
|
+
dotnet: {
|
|
41
|
+
brew: 'dotnet',
|
|
42
|
+
apt: 'dotnet-sdk-8.0',
|
|
43
|
+
dnf: 'dotnet-sdk',
|
|
44
|
+
pacman: 'dotnet-sdk',
|
|
45
|
+
zypper: 'dotnet-sdk',
|
|
46
|
+
apk: 'dotnet8-sdk',
|
|
47
|
+
winget: 'Microsoft.DotNet.SDK.8',
|
|
48
|
+
choco: 'dotnet-sdk',
|
|
49
|
+
scoop: 'dotnet-sdk',
|
|
50
|
+
},
|
|
51
|
+
scala: {
|
|
52
|
+
dnf: { manual: 'scala is not in official Fedora repos — use Coursier or SDKMAN: https://get-coursier.io' },
|
|
53
|
+
apk: { manual: 'scala is not in Alpine repos — use Coursier: https://get-coursier.io' },
|
|
54
|
+
winget: 'VirtusLab.ScalaCLI',
|
|
55
|
+
},
|
|
56
|
+
kotlin: { dnf: { manual: 'kotlin is not in official Fedora repos — install via sdkman: https://sdkman.io' }, apk: { manual: 'kotlin is not in Alpine repos — install manually: https://kotlinlang.org/docs/getting-started.html' }, winget: 'Kotlin.Kotlin' },
|
|
57
|
+
clojure: {
|
|
58
|
+
apt: { manual: 'install from the Clojure apt repo: https://clojure.org/guides/install_clojure' },
|
|
59
|
+
dnf: { manual: 'install from the Clojure rpm repo: https://clojure.org/guides/install_clojure' },
|
|
60
|
+
zypper: { manual: 'install from the Clojure rpm repo: https://clojure.org/guides/install_clojure' },
|
|
61
|
+
apk: { manual: 'clojure is not in Alpine repos — install via script: https://clojure.org/guides/install_clojure' },
|
|
62
|
+
winget: 'Clojure.ClojureCLI',
|
|
63
|
+
},
|
|
64
|
+
sbt: {
|
|
65
|
+
dnf: { manual: 'install from the sbt rpm repo: https://www.scala-sbt.org/download' },
|
|
66
|
+
pacman: { manual: 'sbt lives in the AUR — https://aur.archlinux.org/packages/sbt' },
|
|
67
|
+
zypper: { manual: 'install from the sbt rpm repo: https://www.scala-sbt.org/download' },
|
|
68
|
+
apk: { manual: 'sbt is not in Alpine repos — https://www.scala-sbt.org/download' },
|
|
69
|
+
winget: 'sbt.sbt',
|
|
70
|
+
},
|
|
71
|
+
graalvm: {
|
|
72
|
+
brew: { manual: 'install the cask: brew install --cask graalvm/tap/graalvm-jdk' },
|
|
73
|
+
apt: { manual: 'download from https://www.graalvm.org/downloads/' },
|
|
74
|
+
dnf: { manual: 'download from https://www.graalvm.org/downloads/' },
|
|
75
|
+
pacman: { manual: 'download from https://www.graalvm.org/downloads/' },
|
|
76
|
+
zypper: { manual: 'download from https://www.graalvm.org/downloads/' },
|
|
77
|
+
apk: { manual: 'download from https://www.graalvm.org/downloads/' },
|
|
78
|
+
winget: { manual: 'no official GraalVM winget package — download a JDK build from https://www.graalvm.org/downloads/' },
|
|
79
|
+
},
|
|
80
|
+
ruby: { winget: 'RubyInstallerTeam.RubyWithDevKit.3.4' },
|
|
81
|
+
php: { brew: 'php', apt: 'php-cli', dnf: 'php-cli', zypper: 'php8', winget: 'PHP.PHP.8.4' },
|
|
82
|
+
symfony: {
|
|
83
|
+
brew: 'symfony-cli/tap',
|
|
84
|
+
apt: 'symfony-cli',
|
|
85
|
+
dnf: 'symfony-cli',
|
|
86
|
+
pacman: { manual: 'install via the Symfony installer script: https://symfony.com/download' },
|
|
87
|
+
zypper: { manual: 'install via the Symfony installer script: https://symfony.com/download' },
|
|
88
|
+
apk: { manual: 'install via the Symfony installer script: https://symfony.com/download' },
|
|
89
|
+
choco: 'symfony-cli',
|
|
90
|
+
scoop: 'symfony-cli',
|
|
91
|
+
winget: 'Symfony-cli.Symfony-cli',
|
|
92
|
+
},
|
|
93
|
+
'wp-cli': { apt: { manual: 'install via the official phar: https://wp-cli.org/#installing' }, dnf: { manual: 'install via the official phar: https://wp-cli.org/#installing' }, pacman: { manual: 'install via the official phar: https://wp-cli.org/#installing' }, zypper: { manual: 'install via the official phar: https://wp-cli.org/#installing' }, apk: { manual: 'install via the official phar: https://wp-cli.org/#installing' }, winget: { manual: 'wp-cli is a phar — download from https://wp-cli.org/#installing' } },
|
|
94
|
+
|
|
95
|
+
// ── jvm ecosystem ─────────────────────────────────────────────
|
|
96
|
+
gradle: { winget: 'Gradle.Gradle' },
|
|
97
|
+
maven: { winget: 'Apache.Maven' },
|
|
98
|
+
ant: { winget: 'ApacheAnt.ApacheAnt' },
|
|
99
|
+
leiningen: { winget: 'Leiningen.Leiningen' },
|
|
100
|
+
jbang: { apt: { manual: 'jbang is not in Debian/Ubuntu repos — https://www.jbang.dev/download/' }, dnf: { manual: 'jbang is not in Fedora repos — https://www.jbang.dev/download/' }, pacman: { manual: 'jbang is not in official Arch repos — https://www.jbang.dev/download/' }, zypper: { manual: 'jbang is not in openSUSE repos — https://www.jbang.dev/download/' }, apk: { manual: 'jbang is not in Alpine repos — https://www.jbang.dev/download/' }, winget: 'jbangdev.jbang' },
|
|
101
|
+
'spring-boot': { apt: { manual: 'install via sdkman: https://sdkman.io' }, dnf: { manual: 'install via sdkman: https://sdkman.io' }, pacman: { manual: 'install via sdkman: https://sdkman.io' }, zypper: { manual: 'install via sdkman: https://sdkman.io' }, apk: { manual: 'install via sdkman: https://sdkman.io' }, winget: { manual: 'no official Spring Boot CLI winget package — install via sdkman: https://sdkman.io' } },
|
|
102
|
+
quarkus: { apt: { manual: 'quarkus CLI is not in Debian/Ubuntu repos — https://quarkus.io/guides/cli-tooling' }, dnf: { manual: 'quarkus CLI is not in Fedora repos — https://quarkus.io/guides/cli-tooling' }, pacman: { manual: 'quarkus CLI is not in official Arch repos — https://quarkus.io/guides/cli-tooling' }, zypper: { manual: 'quarkus CLI is not in openSUSE repos — https://quarkus.io/guides/cli-tooling' }, apk: { manual: 'quarkus CLI is not in Alpine repos — https://quarkus.io/guides/cli-tooling' }, winget: { manual: 'no official Quarkus CLI winget package — see https://quarkus.io/guides/cli-tooling' } },
|
|
103
|
+
micronaut: { apt: { manual: 'install via sdkman: https://sdkman.io' }, dnf: { manual: 'install via sdkman: https://sdkman.io' }, pacman: { manual: 'install via sdkman: https://sdkman.io' }, zypper: { manual: 'install via sdkman: https://sdkman.io' }, apk: { manual: 'install via sdkman: https://sdkman.io' }, winget: { manual: 'no official Micronaut CLI winget package — install via sdkman: https://sdkman.io' } },
|
|
104
|
+
visualvm: { apt: { manual: 'download from https://visualvm.github.io/' }, dnf: { manual: 'download from https://visualvm.github.io/' }, pacman: { manual: 'download from https://visualvm.github.io/' }, zypper: { manual: 'download from https://visualvm.github.io/' }, apk: { manual: 'download from https://visualvm.github.io/' }, winget: 'VisualVM.VisualVM' },
|
|
105
|
+
jmeter: { apt: { manual: 'download from https://jmeter.apache.org/download_jmeter.cgi' }, dnf: { manual: 'download from https://jmeter.apache.org/download_jmeter.cgi' }, pacman: { manual: 'download from https://jmeter.apache.org/download_jmeter.cgi' }, zypper: { manual: 'download from https://jmeter.apache.org/download_jmeter.cgi' }, apk: { manual: 'download from https://jmeter.apache.org/download_jmeter.cgi' }, winget: 'Apache.JMeter' },
|
|
106
|
+
jolokia: { apt: { manual: 'download from https://jolokia.org/' }, dnf: { manual: 'download from https://jolokia.org/' }, pacman: { manual: 'jolokia lives in the AUR' }, zypper: { manual: 'download from https://jolokia.org/' }, apk: { manual: 'download from https://jolokia.org/' }, winget: { manual: 'jolokia is a JVM agent, not a CLI — add it to your app: https://jolokia.org' } },
|
|
107
|
+
flyway: { apt: { manual: 'download from https://documentation.red-gate.com/fd/quickstart-maven-224919678.html or use the download bundle' }, dnf: { manual: 'download from https://www.red-gate.com/products/flyway/ (download bundle)' }, pacman: { manual: 'flyway lives in the AUR — https://aur.archlinux.org/packages/flyway' }, zypper: { manual: 'download from https://www.red-gate.com/products/flyway/ (download bundle)' }, apk: { manual: 'download from https://www.red-gate.com/products/flyway/ (download bundle)' }, winget: 'Redgate.Flyway' },
|
|
108
|
+
liquibase: { apt: { manual: 'download from https://www.liquibase.org/download' }, dnf: { manual: 'download from https://www.liquibase.org/download' }, pacman: { manual: 'download from https://www.liquibase.org/download' }, zypper: { manual: 'download from https://www.liquibase.org/download' }, apk: { manual: 'download from https://www.liquibase.org/download' }, winget: 'Liquibase.Liquibase' },
|
|
109
|
+
coursier: { apt: { manual: 'install via script: curl -fL https://github.com/coursier/coursier/raw/stable/installs/cs-x86_64-pc-linux.gz | gzip -d > cs && chmod +x cs' }, dnf: { manual: 'install via script: https://get-coursier.io/docs/cli-installation' }, pacman: { manual: 'coursier lives in the AUR' }, zypper: { manual: 'install via script: https://get-coursier.io/docs/cli-installation' }, apk: { manual: 'install via script: https://get-coursier.io/docs/cli-installation' }, winget: { manual: 'install via the coursier bootstrapper: https://get-coursier.io/docs/cli-installation' } },
|
|
110
|
+
mill: { apt: { manual: 'mill is not in Debian/Ubuntu repos — https://mill-build.org/mill/cli_installation.html' }, dnf: { manual: 'mill is not in Fedora repos — https://mill-build.org/mill/cli_installation.html' }, pacman: { manual: 'mill lives in the AUR' }, zypper: { manual: 'mill is not in openSUSE repos — https://mill-build.org/mill/cli_installation.html' }, apk: { manual: 'mill is not in Alpine repos — https://mill-build.org/mill/cli_installation.html' }, winget: { manual: 'no official mill winget package — see https://mill-build.org/mill/cli_installation.html' } },
|
|
111
|
+
ammonite: { apt: { manual: 'install via script: https://ammonite.io/#Ammonite-ops' }, dnf: { manual: 'install via script: https://ammonite.io/#Ammonite-ops' }, pacman: { manual: 'install via script: https://ammonite.io/#Ammonite-ops' }, zypper: { manual: 'install via script: https://ammonite.io/#Ammonite-ops' }, apk: { manual: 'install via script: https://ammonite.io/#Ammonite-ops' }, winget: { manual: 'install via script: https://ammonite.io/#Ammonite-ops' } },
|
|
112
|
+
ktlint: { apt: { manual: 'ktlint is not in Debian/Ubuntu repos — https://pinterest.github.io/ktlint/install/cli/' }, dnf: { manual: 'ktlint is not in Fedora repos — https://pinterest.github.io/ktlint/install/cli/' }, pacman: { manual: 'ktlint lives in the AUR' }, zypper: { manual: 'ktlint is not in openSUSE repos — https://pinterest.github.io/ktlint/install/cli/' }, apk: { manual: 'ktlint is not in Alpine repos — https://pinterest.github.io/ktlint/install/cli/' }, winget: { manual: 'no official ktlint winget package — see https://pinterest.github.io/ktlint/install/cli/' } },
|
|
113
|
+
detekt: { apt: { manual: 'detekt is not in Debian/Ubuntu repos — https://detekt.dev/docs/gettingstarted/cli/' }, dnf: { manual: 'detekt is not in Fedora repos — https://detekt.dev/docs/gettingstarted/cli/' }, pacman: { manual: 'detekt lives in the AUR' }, zypper: { manual: 'detekt is not in openSUSE repos — https://detekt.dev/docs/gettingstarted/cli/' }, apk: { manual: 'detekt is not in Alpine repos — https://detekt.dev/docs/gettingstarted/cli/' }, winget: { manual: 'no official detekt winget package — see https://detekt.dev/docs/gettingstarted/cli/' } },
|
|
114
|
+
|
|
115
|
+
// ── c/c++ ─────────────────────────────────────────────────────
|
|
116
|
+
gcc: { winget: 'BrechtSanders.WinLibs.POSIX.UCRT' },
|
|
117
|
+
clang: { winget: 'LLVM.LLVM' },
|
|
118
|
+
cmake: { winget: 'Kitware.CMake' },
|
|
119
|
+
make: { winget: 'ezwinports.make' },
|
|
120
|
+
ninja: { winget: 'Ninja-build.Ninja' },
|
|
121
|
+
'clang-format': { dnf: 'clang-tools-extra', pacman: 'clang', zypper: 'clang', apk: 'clang', winget: { manual: 'clang-format ships with the LLVM toolchain — install it via ADM (tool "clang") or: winget install LLVM.LLVM' } },
|
|
122
|
+
'clang-tidy': { dnf: 'clang-tools-extra', pacman: 'clang', zypper: 'clang', apk: 'clang', winget: { manual: 'clang-tidy ships with the LLVM toolchain — install it via ADM (tool "clang") or: winget install LLVM.LLVM' } },
|
|
123
|
+
cppcheck: { winget: 'Cppcheck.Cppcheck' },
|
|
124
|
+
gdb: { winget: { manual: 'gdb ships with the WinLibs GCC toolchain — install it via ADM (tool "gcc") or MSYS2: https://www.msys2.org' } },
|
|
125
|
+
lldb: { winget: { manual: 'lldb ships with LLVM toolchain distributions — install via ADM (tool "clang") or: scoop install llvm' } },
|
|
126
|
+
ccache: { winget: 'Ccache.Ccache' },
|
|
127
|
+
valgrind: { winget: { manual: 'valgrind is a Unix profiler — use WSL: wsl -e bash -c "sudo apt install valgrind"' } },
|
|
128
|
+
distcc: { winget: { manual: 'distcc is a Unix distributed compiler — use WSL: https://distcc.github.io' } },
|
|
129
|
+
bear: { winget: { manual: 'bear (compilation DB generator) is a Unix tool — use WSL: https://github.com/rizsotto/Bear' } },
|
|
130
|
+
doxygen: { winget: 'Doxygen.Doxygen' },
|
|
131
|
+
bazelisk: { apt: { manual: 'download the binary from https://github.com/bazelbuild/bazelisk/releases' }, dnf: { manual: 'download the binary from https://github.com/bazelbuild/bazelisk/releases' }, pacman: { manual: 'download the binary from https://github.com/bazelbuild/bazelisk/releases' }, zypper: { manual: 'download the binary from https://github.com/bazelbuild/bazelisk/releases' }, apk: { manual: 'download the binary from https://github.com/bazelbuild/bazelisk/releases' }, winget: { manual: 'no official bazelisk winget package — download from https://github.com/bazelbuild/bazelisk/releases or: choco install bazelisk' } },
|
|
132
|
+
|
|
133
|
+
// ── universal ─────────────────────────────────────────────────
|
|
134
|
+
// Windows winget ids differ from the tool ids — explicit mapping below
|
|
135
|
+
// (every id verified against the live winget catalog).
|
|
136
|
+
curl: { winget: 'cURL.cURL' },
|
|
137
|
+
jq: { winget: 'jqlang.jq' },
|
|
138
|
+
yq: { winget: 'MikeFarah.yq' },
|
|
139
|
+
git: { winget: 'Git.Git' },
|
|
140
|
+
gh: { winget: 'GitHub.cli' },
|
|
141
|
+
hub: { winget: 'GitHub.hub' },
|
|
142
|
+
'git-lfs': { winget: 'GitHub.GitLFS' },
|
|
143
|
+
gitflow: {
|
|
144
|
+
dnf: { manual: 'gitflow is not in official Fedora repos — https://github.com/petervanderdoes/gitflow-avh/wiki/Installation' },
|
|
145
|
+
zypper: { manual: 'gitflow is not in openSUSE repos — https://github.com/petervanderdoes/gitflow-avh/wiki/Installation' },
|
|
146
|
+
apk: { manual: 'gitflow is not in Alpine repos — https://github.com/petervanderdoes/gitflow-avh/wiki/Installation' },
|
|
147
|
+
winget: { manual: 'no git-flow winget package — via Chocolatey: choco install gitflow.avhEdition' },
|
|
148
|
+
choco: 'gitflow.avhEdition',
|
|
149
|
+
},
|
|
150
|
+
tig: { winget: { manual: 'tig is a Unix TUI — use WSL: wsl -e bash -c "sudo apt install tig"' } },
|
|
151
|
+
kubectl: { winget: 'Kubernetes.kubectl' },
|
|
152
|
+
k9s: { apt: { manual: 'download from https://github.com/derailed/k9s/releases' }, dnf: { manual: 'download from https://github.com/derailed/k9s/releases' }, zypper: { manual: 'download from https://github.com/derailed/k9s/releases' }, winget: 'Derailed.k9s' },
|
|
153
|
+
terraform: { winget: 'Hashicorp.Terraform' },
|
|
154
|
+
packer: { winget: 'Hashicorp.Packer' },
|
|
155
|
+
vagrant: { apt: { manual: 'install from the HashiCorp apt repo: https://developer.hashicorp.com/vagrant/install' }, dnf: { manual: 'install from the HashiCorp rpm repo: https://developer.hashicorp.com/vagrant/install' }, apk: { manual: 'vagrant is not in Alpine repos — https://developer.hashicorp.com/vagrant/install' }, winget: 'HashiCorp.Vagrant' },
|
|
156
|
+
'ansible-dev': { apt: { manual: 'ansible-dev is not in Debian/Ubuntu repos — install ansible via pip instead' }, dnf: { manual: 'ansible-dev is not in Fedora repos — install ansible via pip instead' }, pacman: { manual: 'ansible-dev is not in official Arch repos — install ansible via pip instead' }, zypper: { manual: 'ansible-dev is not in openSUSE repos — install ansible via pip instead' }, apk: { manual: 'ansible-dev is not in Alpine repos — install ansible via pip instead' }, winget: { manual: 'no ansible winget package — install via pip: pip install ansible' } },
|
|
157
|
+
neovim: { winget: 'Neovim.Neovim' },
|
|
158
|
+
zsh: { winget: { manual: 'zsh is a Unix shell — use WSL: wsl -e bash -c "sudo apt install zsh"' } },
|
|
159
|
+
'starship-brew': {
|
|
160
|
+
brew: 'starship',
|
|
161
|
+
apt: { manual: 'install via script: curl -sS https://starship.rs/install.sh | sh' },
|
|
162
|
+
dnf: { manual: 'install via script: curl -sS https://starship.rs/install.sh | sh' },
|
|
163
|
+
zypper: { manual: 'install via script: curl -sS https://starship.rs/install.sh | sh' },
|
|
164
|
+
apk: { manual: 'install via script: curl -sS https://starship.rs/install.sh | sh' },
|
|
165
|
+
winget: 'Starship.Starship',
|
|
166
|
+
},
|
|
167
|
+
htop: { winget: { manual: 'htop is a Unix TUI — use WSL: wsl -e bash -c "sudo apt install htop"' } },
|
|
168
|
+
ncdu: { winget: { manual: 'ncdu is a Unix TUI — use WSL: wsl -e bash -c "sudo apt install ncdu"' } },
|
|
169
|
+
// GnuWin32.Tree runs an INTERACTIVE setup wizard — headless winget install
|
|
170
|
+
// aborts with "You cancelled the installation" (HITL finding, 0.3.0).
|
|
171
|
+
tree: { winget: { manual: 'the winget tree package uses an interactive installer — use WSL: wsl -e bash -c "sudo apt install tree", or scoop: scoop install tree' } },
|
|
172
|
+
openssl: { winget: 'ShiningLight.OpenSSL.Light' },
|
|
173
|
+
// rsync is a Unix tool — no native Windows package exists anywhere
|
|
174
|
+
rsync: { winget: { manual: 'rsync is a Unix tool and has no native Windows build — use WSL: wsl -e rsync <args>, or try Rclone for cloud sync: winget install Rclone.Rclone' }, choco: { manual: 'rsync on Windows requires WSL/Cygwin — recommended: wsl -e rsync <args>' } },
|
|
175
|
+
// wget's winget manifest is permanently outdated (installer hash mismatch)
|
|
176
|
+
// and Windows 10+ ships curl anyway — route to manual guidance.
|
|
177
|
+
wget: { winget: { manual: 'the winget wget package is currently broken (installer hash mismatch). Windows 10+ already has curl built in: curl -O <url>. Or install wget via scoop: scoop install wget' } },
|
|
178
|
+
ffmpeg: { winget: 'Gyan.FFmpeg' },
|
|
179
|
+
tmux: { winget: { manual: 'tmux is a Unix terminal multiplexer and is not available on Windows — use WSL: wsl -e bash -c "sudo apt install tmux"' }, choco: { manual: 'tmux on Windows requires WSL/Cygwin — recommended: wsl -e bash -c "sudo apt install tmux"' } },
|
|
180
|
+
lazydocker: { winget: 'JesseDuffield.Lazydocker', apt: { manual: 'download from https://github.com/jesseduffield/lazydocker/releases' }, dnf: { manual: 'download from https://github.com/jesseduffield/lazydocker/releases' }, zypper: { manual: 'download from https://github.com/jesseduffield/lazydocker/releases' }, apk: { manual: 'download from https://github.com/jesseduffield/lazydocker/releases' } },
|
|
181
|
+
docker: {
|
|
182
|
+
apt: 'docker.io',
|
|
183
|
+
dnf: 'moby-engine',
|
|
184
|
+
winget: 'Docker.DockerDesktop',
|
|
185
|
+
choco: 'docker-desktop',
|
|
186
|
+
scoop: 'docker',
|
|
187
|
+
},
|
|
188
|
+
'docker-compose': {
|
|
189
|
+
apt: 'docker-compose-v2',
|
|
190
|
+
apk: 'docker-cli-compose',
|
|
191
|
+
winget: { manual: 'docker compose ships with Docker Desktop (winget install Docker.DockerDesktop)' },
|
|
192
|
+
},
|
|
193
|
+
watchman: {
|
|
194
|
+
apt: { manual: 'watchman is not in Debian/Ubuntu repos — build from source: https://facebook.github.io/watchman/docs/install' },
|
|
195
|
+
dnf: { manual: 'watchman is not in Fedora repos — build from source: https://facebook.github.io/watchman/docs/install' },
|
|
196
|
+
pacman: { manual: 'watchman lives in the AUR' },
|
|
197
|
+
zypper: { manual: 'watchman is not in openSUSE repos — build from source: https://facebook.github.io/watchman/docs/install' },
|
|
198
|
+
apk: { manual: 'watchman is not in Alpine repos — build from source: https://facebook.github.io/watchman/docs/install' },
|
|
199
|
+
winget: 'facebook.watchman',
|
|
200
|
+
},
|
|
201
|
+
imagemagick: { dnf: 'ImageMagick', zypper: 'ImageMagick', winget: 'ImageMagick.ImageMagick' },
|
|
202
|
+
shellcheck: { winget: { manual: 'no official shellcheck winget package — via scoop: scoop install shellcheck, or use WSL' } },
|
|
203
|
+
shfmt: { winget: { manual: 'no shfmt winget package — download from https://github.com/mvdan/sh/releases or: scoop install shfmt' } },
|
|
204
|
+
hadolint: { apt: { manual: 'download from https://github.com/hadolint/hadolint/releases' }, dnf: { manual: 'download from https://github.com/hadolint/hadolint/releases' }, pacman: { manual: 'hadolint lives in the AUR' }, zypper: { manual: 'download from https://github.com/hadolint/hadolint/releases' }, apk: { manual: 'download from https://github.com/hadolint/hadolint/releases' }, winget: 'hadolint.hadolint' },
|
|
205
|
+
tfsec: { apt: { manual: 'tfsec is not in Debian/Ubuntu repos — download from https://github.com/aquasecurity/tfsec/releases (now part of trivy)' }, dnf: { manual: 'tfsec is not in Fedora repos — download from https://github.com/aquasecurity/tfsec/releases (now part of trivy)' }, pacman: { manual: 'tfsec lives in the AUR' }, zypper: { manual: 'download from https://github.com/aquasecurity/tfsec/releases (now part of trivy)' }, winget: { manual: 'tfsec is deprecated in favor of trivy — install the ADM tool "trivy" instead' } },
|
|
206
|
+
trivy: { apt: { manual: 'install from the Aqua apt repo: https://trivy.dev/latest/getting-started/installation/' }, dnf: { manual: 'install from the Aqua rpm repo: https://trivy.dev/latest/getting-started/installation/' }, pacman: { manual: 'trivy lives in the AUR' }, zypper: { manual: 'install from the Aqua rpm repo: https://trivy.dev/latest/getting-started/installation/' }, winget: 'Aquasecurity.Trivy' },
|
|
207
|
+
sonarqube: { apt: { manual: 'sonarqube runs from the zip bundle or docker: https://docs.sonarsource.com/sonarqube/latest/setup-and-upgrade/install-the-server/' }, dnf: { manual: 'sonarqube runs from the zip bundle or docker: https://docs.sonarsource.com/sonarqube/latest/setup-and-upgrade/install-the-server/' }, pacman: { manual: 'sonarqube runs from the zip bundle or docker: https://docs.sonarsource.com/sonarqube/latest/setup-and-upgrade/install-the-server/' }, zypper: { manual: 'sonarqube runs from the zip bundle or docker: https://docs.sonarsource.com/sonarqube/latest/setup-and-upgrade/install-the-server/' }, apk: { manual: 'sonarqube runs from the zip bundle or docker: https://docs.sonarsource.com/sonarqube/latest/setup-and-upgrade/install-the-server/' }, winget: { manual: 'sonarqube runs from the zip bundle or docker: https://docs.sonarsource.com/sonarqube/latest/setup-and-upgrade/install-the-server/' } },
|
|
208
|
+
overmind: { apt: { manual: 'download from https://github.com/DarthSim/overmind/releases' }, dnf: { manual: 'download from https://github.com/DarthSim/overmind/releases' }, pacman: { manual: 'overmind lives in the AUR' }, zypper: { manual: 'download from https://github.com/DarthSim/overmind/releases' }, apk: { manual: 'download from https://github.com/DarthSim/overmind/releases' }, winget: { manual: 'no overmind winget package — download from https://github.com/DarthSim/overmind/releases' } },
|
|
209
|
+
|
|
210
|
+
// ── ai ────────────────────────────────────────────────────────
|
|
211
|
+
ollama: { apt: { manual: 'install via script: curl -fsSL https://ollama.com/install.sh | sh' }, dnf: { manual: 'install via script: curl -fsSL https://ollama.com/install.sh | sh' }, pacman: { manual: 'ollama is not in official Arch repos — install via script: curl -fsSL https://ollama.com/install.sh | sh' }, zypper: { manual: 'install via script: curl -fsSL https://ollama.com/install.sh | sh' }, apk: { manual: 'install via script: curl -fsSL https://ollama.com/install.sh | sh' }, winget: 'Ollama.Ollama' },
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
// Resolves the package name for a tool on a manager.
|
|
215
|
+
// Returns { name } or { manual } — never undefined (completeness contract).
|
|
216
|
+
function resolvePackageName(id, manager) {
|
|
217
|
+
const entry = PACKAGE_NAMES[id];
|
|
218
|
+
if (entry) {
|
|
219
|
+
const perManager = entry[manager];
|
|
220
|
+
if (typeof perManager === 'string') return { name: perManager };
|
|
221
|
+
if (perManager && typeof perManager === 'object' && perManager.manual) {
|
|
222
|
+
return { manual: perManager.manual };
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
// Default mapping: package name equals the tool id.
|
|
226
|
+
return { name: id };
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
module.exports = { PACKAGE_NAMES, resolvePackageName };
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
// Per-OS×arch script URL resolver for installMethod "script" tools.
|
|
2
|
+
// resolveScript(id, os, arch) → { url, run } | { manual }
|
|
3
|
+
// url — download/script URL (for the plan and for humans)
|
|
4
|
+
// run — the exact command that installs it (POSIX sh unless noted)
|
|
5
|
+
|
|
6
|
+
const key = (os, arch) => `${os}-${arch}`;
|
|
7
|
+
|
|
8
|
+
const LINUX_X86 = 'Linux-x86_64';
|
|
9
|
+
const LINUX_ARM = 'Linux-aarch64';
|
|
10
|
+
const MAC_X86 = 'MacOSX-x86_64';
|
|
11
|
+
const MAC_ARM = 'MacOSX-arm64';
|
|
12
|
+
|
|
13
|
+
const condaAsset = { 'linux-x64': LINUX_X86, 'linux-arm64': LINUX_ARM, 'darwin-x64': MAC_X86, 'darwin-arm64': MAC_ARM };
|
|
14
|
+
|
|
15
|
+
const SCRIPTS = {
|
|
16
|
+
conda: {
|
|
17
|
+
'linux-x64': () => ({ url: `https://repo.anaconda.com/miniconda/Miniconda3-latest-${condaAsset['linux-x64']}.sh`, run: 'curl -fsSL https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh -o /tmp/miniconda.sh && sh /tmp/miniconda.sh -b' }),
|
|
18
|
+
'linux-arm64': () => ({ url: `https://repo.anaconda.com/miniconda/Miniconda3-latest-${condaAsset['linux-arm64']}.sh`, run: 'curl -fsSL https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-aarch64.sh -o /tmp/miniconda.sh && sh /tmp/miniconda.sh -b' }),
|
|
19
|
+
'darwin-x64': () => ({ url: `https://repo.anaconda.com/miniconda/Miniconda3-latest-${condaAsset['darwin-x64']}.sh`, run: 'curl -fsSL https://repo.anaconda.com/miniconda/Miniconda3-latest-MacOSX-x86_64.sh -o /tmp/miniconda.sh && sh /tmp/miniconda.sh -b' }),
|
|
20
|
+
'darwin-arm64': () => ({ url: `https://repo.anaconda.com/miniconda/Miniconda3-latest-${condaAsset['darwin-arm64']}.sh`, run: 'curl -fsSL https://repo.anaconda.com/miniconda/Miniconda3-latest-MacOSX-arm64.sh -o /tmp/miniconda.sh && sh /tmp/miniconda.sh -b' }),
|
|
21
|
+
'win32-x64': () => ({ url: 'https://repo.anaconda.com/miniconda/Miniconda3-latest-Windows-x86_64.exe', run: 'Start-Process -Wait $env:TEMP\\Miniconda3-latest-Windows-x86_64.exe /S', shell: 'powershell' }),
|
|
22
|
+
},
|
|
23
|
+
bun: {
|
|
24
|
+
'linux-x64': () => ({ url: 'https://bun.sh/install', run: 'curl -fsSL https://bun.sh/install | sh' }),
|
|
25
|
+
'linux-arm64': () => ({ url: 'https://bun.sh/install', run: 'curl -fsSL https://bun.sh/install | sh' }),
|
|
26
|
+
'darwin-x64': () => ({ url: 'https://bun.sh/install', run: 'curl -fsSL https://bun.sh/install | sh' }),
|
|
27
|
+
'darwin-arm64': () => ({ url: 'https://bun.sh/install', run: 'curl -fsSL https://bun.sh/install | sh' }),
|
|
28
|
+
'win32-x64': () => ({ url: 'https://bun.sh/install.ps1', run: 'powershell -c "irm bun.sh/install.ps1 | iex"', shell: 'powershell' }),
|
|
29
|
+
'win32-arm64': () => ({ url: 'https://bun.sh/install.ps1', run: 'powershell -c "irm bun.sh/install.ps1 | iex"', shell: 'powershell' }),
|
|
30
|
+
},
|
|
31
|
+
gcloud: {
|
|
32
|
+
'linux-x64': () => ({ url: 'https://sdk.cloud.google.com', run: 'curl -fsSL https://sdk.cloud.google.com | sh' }),
|
|
33
|
+
'linux-arm64': () => ({ url: 'https://sdk.cloud.google.com', run: 'curl -fsSL https://sdk.cloud.google.com | sh' }),
|
|
34
|
+
'darwin-x64': () => ({ url: 'https://sdk.cloud.google.com', run: 'curl -fsSL https://sdk.cloud.google.com | sh' }),
|
|
35
|
+
'darwin-arm64': () => ({ url: 'https://sdk.cloud.google.com', run: 'curl -fsSL https://sdk.cloud.google.com | sh' }),
|
|
36
|
+
'win32-x64': () => ({ manual: 'install gcloud CLI for Windows: https://cloud.google.com/sdk/docs/install-sdk-for-windows' }),
|
|
37
|
+
'win32-arm64': () => ({ manual: 'install gcloud CLI for Windows: https://cloud.google.com/sdk/docs/install-sdk-for-windows' }),
|
|
38
|
+
},
|
|
39
|
+
'azure-cli': {
|
|
40
|
+
'linux-x64': () => ({ url: 'https://aka.ms/InstallAzureCli', run: 'curl -fsSL https://aka.ms/InstallAzureCli | sh' }),
|
|
41
|
+
'linux-arm64': () => ({ url: 'https://aka.ms/InstallAzureCli', run: 'curl -fsSL https://aka.ms/InstallAzureCli | sh' }),
|
|
42
|
+
'darwin-x64': () => ({ url: 'https://aka.ms/InstallAzureCli', run: 'curl -fsSL https://aka.ms/InstallAzureCli | sh' }),
|
|
43
|
+
'darwin-arm64': () => ({ url: 'https://aka.ms/InstallAzureCli', run: 'curl -fsSL https://aka.ms/InstallAzureCli | sh' }),
|
|
44
|
+
'win32-x64': () => ({ manual: 'on Windows install azure-cli via winget: winget install Microsoft.AzureCLI' }),
|
|
45
|
+
'win32-arm64': () => ({ manual: 'on Windows install azure-cli via winget: winget install Microsoft.AzureCLI' }),
|
|
46
|
+
},
|
|
47
|
+
rustup: {
|
|
48
|
+
'linux-x64': () => ({ url: 'https://sh.rustup.rs', run: 'curl -fsSL https://sh.rustup.rs | sh -s -- -y' }),
|
|
49
|
+
'linux-arm64': () => ({ url: 'https://sh.rustup.rs', run: 'curl -fsSL https://sh.rustup.rs | sh -s -- -y' }),
|
|
50
|
+
'darwin-x64': () => ({ url: 'https://sh.rustup.rs', run: 'curl -fsSL https://sh.rustup.rs | sh -s -- -y' }),
|
|
51
|
+
'darwin-arm64': () => ({ url: 'https://sh.rustup.rs', run: 'curl -fsSL https://sh.rustup.rs | sh -s -- -y' }),
|
|
52
|
+
'win32-x64': () => ({ url: 'https://win.rustup.rs/x86_64', run: 'iwr -useb https://win.rustup.rs/x86_64 -OutFile rustup-init.exe; ./rustup-init.exe -y', shell: 'powershell' }),
|
|
53
|
+
'win32-arm64': () => ({ url: 'https://win.rustup.rs/aarch64', run: 'iwr -useb https://win.rustup.rs/aarch64 -OutFile rustup-init.exe; ./rustup-init.exe -y', shell: 'powershell' }),
|
|
54
|
+
},
|
|
55
|
+
'oh-my-zsh': {
|
|
56
|
+
universal: () => ({ url: 'https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh', run: 'sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"' }),
|
|
57
|
+
},
|
|
58
|
+
helm: {
|
|
59
|
+
'linux-x64': () => ({ url: 'https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3', run: 'curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | sh' }),
|
|
60
|
+
'linux-arm64': () => ({ url: 'https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3', run: 'curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | sh' }),
|
|
61
|
+
'darwin-x64': () => ({ url: 'https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3', run: 'curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | sh' }),
|
|
62
|
+
'darwin-arm64': () => ({ url: 'https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3', run: 'curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | sh' }),
|
|
63
|
+
'win32-x64': () => ({ manual: 'on Windows install helm via winget: winget install Helm.Helm' }),
|
|
64
|
+
'win32-arm64': () => ({ manual: 'on Windows install helm via winget: winget install Helm.Helm' }),
|
|
65
|
+
},
|
|
66
|
+
composer: {
|
|
67
|
+
'linux-x64': () => ({ url: 'https://getcomposer.org/installer', run: 'curl -fsSL https://getcomposer.org/installer -o /tmp/composer-setup.php && php /tmp/composer-setup.php --install-dir=$HOME/.local/bin --filename=composer' }),
|
|
68
|
+
'linux-arm64': () => ({ url: 'https://getcomposer.org/installer', run: 'curl -fsSL https://getcomposer.org/installer -o /tmp/composer-setup.php && php /tmp/composer-setup.php --install-dir=$HOME/.local/bin --filename=composer' }),
|
|
69
|
+
'darwin-x64': () => ({ url: 'https://getcomposer.org/installer', run: 'curl -fsSL https://getcomposer.org/installer -o /tmp/composer-setup.php && php /tmp/composer-setup.php --install-dir=$HOME/.local/bin --filename=composer' }),
|
|
70
|
+
'darwin-arm64': () => ({ url: 'https://getcomposer.org/installer', run: 'curl -fsSL https://getcomposer.org/installer -o /tmp/composer-setup.php && php /tmp/composer-setup.php --install-dir=$HOME/.local/bin --filename=composer' }),
|
|
71
|
+
'win32-x64': () => ({ manual: 'install composer via the installer: https://getcomposer.org/doc/00-intro.md#installation-windows' }),
|
|
72
|
+
'win32-arm64': () => ({ manual: 'install composer via the installer: https://getcomposer.org/doc/00-intro.md#installation-windows' }),
|
|
73
|
+
},
|
|
74
|
+
vcpkg: {
|
|
75
|
+
universal: () => ({ url: 'https://github.com/microsoft/vcpkg', run: 'git clone https://github.com/microsoft/vcpkg $HOME/.adm/vcpkg && $HOME/.adm/vcpkg/bootstrap-vcpkg.sh' }),
|
|
76
|
+
'win32-x64': () => ({ url: 'https://github.com/microsoft/vcpkg', run: 'git clone https://github.com/microsoft/vcpkg $env:USERPROFILE\\.adm\\vcpkg; & $env:USERPROFILE\\.adm\\vcpkg\\bootstrap-vcpkg.bat', shell: 'powershell' }),
|
|
77
|
+
},
|
|
78
|
+
bazel: {
|
|
79
|
+
'linux-x64': () => ({ url: 'https://github.com/bazelbuild/bazelisk/releases/latest/download/bazelisk-linux-amd64', run: 'mkdir -p $HOME/.local/bin && curl -fsSL https://github.com/bazelbuild/bazelisk/releases/latest/download/bazelisk-linux-amd64 -o $HOME/.local/bin/bazel && chmod +x $HOME/.local/bin/bazel' }),
|
|
80
|
+
'linux-arm64': () => ({ url: 'https://github.com/bazelbuild/bazelisk/releases/latest/download/bazelisk-linux-arm64', run: 'mkdir -p $HOME/.local/bin && curl -fsSL https://github.com/bazelbuild/bazelisk/releases/latest/download/bazelisk-linux-arm64 -o $HOME/.local/bin/bazel && chmod +x $HOME/.local/bin/bazel' }),
|
|
81
|
+
'darwin-x64': () => ({ url: 'https://github.com/bazelbuild/bazelisk/releases/latest/download/bazelisk-darwin-amd64', run: 'mkdir -p $HOME/.local/bin && curl -fsSL https://github.com/bazelbuild/bazelisk/releases/latest/download/bazelisk-darwin-amd64 -o $HOME/.local/bin/bazel && chmod +x $HOME/.local/bin/bazel' }),
|
|
82
|
+
'darwin-arm64': () => ({ url: 'https://github.com/bazelbuild/bazelisk/releases/latest/download/bazelisk-darwin-arm64', run: 'mkdir -p $HOME/.local/bin && curl -fsSL https://github.com/bazelbuild/bazelisk/releases/latest/download/bazelisk-darwin-arm64 -o $HOME/.local/bin/bazel && chmod +x $HOME/.local/bin/bazel' }),
|
|
83
|
+
'win32-x64': () => ({ manual: 'install bazelisk via choco: choco install bazelisk' }),
|
|
84
|
+
'win32-arm64': () => ({ manual: 'install bazelisk via choco: choco install bazelisk' }),
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
// resolveScript(id, os, arch) → { url, run, shell? } | { manual }
|
|
89
|
+
// Falls back to a "universal" entry (platform-independent script) when the
|
|
90
|
+
// exact os-arch pair has none.
|
|
91
|
+
function resolveScript(id, os, arch) {
|
|
92
|
+
const table = SCRIPTS[id];
|
|
93
|
+
if (!table) return { manual: `no script installer known for "${id}" — install manually: https://github.com/CrystalPlatforms/ADM-CLI#manual-tools` };
|
|
94
|
+
const exact = table[key(os, arch)];
|
|
95
|
+
if (exact) return exact();
|
|
96
|
+
if (table.universal) return table.universal();
|
|
97
|
+
return { manual: `"${id}" has no install script for ${key(os, arch)} — install manually` };
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
module.exports = { resolveScript, SCRIPTS };
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const os = require('os');
|
|
4
|
+
const { parseOsRelease } = require('./os-release');
|
|
5
|
+
const { detectShell, rcFileFor } = require('./shell');
|
|
6
|
+
|
|
7
|
+
// Maps a distro id (or an id from ID_LIKE) to its default package manager.
|
|
8
|
+
const DISTRO_MANAGERS = {
|
|
9
|
+
ubuntu: 'apt',
|
|
10
|
+
debian: 'apt',
|
|
11
|
+
linuxmint: 'apt',
|
|
12
|
+
pop: 'apt',
|
|
13
|
+
neon: 'apt',
|
|
14
|
+
kali: 'apt',
|
|
15
|
+
raspbian: 'apt',
|
|
16
|
+
fedora: 'dnf',
|
|
17
|
+
rhel: 'dnf',
|
|
18
|
+
centos: 'dnf',
|
|
19
|
+
rocky: 'dnf',
|
|
20
|
+
almalinux: 'dnf',
|
|
21
|
+
amzn: 'dnf',
|
|
22
|
+
ol: 'dnf',
|
|
23
|
+
arch: 'pacman',
|
|
24
|
+
manjaro: 'pacman',
|
|
25
|
+
endeavouros: 'pacman',
|
|
26
|
+
garuda: 'pacman',
|
|
27
|
+
opensuse: 'zypper',
|
|
28
|
+
'opensuse-leap': 'zypper',
|
|
29
|
+
'opensuse-tumbleweed': 'zypper',
|
|
30
|
+
suse: 'zypper',
|
|
31
|
+
sles: 'zypper',
|
|
32
|
+
alpine: 'apk',
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
// Normalizes process.arch to the supported set (others pass through).
|
|
36
|
+
function normalizeArch(arch) {
|
|
37
|
+
if (arch === 'x64' || arch === 'ia32') return 'x64';
|
|
38
|
+
if (arch === 'arm64') return 'arm64';
|
|
39
|
+
return arch;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function readOsRelease(readFile) {
|
|
43
|
+
for (const file of ['/etc/os-release', '/usr/lib/os-release']) {
|
|
44
|
+
try {
|
|
45
|
+
return readFile(file, 'utf8');
|
|
46
|
+
} catch {
|
|
47
|
+
// try next location
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function isWSL({ platform, env, readFile }) {
|
|
54
|
+
if (platform !== 'linux') return false;
|
|
55
|
+
if (env.WSL_DISTRO_NAME || env.WSL_INTEROP) return true;
|
|
56
|
+
try {
|
|
57
|
+
return /microsoft/i.test(readFile('/proc/version', 'utf8'));
|
|
58
|
+
} catch {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Resolves the package manager for a linux distro:
|
|
64
|
+
// exact ID first, then any ID_LIKE entry.
|
|
65
|
+
function linuxManager(osReleaseInfo) {
|
|
66
|
+
if (osReleaseInfo.id && DISTRO_MANAGERS[osReleaseInfo.id]) {
|
|
67
|
+
return DISTRO_MANAGERS[osReleaseInfo.id];
|
|
68
|
+
}
|
|
69
|
+
for (const like of osReleaseInfo.idLike || []) {
|
|
70
|
+
if (DISTRO_MANAGERS[like]) return DISTRO_MANAGERS[like];
|
|
71
|
+
}
|
|
72
|
+
return 'none';
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Detects the full execution environment. All system access is injectable
|
|
76
|
+
// via opts (env/platform/arch/readFile) for deterministic tests.
|
|
77
|
+
//
|
|
78
|
+
// Result shape (frozen — also emitted by `adm config --json`):
|
|
79
|
+
// os, distro, distroVersion, manager, arch, isWSL, shell, rcFile
|
|
80
|
+
function detectEnvironment(opts = {}) {
|
|
81
|
+
const platform = opts.platform || process.platform;
|
|
82
|
+
const env = opts.env || process.env;
|
|
83
|
+
const readFile = opts.readFile || fs.readFileSync;
|
|
84
|
+
const arch = normalizeArch(opts.arch || process.arch);
|
|
85
|
+
|
|
86
|
+
let distro = null;
|
|
87
|
+
let distroVersion = null;
|
|
88
|
+
let manager = 'none';
|
|
89
|
+
|
|
90
|
+
if (platform === 'linux') {
|
|
91
|
+
const info = parseOsRelease(opts.osRelease || readOsRelease(readFile) || '');
|
|
92
|
+
if (info.id) distro = info.id;
|
|
93
|
+
distroVersion = info.version;
|
|
94
|
+
manager = linuxManager(info);
|
|
95
|
+
} else if (platform === 'darwin') {
|
|
96
|
+
manager = 'brew';
|
|
97
|
+
} else if (platform === 'win32') {
|
|
98
|
+
manager = 'winget';
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const shell = detectShell({ env, platform, readParentName: opts.readParentName, readPpid: opts.readPpid, ppid: opts.ppid });
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
os: platform,
|
|
105
|
+
distro,
|
|
106
|
+
distroVersion,
|
|
107
|
+
manager,
|
|
108
|
+
arch,
|
|
109
|
+
isWSL: isWSL({ platform, env, readFile }),
|
|
110
|
+
shell,
|
|
111
|
+
rcFile: rcFileFor(shell),
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Text block for `adm config`.
|
|
116
|
+
function formatEnvironment(env) {
|
|
117
|
+
const osPart = env.distro
|
|
118
|
+
? `${env.os} — ${env.distro}${env.distroVersion ? ' ' + env.distroVersion : ''}`
|
|
119
|
+
: env.os;
|
|
120
|
+
const lines = [
|
|
121
|
+
` OS: ${osPart}${env.isWSL ? ' (WSL)' : ''}`,
|
|
122
|
+
` Arch: ${env.arch}`,
|
|
123
|
+
` Packages: ${env.manager}`,
|
|
124
|
+
` Shell: ${env.shell}${env.rcFile ? ` (${env.rcFile})` : ''}`,
|
|
125
|
+
];
|
|
126
|
+
return lines.join('\n');
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
module.exports = { detectEnvironment, formatEnvironment, normalizeArch, DISTRO_MANAGERS };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// Parses /etc/os-release content into { id, version, idLike }.
|
|
2
|
+
// Handles the common formats: KEY=value with optional quotes,
|
|
3
|
+
// comments, blank lines.
|
|
4
|
+
function parseOsRelease(content) {
|
|
5
|
+
if (!content) return { id: null, version: null, idLike: [] };
|
|
6
|
+
const fields = {};
|
|
7
|
+
for (const rawLine of content.split('\n')) {
|
|
8
|
+
const line = rawLine.trim();
|
|
9
|
+
if (!line || line.startsWith('#')) continue;
|
|
10
|
+
const eq = line.indexOf('=');
|
|
11
|
+
if (eq === -1) continue;
|
|
12
|
+
const key = line.slice(0, eq).trim();
|
|
13
|
+
let value = line.slice(eq + 1).trim();
|
|
14
|
+
if ((value.startsWith('"') && value.endsWith('"')) ||
|
|
15
|
+
(value.startsWith("'") && value.endsWith("'"))) {
|
|
16
|
+
value = value.slice(1, -1);
|
|
17
|
+
}
|
|
18
|
+
fields[key] = value;
|
|
19
|
+
}
|
|
20
|
+
return {
|
|
21
|
+
id: (fields.ID || '').toLowerCase() || null,
|
|
22
|
+
version: fields.VERSION_ID || null,
|
|
23
|
+
idLike: (fields.ID_LIKE || '').toLowerCase().split(/\s+/).filter(Boolean),
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
module.exports = { parseOsRelease };
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
|
|
3
|
+
// Map a shell name to the rc file ADM writes PATH entries to (#29 uses this).
|
|
4
|
+
const RC_FILES = {
|
|
5
|
+
bash: '.bashrc',
|
|
6
|
+
zsh: '.zshrc',
|
|
7
|
+
sh: '.profile',
|
|
8
|
+
ksh: '.kshrc',
|
|
9
|
+
fish: '.config/fish/config.fish',
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
function rcFileFor(shellName) {
|
|
13
|
+
return RC_FILES[shellName] || null;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function basename(p) {
|
|
17
|
+
return String(p || '').split(/[\\/]/).pop().trim().toLowerCase();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function normalizeShellName(name) {
|
|
21
|
+
const base = basename(name);
|
|
22
|
+
if (!base) return null;
|
|
23
|
+
// Strip version suffixes and login-shell markers: "zsh-5.9" → "zsh"
|
|
24
|
+
if (base.startsWith('bash')) return 'bash';
|
|
25
|
+
if (base.startsWith('zsh')) return 'zsh';
|
|
26
|
+
if (base.startsWith('fish')) return 'fish';
|
|
27
|
+
if (base.startsWith('ksh')) return 'ksh';
|
|
28
|
+
if (base === 'sh' || base === 'dash' || base === 'ash') return 'sh';
|
|
29
|
+
if (base.startsWith('powershell')) return 'powershell';
|
|
30
|
+
if (base === 'pwsh') return 'pwsh';
|
|
31
|
+
if (base === 'cmd') return 'cmd';
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Walks up the parent process chain (ppid → parent → grandparent…) and
|
|
36
|
+
// returns the first ancestor whose name is a known shell. `readParentName(pid)`
|
|
37
|
+
// returns the process name for a pid, or null when it can't be read.
|
|
38
|
+
// `readPpid(pid)` returns the parent pid, or null to stop walking.
|
|
39
|
+
function shellFromParentChain(ppid, readParentName, readPpid, maxDepth = 4) {
|
|
40
|
+
let pid = ppid;
|
|
41
|
+
for (let depth = 0; depth < maxDepth && pid && pid > 1; depth++) {
|
|
42
|
+
const shell = normalizeShellName(readParentName(pid));
|
|
43
|
+
if (shell) return shell;
|
|
44
|
+
if (!readPpid) break;
|
|
45
|
+
pid = readPpid(pid);
|
|
46
|
+
if (pid === null || pid === undefined) break;
|
|
47
|
+
}
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function linuxParentName(pid) {
|
|
52
|
+
try {
|
|
53
|
+
return fs.readFileSync(`/proc/${pid}/comm`, 'utf8').trim();
|
|
54
|
+
} catch {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function linuxParentPpid(pid) {
|
|
60
|
+
try {
|
|
61
|
+
const stat = fs.readFileSync(`/proc/${pid}/stat`, 'utf8');
|
|
62
|
+
// Field 4 (ppid) sits after the comm field, which may contain spaces and
|
|
63
|
+
// parens — find the last ')' and parse fields after it.
|
|
64
|
+
const afterComm = stat.slice(stat.lastIndexOf(')') + 1).trim().split(/\s+/);
|
|
65
|
+
return parseInt(afterComm[1], 10) || null;
|
|
66
|
+
} catch {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function darwinParentName(pid) {
|
|
72
|
+
try {
|
|
73
|
+
const { execFileSync } = require('child_process');
|
|
74
|
+
const out = execFileSync('ps', ['-o', 'comm=', '-p', String(pid)], { encoding: 'utf8' });
|
|
75
|
+
return out.trim() || null;
|
|
76
|
+
} catch {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Detects the CURRENT shell (the one that launched adm):
|
|
82
|
+
// 1. parent process chain (works even when $SHELL points at the login shell)
|
|
83
|
+
// 2. fallback: $SHELL
|
|
84
|
+
// Windows: git-bash (MSYSTEM) → bash; otherwise powershell/cmd.
|
|
85
|
+
function detectShell(opts = {}) {
|
|
86
|
+
const env = opts.env || process.env;
|
|
87
|
+
const platform = opts.platform || process.platform;
|
|
88
|
+
|
|
89
|
+
if (platform === 'win32') {
|
|
90
|
+
if (env.MSYSTEM) return 'bash';
|
|
91
|
+
if (env.PSModulePath) return 'powershell';
|
|
92
|
+
return 'cmd';
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const readParentName = opts.readParentName ||
|
|
96
|
+
(platform === 'darwin' ? darwinParentName : linuxParentName);
|
|
97
|
+
const readPpid = opts.readPpid || (platform === 'darwin' ? null : linuxParentPpid);
|
|
98
|
+
|
|
99
|
+
const chainShell = shellFromParentChain(
|
|
100
|
+
opts.ppid || process.ppid,
|
|
101
|
+
readParentName,
|
|
102
|
+
readPpid,
|
|
103
|
+
);
|
|
104
|
+
if (chainShell) return chainShell;
|
|
105
|
+
|
|
106
|
+
const fromEnv = normalizeShellName(env.SHELL);
|
|
107
|
+
if (fromEnv) return fromEnv;
|
|
108
|
+
return 'unknown';
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
module.exports = { detectShell, rcFileFor, normalizeShellName, shellFromParentChain, RC_FILES };
|