@aikidosec/safe-chain 0.0.4-connect-timeout-beta

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.
Files changed (94) hide show
  1. package/LICENSE +674 -0
  2. package/README.md +257 -0
  3. package/bin/aikido-bun.js +14 -0
  4. package/bin/aikido-bunx.js +14 -0
  5. package/bin/aikido-npm.js +14 -0
  6. package/bin/aikido-npx.js +14 -0
  7. package/bin/aikido-pip.js +20 -0
  8. package/bin/aikido-pip3.js +21 -0
  9. package/bin/aikido-pnpm.js +14 -0
  10. package/bin/aikido-pnpx.js +14 -0
  11. package/bin/aikido-python.js +30 -0
  12. package/bin/aikido-python3.js +30 -0
  13. package/bin/aikido-uv.js +16 -0
  14. package/bin/aikido-yarn.js +14 -0
  15. package/bin/safe-chain.js +190 -0
  16. package/docs/banner.svg +151 -0
  17. package/docs/npm-to-binary-migration.md +89 -0
  18. package/docs/safe-package-manager-demo.gif +0 -0
  19. package/docs/safe-package-manager-demo.png +0 -0
  20. package/docs/shell-integration.md +149 -0
  21. package/package.json +68 -0
  22. package/src/api/aikido.js +54 -0
  23. package/src/api/npmApi.js +71 -0
  24. package/src/config/cliArguments.js +138 -0
  25. package/src/config/configFile.js +192 -0
  26. package/src/config/environmentVariables.js +7 -0
  27. package/src/config/settings.js +100 -0
  28. package/src/environment/environment.js +14 -0
  29. package/src/environment/userInteraction.js +122 -0
  30. package/src/main.js +104 -0
  31. package/src/packagemanager/_shared/matchesCommand.js +18 -0
  32. package/src/packagemanager/bun/createBunPackageManager.js +53 -0
  33. package/src/packagemanager/currentPackageManager.js +72 -0
  34. package/src/packagemanager/npm/createPackageManager.js +72 -0
  35. package/src/packagemanager/npm/dependencyScanner/commandArgumentScanner.js +74 -0
  36. package/src/packagemanager/npm/dependencyScanner/nullScanner.js +9 -0
  37. package/src/packagemanager/npm/parsing/parsePackagesFromInstallArgs.js +144 -0
  38. package/src/packagemanager/npm/runNpmCommand.js +25 -0
  39. package/src/packagemanager/npm/utils/abbrevs-generated.js +359 -0
  40. package/src/packagemanager/npm/utils/cmd-list.js +174 -0
  41. package/src/packagemanager/npm/utils/npmCommands.js +34 -0
  42. package/src/packagemanager/npx/createPackageManager.js +15 -0
  43. package/src/packagemanager/npx/dependencyScanner/commandArgumentScanner.js +43 -0
  44. package/src/packagemanager/npx/parsing/parsePackagesFromArguments.js +130 -0
  45. package/src/packagemanager/npx/runNpxCommand.js +25 -0
  46. package/src/packagemanager/pip/createPackageManager.js +21 -0
  47. package/src/packagemanager/pip/pipSettings.js +30 -0
  48. package/src/packagemanager/pip/runPipCommand.js +175 -0
  49. package/src/packagemanager/pnpm/createPackageManager.js +57 -0
  50. package/src/packagemanager/pnpm/dependencyScanner/commandArgumentScanner.js +35 -0
  51. package/src/packagemanager/pnpm/parsing/parsePackagesFromArguments.js +109 -0
  52. package/src/packagemanager/pnpm/runPnpmCommand.js +36 -0
  53. package/src/packagemanager/uv/createUvPackageManager.js +18 -0
  54. package/src/packagemanager/uv/runUvCommand.js +71 -0
  55. package/src/packagemanager/yarn/createPackageManager.js +41 -0
  56. package/src/packagemanager/yarn/dependencyScanner/commandArgumentScanner.js +35 -0
  57. package/src/packagemanager/yarn/parsing/parsePackagesFromArguments.js +128 -0
  58. package/src/packagemanager/yarn/runYarnCommand.js +41 -0
  59. package/src/registryProxy/certBundle.js +95 -0
  60. package/src/registryProxy/certUtils.js +128 -0
  61. package/src/registryProxy/http-utils.js +17 -0
  62. package/src/registryProxy/interceptors/createInterceptorForEcoSystem.js +25 -0
  63. package/src/registryProxy/interceptors/interceptorBuilder.js +140 -0
  64. package/src/registryProxy/interceptors/npm/modifyNpmInfo.js +177 -0
  65. package/src/registryProxy/interceptors/npm/npmInterceptor.js +47 -0
  66. package/src/registryProxy/interceptors/npm/parseNpmPackageUrl.js +43 -0
  67. package/src/registryProxy/interceptors/pipInterceptor.js +115 -0
  68. package/src/registryProxy/mitmRequestHandler.js +231 -0
  69. package/src/registryProxy/plainHttpProxy.js +95 -0
  70. package/src/registryProxy/registryProxy.js +184 -0
  71. package/src/registryProxy/tunnelRequestHandler.js +180 -0
  72. package/src/scanning/audit/index.js +129 -0
  73. package/src/scanning/index.js +82 -0
  74. package/src/scanning/malwareDatabase.js +131 -0
  75. package/src/shell-integration/helpers.js +213 -0
  76. package/src/shell-integration/path-wrappers/templates/unix-wrapper.template.sh +22 -0
  77. package/src/shell-integration/path-wrappers/templates/windows-wrapper.template.cmd +24 -0
  78. package/src/shell-integration/setup-ci.js +170 -0
  79. package/src/shell-integration/setup.js +127 -0
  80. package/src/shell-integration/shellDetection.js +37 -0
  81. package/src/shell-integration/startup-scripts/include-python/init-fish.fish +94 -0
  82. package/src/shell-integration/startup-scripts/include-python/init-posix.sh +81 -0
  83. package/src/shell-integration/startup-scripts/include-python/init-pwsh.ps1 +115 -0
  84. package/src/shell-integration/startup-scripts/init-fish.fish +71 -0
  85. package/src/shell-integration/startup-scripts/init-posix.sh +58 -0
  86. package/src/shell-integration/startup-scripts/init-pwsh.ps1 +92 -0
  87. package/src/shell-integration/supported-shells/bash.js +134 -0
  88. package/src/shell-integration/supported-shells/fish.js +77 -0
  89. package/src/shell-integration/supported-shells/powershell.js +73 -0
  90. package/src/shell-integration/supported-shells/windowsPowershell.js +73 -0
  91. package/src/shell-integration/supported-shells/zsh.js +74 -0
  92. package/src/shell-integration/teardown.js +64 -0
  93. package/src/utils/safeSpawn.js +137 -0
  94. package/tsconfig.json +21 -0
