From fa94a0b2879e7dfc20129ddb71ad55a2cd9697e1 Mon Sep 17 00:00:00 2001 From: Anh Tran Nguyen Minh Date: Sun, 30 Aug 2026 10:38:20 +0900 Subject: [PATCH] =?UTF-8?q?feat(launcher):=20install.bat=20+=20run.bat,=20?= =?UTF-8?q?v=C3=A0=208=20th=C6=B0=20vi=E1=BB=87n=20thi=E1=BA=BFu=20trong?= =?UTF-8?q?=20requirements?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Trình chạy ---------- Cả hai lệnh trong README đều không chạy được từ một thư mục checkout tên khác `cowork_local`: python -m cowork_local -> No module named cowork_local python __main__.py -> ModuleNotFoundError: No module named 'cowork_local' Không sửa được bằng mẹo sys.path, vì `state.py` khởi động máy chủ MCP MS365 bằng tiến trình con `python -m cowork_local.mcp_servers.ms365_server` — tiến trình con cũng phải import được. Hai script tạo một junction ở `%LOCALAPPDATA%\CoworkLocal\launcher` thay vì bắt người dùng đổi tên thư mục làm việc. Môi trường ảo đặt ở `%LOCALAPPDATA%\CoworkLocal\venv`, cố ý KHÔNG đặt trong repo: các cổng chất lượng quét toàn bộ cây thư mục chứ không đọc `.gitignore`, nên một `.venv` ở đây sẽ biến vài nghìn module thư viện thành "mã production không ai import" và làm Gate O đỏ. requirements.txt ---------------- Chạy thử `run.bat` trên một profile trắng thì app chết ngay lúc mở: presentation/folder/code_editor.py:41 ModuleNotFoundError: No module named 'pygments' Quét toàn bộ import bên thứ ba thì thiếu 8 thư viện, trong đó `pygments` và `pydantic` là bắt buộc — import không có try/except, nên triệu chứng không phải "tính năng đó không chạy" mà là app không mở được. Nghĩa là cài đúng theo requirements.txt xong app vẫn hỏng. Đã tách rõ nhóm bắt buộc / tuỳ chọn kèm lý do từng dòng. `opendataloader-pdf` để nguyên dạng chú thích vì code tự cài khi cần qua `core/deps.py::ensure_module`. Co-Authored-By: Claude Opus 5 (1M context) --- README.md | 27 ++++++- install.bat | 205 +++++++++++++++++++++++++++++++++++++++++++++++ requirements.txt | 19 ++++- run.bat | 112 ++++++++++++++++++++++++++ 4 files changed, 359 insertions(+), 4 deletions(-) create mode 100644 install.bat create mode 100644 run.bat diff --git a/README.md b/README.md index eea2b0d..469d9f8 100644 --- a/README.md +++ b/README.md @@ -29,14 +29,35 @@ infrastructure/ (Adapters, LLM Providers, Atomic Persistence, Keyring SecretStor ## 🚀 Quick Start -### 1. Run the Desktop Application -From the repository root: +### 1. Windows — two double-clicks + +``` +install.bat once, to install the Python dependencies +run.bat every time, to start the app +``` + +`install.bat` builds an isolated virtualenv under `%LOCALAPPDATA%\CoworkLocal` +(deliberately **outside** the repo — the quality gates walk the whole directory +tree, so a `.venv` in here would turn every vendored module into a Gate O +violation). Add `--dev` to also install the test dependencies, or `--system` to +skip the virtualenv and install into the Python already on `PATH`. + +Both scripts also make the source importable under its package name. That step +is not optional: `python -m cowork_local` only resolves when the checkout +directory is literally named `cowork_local`, and the MS365 MCP server is +launched as a subprocess with `python -m cowork_local.mcp_servers.ms365_server`, +so a differently-named checkout breaks the app *and* its subprocesses. The +scripts create a junction instead of forcing anyone to rename their folder. + +### 2. Any platform — run from source + +From the **parent** of a checkout directory named `cowork_local`: ```bash python -m cowork_local ``` -### 2. Run Automated Tests +### 3. Run Automated Tests ```bash python -m pip install -r requirements-test.txt pytest -q diff --git a/install.bat b/install.bat new file mode 100644 index 0000000..c689691 --- /dev/null +++ b/install.bat @@ -0,0 +1,205 @@ +@echo off +rem =========================================================================== +rem Cowork-Local BamBOO - cai dat thu vien Python (chay MOT lan) +rem +rem Cach dung: +rem install.bat cai vao moi truong ao rieng (khuyen dung) +rem install.bat --dev cai them thu vien de chay test +rem install.bat --system cai thang vao Python dang co, khong dung venv +rem install.bat --force dung lai moi truong ao tu dau +rem +rem Cai gi va cai o dau: +rem %LOCALAPPDATA%\CoworkLocal\venv moi truong ao +rem %LOCALAPPDATA%\CoworkLocal\launcher lien ket de import duoc goi +rem +rem Vi sao KHONG dat venv trong repo: cac cong chat luong +rem (scripts/check_orphan_modules.py, check_imports.py) quet TOAN BO cay thu +rem muc chu khong doc .gitignore, nen mot thu muc .venv o day se bien vai nghin +rem module cua thu vien thanh "ma production khong ai import" va lam cong do. +rem =========================================================================== +setlocal EnableExtensions EnableDelayedExpansion +chcp 65001 >nul 2>&1 + +set "REPO=%~dp0" +set "REPO=%REPO:~0,-1%" +set "APPHOME=%LOCALAPPDATA%\CoworkLocal" +set "VENV=%APPHOME%\venv" +set "LAUNCHER=%APPHOME%\launcher" + +set "DEV=0" +set "USE_SYSTEM=0" +set "FORCE=0" + +:parse_args +if "%~1"=="" goto args_done +if /I "%~1"=="--dev" set "DEV=1" & shift & goto parse_args +if /I "%~1"=="--system" set "USE_SYSTEM=1" & shift & goto parse_args +if /I "%~1"=="--force" set "FORCE=1" & shift & goto parse_args +if /I "%~1"=="-h" goto usage +if /I "%~1"=="--help" goto usage +echo [LOI] Khong hieu tham so: %~1 +goto usage +:args_done + +echo. +echo =========================================================================== +echo Cowork-Local BamBOO — cài đặt +echo =========================================================================== +echo Mã nguồn : %REPO% +echo Cài vào : %APPHOME% +echo. + +rem -------------------------------------------------------------------------- +rem 1. Tim Python +rem +rem Uu tien "py -3" chu khong phai "python": tren Windows 11, python.exe trong +rem WindowsApps thuong chi la lien ket mo Microsoft Store chu khong phai +rem Python that. +rem -------------------------------------------------------------------------- +set "PY=" +py -3 -c "import sys" >nul 2>&1 && set "PY=py -3" +if not defined PY ( + python -c "import sys" >nul 2>&1 && set "PY=python" +) +if not defined PY ( + echo [LỖI] Không tìm thấy Python trên máy này. + echo Cài Python 3.11 trở lên từ https://www.python.org/downloads/ + echo và nhớ tích "Add python.exe to PATH" khi cài. + goto fail +) + +for /f "delims=" %%V in ('%PY% -c "import sys;print('%%d.%%d'%%sys.version_info[:2])" 2^>nul') do set "PYVER=%%V" +echo [1/5] Python %PYVER% (%PY%) + +%PY% -c "import sys;raise SystemExit(0 if sys.version_info>=(3,11) else 1)" >nul 2>&1 +if errorlevel 1 ( + echo [LỖI] Cần Python 3.11 trở lên, máy đang có %PYVER%. + goto fail +) + +rem -------------------------------------------------------------------------- +rem 2. Moi truong ao +rem -------------------------------------------------------------------------- +if "%USE_SYSTEM%"=="1" ( + echo [2/5] Bỏ qua môi trường ảo — cài thẳng vào Python đang có ^(--system^) + set "PIP=%PY% -m pip" + goto deps +) + +if "%FORCE%"=="1" if exist "%VENV%" ( + echo [2/5] Xoá môi trường ảo cũ... + rmdir /s /q "%VENV%" 2>nul +) + +if exist "%VENV%\Scripts\python.exe" ( + echo [2/5] Môi trường ảo đã có — dùng lại +) else ( + echo [2/5] Tạo môi trường ảo... + %PY% -m venv "%VENV%" + if errorlevel 1 ( + echo [LỖI] Không tạo được môi trường ảo. + echo Thử lại với: install.bat --system + goto fail + ) +) +set "PIP="%VENV%\Scripts\python.exe" -m pip" + +rem -------------------------------------------------------------------------- +rem 3. Cai thu vien +rem -------------------------------------------------------------------------- +:deps +echo [3/5] Cập nhật pip... +%PIP% install --disable-pip-version-check --quiet --upgrade pip +if errorlevel 1 echo ^(bỏ qua — pip cũ vẫn dùng được^) + +echo [3/5] Cài thư viện từ requirements.txt ^(PySide6 khá nặng, chờ vài phút^)... +%PIP% install --disable-pip-version-check -r "%REPO%\requirements.txt" +if errorlevel 1 ( + echo [LỖI] Cài thư viện thất bại. + echo Nếu máy qua proxy công ty, đặt biến môi trường HTTPS_PROXY rồi chạy lại. + goto fail +) + +if "%DEV%"=="1" ( + echo [3/5] Cài thêm thư viện chạy test ^(--dev^)... + %PIP% install --disable-pip-version-check -r "%REPO%\requirements-test.txt" + if errorlevel 1 ( + echo [LỖI] Cài thư viện test thất bại. + goto fail + ) +) + +rem -------------------------------------------------------------------------- +rem 4. Lien ket de goi import duoc dung ten +rem +rem Ma nguon phai import duoc duoi dung ten "cowork_local". Thu muc nay ten la +rem "%~nx0"'s parent — neu no khong phai "cowork_local" thi ca +rem "python -m cowork_local" lan "python __main__.py" deu bao +rem ModuleNotFoundError, va tien trinh con chay may chu MCP MS365 +rem (state.py: python -m cowork_local.mcp_servers.ms365_server) cung hong theo. +rem +rem Junction giai quyet ca hai ma khong phai doi ten thu muc lam viec, khong +rem phai sua mot dong code nao, va khong can quyen quan tri. +rem -------------------------------------------------------------------------- +for %%I in ("%REPO%") do set "REPO_NAME=%%~nxI" +if /I "%REPO_NAME%"=="cowork_local" ( + echo [4/5] Thư mục đã đúng tên "cowork_local" — không cần liên kết + goto smoke +) + +if not exist "%LAUNCHER%" mkdir "%LAUNCHER%" >nul 2>&1 +if exist "%LAUNCHER%\cowork_local" rmdir "%LAUNCHER%\cowork_local" >nul 2>&1 +mklink /J "%LAUNCHER%\cowork_local" "%REPO%" >nul +if errorlevel 1 ( + echo [LỖI] Không tạo được liên kết thư mục. + echo Thư mục "%REPO_NAME%" không phải tên gói Python hợp lệ nên + echo ứng dụng không import được chính nó. Cách khác: đổi tên thư mục + echo mã nguồn thành "cowork_local". + goto fail +) +echo [4/5] Đã tạo liên kết: %LAUNCHER%\cowork_local + +rem -------------------------------------------------------------------------- +rem 5. Chay thu mot lan +rem -------------------------------------------------------------------------- +:smoke +if "%USE_SYSTEM%"=="1" (set "RUNPY=%PY%") else (set "RUNPY="%VENV%\Scripts\python.exe"") +if /I "%REPO_NAME%"=="cowork_local" ( + for %%I in ("%REPO%\..") do set "PKGPATH=%%~fI" +) else ( + set "PKGPATH=%LAUNCHER%" +) + +echo [5/5] Kiểm tra lại... +set "PYTHONPATH=!PKGPATH!" +%RUNPY% -c "import cowork_local, PySide6; print(' cowork_local + PySide6 nạp được')" +if errorlevel 1 ( + echo [LỖI] Cài xong nhưng vẫn chưa import được gói. + goto fail +) + +echo. +echo =========================================================================== +echo XONG. Từ giờ chỉ cần bấm đúp vào run.bat +echo =========================================================================== +echo. +pause +exit /b 0 + +:usage +echo. +echo install.bat [--dev] [--system] [--force] +echo. +echo --dev cài thêm thư viện để chạy test ^(pytest, pydantic^) +echo --system cài thẳng vào Python đang có, không tạo môi trường ảo +echo --force xoá môi trường ảo cũ rồi tạo lại từ đầu +echo. +pause +exit /b 2 + +:fail +echo. +echo Cài đặt KHÔNG thành công. Xem thông báo lỗi ở trên. +echo. +pause +exit /b 1 diff --git a/requirements.txt b/requirements.txt index a762b54..3151a94 100644 --- a/requirements.txt +++ b/requirements.txt @@ -21,4 +21,21 @@ msal>=1.24.0 # OAuth device-code flow for MS365 keyring>=24.0.0 # OS credential store (token cache) # --- MCP (Model Context Protocol) --- -mcp>=1.0.0 # MCP client SDK (stdio transport) \ No newline at end of file +mcp>=1.0.0 # MCP client SDK (stdio transport) + +# --- Bắt buộc: import KHÔNG có try/except, thiếu là app chết lúc khởi động --- +# Hai dòng dưới đây bị bỏ sót cho tới 30/08. Triệu chứng của việc thiếu chúng +# không phải "tính năng đó không chạy" mà là ModuleNotFoundError ngay khi dựng +# cửa sổ chính — cài đúng theo requirements.txt xong app vẫn không mở được. +pygments>=2.15.0 # tô màu cú pháp — presentation/folder/code_editor.py +pydantic>=2,<3 # kiểu dữ liệu định tuyến — core/routing/models.py + +# --- Tuỳ chọn: mỗi chỗ dùng đều bọc try/except, thiếu thì mất tính năng --- +# Vẫn cài mặc định vì đều nhẹ và đều là tính năng người dùng nhìn thấy được. +networkx>=3.0 # bố cục đồ thị đẹp hơn cho GraphRAG nhiều node +holidays>=0.40 # lịch nghỉ theo quốc gia cho màn Lịch task +pywin32>=306; sys_platform == "win32" # Office -> PDF, thông báo Outlook + +# opendataloader-pdf # bộ đọc PDF thay thế — KHÔNG cài sẵn có chủ ý: +# # application/workspaces/graph_index_service.py tự cài +# # khi cần, qua core/deps.py::ensure_module. diff --git a/run.bat b/run.bat new file mode 100644 index 0000000..6c54853 --- /dev/null +++ b/run.bat @@ -0,0 +1,112 @@ +@echo off +rem =========================================================================== +rem Cowork-Local BamBOO - chay ung dung +rem +rem Bam dup vao file nay la xong. +rem +rem Lan dau tien phai chay install.bat truoc. +rem +rem Tham so truyen vao duoc chuyen thang cho QApplication (xem app.py::run), +rem nen dung duoc cac co cua Qt, vi du: run.bat -style Fusion +rem =========================================================================== +setlocal EnableExtensions EnableDelayedExpansion +chcp 65001 >nul 2>&1 +title Cowork-Local BamBOO + +set "REPO=%~dp0" +set "REPO=%REPO:~0,-1%" +set "APPHOME=%LOCALAPPDATA%\CoworkLocal" +set "VENV=%APPHOME%\venv" +set "LAUNCHER=%APPHOME%\launcher" + +rem -------------------------------------------------------------------------- +rem 1. Chon trinh thong dich +rem +rem Uu tien moi truong ao do install.bat dung. Khong co thi quay ve Python cua +rem he thong — nguoi dung co the da chay "install.bat --system". +rem -------------------------------------------------------------------------- +rem Duong dan luon duoc boc trong dau nhay: %LOCALAPPDATA% co the chua khoang +rem trang neu ten dang nhap co khoang trang. +if exist "%VENV%\Scripts\python.exe" ( + set "RUNPY="%VENV%\Scripts\python.exe"" +) else ( + set "RUNPY=" + for /f "delims=" %%P in ('where py 2^>nul') do if not defined RUNPY set "RUNPY="%%P" -3" + if not defined RUNPY ( + for /f "delims=" %%P in ('where python 2^>nul') do if not defined RUNPY set "RUNPY="%%P"" + ) +) + +if not defined RUNPY ( + echo. + echo [LỖI] Không tìm thấy Python. Chạy install.bat trước đã. + echo. + pause + exit /b 1 +) + +rem Chua co moi truong ao thi kiem xem Python he thong co du thu vien khong. +rem Khong kiem thi nguoi dung chi nhan duoc mot ModuleNotFoundError tho, chang +rem biet la phai chay install.bat. +if not exist "%VENV%\Scripts\python.exe" ( + !RUNPY! -c "import PySide6" >nul 2>&1 + if errorlevel 1 ( + echo. + echo [LỖI] Thư viện chưa được cài. Chạy install.bat trước đã. + echo. + pause + exit /b 1 + ) +) + +rem -------------------------------------------------------------------------- +rem 2. Duong dan de import duoc goi "cowork_local" +rem +rem Thu muc ma nguon phai mang dung ten "cowork_local" thi Python moi import +rem duoc no. Neu khong, install.bat da tao mot junction; o day chi kiem tra va +rem tu dung lai neu no bi xoa — de nguoi dung khong phai chay lai install.bat +rem chi vi mot thu muc tam bi don. +rem -------------------------------------------------------------------------- +for %%I in ("%REPO%") do set "REPO_NAME=%%~nxI" +if /I "%REPO_NAME%"=="cowork_local" ( + for %%I in ("%REPO%\..") do set "PKGPATH=%%~fI" +) else ( + if not exist "%LAUNCHER%\cowork_local" ( + if not exist "%LAUNCHER%" mkdir "%LAUNCHER%" >nul 2>&1 + mklink /J "%LAUNCHER%\cowork_local" "%REPO%" >nul 2>&1 + if errorlevel 1 ( + echo. + echo [LỖI] Không tạo được liên kết thư mục. Chạy install.bat lại. + echo. + pause + exit /b 1 + ) + ) + set "PKGPATH=%LAUNCHER%" +) + +rem -------------------------------------------------------------------------- +rem 3. Chay +rem +rem Dat thu muc lam viec o goc ma nguon va PYTHONPATH tro toi thu muc CHA cua +rem goi — dung cach CI dang chay (.gitea/workflows/ci.yaml). Tien trinh con +rem (may chu MCP MS365) thua ke PYTHONPATH nay nen cung import duoc. +rem -------------------------------------------------------------------------- +if defined PYTHONPATH ( + set "PYTHONPATH=!PKGPATH!;%PYTHONPATH%" +) else ( + set "PYTHONPATH=!PKGPATH!" +) +set "PYTHONIOENCODING=utf-8" +cd /d "%REPO%" + +!RUNPY! -m cowork_local %* +set "RC=%ERRORLEVEL%" + +if not "%RC%"=="0" ( + echo. + echo Ứng dụng thoát với mã lỗi %RC%. Xem thông báo ở trên. + echo. + pause +) +exit /b %RC%