init: fork of kejilion/sh with certbot host-network fix
Some checks failed
Weekly Translation Workflow / translate (push) Has been cancelled

- Replace 'docker run -p 80:80' with '--network host' for certbot standalone HTTP-01 to avoid timeout in environments where Docker NAT port-publish breaks LE inbound traffic but host port 80 is reachable.
- Applies to kejilion.sh (all locale variants) and auto_cert_renewal.sh.
- README install command points to this Gitea mirror.
This commit is contained in:
2026-05-18 17:06:42 +08:00
commit 0784a11c19
73 changed files with 167210 additions and 0 deletions

160
.github/workflows/translate.yml vendored Normal file
View File

@@ -0,0 +1,160 @@
name: Weekly Translation Workflow
on:
schedule:
- cron: '0 2 * * 0'
workflow_dispatch:
permissions:
contents: write
jobs:
translate:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.9'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install deep-translator
- name: Create translation directories
run: |
mkdir -p en tw kr jp
- name: Create translation script
run: |
cat > translate.py << 'EOF'
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from deep_translator import GoogleTranslator
import re
import os
import sys
def is_chinese(text):
return bool(re.search(r'[\u4e00-\u9fff]', text))
def translate_text(text, target_lang):
if not text.strip() or not is_chinese(text):
return text
try:
# 过滤掉一些不该翻译的特殊符号
clean_text = text.strip()
result = GoogleTranslator(source='zh-CN', target=target_lang).translate(clean_text)
return result
except Exception as e:
print(f"\n[Error] {e}")
return text
def process_content_with_vars(content, target_lang):
"""
核心逻辑:保护 ${var} 和 $var翻译其中的中文部分
"""
# 匹配 ${var} 或 $var (字母数字下划线)
parts = re.split(r'(\$\{\w+\}|\$\w+)', content)
translated_parts = []
for p in parts:
if p.startswith('$'): # 变量部分,保持原样
translated_parts.append(p)
elif is_chinese(p): # 中文部分,翻译
translated_parts.append(translate_text(p, target_lang))
else: # 其他英文/符号,保持原样
translated_parts.append(p)
return "".join(translated_parts)
def universal_translator(line, target_lang):
"""
通用翻译引擎:识别行内所有引号内容并翻译
"""
# 1. 保护注释行
leading_space = re.match(r'^(\s*)', line).group(1)
stripped = line.strip()
if stripped.startswith('#'):
comment_content = stripped[1:].strip()
if is_chinese(comment_content):
return f"{leading_space}# {translate_text(comment_content, target_lang)}\n"
return line
# 2. 识别所有引号内的内容 (双引号或单引号)
# 使用正则匹配引号对,同时处理转义引号 \"
def replacer(match):
quote_type = match.group(1) # ' 或 "
content = match.group(2) # 引号内的文本内容
if is_chinese(content):
# 翻译内容,但保护里面的变量
translated = process_content_with_vars(content, target_lang)
return f"{quote_type}{translated}{quote_type}"
return match.group(0)
# 匹配 "content" 或 'content'
new_line = re.sub(r'([\'"])(.*?)(?<!\\)\1', replacer, line)
return new_line
def translate_file(input_file, output_file, target_lang):
print(f"Translating to {target_lang}...")
if not os.path.exists(input_file): return False
with open(input_file, 'r', encoding='utf-8') as f:
lines = f.readlines()
total = len(lines)
with open(output_file, 'w', encoding='utf-8') as f_out:
for i, line in enumerate(lines):
if (i + 1) % 10 == 0 or i + 1 == total:
print(f"\rProgress: {(i+1)/total*100:.1f}%", end='')
# 只要行内有中文,就尝试用通用引擎翻译
if is_chinese(line):
f_out.write(universal_translator(line, target_lang))
else:
f_out.write(line)
print(f"\n{target_lang} Success.")
return True
if __name__ == "__main__":
input_file = 'kejilion.sh'
langs = {'en': 'en', 'tw': 'zh-TW', 'kr': 'ko', 'jp': 'ja'}
for dir_name, lang_code in langs.items():
translate_file(input_file, f'{dir_name}/kejilion.sh', lang_code)
EOF
- name: Run translation
run: python translate.py
- name: Check for changes
id: check_changes
run: |
git add .
git diff --staged --quiet || echo "has_changes=true" >> $GITHUB_OUTPUT
- name: Commit and push changes
if: steps.check_changes.outputs.has_changes == 'true'
run: |
git config --local user.email "action@github.com"
git config --local user.name "GitHub Action"
git commit -m "🌐 Weekly translation update - $(date +'%Y-%m-%d %H:%M:%S')"
git push
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Create summary
if: always()
run: |
echo "## Translation Summary 📊" >> $GITHUB_STEP_SUMMARY
echo "| Language | Status |" >> $GITHUB_STEP_SUMMARY
echo "|----------|--------|" >> $GITHUB_STEP_SUMMARY
for dir in en tw kr jp; do
[ -f "$dir/kejilion.sh" ] && status="✅ Success" || status="❌ Failed"
echo "| $dir | $status |" >> $GITHUB_STEP_SUMMARY
done

67
CF-Under-Attack.sh Normal file
View File

@@ -0,0 +1,67 @@
#!/bin/bash
# 设置变量
EMAIL="AAAA"
API_KEY="BBBB"
ZONE_ID="CCCC"
LOAD_THRESHOLD=5.0 # 设置高负载阈值
TELEGRAM_BOT_TOKEN="输入TG机器人API"
CHAT_ID="输入TG用户ID"
# 获取当前系统负载
CURRENT_LOAD=$(uptime | awk -F'load average:' '{ print $2 }' | cut -d, -f1 | awk '{print $1}')
echo "当前系统负载: $CURRENT_LOAD"
send_tg_notification() {
local MESSAGE=$1
curl -s -X POST "https://api.telegram.org/bot$TELEGRAM_BOT_TOKEN/sendMessage" -d "chat_id=$CHAT_ID" -d "text=$MESSAGE"
}
# 获取当前的“Under Attack”模式状态
STATUS=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/settings/security_level" \
-H "X-Auth-Email: $EMAIL" \
-H "X-Auth-Key: $API_KEY" \
-H "Content-Type: application/json" | jq -r '.result.value')
echo "当前的Under Attack模式状态: $STATUS"
# 检查系统负载是否高于阈值
if (( $(echo "$CURRENT_LOAD > $LOAD_THRESHOLD" | bc -l) )); then
if [ "$STATUS" != "under_attack" ]; then
echo "系统负载高于阈值开启Under Attack模式"
# send_tg_notification "系统负载高于阈值开启Under Attack模式"
NEW_STATUS="under_attack"
else
echo "系统负载高但Under Attack模式已经开启"
exit 0
fi
else
if [ "$STATUS" == "under_attack" ]; then
echo "系统负载低于阈值关闭Under Attack模式"
# send_tg_notification "系统负载低于阈值关闭Under Attack模式"
NEW_STATUS="high"
else
echo "系统负载低Under Attack模式已经关闭"
exit 0
fi
fi
# 更新“Under Attack”模式状态
RESPONSE=$(curl -s -X PATCH "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/settings/security_level" \
-H "X-Auth-Email: $EMAIL" \
-H "X-Auth-Key: $API_KEY" \
-H "Content-Type: application/json" \
--data "{\"value\":\"$NEW_STATUS\"}")
if [[ $(echo $RESPONSE | jq -r '.success') == "true" ]]; then
echo "成功更新Under Attack模式状态为: $NEW_STATUS"
else
echo "更新Under Attack模式状态失败"
echo "响应: $RESPONSE"
fi

201
LICENSE Normal file
View File

@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

33
Limiting_Shut_down.sh Normal file
View File

