karnel-termux 4.17.0 → 4.17.2

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 (35) hide show
  1. package/install.sh +20 -2
  2. package/karnel/RELEASE_COMMIT +1 -1
  3. package/karnel/bin/karnel +2 -1
  4. package/karnel/cli/commands/backup.sh +1 -1
  5. package/karnel/cli/commands/doctor/fixes.sh +2 -2
  6. package/karnel/cli/commands/init.sh +21 -21
  7. package/karnel/cli/commands/supabase.sh +1 -1
  8. package/karnel/tools/ai/antigravity-cli/install.sh +18 -6
  9. package/karnel/tools/ai/ctx7/install.sh +7 -0
  10. package/karnel/tools/ai/droid/install.sh +19 -6
  11. package/karnel/tools/ai/freebuff/install.sh +7 -0
  12. package/karnel/tools/ai/goose/install.sh +1 -0
  13. package/karnel/tools/ai/keelcode/install.sh +7 -0
  14. package/karnel/tools/ai/kimchi-code/install.sh +9 -3
  15. package/karnel/tools/ai/oh-my-pi/install.sh +31 -6
  16. package/karnel/tools/ai/omni-route/install.sh +7 -0
  17. package/karnel/tools/ai/openspec/install.sh +7 -0
  18. package/karnel/tools/ai/puter/install.sh +7 -0
  19. package/karnel/tools/ai/supercode-cli/install.sh +7 -0
  20. package/karnel/tools/db/redis/install.sh +4 -1
  21. package/karnel/tools/deploy/netlify/install.sh +7 -0
  22. package/karnel/tools/deploy/railway/install.sh +9 -0
  23. package/karnel/tools/deploy/vercel/install.sh +7 -0
  24. package/karnel/tools/dev/openssh/install.sh +4 -1
  25. package/karnel/tools/dev/tmux/install.sh +4 -1
  26. package/karnel/tools/lang/bun/install.sh +26 -7
  27. package/karnel/tools/npm/turbopack/install.sh +2 -2
  28. package/karnel/tools/osint/robin/install.sh +6 -2
  29. package/karnel/tools/security/amass/install.sh +7 -0
  30. package/karnel/tools/security/subfinder/install.sh +7 -0
  31. package/karnel/tools/shell/zsh-defer/install.sh +1 -1
  32. package/karnel/utils/install.sh +7 -1
  33. package/karnel/utils/tools.sh +4 -0
  34. package/karnel/utils/uninstall.sh +2 -1
  35. package/package.json +1 -1
package/install.sh CHANGED
@@ -79,7 +79,9 @@ _INSTALL_KARNEL_SYMLINK_CHANGED=false
79
79
  _INSTALL_PREVIOUS_KARNEL_SYMLINK=""
80
80
 
