refactor: Web 服务内嵌主进程 + 文档移到 docs/ 二级目录

1. Web 服务改为 threading 模式:
   - 新增 run_web_server(port) / stop_web_server() 函数
   - TUI 通过 from web_api import run_web_server 直接调用
   - 使用 werkzeug make_server 实现线程内优雅启动/停止
2. 构建脚本精简为单二进制:
   - 移除 trulymem-web 独立打包步骤
   - 添加 werkzeug 到 hidden-imports
3. 详细启动文档移到 docs/zh/quick_start.md
4. README 精简为概述,指向 docs/ 目录
This commit is contained in:
root
2026-04-28 10:50:24 +08:00
parent aefaea7b90
commit 4770b3b990
11 changed files with 526 additions and 777 deletions

View File

@ -1,50 +1,41 @@
#!/bin/bash
set -e
echo "===== Building TrulyMEM AppImage for Linux ====="
echo "===== Building TrulyMEM AppImage ====="
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
cd "$PROJECT_ROOT"
if ! command -v python3 &> /dev/null; then
echo "Error: python3 not found"
exit 1
fi
APPDIR="$PROJECT_ROOT/TrulyMEM.AppDir"
rm -rf "$APPDIR"
mkdir -p "$APPDIR/usr/bin"
mkdir -p "$APPDIR/usr/share/trulymem"
mkdir -p "$APPDIR/usr/share/trulymem-web"
echo "Project root: $PROJECT_ROOT"
echo "===== Step 1: Build binaries with PyInstaller ====="
VENV_DIR="$PROJECT_ROOT/.venv_appimage"
rm -rf "$VENV_DIR"
# ── 检查依赖 ──────────────────────────────────────────────────────────────
if ! command -v python3 &> /dev/null; then
echo "Error: python3 not found"; exit 1
fi
APPIMAGE_TOOL="${APPIMAGE_TOOL:-appimagetool}"
if ! command -v "$APPIMAGE_TOOL" &> /dev/null && [ ! -f "$APPIMAGE_TOOL" ]; then
echo "Warning: $APPIMAGE_TOOL not found, will try to create AppDir only"
SKIP_APPIMAGE=1
fi
# ── 虚拟环境 ──────────────────────────────────────────────────────────────
VENV_DIR="$PROJECT_ROOT/.venv_build"
echo "Creating virtual environment: $VENV_DIR"
python3 -m venv "$VENV_DIR"
source "$VENV_DIR/bin/activate"
pip install --upgrade pip
pip install -r requirements.txt
pip install pyinstaller
rm -rf "$PROJECT_ROOT/build/pyinstaller_build" "$PROJECT_ROOT/dist"
CORE_HIDDEN=(
--hidden-import core
--hidden-import core.embedded_db
--hidden-import core.graph_client
--hidden-import core.tool_executor
--hidden-import core.tool_limiter
--hidden-import core.tools
--hidden-import core.tools.memory_tools
--hidden-import core.prompts
--hidden-import core.prompts.prompt_manager
--hidden-import core.server
--hidden-import core.client
--hidden-import core.migrate
--hidden-import core.activity_recorder
)
echo "Cleaning previous builds..."
rm -rf build/dist build/__pycache__ 2>/dev/null || true
echo "Running PyInstaller for TUI..."
pyinstaller trulymem_entry.py \
# ── 打包 ──────────────────────────────────────────────────────────────────
echo "================================"
echo "Building TrulyMEM (TUI + Web embedded)"
echo "================================"
python -m PyInstaller trulymem_entry.py \
--clean --onefile --console --name TrulyMEM \
--distpath "$PROJECT_ROOT/dist" \
--workpath "$PROJECT_ROOT/build/pyinstaller_build/tui" \
--add-data "ui/styles:ui/styles" \
--add-data "core/prompts/templates:core/prompts/templates" \
--add-data "static:static" \
@ -58,7 +49,19 @@ pyinstaller trulymem_entry.py \
--hidden-import openai._client \
--hidden-import neo4j \
--hidden-import sqlite3 \
"${CORE_HIDDEN[@]}" \
--hidden-import core \
--hidden-import core.embedded_db \
--hidden-import core.graph_client \
--hidden-import core.tool_executor \
--hidden-import core.tool_limiter \
--hidden-import core.tools \
--hidden-import core.tools.memory_tools \
--hidden-import core.prompts \
--hidden-import core.prompts.prompt_manager \
--hidden-import core.server \
--hidden-import core.client \
--hidden-import core.migrate \
--hidden-import core.activity_recorder \
--hidden-import ui \
--hidden-import ui.app \
--hidden-import ui.login_screen \
@ -74,88 +77,65 @@ pyinstaller trulymem_entry.py \
--hidden-import web_api \
--hidden-import flask \
--hidden-import flask_cors \
--hidden-import werkzeug \
--collect-all textual \
--noconfirm
echo "Running PyInstaller for Web..."
pyinstaller web_api.py \
--clean --onefile --console --name trulymem-web \
--distpath "$PROJECT_ROOT/dist" \
--workpath "$PROJECT_ROOT/build/pyinstaller_build/web" \
--add-data "templates:templates" \
--add-data "static:static" \
--hidden-import flask \
--hidden-import flask_cors \
"${CORE_HIDDEN[@]}" \
--noconfirm
# ── 创建 AppDir ───────────────────────────────────────────────────────────
APP_NAME="TrulyMEM"
APP_DIR="$PROJECT_ROOT/build/${APP_NAME}.AppDir"
mkdir -p "$APP_DIR/usr/bin"
cp "dist/TrulyMEM" "$APP_DIR/usr/bin/TrulyMEM"
cp "$PROJECT_ROOT/dist/TrulyMEM" "$APPDIR/usr/bin/"
cp "$PROJECT_ROOT/dist/trulymem-web" "$APPDIR/usr/bin/"
cp "$PROJECT_ROOT/trulymem_entry.py" "$APPDIR/usr/share/trulymem/"
echo "===== Step 2: Create AppImage structure ====="
cat > "$APPDIR/AppRun" << 'EOF'
#!/bin/bash
set -e
SELF=$(readlink -f "$0")
APPDIR=$(dirname "$SELF")
export PATH="$APPDIR/usr/bin:$PATH"
exec "$APPDIR/usr/bin/TrulyMEM" "$@"
EOF
chmod +x "$APPDIR/AppRun"
cat > "$APPDIR/trulymem.desktop" << 'EOF'
# Desktop 文件
cat > "$APP_DIR/${APP_NAME}.desktop" << EOF
[Desktop Entry]
Name=TrulyMEM
Comment=AI Memory System with Long-term Memory
Exec=TrulyMEM %U
Icon=trulymem
Comment=TrueHumanMEM - AI Memory System
Exec=TrulyMEM
Icon=${APP_NAME}
Terminal=true
Type=Application
Categories=Utility;X-AI;
Categories=Utility;AI;
EOF
# Copy icon
if [ -f "$PROJECT_ROOT/pic/TrulyMEM.png" ]; then
cp "$PROJECT_ROOT/pic/TrulyMEM.png" "$APPDIR/trulymem.png"
elif [ -f "$PROJECT_ROOT/pic/TrulyMEM.iconset/icon_256x256.png" ]; then
cp "$PROJECT_ROOT/pic/TrulyMEM.iconset/icon_256x256.png" "$APPDIR/trulymem.png"
elif [ -f "$PROJECT_ROOT/pic/TrulyMEM.iconset/icon_128x128.png" ]; then
cp "$PROJECT_ROOT/pic/TrulyMEM.iconset/icon_128x128.png" "$APPDIR/trulymem.png"
# 图标
mkdir -p "$APP_DIR/usr/share/icons/hicolor/256x256/apps"
# 如果有图标文件就复制,否则创建占位
if [ -f "pic/image.png" ]; then
cp "pic/image.png" "$APP_DIR/usr/share/icons/hicolor/256x256/apps/${APP_NAME}.png"
cp "pic/image.png" "$APP_DIR/${APP_NAME}.png"
else
echo "Warning: No icon file found"
# 创建最小占位图标1x1 透明 PNG
echo -n "" > "$APP_DIR/${APP_NAME}.png"
fi
APPIMAGE="$PROJECT_ROOT/TrulyMEM.AppImage"
rm -f "$APPIMAGE"
# AppRun
cat > "$APP_DIR/AppRun" << 'APPRUN'
#!/bin/bash
HERE="$(dirname "$(readlink -f "${0}")")"
export PATH="${HERE}/usr/bin:${PATH}"
exec "${HERE}/usr/bin/TrulyMEM" "$@"
APPRUN
chmod +x "$APP_DIR/AppRun"
echo "===== Step 3: Package as AppImage ====="
cd /tmp
if ! command -v appimagetool &> /dev/null; then
echo "Downloading appimagetool..."
wget -q https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage -O appimagetool 2>/dev/null || \
curl -sL https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage -o appimagetool
chmod +x appimagetool 2>/dev/null || true
fi
cd "$PROJECT_ROOT"
if [ -x /tmp/appimagetool ]; then
/tmp/appimagetool "$APPDIR" "$APPIMAGE" || echo "appimagetool failed, keeping AppDir"
elif command -v appimagetool &> /dev/null; then
appimagetool "$APPDIR" "$APPIMAGE"
# ── 构建 AppImage ────────────────────────────────────────────────────────
if [ "$SKIP_APPIMAGE" != "1" ]; then
echo "================================"
echo "Building AppImage"
echo "================================"
# 支持 ARCH 环境变量
ARCH="${ARCH:-$(uname -m)}" "$APPIMAGE_TOOL" "$APP_DIR" "dist/TrulyMEM-${ARCH}.AppImage"
echo "AppImage: dist/TrulyMEM-${ARCH}.AppImage"
else
echo "Warning: appimagetool not available, AppDir at: $APPDIR"
echo "(Skipped AppImage packaging, AppDir ready at $APP_DIR)"
fi
echo "================================"
echo "===== Build Complete ====="
[ -f "$APPIMAGE" ] && echo "AppImage: $APPIMAGE" && ls -la "$APPIMAGE"
[ -d "$APPDIR" ] && echo "AppDir: $APPDIR"
echo "Binary: dist/TrulyMEM"
ls -la dist/
echo "===== Cleanup ====="
deactivate
rm -rf "$VENV_DIR"
rm -rf "$PROJECT_ROOT/build/pyinstaller_build"
rm -f /tmp/appimagetool
if [ -f "$APPIMAGE" ]; then
rm -rf "$APPDIR"
fi
echo "Done!"
echo "Build finished successfully!"

