Skip to content

Linux compatibility (systemd service backend + Linux-path test coverage) #494

Description

@griffinwork40

Linux compatibility

Status — updated 2026-07-11:Phase 1 (the 🔴 service-layer blocker) is DONE. A Linux systemd --user backend, sitting behind a platform-neutral ServiceManager seam, shipped in #515 (CI all-green, pending merge). Remaining work — this issue stays open until it lands: the four "works-but-untested" Linux branches still have zero Linux-path test coverage (🟡 Phase 2), and CI still runs only on ubuntu-latest (no macOS matrix). Original scoping below is preserved as filed; see the status comment for the up-to-date breakdown.

Text clipboard, keychain, OAuth, and terminal-spawn already carry working Linux branches (unchanged) — those were never the gap.


✅ Blocker RESOLVED in #515 — Service layer is now cross-platform (systemd --user backend)

Done (PR #515): extracted a platform-neutral ServiceManager interface + serviceManagerFor(platform) factory (src/service/{types,index}.ts), added a full systemd --user backend (src/service/systemd/*) with a companion .path unit for the WatchPaths equivalent, adapted launchd behind the same interface (776-line launchd suite untouched), replaced the assertMacOS() hard-throw with factory dispatch, and updated the service-setup/get-started skills. tsc --noEmit clean; full vitest 11,233 passed / 0 failed; CI all-green. Original problem statement preserved below.

afk service (install/uninstall/status as an always-on, auto-restart background process) hard-refuses on any non-darwin platform and there is no systemd implementation shipped.

  • src/cli/commands/service.ts:37-43assertMacOS() throws 'afk service' uses macOS launchd and is only supported on darwin. It gates the install/uninstall actions (called at service.ts:65).
  • src/service/ contains only launchd.ts + launchd.test.ts — no systemd module. Confirmed: zero matches for systemd or WantedBy anywhere under src/.
  • The service-setup skill still tells Linux users the feature is unavailable: src/skills/service-setup/prompts/system.md:35"On Linux you'd want a systemd user unit (not yet shipped)."

Work item (headline remaining work): add a systemd user-unit backend (~/.config/systemd/user/*.service + systemctl --user bootstrap), branch service.ts on process.platform, and replace the assertMacOS() hard-throw with a platform dispatch. daemon and telegram are the two service names to support (SERVICE_NAMES, service.ts:26).


🟡 Linux paths that WORK but are UNTESTED

Each branch below was code-read and confirmed correct. The blocker is coverage: no test exercises the Linux branch of any of these — verified by reading each corresponding *.test.ts.

  • readProcessMeta() /proc parsingsrc/telegram/manager.ts:276-293 reads /proc/<pid>/stat (starttime) + /proc/<pid>/status (VmRSS) on Linux. ❌ Not covered — manager.test.ts has no readProcessMeta//proc reference. Add a Linux test that mocks readFileSync/statSync for those paths and asserts { uptimeSec, memoryMb }.
  • OAuth defaultBackend() Linux credential filesrc/agent/mcp/oauth.ts:106-137 reads/writes ~/.claude/.credentials.json (mode 0o600) on Linux. ❌ Not covered — oauth.test.ts has no defaultBackend/credentials.json reference. Add a Linux test that stubs process.platform, points homedir() at a temp dir, and round-trips read/write (asserting 0o600).
  • Chrome Linux binary detectionsrc/cli/commands/browser.ts:140-164 probes google-chrome / google-chrome-stable / chromium / chromium-browser on non-darwin. ❌ Not covered and currently untestable as writtendetectChromeMajorVersion is private/non-exported. Export it (or inject an execFileSync seam), then add a Linux test asserting the candidate list is probed and a version parsed.
  • terminal-font-size Linux editor discoverysrc/agent/tools/handlers/terminal-font-size.ts:72-77 appends ~/.config/Code/User/settings.json + ~/.config/Cursor/User/settings.json on Linux. ❌ Not covered — every test in terminal-font-size.test.ts injects a mock discoverFn, so the real discoverEditors() Linux branch is never invoked. Add a test that calls the real discoverEditors() with process.platform stubbed to linux and HOME at a temp dir.

🟡 CI — add a macOS matrix entry

  • Not done. All jobs run on ubuntu-latest only — .github/workflows/ci.yml:25/75/113/145, plus publish.yml and auto-release.yml. No macos-latest / matrix.os anywhere in .github/workflows/. Convert the test job (ci.yml:73-105) to strategy.matrix.os: [ubuntu-latest, macos-latest] with runs-on: ${{ matrix.os }}, so the darwin ps -p / security / /Applications/... branches get exercised alongside the Linux branches. The fork-PR constraint (GitHub-hosted runners only) is already satisfied — macos-latest is GitHub-hosted.

Acceptance criteria


Out of scope

  • Windows support (no win32 branches audited or planned here).
  • Native Keychain/libsecret integration — the Linux OAuth path deliberately uses a 0o600 plaintext file at ~/.claude/.credentials.json (oauth.ts:133-137), matching Claude Code's on-disk format; a hardened secret-store backend is a separate follow-up.
  • Replacing the macOS security-CLI keychain shell-out with a native binding (already a follow-up in oauth.ts:126-127).
  • Bundling/pinning a Chrome binary — detection remains best-effort with a soft note, never a hard failure.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions