Skip to content

Weirui/dev/packing#784

Closed
rayrayraykk wants to merge 7 commits intoagentscope-ai:mainfrom
rayrayraykk:weirui/dev/packing
Closed

Weirui/dev/packing#784
rayrayraykk wants to merge 7 commits intoagentscope-ai:mainfrom
rayrayraykk:weirui/dev/packing

Conversation

@rayrayraykk
Copy link
Copy Markdown
Member

Description

[Describe what this PR does and why]

Related Issue: Fixes #(issue_number) or Relates to #(issue_number)

Security Considerations: [If applicable, e.g. channel auth, env/config handling]

Type of Change

  • Bug fix
  • New feature
  • Breaking change
  • Documentation
  • Refactoring

Component(s) Affected

  • Core / Backend (app, agents, config, providers, utils, local_models)
  • Console (frontend web UI)
  • Channels (DingTalk, Feishu, QQ, Discord, iMessage, etc.)
  • Skills
  • CLI
  • Documentation (website)
  • Tests
  • CI/CD
  • Scripts / Deploy

Checklist

  • I ran pre-commit run --all-files locally and it passes
  • If pre-commit auto-fixed files, I committed those changes and reran checks
  • I ran tests locally (pytest or as relevant) and they pass
  • Documentation updated (if needed)
  • Ready for review

Testing

[How to test these changes]

Local Verification Evidence

pre-commit run --all-files
# paste summary result

pytest
# paste summary result

Additional Notes

[Optional: any other context]

Copilot AI review requested due to automatic review settings March 6, 2026 04:35
@rayrayraykk rayrayraykk closed this Mar 6, 2026
@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances CoPaw's deployment options by introducing comprehensive desktop application packaging capabilities for both macOS and Windows. It integrates pywebview to provide a native desktop experience and utilizes conda-pack for creating self-contained environments, ensuring consistent application behavior across different user systems. The changes include new build scripts, an SVG icon, and a dedicated CLI command to run the desktop version, making CoPaw more accessible as a standalone application.

Highlights

  • Desktop Application Support: Introduced new functionality to package CoPaw as a desktop application for macOS and Windows, enhancing its standalone usability.
  • Automated Packaging Scripts: Added a comprehensive suite of scripts (build_common.py, build_macos.sh, build_win.ps1, copaw_desktop.nsi, gen_icon_icns.py) to automate the creation of platform-specific installers and app bundles using conda-pack and pywebview.
  • New CLI Command: Implemented a copaw desktop command that launches the CoPaw application within a native webview window, leveraging the newly added pywebview dependency for a dedicated desktop experience.
  • Dependency Updates: Included pywebview as a core dependency in pyproject.toml and defined a full extra dependency group for copaw to simplify the installation of all optional components.
  • Documentation and Assets: Provided detailed English and Chinese READMEs for the new desktop packaging process and added an SVG icon for the desktop application.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • pyproject.toml
    • Added pywebview as a core dependency.
    • Defined a new full extra dependency group for copaw.
  • scripts/pack/README.md
    • Added a new README detailing the desktop packaging process for macOS and Windows.
  • scripts/pack/README_zh.md
    • Added a Chinese version of the desktop packaging README.
  • scripts/pack/assets/icon.svg
    • Added an SVG icon for the CoPaw desktop application.
  • scripts/pack/build_common.py
    • Added a Python script to create and conda-pack temporary environments.
  • scripts/pack/build_macos.sh
    • Added a shell script to build the macOS .app bundle, including console frontend compilation and icon generation.
  • scripts/pack/build_win.ps1
    • Added a PowerShell script to build the Windows NSIS installer, including console frontend compilation.
  • scripts/pack/copaw_desktop.nsi
    • Added an NSIS script for creating the Windows installer with shortcuts.
  • scripts/pack/gen_icon_icns.py
    • Added a Python script to generate macOS .icns files from an SVG icon.
  • src/copaw/cli/desktop_cmd.py
    • Added a new CLI command desktop to run the CoPaw app in a pywebview window.
  • src/copaw/cli/main.py
    • Imported and registered the new desktop_cmd with the main CLI.
