@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,224 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const os = require('os');
|
|
6
|
+
|
|
7
|
+
const DEVUTILS_DIR = path.join(os.homedir(), '.devutils');
|
|
8
|
+
const CONFIG_FILE = path.join(DEVUTILS_DIR, 'config.json');
|
|
9
|
+
|
|
10
|
+
const meta = {
|
|
11
|
+
description: 'Push the current config profile to the remote backup (repo or gist), or export to a local file.',
|
|
12
|
+
arguments: [],
|
|
13
|
+
flags: [
|
|
14
|
+
{ name: 'file', type: 'string', description: 'Export to a local file instead of remote backup' },
|
|
15
|
+
{ name: 'profile', type: 'string', description: 'Export a specific profile (defaults to the active profile)' }
|
|
16
|
+
]
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* The list of config files that are included in an export.
|
|
21
|
+
* Auth tokens are NOT included -- they are sensitive and should be
|
|
22
|
+
* re-created on new machines via "dev auth login".
|
|
23
|
+
*/
|
|
24
|
+
const FILES_TO_EXPORT = ['config.json', 'aliases.json', 'ai.json', 'plugins.json'];
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Read a config file from ~/.devutils/ if it exists.
|
|
28
|
+
* Returns the parsed content, or null if the file is missing or invalid JSON.
|
|
29
|
+
*
|
|
30
|
+
* @param {string} filename - The filename to read (e.g., 'config.json').
|
|
31
|
+
* @returns {object|null} The parsed JSON content, or null.
|
|
32
|
+
*/
|
|
33
|
+
function readConfigFile(filename) {
|
|
34
|
+
const filePath = path.join(DEVUTILS_DIR, filename);
|
|
35
|
+
if (!fs.existsSync(filePath)) return null;
|
|
36
|
+
try {
|
|
37
|
+
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
38
|
+
} catch {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Write the sync timestamp after a successful export.
|
|
45
|
+
* This is used by future sync-check features to know when the last
|
|
46
|
+
* export or import happened.
|
|
47
|
+
*
|
|
48
|
+
* @param {string} profileName - The profile that was exported.
|
|
49
|
+
*/
|
|
50
|
+
function writeSyncTimestamp(profileName) {
|
|
51
|
+
const syncPath = path.join(DEVUTILS_DIR, 'sync.json');
|
|
52
|
+
const syncData = {
|
|
53
|
+
lastSync: new Date().toISOString(),
|
|
54
|
+
direction: 'export',
|
|
55
|
+
profile: profileName
|
|
56
|
+
};
|
|
57
|
+
fs.writeFileSync(syncPath, JSON.stringify(syncData, null, 2) + '\n');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Run the config export command.
|
|
62
|
+
* Supports two modes:
|
|
63
|
+
* - File mode (--file): Bundles all config files into a single JSON file on disk.
|
|
64
|
+
* - Remote mode (default): Pushes config to a GitHub repo or gist based on backup settings.
|
|
65
|
+
*
|
|
66
|
+
* @param {object} args - Parsed CLI arguments (positional, flags).
|
|
67
|
+
* @param {object} context - CLI context (output, prompt, errors).
|
|
68
|
+
*/
|
|
69
|
+
async function run(args, context) {
|
|
70
|
+
const github = require('../../lib/github');
|
|
71
|
+
|
|
72
|
+
// Read current config to get profile and backup settings
|
|
73
|
+
const config = readConfigFile('config.json');
|
|
74
|
+
const profileName = args.flags.profile || (config && config.profile) || 'default';
|
|
75
|
+
|
|
76
|
+
// --- File export mode ---
|
|
77
|
+
if (args.flags.file) {
|
|
78
|
+
const bundle = {
|
|
79
|
+
exportedAt: new Date().toISOString(),
|
|
80
|
+
profile: profileName,
|
|
81
|
+
config: readConfigFile('config.json'),
|
|
82
|
+
aliases: readConfigFile('aliases.json'),
|
|
83
|
+
ai: readConfigFile('ai.json'),
|
|
84
|
+
plugins: readConfigFile('plugins.json')
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const outputPath = path.resolve(args.flags.file);
|
|
88
|
+
fs.writeFileSync(outputPath, JSON.stringify(bundle, null, 2) + '\n');
|
|
89
|
+
context.output.info(`Config exported to ${outputPath}`);
|
|
90
|
+
|
|
91
|
+
writeSyncTimestamp(profileName);
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// --- Remote export mode ---
|
|
96
|
+
if (!config || !config.backup) {
|
|
97
|
+
context.output.info('No backup storage configured.');
|
|
98
|
+
context.output.info('Run "dev config init" to set up backup storage.');
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const backend = config.backup.backend;
|
|
103
|
+
const location = config.backup.location;
|
|
104
|
+
|
|
105
|
+
if (backend === 'repo') {
|
|
106
|
+
// Check gh authentication
|
|
107
|
+
const isAuth = await github.isAuthenticated();
|
|
108
|
+
if (!isAuth) {
|
|
109
|
+
context.output.info('Not authenticated with GitHub. Run: gh auth login');
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const cacheDir = path.join(DEVUTILS_DIR, 'cache');
|
|
114
|
+
const repoDir = path.join(cacheDir, 'config-backup');
|
|
115
|
+
fs.mkdirSync(cacheDir, { recursive: true });
|
|
116
|
+
|
|
117
|
+
// Clone or pull the repo
|
|
118
|
+
if (fs.existsSync(path.join(repoDir, '.git'))) {
|
|
119
|
+
// Already cloned -- pull latest
|
|
120
|
+
const pullResult = await github.pullRepo(repoDir);
|
|
121
|
+
if (!pullResult.success) {
|
|
122
|
+
context.output.error('Failed to pull latest config: ' + pullResult.error);
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
} else {
|
|
126
|
+
// First time -- need repo location
|
|
127
|
+
if (!location) {
|
|
128
|
+
context.output.info('No backup repository configured.');
|
|
129
|
+
context.output.info('Run "dev config init --force" to set up a backup repository.');
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const cloneResult = await github.cloneRepo(location, repoDir);
|
|
134
|
+
if (!cloneResult.success) {
|
|
135
|
+
context.output.error('Failed to clone backup repo: ' + cloneResult.error);
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Copy config files into the profile directory
|
|
141
|
+
const profileDir = path.join(repoDir, 'profiles', profileName);
|
|
142
|
+
fs.mkdirSync(profileDir, { recursive: true });
|
|
143
|
+
|
|
144
|
+
for (const filename of FILES_TO_EXPORT) {
|
|
145
|
+
const sourcePath = path.join(DEVUTILS_DIR, filename);
|
|
146
|
+
if (fs.existsSync(sourcePath)) {
|
|
147
|
+
fs.copyFileSync(sourcePath, path.join(profileDir, filename));
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Push
|
|
152
|
+
const pushResult = await github.pushRepo(
|
|
153
|
+
repoDir,
|
|
154
|
+
`Update profile "${profileName}" - ${new Date().toISOString()}`
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
if (!pushResult.success) {
|
|
158
|
+
context.output.error('Failed to push config: ' + pushResult.error);
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
context.output.info(`Config exported to repo (profile: ${profileName}).`);
|
|
163
|
+
writeSyncTimestamp(profileName);
|
|
164
|
+
|
|
165
|
+
} else if (backend === 'gist') {
|
|
166
|
+
// Check gh authentication
|
|
167
|
+
const isAuth = await github.isAuthenticated();
|
|
168
|
+
if (!isAuth) {
|
|
169
|
+
context.output.info('Not authenticated with GitHub. Run: gh auth login');
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Bundle config files into a single JSON for the gist
|
|
174
|
+
const bundle = {
|
|
175
|
+
config: readConfigFile('config.json'),
|
|
176
|
+
aliases: readConfigFile('aliases.json'),
|
|
177
|
+
ai: readConfigFile('ai.json'),
|
|
178
|
+
plugins: readConfigFile('plugins.json')
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
const filename = `${profileName}.json`;
|
|
182
|
+
const content = JSON.stringify(bundle, null, 2);
|
|
183
|
+
|
|
184
|
+
if (location) {
|
|
185
|
+
// Update existing gist
|
|
186
|
+
const result = await github.updateGist(location, { [filename]: content });
|
|
187
|
+
if (!result.success) {
|
|
188
|
+
context.output.error('Failed to update gist: ' + result.error);
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
context.output.info(`Config exported to gist (profile: ${profileName}).`);
|
|
192
|
+
} else {
|
|
193
|
+
// Create new gist
|
|
194
|
+
const result = await github.createGist(
|
|
195
|
+
{ [filename]: content },
|
|
196
|
+
'DevUtils CLI configuration backup',
|
|
197
|
+
true
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
if (!result.success) {
|
|
201
|
+
context.output.error('Failed to create gist: ' + result.error);
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Save the gist ID back to config so future exports update the same gist
|
|
206
|
+
config.backup.location = result.id;
|
|
207
|
+
fs.writeFileSync(
|
|
208
|
+
CONFIG_FILE,
|
|
209
|
+
JSON.stringify(config, null, 2) + '\n'
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
context.output.info(`Config exported to new gist (profile: ${profileName}).`);
|
|
213
|
+
context.output.info(`Gist URL: ${result.url}`);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
writeSyncTimestamp(profileName);
|
|
217
|
+
|
|
218
|
+
} else {
|
|
219
|
+
context.output.info(`Unknown backup backend: ${backend}`);
|
|
220
|
+
context.output.info('Run "dev config init --force" to reconfigure backup storage.');
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
module.exports = { meta, run };
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const os = require('os');
|
|
6
|
+
|
|
7
|
+
const CONFIG_FILE = path.join(os.homedir(), '.devutils', 'config.json');
|
|
8
|
+
|
|
9
|
+
const meta = {
|
|
10
|
+
description: 'Read a specific config value by dot-notation key.',
|
|
11
|
+
arguments: [
|
|
12
|
+
{ name: 'key', required: true, description: 'Dot-notation path to the config value (e.g., user.email, defaults.license)' },
|
|
13
|
+
],
|
|
14
|
+
flags: [],
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
async function run(args, context) {
|
|
18
|
+
const key = args.positional[0];
|
|
19
|
+
|
|
20
|
+
if (!key) {
|
|
21
|
+
context.errors.throwError(400, 'Missing required argument: <key>. Example: dev config get user.email', 'config');
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (!fs.existsSync(CONFIG_FILE)) {
|
|
26
|
+
context.errors.throwError(404, 'Config not found. Run "dev config init" first.', 'config');
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const raw = fs.readFileSync(CONFIG_FILE, 'utf8');
|
|
31
|
+
const config = JSON.parse(raw);
|
|
32
|
+
|
|
33
|
+
// Resolve dot-notation key
|
|
34
|
+
const parts = key.split('.');
|
|
35
|
+
let value = config;
|
|
36
|
+
|
|
37
|
+
for (const part of parts) {
|
|
38
|
+
if (value === null || value === undefined || typeof value !== 'object') {
|
|
39
|
+
context.errors.throwError(404, `Key "${key}" not found in config.`, 'config');
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
if (!(part in value)) {
|
|
43
|
+
context.errors.throwError(404, `Key "${key}" not found in config.`, 'config');
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
value = value[part];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
context.output.out(value);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
module.exports = { meta, run };
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const os = require('os');
|
|
6
|
+
|
|
7
|
+
const DEVUTILS_DIR = path.join(os.homedir(), '.devutils');
|
|
8
|
+
const CONFIG_FILE = path.join(DEVUTILS_DIR, 'config.json');
|
|
9
|
+
|
|
10
|
+
const meta = {
|
|
11
|
+
description: 'Pull config from remote backup and apply locally, or import from a local file.',
|
|
12
|
+
arguments: [],
|
|
13
|
+
flags: [
|
|
14
|
+
{ name: 'file', type: 'string', description: 'Import from a local file instead of remote backup' },
|
|
15
|
+
{ name: 'profile', type: 'string', description: 'Import a specific profile from the backup' }
|
|
16
|
+
]
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* The config file keys and their mapping in the export bundle.
|
|
21
|
+
* Each entry maps a local filename to the bundle property name.
|
|
22
|
+
*/
|
|
23
|
+
const FILES_TO_IMPORT = {
|
|
24
|
+
'config.json': 'config',
|
|
25
|
+
'aliases.json': 'aliases',
|
|
26
|
+
'ai.json': 'ai',
|
|
27
|
+
'plugins.json': 'plugins'
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Read a config file from ~/.devutils/ if it exists.
|
|
32
|
+
* Returns the parsed content, or null if the file is missing or invalid JSON.
|
|
33
|
+
*
|
|
34
|
+
* @param {string} filename - The filename to read (e.g., 'config.json').
|
|
35
|
+
* @returns {object|null} The parsed JSON content, or null.
|
|
36
|
+
*/
|
|
37
|
+
function readConfigFile(filename) {
|
|
38
|
+
const filePath = path.join(DEVUTILS_DIR, filename);
|
|
39
|
+
if (!fs.existsSync(filePath)) return null;
|
|
40
|
+
try {
|
|
41
|
+
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
42
|
+
} catch {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Write imported config data to ~/.devutils/.
|
|
49
|
+
* Only writes files that have data (skips null entries).
|
|
50
|
+
*
|
|
51
|
+
* @param {object} bundle - The import bundle with config, aliases, ai, plugins keys.
|
|
52
|
+
* @returns {number} The number of files written.
|
|
53
|
+
*/
|
|
54
|
+
function writeImportedFiles(bundle) {
|
|
55
|
+
fs.mkdirSync(DEVUTILS_DIR, { recursive: true });
|
|
56
|
+
|
|
57
|
+
let imported = 0;
|
|
58
|
+
for (const [filename, key] of Object.entries(FILES_TO_IMPORT)) {
|
|
59
|
+
const data = bundle[key];
|
|
60
|
+
if (data) {
|
|
61
|
+
fs.writeFileSync(
|
|
62
|
+
path.join(DEVUTILS_DIR, filename),
|
|
63
|
+
JSON.stringify(data, null, 2) + '\n'
|
|
64
|
+
);
|
|
65
|
+
imported++;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return imported;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Write the sync timestamp after a successful import.
|
|
74
|
+
*
|
|
75
|
+
* @param {string} profileName - The profile that was imported.
|
|
76
|
+
*/
|
|
77
|
+
function writeSyncTimestamp(profileName) {
|
|
78
|
+
const syncPath = path.join(DEVUTILS_DIR, 'sync.json');
|
|
79
|
+
const syncData = {
|
|
80
|
+
lastSync: new Date().toISOString(),
|
|
81
|
+
direction: 'import',
|
|
82
|
+
profile: profileName
|
|
83
|
+
};
|
|
84
|
+
fs.writeFileSync(syncPath, JSON.stringify(syncData, null, 2) + '\n');
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Print the next-steps reminder after a successful import.
|
|
89
|
+
*
|
|
90
|
+
* @param {object} context - CLI context (output).
|
|
91
|
+
*/
|
|
92
|
+
function printNextSteps(context) {
|
|
93
|
+
context.output.info('');
|
|
94
|
+
context.output.info('Next steps:');
|
|
95
|
+
context.output.info(' 1. Run "dev alias sync" to rebuild alias wrapper scripts');
|
|
96
|
+
context.output.info(' 2. Run "dev status" to verify your configuration');
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Run the config import command.
|
|
101
|
+
* Supports two modes:
|
|
102
|
+
* - File mode (--file): Reads a JSON export file from disk and writes config locally.
|
|
103
|
+
* - Remote mode (default): Pulls config from a GitHub repo or gist based on backup settings.
|
|
104
|
+
*
|
|
105
|
+
* @param {object} args - Parsed CLI arguments (positional, flags).
|
|
106
|
+
* @param {object} context - CLI context (output, prompt, errors).
|
|
107
|
+
*/
|
|
108
|
+
async function run(args, context) {
|
|
109
|
+
const github = require('../../lib/github');
|
|
110
|
+
|
|
111
|
+
// --- File import mode ---
|
|
112
|
+
if (args.flags.file) {
|
|
113
|
+
const filePath = path.resolve(args.flags.file);
|
|
114
|
+
if (!fs.existsSync(filePath)) {
|
|
115
|
+
context.output.error(`File not found: ${filePath}`);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
let bundle;
|
|
120
|
+
try {
|
|
121
|
+
bundle = JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
122
|
+
} catch (err) {
|
|
123
|
+
context.output.error('Invalid JSON file: ' + err.message);
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Validate the bundle has expected fields
|
|
128
|
+
if (!bundle.config) {
|
|
129
|
+
context.output.error('Invalid export file: missing config data.');
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const imported = writeImportedFiles(bundle);
|
|
134
|
+
const profileName = bundle.profile || 'default';
|
|
135
|
+
|
|
136
|
+
context.output.info(`Config imported from ${filePath} (${imported} file(s)).`);
|
|
137
|
+
writeSyncTimestamp(profileName);
|
|
138
|
+
printNextSteps(context);
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// --- Remote import mode ---
|
|
143
|
+
// Read current config to get backup settings
|
|
144
|
+
const config = readConfigFile('config.json');
|
|
145
|
+
if (!config || !config.backup) {
|
|
146
|
+
context.output.info('No backup storage configured.');
|
|
147
|
+
context.output.info('Run "dev config init" to set up backup storage, or use --file to import from a local file.');
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const backend = config.backup.backend;
|
|
152
|
+
const location = config.backup.location;
|
|
153
|
+
|
|
154
|
+
if (backend === 'repo') {
|
|
155
|
+
// Check gh authentication
|
|
156
|
+
const isAuth = await github.isAuthenticated();
|
|
157
|
+
if (!isAuth) {
|
|
158
|
+
context.output.info('Not authenticated with GitHub. Run: gh auth login');
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const cacheDir = path.join(DEVUTILS_DIR, 'cache');
|
|
163
|
+
const repoDir = path.join(cacheDir, 'config-backup');
|
|
164
|
+
fs.mkdirSync(cacheDir, { recursive: true });
|
|
165
|
+
|
|
166
|
+
// Clone or pull the repo
|
|
167
|
+
if (fs.existsSync(path.join(repoDir, '.git'))) {
|
|
168
|
+
const pullResult = await github.pullRepo(repoDir);
|
|
169
|
+
if (!pullResult.success) {
|
|
170
|
+
context.output.error('Failed to pull latest config: ' + pullResult.error);
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
} else {
|
|
174
|
+
if (!location) {
|
|
175
|
+
context.output.info('No backup repository configured.');
|
|
176
|
+
context.output.info('Run "dev config init --force" to set up a backup repository.');
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const cloneResult = await github.cloneRepo(location, repoDir);
|
|
181
|
+
if (!cloneResult.success) {
|
|
182
|
+
context.output.error('Failed to clone backup repo: ' + cloneResult.error);
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// List available profiles
|
|
188
|
+
const profilesDir = path.join(repoDir, 'profiles');
|
|
189
|
+
if (!fs.existsSync(profilesDir)) {
|
|
190
|
+
context.output.info('No profiles found in backup repository.');
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const profiles = fs.readdirSync(profilesDir, { withFileTypes: true })
|
|
195
|
+
.filter(d => d.isDirectory())
|
|
196
|
+
.map(d => d.name);
|
|
197
|
+
|
|
198
|
+
if (profiles.length === 0) {
|
|
199
|
+
context.output.info('No profiles found in backup repository.');
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Select profile
|
|
204
|
+
let profileName = args.flags.profile;
|
|
205
|
+
if (!profileName) {
|
|
206
|
+
if (profiles.length === 1) {
|
|
207
|
+
profileName = profiles[0];
|
|
208
|
+
} else {
|
|
209
|
+
// Let the user pick
|
|
210
|
+
profileName = await context.prompt.choose(
|
|
211
|
+
'Which profile do you want to import?',
|
|
212
|
+
profiles
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const profileDir = path.join(profilesDir, profileName);
|
|
218
|
+
if (!fs.existsSync(profileDir)) {
|
|
219
|
+
context.output.info(`Profile "${profileName}" not found in backup.`);
|
|
220
|
+
context.output.info(`Available profiles: ${profiles.join(', ')}`);
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Copy config files from the profile directory into ~/.devutils/
|
|
225
|
+
const filesToCopy = ['config.json', 'aliases.json', 'ai.json', 'plugins.json'];
|
|
226
|
+
let imported = 0;
|
|
227
|
+
|
|
228
|
+
for (const filename of filesToCopy) {
|
|
229
|
+
const sourcePath = path.join(profileDir, filename);
|
|
230
|
+
if (fs.existsSync(sourcePath)) {
|
|
231
|
+
fs.copyFileSync(sourcePath, path.join(DEVUTILS_DIR, filename));
|
|
232
|
+
imported++;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
context.output.info(`Imported ${imported} config file(s) from profile "${profileName}".`);
|
|
237
|
+
writeSyncTimestamp(profileName);
|
|
238
|
+
printNextSteps(context);
|
|
239
|
+
|
|
240
|
+
} else if (backend === 'gist') {
|
|
241
|
+
if (!location) {
|
|
242
|
+
context.output.info('No backup gist configured.');
|
|
243
|
+
context.output.info('Run "dev config init --force" to set up a backup gist.');
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Check gh authentication
|
|
248
|
+
const isAuth = await github.isAuthenticated();
|
|
249
|
+
if (!isAuth) {
|
|
250
|
+
context.output.info('Not authenticated with GitHub. Run: gh auth login');
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
const gistResult = await github.getGist(location);
|
|
255
|
+
if (!gistResult.success) {
|
|
256
|
+
context.output.error('Failed to read gist: ' + gistResult.error);
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// List available profiles (each is a .json file in the gist)
|
|
261
|
+
const profileFiles = Object.keys(gistResult.files).filter(f => f.endsWith('.json'));
|
|
262
|
+
const profiles = profileFiles.map(f => f.replace('.json', ''));
|
|
263
|
+
|
|
264
|
+
if (profiles.length === 0) {
|
|
265
|
+
context.output.info('No profiles found in backup gist.');
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
let profileName = args.flags.profile;
|
|
270
|
+
if (!profileName) {
|
|
271
|
+
if (profiles.length === 1) {
|
|
272
|
+
profileName = profiles[0];
|
|
273
|
+
} else {
|
|
274
|
+
profileName = await context.prompt.choose(
|
|
275
|
+
'Which profile do you want to import?',
|
|
276
|
+
profiles
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
const filename = profileName + '.json';
|
|
282
|
+
const content = gistResult.files[filename];
|
|
283
|
+
if (!content) {
|
|
284
|
+
context.output.info(`Profile "${profileName}" not found in gist.`);
|
|
285
|
+
context.output.info(`Available profiles: ${profiles.join(', ')}`);
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
let bundle;
|
|
290
|
+
try {
|
|
291
|
+
bundle = JSON.parse(content);
|
|
292
|
+
} catch {
|
|
293
|
+
context.output.error('Failed to parse profile data from gist.');
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
const imported = writeImportedFiles(bundle);
|
|
298
|
+
context.output.info(`Imported ${imported} config file(s) from profile "${profileName}".`);
|
|
299
|
+
writeSyncTimestamp(profileName);
|
|
300
|
+
printNextSteps(context);
|
|
301
|
+
|
|
302
|
+
} else {
|
|
303
|
+
context.output.info(`Unknown backup backend: ${backend}`);
|
|
304
|
+
context.output.info('Run "dev config init --force" to reconfigure backup storage.');
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
module.exports = { meta, run };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Config service registration.
|
|
3
|
+
* User configuration and onboarding.
|
|
4
|
+
*/
|
|
5
|
+
module.exports = {
|
|
6
|
+
name: 'config',
|
|
7
|
+
description: 'User configuration and onboarding',
|
|
8
|
+
commands: {
|
|
9
|
+
init: () => require('./init'),
|
|
10
|
+
show: () => require('./show'),
|
|
11
|
+
get: () => require('./get'),
|
|
12
|
+
set: () => require('./set'),
|
|
13
|
+
reset: () => require('./reset'),
|
|
14
|
+
export: () => require('./export'),
|
|
15
|
+
import: () => require('./import'),
|
|
16
|
+
}
|
|
17
|
+
};
|