View File

@ -22,25 +22,8 @@ pip install -r requirements.txt
echo "Cleaning previous builds..."
rm -rf build/dist build/__pycache__ 2>/dev/null || true
# 共用 hidden importsTUI + Web 都需要的核心库)
CORE_HIDDEN=(
--hidden-import core
--hidden-import core.embedded_db
--hidden-import core.graph_client
--hidden-import core.tool_executor
--hidden-import core.tool_limiter
--hidden-import core.tools
--hidden-import core.tools.memory_tools
--hidden-import core.prompts
--hidden-import core.prompts.prompt_manager
--hidden-import core.server
--hidden-import core.client
--hidden-import core.migrate
--hidden-import core.activity_recorder
)
echo "================================"
echo "1⃣ Build TUI: TrulyMEM"
echo "Building TrulyMEM (TUI + Web embedded)"
echo "================================"
python -m PyInstaller trulymem_entry.py \
--clean --onefile --console --name TrulyMEM \
@ -57,7 +40,19 @@ python -m PyInstaller trulymem_entry.py \
--hidden-import openai._client \
--hidden-import neo4j \
--hidden-import sqlite3 \
"${CORE_HIDDEN[@]}" \
--hidden-import core \
--hidden-import core.embedded_db \
--hidden-import core.graph_client \
--hidden-import core.tool_executor \
--hidden-import core.tool_limiter \
--hidden-import core.tools \
--hidden-import core.tools.memory_tools \
--hidden-import core.prompts \
--hidden-import core.prompts.prompt_manager \
--hidden-import core.server \
--hidden-import core.client \
--hidden-import core.migrate \
--hidden-import core.activity_recorder \
--hidden-import ui \
--hidden-import ui.app \
--hidden-import ui.login_screen \
@ -73,25 +68,13 @@ python -m PyInstaller trulymem_entry.py \
--hidden-import web_api \
--hidden-import flask \
--hidden-import flask_cors \
--hidden-import werkzeug \
--collect-all textual \
--noconfirm
echo "================================"
echo "2⃣ Build Web: trulymem-web"
echo "================================"
python -m PyInstaller web_api.py \
--clean --onefile --console --name trulymem-web \
--add-data "templates:templates" \
--add-data "static:static" \
--hidden-import flask \
--hidden-import flask_cors \
"${CORE_HIDDEN[@]}" \
--noconfirm
echo "================================"
echo "===== Build Complete ====="
echo "Binary: dist/TrulyMEM"
echo "Binary: dist/trulymem-web"
ls -la dist/
deactivate

