mirror of
https://gitcode.com/JianFeeeee/ModelRouter.git
synced 2026-11-12 04:07:25 +00:00
refactor(rename)!: llmsproxy → modelrouter(module / 二进制 / 路径 / env)
产品名早已是 ModelRouter,但仓库标识符仍沿用旧名。这次统一,读写两侧都做兼容, 已部署的机器不会因为顺序问题立刻失效。 - go module `llmsproxy` → `modelrouter`;全仓 import 与 `cmd/modelrouter` 同步。 - `Makefile BINARY=modelrouter`;发行包名、systemd unit、/usr/share 资源、 `/usr/local/bin` 目标全部改为 modelrouter。 - 默认配置目录 `/etc/llmsproxy` → `/etc/modelrouter`。**读取侧回退**:插件与 `storeDir` 仍会检查旧路径,所以"先换二进制、后改 unit"不会立即失效。 - 环境变量 `LLMSPROXY_*`(PLUGIN_DIR / XDISPLAY_BASE / ARCH / LIVE_ZEN / BUILD_DIR)改为 `MODELROUTER_*`;`LLMS_PROXY_MASTER_KEY` → `MODELROUTER_MASTER_KEY`。旧名在读取侧继续接受。 - 桌面端内嵌核心 `bin/llmsproxy` → `bin/modelrouter`,并保留旧文件名回退, 避免升级到一半的 profile 被判成"core binary missing"。 - 新增 `packaging/migrate-to-modelrouter.sh`:**默认 dry-run**,复制而非移动、 旧目录永不删除、支持 `--rollback`;随 release tar 分发并由 core-dist 校验。 - `deploy.sh` 增加改名保护:发现旧 `/etc/llmsproxy` 而新配置不存在时**中止**, 否则会部署出一个"能启动但没有 key、没有源"的空实例。 - 临时目录前缀 `/tmp/llmsproxy-*` → `/tmp/modelrouter-*`;清理逻辑同时回收两代。 兼容别名(`__llmsproxy_*` 全局、`llmsproxy` SDK 别名、旧路径/旧 env)刻意保留, 它们是升级路径而非遗留;docs/plugins.md 已标注为废弃。 本提交未包含:`adapters/opencodezen.lua`、`adapter_opencodezen_test.go`、 `adapter_opencodezen_live_test.go`(OpenCode UA/免费池的既有改动,另批提交)。
This commit is contained in:
@ -1,4 +1,4 @@
|
||||
# llmsproxy — 统一 OpenAI 兼容网关配置(示例模板)
|
||||
# modelrouter — 统一 OpenAI 兼容网关配置(示例模板)
|
||||
#
|
||||
# 这个是打包进发行包、供首次安装使用的模板。它故意不含任何可用的凭据:
|
||||
# 首次启动时程序会生成一个随机 admin key 并写入实际配置(WebUI「密钥」页可见)。
|
||||
@ -20,14 +20,10 @@ default_model: AUTO
|
||||
# Lua 适配器目录(默认 adapters/,首次启动自动写入内置适配器)
|
||||
adapter_dir: adapters
|
||||
|
||||
# 插件目录(可选)。设置后:
|
||||
# - 首次启动会把随核心发布的示例插件(billing:按源/模型/密钥计费 + 仪表盘)
|
||||
# 写入本目录,它会在请求流水线上挂钩子,并向 WebUI 注入一个「Billing」页面
|
||||
# 和状态页上的一块总开销组件;
|
||||
# - 之后 WebUI「插件」页与 Electron 壳的设置面板可安装/禁用/删除/编辑。
|
||||
# 目录一旦存在即以目录为准:删除或改写内置插件都是真实生效的操作。
|
||||
# 留空 = 插件功能完全关闭(不影响网关其它功能)。
|
||||
# plugin_dir: /etc/llmsproxy/plugins
|
||||
# Plugin directory (optional). Plugins are independent Lua files managed by the
|
||||
# operator. Release packages do not include or install plugins automatically.
|
||||
# Place desired .lua files in this directory before starting the gateway.
|
||||
# plugin_dir: /etc/modelrouter/plugins
|
||||
|
||||
# 运行时文件:存放 WebUI 管理的源模板、已删除标记、预置模板名单。
|
||||
#
|
||||
|
||||
@ -1,14 +1,14 @@
|
||||
#!/usr/bin/env bash
|
||||
# Build the llmsproxy CORE distribution packages (deb + rpm + tar.gz).
|
||||
# Build the modelrouter CORE distribution packages (deb + rpm + tar.gz).
|
||||
#
|
||||
# Unlike the GUI packages (Electron), the core is a single static-ish binary
|
||||
# that server admins install with dpkg/rpm. The systemd unit bakes in the
|
||||
# memory tuning (GOGC=50, MALLOC_ARENA_MAX=2) measured on the production box.
|
||||
#
|
||||
# Requires: go, nfpm (PATH), dpkg-deb/rpmbuild for verification
|
||||
# Outputs: cmd/build/dist/llmsproxy_<ver>_amd64.deb
|
||||
# cmd/build/dist/llmsproxy-<ver>.x86_64.rpm
|
||||
# cmd/build/dist/llmsproxy-<ver>-linux-amd64.tar.gz
|
||||
# Outputs: cmd/build/dist/modelrouter_<ver>_amd64.deb
|
||||
# cmd/build/dist/modelrouter-<ver>.x86_64.rpm
|
||||
# cmd/build/dist/modelrouter-<ver>-linux-amd64.tar.gz
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
@ -16,7 +16,7 @@ cd "$ROOT"
|
||||
|
||||
VERSION="${1:-$(git describe --tags --dirty 2>/dev/null | sed 's/^v//' || echo 0.1.0)}"
|
||||
DIST="$ROOT/cmd/build/dist"
|
||||
BUILD_BIN="$ROOT/cmd/build/llmsproxy"
|
||||
BUILD_BIN="$ROOT/cmd/build/modelrouter"
|
||||
|
||||
log() { printf '\033[1;34m[core-dist]\033[0m %s\n' "$*"; }
|
||||
|
||||
@ -35,7 +35,7 @@ mkdir -p "$DIST"
|
||||
|
||||
# 1) build the core binary (luajit tag, -s -w like deploy.sh)
|
||||
log "building core binary (luajit, stripped)..."
|
||||
CGO_ENABLED=1 go build -tags luajit -trimpath -ldflags="-s -w" -o "$BUILD_BIN" ./cmd/llmsproxy
|
||||
CGO_ENABLED=1 go build -tags luajit -trimpath -ldflags="-s -w" -o "$BUILD_BIN" ./cmd/modelrouter
|
||||
|
||||
# 2) sanity gates: binary must contain the expected symbols and not be degenerate.
|
||||
# NOTE under `set -o pipefail`, `strings | grep -q` is BROKEN: grep -q exits
|
||||
@ -65,41 +65,34 @@ rm -f "$NFPM_CFG"
|
||||
|
||||
# 4) tar.gz
|
||||
log "packaging tar.gz..."
|
||||
TAR_DIR="$DIST/llmsproxy-$VERSION-linux-amd64"
|
||||
TAR_DIR="$DIST/modelrouter-$VERSION-linux-amd64"
|
||||
rm -rf "$TAR_DIR"; mkdir -p "$TAR_DIR"
|
||||
cp "$BUILD_BIN" "$TAR_DIR/llmsproxy"
|
||||
cp "$BUILD_BIN" "$TAR_DIR/modelrouter"
|
||||
# Use the tracked sanitised template, NOT the local config.yaml: that file is
|
||||
# gitignored and carries the operator's real gateway_keys / upstream keys. It
|
||||
# was previously copied verbatim as config.example.yaml, which shipped a real,
|
||||
# working admin key (sk-gw-local-0001) to every install — and postinst.sh copies
|
||||
# the example to /etc when none exists, so it ended up live in production.
|
||||
cp "$ROOT/packaging/config.example.yaml" "$TAR_DIR/config.example.yaml"
|
||||
cp "$ROOT/packaging/llmsproxy.service" "$TAR_DIR/llmsproxy.service"
|
||||
cp "$ROOT/packaging/modelrouter.service" "$TAR_DIR/modelrouter.service"
|
||||
# 改名迁移脚本随包分发:升级自 llmsproxy 时代的机器需要它(dry-run 默认)。
|
||||
cp "$ROOT/packaging/migrate-to-modelrouter.sh" "$TAR_DIR/migrate-to-modelrouter.sh"
|
||||
mkdir -p "$TAR_DIR/adapters"
|
||||
cp "$ROOT"/internal/lua/adapters/*.lua "$TAR_DIR/adapters/"
|
||||
|
||||
# The shipped plugins go into the archive too, not just into the binary's
|
||||
# embedded FS.
|
||||
#
|
||||
# Seeding works without them (internal/lua/vm.go embeds plugins/*.lua and
|
||||
# writeBundledPlugins materialises them on first start — verified: a fresh
|
||||
# plugin_dir gains billing.lua, 70031 bytes, and the plugin loads), so this is
|
||||
# not a functional fix. It is a contents/claim mismatch: the v1.8.0 release
|
||||
# notes tell the operator the billing plugin ships with the core, and this
|
||||
# archive — the artefact they unpack and inspect — did not contain it. Shipping
|
||||
# the file also means an operator can read and adapt it before installing,
|
||||
# instead of only discovering it after the first boot writes it out.
|
||||
mkdir -p "$TAR_DIR/plugins"
|
||||
cp "$ROOT"/internal/lua/plugins/*.lua "$TAR_DIR/plugins/"
|
||||
# The core distribution contains only the gateway and protocol adapters.
|
||||
# Optional plugins remain in the source repository, but are neither embedded in
|
||||
# the binary nor copied into release archives. Operators install selected Lua
|
||||
# plugins into plugin_dir separately.
|
||||
|
||||
tar -C "$DIST" -czf "$DIST/llmsproxy-$VERSION-linux-amd64.tar.gz" \
|
||||
tar -C "$DIST" -czf "$DIST/modelrouter-$VERSION-linux-amd64.tar.gz" \
|
||||
"$(basename "$TAR_DIR")"
|
||||
rm -rf "$TAR_DIR"
|
||||
|
||||
# 5) verify artifacts are healthy (the same gate that would have caught the
|
||||
# 264KB exe fiasco)
|
||||
log "verifying packages..."
|
||||
for f in "$DIST"/llmsproxy_*.deb "$DIST"/llmsproxy-*.rpm "$DIST"/*.tar.gz; do
|
||||
for f in "$DIST"/modelrouter_*.deb "$DIST"/modelrouter-*.rpm "$DIST"/*.tar.gz; do
|
||||
[ -f "$f" ] || continue
|
||||
sz=$(stat -c%s "$f")
|
||||
[ "$sz" -gt 100000 ] || { echo "[core-dist] FATAL: $f suspiciously small ($sz B)"; exit 1; }
|
||||
@ -111,7 +104,7 @@ done
|
||||
# `set -e` that aborted the script right here, so the "done" line never printed
|
||||
# and NO content verification below ever ran. The size gate above was the only
|
||||
# check that ever executed. Pick the newest deb explicitly.
|
||||
newest_deb=$(ls -t "$DIST"/llmsproxy_*.deb 2>/dev/null | head -1 || true)
|
||||
newest_deb=$(ls -t "$DIST"/modelrouter_*.deb 2>/dev/null | head -1 || true)
|
||||
if [ -n "$newest_deb" ]; then
|
||||
dpkg-deb -I "$newest_deb" 2>/dev/null | grep -E 'Package|Version' | head -2 || true
|
||||
fi
|
||||
@ -123,22 +116,24 @@ fi
|
||||
# behaviour hides it at runtime — the operator sees a working billing plugin and
|
||||
# concludes the package is complete. Assert the file is in the archive.
|
||||
log "verifying archive contents..."
|
||||
TARBALL="$DIST/llmsproxy-$VERSION-linux-amd64.tar.gz"
|
||||
TARBALL="$DIST/modelrouter-$VERSION-linux-amd64.tar.gz"
|
||||
[ -f "$TARBALL" ] || { echo "[core-dist] FATAL: $TARBALL missing"; exit 1; }
|
||||
listing=$(tar tzf "$TARBALL" 2>/dev/null || true)
|
||||
[ -n "$listing" ] || { echo "[core-dist] FATAL: cannot list $TARBALL"; exit 1; }
|
||||
for want in llmsproxy config.example.yaml llmsproxy.service plugins/billing.lua; do
|
||||
for want in modelrouter config.example.yaml modelrouter.service migrate-to-modelrouter.sh; do
|
||||
if ! printf '%s\n' "$listing" | grep -q "/$want\$"; then
|
||||
echo "[core-dist] FATAL: archive is missing $want"
|
||||
echo " (release notes claim the billing plugin ships with the core)"
|
||||
echo " (the archive must carry the binary, config template, unit and migration script)"
|
||||
exit 1
|
||||
fi
|
||||
log " OK archive contains $want"
|
||||
done
|
||||
# Directories are matched by their CONTENTS, not by name: tar written with
|
||||
# `tar -czf` from a populated tree lists "…/adapters/openai.lua" and never a bare
|
||||
# "…/adapters" entry, so an earlier version of this check that grepped for
|
||||
# "/adapters$" reported a complete archive as broken.
|
||||
n_plugins=$(printf '%s\n' "$listing" | grep -c "/plugins/.*\.lua\$" || true)
|
||||
if [ "$n_plugins" -ne 0 ]; then
|
||||
echo "[core-dist] FATAL: archive unexpectedly contains $n_plugins plugins"
|
||||
exit 1
|
||||
fi
|
||||
log " OK archive excludes optional plugins"
|
||||
n_adapters=$(printf '%s\n' "$listing" | grep -c "/adapters/.*\.lua\$" || true)
|
||||
if [ "$n_adapters" -lt 10 ]; then
|
||||
echo "[core-dist] FATAL: archive has only $n_adapters adapters (expected >= 10)"
|
||||
|
||||
@ -115,7 +115,7 @@ python3 -c 'import websocket' >/dev/null 2>&1 && echo " OK python3 websocket-cl
|
||||
|
||||
if [ "$ok" = 1 ]; then
|
||||
echo
|
||||
echo "OK 浏览器栈就绪。重启 llmsproxy 后,flow=vnc 的平台即可用 VNC 兜底登录。"
|
||||
echo "OK 浏览器栈就绪。重启 modelrouter 后,flow=vnc 的平台即可用 VNC 兜底登录。"
|
||||
else
|
||||
echo
|
||||
echo "WARN: 仍有缺失项,VNC 兜底在该机器上不可用。插件的 deps/status 端点会一样报告。" >&2
|
||||
|
||||
201
packaging/migrate-to-modelrouter.sh
Executable file
201
packaging/migrate-to-modelrouter.sh
Executable file
@ -0,0 +1,201 @@
|
||||
#!/usr/bin/env bash
|
||||
# migrate-to-modelrouter.sh — 把 llmsproxy 时代的部署迁到 modelrouter 命名。
|
||||
#
|
||||
# 背景:产品名早已是 ModelRouter,但二进制/配置目录/服务名/插件目录仍沿用
|
||||
# 仓库旧名 llmsproxy。这次改名不留"半新半旧"的机器:脚本复制(不是移动)
|
||||
# 旧目录到新路径、改写 systemd unit、安装新二进制,并在最后做一遍校验。
|
||||
#
|
||||
# 安全约定:
|
||||
# * **默认 dry-run**,只打印将要做什么;加 --apply 才真正执行。
|
||||
# * 旧目录**永不删除**,迁移结束仍可 `--rollback` 回到旧布局。
|
||||
# * 已存在的新路径不会被静默覆盖:先备份成 .bak-<时间戳>,再写入。
|
||||
# * 不自动重启正在运行的服务,除非显式 --restart。
|
||||
#
|
||||
# 用法:
|
||||
# sudo ./packaging/migrate-to-modelrouter.sh # 预演
|
||||
# sudo ./packaging/migrate-to-modelrouter.sh --apply # 执行
|
||||
# sudo ./packaging/migrate-to-modelrouter.sh --apply --restart
|
||||
# sudo ./packaging/migrate-to-modelrouter.sh --rollback --apply
|
||||
set -euo pipefail
|
||||
|
||||
OLD_NAME="llmsproxy"
|
||||
NEW_NAME="modelrouter"
|
||||
OLD_ETC="/etc/${OLD_NAME}"
|
||||
NEW_ETC="/etc/${NEW_NAME}"
|
||||
OLD_BIN="/usr/local/bin/${OLD_NAME}"
|
||||
NEW_BIN="/usr/local/bin/${NEW_NAME}"
|
||||
OLD_UNIT="/etc/systemd/system/${OLD_NAME}.service"
|
||||
NEW_UNIT="/etc/systemd/system/${NEW_NAME}.service"
|
||||
OLD_SHARE="/usr/share/${OLD_NAME}"
|
||||
NEW_SHARE="/usr/share/${NEW_NAME}"
|
||||
|
||||
APPLY=0
|
||||
RESTART=0
|
||||
ROLLBACK=0
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--apply) APPLY=1 ;;
|
||||
--restart) RESTART=1 ;;
|
||||
--rollback) ROLLBACK=1 ;;
|
||||
-h|--help) sed -n '2,30p' "$0"; exit 0 ;;
|
||||
*) echo "unknown argument: $arg" >&2; exit 2 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
ok() { printf ' \033[32m✓\033[0m %s\n' "$*"; }
|
||||
skip() { printf ' \033[90m·\033[0m %s\n' "$*"; }
|
||||
plan() { printf ' \033[33m→\033[0m %s\n' "$*"; }
|
||||
die() { printf '\033[31mfail\033[0m %s\n' "$*" >&2; exit 1; }
|
||||
|
||||
run() {
|
||||
if [ "$APPLY" -eq 1 ]; then
|
||||
"$@"
|
||||
else
|
||||
printf ' [dry-run] %s\n' "$*"
|
||||
fi
|
||||
}
|
||||
|
||||
backup_if_exists() {
|
||||
local p="$1"
|
||||
if [ -e "$p" ]; then
|
||||
local b="${p}.bak-$(date +%Y%m%d%H%M%S)"
|
||||
plan "备份已存在的 $p → $b"
|
||||
run cp -a "$p" "$b"
|
||||
fi
|
||||
}
|
||||
|
||||
stamp() { date +%Y%m%d%H%M%S; }
|
||||
|
||||
if [ "$(id -u)" -ne 0 ] && [ "$APPLY" -eq 1 ]; then
|
||||
die "--apply 需要 root(会写 /etc 与 /usr/local/bin)"
|
||||
fi
|
||||
|
||||
if [ "$ROLLBACK" -eq 1 ]; then
|
||||
echo "== 回滚到 ${OLD_NAME} 布局 =="
|
||||
[ -d "$NEW_ETC" ] || die "$NEW_ETC 不存在,没有可回滚的内容"
|
||||
if [ -e "$OLD_ETC" ]; then
|
||||
plan "$OLD_ETC 已存在,保留它(回滚包在 ${NEW_ETC} 原地)"
|
||||
plan "如需覆盖请先手工比对:diff -r $OLD_ETC $NEW_ETC"
|
||||
else
|
||||
plan "复制 $NEW_ETC → $OLD_ETC(不删除新目录)"
|
||||
run cp -a "$NEW_ETC" "$OLD_ETC"
|
||||
fi
|
||||
if [ -e "$NEW_UNIT" ]; then
|
||||
plan "恢复 $OLD_UNIT"
|
||||
run cp "$NEW_UNIT" "$OLD_UNIT"
|
||||
run sed -i "s#${NEW_NAME}#${OLD_NAME}#g" "$OLD_UNIT"
|
||||
fi
|
||||
if [ "$RESTART" -eq 1 ]; then
|
||||
run systemctl daemon-reload
|
||||
run systemctl enable --now "${OLD_NAME}.service"
|
||||
fi
|
||||
echo
|
||||
echo "回滚$( [ "$APPLY" -eq 1 ] && echo '完成' || echo '预演完成(未改动任何文件)' )"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "== 迁移 ${OLD_NAME} → ${NEW_NAME}$( [ "$APPLY" -eq 1 ] && echo '' || echo '(dry-run,加 --apply 执行)' ) =="
|
||||
echo
|
||||
|
||||
# ---- 1. 状态目录 -------------------------------------------------------
|
||||
echo "[1/5] 配置与状态目录"
|
||||
if [ -d "$OLD_ETC" ]; then
|
||||
if [ -d "$NEW_ETC" ]; then
|
||||
ok "$NEW_ETC 已存在,保持不动(旧目录仍保留在 $OLD_ETC)"
|
||||
plan "合并前请人工比对:diff -r $OLD_ETC $NEW_ETC"
|
||||
else
|
||||
plan "复制 $OLD_ETC → $NEW_ETC(保留原目录,可回滚)"
|
||||
run cp -a "$OLD_ETC" "$NEW_ETC"
|
||||
fi
|
||||
else
|
||||
skip "$OLD_ETC 不存在,跳过(新安装无需迁移)"
|
||||
fi
|
||||
|
||||
# 配置内的绝对路径也要跟着改;只改这些键,避免误伤注释里的示例。
|
||||
if [ -d "$NEW_ETC" ] && [ -f "$NEW_ETC/config.yaml" ]; then
|
||||
if grep -q "$OLD_ETC" "$NEW_ETC/config.yaml"; then
|
||||
plan "把 $NEW_ETC/config.yaml 中的 $OLD_ETC 改为 $NEW_ETC"
|
||||
backup_if_exists "$NEW_ETC/config.yaml"
|
||||
run sed -i "s#${OLD_ETC}#${NEW_ETC}#g" "$NEW_ETC/config.yaml"
|
||||
else
|
||||
ok "config.yaml 内无旧路径"
|
||||
fi
|
||||
# master.key 的权限是安全边界,迁移后复核一次。
|
||||
if [ -f "$NEW_ETC/master.key" ]; then
|
||||
plan "复核 master.key 权限为 0600"
|
||||
run chmod 0600 "$NEW_ETC/master.key"
|
||||
fi
|
||||
fi
|
||||
|
||||
# ---- 2. 二进制 ---------------------------------------------------------
|
||||
echo "[2/5] 二进制"
|
||||
if [ -x "$NEW_BIN" ]; then
|
||||
ok "$NEW_BIN 已存在(安装包已就位)"
|
||||
elif [ -x "$OLD_BIN" ]; then
|
||||
plan "复制 $OLD_BIN → $NEW_BIN 并保留旧文件"
|
||||
run cp -a "$OLD_BIN" "$NEW_BIN"
|
||||
run chmod 0755 "$NEW_BIN"
|
||||
else
|
||||
plan "尚无二进制:从发行包或 'make build' 得到 $NEW_BIN 后重跑本脚本"
|
||||
fi
|
||||
|
||||
# ---- 3. systemd ------------------------------------------------------
|
||||
echo "[3/5] systemd unit"
|
||||
if [ -f "$NEW_UNIT" ]; then
|
||||
ok "$NEW_UNIT 已存在"
|
||||
elif [ -f "$OLD_UNIT" ]; then
|
||||
plan "生成 $NEW_UNIT(改写 ExecStart/WorkingDirectory/ReadWritePaths)"
|
||||
if [ "$APPLY" -eq 1 ]; then
|
||||
sed "s#/usr/local/bin/${OLD_NAME}#${NEW_BIN}#g; s#${OLD_ETC}#${NEW_ETC}#g" \
|
||||
"$OLD_UNIT" > "$NEW_UNIT"
|
||||
chmod 0644 "$NEW_UNIT"
|
||||
else
|
||||
printf ' [dry-run] sed ... %s > %s\n' "$OLD_UNIT" "$NEW_UNIT"
|
||||
fi
|
||||
plan "旧 unit 保留:$OLD_UNIT(确认新服务可用后再 disable)"
|
||||
else
|
||||
skip "没有 systemd unit,跳过"
|
||||
fi
|
||||
|
||||
# ---- 4. 分享目录(安装脚本/示例配置)----------------------------------
|
||||
echo "[4/5] /usr/share 资源"
|
||||
if [ -d "$OLD_SHARE" ] && [ ! -d "$NEW_SHARE" ]; then
|
||||
plan "复制 $OLD_SHARE → $NEW_SHARE"
|
||||
run cp -a "$OLD_SHARE" "$NEW_SHARE"
|
||||
else
|
||||
skip "$OLD_SHARE → $NEW_SHARE 无需处理"
|
||||
fi
|
||||
|
||||
# ---- 5. 校验 ---------------------------------------------------------
|
||||
echo "[5/5] 校验"
|
||||
if [ "$APPLY" -eq 1 ]; then
|
||||
[ -x "$NEW_BIN" ] || die "$NEW_BIN 不可执行"
|
||||
ok "$NEW_BIN 可执行"
|
||||
if [ -f "$NEW_ETC/config.yaml" ]; then
|
||||
"$NEW_BIN" -config "$NEW_ETC/config.yaml" -check >/dev/null || die "配置校验失败"
|
||||
ok "配置校验通过($NEW_ETC/config.yaml)"
|
||||
fi
|
||||
if [ -f "$NEW_UNIT" ]; then
|
||||
systemctl daemon-reload
|
||||
ok "systemd 已 reload"
|
||||
fi
|
||||
if [ "$RESTART" -eq 1 ] && [ -f "$NEW_UNIT" ]; then
|
||||
systemctl enable --now "${NEW_NAME}.service"
|
||||
ok "${NEW_NAME}.service 已启动"
|
||||
else
|
||||
skip "未重启服务(需要时加 --restart)"
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "迁移完成。旧布局原样保留,回滚:"
|
||||
echo " sudo $0 --rollback --apply"
|
||||
echo
|
||||
echo "确认无误后再手工清理(本脚本不会删):"
|
||||
echo " systemctl disable --now ${OLD_NAME}.service 2>/dev/null || true"
|
||||
echo " rm -f $OLD_UNIT"
|
||||
echo " rm -rf $OLD_BIN $OLD_ETC $OLD_SHARE"
|
||||
else
|
||||
echo
|
||||
echo "以上为预演。确认后执行:"
|
||||
echo " sudo $0 --apply"
|
||||
fi
|
||||
@ -1,5 +1,5 @@
|
||||
[Unit]
|
||||
Description=LLMSProxy - unified OpenAI-compatible multi-source LLM gateway
|
||||
Description=ModelRouter - unified OpenAI-compatible multi-source LLM gateway
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
@ -15,19 +15,19 @@ Type=simple
|
||||
# the extra GC cycles are free.
|
||||
Environment=GOGC=50
|
||||
Environment=MALLOC_ARENA_MAX=2
|
||||
ExecStart=/usr/local/bin/llmsproxy -config /etc/llmsproxy/config.yaml
|
||||
WorkingDirectory=/etc/llmsproxy
|
||||
ExecStart=/usr/local/bin/modelrouter -config /etc/modelrouter/config.yaml
|
||||
WorkingDirectory=/etc/modelrouter
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
|
||||
# ---- 加固(2026-10-01 逐条实测后加入,不是照抄文档)----
|
||||
#
|
||||
# 为什么仍然以 root 运行:master.key 是 0600 root。加 User=llmsproxy 实测直接
|
||||
# 为什么仍然以 root 运行:master.key 是 0600 root。加 User=modelrouter 实测直接
|
||||
# 起不来,而且失败方式很隐蔽——
|
||||
# [config] secrets disabled: open /etc/llmsproxy/master.key: permission denied
|
||||
# [config] secrets disabled: open /etc/modelrouter/master.key: permission denied
|
||||
# 只是**一行日志**,服务会带着"敏感值将以明文落盘"继续跑起来。
|
||||
# 也就是说降权在当前文件权限下不是加固,而是把密钥降级。要降权必须先把
|
||||
# master.key 交给服务用户并统一 /etc/llmsproxy 的属主,那是一次独立的、有回滚
|
||||
# master.key 交给服务用户并统一 /etc/modelrouter 的属主,那是一次独立的、有回滚
|
||||
# 需求的变更,不该和加固混在一起。
|
||||
#
|
||||
# 下面每一条都在一个独立探针单元(临时端口 + 独立 runtime_file/adapter_dir)
|
||||
@ -36,11 +36,11 @@ RestartSec=5
|
||||
# kill -9 后行为符合预期。systemd 对非法指令值不报错只"忽略",逐条实测是唯一
|
||||
# 可靠做法。
|
||||
NoNewPrivileges=yes
|
||||
# 读路径全部落在 /etc/llmsproxy;写路径经核对只有 config.yaml / runtime.json /
|
||||
# 读路径全部落在 /etc/modelrouter;写路径经核对只有 config.yaml / runtime.json /
|
||||
# audit.jsonl / adapters/*.lua / master.key,全在该目录下(internal/{config,gateway,
|
||||
# core,lua} 里的 WriteFile|Rename|Remove 调用点)。
|
||||
ProtectSystem=strict
|
||||
ReadWritePaths=/etc/llmsproxy
|
||||
ReadWritePaths=/etc/modelrouter
|
||||
ProtectHome=yes
|
||||
PrivateTmp=yes
|
||||
ProtectKernelTunables=yes
|
||||
@ -80,7 +80,7 @@ CapabilityBoundingSet=
|
||||
# 仍然保留的加固(见下):NoNewPrivileges / ProtectSystem=strict /
|
||||
# ReadWritePaths / ProtectHome / PrivateTmp / CapabilityBoundingSet=(空)/
|
||||
# ProtectKernel* / RestrictSUIDSGID / RestrictRealtime / LockPersonality。
|
||||
# 这些**都不影响**浏览器栈(已逐条实测),并把可写面收窄到 /etc/llmsproxy。
|
||||
# 这些**都不影响**浏览器栈(已逐条实测),并把可写面收窄到 /etc/modelrouter。
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@ -1,8 +1,8 @@
|
||||
# nfpm config for the llmsproxy core gateway binary.
|
||||
# nfpm config for the modelrouter core gateway binary.
|
||||
# The version placeholder below is substituted by packaging/core-dist.sh (nfpm v2
|
||||
# here does not honour {{ .Env.VERSION }} template rendering), so do not edit it
|
||||
# by hand.
|
||||
name: llmsproxy
|
||||
name: modelrouter
|
||||
arch: amd64
|
||||
platform: linux
|
||||
version: __VERSION__
|
||||
@ -19,36 +19,36 @@ homepage: https://gitcode.com/JianFeeeee/ModelRouter
|
||||
license: MIT
|
||||
contents:
|
||||
# binary
|
||||
- src: ./cmd/build/llmsproxy
|
||||
dst: /usr/bin/llmsproxy
|
||||
- src: ./cmd/build/modelrouter
|
||||
dst: /usr/bin/modelrouter
|
||||
file_info:
|
||||
mode: 0755
|
||||
# systemd unit (memory tuning baked in)
|
||||
- src: ./packaging/llmsproxy.service
|
||||
dst: /etc/systemd/system/llmsproxy.service
|
||||
- src: ./packaging/modelrouter.service
|
||||
dst: /etc/systemd/system/modelrouter.service
|
||||
file_info:
|
||||
mode: 0644
|
||||
# example config (seeded to /etc/llmsproxy on first install by postinst).
|
||||
# example config (seeded to /etc/modelrouter on first install by postinst).
|
||||
# Must be the tracked sanitised template: ./config.yaml is the operator's own
|
||||
# gitignored config and shipping it would leak real credentials — it shipped a
|
||||
# working admin key (sk-gw-local-0001) to every install for a while.
|
||||
- src: ./packaging/config.example.yaml
|
||||
dst: /usr/share/llmsproxy/config.example.yaml
|
||||
dst: /usr/share/modelrouter/config.example.yaml
|
||||
file_info:
|
||||
mode: 0644
|
||||
# built-in Lua adapters (deployed to /etc/llmsproxy/adapters)
|
||||
# built-in Lua adapters (deployed to /etc/modelrouter/adapters)
|
||||
- src: ./internal/lua/adapters/*.lua
|
||||
dst: /usr/share/llmsproxy/adapters/
|
||||
dst: /usr/share/modelrouter/adapters/
|
||||
file_info:
|
||||
mode: 0644
|
||||
# 服务端浏览器栈依赖安装脚本(VNC 兜底登录用;需 root,运维显式执行)
|
||||
- src: ./packaging/install-browser-stack.sh
|
||||
dst: /usr/share/llmsproxy/install-browser-stack.sh
|
||||
dst: /usr/share/modelrouter/install-browser-stack.sh
|
||||
file_info:
|
||||
mode: 0755
|
||||
# README pointer
|
||||
- src: ./README.md
|
||||
dst: /usr/share/doc/llmsproxy/README.md
|
||||
dst: /usr/share/doc/modelrouter/README.md
|
||||
file_info:
|
||||
mode: 0644
|
||||
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
#!/bin/sh
|
||||
# llmsproxy core package postinst
|
||||
# modelrouter core package postinst
|
||||
set -e
|
||||
|
||||
# Runtime state belongs under /etc/llmsproxy just like the production layout:
|
||||
# Runtime state belongs under /etc/modelrouter just like the production layout:
|
||||
# runtime.json holds keys/created sources and is NOT touched on reinstall.
|
||||
mkdir -p /etc/llmsproxy/adapters
|
||||
mkdir -p /etc/modelrouter/adapters
|
||||
|
||||
if [ -f /etc/llmsproxy/config.yaml ]; then
|
||||
echo "llmsproxy: keeping existing /etc/llmsproxy/config.yaml"
|
||||
if [ -f /etc/modelrouter/config.yaml ]; then
|
||||
echo "modelrouter: keeping existing /etc/modelrouter/config.yaml"
|
||||
else
|
||||
# Do NOT seed a starter config here. Packaging/installing one used to hand
|
||||
# everyone the same file, and while it was copied from the maintainer's
|
||||
@ -21,12 +21,12 @@ else
|
||||
#
|
||||
# (Seeding it with gateway_keys: [] would be worse: the gateway would start
|
||||
# but reject every request, with no way in except editing the file.)
|
||||
echo "llmsproxy: no /etc/llmsproxy/config.yaml — a random admin key will be generated on first start"
|
||||
echo "modelrouter: no /etc/modelrouter/config.yaml — a random admin key will be generated on first start"
|
||||
fi
|
||||
|
||||
# systemd
|
||||
if command -v systemctl >/dev/null 2>&1; then
|
||||
systemctl daemon-reload >/dev/null 2>&1 || true
|
||||
echo "llmsproxy: use 'systemctl enable --now llmsproxy' to start"
|
||||
echo "modelrouter: use 'systemctl enable --now modelrouter' to start"
|
||||
fi
|
||||
exit 0
|
||||
@ -1,13 +1,13 @@
|
||||
#!/bin/sh
|
||||
# llmsproxy core package prerm
|
||||
# modelrouter core package prerm
|
||||
set -e
|
||||
|
||||
if command -v systemctl >/dev/null 2>&1; then
|
||||
# stop the service if this package's unit is the one installed
|
||||
if systemctl list-unit-files llmsproxy.service >/dev/null 2>&1 && \
|
||||
systemctl is-active llmsproxy.service >/dev/null 2>&1; then
|
||||
systemctl stop llmsproxy.service >/dev/null 2>&1 || true
|
||||
systemctl disable llmsproxy.service >/dev/null 2>&1 || true
|
||||
if systemctl list-unit-files modelrouter.service >/dev/null 2>&1 && \
|
||||
systemctl is-active modelrouter.service >/dev/null 2>&1; then
|
||||
systemctl stop modelrouter.service >/dev/null 2>&1 || true
|
||||
systemctl disable modelrouter.service >/dev/null 2>&1 || true
|
||||
fi
|
||||
systemctl daemon-reload >/dev/null 2>&1 || true
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user