Skip to content

Add runpane npm and PyPI installer packages#246

Merged
parsakhaz merged 9 commits into
mainfrom
pip-and-node-packages
Jun 17, 2026
Merged

Add runpane npm and PyPI installer packages#246
parsakhaz merged 9 commits into
mainfrom
pip-and-node-packages

Conversation

@parsakhaz

Copy link
Copy Markdown
Member

Summary

Fixes #243.

Adds runpane wrapper packages for npm and PyPI so users can install or set up Pane from package-manager-native commands instead of choosing between platform-specific shell scripts.

  • Adds packages/runpane, a Node/TypeScript CLI with runpane install client, runpane install daemon, runpane update, runpane version, and runpane doctor.
  • Adds packages/runpane-py, a stdlib-only Python CLI exposing the same command contract through pip, pipx, uvx, and python -m runpane.
  • Routes wrapper downloads through runpane.com/api/download with source=npm or source=pip, with GitHub release asset fallback.
  • Updates Remote Pane docs, root README install commands, Settings copy buttons, and updater copy to prefer npx --yes runpane@latest / pnpm dlx / pipx paths.
  • Adds npm and PyPI publish jobs to the release workflow, using trusted publishing by default with temporary token fallback support.
  • Adds docs/RUNPANE_CLI_CONTRACT.md, version sync checks, package-manager smoke tests, and cross-OS wrapper CI for Node/Python compatibility.

Compatibility

  • npm wrapper runtime floor is Node >=18.17.0.
  • PyPI wrapper runtime floor is Python >=3.8.
  • Root Electron app development/package engine remains Node >=22.14.0.

Testing

Local checks run:

  • pnpm run check:runpane-package-versions
  • pnpm run test:runpane-contract
  • pnpm run test:runpane-package-smoke
  • npx --yes node@18.17.0 packages/runpane/dist/cli.js --help
  • npx --yes node@18.17.0 scripts/test-runpane-contract.js
  • pnpm typecheck
  • pnpm lint (0 errors; existing warnings remain)
  • git diff --check
  • Workflow YAML parse check

Manual smoke also covered local tarball/package installs on WSL and Windows for npx, npm install, pnpm dlx, pnpm add, and Python venv installs. Real registry publish and real Pane installation were not run from this branch.

Release Notes

Before release, configure or verify trusted publishers:

  • npm package runpane: repository dcouple/Pane, workflow build.yml.
  • PyPI project runpane: repository dcouple/Pane, workflow build.yml, environment pypi.

Temporary NPM_TOKEN / PYPI_API_TOKEN fallbacks are supported for first package reservation or recovery, but should be removed/rotated after use.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fefa582cd7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/runpane/src/cli.ts

@parsakhaz parsakhaz left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

PR Review

Issue context: #243 - Ship npm + pip installer packages (runpane) as cross-platform installer/configurator wrappers for the Pane client and daemon.

Quality Gates

  • Typecheck: PASS
  • Lint: PASS (0 errors; existing warnings only)

Must-Fix (0)

None.

Should-Fix (1)

  1. Python resolve_existing_pane_path uses subprocess for macOS detection instead of platform.system() (packages/runpane-py/src/runpane/installers.py:34)
    subprocess.run(["uname"], capture_output=True, text=True).stdout.strip() == "Darwin" is inconsistent with platforms.py which already uses platform.system(). More importantly, subprocess.run(["uname"]) can raise FileNotFoundError in minimal containers or constrained environments where /usr/bin/uname isn't available. Fix: import platform at the top and replace with elif platform.system().lower() == "darwin":.

Suggestions (2)

  1. Both download implementations read entire response into memory (packages/runpane/src/download.ts:57, packages/runpane-py/src/runpane/download.py:28)
    Pane installers can be 100MB+. Python reads the entire response with response.read(), Node uses Buffer.from(await response.arrayBuffer()). Streaming would reduce peak memory: Python could use shutil.copyfileobj(response, target, length=1024*1024), Node could pipe response.body to a fs.createWriteStream. Not blocking, but worth considering for daemon installs on low-memory servers.

  2. matchesPlatform substring matching (packages/runpane/src/releases.ts:112, packages/runpane-py/src/runpane/releases.py:92)
    "win" in name.lower() would match any asset name containing "darwin" (since "darwin" contains "win" at index 3). The current asset naming convention ("macOS", "Windows", "linux") avoids this, and the format filter provides a safety net, so this isn't a real bug today. But if asset names ever used "darwin" instead of "macOS", a Windows platform query could select a macOS artifact. Matching "windows" in lower or "win-" in lower or "win32" in lower would be more robust.