@@ -0,0 +1,33 @@
#!/bin/bash
# 获取总的接收和发送流量
output=$(awk 'BEGIN { rx_total = 0; tx_total = 0 }
$1 ~ /^(eth|ens|enp|eno)[0-9]+/ { rx_total += $2; tx_total += $10 }
END {
printf("%.0f Bytes %.0f Bytes", rx_total, tx_total);
}' /proc/net/dev)
# 获取接收和发送的流量数据
rx=$(echo "$output" | awk '{print $1}')
tx=$(echo "$output" | awk '{print $3}')
# 显示当前流量使用情况
echo "当前接收流量: $rx"
echo "当前发送流量: $tx"
threshold_gb=110
# 将GB转换为字节
threshold=$((threshold_gb * 1024 * 1024 * 1024))
# 检查是否达到流量阈值
if (( $rx > $threshold || $tx > $threshold )); then
echo "流量达到${threshold},正在关闭服务器..."
# 在此处执行关闭服务器的命令,例如:
shutdown -h now
# 或者
# systemctl poweroff
else
echo "当前流量未达到${threshold},继续监视..."
fi

48
Limiting_Shut_down1.sh Normal file
View File

@@ -0,0 +1,48 @@
#!/bin/bash
# 获取总的接收流量(只统计公网网卡)
rx_output=$(awk 'BEGIN { rx_total = 0 }
$1 ~ /^(eth|ens|enp|eno)[0-9]+/ { rx_total += $2 }
END {
printf("%.0f Bytes", rx_total);
}' /proc/net/dev)
# 获取总的发送流量(只统计公网网卡)
tx_output=$(awk 'BEGIN { tx_total = 0 }
$1 ~ /^(eth|ens|enp|eno)[0-9]+/ { tx_total += $10 }
END {
printf("%.0f Bytes", tx_total);
}' /proc/net/dev)
# 获取接收流量数据
rx=$(echo "$rx_output" | awk '{print $1}')
# 获取发送流量数据
tx=$(echo "$tx_output" | awk '{print $1}')
# 显示当前流量使用情况
echo "当前接收流量: $rx Bytes"
echo "当前发送流量: $tx Bytes"
rx_threshold_gb=110
tx_threshold_gb=120
# 将GB转换为字节
rx_threshold=$((rx_threshold_gb * 1024 * 1024 * 1024))
tx_threshold=$((tx_threshold_gb * 1024 * 1024 * 1024))
# 检查是否达到接收流量阈值
if (( rx > rx_threshold )); then
echo "接收流量达到${rx_threshold_gb}GB (${rx_threshold} Bytes),正在关闭服务器..."
shutdown -h now
else
echo "当前接收流量未达到${rx_threshold_gb}GB (${rx_threshold} Bytes),继续监视..."
fi
# 检查是否达到发送流量阈值
if (( tx > tx_threshold )); then
echo "发送流量达到${tx_threshold_gb}GB (${tx_threshold} Bytes),正在关闭服务器..."
shutdown -h now
else
echo "当前发送流量未达到${tx_threshold_gb}GB (${tx_threshold} Bytes),继续监视..."
fi

32
PandoraNext/config.json Normal file
View File

@@ -0,0 +1,32 @@
{
"bind": "0.0.0.0:8181",
"tls": {
"enabled": false,
"cert_file": "",
"key_file": ""
},
"timeout": 600,
"proxy_url": "",
"license_id": "github",
"public_share": false,
"site_password": "",
"setup_password": "webgptpasswd",
"server_tokens": true,
"proxy_api_prefix": "",
"isolated_conv_title": "*",
"disable_signup": false,
"auto_conv_arkose": false,
"proxy_file_service": false,
"custom_doh_host": "",
"captcha": {
"provider": "",
"site_key": "",
"site_secret": "",
"site_login": false,
"setup_login": false,
"oai_username": false,
"oai_password": false,
"oai_signup": false
},
"whitelist": null
}

17
PandoraNext/tokens.json Normal file
View File

@@ -0,0 +1,17 @@
{
"test-1": {
"token": "access token / session token / refresh token",
"shared": true,
"show_user_info": false
},
"test-2": {
"token": "access token / session token / refresh token",
"shared": true,
"show_user_info": true,
"plus": true
},
"test2": {
"token": "access token / session token / refresh token / share token / username & password",
"password": "12345"
}
}

10
README.fa.md Normal file
View File

@@ -0,0 +1,10 @@
## 📜 معرفی (معرفی اسکریپت)
ابزار اسکریپت KejiLion یک جعبه‌ابزار همه‌کاره برای مدیریت، نظارت و تست سیستم‌های لینوکس است. این ابزار با هدف ارائه راه‌حل‌های ساده و کارآمد برای کاربران مبتدی و حرفه‌ای طراحی شده است. از جمله ویژگی‌های برجسته آن می‌توان به مدیریت پیشرفته Docker، استقرار خودکار LNMP (Linux + Nginx + MySQL + PHP)، بهینه‌سازی امنیتی سایت‌ها، ابزارهای تست شبکه و پشتیبان‌گیری/بازیابی کامل اشاره کرد. همچنین این اسکریپت با پشتیبانی از نصب پنل‌ها و ابزارهای محبوب، نگهداری سیستم را بسیار آسان می‌کند.
هدف ما تبدیل شدن به بهترین ابزار اسکریپت لینوکس با نصب یک‌کلیکی در سراسر اینترنت است تا پشتیبانی فنی سریع، ساده و حرفه‌ای برای کاربران فراهم شود.
## 🚀 نصب با یک کلیک (One-Click Installation) FA
```bash
bash <(curl -sL kejilion.sh) ir

65
README.ja.md Normal file
View File

@@ -0,0 +1,65 @@
# ワンクリック スクリプト ツール (kejilion.sh)
## 📜 はじめに
TechLion のシェル スクリプト ツールは、Linux の監視、テスト、管理用に設計されたオールインワンのスクリプト ツールボックスです。初心者でも経験豊富なユーザーでも、このツールは便利なソリューションを提供します。オリジナルの Docker 管理機能を統合しており、コンテナ化されたアプリケーションを簡単に管理できます。 LNMP ウェブサイト構築ソリューションは、ウェブサイトを迅速に構築するのに役立ち、サイトの最適化、防御、バックアップ、復元、移行などの機能が完全に装備されています。また、さまざまなシステム ツール パネルのインストールと使用を統合し、システムのメンテナンスを容易にします。私たちの目標は、ネットワーク全体で最高の Linux ワンクリック スクリプト ツールとなり、ユーザーに効率的で便利な技術サポートを提供することです。
***
## 🌐 サポートされているシステム
>Ubuntu
>Debian
>CentOS
>Alpine
>Kali
>Arch
>RedHat
>Fedora
>Alma
>Rocky
***
## 🚀 ワンクリックインストール
```bash
bash <(curl -sL kejilion.sh) jp
```
***
## 🖼️ 実際のスクリーンショット
<img src="https://kejilion.sh/img/screenshots/kejilionsh_jp.webp" alt="日本語版プレビュー" width="75%"/>
***
## 📦 コア機能
- **システム情報概要**: CPU、メモリ、ディスク、帯域幅などの動作状態をすばやく表示します。
- **ネットワーク テスト ツール**: 統合速度テスト、バックホール、遅延、パケット損失検出など。
- **Docker コンテナ管理**: 独自のコンテナ可視化 + 強化されたコンテナ制御コマンド
- **LNMPワンクリックデプロイメント**: Nginx + MySQL + PHPサイトを簡単に構築
- **ウェブサイトの防御と最適化**:CC対策、クローラー対策、ファイアウォールの自動構成、パフォーマンスの最適化
- **バックアップと移行**: サイトとデータベースのワンクリックバックアップ/復元/リモート移行
- **BBRアクセラレーション最適化**:カーネルアクセラレーション、ネットワーク輻輳制御インテリジェントスイッチング
- **アプリケーションマーケット統合**: 主流のツールとパネルが組み込まれており、一般的なサービスのワンクリックインストールをサポートします。
- **自動更新メカニズム**: スクリプトのバージョンを定期的にチェックして、最新かつ安定した状態を維持します
***
## 💖 私たちをサポートしてください
脚本は大丈夫だと思います。 USTD TRC20報酬
<strong style="color: navy;">TCP3PLGUTG9Z4z4tnHHSLbw5bgp8NXhTT3</strong>
***
## スターの歴史
[![星の歴史チャート](https://api.star-history.com/svg?repos=kejilion/sh&type=Date)](https://star-history.com/#kejilion/sh&Date)

68
README.kr.md Normal file
View File

@@ -0,0 +1,68 @@
# 원클릭 스크립트 도구(kejilion.sh)
## 📜 소개
TechLion의 Shell Script Tool은 Linux 모니터링, 테스트, 관리를 위해 설계된 올인원 스크립팅 툴박스입니다. 초보자든 숙련된 사용자든, 이 도구는 편리한 솔루션을 제공할 수 있습니다. 원래의 Docker 관리 기능을 통합하여 컨테이너화된 애플리케이션을 쉽게 관리할 수 있습니다. LNMP 웹사이트 구축 솔루션은 웹사이트를 빠르게 구축하는 데 도움이 되며, 사이트 최적화, 방어, 백업, 복구 및 마이그레이션 기능을 완벽하게 갖추고 있습니다. 다양한 시스템 도구 패널의 설치와 사용을 통합하여 시스템 유지관리를 더욱 쉽게 만들어줍니다. 저희의 목표는 전체 네트워크 상에서 최고의 Linux 원클릭 스크립팅 도구가 되어 사용자에게 효율적이고 편리한 기술 지원을 제공하는 것입니다.
***
## 🌐 지원 시스템
>Ubuntu
>Debian
>CentOS
>Alpine
>Kali
>Arch
>RedHat
>Fedora
>Alma
>Rocky
***
## 🚀 원클릭 설치
```bash
bash <(curl -sL kejilion.sh) kr
```
***
## 🖼️ 실제 화면 미리보기 (Preview)
<img src="https://kejilion.sh/img/screenshots/kejilionsh_kr.webp" alt="한국어 버전 미리보기" width="75%"/>
***
## 📦 핵심 기능
- **시스템 정보 개요**: CPU, 메모리, 디스크, 대역폭 등의 작동 상태를 빠르게 표시합니다.
- **네트워크 테스트 도구**: 통합 속도 테스트, 백홀, 지연, 패킷 손실 감지 등
- **Docker 컨테이너 관리**: 독점적인 컨테이너 시각화 + 향상된 컨테이너 제어 명령
- **LNMP 원클릭 배포**: Nginx + MySQL + PHP 사이트를 쉽게 구축
- **웹사이트 방어 및 최적화**: CC 차단, 크롤러 차단, 방화벽 자동 구성 및 성능 최적화
- **백업 및 마이그레이션**: 사이트 및 데이터베이스의 원클릭 백업/복원/원격 마이그레이션
- **BBR 가속 최적화**: 커널 가속, 네트워크 혼잡 제어 지능형 스위칭
- **애플리케이션 마켓 통합**: 기본 제공 도구 및 패널, 공통 서비스의 원클릭 설치 지원
- **자동 업데이트 메커니즘**: 스크립트 버전을 정기적으로 확인하여 최신 상태로 유지하고 안정성을 유지합니다.
***
## 💖 저희를 지원해주세요
대본은 괜찮은 것 같아요. USTD TRC20 보상
<strong style="color: navy;">TCP3PLGUTG9Z4z4tnHHSLbw5bgp8NXhTT3</strong>
***
## 스타 역사
[![별 역사 차트](https://api.star-history.com/svg?repos=kejilion/sh&type=Date)](https://star-history.com/#kejilion/sh&Date)

154
README.md Normal file
View File

@@ -0,0 +1,154 @@
<br><br><br>
<div align="center">
<img src="https://kejilion.sh/kejilionsh_logo.webp?v=2" alt="logo" width="650">
</div>
<div align="center" style="margin-top:-200px;">
<h1 style="font-size:150px;">KEJILION.SH - 科技lion一键脚本工具</h1>
</div>
<p align="center">
<a href="/README.md">
<img src="https://img.shields.io/badge/简体中文-2F4F4F?style=for-the-badge&logo=google-chrome&logoColor=white" alt="简体中文" style="margin: 5px;">
</a>
<a href="/README.tw.md">
<img src="https://img.shields.io/badge/繁體中文-2F4F4F?style=for-the-badge&logo=google-chrome&logoColor=white" alt="繁體中文" style="margin: 5px;">
</a>
<a href="/README.md">
<img src="https://img.shields.io/badge/English-2F4F4F?style=for-the-badge&logo=google-chrome&logoColor=white" alt="English" style="margin: 5px;">
</a>
<a href="/README.kr.md">
<img src="https://img.shields.io/badge/한국어-2F4F4F?style=for-the-badge&logo=google-chrome&logoColor=white" alt="한국어" style="margin: 5px;">
</a>
<a href="/README.ja.md">
<img src="https://img.shields.io/badge/日本語-2F4F4F?style=for-the-badge&logo=google-chrome&logoColor=white" alt="日本語" style="margin: 5px;">
</a>
<a href="/README.ru.md">
<img src="https://img.shields.io/badge/Русский-2F4F4F?style=for-the-badge&logo=google-chrome&logoColor=white" alt="Русский" style="margin: 5px;">
</a>
<a href="/README.fa.md">
<img src="https://img.shields.io/badge/فارسی-2F4F4F?style=for-the-badge&logo=google-chrome&logoColor=white" alt="فارسی" style="margin: 5px;">
</a>
</p>
<br><br><br>
## 📜 介绍 (Introduction)
科技Lion 的 Shell 脚本工具是一款全能脚本工具箱,专为 Linux 监控、测试和管理而设计。无论您是初学者还是经验丰富的用户,该工具都能为您提供便捷的解决方案。集成了独创的 Docker 管理功能让您轻松管理容器化应用LNMP建站解决方案能帮助您快速搭建网站站点优化、防御、备份还原迁移一应俱全并且整合了各类系统工具面板的安装及使用使系统维护变得更加简单。我们的目标是成为全网最优秀的 Linux 一键脚本工具,为用户提供高效、便捷的科技支持。
KejiLion's Shell script tool is an all-in-one script toolbox designed for Linux monitoring, testing, and management. Whether you are a beginner or an experienced user, this tool offers convenient solutions. It integrates unique Docker management features, enabling easy containerized application management. The LNMP site-building solution helps you quickly set up websites, covering optimization, defense, backup, restoration, and migration. It also includes the installation and use of various system tool panels, making system maintenance simpler. Our goal is to become the best Linux one-click script tool on the internet, providing users with efficient and convenient tech support.
<br><br>
## 🌐 支持系统 (Supported Systems)
<p align="left">
<a href="#">
<img src="https://img.shields.io/badge/Ubuntu-FFB6C1?style=for-the-badge&logo=ubuntu&logoColor=black" alt="Ubuntu" style="margin: 5px;">
</a>
<a href="#">
<img src="https://img.shields.io/badge/Debian-AFEEEE?style=for-the-badge&logo=debian&logoColor=black" alt="Debian" style="margin: 5px;">
</a>
<a href="#">
<img src="https://img.shields.io/badge/CentOS-98FB98?style=for-the-badge&logo=centos&logoColor=black" alt="CentOS" style="margin: 5px;">
</a>
<a href="#">
<img src="https://img.shields.io/badge/alpinelinux-ADD8E6?style=for-the-badge&logo=alpinelinux&logoColor=black" alt="alpinelinux" style="margin: 5px;">
</a>
<a href="#">
<img src="https://img.shields.io/badge/Kali-D3D3D3?style=for-the-badge&logo=kali-linux&logoColor=black" alt="Kali" style="margin: 5px;">
</a>
<a href="#">
<img src="https://img.shields.io/badge/Arch-FFFFE0?style=for-the-badge&logo=archlinux&logoColor=black" alt="Arch" style="margin: 5px;">
</a>
<a href="#">
<img src="https://img.shields.io/badge/RedHat-FFE4E1?style=for-the-badge&logo=redhat&logoColor=black" alt="RedHat" style="margin: 5px;">
</a>
<a href="#">
<img src="https://img.shields.io/badge/Fedora-FFD700?style=for-the-badge&logo=fedora&logoColor=black" alt="Fedora" style="margin: 5px;">
</a>
<a href="#">
<img src="https://img.shields.io/badge/almalinux-FFEFD5?style=for-the-badge&logo=almalinux&logoColor=black" alt="almalinux" style="margin: 5px;">
</a>
<a href="#">
<img src="https://img.shields.io/badge/Rocky-FFFACD?style=for-the-badge&logo=rocky-linux&logoColor=black" alt="Rocky" style="margin: 5px;">
</a>
</p>
<br><br>
> **🔱 Fork 说明**:本仓库 fork 自 [kejilion/sh](https://github.com/kejilion/sh),主要修复 certbot standalone 在部分服务器上因 Docker NAT 端口映射 (`-p 80:80`) 导致 Let's Encrypt HTTP-01 验证超时的问题,改用 `--network host` 直接占用宿主机 80 端口完成验证。其余逻辑与上游保持一致。
## 🚀 一键安装 (One-Click Installation) CN
```bash
bash <(curl -sL https://gitea.mygoband.com/openclaw/sh/raw/branch/main/kejilion.sh)
```
## 🚀 一键安装 (One-Click Installation) EN
```bash
bash <(curl -sL https://gitea.mygoband.com/openclaw/sh/raw/branch/main/kejilion.sh) en
```
<br><br>
## 🖼️ 效果图预览 (Preview)
<p>
<img src="https://kejilion.sh/img/screenshots/kejilionsh.webp" alt="中文版" width="48%" style="display:inline-block; margin-right:10px;"/>
<img src="https://kejilion.sh/img/screenshots/kejilionsh_en.webp" alt="English Version" width="48%" style="display:inline-block;"/>
</p>
<br><br>
## 📦 核心功能 (Core Features)
- **系统信息概览**:快速展示 CPU、内存、磁盘、带宽等运行状态
*System status overview: CPU, memory, disk, bandwidth and more*<br>
- **网络测试工具**:集成测速、回程、延迟、丢包检测等
*Network tools: speed test, route trace, latency, packet loss test*<br>
- **Docker 容器管理**:独家容器可视化 + 容器控制增强命令
*Advanced Docker management with enhanced commands and visualization*<br>
- **LNMP 一键部署**:轻松搭建 Nginx + MySQL + PHP 站点
*One-click LNMP stack deployment (Nginx, MySQL, PHP)*<br>
- **网站防御与优化**防CC、防爬虫自动配置防火墙与性能优化
*Site defense and optimization: anti-CC, anti-crawler, firewall and tuning*<br>
- **备份与迁移**:站点与数据库一键备份/恢复/远程迁移
*Backup & migration: one-click site/database backup and remote restore*<br>
- **BBR 加速优化**:内核加速、网络拥塞控制智能切换
*Network acceleration: BBR/tcp congestion control optimization*<br>
- **应用市场集成**:内置主流工具与面板,支持一键安装常用服务
*App Store integration: built-in panels and tools for one-click deployment*<br>
- **自动更新机制**:定时检测脚本版本,保持最新最稳定
*Auto-update engine: ensure you're always running the latest version*<br>
<br><br>
## 💖 支持我们 (Support Us)
觉得脚本还可以 USTD TRC20 打赏
Feel free to support us with USTD TRC20 donations.
<strong style="color: navy;">TCP3PLGUTG9Z4z4tnHHSLbw5bgp8NXhTT3</strong>
<br><br>
## ⭐ Star History
[![Star History Chart](https://api.star-history.com/svg?repos=kejilion/sh&type=Date)](https://star-history.com/#kejilion/sh&Date)

127
README.ru.md Normal file
View File

@@ -0,0 +1,127 @@
<br><br><br>
<div align="center">
<img src="https://kejilion.sh/img/screenshots/kejilionsh_logo.webp?v=2" alt="логотип" width="650">
</div>
<div align="center" style="margin-top:-200px;">
<h1 style="font-size:150px;">KEJILION.SH - инструмент для создания скриптов Technology Lion с одним ключом</h1>
</div>
<p align="center">
<a href="/README.md">
<img src="https://img.shields.io/badge/简体中文-2F4F4F?style=for-the-badge&logo=google-chrome&logoColor=white" alt="简体中文" style="margin: 5px;">
</a>
<a href="/README.tw.md">
<img src="https://img.shields.io/badge/繁體中文-2F4F4F?style=for-the-badge&logo=google-chrome&logoColor=white" alt="繁體中文" style="margin: 5px;">
</a>
<a href="/README.md">
<img src="https://img.shields.io/badge/English-2F4F4F?style=for-the-badge&logo=google-chrome&logoColor=white" alt="English" style="margin: 5px;">
</a>
<a href="/README.kr.md">
<img src="https://img.shields.io/badge/한국어-2F4F4F?style=for-the-badge&logo=google-chrome&logoColor=white" alt="한국어" style="margin: 5px;">
</a>
<a href="/README.ja.md">
<img src="https://img.shields.io/badge/日本語-2F4F4F?style=for-the-badge&logo=google-chrome&logoColor=white" alt="日本語" style="margin: 5px;">
</a>
<a href="/README.ru.md">
<img src="https://img.shields.io/badge/Русский-2F4F4F?style=for-the-badge&logo=google-chrome&logoColor=white" alt="Русский" style="margin: 5px;">
</a>
<a href="/README.fa.md">
<img src="https://img.shields.io/badge/فارسی-2F4F4F?style=for-the-badge&logo=google-chrome&logoColor=white" alt="فارسی" style="margin: 5px;">
</a>
</p>
<br><br><br>
## 📜 Введение)
Shell Script Tool от TechLion — это комплексный набор инструментов для написания скриптов, предназначенный для мониторинга, тестирования и управления Linux. Независимо от того, являетесь ли вы новичком или опытным пользователем, этот инструмент может предоставить вам удобное решение. Он интегрирует оригинальную функцию управления Docker, позволяя вам легко управлять контейнеризированными приложениями; Решение для создания веб-сайтов LNMP поможет вам быстро создать веб-сайт, оно полностью оснащено функциями оптимизации, защиты, резервного копирования, восстановления и миграции сайта; и интегрирует установку и использование различных панелей системных инструментов для упрощения обслуживания системы. Наша цель — стать лучшим инструментом для создания скриптов Linux в один клик во всей сети и предоставить пользователям эффективную и удобную технологическую поддержку.
<br><br>
## 🌐 Система поддержки
<p align="влево">
<a href="#">
<img src="https://img.shields.io/badge/Ubuntu-FFB6C1?style=for-the-badge&logo=ubuntu&logoColor=black" alt="Ubuntu" style="margin: 5px;">
</a>
<a href="#">
<img src="https://img.shields.io/badge/Debian-AFEEEE?style=for-the-badge&logo=debian&logoColor=black" alt="Debian" style="margin: 5px;">
</a>
<a href="#">
<img src="https://img.shields.io/badge/CentOS-98FB98?style=for-the-badge&logo=centos&logoColor=black" alt="CentOS" style="margin: 5px;">
</a>
<a href="#">
<img src="https://img.shields.io/badge/alpinelinux-ADD8E6?style=for-the-badge&logo=alpinelinux&logoColor=black" alt="alpinelinux" style="margin: 5px;">
</a>
<a href="#">
<img src="https://img.shields.io/badge/Kali-D3D3D3?style=for-the-badge&logo=kali-linux&logoColor=black" alt="Kali" style="margin: 5px;">
</a>
<a href="#">
<img src="https://img.shields.io/badge/Arch-FFFFE0?style=for-the-badge&logo=archlinux&logoColor=black" alt="Arch" style="margin: 5px;">
</a>
<a href="#">
<img src="https://img.shields.io/badge/RedHat-FFE4E1?style=for-the-badge&logo=redhat&logoColor=black" alt="RedHat" style="margin: 5px;">
</a>
<a href="#">
<img src="https://img.shields.io/badge/Fedora-FFD700?style=for-the-badge&logo=fedora&logoColor=black" alt="Fedora" style="margin: 5px;">
</a>
<a href="#">
<img src="https://img.shields.io/badge/almalinux-FFEFD5?style=for-the-badge&logo=almalinux&logoColor=black" alt="almalinux" style="margin: 5px;">
</a>
<a href="#">
<img src="https://img.shields.io/badge/Rocky-FFFACD?style=for-the-badge&logo=rocky-linux&logoColor=black" alt="Rocky" style="margin: 5px;">
</a>
</р>
<br><br>
## 🚀 Установка в один клик
```bash
bash <(curl -sL kejilion.sh) ru
```
<br><br>
## 🖼️ Предпросмотр интерфейса
<img src="https://kejilion.sh/img/screenshots/kejilionsh_ru.webp" alt="Предпросмотр русской версии" width="75%"/>
<br><br>
## 📦 Основные характеристики
- **Обзор информации о системе**: быстрое отображение рабочего состояния ЦП, памяти, диска, пропускной способности и т. д.
- **Инструменты тестирования сети**: интегрированный тест скорости, транзитная связь, задержка, обнаружение потери пакетов и т. д.
- **Управление контейнерами Docker**: эксклюзивная визуализация контейнеров + улучшенные команды управления контейнерами
- **Развертывание LNMP в один клик**: простая сборка сайта Nginx + MySQL + PHP
- **Защита и оптимизация веб-сайта**: защита от CC, защита от сканеров, автоматическая настройка брандмауэра и оптимизация производительности
- **Резервное копирование и миграция**: резервное копирование/восстановление/удаленная миграция сайтов и баз данных в один клик
- **Оптимизация ускорения BBR**: ускорение ядра, интеллектуальное переключение управления перегрузками сети
- **Интеграция с рынком приложений**: встроенные основные инструменты и панели, поддержка установки распространенных служб одним щелчком мыши
- **Механизм автоматического обновления**: регулярное определение версий скриптов для поддержания последних и наиболее стабильных версий
<br><br>
## 💖 Поддержите нас
Я думаю, что сценарий хорош. Награда USTD TRC20
<strong style="color: navy;">TCP3PLGUTG9Z4z4tnHHSLbw5bgp8NXhTT3</strong>
<br><br>
## ⭐ Звездные тренды
[![Таблица истории звезд](https://api.star-history.com/svg?repos=kejilion/sh&type=Date)](https://star-history.com/#kejilion/sh&Date)

67
README.tw.md Normal file
View File

@@ -0,0 +1,67 @@
# 一鍵腳本工具 (kejilion.sh)
## 📜 介紹 (Introduction)
科技Lion 的 Shell 腳本工具是一款全能腳本工具箱,專為 Linux 監控、測試和管理而設計。無論您是初學者還是經驗豐富的用戶,該工具都能為您提供便利的解決方案。整合了獨創的 Docker 管理功能讓您輕鬆管理容器化應用LNMP建站解決方案可協助您快速建置網站網站最佳化、防禦、備份還原遷移一應俱全並且整合了各類系統工具面板的安裝及使用使系統維護變得更加簡單。我們的目標是成為全網最優秀的 Linux 一鍵腳本工具,提供使用者高效、便利的科技支援。
***
## 🌐 支援系統
>Ubuntu
>Debian
>CentOS
>Alpine
>Kali
>Arch
>RedHat
>Fedora
>Alma
>Rocky
***
## 🚀 一鍵安裝
```bash
bash <(curl -sL kejilion.sh) tw
```
***
## 🖼️ 실제 화면 미리보기 (Preview)
<img src="https://kejilion.sh/img/screenshots/kejilionsh_tw.webp" alt="한국어 버전 미리보기" width="75%"/>
***
## 📦 核心功能
- **系統資訊概覽**:快速展示 CPU、記憶體、磁碟、頻寬等運作狀態
- **網路測試工具**:整合測速、回程、延遲、丟包偵測等
- **Docker 容器管理**:獨家容器視覺化 + 容器控制增強指令
- **LNMP 一鍵部署**:輕鬆建立 Nginx + MySQL + PHP 站點
- **網站防禦與最佳化**防CC、防爬蟲自動設定防火牆與效能最佳化
- **備份與遷移**:網站與資料庫一鍵備份/還原/遠端遷移
- **BBR 加速優化**:核心加速、網路擁塞控制智慧切換
- **應用市場整合**:內建主流工具與面板,支援一鍵安裝常用服務
- **自動更新機制**:定時偵測腳本版本,保持最新最穩定
***
## 💖 支持我們
覺得腳本還可以 USTD TRC20 打賞
<strong style="color: navy;">TCP3PLGUTG9Z4z4tnHHSLbw5bgp8NXhTT3</strong>
***
## Star History
[![Star History Chart](https://api.star-history.com/svg?repos=kejilion/sh&type=Date)](https://star-history.com/#kejilion/sh&Date)

29
TG-SSH-check-notify.sh Normal file
View File

@@ -0,0 +1,29 @@
#!/bin/bash
# 获取登录信息
country=$(curl -s ipinfo.io/$public_ip/country)
isp_info=$(curl -s ipinfo.io/org | sed -e 's/\"//g' | awk -F' ' '{print $2}')
ipv4_address=$(curl -s ipv4.ip.sb)
masked_ip=$(echo $ipv4_address | awk -F'.' '{print "*."$3"."$4}')
IP=$(echo $SSH_CONNECTION | awk '{print $1}')
TIME=$(date +"%Y年%m月%d日 %H:%M:%S")
# 查询IP地址对应的地区信息
#LOCATION=$(curl -s https://ipapi.co/$IP/json/ | jq -r '.city')
LOCATION=$(curl -s "http://opendata.baidu.com/api.php?query=$IP&co=&resource_id=6006&oe=utf8&format=json" | jq -r '.data[0].location')
# 获取当前用户名
USERNAME=$(whoami)
# 发送Telegram消息
MESSAGE=" 登录信息:
登录机器:${isp_info}-${country}-${masked_ip}
登录名:$USERNAME
登录IP$IP
登录时间:$TIME
登录地区:$LOCATION"
curl -s -X POST "https://api.telegram.org/bot$TELEGRAM_BOT_TOKEN/sendMessage" -d "chat_id=$CHAT_ID&text=$MESSAGE" > /dev/null 2>&1

102
TG-check-notify.sh Normal file
View File

@@ -0,0 +1,102 @@
#!/bin/bash
# 你需要配置Telegram Bot Token和Chat ID
TELEGRAM_BOT_TOKEN="输入TG的机器人API"
CHAT_ID="输入TG的接收通知的账号ID"
# 你可以修改监控阈值设置
CPU_THRESHOLD=70
MEMORY_THRESHOLD=70
DISK_THRESHOLD=70
NETWORK_THRESHOLD_GB=1000
# 获取设备信息的变量
country=$(curl -s ipinfo.io/$public_ip/country)
isp_info=$(curl -s ipinfo.io/org | sed -e 's/\"//g' | awk -F' ' '{print $2}')
ipv4_address=$(curl -s ipv4.ip.sb)
masked_ip=$(echo $ipv4_address | awk -F'.' '{print "*."$3"."$4}')
# 发送Telegram通知的函数
send_tg_notification() {
local MESSAGE=$1
curl -s -X POST "https://api.telegram.org/bot$TELEGRAM_BOT_TOKEN/sendMessage" -d "chat_id=$CHAT_ID" -d "text=$MESSAGE"
}
# 获取CPU使用率
get_cpu_usage() {
awk '{u=$2+$4; t=$2+$4+$5; if (NR==1){u1=u; t1=t;} else printf "%.0f\n", (($2+$4-u1) * 100 / (t-t1))}' \
<(grep 'cpu ' /proc/stat) <(sleep 1; grep 'cpu ' /proc/stat)
}
# 获取内存使用率
get_memory_usage() {
free | awk '/Mem/ {printf("%.0f"), $3/$2 * 100}'
}
# 获取硬盘使用率
get_disk_usage() {
df / | awk 'NR==2 {print $5}' | sed 's/%//'
}
# 获取总的接收流量(以 GB 为单位)
get_rx_bytes() {
awk 'BEGIN { rx_total = 0 }
# 匹配常见的公网网卡命名: eth*, ens*, enp*, eno*
$1 ~ /^(eth|ens|enp|eno)[0-9]+/ { rx_total += $2 }
END {
printf("%.2f", rx_total / (1024 * 1024 * 1024));
}' /proc/net/dev
}
# 获取总的发送流量(以 GB 为单位)
get_tx_bytes() {
awk 'BEGIN { tx_total = 0 }
# 匹配常见的公网网卡命名: eth*, ens*, enp*, eno*
$1 ~ /^(eth|ens|enp|eno)[0-9]+/ { tx_total += $10 }
END {
printf("%.2f", tx_total / (1024 * 1024 * 1024));
}' /proc/net/dev
}
# 检查并发送通知
check_and_notify() {
local USAGE=$1
local TYPE=$2
local THRESHOLD=$3
local CURRENT_VALUE=$4
if (( $(echo "$USAGE > $THRESHOLD" | bc -l) )); then
send_tg_notification "警告: ${isp_info}-${country}-${masked_ip}$TYPE 使用率已达到 $USAGE%,超过阈值 $THRESHOLD%。"
fi
}
# 主循环
while true; do
CPU_USAGE=$(get_cpu_usage)
MEMORY_USAGE=$(get_memory_usage)
DISK_USAGE=$(get_disk_usage)
RX_GB=$(get_rx_bytes)
TX_GB=$(get_tx_bytes)
check_and_notify $CPU_USAGE "CPU" $CPU_THRESHOLD $CPU_USAGE
check_and_notify $MEMORY_USAGE "内存" $MEMORY_THRESHOLD $MEMORY_USAGE
check_and_notify $DISK_USAGE "硬盘" $DISK_THRESHOLD $DISK_USAGE
# 检查入站流量是否超过阈值
if (( $(echo "$RX_GB > $NETWORK_THRESHOLD_GB" | bc -l) )); then
send_tg_notification "警告: ${isp_info}-${country}-${masked_ip} 的入站流量已达到 ${RX_GB}GB超过阈值 ${NETWORK_THRESHOLD_GB}GB。"
fi
# 检查出站流量是否超过阈值
if (( $(echo "$TX_GB > $NETWORK_THRESHOLD_GB" | bc -l) )); then
send_tg_notification "警告: ${isp_info}-${country}-${masked_ip} 的出站流量已达到 ${TX_GB}GB超过阈值 ${NETWORK_THRESHOLD_GB}GB。"
fi
# 休眠5分钟
sleep 300
done

4
apps/README.md Normal file
View File

@@ -0,0 +1,4 @@
# 🚀 开发者应用入驻指南 (kejilion.sh)
https://github.com/kejilion/apps

30
archive.key Normal file
View File

@@ -0,0 +1,30 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v2
mQENBFhxW04BCAC61HuxBVf1XJiQjXu/DSAtVcnuK38geDoDjcqFtHskFy32NgJG
X118EFNym6noF+oibaSftI9yjHthWvMnYZ/+DPwd7YZhbAjBvxMIQCsP6cFVxrgc
VV8g+uh4TCfbpalDBFoncRhQCgkmDN9Vd4kIWRh6BHJuzpKB/h2KxUHZVEKgWlK2
dR1xUtbrc+kp8gLwPbxTgC3tZ4x2uMMMlnbyCMSRa5oJ/AvoW4W1XphKL9ivsFHM
PSQkUBDvgv2RPw+0XBxPy8SYE0r0onx0ZIpjJRTODt3bSV6/0owwlpNogV9bT8HY
kl3+w3mTwax6S1akHZuJtLkZS0uUBz1BHt5bABEBAAG0IVhhbk1vZCBLZXJuZWwg
PGtlcm5lbEB4YW5tb2Qub3JnPokBNwQTAQgAIQUCWHFbTgIbAwULCQgHAgYVCAkK
CwIEFgIDAQIeAQIXgAAKCRCG99Ce5zTmIwTmB/9/S4rmwU6efDgEaBDwBDbOfLBA
P2+kDpabjG4K+V4NSvDqlPN49KrI7C21jHghAa2VuTPbSZVQ9ziUd5DjX9OuXov8
CYVG+rrlG1UadHS8SBpgw0gNylEvo9/U6u0hl8mrbVOlpzu+eE+e4cMTHax2y580
fC2xmnM8wKgyRFEyVc6ilWU+UNTAeUFlg0YfU3cV1Ut4DzVFfamtNYg0p7Q/9MSy
VgFpt5C2U5prk4wi++51OgrtaNhMrUhzYXLINWVF6IrXhQ+mkI/FWXUZ0oyVo55v
+dQzuds/gos90q+tKyE514pYAmwQSftSjf+RmHOMpPQyMZZKSywrz4vlfveDuQEN
BFhxW04BCACs5bXq73MDb2+AsvNL2XkkbnzmE4K3k0gejB9OxrO+puAZn3wWyYIk
b0Op8qVUh+/FIiW/uFfmdFD8BypC3YkCNfg6e74f5TT3qQciccpMGy62teo3jfhT
T8E1OL1i76ALq7eNbByJKiKLBrTUDM6BDIeRZBWXQMase4+aqUAP47Kd/ByPsmCh
/pzb6yPdDPKwkspELssdPXYI7enddjQsCPoBko0j8CTPgKqMTeCuKMXCtD2gtRBN
eoVj4cbjZoZvBh8oJktzbYA8FX8eKdxIXhSP9MoVOPSWhxIQdwzkzUPK+0vUV8jA
NBTnGOkrRJPOHGPJWFWnTUGrzvcwi7czABEBAAGJAR8EGAEIAAkFAlhxW04CGwwA
CgkQhvfQnuc05iMIswgAmzSpCHFGKdkFLdC673FidJcL8adKFTO5Mpyholc5N8vG
ROJbpso+DpssF14NKoBfBWqPRgHxYzHakxHiNf0R2+EEwXH3rblzpx3PXzB0OgNe
T9T0UStrGgc9nZ8nZVURHZZ2z5zakEWS+rB2TiSxz3YArR3wiTHQW49G09uZvfp6
5Mim2w+eUxbQ689eT0DlDI1d2eDP/j5lrv1elsg3kBE2Awzdvi8DdGUpMFrSsYJw
WS85uZrwbeAs/nPO62wNIvAbbRsWnDg3AV3vc02eRvy52tTBY1W/67N02M4AxgPd
ukDDFZMifwa03yTHD/a57O4dFOnzsEVojBnbzQ7W7w==
=HKlF
-----END PGP PUBLIC KEY BLOCK-----

66
auto_cert_renewal-1.sh Normal file
View File

@@ -0,0 +1,66 @@
# 定义证书存储目录
certs_directory="/etc/letsencrypt/live/"
days_before_expiry=5 # 设置在证书到期前几天触发续签
# 遍历所有证书文件
for cert_dir in $certs_directory*; do
# 获取域名
domain=$(basename "$cert_dir")
# 忽略 README 目录
if [ "$domain" = "README" ]; then
continue
fi
# 输出正在检查的证书信息
echo "检查证书过期日期: ${domain}"
# 获取fullchain.pem文件路径
cert_file="${cert_dir}/fullchain.pem"
# 获取证书过期日期
expiration_date=$(openssl x509 -enddate -noout -in "${cert_file}" | cut -d "=" -f 2-)
# 输出证书过期日期
echo "过期日期: ${expiration_date}"
# 将日期转换为时间戳
expiration_timestamp=$(date -d "${expiration_date}" +%s)
current_timestamp=$(date +%s)
# 计算距离过期还有几天
days_until_expiry=$(( ($expiration_timestamp - $current_timestamp) / 86400 ))
# 检查是否需要续签(在满足续签条件的情况下)
if [ $days_until_expiry -le $days_before_expiry ]; then
echo "证书将在${days_before_expiry}天内过期,正在进行自动续签。"
# 停止 Nginx
docker stop nginx
iptables -P INPUT ACCEPT
iptables -P FORWARD ACCEPT
iptables -P OUTPUT ACCEPT
iptables -F
ip6tables -P INPUT ACCEPT
ip6tables -P FORWARD ACCEPT
ip6tables -P OUTPUT ACCEPT
ip6tables -F
# 续签证书
certbot certonly --standalone -d $domain --email your@email.com --agree-tos --no-eff-email --force-renewal
# 启动 Nginx
docker start nginx
echo "证书已成功续签。"
else
# 若未满足续签条件,则输出证书仍然有效
echo "证书仍然有效,距离过期还有 ${days_until_expiry} 天。"
fi
# 输出分隔线
echo "--------------------------"
done

93
auto_cert_renewal.sh Normal file
View File

@@ -0,0 +1,93 @@
# 定义证书存储目录
certs_directory="/home/web/certs/"
days_before_expiry=15 # 设置在证书到期前几天触发续签
# 遍历所有证书文件
for cert_file in $certs_directory*_cert.pem; do
# 获取域名
yuming=$(basename "$cert_file" "_cert.pem")
# 输出正在检查的证书信息
echo "检查证书过期日期: ${yuming}"
# 获取证书过期日期
expiration_date=$(openssl x509 -enddate -noout -in "${certs_directory}${yuming}_cert.pem" | cut -d "=" -f 2-)
# 输出证书过期日期
echo "过期日期: ${expiration_date}"
# 将日期转换为时间戳
expiration_timestamp=$(date -d "${expiration_date}" +%s)
current_timestamp=$(date +%s)
# 计算距离过期还有几天
days_until_expiry=$(( ($expiration_timestamp - $current_timestamp) / 86400 ))
if [ $days_until_expiry -le $days_before_expiry ]; then
echo "证书将在${days_before_expiry}天内过期,正在进行自动续签。"
# 1. 检查目录是否存在
docker exec nginx [ -d /var/www/letsencrypt ] && DIR_OK=true || DIR_OK=false
# 2. 检查配置文件是否包含关键字
# 假设你的配置文件在容器内的 /etc/nginx/conf.d/ 目录下(这是 Nginx 容器的默认路径)
docker exec nginx grep -q "letsencrypt" /etc/nginx/conf.d/$yuming.conf && CONF_OK=true || CONF_OK=false
# 输出结果
echo "--- 自动化环境检测报告 ---"
if [ "$DIR_OK" = true ]; then echo "✅ 目录检测:/var/www/letsencrypt 存在"; else echo "❌ 目录检测:/var/www/letsencrypt 不存在"; fi
if [ "$CONF_OK" = true ]; then echo "✅ 配置检测:$yuming.conf 已包含续签规则"; else echo "❌ 配置检测:$yuming.conf 未发现 letsencrypt 字样"; fi
if [ "$DIR_OK" = true ] && [ "$CONF_OK" = true ]; then
docker run --rm -v /etc/letsencrypt/:/etc/letsencrypt certbot/certbot delete --cert-name "$yuming" -n
docker run --rm \
-v "/etc/letsencrypt:/etc/letsencrypt" \
-v "/home/web/letsencrypt:/var/www/letsencrypt" \
certbot/certbot certonly \
--webroot \
-w /var/www/letsencrypt \
-d "$yuming" \
--email your@email.com \
--agree-tos \
--no-eff-email \
--key-type ecdsa \
--force-renewal
mkdir -p /home/web/certs/
cp /etc/letsencrypt/live/$yuming/fullchain.pem /home/web/certs/${yuming}_cert.pem > /dev/null 2>&1
cp /etc/letsencrypt/live/$yuming/privkey.pem /home/web/certs/${yuming}_key.pem > /dev/null 2>&1
openssl rand -out /home/web/certs/ticket12.key 48
openssl rand -out /home/web/certs/ticket13.key 80
docker exec nginx nginx -t && docker exec nginx nginx -s reload
else
docker run --rm -v /etc/letsencrypt/:/etc/letsencrypt certbot/certbot delete --cert-name "$yuming" -n
docker stop nginx > /dev/null 2>&1
docker run --rm --network host -v /etc/letsencrypt/:/etc/letsencrypt certbot/certbot certonly --standalone --http-01-address 0.0.0.0 --http-01-port 80 -d $yuming --email your@email.com --agree-tos --no-eff-email --force-renewal --key-type ecdsa
mkdir -p /home/web/certs/
cp /etc/letsencrypt/live/$yuming/fullchain.pem /home/web/certs/${yuming}_cert.pem > /dev/null 2>&1
cp /etc/letsencrypt/live/$yuming/privkey.pem /home/web/certs/${yuming}_key.pem > /dev/null 2>&1
openssl rand -out /home/web/certs/ticket12.key 48
openssl rand -out /home/web/certs/ticket13.key 80
docker start nginx > /dev/null 2>&1
fi
echo "证书已成功续签。"
else
# 若未满足续签条件,则输出证书仍然有效
echo "证书仍然有效,距离过期还有 ${days_until_expiry} 天。"
fi
# 输出分隔线
echo "--------------------------"
done

10
beifen.sh Normal file
View File

@@ -0,0 +1,10 @@
#!/bin/bash
# Create a tar archive of the web directory
cd /home/ && tar czvf web_$(date +"%Y%m%d%H%M%S").tar.gz web
# Transfer the tar archive to another VPS
cd /home/ && ls -t /home/*.tar.gz | head -1 | xargs -I {} sshpass -p 123456 scp -o StrictHostKeyChecking=no -P 22 {} root@0.0.0.0:/home/
# Keep only 5 tar archives and delete the rest
cd /home/ && ls -t /home/*.tar.gz | tail -n +4 | xargs -I {} rm {}

11
check_x86-64_psabi.sh Normal file
View File

@@ -0,0 +1,11 @@
#!/usr/bin/awk -f
BEGIN {
while (!/flags/) if (getline < "/proc/cpuinfo" != 1) exit 1
if (/lm/&&/cmov/&&/cx8/&&/fpu/&&/fxsr/&&/mmx/&&/syscall/&&/sse2/) level = 1
if (level == 1 && /cx16/&&/lahf/&&/popcnt/&&/sse4_1/&&/sse4_2/&&/ssse3/) level = 2
if (level == 2 && /avx/&&/avx2/&&/bmi1/&&/bmi2/&&/f16c/&&/fma/&&/abm/&&/movbe/&&/xsave/) level = 3
if (level == 3 && /avx512f/&&/avx512bw/&&/avx512cd/&&/avx512dq/&&/avx512vl/) level = 3
if (level > 0) { print "CPU supports x86-64-v" level; exit level + 1 }
exit 1
}

88
cloudflare.conf Normal file
View File

@@ -0,0 +1,88 @@
#
# Author: Mike Rushton
#
# IMPORTANT
#
# Please set jail.local's permission to 640 because it contains your CF API key.
#
# This action depends on curl (and optionally jq).
# Referenced from http://www.normyee.net/blog/2012/02/02/adding-cloudflare-support-to-fail2ban by NORM YEE
#
# To get your CloudFlare API Key: https://www.cloudflare.com/a/account/my-account
#
# CloudFlare API error codes: https://www.cloudflare.com/docs/host-api.html#s4.2
[Definition]
# Option: actionstart
# Notes.: command executed on demand at the first ban (or at the start of Fail2Ban if actionstart_on_demand is set to false).
# Values: CMD
#
actionstart =
# Option: actionstop
# Notes.: command executed at the stop of jail (or at the end of Fail2Ban)
# Values: CMD
#
actionstop =
# Option: actioncheck
# Notes.: command executed once before each actionban command
# Values: CMD
#
actioncheck =
# Option: actionban
# Notes.: command executed when banning an IP. Take care that the
# command is executed with Fail2Ban user rights.
# Tags: <ip> IP address
# <failures> number of failures
# <time> unix timestamp of the ban time
# Values: CMD
#
# API v1
#actionban = curl -s -o /dev/null https://www.cloudflare.com/api_json.html -d 'a=ban' -d 'tkn=<cftoken>' -d 'email=<cfuser>' -d 'key=<ip>'
# API v4
actionban = curl -s -o /dev/null -X POST <_cf_api_prms> \
-d '{"mode":"block","configuration":{"target":"<cftarget>","value":"<ip>"},"notes":"Fail2Ban <name>"}' \
<_cf_api_url>
# Option: actionunban
# Notes.: command executed when unbanning an IP. Take care that the
# command is executed with Fail2Ban user rights.
# Tags: <ip> IP address
# <failures> number of failures
# <time> unix timestamp of the ban time
# Values: CMD
#
# API v1
#actionunban = curl -s -o /dev/null https://www.cloudflare.com/api_json.html -d 'a=nul' -d 'tkn=<cftoken>' -d 'email=<cfuser>' -d 'key=<ip>'
# API v4
actionunban = id=$(curl -s -X GET <_cf_api_prms> \
"<_cf_api_url>?mode=block&configuration_target=<cftarget>&configuration_value=<ip>&page=1&per_page=1&notes=Fail2Ban%%20<name>" \
| { jq -r '.result[0].id' 2>/dev/null || tr -d '\n' | sed -nE 's/^.*"result"\s*:\s*\[\s*\{\s*"id"\s*:\s*"([^"]+)".*$/\1/p'; })
if [ -z "$id" ]; then echo "<name>: id for <ip> cannot be found"; exit 0; fi;
curl -s -o /dev/null -X DELETE <_cf_api_prms> "<_cf_api_url>/$id"
_cf_api_url = https://api.cloudflare.com/client/v4/user/firewall/access_rules/rules
_cf_api_prms = -H 'X-Auth-Email: <cfuser>' -H 'X-Auth-Key: <cftoken>' -H 'Content-Type: application/json'
[Init]
# If you like to use this action with mailing whois lines, you could use the composite action
# action_cf_mwl predefined in jail.conf, just define in your jail:
#
# action = %(action_cf_mwl)s
# # Your CF account e-mail
# cfemail =
# # Your CF API Key
# cfapikey =
cftoken = APIKEY00000
cfuser = kejilion@outlook.com
cftarget = ip
[Init?family=inet6]
cftarget = ip6

22246
cn/kejilion.sh Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1 @@
../../../tests/openclaw/tests_openclaw_memory_auto_setup_smoke.sh

View File

@@ -0,0 +1,214 @@
#!/usr/bin/env bash
set -euo pipefail
REPO_ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
SCRIPT="$REPO_ROOT/kejilion.sh"
WORKDIR="${TMPDIR:-/tmp}/openclaw-memory-menu-test-$$"
mkdir -p "$WORKDIR/bin" "$WORKDIR/home/.openclaw/workspace/memory"
KEEP_WORKDIR=${KEEP_WORKDIR:-false}
trap '[ "$KEEP_WORKDIR" = "true" ] || rm -rf "$WORKDIR"' EXIT
# 抽取 OpenClaw 记忆菜单实现
cat > "$WORKDIR/harness.sh" <<'EOF_INNER'
#!/usr/bin/env bash
set -euo pipefail
break_end() { return 0; }
send_stats() { return 0; }
EOF_INNER
awk 'BEGIN{p=0} /openclaw_memory_config_file\(\) \{/{p=1} /openclaw_memory_menu\(\) \{/{p=0} p{print}' "$SCRIPT" >> "$WORKDIR/harness.sh"
awk 'BEGIN{p=0} /openclaw_memory_menu\(\) \{/{p=1} /openclaw_backup_restore_menu\(\) \{/{p=0} p{print}' "$SCRIPT" >> "$WORKDIR/harness.sh"
chmod +x "$WORKDIR/harness.sh"
# stub: openclaw
cat > "$WORKDIR/bin/openclaw" <<'EOF_INNER'
#!/usr/bin/env bash
cmd="$*"
if [[ "$cmd" == "config get"* ]]; then
key="$3"
case "$key" in
memory.backend) echo "qmd" ;;
memory.qmd.includeDefaultMemory) echo "true" ;;
memory.qmd.command) echo "qmd" ;;
agents.defaults.memorySearch.local.modelPath) echo "hf:ggml-org/embeddinggemma-300M-GGUF/embeddinggemma-300M-Q8_0.gguf" ;;
agents.defaults.memorySearch.provider) echo "local" ;;
*) echo "" ;;
esac
exit 0
fi
if [[ "$cmd" == "config set"* ]]; then
key="$3"
val="$4"
if [[ "$key" == "memory.qmd.includeDefaultMemory" ]]; then
echo "$val" > "${HOME}/.openclaw/includeDefaultMemory"
fi
if [[ "$key" == "memory.backend" ]]; then
echo "$val" > "${HOME}/.openclaw/backend"
fi
if [[ "$key" == "memory.qmd.command" ]]; then
echo "$val" > "${HOME}/.openclaw/qmd_command"
fi
if [[ "$key" == "agents.defaults.memorySearch.provider" ]]; then
echo "$val" > "${HOME}/.openclaw/provider"
fi
exit 0
fi
if [[ "$cmd" == "memory status" ]]; then
cat <<TXT
Provider: qmd (requested: qmd)
Vector: ready
Indexed: 23/14 files
Workspace: ${HOME}/.openclaw/workspace
Store: ${OPENCLAW_STORE:-~/.openclaw/workspace/memory/index.sqlite}
TXT
exit 0
fi
if [[ "$cmd" == "memory index"* ]]; then
echo "$cmd" >> "${HOME}/.openclaw/index_calls"
echo "index ok"
exit 0
fi
if [[ "$cmd" == "gateway restart" ]]; then
echo "$cmd" >> "${HOME}/.openclaw/gateway_calls"
echo "gateway restarted"
exit 0
fi
if [[ "$cmd" == "gateway stop" ]]; then
echo "gateway stopped"
exit 0
fi
if [[ "$cmd" == "gateway start" ]]; then
echo "gateway started"
exit 0
fi
echo "mock openclaw $*"
EOF_INNER
chmod +x "$WORKDIR/bin/openclaw"
# stub: qmd
cat > "$WORKDIR/bin/qmd" <<'EOF_INNER'
#!/usr/bin/env bash
exit 0
EOF_INNER
chmod +x "$WORKDIR/bin/qmd"
# stub: curl (用于网络探测)
cat > "$WORKDIR/bin/curl" <<'EOF_INNER'
#!/usr/bin/env bash
if [[ "$*" == *"huggingface.co"* ]]; then
exit 1
fi
if [[ "$*" == *"hf-mirror.com"* ]]; then
exit 0
fi
exit 0
EOF_INNER
chmod +x "$WORKDIR/bin/curl"
export HOME="$WORKDIR/home"
export PATH="$WORKDIR/bin:$PATH"
export TERM=xterm
# minimal openclaw.json
cat > "$HOME/.openclaw/openclaw.json" <<'JSON'
{
"memory": {
"backend": "qmd",
"qmd": {"command": "qmd", "includeDefaultMemory": true}
},
"agents": {
"defaults": {
"memorySearch": {
"provider": "local",
"local": {
"modelPath": "hf:ggml-org/embeddinggemma-300M-GGUF/embeddinggemma-300M-Q8_0.gguf"
}
}
}
}
}
JSON
# mock index sqlite
mkdir -p "$HOME/.openclaw/workspace/memory"
touch "$HOME/.openclaw/workspace/memory/index.sqlite"
# mock memory files
cat > "$HOME/.openclaw/workspace/MEMORY.md" <<'TXT'
# MEMORY
line1
line2
line3
TXT
cat > "$HOME/.openclaw/workspace/memory/2026-03-11.md" <<'TXT'
# 2026-03-11
foo
bar
TXT
source "$WORKDIR/harness.sh"
run_menu() {
local input="$1"
local title="$2"
local outfile="$3"
echo "[TEST] $title"
printf "%b" "$input" | openclaw_memory_menu >"$outfile"
}
# 1) 状态展示(直接返回)
run_menu "0\n" "status" "$WORKDIR/out_status.txt"
# 2) 更新索引(增量)
run_menu "1\nyes\n\n\n0\n" "index" "$WORKDIR/out_index.txt"
# 3) 查看记忆文件(列表+查看)
run_menu "2\n1\n\n\n\n\n0\n0\n" "files" "$WORKDIR/out_files.txt"
# 4) 索引修复(执行修复 + 不立即重建)
run_menu "3\ny\n\n\n0\n" "fix" "$WORKDIR/out_fix.txt"
# 5) 记忆方案(自动推荐并取消)
run_menu "4\n1\n\n0\n0\n" "scheme" "$WORKDIR/out_scheme.txt"
# 6) Store 缺失时仍执行 rebuild
: > "$HOME/.openclaw/index_calls"
OPENCLAW_STORE="~/.openclaw/workspace/memory/missing.sqlite" \
openclaw_memory_rebuild_index_safe >"$WORKDIR/out_missing.txt"
export WORKDIR
python3 - <<'PY'
import glob
import os
flag_path = os.path.expanduser('~/.openclaw/includeDefaultMemory')
if not os.path.exists(flag_path):
raise SystemExit('includeDefaultMemory not updated')
flag = open(flag_path, 'r', encoding='utf-8').read().strip()
if flag != 'false':
raise SystemExit('includeDefaultMemory not updated')
# ensure backup created
baks = glob.glob(os.path.expanduser('~/.openclaw/workspace/memory/index.sqlite.bak.*'))
if not baks:
raise SystemExit('index sqlite backup not created')
# ensure missing store still triggers force index
calls_path = os.path.expanduser('~/.openclaw/index_calls')
if not os.path.exists(calls_path):
raise SystemExit('index calls not recorded')
with open(calls_path, 'r', encoding='utf-8') as fh:
calls = fh.read()
if 'memory index --force' not in calls:
raise SystemExit('force index not called for missing store')
# ensure gateway restart called after rebuild
gw_calls_path = os.path.expanduser('~/.openclaw/gateway_calls')
if not os.path.exists(gw_calls_path):
raise SystemExit('gateway restart not recorded')
with open(gw_calls_path, 'r', encoding='utf-8') as fh:
gw_calls = fh.read()
if 'gateway restart' not in gw_calls:
raise SystemExit('gateway restart not called after rebuild')
# ensure warning emitted
with open(os.path.join(os.environ['WORKDIR'], 'out_missing.txt'), 'r', encoding='utf-8') as fh:
out = fh.read()
if 'Store 原始值' not in out:
raise SystemExit('missing store warning not found')
if '索引已重建并自动重启网关' not in out:
raise SystemExit('auto restart message not found')
print('SMOKE_OK')
PY

44
custom_mysql_config-1.cnf Normal file
View File

@@ -0,0 +1,44 @@
[mysqld]
# 连接和线程管理
max_connections = 200 # 小机型避免连接风暴
thread_cache_size = 64
interactive_timeout = 20
wait_timeout = 20
# InnoDB设置
innodb_buffer_pool_size = 512M # 2G RAM 中最重要的分配,留足系统空间
innodb_buffer_pool_instances = 1 # 小内存不需要分片
innodb_log_buffer_size = 8M
innodb_redo_log_capacity = 64M
innodb_lock_wait_timeout = 30
innodb_file_per_table = 1
innodb_flush_log_at_trx_commit = 1
innodb_io_capacity = 400 # 普通SSD预期值
innodb_io_capacity_max = 800
# 缓存和表限制
table_open_cache = 512
open_files_limit = 20000
tmp_table_size = 32M
max_heap_table_size = 32M
max_allowed_packet = 32M
# 缓冲区大小
sort_buffer_size = 2M
read_buffer_size = 512K
join_buffer_size = 1M
# 日志管理
log_error_verbosity = 3
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 1
log_queries_not_using_indexes = 1
# 其他
sql_mode=STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION
performance_schema=ON
disable-log-bin

45
custom_mysql_config.cnf Normal file
View File

@@ -0,0 +1,45 @@
[mysqld]
# 连接和线程管理
max_connections = 2048
thread_cache_size = 512
interactive_timeout = 30
wait_timeout = 30
# 查询缓存MySQL8废除Query Cache不配置
# no query_cache_size or query_cache_type needed
# InnoDB设置
innodb_buffer_pool_size = 4096M # ⭐如果内存允许给到一半RAM数据库读性能暴涨
innodb_buffer_pool_instances = 4 # ⭐分片更细(>2G建议4-8分片
innodb_log_buffer_size = 32M # ⭐更大事务写更稳
innodb_redo_log_capacity = 128M # ⭐防止大事务日志爆满
innodb_lock_wait_timeout = 30
innodb_file_per_table = 1
innodb_flush_log_at_trx_commit = 1 # ⭐坚持ACID不放松一致性
innodb_io_capacity = 2000 # ⭐如果是NVMe磁盘可以调高>SSD
innodb_io_capacity_max = 4000
# 缓存和表限制
table_open_cache = 4000 # ⭐动态表多的网站,加大打开表缓存
open_files_limit = 65535 # ⭐极限设置注意宿主机ulimit
tmp_table_size = 64M # ⭐调大,减少临时表落磁盘
max_heap_table_size = 64M # ⭐配合tmp_table_size一起调大
max_allowed_packet = 64M # ⭐适配大SQL比如大BLOB字段
# 缓冲区大小
sort_buffer_size = 8M # ⭐适配中大型ORDER BY场景
read_buffer_size = 2M
join_buffer_size = 4M # ⭐稍微放大Join场景效率更高
# 日志管理
log_error_verbosity = 3 # ⭐详细日志,便于追查慢问题
slow_query_log = 1 # ⭐打开慢查询日志
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 1 # ⭐超过1秒算慢查询
log_queries_not_using_indexes = 1 # ⭐记录无索引扫描
# 其他
sql_mode=STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION
performance_schema=ON # ⭐生产环境推荐打开,监控分析好用
disable-log-bin

22252
en/kejilion.sh Normal file

File diff suppressed because it is too large Load Diff

76
en/to-en.py Normal file
View File

@@ -0,0 +1,76 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from deep_translator import GoogleTranslator
import re
import os
def is_chinese(text):
return bool(re.search(r'[\u4e00-\u9fff]', text))
def translate_text(text):
try:
return GoogleTranslator(source='zh-CN', target='en').translate(text)
except Exception as e:
print(f"\nTranslation error: {e}")
return text
def translate_line_preserving_variables(line):
"""
Translate only Chinese parts in echo/read/send_stats commands, excluding shell variables
"""
# Match double or single quoted strings
def repl(match):
full_string = match.group(0)
quote = full_string[0]
content = full_string[1:-1]
# Split by variable expressions
parts = re.split(r'(\$\{?\w+\}?)', content)
translated_parts = [
translate_text(p) if is_chinese(p) else p
for p in parts
]
return quote + ''.join(translated_parts) + quote
return re.sub(r'(?:\'[^\']*\'|"[^"]*")', repl, line)
def translate_file(input_file, output_file):
total_lines = sum(1 for _ in open(input_file, 'r', encoding='utf-8'))
processed_lines = 0
with open(input_file, 'r', encoding='utf-8') as f_in, \
open(output_file, 'w', encoding='utf-8') as f_out:
for line in f_in:
processed_lines += 1
progress = processed_lines / total_lines * 100
print(f"\rProcessing: {progress:.1f}% ({processed_lines}/{total_lines})", end='')
leading_space = re.match(r'^(\s*)', line).group(1)
stripped = line.strip()
if stripped.startswith('#') and is_chinese(stripped):
comment_mark = '#'
comment_text = stripped[1:].strip()
if comment_text:
translated = translate_text(comment_text)
f_out.write(f"{leading_space}{comment_mark} {translated}\n")
else:
f_out.write(line)
elif any(cmd in stripped for cmd in ['echo', 'read', 'send_stats']) and is_chinese(stripped):
translated_line = translate_line_preserving_variables(line)
f_out.write(translated_line)
else:
f_out.write(line)
print("\nTranslation completed.")
print(f"Original file size: {os.path.getsize(input_file)} bytes")
print(f"Translated file size: {os.path.getsize(output_file)} bytes")
if __name__ == "__main__":
input_file = 'kejilion.sh'
output_file = 'kejilion_en.sh'
translate_file(input_file, output_file)

9
fail2ban-nginx-cc.conf Normal file
View File

@@ -0,0 +1,9 @@
[Definition]
# failregex = ^<HOST> .* "(GET|POST|HEAD).*HTTP.*" (404|503) .*$
# failregex = ^<HOST> .* "(GET|POST|HEAD).*HTTP.*" (404|503|444) .*
# failregex = ^<HOST> .* "(GET|POST|HEAD).*HTTP.*" (403|404|429) .*
# failregex = ^<HOST> .* "(GET|POST|HEAD).*HTTP.*" ([45]\d\d) .*
# ignoreregex =.*(robots.txt|favicon.ico|jpg|png)
failregex = ^<HOST> .* HTTP.* (403|429) .*$
ignoreregex = ^.*(\/(?:robots\.txt|favicon\.ico|.*\.(?:jpg|png|gif|jpeg|svg|webp|bmp|tiff|css|js|woff|woff2|eot|ttf|otf))$)

631
hermes_manager.sh Executable file
View File

@@ -0,0 +1,631 @@
#!/bin/bash
# Hermes Agent 终端管理脚本 (轻量版)
# 设计哲学:极简、直观、调用原生功能
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
NC='\033[0m'
# 确保 hermes 命令可用 (处理环境变量未加载的情况)
if ! command -v hermes >/dev/null 2>&1; then
if [ -d "$HOME/.hermes/hermes-agent/venv/bin" ]; then
export PATH="$HOME/.hermes/hermes-agent/venv/bin:$PATH"
fi
fi
# 检查是否安装
# --- 科技lion 增强版 API 管理核心 ---
CONFIG_FILE="$HOME/.hermes/config.yaml"
config_tool() {
python3 - "$CONFIG_FILE" "$@" <<'EOF'
import sys, yaml, json, os
path = sys.argv[1]
action = sys.argv[2]
def load():
if not os.path.exists(path):
return {}
try:
with open(path, 'r', encoding='utf-8') as f:
return yaml.safe_load(f) or {}
except:
return {}
def save(d):
with open(path, 'w', encoding='utf-8') as f:
yaml.dump(d, f, sort_keys=False, allow_unicode=True)
try:
data = load()
if action == "get_info":
m = data.get('model', {})
res = {"m": m.get('default', '-'), "p": m.get('provider', '-'), "u": m.get('base_url', '-')}
print(json.dumps(res))
elif action == "list_p":
print(json.dumps(data.get('custom_providers', [])))
elif action == "add_p":
n, u, k, m = sys.argv[3:7]
ps = data.get('custom_providers', [])
if not isinstance(ps, list): ps = []
ps = [p for p in ps if p.get('name') != n]
ps.append({"name": n, "base_url": u, "api_key": k, "model": m})
data['custom_providers'] = ps
save(data)
elif action == "bulk_add":
n_base, u, k, models_json = sys.argv[3:7]
new_m_ids = json.loads(models_json)
ps = data.get('custom_providers', [])
if not isinstance(ps, list): ps = []
# 移除旧的同前缀条目
ps = [p for p in ps if not (isinstance(p, dict) and p.get('name', '').startswith(n_base + "/"))]
# 移除可能存在的同名根条目
ps = [p for p in ps if p.get('name') != n_base]
for m_id in new_m_ids:
ps.append({"name": f"{n_base}/{m_id}", "base_url": u, "api_key": k, "model": m_id})
data['custom_providers'] = ps
save(data)
elif action == "del_p":
n = sys.argv[3]
ps = data.get('custom_providers', [])
if isinstance(ps, list):
data['custom_providers'] = [p for p in ps if p.get('name') != n and not p.get('name', '').startswith(n + "/")]
save(data)
elif action == "list_groups":
ps = data.get('custom_providers', [])
groups = []
seen = set()
for p in (ps if isinstance(ps, list) else []):
name = p.get('name', '')
g = name.split('/')[0] if '/' in name else name
if g and g not in seen:
seen.add(g)
cnt = sum(1 for x in ps if x.get('name', '') == g or x.get('name', '').startswith(g + '/'))
groups.append({"name": g, "count": cnt})
print(json.dumps(groups))
elif action == "switch":
n, u, k, m = sys.argv[3:7]
data['model'] = {"default": m, "provider": "custom", "base_url": u, "api_key": k}
save(data)
except Exception as e:
# 确保出错时返回合法的 JSON 避免 Bash 报错
print(json.dumps([]))
sys.exit(1)
EOF
}
install_gum() {
if command -v gum >/dev/null 2>&1; then
return 0
fi
echo -e "${YELLOW}正在安装 gum (交互式选择器)...${NC}"
if command -v apt >/dev/null 2>&1; then
mkdir -p /etc/apt/keyrings
curl -fsSL https://repo.charm.sh/apt/gpg.key | gpg --dearmor -o /etc/apt/keyrings/charm.gpg 2>/dev/null
echo "deb [signed-by=/etc/apt/keyrings/charm.gpg] https://repo.charm.sh/apt/ * *" | tee /etc/apt/sources.list.d/charm.list > /dev/null
apt update -qq && apt install -y -qq gum
elif command -v dnf >/dev/null 2>&1 || command -v yum >/dev/null 2>&1; then
cat > /etc/yum.repos.d/charm.repo <<'REPO'
[charm]
name=Charm
baseurl=https://repo.charm.sh/yum/
enabled=1
gpgcheck=1
gpgkey=https://repo.charm.sh/yum/gpg.key
REPO
rpm --import https://repo.charm.sh/yum/gpg.key
if command -v dnf >/dev/null 2>&1; then dnf install -y gum; else yum install -y gum; fi
elif command -v zypper >/dev/null 2>&1; then
zypper --non-interactive install gum
fi
}
hermes_model_probe() {
local target_model="$1"
local ps_json="$2"
local probe_timeout=15
local provider_name request_model base_url api_key
# 从 custom_providers 中查找对应条目
provider_name="$target_model"
local entry=$(echo "$ps_json" | jq -c --arg n "$provider_name" '.[] | select(.name == $n)' 2>/dev/null)
if [ -z "$entry" ]; then
HERMES_PROBE_STATUS="ERROR"
HERMES_PROBE_MESSAGE="未找到模型配置"
HERMES_PROBE_LATENCY="-"
HERMES_PROBE_REPLY="-"
return 1
fi
base_url=$(echo "$entry" | jq -r .base_url)
api_key=$(echo "$entry" | jq -r .api_key)
request_model=$(echo "$entry" | jq -r .model)
base_url="${base_url%/}"
local tmp_response
tmp_response=$(mktemp)
# 使用 Python 探测,精确计时
local probe_result
probe_result=$(python3 - "$base_url" "$api_key" "$request_model" "$tmp_response" "$probe_timeout" <<'PYEOF'
import sys, time, json
try:
import urllib.request, urllib.error
except ImportError:
print("1|0|0")
sys.exit(0)
base_url, api_key, model, resp_path, timeout = sys.argv[1:6]
timeout = int(timeout)
url = base_url + "/chat/completions"
payload = json.dumps({"model": model, "messages": [{"role": "user", "content": "hi"}], "temperature": 0, "max_tokens": 16}).encode()
req = urllib.request.Request(url, data=payload, headers={
"Content-Type": "application/json",
"Authorization": f"Bearer {api_key}",
}, method="POST")
start = time.time()
body = b""
status = 0
exit_code = 0
try:
with urllib.request.urlopen(req, timeout=timeout) as resp:
status = getattr(resp, "status", 200)
body = resp.read()
except urllib.error.HTTPError as e:
status = getattr(e, "code", 0) or 0
body = e.read()
exit_code = 22
except Exception as e:
body = str(e).encode("utf-8", errors="replace")
exit_code = 1
elapsed = int((time.time() - start) * 1000)
with open(resp_path, "wb") as f:
f.write(body)
print(f"{exit_code}|{status}|{elapsed}")
PYEOF
)
local p_exit p_http p_latency
p_exit=${probe_result%%|*}
p_http=${probe_result#*|}
p_http=${p_http%%|*}
p_latency=${probe_result##*|}
# 提取回复摘要
local reply_preview
reply_preview=$(python3 - "$tmp_response" <<'PYEOF'
import json, sys
from pathlib import Path
raw = Path(sys.argv[1]).read_text(encoding="utf-8", errors="replace").strip()
reply = ""
if raw:
try:
data = json.loads(raw)
if isinstance(data, dict):
choices = data.get("choices") or []
if choices and isinstance(choices[0], dict):
msg = choices[0].get("message") or {}
if isinstance(msg, dict):
reply = msg.get("content") or ""
if not reply:
for key in ("error", "message", "detail"):
v = data.get(key)
if isinstance(v, str) and v.strip():
reply = v.strip(); break
if isinstance(v, dict):
n = v.get("message")
if isinstance(n, str) and n.strip():
reply = n.strip(); break
except:
reply = raw
reply = " ".join(str(reply).split())[:120]
print(reply if reply else "(空返回)")
PYEOF
)
rm -f "$tmp_response"
if [ "$p_exit" = "0" ] && [ "$p_http" -ge 200 ] 2>/dev/null && [ "$p_http" -lt 300 ] 2>/dev/null; then
HERMES_PROBE_STATUS="OK"
HERMES_PROBE_MESSAGE="HTTP ${p_http}"
HERMES_PROBE_LATENCY="${p_latency}ms"
HERMES_PROBE_REPLY="$reply_preview"
return 0
else
HERMES_PROBE_STATUS="FAIL"
HERMES_PROBE_MESSAGE="HTTP ${p_http:-0} / exit ${p_exit:-1}"
HERMES_PROBE_LATENCY="${p_latency:-?}ms"
HERMES_PROBE_REPLY="$reply_preview"
return 1
fi
}
hermes_probe_status_line() {
local status_text="$1"
local color_ok='\033[32m' color_fail='\033[31m' reset='\033[0m'
if [ "$status_text" = "可用" ]; then
printf "%b最小检测结果%s%b\n" "$color_ok" "$status_text" "$reset"
else
printf "%b最小检测结果%s%b\n" "$color_fail" "$status_text" "$reset"
fi
}
api_management_submenu() {
while true; do
clear
info=$(config_tool get_info)
echo -e "${BLUE}=======================================${NC}"
echo -e " ${PURPLE}API & 模型管理 (OpenClaw 风格)${NC}"
echo -e "${BLUE}=======================================${NC}"
echo -e "${CYAN}当前激活模型:${NC} ${GREEN}$(echo $info | jq -r .m)${NC}"
echo -e "---------------------------------------"
echo -e "${CYAN}已配置 API 列表:${NC}"
ps_list=$(config_tool list_p)
if [ "$(echo "$ps_list" | jq '. | length')" -eq 0 ]; then
echo -e " ${YELLOW}(暂无配置)${NC}"
else
echo "$ps_list" | jq -r '.[] | " ● [\(.name)] \(.model) | \(.base_url)"'
fi
echo -e "---------------------------------------"
echo -e "1. ${YELLOW}切换模型 (带测速)${NC}"
echo -e "2. 添加 API 供应商 (自动同步)${NC}"
echo -e "3. 删除 API 供应商"
echo -e "0. 返回主菜单"
echo -e "---------------------------------------"
read -p "选择序号: " sub_choice
case "$sub_choice" in
1)
local orange="#FF8C00"
local ps_json models_list model_count default_model selected_model confirm_switch
ps_json=$(config_tool list_p)
model_count=$(echo "$ps_json" | jq '. | length')
if [ "$model_count" -eq 0 ] 2>/dev/null || [ -z "$model_count" ]; then
echo -e "${RED}无 API 配置! 请先添加供应商。${NC}"
sleep 1
continue
fi
# 构建带编号的模型列表
models_list=$(echo "$ps_json" | jq -r '.[].name' | awk '{print "(" NR ") " $0}')
default_model=$(config_tool get_info | jq -r .m)
while true; do
clear
install_gum
# 若 gum 不可用,降级为手动输入
if ! command -v gum >/dev/null 2>&1; then
echo "--- 模型管理 ---"
echo "当前可用模型:"
echo "$models_list"
echo "当前默认:${default_model}"
echo "----------------"
read -e -p "请输入模型编号或名称 (输入 0 退出): " selected_model
if [ "$selected_model" = "0" ]; then
break
fi
if [ -z "$selected_model" ]; then
echo "错误:不能为空,请重试。"
sleep 1
continue
fi
# 如果输入的是纯数字,从列表中取名称
if [[ "$selected_model" =~ ^[0-9]+$ ]]; then
selected_model=$(echo "$ps_json" | jq -r --argjson i "$((selected_model-1))" '.[$i].name // empty')
if [ -z "$selected_model" ]; then
echo "序号无效,请重试。"
sleep 1
continue
fi
fi
else
# gum 模式 — 完全复刻 openclaw 风格
gum style --foreground "$orange" --bold "模型管理"
gum style --foreground "$orange" "可用模型:${model_count}"
gum style --foreground "$orange" "当前默认:${default_model}"
echo ""
gum style --faint "↑↓ 选择 / 输入搜索 / Enter 测试 / Esc 退出"
echo ""
selected_model=$(echo "$models_list" | gum filter \
--placeholder "搜索模型(如 cli-api/gpt-4o" \
--prompt "选择模型 > " \
--indicator "➜ " \
--prompt.foreground "$orange" \
--indicator.foreground "$orange" \
--cursor-text.foreground "$orange" \
--match.foreground "$orange" \
--header "" \
--height 35)
if [ -z "$selected_model" ] || echo "$selected_model" | head -n 1 | grep -iqE '^(error|usage|gum:)'; then
echo "操作已取消,正在退出..."
break
fi
fi
# 去掉编号前缀 "(N) "
selected_model=$(echo "$selected_model" | sed -E 's/^\([0-9]+\)[[:space:]]+//')
echo ""
echo "正在检测模型: $selected_model"
if hermes_model_probe "$selected_model" "$ps_json"; then
hermes_probe_status_line "可用"
else
hermes_probe_status_line "不可用"
fi
echo "状态:$HERMES_PROBE_MESSAGE"
echo "延迟:$HERMES_PROBE_LATENCY"
echo "摘要:$HERMES_PROBE_REPLY"
echo ""
printf "是否切换到该模型?[y/NEsc 返回列表]: "
IFS= read -rsn1 confirm_switch
echo ""
if [ "$confirm_switch" = $'\x1b' ]; then
confirm_switch="no"
else
case "$confirm_switch" in
[yY])
IFS= read -rsn1 -t 5 _enter_key
confirm_switch="yes"
;;
*) confirm_switch="no" ;;
esac
fi
if [ "$confirm_switch" != "yes" ]; then
echo "已返回模型选择列表。"
sleep 1
continue
fi
# 执行切换
local entry_data
entry_data=$(echo "$ps_json" | jq -c --arg n "$selected_model" '.[] | select(.name == $n)')
local sw_u sw_k sw_m
sw_u=$(echo "$entry_data" | jq -r .base_url)
sw_k=$(echo "$entry_data" | jq -r .api_key)
sw_m=$(echo "$entry_data" | jq -r .model)
echo "正在切换模型为: $selected_model ..."
config_tool switch "$selected_model" "$sw_u" "$sw_k" "$sw_m"
# 重启 gateway
echo -e "${YELLOW}正在重启 Gateway...${NC}"
hermes gateway stop >/dev/null 2>&1
hermes gateway start >/dev/null 2>&1
echo -e "${GREEN}✅ 模型已切换为: $sw_m${NC}"
sleep 2
break
done
;;
2)
echo -e "${CYAN}--- 添加新 API 供应商 ---${NC}"
read -p "请输入供应商名称 (如: DeepSeek): " n
[ -z "$n" ] && continue
read -p "请输入 Base URL (如: https://api.deepseek.com/v1): " u
[ -z "$u" ] && continue
u="${u%/}"
echo -ne "${YELLOW}请输入 API Key (输入隐藏): ${NC}"
read -s k
echo ""
[ -z "$k" ] && continue
echo -e "${YELLOW}🔍 正在获取完整模型列表...${NC}"
m_json=$(curl -s -m 10 -H "Authorization: Bearer $k" "$u/models")
# 提取所有 ID
m_list_str=$(echo "$m_json" | jq -r '.data[].id' 2>/dev/null | sort)
if [ -n "$m_list_str" ]; then
# 转换为数组
m_array=()
while read -r line; do m_array+=("$line"); done <<< "$m_list_str"
m_count=${#m_array[@]}
echo -e "${GREEN}✅ 发现 $m_count 个模型。请选择一个作为当前默认:${NC}"
PS3="请输入序号: "
select m_default in "${m_array[@]}"; do
[ -n "$m_default" ] && break
done
echo -e "---------------------------------------"
read -p "是否同时添加该供应商的所有 $m_count 个模型?(y/N): " bulk_confirm
if [[ "$bulk_confirm" =~ ^[Yy]$ ]]; then
# 转换数组为 JSON
m_json_list=$(echo "$m_list_str" | jq -R . | jq -s -c .)
config_tool bulk_add "$n" "$u" "$k" "$m_json_list"
config_tool switch "$n/$m_default" "$u" "$k" "$m_default"
echo -e "${GREEN}✅ 已全量导入 $m_count 个模型。${NC}"
else
config_tool add_p "$n" "$u" "$k" "$m_default"
echo -e "${GREEN}✅ 已添加单个模型: $m_default${NC}"
fi
else
echo -e "${RED}❌ 无法获取列表。${NC}"
read -p "请手动输入模型 ID: " m_manual
[ -n "$m_manual" ] && config_tool add_p "$n" "$u" "$k" "$m_manual"
fi
sleep 2
;;
3)
echo -e "${CYAN}已配置的供应商分组:${NC}"
groups_json=$(config_tool list_groups)
g_count=$(echo "$groups_json" | jq '. | length')
if [ "$g_count" -eq 0 ]; then
echo -e " ${YELLOW}(暂无配置)${NC}"
sleep 1
continue
fi
# 列出供应商分组
g_names=()
while read -r row; do
g_name=$(echo "$row" | jq -r .name)
g_cnt=$(echo "$row" | jq -r .count)
g_names+=("$g_name")
echo -e " ${GREEN}${#g_names[@]}.${NC} $g_name (${g_cnt} 个模型)"
done < <(echo "$groups_json" | jq -c '.[]')
echo -e " ${GREEN}0.${NC} 取消"
read -p "选择要删除的供应商序号: " d_idx
if [ "$d_idx" == "0" ] || [ -z "$d_idx" ]; then continue; fi
d_name="${g_names[$((d_idx-1))]}"
if [ -n "$d_name" ]; then
read -p "确认删除 [$d_name] 及其所有模型? (y/N): " del_confirm
if [[ "$del_confirm" =~ ^[Yy]$ ]]; then
config_tool del_p "$d_name"
echo -e "${RED}🗑️ 已删除 $d_name${NC}"
sleep 1
fi
fi
;;
0) break ;;
esac
done
}
check_installed() {
if command -v hermes >/dev/null 2>&1; then return 0; else return 1; fi
}
# 获取版本号
get_version() {
if check_installed; then
hermes --version | head -n 1
fi
}
# 获取网关状态
get_gateway_status() {
if check_installed; then
# 匹配后台 gateway 进程或 systemd 服务
if pgrep -f "hermes_cli.main gateway" > /dev/null || pgrep -f "hermes gateway run" > /dev/null || pgrep -f "hermes-gateway" > /dev/null; then
echo -e "${GREEN}运行中${NC}"
else
echo -e "${RED}已停止${NC}"
fi
else
echo -e "${RED}未安装${NC}"
fi
}
refresh_hermes_path() {
if ! command -v hermes >/dev/null 2>&1; then
if [ -d "$HOME/.hermes/hermes-agent/venv/bin" ]; then
export PATH="$HOME/.hermes/hermes-agent/venv/bin:$PATH"
fi
fi
}
# 主菜单UI
show_menu() {
clear
echo -e "${CYAN}=================================================${NC}"
echo -e "${YELLOW} Hermes Agent 终端管理工具 ${NC}"
echo -e "${CYAN}=================================================${NC}"
echo -e " 运行状态 : $(get_gateway_status)"
echo -e " 当前版本 : $(get_version)"
echo -e "${CYAN}-------------------------------------------------${NC}"
echo -e "${GREEN}1.${NC} 安装 Hermes Agent"
echo -e "${GREEN}2.${NC} 启动 Gateway (消息网关/后台服务)"
echo -e "${GREEN}3.${NC} 停止 Gateway"
echo -e "${GREEN}4.${NC} API/模型管理 (提供商与模型切换)"
echo -e "${GREEN}5.${NC} 启动终端对话UI (Interactive Chat)"
echo -e "${GREEN}6.${NC} 运行初始化配置向导 (Setup Wizard)"
echo -e "${GREEN}7.${NC} 检查并更新 Hermes"
echo -e "${GREEN}8.${NC} 卸载 Hermes"
echo -e "${GREEN}0.${NC} 退出"
echo -e "${CYAN}=================================================${NC}"
if ! read -p " 请输入数字 [0-8]: " choice; then
echo -e "\n${GREEN}退出脚本。${NC}"
exit 0
fi
echo ""
case $choice in
1)
echo -e "${YELLOW}开始安装 Hermes Agent...${NC}"
curl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh | bash
refresh_hermes_path
hermes gateway install
hermes gateway start
;;
2)
if check_installed; then
echo -e "${YELLOW}正在启动 Gateway...${NC}"
systemctl --user start hermes-gateway
hermes gateway stop
hermes gateway start
else echo -e "${RED}请先安装 Hermes。${NC}"; fi
;;
3)
if check_installed; then
echo -e "${YELLOW}正在停止 Gateway...${NC}"
hermes gateway stop
else echo -e "${RED}请先安装 Hermes。${NC}"; fi
;;
4)
if check_installed; then
echo -e "${YELLOW}进入模型配置向导...${NC}"
api_management_submenu
else echo -e "${RED}请先安装 Hermes。${NC}"; fi
;;
5)
if check_installed; then
echo -e "${YELLOW}即将进入交互式终端,输入 /exit 即可退出返回。${NC}"
sleep 1
hermes
else echo -e "${RED}请先安装 Hermes。${NC}"; fi
;;
6)
if check_installed; then
echo -e "${YELLOW}正在启动初始化配置向导...${NC}"
hermes setup
else echo -e "${RED}请先安装 Hermes。${NC}"; fi
;;
7)
if check_installed; then
echo -e "${YELLOW}正在检查更新...${NC}"
hermes update
else echo -e "${RED}请先安装 Hermes。${NC}"; fi
;;
8)
if check_installed; then
read -p "确定要卸载 Hermes 吗?所有数据将被清除。(y/N): " confirm
if [[ "$confirm" =~ ^[Yy]$ ]]; then
hermes uninstall
else echo "已取消。"; fi
else echo -e "${RED}请先安装 Hermes。${NC}"; fi
;;
0)
echo -e "${GREEN}感谢使用,再见!${NC}"
exit 0
;;
*)
echo -e "${RED}输入错误,请重新选择。${NC}"
;;
esac
echo ""
read -p "按回车键返回主菜单..."
}
# 主循环
while true; do
show_menu
done

12193
ir/kejilion.sh Normal file

File diff suppressed because it is too large Load Diff

76
ir/to-fa.py Normal file
View File

@@ -0,0 +1,76 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from deep_translator import GoogleTranslator
import re
import os
def is_chinese(text):
return bool(re.search(r'[\u4e00-\u9fff]', text))
def translate_text(text):
try:
return GoogleTranslator(source='zh-CN', target='fa').translate(text)
except Exception as e:
print(f"\nTranslation error: {e}")
return text
def translate_line_preserving_variables(line):
"""
Translate only Chinese parts in echo/read/send_stats commands, excluding shell variables
"""
# Match double or single quoted strings
def repl(match):
full_string = match.group(0)
quote = full_string[0]
content = full_string[1:-1]
# Split by variable expressions
parts = re.split(r'(\$\{?\w+\}?)', content)
translated_parts = [
translate_text(p) if is_chinese(p) else p
for p in parts
]
return quote + ''.join(translated_parts) + quote
return re.sub(r'(?:\'[^\']*\'|"[^"]*")', repl, line)
def translate_file(input_file, output_file):
total_lines = sum(1 for _ in open(input_file, 'r', encoding='utf-8'))
processed_lines = 0
with open(input_file, 'r', encoding='utf-8') as f_in, \
open(output_file, 'w', encoding='utf-8') as f_out:
for line in f_in:
processed_lines += 1
progress = processed_lines / total_lines * 100
print(f"\rProcessing: {progress:.1f}% ({processed_lines}/{total_lines})", end='')
leading_space = re.match(r'^(\s*)', line).group(1)
stripped = line.strip()
if stripped.startswith('#') and is_chinese(stripped):
comment_mark = '#'
comment_text = stripped[1:].strip()
if comment_text:
translated = translate_text(comment_text)
f_out.write(f"{leading_space}{comment_mark} {translated}\n")
else:
f_out.write(line)
elif any(cmd in stripped for cmd in ['echo', 'read', 'send_stats']) and is_chinese(stripped):
translated_line = translate_line_preserving_variables(line)
f_out.write(translated_line)
else:
f_out.write(line)
print("\nTranslation completed.")
print(f"Original file size: {os.path.getsize(input_file)} bytes")
print(f"Translated file size: {os.path.getsize(output_file)} bytes")
if __name__ == "__main__":
input_file = 'kejilion.sh'
output_file = 'kejilion_fa.sh'
translate_file(input_file, output_file)

22252
jp/kejilion.sh Normal file

File diff suppressed because it is too large Load Diff

76
jp/to-jp.py Normal file
View File

@@ -0,0 +1,76 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from deep_translator import GoogleTranslator
import re
import os
def is_chinese(text):
return bool(re.search(r'[\u4e00-\u9fff]', text))
def translate_text(text):
try:
return GoogleTranslator(source='auto', target='ja').translate(text)
except Exception as e:
print(f"\nTranslation error: {e}")
return text
def translate_line_preserving_variables(line):
"""
Translate only Chinese parts in echo/read/send_stats commands, excluding shell variables
"""
# Match double or single quoted strings
def repl(match):
full_string = match.group(0)
quote = full_string[0]
content = full_string[1:-1]
# Split by variable expressions
parts = re.split(r'(\$\{?\w+\}?)', content)
translated_parts = [
translate_text(p) if is_chinese(p) else p
for p in parts
]
return quote + ''.join(translated_parts) + quote
return re.sub(r'(?:\'[^\']*\'|"[^"]*")', repl, line)
def translate_file(input_file, output_file):
total_lines = sum(1 for _ in open(input_file, 'r', encoding='utf-8'))
processed_lines = 0
with open(input_file, 'r', encoding='utf-8') as f_in, \
open(output_file, 'w', encoding='utf-8') as f_out:
for line in f_in:
processed_lines += 1
progress = processed_lines / total_lines * 100
print(f"\rProcessing: {progress:.1f}% ({processed_lines}/{total_lines})", end='')
leading_space = re.match(r'^(\s*)', line).group(1)
stripped = line.strip()
if stripped.startswith('#') and is_chinese(stripped):
comment_mark = '#'
comment_text = stripped[1:].strip()
if comment_text:
translated = translate_text(comment_text)
f_out.write(f"{leading_space}{comment_mark} {translated}\n")
else:
f_out.write(line)
elif any(cmd in stripped for cmd in ['echo', 'read', 'send_stats']) and is_chinese(stripped):
translated_line = translate_line_preserving_variables(line)
f_out.write(translated_line)
else:
f_out.write(line)
print("\nTranslation completed.")
print(f"Original file size: {os.path.getsize(input_file)} bytes")
print(f"Translated file size: {os.path.getsize(output_file)} bytes")
if __name__ == "__main__":
input_file = 'kejilion.sh'
output_file = 'kejilion_jp.sh'
translate_file(input_file, output_file)

22252
kejilion.sh Normal file

File diff suppressed because it is too large Load Diff

1514
kejilion_sh_log.txt Normal file

File diff suppressed because it is too large Load Diff

22252
kr/kejilion.sh Normal file

File diff suppressed because it is too large Load Diff

76
kr/to-kr.py Normal file
View File

@@ -0,0 +1,76 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from deep_translator import GoogleTranslator
import re
import os
def is_chinese(text):
return bool(re.search(r'[\u4e00-\u9fff]', text))
def translate_text(text):
try:
return GoogleTranslator(source='zh-CN', target='ko').translate(text)
except Exception as e:
print(f"\nTranslation error: {e}")
return text
def translate_line_preserving_variables(line):
"""
Translate only Chinese parts in echo/read/send_stats commands, excluding shell variables
"""
# Match double or single quoted strings
def repl(match):
full_string = match.group(0)
quote = full_string[0]
content = full_string[1:-1]
# Split by variable expressions
parts = re.split(r'(\$\{?\w+\}?)', content)
translated_parts = [
translate_text(p) if is_chinese(p) else p
for p in parts
]
return quote + ''.join(translated_parts) + quote
return re.sub(r'(?:\'[^\']*\'|"[^"]*")', repl, line)
def translate_file(input_file, output_file):
total_lines = sum(1 for _ in open(input_file, 'r', encoding='utf-8'))
processed_lines = 0
with open(input_file, 'r', encoding='utf-8') as f_in, \
open(output_file, 'w', encoding='utf-8') as f_out:
for line in f_in:
processed_lines += 1
progress = processed_lines / total_lines * 100
print(f"\rProcessing: {progress:.1f}% ({processed_lines}/{total_lines})", end='')
leading_space = re.match(r'^(\s*)', line).group(1)
stripped = line.strip()
if stripped.startswith('#') and is_chinese(stripped):
comment_mark = '#'
comment_text = stripped[1:].strip()
if comment_text:
translated = translate_text(comment_text)
f_out.write(f"{leading_space}{comment_mark} {translated}\n")
else:
f_out.write(line)
elif any(cmd in stripped for cmd in ['echo', 'read', 'send_stats']) and is_chinese(stripped):
translated_line = translate_line_preserving_variables(line)
f_out.write(translated_line)
else:
f_out.write(line)
print("\nTranslation completed.")
print(f"Original file size: {os.path.getsize(input_file)} bytes")
print(f"Translated file size: {os.path.getsize(output_file)} bytes")
if __name__ == "__main__":
input_file = 'kejilion.sh'
output_file = 'kejilion_kr.sh'
translate_file(input_file, output_file)

59
ldnmp.sh Normal file
View File

@@ -0,0 +1,59 @@
#!/bin/bash
# 获取用户输入,用于替换 docker-compose.yml 文件中的占位符
read -p "请输入 数据库ROOT密码" dbrootpasswd
read -p "请输入 数据库用户名:" dbuse
read -p "请输入 数据库用户密码:" dbusepasswd
# 更新并安装必要的软件包
DEBIAN_FRONTEND=noninteractive apt update -y
DEBIAN_FRONTEND=noninteractive apt full-upgrade -y
apt install -y curl wget sudo socat unzip tar htop
# 安装 Docker
curl -fsSL https://get.docker.com | sh
# 安装 Docker Compose
curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose && chmod +x /usr/local/bin/docker-compose
# 创建必要的目录和文件
cd /home && mkdir -p web/html web/mysql web/certs web/conf.d web/redis && touch web/docker-compose.yml
# 下载 docker-compose.yml 文件并进行替换
wget -O /home/web/docker-compose.yml https://raw.githubusercontent.com/kejilion/docker/main/LNMP-docker-compose-4.yml
# 在 docker-compose.yml 文件中进行替换
sed -i "s/webroot/$dbrootpasswd/g" /home/web/docker-compose.yml
sed -i "s/kejilionYYDS/$dbusepasswd/g" /home/web/docker-compose.yml
sed -i "s/kejilion/$dbuse/g" /home/web/docker-compose.yml
iptables -P INPUT ACCEPT
iptables -P FORWARD ACCEPT
iptables -P OUTPUT ACCEPT
iptables -F
cd /home/web && docker-compose up -d
docker exec php apt update &&
docker exec php apt install -y libmariadb-dev-compat libmariadb-dev libzip-dev libmagickwand-dev imagemagick &&
docker exec php docker-php-ext-install mysqli pdo_mysql zip exif gd intl bcmath opcache &&
docker exec php pecl install imagick &&
docker exec php sh -c 'echo "extension=imagick.so" > /usr/local/etc/php/conf.d/imagick.ini' &&
docker exec php pecl install redis &&
docker exec php sh -c 'echo "extension=redis.so" > /usr/local/etc/php/conf.d/docker-php-ext-redis.ini' &&
docker exec php sh -c 'echo "upload_max_filesize=50M \n post_max_size=50M" > /usr/local/etc/php/conf.d/uploads.ini' &&
docker exec php sh -c 'echo "memory_limit=256M" > /usr/local/etc/php/conf.d/memory.ini'
docker exec php74 apt update &&
docker exec php74 apt install -y libmariadb-dev-compat libmariadb-dev libzip-dev libmagickwand-dev imagemagick &&
docker exec php74 docker-php-ext-install mysqli pdo_mysql zip gd intl bcmath opcache &&
docker exec php74 pecl install imagick &&
docker exec php74 sh -c 'echo "extension=imagick.so" > /usr/local/etc/php/conf.d/imagick.ini' &&
docker exec php74 pecl install redis &&
docker exec php74 sh -c 'echo "extension=redis.so" > /usr/local/etc/php/conf.d/docker-php-ext-redis.ini' &&
docker exec php74 sh -c 'echo "upload_max_filesize=50M \n post_max_size=50M" > /usr/local/etc/php/conf.d/uploads.ini' &&
docker exec php74 sh -c 'echo "memory_limit=256M" > /usr/local/etc/php/conf.d/memory.ini'

442
mc.sh Normal file
View File

@@ -0,0 +1,442 @@
#!/bin/bash
ln -sf ~/mc.sh /usr/local/bin/mcs
ip_address() {
# 检测 IPv4 地址
ipv4_address=$(curl -s --connect-timeout 5 ipv4.ip.sb 2>/dev/null || echo "")
# 检测 IPv6 地址
ipv6_address=$(curl -s --connect-timeout 5 ipv6.ip.sb 2>/dev/null || echo "")
# 设置显示变量
if [ -n "$ipv4_address" ] && [ -n "$ipv6_address" ]; then
ip_display="\033[93m IPv4: $ipv4_address:25565 IPv6: $ipv6_address:25565 \033[0m"
elif [ -n "$ipv4_address" ]; then
ip_display="\033[93m IPv4: $ipv4_address:25565 \033[0m"
elif [ -n "$ipv6_address" ]; then
ip_display="\033[93m IPv6: $ipv6_address:25565 \033[0m"
else
ip_display="\033[93m 无法获取IP地址 \033[0m"
fi
}
install() {
if [ $# -eq 0 ]; then
echo "未提供软件包参数!"
return 1
fi
for package in "$@"; do
if ! command -v "$package" &>/dev/null; then
if command -v apt &>/dev/null; then
apt update -y && apt install -y "$package"
elif command -v yum &>/dev/null; then
yum -y update && yum -y install "$package"
elif command -v apk &>/dev/null; then
apk update && apk add "$package"
else
echo "未知的包管理器!"
return 1
fi
fi
done
return 0
}
remove() {
if [ $# -eq 0 ]; then
echo "未提供软件包参数!"
return 1
fi
for package in "$@"; do
if command -v apt &>/dev/null; then
apt purge -y "$package"
elif command -v yum &>/dev/null; then
yum remove -y "$package"
elif command -v apk &>/dev/null; then
apk del "$package"
else
echo "未知的包管理器!"
return 1
fi
done
return 0
}
break_end() {
echo -e "\033[0;32m操作完成\033[0m"
echo "按任意键继续..."
read -n 1 -s -r -p ""
echo ""
clear
}
mc() {
p
exit
}
install_add_docker() {
if [ -f "/etc/alpine-release" ]; then
apk update
apk add docker docker-compose
rc-update add docker default
service docker start
else
curl -fsSL https://get.docker.com | sh && ln -s /usr/libexec/docker/cli-plugins/docker-compose /usr/local/bin
systemctl start docker
systemctl enable docker
fi
}
install_docker() {
if ! command -v docker &>/dev/null; then
install_add_docker
else
echo "Docker 已经安装"
fi
}
mc_start() {
ip_address
docker start mcserver > /dev/null 2>&1
echo -e "\033[0;32mMinecraft服务启动啦\033[0m"
echo -e "\033[0;32m游戏下载地址: https://www.xbox.com/zh-cn/games/store/minecraft-java-bedrock-edition-for-pc/9nxp44l49shj\033[0m"
echo -e "\033[0;32m进入游戏连接:$ip_display\033[0;32m开始冒险吧\033[0m"
}
mc_backup() {
cd ~
curl -sS -O https://kejilion.pro/mc_backup.sh && chmod +x mc_backup.sh
}
mc_install_status() {
CONTAINER_NAME="mcserver"
# 检查容器是否已安装
if [ "$(docker ps -a -q -f name=$CONTAINER_NAME 2>/dev/null)" ]; then
container_status="\e[32mMinecraft服务已安装\e[0m" # 绿色
else
container_status="\e[90mMinecraft服务未安装\e[0m" # 灰色
fi
ip_address
# 检查 Docker 容器是否正在运行
if docker ps --format "table {{.Names}}" | grep -q "^$CONTAINER_NAME$"; then
tmux_status="\e[32m已开服:$ip_display\e[0m" # 绿色
else
tmux_status="\e[90m未开服\e[0m" # 灰色
fi
}
while true; do
clear
mc_install_status
echo -e "\033[92m███╗ ███╗██╗███╗ ██╗███████╗ ██████╗██████╗ █████╗ ███████╗████████╗\033[0m"
echo -e "\033[92m████╗ ████║██║████╗ ██║██╔════╝██╔════╝██╔══██╗██╔══██╗██╔════╝╚══██╔══╝\033[0m"
echo -e "\033[92m██╔████╔██║██║██╔██╗ ██║█████╗ ██║ ██████╔╝███████║█████╗ ██║ \033[0m"
echo -e "\033[92m██║╚██╔╝██║██║██║╚██╗██║██╔══╝ ██║ ██╔══██╗██╔══██║██╔══╝ ██║ \033[0m"
echo -e "\033[92m██║ ╚═╝ ██║██║██║ ╚████║███████╗╚██████╗██║ ██║██║ ██║██║ ██║ \033[0m"
echo -e "\033[92m╚═╝ ╚═╝╚═╝╚═╝ ╚═══╝╚══════╝ ╚═════╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ \033[0m"
echo -e "\033[92mMinecraft开服一键脚本工具v1.0.1 by AkarinLiu\033[0m"
echo -e "\033[92m-输入\033[92mmcs\033[92m可快速启动此脚本-\033[0m"
echo -e "$container_status $tmux_status"
echo "------------------------"
echo "1. 安装Minecraft服务"
echo "2. 开启Minecraft服务"
echo "3. 关闭Minecraft服务"
echo "4. 重启Minecraft服务"
echo "------------------------"
echo "5. 查看服务器状态"
echo "6. 设置虚拟内存"
echo "------------------------"
echo "7. 导出游戏存档"
echo "8. 导入游戏存档"
echo "9. 定时备份游戏存档"
echo "------------------------"
echo "10. 修改游戏配置"
echo "o. 添加管理员权限"
echo "p. 删除管理员权限"
echo "------------------------"
echo "11. 更新Minecraft服务"
echo "12. 卸载Minecraft服务"
echo "------------------------"
echo "k. 科技lion脚本工具箱"
echo "------------------------"
echo "00. 脚本更新"
echo "------------------------"
echo "0. 退出脚本"
echo "------------------------"
read -p "请输入你的选择: " choice
case $choice in
1)
clear
install_docker
docker run -d --name mcserver -p 25565:25565/tcp --restart=always -e EULA=true -e CREATE_CONSOLE_IN_PIPE=true -v mcserver:/data:rw itzg/minecraft-server
clear
mc_start
;;
2)
clear
docker start $CONTAINER_NAME > /dev/null 2>&1
mc_start
;;
3)
clear
docker stop $CONTAINER_NAME > /dev/null 2>&1
echo -e "\033[0;32mMinecraft服务已关闭\033[0m"
;;
4)
clear
docker restart $CONTAINER_NAME > /dev/null 2>&1
mc_start
;;
5)
clear
install btop
clear
btop
;;
6)
clear
swap_used=$(free -m | awk 'NR==3{print $3}')
swap_total=$(free -m | awk 'NR==3{print $2}')
if [ "$swap_total" -eq 0 ]; then
swap_percentage=0
else
swap_percentage=$((swap_used * 100 / swap_total))
fi
swap_info="${swap_used}MB/${swap_total}MB (${swap_percentage}%)"
echo "当前虚拟内存: $swap_info"
read -p "是否调整大小?(Y/N): " choice
case "$choice" in
[Yy])
# 输入新的虚拟内存大小
read -p "请输入虚拟内存大小MB: " new_swap
# 获取当前系统中所有的 swap 分区
swap_partitions=$(grep -E '^/dev/' /proc/swaps | awk '{print $1}')
# 遍历并删除所有的 swap 分区
for partition in $swap_partitions; do
swapoff "$partition"
wipefs -a "$partition" # 清除文件系统标识符
mkswap -f "$partition"
echo "已删除并重新创建 swap 分区: $partition"
done
# 确保 /swapfile 不再被使用
swapoff /swapfile
# 删除旧的 /swapfile
rm -f /swapfile
# 创建新的 swap 分区
dd if=/dev/zero of=/swapfile bs=1M count=$new_swap
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile
if [ -f /etc/alpine-release ]; then
echo "/swapfile swap swap defaults 0 0" >> /etc/fstab
echo "nohup swapon /swapfile" >> /etc/local.d/swap.start
chmod +x /etc/local.d/swap.start
rc-update add local
else
echo "/swapfile swap swap defaults 0 0" >> /etc/fstab
fi
echo "虚拟内存大小已调整为${new_swap}MB"
;;
[Nn])
echo "已取消"
;;
*)
echo "无效的选择,请输入 Y 或 N。"
;;
esac
;;
7)
clear
mkdir -p /home/game
docker cp $CONTAINER_NAME:/data/world /home/game/mc/ > /dev/null 2>&1
cd /home/game && tar czvf mcsave_$(date +"%Y%m%d%H%M%S").tar.gz mc > /dev/null 2>&1
rm -rf /home/game/mc/
echo -e "\033[0;32m游戏存档已导出存放在: /home/game/mc/\033[0m"
;;
8)
clear
docker stop mcserver > /dev/null 2>&1
docker exec -it mcserver bash -c "rm -rf /data/world/*"
cd /home/game/ && ls -t /home/game/mc/*.tar.gz | head -1 | xargs -I {} tar -xzf {}
docker cp /home/game/mc/world/* mcserver:/data/world
rm -rf /home/game/mc/
echo -e "\033[0;32m游戏存档已导入\033[0m"
docker restart mcserver > /dev/null 2>&1
mc_start
;;
9)
clear
echo "Minecraft游戏存档定时备份"
echo "------------------------"
echo "1. 每周备份 2. 每天备份 3. 每小时备份"
echo "------------------------"
read -p "请输入你的选择: " dingshi
case $dingshi in
1)
mc_backup
(crontab -l ; echo "0 0 * * 1 ./mc_backup.sh") | crontab - > /dev/null 2>&1
echo "每周一备份,已设置"
;;
2)
mc_backup
(crontab -l ; echo "0 3 * * * ./mc_backup.sh") | crontab - > /dev/null 2>&1
echo "每天凌晨3点备份已设置"
;;
3)
mc_backup
(crontab -l ; echo "0 * * * * ./mc_backup.sh") | crontab - > /dev/null 2>&1
echo "每小时整点备份,已设置"
;;
*)
echo "已取消"
;;
esac
;;
10)
clear
echo "配置游戏参数"
echo "------------------------"
read -p "设置游戏难度: 0.和平 1. 简单 2. 普通 3. 困难):" Difficulty
case $Difficulty in
0)
docker exec --user 1000 mcserver mc-send-to-console difficulty peaceful
;;
1)
docker exec --user 1000 mcserver mc-send-to-console difficulty easy
;;
2)
docker exec --user 1000 mcserver mc-send-to-console difficulty normal
;;
3)
docker exec --user 1000 mcserver mc-send-to-console difficulty hard
;;
*)
echo "-默认设置为普通难度"
docker exec --user 1000 mcserver mc-send-to-console difficulty normal
;;
esac
read -p "死亡后掉落设置: 1. 掉落 2. 不掉落):" DeathPenalty
case $DeathPenalty in
1)
docker exec --user 1000 mcserver mc-send-to-console gamerule KeepInventoy false
;;
2)
docker exec --user 1000 mcserver mc-send-to-console gamerule KeepInventoy true
;;
*)
docker exec --user 1000 mcserver mc-send-to-console gamerule KeepInventoy false
echo "-默认设置为掉落"
;;
esac
read -p "设置pvp模式: 1. 开启 2. 关闭):" mc_pvp
case $mc_pvp in
1)
docker exec --user 1000 mcserver mc-send-to-console gamerule pvp true
;;
2)
docker exec --user 1000 mcserver mc-send-to-console gamerule pvp false
;;
*)
docker exec --user 1000 mcserver mc-send-to-console gamerule pvp false
echo "-默认关闭pvp模式"
;;
esac
# 更新配置
echo -e "\033[0;32m游戏配置已更改\033[0m"
;;
11)
clear
docker stop mcserver > /dev/null 2>&1
docker restart mcserver > /dev/null 2>&1
docker exec -it mcserver bash -c "/home/steam/mcserver/mcserver.sh +login anonymous +app_update 2394010 validate +quit"
clear
echo -e "\033[0;32mMinecraft已更新\033[0m"
mc_start
;;
12)
clear
docker rm -f mcserver
docker rmi -f itzg/minecraft-server
;;
o)
read -p "请输入 Minecraft Java 版档案名称:" mc_op
docker exec --user 1000 mcserver mc-send-to-console op $mc_op
;;
p)
read -p "请输入 Minecraft Java 版档案名称:" mc_deop
docker exec --user 1000 mcserver mc-send-to-console deop $mc_deop
;;
k)
cd ~
curl -sS -O https://kejilion.pro/kejilion.sh && chmod +x kejilion.sh && ./kejilion.sh
exit
;;
00)
cd ~
curl -sS -O https://kejilion.pro/mc_log.sh && chmod +x mc_log.sh && ./mc_log.sh
rm mc_log.sh
echo ""
curl -sS -O https://kejilion.pro/mc.sh && chmod +x mc.sh
echo "脚本已更新到最新版本!"
break_end
mc
;;
0)
clear
exit
;;
*)
echo "无效的输入!"
;;
esac
break_end
done

7
mc_backup.sh Normal file
View File

@@ -0,0 +1,7 @@
#!/bin/bash
clear
mkdir -p /home/game
docker cp mcserver:/data /home/game/mc
cd /home/game/mc && tar czvf mc_$(date +"%Y%m%d%H%M%S").tar.gz mc
rm -rf /home/game/mc/
echo -e "\033[0;32m游戏存档已导出存放在: /home/game/mc/\033[0m"

7
mc_log.sh Normal file
View File

@@ -0,0 +1,7 @@
clear
echo "脚本更新日志"
echo "------------------------"
echo "2025-11-16 v1.0.1"
echo "Minecraft 开服脚本魔改自 幻兽帕鲁开服脚本"
echo "2025-11-17 v1.0.1"
ech "对 Minecraft 的难度等细节进行了适配"

608
network-optimize.sh Normal file
View File

@@ -0,0 +1,608 @@
#!/bin/bash
# ============================================================================
# 自动网络性能检测与内核优化脚本
# 适配Debian/Ubuntu, CentOS/RHEL/Alma/Rocky, Alpine, Arch, Fedora
# 作者kejilion
# ============================================================================
# ======================== 颜色定义 ========================
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
WHITE='\033[1;37m'
NC='\033[0m'
# ======================== 工具函数 ========================
info() { echo -e "${CYAN}[检测]${NC} $*"; }
ok() { echo -e "${GREEN}[✓]${NC} $*"; }
warn() { echo -e "${YELLOW}[!]${NC} $*"; }
fail() { echo -e "${RED}[✗]${NC} $*"; }
title() { echo -e "\n${WHITE}━━━ $* ━━━${NC}"; }
# 获取内核主版本号用于兼容性判断
kernel_version() {
local ver
ver=$(uname -r | grep -oP '^\d+\.\d+')
echo "$ver"
}
# 比较版本号: version_ge 5.4 4.9 => true
version_ge() {
printf '%s\n%s' "$2" "$1" | sort -V -C
}
# 安全读取 sysctl 值
sysctl_get() {
sysctl -n "$1" 2>/dev/null || echo "N/A"
}
# 获取发行版
detect_distro() {
if [ -f /etc/os-release ]; then
. /etc/os-release
case "$ID" in
debian|ubuntu|linuxmint|pop|kali) echo "debian" ;;
centos|rhel|almalinux|rocky|ol|amzn|fedora) echo "rhel" ;;
alpine) echo "alpine" ;;
arch|manjaro|endeavouros) echo "arch" ;;
opensuse*|sles) echo "suse" ;;
*) echo "unknown" ;;
esac
elif [ -f /etc/debian_version ]; then
echo "debian"
elif [ -f /etc/redhat-release ]; then
echo "rhel"
else
echo "unknown"
fi
}
# ======================== 网络检测 ========================
# 检测网卡速率Mbps
detect_link_speed() {
local speed=0
local iface
# 获取默认路由网卡
iface=$(ip route show default 2>/dev/null | awk '/default/{print $5}' | head -1)
[ -z "$iface" ] && iface=$(ls /sys/class/net/ | grep -v lo | head -1)
if [ -n "$iface" ]; then
# 尝试 ethtool
if command -v ethtool &>/dev/null; then
speed=$(ethtool "$iface" 2>/dev/null | awk '/Speed:/{gsub(/[^0-9]/,"",$2); print $2}')
fi
# 尝试 sysfs
if [ -z "$speed" ] || [ "$speed" = "0" ]; then
speed=$(cat "/sys/class/net/$iface/speed" 2>/dev/null)
fi
fi
# VPS 虚拟网卡可能读不到速率,默认 1000
[ -z "$speed" ] || [ "$speed" -le 0 ] 2>/dev/null && speed=1000
echo "$speed"
}
# 检测网络延迟msping 多个公共 DNS
detect_latency() {
local total=0 count=0 avg
local targets=("8.8.8.8" "1.1.1.1" "9.9.9.9" "208.67.222.222")
for t in "${targets[@]}"; do
local rtt
rtt=$(ping -c 3 -W 2 "$t" 2>/dev/null | awk -F'/' '/avg/{print $5}')
if [ -n "$rtt" ]; then
total=$(awk "BEGIN{print $total + $rtt}")
count=$((count + 1))
fi
done
if [ "$count" -gt 0 ]; then
avg=$(awk "BEGIN{printf \"%.1f\", $total / $count}")
else
avg="50"
fi
echo "$avg"
}
# 检测丢包率(%
detect_packet_loss() {
local loss
loss=$(ping -c 10 -W 2 8.8.8.8 2>/dev/null | awk -F',' '/loss/{gsub(/%/,"",$3); gsub(/ packet/,"",$3); print $3+0}')
[ -z "$loss" ] && loss=0
echo "$loss"
}
# 获取当前 TCP 拥塞算法
detect_congestion() {
sysctl_get net.ipv4.tcp_congestion_control
}
# 检测总物理内存MB
detect_memory_mb() {
awk '/MemTotal/{printf "%d", $2/1024}' /proc/meminfo
}
# ======================== 带宽分级 ========================
# 返回: low / medium / high / ultra
classify_bandwidth() {
local speed=$1
if [ "$speed" -lt 100 ]; then
echo "low"
elif [ "$speed" -lt 1000 ]; then
echo "medium"
elif [ "$speed" -lt 10000 ]; then
echo "high"
else
echo "ultra"
fi
}
# ======================== 核心优化函数 ========================
auto_optimize_network() {
local CONF="/etc/sysctl.d/99-network-optimize.conf"
local BACKUP="/etc/sysctl.d/99-network-optimize.conf.bak.$(date +%s)"
local KVER
KVER=$(kernel_version)
local DISTRO
DISTRO=$(detect_distro)
echo -e "${WHITE}"
echo "╔══════════════════════════════════════════════════════════════╗"
echo "║ 自动网络性能检测与内核优化 ║"
echo "╚══════════════════════════════════════════════════════════════╝"
echo -e "${NC}"
# ── 权限检查 ──
if [ "$(id -u)" -ne 0 ]; then
fail "请以 root 权限运行"
return 1
fi
# ── 环境信息 ──
title "环境信息"
info "发行版: $(. /etc/os-release 2>/dev/null && echo "$PRETTY_NAME" || echo "$DISTRO")"
info "内核版本: $(uname -r)"
info "架构: $(uname -m)"
info "内存: $(detect_memory_mb) MB"
# ── 网络检测 ──
title "网络性能检测"
local SPEED LATENCY LOSS CONGESTION MEM_MB BW_CLASS
info "检测网卡速率..."
SPEED=$(detect_link_speed)
ok "链路速率: ${SPEED} Mbps"
info "检测网络延迟..."
LATENCY=$(detect_latency)
ok "平均延迟: ${LATENCY} ms"
info "检测丢包率..."
LOSS=$(detect_packet_loss)
ok "丢包率: ${LOSS}%"
CONGESTION=$(detect_congestion)
ok "当前拥塞算法: ${CONGESTION}"
MEM_MB=$(detect_memory_mb)
BW_CLASS=$(classify_bandwidth "$SPEED")
title "检测结论"
case "$BW_CLASS" in
low) info "带宽等级: ${YELLOW}低带宽 (<100M)${NC} → 保守优化" ;;
medium) info "带宽等级: ${CYAN}中带宽 (100M-1G)${NC} → 标准优化" ;;
high) info "带宽等级: ${GREEN}高带宽 (1G-10G)${NC} → 激进优化" ;;
ultra) info "带宽等级: ${WHITE}超高带宽 (>10G)${NC} → 极致优化" ;;
esac
local HIGH_LATENCY=false
if [ "$(awk "BEGIN{print ($LATENCY > 100)}")" = "1" ]; then
HIGH_LATENCY=true
warn "高延迟网络 (${LATENCY}ms > 100ms),启用额外优化"
fi
# ── 备份现有配置 ──
title "备份与准备"
if [ -f "$CONF" ]; then
cp "$CONF" "$BACKUP"
ok "已备份: $BACKUP"
else
ok "首次优化,无需备份"
fi
# ── 根据带宽等级设定参数 ──
local TCP_RMEM_MAX TCP_WMEM_MAX TCP_RMEM TCP_WMEM
local NETDEV_BUDGET NETDEV_BUDGET_USECS
local SOMAXCONN NETDEV_BACKLOG
local CONNTRACK_MAX
case "$BW_CLASS" in
low)
TCP_RMEM="4096 65536 2097152"
TCP_WMEM="4096 65536 2097152"
TCP_RMEM_MAX=2097152
TCP_WMEM_MAX=2097152
SOMAXCONN=1024
NETDEV_BACKLOG=1000
NETDEV_BUDGET=300
NETDEV_BUDGET_USECS=2000
CONNTRACK_MAX=65536
;;
medium)
TCP_RMEM="4096 131072 16777216"
TCP_WMEM="4096 131072 16777216"
TCP_RMEM_MAX=16777216
TCP_WMEM_MAX=16777216
SOMAXCONN=4096
NETDEV_BACKLOG=5000
NETDEV_BUDGET=600
NETDEV_BUDGET_USECS=4000
CONNTRACK_MAX=262144
;;
high)
TCP_RMEM="4096 262144 67108864"
TCP_WMEM="4096 262144 67108864"
TCP_RMEM_MAX=67108864
TCP_WMEM_MAX=67108864
SOMAXCONN=8192
NETDEV_BACKLOG=10000
NETDEV_BUDGET=1200
NETDEV_BUDGET_USECS=6000
CONNTRACK_MAX=524288
;;
ultra)
TCP_RMEM="4096 524288 134217728"
TCP_WMEM="4096 524288 134217728"
TCP_RMEM_MAX=134217728
TCP_WMEM_MAX=134217728
SOMAXCONN=16384
NETDEV_BACKLOG=20000
NETDEV_BUDGET=2400
NETDEV_BUDGET_USECS=8000
CONNTRACK_MAX=1048576
;;
esac
# 高延迟追加:加大初始窗口
local HIGH_LAT_EXTRA=""
if $HIGH_LATENCY; then
# 高延迟网络使用更大的初始拥塞窗口提升吞吐
HIGH_LAT_EXTRA="
# ── 高延迟网络额外优化 ──
net.ipv4.tcp_slow_start_after_idle = 0
net.ipv4.tcp_notsent_lowat = 16384"
fi
# 内存不足时缩小缓冲区
if [ "$MEM_MB" -lt 512 ]; then
warn "内存不足 512MB缩小 TCP 缓冲区"
TCP_RMEM="4096 32768 1048576"
TCP_WMEM="4096 32768 1048576"
TCP_RMEM_MAX=1048576
TCP_WMEM_MAX=1048576
CONNTRACK_MAX=32768
fi
# ── 根据内存大小设定 VM 参数 ──
local SWAPPINESS MIN_FREE_KB
if [ "$MEM_MB" -ge 16384 ]; then
# 16G+ 大内存:激进
SWAPPINESS=5
MIN_FREE_KB=131072
elif [ "$MEM_MB" -ge 4096 ]; then
# 4G-16G标准
SWAPPINESS=10
MIN_FREE_KB=65536
elif [ "$MEM_MB" -ge 1024 ]; then
# 1G-4G保守
SWAPPINESS=20
MIN_FREE_KB=32768
else
# <1G极度保守
SWAPPINESS=30
MIN_FREE_KB=16384
fi
# ── BBR 检测与加载 ──
title "TCP 拥塞算法优化"
local USE_BBR=false
local TARGET_CC="cubic"
if version_ge "$KVER" "4.9"; then
# 内核 >= 4.9 支持 BBR
if ! lsmod 2>/dev/null | grep -q tcp_bbr; then
modprobe tcp_bbr 2>/dev/null
if lsmod 2>/dev/null | grep -q tcp_bbr; then
ok "BBR 模块已加载"
else
warn "BBR 模块加载失败,使用 cubic"
fi
fi
if sysctl net.ipv4.tcp_available_congestion_control 2>/dev/null | grep -q bbr; then
USE_BBR=true
TARGET_CC="bbr"
ok "拥塞算法: cubic → ${GREEN}BBR${NC}"
fi
else
warn "内核 $KVER < 4.9,不支持 BBR保持 cubic"
fi
# BBR 配合 fq 队列调度
local QDISC="fq"
if ! $USE_BBR; then
QDISC="fq_codel"
fi
# ── 生成优化配置 ──
title "写入优化配置"
cat > "$CONF" << SYSCTL
# ============================================================================
# 自动网络优化配置
# 生成时间: $(date '+%Y-%m-%d %H:%M:%S')
# 带宽等级: $BW_CLASS (${SPEED}Mbps)
# 平均延迟: ${LATENCY}ms | 丢包率: ${LOSS}%
# 内核: $(uname -r) | 发行版: $DISTRO | 内存: ${MEM_MB}MB
# ============================================================================
# ── TCP 拥塞控制 ──
net.core.default_qdisc = $QDISC
net.ipv4.tcp_congestion_control = $TARGET_CC
# ── TCP 缓冲区 ──
net.core.rmem_max = $TCP_RMEM_MAX
net.core.wmem_max = $TCP_WMEM_MAX
net.core.rmem_default = $(echo "$TCP_RMEM" | awk '{print $2}')
net.core.wmem_default = $(echo "$TCP_WMEM" | awk '{print $2}')
net.ipv4.tcp_rmem = $TCP_RMEM
net.ipv4.tcp_wmem = $TCP_WMEM
net.ipv4.udp_rmem_min = 8192
net.ipv4.udp_wmem_min = 8192
# ── 连接队列 ──
net.core.somaxconn = $SOMAXCONN
net.core.netdev_max_backlog = $NETDEV_BACKLOG
net.core.netdev_budget = $NETDEV_BUDGET
$(sysctl -n net.core.netdev_budget_usecs &>/dev/null && echo "net.core.netdev_budget_usecs = $NETDEV_BUDGET_USECS" || echo "# netdev_budget_usecs 不支持,跳过")
# ── TCP 连接优化 ──
net.ipv4.tcp_fastopen = 3
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 15
net.ipv4.tcp_keepalive_time = 300
net.ipv4.tcp_keepalive_intvl = 30
net.ipv4.tcp_keepalive_probes = 5
net.ipv4.tcp_max_syn_backlog = $SOMAXCONN
net.ipv4.tcp_max_tw_buckets = 65536
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_synack_retries = 2
net.ipv4.tcp_syn_retries = 3
net.ipv4.tcp_mtu_probing = 1
net.ipv4.tcp_sack = 1
net.ipv4.tcp_timestamps = 1
net.ipv4.tcp_window_scaling = 1
# ── 内存与端口 ──
net.ipv4.ip_local_port_range = 1024 65535
net.ipv4.tcp_mem = $((MEM_MB * 1024 / 8)) $((MEM_MB * 1024 / 4)) $((MEM_MB * 1024 / 2))
net.ipv4.tcp_max_orphans = 32768
# ── 安全与防护 ──
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
net.ipv4.icmp_echo_ignore_broadcasts = 1
net.ipv4.icmp_ignore_bogus_error_responses = 1
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0
# ── IPv6 优化 ──
net.ipv6.conf.all.accept_redirects = 0
net.ipv6.conf.default.accept_redirects = 0
# ── 虚拟内存优化 ──
vm.swappiness = $SWAPPINESS
vm.dirty_ratio = 15
vm.dirty_background_ratio = 5
vm.overcommit_memory = 1
vm.min_free_kbytes = $MIN_FREE_KB
vm.vfs_cache_pressure = 50
# ── CPU/内核调度优化 ──
kernel.sched_autogroup_enabled = 0
$([ -f /proc/sys/kernel/numa_balancing ] && echo "kernel.numa_balancing = 0" || echo "# numa_balancing 不支持,跳过")
# ── 文件描述符 ──
fs.file-max = 1048576
fs.nr_open = 1048576
# ── 连接跟踪 ──
$(if [ -f /proc/sys/net/netfilter/nf_conntrack_max ]; then
echo "net.netfilter.nf_conntrack_max = $CONNTRACK_MAX"
echo "net.netfilter.nf_conntrack_tcp_timeout_established = 7200"
echo "net.netfilter.nf_conntrack_tcp_timeout_time_wait = 30"
echo "net.netfilter.nf_conntrack_tcp_timeout_close_wait = 15"
echo "net.netfilter.nf_conntrack_tcp_timeout_fin_wait = 15"
else
echo "# conntrack 未启用,跳过"
fi)
$HIGH_LAT_EXTRA
SYSCTL
ok "配置已写入: $CONF"
# ── 应用配置 ──
title "应用优化"
local apply_errors
apply_errors=$(sysctl -p "$CONF" 2>&1 | grep -i "error\|invalid\|cannot" || true)
if [ -n "$apply_errors" ]; then
warn "部分参数不支持(已跳过):"
echo "$apply_errors" | while read -r line; do
echo -e " ${YELLOW}$line${NC}"
done
fi
ok "sysctl 参数已应用"
# ── 禁用透明大页面(减少延迟抖动) ──
if [ -f /sys/kernel/mm/transparent_hugepage/enabled ]; then
echo never > /sys/kernel/mm/transparent_hugepage/enabled 2>/dev/null && \
ok "透明大页面已禁用" || warn "透明大页面禁用失败"
fi
# ── 设置文件描述符限制 ──
if ! grep -q "# network-optimize" /etc/security/limits.conf 2>/dev/null; then
cat >> /etc/security/limits.conf << 'LIMITS'
# network-optimize - 自动网络优化添加
* soft nofile 1048576
* hard nofile 1048576
root soft nofile 1048576
root hard nofile 1048576
LIMITS
ok "文件描述符限制已更新"
fi
# ── 持久化 BBR 模块 ──
if $USE_BBR; then
if [ ! -f /etc/modules-load.d/bbr.conf ]; then
echo "tcp_bbr" > /etc/modules-load.d/bbr.conf 2>/dev/null
ok "BBR 模块持久化"
fi
fi
# ── 优化摘要 ──
title "优化摘要"
echo -e "${WHITE}┌──────────────────────────────────────────────────┐${NC}"
printf "${WHITE}${NC} %-20s ${WHITE}${NC} %-25s ${WHITE}${NC}\n" "项目" "值"
echo -e "${WHITE}├──────────────────────────────────────────────────┤${NC}"
printf "${WHITE}${NC} %-20s ${WHITE}${NC} %-25s ${WHITE}${NC}\n" "带宽等级" "$BW_CLASS (${SPEED}Mbps)"
printf "${WHITE}${NC} %-20s ${WHITE}${NC} %-25s ${WHITE}${NC}\n" "拥塞算法" "$TARGET_CC"
printf "${WHITE}${NC} %-20s ${WHITE}${NC} %-25s ${WHITE}${NC}\n" "队列调度" "$QDISC"
printf "${WHITE}${NC} %-20s ${WHITE}${NC} %-25s ${WHITE}${NC}\n" "TCP 缓冲区(max)" "$(numfmt --to=iec $TCP_RMEM_MAX 2>/dev/null || echo ${TCP_RMEM_MAX})"
printf "${WHITE}${NC} %-20s ${WHITE}${NC} %-25s ${WHITE}${NC}\n" "连接队列" "$SOMAXCONN"
printf "${WHITE}${NC} %-20s ${WHITE}${NC} %-25s ${WHITE}${NC}\n" "网络延迟" "${LATENCY}ms"
printf "${WHITE}${NC} %-20s ${WHITE}${NC} %-25s ${WHITE}${NC}\n" "高延迟优化" "$(if $HIGH_LATENCY; then echo '已启用'; else echo '未需要'; fi)"
printf "${WHITE}${NC} %-20s ${WHITE}${NC} %-25s ${WHITE}${NC}\n" "配置文件" "$CONF"
echo -e "${WHITE}└──────────────────────────────────────────────────┘${NC}"
echo -e "\n${GREEN}网络优化完成!${NC}"
echo -e "回滚命令: ${CYAN}restore_network_defaults${NC}"
}
# ======================== 回滚函数 ========================
restore_network_defaults() {
local CONF="/etc/sysctl.d/99-network-optimize.conf"
title "回滚网络优化"
if [ "$(id -u)" -ne 0 ]; then
fail "请以 root 权限运行"
return 1
fi
# 查找最近的备份
local latest_bak
latest_bak=$(ls -t /etc/sysctl.d/99-network-optimize.conf.bak.* 2>/dev/null | head -1)
if [ -n "$latest_bak" ]; then
cp "$latest_bak" "$CONF"
sysctl -p "$CONF" 2>/dev/null
ok "已从备份恢复: $latest_bak"
elif [ -f "$CONF" ]; then
rm -f "$CONF"
sysctl --system 2>/dev/null
ok "已删除优化配置,恢复系统默认"
else
warn "没有优化配置需要回滚"
return 0
fi
# 清理 limits.conf 添加的部分
if grep -q "# network-optimize" /etc/security/limits.conf 2>/dev/null; then
sed -i '/# network-optimize/,+4d' /etc/security/limits.conf
ok "文件描述符限制已恢复"
fi
# 清理 BBR 持久化
rm -f /etc/modules-load.d/bbr.conf 2>/dev/null
ok "网络配置已回滚"
}
# ======================== 查看当前状态 ========================
show_network_status() {
title "当前网络内核参数"
local params=(
"net.ipv4.tcp_congestion_control"
"net.core.default_qdisc"
"net.core.rmem_max"
"net.core.wmem_max"
"net.ipv4.tcp_rmem"
"net.ipv4.tcp_wmem"
"net.core.somaxconn"
"net.core.netdev_max_backlog"
"net.ipv4.tcp_fastopen"
"net.ipv4.tcp_tw_reuse"
"net.ipv4.tcp_fin_timeout"
"net.ipv4.ip_local_port_range"
"net.ipv4.tcp_mtu_probing"
"fs.file-max"
)
for p in "${params[@]}"; do
local val
val=$(sysctl_get "$p")
printf " %-45s = %s\n" "$p" "$val"
done
if [ -f /etc/sysctl.d/99-network-optimize.conf ]; then
echo ""
ok "优化配置已安装: /etc/sysctl.d/99-network-optimize.conf"
else
echo ""
warn "未检测到优化配置"
fi
}
# ======================== 入口 ========================
# 用法:
# source network-optimize.sh
# auto_optimize_network # 自动检测并优化
# show_network_status # 查看当前状态
# restore_network_defaults # 回滚到默认
#
# 或直接运行:
# bash network-optimize.sh
# ========================
if [[ "${BASH_SOURCE[0]}" == "${0}" ]] || [[ -z "${BASH_SOURCE[0]}" ]]; then
# 支持参数: bash xxx.sh restore / bash xxx.sh status
# 支持环境变量: ACTION=restore curl ... | bash
_action="${1:-${ACTION:-optimize}}"
case "$_action" in
restore|rollback|回滚)
restore_network_defaults
;;
status|状态)
show_network_status
;;
*)
auto_optimize_network
;;
esac
fi

62
nginx.local Normal file
View File

@@ -0,0 +1,62 @@
[fail2ban-nginx-cc]
enabled = true
filter = fail2ban-nginx-cc
chain = DOCKER-USER
port = http,https
action = cloudflare
logpath = /home/web/log/nginx/access.log
maxretry = 3
bantime = 3600
findtime = 3600
ignoreip = 192.168.0.1/24
[nginx-http-auth]
enabled = true
chain = DOCKER-USER
mode = fallback
port = http,https
logpath = /home/web/log/nginx/error.log
[nginx-limit-req]
enabled = true
chain = DOCKER-USER
port = http,https
action = cloudflare
logpath = /home/web/log/nginx/error.log
[nginx-botsearch]
enabled = true
chain = DOCKER-USER
port = http,https
action = cloudflare
logpath = /home/web/log/nginx/error.log
[nginx-bad-request]
enabled = true
chain = DOCKER-USER
port = http,https
action = cloudflare
logpath = /home/web/log/nginx/access.log
[php-url-fopen]
enabled = true
chain = DOCKER-USER
port = http,https
action = cloudflare
logpath = /home/web/log/nginx/access.log

37
optimized_php.ini Normal file
View File

@@ -0,0 +1,37 @@
; security
display_errors = Off
error_reporting = E_ALL & ~E_NOTICE & ~E_WARNING & ~E_DEPRECATED
expose_php = Off
allow_url_fopen = Off
allow_url_include = Off
disable_functions = passthru,system,proc_open,popen,parse_ini_file,show_source
default_charset = "UTF-8"
session.cookie_httponly = 1
session.cookie_secure = 1
session.use_strict_mode = 1
session.use_only_cookies = 1
; Opcache配置
opcache.enable=1
opcache.enable_cli=1
opcache.memory_consumption=512
opcache.interned_strings_buffer=32
opcache.max_accelerated_files=100000
opcache.revalidate_freq=0
opcache.validate_timestamps=0
opcache.fast_shutdown=1
opcache.save_comments=1
opcache.file_update_protection=0
opcache.max_wasted_percentage=5
opcache.jit=tracing
opcache.jit_buffer_size=64M
; Realpath Cache配置
realpath_cache_size=4096k
realpath_cache_ttl=3600

7
pal_backup.sh Normal file
View File

@@ -0,0 +1,7 @@
#!/bin/bash
clear
mkdir -p /home/game
docker cp steamcmd:/home/steam/Steam/steamapps/common/PalServer/Pal/Saved/ /home/game/palworld/
cd /home/game && tar czvf palworld_$(date +"%Y%m%d%H%M%S").tar.gz palworld
rm -rf /home/game/palworld/
echo -e "\033[0;32m游戏存档已导出存放在: /home/game/\033[0m"

13
pal_log.sh Normal file
View File

@@ -0,0 +1,13 @@
clear
echo "脚本更新日志"
echo "------------------------"
echo "2024-2-1 v1.0"
echo "风靡全球的幻兽帕鲁服务端管理面板上线!"
echo "------------------------"
echo "2024-2-2 v1.0.1"
echo "增加了游戏存档定时备份,可选每周,每天,每小时"
echo "主菜单增加了游戏服务安装状态以及开服情况的智能显示"
echo "主菜单增加k选项与科技lion官方脚本工具联动。"
echo "主菜单增加游戏配置修改功能"
echo "对脚本细节调优,体验更好"
echo "------------------------"

445
palworld.sh Normal file
View File

@@ -0,0 +1,445 @@
#!/bin/bash
ln -sf ~/palworld.sh /usr/local/bin/p
ip_address() {
ipv4_address=$(curl -s ipv4.ip.sb)
ipv6_address=$(curl -s --max-time 1 ipv6.ip.sb)
}
install() {
if [ $# -eq 0 ]; then
echo "未提供软件包参数!"
return 1
fi
for package in "$@"; do
if ! command -v "$package" &>/dev/null; then
if command -v apt &>/dev/null; then
apt update -y && apt install -y "$package"
elif command -v yum &>/dev/null; then
yum -y update && yum -y install "$package"
elif command -v apk &>/dev/null; then
apk update && apk add "$package"
else
echo "未知的包管理器!"
return 1
fi
fi
done
return 0
}
remove() {
if [ $# -eq 0 ]; then
echo "未提供软件包参数!"
return 1
fi
for package in "$@"; do
if command -v apt &>/dev/null; then
apt purge -y "$package"
elif command -v yum &>/dev/null; then
yum remove -y "$package"
elif command -v apk &>/dev/null; then
apk del "$package"
else
echo "未知的包管理器!"
return 1
fi
done
return 0
}
break_end() {
echo -e "\033[0;32m操作完成\033[0m"
echo "按任意键继续..."
read -n 1 -s -r -p ""
echo ""
clear
}
palworld() {
p
exit
}
install_add_docker() {
if [ -f "/etc/alpine-release" ]; then
apk update
apk add docker docker-compose
rc-update add docker default
service docker start
else
curl -fsSL https://get.docker.com | sh && ln -s /usr/libexec/docker/cli-plugins/docker-compose /usr/local/bin
systemctl start docker
systemctl enable docker
fi
}
install_docker() {
if ! command -v docker &>/dev/null; then
install_add_docker
else
echo "Docker 已经安装"
fi
}
pal_start() {
ip_address
tmux new -d -s my1 "docker exec -it steamcmd bash -c '/home/steam/Steam/steamapps/common/PalServer/PalServer.sh'"
echo -e "\033[0;32m幻兽帕鲁服务启动啦\033[0m"
echo -e "\033[0;32m游戏下载地址: https://store.steampowered.com/app/1623730\033[0m"
echo -e "\033[0;32m进入游戏连接:\033[93m $ipv4_address:8255 \033[0;32m开始冒险吧\033[0m"
}
pal_backup() {
cd ~
curl -sS -O https://kejilion.pro/pal_backup.sh && chmod +x pal_backup.sh
}
pal_install_status() {
CONTAINER_NAME="steamcmd"
# 检查容器是否已安装
if [ "$(docker ps -a -q -f name=$CONTAINER_NAME 2>/dev/null)" ]; then
container_status="\e[32m幻兽帕鲁服务已安装\e[0m" # 绿色
else
container_status="\e[90m幻兽帕鲁服务未安装\e[0m" # 灰色
fi
SESSION_NAME="my1"
ip_address
# 检查 tmux 中是否存在指定的工作区
if tmux has-session -t $SESSION_NAME 2>/dev/null; then
tmux_status="\e[32m已开服:\033[93m $ipv4_address:8255\e[0m" # 绿色
else
tmux_status="\e[90m未开服\e[0m" # 灰色
fi
}
while true; do
clear
pal_install_status
echo -e "\033[93m . . ."
echo "._ _.|. , _ ._.| _|"
echo "[_)(_]| \/\/ (_)[ |(_]"
echo "| "
echo -e "\033[96m幻兽帕鲁开服一键脚本工具v1.0.2 by KEJILION\033[0m"
echo -e "\033[96m-输入\033[93mp\033[96m可快速启动此脚本-\033[0m"
echo -e "$container_status $tmux_status"
echo "------------------------"
echo "1. 安装幻兽帕鲁服务"
echo "2. 开启幻兽帕鲁服务"
echo "3. 关闭幻兽帕鲁服务"
echo "4. 重启幻兽帕鲁服务"
echo "------------------------"
echo "5. 查看服务器状态"
echo "6. 设置虚拟内存"
echo "------------------------"
echo "7. 导出游戏存档"
echo "8. 导入游戏存档"
echo "9. 定时备份游戏存档"
echo "------------------------"
echo "10. 修改游戏配置"
echo "------------------------"
echo "11. 更新幻兽帕鲁服务"
echo "12. 卸载幻兽帕鲁服务"
echo "------------------------"
echo "k. 科技lion脚本工具箱"
echo "------------------------"
echo "00. 脚本更新"
echo "------------------------"
echo "0. 退出脚本"
echo "------------------------"
read -p "请输入你的选择: " choice
case $choice in
1)
clear
install_docker
install tmux
docker run -dit --name steamcmd -p 8255:8211/udp --restart=always cm2network/steamcmd
docker exec -it steamcmd bash -c "/home/steam/steamcmd/steamcmd.sh +login anonymous +app_update 2394010 validate +quit"
clear
pal_start
;;
2)
clear
docker start steamcmd > /dev/null 2>&1
pal_start
;;
3)
clear
tmux kill-session -t my1
docker stop steamcmd > /dev/null 2>&1
echo -e "\033[0;32m幻兽帕鲁服务已关闭\033[0m"
;;
4)
clear
tmux kill-session -t my1
docker restart steamcmd > /dev/null 2>&1
pal_start
;;
5)
clear
install btop
clear
btop
;;
6)
clear
swap_used=$(free -m | awk 'NR==3{print $3}')
swap_total=$(free -m | awk 'NR==3{print $2}')
if [ "$swap_total" -eq 0 ]; then
swap_percentage=0
else
swap_percentage=$((swap_used * 100 / swap_total))
fi
swap_info="${swap_used}MB/${swap_total}MB (${swap_percentage}%)"
echo "当前虚拟内存: $swap_info"
read -p "是否调整大小?(Y/N): " choice
case "$choice" in
[Yy])
# 输入新的虚拟内存大小
read -p "请输入虚拟内存大小MB: " new_swap
# 获取当前系统中所有的 swap 分区
swap_partitions=$(grep -E '^/dev/' /proc/swaps | awk '{print $1}')
# 遍历并删除所有的 swap 分区
for partition in $swap_partitions; do
swapoff "$partition"
wipefs -a "$partition" # 清除文件系统标识符
mkswap -f "$partition"
echo "已删除并重新创建 swap 分区: $partition"
done
# 确保 /swapfile 不再被使用
swapoff /swapfile
# 删除旧的 /swapfile
rm -f /swapfile
# 创建新的 swap 分区
dd if=/dev/zero of=/swapfile bs=1M count=$new_swap
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile
if [ -f /etc/alpine-release ]; then
echo "/swapfile swap swap defaults 0 0" >> /etc/fstab
echo "nohup swapon /swapfile" >> /etc/local.d/swap.start
chmod +x /etc/local.d/swap.start
rc-update add local
else
echo "/swapfile swap swap defaults 0 0" >> /etc/fstab
fi
echo "虚拟内存大小已调整为${new_swap}MB"
;;
[Nn])
echo "已取消"
;;
*)
echo "无效的选择,请输入 Y 或 N。"
;;
esac
;;
7)
clear
mkdir -p /home/game
docker cp steamcmd:/home/steam/Steam/steamapps/common/PalServer/Pal/Saved/ /home/game/palworld/ > /dev/null 2>&1
cd /home/game && tar czvf palworld_$(date +"%Y%m%d%H%M%S").tar.gz palworld > /dev/null 2>&1
rm -rf /home/game/palworld/
echo -e "\033[0;32m游戏存档已导出存放在: /home/game/\033[0m"
;;
8)
clear
tmux kill-session -t my1
docker exec -it steamcmd bash -c "rm -rf /home/steam/Steam/steamapps/common/PalServer/Pal/Saved/*"
cd /home/game/ && ls -t /home/game/*.tar.gz | head -1 | xargs -I {} tar -xzf {}
docker cp /home/game/palworld/Config steamcmd:/home/steam/Steam/steamapps/common/PalServer/Pal/Saved/Config > /dev/null 2>&1
docker cp /home/game/palworld/ImGui steamcmd:/home/steam/Steam/steamapps/common/PalServer/Pal/Saved/ImGui > /dev/null 2>&1
docker cp /home/game/palworld/SaveGames steamcmd:/home/steam/Steam/steamapps/common/PalServer/Pal/Saved/SaveGames > /dev/null 2>&1
docker exec -it -u root steamcmd bash -c "chmod -R 777 /home/steam/Steam/steamapps/common/PalServer/Pal/Saved/"
rm -rf /home/game/palworld/
echo -e "\033[0;32m游戏存档已导入\033[0m"
docker restart steamcmd > /dev/null 2>&1
pal_start
;;
9)
clear
echo "幻兽帕鲁游戏存档定时备份"
echo "------------------------"
echo "1. 每周备份 2. 每天备份 3. 每小时备份"
echo "------------------------"
read -p "请输入你的选择: " dingshi
case $dingshi in
1)
pal_backup
(crontab -l ; echo "0 0 * * 1 ./pal_backup.sh") | crontab - > /dev/null 2>&1
echo "每周一备份,已设置"
;;
2)
pal_backup
(crontab -l ; echo "0 3 * * * ./pal_backup.sh") | crontab - > /dev/null 2>&1
echo "每天凌晨3点备份已设置"
;;
3)
pal_backup
(crontab -l ; echo "0 * * * * ./pal_backup.sh") | crontab - > /dev/null 2>&1
echo "每小时整点备份,已设置"
;;
*)
echo "已取消"
;;
esac
;;
10)
clear
tmux kill-session -t my1
cd ~ && curl -sS -O https://kejilion.pro/PalWorldSettings.ini
echo "配置游戏参数"
echo "------------------------"
read -p "设置加入的密码(回车默认无密码): " server_password
read -p "设置游戏难度: 1. 简单 2. 普通 3. 困难):" Difficulty
case $Difficulty in
1)
Difficulty=1
;;
2)
Difficulty=2
;;
3)
Difficulty=3
;;
*)
echo "-默认设置为普通难度"
Difficulty=2
;;
esac
read -p "经验值倍率: 回车默认1倍:" exp_rate
ExpRate=${exp_rate:-1}
read -p "死亡后掉落设置: 1. 掉落 2. 不掉落):" DeathPenalty
case $DeathPenalty in
1)
DeathPenalty=All
;;
2)
DeathPenalty=None
;;
*)
DeathPenalty=All
echo "-默认设置为掉落"
;;
esac
read -p "设置pvp模式: 1. 开启 2. 关闭):" pal_pvp
case $pal_pvp in
1)
pal_pvp=True
;;
2)
pal_pvp=False
;;
*)
pal_pvp=False
echo "-默认关闭pvp模式"
;;
esac
# 更新配置文件
sed -i "s/ServerPassword=\"\"/ServerPassword=\"$server_password\"/" ~/PalWorldSettings.ini
sed -i "s/Difficulty=2/Difficulty=$Difficulty/" ~/PalWorldSettings.ini
sed -i "s/ExpRate=1.000000/ExpRate=$ExpRate/" ~/PalWorldSettings.ini
sed -i "s/DeathPenalty=All/DeathPenalty=$DeathPenalty/" ~/PalWorldSettings.ini
sed -i "s/bEnablePlayerToPlayerDamage=False/bEnablePlayerToPlayerDamage=$pal_pvp/" ~/PalWorldSettings.ini
sed -i "s/bIsPvP=False/bIsPvP=$pal_pvp/" ~/PalWorldSettings.ini
echo "------------------------"
echo "配置文件已更新"
docker exec -it steamcmd bash -c "rm -f /home/steam/Steam/steamapps/common/PalServer/Pal/Saved/Config/LinuxServer/PalWorldSettings.ini"
docker cp ~/PalWorldSettings.ini steamcmd:/home/steam/Steam/steamapps/common/PalServer/Pal/Saved/Config/LinuxServer/ > /dev/null 2>&1
docker exec -it -u root steamcmd bash -c "chmod -R 777 /home/steam/Steam/steamapps/common/PalServer/Pal/Saved/"
rm -f ~/PalWorldSettings.ini
echo -e "\033[0;32m游戏配置已导入\033[0m"
docker restart steamcmd > /dev/null 2>&1
pal_start
;;
11)
clear
tmux kill-session -t my1
docker restart steamcmd > /dev/null 2>&1
docker exec -it steamcmd bash -c "/home/steam/steamcmd/steamcmd.sh +login anonymous +app_update 2394010 validate +quit"
clear
echo -e "\033[0;32m幻兽帕鲁已更新\033[0m"
pal_start
;;
12)
clear
docker rm -f steamcmd
docker rmi -f cm2network/steamcmd
;;
k)
cd ~
curl -sS -O https://kejilion.pro/kejilion.sh && chmod +x kejilion.sh && ./kejilion.sh
exit
;;
00)
cd ~
curl -sS -O https://kejilion.pro/pal_log.sh && chmod +x pal_log.sh && ./pal_log.sh
rm pal_log.sh
echo ""
curl -sS -O https://kejilion.pro/palworld.sh && chmod +x palworld.sh
echo "脚本已更新到最新版本!"
break_end
palworld
;;
0)
clear
exit
;;
*)
echo "无效的输入!"
;;
esac
break_end
done

12222
ru/kejilion.sh Normal file

File diff suppressed because it is too large Load Diff

76
ru/to-ru.py Normal file
View File

@@ -0,0 +1,76 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from deep_translator import GoogleTranslator
import re
import os
def is_chinese(text):
return bool(re.search(r'[\u4e00-\u9fff]', text))
def translate_text(text):
try:
return GoogleTranslator(source='zh-CN', target='ru').translate(text)
except Exception as e:
print(f"\nTranslation error: {e}")
return text
def translate_line_preserving_variables(line):
"""
Translate only Chinese parts in echo/read/send_stats commands, excluding shell variables
"""
# Match double or single quoted strings
def repl(match):
full_string = match.group(0)
quote = full_string[0]
content = full_string[1:-1]
# Split by variable expressions
parts = re.split(r'(\$\{?\w+\}?)', content)
translated_parts = [
translate_text(p) if is_chinese(p) else p
for p in parts
]
return quote + ''.join(translated_parts) + quote
return re.sub(r'(?:\'[^\']*\'|"[^"]*")', repl, line)
def translate_file(input_file, output_file):
total_lines = sum(1 for _ in open(input_file, 'r', encoding='utf-8'))
processed_lines = 0
with open(input_file, 'r', encoding='utf-8') as f_in, \
open(output_file, 'w', encoding='utf-8') as f_out:
for line in f_in:
processed_lines += 1
progress = processed_lines / total_lines * 100
print(f"\rProcessing: {progress:.1f}% ({processed_lines}/{total_lines})", end='')
leading_space = re.match(r'^(\s*)', line).group(1)
stripped = line.strip()
if stripped.startswith('#') and is_chinese(stripped):
comment_mark = '#'
comment_text = stripped[1:].strip()
if comment_text:
translated = translate_text(comment_text)
f_out.write(f"{leading_space}{comment_mark} {translated}\n")
else:
f_out.write(line)
elif any(cmd in stripped for cmd in ['echo', 'read', 'send_stats']) and is_chinese(stripped):
translated_line = translate_line_preserving_variables(line)
f_out.write(translated_line)
else:
f_out.write(line)
print("\nTranslation completed.")
print(f"Original file size: {os.path.getsize(input_file)} bytes")
print(f"Translated file size: {os.path.getsize(output_file)} bytes")
if __name__ == "__main__":
input_file = 'kejilion.sh'
output_file = 'kejilion_ru.sh'
translate_file(input_file, output_file)

38
run_openclaw_manager_matrix.sh Executable file
View File

@@ -0,0 +1,38 @@
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")"
IMAGES=(
"debian:12"
"debian:13"
"ubuntu:22.04"
"ubuntu:24.04"
"rockylinux:9"
"almalinux:9"
"fedora:41"
)
install_cmd() {
case "$1" in
debian:*|ubuntu:*)
echo 'apt-get update -y >/dev/null && DEBIAN_FRONTEND=noninteractive apt-get install -y bash jq grep sed coreutils >/dev/null'
;;
rockylinux:*|almalinux:*|fedora:*)
echo 'dnf install -y bash jq grep sed coreutils >/dev/null'
;;
*)
return 1
;;
esac
}
for img in "${IMAGES[@]}"; do
echo "===== $img ====="
cmd=$(install_cmd "$img")
if docker run --rm -v "$PWD":/src -w /src "$img" bash -lc "$cmd && bash -n kejilion.sh && ./tests_openclaw_manager_smoke.sh"; then
echo "RESULT $img OK"
else
echo "RESULT $img FAIL"
fi
echo
done

5
sshd.local Normal file
View File

@@ -0,0 +1,5 @@
[sshd]
enabled = true
mode = normal
backend = systemd

36
tests/openclaw/README.md Normal file
View File

@@ -0,0 +1,36 @@
# OpenClaw tests
这里的脚本用于对 `kejilion.sh` 中的 OpenClaw 相关功能做最小回归与冒烟测试,主要覆盖:
- API 模型同步/协议探测
- 记忆菜单
- 插件/技能菜单
- API 列表颜色/对齐展示(最小验证)
## 运行方式
在仓库根目录执行(推荐):
```bash
bash tests/openclaw/tests_openclaw_api_protocol_detect_smoke.sh
bash tests/openclaw/tests_openclaw_api_sync_diff_smoke.sh
bash tests/openclaw/tests_openclaw_memory_menu_smoke.sh
bash tests/openclaw/tests_openclaw_plugin_skill_menu_smoke.sh
bash tests/openclaw/tests_openclaw_api_color_align_min.sh
```
也可在任意工作目录执行:
```bash
/path/to/repo/tests/openclaw/tests_openclaw_memory_menu_smoke.sh
```
## 安全说明
- 脚本会创建临时工作目录与临时 `HOME`(位于 `/tmp`),并在结束后自动清理(除非设置 `KEEP_WORKDIR=true`)。
- 运行过程使用 stub/模拟命令,不会改动真实 OpenClaw 配置或系统状态。
- 如需保留临时目录用于排查,请执行:
```bash
KEEP_WORKDIR=true bash tests/openclaw/tests_openclaw_memory_menu_smoke.sh
```

View File

@@ -0,0 +1,130 @@
#!/usr/bin/env bash
set -euo pipefail
REPO_ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
SCRIPT="$REPO_ROOT/kejilion.sh"
WORKDIR="${TMPDIR:-/tmp}/openclaw-change-model-dual-probe-$$"
mkdir -p "$WORKDIR/home/.openclaw"
KEEP_WORKDIR=${KEEP_WORKDIR:-false}
trap '[ "$KEEP_WORKDIR" = "true" ] || rm -rf "$WORKDIR"' EXIT
python3 - "$SCRIPT" "$WORKDIR" <<'PY'
import json
import re
import sys
from pathlib import Path
script_path = Path(sys.argv[1])
workdir = Path(sys.argv[2])
text = script_path.read_text(encoding='utf-8')
match = re.search(r'\n\t\topenclaw_model_probe\(\) \{.*?\n\t\t\}', text, re.S)
if not match:
raise SystemExit('openclaw_model_probe not found')
func = match.group(0).strip('\n')
config = {
"models": {
"providers": {
"demo": {
"baseUrl": "https://api.example.com/v1",
"apiKey": "sk-test"
}
}
}
}
(workdir / 'home' / '.openclaw').mkdir(parents=True, exist_ok=True)
(workdir / 'home' / '.openclaw' / 'openclaw.json').write_text(json.dumps(config, ensure_ascii=False, indent=2) + '\n', encoding='utf-8')
harness = f'''#!/usr/bin/env bash
set -euo pipefail
HOME="{workdir / 'home'}"
OPENCLAW_PROBE_STATUS=""
OPENCLAW_PROBE_MESSAGE=""
OPENCLAW_PROBE_LATENCY=""
OPENCLAW_PROBE_REPLY=""
{func}
'''
(workdir / 'harness.sh').write_text(harness, encoding='utf-8')
PY
run_case() {
local case_name="$1"
CASE_NAME="$case_name" WORKDIR="$WORKDIR" bash <<'EOF_CASE'
set -euo pipefail
source "$WORKDIR/harness.sh"
python3() {
if [ "$1" != "-" ]; then
command python3 "$@"
return
fi
local script
script=$(cat)
shift
CASE_NAME="$CASE_NAME" command python3 - "$script" "$@" <<'PYWRAP'
import io
import json
import os
import sys
import urllib.error
import urllib.request
from pathlib import Path
script = sys.argv[1]
args = sys.argv[2:]
case = os.environ['CASE_NAME']
class Resp(io.BytesIO):
def __init__(self, data, code=200):
super().__init__(data)
self.status = code
def __enter__(self):
return self
def __exit__(self, exc_type, exc, tb):
return False
real_urlopen = urllib.request.urlopen
def fake_urlopen(req, timeout=0):
url = getattr(req, 'full_url', '')
if case == 'fallback-success':
if url.endswith('/chat/completions'):
raise urllib.error.HTTPError(url, 404, 'Not Found', {}, io.BytesIO(b'{"error":"chat disabled"}'))
if url.endswith('/responses'):
body = json.dumps({"output": [{"content": [{"text": "pong from responses"}]}]}).encode('utf-8')
return Resp(body, 200)
elif case == 'all-fail':
if url.endswith('/chat/completions'):
raise urllib.error.HTTPError(url, 404, 'Not Found', {}, io.BytesIO(b'{"error":"chat disabled"}'))
if url.endswith('/responses'):
raise urllib.error.HTTPError(url, 404, 'Not Found', {}, io.BytesIO(b'{"error":"responses disabled"}'))
return real_urlopen(req, timeout=timeout)
urllib.request.urlopen = fake_urlopen
sys.argv = ['python3'] + args
ns = {'__name__': '__main__'}
exec(compile(script, '<stdin>', 'exec'), ns, ns)
PYWRAP
}
set +e
openclaw_model_probe "demo/test-model"
probe_rc=$?
set -e
printf 'RC=%s\nSTATUS=%s\nMESSAGE=%s\nLATENCY=%s\nREPLY=%s\n' "$probe_rc" "$OPENCLAW_PROBE_STATUS" "$OPENCLAW_PROBE_MESSAGE" "$OPENCLAW_PROBE_LATENCY" "$OPENCLAW_PROBE_REPLY"
EOF_CASE
}
out1=$(run_case fallback-success)
printf '%s\n' "$out1" | grep -q 'STATUS=OK'
printf '%s\n' "$out1" | grep -q 'MESSAGE=/responses -> HTTP 200'
printf '%s\n' "$out1" | grep -q 'REPLY=pong from responses'
out2=$(run_case all-fail || true)
printf '%s\n' "$out2" | grep -q 'RC=1'
printf '%s\n' "$out2" | grep -q 'STATUS=FAIL'
printf '%s\n' "$out2" | grep -q '/responses -> HTTP 404 / exit 22/chat/completions -> HTTP 404 / exit 22'
printf '%s\n' 'SMOKE_OK'

View File

@@ -0,0 +1,40 @@
#!/usr/bin/env bash
set -euo pipefail
# 模拟脚本原生颜色变量
# shellcheck disable=SC2034
gl_hong='\033[31m'
# shellcheck disable=SC2034
gl_lv='\033[32m'
# shellcheck disable=SC2034
gl_huang='\033[33m'
# shellcheck disable=SC2034
gl_bai='\033[0m'
printf '%s\n' '--- OpenClaw API 列表(最小颜色/对齐验证) ---'
printf '%-4s %-18s %-44s %-8s %-12s\n' '序号' '名称' 'API地址' '模型数量' '延迟/状态'
printf '%s\n' '----------------------------------------------------------------------------------------------'
# 模拟 Python 计算后的字段(纯数据,不含颜色码)
while IFS=$'\t' read -r rec_type idx name base_url model_count latency_txt latency_level; do
[[ "$rec_type" == "ROW" ]] || continue
latency_color="$gl_bai"
case "$latency_level" in
low) latency_color="$gl_lv" ;;
medium) latency_color="$gl_huang" ;;
high|unavailable) latency_color="$gl_hong" ;;
unchecked) latency_color="$gl_bai" ;;
esac
# 颜色在 Shell 层拼接并输出
printf '%-4s %-18s %-44s %b%-8s%b %b%-12s%b\n' \
"$idx" "$name" "$base_url" \
"$gl_huang" "$model_count" "$gl_bai" \
"$latency_color" "$latency_txt" "$gl_bai"
done <<'EOF'
ROW 1. alpha-openai https://api.alpha.example/v1 128 120ms low
ROW 2. beta-proxy https://api.beta.example/v1 64 1300ms medium
ROW 3. gamma-down https://api.gamma.example/v1 0 不可用 unavailable
ROW 4. delta-custom - 12 未检测 unchecked
EOF

View File

@@ -0,0 +1,79 @@
#!/usr/bin/env bash
set -euo pipefail
REPO_ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
SCRIPT="$REPO_ROOT/kejilion.sh"
WORKDIR="${TMPDIR:-/tmp}/openclaw-api-protocol-test-$$"
mkdir -p "$WORKDIR/bin" "$WORKDIR/home/.openclaw"
KEEP_WORKDIR=${KEEP_WORKDIR:-false}
trap '[ "$KEEP_WORKDIR" = "true" ] || rm -rf "$WORKDIR"' EXIT
# 抽取 OpenClaw API 添加逻辑(只验证:不再做协议探测/自动纠正)
cat > "$WORKDIR/harness.sh" <<'EOF_INNER'
#!/usr/bin/env bash
set -euo pipefail
break_end() { return 0; }
send_stats() { return 0; }
start_gateway() { return 0; }
install() { return 0; }
EOF_INNER
awk 'BEGIN{p=0}
/add-all-models-from-provider\(\) \{/{p=1}
/openclaw_api_manage_list\(\) \{/{p=0}
p{print}
' "$SCRIPT" >> "$WORKDIR/harness.sh"
awk 'BEGIN{p=0}
/openclaw_detect_api_protocol_by_provider\(\) \{/{p=1}
/fix-openclaw-provider-protocol-interactive\(\) \{/{p=0}
p{print}
' "$SCRIPT" >> "$WORKDIR/harness.sh"
chmod +x "$WORKDIR/harness.sh"
# stub: curl
cat > "$WORKDIR/bin/curl" <<'EOF_INNER'
#!/usr/bin/env bash
set -e
args="$*"
if [[ "$args" == *"/models"* ]]; then
cat <<'JSON'
{"data": [{"id": "gpt-4o"}, {"id": "gpt-4o-mini"}]}
JSON
exit 0
fi
printf ""
exit 0
EOF_INNER
chmod +x "$WORKDIR/bin/curl"
export HOME="$WORKDIR/home"
export PATH="$WORKDIR/bin:$PATH"
export TERM=xterm
cat > "$HOME/.openclaw/openclaw.json" <<'JSON'
{
"models": {
"mode": "merge",
"providers": {}
},
"agents": {
"defaults": {
"models": {}
}
}
}
JSON
source "$WORKDIR/harness.sh"
# 添加 provider不应依赖协议探测若脚本内部仍调用已删除函数会报错
add-all-models-from-provider "demo" "https://api.example.com/v1" "sk-test"
echo "SMOKE_OK: no protocol probing required during add"
# 协议修复函数现在不再修改配置,只输出提示
openclaw_detect_api_protocol_by_provider "$HOME/.openclaw/openclaw.json" "demo" >/dev/null 2>&1 || true
echo "SMOKE_OK: protocol switch is available"

View File

@@ -0,0 +1,133 @@
#!/usr/bin/env bash
set -euo pipefail
REPO_ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
SCRIPT="$REPO_ROOT/kejilion.sh"
WORKDIR="${TMPDIR:-/tmp}/openclaw-api-sync-diff-test-$$"
mkdir -p "$WORKDIR/bin" "$WORKDIR/home/.openclaw"
KEEP_WORKDIR=${KEEP_WORKDIR:-false}
trap '[ "$KEEP_WORKDIR" = "true" ] || rm -rf "$WORKDIR"' EXIT
cat > "$WORKDIR/harness.sh" <<'EOF_INNER'
#!/usr/bin/env bash
set -euo pipefail
break_end() { return 0; }
send_stats() { return 0; }
start_gateway() { return 0; }
install() { return 0; }
EOF_INNER
awk 'BEGIN{p=0}
/sync_openclaw_api_models\(\) \{/{p=1}
/install_moltbot\(\) \{/{p=0}
p{print}
' "$SCRIPT" >> "$WORKDIR/harness.sh"
chmod +x "$WORKDIR/harness.sh"
cat > "$WORKDIR/bin/python3" <<'EOF_INNER'
#!/usr/bin/env bash
if [[ "$1" != "-" ]]; then
exec /usr/bin/python3 "$@"
fi
SCRIPT=$(cat)
export SCRIPT
/usr/bin/python3 - "${@:2}" <<"PY_STDIN"
import io
import json
import os
import urllib.request
import urllib.error
class _Resp(io.BytesIO):
def __init__(self, data, code=200):
super().__init__(data)
self._code = code
def getcode(self):
return self._code
def __enter__(self):
return self
def __exit__(self, exc_type, exc, tb):
return False
_real = urllib.request.urlopen
def urlopen(req, timeout=0):
url = getattr(req, "full_url", "") or getattr(req, "get_full_url", lambda: "")()
if url.endswith("/models"):
data = json.dumps({"data": [{"id": "alpha"}, {"id": "beta"}]}).encode("utf-8")
return _Resp(data, 200)
if url.endswith("/responses") or url.endswith("/chat/completions"):
raise urllib.error.HTTPError(url, 404, "Not Found", {}, None)
return _real(req, timeout=timeout)
urllib.request.urlopen = urlopen
script = os.environ.get("SCRIPT", "")
exec(compile(script, "<stdin>", "exec"))
PY_STDIN
exit $?
EOF_INNER
chmod +x "$WORKDIR/bin/python3"
cat > "$WORKDIR/bin/curl" <<'EOF_INNER'
#!/usr/bin/env bash
# fake curl for /models, /responses, /chat/completions (for shell probes)
set -e
args="$*"
if [[ "$args" == *"/responses"* ]]; then
printf "404"
exit 0
fi
if [[ "$args" == *"/chat/completions"* ]]; then
printf "404"
exit 0
fi
printf "000"
exit 0
EOF_INNER
chmod +x "$WORKDIR/bin/curl"
export HOME="$WORKDIR/home"
export PATH="$WORKDIR/bin:$PATH"
export TERM=xterm
export ENABLE_STATS=false
export sh_v=testing
cat > "$HOME/.openclaw/openclaw.json" <<'JSON'
{
"models": {
"mode": "merge",
"providers": {
"demo": {
"api": "openai-completions",
"baseUrl": "https://api.example.com/v1",
"apiKey": "sk-test",
"models": [
{"id": "alpha", "name": "demo / alpha"},
{"id": "gamma", "name": "demo / gamma"}
]
}
}
},
"agents": {
"defaults": {
"models": {
"demo/alpha": {},
"demo/gamma": {}
},
"model": "demo/alpha"
}
}
}
JSON
source "$WORKDIR/harness.sh"
output=$(sync_openclaw_api_models 2>&1 || true)
printf '%s\n' "$output" | grep -q " 新增模型(1):"
printf '%s\n' "$output" | grep -q "^ + beta$"
printf '%s\n' "$output" | grep -q " 删除模型(1):"
printf '%s\n' "$output" | grep -q "^ - gamma$"
printf '%s\n' "SMOKE_OK"

View File

@@ -0,0 +1,237 @@
#!/usr/bin/env bash
set -euo pipefail
REPO_ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
SCRIPT="$REPO_ROOT/kejilion.sh"
WORKDIR="${TMPDIR:-/tmp}/openclaw-memory-auto-setup-test-$$"
mkdir -p "$WORKDIR/bin"
KEEP_WORKDIR=${KEEP_WORKDIR:-false}
trap '[ "$KEEP_WORKDIR" = "true" ] || rm -rf "$WORKDIR"' EXIT
# 抽取 OpenClaw 记忆菜单实现
cat > "$WORKDIR/harness.sh" <<'EOF_INNER'
#!/usr/bin/env bash
set -euo pipefail
break_end() { return 0; }
send_stats() { return 0; }
EOF_INNER
awk 'BEGIN{p=0} /openclaw_memory_config_file\(\) \{/{p=1} /openclaw_memory_menu\(\) \{/{p=0} p{print}' "$SCRIPT" >> "$WORKDIR/harness.sh"
chmod +x "$WORKDIR/harness.sh"
# stub: openclaw
cat > "$WORKDIR/bin/openclaw" <<'EOF_INNER'
#!/usr/bin/env bash
set -euo pipefail
cmd="$*"
config_dir="$HOME/.openclaw/config"
mkdir -p "$config_dir"
key_file() {
local key="$1"
echo "$config_dir/${key//./_}"
}
if [[ "$cmd" == "config get"* ]]; then
key="$3"
file=$(key_file "$key")
if [ -f "$file" ]; then
cat "$file"
fi
exit 0
fi
if [[ "$cmd" == "config set"* ]]; then
key="$3"
val="$4"
file=$(key_file "$key")
echo "$val" > "$file"
exit 0
fi
if [[ "$1" == "memory" && "$2" == "status" ]]; then
agent="main"
while [ $# -gt 0 ]; do
if [ "$1" = "--agent" ] && [ $# -ge 2 ]; then
agent="$2"
shift 2
continue
fi
shift
done
cat <<TXT
Provider: qmd (requested: qmd)
Vector: ready
Indexed: 0/0 files
Workspace: ${HOME}/.openclaw/workspace
Store: ${OPENCLAW_STORE:-~/.openclaw/workspace/memory/index.sqlite}
TXT
exit 0
fi
if [[ "$1" == "memory" && "$2" == "index" ]]; then
echo "$cmd" >> "${HOME}/.openclaw/index_calls"
echo "index ok"
exit 0
fi
echo "mock openclaw $*"
EOF_INNER
chmod +x "$WORKDIR/bin/openclaw"
# stub: curl
cat > "$WORKDIR/bin/curl" <<'EOF_INNER'
#!/usr/bin/env bash
set -euo pipefail
args=("$@")
is_head=0
for arg in "${args[@]}"; do
if [ "$arg" = "-I" ]; then
is_head=1
fi
done
url="${args[$((${#args[@]}-1))]}"
for arg in "${args[@]}"; do
if [[ "$arg" == *"ipinfo.io/country"* ]]; then
echo "${FAKE_COUNTRY:-US}"
exit 0
fi
if [[ "$arg" == *"bun.sh/install"* ]]; then
cat <<'SCRIPT'
mkdir -p "$HOME/.bun/bin"
cat > "$HOME/.bun/bin/bun" <<'BUN'
#!/usr/bin/env bash
set -euo pipefail
if [[ "$1" == "install" && "$2" == "-g" ]]; then
mkdir -p "$HOME/.bun/bin"
cat > "$HOME/.bun/bin/qmd" <<'QMD'
#!/usr/bin/env bash
exit 0
QMD
chmod +x "$HOME/.bun/bin/qmd"
fi
echo "$*" >> "$HOME/.openclaw/bun_calls"
exit 0
BUN
chmod +x "$HOME/.bun/bin/bun"
SCRIPT
exit 0
fi
done
if [ "$is_head" = "1" ]; then
if [[ "$url" == *"huggingface.co"* ]]; then
exit ${FAKE_HF_OK:-0}
fi
if [[ "$url" == *"hf-mirror.com"* ]]; then
exit ${FAKE_MIRROR_OK:-0}
fi
exit 0
fi
dest=""
for ((i=0;i<${#args[@]};i++)); do
if [ "${args[$i]}" = "-o" ] && [ $((i+1)) -lt ${#args[@]} ]; then
dest="${args[$((i+1))]}"
fi
done
if [ -n "$dest" ] && [[ "$url" == *"embeddinggemma"* ]]; then
mkdir -p "$(dirname "$dest")"
echo "model" > "$dest"
echo "$url" >> "$HOME/.openclaw/download_calls"
exit 0
fi
exit 0
EOF_INNER
chmod +x "$WORKDIR/bin/curl"
# stub: wget (fallback, not used in test)
cat > "$WORKDIR/bin/wget" <<'EOF_INNER'
#!/usr/bin/env bash
exit 0
EOF_INNER
chmod +x "$WORKDIR/bin/wget"
export PATH="$WORKDIR/bin:$PATH"
export TERM=xterm
source "$WORKDIR/harness.sh"
make_home() {
local name="$1"
local home="$WORKDIR/$name/home"
mkdir -p "$home/.openclaw/workspace/memory" "$home/.openclaw"
echo "$home"
}
run_auto() {
local input="$1"
local scheme="$2"
local outfile="$3"
printf "%b" "$input" | openclaw_memory_auto_setup_run "$scheme" >"$outfile"
}
# Case 1: qmd 不存在 -> 安装 -> 写入绝对路径 -> index
HOME_QMD=$(make_home "qmd")
export HOME="$HOME_QMD"
FAKE_COUNTRY=US FAKE_HF_OK=0 FAKE_MIRROR_OK=1 \
run_auto "yes\n" "qmd" "$WORKDIR/out_qmd.txt"
# Case 2: local 模型不存在 -> 下载 -> 写入 modelPath -> index (CN)
HOME_LOCAL=$(make_home "local")
export HOME="$HOME_LOCAL"
FAKE_COUNTRY=CN FAKE_HF_OK=1 FAKE_MIRROR_OK=0 \
run_auto "yes\n" "local" "$WORKDIR/out_local.txt"
# Case 3: 已存在 -> 跳过下载/安装
HOME_SKIP=$(make_home "skip")
export HOME="$HOME_SKIP"
mkdir -p "$HOME/.openclaw/models/embedding" "$HOME/.openclaw/config"
MODEL_EXIST="$HOME/.openclaw/models/embedding/embeddinggemma-300M-Q8_0.gguf"
echo "local" > "$HOME/.openclaw/config/memory_backend"
echo "local" > "$HOME/.openclaw/config/agents_defaults_memorySearch_provider"
echo "$MODEL_EXIST" > "$HOME/.openclaw/config/agents_defaults_memorySearch_local_modelPath"
touch "$MODEL_EXIST"
# 预置 qmd
mkdir -p "$HOME/.bun/bin"
cat > "$HOME/.bun/bin/qmd" <<'QMD'
#!/usr/bin/env bash
exit 0
QMD
chmod +x "$HOME/.bun/bin/qmd"
export PATH="$HOME/.bun/bin:$PATH"
FAKE_COUNTRY=US FAKE_HF_OK=0 FAKE_MIRROR_OK=0 \
run_auto "yes\n" "local" "$WORKDIR/out_skip.txt"
export WORKDIR
python3 - <<'PY'
import os
from pathlib import Path
workdir = os.environ['WORKDIR']
# Case1: qmd command absolute + index called
home_qmd = Path(workdir) / 'qmd' / 'home'
qmd_cmd = (home_qmd / '.openclaw/config/memory_qmd_command').read_text().strip()
if not qmd_cmd.startswith('/'):
raise SystemExit('qmd command not absolute')
index_calls = (home_qmd / '.openclaw/index_calls').read_text()
if 'memory index' not in index_calls or '--force' not in index_calls:
raise SystemExit('qmd preheat index not called')
# Case2: local model downloaded + modelPath written
home_local = Path(workdir) / 'local' / 'home'
model_path = (home_local / '.openclaw/config/agents_defaults_memorySearch_local_modelPath').read_text().strip()
if not Path(model_path).exists():
raise SystemExit('model file not downloaded')
if 'hf-mirror.com' not in (Path(workdir) / 'out_local.txt').read_text():
raise SystemExit('mirror not used for CN')
# Case3: skip download
home_skip = Path(workdir) / 'skip' / 'home'
download_calls = home_skip / '.openclaw/download_calls'
if download_calls.exists():
raise SystemExit('download should be skipped when model exists')
print('SMOKE_OK')
PY

View File

@@ -0,0 +1,230 @@
#!/usr/bin/env bash
set -euo pipefail
REPO_ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
SCRIPT="$REPO_ROOT/kejilion.sh"
WORKDIR="${TMPDIR:-/tmp}/openclaw-memory-menu-test-$$"
mkdir -p "$WORKDIR/bin" "$WORKDIR/home/.openclaw/workspace/memory"
KEEP_WORKDIR=${KEEP_WORKDIR:-false}
trap '[ "$KEEP_WORKDIR" = "true" ] || rm -rf "$WORKDIR"' EXIT
# 抽取 OpenClaw 记忆菜单实现
cat > "$WORKDIR/harness.sh" <<'EOF_INNER'
#!/usr/bin/env bash
set -euo pipefail
break_end() { return 0; }
send_stats() { return 0; }
EOF_INNER
awk 'BEGIN{p=0} /openclaw_memory_config_file\(\) \{/{p=1} /openclaw_memory_menu\(\) \{/{p=0} p{print}' "$SCRIPT" >> "$WORKDIR/harness.sh"
awk 'BEGIN{p=0} /openclaw_memory_menu\(\) \{/{p=1} /openclaw_backup_restore_menu\(\) \{/{p=0} p{print}' "$SCRIPT" >> "$WORKDIR/harness.sh"
chmod +x "$WORKDIR/harness.sh"
# stub: openclaw
cat > "$WORKDIR/bin/openclaw" <<'EOF_INNER'
#!/usr/bin/env bash
cmd="$*"
if [[ "$cmd" == "config get"* ]]; then
key="$3"
case "$key" in
memory.backend) echo "qmd" ;;
memory.qmd.includeDefaultMemory)
if [ -f "${HOME}/.openclaw/includeDefaultMemory" ]; then
cat "${HOME}/.openclaw/includeDefaultMemory"
else
echo "true"
fi
;;
memory.qmd.command) echo "qmd" ;;
agents.defaults.memorySearch.local.modelPath) echo "hf:ggml-org/embeddinggemma-300M-GGUF/embeddinggemma-300M-Q8_0.gguf" ;;
agents.defaults.memorySearch.provider) echo "local" ;;
*) echo "" ;;
esac
exit 0
fi
if [[ "$cmd" == "config set"* ]]; then
key="$3"
val="$4"
if [[ "$key" == "memory.qmd.includeDefaultMemory" ]]; then
echo "$val" > "${HOME}/.openclaw/includeDefaultMemory"
fi
if [[ "$key" == "memory.backend" ]]; then
echo "$val" > "${HOME}/.openclaw/backend"
fi
if [[ "$key" == "memory.qmd.command" ]]; then
echo "$val" > "${HOME}/.openclaw/qmd_command"
fi
if [[ "$key" == "agents.defaults.memorySearch.provider" ]]; then
echo "$val" > "${HOME}/.openclaw/provider"
fi
exit 0
fi
if [[ "$1" == "memory" && "$2" == "status" ]]; then
agent="main"
while [ $# -gt 0 ]; do
if [ "$1" = "--agent" ] && [ $# -ge 2 ]; then
agent="$2"
shift 2
continue
fi
shift
done
cat <<TXT
Provider: qmd (requested: qmd)
Vector: ready
Indexed: 23/14 files
Workspace: ${HOME}/.openclaw/workspace
Store: ${OPENCLAW_STORE:-~/.openclaw/workspace/memory/index.sqlite}
TXT
exit 0
fi
if [[ "$1" == "memory" && "$2" == "index" ]]; then
echo "$cmd" >> "${HOME}/.openclaw/index_calls"
echo "index ok"
exit 0
fi
if [[ "$cmd" == "gateway restart" ]]; then
echo "$cmd" >> "${HOME}/.openclaw/gateway_calls"
echo "gateway restarted"
exit 0
fi
if [[ "$cmd" == "gateway stop" ]]; then
echo "gateway stopped"
exit 0
fi
if [[ "$cmd" == "gateway start" ]]; then
echo "gateway started"
exit 0
fi
echo "mock openclaw $*"
EOF_INNER
chmod +x "$WORKDIR/bin/openclaw"
# stub: qmd
cat > "$WORKDIR/bin/qmd" <<'EOF_INNER'
#!/usr/bin/env bash
exit 0
EOF_INNER
chmod +x "$WORKDIR/bin/qmd"
# stub: curl (用于网络探测)
cat > "$WORKDIR/bin/curl" <<'EOF_INNER'
#!/usr/bin/env bash
if [[ "$*" == *"huggingface.co"* ]]; then
exit 1
fi
if [[ "$*" == *"hf-mirror.com"* ]]; then
exit 0
fi
exit 0
EOF_INNER
chmod +x "$WORKDIR/bin/curl"
export HOME="$WORKDIR/home"
export PATH="$WORKDIR/bin:$PATH"
export TERM=xterm
# minimal openclaw.json
cat > "$HOME/.openclaw/openclaw.json" <<'JSON'
{
"memory": {
"backend": "qmd",
"qmd": {"command": "qmd", "includeDefaultMemory": true}
},
"agents": {
"defaults": {
"memorySearch": {
"provider": "local",
"local": {
"modelPath": "hf:ggml-org/embeddinggemma-300M-GGUF/embeddinggemma-300M-Q8_0.gguf"
}
}
}
}
}
JSON
# mock index sqlite
mkdir -p "$HOME/.openclaw/workspace/memory"
touch "$HOME/.openclaw/workspace/memory/index.sqlite"
# mock memory files
cat > "$HOME/.openclaw/workspace/MEMORY.md" <<'TXT'
# MEMORY
line1
line2
line3
TXT
cat > "$HOME/.openclaw/workspace/memory/2026-03-11.md" <<'TXT'
# 2026-03-11
foo
bar
TXT
source "$WORKDIR/harness.sh"
run_menu() {
local input="$1"
local title="$2"
local outfile="$3"
echo "[TEST] $title"
printf "%b" "$input" | openclaw_memory_menu >"$outfile"
}
# 1) 状态展示(直接返回)
run_menu "0\n" "status" "$WORKDIR/out_status.txt"
# 2) 更新索引(增量)
run_menu "1\nyes\n\n\n0\n" "index" "$WORKDIR/out_index.txt"
# 3) 查看记忆文件(列表+查看)
run_menu "2\n1\n\n\n\n\n0\n0\n" "files" "$WORKDIR/out_files.txt"
# 4) 索引修复(检测到 includeDefaultMemory=false → 恢复为 true 并重建)
echo "false" > "$HOME/.openclaw/includeDefaultMemory"
run_menu "3\ny\n\n\n0\n" "fix" "$WORKDIR/out_fix.txt"
# 5) 记忆方案(自动推荐并取消)
run_menu "4\n1\n\n0\n0\n" "scheme" "$WORKDIR/out_scheme.txt"
# 6) Store 缺失时仍执行 rebuild
: > "$HOME/.openclaw/index_calls"
OPENCLAW_STORE="~/.openclaw/workspace/memory/missing.sqlite" \
openclaw_memory_rebuild_index_safe >"$WORKDIR/out_missing.txt"
export WORKDIR
python3 - <<'PY'
import glob
import os
flag_path = os.path.expanduser('~/.openclaw/includeDefaultMemory')
if not os.path.exists(flag_path):
raise SystemExit('includeDefaultMemory not updated')
flag = open(flag_path, 'r', encoding='utf-8').read().strip()
if flag != 'true':
raise SystemExit('includeDefaultMemory not restored to true')
# ensure backup created
baks = glob.glob(os.path.expanduser('~/.openclaw/workspace/memory/index.sqlite.bak.*'))
if not baks:
raise SystemExit('index sqlite backup not created')
# ensure missing store still triggers force index
calls_path = os.path.expanduser('~/.openclaw/index_calls')
if not os.path.exists(calls_path):
raise SystemExit('index calls not recorded')
with open(calls_path, 'r', encoding='utf-8') as fh:
calls = fh.read()
if 'memory index' not in calls or '--force' not in calls:
raise SystemExit('force index not called for missing store')
# ensure gateway restart called after rebuild
gw_calls_path = os.path.expanduser('~/.openclaw/gateway_calls')
if not os.path.exists(gw_calls_path):
raise SystemExit('gateway restart not recorded')
with open(gw_calls_path, 'r', encoding='utf-8') as fh:
gw_calls = fh.read()
if 'gateway restart' not in gw_calls:
raise SystemExit('gateway restart not called after rebuild')
# ensure warning emitted
with open(os.path.join(os.environ['WORKDIR'], 'out_missing.txt'), 'r', encoding='utf-8') as fh:
out = fh.read()
if 'Store 原始值' not in out:
raise SystemExit('missing store warning not found')
if '索引已重建并自动重启网关' not in out:
raise SystemExit('auto restart message not found')
print('SMOKE_OK')
PY

View File

@@ -0,0 +1,150 @@
#!/usr/bin/env bash
set -euo pipefail
REPO_ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
SCRIPT="$REPO_ROOT/kejilion.sh"
WORKDIR="${TMPDIR:-/tmp}/openclaw-memory-multi-agent-test-$$"
mkdir -p "$WORKDIR/bin" "$WORKDIR/home/.openclaw/workspace/memory"
KEEP_WORKDIR=${KEEP_WORKDIR:-false}
trap '[ "$KEEP_WORKDIR" = "true" ] || rm -rf "$WORKDIR"' EXIT
cat > "$WORKDIR/harness.sh" <<'EOF_INNER'
#!/usr/bin/env bash
set -euo pipefail
break_end() { return 0; }
send_stats() { return 0; }
EOF_INNER
awk 'BEGIN{p=0} /openclaw_memory_config_file\(\) \{/{p=1} /openclaw_permission_config_file\(\) \{/{p=0} p{print}' "$SCRIPT" >> "$WORKDIR/harness.sh"
chmod +x "$WORKDIR/harness.sh"
cat > "$WORKDIR/bin/openclaw" <<'EOF_INNER'
#!/usr/bin/env bash
set -euo pipefail
cmd="$*"
if [[ "$cmd" == "agents list --json" ]]; then
cat <<'JSON'
[
{"id":"main","workspace":"~/.openclaw/workspace"},
{"id":"work","workspace":"~/.openclaw/workspace-work"}
]
JSON
exit 0
fi
if [[ "$cmd" == "config get"* ]]; then
key="$3"
case "$key" in
memory.backend) echo "qmd" ;;
memory.qmd.command) echo "qmd" ;;
agents.defaults.memorySearch.provider) echo "local" ;;
agents.defaults.memorySearch.local.modelPath) echo "$HOME/.openclaw/models/embedding/model.gguf" ;;
*) echo "" ;;
esac
exit 0
fi
if [[ "$cmd" == "config set"* || "$cmd" == "config unset"* ]]; then
exit 0
fi
if [[ "$1" == "memory" && "$2" == "status" ]]; then
agent="main"
while [ $# -gt 0 ]; do
if [ "$1" = "--agent" ] && [ $# -ge 2 ]; then
agent="$2"
shift 2
continue
fi
shift
done
if [ "$agent" = "work" ]; then
cat <<TXT
Provider: qmd (requested: qmd)
Vector: ready
Indexed: 3/2 files
Workspace: $HOME/.openclaw/workspace-work
Store: $HOME/.openclaw/workspace-work/memory/index.sqlite
TXT
else
cat <<TXT
Provider: qmd (requested: qmd)
Vector: ready
Indexed: 5/4 files
Workspace: $HOME/.openclaw/workspace
Store: $HOME/.openclaw/workspace/memory/index.sqlite
TXT
fi
exit 0
fi
if [[ "$1" == "memory" && "$2" == "index" ]]; then
echo "$cmd" >> "$HOME/.openclaw/index_calls"
exit 0
fi
if [[ "$1" == "gateway" && "$2" == "restart" ]]; then
echo "$cmd" >> "$HOME/.openclaw/gateway_calls"
exit 0
fi
echo "mock openclaw $*"
EOF_INNER
chmod +x "$WORKDIR/bin/openclaw"
export HOME="$WORKDIR/home"
export PATH="$WORKDIR/bin:$PATH"
export TERM=xterm
mkdir -p "$HOME/.openclaw/workspace/memory" "$HOME/.openclaw/workspace-work/memory"
touch "$HOME/.openclaw/workspace/memory/index.sqlite" "$HOME/.openclaw/workspace-work/memory/index.sqlite"
cat > "$HOME/.openclaw/openclaw.json" <<'JSON'
{
"agents": {
"list": [
{"id": "work", "workspace": "~/.openclaw/workspace-work"}
]
}
}
JSON
cat > "$HOME/.openclaw/workspace/MEMORY.md" <<'TXT'
# main memory
TXT
cat > "$HOME/.openclaw/workspace-work/MEMORY.md" <<'TXT'
# work memory
TXT
cat > "$HOME/.openclaw/workspace-work/memory/2026-03-23.md" <<'TXT'
# work note
TXT
source "$WORKDIR/harness.sh"
openclaw_memory_render_status > "$WORKDIR/out_status.txt"
openclaw_memory_file_render_list > "$WORKDIR/out_files.txt"
openclaw_memory_prepare_workspace_all > "$WORKDIR/out_prepare.txt"
openclaw_memory_rebuild_index_all > "$WORKDIR/out_rebuild.txt"
export WORKDIR
python3 - <<'PY'
import os
from pathlib import Path
workdir = Path(os.environ['WORKDIR'])
home = workdir / 'home'
status_out = (workdir / 'out_status.txt').read_text(encoding='utf-8')
files_out = (workdir / 'out_files.txt').read_text(encoding='utf-8')
prepare_out = (workdir / 'out_prepare.txt').read_text(encoding='utf-8')
rebuild_out = (workdir / 'out_rebuild.txt').read_text(encoding='utf-8')
index_calls = (home / '.openclaw' / 'index_calls').read_text(encoding='utf-8')
gateway_calls = (home / '.openclaw' / 'gateway_calls').read_text(encoding='utf-8')
if 'Agent: main' not in status_out or 'Agent: work' not in status_out:
raise SystemExit('multi-agent status missing agent sections')
if 'main/MEMORY.md' not in files_out or 'work/MEMORY.md' not in files_out:
raise SystemExit('multi-agent file list missing memory files')
if 'work/memory/2026-03-23.md' not in files_out:
raise SystemExit('work daily memory file missing')
if '检查并准备 2 个智能体工作区' not in prepare_out:
raise SystemExit('prepare summary missing agent count')
if '--agent main --force' not in index_calls or '--agent work --force' not in index_calls:
raise SystemExit('force index not called for all agents')
if 'gateway restart' not in gateway_calls:
raise SystemExit('gateway restart not called after multi-agent rebuild')
if '已为 2 个智能体重建索引' not in rebuild_out:
raise SystemExit('multi-agent rebuild summary missing')
print('SMOKE_OK')
PY

View File

@@ -0,0 +1,132 @@
#!/usr/bin/env bash
set -euo pipefail
REPO_ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
SCRIPT="$REPO_ROOT/kejilion.sh"
WORKDIR="${TMPDIR:-/tmp}/openclaw-plugin-skill-menu-test-$$"
mkdir -p "$WORKDIR/bin" "$WORKDIR/home/.openclaw/workspace/skills" "$WORKDIR/home/.openclaw"
KEEP_WORKDIR=${KEEP_WORKDIR:-false}
trap '[ "$KEEP_WORKDIR" = "true" ] || rm -rf "$WORKDIR"' EXIT
cat > "$WORKDIR/harness.sh" <<'EOF_INNER'
#!/usr/bin/env bash
set -euo pipefail
break_end() { return 0; }
send_stats() { return 0; }
start_gateway() { openclaw gateway stop; openclaw gateway start; }
EOF_INNER
# 抽取插件/技能管理实现
awk 'BEGIN{p=0} /resolve_openclaw_plugin_id\(\) \{/{p=1} /openclaw_json_get_bool\(\) \{/{p=0} p{print}' "$SCRIPT" >> "$WORKDIR/harness.sh"
chmod +x "$WORKDIR/harness.sh"
# stub: openclaw
cat > "$WORKDIR/bin/openclaw" <<'EOF_INNER'
#!/usr/bin/env bash
cmd="$*"
if [[ "$cmd" == "plugins list" ]]; then
cat <<TXT
feishu disabled
telegram enabled
TXT
exit 0
fi
if [[ "$cmd" == "plugins enable"* ]]; then
echo "enabled $*" >> "${TEST_LOG:-/tmp/openclaw-plugin.log}"
exit 0
fi
if [[ "$cmd" == "plugins install"* ]]; then
echo "install $*" >> "${TEST_LOG:-/tmp/openclaw-plugin.log}"
exit 0
fi
if [[ "$cmd" == "plugins disable"* ]]; then
echo "disable $*" >> "${TEST_LOG:-/tmp/openclaw-plugin.log}"
exit 0
fi
if [[ "$cmd" == "plugins uninstall"* ]]; then
# 模拟预装卸载失败
if [[ "$cmd" == *"feishu"* ]]; then
exit 1
fi
echo "uninstall $*" >> "${TEST_LOG:-/tmp/openclaw-plugin.log}"
exit 0
fi
if [[ "$cmd" == "skills list" ]]; then
echo "skill1"
exit 0
fi
if [[ "$cmd" == "gateway stop" ]]; then
echo "gateway stop" >> "${TEST_LOG:-/tmp/openclaw-plugin.log}"
exit 0
fi
if [[ "$cmd" == "gateway start" ]]; then
echo "gateway start" >> "${TEST_LOG:-/tmp/openclaw-plugin.log}"
exit 0
fi
echo "mock openclaw $*" >> "${TEST_LOG:-/tmp/openclaw-plugin.log}"
EOF_INNER
chmod +x "$WORKDIR/bin/openclaw"
# stub: npx
cat > "$WORKDIR/bin/npx" <<'EOF_INNER'
#!/usr/bin/env bash
if [[ "$*" == *"clawhub uninstall"* ]]; then
echo "uninstall $*" >> "${TEST_LOG:-/tmp/openclaw-plugin.log}"
exit 0
fi
if [[ "$*" == *"clawhub install"* ]]; then
echo "install $*" >> "${TEST_LOG:-/tmp/openclaw-plugin.log}"
exit 0
fi
exit 0
EOF_INNER
chmod +x "$WORKDIR/bin/npx"
export HOME="$WORKDIR/home"
export PATH="$WORKDIR/bin:$PATH"
export TERM=xterm
export TEST_LOG="$WORKDIR/test.log"
cat > "$HOME/.openclaw/openclaw.json" <<'JSON'
{"plugins":{"allow":["telegram","feishu"]}}
JSON
# 模拟用户技能目录
mkdir -p "$HOME/.openclaw/workspace/skills/skillA"
source "$WORKDIR/harness.sh"
# 插件安装:多输入,确保只重启一次
printf "1\nfeishu telegram\n0\n" | install_plugin > "$WORKDIR/plugin_install.out"
# 插件删除:多输入,确保删除流程与 allowlist 移除
printf "2\nfeishu telegram\n0\n" | install_plugin > "$WORKDIR/plugin_delete.out"
# 技能删除:二次确认 + 多输入,删除目录
printf "2\nskillA skillB\ny\n0\n" | install_skill > "$WORKDIR/skill_delete.out"
# 断言start_gateway 只调用一次/每次菜单
start_count=$(grep -c "gateway start" "$TEST_LOG" || true)
if [ "$start_count" -ne 3 ]; then
echo "Expected 3 gateway starts (plugin install/delete + skill delete), got $start_count"
exit 1
fi
# 断言:用户技能目录已删除
if [ -d "$HOME/.openclaw/workspace/skills/skillA" ]; then
echo "Skill directory was not removed"
exit 1
fi
# 断言plugins.allow 移除
if grep -q '"feishu"' "$HOME/.openclaw/openclaw.json"; then
echo "feishu still in plugins.allow"
exit 1
fi
# 断言:输出汇总
grep -q "操作汇总" "$WORKDIR/plugin_install.out"
grep -q "操作汇总" "$WORKDIR/plugin_delete.out"
grep -q "操作汇总" "$WORKDIR/skill_delete.out"
echo "SMOKE_OK"

View File

@@ -0,0 +1,66 @@
#!/usr/bin/env bash
set -euo pipefail
repo_root=$(cd "$(dirname "$0")/../.." && pwd)
script="$repo_root/kejilion.sh"
workdir=$(mktemp -d)
trap 'rm -rf "$workdir"' EXIT
export HOME="$workdir/home"
mkdir -p "$HOME/.openclaw/agents/main/sessions"
mkdir -p "$HOME/.openclaw/agents/work/sessions"
funcs_file="$workdir/funcs.sh"
{
awk '/^[[:space:]]*openclaw_get_agents_dir\(\)/,/^[[:space:]]*\}$/' "$script"
echo
awk '/^[[:space:]]*openclaw_sync_sessions_model\(\)/,/^[[:space:]]*\}$/' "$script"
} > "$funcs_file"
cat > "$HOME/.openclaw/agents/main/sessions/sessions.json" <<'JSON'
{
"agent:main:telegram:direct:123": {
"sessionId": "abc-123",
"modelOverride": "gpt-4",
"providerOverride": "openai"
},
"agent:main:main": {
"sessionId": "def-456"
}
}
JSON
cat > "$HOME/.openclaw/agents/work/sessions/sessions.json" <<'JSON'
{
"agent:work:slack:channel:C789": {
"sessionId": "ghi-789",
"modelOverride": "claude-3",
"providerOverride": "anthropic"
}
}
JSON
source "$funcs_file"
openclaw_sync_sessions_model "cli-api/gpt-5-pro"
echo "=== VERIFICATION ==="
main_file="$HOME/.openclaw/agents/main/sessions/sessions.json"
work_file="$HOME/.openclaw/agents/work/sessions/sessions.json"
main_model=$(jq -r '."agent:main:telegram:direct:123".modelOverride' "$main_file")
main_provider=$(jq -r '."agent:main:telegram:direct:123".providerOverride' "$main_file")
main2_model=$(jq -r '."agent:main:main".modelOverride' "$main_file")
main2_provider=$(jq -r '."agent:main:main".providerOverride // "null"' "$main_file")
work_model=$(jq -r '."agent:work:slack:channel:C789".modelOverride' "$work_file")
work_provider=$(jq -r '."agent:work:slack:channel:C789".providerOverride' "$work_file")
[ "$main_model" = "gpt-5-pro" ] || { echo "FAIL: main model (got: $main_model)"; exit 1; }
[ "$main_provider" = "cli-api" ] || { echo "FAIL: main provider (got: $main_provider)"; exit 1; }
[ "$main2_model" = "gpt-5-pro" ] || { echo "FAIL: main2 model (got: $main2_model)"; exit 1; }
[ "$main2_provider" = "cli-api" ] || { echo "FAIL: main2 provider (got: $main2_provider)"; exit 1; }
[ "$work_model" = "gpt-5-pro" ] || { echo "FAIL: work model (got: $work_model)"; exit 1; }
[ "$work_provider" = "cli-api" ] || { echo "FAIL: work provider (got: $work_provider)"; exit 1; }
echo "PASS: openclaw_sync_sessions_model smoke"

View File

@@ -0,0 +1,77 @@
#!/usr/bin/env bash
set -euo pipefail
repo_root=$(cd "$(dirname "$0")/.." && pwd)
script="$repo_root/kejilion.sh"
workdir=$(mktemp -d)
trap 'rm -rf "$workdir"' EXIT
export HOME="$workdir/home"
mkdir -p "$HOME/.openclaw"
python_extract() {
python3 - "$script" <<'PY'
import re, sys
text = open(sys.argv[1], encoding='utf-8').read()
patterns = [
r'openclaw_get_config_file\(\) \{.*?\n\t\t\}',
r'openclaw_memory_config_file\(\) \{.*?\n\t\}',
r'openclaw_permission_config_file\(\) \{.*?\n\t\}',
]
for pat in patterns:
m = re.search(pat, text, re.S)
if not m:
raise SystemExit(f'missing pattern: {pat}')
print(m.group(0))
print()
PY
}
snippet="$workdir/snippet.sh"
{
echo '#!/usr/bin/env bash'
echo 'set -euo pipefail'
python_extract
cat <<'SH'
main() {
echo "CFG=$(openclaw_get_config_file)"
echo "MEM=$(openclaw_memory_config_file)"
echo "PERM=$(openclaw_permission_config_file)"
}
main "$@"
SH
} > "$snippet"
chmod +x "$snippet"
assert_eq() {
local got="$1" expected="$2" msg="$3"
if [ "$got" != "$expected" ]; then
echo "ASSERT FAIL: $msg"
echo "got: $got"
echo "exp: $expected"
exit 1
fi
}
user_cfg="$HOME/.openclaw/openclaw.json"
root_cfg="$workdir/root/.openclaw/openclaw.json"
mkdir -p "$(dirname "$root_cfg")"
out=$("$snippet")
assert_eq "$(printf '%s\n' "$out" | sed -n '1p')" "CFG=$user_cfg" 'default config path should be user path'
assert_eq "$(printf '%s\n' "$out" | sed -n '2p')" "MEM=$user_cfg" 'memory config path should follow helper'
assert_eq "$(printf '%s\n' "$out" | sed -n '3p')" "PERM=$user_cfg" 'permission config path should follow helper'
printf '{}\n' > "$root_cfg"
rm -f "$user_cfg"
out=$("$snippet")
assert_eq "$(printf '%s\n' "$out" | sed -n '1p')" "CFG=$user_cfg" 'non-root should not silently fallback to root config'
assert_eq "$(printf '%s\n' "$out" | sed -n '2p')" "MEM=$user_cfg" 'memory config should stay in user path for non-root'
assert_eq "$(printf '%s\n' "$out" | sed -n '3p')" "PERM=$user_cfg" 'permission config should stay in user path for non-root'
mkdir -p "$(dirname "$user_cfg")"
printf '{"user":true}\n' > "$user_cfg"
out=$("$snippet")
assert_eq "$(printf '%s\n' "$out" | sed -n '1p')" "CFG=$user_cfg" 'user config should take precedence'
echo 'PASS: openclaw config path resolution smoke'

163
tests_openclaw_manager_smoke.sh Executable file
View File

@@ -0,0 +1,163 @@
#!/usr/bin/env bash
set -euo pipefail
REPO_DIR="$(cd "$(dirname "$0")" && pwd)"
SCRIPT="$REPO_DIR/kejilion.sh"
WORKDIR="${TMPDIR:-/tmp}/openclaw-manager-test-$$"
mkdir -p "$WORKDIR/bin" "$WORKDIR/home/.openclaw"
trap 'rm -rf "$WORKDIR"' EXIT
cat > "$WORKDIR/harness.sh" <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
install() { return 0; }
break_end() { :; }
send_stats() { :; }
EOF
sed -n '10597,10766p' "$SCRIPT" >> "$WORKDIR/harness.sh"
printf '\n' >> "$WORKDIR/harness.sh"
sed -n '12815,13534p' "$SCRIPT" >> "$WORKDIR/harness.sh"
printf '\n' >> "$WORKDIR/harness.sh"
sed -n '13781,13825p' "$SCRIPT" >> "$WORKDIR/harness.sh"
printf '\n' >> "$WORKDIR/harness.sh"
chmod +x "$WORKDIR/harness.sh"
cat > "$WORKDIR/bin/curl" <<'EOF'
#!/usr/bin/env bash
for arg in "$@"; do
if [ "$arg" = "-I" ]; then
exit 0
fi
done
if [[ "$*" == *"/models"* ]]; then
cat <<JSON
{"data":[{"id":"gpt-5.4"},{"id":"gpt-5.3-codex"},{"id":"claude-opus-4-6-thinking"}]}
JSON
else
echo "US"
fi
EOF
chmod +x "$WORKDIR/bin/curl"
cat > "$WORKDIR/bin/openclaw" <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
cmd="${1:-}"
shift || true
log_file="$HOME/.openclaw/mock_openclaw.log"
echo "openclaw $cmd $*" >> "$log_file"
case "$cmd" in
dashboard)
echo "Dashboard: http://127.0.0.1:18789/#token=deadbeef"
;;
config)
sub="${1:-}"
shift || true
config_file="$HOME/.openclaw/mock_config.env"
touch "$config_file"
case "$sub" in
set)
key="$1"
shift || true
value="$*"
grep -v "^${key}=" "$config_file" > "${config_file}.tmp" || true
echo "${key}=${value}" >> "${config_file}.tmp"
mv "${config_file}.tmp" "$config_file"
;;
get)
key="$1"
if grep -q "^${key}=" "$config_file"; then
awk -F'=' -v k="$key" '$1==k {print substr($0, index($0, "=")+1); exit}' "$config_file"
fi
;;
unset)
key="$1"
grep -v "^${key}=" "$config_file" > "${config_file}.tmp" || true
mv "${config_file}.tmp" "$config_file"
;;
*)
echo "mock openclaw config $sub $*"
;;
esac
;;
memory)
sub="${1:-}"
shift || true
case "$sub" in
status)
echo "Provider: builtin"
echo "Vector: ok"
echo "Indexed: 0/0"
echo "Workspace: $HOME/.openclaw/workspace"
;;
index)
echo "mock memory index $*"
;;
*)
echo "mock openclaw memory $sub $*"
;;
esac
;;
gateway)
sub="${1:-}"
shift || true
if [ "$sub" = "restart" ]; then
echo "mock gateway restart"
else
echo "mock openclaw gateway $sub $*"
fi
;;
*)
echo "mock openclaw $cmd $*"
;;
esac
EOF
chmod +x "$WORKDIR/bin/openclaw"
export HOME="$WORKDIR/home"
export PATH="$WORKDIR/bin:$PATH"
cat > "$HOME/.openclaw/openclaw.json" <<'JSON'
{"models":{"mode":"merge","providers":{}}}
JSON
mkdir -p /home/web/conf.d
cat > /home/web/conf.d/test-openclaw.conf <<'EOF'
server {
listen 443 ssl;
server_name claw.example.com;
location / {
proxy_pass http://127.0.0.1:18789;
}
}
EOF
source "$WORKDIR/harness.sh"
echo '[TEST] add-all-models-from-provider'
add-all-models-from-provider "cli-api" "https://example.com/v1" "dummy-token" >/tmp/add-models.out
jq -e '.models.providers["cli-api"].models | length == 3' "$HOME/.openclaw/openclaw.json" >/dev/null
jq -r '.models.providers["cli-api"].models[].id' "$HOME/.openclaw/openclaw.json"
echo '[TEST] openclaw_find_webui_domain'
openclaw_find_webui_domain
echo '[TEST] openclaw_show_webui_addr'
openclaw_show_webui_addr
echo '[TEST] openclaw_memory_auto_setup_run local'
mkdir -p "$HOME/.openclaw/models/embedding"
touch "$HOME/.openclaw/models/embedding/embeddinggemma-300M-Q8_0.gguf"
echo "memory.local=legacy" > "$HOME/.openclaw/mock_config.env"
printf "yes\n" | openclaw_memory_auto_setup_run "local" >/tmp/memory-auto.out
grep -q '^memory.backend=builtin' "$HOME/.openclaw/mock_config.env"
grep -q '^agents.defaults.memorySearch.provider=local' "$HOME/.openclaw/mock_config.env"
if grep -q '^memory.local=' "$HOME/.openclaw/mock_config.env"; then
echo "memory.local should be removed"
exit 1
fi
grep -q 'openclaw memory index --force' "$HOME/.openclaw/mock_openclaw.log"
grep -q 'openclaw gateway restart' "$HOME/.openclaw/mock_openclaw.log"
echo 'SMOKE_OK'

92
translate.py Normal file
View File

@@ -0,0 +1,92 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from deep_translator import GoogleTranslator
import re
import os
import sys
def is_chinese(text):
return bool(re.search(r'[\u4e00-\u9fff]', text))
def translate_text(text, target_lang):
if not text.strip() or not is_chinese(text):
return text
try:
# 过滤掉一些不该翻译的特殊符号
clean_text = text.strip()
result = GoogleTranslator(source='zh-CN', target=target_lang).translate(clean_text)
return result
except Exception as e:
print(f"\n[Error] {e}")
return text
def process_content_with_vars(content, target_lang):
"""
核心逻辑:保护 ${var} 和 $var翻译其中的中文部分
"""
# 匹配 ${var} 或 $var (字母数字下划线)
parts = re.split(r'(\$\{\w+\}|\$\w+)', content)
translated_parts = []
for p in parts:
if p.startswith('$'): # 变量部分,保持原样
translated_parts.append(p)
elif is_chinese(p): # 中文部分,翻译
translated_parts.append(translate_text(p, target_lang))
else: # 其他英文/符号,保持原样
translated_parts.append(p)
return "".join(translated_parts)
def universal_translator(line, target_lang):
"""
通用翻译引擎:识别行内所有引号内容并翻译
"""
# 1. 保护注释行
leading_space = re.match(r'^(\s*)', line).group(1)
stripped = line.strip()
if stripped.startswith('#'):
comment_content = stripped[1:].strip()
if is_chinese(comment_content):
return f"{leading_space}# {translate_text(comment_content, target_lang)}\n"
return line
# 2. 识别所有引号内的内容 (双引号或单引号)
# 使用正则匹配引号对,同时处理转义引号 \"
def replacer(match):
quote_type = match.group(1) # ' 或 "
content = match.group(2) # 引号内的文本内容
if is_chinese(content):
# 翻译内容,但保护里面的变量
translated = process_content_with_vars(content, target_lang)
return f"{quote_type}{translated}{quote_type}"
return match.group(0)
# 匹配 "content" 或 'content'
new_line = re.sub(r'([\'"])(.*?)(?<!\\)\1', replacer, line)
return new_line
def translate_file(input_file, output_file, target_lang):
print(f"Translating to {target_lang}...")
if not os.path.exists(input_file): return False
with open(input_file, 'r', encoding='utf-8') as f:
lines = f.readlines()
total = len(lines)
with open(output_file, 'w', encoding='utf-8') as f_out:
for i, line in enumerate(lines):
if (i + 1) % 10 == 0 or i + 1 == total:
print(f"\rProgress: {(i+1)/total*100:.1f}%", end='')
# 只要行内有中文,就尝试用通用引擎翻译
if is_chinese(line):
f_out.write(universal_translator(line, target_lang))
else:
f_out.write(line)
print(f"\n{target_lang} Success.")
return True
if __name__ == "__main__":
input_file = 'kejilion.sh'
langs = {'en': 'en', 'tw': 'zh-TW', 'kr': 'ko', 'jp': 'ja'}
for dir_name, lang_code in langs.items():
translate_file(input_file, f'{dir_name}/kejilion.sh', lang_code)

22252
tw/kejilion.sh Normal file

File diff suppressed because it is too large Load Diff

76
tw/to-tw.py Normal file
View File

@@ -0,0 +1,76 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from deep_translator import GoogleTranslator
import re
import os
def is_chinese(text):
return bool(re.search(r'[\u4e00-\u9fff]', text))
def translate_text(text):
try:
return GoogleTranslator(source='zh-CN', target='zh-TW').translate(text)
except Exception as e:
print(f"\nTranslation error: {e}")
return text
def translate_line_preserving_variables(line):
"""
Translate only Chinese parts in echo/read/send_stats commands, excluding shell variables
"""
# Match double or single quoted strings
def repl(match):
full_string = match.group(0)
quote = full_string[0]
content = full_string[1:-1]
# Split by variable expressions
parts = re.split(r'(\$\{?\w+\}?)', content)
translated_parts = [
translate_text(p) if is_chinese(p) else p
for p in parts
]
return quote + ''.join(translated_parts) + quote
return re.sub(r'(?:\'[^\']*\'|"[^"]*")', repl, line)
def translate_file(input_file, output_file):
total_lines = sum(1 for _ in open(input_file, 'r', encoding='utf-8'))
processed_lines = 0
with open(input_file, 'r', encoding='utf-8') as f_in, \
open(output_file, 'w', encoding='utf-8') as f_out:
for line in f_in:
processed_lines += 1
progress = processed_lines / total_lines * 100
print(f"\rProcessing: {progress:.1f}% ({processed_lines}/{total_lines})", end='')
leading_space = re.match(r'^(\s*)', line).group(1)
stripped = line.strip()
if stripped.startswith('#') and is_chinese(stripped):
comment_mark = '#'
comment_text = stripped[1:].strip()
if comment_text:
translated = translate_text(comment_text)
f_out.write(f"{leading_space}{comment_mark} {translated}\n")
else:
f_out.write(line)
elif any(cmd in stripped for cmd in ['echo', 'read', 'send_stats']) and is_chinese(stripped):
translated_line = translate_line_preserving_variables(line)
f_out.write(translated_line)
else:
f_out.write(line)
print("\nTranslation completed.")
print(f"Original file size: {os.path.getsize(input_file)} bytes")
print(f"Translated file size: {os.path.getsize(output_file)} bytes")
if __name__ == "__main__":
input_file = 'kejilion.sh'
output_file = 'kejilion_tw.sh'
translate_file(input_file, output_file)

423
update_log.sh Normal file
View File

@@ -0,0 +1,423 @@
clear
echo "脚本更新日志"
echo "------------------------"
echo "2023-8-13 v1.0.3"
echo "1.甲骨文云的DD脚本添加了Ubuntu 20.04的重装选项。"
echo "2.LDNMP建站开放了苹果CMS网站的搭建功能."
echo "3.系统信息查询,增加了内核版本显示,美化了界面。"
echo "4.甲骨文脚本中添加了开启ROOT登录的选项。"
echo "------------------------"
echo "2023-8-13 v1.0.4"
echo "1.LDNMP建站开放了独角数卡网站的搭建功能."
echo "2.LDNMP建站优化了备份全站到远端服务器的稳定性."
echo "3.Docker管理全局状态信息添加了所有docker卷的显示."
echo "------------------------"
echo "2023-8-14 v1.1"
echo "Docker管理器全面升级体验前所未有"
echo "-加入了docker容器管理面板"
echo "-加入了docker镜像管理面板"
echo "-加入了docker网络管理面板"
echo "-加入了docker卷管理面板"
echo "-删除docker时追加确认信息拒绝误操作"
echo "------------------------"
echo "2023-8-14 v1.2"
echo "1.新增了11选项加入了常用面板工具合集"
echo "-支持安装各种面板,包括: 宝塔宝塔国际版1panelNginx Proxy Manager等等满足更多人群的使用需求"
echo "2.优化了菜单效果"
echo "------------------------"
echo "2023-8-14 v1.3"
echo "新增了12选项我的工作区功能"
echo "-将为你提供5个后台运行的工作区用来执行后台任务。即使你断开SSH也不会中断"
echo "-非常有意思的功能,快去试试吧!"
echo "------------------------"
echo "2023-8-14 v1.3.2"
echo "新增了13选项系统工具"
echo "科技lion一键脚本可以通过设置快捷键唤醒打开了我设置的k作为脚本打开的快捷键无需复制长命令了"
echo "加入了ROOT密码修改切换成ROOT登录模式"
echo "系统设置中还有很多功能没开发,敬请期待!"
echo "------------------------"
echo "2023-8-15 v1.4"
echo "全面适配Centos系统实现UbuntuDebianCentos三大主流系统的适配"
echo "优化LDNMP中PHP输入数据最大时间解决WordPress网站导入部分主题失败的问题"
echo "------------------------"
echo "2023-8-15 v1.4.1"
echo "选项13系统工具中加入了安装Python最新版的选项感谢群友春风得意马蹄疾的投稿很好用"
echo "------------------------"
echo "2023-8-15 v1.4.2"
echo "docker管理中增加容器日志查看"
echo "选项13系统工具中加入了留言板的选项可以留下你的宝贵意见也可以在这里聊天贼好玩"
echo "------------------------"
echo "2023-8-15 v1.4.5"
echo "优化了信息查询运行效率"
echo "信息查询新增了地理位置显示"
echo "优化了脚本内系统判断机制!"
echo "------------------------"
echo "2023-8-16 v1.4.6"
echo "LDNMP建站中加入了删除站点删除数据库功能"
echo "------------------------"
echo "2023-8-16 v1.4.7"
echo "选项11中增加了一键搭建alist多存储文件列表工具的"
echo "选项11中增加了一键搭建网页版乌班图远程桌面"
echo "选项13中增加了开放所有端口功能"
echo "------------------------"
echo "2023-8-16 v1.4.8"
echo "系统信息查询中,终于可以显示总流量消耗了!总接收和总发送两个信息"
echo "------------------------"
echo "2023-8-17 v1.4.9"
echo "系统工具中新增SSH端口修改功能"
echo "系统工具中新增优化DNS地址功能"
echo "------------------------"
echo "2023-8-18 v1.5"
echo "系统性优化了代码,去除了无效的代码与空格"
echo "系统信息查询添加了系统时间"
echo "禁用ROOT账户创建新的账户更安全"
echo "------------------------"
echo "2023-8-18 v1.5.1"
echo "LDNMP加入了安装bingchatAI聊天网站"
echo "面板工具中添加了哪吒探针脚本整合"
echo "------------------------"
echo "2023-8-18 v1.5.2"
echo "LDNMP加入了更新LDNMP选项"
echo "------------------------"
echo "2023-8-19 v1.5.3"
echo "面板工具添加安装QB离线BT磁力下载面板"
echo "优化IP获取源"
echo "------------------------"
echo "2023-8-20 v1.5.4"
echo "面板工具已安装的工具支持状态检测,可以进行删除了!"
echo "------------------------"
echo "2023-8-21 v1.5.5"
echo "系统工具中添加优先ipv4/ipv6选项"
echo "系统工具中添加查看端口占用状态选项"
echo "------------------------"
echo "2023-8-21 v1.5.6"
echo "LDNMP建站添加了定时自动远程备份功能"
echo "------------------------"
echo "2023-8-22 v1.5.7"
echo "面板工具增加了邮件服务器搭建请确保服务器的25.80.443开放"
echo "------------------------"
echo "2023-8-23 v1.5.8"
echo "面板工具增加了聊天系统搭建"
echo "------------------------"
echo "2023-8-24 v1.5.9"
echo "面板工具增加了禅道项目管理软件搭建"
echo "------------------------"
echo "2023-8-24 v1.6"
echo "面板工具增加了青龙面板搭建"
echo "调整了面板工具列表的排版显示效果"
echo "------------------------"
echo "2023-8-27 v1.6.1"
echo "LDNMP大幅优化安装体验添加安装进度条和百分比显示太刁了"
echo "------------------------"
echo "2023-8-28 v1.6.2"
echo "docker管理可以显示容器所属网络并且可以加入网络和退出网络了"
echo "------------------------"
echo "2023-8-28 v1.6.3"
echo "系统工具中增加修改虚拟内存大小的选项"
echo "系统信息查询中显示虚拟内存占用"
echo "------------------------"
echo "2023-8-29 v1.6.4"
echo "面板工具加入cloudreve网盘的搭建"
echo "面板工具加入简单图床程序搭建"
echo "------------------------"
echo "2023-8-29 v1.6.5"
echo "LDNMP加入了高逼格的flarum论坛搭建"
echo "面板工具加入简单图床程序搭建"
echo "------------------------"
echo "2023-9-1 v1.6.6"
echo "LDNMP环境安装时用户密码将随机生成提升安全性安装环境更简单"
echo "LDNMP环境安装时如果安装过docker将自动跳过节省安装时间"
echo "LDNMP环境更新WordPress到6.3.1版本"
echo "------------------------"
echo "2023-9-1 v1.6.7"
echo "添加了账户管理功能,查看当前账户列表,添加删除账户,账号权限管理等"
echo "------------------------"
echo "2023-9-4 v1.6.8"
echo "独角数卡登录时报错,显示解决办法"
echo "------------------------"
echo "2023-9-6 v1.6.9"
echo "系统工具中添加随机用户密码生成器,方便懒得想用户名和密码的小伙伴"
echo "优化了所有搭建网站与面板后的信息复制体验"
echo "------------------------"
echo "2023-9-11 v1.7"
echo "面板工具中添加emby多媒体管理系统的搭建"
echo "------------------------"
echo "2023-9-15 v1.7.1"
echo "LDNMP建站中可以搭建Bitwarden密码管理平台了"
echo "------------------------"
echo "2023-9-18 v1.7.2"
echo "LDNMP建站将站点信息查询和站点管理合并"
echo "LDNMP站点管理中添加证书重新申请和站点更换域名的功能"
echo "------------------------"
echo "2023-9-25 v1.8"
echo "LDNMP建站增加了服务器与网站防护功能防御暴力破解防御网站被攻击"
echo "------------------------"
echo "2023-9-28 v1.8.2"
echo "LDNMP建站优化了运行速度和安全性增加了频率限制"
echo "LDNMP建站优化了防御程序的高可用性"
echo "------------------------"
echo "2023-10-3 v1.8.3"
echo "系统工具增加系统时区切换功能"
echo "------------------------"
echo "2023-10-7 v1.8.4"
echo "LDNMP建站添加halo博客网站搭建"
echo "------------------------"
echo "2023-10-12 v1.8.5"
echo "LDNMP建站添加优化LDNMP环境选项可以开启高性能模式大幅提升网站性能应对高并发"
echo "------------------------"
echo "2023-10-14 v1.8.6"
echo "面板工具增加了测速流量监控面板的安装"
echo "------------------------"
echo "2023-10-16 v1.8.7"
echo "系统工具中添加开启BBR3加速功能"
echo "------------------------"
echo "2023-10-18 v1.8.8"
echo "系统工具中优化BBR3加速安装流程可根据CPU型号自行安装适合的内核版本"
echo "------------------------"
echo "2023-10-19 v1.8.9"
echo "系统工具中BBRv3功能增加了更新内核和卸载内核功能"
echo "------------------------"
echo "2023-10-21 v1.9"
echo "开放端口相关优化"
echo "解决部分系统SSH端口切换后重启失联的问题"
echo "------------------------"
echo "2023-10-26 v1.9.1"
echo "LNMP建站管理中添加了站点缓存清理功能"
echo "面板工具中卸载对应应用时添加了应用目录一并删除,删除更彻底!"
echo "------------------------"
echo "2023-10-28 v1.9.2"
echo "系统工具中修复了虚拟内存大小重启后还原的问题"
echo "------------------------"
echo "2023-11-07 v1.9.3"
echo "面板工具中增加AdGuardHome去广告软件安装和管理"
echo "------------------------"
echo "2023-11-08 v1.9.4"
echo "系统工具添加了防火墙高级管理功能可以开关端口可以IP黑白名单"
echo "未来会上线地域黑白名单等高级功能"
echo "------------------------"
echo "2023-11-09 v1.9.5"
echo "系统工具中防火墙添加udp控制"
echo "------------------------"
echo "2023-11-10 v1.9.6"
echo "测试脚本合集增加了缝合怪一条龙测试"
echo "系统信息查询中添加了系统运行时长显示"
echo "------------------------"
echo "2023-11-10 v1.9.7"
echo "LDNMP建站增加typecho轻量博客的搭建"
echo "------------------------"
echo "2023-11-16 v1.9.8"
echo "面板工具中增加了在线office办公软件安装"
echo "------------------------"
echo "2023-11-21 v1.9.9"
echo "面板工具中增加了雷池WAF防火墙程序安装"
echo "------------------------"
echo "2023-11-28 v2.0"
echo "LDNMP建站中增加仅安装nginx的选项专门服务于站点重定向和站点反向代理"
echo "精简无用的代码,优化执行效率"
echo "------------------------"
echo "2023-11-29 v2.0.1"
echo "LDNMP建站改用cerbot申请证书更稳定更快速。弃用acme"
echo "------------------------"
echo "2023-11-30 v2.0.2"
echo "面板工具修复QB无法登录问题"
echo "面板工具修复RocketChat进入后无限加载问题"
echo "系统工具中添加修改主机名功能"
echo "系统工具中添加服务器重启功能"
echo "------------------------"
echo "2023-12-04 v2.0.3"
echo "LDNMP建站过程中增加了nginx自我检测修复功能"
echo "系统工具添加更新源切换功能,请先在测试环境使用"
echo "LDNMP建站增加自定义上传静态html界面功能"
echo "------------------------"
echo "2023-12-05 v2.0.4"
echo "LDNMP建站中仅安装nginx功能添加安装成功提示更优雅直观"
echo "LDNMP建站中仅安装nginx功能支持自动更新nginx版本"
echo "优化代码细节,定义调用函数,脚本执行更简洁,提升效率"
echo "------------------------"
echo "2023-12-07 v2.0.5"
echo "LDNMP在站点数据管理中增加查看站点分析报告功能可以对网站流量进行监控与分析"
echo "主菜单添加手动更新脚本功能"
echo "------------------------"
echo "2023-12-08 v2.0.6"
echo "主菜单中更新日志和脚本更新合并,更新时可以看到更新日志,更直觉"
echo "面板工具中新增了docker管理面板portainer的安装"
echo "面板工具中新增了VScode网页版的安装"
echo "------------------------"
echo "2023-12-15 v2.0.7"
echo "系统工具中添加了定时任务的管理功能"
echo "------------------------"
echo "2023-12-16 v2.0.8"
echo "大量安装软件的代码整合更简单快速安装各类软件包"
echo "优化选项4中的常用工具安装及使用体验已安装可以提示使用方法"
echo "选项4中新增多款实用工具如btop现代化监控工具安装即用q退出"
echo "------------------------"
echo "2023-12-18 v2.0.9"
echo "优化安装代码,全局调用,安装智能检测,安装软件更快"
echo "------------------------"
echo "2023-12-19 v2.1"
echo "选项4中,新增自定义安装卸载指定的工具"
echo "优化了清理逻辑,清理系统更快更干净"
echo "优化了卸载软件包的逻辑,根据系统执行卸载,更智能"
echo "优化主菜单到二级菜单的交互,二级菜单更沉浸,二级菜单箭头引导调整"
echo "------------------------"
echo "2023-12-20 v2.1.1"
echo "史诗级代码精简屎山大扫除脚本运行更快速脚本大小缩减20%"
echo "LDNMP安装环境时增加端口检测功能端口被占用会无法安装"
echo "面板工具中添加Uptime Kuma监控工具的安装"
echo "面板工具中添加Memos网页备忘录的安装"
echo "------------------------"
echo "2023-12-23 v2.1.2"
echo "面板工具中添加潘多拉GPT镜像站安装"
echo "------------------------"
echo "2023-12-26 v2.1.3"
echo "选项4常用工具中添加跑火车屏保俄罗斯方块贪吃蛇太空入侵者三款小游戏"
echo "------------------------"
echo "2023-12-30 v2.1.4"
echo "LDNMP增加了防止源站IP泄露机制保护源站IP与证书潜在安全隐患"
echo "------------------------"
echo "2024-01-04 v2.1.5"
echo "脚本添加了启动快捷键命令行输入k可以快速启动科技lion脚本工具"
echo "------------------------"
echo "2024-01-04 v2.1.6"
echo "脚本添加了启动快捷键命令行输入k可以快速启动科技lion脚本工具"
echo "面板工具1panel增加了已安装状态支持查看面板信息修改用户密码支持卸载面板"
echo "------------------------"
echo "2024-01-06 v2.1.7"
echo "面板工具增加了nextcloud网盘的搭建"
echo "------------------------"
echo "2024-01-09 v2.1.8"
echo "LDNMP建站增加对ipv6的建站支持解析v6地址建站据说提升站点安全性性能也有提升"
echo "------------------------"
echo "2024-01-10 v2.1.9"
echo "面板工具增加QD-Today定时任务管理框架的安装"
echo "------------------------"
echo "2024-01-12 v2.2"
echo "面板工具增加了Dockge容器堆栈管理面板的安装"
echo "面板工具增加了LibreSpeed轻量级测速工具的安装"
echo "优化了脚本快捷启动输入k快速启动脚本支持任何目录下使用"
echo "------------------------"
echo "2024-01-16 v2.2.1"
echo "主菜单添加14选项VPS集群控制系统可以一键操控所有VPS执行任务。"
echo "VPS集群控制属于测试版本请用闲置机器开始体验有任何问题欢迎留言反馈"
echo "------------------------"
echo "2024-01-17 v2.2.2"
echo "面板工具增加了搜索聚合网站的安装"
echo "优化了集群控制体验,集群环境备份还原卸载等功能上线"
echo "------------------------"
echo "2024-01-18 v2.2.3"
echo "面板工具增加了私有相册系统的安装"
echo "------------------------"
echo "2024-01-21 v2.2.4"
echo "面板工具增加了PDF工具大全应用的安装"
echo "------------------------"
echo "2024-01-23 v2.2.5"
echo "优化了LDNMP建站配置时进度条体验让读条不至于卡在一个地方很久。拆分配置环节让体验更加顺畅"
echo "------------------------"
echo "2024-01-25 v2.2.6"
echo "精简了LDNMP建站镜像大小采用官方alpine精简镜像包更快更轻更安全"
echo "脚本适配alpine系统"
echo "系统工具中重装系统选项升级增加了Debian11 Debian10 ubuntu22.04 centos7 alpine3.19 windows11的安装选项"
echo "------------------------"
echo "2024-01-30 v2.2.7"
echo "LDNMP建站更新halo2的安装版本到2.11"
echo "修复alpine系统下虚拟内存重启后失效的问题"
echo "优化alpine系统下docker安装体验安装docker应用时自动识别安装docker环境"
echo "修复alpine系统下CPU占用显示异常的问题"
echo "------------------------"
echo "2024-02-1 v2.2.8"
echo "主菜单临时增加p选项与幻兽帕鲁开服脚本联动"
echo "------------------------"
echo "2024-02-5 v2.2.9"
echo "修改主机名支持alpine系统"
echo "------------------------"
echo "2024-02-8 v2.3"
echo "面板工具增加了drawio在线绘图工具的安装"
echo "------------------------"
echo "2024-02-21 v2.3.1"
echo "主菜单选项12我的工作区增加至10个更利于多线程后台任务"
echo "------------------------"
echo "2024-02-26 v2.3.2"
echo "系统工具中的选项8中一键重装系统的体验进行优化重装时展示系统重装后的用户名密码和端口号"
echo "一键重装系统增加了更多Windows版本重装 11 10 2019 2022"
echo "一键重装系统增加了更多版本重装"
echo "一键重装Windows系统默认为中文版了"
echo "主菜单选项5的BBR管理适配了alpine的新界面"
echo "------------------------"
echo "2024-03-06 v2.3.3"
echo "系统工具中新增了host解析设置功能"
echo "优化了alpine系统的主机名修改逻辑"
echo "------------------------"
echo "2024-03-11 v2.3.4"
echo "系统工具中新增fail2banSSH防御程序防止你的SSH被暴力破解"
echo "------------------------"
echo "2024-03-20 v2.3.5"
echo "面板工具加入了PVE开小鸡的面板感谢oneclickvirt大佬的一键安装脚本"
echo "------------------------"
echo "2024-03-29 v2.3.6"
echo "LDNMP安装环境时提前设置1G虚拟内存提升建站环境安装速度和稳定性"
echo "------------------------"
echo "2024-04-01 v2.3.7"
echo "LDNMP改进了防御能力可以拦截404攻击守护网站安全。脚本进入选择10再选择35站点防御防御原来这么简单"
echo "------------------------"
echo "2024-04-02 v2.3.8"
echo "LDNMP站点防御接入cf添加了cloudflare模式添加了参数配置选项"
echo "------------------------"
echo "2024-04-07 v2.3.9"
echo "LDNMP申请域名证书解决证书链不完整的问题谢谢wuying2021分支提供思路已整合至主线版本"
echo "------------------------"
echo "2024-04-12 v2.4"
echo "面板工具中添加了24选项 webtop远程桌面程序alpine中文可视化桌面系统很好用"
echo "------------------------"
echo "2024-04-14 v2.4.1"
echo "面板工具中添加Sun-Panel导航面板的安装"
echo "LDNMP建站中halo镜像版本更新到最新版本"
echo "测试脚本合集中追加了两项nxtrace快速回程测试脚本 nxtrace指定IP回程测试脚本"
echo "测试脚本合集中追加了两项ludashi2020的三网线路测试"
echo "测试脚本合集界面重构,分类更清晰,方便未来扩展使用"
echo "------------------------"
echo "2024-04-18 v2.4.2"
echo "使用docker容器方式部署fail2ban防暴力破解程序ssh和nginx都能防御"
echo "优化了重启服务器的逻辑,增加了确认提示。"
echo "增加了dnf包管理的适配"
echo "安装BBRV3,cpu测速时将自动分配1024M的虚拟内存。"
echo "------------------------"
echo "2024-04-23 v2.4.3"
echo "面板工具中添加了34选项一个文件共享平台可以传文件传图片做分享链接用"
echo "------------------------"
echo "2024-04-26 v2.4.4"
echo "面板工具中添加了33选项一个极简朋友圈网页程序高仿微信朋友圈"
echo "------------------------"
echo "2024-04-29 v2.4.5"
echo "系统工具中添加了限流关机功能,到达限定流量后自动关机,针对小流量怕反撸的机型而设计!"
echo "------------------------"
echo "2024-04-30 v2.4.6"
echo "LDNMP建站分类调整将不需要安装PHP就能搭建的应用移至nginx区块中"
echo "LDNMP建站如果没装环境直接部署网站会弹出检测提示要求先装环境再建站"
echo "缩小脚本体积降了5kb整合了一些老大难代码模块化更顺畅小巧"
echo "------------------------"
echo "2024-05-04 v2.4.7"
echo "面板工具新增36选项加入了对AI聚合聊天网站的安装"
echo "------------------------"
echo "2024-05-05 v2.4.8"
echo "LDNMP增加了自定义PHP动态站点功能你可以上传自己的PHP项目"
echo "LDNMP站点管理中增加了编辑查看全局和站点配置"
echo "------------------------"
echo "2024-05-09 v2.4.9"
echo "LDNMP静态站点动态站点自定义搭建优化支持远程下载源码手动上传源码"
echo "修改主机名大小写Y的支持"
echo "检测脚本添加了xykt大佬的IP质量体检脚本非常美观实用的脚本"
echo "------------------------"
echo "2024-05-09 v2.4.10"
echo "更新脚本逻辑进行优化,可选择是否更新,并且显示当前和最新的版本号,更智能!"
echo "------------------------"
echo "2024-05-09 v2.5"
echo "重构并定义脚本中出现的红绿蓝黄灰白文字颜色,创建未来统一灵活调用文字颜色"
echo "面板工具中添加了MYIP工具箱面板可以查看当前使用的IP信息与状态"
echo "------------------------"
echo "2024-05-11 v2.5.1"
echo "对docker环境检测进行优化应用部署将会更稳定"
echo "对一些提示的文字颜色进行了优化,对一些警示文字进行红色黄色标注"
echo "openclaw的备份还原功能新增"
echo "------------------------"

164
upgrade_openssh9.8p1.sh Normal file
View File

@@ -0,0 +1,164 @@
#!/bin/bash
# 设置OpenSSH的版本号
OPENSSH_VERSION=$(curl -s https://cdn.openbsd.org/pub/OpenBSD/OpenSSH/portable/ | grep -oP 'openssh-\K[0-9]+\.[0-9]+p[0-9]+' | sort -V | tail -n 1)
# 检测系统类型
if [ -f /etc/os-release ]; then
. /etc/os-release
OS=$ID
else
echo "无法检测操作系统类型。"
exit 1
fi
# 等待并检查锁文件
wait_for_lock() {
while fuser /var/lib/dpkg/lock-frontend >/dev/null 2>&1; do
echo "等待dpkg锁释放..."
sleep 1
done
}
# 修复dpkg中断问题
fix_dpkg() {
DEBIAN_FRONTEND=noninteractive dpkg --configure -a
}
# 安装依赖包
install_dependencies() {
case $OS in
ubuntu|debian)
wait_for_lock
fix_dpkg
DEBIAN_FRONTEND=noninteractive apt update
DEBIAN_FRONTEND=noninteractive apt install -y build-essential zlib1g-dev libssl-dev libpam0g-dev wget ntpdate -o Dpkg::Options::="--force-confnew"
;;
centos|rhel|almalinux|rocky|fedora)
yum install -y epel-release
yum groupinstall -y "Development Tools"
yum install -y zlib-devel openssl-devel pam-devel wget ntpdate
;;
alpine)
apk add build-base zlib-dev openssl-dev pam-dev wget ntpdate
;;
*)
echo "不支持的操作系统:$OS"
exit 1
;;
esac
}
# 下载、编译和安装OpenSSH
install_openssh() {
wget --no-check-certificate https://cdn.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-${OPENSSH_VERSION}.tar.gz
# 解压最新的 .tar.gz 文件
tar -xzf openssh-*.tar.gz
# 获取解压出来的目录名并进入(自动适配)
DIR_NAME=$(tar -tzf openssh-*.tar.gz | head -1 | cut -f1 -d"/")
cd "$DIR_NAME"
./configure
make
make install
}
# 重启SSH服务
restart_ssh() {
mv /usr/bin/ssh /usr/bin/ssh.bak
ln -s /usr/local/bin/ssh /usr/bin/ssh
case $OS in
ubuntu|debian)
systemctl restart ssh
;;
centos|rhel|almalinux|rocky|fedora)
systemctl restart sshd
;;
alpine)
rc-service sshd restart
;;
*)
echo "不支持的操作系统:$OS"
exit 1
;;
esac
}
# 设置路径优先级
set_path_priority() {
NEW_SSH_PATH=$(which sshd) # 假设新版本的sshd和ssh在同一个目录
NEW_SSH_DIR=$(dirname "$NEW_SSH_PATH")
if [[ ":$PATH:" != *":$NEW_SSH_DIR:"* ]]; then
export PATH="$NEW_SSH_DIR:$PATH"
echo "export PATH=\"$NEW_SSH_DIR:\$PATH\"" >> ~/.bashrc
fi
}
# 验证更新
verify_installation() {
echo "SSH版本信息"
ssh -V
sshd -V
}
# 清理下载的文件
clean_up() {
cd ..
rm -rf openssh-${OPENSSH_VERSION}*
}
# 标题
check_openssh_test() {
echo "SSH高危漏洞修复工具"
echo "视频介绍: https://www.bilibili.com/video/BV1dm421G7dy?t=0.1"
echo "--------------------------"
}
# 检查OpenSSH版本
check_openssh_version() {
current_version=$(ssh -V 2>&1 | awk '{print $1}' | cut -d_ -f2 | cut -d'p' -f1)
# 版本范围
min_version=8.5
max_version=9.8
if awk -v ver="$current_version" -v min="$min_version" -v max="$max_version" 'BEGIN{if(ver>=min && ver<=max) exit 0; else exit 1}'; then
check_openssh_test
echo "SSH版本: $current_version 在8.5到9.8之间,需要修复。"
read -p "确定继续吗?(Y/N): " choice
case "$choice" in
[Yy])
install_dependencies
install_openssh
restart_ssh
set_path_priority
verify_installation
clean_up
;;
[Nn])
echo "已取消"
exit 1
;;
*)
echo "无效的选择,请输入 Y 或 N。"
exit 1
;;
esac
else
check_openssh_test
echo "SSH版本: $current_version 不在8.5到9.8之间,无需修复。"
exit 1
fi
}
check_openssh_version

31
valkey.conf Normal file
View File

@@ -0,0 +1,31 @@
# 基础设置
bind 0.0.0.0
protected-mode yes
port 6379
timeout 300
tcp-keepalive 300
# 性能优化
io-threads 4
io-threads-do-reads yes
databases 1
maxclients 10000
# 内存管理
maxmemory 2gb
maxmemory-policy allkeys-lru
# 禁用持久化
save ""
appendonly no
rdbcompression no
rdbchecksum no
# 日志设置
loglevel warning
logfile ""
# 异步删除优化
lazyfree-lazy-eviction yes
lazyfree-lazy-expire yes
lazyfree-lazy-server-del yes

490
www-1.conf Normal file
View File

@@ -0,0 +1,490 @@
; Start a new pool named 'www'.
; the variable $pool can be used in any directive and will be replaced by the
; pool name ('www' here)
[www]
; Per pool prefix
; It only applies on the following directives:
; - 'access.log'
; - 'slowlog'
; - 'listen' (unixsocket)
; - 'chroot'
; - 'chdir'
; - 'php_values'
; - 'php_admin_values'
; When not set, the global prefix (or NONE) applies instead.
; Note: This directive can also be relative to the global prefix.
; Default Value: none
;prefix = /path/to/pools/$pool
; Unix user/group of the child processes. This can be used only if the master
; process running user is root. It is set after the child process is created.
; The user and group can be specified either by their name or by their numeric
; IDs.
; Note: If the user is root, the executable needs to be started with
; --allow-to-run-as-root option to work.
; Default Values: The user is set to master process running user by default.
; If the group is not set, the user's group is used.
user = www-data
group = www-data
; The address on which to accept FastCGI requests.
; Valid syntaxes are:
; 'ip.add.re.ss:port' - to listen on a TCP socket to a specific IPv4 address on
; a specific port;
; '[ip:6:addr:ess]:port' - to listen on a TCP socket to a specific IPv6 address on
; a specific port;
; 'port' - to listen on a TCP socket to all addresses
; (IPv6 and IPv4-mapped) on a specific port;
; '/path/to/unix/socket' - to listen on a unix socket.
; Note: This value is mandatory.
listen = 127.0.0.1:9000
; Set listen(2) backlog.
; Default Value: 511 (-1 on Linux, FreeBSD and OpenBSD)
;listen.backlog = 511
; Set permissions for unix socket, if one is used. In Linux, read/write
; permissions must be set in order to allow connections from a web server. Many
; BSD-derived systems allow connections regardless of permissions. The owner
; and group can be specified either by name or by their numeric IDs.
; Default Values: Owner is set to the master process running user. If the group
; is not set, the owner's group is used. Mode is set to 0660.
;listen.owner = www-data
;listen.group = www-data
;listen.mode = 0660
; When POSIX Access Control Lists are supported you can set them using
; these options, value is a comma separated list of user/group names.
; When set, listen.owner and listen.group are ignored
;listen.acl_users =
;listen.acl_groups =
; List of addresses (IPv4/IPv6) of FastCGI clients which are allowed to connect.
; Equivalent to the FCGI_WEB_SERVER_ADDRS environment variable in the original
; PHP FCGI (5.2.2+). Makes sense only with a tcp listening socket. Each address
; must be separated by a comma. If this value is left blank, connections will be
; accepted from any ip address.
; Default Value: any
;listen.allowed_clients = 127.0.0.1
; Set the associated the route table (FIB). FreeBSD only
; Default Value: -1
;listen.setfib = 1
; Specify the nice(2) priority to apply to the pool processes (only if set)
; The value can vary from -19 (highest priority) to 20 (lower priority)
; Note: - It will only work if the FPM master process is launched as root
; - The pool processes will inherit the master process priority
; unless it specified otherwise
; Default Value: no set
; process.priority = -19
; Set the process dumpable flag (PR_SET_DUMPABLE prctl for Linux or
; PROC_TRACE_CTL procctl for FreeBSD) even if the process user
; or group is different than the master process user. It allows to create process
; core dump and ptrace the process for the pool user.
; Default Value: no
; process.dumpable = yes
; Choose how the process manager will control the number of child processes.
; Possible Values:
; static - a fixed number (pm.max_children) of child processes;
; dynamic - the number of child processes are set dynamically based on the
; following directives. With this process management, there will be
; always at least 1 children.
; pm.max_children - the maximum number of children that can
; be alive at the same time.
; pm.start_servers - the number of children created on startup.
; pm.min_spare_servers - the minimum number of children in 'idle'
; state (waiting to process). If the number
; of 'idle' processes is less than this
; number then some children will be created.
; pm.max_spare_servers - the maximum number of children in 'idle'
; state (waiting to process). If the number
; of 'idle' processes is greater than this
; number then some children will be killed.
; pm.max_spawn_rate - the maximum number of rate to spawn child
; processes at once.
; ondemand - no children are created at startup. Children will be forked when
; new requests will connect. The following parameter are used:
; pm.max_children - the maximum number of children that
; can be alive at the same time.
; pm.process_idle_timeout - The number of seconds after which
; an idle process will be killed.
; Note: This value is mandatory.
pm = dynamic
; The number of child processes to be created when pm is set to 'static' and the
; maximum number of child processes when pm is set to 'dynamic' or 'ondemand'.
; This value sets the limit on the number of simultaneous requests that will be
; served. Equivalent to the ApacheMaxClients directive with mpm_prefork.
; Equivalent to the PHP_FCGI_CHILDREN environment variable in the original PHP
; CGI. The below defaults are based on a server without much resources. Don't
; forget to tweak pm.* to fit your needs.
; Note: Used when pm is set to 'static', 'dynamic' or 'ondemand'
; Note: This value is mandatory.
pm.max_children = 10
; The number of child processes created on startup.
; Note: Used only when pm is set to 'dynamic'
; Default Value: (min_spare_servers + max_spare_servers) / 2
pm.start_servers = 3
; The desired minimum number of idle server processes.
; Note: Used only when pm is set to 'dynamic'
; Note: Mandatory when pm is set to 'dynamic'
pm.min_spare_servers = 1
; The desired maximum number of idle server processes.
; Note: Used only when pm is set to 'dynamic'
; Note: Mandatory when pm is set to 'dynamic'
pm.max_spare_servers = 3
; The number of rate to spawn child processes at once.
; Note: Used only when pm is set to 'dynamic'
; Note: Mandatory when pm is set to 'dynamic'
; Default Value: 32
;pm.max_spawn_rate = 32
; The number of seconds after which an idle process will be killed.
; Note: Used only when pm is set to 'ondemand'
; Default Value: 10s
;pm.process_idle_timeout = 10s;
; The number of requests each child process should execute before respawning.
; This can be useful to work around memory leaks in 3rd party libraries. For
; endless request processing specify '0'. Equivalent to PHP_FCGI_MAX_REQUESTS.
; Default Value: 0
pm.max_requests = 500
; The URI to view the FPM status page. If this value is not set, no URI will be
; recognized as a status page. It shows the following information:
; pool - the name of the pool;
; process manager - static, dynamic or ondemand;
; start time - the date and time FPM has started;
; start since - number of seconds since FPM has started;
; accepted conn - the number of request accepted by the pool;
; listen queue - the number of request in the queue of pending
; connections (see backlog in listen(2));
; max listen queue - the maximum number of requests in the queue
; of pending connections since FPM has started;
; listen queue len - the size of the socket queue of pending connections;
; idle processes - the number of idle processes;
; active processes - the number of active processes;
; total processes - the number of idle + active processes;
; max active processes - the maximum number of active processes since FPM
; has started;
; max children reached - number of times, the process limit has been reached,
; when pm tries to start more children (works only for
; pm 'dynamic' and 'ondemand');
; Value are updated in real time.
; Example output:
; pool: www
; process manager: static
; start time: 01/Jul/2011:17:53:49 +0200
; start since: 62636
; accepted conn: 190460
; listen queue: 0
; max listen queue: 1
; listen queue len: 42
; idle processes: 4
; active processes: 11
; total processes: 15
; max active processes: 12
; max children reached: 0
;
; By default the status page output is formatted as text/plain. Passing either
; 'html', 'xml' or 'json' in the query string will return the corresponding
; output syntax. Example:
; http://www.foo.bar/status
; http://www.foo.bar/status?json
; http://www.foo.bar/status?html
; http://www.foo.bar/status?xml
;
; By default the status page only outputs short status. Passing 'full' in the
; query string will also return status for each pool process.
; Example:
; http://www.foo.bar/status?full
; http://www.foo.bar/status?json&full
; http://www.foo.bar/status?html&full
; http://www.foo.bar/status?xml&full
; The Full status returns for each process:
; pid - the PID of the process;
; state - the state of the process (Idle, Running, ...);
; start time - the date and time the process has started;
; start since - the number of seconds since the process has started;
; requests - the number of requests the process has served;
; request duration - the duration in µs of the requests;
; request method - the request method (GET, POST, ...);
; request URI - the request URI with the query string;
; content length - the content length of the request (only with POST);
; user - the user (PHP_AUTH_USER) (or '-' if not set);
; script - the main script called (or '-' if not set);
; last request cpu - the %cpu the last request consumed
; it's always 0 if the process is not in Idle state
; because CPU calculation is done when the request
; processing has terminated;
; last request memory - the max amount of memory the last request consumed
; it's always 0 if the process is not in Idle state
; because memory calculation is done when the request
; processing has terminated;
; If the process is in Idle state, then informations are related to the
; last request the process has served. Otherwise informations are related to
; the current request being served.
; Example output:
; ************************
; pid: 31330
; state: Running
; start time: 01/Jul/2011:17:53:49 +0200
; start since: 63087
; requests: 12808
; request duration: 1250261
; request method: GET
; request URI: /test_mem.php?N=10000
; content length: 0
; user: -
; script: /home/fat/web/docs/php/test_mem.php
; last request cpu: 0.00
; last request memory: 0
;
; Note: There is a real-time FPM status monitoring sample web page available
; It's available in: /usr/local/share/php/fpm/status.html
;
; Note: The value must start with a leading slash (/). The value can be
; anything, but it may not be a good idea to use the .php extension or it
; may conflict with a real PHP file.
; Default Value: not set
;pm.status_path = /status
; The address on which to accept FastCGI status request. This creates a new
; invisible pool that can handle requests independently. This is useful
; if the main pool is busy with long running requests because it is still possible
; to get the status before finishing the long running requests.
;
; Valid syntaxes are:
; 'ip.add.re.ss:port' - to listen on a TCP socket to a specific IPv4 address on
; a specific port;
; '[ip:6:addr:ess]:port' - to listen on a TCP socket to a specific IPv6 address on
; a specific port;
; 'port' - to listen on a TCP socket to all addresses
; (IPv6 and IPv4-mapped) on a specific port;
; '/path/to/unix/socket' - to listen on a unix socket.
; Default Value: value of the listen option
;pm.status_listen = 127.0.0.1:9001
; The ping URI to call the monitoring page of FPM. If this value is not set, no
; URI will be recognized as a ping page. This could be used to test from outside
; that FPM is alive and responding, or to
; - create a graph of FPM availability (rrd or such);
; - remove a server from a group if it is not responding (load balancing);
; - trigger alerts for the operating team (24/7).
; Note: The value must start with a leading slash (/). The value can be
; anything, but it may not be a good idea to use the .php extension or it
; may conflict with a real PHP file.
; Default Value: not set
;ping.path = /ping
; This directive may be used to customize the response of a ping request. The
; response is formatted as text/plain with a 200 response code.
; Default Value: pong
;ping.response = pong
; The access log file
; Default: not set
;access.log = log/$pool.access.log
; The access log format.
; The following syntax is allowed
; %%: the '%' character
; %C: %CPU used by the request
; it can accept the following format:
; - %{user}C for user CPU only
; - %{system}C for system CPU only
; - %{total}C for user + system CPU (default)
; %d: time taken to serve the request
; it can accept the following format:
; - %{seconds}d (default)
; - %{milliseconds}d
; - %{milli}d
; - %{microseconds}d
; - %{micro}d
; %e: an environment variable (same as $_ENV or $_SERVER)
; it must be associated with embraces to specify the name of the env
; variable. Some examples:
; - server specifics like: %{REQUEST_METHOD}e or %{SERVER_PROTOCOL}e
; - HTTP headers like: %{HTTP_HOST}e or %{HTTP_USER_AGENT}e
; %f: script filename
; %l: content-length of the request (for POST request only)
; %m: request method
; %M: peak of memory allocated by PHP
; it can accept the following format:
; - %{bytes}M (default)
; - %{kilobytes}M
; - %{kilo}M
; - %{megabytes}M
; - %{mega}M
; %n: pool name
; %o: output header
; it must be associated with embraces to specify the name of the header:
; - %{Content-Type}o
; - %{X-Powered-By}o
; - %{Transfert-Encoding}o
; - ....
; %p: PID of the child that serviced the request
; %P: PID of the parent of the child that serviced the request
; %q: the query string
; %Q: the '?' character if query string exists
; %r: the request URI (without the query string, see %q and %Q)
; %R: remote IP address
; %s: status (response code)
; %t: server time the request was received
; it can accept a strftime(3) format:
; %d/%b/%Y:%H:%M:%S %z (default)
; The strftime(3) format must be encapsulated in a %{<strftime_format>}t tag
; e.g. for a ISO8601 formatted timestring, use: %{%Y-%m-%dT%H:%M:%S%z}t
; %T: time the log has been written (the request has finished)
; it can accept a strftime(3) format:
; %d/%b/%Y:%H:%M:%S %z (default)
; The strftime(3) format must be encapsulated in a %{<strftime_format>}t tag
; e.g. for a ISO8601 formatted timestring, use: %{%Y-%m-%dT%H:%M:%S%z}t
; %u: remote user
;
; Default: "%R - %u %t \"%m %r\" %s"
;access.format = "%R - %u %t \"%m %r%Q%q\" %s %f %{milli}d %{kilo}M %C%%"
; A list of request_uri values which should be filtered from the access log.
;
; As a security precuation, this setting will be ignored if:
; - the request method is not GET or HEAD; or
; - there is a request body; or
; - there are query parameters; or
; - the response code is outwith the successful range of 200 to 299
;
; Note: The paths are matched against the output of the access.format tag "%r".
; On common configurations, this may look more like SCRIPT_NAME than the
; expected pre-rewrite URI.
;
; Default Value: not set
;access.suppress_path[] = /ping
;access.suppress_path[] = /health_check.php
; The log file for slow requests
; Default Value: not set
; Note: slowlog is mandatory if request_slowlog_timeout is set
;slowlog = log/$pool.log.slow
; The timeout for serving a single request after which a PHP backtrace will be
; dumped to the 'slowlog' file. A value of '0s' means 'off'.
; Available units: s(econds)(default), m(inutes), h(ours), or d(ays)
; Default Value: 0
;request_slowlog_timeout = 0
; Depth of slow log stack trace.
; Default Value: 20
;request_slowlog_trace_depth = 20
; The timeout for serving a single request after which the worker process will
; be killed. This option should be used when the 'max_execution_time' ini option
; does not stop script execution for some reason. A value of '0' means 'off'.
; Available units: s(econds)(default), m(inutes), h(ours), or d(ays)
; Default Value: 0
request_terminate_timeout = 1600
; The timeout set by 'request_terminate_timeout' ini option is not engaged after
; application calls 'fastcgi_finish_request' or when application has finished and
; shutdown functions are being called (registered via register_shutdown_function).
; This option will enable timeout limit to be applied unconditionally
; even in such cases.
; Default Value: no
;request_terminate_timeout_track_finished = no
; Set open file descriptor rlimit.
; Default Value: system defined value
;rlimit_files = 1024
; Set max core size rlimit.
; Possible Values: 'unlimited' or an integer greater or equal to 0
; Default Value: system defined value
;rlimit_core = 0
; Chroot to this directory at the start. This value must be defined as an
; absolute path. When this value is not set, chroot is not used.
; Note: you can prefix with '$prefix' to chroot to the pool prefix or one
; of its subdirectories. If the pool prefix is not set, the global prefix
; will be used instead.
; Note: chrooting is a great security feature and should be used whenever
; possible. However, all PHP paths will be relative to the chroot
; (error_log, sessions.save_path, ...).
; Default Value: not set
;chroot =
; Chdir to this directory at the start.
; Note: relative path can be used.
; Default Value: current directory or / when chroot
;chdir = /var/www
; Redirect worker stdout and stderr into main error log. If not set, stdout and
; stderr will be redirected to /dev/null according to FastCGI specs.
; Note: on highloaded environment, this can cause some delay in the page
; process time (several ms).
; Default Value: no
;catch_workers_output = yes
; Decorate worker output with prefix and suffix containing information about
; the child that writes to the log and if stdout or stderr is used as well as
; log level and time. This options is used only if catch_workers_output is yes.
; Settings to "no" will output data as written to the stdout or stderr.
; Default value: yes
;decorate_workers_output = no
; Clear environment in FPM workers
; Prevents arbitrary environment variables from reaching FPM worker processes
; by clearing the environment in workers before env vars specified in this
; pool configuration are added.
; Setting to "no" will make all environment variables available to PHP code
; via getenv(), $_ENV and $_SERVER.
; Default Value: yes
;clear_env = no
; Limits the extensions of the main script FPM will allow to parse. This can
; prevent configuration mistakes on the web server side. You should only limit
; FPM to .php extensions to prevent malicious users to use other extensions to
; execute php code.
; Note: set an empty value to allow all extensions.
; Default Value: .php
;security.limit_extensions = .php .php3 .php4 .php5 .php7
; Pass environment variables like LD_LIBRARY_PATH. All $VARIABLEs are taken from
; the current environment.
; Default Value: clean env
;env[HOSTNAME] = $HOSTNAME
;env[PATH] = /usr/local/bin:/usr/bin:/bin
;env[TMP] = /tmp
;env[TMPDIR] = /tmp
;env[TEMP] = /tmp
; Additional php.ini defines, specific to this pool of workers. These settings
; overwrite the values previously defined in the php.ini. The directives are the
; same as the PHP SAPI:
; php_value/php_flag - you can set classic ini defines which can
; be overwritten from PHP call 'ini_set'.
; php_admin_value/php_admin_flag - these directives won't be overwritten by
; PHP call 'ini_set'
; For php_*flag, valid values are on, off, 1, 0, true, false, yes or no.
; Defining 'extension' will load the corresponding shared extension from
; extension_dir. Defining 'disable_functions' or 'disable_classes' will not
; overwrite previously defined php.ini values, but will append the new value
; instead.
; Note: path INI options can be relative and will be expanded with the prefix
; (pool, global or /usr/local)
; Default Value: nothing is defined by default except the values in php.ini and
; specified at startup with the -d argument
;php_admin_value[sendmail_path] = /usr/sbin/sendmail -t -i -f www@my.domain.com
php_flag[display_errors] = off
php_admin_value[error_log] = /var/log/fpm-php.www.log
php_admin_flag[log_errors] = on
;php_admin_value[memory_limit] = 32M

490
www.conf Normal file
View File

@@ -0,0 +1,490 @@
; Start a new pool named 'www'.
; the variable $pool can be used in any directive and will be replaced by the
; pool name ('www' here)
[www]
; Per pool prefix
; It only applies on the following directives:
; - 'access.log'
; - 'slowlog'
; - 'listen' (unixsocket)
; - 'chroot'
; - 'chdir'
; - 'php_values'
; - 'php_admin_values'
; When not set, the global prefix (or NONE) applies instead.
; Note: This directive can also be relative to the global prefix.
; Default Value: none
;prefix = /path/to/pools/$pool
; Unix user/group of the child processes. This can be used only if the master
; process running user is root. It is set after the child process is created.
; The user and group can be specified either by their name or by their numeric
; IDs.
; Note: If the user is root, the executable needs to be started with
; --allow-to-run-as-root option to work.
; Default Values: The user is set to master process running user by default.
; If the group is not set, the user's group is used.
user = www-data
group = www-data
; The address on which to accept FastCGI requests.
; Valid syntaxes are:
; 'ip.add.re.ss:port' - to listen on a TCP socket to a specific IPv4 address on
; a specific port;
; '[ip:6:addr:ess]:port' - to listen on a TCP socket to a specific IPv6 address on
; a specific port;
; 'port' - to listen on a TCP socket to all addresses
; (IPv6 and IPv4-mapped) on a specific port;
; '/path/to/unix/socket' - to listen on a unix socket.
; Note: This value is mandatory.
listen = 127.0.0.1:9000
; Set listen(2) backlog.
; Default Value: 511 (-1 on Linux, FreeBSD and OpenBSD)
;listen.backlog = 511
; Set permissions for unix socket, if one is used. In Linux, read/write
; permissions must be set in order to allow connections from a web server. Many
; BSD-derived systems allow connections regardless of permissions. The owner
; and group can be specified either by name or by their numeric IDs.
; Default Values: Owner is set to the master process running user. If the group
; is not set, the owner's group is used. Mode is set to 0660.
;listen.owner = www-data
;listen.group = www-data
;listen.mode = 0660
; When POSIX Access Control Lists are supported you can set them using
; these options, value is a comma separated list of user/group names.
; When set, listen.owner and listen.group are ignored
;listen.acl_users =
;listen.acl_groups =
; List of addresses (IPv4/IPv6) of FastCGI clients which are allowed to connect.
; Equivalent to the FCGI_WEB_SERVER_ADDRS environment variable in the original
; PHP FCGI (5.2.2+). Makes sense only with a tcp listening socket. Each address
; must be separated by a comma. If this value is left blank, connections will be
; accepted from any ip address.
; Default Value: any
;listen.allowed_clients = 127.0.0.1
; Set the associated the route table (FIB). FreeBSD only
; Default Value: -1
;listen.setfib = 1
; Specify the nice(2) priority to apply to the pool processes (only if set)
; The value can vary from -19 (highest priority) to 20 (lower priority)
; Note: - It will only work if the FPM master process is launched as root
; - The pool processes will inherit the master process priority
; unless it specified otherwise
; Default Value: no set
; process.priority = -19
; Set the process dumpable flag (PR_SET_DUMPABLE prctl for Linux or
; PROC_TRACE_CTL procctl for FreeBSD) even if the process user
; or group is different than the master process user. It allows to create process
; core dump and ptrace the process for the pool user.
; Default Value: no
; process.dumpable = yes
; Choose how the process manager will control the number of child processes.
; Possible Values:
; static - a fixed number (pm.max_children) of child processes;
; dynamic - the number of child processes are set dynamically based on the
; following directives. With this process management, there will be
; always at least 1 children.
; pm.max_children - the maximum number of children that can
; be alive at the same time.
; pm.start_servers - the number of children created on startup.
; pm.min_spare_servers - the minimum number of children in 'idle'
; state (waiting to process). If the number
; of 'idle' processes is less than this
; number then some children will be created.
; pm.max_spare_servers - the maximum number of children in 'idle'
; state (waiting to process). If the number
; of 'idle' processes is greater than this
; number then some children will be killed.
; pm.max_spawn_rate - the maximum number of rate to spawn child
; processes at once.
; ondemand - no children are created at startup. Children will be forked when
; new requests will connect. The following parameter are used:
; pm.max_children - the maximum number of children that
; can be alive at the same time.
; pm.process_idle_timeout - The number of seconds after which
; an idle process will be killed.
; Note: This value is mandatory.
pm = dynamic
; The number of child processes to be created when pm is set to 'static' and the
; maximum number of child processes when pm is set to 'dynamic' or 'ondemand'.
; This value sets the limit on the number of simultaneous requests that will be
; served. Equivalent to the ApacheMaxClients directive with mpm_prefork.
; Equivalent to the PHP_FCGI_CHILDREN environment variable in the original PHP
; CGI. The below defaults are based on a server without much resources. Don't
; forget to tweak pm.* to fit your needs.
; Note: Used when pm is set to 'static', 'dynamic' or 'ondemand'
; Note: This value is mandatory.
pm.max_children = 20
; The number of child processes created on startup.
; Note: Used only when pm is set to 'dynamic'
; Default Value: (min_spare_servers + max_spare_servers) / 2
pm.start_servers = 5
; The desired minimum number of idle server processes.
; Note: Used only when pm is set to 'dynamic'
; Note: Mandatory when pm is set to 'dynamic'
pm.min_spare_servers = 1
; The desired maximum number of idle server processes.
; Note: Used only when pm is set to 'dynamic'
; Note: Mandatory when pm is set to 'dynamic'
pm.max_spare_servers = 5
; The number of rate to spawn child processes at once.
; Note: Used only when pm is set to 'dynamic'
; Note: Mandatory when pm is set to 'dynamic'
; Default Value: 32
;pm.max_spawn_rate = 32
; The number of seconds after which an idle process will be killed.
; Note: Used only when pm is set to 'ondemand'
; Default Value: 10s
;pm.process_idle_timeout = 10s;
; The number of requests each child process should execute before respawning.
; This can be useful to work around memory leaks in 3rd party libraries. For
; endless request processing specify '0'. Equivalent to PHP_FCGI_MAX_REQUESTS.
; Default Value: 0
pm.max_requests = 500
; The URI to view the FPM status page. If this value is not set, no URI will be
; recognized as a status page. It shows the following information:
; pool - the name of the pool;
; process manager - static, dynamic or ondemand;
; start time - the date and time FPM has started;
; start since - number of seconds since FPM has started;
; accepted conn - the number of request accepted by the pool;
; listen queue - the number of request in the queue of pending
; connections (see backlog in listen(2));
; max listen queue - the maximum number of requests in the queue
; of pending connections since FPM has started;
; listen queue len - the size of the socket queue of pending connections;
; idle processes - the number of idle processes;
; active processes - the number of active processes;
; total processes - the number of idle + active processes;
; max active processes - the maximum number of active processes since FPM
; has started;
; max children reached - number of times, the process limit has been reached,
; when pm tries to start more children (works only for
; pm 'dynamic' and 'ondemand');
; Value are updated in real time.
; Example output:
; pool: www
; process manager: static
; start time: 01/Jul/2011:17:53:49 +0200
; start since: 62636
; accepted conn: 190460
; listen queue: 0
; max listen queue: 1
; listen queue len: 42
; idle processes: 4
; active processes: 11
; total processes: 15
; max active processes: 12
; max children reached: 0
;
; By default the status page output is formatted as text/plain. Passing either
; 'html', 'xml' or 'json' in the query string will return the corresponding
; output syntax. Example:
; http://www.foo.bar/status
; http://www.foo.bar/status?json
; http://www.foo.bar/status?html
; http://www.foo.bar/status?xml
;
; By default the status page only outputs short status. Passing 'full' in the
; query string will also return status for each pool process.
; Example:
; http://www.foo.bar/status?full
; http://www.foo.bar/status?json&full
; http://www.foo.bar/status?html&full
; http://www.foo.bar/status?xml&full
; The Full status returns for each process:
; pid - the PID of the process;
; state - the state of the process (Idle, Running, ...);
; start time - the date and time the process has started;
; start since - the number of seconds since the process has started;
; requests - the number of requests the process has served;
; request duration - the duration in µs of the requests;
; request method - the request method (GET, POST, ...);
; request URI - the request URI with the query string;
; content length - the content length of the request (only with POST);
; user - the user (PHP_AUTH_USER) (or '-' if not set);
; script - the main script called (or '-' if not set);
; last request cpu - the %cpu the last request consumed
; it's always 0 if the process is not in Idle state
; because CPU calculation is done when the request
; processing has terminated;
; last request memory - the max amount of memory the last request consumed
; it's always 0 if the process is not in Idle state
; because memory calculation is done when the request
; processing has terminated;
; If the process is in Idle state, then informations are related to the
; last request the process has served. Otherwise informations are related to
; the current request being served.
; Example output:
; ************************
; pid: 31330
; state: Running
; start time: 01/Jul/2011:17:53:49 +0200
; start since: 63087
; requests: 12808
; request duration: 1250261
; request method: GET
; request URI: /test_mem.php?N=10000
; content length: 0
; user: -
; script: /home/fat/web/docs/php/test_mem.php
; last request cpu: 0.00
; last request memory: 0
;
; Note: There is a real-time FPM status monitoring sample web page available
; It's available in: /usr/local/share/php/fpm/status.html
;
; Note: The value must start with a leading slash (/). The value can be
; anything, but it may not be a good idea to use the .php extension or it
; may conflict with a real PHP file.
; Default Value: not set
;pm.status_path = /status
; The address on which to accept FastCGI status request. This creates a new
; invisible pool that can handle requests independently. This is useful
; if the main pool is busy with long running requests because it is still possible
; to get the status before finishing the long running requests.
;
; Valid syntaxes are:
; 'ip.add.re.ss:port' - to listen on a TCP socket to a specific IPv4 address on
; a specific port;
; '[ip:6:addr:ess]:port' - to listen on a TCP socket to a specific IPv6 address on
; a specific port;
; 'port' - to listen on a TCP socket to all addresses
; (IPv6 and IPv4-mapped) on a specific port;
; '/path/to/unix/socket' - to listen on a unix socket.
; Default Value: value of the listen option
;pm.status_listen = 127.0.0.1:9001
; The ping URI to call the monitoring page of FPM. If this value is not set, no
; URI will be recognized as a ping page. This could be used to test from outside
; that FPM is alive and responding, or to
; - create a graph of FPM availability (rrd or such);
; - remove a server from a group if it is not responding (load balancing);
; - trigger alerts for the operating team (24/7).
; Note: The value must start with a leading slash (/). The value can be
; anything, but it may not be a good idea to use the .php extension or it
; may conflict with a real PHP file.
; Default Value: not set
;ping.path = /ping
; This directive may be used to customize the response of a ping request. The
; response is formatted as text/plain with a 200 response code.
; Default Value: pong
;ping.response = pong
; The access log file
; Default: not set
;access.log = log/$pool.access.log
; The access log format.
; The following syntax is allowed
; %%: the '%' character
; %C: %CPU used by the request
; it can accept the following format:
; - %{user}C for user CPU only
; - %{system}C for system CPU only
; - %{total}C for user + system CPU (default)
; %d: time taken to serve the request
; it can accept the following format:
; - %{seconds}d (default)
; - %{milliseconds}d
; - %{milli}d
; - %{microseconds}d
; - %{micro}d
; %e: an environment variable (same as $_ENV or $_SERVER)
; it must be associated with embraces to specify the name of the env
; variable. Some examples:
; - server specifics like: %{REQUEST_METHOD}e or %{SERVER_PROTOCOL}e
; - HTTP headers like: %{HTTP_HOST}e or %{HTTP_USER_AGENT}e
; %f: script filename
; %l: content-length of the request (for POST request only)
; %m: request method
; %M: peak of memory allocated by PHP
; it can accept the following format:
; - %{bytes}M (default)
; - %{kilobytes}M
; - %{kilo}M
; - %{megabytes}M
; - %{mega}M
; %n: pool name
; %o: output header
; it must be associated with embraces to specify the name of the header:
; - %{Content-Type}o
; - %{X-Powered-By}o
; - %{Transfert-Encoding}o
; - ....
; %p: PID of the child that serviced the request
; %P: PID of the parent of the child that serviced the request
; %q: the query string
; %Q: the '?' character if query string exists
; %r: the request URI (without the query string, see %q and %Q)
; %R: remote IP address
; %s: status (response code)
; %t: server time the request was received
; it can accept a strftime(3) format:
; %d/%b/%Y:%H:%M:%S %z (default)
; The strftime(3) format must be encapsulated in a %{<strftime_format>}t tag
; e.g. for a ISO8601 formatted timestring, use: %{%Y-%m-%dT%H:%M:%S%z}t
; %T: time the log has been written (the request has finished)
; it can accept a strftime(3) format:
; %d/%b/%Y:%H:%M:%S %z (default)
; The strftime(3) format must be encapsulated in a %{<strftime_format>}t tag
; e.g. for a ISO8601 formatted timestring, use: %{%Y-%m-%dT%H:%M:%S%z}t
; %u: remote user
;
; Default: "%R - %u %t \"%m %r\" %s"
;access.format = "%R - %u %t \"%m %r%Q%q\" %s %f %{milli}d %{kilo}M %C%%"
; A list of request_uri values which should be filtered from the access log.
;
; As a security precuation, this setting will be ignored if:
; - the request method is not GET or HEAD; or
; - there is a request body; or
; - there are query parameters; or
; - the response code is outwith the successful range of 200 to 299
;
; Note: The paths are matched against the output of the access.format tag "%r".
; On common configurations, this may look more like SCRIPT_NAME than the
; expected pre-rewrite URI.
;
; Default Value: not set
;access.suppress_path[] = /ping
;access.suppress_path[] = /health_check.php
; The log file for slow requests
; Default Value: not set
; Note: slowlog is mandatory if request_slowlog_timeout is set
;slowlog = log/$pool.log.slow
; The timeout for serving a single request after which a PHP backtrace will be
; dumped to the 'slowlog' file. A value of '0s' means 'off'.
; Available units: s(econds)(default), m(inutes), h(ours), or d(ays)
; Default Value: 0
;request_slowlog_timeout = 0
; Depth of slow log stack trace.
; Default Value: 20
;request_slowlog_trace_depth = 20
; The timeout for serving a single request after which the worker process will
; be killed. This option should be used when the 'max_execution_time' ini option
; does not stop script execution for some reason. A value of '0' means 'off'.
; Available units: s(econds)(default), m(inutes), h(ours), or d(ays)
; Default Value: 0
request_terminate_timeout = 1600
; The timeout set by 'request_terminate_timeout' ini option is not engaged after
; application calls 'fastcgi_finish_request' or when application has finished and
; shutdown functions are being called (registered via register_shutdown_function).
; This option will enable timeout limit to be applied unconditionally
; even in such cases.
; Default Value: no
;request_terminate_timeout_track_finished = no
; Set open file descriptor rlimit.
; Default Value: system defined value
;rlimit_files = 1024
; Set max core size rlimit.
; Possible Values: 'unlimited' or an integer greater or equal to 0
; Default Value: system defined value
;rlimit_core = 0
; Chroot to this directory at the start. This value must be defined as an
; absolute path. When this value is not set, chroot is not used.
; Note: you can prefix with '$prefix' to chroot to the pool prefix or one
; of its subdirectories. If the pool prefix is not set, the global prefix
; will be used instead.
; Note: chrooting is a great security feature and should be used whenever
; possible. However, all PHP paths will be relative to the chroot
; (error_log, sessions.save_path, ...).
; Default Value: not set
;chroot =
; Chdir to this directory at the start.
; Note: relative path can be used.
; Default Value: current directory or / when chroot
;chdir = /var/www
; Redirect worker stdout and stderr into main error log. If not set, stdout and
; stderr will be redirected to /dev/null according to FastCGI specs.
; Note: on highloaded environment, this can cause some delay in the page
; process time (several ms).
; Default Value: no
;catch_workers_output = yes
; Decorate worker output with prefix and suffix containing information about
; the child that writes to the log and if stdout or stderr is used as well as
; log level and time. This options is used only if catch_workers_output is yes.
; Settings to "no" will output data as written to the stdout or stderr.
; Default value: yes
;decorate_workers_output = no
; Clear environment in FPM workers
; Prevents arbitrary environment variables from reaching FPM worker processes
; by clearing the environment in workers before env vars specified in this
; pool configuration are added.
; Setting to "no" will make all environment variables available to PHP code
; via getenv(), $_ENV and $_SERVER.
; Default Value: yes
;clear_env = no
; Limits the extensions of the main script FPM will allow to parse. This can
; prevent configuration mistakes on the web server side. You should only limit
; FPM to .php extensions to prevent malicious users to use other extensions to
; execute php code.
; Note: set an empty value to allow all extensions.
; Default Value: .php
;security.limit_extensions = .php .php3 .php4 .php5 .php7
; Pass environment variables like LD_LIBRARY_PATH. All $VARIABLEs are taken from
; the current environment.
; Default Value: clean env
;env[HOSTNAME] = $HOSTNAME
;env[PATH] = /usr/local/bin:/usr/bin:/bin
;env[TMP] = /tmp
;env[TMPDIR] = /tmp
;env[TEMP] = /tmp
; Additional php.ini defines, specific to this pool of workers. These settings
; overwrite the values previously defined in the php.ini. The directives are the
; same as the PHP SAPI:
; php_value/php_flag - you can set classic ini defines which can
; be overwritten from PHP call 'ini_set'.
; php_admin_value/php_admin_flag - these directives won't be overwritten by
; PHP call 'ini_set'
; For php_*flag, valid values are on, off, 1, 0, true, false, yes or no.
; Defining 'extension' will load the corresponding shared extension from
; extension_dir. Defining 'disable_functions' or 'disable_classes' will not
; overwrite previously defined php.ini values, but will append the new value
; instead.
; Note: path INI options can be relative and will be expanded with the prefix
; (pool, global or /usr/local)
; Default Value: nothing is defined by default except the values in php.ini and
; specified at startup with the -d argument
;php_admin_value[sendmail_path] = /usr/sbin/sendmail -t -i -f www@my.domain.com
php_flag[display_errors] = off
php_admin_value[error_log] = /var/log/fpm-php.www.log
php_admin_flag[log_errors] = on
;php_admin_value[memory_limit] = 32M