View File

@ -19,37 +19,14 @@ source "$VENV_DIR/bin/activate"
pip install --upgrade pip
pip install -r requirements.txt
# 生成图标
if [ -d "pic/TrulyMEM.iconset" ]; then
iconutil -c icns pic/TrulyMEM.iconset -o pic/TrulyMEM.icns
echo "ICNS icon generated: pic/TrulyMEM.icns"
fi
echo "Cleaning previous builds..."
rm -rf build/dist build/__pycache__ 2>/dev/null || true
CORE_HIDDEN=(
--hidden-import core
--hidden-import core.embedded_db
--hidden-import core.graph_client
--hidden-import core.tool_executor
--hidden-import core.tool_limiter
--hidden-import core.tools
--hidden-import core.tools.memory_tools
--hidden-import core.prompts
--hidden-import core.prompts.prompt_manager
--hidden-import core.server
--hidden-import core.client
--hidden-import core.migrate
--hidden-import core.activity_recorder
)
echo "================================"
echo "1⃣ Build TUI: TrulyMEM"
echo "Building TrulyMEM (TUI + Web embedded)"
echo "================================"
python -m PyInstaller trulymem_entry.py \
--clean --onefile --console --name TrulyMEM \
--icon "pic/TrulyMEM.icns" \
--add-data "ui/styles:ui/styles" \
--add-data "core/prompts/templates:core/prompts/templates" \
--add-data "static:static" \
@ -63,7 +40,19 @@ python -m PyInstaller trulymem_entry.py \
--hidden-import openai._client \
--hidden-import neo4j \
--hidden-import sqlite3 \
"${CORE_HIDDEN[@]}" \
--hidden-import core \
--hidden-import core.embedded_db \
--hidden-import core.graph_client \
--hidden-import core.tool_executor \
--hidden-import core.tool_limiter \
--hidden-import core.tools \
--hidden-import core.tools.memory_tools \
--hidden-import core.prompts \
--hidden-import core.prompts.prompt_manager \
--hidden-import core.server \
--hidden-import core.client \
--hidden-import core.migrate \
--hidden-import core.activity_recorder \
--hidden-import ui \
--hidden-import ui.app \
--hidden-import ui.login_screen \
@ -79,25 +68,13 @@ python -m PyInstaller trulymem_entry.py \
--hidden-import web_api \
--hidden-import flask \
--hidden-import flask_cors \
--hidden-import werkzeug \
--collect-all textual \
--noconfirm
echo "================================"
echo "2⃣ Build Web: trulymem-web"
echo "================================"
python -m PyInstaller web_api.py \
--clean --onefile --console --name trulymem-web \
--add-data "templates:templates" \
--add-data "static:static" \
--hidden-import flask \
--hidden-import flask_cors \
"${CORE_HIDDEN[@]}" \
--noconfirm
echo "================================"
echo "===== Build Complete ====="
echo "Binary: dist/TrulyMEM"
echo "Binary: dist/trulymem-web"
ls -la dist/
deactivate

