karnel-termux 4.16.6 → 4.17.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/install.sh +5 -5
- package/karnel/RELEASE_COMMIT +1 -1
- package/karnel/bin/karnel +0 -0
- package/karnel/cli/commands/help.sh +0 -0
- package/karnel/cli/commands/update.sh +29 -4
- package/karnel/tools/ai/all.sh +0 -0
- package/karnel/tools/ai/antigravity-cli/install.sh +0 -0
- package/karnel/tools/ai/cactus/install.sh +1 -1
- package/karnel/tools/ai/claude-code/install.sh +0 -0
- package/karnel/tools/ai/cline/bin/cline +0 -0
- package/karnel/tools/ai/cline/install.sh +0 -0
- package/karnel/tools/ai/codebuff/bin/codebuff +0 -0
- package/karnel/tools/ai/codebuff/install.sh +0 -0
- package/karnel/tools/ai/codegraph/install.sh +0 -0
- package/karnel/tools/ai/codex/install.sh +0 -0
- package/karnel/tools/ai/command-code/install.sh +0 -0
- package/karnel/tools/ai/copilot-termux/install.sh +0 -0
- package/karnel/tools/ai/crush/install.sh +0 -0
- package/karnel/tools/ai/ctx7/install.sh +0 -0
- package/karnel/tools/ai/engram/install.sh +0 -0
- package/karnel/tools/ai/gemini-cli/install.sh +0 -0
- package/karnel/tools/ai/gentle-ai/install.sh +0 -0
- package/karnel/tools/ai/gga/install.sh +0 -0
- package/karnel/tools/ai/gga/termux.patch +0 -0
- package/karnel/tools/ai/hermes-agent/install.sh +0 -0
- package/karnel/tools/ai/kilocode-cli/install.sh +0 -0
- package/karnel/tools/ai/kimchi-code/install.sh +6 -0
- package/karnel/tools/ai/kimi-code/install.sh +0 -0
- package/karnel/tools/ai/kiro/install.sh +0 -0
- package/karnel/tools/ai/mimocode/install.sh +0 -0
- package/karnel/tools/ai/minimax-cli/install.sh +0 -0
- package/karnel/tools/ai/mistral-vibe/install.sh +0 -0
- package/karnel/tools/ai/odysseus/install.sh +0 -0
- package/karnel/tools/ai/ollama/install.sh +0 -0
- package/karnel/tools/ai/omni-route/install.sh +0 -0
- package/karnel/tools/ai/openclaude/install.sh +0 -0
- package/karnel/tools/ai/openclaw/install.sh +0 -0
- package/karnel/tools/ai/opencode/install.sh +13 -2
- package/karnel/tools/ai/openspec/install.sh +0 -0
- package/karnel/tools/ai/pi/install.sh +0 -0
- package/karnel/tools/ai/qwen-code/install.sh +0 -0
- package/karnel/tools/ai/walkie/install.sh +4 -2
- package/karnel/tools/auto/n8n/install.sh +0 -0
- package/karnel/tools/db/mariadb/install.sh +0 -0
- package/karnel/tools/db/mongodb/install.sh +0 -0
- package/karnel/tools/db/postgresql/install.sh +0 -0
- package/karnel/tools/db/redis/install.sh +0 -0
- package/karnel/tools/db/sqlite/install.sh +0 -0
- package/karnel/tools/deploy/netlify/install.sh +0 -0
- package/karnel/tools/deploy/railway/install.sh +0 -0
- package/karnel/tools/deploy/vercel/install.sh +0 -0
- package/karnel/tools/dev/bat/install.sh +0 -0
- package/karnel/tools/dev/bc/install.sh +0 -0
- package/karnel/tools/dev/cloudflared/install.sh +0 -0
- package/karnel/tools/dev/curl/install.sh +0 -0
- package/karnel/tools/dev/fzf/install.sh +0 -0
- package/karnel/tools/dev/gh/install.sh +0 -0
- package/karnel/tools/dev/html2text/install.sh +0 -0
- package/karnel/tools/dev/imagemagick/install.sh +0 -0
- package/karnel/tools/dev/jq/install.sh +0 -0
- package/karnel/tools/dev/lsd/install.sh +0 -0
- package/karnel/tools/dev/make/install.sh +0 -0
- package/karnel/tools/dev/ncurses/install.sh +0 -0
- package/karnel/tools/dev/openssh/install.sh +0 -0
- package/karnel/tools/dev/proot/install.sh +0 -0
- package/karnel/tools/dev/shfmt/install.sh +0 -0
- package/karnel/tools/dev/snyk/install.sh +0 -0
- package/karnel/tools/dev/tmate/install.sh +0 -0
- package/karnel/tools/dev/tmux/install.sh +0 -0
- package/karnel/tools/dev/translate/install.sh +0 -0
- package/karnel/tools/dev/tree/install.sh +0 -0
- package/karnel/tools/dev/udocker/install.sh +0 -0
- package/karnel/tools/dev/wget/install.sh +0 -0
- package/karnel/tools/editor/code-server/install.sh +0 -0
- package/karnel/tools/editor/neovim/install.sh +0 -0
- package/karnel/tools/editor/nvchad/install.sh +0 -0
- package/karnel/tools/games/arcade/install.sh +0 -0
- package/karnel/tools/games/buzz/install.sh +0 -0
- package/karnel/tools/games/ctfgod/install.sh +0 -0
- package/karnel/tools/games/detective/install.sh +0 -0
- package/karnel/tools/games/pet-friends/install.sh +0 -0
- package/karnel/tools/games/tamagotchi/install.sh +0 -0
- package/karnel/tools/lang/bun/bin/bunx +0 -0
- package/karnel/tools/lang/bun/install.sh +0 -0
- package/karnel/tools/lang/clang/install.sh +0 -0
- package/karnel/tools/lang/golang/install.sh +0 -0
- package/karnel/tools/lang/nodejs/install.sh +0 -0
- package/karnel/tools/lang/perl/install.sh +0 -0
- package/karnel/tools/lang/php/install.sh +0 -0
- package/karnel/tools/lang/python/install.sh +0 -0
- package/karnel/tools/lang/rust/install.sh +0 -0
- package/karnel/tools/network/dark/install.sh +0 -0
- package/karnel/tools/network/dedsec-network/install.sh +0 -0
- package/karnel/tools/npm/live-server/install.sh +0 -0
- package/karnel/tools/npm/localtunnel/install.sh +0 -0
- package/karnel/tools/npm/markserv/install.sh +0 -0
- package/karnel/tools/npm/ncu/install.sh +0 -0
- package/karnel/tools/npm/nestjs/install.sh +0 -0
- package/karnel/tools/npm/ngrok/install.sh +0 -0
- package/karnel/tools/npm/prettier/install.sh +0 -0
- package/karnel/tools/npm/psqlformat/install.sh +0 -0
- package/karnel/tools/npm/turbopack/install.sh +0 -0
- package/karnel/tools/npm/typescript/install.sh +0 -0
- package/karnel/tools/npm/vercel/install.sh +0 -0
- package/karnel/tools/osint/robin/common.sh +0 -0
- package/karnel/tools/osint/robin/install.sh +0 -0
- package/karnel/tools/security/aircrack-ng/install.sh +0 -0
- package/karnel/tools/security/amass/install.sh +0 -0
- package/karnel/tools/security/binwalk/install.sh +0 -0
- package/karnel/tools/security/burpsuite/install.sh +0 -0
- package/karnel/tools/security/dirb/install.sh +0 -0
- package/karnel/tools/security/dnsrecon/install.sh +0 -0
- package/karnel/tools/security/enum4linux/install.sh +0 -0
- package/karnel/tools/security/exiftool/install.sh +0 -0
- package/karnel/tools/security/ffuf/install.sh +0 -0
- package/karnel/tools/security/foremost/install.sh +0 -0
- package/karnel/tools/security/gobuster/install.sh +0 -0
- package/karnel/tools/security/hashcat/install.sh +0 -0
- package/karnel/tools/security/hydra/install.sh +0 -0
- package/karnel/tools/security/john/install.sh +0 -0
- package/karnel/tools/security/masscan/install.sh +0 -0
- package/karnel/tools/security/metasploit/install.sh +0 -0
- package/karnel/tools/security/netcat/install.sh +0 -0
- package/karnel/tools/security/nikto/install.sh +0 -0
- package/karnel/tools/security/nmap/install.sh +0 -0
- package/karnel/tools/security/smbclient/install.sh +0 -0
- package/karnel/tools/security/sqlmap/install.sh +0 -0
- package/karnel/tools/security/steghide/install.sh +0 -0
- package/karnel/tools/security/subfinder/install.sh +0 -0
- package/karnel/tools/security/tcpdump/install.sh +0 -0
- package/karnel/tools/security/theharvester/install.sh +0 -0
- package/karnel/tools/security/wafw00f/install.sh +0 -0
- package/karnel/tools/security/whatweb/install.sh +0 -0
- package/karnel/tools/security/whois/install.sh +0 -0
- package/karnel/tools/security/wpscan/install.sh +0 -0
- package/karnel/tools/security/zap/install.sh +0 -0
- package/karnel/tools/shell/better-npm/install.sh +0 -0
- package/karnel/tools/shell/fzf-tab/install.sh +0 -0
- package/karnel/tools/shell/history-substring/install.sh +0 -0
- package/karnel/tools/shell/powerlevel10k/install.sh +0 -0
- package/karnel/tools/shell/you-should-use/install.sh +0 -0
- package/karnel/tools/shell/zsh-autopair/install.sh +0 -0
- package/karnel/tools/shell/zsh-autosuggestions/install.sh +0 -0
- package/karnel/tools/shell/zsh-completions/install.sh +0 -0
- package/karnel/tools/shell/zsh-defer/install.sh +0 -0
- package/karnel/tools/shell/zsh-syntax-highlighting/install.sh +0 -0
- package/karnel/tools/ui/banner/install.sh +0 -0
- package/karnel/tools/ui/cursor/install.sh +0 -0
- package/karnel/tools/ui/extra-keys/install.sh +0 -0
- package/karnel/tools/ui/font/install.sh +0 -0
- package/karnel/tools/utils/applaunch/applaunch.py +0 -0
- package/karnel/tools/utils/applaunch/install.sh +0 -0
- package/karnel/tools/utils/fconv/fconv.py +0 -0
- package/karnel/tools/utils/fconv/install.sh +0 -0
- package/karnel/tools/utils/filecheck/filecheck.py +0 -0
- package/karnel/tools/utils/filecheck/install.sh +0 -0
- package/karnel/tools/utils/httptmux/install.sh +0 -0
- package/karnel/tools/utils/notes/install.sh +0 -0
- package/karnel/tools/utils/notes/notes.py +0 -0
- package/karnel/tools/utils/passman/install.sh +0 -0
- package/karnel/tools/utils/passman/passman.py +0 -0
- package/karnel/tools/utils/qrcode/install.sh +0 -0
- package/karnel/tools/utils/qrcode/qrcode.py +0 -0
- package/karnel/tools/utils/splash/install.sh +0 -0
- package/karnel/tools/utils/splash/splash.py +0 -0
- package/karnel/tools/utils/treex/install.sh +0 -0
- package/karnel/tools/utils/treex/treex.py +0 -0
- package/karnel/tools/utils/websites/install.sh +0 -0
- package/karnel/tools/utils/websites/websites.py +0 -0
- package/karnel/tools/utils/zork/install.sh +0 -0
- package/karnel/tools/utils/zork/zork.sh +0 -0
- package/karnel/utils/agent_actions.sh +46 -5
- package/karnel/utils/agent_llm.sh +64 -28
- package/karnel/utils/bootstrap.sh +4 -2
- package/package.json +1 -1
- package/scripts/check-package.js +15 -5
- package/scripts/npm-install.js +20 -1
package/install.sh
CHANGED
|
@@ -145,7 +145,7 @@ bootstrap_dependencies() {
|
|
|
145
145
|
|
|
146
146
|
if [[ $needed_tput -eq 1 ]]; then
|
|
147
147
|
echo -e " ${P_BORDER}→${P_NC} Installing ncurses-utils..."
|
|
148
|
-
pkg install -y ncurses-utils &>/dev/null
|
|
148
|
+
pkg install -y ncurses-utils &>/dev/null || true
|
|
149
149
|
echo -e " ${P_OK}✔${P_NC} ncurses installed"
|
|
150
150
|
echo
|
|
151
151
|
fi
|
|
@@ -153,7 +153,7 @@ bootstrap_dependencies() {
|
|
|
153
153
|
if [[ $needed_git -eq 1 ]]; then
|
|
154
154
|
log_info "Installing git..."
|
|
155
155
|
progress_bar 0 10
|
|
156
|
-
pkg install -y git &>/dev/null
|
|
156
|
+
pkg install -y git &>/dev/null || true
|
|
157
157
|
progress_bar 10 10
|
|
158
158
|
echo
|
|
159
159
|
log_ok "git installed"
|
|
@@ -162,7 +162,7 @@ bootstrap_dependencies() {
|
|
|
162
162
|
if [[ $needed_glow -eq 1 ]]; then
|
|
163
163
|
log_info "Installing glow..."
|
|
164
164
|
progress_bar 0 10
|
|
165
|
-
pkg install -y glow &>/dev/null
|
|
165
|
+
pkg install -y glow &>/dev/null || true
|
|
166
166
|
progress_bar 10 10
|
|
167
167
|
echo
|
|
168
168
|
log_ok "glow installed"
|
|
@@ -171,7 +171,7 @@ bootstrap_dependencies() {
|
|
|
171
171
|
if [[ $needed_gh -eq 1 ]]; then
|
|
172
172
|
log_info "Installing gh (GitHub CLI)..."
|
|
173
173
|
progress_bar 0 10
|
|
174
|
-
pkg install -y gh &>/dev/null
|
|
174
|
+
pkg install -y gh &>/dev/null || true
|
|
175
175
|
progress_bar 10 10
|
|
176
176
|
echo
|
|
177
177
|
log_ok "gh installed"
|
|
@@ -180,7 +180,7 @@ bootstrap_dependencies() {
|
|
|
180
180
|
if [[ $needed_rg -eq 1 ]]; then
|
|
181
181
|
log_info "Installing ripgrep..."
|
|
182
182
|
progress_bar 0 10
|
|
183
|
-
pkg install -y ripgrep &>/dev/null
|
|
183
|
+
pkg install -y ripgrep &>/dev/null || true
|
|
184
184
|
progress_bar 10 10
|
|
185
185
|
echo
|
|
186
186
|
log_ok "ripgrep installed"
|
package/karnel/RELEASE_COMMIT
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
57a1aa3e5401b6ba9b37965d8680bcb543b0fc00
|
package/karnel/bin/karnel
CHANGED
|
File without changes
|
|
File without changes
|
|
@@ -210,6 +210,18 @@ _update_cleanup() {
|
|
|
210
210
|
rm -f "$KARNEL_CACHE/new_version" "$KARNEL_CACHE/last_version_check"
|
|
211
211
|
}
|
|
212
212
|
|
|
213
|
+
_update_release_commit() {
|
|
214
|
+
local tag="$1" commit=""
|
|
215
|
+
if command -v git &>/dev/null 2>&1; then
|
|
216
|
+
commit=$(git ls-remote "https://github.com/israelmarques1024-dotcom/karnel-termux.git" "refs/tags/${tag}^{}" 2>/dev/null | awk '{print $1}')
|
|
217
|
+
fi
|
|
218
|
+
if [[ -z "$commit" ]] && command -v curl &>/dev/null 2>&1; then
|
|
219
|
+
commit=$(curl -fsS -m 10 "https://api.github.com/repos/israelmarques1024-dotcom/karnel-termux/commits/${tag}" 2>/dev/null \
|
|
220
|
+
| grep -m1 '"sha"' | sed -E 's/.*"sha":[[:space:]]*"([0-9a-f]{40})".*/\1/')
|
|
221
|
+
fi
|
|
222
|
+
[[ "$commit" =~ ^[0-9a-f]{40}$ ]] && echo "$commit"
|
|
223
|
+
}
|
|
224
|
+
|
|
213
225
|
_update_try_curl() {
|
|
214
226
|
command -v curl &>/dev/null || return 1
|
|
215
227
|
|
|
@@ -252,10 +264,23 @@ _update_try_curl() {
|
|
|
252
264
|
|
|
253
265
|
rm -f "$meta" "$sumfile"
|
|
254
266
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
267
|
+
# Resolve the release's target commit so the installer's "immutable
|
|
268
|
+
# commit SHA" requirement is satisfied even when the downloaded asset
|
|
269
|
+
# does not have RELEASE_COMMIT baked in.
|
|
270
|
+
local commit
|
|
271
|
+
commit=$(_update_release_commit "$tag")
|
|
272
|
+
if [[ -n "$commit" ]]; then
|
|
273
|
+
if bash "$installer" --ref "$tag" --commit "$commit"; then
|
|
274
|
+
rm -f "$installer"
|
|
275
|
+
log_success "Karnel-Termux updated via curl ($tag)"
|
|
276
|
+
return 0
|
|
277
|
+
fi
|
|
278
|
+
else
|
|
279
|
+
if bash "$installer" --ref "$tag"; then
|
|
280
|
+
rm -f "$installer"
|
|
281
|
+
log_success "Karnel-Termux updated via curl ($tag)"
|
|
282
|
+
return 0
|
|
283
|
+
fi
|
|
259
284
|
fi
|
|
260
285
|
|
|
261
286
|
rm -f "$installer"
|
package/karnel/tools/ai/all.sh
CHANGED
|
File without changes
|
|
File without changes
|
|
@@ -191,7 +191,7 @@ _cactus_write_wrapper() {
|
|
|
191
191
|
# The server loads the engine via ctypes inside the python process, so the
|
|
192
192
|
# whole interpreter must run under qemu-aarch64; CACTUS_EMULATE=1 makes the
|
|
193
193
|
# CLI wrap native subprocess children (bin/run) with qemu as well.
|
|
194
|
-
exec proot-distro login --shared-tmp ubuntu -- env CACTUS_EMULATE=1 qemu-aarch64 -cpu max -L / $CACTUS_CONTAINER_DIR/venv/bin/python -m cactus "
|
|
194
|
+
exec proot-distro login --shared-tmp ubuntu -- env CACTUS_EMULATE=1${CACTUS_CLOUD_KEY:+ CACTUS_CLOUD_KEY="$CACTUS_CLOUD_KEY"}${CACTUS_BASE_URL:+ CACTUS_BASE_URL="$CACTUS_BASE_URL"} qemu-aarch64 -cpu max -L / $CACTUS_CONTAINER_DIR/venv/bin/python -m cactus "$@"
|
|
195
195
|
EOF
|
|
196
196
|
else
|
|
197
197
|
cat >"$temporary" <<EOF
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -394,7 +394,13 @@ uninstall_kimchi_code() {
|
|
|
394
394
|
"$HOME/.local/share/kimchi" \
|
|
395
395
|
"$HOME/.local/state/kimchi" \
|
|
396
396
|
"$HOME/.cache/kimchi"
|
|
397
|
+
# Also remove the binaries installed inside the ubuntu container for
|
|
398
|
+
# proot/glibc installs (they are not cleaned by _uninstall_kimchi_impl).
|
|
399
|
+
if [ -n "$(_kimchi_detect_ubuntu_root)" ] && _kimchi_wrapper_owned; then
|
|
400
|
+
_kimchi_proot_ubuntu /bin/bash -c 'rm -f /usr/local/bin/kimchi && rm -rf /usr/local/share/kimchi' &>>"$LOG_FILE"
|
|
401
|
+
fi
|
|
397
402
|
loading "Uninstalling Kimchi" _uninstall_kimchi_impl
|
|
403
|
+
rm -f "$PREFIX/bin/kimchi"
|
|
398
404
|
return $?
|
|
399
405
|
fi
|
|
400
406
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -96,6 +96,8 @@ _install_opencode_proot_impl() {
|
|
|
96
96
|
npm install -g opencode-ai@1.18.15
|
|
97
97
|
' &>>"$LOG_FILE"
|
|
98
98
|
|
|
99
|
+
proot_ubuntu /bin/bash -c 'touch /root/.opencode/.karnel-managed' &>>"$LOG_FILE"
|
|
100
|
+
|
|
99
101
|
local ubuntu_root
|
|
100
102
|
ubuntu_root="$(detect_ubuntu_root)"
|
|
101
103
|
if [ -z "$ubuntu_root" ]; then
|
|
@@ -153,7 +155,11 @@ uninstall_opencode() {
|
|
|
153
155
|
return 0
|
|
154
156
|
fi
|
|
155
157
|
|
|
156
|
-
proot_ubuntu
|
|
158
|
+
if proot_ubuntu test -f /root/.opencode/.karnel-managed &>>"$LOG_FILE"; then
|
|
159
|
+
proot_ubuntu /bin/bash -c 'rm -rf /root/.opencode' &>>"$LOG_FILE"
|
|
160
|
+
else
|
|
161
|
+
log_warn "OpenCode data in the Ubuntu container was not installed by Karnel — leaving it untouched"
|
|
162
|
+
fi
|
|
157
163
|
local ubuntu_root
|
|
158
164
|
ubuntu_root="$(detect_ubuntu_root)/root/.bashrc"
|
|
159
165
|
if [ -f "$ubuntu_root" ]; then
|
|
@@ -179,7 +185,12 @@ _do_update_opencode() {
|
|
|
179
185
|
return $?
|
|
180
186
|
fi
|
|
181
187
|
|
|
182
|
-
proot_ubuntu
|
|
188
|
+
if proot_ubuntu test -f /root/.opencode/.karnel-managed &>>"$LOG_FILE"; then
|
|
189
|
+
proot_ubuntu /bin/bash -c 'rm -rf /root/.opencode' &>>"$LOG_FILE"
|
|
190
|
+
else
|
|
191
|
+
log_warn "Existing OpenCode in the Ubuntu container was not installed by Karnel — aborting update to avoid data loss"
|
|
192
|
+
return 1
|
|
193
|
+
fi
|
|
183
194
|
proot_ubuntu /bin/bash -c '
|
|
184
195
|
export SHELL=/bin/bash
|
|
185
196
|
export TMPDIR=/tmp
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -646,8 +646,10 @@ update_walkie() {
|
|
|
646
646
|
return 0
|
|
647
647
|
fi
|
|
648
648
|
log_info "New walkie revision available: $remote_commit (installed: $installed_commit)"
|
|
649
|
-
local answer
|
|
650
|
-
|
|
649
|
+
local answer="y"
|
|
650
|
+
if [[ -z "$KARNEL_NONINTERACTIVE" && -t 0 ]]; then
|
|
651
|
+
read -r -p "Update walkie? [Y/n] " answer
|
|
652
|
+
fi
|
|
651
653
|
case "$answer" in
|
|
652
654
|
""|y|Y|yes|Yes|YES) _update_walkie ;;
|
|
653
655
|
*) log_info "Update skipped" ;;
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -378,12 +378,19 @@ agent_apply_files() {
|
|
|
378
378
|
fi
|
|
379
379
|
while IFS=$'\t' read -r n path lang; do
|
|
380
380
|
[[ -z "$n" ]] && continue
|
|
381
|
-
#
|
|
381
|
+
# Resolve the destination and CONFINE it to the workspace. Absolute
|
|
382
|
+
# paths and ".." escapes are rejected so the model cannot overwrite
|
|
383
|
+
# arbitrary files (e.g. shell rc files outside the project).
|
|
382
384
|
if [[ "$path" != /* ]]; then
|
|
383
385
|
target="$ws/$path"
|
|
384
386
|
else
|
|
385
387
|
target="$path"
|
|
386
388
|
fi
|
|
389
|
+
target_abs="$(realpath -m "$target" 2>/dev/null)"
|
|
390
|
+
if [[ "$target_abs" != "$ws"/* ]]; then
|
|
391
|
+
log_error "Refusing to write outside the workspace: $path"
|
|
392
|
+
continue
|
|
393
|
+
fi
|
|
387
394
|
# write-protected: the model must not create/edit a file the task
|
|
388
395
|
# only references (delete/move/run...). Skip it silently.
|
|
389
396
|
if (( ${#noread[@]} > 0 )); then
|
|
@@ -411,6 +418,21 @@ agent_apply_files() {
|
|
|
411
418
|
# during a read/explain task) — nothing to create or edit
|
|
412
419
|
continue
|
|
413
420
|
fi
|
|
421
|
+
# Overwriting an existing file requires approval (mirrors the
|
|
422
|
+
# command confirmation flow). New files inside the workspace are
|
|
423
|
+
# applied automatically.
|
|
424
|
+
if (( existed )) && [[ "$AGENT_CONFIRM_COMMANDS" == "1" && "$AGENT_YES" != "1" ]]; then
|
|
425
|
+
if [[ -t 0 ]]; then
|
|
426
|
+
local _wf
|
|
427
|
+
if ! agent_confirm "Overwrite $target?" _wf; then
|
|
428
|
+
log_warn "Skipped overwriting $target"
|
|
429
|
+
continue
|
|
430
|
+
fi
|
|
431
|
+
else
|
|
432
|
+
log_warn "No terminal — skipping overwrite: $target (use -y to auto-approve)"
|
|
433
|
+
continue
|
|
434
|
+
fi
|
|
435
|
+
fi
|
|
414
436
|
if cat "$d/action_file_$n" >"$target" 2>/dev/null; then
|
|
415
437
|
if (( shown == 0 )); then
|
|
416
438
|
shown=1
|
|
@@ -453,8 +475,12 @@ _agent_cmd_is_write() {
|
|
|
453
475
|
if printf '%s\n' "$cmd" | grep -qE '(^|[;&|[:space:]])(>|>>)[^=&]'; then
|
|
454
476
|
return 0
|
|
455
477
|
fi
|
|
456
|
-
#
|
|
457
|
-
printf '%s\n' "$cmd" | grep -
|
|
478
|
+
# command substitution / backticks execute arbitrary code → write
|
|
479
|
+
if printf '%s\n' "$cmd" | grep -qE '\$\(|`'; then
|
|
480
|
+
return 0
|
|
481
|
+
fi
|
|
482
|
+
# blocklist of commands that write to disk / change state / spawn a shell
|
|
483
|
+
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|kill|pkill|killall|service|systemctl|halt|reboot|shutdown|sudo|doas|su|pkexec|chroot|bash|sh|dash|zsh|ksh|eval|source|exec|screen|tmux)[[:space:]]' && return 0
|
|
458
484
|
return 1
|
|
459
485
|
}
|
|
460
486
|
|
|
@@ -1107,7 +1133,11 @@ agent_voice_capture() {
|
|
|
1107
1133
|
# left alone.
|
|
1108
1134
|
# ------------------------------------------------------------
|
|
1109
1135
|
AGENT_SERVER_LOG="$KARNEL_CACHE/karnel-agent.log"
|
|
1110
|
-
|
|
1136
|
+
# Per-session PID file so one `karnel agent` session does not tear down a
|
|
1137
|
+
# server started by a different session. AGENT_SERVER_OWNED tracks whether
|
|
1138
|
+
# THIS session launched the server (only then do we stop it on exit).
|
|
1139
|
+
AGENT_SERVER_PID_FILE="$KARNEL_CACHE/agent/server_$$.pid"
|
|
1140
|
+
AGENT_SERVER_OWNED="${AGENT_SERVER_OWNED:-0}"
|
|
1111
1141
|
|
|
1112
1142
|
agent_server_running() {
|
|
1113
1143
|
[[ -f "$AGENT_SERVER_PID_FILE" ]] || return 1
|
|
@@ -1168,11 +1198,19 @@ agent_server_ensure() {
|
|
|
1168
1198
|
list_item "Logs: ${D_CYAN}$AGENT_SERVER_LOG${D_NC}"
|
|
1169
1199
|
mkdir -p "$(dirname "$AGENT_SERVER_PID_FILE")"
|
|
1170
1200
|
rm -f "$AGENT_SERVER_PID_FILE"
|
|
1201
|
+
AGENT_SERVER_OWNED=1
|
|
1202
|
+
# Cloud handoff: if a Cactus Cloud key is present, drop --no-cloud-handoff
|
|
1203
|
+
# so the local server proxies inference to the cloud (fast on this device).
|
|
1204
|
+
# Keyless users keep the fully-local path.
|
|
1205
|
+
local server_cmd="$AGENT_SERVER_CMD"
|
|
1206
|
+
if [[ -n "${CACTUS_CLOUD_KEY:-}" ]]; then
|
|
1207
|
+
server_cmd="${server_cmd//--no-cloud-handoff/}"
|
|
1208
|
+
fi
|
|
1171
1209
|
# stdin from /dev/null so proot-distro (used by the cactus wrapper)
|
|
1172
1210
|
# never blocks waiting for the terminal. The background wrapper writes
|
|
1173
1211
|
# its OWN real PID (setsid may fork, so $! can point at a dead
|
|
1174
1212
|
# intermediate); `exec` keeps that PID for the actual server.
|
|
1175
|
-
setsid bash -c 'echo $$ > "$1"; shift; exec "$@"' _ "$AGENT_SERVER_PID_FILE" $
|
|
1213
|
+
setsid bash -c 'echo $$ > "$1"; shift; exec "$@"' _ "$AGENT_SERVER_PID_FILE" $server_cmd </dev/null >"$AGENT_SERVER_LOG" 2>&1 &
|
|
1176
1214
|
# wait for the wrapper to write its PID before we can track/stop it
|
|
1177
1215
|
local i
|
|
1178
1216
|
for ((i = 0; i < 5; i++)); do
|
|
@@ -1187,6 +1225,9 @@ agent_server_ensure() {
|
|
|
1187
1225
|
}
|
|
1188
1226
|
|
|
1189
1227
|
agent_server_stop() {
|
|
1228
|
+
# Only stop a server THIS session started; a server launched by
|
|
1229
|
+
# another session (or manually) is left untouched.
|
|
1230
|
+
[[ "$AGENT_SERVER_OWNED" == "1" ]] || return 0
|
|
1190
1231
|
[[ -f "$AGENT_SERVER_PID_FILE" ]] || return 0
|
|
1191
1232
|
local pid
|
|
1192
1233
|
pid=$(cat "$AGENT_SERVER_PID_FILE" 2>/dev/null)
|
|
@@ -20,7 +20,7 @@ AGENT_MAX_ITERATIONS="${AGENT_MAX_ITERATIONS:-12}"
|
|
|
20
20
|
AGENT_WORKSPACE="${AGENT_WORKSPACE:-$PWD}"
|
|
21
21
|
AGENT_CONFIRM_COMMANDS="${AGENT_CONFIRM_COMMANDS:-1}"
|
|
22
22
|
AGENT_CONTEXT_WINDOW="${AGENT_CONTEXT_WINDOW:-8192}"
|
|
23
|
-
AGENT_SERVER_CMD="${AGENT_SERVER_CMD:-cactus serve Cactus-Compute/gemma-4-
|
|
23
|
+
AGENT_SERVER_CMD="${AGENT_SERVER_CMD:-cactus serve Cactus-Compute/gemma-4-e2b-it-cq4 --host 127.0.0.1 --port 8000 --no-cloud-handoff}"
|
|
24
24
|
|
|
25
25
|
# ------------------------------------------------------------
|
|
26
26
|
# agent_config_load — read persisted config
|
|
@@ -36,14 +36,24 @@ agent_config_load() {
|
|
|
36
36
|
AGENT_TEMPERATURE="${AGENT_TEMPERATURE:-0.3}"
|
|
37
37
|
AGENT_MAX_TOKENS="${AGENT_MAX_TOKENS:-2048}"
|
|
38
38
|
AGENT_MAX_ITERATIONS="${AGENT_MAX_ITERATIONS:-12}"
|
|
39
|
-
#
|
|
40
|
-
#
|
|
41
|
-
#
|
|
42
|
-
|
|
43
|
-
|
|
39
|
+
# Honour a saved workspace ONLY when the user explicitly configured
|
|
40
|
+
# one (the persisted file contains AGENT_WORKSPACE=) and it still
|
|
41
|
+
# exists. Otherwise the agent always starts in the current directory.
|
|
42
|
+
if grep -q '^AGENT_WORKSPACE=' "$AGENT_CONF_FILE" 2>/dev/null; then
|
|
43
|
+
[[ -d "$AGENT_WORKSPACE" ]] || AGENT_WORKSPACE="$PWD"
|
|
44
|
+
else
|
|
45
|
+
AGENT_WORKSPACE="$PWD"
|
|
46
|
+
fi
|
|
44
47
|
AGENT_CONFIRM_COMMANDS="${AGENT_CONFIRM_COMMANDS:-1}"
|
|
45
48
|
AGENT_CONTEXT_WINDOW="${AGENT_CONTEXT_WINDOW:-8192}"
|
|
46
|
-
AGENT_SERVER_CMD="${AGENT_SERVER_CMD:-cactus serve Cactus-Compute/gemma-4-
|
|
49
|
+
AGENT_SERVER_CMD="${AGENT_SERVER_CMD:-cactus serve Cactus-Compute/gemma-4-e2b-it-cq4 --host 127.0.0.1 --port 8000 --no-cloud-handoff}"
|
|
50
|
+
# Reject tampered config (defense-in-depth): numeric/boolean fields
|
|
51
|
+
# must match the expected shape or they are reset to safe defaults.
|
|
52
|
+
[[ "$AGENT_TEMPERATURE" =~ ^[0-9]+(\.[0-9]+)?$ ]] || AGENT_TEMPERATURE=0.3
|
|
53
|
+
[[ "$AGENT_MAX_TOKENS" =~ ^[0-9]+$ ]] || AGENT_MAX_TOKENS=2048
|
|
54
|
+
[[ "$AGENT_MAX_ITERATIONS" =~ ^[0-9]+$ ]] || AGENT_MAX_ITERATIONS=12
|
|
55
|
+
[[ "$AGENT_CONFIRM_COMMANDS" =~ ^[01]$ ]] || AGENT_CONFIRM_COMMANDS=1
|
|
56
|
+
[[ "$AGENT_CONTEXT_WINDOW" =~ ^[0-9]+$ ]] || AGENT_CONTEXT_WINDOW=8192
|
|
47
57
|
}
|
|
48
58
|
|
|
49
59
|
# ------------------------------------------------------------
|
|
@@ -51,17 +61,19 @@ AGENT_SERVER_CMD="${AGENT_SERVER_CMD:-cactus serve Cactus-Compute/gemma-4-E2B-it
|
|
|
51
61
|
# ------------------------------------------------------------
|
|
52
62
|
agent_config_save() {
|
|
53
63
|
mkdir -p "$AGENT_CONF_DIR"
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
64
|
+
{
|
|
65
|
+
printf 'AGENT_MODEL="%s"\n' "$AGENT_MODEL"
|
|
66
|
+
printf 'AGENT_ENDPOINT="%s"\n' "$AGENT_ENDPOINT"
|
|
67
|
+
printf 'AGENT_TEMPERATURE="%s"\n' "$AGENT_TEMPERATURE"
|
|
68
|
+
printf 'AGENT_MAX_TOKENS="%s"\n' "$AGENT_MAX_TOKENS"
|
|
69
|
+
printf 'AGENT_MAX_ITERATIONS="%s"\n' "$AGENT_MAX_ITERATIONS"
|
|
70
|
+
printf 'AGENT_CONFIRM_COMMANDS="%s"\n' "$AGENT_CONFIRM_COMMANDS"
|
|
71
|
+
printf 'AGENT_CONTEXT_WINDOW="%s"\n' "$AGENT_CONTEXT_WINDOW"
|
|
72
|
+
printf 'AGENT_SERVER_CMD="%s"\n' "$AGENT_SERVER_CMD"
|
|
73
|
+
if [[ "${KARNEL_AGENT_WS_SET:-0}" == "1" ]]; then
|
|
74
|
+
printf 'AGENT_WORKSPACE="%s"\n' "$AGENT_WORKSPACE"
|
|
75
|
+
fi
|
|
76
|
+
} >"$AGENT_CONF_FILE"
|
|
65
77
|
}
|
|
66
78
|
|
|
67
79
|
# ------------------------------------------------------------
|
|
@@ -73,13 +85,26 @@ agent_config_set() {
|
|
|
73
85
|
case "$key" in
|
|
74
86
|
model | --model | -m) AGENT_MODEL="$value" ;;
|
|
75
87
|
endpoint | --endpoint | -u) AGENT_ENDPOINT="$value" ;;
|
|
76
|
-
temp | temperature)
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
88
|
+
temp | temperature)
|
|
89
|
+
[[ "$value" =~ ^[0-9]+(\.[0-9]+)?$ ]] || { log_error "temperature must be a number (e.g. 0.3)"; return 1; }
|
|
90
|
+
AGENT_TEMPERATURE="$value" ;;
|
|
91
|
+
max_tokens | maxtokens)
|
|
92
|
+
[[ "$value" =~ ^[0-9]+$ ]] || { log_error "max_tokens must be a positive integer"; return 1; }
|
|
93
|
+
AGENT_MAX_TOKENS="$value" ;;
|
|
94
|
+
iterations | max_iterations)
|
|
95
|
+
[[ "$value" =~ ^[0-9]+$ ]] || { log_error "iterations must be a positive integer"; return 1; }
|
|
96
|
+
AGENT_MAX_ITERATIONS="$value" ;;
|
|
97
|
+
confirm_commands | confirm)
|
|
98
|
+
[[ "$value" =~ ^[01]$ ]] || { log_error "confirm_commands must be 0 or 1"; return 1; }
|
|
99
|
+
AGENT_CONFIRM_COMMANDS="$value" ;;
|
|
100
|
+
context_window | context)
|
|
101
|
+
[[ "$value" =~ ^[0-9]+$ ]] || { log_error "context_window must be a positive integer"; return 1; }
|
|
102
|
+
AGENT_CONTEXT_WINDOW="$value" ;;
|
|
81
103
|
server_command | server) AGENT_SERVER_CMD="$value" ;;
|
|
82
|
-
workspace)
|
|
104
|
+
workspace)
|
|
105
|
+
[[ -n "$value" ]] || { log_error "workspace requires a path"; return 1; }
|
|
106
|
+
AGENT_WORKSPACE="$value"
|
|
107
|
+
KARNEL_AGENT_WS_SET=1 ;;
|
|
83
108
|
*)
|
|
84
109
|
log_error "Unknown setting: $key"
|
|
85
110
|
echo
|
|
@@ -162,15 +187,26 @@ agent_models_list() {
|
|
|
162
187
|
agent_build_payload() {
|
|
163
188
|
local messages="$1"
|
|
164
189
|
local extra="${2:-}"
|
|
165
|
-
local payload
|
|
190
|
+
local payload temp mt
|
|
191
|
+
# Coerce numeric fields to valid JSON numbers; a bad/empty config
|
|
192
|
+
# value would otherwise make jq emit an empty payload and silently
|
|
193
|
+
# break every ask/run call.
|
|
194
|
+
temp="$AGENT_TEMPERATURE"
|
|
195
|
+
mt="$AGENT_MAX_TOKENS"
|
|
196
|
+
[[ "$temp" =~ ^[0-9]+(\.[0-9]+)?$ ]] || temp=0.3
|
|
197
|
+
[[ "$mt" =~ ^[0-9]+$ ]] || mt=2048
|
|
166
198
|
payload=$(jq -nc \
|
|
167
199
|
--arg model "$AGENT_MODEL" \
|
|
168
200
|
--argjson msgs "$messages" \
|
|
169
|
-
--argjson temp "$
|
|
170
|
-
--argjson mt "$
|
|
201
|
+
--argjson temp "$temp" \
|
|
202
|
+
--argjson mt "$mt" \
|
|
171
203
|
'{model:$model, messages:$msgs, temperature:$temp, max_tokens:$mt}')
|
|
204
|
+
if [[ -z "$payload" ]]; then
|
|
205
|
+
log_error "Failed to build request payload (check agent config values)"
|
|
206
|
+
return 1
|
|
207
|
+
fi
|
|
172
208
|
if [[ -n "$extra" ]]; then
|
|
173
|
-
payload=$(echo "$payload" | jq -c "$extra")
|
|
209
|
+
payload=$(echo "$payload" | jq -c "$extra") || { log_error "Failed to apply payload extra"; return 1; }
|
|
174
210
|
fi
|
|
175
211
|
echo "$payload"
|
|
176
212
|
}
|
|
@@ -14,8 +14,10 @@ import() {
|
|
|
14
14
|
local base="${KARNEL_PATH}"
|
|
15
15
|
local resolved="${1/@/$base}.sh"
|
|
16
16
|
local canonical base_canonical
|
|
17
|
-
canonical="$(realpath "$resolved" 2>/dev/null || readlink -f "$resolved" 2>/dev/null
|
|
18
|
-
|
|
17
|
+
canonical="$(realpath "$resolved" 2>/dev/null || readlink -f "$resolved" 2>/dev/null)"
|
|
18
|
+
[[ -z "$canonical" ]] && { echo "karnel: import error: cannot resolve path: $1" >&2; return 1; }
|
|
19
|
+
base_canonical="$(realpath "$base" 2>/dev/null || readlink -f "$base" 2>/dev/null)"
|
|
20
|
+
[[ -z "$base_canonical" ]] && { echo "karnel: import error: cannot resolve KARNEL_PATH" >&2; return 1; }
|
|
19
21
|
|
|
20
22
|
if [[ "$canonical" != "$base_canonical"/* ]]; then
|
|
21
23
|
echo "karnel: import error: path traversal denied: $1" >&2
|
package/package.json
CHANGED
package/scripts/check-package.js
CHANGED
|
@@ -28,11 +28,21 @@ cpSync(packageRoot, stagingRoot, {
|
|
|
28
28
|
return relative === "" || !relative.split(path.sep).some((part) => part === ".git" || part === "node_modules");
|
|
29
29
|
},
|
|
30
30
|
});
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
31
|
+
// Always bind the staged package to the actual repository HEAD. A stale
|
|
32
|
+
// committed karnel/RELEASE_COMMIT (left over from an earlier release) must
|
|
33
|
+
// never break release validation — the packed commit is what matters, and it
|
|
34
|
+
// must equal HEAD. When HEAD is unavailable (e.g. a non-git fixture) we fall
|
|
35
|
+
// back to a committed marker that is itself a valid SHA.
|
|
36
|
+
let committedCommit = "";
|
|
37
|
+
if (existsSync(releaseCommitPath)) {
|
|
38
|
+
committedCommit = readFileSync(releaseCommitPath, "utf8").trim();
|
|
39
|
+
}
|
|
40
|
+
if (/^[0-9a-f]{40}$/.test(repositoryHead)) {
|
|
41
|
+
writeFileSync(releaseCommitPath, `${repositoryHead}\n`, { mode: 0o600 });
|
|
42
|
+
} else if (/^[0-9a-f]{40}$/.test(committedCommit)) {
|
|
43
|
+
writeFileSync(releaseCommitPath, `${committedCommit}\n`, { mode: 0o600 });
|
|
44
|
+
} else {
|
|
45
|
+
throw new Error("Required package file is missing: karnel/RELEASE_COMMIT");
|
|
36
46
|
}
|
|
37
47
|
let output;
|
|
38
48
|
try {
|
package/scripts/npm-install.js
CHANGED
|
@@ -91,7 +91,26 @@ const main = () => {
|
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
try {
|
|
94
|
-
|
|
94
|
+
let releaseCommit = '';
|
|
95
|
+
if (fs.existsSync(releaseCommitPath)) {
|
|
96
|
+
releaseCommit = fs.readFileSync(releaseCommitPath, 'utf8').trim();
|
|
97
|
+
}
|
|
98
|
+
if (!/^[0-9a-f]{40}$/.test(releaseCommit)) {
|
|
99
|
+
// The published tarball may not contain a baked RELEASE_COMMIT (e.g. a
|
|
100
|
+
// plain `npm publish`). Derive it from git so the installer's immutable
|
|
101
|
+
// commit-SHA requirement is still satisfied.
|
|
102
|
+
try {
|
|
103
|
+
releaseCommit = execSync('git rev-parse HEAD', {
|
|
104
|
+
cwd: path.resolve(__dirname, '..'),
|
|
105
|
+
encoding: 'utf8'
|
|
106
|
+
}).trim();
|
|
107
|
+
} catch {
|
|
108
|
+
releaseCommit = '';
|
|
109
|
+
}
|
|
110
|
+
if (/^[0-9a-f]{40}$/.test(releaseCommit)) {
|
|
111
|
+
fs.writeFileSync(releaseCommitPath, releaseCommit + '\n');
|
|
112
|
+
}
|
|
113
|
+
}
|
|
95
114
|
if (!/^[0-9a-f]{40}$/.test(releaseCommit)) {
|
|
96
115
|
throw new Error('package contains an invalid RELEASE_COMMIT');
|
|
97
116
|
}
|