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:
JianFeeeee
2026-10-26 22:28:08 +08:00
parent de7553eaa2
commit 688dce88d1
107 changed files with 548 additions and 308 deletions

View File

@ -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 管理的源模板、已删除标记、预置模板名单。
#

View File

@ -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)"

View File

@ -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

View 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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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