release: bump version to 0.41.2
This commit is contained in:
@@ -1,6 +1,10 @@
|
||||
FROM python:3.14-slim
|
||||
ARG PYTHON_IMAGE=python:3.14-slim
|
||||
ARG UV_IMAGE=ghcr.io/astral-sh/uv:latest
|
||||
|
||||
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/
|
||||
FROM ${UV_IMAGE} AS uv
|
||||
FROM ${PYTHON_IMAGE}
|
||||
|
||||
COPY --from=uv /uv /uvx /bin/
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
FROM python:3.14-slim
|
||||
ARG PYTHON_IMAGE=python:3.14-slim
|
||||
ARG UV_IMAGE=ghcr.io/astral-sh/uv:latest
|
||||
|
||||
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/
|
||||
FROM ${UV_IMAGE} AS uv
|
||||
FROM ${PYTHON_IMAGE}
|
||||
|
||||
COPY --from=uv /uv /uvx /bin/
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
|
||||
@@ -18,6 +18,9 @@ services:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: aiprovider/Dockerfile
|
||||
args:
|
||||
PYTHON_IMAGE: ${PYTHON_IMAGE:-python:3.14-slim}
|
||||
UV_IMAGE: ${UV_IMAGE:-ghcr.io/astral-sh/uv:latest}
|
||||
container_name: planet_aiprovider
|
||||
ports:
|
||||
- "8010:8010"
|
||||
|
||||
@@ -5,6 +5,9 @@ services:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: aiprovider/Dockerfile
|
||||
args:
|
||||
PYTHON_IMAGE: ${PYTHON_IMAGE:-python:3.14-slim}
|
||||
UV_IMAGE: ${UV_IMAGE:-ghcr.io/astral-sh/uv:latest}
|
||||
container_name: planet_aiprovider
|
||||
ports:
|
||||
- "8010:8010"
|
||||
|
||||
@@ -5,6 +5,9 @@ services:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: aiprovider/Dockerfile
|
||||
args:
|
||||
PYTHON_IMAGE: ${PYTHON_IMAGE:-python:3.14-slim}
|
||||
UV_IMAGE: ${UV_IMAGE:-ghcr.io/astral-sh/uv:latest}
|
||||
env_file:
|
||||
- ./aiprovider/.env
|
||||
container_name: planet_aiprovider
|
||||
|
||||
@@ -8,6 +8,22 @@ This project follows the repository versioning rule:
|
||||
- `improvement` -> `+0.0.1`(bugfix + 小功能混合)
|
||||
- `bugfix` -> `+0.0.1`
|
||||
|
||||
## [0.41.2] — 2026-04-27
|
||||
|
||||
### 🔧 Improvements
|
||||
- `planet.sh` 启动链路新增 verbose 滚动输出窗口,并在后端端口占用时打印目标地址和监听进程诊断
|
||||
- Docker 构建支持通过 build args 覆盖 Python 与 uv 镜像,方便 Docker Hub 不稳定时切换镜像源
|
||||
|
||||
### 🐛 Fixes
|
||||
- Earth 海缆登陆点改为基于相机射线与地球遮挡判断可见性,修复旋转后 pin 可见性滞后一帧的问题
|
||||
|
||||
### 🔧 Improvements
|
||||
- `docker-compose*.yml` 为 AI Provider 构建传入 `PYTHON_IMAGE` / `UV_IMAGE` 参数,默认仍使用官方镜像
|
||||
- 后端启动失败遇到 `Address already in use` 时输出 `lsof`、`ss` 与 PID 命令行信息
|
||||
- verbose 模式下 AI Provider build、后端与前端启动日志会在 spinner 下方保留最新 5 行滚动展示
|
||||
|
||||
---
|
||||
|
||||
## [0.41.1] — 2026-04-27
|
||||
|
||||
### 🐛 Fixes
|
||||
|
||||
@@ -16,12 +16,13 @@
|
||||
## Current Version
|
||||
|
||||
- `main` 当前主线历史推导到:`0.16.5`
|
||||
- `dev` 当前开发分支历史推导到:`0.41.1`
|
||||
- `dev` 当前开发分支历史推导到:`0.41.2`
|
||||
|
||||
## Timeline
|
||||
|
||||
| Version | Type | Branch | Commit | Summary |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `0.41.2` | improvement | `dev` | `pending` | 启动脚本新增 verbose 滚动日志与端口占用诊断,Docker 构建支持镜像源覆盖,并修复 Earth 登陆点遮挡判断 |
|
||||
| `0.41.1` | improvement | `dev` | `pending` | 修复新闻直播持久化失效、pin 边缘遮挡;图标抽取为 SVG 并建立规范 |
|
||||
| `0.41.0` | feature | `dev` | `pending` | Earth 图层顺序拆分、基座海陆色块、国界交互、高清材质/云图/地形层级与样式文档落地 |
|
||||
| `0.40.5` | improvement | `dev` | `pending` | 卫星 ribbon 拖尾、Iridium 覆盖球面投影填充+外圈、搜索自动聚焦修复 |
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "planet-frontend",
|
||||
"version": "0.41.1",
|
||||
"version": "0.41.2",
|
||||
"private": true,
|
||||
"packageManager": "bun@1",
|
||||
"dependencies": {
|
||||
|
||||
@@ -21,7 +21,11 @@ let cableIdMap = new Map();
|
||||
let cableStates = new Map();
|
||||
let cablesVisible = true;
|
||||
let landingPointTexture = null;
|
||||
const _lpEarthWorldPos = new THREE.Vector3();
|
||||
const _lpWorldPos = new THREE.Vector3();
|
||||
const _lpCameraRel = new THREE.Vector3();
|
||||
const _lpPointRel = new THREE.Vector3();
|
||||
const _lpCameraToPoint = new THREE.Vector3();
|
||||
|
||||
function createLandingPointTexture() {
|
||||
const size = CABLE_CONFIG.landingPoint.textureSize;
|
||||
@@ -460,7 +464,7 @@ export async function loadLandingPoints(scene, earthObj, options = {}) {
|
||||
color: CABLE_CONFIG.landingPoint.color,
|
||||
transparent: true,
|
||||
opacity: CABLE_CONFIG.landingPoint.opacity,
|
||||
depthTest: true,
|
||||
depthTest: false,
|
||||
depthWrite: false,
|
||||
}),
|
||||
);
|
||||
@@ -597,16 +601,40 @@ export function getAllLandingPoints() {
|
||||
return landingPoints;
|
||||
}
|
||||
|
||||
// Hide pins within ~3° of the limb (normalised dot < 0.05) to prevent the
|
||||
// "half-clipped-into-globe" look caused by depthTest clipping the billboard
|
||||
// against closer earth-surface geometry near the horizon.
|
||||
const _FACING_DOT_MIN_SQ = 0.05 * 0.05;
|
||||
|
||||
function isFacingCamera(lp, camera) {
|
||||
lp.getWorldPosition(_lpWorldPos);
|
||||
const d = _lpWorldPos.dot(camera.position);
|
||||
if (d <= 0) return false;
|
||||
return d * d > _FACING_DOT_MIN_SQ * _lpWorldPos.lengthSq() * camera.position.lengthSq();
|
||||
if (lp.parent) {
|
||||
lp.parent.getWorldPosition(_lpEarthWorldPos);
|
||||
} else {
|
||||
_lpEarthWorldPos.set(0, 0, 0);
|
||||
}
|
||||
|
||||
_lpCameraRel.copy(camera.position).sub(_lpEarthWorldPos);
|
||||
_lpPointRel.copy(_lpWorldPos).sub(_lpEarthWorldPos);
|
||||
_lpCameraToPoint.subVectors(_lpPointRel, _lpCameraRel);
|
||||
|
||||
const distanceSq = _lpCameraToPoint.lengthSq();
|
||||
if (distanceSq <= 0) return true;
|
||||
|
||||
const distance = Math.sqrt(distanceSq);
|
||||
_lpCameraToPoint.multiplyScalar(1 / distance);
|
||||
|
||||
// The pin sprite is rendered without depth testing so its full shape does
|
||||
// not get sliced by the globe. Instead, hide it when the camera-to-anchor
|
||||
// segment is occluded by a slightly inflated globe, matching the behavior of
|
||||
// the BGP and compute-center markers near the limb.
|
||||
const occlusionRadius =
|
||||
CONFIG.earthRadius + CABLE_CONFIG.landingPoint.altitudeOffset * 0.45;
|
||||
const cameraProjection = _lpCameraRel.dot(_lpCameraToPoint);
|
||||
const cameraRadiusSq = _lpCameraRel.lengthSq();
|
||||
const discriminant =
|
||||
cameraProjection * cameraProjection -
|
||||
(cameraRadiusSq - occlusionRadius * occlusionRadius);
|
||||
|
||||
if (discriminant < 0) return true;
|
||||
|
||||
const nearestIntersection = -cameraProjection - Math.sqrt(discriminant);
|
||||
return nearestIntersection <= 0 || nearestIntersection >= distance;
|
||||
}
|
||||
|
||||
export function applyLandingPointVisualState(lockedCableName, dimAll = false, camera = null) {
|
||||
|
||||
@@ -3399,6 +3399,12 @@ function animate() {
|
||||
}
|
||||
}
|
||||
|
||||
// Force matrixWorld to be current before any isFacingCamera checks below.
|
||||
// Without this, getWorldPosition() on LP sprites reads the previous frame's
|
||||
// transform (earth rotation is updated above but scene.updateMatrixWorld()
|
||||
// only runs inside renderer.render(), which hasn't been called yet).
|
||||
earth?.updateMatrixWorld(true);
|
||||
|
||||
applyCableVisualState();
|
||||
const activeCruiseMarker =
|
||||
isCruiseModeActive() && isCruisePresentationPinned()
|
||||
|
||||
184
planet.sh
184
planet.sh
@@ -35,6 +35,10 @@ WAIT_SPINNER_TICKS_PER_SECOND=8
|
||||
WAIT_SPINNER_DETAIL=""
|
||||
WAIT_SPINNER_MESSAGE=""
|
||||
WAIT_SESSION_ACTIVE=0
|
||||
VERBOSE=0
|
||||
WAIT_VERBOSE_LOG_FILE=""
|
||||
WAIT_VERBOSE_LINE_COUNT="${WAIT_VERBOSE_LINE_COUNT:-5}"
|
||||
WAIT_VERBOSE_TAIL_LINE_COUNT="${WAIT_VERBOSE_TAIL_LINE_COUNT:-40}"
|
||||
HTTP_CHECK_MAX_TIME="${HTTP_CHECK_MAX_TIME:-0.2}"
|
||||
|
||||
BACKEND_MAX_RETRIES="${BACKEND_MAX_RETRIES:-3}"
|
||||
@@ -186,7 +190,36 @@ render_wait_spinner() {
|
||||
local frame_index=$(( (step - 1) % ${#WAIT_SPINNER_FRAMES[@]} ))
|
||||
local frame="${WAIT_SPINNER_FRAMES[$frame_index]}"
|
||||
local detail="${WAIT_SPINNER_DETAIL:- }"
|
||||
printf "\r${DIM}·${NC} ${CYAN}%-2s${NC} ${WHITE}%s${NC}\033[K\n${DIM} %s${NC}\033[K\033[1A\r" "$frame" "$message" "$detail" >&2
|
||||
local verbose_line=""
|
||||
local verbose_lines=()
|
||||
local line_count=0
|
||||
local cursor_up=1
|
||||
|
||||
if [ "$VERBOSE" -eq 1 ] && [ -n "$WAIT_VERBOSE_LOG_FILE" ] && [ -f "$WAIT_VERBOSE_LOG_FILE" ]; then
|
||||
while IFS= read -r verbose_line; do
|
||||
verbose_line="$(sanitize_wait_detail "$verbose_line")"
|
||||
[ -n "$verbose_line" ] || continue
|
||||
verbose_lines+=("$verbose_line")
|
||||
done < <(tail -n "$WAIT_VERBOSE_TAIL_LINE_COUNT" "$WAIT_VERBOSE_LOG_FILE" 2>/dev/null)
|
||||
|
||||
while [ "${#verbose_lines[@]}" -gt "$WAIT_VERBOSE_LINE_COUNT" ]; do
|
||||
verbose_lines=("${verbose_lines[@]:1}")
|
||||
done
|
||||
fi
|
||||
|
||||
printf "\r${DIM}·${NC} ${CYAN}%-2s${NC} ${WHITE}%s${NC}\033[K\n${DIM} %s${NC}\033[K" "$frame" "$message" "$detail" >&2
|
||||
|
||||
if [ "$VERBOSE" -eq 1 ]; then
|
||||
line_count=1
|
||||
while [ "$line_count" -le "$WAIT_VERBOSE_LINE_COUNT" ]; do
|
||||
verbose_line="${verbose_lines[$line_count]:- }"
|
||||
printf "\n${DIM} %s${NC}\033[K" "$verbose_line" >&2
|
||||
line_count=$((line_count + 1))
|
||||
done
|
||||
cursor_up=$((WAIT_VERBOSE_LINE_COUNT + 1))
|
||||
fi
|
||||
|
||||
printf "\033[%sA\r" "$cursor_up" >&2
|
||||
}
|
||||
|
||||
animate_wait_spinner() {
|
||||
@@ -208,7 +241,29 @@ animate_wait_spinner() {
|
||||
}
|
||||
|
||||
clear_wait_spinner() {
|
||||
printf "\r\033[K\n\033[K\033[1A\r" >&2
|
||||
local line_count=0
|
||||
local clear_verbose_lines=0
|
||||
|
||||
if [ "$VERBOSE" -eq 1 ] && { [ "$WAIT_SESSION_ACTIVE" -eq 1 ] || [ -n "$WAIT_VERBOSE_LOG_FILE" ]; }; then
|
||||
clear_verbose_lines=1
|
||||
fi
|
||||
|
||||
printf "\r\033[K" >&2
|
||||
line_count=0
|
||||
while [ "$line_count" -lt 1 ]; do
|
||||
printf "\n\033[K" >&2
|
||||
line_count=$((line_count + 1))
|
||||
done
|
||||
|
||||
if [ "$clear_verbose_lines" -eq 1 ]; then
|
||||
line_count=0
|
||||
while [ "$line_count" -lt "$WAIT_VERBOSE_LINE_COUNT" ]; do
|
||||
printf "\n\033[K" >&2
|
||||
line_count=$((line_count + 1))
|
||||
done
|
||||
fi
|
||||
|
||||
printf "\033[%sA\r" "$((clear_verbose_lines == 1 ? WAIT_VERBOSE_LINE_COUNT + 1 : 1))" >&2
|
||||
}
|
||||
|
||||
start_wait_session() {
|
||||
@@ -247,6 +302,7 @@ close_wait_session_context() {
|
||||
reset_wait_spinner_state() {
|
||||
WAIT_SPINNER_DETAIL=""
|
||||
WAIT_SPINNER_MESSAGE=""
|
||||
WAIT_VERBOSE_LOG_FILE=""
|
||||
}
|
||||
|
||||
sanitize_wait_detail() {
|
||||
@@ -274,12 +330,19 @@ set_wait_detail() {
|
||||
run_command_with_spinner() {
|
||||
local message="$1"
|
||||
shift
|
||||
local verbose_log_file=""
|
||||
|
||||
if [ "$WAIT_SESSION_ACTIVE" -eq 1 ]; then
|
||||
set_wait_detail "$message"
|
||||
fi
|
||||
|
||||
if [ "$VERBOSE" -eq 1 ]; then
|
||||
verbose_log_file="$(mktemp /tmp/planet_verbose.XXXXXX.log)"
|
||||
WAIT_VERBOSE_LOG_FILE="$verbose_log_file"
|
||||
"$@" > "$verbose_log_file" 2>&1 &
|
||||
else
|
||||
"$@" &
|
||||
fi
|
||||
local command_pid=$!
|
||||
local exit_code=0
|
||||
|
||||
@@ -294,6 +357,9 @@ run_command_with_spinner() {
|
||||
done
|
||||
|
||||
wait "$command_pid" || exit_code=$?
|
||||
if [ "$VERBOSE" -eq 1 ] && [ -n "$verbose_log_file" ]; then
|
||||
WAIT_VERBOSE_LOG_FILE="$verbose_log_file"
|
||||
fi
|
||||
return "$exit_code"
|
||||
}
|
||||
|
||||
@@ -619,7 +685,7 @@ ensure_ai_provider_image_current() {
|
||||
build_ai_provider_image_with_fallback() {
|
||||
if compose_available; then
|
||||
set_wait_detail "使用 docker compose 构建 AI Provider 镜像"
|
||||
if run_command_with_spinner "构建 AI Provider 镜像" sh -c "docker compose build aiprovider > \"$AI_PROVIDER_BUILD_LOG_FILE\" 2>&1"; then
|
||||
if run_ai_provider_build_command "docker compose"; then
|
||||
return 0
|
||||
fi
|
||||
if compose_v1_available; then
|
||||
@@ -632,7 +698,7 @@ build_ai_provider_image_with_fallback() {
|
||||
|
||||
if compose_v1_available; then
|
||||
set_wait_detail "使用 docker-compose v1 构建 AI Provider 镜像"
|
||||
if run_command_with_spinner "构建 AI Provider 镜像" sh -c "docker-compose build aiprovider > \"$AI_PROVIDER_BUILD_LOG_FILE\" 2>&1"; then
|
||||
if run_ai_provider_build_command "docker-compose"; then
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
@@ -641,6 +707,17 @@ build_ai_provider_image_with_fallback() {
|
||||
report_missing_compose
|
||||
}
|
||||
|
||||
run_ai_provider_build_command() {
|
||||
local compose_command="$1"
|
||||
|
||||
if [ "$VERBOSE" -eq 1 ]; then
|
||||
run_command_with_spinner "构建 AI Provider 镜像" sh -c "${compose_command} build aiprovider 2>&1 | tee \"$AI_PROVIDER_BUILD_LOG_FILE\""
|
||||
return $?
|
||||
fi
|
||||
|
||||
run_command_with_spinner "构建 AI Provider 镜像" sh -c "${compose_command} build aiprovider > \"$AI_PROVIDER_BUILD_LOG_FILE\" 2>&1"
|
||||
}
|
||||
|
||||
install_uv_if_needed() {
|
||||
if command -v uv >/dev/null 2>&1; then
|
||||
return 0
|
||||
@@ -909,6 +986,9 @@ wait_for_frontend_ready() {
|
||||
else
|
||||
set_wait_detail "启动 Vite 开发服务器"
|
||||
fi
|
||||
if [ "$VERBOSE" -eq 1 ]; then
|
||||
WAIT_VERBOSE_LOG_FILE="$log_file"
|
||||
fi
|
||||
|
||||
while [ "$tick" -lt "$max_ticks" ]; do
|
||||
WAIT_SPINNER_STEP=$((WAIT_SPINNER_STEP + 1))
|
||||
@@ -1045,17 +1125,25 @@ start_backend_with_retry() {
|
||||
: > /tmp/planet_backend.log
|
||||
PYTHONPATH="$SCRIPT_DIR/backend" nohup uv run --project "$SCRIPT_DIR" python -m uvicorn app.main:app --host 0.0.0.0 --port "$backend_port" --reload > /tmp/planet_backend.log 2>&1 &
|
||||
BACKEND_PID=$!
|
||||
if [ "$VERBOSE" -eq 1 ]; then
|
||||
WAIT_VERBOSE_LOG_FILE="/tmp/planet_backend.log"
|
||||
fi
|
||||
|
||||
if wait_for_http "http://localhost:${backend_port}/health" "$BACKEND_HEALTH_CHECK_ATTEMPTS" "$BACKEND_HEALTH_CHECK_INTERVAL" "后端"; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
kill "$BACKEND_PID" 2>/dev/null || true
|
||||
if backend_log_indicates_port_conflict "/tmp/planet_backend.log"; then
|
||||
report_backend_port_conflict_if_needed "$backend_port" "/tmp/planet_backend.log"
|
||||
return 1
|
||||
fi
|
||||
animate_wait_spinner "后端第 ${retry}/${BACKEND_MAX_RETRIES} 次启动未就绪,准备重试" "$BACKEND_HEALTH_CHECK_INTERVAL"
|
||||
retry=$((retry + 1))
|
||||
done
|
||||
|
||||
clear_wait_spinner
|
||||
report_backend_port_conflict_if_needed "$backend_port" "/tmp/planet_backend.log" || true
|
||||
return 1
|
||||
}
|
||||
|
||||
@@ -1387,6 +1475,66 @@ frontend_log_indicates_port_conflict() {
|
||||
grep -q "Port .* is already in use" "$log_file" 2>/dev/null
|
||||
}
|
||||
|
||||
backend_log_indicates_port_conflict() {
|
||||
local log_file="$1"
|
||||
|
||||
[ -f "$log_file" ] || return 1
|
||||
grep -Eiq "Address already in use|Errno 98" "$log_file" 2>/dev/null
|
||||
}
|
||||
|
||||
print_port_listener_details() {
|
||||
local port="$1"
|
||||
local found=0
|
||||
local pid=""
|
||||
local command_line=""
|
||||
local lsof_output=""
|
||||
local ss_output=""
|
||||
local line=""
|
||||
|
||||
if command -v lsof >/dev/null 2>&1; then
|
||||
lsof_output="$(lsof -nP -iTCP:"${port}" -sTCP:LISTEN 2>/dev/null || true)"
|
||||
if [ -n "$lsof_output" ]; then
|
||||
printf "%s\n" "$lsof_output" | awk '{print " " $0}'
|
||||
found=1
|
||||
fi
|
||||
fi
|
||||
|
||||
if command -v ss >/dev/null 2>&1; then
|
||||
ss_output="$(ss -ltnpH "( sport = :${port} )" 2>/dev/null || true)"
|
||||
while IFS= read -r line; do
|
||||
[ -n "$line" ] || continue
|
||||
printf "${DIM} ss: %s${NC}\n" "$line"
|
||||
found=1
|
||||
done <<EOF
|
||||
$ss_output
|
||||
EOF
|
||||
fi
|
||||
|
||||
for pid in $(collect_port_pids "$port" || true); do
|
||||
command_line="$(ps -p "$pid" -o args= 2>/dev/null | sed -E 's/[[:space:]]+/ /g; s/^ //; s/ $//')"
|
||||
[ -n "$command_line" ] || command_line="未知命令"
|
||||
printf "${DIM} pid %s: %s${NC}\n" "$pid" "$command_line"
|
||||
found=1
|
||||
done
|
||||
|
||||
if [ "$found" -eq 0 ]; then
|
||||
log_note "未能在当前环境内定位端口 ${port} 的监听进程,可能被宿主机或外部网络命名空间占用。"
|
||||
fi
|
||||
}
|
||||
|
||||
report_backend_port_conflict_if_needed() {
|
||||
local backend_port="$1"
|
||||
local log_file="$2"
|
||||
|
||||
if backend_log_indicates_port_conflict "$log_file" || ! can_bind_port "$backend_port"; then
|
||||
log_error "后端地址已被占用: 0.0.0.0:${backend_port} / 127.0.0.1:${backend_port} / [::1]:${backend_port}"
|
||||
print_port_listener_details "$backend_port"
|
||||
return 0
|
||||
fi
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
# Frontend lifecycle helpers
|
||||
cleanup_frontend_processes() {
|
||||
local frontend_port="${1:-$DEFAULT_FRONTEND_PORT}"
|
||||
@@ -1527,6 +1675,10 @@ parse_service_args() {
|
||||
FRONTEND_LAN_ENABLED=1
|
||||
shift 1
|
||||
;;
|
||||
-v|--verbose)
|
||||
VERBOSE=1
|
||||
shift 1
|
||||
;;
|
||||
*)
|
||||
log_error "未知参数: $1"
|
||||
exit 1
|
||||
@@ -1831,6 +1983,25 @@ log() {
|
||||
esac
|
||||
}
|
||||
|
||||
parse_global_args() {
|
||||
while [ "$#" -gt 0 ]; do
|
||||
case "$1" in
|
||||
-v|--verbose)
|
||||
VERBOSE=1
|
||||
shift 1
|
||||
;;
|
||||
*)
|
||||
break
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
GLOBAL_ARG_REMAINDER=("$@")
|
||||
}
|
||||
|
||||
parse_global_args "$@"
|
||||
set -- "${GLOBAL_ARG_REMAINDER[@]}"
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
shift
|
||||
@@ -1854,9 +2025,10 @@ case "$1" in
|
||||
;;
|
||||
*)
|
||||
log_error "用法: ./planet.sh {start|stop|restart|createuser|health|log}"
|
||||
log_note "start 启动服务,可选: -b <后端端口> -f <前端端口> -a <AI Provider 端口> --allow-lan"
|
||||
log_note "全局参数: -v, --verbose 在当前执行行下方滚动显示最多 5 行命令输出"
|
||||
log_note "start 启动服务,可选: -b <后端端口> -f <前端端口> -a <AI Provider 端口> --allow-lan --verbose"
|
||||
log_note "stop 停止服务"
|
||||
log_note "restart 重启服务,可选: -b [后端端口] -f [前端端口] -a [AI Provider 端口] -d --allow-lan"
|
||||
log_note "restart 重启服务,可选: -b [后端端口] -f [前端端口] -a [AI Provider 端口] -d --allow-lan --verbose"
|
||||
log_note "createuser 交互创建用户"
|
||||
log_note "health 检查健康状态"
|
||||
log_note "log 查看日志"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[project]
|
||||
name = "planet"
|
||||
version = "0.41.1"
|
||||
version = "0.41.2"
|
||||
description = "智能星球计划 - 态势感知系统"
|
||||
requires-python = ">=3.14"
|
||||
dependencies = [
|
||||
|
||||
Reference in New Issue
Block a user