Ignored Files
  • Ignored by pattern: .github/workflows/** (1)
    • .github/workflows/desktop-release.yml
Activity
  • No human activity (comments, reviews, or progress updates) has been recorded for this pull request since its creation.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Generative AI Prohibited Use Policy, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a comprehensive set of scripts and code to package the CoPaw application as a desktop app for both Windows and macOS. The approach leverages conda-pack to create self-contained environments and pywebview for the user interface, which is a solid strategy. The implementation includes build scripts for both platforms, a new desktop CLI command, and supporting documentation. While the overall structure is well-conceived, I've identified critical issues in both the macOS and Windows build scripts related to the unpacking of the conda environment. These issues will prevent the packaging process from completing successfully and need to be addressed.

Comment on lines +32 to +33
if (Test-Path $Unpacked) { Remove-Item -Recurse -Force $Unpacked }
Expand-Archive -Path $Archive -DestinationPath $Unpacked -Force
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The Expand-Archive cmdlet does not support stripping top-level directories. Since conda-pack creates an archive with a top-level directory, the environment will be unpacked into a subdirectory within $Unpacked (e.g., dist\win-unpacked\copaw_pack_...). The rest of the script incorrectly assumes that environment files like python.exe are at the root of $Unpacked, which will cause the script to fail.

To fix this, you need to move the contents of the unpacked subdirectory up to the $Unpacked directory. You can add the following logic after the Expand-Archive command:

# ... after Expand-Archive

# conda-pack creates a single directory inside the archive. Move its contents up.
$EnvDir = Get-ChildItem -Path $Unpacked -Directory | Select-Object -First 1
if ($EnvDir) {
    # Move contents of the subdirectory up
    Get-ChildItem -Path $EnvDir.FullName | Move-Item -Destination $Unpacked
    # Remove the now-empty subdirectory
    Remove-Item -Recurse -Force $EnvDir.FullName
}


# Unpack conda env into Resources/env
mkdir -p "${APP_DIR}/Contents/Resources/env"
tar -xzf "$ARCHIVE" -C "${APP_DIR}/Contents/Resources/env" --strip-components=0
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The tar command uses --strip-components=0, which is likely incorrect. conda-pack typically creates an archive with a top-level directory named after the environment. To place the environment contents directly into ${APP_DIR}/Contents/Resources/env, you should use --strip-components=1. Without this change, the paths to binaries within the .app bundle will be incorrect, causing the application to fail at launch.

Suggested change
tar -xzf "$ARCHIVE" -C "${APP_DIR}/Contents/Resources/env" --strip-components=0
tar -xzf "$ARCHIVE" -C "${APP_DIR}/Contents/Resources/env" --strip-components=1

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a “desktop” distribution path for CoPaw by introducing a new CLI subcommand that launches the FastAPI app on a free port and opens it in a native webview window, plus cross-platform packaging scripts and a release workflow to build desktop artifacts.

Changes:

  • Add copaw desktop CLI command and register it in the main CLI entrypoint.
  • Add Windows/macOS packaging scripts (conda-pack based) and assets/docs to build distributable desktop bundles/installers.
  • Add a GitHub Actions workflow to build and upload Windows/macOS desktop artifacts on release/manual dispatch.

Reviewed changes

Copilot reviewed 11 out of 12 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
src/copaw/cli/main.py Registers the new desktop command and records its import timing.
src/copaw/cli/desktop_cmd.py Implements copaw desktop (spawn uvicorn + open pywebview window).
scripts/pack/gen_icon_icns.py Generates a macOS .icns icon from the repo SVG asset.
scripts/pack/copaw_desktop.nsi NSIS script to package the unpacked Windows build into an installer.
scripts/pack/build_win.ps1 Windows one-click build: console build → conda-pack → unpack → NSIS installer.
scripts/pack/build_macos.sh macOS one-click build: console build → conda-pack → .app bundle (+ optional zip/dmg).
scripts/pack/build_common.py Shared helper to create a temp conda env, install CoPaw, and run conda-pack.
scripts/pack/assets/icon.svg Adds a desktop app icon SVG source.
scripts/pack/README_zh.md Chinese documentation for desktop packaging scripts and CI workflow.
scripts/pack/README.md English documentation for desktop packaging scripts and CI workflow.
pyproject.toml Adds pywebview dependency and introduces a full optional-dependency extra.
.github/workflows/desktop-release.yml Workflow to build Windows installer + macOS zip and upload to artifacts/release assets.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +119 to +122
proc.wait()
except KeyboardInterrupt:
proc.terminate()
proc.wait()
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On server startup timeout, the code prints an error but then calls proc.wait() without terminating the subprocess. That can leave the command hanging indefinitely while the server continues running, which is surprising after a "did not become ready" message. Consider terminating the subprocess and exiting non-zero (or explicitly documenting/implementing a mode that keeps the server running and opens the URL automatically).

Suggested change
proc.wait()
except KeyboardInterrupt:
proc.terminate()
proc.wait()
proc.terminate()
proc.wait()
except KeyboardInterrupt:
proc.kill()
proc.wait()
sys.exit(1)

Copilot uses AI. Check for mistakes.
Comment on lines +29 to +30
def _wait_for_http(host: str, port: int, timeout_sec: float = 15.0) -> bool:
"""Return True when something accepts TCP on host:port."""
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Function name _wait_for_http doesn't match its behavior/docstring: it only waits for a TCP connect, not an HTTP response. Rename to something like _wait_for_tcp (or implement a real HTTP health check) to avoid misleading semantics.

Copilot uses AI. Check for mistakes.
Comment on lines +95 to +96
finally:
_run(["conda", "env", "remove", "-n", env_name, "-y"])
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The finally block always runs conda env remove ... with check=True. If env creation fails partway through, this cleanup step can fail and mask the original error. Track whether the env was successfully created (or ignore failures during cleanup) so build failures report the real root cause and cleanup is best-effort.

Copilot uses AI. Check for mistakes.
Comment thread pyproject.toml
Comment on lines 23 to 27
"lark-oapi>=1.5.3",
"python-telegram-bot>=20.0",
"twilio>=9.10.2",
"pywebview>=4.0",
]
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pywebview is added to the base project.dependencies, which forces all copaw installs (including headless/server deployments) to pull in a GUI/webview dependency that can be platform-specific and fail to build/run. This should be moved into a dedicated optional extra (e.g. desktop) and the CLI should reference that extra consistently.

Copilot uses AI. Check for mistakes.
Comment thread pyproject.toml
Comment on lines +73 to +75
full = [
"copaw[local,ollama,llamacpp,mlx]",
]
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The full extra depends on copaw[...] (the same distribution), which creates a self-referential requirement that can confuse dependency resolvers and sometimes leads to resolution loops. Prefer expanding full to the concrete dependency list (or using setuptools' recommended pattern for an "all" extra without referencing the project itself).

Copilot uses AI. Check for mistakes.
Comment on lines +72 to +77
if webview is None:
click.echo(
"Webview support not installed. Run: pip install copaw[desktop]",
err=True,
)
sys.exit(1)
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This command uses sys.exit(1) inside a Click command. In this CLI codebase, other commands typically raise click.ClickException (or use Click's error/abort mechanisms), which results in consistent formatting and easier testing. Consider raising a Click exception with the same message and returning a non-zero exit code via Click instead of calling sys.exit directly.

Copilot uses AI. Check for mistakes.
Comment on lines +103 to +113
if _wait_for_http(host, port):
webview.create_window(
"CoPaw Desktop",
url,
width=1280,
height=800,
)
webview.start() # blocks until user closes the window
proc.terminate()
proc.wait()
return # normal exit after user closed window
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

webview.start() can raise (e.g., backend init failure). If that happens, the uvicorn subprocess will keep running and the with Popen(...) context will block on exit waiting for it, potentially hanging the CLI. Wrap the webview lifecycle in a try/finally that terminates/kills the child process (with a timeout + kill() fallback) so the subprocess is always cleaned up on errors.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants