@fredlackey/devutils 0.0.18 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +214 -141
- package/package.json +8 -83
- package/src/api/loader.js +229 -0
- package/src/api/registry.json +62 -0
- package/src/cli.js +293 -60
- package/src/commands/ai/index.js +16 -0
- package/src/commands/ai/launch.js +112 -0
- package/src/commands/ai/list.js +54 -0
- package/src/commands/ai/resume.js +70 -0
- package/src/commands/ai/sessions.js +121 -0
- package/src/commands/ai/set.js +131 -0
- package/src/commands/ai/show.js +74 -0
- package/src/commands/ai/tools.js +46 -0
- package/src/commands/alias/add.js +93 -0
- package/src/commands/alias/helpers.js +107 -0
- package/src/commands/alias/index.js +14 -0
- package/src/commands/alias/list.js +55 -0
- package/src/commands/alias/remove.js +62 -0
- package/src/commands/alias/sync.js +109 -0
- package/src/commands/api/disable.js +73 -0
- package/src/commands/api/enable.js +148 -0
- package/src/commands/api/index.js +15 -0
- package/src/commands/api/list.js +66 -0
- package/src/commands/api/update.js +87 -0
- package/src/commands/auth/index.js +15 -0
- package/src/commands/auth/list.js +49 -0
- package/src/commands/auth/login.js +384 -0
- package/src/commands/auth/logout.js +111 -0
- package/src/commands/auth/refresh.js +184 -0
- package/src/commands/auth/services.js +169 -0
- package/src/commands/auth/status.js +104 -0
- package/src/commands/config/export.js +224 -0
- package/src/commands/config/get.js +52 -0
- package/src/commands/config/import.js +308 -0
- package/src/commands/config/index.js +17 -0
- package/src/commands/config/init.js +143 -0
- package/src/commands/config/reset.js +57 -0
- package/src/commands/config/set.js +93 -0
- package/src/commands/config/show.js +35 -0
- package/src/commands/help.js +338 -0
- package/src/commands/identity/add.js +133 -0
- package/src/commands/identity/index.js +17 -0
- package/src/commands/identity/link.js +76 -0
- package/src/commands/identity/list.js +48 -0
- package/src/commands/identity/remove.js +72 -0
- package/src/commands/identity/show.js +65 -0
- package/src/commands/identity/sync.js +172 -0
- package/src/commands/identity/unlink.js +57 -0
- package/src/commands/ignore/add.js +165 -0
- package/src/commands/ignore/index.js +14 -0
- package/src/commands/ignore/list.js +89 -0
- package/src/commands/ignore/markers.js +43 -0
- package/src/commands/ignore/remove.js +164 -0
- package/src/commands/ignore/show.js +169 -0
- package/src/commands/machine/detect.js +122 -0
- package/src/commands/machine/index.js +14 -0
- package/src/commands/machine/list.js +74 -0
- package/src/commands/machine/set.js +106 -0
- package/src/commands/machine/show.js +35 -0
- package/src/commands/schema.js +152 -0
- package/src/commands/search/collections.js +134 -0
- package/src/commands/search/get.js +71 -0
- package/src/commands/search/index-cmd.js +54 -0
- package/src/commands/search/index.js +21 -0
- package/src/commands/search/keyword.js +60 -0
- package/src/commands/search/qmd.js +70 -0
- package/src/commands/search/query.js +64 -0
- package/src/commands/search/semantic.js +62 -0
- package/src/commands/search/status.js +46 -0
- package/src/commands/status.js +224 -171
- package/src/commands/tools/check.js +79 -0
- package/src/commands/tools/index.js +14 -0
- package/src/commands/tools/install.js +110 -0
- package/src/commands/tools/list.js +91 -0
- package/src/commands/tools/search.js +60 -0
- package/src/commands/update.js +83 -112
- package/src/commands/util/add.js +151 -0
- package/src/commands/util/index.js +15 -0
- package/src/commands/util/list.js +97 -0
- package/src/commands/util/remove.js +76 -0
- package/src/commands/util/run.js +79 -0
- package/src/commands/util/show.js +67 -0
- package/src/commands/version.js +21 -88
- package/src/installers/_template.js +104 -0
- package/src/installers/git.js +150 -0
- package/src/installers/homebrew.js +190 -0
- package/src/installers/node.js +223 -0
- package/src/installers/registry.json +29 -0
- package/src/lib/config.js +125 -0
- package/src/lib/detect.js +74 -0
- package/src/lib/errors.js +114 -0
- package/src/lib/github.js +315 -0
- package/src/lib/installer.js +225 -0
- package/src/lib/output.js +239 -0
- package/src/lib/platform.js +112 -0
- package/src/lib/platforms/amazon-linux.js +41 -0
- package/src/lib/platforms/gitbash.js +46 -0
- package/src/lib/platforms/macos.js +45 -0
- package/src/lib/platforms/raspbian.js +41 -0
- package/src/lib/platforms/ubuntu.js +39 -0
- package/src/lib/platforms/windows.js +45 -0
- package/src/lib/prompt.js +161 -0
- package/src/lib/schema.js +211 -0
- package/src/lib/shell.js +75 -0
- package/src/patterns/gitignore/claude-code.txt +25 -0
- package/src/patterns/gitignore/docker.txt +15 -0
- package/src/patterns/gitignore/go.txt +24 -0
- package/src/patterns/gitignore/java.txt +38 -0
- package/src/patterns/gitignore/jetbrains.txt +26 -0
- package/src/patterns/gitignore/linux.txt +18 -0
- package/src/patterns/gitignore/macos.txt +27 -0
- package/src/patterns/gitignore/node.txt +51 -0
- package/src/patterns/gitignore/python.txt +55 -0
- package/src/patterns/gitignore/rust.txt +14 -0
- package/src/patterns/gitignore/terraform.txt +30 -0
- package/src/patterns/gitignore/vscode.txt +15 -0
- package/src/patterns/gitignore/windows.txt +25 -0
- package/src/utils/clone/index.js +165 -0
- package/src/utils/git-push/index.js +230 -0
- package/src/utils/git-status/index.js +116 -0
- package/src/utils/git-status/unix.sh +75 -0
- package/src/utils/registry.json +41 -0
- package/bin/dev.js +0 -16
- package/files/README.md +0 -0
- package/files/claude/.claude/commands/setup-context.md +0 -3
- package/files/monorepos/_archive/README.md +0 -36
- package/files/monorepos/_legacy/README.md +0 -36
- package/files/monorepos/ai-docs/README.md +0 -33
- package/files/monorepos/apps/README.md +0 -24
- package/files/monorepos/docs/README.md +0 -40
- package/files/monorepos/packages/README.md +0 -25
- package/files/monorepos/research/README.md +0 -29
- package/files/monorepos/scripts/README.md +0 -24
- package/src/commands/README.md +0 -41
- package/src/commands/configure.js +0 -199
- package/src/commands/identity.js +0 -1630
- package/src/commands/ignore.js +0 -247
- package/src/commands/install.js +0 -526
- package/src/commands/setup.js +0 -246
- package/src/completion.js +0 -284
- package/src/constants.js +0 -45
- package/src/ignore/claude-code.txt +0 -10
- package/src/ignore/docker.txt +0 -18
- package/src/ignore/linux.txt +0 -23
- package/src/ignore/macos.txt +0 -36
- package/src/ignore/node.txt +0 -55
- package/src/ignore/terraform.txt +0 -37
- package/src/ignore/vscode.txt +0 -18
- package/src/ignore/windows.txt +0 -35
- package/src/index.js +0 -0
- package/src/installs/README.md +0 -399
- package/src/installs/adobe-creative-cloud.js +0 -546
- package/src/installs/adobe-creative-cloud.md +0 -605
- package/src/installs/appcleaner.js +0 -321
- package/src/installs/appcleaner.md +0 -699
- package/src/installs/apt-transport-https.js +0 -390
- package/src/installs/apt-transport-https.md +0 -678
- package/src/installs/atomicparsley.js +0 -642
- package/src/installs/atomicparsley.md +0 -795
- package/src/installs/aws-cli.js +0 -797
- package/src/installs/aws-cli.md +0 -727
- package/src/installs/balena-etcher.js +0 -710
- package/src/installs/balena-etcher.md +0 -761
- package/src/installs/bambu-studio.js +0 -1143
- package/src/installs/bambu-studio.md +0 -780
- package/src/installs/bash-completion.js +0 -575
- package/src/installs/bash-completion.md +0 -833
- package/src/installs/bash.js +0 -417
- package/src/installs/bash.md +0 -993
- package/src/installs/beyond-compare.js +0 -603
- package/src/installs/beyond-compare.md +0 -813
- package/src/installs/brave-browser.js +0 -968
- package/src/installs/brave-browser.md +0 -650
- package/src/installs/build-essential.js +0 -529
- package/src/installs/build-essential.md +0 -977
- package/src/installs/ca-certificates.js +0 -618
- package/src/installs/ca-certificates.md +0 -937
- package/src/installs/caffeine.js +0 -508
- package/src/installs/caffeine.md +0 -839
- package/src/installs/camtasia.js +0 -596
- package/src/installs/camtasia.md +0 -762
- package/src/installs/chatgpt.js +0 -476
- package/src/installs/chatgpt.md +0 -814
- package/src/installs/chocolatey.js +0 -456
- package/src/installs/chocolatey.md +0 -661
- package/src/installs/chrome-canary.js +0 -419
- package/src/installs/chrome-canary.md +0 -641
- package/src/installs/chromium.js +0 -667
- package/src/installs/chromium.md +0 -838
- package/src/installs/claude-code.js +0 -576
- package/src/installs/claude-code.md +0 -1173
- package/src/installs/cloudflare-warp.js +0 -900
- package/src/installs/cloudflare-warp.md +0 -1047
- package/src/installs/comet-browser.js +0 -588
- package/src/installs/comet-browser.md +0 -731
- package/src/installs/curl.js +0 -379
- package/src/installs/curl.md +0 -714
- package/src/installs/cursor.js +0 -579
- package/src/installs/cursor.md +0 -970
- package/src/installs/dbeaver.js +0 -924
- package/src/installs/dbeaver.md +0 -939
- package/src/installs/dbschema.js +0 -692
- package/src/installs/dbschema.md +0 -925
- package/src/installs/dependencies.md +0 -453
- package/src/installs/development-tools.js +0 -600
- package/src/installs/development-tools.md +0 -977
- package/src/installs/docker.js +0 -1029
- package/src/installs/docker.md +0 -1109
- package/src/installs/drawio.js +0 -1019
- package/src/installs/drawio.md +0 -795
- package/src/installs/elmedia-player.js +0 -347
- package/src/installs/elmedia-player.md +0 -556
- package/src/installs/ffmpeg.js +0 -889
- package/src/installs/ffmpeg.md +0 -852
- package/src/installs/file.js +0 -464
- package/src/installs/file.md +0 -987
- package/src/installs/gemini-cli.js +0 -811
- package/src/installs/gemini-cli.md +0 -1153
- package/src/installs/git.js +0 -400
- package/src/installs/git.md +0 -907
- package/src/installs/gitego.js +0 -949
- package/src/installs/gitego.md +0 -1172
- package/src/installs/go.js +0 -931
- package/src/installs/go.md +0 -958
- package/src/installs/google-antigravity.js +0 -913
- package/src/installs/google-antigravity.md +0 -1075
- package/src/installs/google-chrome.js +0 -833
- package/src/installs/google-chrome.md +0 -862
- package/src/installs/gpg.js +0 -480
- package/src/installs/gpg.md +0 -1056
- package/src/installs/homebrew.js +0 -1028
- package/src/installs/homebrew.md +0 -988
- package/src/installs/imageoptim.js +0 -968
- package/src/installs/imageoptim.md +0 -1119
- package/src/installs/installers.json +0 -4032
- package/src/installs/installers.json.tmp +0 -3953
- package/src/installs/jq.js +0 -400
- package/src/installs/jq.md +0 -809
- package/src/installs/keyboard-maestro.js +0 -719
- package/src/installs/keyboard-maestro.md +0 -825
- package/src/installs/kiro.js +0 -864
- package/src/installs/kiro.md +0 -1015
- package/src/installs/latex.js +0 -789
- package/src/installs/latex.md +0 -1095
- package/src/installs/lftp.js +0 -356
- package/src/installs/lftp.md +0 -907
- package/src/installs/lsb-release.js +0 -346
- package/src/installs/lsb-release.md +0 -814
- package/src/installs/messenger.js +0 -847
- package/src/installs/messenger.md +0 -900
- package/src/installs/microsoft-office.js +0 -568
- package/src/installs/microsoft-office.md +0 -760
- package/src/installs/microsoft-teams.js +0 -801
- package/src/installs/microsoft-teams.md +0 -886
- package/src/installs/moom.js +0 -326
- package/src/installs/moom.md +0 -570
- package/src/installs/node.js +0 -904
- package/src/installs/node.md +0 -1153
- package/src/installs/nordpass.js +0 -716
- package/src/installs/nordpass.md +0 -921
- package/src/installs/nordvpn.js +0 -892
- package/src/installs/nordvpn.md +0 -1052
- package/src/installs/nvm.js +0 -995
- package/src/installs/nvm.md +0 -1057
- package/src/installs/ohmyzsh.js +0 -529
- package/src/installs/ohmyzsh.md +0 -1094
- package/src/installs/openssh.js +0 -804
- package/src/installs/openssh.md +0 -1056
- package/src/installs/pandoc.js +0 -662
- package/src/installs/pandoc.md +0 -1036
- package/src/installs/parallels-desktop.js +0 -431
- package/src/installs/parallels-desktop.md +0 -446
- package/src/installs/pinentry.js +0 -510
- package/src/installs/pinentry.md +0 -1142
- package/src/installs/pngyu.js +0 -869
- package/src/installs/pngyu.md +0 -896
- package/src/installs/postman.js +0 -799
- package/src/installs/postman.md +0 -940
- package/src/installs/procps.js +0 -425
- package/src/installs/procps.md +0 -851
- package/src/installs/safari-tech-preview.js +0 -374
- package/src/installs/safari-tech-preview.md +0 -533
- package/src/installs/sfnt2woff.js +0 -658
- package/src/installs/sfnt2woff.md +0 -795
- package/src/installs/shellcheck.js +0 -481
- package/src/installs/shellcheck.md +0 -1005
- package/src/installs/slack.js +0 -741
- package/src/installs/slack.md +0 -865
- package/src/installs/snagit.js +0 -585
- package/src/installs/snagit.md +0 -844
- package/src/installs/software-properties-common.js +0 -372
- package/src/installs/software-properties-common.md +0 -805
- package/src/installs/spotify.js +0 -877
- package/src/installs/spotify.md +0 -901
- package/src/installs/studio-3t.js +0 -823
- package/src/installs/studio-3t.md +0 -918
- package/src/installs/sublime-text.js +0 -804
- package/src/installs/sublime-text.md +0 -914
- package/src/installs/superwhisper.js +0 -706
- package/src/installs/superwhisper.md +0 -630
- package/src/installs/tailscale.js +0 -745
- package/src/installs/tailscale.md +0 -1100
- package/src/installs/tar.js +0 -389
- package/src/installs/tar.md +0 -946
- package/src/installs/termius.js +0 -798
- package/src/installs/termius.md +0 -844
- package/src/installs/terraform.js +0 -779
- package/src/installs/terraform.md +0 -899
- package/src/installs/tfenv.js +0 -778
- package/src/installs/tfenv.md +0 -1091
- package/src/installs/tidal.js +0 -771
- package/src/installs/tidal.md +0 -864
- package/src/installs/tmux.js +0 -346
- package/src/installs/tmux.md +0 -1030
- package/src/installs/tree.js +0 -411
- package/src/installs/tree.md +0 -833
- package/src/installs/unzip.js +0 -460
- package/src/installs/unzip.md +0 -879
- package/src/installs/vim.js +0 -421
- package/src/installs/vim.md +0 -1040
- package/src/installs/vlc.js +0 -821
- package/src/installs/vlc.md +0 -927
- package/src/installs/vscode.js +0 -843
- package/src/installs/vscode.md +0 -1002
- package/src/installs/wget.js +0 -420
- package/src/installs/wget.md +0 -791
- package/src/installs/whatsapp.js +0 -729
- package/src/installs/whatsapp.md +0 -854
- package/src/installs/winpty.js +0 -352
- package/src/installs/winpty.md +0 -620
- package/src/installs/woff2.js +0 -553
- package/src/installs/woff2.md +0 -977
- package/src/installs/wsl.js +0 -572
- package/src/installs/wsl.md +0 -699
- package/src/installs/xcode-clt.js +0 -520
- package/src/installs/xcode-clt.md +0 -351
- package/src/installs/xcode.js +0 -560
- package/src/installs/xcode.md +0 -573
- package/src/installs/yarn.js +0 -824
- package/src/installs/yarn.md +0 -1074
- package/src/installs/yq.js +0 -654
- package/src/installs/yq.md +0 -944
- package/src/installs/yt-dlp.js +0 -701
- package/src/installs/yt-dlp.md +0 -946
- package/src/installs/yum-utils.js +0 -297
- package/src/installs/yum-utils.md +0 -648
- package/src/installs/zoom.js +0 -759
- package/src/installs/zoom.md +0 -884
- package/src/installs/zsh.js +0 -455
- package/src/installs/zsh.md +0 -1008
- package/src/scripts/README.md +0 -617
- package/src/scripts/STATUS.md +0 -208
- package/src/scripts/afk.js +0 -411
- package/src/scripts/backup-all.js +0 -746
- package/src/scripts/backup-source.js +0 -727
- package/src/scripts/brewd.js +0 -389
- package/src/scripts/brewi.js +0 -520
- package/src/scripts/brewr.js +0 -527
- package/src/scripts/brews.js +0 -477
- package/src/scripts/brewu.js +0 -504
- package/src/scripts/c.js +0 -201
- package/src/scripts/ccurl.js +0 -341
- package/src/scripts/certbot-crontab-init.js +0 -504
- package/src/scripts/certbot-init.js +0 -657
- package/src/scripts/ch.js +0 -355
- package/src/scripts/claude-danger.js +0 -268
- package/src/scripts/clean-dev.js +0 -435
- package/src/scripts/clear-dns-cache.js +0 -541
- package/src/scripts/clone.js +0 -435
- package/src/scripts/code-all.js +0 -437
- package/src/scripts/count-files.js +0 -211
- package/src/scripts/count-folders.js +0 -211
- package/src/scripts/count.js +0 -264
- package/src/scripts/d.js +0 -219
- package/src/scripts/datauri.js +0 -389
- package/src/scripts/delete-files.js +0 -380
- package/src/scripts/docker-clean.js +0 -426
- package/src/scripts/dp.js +0 -442
- package/src/scripts/e.js +0 -390
- package/src/scripts/empty-trash.js +0 -513
- package/src/scripts/evm.js +0 -444
- package/src/scripts/fetch-github-repos.js +0 -456
- package/src/scripts/get-channel.js +0 -345
- package/src/scripts/get-course.js +0 -399
- package/src/scripts/get-dependencies.js +0 -306
- package/src/scripts/get-folder.js +0 -799
- package/src/scripts/get-tunes.js +0 -426
- package/src/scripts/get-video.js +0 -367
- package/src/scripts/git-backup.js +0 -577
- package/src/scripts/git-clone.js +0 -493
- package/src/scripts/git-pup.js +0 -319
- package/src/scripts/git-push.js +0 -396
- package/src/scripts/h.js +0 -622
- package/src/scripts/hide-desktop-icons.js +0 -499
- package/src/scripts/hide-hidden-files.js +0 -538
- package/src/scripts/install-dependencies-from.js +0 -456
- package/src/scripts/ips.js +0 -663
- package/src/scripts/iso.js +0 -370
- package/src/scripts/killni.js +0 -577
- package/src/scripts/ll.js +0 -467
- package/src/scripts/local-ip.js +0 -325
- package/src/scripts/m.js +0 -524
- package/src/scripts/map.js +0 -309
- package/src/scripts/mkd.js +0 -351
- package/src/scripts/ncu-update-all.js +0 -457
- package/src/scripts/nginx-init.js +0 -718
- package/src/scripts/npmi.js +0 -382
- package/src/scripts/o.js +0 -511
- package/src/scripts/org-by-date.js +0 -338
- package/src/scripts/p.js +0 -224
- package/src/scripts/packages.js +0 -330
- package/src/scripts/path.js +0 -225
- package/src/scripts/ports.js +0 -597
- package/src/scripts/q.js +0 -305
- package/src/scripts/refresh-files.js +0 -394
- package/src/scripts/remove-smaller-files.js +0 -516
- package/src/scripts/rename-files-with-date.js +0 -533
- package/src/scripts/resize-image.js +0 -539
- package/src/scripts/rm-safe.js +0 -669
- package/src/scripts/s.js +0 -540
- package/src/scripts/set-git-public.js +0 -365
- package/src/scripts/show-desktop-icons.js +0 -475
- package/src/scripts/show-hidden-files.js +0 -472
- package/src/scripts/tpa.js +0 -280
- package/src/scripts/tpo.js +0 -280
- package/src/scripts/u.js +0 -505
- package/src/scripts/vpush.js +0 -437
- package/src/scripts/y.js +0 -283
- package/src/utils/README.md +0 -95
- package/src/utils/common/apps.js +0 -143
- package/src/utils/common/display.js +0 -157
- package/src/utils/common/network.js +0 -185
- package/src/utils/common/os.js +0 -294
- package/src/utils/common/package-manager.js +0 -301
- package/src/utils/common/privileges.js +0 -138
- package/src/utils/common/shell.js +0 -261
- package/src/utils/macos/apps.js +0 -228
- package/src/utils/macos/brew.js +0 -315
- package/src/utils/ubuntu/apt.js +0 -307
- package/src/utils/ubuntu/desktop.js +0 -292
- package/src/utils/ubuntu/snap.js +0 -344
- package/src/utils/ubuntu/systemd.js +0 -286
- package/src/utils/windows/choco.js +0 -465
- package/src/utils/windows/env.js +0 -246
- package/src/utils/windows/registry.js +0 -269
- package/src/utils/windows/shell.js +0 -240
- package/src/utils/windows/winget.js +0 -489
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { loadAliases, saveAliases, generateWrapper, BIN_DIR } = require('./helpers');
|
|
4
|
+
|
|
5
|
+
const meta = {
|
|
6
|
+
description: 'Create a global shorthand command that maps to any dev command.',
|
|
7
|
+
arguments: [
|
|
8
|
+
{ name: 'name', required: true, description: 'The alias name (what the user will type)' },
|
|
9
|
+
{ name: 'command', required: true, description: 'The full command to run (quoted string)' }
|
|
10
|
+
],
|
|
11
|
+
flags: [
|
|
12
|
+
{ name: 'force', type: 'boolean', description: 'Overwrite an existing alias without prompting' }
|
|
13
|
+
]
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Creates a new alias by writing the mapping to aliases.json and generating
|
|
18
|
+
* a wrapper script in ~/.devutils/bin/.
|
|
19
|
+
*
|
|
20
|
+
* @param {object} args - Parsed command arguments (positional and flags).
|
|
21
|
+
* @param {object} context - The command context (output, prompt, errors, shell, platform).
|
|
22
|
+
*/
|
|
23
|
+
async function run(args, context) {
|
|
24
|
+
// Step 1: Parse arguments
|
|
25
|
+
// The first positional arg is the alias name. Everything after it is the command.
|
|
26
|
+
const name = args.positional[0];
|
|
27
|
+
const command = args.positional.slice(1).join(' ');
|
|
28
|
+
|
|
29
|
+
if (!name || !command) {
|
|
30
|
+
context.output.error('Usage: dev alias add <name> "<command>"');
|
|
31
|
+
context.output.error('');
|
|
32
|
+
context.output.error('Examples:');
|
|
33
|
+
context.output.error(' dev alias add gs "dev util run git-status"');
|
|
34
|
+
context.output.error(' dev alias add clone "dev util run clone"');
|
|
35
|
+
context.output.error(' dev alias add claude-danger "dev ai launch claude"');
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Step 2: Validate the alias name
|
|
40
|
+
// Must be lowercase letters, numbers, and hyphens. Must start with a letter or number.
|
|
41
|
+
if (!/^[a-z0-9][a-z0-9-]*$/.test(name)) {
|
|
42
|
+
context.output.error('Alias names must be lowercase letters, numbers, and hyphens only.');
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Step 3: Check for system command conflicts
|
|
47
|
+
// Use shell.which to see if the name matches an existing command on the system PATH.
|
|
48
|
+
// Exclude our own bin directory from the check so re-creating an alias is not a conflict.
|
|
49
|
+
const binDir = BIN_DIR;
|
|
50
|
+
const existingPath = context.shell.which(name);
|
|
51
|
+
|
|
52
|
+
if (existingPath && !existingPath.startsWith(binDir)) {
|
|
53
|
+
context.output.error(`Warning: "${name}" already exists on your system at ${existingPath}`);
|
|
54
|
+
context.output.error('Creating this alias will shadow the existing command.');
|
|
55
|
+
|
|
56
|
+
if (!args.flags.force) {
|
|
57
|
+
const proceed = await context.prompt.confirm(
|
|
58
|
+
'Create the alias anyway?',
|
|
59
|
+
false
|
|
60
|
+
);
|
|
61
|
+
if (!proceed) {
|
|
62
|
+
context.output.info('Cancelled.');
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Step 4: Check for existing aliases
|
|
69
|
+
// If an alias with this name already exists, warn the user unless --force is set.
|
|
70
|
+
const aliases = loadAliases();
|
|
71
|
+
|
|
72
|
+
if (aliases[name] && !args.flags.force) {
|
|
73
|
+
context.output.error(`Alias "${name}" already exists: ${aliases[name]}`);
|
|
74
|
+
context.output.error('Use --force to overwrite it.');
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Step 5: Write the alias to aliases.json
|
|
79
|
+
aliases[name] = command;
|
|
80
|
+
saveAliases(aliases);
|
|
81
|
+
|
|
82
|
+
// Step 6: Generate the wrapper script
|
|
83
|
+
const platform = context.platform.detect();
|
|
84
|
+
generateWrapper(name, command, binDir, platform.type);
|
|
85
|
+
|
|
86
|
+
// Step 7: Print confirmation
|
|
87
|
+
context.output.info(`Alias "${name}" created.`);
|
|
88
|
+
context.output.info(` ${name} -> ${command}`);
|
|
89
|
+
context.output.info('');
|
|
90
|
+
context.output.info(`You can now run: ${name}`);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
module.exports = { meta, run };
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const os = require('os');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Path to the aliases.json file in the user's .devutils directory.
|
|
9
|
+
* This is the source of truth for all alias definitions.
|
|
10
|
+
* @type {string}
|
|
11
|
+
*/
|
|
12
|
+
const ALIASES_FILE = path.join(os.homedir(), '.devutils', 'aliases.json');
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Path to the bin directory where wrapper scripts are generated.
|
|
16
|
+
* This directory should be on the user's PATH.
|
|
17
|
+
* @type {string}
|
|
18
|
+
*/
|
|
19
|
+
const BIN_DIR = path.join(os.homedir(), '.devutils', 'bin');
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Reads aliases.json and returns the parsed object.
|
|
23
|
+
* Returns an empty object if the file does not exist or cannot be parsed.
|
|
24
|
+
*
|
|
25
|
+
* @returns {object} A flat object mapping alias names to command strings.
|
|
26
|
+
*/
|
|
27
|
+
function loadAliases() {
|
|
28
|
+
if (!fs.existsSync(ALIASES_FILE)) {
|
|
29
|
+
return {};
|
|
30
|
+
}
|
|
31
|
+
try {
|
|
32
|
+
return JSON.parse(fs.readFileSync(ALIASES_FILE, 'utf8'));
|
|
33
|
+
} catch {
|
|
34
|
+
return {};
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Writes the aliases object to aliases.json.
|
|
40
|
+
* Creates the parent directory if it does not exist.
|
|
41
|
+
*
|
|
42
|
+
* @param {object} aliases - A flat object mapping alias names to command strings.
|
|
43
|
+
*/
|
|
44
|
+
function saveAliases(aliases) {
|
|
45
|
+
const dir = path.dirname(ALIASES_FILE);
|
|
46
|
+
if (!fs.existsSync(dir)) {
|
|
47
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
48
|
+
}
|
|
49
|
+
fs.writeFileSync(ALIASES_FILE, JSON.stringify(aliases, null, 2) + '\n');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Generates a wrapper script for an alias in the bin directory.
|
|
54
|
+
* On Unix (macOS, Linux, Git Bash), writes a shell script with exec.
|
|
55
|
+
* On Windows, writes a .cmd file.
|
|
56
|
+
*
|
|
57
|
+
* @param {string} name - The alias name (used as the filename).
|
|
58
|
+
* @param {string} command - The full command the alias maps to.
|
|
59
|
+
* @param {string} binDir - The directory to write the wrapper script into.
|
|
60
|
+
* @param {string} platformType - The platform type from platform.detect().type.
|
|
61
|
+
*/
|
|
62
|
+
function generateWrapper(name, command, binDir, platformType) {
|
|
63
|
+
if (!fs.existsSync(binDir)) {
|
|
64
|
+
fs.mkdirSync(binDir, { recursive: true });
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (platformType === 'windows') {
|
|
68
|
+
// Windows .cmd file
|
|
69
|
+
const scriptPath = path.join(binDir, name + '.cmd');
|
|
70
|
+
const content = `@${command} %*\r\n`;
|
|
71
|
+
fs.writeFileSync(scriptPath, content);
|
|
72
|
+
} else {
|
|
73
|
+
// Unix shell script (macOS, Linux, Git Bash)
|
|
74
|
+
const scriptPath = path.join(binDir, name);
|
|
75
|
+
const content = `#!/bin/sh\nexec ${command} "$@"\n`;
|
|
76
|
+
fs.writeFileSync(scriptPath, content, { mode: 0o755 });
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Deletes wrapper scripts for an alias from the bin directory.
|
|
82
|
+
* Removes both Unix (no extension) and Windows (.cmd) formats to handle
|
|
83
|
+
* cross-platform scenarios.
|
|
84
|
+
*
|
|
85
|
+
* @param {string} name - The alias name to delete.
|
|
86
|
+
* @param {string} binDir - The directory containing the wrapper scripts.
|
|
87
|
+
*/
|
|
88
|
+
function deleteWrapper(name, binDir) {
|
|
89
|
+
const unixPath = path.join(binDir, name);
|
|
90
|
+
const windowsPath = path.join(binDir, name + '.cmd');
|
|
91
|
+
|
|
92
|
+
if (fs.existsSync(unixPath)) {
|
|
93
|
+
fs.unlinkSync(unixPath);
|
|
94
|
+
}
|
|
95
|
+
if (fs.existsSync(windowsPath)) {
|
|
96
|
+
fs.unlinkSync(windowsPath);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
module.exports = {
|
|
101
|
+
ALIASES_FILE,
|
|
102
|
+
BIN_DIR,
|
|
103
|
+
loadAliases,
|
|
104
|
+
saveAliases,
|
|
105
|
+
generateWrapper,
|
|
106
|
+
deleteWrapper,
|
|
107
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Alias service registration.
|
|
3
|
+
* Shorthand bin entries (user-controlled).
|
|
4
|
+
*/
|
|
5
|
+
module.exports = {
|
|
6
|
+
name: 'alias',
|
|
7
|
+
description: 'Shorthand bin entries (user-controlled)',
|
|
8
|
+
commands: {
|
|
9
|
+
add: () => require('./add'),
|
|
10
|
+
remove: () => require('./remove'),
|
|
11
|
+
list: () => require('./list'),
|
|
12
|
+
sync: () => require('./sync'),
|
|
13
|
+
}
|
|
14
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const { loadAliases, BIN_DIR } = require('./helpers');
|
|
6
|
+
|
|
7
|
+
const meta = {
|
|
8
|
+
description: 'List all registered aliases and the commands they map to.',
|
|
9
|
+
arguments: [],
|
|
10
|
+
flags: []
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Lists all registered aliases from aliases.json, sorted alphabetically.
|
|
15
|
+
* Shows a status indicator for aliases that are missing their wrapper script.
|
|
16
|
+
*
|
|
17
|
+
* @param {object} args - Parsed command arguments (positional and flags).
|
|
18
|
+
* @param {object} context - The command context (output).
|
|
19
|
+
*/
|
|
20
|
+
async function run(args, context) {
|
|
21
|
+
// Step 1: Load aliases.json
|
|
22
|
+
const aliases = loadAliases();
|
|
23
|
+
const entries = Object.entries(aliases);
|
|
24
|
+
|
|
25
|
+
// Step 2: Handle the empty case
|
|
26
|
+
if (entries.length === 0) {
|
|
27
|
+
context.output.info('No aliases registered.');
|
|
28
|
+
context.output.info('');
|
|
29
|
+
context.output.info('Create one with: dev alias add <name> "<command>"');
|
|
30
|
+
context.output.info('Example: dev alias add gs "dev util run git-status"');
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Step 3: Sort alphabetically by name
|
|
35
|
+
entries.sort((a, b) => a[0].localeCompare(b[0]));
|
|
36
|
+
|
|
37
|
+
// Step 4: Display the aliases
|
|
38
|
+
const binDir = BIN_DIR;
|
|
39
|
+
|
|
40
|
+
context.output.info(`Aliases (${entries.length}):`);
|
|
41
|
+
context.output.info('');
|
|
42
|
+
|
|
43
|
+
for (const [name, command] of entries) {
|
|
44
|
+
// Check if the wrapper script exists (either Unix or Windows format)
|
|
45
|
+
const scriptExists = fs.existsSync(path.join(binDir, name))
|
|
46
|
+
|| fs.existsSync(path.join(binDir, name + '.cmd'));
|
|
47
|
+
const status = scriptExists ? '' : ' (no script -- run dev alias sync)';
|
|
48
|
+
const nameCol = name.padEnd(25);
|
|
49
|
+
context.output.info(` ${nameCol} -> ${command}${status}`);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
context.output.info('');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
module.exports = { meta, run };
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { loadAliases, saveAliases, deleteWrapper, BIN_DIR } = require('./helpers');
|
|
4
|
+
|
|
5
|
+
const meta = {
|
|
6
|
+
description: 'Remove an alias and delete its wrapper script from ~/.devutils/bin/.',
|
|
7
|
+
arguments: [
|
|
8
|
+
{ name: 'name', required: true, description: 'The alias name to remove' }
|
|
9
|
+
],
|
|
10
|
+
flags: [
|
|
11
|
+
{ name: 'confirm', type: 'boolean', description: 'Skip the confirmation prompt' }
|
|
12
|
+
]
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Removes an alias by deleting its entry from aliases.json and removing
|
|
17
|
+
* the wrapper script from ~/.devutils/bin/.
|
|
18
|
+
*
|
|
19
|
+
* @param {object} args - Parsed command arguments (positional and flags).
|
|
20
|
+
* @param {object} context - The command context (output, prompt, errors).
|
|
21
|
+
*/
|
|
22
|
+
async function run(args, context) {
|
|
23
|
+
// Step 1: Validate the name and check it exists
|
|
24
|
+
const name = args.positional[0];
|
|
25
|
+
if (!name) {
|
|
26
|
+
context.output.error('Usage: dev alias remove <name>');
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const aliases = loadAliases();
|
|
31
|
+
|
|
32
|
+
if (!aliases[name]) {
|
|
33
|
+
context.output.error(`Alias "${name}" is not registered.`);
|
|
34
|
+
context.output.error('Run "dev alias list" to see all aliases.');
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Step 2: Confirm removal unless --confirm flag is set
|
|
39
|
+
if (!args.flags.confirm) {
|
|
40
|
+
const ok = await context.prompt.confirm(
|
|
41
|
+
`Remove alias "${name}" (${aliases[name]})?`,
|
|
42
|
+
true
|
|
43
|
+
);
|
|
44
|
+
if (!ok) {
|
|
45
|
+
context.output.info('Cancelled.');
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Step 3: Delete the wrapper script (both Unix and Windows formats)
|
|
51
|
+
const binDir = BIN_DIR;
|
|
52
|
+
deleteWrapper(name, binDir);
|
|
53
|
+
|
|
54
|
+
// Step 4: Remove from aliases.json
|
|
55
|
+
delete aliases[name];
|
|
56
|
+
saveAliases(aliases);
|
|
57
|
+
|
|
58
|
+
// Step 5: Print confirmation
|
|
59
|
+
context.output.info(`Alias "${name}" removed.`);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
module.exports = { meta, run };
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const { loadAliases, generateWrapper, BIN_DIR } = require('./helpers');
|
|
6
|
+
|
|
7
|
+
const meta = {
|
|
8
|
+
description: 'Rebuild all alias wrapper scripts from aliases.json. Cleans up orphaned scripts.',
|
|
9
|
+
arguments: [],
|
|
10
|
+
flags: [
|
|
11
|
+
{ name: 'dry-run', type: 'boolean', description: 'Show what would be done without doing it' }
|
|
12
|
+
]
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Regenerates all wrapper scripts from aliases.json and removes orphaned
|
|
17
|
+
* scripts that no longer have a matching alias entry. This is the repair
|
|
18
|
+
* command for the alias system -- after importing config on a new machine,
|
|
19
|
+
* run sync to rebuild all the wrapper scripts.
|
|
20
|
+
*
|
|
21
|
+
* @param {object} args - Parsed command arguments (positional and flags).
|
|
22
|
+
* @param {object} context - The command context (output, platform, flags).
|
|
23
|
+
*/
|
|
24
|
+
async function run(args, context) {
|
|
25
|
+
// Step 1: Load aliases.json
|
|
26
|
+
const aliases = loadAliases();
|
|
27
|
+
|
|
28
|
+
// Step 2: Ensure the bin directory exists
|
|
29
|
+
const binDir = BIN_DIR;
|
|
30
|
+
if (!fs.existsSync(binDir)) {
|
|
31
|
+
fs.mkdirSync(binDir, { recursive: true });
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Step 3: Scan existing scripts in the bin directory
|
|
35
|
+
const existingFiles = fs.readdirSync(binDir);
|
|
36
|
+
|
|
37
|
+
// Step 4: Determine what to create and what to remove
|
|
38
|
+
const platform = context.platform.detect();
|
|
39
|
+
|
|
40
|
+
// Find orphans: files in bin/ that are not in aliases.json
|
|
41
|
+
const orphans = existingFiles.filter(file => {
|
|
42
|
+
// Strip .cmd extension for comparison
|
|
43
|
+
const baseName = file.endsWith('.cmd') ? file.slice(0, -4) : file;
|
|
44
|
+
return !aliases[baseName];
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// Find missing: aliases that do not have a wrapper script yet
|
|
48
|
+
const missing = Object.keys(aliases).filter(name => {
|
|
49
|
+
return !fs.existsSync(path.join(binDir, name))
|
|
50
|
+
&& !fs.existsSync(path.join(binDir, name + '.cmd'));
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// Step 5: Handle dry-run mode
|
|
54
|
+
// --dry-run is a global flag (context.flags.dryRun) or a command flag (args.flags['dry-run'])
|
|
55
|
+
const isDryRun = context.flags.dryRun || args.flags['dry-run'];
|
|
56
|
+
|
|
57
|
+
if (isDryRun) {
|
|
58
|
+
if (missing.length === 0 && orphans.length === 0) {
|
|
59
|
+
context.output.info('Everything is in sync. Nothing to do.');
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (missing.length > 0) {
|
|
64
|
+
context.output.info(`Would create ${missing.length} wrapper script(s):`);
|
|
65
|
+
for (const name of missing) {
|
|
66
|
+
context.output.info(` + ${name} -> ${aliases[name]}`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (orphans.length > 0) {
|
|
71
|
+
context.output.info(`Would remove ${orphans.length} orphaned script(s):`);
|
|
72
|
+
for (const file of orphans) {
|
|
73
|
+
context.output.info(` - ${file}`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Step 6: Generate all wrapper scripts
|
|
80
|
+
// Regenerate every script (not just missing ones) to ensure they are all up to date.
|
|
81
|
+
// This handles cases where the command mapping changed in aliases.json.
|
|
82
|
+
let created = 0;
|
|
83
|
+
for (const [name, command] of Object.entries(aliases)) {
|
|
84
|
+
generateWrapper(name, command, binDir, platform.type);
|
|
85
|
+
created++;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Step 7: Remove orphaned scripts
|
|
89
|
+
let removed = 0;
|
|
90
|
+
for (const file of orphans) {
|
|
91
|
+
const filePath = path.join(binDir, file);
|
|
92
|
+
fs.unlinkSync(filePath);
|
|
93
|
+
removed++;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Step 8: Report results
|
|
97
|
+
context.output.info('Alias sync complete.');
|
|
98
|
+
context.output.info(` ${created} script(s) generated`);
|
|
99
|
+
if (removed > 0) {
|
|
100
|
+
context.output.info(` ${removed} orphaned script(s) removed`);
|
|
101
|
+
}
|
|
102
|
+
context.output.info('');
|
|
103
|
+
|
|
104
|
+
if (created > 0) {
|
|
105
|
+
context.output.info('Make sure ~/.devutils/bin is in your PATH.');
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
module.exports = { meta, run };
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const shell = require('../../lib/shell');
|
|
5
|
+
const loader = require('../../api/loader');
|
|
6
|
+
|
|
7
|
+
const meta = {
|
|
8
|
+
description: 'Remove an installed API plugin',
|
|
9
|
+
arguments: [
|
|
10
|
+
{ name: 'name', description: 'Plugin name to remove', required: true }
|
|
11
|
+
],
|
|
12
|
+
flags: [
|
|
13
|
+
{ name: 'confirm', type: 'boolean', description: 'Skip confirmation prompt' }
|
|
14
|
+
]
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Removes an installed API plugin.
|
|
19
|
+
* Runs npm uninstall, removes the entry from plugins.json,
|
|
20
|
+
* and asks for confirmation unless --confirm is passed.
|
|
21
|
+
*
|
|
22
|
+
* @param {object} args - Parsed CLI arguments { positional, flags }.
|
|
23
|
+
* @param {object} context - CLI context { output, errors, prompt }.
|
|
24
|
+
*/
|
|
25
|
+
async function run(args, context) {
|
|
26
|
+
const pluginName = args.positional[0];
|
|
27
|
+
|
|
28
|
+
if (!pluginName) {
|
|
29
|
+
context.errors.throwError(400, 'Missing required argument: <name>. Example: dev api disable gmail', 'api');
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Check if the plugin is installed
|
|
34
|
+
const plugins = loader.readPluginsJson();
|
|
35
|
+
const entry = plugins[pluginName];
|
|
36
|
+
|
|
37
|
+
if (!entry) {
|
|
38
|
+
context.output.info(`Plugin "${pluginName}" is not installed.`);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Ask for confirmation unless --confirm is passed
|
|
43
|
+
if (!args.flags.confirm) {
|
|
44
|
+
const ok = await context.prompt.confirm(
|
|
45
|
+
`Remove plugin "${pluginName}" (${entry.package})?`,
|
|
46
|
+
false
|
|
47
|
+
);
|
|
48
|
+
if (!ok) {
|
|
49
|
+
context.output.info('Cancelled.');
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Run npm uninstall
|
|
55
|
+
const result = await shell.exec(`npm uninstall ${entry.package}`, { cwd: loader.PLUGINS_DIR });
|
|
56
|
+
|
|
57
|
+
if (result.exitCode !== 0) {
|
|
58
|
+
context.errors.throwError(
|
|
59
|
+
500,
|
|
60
|
+
`Failed to uninstall plugin "${pluginName}".\n${result.stderr || result.stdout}`,
|
|
61
|
+
'api'
|
|
62
|
+
);
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Remove the entry from plugins.json
|
|
67
|
+
delete plugins[pluginName];
|
|
68
|
+
fs.writeFileSync(loader.PLUGINS_FILE, JSON.stringify(plugins, null, 2) + '\n');
|
|
69
|
+
|
|
70
|
+
context.output.info(`Plugin "${pluginName}" removed.`);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
module.exports = { meta, run };
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const shell = require('../../lib/shell');
|
|
6
|
+
const loader = require('../../api/loader');
|
|
7
|
+
|
|
8
|
+
const meta = {
|
|
9
|
+
description: 'Install an API plugin from npm or a git repository',
|
|
10
|
+
arguments: [
|
|
11
|
+
{ name: 'name', description: 'Plugin name or full package name', required: true }
|
|
12
|
+
],
|
|
13
|
+
flags: [
|
|
14
|
+
{ name: 'source', type: 'string', description: 'Install source type: "npm" or "git" (default: npm)' },
|
|
15
|
+
{ name: 'url', type: 'string', description: 'Package name or git URL when using a non-registry plugin' }
|
|
16
|
+
]
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Installs an API plugin from npm or a git repository.
|
|
21
|
+
* Resolves short names (like "gmail") through the registry, or accepts
|
|
22
|
+
* full package names (starting with "@") and git URLs directly.
|
|
23
|
+
*
|
|
24
|
+
* @param {object} args - Parsed CLI arguments { positional, flags }.
|
|
25
|
+
* @param {object} context - CLI context { output, errors, prompt }.
|
|
26
|
+
*/
|
|
27
|
+
async function run(args, context) {
|
|
28
|
+
const pluginName = args.positional[0];
|
|
29
|
+
|
|
30
|
+
if (!pluginName) {
|
|
31
|
+
context.errors.throwError(400, 'Missing required argument: <name>. Example: dev api enable gmail', 'api');
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Resolve the package name from the registry or direct input
|
|
36
|
+
const registry = loader.getRegistryPlugins();
|
|
37
|
+
const registryEntry = registry.find(entry => entry.name === pluginName);
|
|
38
|
+
let packageName;
|
|
39
|
+
|
|
40
|
+
if (registryEntry) {
|
|
41
|
+
// Found in registry - use the registry package name
|
|
42
|
+
packageName = registryEntry.package;
|
|
43
|
+
} else if (args.flags.url) {
|
|
44
|
+
// Not in registry but user provided a URL/package name
|
|
45
|
+
packageName = args.flags.url;
|
|
46
|
+
} else if (pluginName.startsWith('@') || pluginName.includes('/')) {
|
|
47
|
+
// Looks like a scoped npm package or git URL - use directly
|
|
48
|
+
packageName = pluginName;
|
|
49
|
+
} else {
|
|
50
|
+
context.errors.throwError(
|
|
51
|
+
404,
|
|
52
|
+
`Plugin "${pluginName}" not found in the registry. Use --source git --url <git-url> to install from a git repository, or --url <package-name> to install a non-registry npm package.`,
|
|
53
|
+
'api'
|
|
54
|
+
);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Check if already installed
|
|
59
|
+
const installed = loader.readPluginsJson();
|
|
60
|
+
if (installed[pluginName]) {
|
|
61
|
+
context.output.info(
|
|
62
|
+
`Plugin "${pluginName}" is already installed (version ${installed[pluginName].version || 'unknown'}). Use "dev api update ${pluginName}" to update.`
|
|
63
|
+
);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Ensure the plugins directory exists with a package.json
|
|
68
|
+
const pluginsDir = loader.PLUGINS_DIR;
|
|
69
|
+
const pkgJsonPath = path.join(pluginsDir, 'package.json');
|
|
70
|
+
|
|
71
|
+
if (!fs.existsSync(pkgJsonPath)) {
|
|
72
|
+
fs.mkdirSync(pluginsDir, { recursive: true });
|
|
73
|
+
fs.writeFileSync(pkgJsonPath, JSON.stringify({
|
|
74
|
+
name: 'devutils-plugins',
|
|
75
|
+
version: '1.0.0',
|
|
76
|
+
private: true,
|
|
77
|
+
description: 'DevUtils CLI plugin packages'
|
|
78
|
+
}, null, 2) + '\n');
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Determine the install target
|
|
82
|
+
const installTarget = args.flags.url || packageName;
|
|
83
|
+
|
|
84
|
+
// Run npm install
|
|
85
|
+
context.output.info(`Installing ${pluginName} (${installTarget})...`);
|
|
86
|
+
const result = await shell.exec(`npm install ${installTarget}`, { cwd: pluginsDir });
|
|
87
|
+
|
|
88
|
+
if (result.exitCode !== 0) {
|
|
89
|
+
context.errors.throwError(
|
|
90
|
+
500,
|
|
91
|
+
`Failed to install plugin "${pluginName}".\n${result.stderr || result.stdout}`,
|
|
92
|
+
'api'
|
|
93
|
+
);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Read the installed version from the plugin's package.json
|
|
98
|
+
let installedVersion = 'unknown';
|
|
99
|
+
try {
|
|
100
|
+
const pluginPkgPath = path.join(pluginsDir, 'node_modules', packageName, 'package.json');
|
|
101
|
+
const pluginPkg = JSON.parse(fs.readFileSync(pluginPkgPath, 'utf8'));
|
|
102
|
+
installedVersion = pluginPkg.version || 'unknown';
|
|
103
|
+
} catch (err) {
|
|
104
|
+
// Could not read version, continue with 'unknown'
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Update plugins.json
|
|
108
|
+
const plugins = loader.readPluginsJson();
|
|
109
|
+
const sourceType = args.flags.source || 'npm';
|
|
110
|
+
plugins[pluginName] = {
|
|
111
|
+
package: packageName,
|
|
112
|
+
version: installedVersion,
|
|
113
|
+
source: sourceType,
|
|
114
|
+
installedAt: new Date().toISOString()
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
// Only store the URL if one was explicitly provided
|
|
118
|
+
if (args.flags.url) {
|
|
119
|
+
plugins[pluginName].url = args.flags.url;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Ensure the parent directory exists for plugins.json
|
|
123
|
+
const pluginsFileDir = path.dirname(loader.PLUGINS_FILE);
|
|
124
|
+
if (!fs.existsSync(pluginsFileDir)) {
|
|
125
|
+
fs.mkdirSync(pluginsFileDir, { recursive: true });
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
fs.writeFileSync(loader.PLUGINS_FILE, JSON.stringify(plugins, null, 2) + '\n');
|
|
129
|
+
|
|
130
|
+
// Validate the plugin contract
|
|
131
|
+
const loadResult = loader.loadPlugin(pluginName);
|
|
132
|
+
if (loadResult.error) {
|
|
133
|
+
context.output.info(
|
|
134
|
+
`Warning: Plugin installed but does not follow the expected contract. It may not work correctly.\n${loadResult.message}`
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Print success
|
|
139
|
+
context.output.info(`Plugin "${pluginName}" installed (v${installedVersion}).`);
|
|
140
|
+
|
|
141
|
+
// Show auth hint if available from the registry entry
|
|
142
|
+
const authService = registryEntry ? registryEntry.auth : null;
|
|
143
|
+
if (authService) {
|
|
144
|
+
context.output.info(`This plugin requires "${authService}" authentication. Run "dev auth login ${authService}" if you haven't already.`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
module.exports = { meta, run };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API service registration.
|
|
3
|
+
* API plugin system — manages plugin installation, removal, and updates.
|
|
4
|
+
* Plugin commands are loaded at runtime from ~/.devutils/plugins/.
|
|
5
|
+
*/
|
|
6
|
+
module.exports = {
|
|
7
|
+
name: 'api',
|
|
8
|
+
description: 'API plugin system',
|
|
9
|
+
commands: {
|
|
10
|
+
list: () => require('./list'),
|
|
11
|
+
enable: () => require('./enable'),
|
|
12
|
+
disable: () => require('./disable'),
|
|
13
|
+
update: () => require('./update'),
|
|
14
|
+
}
|
|
15
|
+
};
|