karnel-termux 4.13.11 → 4.14.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 +7 -5
- package/docs/ARCHITECTURE.md +1 -1
- package/docs/CHANGELOG.md +12 -0
- package/docs/README.md +1 -1
- package/docs/cli/README.md +3 -3
- package/karnel/cli/commands/ia.sh +7 -7
- package/karnel/cli/commands/install.sh +7 -4
- package/karnel/cli/commands/list.sh +2 -0
- package/karnel/cli/commands/reinstall.sh +1 -185
- package/karnel/cli/karnel.sh +9 -45
- package/karnel/modules/ai.sh +9 -17
- package/karnel/tools/ai/all.sh +45 -11
- package/karnel/tools/ai/ampcode/install.sh +2 -2
- package/karnel/tools/ai/cactus/README.md +36 -0
- package/karnel/tools/ai/cactus/install.sh +171 -0
- package/karnel/tools/ai/codegraph/install.sh +1 -1
- package/karnel/tools/ai/codex/install.sh +1 -1
- package/karnel/tools/ai/command-code/install.sh +1 -1
- package/karnel/tools/ai/engram/install.sh +1 -1
- package/karnel/tools/ai/gemini-cli/install.sh +1 -1
- package/karnel/tools/ai/hermes-agent/install.sh +1 -1
- package/karnel/tools/ai/hugging-face/README.md +34 -0
- package/karnel/tools/ai/hugging-face/install.sh +129 -0
- package/karnel/tools/ai/kilocode-cli/install.sh +3 -3
- package/karnel/tools/ai/kimi-code/install.sh +1 -1
- package/karnel/tools/ai/minimax-cli/install.sh +1 -1
- package/karnel/tools/ai/mistral-vibe/install.sh +1 -1
- package/karnel/tools/ai/oh-my-pi/install.sh +2 -2
- package/karnel/tools/ai/ollama/install.sh +1 -1
- package/karnel/tools/ai/openclaude/install.sh +1 -1
- package/karnel/tools/ai/openclaw/install.sh +1 -1
- package/karnel/tools/ai/pi/install.sh +1 -1
- package/karnel/tools/ai/qoder/install.sh +2 -2
- package/karnel/tools/ai/qwen-code/install.sh +1 -1
- package/karnel/utils/tools.sh +8 -1
- package/package.json +1 -1
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.
|
|
11
|
+
<img src="https://img.shields.io/badge/version-4.14.0-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">
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
|
|
34
34
|
Created by **Israel Marques**.
|
|
35
35
|
|
|
36
|
-
- **
|
|
36
|
+
- **43 AI tools** — Claude, Gemini, OpenCode, Cactus, Hugging Face, Ollama, KeelCode, Goose, Factory Droid and more
|
|
37
37
|
- **8 languages** — Node.js, Python, Go, Rust, C/C++, PHP, Perl, Bun
|
|
38
38
|
- **5 databases** — PostgreSQL, MariaDB, SQLite, MongoDB, Redis
|
|
39
39
|
- **22 dev tools** — gh, curl, fzf, bat, lsd, jq, tmux, openssh, snyk and more
|
|
@@ -115,7 +115,7 @@ karnel
|
|
|
115
115
|
|--------|-------------|--------------|
|
|
116
116
|
| `lang` | Node.js, Python, Go, Rust, C/C++, PHP, Perl, Bun | `karnel install lang` |
|
|
117
117
|
| `db` | PostgreSQL, MariaDB, SQLite, MongoDB, Redis | `karnel install db` |
|
|
118
|
-
| `ai` |
|
|
118
|
+
| `ai` | 43 AI tools, agents, and local inference clients | `karnel install ai` |
|
|
119
119
|
| `editor` | code-server (VS Code in browser), Neovim, NvChad | `karnel install editor` |
|
|
120
120
|
| `dev` | gh, curl, fzf, bat, lsd, jq and more | `karnel install dev` |
|
|
121
121
|
| `npm` | TypeScript, NestJS CLI, Prettier and more | `karnel install npm` |
|
|
@@ -259,7 +259,7 @@ See the bundled reference with `karnel show osint --robin`.
|
|
|
259
259
|
|
|
260
260
|
---
|
|
261
261
|
|
|
262
|
-
## AI
|
|
262
|
+
## AI Tools (43)
|
|
263
263
|
|
|
264
264
|
```bash
|
|
265
265
|
karnel install ai # Install all
|
|
@@ -278,6 +278,8 @@ karnel install ai --opencode --ollama # Install specific agents
|
|
|
278
278
|
| **OpenClaude** | `--openclaude` | Open source Claude Code alternative |
|
|
279
279
|
| **OpenClaw** | `--openclaw` | Personal AI assistant |
|
|
280
280
|
| **Ollama** | `--ollama` | Run open source LLMs locally |
|
|
281
|
+
| **Cactus** | `--cactus` | On-device ARM64 inference through an isolated Ubuntu runtime |
|
|
282
|
+
| **Hugging Face** | `--hugging-face` | Official Hugging Face Hub CLI (`hf`) |
|
|
281
283
|
| **Codex CLI** | `--codex` | OpenAI coding agent |
|
|
282
284
|
| **OpenCode** | `--opencode` | Open source terminal agent |
|
|
283
285
|
| **MiMoCode** | `--mimocode` | Fast open source AI agent |
|
|
@@ -545,7 +547,7 @@ karnel/
|
|
|
545
547
|
│ │ └── karnel.sh # Main CLI (with TUI)
|
|
546
548
|
│ ├── modules/ # Module orchestrators
|
|
547
549
|
│ ├── tools/ # Tool installers
|
|
548
|
-
│ │ ├── ai/ #
|
|
550
|
+
│ │ ├── ai/ # 43 AI tools
|
|
549
551
|
│ │ ├── lang/ # 8 languages
|
|
550
552
|
│ │ ├── db/ # 5 databases
|
|
551
553
|
│ │ ├── dev/ # 22 dev tools
|
package/docs/ARCHITECTURE.md
CHANGED
|
@@ -27,7 +27,7 @@ karnel-termux/
|
|
|
27
27
|
│ │ ├── dev.sh # Dev tools installer
|
|
28
28
|
│ │ └── ...
|
|
29
29
|
│ ├── tools/ # Individual tool installers
|
|
30
|
-
│ │ ├── ai/ #
|
|
30
|
+
│ │ ├── ai/ # 43 AI tools
|
|
31
31
|
│ │ ├── lang/ # 8 languages
|
|
32
32
|
│ │ ├── db/ # 5 databases
|
|
33
33
|
│ │ ├── dev/ # 22 dev tools
|
package/docs/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Documentation Changelog
|
|
2
2
|
|
|
3
|
+
## 4.14.0 - 2026-08-14
|
|
4
|
+
|
|
5
|
+
- Added tested Cactus 2.0.1 support on ARM64 through Ubuntu Proot with an
|
|
6
|
+
isolated compatible Python runtime.
|
|
7
|
+
- Added the official Hugging Face `hf` CLI in an isolated native Termux
|
|
8
|
+
environment, using HTTP transport when the optional Xet Android wheel is
|
|
9
|
+
unavailable.
|
|
10
|
+
- Made the AI registry authoritative for TUI selection, route discovery, and
|
|
11
|
+
lifecycle dispatch, with contracts covering all 43 registered tools.
|
|
12
|
+
- Fixed AI reinstall and uninstall error propagation, unknown status handling,
|
|
13
|
+
binary alias summaries, ownership checks, and stale tool counts.
|
|
14
|
+
|
|
3
15
|
## 4.13.11 - 2026-08-10
|
|
4
16
|
|
|
5
17
|
- Hardened installers to preserve user-managed commands and data, with atomic
|
package/docs/README.md
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
## What is Karnel?
|
|
15
15
|
|
|
16
16
|
Karnel Termux transforms your Android device into a complete development workstation.
|
|
17
|
-
With a single CLI (`karnel`), you can install and manage
|
|
17
|
+
With a single CLI (`karnel`), you can install and manage 43 AI tools, 8 languages,
|
|
18
18
|
5 databases, 22 dev tools, 12 utility tools, 2 network tools, 6 games, responsible OSINT, and more.
|
|
19
19
|
|
|
20
20
|
## Sections
|
package/docs/cli/README.md
CHANGED
|
@@ -22,7 +22,7 @@ karnel install <module> --tool1 --tool2 # Install specific tools only
|
|
|
22
22
|
|-----------|-------------|
|
|
23
23
|
| `lang` | Node.js, Python, Go, Rust, C/C++, PHP, Perl, Bun |
|
|
24
24
|
| `db` | PostgreSQL, MariaDB, SQLite, MongoDB, Redis |
|
|
25
|
-
| `ai` |
|
|
25
|
+
| `ai` | 43 AI tools (OpenCode, Cactus, Hugging Face, Claude Code, KeelCode, Goose, Factory Droid, Ollama, etc.) |
|
|
26
26
|
| `editor` | code-server (VS Code in browser), Neovim, NvChad |
|
|
27
27
|
| `dev` | gh, curl, fzf, bat, lsd, jq, tmux, openssh, snyk (22 tools) |
|
|
28
28
|
| `npm` | TypeScript, NestJS CLI, Prettier, Vercel CLI, etc. |
|
|
@@ -42,7 +42,7 @@ karnel install <module> --tool1 --tool2 # Install specific tools only
|
|
|
42
42
|
|
|
43
43
|
| Module | Flags |
|
|
44
44
|
|-----------|-------|
|
|
45
|
-
| `ai` | `--qwen-code`, `--gemini-cli`, `--claude-code`, `--mistral-vibe`, `--openclaude`, `--openclaw`, `--ollama`, `--codex`, `--opencode`, `--mimocode`, `--engram`, `--codegraph`, `--pi`, `--antigravity-cli`, `--minimax-cli`, `--gentle-ai`, `--gga`, `--hermes-agent`, `--kimi-code`, `--command-code`, `--codebuff`, `--freebuff`, `--kilocode-cli`, `--kiro`, `--crush`, `--cline`, `--odysseus`, `--kimchi-code`, `--omni-route`, `--ctx7`, `--openspec`, `--supercode-cli`, `--puter`, `--keelcode`, `--copilot-termux`, `--qoder`, `--ampcode`, `--cursor-cli`, `--oh-my-pi`, `--goose`, `--droid` |
|
|
45
|
+
| `ai` | `--qwen-code`, `--gemini-cli`, `--claude-code`, `--mistral-vibe`, `--openclaude`, `--openclaw`, `--ollama`, `--codex`, `--opencode`, `--mimocode`, `--engram`, `--codegraph`, `--pi`, `--antigravity-cli`, `--minimax-cli`, `--gentle-ai`, `--gga`, `--hermes-agent`, `--kimi-code`, `--command-code`, `--codebuff`, `--freebuff`, `--kilocode-cli`, `--kiro`, `--crush`, `--cline`, `--odysseus`, `--kimchi-code`, `--omni-route`, `--ctx7`, `--openspec`, `--supercode-cli`, `--puter`, `--keelcode`, `--copilot-termux`, `--qoder`, `--ampcode`, `--cursor-cli`, `--oh-my-pi`, `--goose`, `--droid`, `--cactus`, `--hugging-face` |
|
|
46
46
|
| `db` | `--postgresql`, `--mariadb`, `--sqlite`, `--mongodb`, `--redis` |
|
|
47
47
|
| `dev` | `--gh`, `--wget`, `--curl`, `--lsd`, `--bat`, `--proot`, `--ncurses`, `--tmate`, `--openssh`, `--tmux`, `--cloudflared`, `--translate`, `--html2text`, `--jq`, `--bc`, `--tree`, `--fzf`, `--imagemagick`, `--shfmt`, `--make`, `--udocker`, `--snyk` |
|
|
48
48
|
| `lang` | `--bun`, `--nodejs`, `--python`, `--perl`, `--php`, `--rust`, `--clang`, `--golang` |
|
|
@@ -136,7 +136,7 @@ binary/command name, and current install status.
|
|
|
136
136
|
|-----------|-------|
|
|
137
137
|
| `lang` | 8 languages |
|
|
138
138
|
| `db` | 5 databases |
|
|
139
|
-
| `ai` |
|
|
139
|
+
| `ai` | 43 AI tools |
|
|
140
140
|
| `editor` | 3 editor components |
|
|
141
141
|
| `dev` | 22 development tools |
|
|
142
142
|
| `npm` | 11 npm global modules |
|
|
@@ -441,8 +441,12 @@ ia_routes() {
|
|
|
441
441
|
|
|
442
442
|
local -a routes=()
|
|
443
443
|
|
|
444
|
-
|
|
445
|
-
|
|
444
|
+
import "@/tools/ai/all"
|
|
445
|
+
local entry binaries cmd
|
|
446
|
+
for entry in "${AI_TOOLS_REGISTRY[@]}"; do
|
|
447
|
+
binaries="${entry##*:}"
|
|
448
|
+
IFS=',' read -ra route_commands <<< "$binaries"
|
|
449
|
+
for cmd in "${route_commands[@]}"; do
|
|
446
450
|
if command -v "$cmd" &>/dev/null; then
|
|
447
451
|
local path
|
|
448
452
|
path=$(command -v "$cmd")
|
|
@@ -450,13 +454,9 @@ ia_routes() {
|
|
|
450
454
|
else
|
|
451
455
|
routes+=("$cmd|not installed|missing")
|
|
452
456
|
fi
|
|
457
|
+
done
|
|
453
458
|
done
|
|
454
459
|
|
|
455
|
-
if [[ ${#routes[@]} -eq 0 ]]; then
|
|
456
|
-
log_warn "No AI CLIs found"
|
|
457
|
-
return 0
|
|
458
|
-
fi
|
|
459
|
-
|
|
460
460
|
table_start "CLI" "Path" "Status"
|
|
461
461
|
for route in "${routes[@]}"; do
|
|
462
462
|
local cli
|
|
@@ -188,9 +188,7 @@ _install_specific_tools() {
|
|
|
188
188
|
local failed_count=0
|
|
189
189
|
local skipped_count=0
|
|
190
190
|
|
|
191
|
-
#
|
|
192
|
-
# To add a new tool: 1) create install.sh 2) add to AI_TOOLS_REGISTRY 3) add case here
|
|
193
|
-
# Import the registry for _validate_tool_installed
|
|
191
|
+
# The registry is authoritative for accepted flags and post-install binaries.
|
|
194
192
|
local -A _tool_binaries=()
|
|
195
193
|
local _entry _id _name _bins
|
|
196
194
|
for _entry in "${AI_TOOLS_REGISTRY[@]}"; do
|
|
@@ -200,7 +198,12 @@ _install_specific_tools() {
|
|
|
200
198
|
|
|
201
199
|
for tool in "${tools[@]}"; do
|
|
202
200
|
local func_name="install_${tool//-/_}"
|
|
203
|
-
|
|
201
|
+
|
|
202
|
+
if [[ -z "${_tool_binaries[$tool]+registered}" ]]; then
|
|
203
|
+
log_warn "Unknown AI tool: --$tool"
|
|
204
|
+
((failed_count++))
|
|
205
|
+
continue
|
|
206
|
+
fi
|
|
204
207
|
|
|
205
208
|
if declare -f "$func_name" &>/dev/null; then
|
|
206
209
|
"$func_name"
|
|
@@ -206,6 +206,8 @@ _list_ai() {
|
|
|
206
206
|
table_row "Oh-My-Pi" "--oh-my-pi" "omp" "$(_check_cmd "omp")"
|
|
207
207
|
table_row "Goose CLI" "--goose" "goose" "$(_check_cmd "goose")"
|
|
208
208
|
table_row "Factory Droid" "--droid" "droid" "$(_check_cmd "droid")"
|
|
209
|
+
table_row "Cactus" "--cactus" "cactus" "$(_check_cmd "cactus")"
|
|
210
|
+
table_row "Hugging Face" "--hugging-face" "hf" "$(_check_cmd "hf")"
|
|
209
211
|
table_row "Copilot-Termux" "--copilot-termux" "copilot" "$(_check_cmd "copilot")"
|
|
210
212
|
table_row "Supercode CLI" "--supercode-cli" "supercode" "$(_check_cmd "supercode")"
|
|
211
213
|
table_row "Puter CLI" "--puter" "puter" "$(_check_cmd "puter")"
|
|
@@ -182,191 +182,7 @@ _reinstall_specific_tools() {
|
|
|
182
182
|
|
|
183
183
|
case "$module" in
|
|
184
184
|
ai)
|
|
185
|
-
|
|
186
|
-
local reinstalled_count=0
|
|
187
|
-
local failed_count=0
|
|
188
|
-
|
|
189
|
-
for tool in "${tools[@]}"; do
|
|
190
|
-
case "$tool" in
|
|
191
|
-
qwen-code)
|
|
192
|
-
reinstall_qwen_code
|
|
193
|
-
case $? in 0) ((reinstalled_count++));; 1) ((failed_count++));; esac
|
|
194
|
-
;;
|
|
195
|
-
gemini-cli)
|
|
196
|
-
reinstall_gemini_cli
|
|
197
|
-
case $? in 0) ((reinstalled_count++));; 1) ((failed_count++));; esac
|
|
198
|
-
;;
|
|
199
|
-
claude-code)
|
|
200
|
-
reinstall_claude_code
|
|
201
|
-
case $? in 0) ((reinstalled_count++));; 1) ((failed_count++));; esac
|
|
202
|
-
;;
|
|
203
|
-
mistral-vibe)
|
|
204
|
-
reinstall_mistral_vibe
|
|
205
|
-
case $? in 0) ((reinstalled_count++));; 1) ((failed_count++));; esac
|
|
206
|
-
;;
|
|
207
|
-
openclaude)
|
|
208
|
-
reinstall_openclaude
|
|
209
|
-
case $? in 0) ((reinstalled_count++));; 1) ((failed_count++));; esac
|
|
210
|
-
;;
|
|
211
|
-
openclaw)
|
|
212
|
-
reinstall_openclaw
|
|
213
|
-
case $? in 0) ((reinstalled_count++));; 1) ((failed_count++));; esac
|
|
214
|
-
;;
|
|
215
|
-
ollama)
|
|
216
|
-
reinstall_ollama
|
|
217
|
-
case $? in 0) ((reinstalled_count++));; 1) ((failed_count++));; esac
|
|
218
|
-
;;
|
|
219
|
-
codex)
|
|
220
|
-
reinstall_codex
|
|
221
|
-
case $? in 0) ((reinstalled_count++));; 1) ((failed_count++));; esac
|
|
222
|
-
;;
|
|
223
|
-
opencode)
|
|
224
|
-
reinstall_opencode
|
|
225
|
-
case $? in 0) ((reinstalled_count++));; 1) ((failed_count++));; esac
|
|
226
|
-
;;
|
|
227
|
-
kilocode-cli)
|
|
228
|
-
reinstall_kilocode_cli
|
|
229
|
-
case $? in 0) ((reinstalled_count++));; 1) ((failed_count++));; esac
|
|
230
|
-
;;
|
|
231
|
-
kimchi-code)
|
|
232
|
-
reinstall_kimchi_code
|
|
233
|
-
case $? in 0) ((reinstalled_count++));; 1) ((failed_count++));; esac
|
|
234
|
-
;;
|
|
235
|
-
mimocode)
|
|
236
|
-
reinstall_mimocode
|
|
237
|
-
case $? in 0) ((reinstalled_count++));; 1) ((failed_count++));; esac
|
|
238
|
-
;;
|
|
239
|
-
engram)
|
|
240
|
-
reinstall_engram
|
|
241
|
-
case $? in 0) ((reinstalled_count++));; 1) ((failed_count++));; esac
|
|
242
|
-
;;
|
|
243
|
-
codegraph)
|
|
244
|
-
reinstall_codegraph
|
|
245
|
-
case $? in 0) ((reinstalled_count++));; 1) ((failed_count++));; esac
|
|
246
|
-
;;
|
|
247
|
-
pi)
|
|
248
|
-
reinstall_pi
|
|
249
|
-
case $? in 0) ((reinstalled_count++));; 1) ((failed_count++));; esac
|
|
250
|
-
;;
|
|
251
|
-
antigravity-cli)
|
|
252
|
-
reinstall_antigravity_cli
|
|
253
|
-
case $? in 0) ((reinstalled_count++));; 1) ((failed_count++));; esac
|
|
254
|
-
;;
|
|
255
|
-
minimax-cli)
|
|
256
|
-
reinstall_minimax_cli
|
|
257
|
-
case $? in 0) ((reinstalled_count++));; 1) ((failed_count++));; esac
|
|
258
|
-
;;
|
|
259
|
-
gentle-ai)
|
|
260
|
-
reinstall_gentle_ai
|
|
261
|
-
case $? in 0) ((reinstalled_count++));; 1) ((failed_count++));; esac
|
|
262
|
-
;;
|
|
263
|
-
gga)
|
|
264
|
-
reinstall_gga
|
|
265
|
-
case $? in 0) ((reinstalled_count++));; 1) ((failed_count++));; esac
|
|
266
|
-
;;
|
|
267
|
-
hermes-agent)
|
|
268
|
-
reinstall_hermes_agent
|
|
269
|
-
case $? in 0) ((reinstalled_count++));; 1) ((failed_count++));; esac
|
|
270
|
-
;;
|
|
271
|
-
kimi-code)
|
|
272
|
-
reinstall_kimi_code
|
|
273
|
-
case $? in 0) ((reinstalled_count++));; 1) ((failed_count++));; esac
|
|
274
|
-
;;
|
|
275
|
-
command-code)
|
|
276
|
-
reinstall_command_code
|
|
277
|
-
case $? in 0) ((reinstalled_count++));; 1) ((failed_count++));; esac
|
|
278
|
-
;;
|
|
279
|
-
codebuff)
|
|
280
|
-
reinstall_codebuff
|
|
281
|
-
case $? in 0) ((reinstalled_count++));; 1) ((failed_count++));; esac
|
|
282
|
-
;;
|
|
283
|
-
freebuff)
|
|
284
|
-
reinstall_freebuff
|
|
285
|
-
case $? in 0) ((reinstalled_count++));; 1) ((failed_count++));; esac
|
|
286
|
-
;;
|
|
287
|
-
ctx7)
|
|
288
|
-
reinstall_ctx7
|
|
289
|
-
case $? in 0) ((reinstalled_count++));; 1) ((failed_count++));; esac
|
|
290
|
-
;;
|
|
291
|
-
openspec)
|
|
292
|
-
reinstall_openspec
|
|
293
|
-
case $? in 0) ((reinstalled_count++));; 1) ((failed_count++));; esac
|
|
294
|
-
;;
|
|
295
|
-
supercode-cli)
|
|
296
|
-
reinstall_supercode_cli
|
|
297
|
-
case $? in 0) ((reinstalled_count++));; 1) ((failed_count++));; esac
|
|
298
|
-
;;
|
|
299
|
-
puter)
|
|
300
|
-
reinstall_puter
|
|
301
|
-
case $? in 0) ((reinstalled_count++));; 1) ((failed_count++));; esac
|
|
302
|
-
;;
|
|
303
|
-
keelcode)
|
|
304
|
-
reinstall_keelcode
|
|
305
|
-
case $? in 0) ((reinstalled_count++));; 1) ((failed_count++));; esac
|
|
306
|
-
;;
|
|
307
|
-
qoder)
|
|
308
|
-
reinstall_qoder
|
|
309
|
-
case $? in 0) ((reinstalled_count++));; 1) ((failed_count++));; esac
|
|
310
|
-
;;
|
|
311
|
-
ampcode)
|
|
312
|
-
reinstall_ampcode
|
|
313
|
-
case $? in 0) ((reinstalled_count++));; 1) ((failed_count++));; esac
|
|
314
|
-
;;
|
|
315
|
-
cursor-cli)
|
|
316
|
-
reinstall_cursor_cli
|
|
317
|
-
case $? in 0) ((reinstalled_count++));; 1) ((failed_count++));; esac
|
|
318
|
-
;;
|
|
319
|
-
oh-my-pi)
|
|
320
|
-
reinstall_oh_my_pi
|
|
321
|
-
case $? in 0) ((reinstalled_count++));; 1) ((failed_count++));; esac
|
|
322
|
-
;;
|
|
323
|
-
goose)
|
|
324
|
-
reinstall_goose
|
|
325
|
-
case $? in 0) ((reinstalled_count++));; 1) ((failed_count++));; esac
|
|
326
|
-
;;
|
|
327
|
-
droid)
|
|
328
|
-
reinstall_droid
|
|
329
|
-
case $? in 0) ((reinstalled_count++));; 1) ((failed_count++));; esac
|
|
330
|
-
;;
|
|
331
|
-
copilot-termux)
|
|
332
|
-
reinstall_copilot_termux
|
|
333
|
-
case $? in 0) ((reinstalled_count++));; 1) ((failed_count++));; esac
|
|
334
|
-
;;
|
|
335
|
-
kiro)
|
|
336
|
-
reinstall_kiro
|
|
337
|
-
case $? in 0) ((reinstalled_count++));; 1) ((failed_count++));; esac
|
|
338
|
-
;;
|
|
339
|
-
crush)
|
|
340
|
-
reinstall_crush
|
|
341
|
-
case $? in 0) ((reinstalled_count++));; 1) ((failed_count++));; esac
|
|
342
|
-
;;
|
|
343
|
-
cline)
|
|
344
|
-
reinstall_cline
|
|
345
|
-
case $? in 0) ((reinstalled_count++));; 1) ((failed_count++));; esac
|
|
346
|
-
;;
|
|
347
|
-
odysseus)
|
|
348
|
-
reinstall_odysseus
|
|
349
|
-
case $? in 0) ((reinstalled_count++));; 1) ((failed_count++));; esac
|
|
350
|
-
;;
|
|
351
|
-
omni-route)
|
|
352
|
-
reinstall_omni_route
|
|
353
|
-
case $? in 0) ((reinstalled_count++));; 1) ((failed_count++));; esac
|
|
354
|
-
;;
|
|
355
|
-
*)
|
|
356
|
-
log_warn "Unknown AI tool: --$tool"
|
|
357
|
-
((failed_count++))
|
|
358
|
-
;;
|
|
359
|
-
esac
|
|
360
|
-
done
|
|
361
|
-
|
|
362
|
-
echo
|
|
363
|
-
if [[ $reinstalled_count -gt 0 ]]; then
|
|
364
|
-
log_success "$reinstalled_count AI tool(s) reinstalled"
|
|
365
|
-
fi
|
|
366
|
-
if [[ $failed_count -gt 0 ]]; then
|
|
367
|
-
log_warn "$failed_count tool(s) failed to reinstall"
|
|
368
|
-
fi
|
|
369
|
-
echo
|
|
185
|
+
_batch_tool_action "ai" "reinstall" "${tools[@]}" || return 1
|
|
370
186
|
;;
|
|
371
187
|
db)
|
|
372
188
|
import "@/tools/db/all"
|
package/karnel/cli/karnel.sh
CHANGED
|
@@ -110,7 +110,7 @@ karnel_help() {
|
|
|
110
110
|
echo
|
|
111
111
|
log_info "Use with: karnel install|update|reinstall|uninstall <target> [--tool...]"
|
|
112
112
|
echo
|
|
113
|
-
printf " ${D_GREEN}%-10s${NC} %s\n" "ai" "
|
|
113
|
+
printf " ${D_GREEN}%-10s${NC} %s\n" "ai" "43 AI tools (OpenCode, Cactus, Hugging Face, Claude, Ollama, Goose, etc.)"
|
|
114
114
|
printf " ${D_GREEN}%-10s${NC} %s\n" "auto" "Automation (n8n)"
|
|
115
115
|
printf " ${D_GREEN}%-10s${NC} %s\n" "db" "PostgreSQL, MariaDB, SQLite, MongoDB, Redis"
|
|
116
116
|
printf " ${D_GREEN}%-10s${NC} %s\n" "deploy" "Vercel, Railway, Netlify, Supabase CLIs"
|
|
@@ -370,7 +370,7 @@ _tui_list_menu() {
|
|
|
370
370
|
local modules="ai db lang dev editor npm shell ui auto deploy games network utils osint voice plugin security"
|
|
371
371
|
local choice
|
|
372
372
|
choice=$(_dialog_menu "List Tools" "Select a module to list:" \
|
|
373
|
-
"ai" "AI
|
|
373
|
+
"ai" "AI Tools (43)" \
|
|
374
374
|
"db" "Databases" \
|
|
375
375
|
"lang" "Programming Languages" \
|
|
376
376
|
"editor" "Code Editors" \
|
|
@@ -760,49 +760,13 @@ _tui_install_checklist() {
|
|
|
760
760
|
)
|
|
761
761
|
;;
|
|
762
762
|
ai)
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
"
|
|
769
|
-
|
|
770
|
-
"ollama" "Ollama" OFF
|
|
771
|
-
"codex" "Codex CLI" OFF
|
|
772
|
-
"opencode" "OpenCode" OFF
|
|
773
|
-
"mimocode" "MiMo Code" OFF
|
|
774
|
-
"engram" "Engram" OFF
|
|
775
|
-
"codegraph" "CodeGraph" OFF
|
|
776
|
-
"pi" "Pi" OFF
|
|
777
|
-
"antigravity-cli" "Antigravity CLI" OFF
|
|
778
|
-
"gentle-ai" "Gentle AI" OFF
|
|
779
|
-
"minimax-cli" "Minimax CLI" OFF
|
|
780
|
-
"gga" "GGA" OFF
|
|
781
|
-
"hermes-agent" "Hermes Agent" OFF
|
|
782
|
-
"kimi-code" "Kimi Code" OFF
|
|
783
|
-
"command-code" "Command Code" OFF
|
|
784
|
-
"codebuff" "Codebuff" OFF
|
|
785
|
-
"freebuff" "Freebuff" OFF
|
|
786
|
-
"crush" "Crush CLI" OFF
|
|
787
|
-
"odysseus" "Odysseus" OFF
|
|
788
|
-
"kilocode-cli" "Kilo Code CLI" OFF
|
|
789
|
-
"kiro" "Kiro CLI" OFF
|
|
790
|
-
"cline" "Cline CLI" OFF
|
|
791
|
-
"kimchi-code" "Kimchi CLI" OFF
|
|
792
|
-
"omni-route" "omniRoute" OFF
|
|
793
|
-
"ctx7" "Context7 Docs" OFF
|
|
794
|
-
"openspec" "OpenSpec SDD" OFF
|
|
795
|
-
"supercode-cli" "Supercode CLI" OFF
|
|
796
|
-
"puter" "Puter CLI" OFF
|
|
797
|
-
"keelcode" "KeelCode" OFF
|
|
798
|
-
"copilot-termux" "Copilot-Termux" OFF
|
|
799
|
-
"qoder" "Qoder" OFF
|
|
800
|
-
"ampcode" "AMP Code CLI" OFF
|
|
801
|
-
"cursor-cli" "Cursor CLI" OFF
|
|
802
|
-
"oh-my-pi" "Oh-My-Pi" OFF
|
|
803
|
-
"goose" "Goose CLI" OFF
|
|
804
|
-
"droid" "Factory Droid" OFF
|
|
805
|
-
)
|
|
763
|
+
import "@/tools/ai/all"
|
|
764
|
+
opts=()
|
|
765
|
+
local entry id name binaries
|
|
766
|
+
for entry in "${AI_TOOLS_REGISTRY[@]}"; do
|
|
767
|
+
IFS=':' read -r id name binaries <<< "$entry"
|
|
768
|
+
opts+=("$id" "$name" OFF)
|
|
769
|
+
done
|
|
806
770
|
;;
|
|
807
771
|
dev)
|
|
808
772
|
opts=(
|
package/karnel/modules/ai.sh
CHANGED
|
@@ -38,14 +38,17 @@ install_ai() {
|
|
|
38
38
|
echo
|
|
39
39
|
|
|
40
40
|
# Mostra apenas ferramentas que foram realmente instaladas (command -v)
|
|
41
|
-
local _reg_entry _id _name _bins
|
|
41
|
+
local _reg_entry _id _name _bins _bin
|
|
42
|
+
local -a _registered_bins
|
|
42
43
|
for _reg_entry in "${AI_TOOLS_REGISTRY[@]}"; do
|
|
43
44
|
IFS=':' read -r _id _name _bins <<< "$_reg_entry"
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
45
|
+
IFS=',' read -ra _registered_bins <<< "$_bins"
|
|
46
|
+
for _bin in "${_registered_bins[@]}"; do
|
|
47
|
+
if command -v "$_bin" &>/dev/null; then
|
|
48
|
+
list_item "$_name ${GRAY}(${D_GREEN}$_bin${GRAY})"
|
|
49
|
+
break
|
|
50
|
+
fi
|
|
51
|
+
done
|
|
49
52
|
done
|
|
50
53
|
|
|
51
54
|
echo
|
|
@@ -117,17 +120,6 @@ reinstall_ai() {
|
|
|
117
120
|
local rc=0
|
|
118
121
|
import "@/tools/ai/all"
|
|
119
122
|
reinstall_all_ai_tools || rc=$?
|
|
120
|
-
echo
|
|
121
|
-
log_info "Reinstalled tools:"
|
|
122
|
-
echo
|
|
123
|
-
|
|
124
|
-
local _reg_entry _id _name _bins
|
|
125
|
-
for _reg_entry in "${AI_TOOLS_REGISTRY[@]}"; do
|
|
126
|
-
IFS=':' read -r _id _name _bins <<< "$_reg_entry"
|
|
127
|
-
list_item "$_name"
|
|
128
|
-
done
|
|
129
|
-
echo
|
|
130
|
-
|
|
131
123
|
if [ "$rc" -eq 0 ]; then
|
|
132
124
|
log_success "AI tools reinstalled successfully"
|
|
133
125
|
else
|
package/karnel/tools/ai/all.sh
CHANGED
|
@@ -69,6 +69,8 @@ AI_TOOLS_REGISTRY=(
|
|
|
69
69
|
"oh-my-pi:Oh-My-Pi:omp"
|
|
70
70
|
"goose:Goose CLI:goose"
|
|
71
71
|
"droid:Factory Droid:droid"
|
|
72
|
+
"cactus:Cactus:cactus"
|
|
73
|
+
"hugging-face:Hugging Face:hf"
|
|
72
74
|
)
|
|
73
75
|
|
|
74
76
|
# ---- IMPORTAR TODOS OS SCRIPTS INDIVIDUAIS ----
|
|
@@ -115,6 +117,41 @@ _validate_tool_installed() {
|
|
|
115
117
|
|
|
116
118
|
# ---- FUNÇÕES DE LOTE ----
|
|
117
119
|
|
|
120
|
+
_ai_tool_registered() {
|
|
121
|
+
local wanted="$1"
|
|
122
|
+
local entry id
|
|
123
|
+
for entry in "${AI_TOOLS_REGISTRY[@]}"; do
|
|
124
|
+
id="${entry%%:*}"
|
|
125
|
+
[[ "$id" == "$wanted" ]] && return 0
|
|
126
|
+
done
|
|
127
|
+
return 1
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
_run_ai_tool_action() {
|
|
131
|
+
local action="$1"
|
|
132
|
+
local id="$2"
|
|
133
|
+
local func_name="${action}_${id//-/_}"
|
|
134
|
+
|
|
135
|
+
_ai_tool_registered "$id" || return 127
|
|
136
|
+
|
|
137
|
+
if [[ "$action" != "reinstall" ]]; then
|
|
138
|
+
declare -f "$func_name" &>/dev/null || return 127
|
|
139
|
+
"$func_name"
|
|
140
|
+
return $?
|
|
141
|
+
fi
|
|
142
|
+
|
|
143
|
+
local uninstall_fn="uninstall_${id//-/_}"
|
|
144
|
+
local install_fn="install_${id//-/_}"
|
|
145
|
+
declare -f "$uninstall_fn" &>/dev/null && declare -f "$install_fn" &>/dev/null || return 127
|
|
146
|
+
|
|
147
|
+
"$uninstall_fn"
|
|
148
|
+
local uninstall_rc=$?
|
|
149
|
+
if [[ $uninstall_rc -ne 0 && $uninstall_rc -ne 2 ]]; then
|
|
150
|
+
return "$uninstall_rc"
|
|
151
|
+
fi
|
|
152
|
+
"$install_fn"
|
|
153
|
+
}
|
|
154
|
+
|
|
118
155
|
_all_ai_tools_action() {
|
|
119
156
|
local action="$1" # install, uninstall, update, reinstall
|
|
120
157
|
local action_label="$2" # Instalando, Removendo, Atualizando, Reinstalando
|
|
@@ -128,15 +165,12 @@ _all_ai_tools_action() {
|
|
|
128
165
|
|
|
129
166
|
for entry in "${AI_TOOLS_REGISTRY[@]}"; do
|
|
130
167
|
IFS=':' read -r id name binaries <<< "$entry"
|
|
131
|
-
local func_name="${action}_${id//-/_}"
|
|
132
|
-
|
|
133
168
|
((current++))
|
|
134
169
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
local rc=$?
|
|
170
|
+
_run_ai_tool_action "$action" "$id"
|
|
171
|
+
local rc=$?
|
|
138
172
|
|
|
139
|
-
|
|
173
|
+
case $rc in
|
|
140
174
|
0)
|
|
141
175
|
# Pós-validação apenas para install
|
|
142
176
|
if [[ "$action" == "install" ]]; then
|
|
@@ -161,11 +195,11 @@ _all_ai_tools_action() {
|
|
|
161
195
|
2)
|
|
162
196
|
((skipped_count++))
|
|
163
197
|
;;
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
198
|
+
*)
|
|
199
|
+
log_warn "$name: ${action}_${id//-/_} returned status $rc"
|
|
200
|
+
((failed_count++))
|
|
201
|
+
;;
|
|
202
|
+
esac
|
|
169
203
|
|
|
170
204
|
progress_update "$current" "$total"
|
|
171
205
|
done
|
|
@@ -214,9 +214,9 @@ uninstall_ampcode() {
|
|
|
214
214
|
mkdir -p "$(dirname "$LOG_FILE")"
|
|
215
215
|
if [ ! -f "$PREFIX/bin/amp" ]; then
|
|
216
216
|
log_warn "AMP Code CLI is not installed"
|
|
217
|
-
return
|
|
217
|
+
return 2
|
|
218
218
|
fi
|
|
219
|
-
loading "Uninstalling AMP Code CLI" _uninstall_ampcode_impl
|
|
219
|
+
loading "Uninstalling AMP Code CLI" _uninstall_ampcode_impl || return 1
|
|
220
220
|
}
|
|
221
221
|
|
|
222
222
|
_uninstall_ampcode_impl() {
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Cactus
|
|
2
|
+
|
|
3
|
+
Cactus is an on-device inference engine for text, vision, speech, embeddings,
|
|
4
|
+
and RAG. Karnel installs the official `cactus-compute` 2.0.1 ARM64 package in
|
|
5
|
+
an Ubuntu `proot-distro` environment because its Linux wheel targets glibc,
|
|
6
|
+
while native Termux uses Android/Bionic.
|
|
7
|
+
|
|
8
|
+
If the installed Ubuntu container uses Python 3.14 or newer, Karnel provisions
|
|
9
|
+
an isolated upstream-compatible Python 3.13 runtime with `uv`; it does not
|
|
10
|
+
replace the container's system Python.
|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
karnel install ai --cactus
|
|
16
|
+
cactus --help
|
|
17
|
+
cactus run Cactus-Compute/needle
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Other examples:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
cactus download Cactus-Compute/needle
|
|
24
|
+
cactus list
|
|
25
|
+
cactus serve Cactus-Compute/needle
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
The first model download can be large and inference performance depends on the
|
|
29
|
+
device's RAM, storage, and thermal limits. Cactus is currently enabled only on
|
|
30
|
+
ARM64 because that is the Linux wheel published by the upstream project.
|
|
31
|
+
|
|
32
|
+
Karnel removes only its managed Python environment and wrapper. Model data
|
|
33
|
+
created elsewhere is preserved. Cactus uses a source-available license with
|
|
34
|
+
commercial-use restrictions; review the upstream license before use.
|
|
35
|
+
|
|
36
|
+
Official project: <https://github.com/cactus-compute/cactus>
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
import "@/utils/log"
|
|
4
|
+
|
|
5
|
+
: "${KARNEL_DATA:=${XDG_DATA_HOME:-$HOME/.local/share}/karnel-data}"
|
|
6
|
+
CACTUS_VERSION="2.0.1"
|
|
7
|
+
CACTUS_DATA_DIR="$KARNEL_DATA/cactus"
|
|
8
|
+
CACTUS_CONTAINER_DIR="/opt/karnel/cactus"
|
|
9
|
+
CACTUS_WRAPPER="$PREFIX/bin/cactus"
|
|
10
|
+
|
|
11
|
+
_cactus_data_owned() {
|
|
12
|
+
[[ -f "$CACTUS_DATA_DIR/.karnel-managed" ]]
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
_cactus_wrapper_owned() {
|
|
16
|
+
local marker="$CACTUS_DATA_DIR/.karnel-wrapper-cactus"
|
|
17
|
+
[[ -f "$marker" && -f "$CACTUS_WRAPPER" ]] || return 1
|
|
18
|
+
[[ "$(sha256sum "$CACTUS_WRAPPER" 2>/dev/null)" == "$(<"$marker")" ]]
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
_cactus_proot() {
|
|
22
|
+
proot-distro login --shared-tmp ubuntu -- "$@"
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
_cactus_container_owned() {
|
|
26
|
+
command -v proot-distro &>/dev/null || return 1
|
|
27
|
+
_cactus_proot test -f "$CACTUS_CONTAINER_DIR/.karnel-managed" &>/dev/null
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
_cactus_verify_ownership() {
|
|
31
|
+
if [[ -e "$CACTUS_DATA_DIR" ]] && ! _cactus_data_owned; then
|
|
32
|
+
log_error "Refusing to replace unowned Cactus data: $CACTUS_DATA_DIR"
|
|
33
|
+
return 1
|
|
34
|
+
fi
|
|
35
|
+
if [[ -e "$CACTUS_WRAPPER" ]] && ! _cactus_wrapper_owned; then
|
|
36
|
+
log_error "Refusing to replace unowned command: $CACTUS_WRAPPER"
|
|
37
|
+
return 1
|
|
38
|
+
fi
|
|
39
|
+
if command -v cactus &>/dev/null && ! _cactus_wrapper_owned; then
|
|
40
|
+
log_error "Refusing to shadow the existing cactus command: $(command -v cactus)"
|
|
41
|
+
return 1
|
|
42
|
+
fi
|
|
43
|
+
if command -v proot-distro &>/dev/null &&
|
|
44
|
+
_cactus_proot test -e "$CACTUS_CONTAINER_DIR" &>/dev/null &&
|
|
45
|
+
! _cactus_container_owned; then
|
|
46
|
+
log_error "Refusing to replace unowned Cactus data in Ubuntu: $CACTUS_CONTAINER_DIR"
|
|
47
|
+
return 1
|
|
48
|
+
fi
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
_cactus_ensure_ubuntu() {
|
|
52
|
+
case "$(uname -m)" in
|
|
53
|
+
aarch64|arm64) ;;
|
|
54
|
+
*)
|
|
55
|
+
log_error "Cactus currently provides a Linux wheel only for ARM64"
|
|
56
|
+
return 1
|
|
57
|
+
;;
|
|
58
|
+
esac
|
|
59
|
+
if ! command -v proot-distro &>/dev/null; then
|
|
60
|
+
pkg install proot-distro -y || return 1
|
|
61
|
+
fi
|
|
62
|
+
if ! proot-distro login ubuntu -- true &>/dev/null; then
|
|
63
|
+
proot-distro install ubuntu:24.04 || return 1
|
|
64
|
+
fi
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
_cactus_validate() {
|
|
68
|
+
_cactus_proot "$CACTUS_CONTAINER_DIR/venv/bin/cactus" --help &>/dev/null
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
_install_cactus_inside_ubuntu() {
|
|
72
|
+
_cactus_proot /bin/bash -c '
|
|
73
|
+
set -e
|
|
74
|
+
export DEBIAN_FRONTEND=noninteractive
|
|
75
|
+
if [ -f /etc/apt/sources.list.d/ubuntu.sources ]; then
|
|
76
|
+
official_source=/etc/apt/sources.list.d/ubuntu.sources
|
|
77
|
+
else
|
|
78
|
+
official_source=/etc/apt/sources.list
|
|
79
|
+
fi
|
|
80
|
+
apt_options=(-o "Dir::Etc::sourcelist=$official_source" -o "Dir::Etc::sourceparts=-")
|
|
81
|
+
apt-get "${apt_options[@]}" update -qq
|
|
82
|
+
apt-get "${apt_options[@]}" install -y -qq ca-certificates python3 python3-pip python3-venv
|
|
83
|
+
if [ -e /opt/karnel/cactus ] && [ ! -f /opt/karnel/cactus/.karnel-managed ]; then
|
|
84
|
+
echo "Refusing to replace unowned /opt/karnel/cactus" >&2
|
|
85
|
+
exit 1
|
|
86
|
+
fi
|
|
87
|
+
rm -rf /opt/karnel/cactus
|
|
88
|
+
mkdir -p /opt/karnel/cactus
|
|
89
|
+
: > /opt/karnel/cactus/.karnel-managed
|
|
90
|
+
if python3 -c "import sys; raise SystemExit(sys.version_info >= (3, 14))"; then
|
|
91
|
+
python3 -m venv /opt/karnel/cactus/venv
|
|
92
|
+
else
|
|
93
|
+
python3 -m venv /opt/karnel/cactus/bootstrap
|
|
94
|
+
/opt/karnel/cactus/bootstrap/bin/python -m pip install --upgrade uv
|
|
95
|
+
export UV_PYTHON_INSTALL_DIR=/opt/karnel/cactus/python
|
|
96
|
+
/opt/karnel/cactus/bootstrap/bin/uv python install 3.13
|
|
97
|
+
/opt/karnel/cactus/bootstrap/bin/uv venv --seed --python 3.13 /opt/karnel/cactus/venv
|
|
98
|
+
fi
|
|
99
|
+
/opt/karnel/cactus/venv/bin/python -m pip install --upgrade "cactus-compute==2.0.1"
|
|
100
|
+
/opt/karnel/cactus/venv/bin/cactus --help >/dev/null
|
|
101
|
+
'
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
_cactus_write_wrapper() {
|
|
105
|
+
mkdir -p "$CACTUS_DATA_DIR" "$PREFIX/bin" || return 1
|
|
106
|
+
: >"$CACTUS_DATA_DIR/.karnel-managed" || return 1
|
|
107
|
+
local temporary
|
|
108
|
+
temporary="$(mktemp "$PREFIX/bin/.cactus.XXXXXX")" || return 1
|
|
109
|
+
cat >"$temporary" <<EOF
|
|
110
|
+
#!$PREFIX/bin/bash
|
|
111
|
+
# Karnel-managed Cactus wrapper
|
|
112
|
+
exec proot-distro login --shared-tmp ubuntu -- $CACTUS_CONTAINER_DIR/venv/bin/cactus "\$@"
|
|
113
|
+
EOF
|
|
114
|
+
chmod 755 "$temporary" || { rm -f "$temporary"; return 1; }
|
|
115
|
+
mv -f "$temporary" "$CACTUS_WRAPPER" || return 1
|
|
116
|
+
sha256sum "$CACTUS_WRAPPER" >"$CACTUS_DATA_DIR/.karnel-wrapper-cactus" || return 1
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
install_cactus() {
|
|
120
|
+
if _cactus_data_owned && _cactus_wrapper_owned && _cactus_container_owned && _cactus_validate; then
|
|
121
|
+
log_info "Cactus is already installed"
|
|
122
|
+
return 2
|
|
123
|
+
fi
|
|
124
|
+
_cactus_verify_ownership || return 1
|
|
125
|
+
|
|
126
|
+
log_info "Installing Cactus $CACTUS_VERSION in Ubuntu (glibc compatibility)..."
|
|
127
|
+
_cactus_ensure_ubuntu || return 1
|
|
128
|
+
if ! _install_cactus_inside_ubuntu || ! _cactus_write_wrapper || ! _cactus_validate; then
|
|
129
|
+
_cactus_wrapper_owned && rm -f "$CACTUS_WRAPPER"
|
|
130
|
+
_cactus_data_owned && rm -rf "$CACTUS_DATA_DIR"
|
|
131
|
+
log_error "Failed to install Cactus"
|
|
132
|
+
return 1
|
|
133
|
+
fi
|
|
134
|
+
log_success "Cactus installed"
|
|
135
|
+
log_info "Start with: cactus run Cactus-Compute/needle"
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
uninstall_cactus() {
|
|
139
|
+
if ! _cactus_data_owned && ! _cactus_wrapper_owned && ! _cactus_container_owned; then
|
|
140
|
+
if [[ -e "$CACTUS_DATA_DIR" || -e "$CACTUS_WRAPPER" ]]; then
|
|
141
|
+
_cactus_verify_ownership
|
|
142
|
+
return $?
|
|
143
|
+
fi
|
|
144
|
+
log_info "Cactus is not installed by Karnel"
|
|
145
|
+
return 2
|
|
146
|
+
fi
|
|
147
|
+
_cactus_verify_ownership || return 1
|
|
148
|
+
|
|
149
|
+
_cactus_wrapper_owned && rm -f "$CACTUS_WRAPPER"
|
|
150
|
+
if _cactus_container_owned; then
|
|
151
|
+
_cactus_proot rm -rf "$CACTUS_CONTAINER_DIR" || return 1
|
|
152
|
+
fi
|
|
153
|
+
_cactus_data_owned && rm -rf "$CACTUS_DATA_DIR"
|
|
154
|
+
log_success "Cactus uninstalled; downloaded model data outside its environment was preserved"
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
update_cactus() {
|
|
158
|
+
_cactus_data_owned && _cactus_wrapper_owned && _cactus_container_owned || {
|
|
159
|
+
log_error "Cactus is not installed by Karnel"
|
|
160
|
+
return 1
|
|
161
|
+
}
|
|
162
|
+
_cactus_verify_ownership || return 1
|
|
163
|
+
_cactus_proot "$CACTUS_CONTAINER_DIR/venv/bin/python" -m pip install --upgrade 'cactus-compute>=2,<3' || return 1
|
|
164
|
+
_cactus_validate || return 1
|
|
165
|
+
log_success "Cactus updated"
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
reinstall_cactus() {
|
|
169
|
+
uninstall_cactus || [[ $? -eq 2 ]] || return 1
|
|
170
|
+
install_cactus
|
|
171
|
+
}
|
|
@@ -142,7 +142,7 @@ uninstall_codegraph() {
|
|
|
142
142
|
log_info "Uninstalling CodeGraph..."
|
|
143
143
|
mkdir -p "$(dirname "$LOG_FILE")"
|
|
144
144
|
|
|
145
|
-
loading "Removing CodeGraph" _uninstall_codegraph_impl
|
|
145
|
+
loading "Removing CodeGraph" _uninstall_codegraph_impl || return 1
|
|
146
146
|
|
|
147
147
|
log_success "CodeGraph uninstalled"
|
|
148
148
|
return 0
|
|
@@ -67,7 +67,7 @@ uninstall_codex() {
|
|
|
67
67
|
log_info "Uninstalling Codex CLI..."
|
|
68
68
|
mkdir -p "$(dirname "$LOG_FILE")"
|
|
69
69
|
|
|
70
|
-
loading "Removing Codex CLI" _uninstall_codex_impl
|
|
70
|
+
loading "Removing Codex CLI" _uninstall_codex_impl || return 1
|
|
71
71
|
|
|
72
72
|
log_success "Codex CLI uninstalled"
|
|
73
73
|
return 0
|
|
@@ -122,7 +122,7 @@ uninstall_command_code() {
|
|
|
122
122
|
log_info "Uninstalling Command Code..."
|
|
123
123
|
mkdir -p "$(dirname "$LOG_FILE")"
|
|
124
124
|
|
|
125
|
-
loading "Removing Command Code files" _uninstall_command_code_impl
|
|
125
|
+
loading "Removing Command Code files" _uninstall_command_code_impl || return 1
|
|
126
126
|
|
|
127
127
|
log_success "Command Code uninstalled"
|
|
128
128
|
return 0
|
|
@@ -85,7 +85,7 @@ uninstall_engram() {
|
|
|
85
85
|
log_info "Uninstalling Engram..."
|
|
86
86
|
mkdir -p "$(dirname "$LOG_FILE")"
|
|
87
87
|
|
|
88
|
-
loading "Removing Engram" _uninstall_engram_impl
|
|
88
|
+
loading "Removing Engram" _uninstall_engram_impl || return 1
|
|
89
89
|
|
|
90
90
|
log_success "Engram uninstalled"
|
|
91
91
|
return 0
|
|
@@ -71,7 +71,7 @@ uninstall_gemini_cli() {
|
|
|
71
71
|
log_info "Uninstalling Gemini CLI..."
|
|
72
72
|
mkdir -p "$(dirname "$LOG_FILE")"
|
|
73
73
|
|
|
74
|
-
loading "Removing Gemini CLI" _uninstall_gemini_cli_impl
|
|
74
|
+
loading "Removing Gemini CLI" _uninstall_gemini_cli_impl || return 1
|
|
75
75
|
|
|
76
76
|
log_success "Gemini CLI uninstalled"
|
|
77
77
|
return 0
|
|
@@ -86,7 +86,7 @@ uninstall_hermes_agent() {
|
|
|
86
86
|
log_info "Uninstalling Hermes Agent..."
|
|
87
87
|
mkdir -p "$(dirname "$LOG_FILE")"
|
|
88
88
|
|
|
89
|
-
loading "Removing Hermes Agent" _uninstall_hermes_agent_impl
|
|
89
|
+
loading "Removing Hermes Agent" _uninstall_hermes_agent_impl || return 1
|
|
90
90
|
|
|
91
91
|
log_success "Hermes Agent uninstalled successfully"
|
|
92
92
|
return 0
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Hugging Face CLI
|
|
2
|
+
|
|
3
|
+
Official `hf` command-line client for the Hugging Face Hub, installed in an
|
|
4
|
+
isolated Python environment so it does not modify the user's global packages.
|
|
5
|
+
|
|
6
|
+
## Install
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
karnel install ai --hugging-face
|
|
10
|
+
hf auth login
|
|
11
|
+
hf auth whoami
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Common commands:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
hf models ls --search qwen --limit 10
|
|
18
|
+
hf download Cactus-Compute/needle
|
|
19
|
+
hf cache ls
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Karnel stores the managed environment under
|
|
23
|
+
`~/.local/share/karnel-data/hugging-face`. Uninstalling removes only that
|
|
24
|
+
environment and the Karnel-owned `hf` command. Credentials and downloaded
|
|
25
|
+
models under `~/.cache/huggingface` are preserved.
|
|
26
|
+
|
|
27
|
+
The optional Xet accelerator is disabled because it does not publish an
|
|
28
|
+
Android wheel. Downloads use the standard Hugging Face HTTP transport instead.
|
|
29
|
+
|
|
30
|
+
Do not put Hugging Face tokens in shell history, repositories, screenshots, or
|
|
31
|
+
issue reports. Prefer the interactive `hf auth login` flow or the `HF_TOKEN`
|
|
32
|
+
environment variable.
|
|
33
|
+
|
|
34
|
+
Official documentation: <https://huggingface.co/docs/huggingface_hub/guides/cli>
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
import "@/utils/log"
|
|
4
|
+
|
|
5
|
+
: "${KARNEL_DATA:=${XDG_DATA_HOME:-$HOME/.local/share}/karnel-data}"
|
|
6
|
+
HUGGING_FACE_DATA_DIR="$KARNEL_DATA/hugging-face"
|
|
7
|
+
HUGGING_FACE_VENV="$HUGGING_FACE_DATA_DIR/venv"
|
|
8
|
+
HUGGING_FACE_WRAPPER="$PREFIX/bin/hf"
|
|
9
|
+
|
|
10
|
+
_hugging_face_data_owned() {
|
|
11
|
+
[[ -f "$HUGGING_FACE_DATA_DIR/.karnel-managed" ]]
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
_hugging_face_wrapper_owned() {
|
|
15
|
+
local marker="$HUGGING_FACE_DATA_DIR/.karnel-wrapper-hf"
|
|
16
|
+
[[ -f "$marker" && -f "$HUGGING_FACE_WRAPPER" ]] || return 1
|
|
17
|
+
[[ "$(sha256sum "$HUGGING_FACE_WRAPPER" 2>/dev/null)" == "$(<"$marker")" ]]
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
_hugging_face_verify_ownership() {
|
|
21
|
+
if [[ -e "$HUGGING_FACE_DATA_DIR" ]] && ! _hugging_face_data_owned; then
|
|
22
|
+
log_error "Refusing to replace unowned Hugging Face data: $HUGGING_FACE_DATA_DIR"
|
|
23
|
+
return 1
|
|
24
|
+
fi
|
|
25
|
+
if [[ -e "$HUGGING_FACE_WRAPPER" || -L "$HUGGING_FACE_WRAPPER" ]] && ! _hugging_face_wrapper_owned; then
|
|
26
|
+
log_error "Refusing to replace unowned command: $HUGGING_FACE_WRAPPER"
|
|
27
|
+
return 1
|
|
28
|
+
fi
|
|
29
|
+
if command -v hf &>/dev/null && ! _hugging_face_wrapper_owned; then
|
|
30
|
+
log_error "Refusing to shadow the existing hf command: $(command -v hf)"
|
|
31
|
+
return 1
|
|
32
|
+
fi
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
_hugging_face_validate() {
|
|
36
|
+
"$HUGGING_FACE_VENV/bin/hf" --help &>/dev/null
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
_hugging_face_install_packages() {
|
|
40
|
+
local python="$HUGGING_FACE_VENV/bin/python"
|
|
41
|
+
"$python" -m pip install --upgrade \
|
|
42
|
+
'click>=8.1' 'filelock>=3.10' 'fsspec>=2023.5' 'httpx>=0.23,<1' \
|
|
43
|
+
'packaging>=20.9' 'pyyaml>=5.1' shellingham 'tqdm>=4.42.1' \
|
|
44
|
+
typer-slim 'typing-extensions>=3.7.4.3' || return 1
|
|
45
|
+
# hf-xet has no Android wheel and its Rust bootstrap rejects the Android target.
|
|
46
|
+
"$python" -m pip install --upgrade --no-deps 'huggingface_hub>=1,<2'
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
_hugging_face_write_wrapper() {
|
|
50
|
+
local temporary
|
|
51
|
+
temporary="$(mktemp "$PREFIX/bin/.hf.XXXXXX")" || return 1
|
|
52
|
+
cat >"$temporary" <<EOF
|
|
53
|
+
#!$PREFIX/bin/bash
|
|
54
|
+
# Karnel-managed Hugging Face CLI wrapper
|
|
55
|
+
export HF_HUB_DISABLE_XET=1
|
|
56
|
+
exec $HUGGING_FACE_VENV/bin/hf "\$@"
|
|
57
|
+
EOF
|
|
58
|
+
chmod 755 "$temporary" || { rm -f "$temporary"; return 1; }
|
|
59
|
+
mv -f "$temporary" "$HUGGING_FACE_WRAPPER" || return 1
|
|
60
|
+
sha256sum "$HUGGING_FACE_WRAPPER" >"$HUGGING_FACE_DATA_DIR/.karnel-wrapper-hf" || return 1
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
_install_hugging_face_impl() {
|
|
64
|
+
if ! command -v python3 &>/dev/null; then
|
|
65
|
+
pkg install python -y || return 1
|
|
66
|
+
fi
|
|
67
|
+
python3 -c 'import sys; raise SystemExit(sys.version_info < (3, 9))' || {
|
|
68
|
+
log_error "Hugging Face CLI requires Python 3.9 or newer"
|
|
69
|
+
return 1
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
mkdir -p "$HUGGING_FACE_DATA_DIR" "$PREFIX/bin" || return 1
|
|
73
|
+
: >"$HUGGING_FACE_DATA_DIR/.karnel-managed" || return 1
|
|
74
|
+
rm -rf "$HUGGING_FACE_VENV"
|
|
75
|
+
python3 -m venv "$HUGGING_FACE_VENV" || return 1
|
|
76
|
+
_hugging_face_install_packages || return 1
|
|
77
|
+
_hugging_face_validate || return 1
|
|
78
|
+
_hugging_face_write_wrapper
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
install_hugging_face() {
|
|
82
|
+
if _hugging_face_data_owned && _hugging_face_wrapper_owned && _hugging_face_validate; then
|
|
83
|
+
log_info "Hugging Face CLI is already installed"
|
|
84
|
+
return 2
|
|
85
|
+
fi
|
|
86
|
+
_hugging_face_verify_ownership || return 1
|
|
87
|
+
|
|
88
|
+
log_info "Installing Hugging Face CLI in an isolated environment..."
|
|
89
|
+
if ! _install_hugging_face_impl; then
|
|
90
|
+
_hugging_face_wrapper_owned && rm -f "$HUGGING_FACE_WRAPPER"
|
|
91
|
+
_hugging_face_data_owned && rm -rf "$HUGGING_FACE_DATA_DIR"
|
|
92
|
+
log_error "Failed to install Hugging Face CLI"
|
|
93
|
+
return 1
|
|
94
|
+
fi
|
|
95
|
+
log_success "Hugging Face CLI installed"
|
|
96
|
+
log_info "Authenticate with: hf auth login"
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
uninstall_hugging_face() {
|
|
100
|
+
if ! _hugging_face_data_owned && ! _hugging_face_wrapper_owned; then
|
|
101
|
+
if [[ -e "$HUGGING_FACE_DATA_DIR" || -e "$HUGGING_FACE_WRAPPER" || -L "$HUGGING_FACE_WRAPPER" ]]; then
|
|
102
|
+
_hugging_face_verify_ownership
|
|
103
|
+
return $?
|
|
104
|
+
fi
|
|
105
|
+
log_info "Hugging Face CLI is not installed by Karnel"
|
|
106
|
+
return 2
|
|
107
|
+
fi
|
|
108
|
+
_hugging_face_verify_ownership || return 1
|
|
109
|
+
|
|
110
|
+
_hugging_face_wrapper_owned && rm -f "$HUGGING_FACE_WRAPPER"
|
|
111
|
+
_hugging_face_data_owned && rm -rf "$HUGGING_FACE_DATA_DIR"
|
|
112
|
+
log_success "Hugging Face CLI uninstalled; credentials and model cache were preserved"
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
update_hugging_face() {
|
|
116
|
+
_hugging_face_data_owned && _hugging_face_wrapper_owned || {
|
|
117
|
+
log_error "Hugging Face CLI is not installed by Karnel"
|
|
118
|
+
return 1
|
|
119
|
+
}
|
|
120
|
+
_hugging_face_verify_ownership || return 1
|
|
121
|
+
_hugging_face_install_packages || return 1
|
|
122
|
+
_hugging_face_validate || return 1
|
|
123
|
+
log_success "Hugging Face CLI updated"
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
reinstall_hugging_face() {
|
|
127
|
+
uninstall_hugging_face || [[ $? -eq 2 ]] || return 1
|
|
128
|
+
install_hugging_face
|
|
129
|
+
}
|
|
@@ -195,10 +195,10 @@ uninstall_kilocode_cli() {
|
|
|
195
195
|
|
|
196
196
|
if ! _kilocode_wrapper_owned && ! _kilocode_alias_owned && ! _kilocode_data_owned; then
|
|
197
197
|
log_warn "Kilo Code CLI is not installed"
|
|
198
|
-
return
|
|
198
|
+
return 2
|
|
199
199
|
fi
|
|
200
200
|
|
|
201
|
-
loading "Uninstalling Kilo Code CLI" _uninstall_kilocode_cli_impl
|
|
201
|
+
loading "Uninstalling Kilo Code CLI" _uninstall_kilocode_cli_impl || return 1
|
|
202
202
|
}
|
|
203
203
|
|
|
204
204
|
_uninstall_kilocode_cli_impl() {
|
|
@@ -213,6 +213,6 @@ update_kilocode_cli() {
|
|
|
213
213
|
}
|
|
214
214
|
|
|
215
215
|
reinstall_kilocode_cli() {
|
|
216
|
-
uninstall_kilocode_cli
|
|
216
|
+
uninstall_kilocode_cli || [[ $? -eq 2 ]] || return 1
|
|
217
217
|
install_kilocode_cli
|
|
218
218
|
}
|
|
@@ -69,7 +69,7 @@ uninstall_kimi_code() {
|
|
|
69
69
|
log_info "Uninstalling Kimi Code..."
|
|
70
70
|
mkdir -p "$(dirname "$LOG_FILE")"
|
|
71
71
|
|
|
72
|
-
loading "Removing Kimi Code" _uninstall_kimi_code_impl
|
|
72
|
+
loading "Removing Kimi Code" _uninstall_kimi_code_impl || return 1
|
|
73
73
|
|
|
74
74
|
log_success "Kimi Code uninstalled successfully"
|
|
75
75
|
return 0
|
|
@@ -69,7 +69,7 @@ uninstall_minimax_cli() {
|
|
|
69
69
|
log_info "Uninstalling MiniMax CLI..."
|
|
70
70
|
mkdir -p "$(dirname "$LOG_FILE")"
|
|
71
71
|
|
|
72
|
-
loading "Removing MiniMax CLI" _uninstall_minimax_cli_impl
|
|
72
|
+
loading "Removing MiniMax CLI" _uninstall_minimax_cli_impl || return 1
|
|
73
73
|
|
|
74
74
|
log_success "MiniMax CLI uninstalled successfully"
|
|
75
75
|
return 0
|
|
@@ -79,7 +79,7 @@ uninstall_mistral_vibe() {
|
|
|
79
79
|
log_info "Uninstalling Mistral Vibe..."
|
|
80
80
|
mkdir -p "$(dirname "$LOG_FILE")"
|
|
81
81
|
|
|
82
|
-
loading "Removing Mistral Vibe" _uninstall_mistral_vibe_impl
|
|
82
|
+
loading "Removing Mistral Vibe" _uninstall_mistral_vibe_impl || return 1
|
|
83
83
|
|
|
84
84
|
log_success "Mistral Vibe uninstalled"
|
|
85
85
|
return 0
|
|
@@ -226,10 +226,10 @@ _omp_is_proot() {
|
|
|
226
226
|
uninstall_oh_my_pi() {
|
|
227
227
|
mkdir -p "$(dirname "$LOG_FILE")"
|
|
228
228
|
if _omp_is_native; then
|
|
229
|
-
loading "Uninstalling Oh-My-Pi (native)" _uninstall_omp_native_impl
|
|
229
|
+
loading "Uninstalling Oh-My-Pi (native)" _uninstall_omp_native_impl || return 1
|
|
230
230
|
log_success "Oh-My-Pi (native) uninstalled"
|
|
231
231
|
elif _omp_is_proot; then
|
|
232
|
-
loading "Uninstalling Oh-My-Pi (proot-distro)" _uninstall_omp_proot_impl
|
|
232
|
+
loading "Uninstalling Oh-My-Pi (proot-distro)" _uninstall_omp_proot_impl || return 1
|
|
233
233
|
log_success "Oh-My-Pi (proot-distro) uninstalled"
|
|
234
234
|
else
|
|
235
235
|
log_info "Oh-My-Pi is not installed"
|
|
@@ -41,7 +41,7 @@ uninstall_ollama() {
|
|
|
41
41
|
log_info "Uninstalling Ollama..."
|
|
42
42
|
mkdir -p "$(dirname "$LOG_FILE")"
|
|
43
43
|
|
|
44
|
-
loading "Removing Ollama" _uninstall_ollama_impl
|
|
44
|
+
loading "Removing Ollama" _uninstall_ollama_impl || return 1
|
|
45
45
|
|
|
46
46
|
log_success "Ollama uninstalled"
|
|
47
47
|
return 0
|
|
@@ -70,7 +70,7 @@ uninstall_openclaude() {
|
|
|
70
70
|
log_info "Uninstalling OpenClaude..."
|
|
71
71
|
mkdir -p "$(dirname "$LOG_FILE")"
|
|
72
72
|
|
|
73
|
-
loading "Removing OpenClaude" _uninstall_openclaude_impl
|
|
73
|
+
loading "Removing OpenClaude" _uninstall_openclaude_impl || return 1
|
|
74
74
|
|
|
75
75
|
log_success "OpenClaude uninstalled"
|
|
76
76
|
return 0
|
|
@@ -72,7 +72,7 @@ uninstall_openclaw() {
|
|
|
72
72
|
log_info "Uninstalling OpenClaw..."
|
|
73
73
|
mkdir -p "$(dirname "$LOG_FILE")"
|
|
74
74
|
|
|
75
|
-
loading "Removing OpenClaw" _uninstall_openclaw_impl
|
|
75
|
+
loading "Removing OpenClaw" _uninstall_openclaw_impl || return 1
|
|
76
76
|
|
|
77
77
|
log_success "OpenClaw uninstalled"
|
|
78
78
|
return 0
|
|
@@ -67,7 +67,7 @@ uninstall_pi() {
|
|
|
67
67
|
log_info "Uninstalling Pi Coding Agent..."
|
|
68
68
|
mkdir -p "$(dirname "$LOG_FILE")"
|
|
69
69
|
|
|
70
|
-
loading "Removing Pi Coding Agent" _uninstall_pi_impl
|
|
70
|
+
loading "Removing Pi Coding Agent" _uninstall_pi_impl || return 1
|
|
71
71
|
|
|
72
72
|
log_success "Pi uninstalled"
|
|
73
73
|
return 0
|
|
@@ -259,10 +259,10 @@ uninstall_qoder() {
|
|
|
259
259
|
|
|
260
260
|
if [ ! -f "$PREFIX/bin/qodercli" ]; then
|
|
261
261
|
log_warn "Qoder is not installed"
|
|
262
|
-
return
|
|
262
|
+
return 2
|
|
263
263
|
fi
|
|
264
264
|
|
|
265
|
-
loading "Uninstalling Qoder" _uninstall_qoder_impl
|
|
265
|
+
loading "Uninstalling Qoder" _uninstall_qoder_impl || return 1
|
|
266
266
|
}
|
|
267
267
|
|
|
268
268
|
_uninstall_qoder_impl() {
|
|
@@ -71,7 +71,7 @@ uninstall_qwen_code() {
|
|
|
71
71
|
log_info "Uninstalling Qwen Code..."
|
|
72
72
|
mkdir -p "$(dirname "$LOG_FILE")"
|
|
73
73
|
|
|
74
|
-
loading "Removing Qwen Code" _uninstall_qwen_code_impl
|
|
74
|
+
loading "Removing Qwen Code" _uninstall_qwen_code_impl || return 1
|
|
75
75
|
|
|
76
76
|
log_success "Qwen Code uninstalled"
|
|
77
77
|
return 0
|
package/karnel/utils/tools.sh
CHANGED
|
@@ -13,7 +13,14 @@ _batch_tool_action() {
|
|
|
13
13
|
|
|
14
14
|
for tool in "${tools[@]}"; do
|
|
15
15
|
local func_name="${action}_${tool//-/_}"
|
|
16
|
-
if declare -f
|
|
16
|
+
if [[ "$module" == "ai" ]] && declare -f _run_ai_tool_action &>/dev/null; then
|
|
17
|
+
_run_ai_tool_action "$action" "$tool"
|
|
18
|
+
case $? in
|
|
19
|
+
0) ((success_count++));;
|
|
20
|
+
2) ((skipped_count++));;
|
|
21
|
+
*) ((failed_count++));;
|
|
22
|
+
esac
|
|
23
|
+
elif declare -f "$func_name" &>/dev/null; then
|
|
17
24
|
"$func_name"
|
|
18
25
|
case $? in
|
|
19
26
|
0) ((success_count++));;
|
package/package.json
CHANGED