karnel-termux 4.9.5 → 4.10.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 CHANGED
@@ -8,7 +8,7 @@
8
8
 
9
9
  <p align="center">
10
10
  <a href="https://github.com/israelmarques1024-dotcom/karnel-termux">
11
- <img src="https://img.shields.io/badge/version-4.9.0-0078D4?style=for-the-badge" alt="Version">
11
+ <img src="https://img.shields.io/badge/version-4.9.6-0078D4?style=for-the-badge" alt="Version">
12
12
  </a>
13
13
  <a href="https://www.npmjs.com/package/karnel-termux">
14
14
  <img src="https://img.shields.io/npm/v/karnel-termux?style=for-the-badge&logo=npm&color=cb3837" alt="npm">
@@ -44,6 +44,8 @@ Created by **Israel Marques**.
44
44
  - **Professional editor** — code-server (VS Code in browser)
45
45
  - **Second brain** — Memory system with AI search and idea graph
46
46
  - **Voice commands** — Speak to your AI agents
47
+ - **Plugin system** — Install community plugins from GitHub: `karnel plugin install <user/repo>`
48
+ - **Security tools** — Nmap, Hydra, SQLMap, Metasploit and more: `karnel install security`
47
49
 
48
50
  > [!IMPORTANT]
49
51
  > Designed exclusively for **Termux on Android**. Does not work on other platforms.
@@ -125,6 +127,34 @@ karnel
125
127
  | `utils` | fconv, notes, treex, passman, applaunch, qrcode, zork and more | `karnel install utils` |
126
128
  | `osint` | Robin v2.8, Tor, Streamlit, and LLM providers | `karnel install osint` |
127
129
  | `voice` | Speech-to-agent through Termux:API | `karnel install voice` |
130
+ | `plugin` | Plugin manager — install extensions from GitHub | `karnel install plugin` |
131
+ | `security` | Nmap, Hydra, SQLMap, Metasploit, Gobuster and more | `karnel install security` |
132
+
133
+ ---
134
+
135
+ ## Plugin System
136
+
137
+ Extend Karnel with community plugins from GitHub:
138
+
139
+ ```bash
140
+ karnel plugin install <user/repo> # Install a plugin
141
+ karnel plugin list # List installed plugins
142
+ karnel plugin remove <name> # Uninstall a plugin
143
+ karnel plugin create <name> # Scaffold a new plugin
144
+ ```
145
+
146
+ Each plugin needs a `karnel-plugin.json` manifest at the root of its repo. Commands go in a `commands/` directory — Karnel discovers them automatically at startup.
147
+
148
+ ## Security Tools
149
+
150
+ Install security auditing tools:
151
+
152
+ ```bash
153
+ karnel install security # Install all
154
+ karnel install security --nmap --hydra --sqlmap # Install specific ones
155
+ ```
156
+
157
+ Includes: Nmap, Hydra, Nikto, SQLMap, Gobuster, Dirb, WPScan, John the Ripper, Aircrack-ng, Metasploit.
128
158
 
129
159
  ---
130
160
 
@@ -152,6 +152,14 @@ _install_full_module() {
152
152
  import "@/modules/utils"
153
153
  install_utils
154
154
  ;;
155
+ plugin)
156
+ import "@/modules/plugin"
157
+ install_plugin_module
158
+ ;;
159
+ security)
160
+ import "@/modules/security"
161
+ install_security
162
+ ;;
155
163
  *)
156
164
  log_warn "Unknown install target: $target"
157
165
  echo "Run 'karnel install' to see available targets"
@@ -806,6 +814,9 @@ _install_specific_tools() {
806
814
  utils)
807
815
  _batch_tool_action "utils" "install" "${tools[@]}"
808
816
  ;;
817
+ security)
818
+ _batch_tool_action "security" "install" "${tools[@]}"
819
+ ;;
809
820
  *)
810
821
  log_warn "Unknown install target: $module"
811
822
  echo "Run 'karnel install' to see available targets"
@@ -76,6 +76,14 @@ list_main() {
76
76
  utils)
77
77
  _list_utils
78
78
  ;;
79
+ plugin)
80
+ import "@/tools/plugins/install"
81
+ echo "Installed Plugins:"
82
+ _list_plugins
83
+ ;;
84
+ security)
85
+ _list_security
86
+ ;;
79
87
  voice)
80
88
  _list_voice
81
89
  ;;
@@ -523,6 +531,30 @@ _grep_config() {
523
531
  fi
524
532
  }
525
533
 
534
+ # ===== LIST SECURITY =====
535
+ _list_security() {
536
+ echo
537
+ box "Security Tools"
538
+ echo
539
+ log_info "Available security tools:"
540
+ echo
541
+ table_start "Tool" "Install Flag" "Command" "Status"
542
+ table_row "Nmap" "--nmap" "nmap" "$(_check_cmd "nmap")"
543
+ table_row "Hydra" "--hydra" "hydra" "$(_check_cmd "hydra")"
544
+ table_row "Nikto" "--nikto" "nikto" "$(_check_cmd "nikto")"
545
+ table_row "SQLMap" "--sqlmap" "sqlmap" "$(_check_cmd "sqlmap")"
546
+ table_row "Gobuster" "--gobuster" "gobuster" "$(_check_cmd "gobuster")"
547
+ table_row "Dirb" "--dirb" "dirb" "$(_check_cmd "dirb")"
548
+ table_row "WPScan" "--wpscan" "wpscan" "$(_check_cmd "wpscan")"
549
+ table_row "John the Ripper" "--john" "john" "$(_check_cmd "john")"
550
+ table_row "Aircrack-ng" "--aircrack-ng" "aircrack-ng" "$(_check_cmd "aircrack-ng")"
551
+ table_row "Metasploit" "--metasploit" "msfconsole" "$(_check_cmd "msfconsole")"
552
+ table_end
553
+ echo
554
+ log_info "Install all: ${D_CYAN}karnel install security${NC}"
555
+ echo
556
+ }
557
+
526
558
  # ===== LIST VOICE =====
527
559
  _list_voice() {
528
560
  echo
@@ -63,6 +63,12 @@ open_main() {
63
63
  voice)
64
64
  url="$OPEN_BASE_URL/karnel/voice"
65
65
  ;;
66
+ plugin)
67
+ url="$OPEN_BASE_URL/karnel/plugin"
68
+ ;;
69
+ security)
70
+ url="$OPEN_BASE_URL/karnel/security"
71
+ ;;
66
72
  osint|robin)
67
73
  url="$OPEN_BASE_URL/karnel/osint"
68
74
  ;;
@@ -0,0 +1,147 @@
1
+ import "@/tools/plugins/install"
2
+
3
+ plugin_main() {
4
+ if [[ $# -eq 0 ]]; then
5
+ plugin_help
6
+ return
7
+ fi
8
+
9
+ local subcmd="$1"
10
+ shift
11
+
12
+ case "$subcmd" in
13
+ install)
14
+ if [[ $# -lt 1 ]]; then
15
+ log_error "Usage: karnel plugin install <user/repo>"
16
+ return 1
17
+ fi
18
+ install_plugin "$1"
19
+ ;;
20
+ remove|uninstall)
21
+ if [[ $# -lt 1 ]]; then
22
+ log_error "Usage: karnel plugin remove <name>"
23
+ return 1
24
+ fi
25
+ uninstall_plugin "$1"
26
+ ;;
27
+ update)
28
+ if [[ $# -lt 1 ]]; then
29
+ log_error "Usage: karnel plugin update <name>"
30
+ return 1
31
+ fi
32
+ update_plugin "$1"
33
+ ;;
34
+ list|ls)
35
+ _list_plugins
36
+ ;;
37
+ search)
38
+ _search_plugins
39
+ ;;
40
+ create|scaffold)
41
+ if [[ $# -lt 1 ]]; then
42
+ log_error "Usage: karnel plugin create <name>"
43
+ return 1
44
+ fi
45
+ _scaffold_plugin "$1"
46
+ ;;
47
+ *)
48
+ log_error "Unknown plugin subcommand: $subcmd"
49
+ plugin_help
50
+ return 1
51
+ ;;
52
+ esac
53
+ }
54
+
55
+ plugin_help() {
56
+ echo
57
+ box "Plugin Manager"
58
+ echo
59
+ log_info "Usage: karnel plugin <subcommand> [options]"
60
+ echo
61
+ separator_section "Subcommands"
62
+ echo
63
+ printf " ${D_CYAN}%-20s${NC} %s\n" "install <user/repo>" "Install a plugin from GitHub"
64
+ printf " ${D_CYAN}%-20s${NC} %s\n" "remove <name>" "Uninstall a plugin"
65
+ printf " ${D_CYAN}%-20s${NC} %s\n" "update <name>" "Update a plugin"
66
+ printf " ${D_CYAN}%-20s${NC} %s\n" "list" "List installed plugins"
67
+ printf " ${D_CYAN}%-20s${NC} %s\n" "search" "Search available plugins in the registry"
68
+ printf " ${D_CYAN}%-20s${NC} %s\n" "create <name>" "Scaffold a new plugin"
69
+ echo
70
+ echo "Plugins are installed from GitHub repos."
71
+ echo "Each plugin must have a karnel-plugin.json manifest at root."
72
+ echo
73
+ separator_section "Examples"
74
+ echo
75
+ echo " karnel plugin install username/my-karnel-plugin"
76
+ echo " karnel plugin list"
77
+ echo " karnel plugin remove my-karnel-plugin"
78
+ echo " karnel plugin search"
79
+ echo " karnel plugin create my-plugin"
80
+ }
81
+
82
+ _search_plugins() {
83
+ local registry_url="https://raw.githubusercontent.com/israelmarques1024-dotcom/karnel-plugins/main/registry.json"
84
+ log_info "Fetching plugin registry..."
85
+ local data
86
+ data="$(curl -sfL "$registry_url" 2>/dev/null)" || {
87
+ log_error "Failed to fetch registry. Check internet connection."
88
+ return 1
89
+ }
90
+ local count
91
+ count="$(echo "$data" | sed -n 's/.*"plugins": \[\(.*\)\]/\1/p' | grep -o '"name"' | wc -l)"
92
+ if [[ "$count" -eq 0 ]]; then
93
+ echo " No plugins available in the registry yet."
94
+ echo " Be the first: https://github.com/israelmarques1024-dotcom/karnel-plugins"
95
+ return 0
96
+ fi
97
+ echo
98
+ box "Available Plugins ($count)"
99
+ echo
100
+ local names
101
+ names="$(echo "$data" | sed 's/.*"plugins":\[//;s/\]$//' | tr '}' '\n' | while IFS= read -r entry; do
102
+ [[ -z "$entry" ]] && continue
103
+ local n d r c
104
+ n="$(echo "$entry" | sed -n 's/.*"name":"\([^"]*\)".*/\1/p')"
105
+ d="$(echo "$entry" | sed -n 's/.*"description":"\([^"]*\)".*/\1/p')"
106
+ r="$(echo "$entry" | sed -n 's/.*"repo":"\([^"]*\)".*/\1/p')"
107
+ printf " ${D_GREEN}%-20s${NC} %s\n" "$n" "$d"
108
+ printf " ${D_CYAN} install:${NC} karnel plugin install %s\n" "$r"
109
+ done)"
110
+ echo "$names"
111
+ echo
112
+ }
113
+
114
+ _scaffold_plugin() {
115
+ local name="$1"
116
+ local dir="$(_plugin_dir "$name")"
117
+
118
+ if [[ -d "$dir" ]]; then
119
+ log_error "Plugin '$name' already exists"
120
+ return 1
121
+ fi
122
+
123
+ mkdir -p "$dir/commands" "$dir/tools"
124
+
125
+ cat > "$dir/karnel-plugin.json" <<EOF
126
+ {
127
+ "name": "$name",
128
+ "version": "1.0.0",
129
+ "description": "My Karnel plugin",
130
+ "commands": ["hello"],
131
+ "tools": []
132
+ }
133
+ EOF
134
+
135
+ cat > "$dir/commands/hello.sh" <<'EOF'
136
+ hello_main() {
137
+ echo "Hello from plugin '$1'!"
138
+ echo "Args: $*"
139
+ }
140
+ EOF
141
+
142
+ chmod +x "$dir/commands/"*.sh
143
+ log_success "Plugin '$name' scaffolded at $dir"
144
+ echo "Edit $dir/karnel-plugin.json to configure"
145
+ echo "Add commands in $dir/commands/"
146
+ echo "Run: karnel hello"
147
+ }
@@ -143,6 +143,14 @@ _reinstall_full_module() {
143
143
  import "@/modules/utils"
144
144
  reinstall_utils
145
145
  ;;
146
+ plugin)
147
+ import "@/modules/plugin"
148
+ reinstall_plugin_module
149
+ ;;
150
+ security)
151
+ import "@/modules/security"
152
+ reinstall_security
153
+ ;;
146
154
  *)
147
155
  log_warn "Unknown reinstall target: $target"
148
156
  echo "Run 'karnel reinstall' to see available targets"
@@ -137,6 +137,14 @@ _uninstall_full_module() {
137
137
  import "@/modules/utils"
138
138
  uninstall_utils
139
139
  ;;
140
+ plugin)
141
+ import "@/modules/plugin"
142
+ uninstall_plugin_module
143
+ ;;
144
+ security)
145
+ import "@/modules/security"
146
+ uninstall_security
147
+ ;;
140
148
  games)
141
149
  import "@/tools/games/all"
142
150
  uninstall_all_games
@@ -143,6 +143,14 @@ _update_full_module() {
143
143
  import "@/modules/utils"
144
144
  update_utils
145
145
  ;;
146
+ plugin)
147
+ import "@/modules/plugin"
148
+ update_plugin_module
149
+ ;;
150
+ security)
151
+ import "@/modules/security"
152
+ update_security
153
+ ;;
146
154
  deploy)
147
155
  import "@/modules/deploy"
148
156
  update_deploy
@@ -36,16 +36,21 @@ karnel_main() {
36
36
 
37
37
  local command_file="$KARNEL_PATH/cli/commands/$cmd.sh"
38
38
 
39
- # verificar si existe el comando
40
39
  if [[ -f "$command_file" ]]; then
41
40
  import "@/cli/commands/$cmd"
42
41
  "${cmd}_main" "$@"
43
- else
44
- log_error "Command not found: $cmd"
45
- echo
46
- karnel_help
47
- return 1
42
+ return $?
48
43
  fi
44
+
45
+ import "@/tools/plugins/install"
46
+ if _plugin_dispatch "$cmd" "$@"; then
47
+ return $?
48
+ fi
49
+
50
+ log_error "Command not found: $cmd"
51
+ echo
52
+ karnel_help
53
+ return 1
49
54
  }
50
55
 
51
56
  karnel_help() {
@@ -72,6 +77,7 @@ karnel_help() {
72
77
  printf " ${D_CYAN}%-12s${NC} %s\n" "list" "List available tools in a module"
73
78
  printf " ${D_CYAN}%-12s${NC} %s\n" "open" "Open docs in browser for a module"
74
79
  printf " ${D_CYAN}%-12s${NC} %s\n" "pg" "PostgreSQL database manager"
80
+ printf " ${D_CYAN}%-12s${NC} %s\n" "plugin" "Plugin manager — install, list, remove"
75
81
  printf " ${D_CYAN}%-12s${NC} %s\n" "reinstall" "Uninstall + install a module"
76
82
  printf " ${D_CYAN}%-12s${NC} %s\n" "restore" "Restore Termux from a backup"
77
83
  printf " ${D_CYAN}%-12s${NC} %s\n" "robin" "Dark Web OSINT tool (Tor + LLM)"
@@ -109,6 +115,8 @@ karnel_help() {
109
115
  printf " ${D_GREEN}%-10s${NC} %s\n" "npm" "Node.js global npm packages"
110
116
  printf " ${D_GREEN}%-10s${NC} %s\n" "osint" "OSINT Tools (Robin — Dark Web + LLM)"
111
117
  printf " ${D_GREEN}%-10s${NC} %s\n" "network" "Network tools (Dark Web, DedSec Network)"
118
+ printf " ${D_GREEN}%-10s${NC} %s\n" "plugin" "Plugin manager — install plugins from GitHub"
119
+ printf " ${D_GREEN}%-10s${NC} %s\n" "security" "Nmap, Hydra, Metasploit, SQLMap, Gobuster, etc."
112
120
  printf " ${D_GREEN}%-10s${NC} %s\n" "shell" "ZSH + Oh My Zsh + 10 plugins"
113
121
  printf " ${D_GREEN}%-10s${NC} %s\n" "ui" "Font, Cursor, Extra-keys, Banner"
114
122
  printf " ${D_GREEN}%-10s${NC} %s\n" "voice" "Speech-to-agent via microphone"
@@ -0,0 +1,34 @@
1
+ install_plugin_module() {
2
+ separator
3
+ box "Plugin Manager"
4
+ separator
5
+
6
+ log_info "Plugin system is built-in. No additional installation needed."
7
+ echo
8
+ log_info "Installing example plugin..."
9
+ install_plugin "israelmarques1024-dotcom/karnel-termux" || true
10
+ }
11
+
12
+ uninstall_plugin_module() {
13
+ log_info "Plugin system is built-in. Nothing to uninstall."
14
+ }
15
+
16
+ update_plugin_module() {
17
+ log_info "Updating all plugins..."
18
+ mkdir -p "$PLUGINS_DIR"
19
+ for dir in "$PLUGINS_DIR"/*/; do
20
+ [[ -d "$dir" ]] || continue
21
+ local name; name="$(basename "$dir")"
22
+ update_plugin "$name"
23
+ done
24
+ }
25
+
26
+ reinstall_plugin_module() {
27
+ log_info "Reinstalling all plugins..."
28
+ mkdir -p "$PLUGINS_DIR"
29
+ for dir in "$PLUGINS_DIR"/*/; do
30
+ [[ -d "$dir" ]] || continue
31
+ local name; name="$(basename "$dir")"
32
+ reinstall_plugin "$name"
33
+ done
34
+ }
@@ -0,0 +1,22 @@
1
+ install_security() {
2
+ separator
3
+ box "Installing Security Tools"
4
+ separator
5
+ import "@/tools/security/all"
6
+ install_all_security
7
+ }
8
+
9
+ uninstall_security() {
10
+ import "@/tools/security/all"
11
+ uninstall_all_security
12
+ }
13
+
14
+ update_security() {
15
+ import "@/tools/security/all"
16
+ update_all_security
17
+ }
18
+
19
+ reinstall_security() {
20
+ import "@/tools/security/all"
21
+ reinstall_all_security
22
+ }
@@ -3,54 +3,18 @@
3
3
  import "@/utils/log"
4
4
  import "@/utils/version"
5
5
  import "@/utils/colors"
6
+ import "@/utils/proot"
7
+ import "@/utils/install"
6
8
 
7
9
  LOG_FILE="$KARNEL_CACHE/install_ai.log"
8
10
  OPENCODE_DATA_DIR="$HOME/.local/share/karnel-data/opencode"
9
11
 
10
- _opencode_detect_ubuntu_root() {
11
- local root
12
- root="$(find /data/data/com.termux -maxdepth 10 -type d \
13
- -name "rootfs" -path "*/containers/ubuntu/*" 2>/dev/null | head -1)"
14
-
15
- if [ -z "$root" ]; then
16
- root="$(find /data/data/com.termux -maxdepth 10 -type d \
17
- -name "ubuntu" -path "*/installed-rootfs/*" 2>/dev/null | head -1)"
18
- fi
19
-
20
- echo "$root"
21
- }
22
-
23
- _opencode_proot_ubuntu() {
24
- proot-distro login \
25
- --shared-tmp \
26
- ubuntu \
27
- -- "$@"
28
- }
29
-
30
- _get_latest_opencode_version() {
31
- curl -fsSL https://api.github.com/repos/anomalyco/opencode/releases/latest |
32
- grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/'
12
+ _install_opencode_deps() {
13
+ loading "Installing glibc and dependencies" _install_opencode_deps_impl
33
14
  }
34
15
 
35
- _opencode_install_deps_native() {
36
- loading "Installing glibc and dependencies" _opencode_install_deps_native_impl
37
- }
38
-
39
- _opencode_install_deps_native_impl() {
40
- if [[ ! -f $PREFIX/etc/apt/sources.list.d/glibc.list ]]; then
41
- if ! pkg install glibc-repo -y &>>"$LOG_FILE"; then
42
- log_error "Failed to install glibc-repo"
43
- return 1
44
- fi
45
- fi
46
-
47
- if [[ ! -f $PREFIX/glibc/lib/libc.so.6 ]]; then
48
- if ! pkg install glibc -y &>>"$LOG_FILE"; then
49
- log_error "Failed to install glibc"
50
- return 1
51
- fi
52
- fi
53
-
16
+ _install_opencode_deps_impl() {
17
+ install_glibc || return 1
54
18
  declare -A DEPS=(
55
19
  ["git"]="git"
56
20
  ["ripgrep"]="rg"
@@ -61,18 +25,7 @@ _opencode_install_deps_native_impl() {
61
25
  ["curl"]="curl"
62
26
  ["tar"]="tar"
63
27
  )
64
-
65
- local pkg_name bin_name
66
- for pkg_name in "${!DEPS[@]}"; do
67
- bin_name="${DEPS[$pkg_name]}"
68
- if ! command -v "$bin_name" &>/dev/null; then
69
- if ! pkg install "$pkg_name" -y &>>"$LOG_FILE"; then
70
- log_error "Failed to install $pkg_name"
71
- return 1
72
- fi
73
- fi
74
- done
75
-
28
+ install_deps DEPS || return 1
76
29
  return 0
77
30
  }
78
31
 
@@ -82,34 +35,21 @@ _download_opencode_binary() {
82
35
 
83
36
  _download_opencode_binary_impl() {
84
37
  local latest_version
85
- latest_version=$(_get_latest_opencode_version)
38
+ latest_version=$(github_latest_tag anomalyco/opencode)
86
39
  if [ -z "$latest_version" ]; then
87
40
  log_error "Failed to fetch latest OpenCode version"
88
41
  return 1
89
42
  fi
90
43
 
91
44
  mkdir -p "$OPENCODE_DATA_DIR"
92
-
93
- local tarball="opencode-linux-arm64.tar.gz"
94
- local download_url="https://github.com/anomalyco/opencode/releases/download/$latest_version/$tarball"
95
-
96
- if ! curl -fsSL "$download_url" -o "$OPENCODE_DATA_DIR/$tarball" &>>"$LOG_FILE"; then
97
- log_error "Failed to download OpenCode binary"
98
- return 1
99
- fi
100
-
101
- if ! tar -zxf "$OPENCODE_DATA_DIR/$tarball" -C "$OPENCODE_DATA_DIR" &>>"$LOG_FILE"; then
102
- log_error "Failed to extract OpenCode binary"
103
- return 1
104
- fi
105
-
106
- rm -f "$OPENCODE_DATA_DIR/$tarball"
45
+ github_download_and_extract \
46
+ anomalyco/opencode "$latest_version" \
47
+ "opencode-linux-arm64.tar.gz" "$OPENCODE_DATA_DIR" || return 1
107
48
 
108
49
  if [ ! -f "$OPENCODE_DATA_DIR/opencode" ]; then
109
50
  log_error "OpenCode binary not found after extraction"
110
51
  return 1
111
52
  fi
112
-
113
53
  chmod +x "$OPENCODE_DATA_DIR/opencode"
114
54
  return 0
115
55
  }
@@ -119,23 +59,14 @@ _compile_opencode_helper() {
119
59
  }
120
60
 
121
61
  _compile_opencode_helper_impl() {
122
- local HELPER_SRC="$KARNEL_PATH/tools/ai/opencode/helper/opencode_helper.c"
123
- if [ ! -f "$HELPER_SRC" ]; then
124
- log_error "Helper source not found at $HELPER_SRC"
125
- return 1
126
- fi
127
-
128
- if ! cc -O2 -o "$PREFIX/bin/opencode" "$HELPER_SRC" &>>"$LOG_FILE"; then
129
- log_error "Failed to compile opencode helper"
130
- return 1
131
- fi
132
-
133
- chmod +x "$PREFIX/bin/opencode"
62
+ compile_helper \
63
+ "$KARNEL_PATH/tools/ai/opencode/helper/opencode_helper.c" \
64
+ "$PREFIX/bin/opencode" || return 1
134
65
  return 0
135
66
  }
136
67
 
137
68
  _install_opencode_native() {
138
- _opencode_install_deps_native || return 1
69
+ _install_opencode_deps || return 1
139
70
  _download_opencode_binary || return 1
140
71
  _compile_opencode_helper || return 1
141
72
  log_success "OpenCode installed natively"
@@ -148,48 +79,35 @@ _install_opencode_proot() {
148
79
 
149
80
  _install_opencode_proot_impl() {
150
81
  mkdir -p "$(dirname "$LOG_FILE")"
82
+ proot_install_ubuntu ubuntu:24.04
151
83
 
152
- if ! command -v proot-distro &>/dev/null; then
153
- pkg install proot-distro -y &>>"$LOG_FILE"
154
- fi
155
-
156
- if [ ! -d "$(_opencode_detect_ubuntu_root)" ]; then
157
- proot-distro install ubuntu:24.04 &>>"$LOG_FILE"
158
- fi
159
-
160
- _opencode_proot_ubuntu /bin/bash -c \
84
+ proot_ubuntu /bin/bash -c \
161
85
  'apt-get update && apt-get upgrade -y && apt-get install -y curl ca-certificates' \
162
86
  &>>"$LOG_FILE"
163
87
 
164
- _opencode_proot_ubuntu /bin/bash -c '
165
- export SHELL=/bin/bash
166
- export TMPDIR=/tmp
167
- export HOME=/root
168
- curl -fsSL https://opencode.ai/install | bash -s -- --no-modify-path
169
- ' &>>"$LOG_FILE"
88
+ proot_ubuntu /bin/bash -c '
89
+ export SHELL=/bin/bash
90
+ export TMPDIR=/tmp
91
+ export HOME=/root
92
+ curl -fsSL https://opencode.ai/install | bash -s -- --no-modify-path
93
+ ' &>>"$LOG_FILE"
170
94
 
171
95
  local ubuntu_root
172
- ubuntu_root="$(_opencode_detect_ubuntu_root)"
173
-
96
+ ubuntu_root="$(detect_ubuntu_root)"
174
97
  if [ -z "$ubuntu_root" ]; then
175
98
  log_error "Ubuntu rootfs not found"
176
99
  return 1
177
100
  fi
178
101
 
179
102
  local opencode_bin="$ubuntu_root/root/.opencode/bin/opencode"
180
-
181
103
  if [ ! -f "$opencode_bin" ]; then
182
104
  log_error "OpenCode binary not found after install"
183
105
  return 1
184
106
  fi
185
107
 
186
- local wrapper_src="$KARNEL_PATH/tools/ai/opencode/bin/opencode"
187
- if [ ! -f "$wrapper_src" ]; then
188
- log_error "Wrapper template not found at $wrapper_src"
189
- return 1
190
- fi
191
- sed "s|__UBUNTU_ROOTFS__|$ubuntu_root|g" "$wrapper_src" >"$PREFIX/bin/opencode"
192
- chmod +x "$PREFIX/bin/opencode"
108
+ generate_wrapper \
109
+ "$KARNEL_PATH/tools/ai/opencode/bin/opencode" \
110
+ "$ubuntu_root" "$PREFIX/bin/opencode" || return 1
193
111
 
194
112
  if ! grep -q '.opencode/bin' "$ubuntu_root/root/.bashrc" 2>/dev/null; then
195
113
  printf '\n# opencode\nexport PATH=/root/.opencode/bin:$PATH\n' >>"$ubuntu_root/root/.bashrc"
@@ -205,18 +123,13 @@ install_opencode() {
205
123
  fi
206
124
 
207
125
  log_info "Select installation method for OpenCode:"
208
-
209
126
  read_select "Installation method" SELECTED_METHOD \
210
127
  "Native (recommended) - Compile with glibc support" \
211
128
  "Proot-distro (alternative) - Ubuntu container"
212
129
 
213
130
  case "$SELECTED_METHOD" in
214
- *Native*)
215
- _install_opencode_native
216
- ;;
217
- *Proot-distro*)
218
- _install_opencode_proot
219
- ;;
131
+ *Native*) _install_opencode_native ;;
132
+ *Proot-distro*) _install_opencode_proot ;;
220
133
  esac
221
134
  }
222
135
 
@@ -236,13 +149,11 @@ uninstall_opencode() {
236
149
  return 0
237
150
  fi
238
151
 
239
- _opencode_proot_ubuntu /bin/bash -c 'rm -rf /root/.opencode' &>>"$LOG_FILE"
240
-
241
- local ubuntu_bashrc
242
- ubuntu_bashrc="$(_opencode_detect_ubuntu_root)/root/.bashrc"
243
-
244
- if [ -f "$ubuntu_bashrc" ]; then
245
- sed -i '/# opencode/d; /export PATH=\/root\/.opencode\/bin/d' "$ubuntu_bashrc"
152
+ proot_ubuntu /bin/bash -c 'rm -rf /root/.opencode' &>>"$LOG_FILE"
153
+ local ubuntu_root
154
+ ubuntu_root="$(detect_ubuntu_root)/root/.bashrc"
155
+ if [ -f "$ubuntu_root" ]; then
156
+ sed -i '/# opencode/d; /export PATH=\/root\/.opencode\/bin/d' "$ubuntu_root"
246
157
  fi
247
158
 
248
159
  if rm -f "$PREFIX/bin/opencode" &>>"$LOG_FILE"; then
@@ -264,9 +175,8 @@ _do_update_opencode() {
264
175
  return $?
265
176
  fi
266
177
 
267
- _opencode_proot_ubuntu /bin/bash -c 'rm -rf /root/.opencode' &>>"$LOG_FILE"
268
-
269
- _opencode_proot_ubuntu /bin/bash -c '
178
+ proot_ubuntu /bin/bash -c 'rm -rf /root/.opencode' &>>"$LOG_FILE"
179
+ proot_ubuntu /bin/bash -c '
270
180
  export SHELL=/bin/bash
271
181
  export TMPDIR=/tmp
272
182
  export HOME=/root
@@ -274,14 +184,11 @@ _do_update_opencode() {
274
184
  ' &>>"$LOG_FILE"
275
185
 
276
186
  local ubuntu_root
277
- ubuntu_root="$(_opencode_detect_ubuntu_root)"
278
- local opencode_bin="$ubuntu_root/root/.opencode/bin/opencode"
279
-
280
- if [ ! -f "$opencode_bin" ]; then
187
+ ubuntu_root="$(detect_ubuntu_root)"
188
+ if [ -z "$ubuntu_root" ] || [ ! -f "$ubuntu_root/root/.opencode/bin/opencode" ]; then
281
189
  log_error "OpenCode binary not found after update"
282
190
  return 1
283
191
  fi
284
-
285
192
  return 0
286
193
  }
287
194
 
@@ -0,0 +1,150 @@
1
+ PLUGINS_DIR="${KARNEL_DATA}/plugins"
2
+
3
+ _plugin_dir() {
4
+ echo "$PLUGINS_DIR/$1"
5
+ }
6
+
7
+ _plugin_manifest() {
8
+ echo "$(_plugin_dir "$1")/karnel-plugin.json"
9
+ }
10
+
11
+ _plugin_installed() {
12
+ [[ -f "$(_plugin_manifest "$1")" ]]
13
+ }
14
+
15
+ _plugin_name_from_repo() {
16
+ local repo="$1"
17
+ basename "$repo" | sed 's/\.git$//'
18
+ }
19
+
20
+ install_plugin() {
21
+ local repo="$1"
22
+ local name
23
+ name="$(_plugin_name_from_repo "$repo")"
24
+
25
+ if _plugin_installed "$name"; then
26
+ log_info "Plugin '$name' is already installed"
27
+ return 2
28
+ fi
29
+
30
+ mkdir -p "$PLUGINS_DIR"
31
+ log_info "Installing plugin '$name' from $repo..."
32
+
33
+ if ! git clone "https://github.com/$repo.git" "$(_plugin_dir "$name")" 2>/dev/null; then
34
+ log_error "Failed to clone $repo"
35
+ return 1
36
+ fi
37
+
38
+ if [[ ! -f "$(_plugin_manifest "$name")" ]]; then
39
+ log_warn "Plugin '$name' has no karnel-plugin.json manifest"
40
+ log_info "Creating default manifest..."
41
+ cat > "$(_plugin_manifest "$name")" <<MANIFEST
42
+ {
43
+ "name": "$name",
44
+ "version": "1.0.0",
45
+ "description": "Plugin installed from $repo",
46
+ "commands": [],
47
+ "tools": []
48
+ }
49
+ MANIFEST
50
+ fi
51
+
52
+ chmod +x "$(_plugin_dir "$name")/commands/"*.sh 2>/dev/null || true
53
+
54
+ log_success "Plugin '$name' installed"
55
+ return 0
56
+ }
57
+
58
+ uninstall_plugin() {
59
+ local name="$1"
60
+
61
+ if ! _plugin_installed "$name"; then
62
+ log_info "Plugin '$name' is not installed"
63
+ return 2
64
+ fi
65
+
66
+ log_info "Removing plugin '$name'..."
67
+ rm -rf "$(_plugin_dir "$name")"
68
+ log_success "Plugin '$name' removed"
69
+ return 0
70
+ }
71
+
72
+ update_plugin() {
73
+ local name="$1"
74
+
75
+ if ! _plugin_installed "$name"; then
76
+ log_info "Plugin '$name' is not installed"
77
+ return 2
78
+ fi
79
+
80
+ log_info "Updating plugin '$name'..."
81
+ if ! git -C "$(_plugin_dir "$name")" pull 2>/dev/null; then
82
+ log_error "Failed to update '$name'"
83
+ return 1
84
+ fi
85
+
86
+ log_success "Plugin '$name' updated"
87
+ return 0
88
+ }
89
+
90
+ reinstall_plugin() {
91
+ local name="$1"
92
+ uninstall_plugin "$name"
93
+ local repo
94
+ repo="$(git -C "$(_plugin_dir "$name")" remote get-url origin 2>/dev/null || echo "")"
95
+ repo="${repo#https://github.com/}"
96
+ repo="${repo%.git}"
97
+ if [[ -n "$repo" ]]; then
98
+ install_plugin "$repo"
99
+ else
100
+ log_error "Cannot determine repository for '$name'"
101
+ return 1
102
+ fi
103
+ }
104
+
105
+ _list_plugins() {
106
+ mkdir -p "$PLUGINS_DIR"
107
+ local found=0
108
+ for dir in "$PLUGINS_DIR"/*/; do
109
+ [[ -d "$dir" ]] || continue
110
+ local name
111
+ name="$(basename "$dir")"
112
+ if [[ -f "$dir/karnel-plugin.json" ]]; then
113
+ local desc
114
+ desc="$(sed -n 's/.*"description": "\(.*\)",/\1/p' "$dir/karnel-plugin.json" 2>/dev/null || echo "No description")"
115
+ printf " ${D_GREEN}%-20s${NC} %s\n" "$name" "$desc"
116
+ found=1
117
+ fi
118
+ done
119
+ [[ $found -eq 0 ]] && echo " No plugins installed"
120
+ }
121
+
122
+ _plugin_commands() {
123
+ mkdir -p "$PLUGINS_DIR"
124
+ for dir in "$PLUGINS_DIR"/*/; do
125
+ [[ -d "$dir" ]] || continue
126
+ local cmd_dir="$dir/commands"
127
+ [[ -d "$cmd_dir" ]] || continue
128
+ for cmd_file in "$cmd_dir"/*.sh; do
129
+ [[ -f "$cmd_file" ]] || continue
130
+ local cmd_name
131
+ cmd_name="$(basename "$cmd_file" .sh)"
132
+ echo "$cmd_file:$cmd_name"
133
+ done
134
+ done
135
+ }
136
+
137
+ _plugin_dispatch() {
138
+ local cmd="$1"
139
+ shift
140
+ while IFS=':' read -r cmd_file cmd_name; do
141
+ if [[ "$cmd_name" == "$cmd" ]]; then
142
+ source "$cmd_file"
143
+ if declare -f "${cmd_name}_main" &>/dev/null; then
144
+ "${cmd_name}_main" "$@"
145
+ return $?
146
+ fi
147
+ fi
148
+ done < <(_plugin_commands)
149
+ return 1
150
+ }
@@ -0,0 +1,42 @@
1
+ SECURITY_TOOLS=(
2
+ "nmap"
3
+ "hydra"
4
+ "nikto"
5
+ "sqlmap"
6
+ "gobuster"
7
+ "dirb"
8
+ "wpscan"
9
+ "john"
10
+ "aircrack-ng"
11
+ "metasploit"
12
+ )
13
+
14
+ for _tool in "${SECURITY_TOOLS[@]}"; do
15
+ source "$(dirname "${BASH_SOURCE[0]}")/$_tool/install.sh"
16
+ done
17
+
18
+ _batch_security() {
19
+ local action="$1"
20
+ local action_past="$2"
21
+ local -n count_var="$3"
22
+ for tool in "${SECURITY_TOOLS[@]}"; do
23
+ func_name="${action}_${tool//-/_}"
24
+ loading "${action_past^}ing ${tool}" "$func_name"
25
+ case $? in
26
+ 0) ((count_var++)) ;;
27
+ 2) ((skipped_count++)) ;;
28
+ 1) ((failed_count++)) ;;
29
+ esac
30
+ done
31
+ }
32
+
33
+ install_all_security() {
34
+ local installed_count=0 skipped_count=0 failed_count=0
35
+ _batch_security "install" "install" installed_count
36
+ echo
37
+ log_success "Security: $installed_count installed, $skipped_count skipped, $failed_count failed"
38
+ }
39
+
40
+ uninstall_all_security() { _batch_security "uninstall" "uninstall" _unused; }
41
+ update_all_security() { _batch_security "update" "update" _unused; }
42
+ reinstall_all_security() { _batch_security "reinstall" "reinstall" _unused; }
@@ -0,0 +1,35 @@
1
+ # Template for security tool installers.
2
+ # Copy this file and replace TOOL_NAME with the actual tool name.
3
+
4
+ _TOOL="tool"
5
+ _TOOL_PKG="$1"
6
+
7
+ install_${_TOOL}() {
8
+ if command -v "$_TOOL" &>/dev/null; then
9
+ log_info "$_TOOL is already installed"
10
+ return 2
11
+ fi
12
+ log_info "Installing $_TOOL..."
13
+ if pkg install -y "$_TOOL_PKG" 2>/dev/null || apt install -y "$_TOOL_PKG" 2>/dev/null; then
14
+ log_success "$_TOOL installed"
15
+ return 0
16
+ fi
17
+ log_error "Failed to install $_TOOL"
18
+ return 1
19
+ }
20
+
21
+ uninstall_${_TOOL}() {
22
+ log_info "Removing $_TOOL..."
23
+ pkg uninstall -y "$_TOOL_PKG" 2>/dev/null || true
24
+ log_success "$_TOOL removed"
25
+ }
26
+
27
+ update_${_TOOL}() {
28
+ log_info "$_TOOL updated via package manager"
29
+ return 2
30
+ }
31
+
32
+ reinstall_${_TOOL}() {
33
+ uninstall_$_TOOL
34
+ install_$_TOOL
35
+ }
@@ -103,23 +103,37 @@ _repeat() {
103
103
  # ================================================================
104
104
  # Counters
105
105
  # ================================================================
106
- _count_ai() { local c=0; for cmd in opencode claude gemini codex qwen vibe mimo hermes kimi ollama freebuff agy mmx pi engram codegraph command-code gentle-ai gga openclaude openclaw kiro crush odysseus kilocode kimchi cline omni-route ctx7 openspec; do command -v "$cmd" &>/dev/null && ((c++)); done; echo "$c"; }
107
- _count_lang() { local c=0; for cmd in node python python3 rustc go clang php perl; do command -v "$cmd" &>/dev/null && ((c++)); done; echo "$c"; }
108
- _count_db() { local c=0; command -v pg_ctl &>/dev/null && ((c++)); command -v mariadb &>/dev/null && ((c++)); command -v sqlite3 &>/dev/null && ((c++)); command -v mongod &>/dev/null && ((c++)); echo "$c"; }
109
- _uptime() {
110
- if [[ -f /proc/uptime ]]; then
111
- local secs; secs=$(awk '{print int($1)}' /proc/uptime 2>/dev/null)
112
- local d=$((secs/86400)) h=$((secs%86400/3600)) m=$((secs%3600/60))
113
- if (( d>0 )); then printf "%dd %dh" "$d" "$h"
114
- elif (( h>0 )); then printf "%dh %dm" "$h" "$m"
115
- else printf "%dm" "$m"; fi
116
- else echo "?"; fi
106
+ _count_ai() {
107
+ local reg="$KARNEL_PATH/tools/ai/all.sh"
108
+ local c=0
109
+ if [[ -f "$reg" ]]; then
110
+ while IFS= read -r line; do
111
+ [[ "$line" =~ ^[[:space:]]*\"([^\"]+)\" ]] || continue
112
+ local entry="${BASH_REMATCH[1]}"
113
+ local bin="${entry##*:}"
114
+ command -v "$bin" &>/dev/null && ((c++))
115
+ done < <(grep -E '^\s+"' "$reg" 2>/dev/null || true)
116
+ fi
117
+ echo "$c"
118
+ }
119
+ _count_lang() { local c=0; for cmd in node python rustc go clang php perl bun; do command -v "$cmd" &>/dev/null && ((c++)); done; echo "$c"; }
120
+ _count_db() { local c=0; command -v pg_ctl &>/dev/null && ((c++)); command -v mariadb &>/dev/null && ((c++)); command -v sqlite3 &>/dev/null && ((c++)); command -v mongod &>/dev/null && ((c++)); command -v redis-cli &>/dev/null && ((c++)); echo "$c"; }
121
+ _count_doctor() {
122
+ local doc_sh="$KARNEL_PATH/cli/commands/doctor/code_langs.sh"
123
+ if [[ -f "$doc_sh" ]]; then
124
+ local count; count=$(grep -c 'LANG_TOOLS\[' "$doc_sh" 2>/dev/null || echo 0)
125
+ echo "$count"
126
+ else echo "0"; fi
117
127
  }
118
- _ram_free() {
119
- if [[ -f /proc/meminfo ]]; then
120
- local kb; kb=$(grep MemAvailable /proc/meminfo 2>/dev/null | awk '{print $2}')
121
- [[ -n "$kb" ]] && echo "$((kb/1024))MB" || echo "?"
122
- else echo "?"; fi
128
+ _pg_status() {
129
+ if command -v pg_ctl &>/dev/null; then
130
+ local data="$KARNEL_DATA/pg/data"
131
+ if [[ -d "$data" ]] && pg_ctl status -D "$data" &>/dev/null 2>&1; then
132
+ echo "ON"
133
+ else
134
+ echo "OFF"
135
+ fi
136
+ else echo "—"; fi
123
137
  }
124
138
 
125
139
  # ================================================================
@@ -236,16 +250,16 @@ _render_frame_line() {
236
250
  # ================================================================
237
251
  # Panel
238
252
  # ================================================================
239
- PANEL_HEADERS=("AI" "Lang" "DB" "Up" "RAM")
240
- PANEL_ICONS=("◆" "</>" "⛁" "" "")
253
+ PANEL_HEADERS=("AI" "Lang" "DB" "Doctor" "PG")
254
+ PANEL_ICONS=("◆" "</>" "⛁" "" "🐘")
241
255
 
242
256
  _panel_value() {
243
257
  case $1 in
244
258
  0) echo "${KAI_VAL:-$(_count_ai)}" ;;
245
259
  1) echo "${KLANG_VAL:-$(_count_lang)}" ;;
246
260
  2) echo "${KDB_VAL:-$(_count_db)}" ;;
247
- 3) echo "${KUP_VAL:-$(_uptime)}" ;;
248
- 4) echo "${KRAM_VAL:-$(_ram_free)}" ;;
261
+ 3) echo "${KDOCTOR_VAL:-$(_count_doctor)}" ;;
262
+ 4) echo "${KPG_VAL:-$(_pg_status)}" ;;
249
263
  esac
250
264
  }
251
265
 
@@ -430,8 +444,8 @@ _render_animated() {
430
444
  KAI_VAL=$(_count_ai)
431
445
  KLANG_VAL=$(_count_lang)
432
446
  KDB_VAL=$(_count_db)
433
- KUP_VAL=$(_uptime)
434
- KRAM_VAL=$(_ram_free)
447
+ KDOCTOR_VAL=$(_count_doctor)
448
+ KPG_VAL=$(_pg_status)
435
449
 
436
450
  # Cache banner for clear() override (capture only, no terminal output)
437
451
  _banner_output=$(_render 2>/dev/null) || true
@@ -7,6 +7,9 @@ __KARNEL_BOOTSTRAP_LOADED=1
7
7
  # registro de imports
8
8
  declare -A __KARNEL_IMPORTED
9
9
 
10
+ # CAUTION: import() calls `source` from within a function. Any `declare` (without -g)
11
+ # in the sourced file creates LOCAL variables scoped to import(), not globals.
12
+ # Always use plain assignments (VAR=val) or `declare -g` for globals in sourced files.
10
13
  import() {
11
14
  local base="${KARNEL_PATH}"
12
15
  local resolved="${1//@/$base}.sh"
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env bash
2
2
 
3
- KARNEL_VERSION="4.9.5"
3
+ KARNEL_VERSION="4.10.0"
4
4
 
5
5
  # -------------------------
6
6
  # Directorios del usuario
@@ -26,6 +26,7 @@ KARNEL_CLI="$KARNEL_PATH/cli"
26
26
  KARNEL_TOOLS="$KARNEL_DATA/tools"
27
27
  KARNEL_RUN="$KARNEL_CACHE/run"
28
28
  KARNEL_LOGS="$KARNEL_CACHE/logs"
29
+ KARNEL_PLUGINS="$KARNEL_DATA/plugins"
29
30
 
30
31
  # -------------------------
31
32
  # Crear directorios
@@ -0,0 +1,107 @@
1
+ #!/usr/bin/env bash
2
+
3
+ LOG_FILE="${LOG_FILE:-$KARNEL_CACHE/install_ai.log}"
4
+
5
+ # ── Dependency installation ────────────────────────────────────
6
+
7
+ declare -A _INSTALL_DEPS_CACHE
8
+
9
+ install_deps() {
10
+ local -n deps=$1
11
+ local pkg_name bin_name
12
+ for pkg_name in "${!deps[@]}"; do
13
+ bin_name="${deps[$pkg_name]}"
14
+ if ! command -v "$bin_name" &>/dev/null; then
15
+ if ! pkg install "$pkg_name" -y &>>"$LOG_FILE"; then
16
+ log_error "Failed to install $pkg_name"
17
+ return 1
18
+ fi
19
+ fi
20
+ done
21
+ return 0
22
+ }
23
+
24
+ # ── Glibc installation ─────────────────────────────────────────
25
+
26
+ install_glibc() {
27
+ if [[ ! -f $PREFIX/etc/apt/sources.list.d/glibc.list ]]; then
28
+ if ! pkg install glibc-repo -y &>>"$LOG_FILE"; then
29
+ log_error "Failed to install glibc-repo"
30
+ return 1
31
+ fi
32
+ fi
33
+ if [[ ! -f $PREFIX/glibc/lib/libc.so.6 ]]; then
34
+ if ! pkg install glibc -y &>>"$LOG_FILE"; then
35
+ log_error "Failed to install glibc"
36
+ return 1
37
+ fi
38
+ fi
39
+ return 0
40
+ }
41
+
42
+ # ── GitHub release helpers ─────────────────────────────────────
43
+
44
+ github_latest_tag() {
45
+ local repo="$1"
46
+ curl -fsSL "https://api.github.com/repos/$repo/releases/latest" |
47
+ grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/'
48
+ }
49
+
50
+ github_download_release() {
51
+ local repo="$1" version="$2" asset="$3" outdir="$4"
52
+ local url="https://github.com/$repo/releases/download/$version/$asset"
53
+ local tmpfile="$outdir/$(basename "$asset")"
54
+ mkdir -p "$outdir"
55
+ if ! curl -fsSL "$url" -o "$tmpfile" &>>"$LOG_FILE"; then
56
+ log_error "Failed to download $asset from $repo"
57
+ return 1
58
+ fi
59
+ echo "$tmpfile"
60
+ }
61
+
62
+ extract_tarball() {
63
+ local tarball="$1" outdir="$2"
64
+ if ! tar -zxf "$tarball" -C "$outdir" &>>"$LOG_FILE"; then
65
+ log_error "Failed to extract $tarball"
66
+ return 1
67
+ fi
68
+ rm -f "$tarball"
69
+ return 0
70
+ }
71
+
72
+ github_download_and_extract() {
73
+ local repo="$1" version="$2" asset="$3" outdir="$4"
74
+ local tarball
75
+ tarball=$(github_download_release "$repo" "$version" "$asset" "$outdir") || return 1
76
+ extract_tarball "$tarball" "$outdir" || return 1
77
+ return 0
78
+ }
79
+
80
+ # ── C helper compilation ───────────────────────────────────────
81
+
82
+ compile_helper() {
83
+ local src="$1" out="$2"
84
+ if [ ! -f "$src" ]; then
85
+ log_error "Helper source not found at $src"
86
+ return 1
87
+ fi
88
+ if ! cc -O2 -o "$out" "$src" &>>"$LOG_FILE"; then
89
+ log_error "Failed to compile $src"
90
+ return 1
91
+ fi
92
+ chmod +x "$out"
93
+ return 0
94
+ }
95
+
96
+ # ── Wrapper generator ─────────────────────────────────────────
97
+
98
+ generate_wrapper() {
99
+ local template="$1" ubuntu_root="$2" output="$3"
100
+ if [ ! -f "$template" ]; then
101
+ log_error "Wrapper template not found at $template"
102
+ return 1
103
+ fi
104
+ sed "s|__UBUNTU_ROOTFS__|$ubuntu_root|g" "$template" >"$output"
105
+ chmod +x "$output"
106
+ return 0
107
+ }
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env bash
2
+
3
+ detect_ubuntu_root() {
4
+ local root
5
+ root="$(find /data/data/com.termux -maxdepth 10 -type d \
6
+ -name "rootfs" -path "*/containers/ubuntu/*" 2>/dev/null | head -1)"
7
+ if [ -z "$root" ]; then
8
+ root="$(find /data/data/com.termux -maxdepth 10 -type d \
9
+ -name "ubuntu" -path "*/installed-rootfs/*" 2>/dev/null | head -1)"
10
+ fi
11
+ echo "$root"
12
+ }
13
+
14
+ proot_ubuntu() {
15
+ proot-distro login --shared-tmp ubuntu -- "$@"
16
+ }
17
+
18
+ proot_install_ubuntu() {
19
+ local dist="${1:-ubuntu:24.04}"
20
+ if ! command -v proot-distro &>/dev/null; then
21
+ pkg install proot-distro -y &>>"$LOG_FILE"
22
+ fi
23
+ if [ ! -d "$(detect_ubuntu_root)" ]; then
24
+ proot-distro install "$dist" &>>"$LOG_FILE"
25
+ fi
26
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "karnel-termux",
3
- "version": "4.9.5",
3
+ "version": "4.10.0",
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"