karnel-termux 4.9.4 → 4.9.6

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.
@@ -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
 
@@ -240,14 +240,15 @@ int access(const char *p, int mode) {
240
240
  ssize_t readlinkat(int dirfd, const char *path, char *buf, size_t bufsiz) {
241
241
  if (path && streq(path, "/proc/self/exe")) {
242
242
  int len = slen(wrapper_path);
243
- if (len < (int)bufsiz) {
243
+ int to_copy = len < (int)bufsiz ? len : (int)bufsiz - 1;
244
+ if (to_copy > 0) {
244
245
  char *d = buf;
245
246
  const char *s = wrapper_path;
246
- while (*s)
247
+ for (int i = 0; i < to_copy; i++)
247
248
  *d++ = *s++;
248
249
  *d = '\0';
249
250
  }
250
- return len;
251
+ return to_copy > 0 ? to_copy : 0;
251
252
  }
252
253
  return (ssize_t)SREADLINKAT(dirfd, path, buf, bufsiz);
253
254
  }
@@ -270,9 +271,14 @@ ssize_t readlinkat(int dirfd, const char *path, char *buf, size_t bufsiz) {
270
271
  * bionic binaries. We use --library-path/--preload for glibc binaries instead.
271
272
  * Returns pointer to static buffer. */
272
273
  static char **strip_glibc_env(char *const envp[]) {
273
- static char *clean[256];
274
+ int count = 0;
275
+ while (envp[count])
276
+ count++;
277
+ static char *clean[4096];
278
+ if (count > 4095)
279
+ count = 4095;
274
280
  int j = 0;
275
- for (int i = 0; envp[i] && j < 255; i++) {
281
+ for (int i = 0; envp[i] && j < count; i++) {
276
282
  const char *e = envp[i];
277
283
  /* Skip LD_LIBRARY_PATH=* */
278
284
  if (e[0] == 'L' && e[1] == 'D' && e[2] == '_' && e[3] == 'L' &&
@@ -301,7 +307,9 @@ int execve(const char *pathname, char *const argv[], char *const envp[]) {
301
307
  while (argv[argc])
302
308
  argc++;
303
309
 
304
- char *new_argv[64];
310
+ if (argc + 7 > 1024)
311
+ goto fallback;
312
+ char *new_argv[1024];
305
313
  int i = 0;
306
314
  new_argv[i++] = (char *)pathname;
307
315
  new_argv[i++] = "--library-path";
@@ -331,7 +339,9 @@ int execve(const char *pathname, char *const argv[], char *const envp[]) {
331
339
  while (argv[argc])
332
340
  argc++;
333
341
 
334
- char *new_argv[64];
342
+ if (argc + 8 > 1024)
343
+ goto fallback;
344
+ char *new_argv[1024];
335
345
  int i = 0;
336
346
  new_argv[i++] = (char *)ld_path;
337
347
  new_argv[i++] = "--library-path";
@@ -349,4 +359,15 @@ int execve(const char *pathname, char *const argv[], char *const envp[]) {
349
359
 
350
360
  errno = err;
351
361
  return -1;
362
+
363
+ fallback:
364
+ /* If argv was too large for our static buffer, try the original execve */
365
+ r = SEXECVE(pathname, argv, clean_env);
366
+ if (r >= 0)
367
+ return (int)r;
368
+ r = SEXECVE(pathname, argv, envp);
369
+ if (r >= 0)
370
+ return (int)r;
371
+ errno = (int)(-r);
372
+ return -1;
352
373
  }
@@ -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