Completeness

The PR fully implements the plan from #243:

  • npm wrapper package with all specified commands (install client/daemon, update, version, doctor)
  • PyPI wrapper package with the same command contract
  • Download attribution via source=npm/source=pip query params
  • SHA256 checksum verification
  • Comprehensive parity tests between Node and Python parsers, platform detection, and artifact selection
  • Version sync mechanism integrated into the release script
  • CI additions for cross-OS/cross-runtime matrix testing
  • Docs, README, Settings UI, and UpdateDialog copy all updated

Non-goal boundaries are respected: no process management, no config ownership, no Homebrew formula.

Summary

This is a well-structured PR. The two wrapper packages maintain behavioral parity with a solid test harness, the release pipeline integration is thorough, and the code is clean. The uname subprocess call is the only item I'd fix before merge; the memory and substring suggestions are nice-to-haves.

@parsakhaz parsakhaz left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

PR Review

Issue context: #243 -- Ship thin CLI wrapper packages on npm (runpane) and PyPI (runpane) as cross-platform installer/configurator entry points for Pane.

Quality Gates

  • Typecheck: PASS
  • Lint: PASS (0 errors; 165 pre-existing warnings)
  • check:runpane-package-versions: PASS
  • test:runpane-contract: PASS
  • test:runpane-package-smoke: PASS

Must-Fix (0)

No blocking issues found.

Should-Fix (1)

  1. install daemon downloads artifact before checking if it can reuse existing Pane (packages/runpane/src/cli.ts:44-48, packages/runpane-py/src/runpane/cli.py:941-942): In both wrappers, installOrUpdate/install_or_update downloads the full release artifact unconditionally, then installPaneArtifact/install_pane_artifact checks for an existing Pane installation and returns early for daemon installs without using the download. On a headless server where Pane is already installed, this wastes a 100+ MB download. The fix is to check shouldReuseExistingPane + resolveExistingPanePath before calling downloadArtifact, and skip the download when an existing binary will be reused.

Suggestions (2)

  1. UpdateDialog npx fallback requires Node.js (frontend/src/components/UpdateDialog.tsx:275, main/src/ipc/updater.ts:11): The macOS manual-update command changed from curl -fsSL https://runpane.com/install.sh | sh to npx --yes runpane@latest update. Since 96% of installs come from website downloads (per the issue) and this is a fallback for when auto-update fails, the curl approach was more universally available because it doesn't require Node.js. Consider keeping curl for the macOS update dialog, or detecting npx availability and falling back.

  2. Minor Python style inconsistency (packages/runpane-py/src/runpane/platforms.py:53): arch_aliases uses list[str] as a return annotation while other files (cli.py, installers.py, etc.) use typing.List[str]. Works correctly at runtime due to from __future__ import annotations, and CI passes on Python 3.8, but it's inconsistent.

Completeness

All tasks from the issue are covered:

  • npm package with install client, install daemon, update, version, doctor commands
  • PyPI package with the same command contract
  • Download attribution via source=npm / source=pip
  • GitHub release asset fallback when website route fails
  • SHA256 checksum verification
  • Platform detection (darwin/linux/win32 x x64/arm64)
  • Daemon passthrough flags forwarded to pane --remote-setup
  • Unknown daemon flags forwarded (future-proofing)
  • Version sync tooling (sync-runpane-package-versions.js)
  • Release script integration with version sync + verification
  • Contract tests ensuring npm/Python parity
  • Package smoke tests (npx, pnpm dlx, npm install, pip install)
  • CI matrix across Node 18/22, Python 3.8/3.13, Linux/macOS/Windows
  • npm and PyPI publish jobs in release workflow with trusted publishing + token fallback
  • README, docs, Settings UI, and UpdateDialog copy updates

Summary

Well-structured PR. The dual-package contract testing is a standout -- parser parity, platform logic parity, and artifact selection parity between Node and Python are all verified in CI, which will prevent drift as both packages evolve. The version sync tooling integrated into the release script is solid.

The one should-fix (wasted download on daemon reuse) is straightforward to address. The UpdateDialog npx requirement is worth a second look since it narrows the audience for the manual-update fallback. Ready to merge after addressing the should-fix.

@parsakhaz parsakhaz merged commit f72f7e2 into main Jun 17, 2026
16 checks passed
@parsakhaz parsakhaz deleted the pip-and-node-packages branch June 17, 2026 04:32
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.

Ship npm + pip installer packages (runpane)

1 participant