Metadata-Version: 2.4
Name: helm-server
Version: 0.1.1
Summary: Pairs the Helm iPhone app with the Cursor CLI on your Linux or macOS server.
License: Proprietary
Keywords: helm,cursor,cursor-agent,iphone,remote
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: Other/Proprietary License
Classifier: Operating System :: MacOS
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Topic :: Software Development
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: cryptography>=41

# helm-server

Run your Cursor agent from your iPhone. helm-server runs on your own Linux or
macOS server, next to your code, and drives the Cursor CLI with your own Cursor
account. The Helm iPhone app pairs with it and talks only to your server.

## Before you install: what this does

- **Installs** one Python package (`helm-server`) for your user. No root.
- **Runs** one small HTTP server on `127.0.0.1:7788`, as your user, under
  systemd (Linux) or launchd (macOS). It only listens on loopback.
- **Reaches the phone** through an https front you choose and control:
  Tailscale Funnel, a Cloudflare tunnel on your account, or your own domain
  with a reverse proxy. helm-server never opens a public port itself.
- **Grants a paired phone** the same power as running Cursor's agent yourself
  in your home directory: it can read and edit files and run shell commands
  as your user. Pairing needs a one-time code you mint on the server.
  Codes expire after 10 minutes and work once.
- **Keeps secrets on the server.** The phone holds a pairing ticket, not your
  Cursor login or the server's tokens.

## Requirements

- Linux or macOS, Python 3.10 or newer
- The Cursor CLI, logged in (`curl https://cursor.com/install -fsS | bash`,
  then `cursor-agent login`)
- An https address your phone can reach (see step 5)
- The Helm app on your iPhone

## Let your coding agent set it up

Paste this to Cursor's agent (or any CLI agent) on the server:

> Install helm-server from PyPI with `uv tool install helm-server` (or
> `pipx install helm-server`), run `helm-server skill install`, then follow the
> `pair-helm` skill to set it up and pair my phone. Explain each step before
> you run it.

The `pair-helm` skill explains everything first, installs nothing without your
yes, never runs `sudo` for you, and ends with a pairing code for the app.
`helm-server skill show` prints it.

## Set it up yourself

1. Install:

   ```bash
   uv tool install helm-server      # or: pipx install helm-server
   ```

2. Start the service:

   ```bash
   helm-server service install
   ```

   On Linux, run the `loginctl enable-linger` command it prints if you want
   the service to keep running after you log out.

3. Check:

   ```bash
   helm-server doctor
   ```

A new install already has a Home space pointed at your home directory. You can pair and run there. To let the phone use another directory later: `helm-server repos add /path/to/project`.

4. When the phone is somewhere else, put an https front in front of `http://127.0.0.1:7788`. Any one of:

   - Tailscale Funnel: `tailscale funnel --bg 7788`
   - A Cloudflare named tunnel pointed at `http://127.0.0.1:7788`
   - Caddy on a domain pointing at this server:

     ```
     helm.example.com {
         reverse_proxy 127.0.0.1:7788
     }
     ```

   Then save that address. Only a public `https://` origin with no path is
   accepted:

   ```bash
   helm-server url set https://helm.example.com
   ```

   `helm-server doctor` should now show every line `ok`, including `reachable`.

5. Pair:

   ```bash
   helm-server pair
   ```

   In Helm on the iPhone: Settings → Your server, paste the https address, then
   paste the code.

## Commands

| Command | What it does |
|---|---|
| `helm-server doctor [--json]` | Checks the Cursor CLI, checkouts, service, address, and reachability |
| `helm-server pair [SPACE]` | Mints a one-time pairing code |
| `helm-server repos add PATH \| list \| remove NAME` | Checkouts Helm may use |
| `helm-server spaces add ID REPO... \| list` | Group checkouts into Spaces |
| `helm-server url set ORIGIN \| show \| clear` | The https address the app connects to |
| `helm-server service install \| start \| stop \| restart \| status \| logs \| uninstall` | The background service |
| `helm-server skill install \| show` | The `pair-helm` skill for coding agents |
| `helm-server run` | Run the server in the foreground |

## Upgrade, sign out, remove

- Upgrade: `uv tool upgrade helm-server`, then `helm-server service restart`.
- Sign out every phone: delete `ticket.secret` (below), then
  `helm-server service restart`. Pair again with `helm-server pair`.
- Remove: `helm-server service uninstall`, then `uv tool uninstall helm-server`.
  Delete the config and state folders below to remove all data.

## Where it keeps things

| | Linux | macOS |
|---|---|---|
| Config and secrets | `~/.config/helm` | `~/Library/Application Support/Helm` |
| State | `~/.local/state/helm` | `~/Library/Application Support/Helm/state` |
| Logs | `~/.local/state/helm/logs` | `~/Library/Logs/Helm` |

Config holds `repos.json`, `spaces.json`, `public-url`, `session.token`,
`ticket.secret`, and `vapid.json`. Every path can be moved with an environment
variable (`HELM_CONFIG_DIR`, `HELM_STATE_DIR`, `HELM_LOG_DIR`, or
`HELM_SPACES_FILE`, `HELM_REPOS_FILE`, `HELM_TOKEN_FILE`,
`HELM_TICKET_SECRET_FILE`, `HELM_VAPID_FILE`, `HELM_ORIGIN_FILE`).

## Agent

Cursor is the only agent at launch (`HELM_AGENT=cursor`, the default).
helm-server finds the Cursor CLI in `~/.local/bin` or on `PATH`; point at another
copy with `HELM_CURSOR_AGENT_BIN` (print runs and host commands) or
`HELM_CURSOR_ACP_BIN` (`agent acp`, used by the app). `/health` reports the agent
and version.

## Releases

Releases are published to PyPI from the `zai-core/helm-server` release workflow
with PyPI trusted publishing. No upload token exists, and each file carries a
signed provenance attestation naming the workflow and tag that built it.

## Operator settings

Off by default. An operator running several hosts or the Telegram bridge sets:

| Variable | Purpose |
|---|---|
| `HELM_FLEET_FILE` | Fleet map, so each host advertises only its own Spaces |
| `HELM_TELEGRAM_BOTS_FILE` | Bot list used to find this host in the fleet |
| `HELM_TELEGRAM_STATE_DIR` | Bridge state, so Telegram-owned chats stay on Telegram |
| `HELM_GITHUB_PAT_DIR` | Per-owner `github_pat.<owner>` files injected into runs |
| `HELM_CORS_ORIGINS` | Browser origins allowed to call the API |
| `HELM_PUSH_CONTACT` | VAPID contact (`mailto:` or https) for web push |
| `HELM_CONSOLE_DIR` | Serve the web console from another folder |

## Development

```bash
. scripts/ci-tools.sh   # pinned uv on PATH
make lint build test-unit test-functional
```

Jenkins runs the same four targets and posts `Jenkins / Lint`, `Build`,
`Unit tests`, and `Functional tests`. Functional tests install the built wheel
into a clean venv, start the real server with a stub `cursor-agent`, and pair
and run over HTTP. See `.github/MERGE_GATE.md`.

To release: merge the console change to scribblr-agent main, put that commit's
SHA in `console.ref`, bump `helm_server.__version__`, merge, then push the tag
`v<version>` on main. `.github/workflows/release.yml` builds the console at
`console.ref`, bundles it, tests the wheel, and publishes.