View File

@ -1,49 +1,33 @@
#!/bin/bash
set -e
@echo off
setlocal enabledelayedexpansion
echo "===== Building TrulyMEM for Windows ====="
echo ===== Building TrulyMEM for Windows =====
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
cd "$PROJECT_ROOT"
echo "Project root: $PROJECT_ROOT"
set SCRIPT_DIR=%~dp0
set PROJECT_ROOT=%SCRIPT_DIR%..
cd /d "%PROJECT_ROOT%"
echo Project root: %CD%
if ! command -v python &> /dev/null; then
echo "Error: python not found"
exit 1
fi
where python >nul 2>nul
if %ERRORLEVEL% neq 0 (
echo Error: python not found
exit /b 1
)
VENV_DIR="$PROJECT_ROOT\.venv_build"
echo "Creating virtual environment: %VENV_DIR%"
set VENV_DIR=%PROJECT_ROOT%\.venv_build
echo Creating virtual environment: %VENV_DIR%
python -m venv "%VENV_DIR%"
call "%VENV_DIR%\Scripts\activate.bat"
pip install --upgrade pip
pip install -r requirements.txt
echo "Cleaning previous builds..."
rmdir /s /q "build\dist" 2>nul
rmdir /s /q "build\__pycache__" 2>nul
del /f /q "TrulyMEM.spec" 2>nul
echo Cleaning previous builds...
if exist build\dist rmdir /s /q build\dist
if exist build\__pycache__ rmdir /s /q build\__pycache__
CORE_HIDDEN=(
--hidden-import core
--hidden-import core.embedded_db
--hidden-import core.graph_client
--hidden-import core.tool_executor
--hidden-import core.tool_limiter
--hidden-import core.tools
--hidden-import core.tools.memory_tools
--hidden-import core.prompts
--hidden-import core.prompts.prompt_manager
--hidden-import core.server
--hidden-import core.client
--hidden-import core.migrate
--hidden-import core.activity_recorder
)
echo "================================"
echo "1. Build TUI: TrulyMEM.exe"
echo "================================"
echo ================================
echo Building TrulyMEM ^(TUI + Web embedded^)
echo ================================
python -m PyInstaller trulymem_entry.py ^
--clean --onefile --console --name TrulyMEM ^
--add-data "ui/styles;ui/styles" ^
@ -59,7 +43,19 @@ python -m PyInstaller trulymem_entry.py ^
--hidden-import openai._client ^
--hidden-import neo4j ^
--hidden-import sqlite3 ^
%CORE_HIDDEN% ^
--hidden-import core ^
--hidden-import core.embedded_db ^
--hidden-import core.graph_client ^
--hidden-import core.tool_executor ^
--hidden-import core.tool_limiter ^
--hidden-import core.tools ^
--hidden-import core.tools.memory_tools ^
--hidden-import core.prompts ^
--hidden-import core.prompts.prompt_manager ^
--hidden-import core.server ^
--hidden-import core.client ^
--hidden-import core.migrate ^
--hidden-import core.activity_recorder ^
--hidden-import ui ^
--hidden-import ui.app ^
--hidden-import ui.login_screen ^
@ -75,24 +71,16 @@ python -m PyInstaller trulymem_entry.py ^
--hidden-import web_api ^
--hidden-import flask ^
--hidden-import flask_cors ^
--hidden-import werkzeug ^
--collect-all textual ^
--noconfirm
echo "================================"
echo "2. Build Web: trulymem-web.exe"
echo "================================"
python -m PyInstaller web_api.py ^
--clean --onefile --console --name trulymem-web ^
--add-data "templates;templates" ^
--add-data "static;static" ^
--hidden-import flask ^
--hidden-import flask_cors ^
%CORE_HIDDEN% ^
--noconfirm
echo ================================
echo ===== Build Complete =====
echo Binary: dist\TrulyMEM.exe
dir dist\
echo "===== Build Complete ====="
echo "Output: dist/TrulyMEM.exe, dist/trulymem-web.exe"
deactivate
rmdir /s /q "%VENV_DIR%"
echo "Build finished successfully!"
call deactivate
if exist "%VENV_DIR%" rmdir /s /q "%VENV_DIR%"
echo Build finished successfully!
endlocal

View File

@ -69,7 +69,7 @@ a = Analysis(
'ui.handlers',
'ui.services', 'ui.services.config_manager', 'ui.services.config_service',
'web_api',
'flask', 'flask_cors',
'flask', 'flask_cors', 'werkzeug',
],
hookspath=[],
hooksconfig={},
@ -100,46 +100,4 @@ exe = EXE(
entitlements_file=None,
)
# ——— Web 服务二进制trulymem-web———
web_a = Analysis(
['web_api.py'],
pathex=[project_root],
binaries=[],
datas=[
(os.path.join(project_root, 'templates'), 'templates'),
(os.path.join(project_root, 'static'), 'static'),
],
hiddenimports=[
'flask', 'flask_cors',
'core', 'core.server', 'core.client',
'core.embedded_db', 'core.activity_recorder',
'core.migrate',
],
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[],
noarchive=False,
optimize=0,
)
web_pyz = PYZ(web_a.pure, block_cipher)
web_exe = EXE(
web_pyz,
web_a.scripts,
web_a.binaries,
web_a.datas,
[],
name='trulymem-web',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
console=False,
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
)