diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9a77874..8fe2abe 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,9 +3,7 @@ name: CI on: pull_request: push: - branches: - - main - workflow_dispatch: + branches: [main] permissions: contents: read @@ -16,7 +14,7 @@ concurrency: jobs: quality: - name: Quality and build + name: Quality runs-on: ubuntu-24.04 timeout-minutes: 15 steps: @@ -24,35 +22,28 @@ jobs: uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - - name: Install pnpm uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9 - - name: Install Node.js uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version-file: .node-version cache: pnpm cache-dependency-path: pnpm-lock.yaml - - name: Install dependencies run: pnpm install --frozen-lockfile - - - name: Check source - run: pnpm check - - - name: Run fake RPC smoke + - name: Typecheck + run: pnpm typecheck + - name: Lint + run: pnpm lint + - name: Format check + run: pnpm format:check + - name: Test + run: pnpm test + - name: Fake RPC smoke run: OMP_RPC_FAKE=1 node scripts/rpc-smoke.mjs - - - name: Download and verify OMP Runtime - run: pnpm runtime:download - - - name: Run real OMP RPC smoke without model API - run: OMP_RPC_TEST_CONFIG=1 node scripts/rpc-smoke.mjs - - - name: Build application + - name: Build run: pnpm build - - name: Check production output shell: bash run: | @@ -66,8 +57,9 @@ jobs: exit 1 fi - linux-package: - name: Linux x64 packages + linux-integration: + name: Linux integration and packages + if: github.event_name == 'push' && github.ref == 'refs/heads/main' needs: quality runs-on: ubuntu-24.04 timeout-minutes: 30 @@ -86,37 +78,36 @@ jobs: cache-dependency-path: pnpm-lock.yaml - name: Install dependencies run: pnpm install --frozen-lockfile - - name: Download Runtime and build packages - run: pnpm runtime:download && pnpm package:linux && pnpm package:check + - name: Download and verify OMP Runtime + run: pnpm runtime:download && pnpm runtime:check + - name: Real RPC smoke + run: OMP_RPC_TEST_CONFIG=1 node scripts/rpc-smoke.mjs + - name: Build x64 packages + run: pnpm package:linux && pnpm package:check - name: Inspect package metadata shell: bash run: | set -euo pipefail deb="$(find dist -maxdepth 1 -name '*.deb' -print -quit)" dpkg-deb --info "$deb" - dpkg-deb --contents "$deb" | tee dist/deb-contents.txt - if rg -n 'tests/|tmp/|omp-linux-arm64|\.map$' dist/deb-contents.txt; then - echo '安装包包含测试、临时、arm64 或 source map 文件' >&2 - exit 1 - fi test "$(dpkg-deb -f "$deb" Architecture)" = amd64 test "$(dpkg-deb -f "$deb" Package)" = omp-desktop - name: Upload packages uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 with: - name: linux-x64-packages + name: main-linux-x64-${{ github.sha }} path: | dist/*.AppImage dist/*.deb dist/*.sha256 dist/build-metrics.json - dist/deb-contents.txt if-no-files-found: error retention-days: 7 linux-display-smoke: - name: Linux ${{ matrix.ubuntu }} / ${{ matrix.arch }} / ${{ matrix.display }} - needs: quality + name: Ubuntu ${{ matrix.ubuntu }} x64 ${{ matrix.display }} smoke + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + needs: linux-integration runs-on: ${{ matrix.runner }} timeout-minutes: 20 strategy: @@ -125,89 +116,47 @@ jobs: include: - runner: ubuntu-22.04 ubuntu: '22.04' - arch: x64 display: x11 - - runner: ubuntu-22.04 - ubuntu: '22.04' - arch: x64 - display: wayland - runner: ubuntu-24.04 ubuntu: '24.04' - arch: x64 - display: x11 - - runner: ubuntu-24.04 - ubuntu: '24.04' - arch: x64 - display: wayland - - runner: ubuntu-22.04-arm - ubuntu: '22.04' - arch: arm64 - display: x11 - - runner: ubuntu-22.04-arm - ubuntu: '22.04' - arch: arm64 - display: wayland - - runner: ubuntu-24.04-arm - ubuntu: '24.04' - arch: arm64 - display: x11 - - runner: ubuntu-24.04-arm - ubuntu: '24.04' - arch: arm64 display: wayland steps: - - name: Checkout + - name: Checkout smoke scripts uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - - - name: Install pnpm - uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9 - + sparse-checkout: | + scripts + .node-version - name: Install Node.js uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version-file: .node-version - cache: pnpm - cache-dependency-path: pnpm-lock.yaml - - - name: Install dependencies - run: pnpm install --frozen-lockfile - - - name: Build application - run: pnpm build - - - name: Configure Electron sandbox - shell: bash - run: | - set -euo pipefail - electron_dir="$(dirname "$(node -p "require('electron')")")" - sandbox_path="$electron_dir/chrome-sandbox" - test -f "$sandbox_path" - sudo chown root:root "$sandbox_path" - sudo chmod 4755 "$sandbox_path" - stat -c '%U:%G %a %n' "$sandbox_path" - - - name: Install X11 test dependencies - if: matrix.display == 'x11' - run: | - sudo apt-get update - sudo apt-get install --yes --no-install-recommends dbus-x11 xauth xvfb - - - name: Install Wayland test dependencies - if: matrix.display == 'wayland' + - name: Download main packages + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: main-linux-x64-${{ github.sha }} + path: candidate + - name: Verify candidate checksums + working-directory: candidate + run: sha256sum -c *.deb.sha256 + - name: Install candidate deb + run: sudo apt-get update && sudo apt-get install --yes ./candidate/*.deb + - name: Install display dependencies run: | sudo apt-get update - sudo apt-get install --yes --no-install-recommends dbus-x11 weston - - - name: Run display smoke - run: dbus-run-session -- bash scripts/ci-linux-display-smoke.sh "${{ matrix.display }}" - + if [[ '${{ matrix.display }}' == x11 ]]; then + sudo apt-get install --yes --no-install-recommends dbus-x11 xauth xvfb + else + sudo apt-get install --yes --no-install-recommends dbus-x11 weston + fi + - name: Run packaged display smoke + run: dbus-run-session -- bash scripts/ci-linux-display-smoke.sh '${{ matrix.display }}' '/opt/OMP Desktop/omp-desktop' - name: Upload smoke diagnostics if: always() uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 with: - name: linux-${{ matrix.ubuntu }}-${{ matrix.arch }}-${{ matrix.display }} + name: main-${{ matrix.ubuntu }}-${{ matrix.display }}-smoke path: | ${{ runner.temp }}/omp-ci/** tests/artifacts/*.png diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2f52350..2b64f9e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -4,7 +4,7 @@ on: workflow_dispatch: inputs: release_version: - description: Stable SemVer without v, for example 0.1.0 + description: Stable SemVer without v, for example 0.1.1 required: true type: string @@ -16,31 +16,20 @@ concurrency: cancel-in-progress: false jobs: - gpu-acceptance: - name: RTX 3090 Wayland acceptance - runs-on: [self-hosted, linux, x64] - timeout-minutes: 20 + build-candidate: + name: Build release candidate + runs-on: ubuntu-24.04 + timeout-minutes: 35 steps: - name: Validate version shell: bash run: | set -euo pipefail - if [[ ! '${{ inputs.release_version }}' =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then - echo 'release_version 必须是稳定 SemVer X.Y.Z' >&2 - exit 1 - fi + [[ '${{ inputs.release_version }}' =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] - name: Checkout requested commit uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - - name: Check native Wayland and RTX 3090 - shell: bash - run: | - set -euo pipefail - test "${XDG_SESSION_TYPE:-}" = wayland - test -n "${WAYLAND_DISPLAY:-}" - nvidia-smi --query-gpu=name --format=csv,noheader | tee gpu-name.txt - grep -Eiq 'RTX 3090' gpu-name.txt - name: Install pnpm uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9 - name: Install Node.js @@ -49,35 +38,129 @@ jobs: node-version-file: .node-version cache: pnpm cache-dependency-path: pnpm-lock.yaml - - name: Install, verify Runtime and build + - name: Install dependencies + run: pnpm install --frozen-lockfile + - name: Set candidate version and build once run: | - set -euo pipefail - pnpm install --frozen-lockfile - runtime_url="$(node -p "JSON.parse(require('fs').readFileSync('runtime/manifest.json', 'utf8')).url")" - curl --fail --location --retry 3 --retry-delay 2 "$runtime_url" -o runtime/omp - chmod 0755 runtime/omp + pnpm pkg set version='${{ inputs.release_version }}' + pnpm runtime:download pnpm runtime:check - pnpm check - pnpm build - - name: Run hardware GPU smoke + pnpm package:linux + pnpm package:check + - name: Check candidate metadata shell: bash run: | set -euo pipefail - system_sandbox=/usr/share/typora/chrome-sandbox - test "$(stat -c '%U:%G %a' "$system_sandbox")" = 'root:root 4755' - electron_binary="$(node -p "require('electron')")" - electron_sandbox="$(dirname "$electron_binary")/chrome-sandbox" - rm -f "$electron_sandbox" - ln -s "$system_sandbox" "$electron_sandbox" - mkdir -p tests/artifacts - OMP_SMOKE_SCREENSHOT="$PWD/tests/artifacts/gpu-smoke.png" \ - OMP_GPU_ACCEPTANCE_OUTPUT="$PWD/tests/artifacts/gpu-info.json" \ - pnpm smoke - if grep -En '"(gpu_compositing|webgl|webgl2)": "(disabled|unavailable|software)' tests/artifacts/gpu-info.json; then - echo 'Electron 没有使用预期的硬件加速' >&2 - exit 1 + deb="$(find dist -maxdepth 1 -name '*.deb' -print -quit)" + test "$(dpkg-deb -f "$deb" Version)" = '${{ inputs.release_version }}' + test "$(dpkg-deb -f "$deb" Architecture)" = amd64 + test "$(dpkg-deb -f "$deb" Package)" = omp-desktop + - name: Upload the only release candidate + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 + with: + name: release-candidate-${{ github.sha }}-${{ inputs.release_version }} + path: | + dist/*.AppImage + dist/*.deb + dist/*.sha256 + dist/build-metrics.json + if-no-files-found: error + retention-days: 30 + + hosted-full-matrix: + name: Ubuntu ${{ matrix.ubuntu }} x64 ${{ matrix.display }} candidate smoke + needs: build-candidate + runs-on: ${{ matrix.runner }} + timeout-minutes: 20 + strategy: + fail-fast: false + matrix: + include: + - runner: ubuntu-22.04 + ubuntu: '22.04' + display: x11 + - runner: ubuntu-22.04 + ubuntu: '22.04' + display: wayland + - runner: ubuntu-24.04 + ubuntu: '24.04' + display: x11 + - runner: ubuntu-24.04 + ubuntu: '24.04' + display: wayland + steps: + - name: Checkout smoke scripts + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + sparse-checkout: | + scripts + .node-version + - name: Install Node.js + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + with: + node-version-file: .node-version + - name: Download the release candidate + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: release-candidate-${{ github.sha }}-${{ inputs.release_version }} + path: candidate + - name: Verify candidate checksums + working-directory: candidate + run: sha256sum -c *.deb.sha256 + - name: Install candidate deb + run: sudo apt-get update && sudo apt-get install --yes ./candidate/*.deb + - name: Install display dependencies + run: | + sudo apt-get update + if [[ '${{ matrix.display }}' == x11 ]]; then + sudo apt-get install --yes --no-install-recommends dbus-x11 xauth xvfb + else + sudo apt-get install --yes --no-install-recommends dbus-x11 weston fi - - name: Verify manual acceptance record + - name: Run candidate display smoke + run: dbus-run-session -- bash scripts/ci-linux-display-smoke.sh '${{ matrix.display }}' '/opt/OMP Desktop/omp-desktop' + - name: Upload display diagnostics + if: always() + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 + with: + name: release-${{ matrix.ubuntu }}-${{ matrix.display }}-smoke + path: | + ${{ runner.temp }}/omp-ci/** + tests/artifacts/*.png + if-no-files-found: warn + retention-days: 30 + + gpu-acceptance: + name: RTX 3090 Wayland candidate acceptance + needs: [build-candidate, hosted-full-matrix] + runs-on: [self-hosted, Linux, X64, rtx3090, wayland] + timeout-minutes: 25 + steps: + - name: Validate native Wayland and RTX 3090 + shell: bash + run: | + set -euo pipefail + test "${XDG_SESSION_TYPE:-}" = wayland + test -n "${WAYLAND_DISPLAY:-}" + nvidia-smi --query-gpu=name --format=csv,noheader | tee gpu-name.txt + grep -Eiq 'RTX 3090' gpu-name.txt + - name: Checkout smoke and acceptance scripts + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + sparse-checkout: | + scripts + .node-version + - name: Download the release candidate + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: release-candidate-${{ github.sha }}-${{ inputs.release_version }} + path: candidate + - name: Verify candidate checksums + working-directory: candidate + run: sha256sum -c *.AppImage.sha256 + - name: Verify existing manual acceptance record shell: bash run: | set -euo pipefail @@ -85,64 +168,74 @@ jobs: acceptance_file="$state_root/omp-desktop/release-acceptance/${GITHUB_SHA}.json" node scripts/gpu-acceptance.mjs verify "$acceptance_file" \ --commit "$GITHUB_SHA" --version '${{ inputs.release_version }}' + mkdir -p tests/artifacts cp "$acceptance_file" tests/artifacts/gpu-acceptance.json + - name: Run the candidate AppImage on real GPU + shell: bash + run: | + set -euo pipefail + appimage="$(find candidate -maxdepth 1 -name '*.AppImage' -print -quit)" + chmod +x "$appimage" + mkdir -p tests/artifacts + APPIMAGE_EXTRACT_AND_RUN=1 \ + OMP_DISPLAY_SERVER=wayland \ + OMP_SMOKE_EXECUTABLE="$PWD/$appimage" \ + OMP_SMOKE_SCREENSHOT="$PWD/tests/artifacts/gpu-smoke.png" \ + OMP_GPU_ACCEPTANCE_OUTPUT="$PWD/tests/artifacts/gpu-info.json" \ + node scripts/electron-smoke.mjs + node --input-type=commonjs - <<'NODE' + const fs = require('node:fs') + const data = JSON.parse(fs.readFileSync('tests/artifacts/gpu-info.json', 'utf8')) + const status = data.featureStatus || {} + for (const key of ['gpu_compositing', 'webgl', 'webgl2']) { + if (!status[key] || /disabled|unavailable|software/i.test(status[key])) { + throw new Error(`GPU feature ${key} 未通过: ${status[key]}`) + } + } + NODE - name: Upload GPU evidence + if: always() uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 with: name: gpu-acceptance-${{ github.sha }} - path: tests/artifacts/* + path: | + tests/artifacts/* + gpu-name.txt if-no-files-found: error retention-days: 90 - build-release: - name: Build draft release - needs: gpu-acceptance + create-draft-release: + name: Create draft release + needs: [build-candidate, hosted-full-matrix, gpu-acceptance] runs-on: ubuntu-24.04 - timeout-minutes: 35 + timeout-minutes: 15 steps: - - name: Checkout accepted commit + - name: Checkout release metadata uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - - name: Install pnpm - uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9 - - name: Install Node.js - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + - name: Download the same release candidate + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 with: - node-version-file: .node-version - cache: pnpm - cache-dependency-path: pnpm-lock.yaml - - name: Build release packages - shell: bash - run: | - set -euo pipefail - pnpm install --frozen-lockfile - pnpm pkg set version='${{ inputs.release_version }}' - runtime_url="$(node -p "JSON.parse(require('fs').readFileSync('runtime/manifest.json', 'utf8')).url")" - curl --fail --location --retry 3 --retry-delay 2 "$runtime_url" -o runtime/omp - chmod 0755 runtime/omp - pnpm runtime:check - pnpm package:linux - pnpm package:check - deb="$(find dist -maxdepth 1 -name '*.deb' -print -quit)" - test "$(dpkg-deb -f "$deb" Version)" = '${{ inputs.release_version }}' - test "$(dpkg-deb -f "$deb" Architecture)" = amd64 + name: release-candidate-${{ github.sha }}-${{ inputs.release_version }} + path: dist + - name: Download GPU evidence + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: gpu-acceptance-${{ github.sha }} + path: gpu-evidence - name: Write release notes shell: bash run: | set -euo pipefail - appimage="$(basename "$(find dist -maxdepth 1 -name '*.AppImage' -print -quit)")" - deb="$(basename "$(find dist -maxdepth 1 -name '*.deb' -print -quit)")" printf '%s\n' \ '# OMP Desktop v${{ inputs.release_version }}' \ '' \ - '- 支持:Ubuntu 22.04、24.04 x64' \ - '- Bundled OMP:v17.1.0' \ - "- AppImage:$appimage" \ - "- Debian:$deb" \ - '- GPU 验收:RTX 3090、原生 Wayland,绑定 commit ${{ github.sha }}' \ + '- x64:Ubuntu 22.04/24.04,X11/Wayland Hosted smoke' \ + '- RTX 3090 原生 Wayland 真机验收' \ + '- AppImage 和 deb 从同一个 Release Candidate 构建,后续未重新打包' \ '' \ - '发布前请人工检查 SHA-256、已知问题和 GPU artifact。' > release-notes.md + '请人工检查 Draft Release 的文件、SHA-256、截图和 GPU artifact,再点击 Publish。' > release-notes.md - name: Create or update Draft Release env: GH_TOKEN: ${{ github.token }} diff --git a/README.md b/README.md index 042876a..f4fce0f 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,105 @@ # OMP Desktop -OMP Desktop 是 OMP 的 Linux 桌面客户端。首个发布版只提供 Ubuntu 22.04/24.04、x86-64 的 AppImage 和 `.deb`,内置 OMP `v17.1.0`。 +面向本地项目的 OMP Agent 桌面客户端。把 Workspace、会话、文件和 Agent 任务放在同一个 Ubuntu 原生窗口中。 -## 使用 +**[下载最新版本](https://github.com/LetItBe12345/omp_app/releases/latest)** · [安装说明](docs/ubuntu-installation.md) · [提交问题](https://github.com/LetItBe12345/omp_app/issues) -1. 从 GitHub Release 下载 `omp-desktop-*-linux-x64.AppImage` 或 `.deb`,按 [Ubuntu 安装说明](docs/ubuntu-installation.md) 校验 SHA-256 并安装。 -2. 第一次没有 Provider 凭据时,在终端运行 `omp-desktop --setup-provider`。这会启动内置 OMP 的交互终端;按 OMP 的提示完成 Provider 登录,然后退出。Desktop 不读取或保存 Token。 -3. 正常运行 `omp-desktop`,选择 Workspace,新建 Session,在模型选择器中选择模型并发送 Prompt。后续新增 Provider 或重新登录在 GUI 中完成。 -4. 图形驱动黑屏时可从终端运行 `omp-desktop --disable-gpu`;该参数只对本次启动生效。 -5. 卸载 `.deb` 使用 `sudo apt remove omp-desktop`。卸载不会删除 Workspace、Session、Runtime 设置或日志;重新安装后会继续使用这些数据。 +[![Release](https://img.shields.io/github/v/release/LetItBe12345/omp_app?label=release)](https://github.com/LetItBe12345/omp_app/releases/latest) +[![CI](https://github.com/LetItBe12345/omp_app/actions/workflows/ci.yml/badge.svg)](https://github.com/LetItBe12345/omp_app/actions/workflows/ci.yml) +[![Ubuntu](https://img.shields.io/badge/Ubuntu-22.04%20%7C%2024.04-E95420)](docs/ubuntu-installation.md) +[![License](https://img.shields.io/badge/license-MIT-2f2f2f)](LICENSE) -安装包、Runtime 和详细验收步骤见 [发布验收](docs/release-acceptance.md);维护者发布流程见 [发布流程](docs/releasing.md)。 +

+ OMP Desktop 在 Ubuntu Wayland 中管理 Workspace、文件树和 Agent 会话 +

+ +

Ubuntu 24.04 · Wayland · 三栏 Workspace 工作界面

+ +OMP Desktop 不是一个新的 IDE。它是 OMP 的图形工作台,让你在不离开项目上下文的情况下管理会话、选择文件、发送任务,并查看 Agent 的流式输出、Thinking、Tool Call 和权限请求。 + +## 当前能力 + +- 管理多个本地 Workspace,以及各 Workspace 下的会话。 +- 在独立文件栏中浏览和搜索当前项目,将文件、目录或历史会话加入上下文。 +- 查看流式回答、Thinking、工具执行状态和 Permission 请求。 +- 选择模型、Thinking 等级和执行权限,支持 Stop 与逐条 Follow-up。 +- 自动恢复上次 Workspace、会话、草稿和 Runtime 设置。 +- 为 OMP Runtime 单独配置直连、自动代理或手动 HTTP 代理,不要求开启系统全局代理。 +- 内置固定版本的 OMP Runtime,安装后不再下载应用组件。 + +## 安装 + +当前正式支持 Ubuntu 22.04 LTS、24.04 LTS x86-64。 + +从 [GitHub Releases](https://github.com/LetItBe12345/omp_app/releases/latest) 下载 `.deb` 和对应的 `.sha256` 文件: + +```sh +sha256sum -c omp-desktop-0.1.0-linux-x64.deb.sha256 +sudo apt install ./omp-desktop-0.1.0-linux-x64.deb +``` + +也可以下载 AppImage 直接运行。完整步骤、FUSE 要求和卸载方式见 [Ubuntu 安装说明](docs/ubuntu-installation.md)。 + +## 首次使用 + +第一次使用前,在终端配置 OMP Provider: + +```sh +omp-desktop --setup-provider +``` + +这条命令会在当前终端启动应用内置的 OMP 配置界面。按提示完成 Provider 登录后退出,再从桌面启动器打开 OMP Desktop。 + +Desktop 不读取或保存 Provider Token。凭据由 OMP 自己管理。 + +进入应用后: + +1. 点击 Workspace 旁的 `+`,选择本地项目目录。 +2. 新建或打开一个会话。 +3. 等待底部 Runtime 状态变为就绪。 +4. 选择模型、Thinking 等级和权限模式,然后发送任务。 +5. 通过文件树或输入框的 `@` 引用补充项目上下文。 + +遇到显卡驱动导致的黑屏时,可以用下面的命令进行临时诊断: + +```sh +omp-desktop --disable-gpu +``` + +该参数只对本次启动生效,不是默认运行方式。 + +## 产品边界 + +当前版本聚焦本地 Agent 工作流,不提供应用内代码编辑器、Git Diff/Review、集成终端、多窗口或插件市场。命令和文件操作由本地 OMP Runtime 执行,并受当前权限模式控制。 + +`v0.1.0` 已在 NVIDIA RTX 3090 原生 Wayland 环境完成硬件验收。Intel、AMD 和 X11 真实硬件环境尚未纳入本次正式验收范围。 ## 开发 +需要 Node.js 24 和 pnpm 11: + ```sh pnpm install pnpm runtime:download pnpm dev +``` + +提交前运行: + +```sh pnpm check +pnpm build ``` -`runtime/omp` 不提交到 Git。它由 `runtime/manifest.json` 固定的 URL 和 SHA-256 下载。生产构建使用 `pnpm package:linux`,只在 Linux x64 上生成 AppImage 和 `.deb`。 +`runtime/omp` 不提交到 Git。版本、下载地址和 SHA-256 固定在 `runtime/manifest.json`。Linux x64 打包命令: + +```sh +pnpm package:linux +pnpm package:check +``` + +架构和 RPC 说明见 [Desktop 架构](docs/desktop-architecture.md) 与 [OMP RPC](docs/OMP_RPC.md)。发布验收和维护者流程见 [发布验收](docs/release-acceptance.md) 与 [发布流程](docs/releasing.md)。 + +## License + +[MIT](LICENSE) diff --git a/docs/assets/omp-desktop-workspace.png b/docs/assets/omp-desktop-workspace.png new file mode 100644 index 0000000..2cb3f7e Binary files /dev/null and b/docs/assets/omp-desktop-workspace.png differ diff --git a/docs/releasing.md b/docs/releasing.md index 216f65f..102eee3 100644 --- a/docs/releasing.md +++ b/docs/releasing.md @@ -1,9 +1,20 @@ # 发布流程 -1. 在 `main` 上完成 CI。PR 只需要 GitHub Hosted CI 通过即可合并。 -2. 在本机原生 Wayland + RTX 3090 上构建当前 commit,完成 [发布验收](release-acceptance.md),生成与 commit 同名的验收记录。 -3. 在 GitHub Actions 手动运行 `Release`,输入不带 `v` 的稳定版本,例如 `0.1.0`。Workflow 会先在 self-hosted runner 检查 Wayland、GPU、Runtime 和验收记录;记录不存在或 commit/version 不匹配时不会打包。 -4. GPU gate 通过后,GitHub x64 Hosted Runner 下载并校验固定的 OMP,构建 AppImage 和 `.deb`,生成各自 SHA-256 文件,并创建 Draft Release `v`。Workflow 不创建 Tag。 -5. 检查 Draft Release 的文件、校验值、Release Notes、GPU 摘要和已知问题。确认无误后在 GitHub 页面点击 Publish;此时 GitHub 才创建 Tag。 +CI 和 CD 分开:PR 只做快速质量检查,`main` 做发布前健康检查,Release 测试并发布同一个候选产物。 -版本参数只接受 `X.Y.Z`。同版本同 commit 的 Draft 可更新;同名 Tag 指向其他 commit 时必须停止。发布前不要把 Token、`gpu-acceptance.json` 外的本机日志或认证文件上传到 artifact。 +## PR 和 main + +- PR 只运行 `CI / Quality`:依赖安装、类型检查、Lint、格式检查、测试、fake RPC smoke 和生产构建。 +- 合并到 `main` 后,额外运行真实 Runtime/RPC、x64 AppImage/deb 构建,以及 Ubuntu 22.04 X11 和 Ubuntu 24.04 Wayland 两组安装包 smoke。 +- PR 不下载真实 Runtime、不打包、不运行 GPU 或显示矩阵。 + +## Release + +1. 确认目标 commit 已在 `main`,并在本机原生 Wayland + RTX 3090 完成 [人工发布验收](release-acceptance.md),生成 `~/.local/state/omp-desktop/release-acceptance/.json`。 +2. 在 GitHub Actions 手动运行 `Release`,输入不带 `v` 的版本,例如 `0.1.1`。 +3. `Build release candidate` 在 Hosted Runner 上只构建一次 AppImage、deb、SHA-256 和构建指标,并上传为 artifact。 +4. 四组 Hosted 测试(Ubuntu 22.04/24.04 × X11/Wayland)都下载并测试这个 artifact;测试安装后的 `.deb`,不重新构建。 +5. `RTX 3090 Wayland candidate acceptance` 使用标签 `[self-hosted, Linux, X64, rtx3090, wayland]`,下载同一个 AppImage,检查真实 GPU、WebGL/WebGL2、窗口、截图和 RPC。不在真机重新打包。 +6. 全部通过后,Workflow 创建 Draft Release `v` 并上传同一批文件。人工检查产物、SHA-256、截图和 GPU 证据,在 GitHub 页面点击 **Publish**。 + +版本参数只接受 `X.Y.Z`。已有正式 Tag 时 Workflow 会停止,避免覆盖。发布 artifact 不得包含 Token、认证文件或本机日志。 diff --git a/scripts/ci-linux-display-smoke.sh b/scripts/ci-linux-display-smoke.sh index 73e5331..b1cc540 100755 --- a/scripts/ci-linux-display-smoke.sh +++ b/scripts/ci-linux-display-smoke.sh @@ -3,10 +3,11 @@ set -euo pipefail display_server="${1:-}" +smoke_executable="${2:-${OMP_SMOKE_EXECUTABLE:-}}" case "$display_server" in x11 | wayland) ;; *) - echo "用法:$0 " >&2 + echo "用法:$0 [executable]" >&2 exit 2 ;; esac @@ -14,8 +15,10 @@ esac artifact_dir="${RUNNER_TEMP:-/tmp}/omp-ci" screenshot_path="$PWD/tests/artifacts/${display_server}-smoke.png" weston_pid="" +smoke_home="$artifact_dir/home" -mkdir -p "$artifact_dir" tests/artifacts +mkdir -p "$artifact_dir" tests/artifacts "$smoke_home" +export HOME="$smoke_home" cleanup() { local exit_code=$? @@ -30,7 +33,7 @@ cleanup() { cp "$app_log" "$artifact_dir/main.log" fi - pgrep -a -f '/electron/dist/electron' >"$artifact_dir/electron-processes-after.txt" || true + pgrep -a -f '(/electron/dist/electron|/omp-desktop)' >"$artifact_dir/electron-processes-after.txt" || true exit "$exit_code" } trap cleanup EXIT @@ -39,8 +42,12 @@ trap cleanup EXIT uname -a cat /etc/os-release printf 'node=%s\n' "$(node --version)" - printf 'pnpm=%s\n' "$(pnpm --version)" - printf 'electron=%s\n' "$(node -p "require('./node_modules/electron/package.json').version")" + printf 'pnpm=%s\n' "$(pnpm --version 2>/dev/null || echo packaged)" + if [[ -f node_modules/electron/package.json ]]; then + printf 'electron=%s\n' "$(node -p "require('./node_modules/electron/package.json').version")" + else + printf 'electron=packaged\n' + fi printf 'display=%s\n' "$display_server" } >"$artifact_dir/environment.txt" @@ -52,10 +59,13 @@ export XMODIFIERS=@im=ibus if [[ "$display_server" == "x11" ]]; then export XDG_SESSION_TYPE=x11 - xvfb-run \ - -a \ - --server-args='-screen 0 1440x900x24 -nolisten tcp' \ - pnpm smoke + smoke_command=(node scripts/electron-smoke.mjs) + if [[ -n "$smoke_executable" ]]; then + export OMP_SMOKE_EXECUTABLE="$smoke_executable" + else + smoke_command=(pnpm smoke) + fi + xvfb-run -a --server-args='-screen 0 1440x900x24 -nolisten tcp' "${smoke_command[@]}" else unset DISPLAY export XDG_SESSION_TYPE=wayland @@ -89,7 +99,12 @@ else exit 1 fi - pnpm smoke + if [[ -n "$smoke_executable" ]]; then + export OMP_SMOKE_EXECUTABLE="$smoke_executable" + node scripts/electron-smoke.mjs + else + pnpm smoke + fi fi if [[ ! -s "$screenshot_path" ]]; then @@ -105,9 +120,9 @@ if ! grep -F "backend: '$expected_input_backend'" "$app_log" >/dev/null; then exit 1 fi -if pgrep -f '/electron/dist/electron' >/dev/null; then +if pgrep -f '(/electron/dist/electron|/omp-desktop)' >/dev/null; then echo 'Smoke 结束后仍有 Electron 进程残留' >&2 - pgrep -a -f '/electron/dist/electron' >&2 || true + pgrep -a -f '(/electron/dist/electron|/omp-desktop)' >&2 || true exit 1 fi diff --git a/scripts/electron-smoke.mjs b/scripts/electron-smoke.mjs index 3b09a58..a0d553c 100644 --- a/scripts/electron-smoke.mjs +++ b/scripts/electron-smoke.mjs @@ -2,9 +2,11 @@ import { spawn } from 'node:child_process' import { access } from 'node:fs/promises' import { constants } from 'node:fs' import process from 'node:process' -import electronBinary from 'electron' - -await access('out/main/index.js', constants.R_OK) +const packagedExecutable = process.env.OMP_SMOKE_EXECUTABLE +const electronBinary = packagedExecutable + ? undefined + : (await import('electron')).default +if (!packagedExecutable) await access('out/main/index.js', constants.R_OK) const useXvfb = process.platform === 'linux' && !process.env.DISPLAY const displayServer = process.env.OMP_DISPLAY_SERVER @@ -28,25 +30,29 @@ const explicitElectronArgs = ? ['--ozone-platform=wayland'] : [] if (softwareRendering) explicitElectronArgs.push('--disable-gpu') +const entrypoint = packagedExecutable ? undefined : 'out/main/index.js' +const smokeArgs = entrypoint + ? [...explicitElectronArgs, entrypoint, '--smoke'] + : [...explicitElectronArgs, '--smoke'] const command = displayServer - ? electronBinary + ? (packagedExecutable ?? electronBinary) : useXvfb ? 'xvfb-run' - : electronBinary + : (packagedExecutable ?? electronBinary) const args = displayServer - ? [...explicitElectronArgs, 'out/main/index.js', '--smoke'] + ? smokeArgs : useXvfb ? [ '-a', - electronBinary, + packagedExecutable ?? electronBinary, '--ozone-platform=x11', - 'out/main/index.js', + ...(entrypoint ? [entrypoint] : []), '--smoke' ] - : ['out/main/index.js', '--smoke'] + : smokeArgs console.log( - `Electron smoke 环境:arch=${process.arch} display=${displayServer ?? (useXvfb ? 'x11-xvfb' : 'auto')} rendering=${softwareRendering ? 'software' : 'default'} shutdown=${terminateOnReady ? 'forced-after-ready' : 'normal'}` + `Electron smoke 环境:arch=${process.arch} display=${displayServer ?? (useXvfb ? 'x11-xvfb' : 'auto')} executable=${packagedExecutable ?? 'source'} rendering=${softwareRendering ? 'software' : 'default'} shutdown=${terminateOnReady ? 'forced-after-ready' : 'normal'}` ) const child = spawn(command, args, { diff --git a/src/main/index.ts b/src/main/index.ts index 6a4c263..a48593b 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -32,6 +32,7 @@ const setupProviderMode = process.argv.includes('--setup-provider') const supportedCliFlags = new Set([ '--version', '--disable-gpu', + '--no-sandbox', '--setup-provider', '--smoke' ])