@@ -0,0 +1,58 @@
1
+ export PATH="$PATH:$HOME/.safe-chain/bin"
2
+
3
+ function npx() {
4
+ wrapSafeChainCommand "npx" "$@"
5
+ }
6
+
7
+ function yarn() {
8
+ wrapSafeChainCommand "yarn" "$@"
9
+ }
10
+
11
+ function pnpm() {
12
+ wrapSafeChainCommand "pnpm" "$@"
13
+ }
14
+
15
+ function pnpx() {
16
+ wrapSafeChainCommand "pnpx" "$@"
17
+ }
18
+
19
+ function bun() {
20
+ wrapSafeChainCommand "bun" "$@"
21
+ }
22
+
23
+ function bunx() {
24
+ wrapSafeChainCommand "bunx" "$@"
25
+ }
26
+
27
+ function npm() {
28
+ if [[ "$1" == "-v" || "$1" == "--version" ]] && [[ $# -eq 1 ]]; then
29
+ # If args is just -v or --version and nothing else, just run the npm version command
30
+ # This is because nvm uses this to check the version of npm
31
+ command npm "$@"
32
+ return
33
+ fi
34
+
35
+ wrapSafeChainCommand "npm" "$@"
36
+ }
37
+
38
+ function printSafeChainWarning() {
39
+ # \033[43;30m is used to set the background color to yellow and text color to black
40
+ # \033[0m is used to reset the text formatting
41
+ printf "\033[43;30mWarning:\033[0m safe-chain is not available to protect you from installing malware. %s will run without it.\n" "$1"
42
+ # \033[36m is used to set the text color to cyan
43
+ printf "Install safe-chain by using \033[36mnpm install -g @aikidosec/safe-chain\033[0m.\n"
44
+ }
45
+
46
+ function wrapSafeChainCommand() {
47
+ local original_cmd="$1"
48
+
49
+ if command -v safe-chain > /dev/null 2>&1; then
50
+ # If the aikido command is available, just run it with the provided arguments
51
+ safe-chain "$@"
52
+ else
53
+ # If the aikido command is not available, print a warning and run the original command
54
+ printSafeChainWarning "$original_cmd"
55
+
56
+ command "$original_cmd" "$@"
57
+ fi
58
+ }
@@ -0,0 +1,92 @@
1
+ # Use cross-platform path separator (: on Unix, ; on Windows)
2
+ $pathSeparator = if ($IsWindows) { ';' } else { ':' }
3
+ $safeChainBin = Join-Path $HOME '.safe-chain' 'bin'
4
+ $env:PATH = "$env:PATH$pathSeparator$safeChainBin"
5
+
6
+ function npx {
7
+ Invoke-WrappedCommand "npx" $args
8
+ }
9
+
10
+ function yarn {
11
+ Invoke-WrappedCommand "yarn" $args
12
+ }
13
+
14
+ function pnpm {
15
+ Invoke-WrappedCommand "pnpm" $args
16
+ }
17
+
18
+ function pnpx {
19
+ Invoke-WrappedCommand "pnpx" $args
20
+ }
21
+
22
+ function bun {
23
+ Invoke-WrappedCommand "bun" $args
24
+ }
25
+
26
+ function bunx {
27
+ Invoke-WrappedCommand "bunx" $args
28
+ }
29
+
30
+ function npm {
31
+ # If args is just -v or --version and nothing else, just run the npm version command
32
+ # This is because nvm uses this to check the version of npm
33
+ if (($args.Length -eq 1) -and (($args[0] -eq "-v") -or ($args[0] -eq "--version"))) {
34
+ Invoke-RealCommand "npm" $args
35
+ return
36
+ }
37
+
38
+ Invoke-WrappedCommand "npm" $args
39
+ }
40
+
41
+ function Write-SafeChainWarning {
42
+ param([string]$Command)
43
+
44
+ # PowerShell equivalent of ANSI color codes: yellow background, black text for "Warning:"
45
+ Write-Host "Warning:" -BackgroundColor Yellow -ForegroundColor Black -NoNewline
46
+ Write-Host " safe-chain is not available to protect you from installing malware. $Command will run without it."
47
+
48
+ # Cyan text for the install command
49
+ Write-Host "Install safe-chain by using " -NoNewline
50
+ Write-Host "npm install -g @aikidosec/safe-chain" -ForegroundColor Cyan -NoNewline
51
+ Write-Host "."
52
+ }
53
+
54
+ function Test-CommandAvailable {
55
+ param([string]$Command)
56
+
57
+ try {
58
+ Get-Command $Command -ErrorAction Stop | Out-Null
59
+ return $true
60
+ }
61
+ catch {
62
+ return $false
63
+ }
64
+ }
65
+
66
+ function Invoke-RealCommand {
67
+ param(
68
+ [string]$Command,
69
+ [string[]]$Arguments
70
+ )
71
+
72
+ # Find the real executable to avoid calling our wrapped functions
73
+ $realCommand = Get-Command -Name $Command -CommandType Application | Select-Object -First 1
74
+ if ($realCommand) {
75
+ & $realCommand.Source @Arguments
76
+ }
77
+ }
78
+
79
+ function Invoke-WrappedCommand {
80
+ param(
81
+ [string]$OriginalCmd,
82
+ [string[]]$Arguments
83
+ )
84
+
85
+ if (Test-CommandAvailable "safe-chain") {
86
+ & safe-chain $OriginalCmd @Arguments
87
+ }
88
+ else {
89
+ Write-SafeChainWarning $OriginalCmd
90
+ Invoke-RealCommand $OriginalCmd $Arguments
91
+ }
92
+ }
@@ -0,0 +1,134 @@
1
+ import {
2
+ addLineToFile,
3
+ doesExecutableExistOnSystem,
4
+ removeLinesMatchingPattern,
5
+ } from "../helpers.js";
6
+ import { execSync, spawnSync } from "child_process";
7
+ import * as os from "os";
8
+
9
+ const shellName = "Bash";
10
+ const executableName = "bash";
11
+ const startupFileCommand = "echo ~/.bashrc";
12
+ const eol = "\n"; // When bash runs on Windows (e.g., Git Bash or WSL), it expects LF line endings.
13
+
14
+ function isInstalled() {
15
+ return doesExecutableExistOnSystem(executableName);
16
+ }
17
+
18
+ /**
19
+ * @param {import("../helpers.js").AikidoTool[]} tools
20
+ *
21
+ * @returns {boolean}
22
+ */
23
+ function teardown(tools) {
24
+ const startupFile = getStartupFile();
25
+
26
+ for (const { tool } of tools) {
27
+ // Remove any existing alias for the tool
28
+ removeLinesMatchingPattern(
29
+ startupFile,
30
+ new RegExp(`^alias\\s+${tool}=`),
31
+ eol
32
+ );
33
+ }
34
+
35
+ // Removes the line that sources the safe-chain bash initialization script (~/.aikido/scripts/init-posix.sh)
36
+ removeLinesMatchingPattern(
37
+ startupFile,
38
+ /^source\s+~\/\.safe-chain\/scripts\/init-posix\.sh/,
39
+ eol
40
+ );
41
+
42
+ return true;
43
+ }
44
+
45
+ function setup() {
46
+ const startupFile = getStartupFile();
47
+
48
+ addLineToFile(
49
+ startupFile,
50
+ `source ~/.safe-chain/scripts/init-posix.sh # Safe-chain bash initialization script`,
51
+ eol
52
+ );
53
+
54
+ return true;
55
+ }
56
+
57
+ function getStartupFile() {
58
+ try {
59
+ var path = execSync(startupFileCommand, {
60
+ encoding: "utf8",
61
+ shell: executableName,
62
+ }).trim();
63
+
64
+ return windowsFixPath(path);
65
+ } catch (/** @type {any} */ error) {
66
+ throw new Error(
67
+ `Command failed: ${startupFileCommand}. Error: ${error.message}`
68
+ );
69
+ }
70
+ }
71
+
72
+ /**
73
+ * @param {string} path
74
+ *
75
+ * @returns {string}
76
+ */
77
+ function windowsFixPath(path) {
78
+ try {
79
+ if (os.platform() !== "win32") {
80
+ return path;
81
+ }
82
+
83
+ // On windows cygwin bash, paths are returned in format /c/user/..., but we need it in format C:\user\...
84
+ // To convert, the cygpath -w command can be used to convert to the desired format.
85
+ // Cygpath only exists on Cygwin, so we first check if the command is available.
86
+ // If it is, we use it to convert the path.
87
+ if (hasCygpath()) {
88
+ return cygpathw(path);
89
+ }
90
+
91
+ return path;
92
+ } catch {
93
+ return path;
94
+ }
95
+ }
96
+
97
+ function hasCygpath() {
98
+ try {
99
+ var result = spawnSync("where", ["cygpath"], { shell: executableName });
100
+ return result.status === 0;
101
+ } catch {
102
+ return false;
103
+ }
104
+ }
105
+
106
+ /**
107
+ * @param {string} path
108
+ *
109
+ * @returns {string}
110
+ */
111
+ function cygpathw(path) {
112
+ try {
113
+ var result = spawnSync("cygpath", ["-w", path], {
114
+ encoding: "utf8",
115
+ shell: executableName,
116
+ });
117
+ if (result.status === 0) {
118
+ return result.stdout.trim();
119
+ }
120
+ return path;
121
+ } catch {
122
+ return path;
123
+ }
124
+ }
125
+
126
+ /**
127
+ * @type {import("../shellDetection.js").Shell}
128
+ */
129
+ export default {
130
+ name: shellName,
131
+ isInstalled,
132
+ setup,
133
+ teardown,
134
+ };
@@ -0,0 +1,77 @@
1
+ import {
2
+ addLineToFile,
3
+ doesExecutableExistOnSystem,
4
+ removeLinesMatchingPattern,
5
+ } from "../helpers.js";
6
+ import { execSync } from "child_process";
7
+
8
+ const shellName = "Fish";
9
+ const executableName = "fish";
10
+ const startupFileCommand = "echo ~/.config/fish/config.fish";
11
+ const eol = "\n"; // When fish runs on Windows (e.g., Git Bash or WSL), it expects LF line endings.
12
+
13
+ function isInstalled() {
14
+ return doesExecutableExistOnSystem(executableName);
15
+ }
16
+
17
+ /**
18
+ * @param {import("../helpers.js").AikidoTool[]} tools
19
+ *
20
+ * @returns {boolean}
21
+ */
22
+ function teardown(tools) {
23
+ const startupFile = getStartupFile();
24
+
25
+ for (const { tool } of tools) {
26
+ // Remove any existing alias for the tool
27
+ removeLinesMatchingPattern(
28
+ startupFile,
29
+ new RegExp(`^alias\\s+${tool}\\s+`),
30
+ eol
31
+ );
32
+ }
33
+
34
+ // Removes the line that sources the safe-chain fish initialization script (~/.safe-chain/scripts/init-fish.fish)
35
+ removeLinesMatchingPattern(
36
+ startupFile,
37
+ /^source\s+~\/\.safe-chain\/scripts\/init-fish\.fish/,
38
+ eol
39
+ );
40
+
41
+ return true;
42
+ }
43
+
44
+ function setup() {
45
+ const startupFile = getStartupFile();
46
+
47
+ addLineToFile(
48
+ startupFile,
49
+ `source ~/.safe-chain/scripts/init-fish.fish # Safe-chain Fish initialization script`,
50
+ eol
51
+ );
52
+
53
+ return true;
54
+ }
55
+
56
+ function getStartupFile() {
57
+ try {
58
+ return execSync(startupFileCommand, {
59
+ encoding: "utf8",
60
+ shell: executableName,
61
+ }).trim();
62
+ } catch (/** @type {any} */ error) {
63
+ throw new Error(
64
+ `Command failed: ${startupFileCommand}. Error: ${error.message}`
65
+ );
66
+ }
67
+ }
68
+
69
+ /**
70
+ * @type {import("../shellDetection.js").Shell}
71
+ */
72
+ export default {
73
+ name: shellName,
74
+ isInstalled,
75
+ setup,
76
+ teardown,
77
+ };
@@ -0,0 +1,73 @@
1
+ import {
2
+ addLineToFile,
3
+ doesExecutableExistOnSystem,
4
+ removeLinesMatchingPattern,
5
+ } from "../helpers.js";
6
+ import { execSync } from "child_process";
7
+
8
+ const shellName = "PowerShell Core";
9
+ const executableName = "pwsh";
10
+ const startupFileCommand = "echo $PROFILE";
11
+
12
+ function isInstalled() {
13
+ return doesExecutableExistOnSystem(executableName);
14
+ }
15
+
16
+ /**
17
+ * @param {import("../helpers.js").AikidoTool[]} tools
18
+ *
19
+ * @returns {boolean}
20
+ */
21
+ function teardown(tools) {
22
+ const startupFile = getStartupFile();
23
+
24
+ for (const { tool } of tools) {
25
+ // Remove any existing alias for the tool
26
+ removeLinesMatchingPattern(
27
+ startupFile,
28
+ new RegExp(`^Set-Alias\\s+${tool}\\s+`)
29
+ );
30
+ }
31
+
32
+ // Remove the line that sources the safe-chain PowerShell initialization script
33
+ removeLinesMatchingPattern(
34
+ startupFile,
35
+ /^\.\s+["']?\$HOME[/\\].safe-chain[/\\]scripts[/\\]init-pwsh\.ps1["']?/
36
+ );
37
+
38
+ return true;
39
+ }
40
+
41
+ function setup() {
42
+ const startupFile = getStartupFile();
43
+
44
+ addLineToFile(
45
+ startupFile,
46
+ `. "$HOME\\.safe-chain\\scripts\\init-pwsh.ps1" # Safe-chain PowerShell initialization script`
47
+ );
48
+
49
+ return true;
50
+ }
51
+
52
+ function getStartupFile() {
53
+ try {
54
+ return execSync(startupFileCommand, {
55
+ encoding: "utf8",
56
+ shell: executableName,
57
+ }).trim();
58
+ } catch (/** @type {any} */ error) {
59
+ throw new Error(
60
+ `Command failed: ${startupFileCommand}. Error: ${error.message}`
61
+ );
62
+ }
63
+ }
64
+
65
+ /**
66
+ * @type {import("../shellDetection.js").Shell}
67
+ */
68
+ export default {
69
+ name: shellName,
70
+ isInstalled,
71
+ setup,
72
+ teardown,
73
+ };
@@ -0,0 +1,73 @@
1
+ import {
2
+ addLineToFile,
3
+ doesExecutableExistOnSystem,
4
+ removeLinesMatchingPattern,
5
+ } from "../helpers.js";
6
+ import { execSync } from "child_process";
7
+
8
+ const shellName = "Windows PowerShell";
9
+ const executableName = "powershell";
10
+ const startupFileCommand = "echo $PROFILE";
11
+
12
+ function isInstalled() {
13
+ return doesExecutableExistOnSystem(executableName);
14
+ }
15
+
16
+ /**
17
+ * @param {import("../helpers.js").AikidoTool[]} tools
18
+ *
19
+ * @returns {boolean}
20
+ */
21
+ function teardown(tools) {
22
+ const startupFile = getStartupFile();
23
+
24
+ for (const { tool } of tools) {
25
+ // Remove any existing alias for the tool
26
+ removeLinesMatchingPattern(
27
+ startupFile,
28
+ new RegExp(`^Set-Alias\\s+${tool}\\s+`)
29
+ );
30
+ }
31
+
32
+ // Remove the line that sources the safe-chain PowerShell initialization script
33
+ removeLinesMatchingPattern(
34
+ startupFile,
35
+ /^\.\s+["']?\$HOME[/\\].safe-chain[/\\]scripts[/\\]init-pwsh\.ps1["']?/
36
+ );
37
+
38
+ return true;
39
+ }
40
+
41
+ function setup() {
42
+ const startupFile = getStartupFile();
43
+
44
+ addLineToFile(
45
+ startupFile,
46
+ `. "$HOME\\.safe-chain\\scripts\\init-pwsh.ps1" # Safe-chain PowerShell initialization script`
47
+ );
48
+
49
+ return true;
50
+ }
51
+
52
+ function getStartupFile() {
53
+ try {
54
+ return execSync(startupFileCommand, {
55
+ encoding: "utf8",
56
+ shell: executableName,
57
+ }).trim();
58
+ } catch (/** @type {any} */ error) {
59
+ throw new Error(
60
+ `Command failed: ${startupFileCommand}. Error: ${error.message}`
61
+ );
62
+ }
63
+ }
64
+
65
+ /**
66
+ * @type {import("../shellDetection.js").Shell}
67
+ */
68
+ export default {
69
+ name: shellName,
70
+ isInstalled,
71
+ setup,
72
+ teardown,
73
+ };
@@ -0,0 +1,74 @@
1
+ import {
2
+ addLineToFile,
3
+ doesExecutableExistOnSystem,
4
+ removeLinesMatchingPattern,
5
+ } from "../helpers.js";
6
+ import { execSync } from "child_process";
7
+
8
+ const shellName = "Zsh";
9
+ const executableName = "zsh";
10
+ const startupFileCommand = "echo ${ZDOTDIR:-$HOME}/.zshrc";
11
+ const eol = "\n"; // When zsh runs on Windows (e.g., Git Bash or WSL), it expects LF line endings.
12
+
13
+ function isInstalled() {
14
+ return doesExecutableExistOnSystem(executableName);
15
+ }
16
+
17
+ /**
18
+ * @param {import("../helpers.js").AikidoTool[]} tools
19
+ *
20
+ * @returns {boolean}
21
+ */
22
+ function teardown(tools) {
23
+ const startupFile = getStartupFile();
24
+
25
+ for (const { tool } of tools) {
26
+ // Remove any existing alias for the tool
27
+ removeLinesMatchingPattern(
28
+ startupFile,
29
+ new RegExp(`^alias\\s+${tool}=`),
30
+ eol
31
+ );
32
+ }
33
+
34
+ // Removes the line that sources the safe-chain zsh initialization script (~/.aikido/scripts/init-posix.sh)
35
+ removeLinesMatchingPattern(
36
+ startupFile,
37
+ /^source\s+~\/\.safe-chain\/scripts\/init-posix\.sh/,
38
+ eol
39
+ );
40
+
41
+ return true;
42
+ }
43
+
44
+ function setup() {
45
+ const startupFile = getStartupFile();
46
+
47
+ addLineToFile(
48
+ startupFile,
49
+ `source ~/.safe-chain/scripts/init-posix.sh # Safe-chain Zsh initialization script`,
50
+ eol
51
+ );
52
+
53
+ return true;
54
+ }
55
+
56
+ function getStartupFile() {
57
+ try {
58
+ return execSync(startupFileCommand, {
59
+ encoding: "utf8",
60
+ shell: executableName,
61
+ }).trim();
62
+ } catch (/** @type {any} */ error) {
63
+ throw new Error(
64
+ `Command failed: ${startupFileCommand}. Error: ${error.message}`
65
+ );
66
+ }
67
+ }
68
+
69
+ export default {
70
+ name: shellName,
71
+ isInstalled,
72
+ setup,
73
+ teardown,
74
+ };
@@ -0,0 +1,64 @@
1
+ import chalk from "chalk";
2
+ import { ui } from "../environment/userInteraction.js";
3
+ import { detectShells } from "./shellDetection.js";
4
+ import { knownAikidoTools, getPackageManagerList } from "./helpers.js";
5
+
6
+ /**
7
+ * @returns {Promise<void>}
8
+ */
9
+ export async function teardown() {
10
+ ui.writeInformation(
11
+ chalk.bold("Removing shell aliases.") +
12
+ ` This will remove safe-chain aliases for ${getPackageManagerList()}.`
13
+ );
14
+ ui.emptyLine();
15
+
16
+ try {
17
+ const shells = detectShells();
18
+ if (shells.length === 0) {
19
+ ui.writeError("No supported shells detected. Cannot remove aliases.");
20
+ return;
21
+ }
22
+
23
+ ui.writeInformation(
24
+ `Detected ${shells.length} supported shell(s): ${shells
25
+ .map((shell) => chalk.bold(shell.name))
26
+ .join(", ")}.`
27
+ );
28
+
29
+ let updatedCount = 0;
30
+ for (const shell of shells) {
31
+ let success = false;
32
+ try {
33
+ success = shell.teardown(knownAikidoTools);
34
+ } catch {
35
+ success = false;
36
+ }
37
+
38
+ if (success) {
39
+ ui.writeInformation(
40
+ `${chalk.bold("- " + shell.name + ":")} ${chalk.green(
41
+ "Teardown successful"
42
+ )}`
43
+ );
44
+ updatedCount++;
45
+ } else {
46
+ ui.writeError(
47
+ `${chalk.bold("- " + shell.name + ":")} ${chalk.red(
48
+ "Teardown failed"
49
+ )}. Please check your ${shell.name} configuration.`
50
+ );
51
+ }
52
+ }
53
+
54
+ if (updatedCount > 0) {
55
+ ui.emptyLine();
56
+ ui.writeInformation(`Please restart your terminal to apply the changes.`);
57
+ }
58
+ } catch (/** @type {any} */ error) {
59
+ ui.writeError(
60
+ `Failed to remove shell aliases: ${error.message}. Please check your shell configuration.`
61
+ );
62
+ return;
63
+ }
64
+ }