karnel-termux 4.17.7 → 4.17.9
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 +2 -2
- package/docs/CHANGELOG.md +53 -0
- package/docs/_config.yml +4 -1
- package/docs/assets/css/{style.css → style.scss} +52 -9
- package/docs/doctor/index.md +1 -1
- package/docs/index.md +11 -8
- package/docs/troubleshooting/index.md +15 -6
- package/karnel/RELEASE_COMMIT +1 -1
- package/karnel/cli/commands/show.sh +9 -2
- package/karnel/cli/commands/update.sh +12 -2
- package/karnel/tools/ai/antigravity-cli/install.sh +6 -3
- package/karnel/tools/ai/cursor-cli/install.sh +7 -0
- package/karnel/tools/ai/droid/install.sh +10 -1
- package/karnel/tools/ai/keelcode/install.sh +1 -0
- package/karnel/tools/deploy/railway/install.sh +10 -7
- package/karnel/tools/deploy/supabase/install.sh +16 -3
- package/karnel/tools/lang/bun/install.sh +23 -13
- package/karnel/tools/utils/zork/install.sh +5 -1
- package/karnel/tools/voice/uninstall.sh +12 -5
- package/karnel/utils/agent_actions.sh +19 -4
- package/karnel/utils/agent_llm.sh +14 -1
- package/karnel/utils/install.sh +14 -6
- package/karnel/utils/log.sh +3 -1
- package/karnel/utils/tools.sh +0 -3
- package/karnel/utils/version.sh +3 -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.17.
|
|
11
|
+
<img src="https://img.shields.io/badge/version-4.17.9-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">
|
|
@@ -61,7 +61,7 @@ Core agent contributions by **devcorex**.
|
|
|
61
61
|
### Via checksummed GitHub release (recommended)
|
|
62
62
|
|
|
63
63
|
```bash
|
|
64
|
-
version=4.17.
|
|
64
|
+
version=4.17.9
|
|
65
65
|
tmpdir=$(mktemp -d) && trap 'rm -rf "$tmpdir"' EXIT
|
|
66
66
|
base="https://github.com/israelmarques1024-dotcom/karnel-termux/releases/download/v$version"
|
|
67
67
|
curl -fsSL "$base/karnel-termux-install.sh" -o "$tmpdir/karnel-termux-install.sh"
|
package/docs/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,58 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Documentation Changelog
|
|
3
|
+
permalink: /CHANGELOG/
|
|
4
|
+
---
|
|
5
|
+
|
|
1
6
|
# Documentation Changelog
|
|
2
7
|
|
|
8
|
+
## 4.17.9
|
|
9
|
+
|
|
10
|
+
- Hardened installer and runtime security across the toolkit:
|
|
11
|
+
- `droid` binary install now refuses when no published SHA-256 digest is
|
|
12
|
+
available (fail-closed) instead of silently skipping verification.
|
|
13
|
+
- `keelcode` and `supabase` now extract archives through the hardened
|
|
14
|
+
`safe_extract_tar` (rejects symlink/absolute-path traversal) instead of
|
|
15
|
+
raw `tar`.
|
|
16
|
+
- Restored OpenCode installation: upstream publishes no checksum, so
|
|
17
|
+
`github_download_and_extract` warns instead of refusing (other tools that
|
|
18
|
+
publish checksums remain fail-closed).
|
|
19
|
+
- `cursor-cli` now validates the downloaded bundle is a real gzip archive
|
|
20
|
+
before extraction.
|
|
21
|
+
- Agent PLAN (read-only) mode write-guard hardened: now blocks `sed -i` /
|
|
22
|
+
`perl -i`, full-screen editors, and file descriptors like `2>file`.
|
|
23
|
+
- Agent compaction no longer drops the freshly built summary during the
|
|
24
|
+
trim safety-net.
|
|
25
|
+
- `voice` uninstall no longer removes `termux-api` unless Karnel installed it.
|
|
26
|
+
- `karnel show` no longer depends on `realpath` (portable fallback).
|
|
27
|
+
- `karnel update` reports the accurate installed version instead of a
|
|
28
|
+
misleading `latest`.
|
|
29
|
+
- Reinstall no longer deletes the ownership marker before confirming the
|
|
30
|
+
install, preventing a tool from being orphaned on install failure.
|
|
31
|
+
|
|
32
|
+
## 4.17.8
|
|
33
|
+
|
|
34
|
+
- Documentation site reconciled with the GitHub Pages deployment: the `Publish
|
|
35
|
+
Docs` workflow (`.github/workflows/docs.yml`) builds Jekyll from `docs/` and
|
|
36
|
+
deploys to GitHub Pages, so the live documentation URL is
|
|
37
|
+
`https://israelmarques1024-dotcom.github.io/karnel-termux/`.
|
|
38
|
+
- `docs/CHANGELOG.md` is now a rendered page (added front matter + permalink)
|
|
39
|
+
and all internal links to it use the clean `/CHANGELOG/` path.
|
|
40
|
+
- Tool counts verified against the actual `karnel/tools/*` and
|
|
41
|
+
`karnel/tools/ai/*` directories: 45 AI tools, 3 editors, 8 languages,
|
|
42
|
+
5 databases, 22 dev tools, 11 npm packages, 10 shell plugins, 4 UI
|
|
43
|
+
components, 4 deploy CLIs, 6 games, 2 network tools, 12 utility tools,
|
|
44
|
+
30 security tools, 1 automation tool.
|
|
45
|
+
|
|
46
|
+
## 4.17.7
|
|
47
|
+
|
|
48
|
+
- Documentation site overhauled: valid `_config.yml`, cayman-compatible custom
|
|
49
|
+
stylesheet that imports the full theme, a workflow that builds Jekyll before
|
|
50
|
+
deploying to GitHub Pages, and tool counts reconciled with the actual
|
|
51
|
+
`karnel/tools/*` and `karnel/tools/ai/*` directories (45 AI tools, 3 editors,
|
|
52
|
+
8 languages, 5 databases, 22 dev tools, 11 npm packages, 10 shell plugins,
|
|
53
|
+
4 UI components, 4 deploy CLIs, 6 games, 2 network tools, 12 utility tools,
|
|
54
|
+
30 security tools, 1 automation tool).
|
|
55
|
+
|
|
3
56
|
## 4.16.1 - 2026-08-22
|
|
4
57
|
|
|
5
58
|
- `karnel agent ask/run`: when the model endpoint is unreachable and Cactus is
|
package/docs/_config.yml
CHANGED
|
@@ -2,11 +2,14 @@ title: Karnel Termux
|
|
|
2
2
|
description: >-
|
|
3
3
|
Modular Dev Environment for Termux — install languages, databases, AI agents,
|
|
4
4
|
editors, and more with one command.
|
|
5
|
+
author: Israel Marques
|
|
6
|
+
lang: en-US
|
|
5
7
|
theme: jekyll-theme-cayman
|
|
6
8
|
markdown: kramdown
|
|
7
9
|
logo: /assets/images/karnel-logo.png
|
|
8
|
-
custom_css: /assets/css/style.css
|
|
10
|
+
custom_css: /karnel-termux/assets/css/style.css
|
|
9
11
|
baseurl: /karnel-termux
|
|
12
|
+
show_downloads: false
|
|
10
13
|
exclude:
|
|
11
14
|
- vendor
|
|
12
15
|
- Gemfile
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
---
|
|
2
|
+
---
|
|
3
|
+
|
|
4
|
+
@import "jekyll-theme-cayman";
|
|
5
|
+
|
|
1
6
|
/* Karnel Termux — custom styling layered on top of jekyll-theme-cayman */
|
|
2
7
|
|
|
3
8
|
:root {
|
|
@@ -9,6 +14,7 @@
|
|
|
9
14
|
--karnel-text: #c9d1d9;
|
|
10
15
|
--karnel-muted: #8b949e;
|
|
11
16
|
--karnel-code-bg: #161b22;
|
|
17
|
+
color-scheme: dark light;
|
|
12
18
|
}
|
|
13
19
|
|
|
14
20
|
html {
|
|
@@ -21,14 +27,34 @@ body {
|
|
|
21
27
|
color: #24292f;
|
|
22
28
|
}
|
|
23
29
|
|
|
30
|
+
/* Logo (added in page content; cayman's default.html does not render site.logo) */
|
|
31
|
+
.karnel-logo {
|
|
32
|
+
display: block;
|
|
33
|
+
margin: 0 auto 1.25rem;
|
|
34
|
+
max-width: 220px;
|
|
35
|
+
height: auto;
|
|
36
|
+
border-radius: 14px;
|
|
37
|
+
box-shadow: 0 6px 22px rgba(0, 0, 0, 0.18);
|
|
38
|
+
}
|
|
39
|
+
|
|
24
40
|
@media (prefers-color-scheme: dark) {
|
|
41
|
+
html {
|
|
42
|
+
background: var(--karnel-bg);
|
|
43
|
+
}
|
|
25
44
|
body {
|
|
26
45
|
background: var(--karnel-bg);
|
|
27
46
|
color: var(--karnel-text);
|
|
28
47
|
}
|
|
48
|
+
.main-content {
|
|
49
|
+
color: var(--karnel-text);
|
|
50
|
+
}
|
|
29
51
|
.page-header {
|
|
30
52
|
background-image: linear-gradient(120deg, #0078d4, #2f81f7);
|
|
31
53
|
}
|
|
54
|
+
.page-header .project-name,
|
|
55
|
+
.page-header .project-tagline {
|
|
56
|
+
color: #ffffff;
|
|
57
|
+
}
|
|
32
58
|
.main-content h1,
|
|
33
59
|
.main-content h2,
|
|
34
60
|
.main-content h3,
|
|
@@ -53,15 +79,6 @@ body {
|
|
|
53
79
|
font-size: 1.1rem;
|
|
54
80
|
}
|
|
55
81
|
|
|
56
|
-
/* Logo */
|
|
57
|
-
.karnel-logo {
|
|
58
|
-
display: block;
|
|
59
|
-
margin: 0 auto 1.25rem;
|
|
60
|
-
max-width: 220px;
|
|
61
|
-
height: auto;
|
|
62
|
-
filter: drop-shadow(0 4px 12px rgba(0, 0, 0, 0.35));
|
|
63
|
-
}
|
|
64
|
-
|
|
65
82
|
/* Landing navigation */
|
|
66
83
|
.karnel-nav {
|
|
67
84
|
display: flex;
|
|
@@ -149,6 +166,15 @@ body {
|
|
|
149
166
|
font-size: 0.88em;
|
|
150
167
|
}
|
|
151
168
|
|
|
169
|
+
/* Inline code should not be wrapped in its own block styling */
|
|
170
|
+
.main-content p code,
|
|
171
|
+
.main-content li code,
|
|
172
|
+
.main-content td code {
|
|
173
|
+
background: rgba(120, 120, 120, 0.12);
|
|
174
|
+
padding: 0.12em 0.4em;
|
|
175
|
+
border-radius: 4px;
|
|
176
|
+
}
|
|
177
|
+
|
|
152
178
|
/* Blockquotes */
|
|
153
179
|
.main-content blockquote {
|
|
154
180
|
border-left: 4px solid var(--karnel-accent-2, #0078d4);
|
|
@@ -158,6 +184,16 @@ body {
|
|
|
158
184
|
border-radius: 0 6px 6px 0;
|
|
159
185
|
}
|
|
160
186
|
|
|
187
|
+
/* Admonition callouts */
|
|
188
|
+
.karnel-note {
|
|
189
|
+
border: 1px solid var(--karnel-accent-2, #0078d4);
|
|
190
|
+
border-left-width: 4px;
|
|
191
|
+
border-radius: 8px;
|
|
192
|
+
padding: 0.75rem 1rem;
|
|
193
|
+
margin: 1.25rem 0;
|
|
194
|
+
background: rgba(0, 120, 212, 0.06);
|
|
195
|
+
}
|
|
196
|
+
|
|
161
197
|
/* Section spacing */
|
|
162
198
|
.main-content h2 {
|
|
163
199
|
margin-top: 2.5rem;
|
|
@@ -203,3 +239,10 @@ body {
|
|
|
203
239
|
color: #e6edf3;
|
|
204
240
|
}
|
|
205
241
|
}
|
|
242
|
+
|
|
243
|
+
/* Footer credit spacing */
|
|
244
|
+
.site-footer {
|
|
245
|
+
margin-top: 2.5rem;
|
|
246
|
+
border-top: 1px solid #e1e4e8;
|
|
247
|
+
padding-top: 1rem;
|
|
248
|
+
}
|
package/docs/doctor/index.md
CHANGED
|
@@ -239,5 +239,5 @@ Regression coverage lives in `tests/smoke.sh` and verifies nested module syntax,
|
|
|
239
239
|
the 76-entry registry, pipeline-safe parsing, mode counts, hidden GitHub workflow
|
|
240
240
|
detection, scoped NestJS detection, TypeScript, and Python manifests.
|
|
241
241
|
|
|
242
|
-
See [the 2026-07-16 changelog](../CHANGELOG
|
|
242
|
+
See [the 2026-07-16 changelog](../CHANGELOG/) for the complete audit and hardening
|
|
243
243
|
record.
|
package/docs/index.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Karnel Termux Documentation
|
|
2
2
|
|
|
3
|
+
<img class="karnel-logo" src="{{ '/assets/images/karnel-logo.png' | relative_url }}" alt="Karnel Termux">
|
|
4
|
+
|
|
3
5
|
> Modular Dev Environment for Termux — install languages, databases, AI agents, editors, and more with one command
|
|
4
6
|
|
|
5
7
|
Navigate the docs:
|
|
@@ -9,19 +11,20 @@ Navigate the docs:
|
|
|
9
11
|
<li><a href="./doctor/">Doctor System</a></li>
|
|
10
12
|
<li><a href="./troubleshooting/">Troubleshooting</a></li>
|
|
11
13
|
<li><a href="./ARCHITECTURE/">Architecture</a></li>
|
|
12
|
-
<li><a href="./CHANGELOG
|
|
14
|
+
<li><a href="./CHANGELOG/">Changelog</a></li>
|
|
13
15
|
<li><a href="https://israelmarques1024-dotcom.github.io/karnel-termux/">Live Site</a></li>
|
|
14
16
|
</ul>
|
|
15
17
|
|
|
16
18
|
## What is Karnel?
|
|
17
19
|
|
|
18
20
|
Karnel Termux transforms your Android device into a complete development workstation.
|
|
19
|
-
With a single CLI (`karnel`), you can install and manage 45 AI tools,
|
|
20
|
-
5 databases, 22 dev tools, 11 npm packages, 10 shell plugins,
|
|
21
|
-
4 deploy CLIs, 6 games, 2 network tools, 12 utility tools,
|
|
22
|
-
|
|
23
|
-
from modules: `ai`, `auto`, `db`,
|
|
24
|
-
`
|
|
21
|
+
With a single CLI (`karnel`), you can install and manage 45 AI tools, 3 editors,
|
|
22
|
+
8 languages, 5 databases, 22 dev tools, 11 npm packages, 10 shell plugins,
|
|
23
|
+
4 UI components, 4 deploy CLIs, 6 games, 2 network tools, 12 utility tools,
|
|
24
|
+
30 security tools, 1 automation tool, responsible OSINT, a second brain,
|
|
25
|
+
voice commands, and reviewed plugins — all from modules: `ai`, `auto`, `db`,
|
|
26
|
+
`deploy`, `dev`, `editor`, `games`, `lang`, `network`, `npm`, `osint`,
|
|
27
|
+
`security`, `shell`, `ui`, `utils`, `voice`, `plugin`.
|
|
25
28
|
|
|
26
29
|
## Sections
|
|
27
30
|
|
|
@@ -35,7 +38,7 @@ from modules: `ai`, `auto`, `db`, `deploy`, `dev`, `editor`, `games`, `lang`,
|
|
|
35
38
|
<a href="https://github.com/israelmarques1024-dotcom/karnel-termux/blob/main/karnel/tools/utils/fconv/README.md">Utility Scripts — fconv, notes, treex, and more</a>
|
|
36
39
|
<a href="./cli/#supabase--remote-project-helpers">Supabase CLI — remote-project helpers</a>
|
|
37
40
|
<a href="./ARCHITECTURE/">Architecture — project structure and module system</a>
|
|
38
|
-
<a href="./CHANGELOG
|
|
41
|
+
<a href="./CHANGELOG/">Changelog — version history and fixes</a>
|
|
39
42
|
</div>
|
|
40
43
|
|
|
41
44
|
## Credits
|
|
@@ -43,12 +43,21 @@ npm publish does not block the release. The workflow needs `contents: write`
|
|
|
43
43
|
permission (already declared). If `gh release create` fails, confirm the tag
|
|
44
44
|
exists and the token used by GitHub Actions has write access.
|
|
45
45
|
|
|
46
|
-
##
|
|
47
|
-
|
|
48
|
-
The
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
46
|
+
## Documentation site (GitHub Pages)
|
|
47
|
+
|
|
48
|
+
The documentation site is a Jekyll (cayman theme) site built from the `docs/`
|
|
49
|
+
directory and published to GitHub Pages by the `Publish Docs` workflow
|
|
50
|
+
(`.github/workflows/docs.yml`) on every push to `main` that changes `docs/**`.
|
|
51
|
+
The workflow runs `actions/jekyll-build-pages` (from `docs/`, output to
|
|
52
|
+
`docs/_site`), uploads the result as a Pages artifact, and deploys it to the
|
|
53
|
+
`github-pages` environment. The live site is
|
|
54
|
+
`https://israelmarques1024-dotcom.github.io/karnel-termux/`.
|
|
55
|
+
|
|
56
|
+
If a build fails, open the Actions run for the failing job and read the Jekyll
|
|
57
|
+
output: the usual causes are invalid `_config.yml` YAML, an SCSS compile error
|
|
58
|
+
in `assets/css/style.scss`, or a malformed `{{ }}` Liquid tag. After a
|
|
59
|
+
successful deploy, the published site is visible under the repository's
|
|
60
|
+
**Settings → Pages**.
|
|
52
61
|
|
|
53
62
|
## Reporting a bug
|
|
54
63
|
|
package/karnel/RELEASE_COMMIT
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
f3ce43f67dd4d213f0af574d1673e8dbd6c910b9
|
|
@@ -86,8 +86,15 @@ show_main() {
|
|
|
86
86
|
return
|
|
87
87
|
fi
|
|
88
88
|
|
|
89
|
-
local readme_path
|
|
90
|
-
|
|
89
|
+
local readme_path raw
|
|
90
|
+
raw="$KARNEL_PATH/tools/$module/$tool/README.md"
|
|
91
|
+
if command -v realpath &>/dev/null; then
|
|
92
|
+
readme_path="$(realpath "$raw" 2>/dev/null)"
|
|
93
|
+
elif command -v readlink &>/dev/null; then
|
|
94
|
+
readme_path="$(readlink -f "$raw" 2>/dev/null)"
|
|
95
|
+
else
|
|
96
|
+
readme_path="$raw"
|
|
97
|
+
fi
|
|
91
98
|
|
|
92
99
|
if [[ -z "$readme_path" ]]; then
|
|
93
100
|
log_error "No documentation found for $module/$tool"
|
|
@@ -347,12 +347,22 @@ _update_try_git() {
|
|
|
347
347
|
return 1
|
|
348
348
|
}
|
|
349
349
|
|
|
350
|
+
_npm_installed_version() {
|
|
351
|
+
local v pr
|
|
352
|
+
v=$(npm list -g karnel-termux 2>/dev/null | grep -oP 'karnel-termux@\K[0-9.]+')
|
|
353
|
+
if [[ -z "$v" ]]; then
|
|
354
|
+
pr="$(npm root -g 2>/dev/null)/karnel-termux/package.json"
|
|
355
|
+
[[ -f "$pr" ]] && v=$(grep -oP '"version"\s*:\s*"\K[0-9.]+' "$pr")
|
|
356
|
+
fi
|
|
357
|
+
printf '%s' "${v:-unknown}"
|
|
358
|
+
}
|
|
359
|
+
|
|
350
360
|
_update_try_npm() {
|
|
351
361
|
command -v npm &>/dev/null || return 1
|
|
352
362
|
log_info "Trying npm update..."
|
|
353
363
|
if npm update -g karnel-termux --ignore-scripts 2>/dev/null; then
|
|
354
364
|
local new_ver
|
|
355
|
-
new_ver=$(
|
|
365
|
+
new_ver=$(_npm_installed_version)
|
|
356
366
|
log_success "Updated to v$new_ver via npm"
|
|
357
367
|
return 0
|
|
358
368
|
fi
|
|
@@ -365,7 +375,7 @@ _update_try_npm_install() {
|
|
|
365
375
|
log_info "Trying npm install..."
|
|
366
376
|
if npm install -g karnel-termux@latest --ignore-scripts 2>/dev/null; then
|
|
367
377
|
local new_ver
|
|
368
|
-
new_ver=$(
|
|
378
|
+
new_ver=$(_npm_installed_version)
|
|
369
379
|
log_success "Updated to v$new_ver via npm install"
|
|
370
380
|
return 0
|
|
371
381
|
fi
|
|
@@ -88,13 +88,16 @@ _antigravity_download_binary_impl() {
|
|
|
88
88
|
fi
|
|
89
89
|
|
|
90
90
|
if [[ "$manifest_sha256" =~ ^[0-9a-f]{64}$ ]]; then
|
|
91
|
-
verify_sha256 "$tarball" "$manifest_sha256" || return 1
|
|
91
|
+
verify_sha256 "$tarball" "$manifest_sha256" || { rm -f "$tarball"; return 1; }
|
|
92
92
|
else
|
|
93
|
-
|
|
93
|
+
log_error "Antigravity CLI manifest does not publish a SHA-256; refusing install"
|
|
94
|
+
rm -f "$tarball"
|
|
95
|
+
return 1
|
|
94
96
|
fi
|
|
95
97
|
|
|
96
|
-
if !
|
|
98
|
+
if ! safe_extract_tar "$tarball" "$AGY_DATA_DIR"; then
|
|
97
99
|
log_error "Failed to extract Antigravity CLI binary"
|
|
100
|
+
rm -f "$tarball"
|
|
98
101
|
return 1
|
|
99
102
|
fi
|
|
100
103
|
|
|
@@ -129,11 +129,18 @@ _download_cursor_binary_impl() {
|
|
|
129
129
|
|
|
130
130
|
# Cursor does not publish a verifiable checksum for this asset, so integrity
|
|
131
131
|
# relies on HTTPS transport only; upstream does not publish a verifiable checksum.
|
|
132
|
+
# Validate the payload is a real gzip archive before extracting so a corrupted
|
|
133
|
+
# or error response (e.g. an HTML error page) is rejected.
|
|
132
134
|
if ! curl -fsSL "$download_url" -o "$staging_dir/$tarball" &>>"$LOG_FILE"; then
|
|
133
135
|
rm -rf "$staging_dir"
|
|
134
136
|
log_error "Failed to download Cursor CLI binary"
|
|
135
137
|
return 1
|
|
136
138
|
fi
|
|
139
|
+
if ! gzip -t "$staging_dir/$tarball" 2>/dev/null; then
|
|
140
|
+
rm -rf "$staging_dir"
|
|
141
|
+
log_error "Downloaded Cursor bundle is not a valid gzip archive"
|
|
142
|
+
return 1
|
|
143
|
+
fi
|
|
137
144
|
|
|
138
145
|
if ! tar -zxf "$staging_dir/$tarball" --strip-components=1 -C "$staging_dir" &>>"$LOG_FILE"; then
|
|
139
146
|
rm -rf "$staging_dir"
|
|
@@ -125,13 +125,22 @@ _download_droid_binary_impl() {
|
|
|
125
125
|
|
|
126
126
|
local expected actual
|
|
127
127
|
expected="$(curl -fsSL "$sha_url" 2>/dev/null | awk '{print $1}')"
|
|
128
|
-
if [
|
|
128
|
+
if [[ ! "$expected" =~ ^[0-9a-f]{64}$ ]]; then
|
|
129
|
+
log_error "No published SHA-256 for Droid binary; refusing install"
|
|
130
|
+
rm -f "$DROID_DATA_DIR/droid"
|
|
131
|
+
return 1
|
|
132
|
+
fi
|
|
133
|
+
if command -v sha256sum &>/dev/null; then
|
|
129
134
|
actual="$(sha256sum "$DROID_DATA_DIR/droid" | awk '{print $1}')"
|
|
130
135
|
if [ "$actual" != "$expected" ]; then
|
|
131
136
|
log_error "Checksum verification failed"
|
|
132
137
|
rm -f "$DROID_DATA_DIR/droid"
|
|
133
138
|
return 1
|
|
134
139
|
fi
|
|
140
|
+
else
|
|
141
|
+
log_error "sha256sum unavailable; refusing install"
|
|
142
|
+
rm -f "$DROID_DATA_DIR/droid"
|
|
143
|
+
return 1
|
|
135
144
|
fi
|
|
136
145
|
|
|
137
146
|
chmod +x "$DROID_DATA_DIR/droid"
|
|
@@ -4,7 +4,7 @@ import "@/utils/log"
|
|
|
4
4
|
import "@/utils/install"
|
|
5
5
|
import "@/utils/version"
|
|
6
6
|
|
|
7
|
-
LOG_FILE="$KARNEL_CACHE/install_deploy.log"
|
|
7
|
+
LOG_FILE="${KARNEL_CACHE:-${XDG_CACHE_HOME:-$HOME/.cache}/karnel}/install_deploy.log"
|
|
8
8
|
RAILWAY_DATA_DIR="${KARNEL_DATA:-${XDG_DATA_HOME:-$HOME/.local/share}/karnel-data}/deploy/railway"
|
|
9
9
|
_RAILWAY_MARKER="$PREFIX/share/karnel-installers/railway"
|
|
10
10
|
_RAILWAY_DATA_MARKER="$RAILWAY_DATA_DIR/.karnel-managed"
|
|
@@ -113,14 +113,17 @@ _install_railway_manual_impl() {
|
|
|
113
113
|
|
|
114
114
|
local expected
|
|
115
115
|
expected=$(github_release_asset_sha256 railwayapp/cli "$latest_version" "railway-${version}-aarch64-linux.tar.gz") || expected=""
|
|
116
|
-
if [[ "$expected" =~ ^[0-9a-f]{64}$ ]]; then
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
}
|
|
116
|
+
if [[ ! "$expected" =~ ^[0-9a-f]{64}$ ]]; then
|
|
117
|
+
log_error "No published SHA-256 for Railway CLI; refusing install"
|
|
118
|
+
rm -f "$RAILWAY_DATA_DIR/railway.tar.gz"
|
|
119
|
+
return 1
|
|
121
120
|
fi
|
|
121
|
+
verify_sha256 "$RAILWAY_DATA_DIR/railway.tar.gz" "$expected" || {
|
|
122
|
+
rm -f "$RAILWAY_DATA_DIR/railway.tar.gz"
|
|
123
|
+
return 1
|
|
124
|
+
}
|
|
122
125
|
|
|
123
|
-
|
|
126
|
+
safe_extract_tar "$RAILWAY_DATA_DIR/railway.tar.gz" "$RAILWAY_DATA_DIR" || {
|
|
124
127
|
rm -f "$RAILWAY_DATA_DIR/railway.tar.gz"
|
|
125
128
|
return 1
|
|
126
129
|
}
|
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
import "@/utils/log"
|
|
4
4
|
import "@/utils/version"
|
|
5
|
+
import "@/utils/install"
|
|
6
|
+
|
|
7
|
+
if ! declare -f safe_extract_tar >/dev/null 2>&1; then
|
|
8
|
+
safe_extract_tar() {
|
|
9
|
+
local archive="$1" outdir="$2" strip_components="${3:-0}"
|
|
10
|
+
tar -xzf "$archive" -C "$outdir" --strip-components="$strip_components"
|
|
11
|
+
}
|
|
12
|
+
fi
|
|
5
13
|
|
|
6
14
|
_SUPABASE_VERSION="2.20.8"
|
|
7
15
|
_SUPABASE_RELEASE_URL="https://github.com/supabase/cli/releases/download/v${_SUPABASE_VERSION}"
|
|
@@ -43,7 +51,9 @@ install_supabase() (
|
|
|
43
51
|
local url="${_SUPABASE_RELEASE_URL}/${filename}"
|
|
44
52
|
local checksum_url="${_SUPABASE_RELEASE_URL}/supabase_${_SUPABASE_VERSION}_checksums.txt"
|
|
45
53
|
local tmp_dir staged_bin marker_staging="" old_bin="" old_marker=""
|
|
46
|
-
|
|
54
|
+
local _kdir="${KARNEL_CACHE:-${XDG_CACHE_HOME:-$HOME/.cache}/karnel}"
|
|
55
|
+
mkdir -p "$_kdir" 2>/dev/null || _kdir="${TMPDIR:-/tmp}"
|
|
56
|
+
tmp_dir=$(mktemp -d "$_kdir/supabase.XXXXXX") || return 1
|
|
47
57
|
staged_bin=""
|
|
48
58
|
trap 'rm -rf "$tmp_dir"; [[ -z "$staged_bin" ]] || rm -f "$staged_bin"; [[ -z "$marker_staging" ]] || rm -f "$marker_staging"' EXIT
|
|
49
59
|
|
|
@@ -79,8 +89,11 @@ install_supabase() (
|
|
|
79
89
|
fi
|
|
80
90
|
log_success "Checksum verified"
|
|
81
91
|
|
|
82
|
-
# Extract
|
|
83
|
-
|
|
92
|
+
# Extract (hardened: rejects symlink/absolute-path traversal)
|
|
93
|
+
safe_extract_tar "$tmp_dir/$filename" "$tmp_dir" || {
|
|
94
|
+
log_error "Extraction failed"
|
|
95
|
+
return 1
|
|
96
|
+
}
|
|
84
97
|
if [[ ! -f "$tmp_dir/supabase" ]]; then
|
|
85
98
|
log_error "Extraction failed"
|
|
86
99
|
return 1
|
|
@@ -143,16 +143,24 @@ _download_bun_binary_native_impl() {
|
|
|
143
143
|
}
|
|
144
144
|
if declare -F github_release_asset_sha256 >/dev/null; then
|
|
145
145
|
expected=$(github_release_asset_sha256 "$BUN_REPO" "bun-v$version" "$zip_name") || expected=""
|
|
146
|
-
if [[ "$expected" =~ ^[0-9a-f]{64}$ ]]; then
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
146
|
+
if [[ ! "$expected" =~ ^[0-9a-f]{64}$ ]]; then
|
|
147
|
+
rm -rf "$staging_dir"
|
|
148
|
+
log_error "No published SHA-256 for Bun native; refusing install"
|
|
149
|
+
return 1
|
|
150
|
+
fi
|
|
151
|
+
verify_sha256 "$staging_dir/$zip_name" "$expected" || { rm -rf "$staging_dir"; return 1; }
|
|
152
|
+
if ! safe_extract_zip "$staging_dir/$zip_name" "$staging_dir"; then
|
|
153
|
+
rm -rf "$staging_dir"
|
|
154
|
+
log_error "Failed to extract Bun binary"
|
|
155
|
+
return 1
|
|
156
|
+
fi
|
|
157
|
+
else
|
|
158
|
+
command -v log_warn >/dev/null 2>&1 && log_warn "Integrity helpers unavailable; skipping Bun integrity verification"
|
|
159
|
+
if ! unzip -o "$staging_dir/$zip_name" -d "$staging_dir" &>>"$LOG_FILE"; then
|
|
160
|
+
rm -rf "$staging_dir"
|
|
161
|
+
log_error "Failed to extract Bun binary"
|
|
162
|
+
return 1
|
|
150
163
|
fi
|
|
151
|
-
fi
|
|
152
|
-
if ! unzip -o "$staging_dir/$zip_name" -d "$staging_dir" &>>"$LOG_FILE"; then
|
|
153
|
-
rm -rf "$staging_dir"
|
|
154
|
-
log_error "Failed to extract Bun binary"
|
|
155
|
-
return 1
|
|
156
164
|
fi
|
|
157
165
|
rm -f "$staging_dir/$zip_name"
|
|
158
166
|
extracted="$staging_dir/bun-linux-aarch64/bun"
|
|
@@ -280,13 +288,14 @@ _install_bun_proot_impl() {
|
|
|
280
288
|
local expected
|
|
281
289
|
expected=$(github_release_asset_sha256 "$BUN_REPO" "bun-v$version" "bun-linux-aarch64.zip") || expected=""
|
|
282
290
|
if [[ ! "$expected" =~ ^[0-9a-f]{64}$ ]]; then
|
|
283
|
-
|
|
291
|
+
log_error "No published SHA-256 for Bun; refusing install"
|
|
292
|
+
return 1
|
|
284
293
|
fi
|
|
285
294
|
_bun_proot_ubuntu /bin/bash -c '
|
|
286
295
|
export HOME=/root TMPDIR=/tmp
|
|
287
296
|
cd /tmp &&
|
|
288
297
|
curl -fsSL "$1" -o bun.zip &&
|
|
289
|
-
|
|
298
|
+
[ "$(sha256sum bun.zip | awk "{print \$1}")" = "$2" ] &&
|
|
290
299
|
unzip -o bun.zip >/dev/null 2>&1 &&
|
|
291
300
|
mkdir -p /usr/local/bin &&
|
|
292
301
|
mv bun-linux-aarch64/bun /usr/local/bin/bun &&
|
|
@@ -392,13 +401,14 @@ _update_bun_proot() {
|
|
|
392
401
|
local expected
|
|
393
402
|
expected=$(github_release_asset_sha256 "$BUN_REPO" "bun-v$version" "bun-linux-aarch64.zip") || expected=""
|
|
394
403
|
if [[ ! "$expected" =~ ^[0-9a-f]{64}$ ]]; then
|
|
395
|
-
|
|
404
|
+
log_error "No published SHA-256 for Bun; refusing update"
|
|
405
|
+
return 1
|
|
396
406
|
fi
|
|
397
407
|
_bun_proot_ubuntu /bin/bash -c '
|
|
398
408
|
export HOME=/root TMPDIR=/tmp
|
|
399
409
|
cd /tmp &&
|
|
400
410
|
curl -fsSL "$1" -o bun.zip &&
|
|
401
|
-
|
|
411
|
+
[ "$(sha256sum bun.zip | awk "{print \$1}")" = "$2" ] &&
|
|
402
412
|
unzip -o bun.zip >/dev/null 2>&1 &&
|
|
403
413
|
mkdir -p /usr/local/bin &&
|
|
404
414
|
mv bun-linux-aarch64/bun /usr/local/bin/bun &&
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import "@/utils/log"
|
|
4
4
|
import "@/utils/version"
|
|
5
|
+
import "@/utils/install"
|
|
5
6
|
|
|
6
7
|
LOG_FILE="$KARNEL_CACHE/install_zork.log"
|
|
7
8
|
ZORK_DATA_DIR="${KARNEL_DATA:-${XDG_DATA_HOME:-$HOME/.local/share}/karnel-data}/zork"
|
|
@@ -62,7 +63,10 @@ _download_zork_data_impl() {
|
|
|
62
63
|
return 1
|
|
63
64
|
fi
|
|
64
65
|
|
|
65
|
-
if
|
|
66
|
+
if declare -F safe_extract_zip >/dev/null; then
|
|
67
|
+
safe_extract_zip "$zip_file" "$staging_dir" 2>>"$LOG_FILE" || {
|
|
68
|
+
rm -rf "$staging_dir"; log_error "Failed to extract Zork ${num}"; return 1; }
|
|
69
|
+
elif ! unzip -o "$zip_file" -d "$staging_dir" 2>>"$LOG_FILE"; then
|
|
66
70
|
rm -rf "$staging_dir"
|
|
67
71
|
log_error "Failed to extract Zork ${num}"
|
|
68
72
|
return 1
|
|
@@ -3,13 +3,20 @@
|
|
|
3
3
|
import "@/utils/log"
|
|
4
4
|
|
|
5
5
|
_uninstall_voice_tool() {
|
|
6
|
+
local karnel_data="${KARNEL_DATA:-${XDG_DATA_HOME:-$HOME/.local/share}/karnel-data}"
|
|
7
|
+
local marker="$karnel_data/voice/.termux-api-managed"
|
|
6
8
|
if command -v termux-api &>/dev/null; then
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
if [[ -f "$marker" ]]; then
|
|
10
|
+
log_info "Removing Termux:API (installed by Karnel)..."
|
|
11
|
+
if ! pkg uninstall -y termux-api &>>"$LOG_FILE"; then
|
|
12
|
+
log_error "Failed to remove Termux:API (see $LOG_FILE)"
|
|
13
|
+
return 1
|
|
14
|
+
fi
|
|
15
|
+
rm -f "$marker"
|
|
16
|
+
log_success "Termux:API removed"
|
|
17
|
+
else
|
|
18
|
+
log_info "Termux:API is present but was not installed by Karnel; leaving it in place"
|
|
11
19
|
fi
|
|
12
|
-
log_success "Termux:API removed"
|
|
13
20
|
else
|
|
14
21
|
log_info "Termux:API is not installed"
|
|
15
22
|
fi
|
|
@@ -481,14 +481,22 @@ _agent_exec_to_file() {
|
|
|
481
481
|
# ------------------------------------------------------------
|
|
482
482
|
_agent_cmd_is_write() {
|
|
483
483
|
local cmd="$1"
|
|
484
|
-
# file redirections
|
|
485
|
-
if printf '%s\n' "$cmd" | grep -qE '(^|[;&|[:space:]])(
|
|
484
|
+
# file redirections: >file >>file 2>file 1>>file &>file (excludes fd-to-fd like >&2 and comparisons >=)
|
|
485
|
+
if printf '%s\n' "$cmd" | grep -qE '(^|[;&|[:space:]])(>|>>|&>|[0-9]>|[0-9]>>)([^&=]|$)'; then
|
|
486
486
|
return 0
|
|
487
487
|
fi
|
|
488
488
|
# command substitution / backticks execute arbitrary code → write
|
|
489
489
|
if printf '%s\n' "$cmd" | grep -qE '\$\(|`'; then
|
|
490
490
|
return 0
|
|
491
491
|
fi
|
|
492
|
+
# in-place editors: sed -i / perl -i modify files in place
|
|
493
|
+
if printf '%s\n' "$cmd" | grep -qiE '(^|[;&|[:space:]])(sed|perl)[[:space:]][^|;&]*-[A-Za-z]*i[A-Za-z]*([[:space:]]|$)'; then
|
|
494
|
+
return 0
|
|
495
|
+
fi
|
|
496
|
+
# full-screen / line editors that can modify files
|
|
497
|
+
if printf '%s\n' "$cmd" | grep -qiE '(^|[;&|[:space:]])(vim|nvim|emacs|ed|ex|vi|nano)[[:space:]]'; then
|
|
498
|
+
return 0
|
|
499
|
+
fi
|
|
492
500
|
# blocklist of commands that write to disk / change state / spawn a shell
|
|
493
501
|
printf '%s\n' "$cmd" | grep -qiE '(^|[;&|[:space:]])(rm|rmdir|unlink|mv|cp|mkdir|touch|ln|chmod|chown|chgrp|install|truncate|tee|dd|mkfs|mount|umount|tar|gzip|gunzip|bzip2|xz|zstd|curl|wget|python3|python|node|npm|bun|yarn|pnpm|cargo|go |rustc|git (add|commit|push|pull|merge|rebase|reset|checkout|switch|restore|clean|stash|tag|clone|init|rm|mv)|pkg (install|uninstall|upgrade|reinstall|update)|apt( |-)|apt-get|dpkg|yum|dnf|pacman|brew|pip|pip3|uv|conda|mysql|psql|sqlite3|redis-cli|mongosh|kill|pkill|killall|service|systemctl|halt|reboot|shutdown|sudo|doas|su|pkexec|chroot|bash|sh|dash|zsh|ksh|eval|source|exec|screen|tmux|ssh|scp|sftp|rsync|telnet|nc|ncat|ftp|lftp|rsh|sshpass|socat|crontab|at |iptables|nft|ufw|docker|podman|kubectl|helm)[[:space:]]' && return 0
|
|
494
502
|
return 1
|
|
@@ -889,7 +897,9 @@ agent_exec_loading() {
|
|
|
889
897
|
local frames=('⠋' '⠙' '⠹' '⠸' '⠼' '⠴' '⠦' '⠧' '⠇' '⠏')
|
|
890
898
|
local delay=0.08
|
|
891
899
|
local tmpfile
|
|
892
|
-
|
|
900
|
+
local _ktmpdir="${KARNEL_CACHE:-${XDG_CACHE_HOME:-$HOME/.cache}/karnel}"
|
|
901
|
+
mkdir -p "$_ktmpdir" 2>/dev/null || _ktmpdir="${TMPDIR:-/tmp}"
|
|
902
|
+
tmpfile="$(mktemp "$_ktmpdir/karnel.XXXXXX" 2>/dev/null)" || tmpfile="$(mktemp 2>/dev/null)"
|
|
893
903
|
|
|
894
904
|
local stty_saved=""
|
|
895
905
|
if [[ -t 0 ]]; then
|
|
@@ -1357,12 +1367,17 @@ agent_context_footer() {
|
|
|
1357
1367
|
agent_history_trim_tokens() {
|
|
1358
1368
|
local history="$1" budget="${2:-0}"
|
|
1359
1369
|
(( budget <= 0 )) && { echo "$history"; return; }
|
|
1360
|
-
local toks n
|
|
1370
|
+
local toks n drop_idx is_summary
|
|
1361
1371
|
while :; do
|
|
1362
1372
|
toks=$(agent_context_tokens "$history")
|
|
1363
1373
|
(( toks <= budget )) && break
|
|
1364
1374
|
n=$(printf '%s' "$history" | jq 'length')
|
|
1365
1375
|
(( n <= 4 )) && break
|
|
1376
|
+
drop_idx=$(printf '%s' "$history" | jq -c 'if .[0].role == "system" then 1 else 0 end')
|
|
1377
|
+
is_summary=$(printf '%s' "$history" | jq -r --argjson i "$drop_idx" '.[$i].content // "" | contains("<compacted_summary>")')
|
|
1378
|
+
if [[ "$is_summary" == "true" ]]; then
|
|
1379
|
+
break
|
|
1380
|
+
fi
|
|
1366
1381
|
history=$(printf '%s' "$history" | jq -c 'if .[0].role == "system" then .[0:1] + .[2:] else .[1:] end')
|
|
1367
1382
|
done
|
|
1368
1383
|
echo "$history"
|
|
@@ -244,7 +244,20 @@ agent_chat_completion() {
|
|
|
244
244
|
# characters. Real UTF-8 text passes through untouched.
|
|
245
245
|
# ------------------------------------------------------------
|
|
246
246
|
_agent_unescape_unicode() {
|
|
247
|
-
|
|
247
|
+
if command -v perl >/dev/null 2>&1; then
|
|
248
|
+
perl -CS -pe 's/\\u([0-9a-fA-F]{4})/chr(hex($1))/ge'
|
|
249
|
+
elif command -v python3 >/dev/null 2>&1; then
|
|
250
|
+
python3 -c 'import sys,re;sys.stdout.write(re.sub(r"\\u([0-9a-fA-F]{4})",lambda m:chr(int(m.group(1),16)),sys.stdin.read()))'
|
|
251
|
+
else
|
|
252
|
+
awk '{
|
|
253
|
+
while (match($0, /\\u[0-9a-fA-F]{4}/)) {
|
|
254
|
+
h = substr($0, RSTART + 2, 4)
|
|
255
|
+
c = sprintf("%c", strtonum("0x" h))
|
|
256
|
+
$0 = substr($0, 1, RSTART - 1) c substr($0, RSTART + 6)
|
|
257
|
+
}
|
|
258
|
+
print
|
|
259
|
+
}'
|
|
260
|
+
fi
|
|
248
261
|
}
|
|
249
262
|
|
|
250
263
|
# ------------------------------------------------------------
|
package/karnel/utils/install.sh
CHANGED
|
@@ -211,9 +211,11 @@ github_release_asset_sha256() {
|
|
|
211
211
|
return 0
|
|
212
212
|
fi
|
|
213
213
|
# Fallback: a checksums.txt / SHA256SUMS asset listing every released file.
|
|
214
|
+
# Match the filename field exactly (it is the LAST field) so a substring or
|
|
215
|
+
# regex-metacharacter in $asset cannot select the wrong line's digest.
|
|
214
216
|
digest=$(curl -fsSL --connect-timeout 10 --max-time 30 \
|
|
215
217
|
"https://github.com/$repo/releases/download/$version/checksums.txt" 2>/dev/null \
|
|
216
|
-
| awk -v a="$asset" '
|
|
218
|
+
| awk -v a="$asset" '{ f=$NF; if (f == a) { sub(/^\*/, "", $1); print $1; exit } }' | tr -d '\r')
|
|
217
219
|
if [[ "$digest" =~ ^[0-9a-f]{64}$ ]]; then
|
|
218
220
|
echo "$digest"
|
|
219
221
|
return 0
|
|
@@ -248,20 +250,26 @@ extract_tarball() {
|
|
|
248
250
|
|
|
249
251
|
replace_managed_directory() {
|
|
250
252
|
local staging="$1" destination="$2" marker="$3"
|
|
251
|
-
local
|
|
253
|
+
local backup_dir backup_target=""
|
|
252
254
|
if [[ -e "$destination" && ! -f "$destination/$marker" ]]; then
|
|
253
255
|
log_error "Refusing to replace unowned installation: $destination"
|
|
254
256
|
return 1
|
|
255
257
|
fi
|
|
256
258
|
printf '%s\n' 'karnel-managed-v1' >"$staging/$marker" || return 1
|
|
257
|
-
|
|
258
|
-
|
|
259
|
+
backup_dir="$(mktemp -d "$(dirname "$destination")/.karnel-managed.XXXXXX")" || return 1
|
|
260
|
+
if [[ -e "$destination" ]]; then
|
|
261
|
+
backup_target="$backup_dir/$(basename "$destination")"
|
|
262
|
+
if ! mv "$destination" "$backup_target"; then
|
|
263
|
+
rm -rf "$backup_dir"
|
|
264
|
+
return 1
|
|
265
|
+
fi
|
|
259
266
|
fi
|
|
260
267
|
if ! mv "$staging" "$destination"; then
|
|
261
|
-
[[
|
|
268
|
+
[[ -n "$backup_target" ]] && mv "$backup_target" "$destination" 2>/dev/null
|
|
269
|
+
rm -rf "$backup_dir"
|
|
262
270
|
return 1
|
|
263
271
|
fi
|
|
264
|
-
rm -rf "$
|
|
272
|
+
rm -rf "$backup_dir"
|
|
265
273
|
}
|
|
266
274
|
|
|
267
275
|
record_managed_file() {
|
package/karnel/utils/log.sh
CHANGED
|
@@ -429,7 +429,9 @@ loading() {
|
|
|
429
429
|
local frames=('⠋' '⠙' '⠹' '⠸' '⠼' '⠴' '⠦' '⠧' '⠇' '⠏')
|
|
430
430
|
local delay=0.08
|
|
431
431
|
local tmpfile
|
|
432
|
-
|
|
432
|
+
local _ktmpdir="${KARNEL_CACHE:-${XDG_CACHE_HOME:-$HOME/.cache}/karnel}"
|
|
433
|
+
mkdir -p "$_ktmpdir" 2>/dev/null || _ktmpdir="${TMPDIR:-/tmp}"
|
|
434
|
+
tmpfile="$(mktemp "$_ktmpdir/karnel.XXXXXX" 2>/dev/null)" || tmpfile="$(mktemp 2>/dev/null)"
|
|
433
435
|
local cmd_pid=""
|
|
434
436
|
|
|
435
437
|
trap 'kill "$cmd_pid" 2>/dev/null; rm -f "$tmpfile"; return 1' INT TERM
|
package/karnel/utils/tools.sh
CHANGED
package/karnel/utils/version.sh
CHANGED
|
@@ -51,7 +51,9 @@ _spin_capture() {
|
|
|
51
51
|
shift
|
|
52
52
|
local frames=('⠋' '⠙' '⠹' '⠸' '⠼' '⠴' '⠦' '⠧' '⠇' '⠏')
|
|
53
53
|
local tmp
|
|
54
|
-
|
|
54
|
+
local _ktmpdir="${KARNEL_CACHE:-${XDG_CACHE_HOME:-$HOME/.cache}/karnel}"
|
|
55
|
+
mkdir -p "$_ktmpdir" 2>/dev/null || _ktmpdir="${TMPDIR:-/tmp}"
|
|
56
|
+
tmp=$(mktemp "$_ktmpdir/karnel.XXXXXX" 2>/dev/null) || tmp=$(mktemp 2>/dev/null)
|
|
55
57
|
local spinner_fd
|
|
56
58
|
if [ -c /dev/tty ] 2>/dev/null; then
|
|
57
59
|
spinner_fd=/dev/tty
|
package/package.json
CHANGED