81
81
  _cleanup_failed() {
82
- echo -e "\n ${P_FAIL}✖${P_NC} Installation failed at step ${CURRENT_STEP}. Cleaning up..."
82
+ set +e
83
+ trap - ERR
84
+ echo -e "\n ${P_FAIL}✖${P_NC} Installation failed at step ${CURRENT_STEP}. Cleaning up..."
83
85
  if $_INSTALL_KARNEL_SYMLINK_CHANGED; then
84
86
  if [[ -n "$_INSTALL_PREVIOUS_KARNEL_SYMLINK" ]]; then
85
87
  local rollback_link_dir
@@ -287,7 +289,23 @@ clone_repo() {
287
289
  log_fail "Failed to clone repository"
288
290
  return 1
289
291
  fi
290
- if [[ -n "$RELEASE_REF" ]]; then
292
+ if [[ -n "$RELEASE_COMMIT" ]]; then
293
+ if ! git -C "$candidate_repo" fetch --depth=1 origin "$RELEASE_COMMIT" &>/dev/null; then
294
+ log_fail "Failed to fetch release commit $RELEASE_COMMIT"
295
+ return 1
296
+ fi
297
+ if ! git -C "$candidate_repo" checkout --quiet --detach "$RELEASE_COMMIT" &>/dev/null; then
298
+ log_fail "Failed to checkout release commit $RELEASE_COMMIT"
299
+ return 1
300
+ fi
301
+ local candidate_commit
302
+ candidate_commit=$(git -C "$candidate_repo" rev-parse HEAD 2>/dev/null) || return 1
303
+ if [[ "$candidate_commit" != "$RELEASE_COMMIT" ]]; then
304
+ log_fail "Release commit mismatch"
305
+ log_info "Expected $RELEASE_COMMIT, got $candidate_commit"
306
+ return 1
307
+ fi
308
+ elif [[ -n "$RELEASE_REF" ]]; then
291
309
  local candidate_commit
292
310
  candidate_commit=$(git -C "$candidate_repo" rev-parse HEAD 2>/dev/null) || return 1
293
311
  if [[ "$candidate_commit" != "$RELEASE_COMMIT" ]]; then
@@ -1 +1 @@
1
- 57a1aa3e5401b6ba9b37965d8680bcb543b0fc00
1
+ 680f9d3f783c6f2bf95e4499c3e230d2d25b7070
package/karnel/bin/karnel CHANGED
@@ -30,7 +30,8 @@ import "@/utils/env"
30
30
 
31
31
  # ===== SEMVER COMPARISON =====
32
32
  _semver_gt() {
33
- [[ "$(printf '%s\n%s' "$1" "$2" | sort -V | tail -1)" == "$1" ]] && [[ "$1" != "$2" ]]
33
+ local a="${1#v}" b="${2#v}"
34
+ [[ "$(printf '%s\n%s' "$a" "$b" | sort -V | tail -1)" == "$a" ]] && [[ "$a" != "$b" ]]
34
35
  }
35
36
 
36
37
  _valid_semver() {
@@ -10,7 +10,7 @@ mkdir -p "$BACKUP_DIR"
10
10
 
11
11
  backup_main() {
12
12
  local cmd="${1:-}"
13
- shift 2>/dev/null || true
13
+ shift || true
14
14
 
15
15
  case "$cmd" in
16
16
  --help|-h) backup_help ;;
@@ -292,7 +292,7 @@ _fix_locale() {
292
292
  if [[ ! -f "$config" ]] && [[ -f "$HOME/.bashrc" ]]; then
293
293
  config="$HOME/.bashrc"
294
294
  fi
295
- echo 'export LANG=en_US.UTF-8' >> "$config" 2>/dev/null
295
+ grep -qxF 'export LANG=en_US.UTF-8' "$config" 2>/dev/null || echo 'export LANG=en_US.UTF-8' >> "$config" 2>/dev/null
296
296
  }
297
297
 
298
298
 
@@ -425,6 +425,6 @@ _fix_keyring() {
425
425
  }
426
426
 
427
427
  _fix_apt_cache() {
428
- rm -rf "$PREFIX/var/apt/lists"/* 2>/dev/null
428
+ rm -rf "${PREFIX:?}/var/apt/lists"/* 2>/dev/null
429
429
  pkg update -y &>/dev/null
430
430
  }
@@ -1078,33 +1078,33 @@ configure_rust() {
1078
1078
 
1079
1079
  # Append dependencies to Cargo.toml
1080
1080
  if [[ -f "Cargo.toml" ]]; then
1081
- # Append after [dependencies] or add it
1082
- if grep -q '^\[dependencies\]' Cargo.toml; then
1083
- cat >>Cargo.toml <<EOF
1084
- EOF
1085
- else
1086
- echo -e "\n[dependencies]" >> Cargo.toml
1081
+ if ! grep -q '^\[dependencies\]' Cargo.toml; then
1082
+ printf '\n[dependencies]\n' >> Cargo.toml
1087
1083
  fi
1088
- cat >>Cargo.toml <<EOF
1089
- tokio = { version = "1.0", features = ["full"] }
1090
- serde = { version = "1.0", features = ["derive"] }
1091
- serde_json = "1.0"
1092
- dotenvy = "0.15"
1093
- EOF
1084
+ local dep name spec
1085
+ local -a deps=(
1086
+ 'tokio = { version = "1.0", features = ["full"] }'
1087
+ 'serde = { version = "1.0", features = ["derive"] }'
1088
+ 'serde_json = "1.0"'
1089
+ 'dotenvy = "0.15"'
1090
+ )
1094
1091
  if [[ "$framework_choice" == "Axum" ]]; then
1095
- cat >>Cargo.toml <<EOF
1096
- axum = "0.7"
1097
- EOF
1092
+ deps+=('axum = "0.7"')
1098
1093
  else
1099
- cat >>Cargo.toml <<EOF
1100
- actix-web = "4.0"
1101
- EOF
1094
+ deps+=('actix-web = "4.0"')
1102
1095
  fi
1103
1096
  if [[ "$db_choice" == "SQLx (PostgreSQL)" ]]; then
1104
- cat >>Cargo.toml <<EOF
1105
- sqlx = { version = "0.7", features = ["runtime-tokio", "postgres", "macros"] }
1106
- EOF
1097
+ deps+=('sqlx = { version = "0.7", features = ["runtime-tokio", "postgres", "macros"] }')
1107
1098
  fi
1099
+ for dep in "${deps[@]}"; do
1100
+ name="${dep%% =*}"
1101
+ name="${name%%[[:space:]]*}"
1102
+ if grep -q "^${name} =" Cargo.toml; then
1103
+ log_info "Dependency already present: ${name}"
1104
+ else
1105
+ sed -i "/^\[dependencies\]/a ${dep}" Cargo.toml
1106
+ fi
1107
+ done
1108
1108
  log_success "Configured Cargo.toml"
1109
1109
  fi
1110
1110
 
@@ -5,7 +5,7 @@ import "@/utils/colors"
5
5
 
6
6
  supabase_main() {
7
7
  local subcommand="${1:-help}"
8
- shift 2>/dev/null || true
8
+ shift || true
9
9
 
10
10
  case "$subcommand" in
11
11
  help|--help|-h)
@@ -3,6 +3,7 @@
3
3
  import "@/utils/log"
4
4
  import "@/utils/colors"
5
5
  import "@/utils/version"
6
+ import "@/utils/install"
6
7
 
7
8
  LOG_FILE="$KARNEL_CACHE/install_ai.log"
8
9
  AGY_DATA_DIR="$HOME/.local/share/karnel-data/antigravity-cli"
@@ -63,17 +64,22 @@ _antigravity_download_binary() {
63
64
  }
64
65
 
65
66
  _antigravity_download_binary_impl() {
66
- local latest_version
67
- latest_version=$(_antigravity_get_latest_version)
68
- if [ -z "$latest_version" ]; then
69
- log_error "Failed to fetch latest Antigravity version"
67
+ local manifest
68
+ manifest=$(curl -fsSL "$MANIFEST_URL") || {
69
+ log_error "Failed to fetch Antigravity CLI manifest"
70
+ return 1
71
+ }
72
+ local latest_version download_url manifest_sha256
73
+ latest_version=$(printf '%s' "$manifest" | jq -r .version)
74
+ download_url=$(printf '%s' "$manifest" | jq -r .url)
75
+ manifest_sha256=$(printf '%s' "$manifest" | jq -r '.sha256 // empty')
76
+ if [ -z "$latest_version" ] || [ -z "$download_url" ]; then
77
+ log_error "Failed to parse Antigravity CLI manifest"
70
78
  return 1
71
79
  fi
72
80
 
73
81
  mkdir -p "$AGY_DATA_DIR"
74
82
 
75
- local download_url
76
- download_url=$(_antigravity_get_download_url)
77
83
  local tarball="$AGY_DATA_DIR/agy.tar.gz"
78
84
 
79
85
  if ! curl -fsSL -o "$tarball" "$download_url" &>>"$LOG_FILE"; then
@@ -81,6 +87,12 @@ _antigravity_download_binary_impl() {
81
87
  return 1
82
88
  fi
83
89
 
90
+ if [[ "$manifest_sha256" =~ ^[0-9a-f]{64}$ ]]; then
91
+ verify_sha256 "$tarball" "$manifest_sha256" || return 1
92
+ else
93
+ log_warn "Antigravity CLI manifest does not publish a SHA-256; skipping integrity verification"
94
+ fi
95
+
84
96
  if ! tar -xzf "$tarball" -C "$AGY_DATA_DIR" &>>"$LOG_FILE"; then
85
97
  log_error "Failed to extract Antigravity CLI binary"
86
98
  return 1
@@ -13,6 +13,13 @@ install_ctx7() {
13
13
  return 2
14
14
  fi
15
15
 
16
+ if ! command -v npm &>/dev/null; then
17
+ if ! pkg install nodejs-lts -y &>>"$LOG_FILE"; then
18
+ log_error "Failed to install Node.js (required by ctx7)"
19
+ return 1
20
+ fi
21
+ fi
22
+
16
23
  log_info "Installing ctx7 (Context7 documentation provider)..."
17
24
  npm install -g ctx7 2>/dev/null
18
25
  local rc=$?
@@ -3,6 +3,7 @@
3
3
  import "@/utils/log"
4
4
  import "@/utils/colors"
5
5
  import "@/utils/version"
6
+ import "@/utils/install"
6
7
  import "@/utils/uninstall"
7
8
 
8
9
  : "${KARNEL_DATA:=${XDG_DATA_HOME:-$HOME/.local/share}/karnel-data}"
@@ -239,14 +240,20 @@ _droid_ubuntu_install_bin() {
239
240
 
240
241
  local binary_url="https://downloads.factory.ai/factory-cli/releases/$latest_version/linux/arm64/droid"
241
242
 
242
- _droid_proot_ubuntu /bin/bash -c "
243
+ local expected
244
+ expected=$(curl -fsSL "$binary_url.sha256" 2>/dev/null | awk '{print $1}') || expected=""
245
+ if [[ ! "$expected" =~ ^[0-9a-f]{64}$ ]]; then
246
+ log_warn "No published SHA-256 for Droid; skipping integrity verification"
247
+ fi
248
+ _droid_proot_ubuntu /bin/bash -c '
243
249
  mkdir -p /tmp/droid-install &&
244
- curl -fsSL '$binary_url' -o /tmp/droid-install/droid &&
250
+ curl -fsSL "$1" -o /tmp/droid-install/droid &&
251
+ { [ -z "$2" ] || [ "$(sha256sum /tmp/droid-install/droid | awk "{print \$1}")" = "$2" ]; } &&
245
252
  mkdir -p /usr/local/bin &&
246
253
  mv /tmp/droid-install/droid /usr/local/bin/droid &&
247
254
  chmod +x /usr/local/bin/droid &&
248
255
  rm -rf /tmp/droid-install
249
- " &>>"$LOG_FILE"
256
+ ' bash "$binary_url" "$expected" &>>"$LOG_FILE"
250
257
 
251
258
  local droid_bin="$(_droid_detect_ubuntu_root)/usr/local/bin/droid"
252
259
  if [ ! -f "$droid_bin" ]; then
@@ -413,14 +420,20 @@ _update_droid_proot_impl() {
413
420
 
414
421
  local binary_url="https://downloads.factory.ai/factory-cli/releases/$latest_version/linux/arm64/droid"
415
422
 
416
- _droid_proot_ubuntu /bin/bash -c "
423
+ local expected
424
+ expected=$(curl -fsSL "$binary_url.sha256" 2>/dev/null | awk '{print $1}') || expected=""
425
+ if [[ ! "$expected" =~ ^[0-9a-f]{64}$ ]]; then
426
+ log_warn "No published SHA-256 for Droid; skipping integrity verification"
427
+ fi
428
+ _droid_proot_ubuntu /bin/bash -c '
417
429
  rm -f /usr/local/bin/droid &&
418
430
  mkdir -p /tmp/droid-update &&
419
- curl -fsSL '$binary_url' -o /tmp/droid-update/droid &&
431
+ curl -fsSL "$1" -o /tmp/droid-update/droid &&
432
+ { [ -z "$2" ] || [ "$(sha256sum /tmp/droid-update/droid | awk "{print \$1}")" = "$2" ]; } &&
420
433
  mv /tmp/droid-update/droid /usr/local/bin/droid &&
421
434
  chmod +x /usr/local/bin/droid &&
422
435
  rm -rf /tmp/droid-update
423
- " &>>"$LOG_FILE"
436
+ ' bash "$binary_url" "$expected" &>>"$LOG_FILE"
424
437
 
425
438
  local ubuntu_root
426
439
  ubuntu_root="$(_droid_detect_ubuntu_root)"
@@ -9,6 +9,13 @@ install_freebuff() {
9
9
  return 2
10
10
  fi
11
11
 
12
+ if ! command -v npm &>/dev/null; then
13
+ if ! pkg install nodejs-lts -y &>>"$LOG_FILE"; then
14
+ log_error "Failed to install Node.js (required by Freebuff)"
15
+ return 1
16
+ fi
17
+ fi
18
+
12
19
  log_info "Installing Freebuff..."
13
20
  npm install -g freebuff || {
14
21
  log_error "Failed to install Freebuff"
@@ -196,6 +196,7 @@ _goose_download_binary_proot_impl() {
196
196
  log_error "Failed to download Goose CLI"
197
197
  return 1
198
198
  fi
199
+ verify_github_release_asset aaif-goose/goose "v${latest_version}" "$tarball" "$GOOSE_DATA_DIR/$tarball" || return 1
199
200
  if ! tar -xzf "$GOOSE_DATA_DIR/$tarball" -C "$GOOSE_DATA_DIR" &>>"$LOG_FILE"; then
200
201
  rm -f "$GOOSE_DATA_DIR/$tarball"
201
202
  log_error "Failed to extract Goose CLI"
@@ -117,6 +117,13 @@ install_keelcode() {
117
117
  fi
118
118
  _keelcode_verify_ownership || return 1
119
119
 
120
+ if ! command -v npm &>/dev/null; then
121
+ if ! pkg install nodejs-lts -y &>>"$LOG_FILE"; then
122
+ log_error "Failed to install Node.js (required by KeelCode)"
123
+ return 1
124
+ fi
125
+ fi
126
+
120
127
  log_info "Installing KeelCode..."
121
128
  local output rc
122
129
  output="$(npm install -g "$KEELCODE_PACKAGE" --force 2>&1)"
@@ -262,9 +262,15 @@ _kimchi_ubuntu_install_bin() {
262
262
 
263
263
  local download_url="https://github.com/getkimchi/kimchi/releases/download/$latest_version/kimchi_linux_arm64.tar.gz"
264
264
 
265
- _kimchi_proot_ubuntu /bin/bash -c "
265
+ local expected
266
+ expected=$(github_release_asset_sha256 getkimchi/kimchi "$latest_version" "kimchi_linux_arm64.tar.gz") || expected=""
267
+ if [[ ! "$expected" =~ ^[0-9a-f]{64}$ ]]; then
268
+ log_warn "No published SHA-256 for Kimchi; skipping integrity verification"
269
+ fi
270
+ _kimchi_proot_ubuntu /bin/bash -c '
266
271
  mkdir -p /tmp/kimchi-install &&
267
- curl -fsSL '$download_url' -o /tmp/kimchi-install/kimchi.tar.gz &&
272
+ curl -fsSL "$1" -o /tmp/kimchi-install/kimchi.tar.gz &&
273
+ { [ -z "$2" ] || [ "$(sha256sum /tmp/kimchi-install/kimchi.tar.gz | awk "{print \$1}")" = "$2" ]; } &&
268
274
  tar -zxf /tmp/kimchi-install/kimchi.tar.gz -C /tmp/kimchi-install &&
269
275
  mkdir -p /usr/local/bin /usr/local/share &&
270
276
  mv /tmp/kimchi-install/bin/kimchi /usr/local/bin/kimchi &&
@@ -272,7 +278,7 @@ _kimchi_ubuntu_install_bin() {
272
278
  rm -rf /usr/local/share/kimchi &&
273
279
  mv /tmp/kimchi-install/share/kimchi /usr/local/share/kimchi &&
274
280
  rm -rf /tmp/kimchi-install
275
- " &>>"$LOG_FILE"
281
+ ' bash "$download_url" "$expected" &>>"$LOG_FILE"
276
282
 
277
283
  local kimchi_bin
278
284
  kimchi_bin="$(_kimchi_detect_ubuntu_root)/usr/local/bin/kimchi"
@@ -3,6 +3,7 @@
3
3
  import "@/utils/log"
4
4
  import "@/utils/colors"
5
5
  import "@/utils/version"
6
+ import "@/utils/install"
6
7
 
7
8
  LOG_FILE="$KARNEL_CACHE/install_ai.log"
8
9
  OMP_DATA_DIR="$HOME/.local/share/karnel-data/oh-my-pi"
@@ -85,8 +86,16 @@ _download_omp_binary_impl() {
85
86
  log_error "Failed to download Oh-My-Pi binary"
86
87
  return 1
87
88
  fi
89
+ local expected
90
+ expected=$(github_release_asset_sha256 can1357/oh-my-pi "$latest_version" "omp-linux-arm64") || expected=""
91
+ if [[ "$expected" =~ ^[0-9a-f]{64}$ ]]; then
92
+ verify_sha256 "$target_dir/omp" "$expected" || return 1
93
+ else
94
+ log_warn "No published SHA-256 for Oh-My-Pi; skipping integrity verification"
95
+ fi
88
96
  chmod +x "$target_dir/omp"
89
97
  echo "$latest_version" > "$target_dir/version"
98
+ : > "$target_dir/.karnel-managed"
90
99
  return 0
91
100
  }
92
101
 
@@ -160,15 +169,21 @@ _install_omp_proot_impl() {
160
169
  return 1
161
170
  fi
162
171
  local download_url="https://github.com/can1357/oh-my-pi/releases/download/$latest_version/omp-linux-arm64"
163
- if ! _omp_proot_ubuntu /bin/bash -c "
172
+ local expected
173
+ expected=$(github_release_asset_sha256 can1357/oh-my-pi "$latest_version" "omp-linux-arm64") || expected=""
174
+ if [[ ! "$expected" =~ ^[0-9a-f]{64}$ ]]; then
175
+ log_warn "No published SHA-256 for Oh-My-Pi; skipping integrity verification"
176
+ fi
177
+ if ! _omp_proot_ubuntu /bin/bash -c '
164
178
  mkdir -p /tmp/omp-install &&
165
- curl -fsSL '$download_url' -o /tmp/omp-install/omp &&
179
+ curl -fsSL "$1" -o /tmp/omp-install/omp &&
180
+ { [ -z "$2" ] || [ "$(sha256sum /tmp/omp-install/omp | awk "{print \$1}")" = "$2" ]; } &&
166
181
  chmod +x /tmp/omp-install/omp &&
167
182
  test -x /tmp/omp-install/omp &&
168
183
  mkdir -p /usr/local/bin &&
169
184
  mv /tmp/omp-install/omp /usr/local/bin/omp &&
170
185
  rm -rf /tmp/omp-install
171
- " &>>"$LOG_FILE"; then
186
+ ' bash "$download_url" "$expected" &>>"$LOG_FILE"; then
172
187
  log_error "Failed to install Oh-My-Pi binary"
173
188
  return 1
174
189
  fi
@@ -209,6 +224,10 @@ install_oh_my_pi() {
209
224
  }
210
225
 
211
226
  _uninstall_omp_native_impl() {
227
+ if [[ ! -f "$OMP_DATA_DIR/.karnel-managed" ]]; then
228
+ log_error "Refusing to remove an Oh-My-Pi installation that is not managed by Karnel"
229
+ return 1
230
+ fi
212
231
  rm -f "$PREFIX/bin/omp"
213
232
  rm -rf "$OMP_DATA_DIR"
214
233
  }
@@ -256,14 +275,20 @@ _update_omp_proot_impl() {
256
275
  return 1
257
276
  fi
258
277
  local download_url="https://github.com/can1357/oh-my-pi/releases/download/$latest_version/omp-linux-arm64"
259
- if ! _omp_proot_ubuntu /bin/bash -c "
278
+ local expected
279
+ expected=$(github_release_asset_sha256 can1357/oh-my-pi "$latest_version" "omp-linux-arm64") || expected=""
280
+ if [[ ! "$expected" =~ ^[0-9a-f]{64}$ ]]; then
281
+ log_warn "No published SHA-256 for Oh-My-Pi; skipping integrity verification"
282
+ fi
283
+ if ! _omp_proot_ubuntu /bin/bash -c '
260
284
  mkdir -p /tmp/omp-update &&
261
- curl -fsSL '$download_url' -o /tmp/omp-update/omp &&
285
+ curl -fsSL "$1" -o /tmp/omp-update/omp &&
286
+ { [ -z "$2" ] || [ "$(sha256sum /tmp/omp-update/omp | awk "{print \$1}")" = "$2" ]; } &&
262
287
  chmod +x /tmp/omp-update/omp &&
263
288
  test -x /tmp/omp-update/omp &&
264
289
  mv /tmp/omp-update/omp /usr/local/bin/omp &&
265
290
  rm -rf /tmp/omp-update
266
- " &>>"$LOG_FILE"; then
291
+ ' bash "$download_url" "$expected" &>>"$LOG_FILE"; then
267
292
  log_error "Failed to update Oh-My-Pi binary"
268
293
  return 1
269
294
  fi
@@ -127,6 +127,13 @@ install_omni_route() {
127
127
  fi
128
128
  fi
129
129
 
130
+ if ! command -v npm &>/dev/null; then
131
+ if ! pkg install nodejs-lts -y &>>"$LOG_FILE"; then
132
+ log_error "Failed to install Node.js (required by omniRoute)"
133
+ return 1
134
+ fi
135
+ fi
136
+
130
137
  log_info "Installing omniRoute (this may take a while)..."
131
138
  if command -v npm >/dev/null 2>&1 && npm i karnelroute --prefix "$OMNI_ROUTE_PKG" 2>>"$LOG_FILE"; then
132
139
  sed -i '1s|^#!/usr/bin/env node|#!'"$PREFIX"'/bin/node|' "$OMNI_ROUTE_LOCAL_BIN" 2>/dev/null
@@ -13,6 +13,13 @@ install_openspec() {
13
13
  return 2
14
14
  fi
15
15
 
16
+ if ! command -v npm &>/dev/null; then
17
+ if ! pkg install nodejs-lts -y &>>"$LOG_FILE"; then
18
+ log_error "Failed to install Node.js (required by openspec)"
19
+ return 1
20
+ fi
21
+ fi
22
+
16
23
  log_info "Installing openspec (Spec-Driven Development framework)..."
17
24
  npm install -g @fission-ai/openspec@latest 2>/dev/null
18
25
  local rc=$?
@@ -12,6 +12,13 @@ install_puter() {
12
12
  return 2
13
13
  fi
14
14
 
15
+ if ! command -v npm &>/dev/null; then
16
+ if ! pkg install nodejs-lts -y &>>"$LOG_FILE"; then
17
+ log_error "Failed to install Node.js (required by Puter CLI)"
18
+ return 1
19
+ fi
20
+ fi
21
+
15
22
  log_info "Installing Puter CLI..."
16
23
  if ! npm install -g "$PUTER_PACKAGE" &>>"$PUTER_LOG_FILE"; then
17
24
  log_error "Failed to install Puter CLI; see $PUTER_LOG_FILE"
@@ -8,6 +8,13 @@ install_supercode_cli() {
8
8
  log_info "Supercode CLI is already installed"
9
9
  return 2
10
10
  fi
11
+ if ! command -v npm &>/dev/null; then
12
+ if ! pkg install nodejs-lts -y &>>"$LOG_FILE"; then
13
+ log_error "Failed to install Node.js (required by Supercode CLI)"
14
+ return 1
15
+ fi
16
+ fi
17
+
11
18
  log_info "Installing Supercode CLI..."
12
19
  local output rc
13
20
  output="$(npm install -g supercode-cli 2>&1)"
@@ -50,7 +50,10 @@ _update_redis_impl() {
50
50
 
51
51
  _do_redis_update() {
52
52
  mkdir -p "$(dirname "$LOG_FILE")"
53
- pkg upgrade redis -y &>>"$LOG_FILE"
53
+ if ! pkg upgrade redis -y &>>"$LOG_FILE"; then
54
+ log_error "Falha ao atualizar Redis"
55
+ return 1
56
+ fi
54
57
  }
55
58
 
56
59
  update_redis() {
@@ -15,6 +15,13 @@ install_netlify() {
15
15
  if command -v netlify &>/dev/null && netlify --version &>/dev/null; then
16
16
  log_info "Netlify CLI is already installed"
17
17
  return 2
18
+ fi
19
+ if ! command -v node &>/dev/null || ! command -v npm &>/dev/null; then
20
+ log_info "Installing Node.js..."
21
+ if ! pkg install -y nodejs-lts &>>"$LOG_FILE"; then
22
+ log_error "Failed to install Node.js"
23
+ return 1
24
+ fi
18
25
  fi
19
26
  if ! _netlify_node_supported; then
20
27
  log_error "Netlify CLI requires Node.js 22.13.0 or newer. Update Node.js and retry."
@@ -185,6 +185,15 @@ uninstall_railway() {
185
185
  }
186
186
 
187
187
  update_railway() {
188
+ if _railway_proot_wrapper_owned; then
189
+ log_info "Railway was installed via Ubuntu PRoot; updating inside the container..."
190
+ if proot-distro login ubuntu -- npm update -g @railway/cli &>>"$LOG_FILE"; then
191
+ log_success "Railway CLI updated through Ubuntu Proot"
192
+ return 0
193
+ fi
194
+ log_warn "Railway CLI update failed inside Ubuntu Proot"
195
+ return 1
196
+ fi
188
197
  _check_update_needed "Railway CLI" "$(_get_installed_npm_version @railway/cli)" "$(_get_remote_npm_version @railway/cli)" _do_update_railway
189
198
  }
190
199
 
@@ -10,6 +10,13 @@ install_vercel() {
10
10
  log_info "Vercel CLI is already installed"
11
11
  return 2
12
12
  fi
13
+ if ! command -v node &>/dev/null || ! command -v npm &>/dev/null; then
14
+ log_info "Installing Node.js..."
15
+ if ! pkg install -y nodejs-lts &>>"$LOG_FILE"; then
16
+ log_error "Failed to install Node.js"
17
+ return 1
18
+ fi
19
+ fi
13
20
  log_info "Installing Vercel CLI..."
14
21
  npm install -g vercel &>/dev/null || {
15
22
  log_error "Failed to install Vercel CLI"
@@ -62,7 +62,10 @@ _update_openssh_pkg() {
62
62
 
63
63
  _do_openssh_update() {
64
64
  mkdir -p "$(dirname "$LOG_FILE")"
65
- yes | pkg upgrade openssh -y &>>"$LOG_FILE"
65
+ if ! yes | pkg upgrade openssh -y &>>"$LOG_FILE"; then
66
+ log_error "Falha ao atualizar OpenSSH"
67
+ return 1
68
+ fi
66
69
  }
67
70
 
68
71
  update_openssh() {
@@ -62,7 +62,10 @@ _update_tmux_pkg() {
62
62
 
63
63
  _do_tmux_update() {
64
64
  mkdir -p "$(dirname "$LOG_FILE")"
65
- yes | pkg upgrade tmux -y &>>"$LOG_FILE"
65
+ if ! yes | pkg upgrade tmux -y &>>"$LOG_FILE"; then
66
+ log_error "Falha ao atualizar Tmux"
67
+ return 1
68
+ fi
66
69
  }
67
70
 
68
71
  update_tmux() {
@@ -3,6 +3,7 @@
3
3
  import "@/utils/log"
4
4
  import "@/utils/colors"
5
5
  import "@/utils/version"
6
+ import "@/utils/install"
6
7
 
7
8
  LOG_FILE="$KARNEL_CACHE/install_lang.log"
8
9
  BUN_DATA_DIR="$KARNEL_DATA/bun"
@@ -268,16 +269,22 @@ _install_bun_proot_impl() {
268
269
  _bun_proot_ubuntu /bin/bash -c \
269
270
  'apt-get update && apt-get upgrade -y && apt-get install -y curl ca-certificates unzip' \
270
271
  &>>"$LOG_FILE"
271
- _bun_proot_ubuntu /bin/bash -c "
272
+ local expected
273
+ expected=$(github_release_asset_sha256 "$BUN_REPO" "bun-v$version" "bun-linux-aarch64.zip") || expected=""
274
+ if [[ ! "$expected" =~ ^[0-9a-f]{64}$ ]]; then
275
+ log_warn "No published SHA-256 for Bun; skipping integrity verification"
276
+ fi
277
+ _bun_proot_ubuntu /bin/bash -c '
272
278
  export HOME=/root TMPDIR=/tmp
273
279
  cd /tmp &&
274
- curl -fsSL '$download_url' -o bun.zip &&
280
+ curl -fsSL "$1" -o bun.zip &&
281
+ { [ -z "$2" ] || [ "$(sha256sum bun.zip | awk "{print \$1}")" = "$2" ]; } &&
275
282
  unzip -o bun.zip >/dev/null 2>&1 &&
276
283
  mkdir -p /usr/local/bin &&
277
284
  mv bun-linux-aarch64/bun /usr/local/bin/bun &&
278
285
  chmod +x /usr/local/bin/bun &&
279
286
  rm -rf bun.zip bun-linux-aarch64
280
- " &>>"$LOG_FILE"
287
+ ' bash "$download_url" "$expected" &>>"$LOG_FILE"
281
288
  local ubuntu_root
282
289
  ubuntu_root="$(_bun_detect_ubuntu_root)"
283
290
  if [ -z "$ubuntu_root" ]; then
@@ -296,6 +303,7 @@ _install_bun_proot_impl() {
296
303
  fi
297
304
  sed "s|__UBUNTU_ROOTFS__|$ubuntu_root|g" "$wrapper_src" >"$PREFIX/bin/bun"
298
305
  chmod +x "$PREFIX/bin/bun"
306
+ mkdir -p "$BUN_DATA_DIR" && : >"$BUN_DATA_DIR/.karnel-proot"
299
307
  return 0
300
308
  }
301
309
 
@@ -339,7 +347,12 @@ _uninstall_bun_impl() {
339
347
  _uninstall_bun_native
340
348
  return $?
341
349
  fi
342
- _uninstall_bun_proot
350
+ if [ -f "$BUN_DATA_DIR/.karnel-proot" ]; then
351
+ _uninstall_bun_proot
352
+ return $?
353
+ fi
354
+ log_error "Refusing to remove unowned bun command: $PREFIX/bin/bun"
355
+ return 1
343
356
  }
344
357
 
345
358
  uninstall_bun() {
@@ -368,16 +381,22 @@ _update_bun_proot() {
368
381
  fi
369
382
  local download_url="https://github.com/$BUN_REPO/releases/download/bun-v$version/bun-linux-aarch64.zip"
370
383
  _bun_proot_ubuntu /bin/bash -c 'rm -f /usr/local/bin/bun' &>>"$LOG_FILE"
371
- _bun_proot_ubuntu /bin/bash -c "
384
+ local expected
385
+ expected=$(github_release_asset_sha256 "$BUN_REPO" "bun-v$version" "bun-linux-aarch64.zip") || expected=""
386
+ if [[ ! "$expected" =~ ^[0-9a-f]{64}$ ]]; then
387
+ log_warn "No published SHA-256 for Bun; skipping integrity verification"
388
+ fi
389
+ _bun_proot_ubuntu /bin/bash -c '
372
390
  export HOME=/root TMPDIR=/tmp
373
391
  cd /tmp &&
374
- curl -fsSL '$download_url' -o bun.zip &&
392
+ curl -fsSL "$1" -o bun.zip &&
393
+ { [ -z "$2" ] || [ "$(sha256sum bun.zip | awk "{print \$1}")" = "$2" ]; } &&
375
394
  unzip -o bun.zip >/dev/null 2>&1 &&
376
395
  mkdir -p /usr/local/bin &&
377
396
  mv bun-linux-aarch64/bun /usr/local/bin/bun &&
378
397
  chmod +x /usr/local/bin/bun &&
379
398
  rm -rf bun.zip bun-linux-aarch64
380
- " &>>"$LOG_FILE"
399
+ ' bash "$download_url" "$expected" &>>"$LOG_FILE"
381
400
  local ubuntu_root
382
401
  ubuntu_root="$(_bun_detect_ubuntu_root)"
383
402
  local bun_bin="$ubuntu_root/usr/local/bin/bun"
@@ -178,8 +178,8 @@ install_turbopack() {
178
178
 
179
179
  uninstall_turbopack() {
180
180
  if ! _has_glibc_node; then
181
- log_warn "Turbopack is not installed"
182
- return 1
181
+ log_info "Turbopack is not installed"
182
+ return 2
183
183
  fi
184
184
 
185
185
  loading "Removing CLI wrappers" _uninstall_wrappers
@@ -493,10 +493,14 @@ uninstall_robin() {
493
493
  return 2
494
494
  fi
495
495
 
496
+ if [[ ! -f "$ROBIN_ROOT/.managed-by-karnel" ]]; then
497
+ log_error "Robin at $ROBIN_ROOT is not managed by Karnel; refusing to remove it to avoid deleting unrelated data"
498
+ return 1
499
+ fi
500
+
496
501
  _robin_acquire_lock || return 1
497
502
  local rc=0
498
- _robin_migrate_legacy_layout || rc=$?
499
- if (( rc == 0 )) && _robin_runtime_active; then
503
+ if _robin_runtime_active; then
500
504
  log_error "Stop Robin before uninstalling it: karnel robin stop"
501
505
  rc=1
502
506
  fi
@@ -24,10 +24,17 @@ install_amass() (
24
24
  log_info "Instalando amass..."
25
25
  if [ ! -f "$_AMASS_MARKER" ] &&
26
26
  { pkg install -y amass 2>/dev/null || apt install -y amass 2>/dev/null; }; then
27
+ local bin; bin="$(command -v amass)"
28
+ [ -n "$bin" ] && sha256sum "$bin" > "$_AMASS_MARKER" 2>/dev/null
27
29
  log_success "amass instalado"
28
30
  return 0
29
31
  fi
30
32
 
33
+ command -v unzip >/dev/null 2>&1 || pkg install -y unzip >/dev/null 2>&1 || {
34
+ log_error "unzip indisponível; não foi possível extrair amass"
35
+ return 1
36
+ }
37
+
31
38
  local arch asset checksum url tmpdir archive amass_bin staged_bin
32
39
  arch=$(_amass_arch) || { log_error "Arquitetura não suportada"; return 1; }
33
40
  [[ "$arch" == armv7 ]] && arch="arm"
@@ -24,10 +24,17 @@ install_subfinder() (
24
24
  log_info "Instalando subfinder..."
25
25
  if [ ! -f "$_SUBFINDER_MARKER" ] &&
26
26
  { pkg install -y subfinder 2>/dev/null || apt install -y subfinder 2>/dev/null; }; then
27
+ local bin; bin="$(command -v subfinder)"
28
+ [ -n "$bin" ] && sha256sum "$bin" > "$_SUBFINDER_MARKER" 2>/dev/null
27
29
  log_success "subfinder instalado"
28
30
  return 0
29
31
  fi
30
32
 
33
+ command -v unzip >/dev/null 2>&1 || pkg install -y unzip >/dev/null 2>&1 || {
34
+ log_error "unzip indisponível; não foi possível extrair subfinder"
35
+ return 1
36
+ }
37
+
31
38
  local arch asset checksum url tmpdir archive staged_bin
32
39
  arch=$(_subfinder_arch) || { log_error "Arquitetura não suportada"; return 1; }
33
40
  [[ "$arch" == armv7 ]] && arch="arm"
@@ -60,7 +60,7 @@ install_zsh_defer() {
60
60
  _uninstall_zsh_defer_impl() {
61
61
  if [[ ! -d "$ZSH_PLUGINS_DIR/zsh-defer" ]]; then
62
62
  log_info "zsh-defer is not installed"
63
- return 2
63
+ return 0
64
64
  fi
65
65
 
66
66
  local remove_rc=0
@@ -269,8 +269,14 @@ managed_file_matches() {
269
269
 
270
270
  github_download_and_extract() {
271
271
  local repo="$1" version="$2" asset="$3" outdir="$4"
272
- local tarball
272
+ local tarball expected
273
273
  tarball=$(github_download_release "$repo" "$version" "$asset" "$outdir") || return 1
274
+ expected=$(github_release_asset_sha256 "$repo" "$version" "$asset") || expected=""
275
+ if [[ "$expected" =~ ^[0-9a-f]{64}$ ]]; then
276
+ verify_sha256 "$tarball" "$expected" || return 1
277
+ else
278
+ log_warn "No published SHA-256 digest for $repo/$asset; skipping integrity verification"
279
+ fi
274
280
  extract_tarball "$tarball" "$outdir" || return 1
275
281
  return 0
276
282
  }
@@ -106,6 +106,10 @@ _batch_tool_action() {
106
106
  local failed_count=0
107
107
  local skipped_count=0
108
108
 
109
+ # An explicit uninstall should actually remove owned data, even when no TTY
110
+ # is attached (piped/non-interactive). Ownership is still guarded upstream.
111
+ [[ "$action" == "uninstall" ]] && export KARNEL_REMOVE_DEFAULT=y
112
+
109
113
  import "@/tools/$module/all"
110
114
 
111
115
  for tool in "${tools[@]}"; do
@@ -3,6 +3,7 @@
3
3
  confirm_remove_paths() {
4
4
  local label="$1"
5
5
  shift
6
+ local default="${KARNEL_REMOVE_DEFAULT:-n}"
6
7
  local answer path
7
8
  local -a existing=()
8
9
 
@@ -11,7 +12,7 @@ confirm_remove_paths() {
11
12
  done
12
13
  (( ${#existing[@]} > 0 )) || return 0
13
14
 
14
- read_confirm_default "Remove $label configuration and data?" "n" answer || return 2
15
+ read_confirm_default "Remove $label configuration and data?" "$default" answer || return 2
15
16
  [[ "$answer" == "y" ]] || return 2
16
17
 
17
18
  rm -rf -- "${existing[@]}"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "karnel-termux",
3
- "version": "4.17.0",
3
+ "version": "4.17.2",
4
4
  "description": "Modular Dev Environment for Termux — install languages, databases, AI agents, editors, and more with one command",
5
5
  "bin": {
6
6
  "karnel": "karnel/bin/karnel"