Skip to content

feat(install): run on Linux, and pick the platform at install time - #245

Open
y-ashish-y wants to merge 3 commits into
citrolabs:mainfrom
y-ashish-y:feat/linux-support-with-review-fixes
Open

feat(install): run on Linux, and pick the platform at install time#245
y-ashish-y wants to merge 3 commits into
citrolabs:mainfrom
y-ashish-y:feat/linux-support-with-review-fixes

Conversation

@y-ashish-y

Copy link
Copy Markdown

What this adds

skills/ego-browser/scripts/install.sh previously opened with

[ "$(uname -s)" = "Darwin" ] || die "this script only supports macOS"

so the only way to run the harness on Linux was to fork the repo. This PR adds the missing half instead of replacing the existing one.

  • The macOS flow is untouched. main() was renamed to main_macos() and lost exactly one line — the guard above. Every DMG function (install_ego_lite, find_ego_lite_app, strip_quarantine_attributes, cleanup, the trap) is byte-identical.
  • A new main() dispatches on uname -s: Darwin → the existing flow, Linux → the new one, anything else → unsupported platform: <name> rather than a macOS-specific message.
  • package/ego-linux (new, 39 files) supplies the globalThis.ego object the macOS app injects natively, backed by a stock Chrome/Chromium over CDP.

Why it does not touch package/ego-browser

package/ego-browser is unchanged in this PR — 0 files. Every helper, locator, driver and format runs as-is on both platforms; the port is a backing implementation, not a second harness. The test suite below is what checks it.

Install on Linux

A build plus a symlink — there is nothing to download:

sh skills/ego-browser/scripts/install.sh

It verifies Node >= 22 and a browser on PATH before touching anything. EGO_LINUX_CHROME points it at a browser that is not on PATH; EGO_LINUX_BIN_DIR links somewhere other than ~/.local/bin. There is no GUI onboarding step on Linux: when the script returns, the command is ready.

Verification

Run on Linux against an unmodified package/ego-browser:

Check Result
package/ego-linux suite 61/61 pass (54 original + 7 new unit tests)
sh skills/ego-browser/scripts/install.sh end to end Pass — npm ci, build, symlink, post-install smoke check
Dispatcher, uname stubbed to Darwin Enters the DMG flow — reaches require_command hdiutil
Dispatcher, uname stubbed to FreeBSD error: unsupported platform: FreeBSD (supported: macOS, Linux)
sh -n on the installer Clean

Review fixes (over PR #234)

  • spaces-server: await detach before release. closeAll / retain / the two error paths in open() used to fire Target.detachFromTarget and immediately call releaseSession; frames in flight were forwarded into drainEvents (→ the agent). Now Promise.all + await waits for each detach to settle before the release. Backed by a regression test (closeAll awaits detach + release still happens when detach fails).
  • spaces-server: 1 MiB body cap. A caller on loopback could OOM the daemon. New ResponseError(413) in readBody; the 413 response carries Connection: close so server.close() does not stall on the half-written socket. Tested.
  • chrome: EGO_LINUX_CDP_URL returns a port. The previous shape was { wsUrl, launched: false } with no port, which silently disabled MRU tab ordering in createTabsApi. Now the port is parsed from the URL and best-effort probed; a dead endpoint is surfaced instead of hanging.
  • bin/ego-browser: detached spawns swallow error. Detached + unref'd, an unhandled 'error' on the child process kills Node — exactly what --spaces is trying to avoid. Extracted spawnDetached helper used by openPanelWindow, runSpacesDaemon, and openSpaces.
  • install.sh: set -euo pipefail + split npm ci / npm run build so each dies with its own message.
  • install.md: align with code. listTabs is scoped to the selected space (was described as browser-wide). Dropped the macOS-only nodejs prefix from the example to match SKILL.md; the shim still accepts it for backward compatibility.
  • package.json: drop the unused bin entry. The symlink in install.sh is the canonical install path.
  • Static XSS regression on spaces-ui.mjs — fails the moment any user-controlled field is assigned via innerHTML instead of textContent / createTextNode.

Scope

Carved down from a working Linux fork to the platform-support core. Deliberately not included:

  • fork branding (root README.md), fork-specific docs and experiments
  • site learnings packs
  • fork CI / lefthook workarounds
  • a driver/pointer.ts drag-timing fix that is platform-agnostic — belongs in its own PR

NagyVikt and others added 3 commits August 7, 2026 05:37
`install.sh` refused everything that was not Darwin, so the only way to use the
harness on Linux was to fork it. This adds the missing half rather than
replacing the existing one: the macOS DMG flow is untouched, and `main()` now
dispatches on `uname -s` instead of dying on it.

The Linux side is `package/ego-linux`, a CDP shim that supplies the
`globalThis.ego` object the macOS app injects natively, backed by a stock
Chrome/Chromium. `package/ego-browser` is unchanged — every helper, locator,
driver and format runs as-is on both platforms. That is the point of the
design, and it is what the test run below actually verifies.

Install on Linux is a build plus a symlink, not a download: the script checks
Node >= 22 and a browser on PATH up front and stops with a clear error rather
than half-installing. `EGO_LINUX_CHROME` and `EGO_LINUX_BIN_DIR` override the
two things worth overriding.

Verified on Linux against an unmodified `package/ego-browser`:
- `package/ego-linux` suite: 54/54 pass
- `sh skills/ego-browser/scripts/install.sh` end to end — build, link, PATH
  warning, and the post-install smoke check
- dispatcher, with `uname` stubbed: Darwin enters the DMG flow (reaches
  `require_command hdiutil`), FreeBSD exits with `unsupported platform`

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two places described `finishDragProbe` by its current constant — a 50 ms
window — and one of them prescribed the fix. Both go stale the moment that
window changes, and neither belongs in install/porting docs: the race is in the
shared harness, not in anything Linux-specific.

The install troubleshooting bullet is dropped outright; it was filed under
"Linux" for a symptom that has nothing to do with the platform. The README's
section keeps the measurement — what was observed, how it reproduces, and why
the shim cannot suppress it — and drops the constant and the remedy, so it
stays true whatever the harness does about it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- spaces-server: await Target.detachFromTarget before releasing the session
  in closeAll, retain, and the two error paths in open(). The previous
  fire-and-forget detach left frames in flight forwarded into the harness
  event buffer (drainEvents -> agent). Promise.all + await is the fix.

- spaces-server: cap request bodies at 1 MiB with a 413 ResponseError so a
  caller cannot OOM the loopback daemon. Pass Connection: close on the
  413 response so the half-written socket does not stall server.close().

- chrome: when EGO_LINUX_CDP_URL is set, parse the port out of the URL and
  best-effort probe it. The previous shape returned { wsUrl, launched }
  with no port, which silently disabled MRU tab ordering in createTabsApi.

- bin/ego-browser: pull detached spawns through a helper that attaches an
  error listener. Detached + unref'd, an unhandled 'error' on the child
  process kills Node, which is exactly what --spaces is trying to avoid.

- install.sh: set -euo pipefail; split the npm ci / npm run build steps so
  each dies with its own message.

- install.md: align with the code — listTabs is scoped to the selected
  space (was described as browser-wide). Drop the macOS-only 'nodejs'
  prefix from the example to match SKILL.md; the shim still accepts it.

- package.json: drop the unused 'bin' entry; the symlink in install.sh is
  the canonical install path.

- spaces-server: export the cast-pool factory under a test-only name so the
  detach/release ordering has a regression test that does not boot Chrome.

- tests: add unit tests for the body cap, the close/open race, the detach-
  fail path, and a static XSS check on spaces-ui.mjs.
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.

1 participant