* fix(autostart): resolve cli.js path locally, register with launchctl, verify state The current `cli/src/cli/tray/autostart.js` is silently broken on every nvm install (and likely Volta / asdf / Homebrew / user-prefix npm) and on every npm version >= 9. Enabling auto-start from the tray menu writes a launchd plist that references a non-existent script; the menu then reports "✓ Auto-start Enabled" because the existence check only verifies the file is on disk. On the next OS boot launchd fails with MODULE_NOT_FOUND. None of this surfaces to the user. This rewrite addresses the four problems reported in #1082: 1. `npm bin -g` was removed in npm 9. autostart.js called it as a primary resolution mechanism and silently fell back to a hardcoded `/usr/local/lib/node_modules/9router/cli.js` path on failure. Replaced with a `getCliJsPath()` helper that tries (in order): the explicit `cliPath` argument, `process.argv[1]` when it's our own cli.js, and a path computed relative to autostart.js's own location (since this file always lives at `<pkg>/src/cli/tray/autostart.js`, cli.js is three levels up regardless of install layout). Returns null on no match. 2. The `/usr/local/...` fallback was outright wrong for nvm/Volta/asdf installs. Dropped entirely. If no candidate resolves, return false instead of writing a plist pointing at a missing script. 3. `enableMacOS()` never called `launchctl load -w`, only `unload`. The plist was therefore inert until the next user login, with no signal to the user that anything was wrong. Now it unloads (defensive, in case of re-enable) and then loads, so the agent is active in the current session. 4. `isAutoStartEnabled()` on macOS only checked file existence — so the tray menu reported "✓ Enabled" even when launchd had the agent in a failed state or hadn't loaded it. Now also runs `launchctl list ${APP_LABEL}` and only returns true if launchd recognizes the label. Additional changes for robustness: - The macOS plist now invokes node + cli.js directly with absolute paths instead of wrapping in `zsh -l -c "..."`. The shell-wrapper approach depended on the user's login shell sourcing nvm/PATH correctly, which is fragile (nvm.sh sourcing varies between users; some setups don't add node to PATH from a non-interactive login shell). - `EnvironmentVariables.PATH` in the plist now explicitly includes node's bin directory plus the standard system paths, so child processes spawned by cli.js (e.g. the runtime `npm install` calls) can still resolve `npm` even under launchd's minimal default env. - Windows and Linux paths were calling `npm bin -g` with the same fallback problem; both now use `getCliJsPath()` consistently. The Windows VBS branch is simplified (always run node+cli.js, drop the `9router.cmd` lookup that depended on the npm prefix path). - Removed the unused `getStartCommand()` helper that was never imported. Fixes #1082 * fix(autostart): skip launchctl unload/load when current process is the agent When the running 9router cli.js was itself spawned by the autostart launchd agent (after a reboot when autostart was previously enabled), clicking the tray menu's "Disable Auto-start" item would unload the agent — and that unload sends SIGTERM to the running process, killing the click handler and the tray icon before the menu could flip its label. Add a small `isAgentSelfMacOS()` probe (parses `launchctl list ${label}` and compares the PID against `process.pid`). When we're the agent itself, disable skips the unload (the plist file removal is enough to prevent the agent from starting on the next login) and enable skips the load (the plist is already loaded under our own PID, the on-disk update is what matters for next boot). External callers — e.g. enable from a manually-launched 9router or from a script — still get the full unload/load behavior they need. Without this, the tray UX after a reboot was: click Disable -> tray icon silently disappears, no menu label change. Now: click Disable -> label flips to "Enable Auto-start", tray stays, plist removed; click Enable again -> label flips back, plist re-created. |
||
|---|---|---|
| .. | ||
| hooks | ||
| scripts | ||
| src/cli | ||
| .gitignore | ||
| .npmignore | ||
| cli.js | ||
| LICENSE | ||
| package.json | ||
| README.md | ||
9Router - FREE AI Router & Token Saver
Never stop coding. Save 20-40% tokens with RTK + auto-fallback to FREE & cheap AI models.
Connect All AI Code Tools (Claude Code, Cursor, Antigravity, Copilot, Codex, Gemini, OpenCode, Cline, OpenClaw...) to 40+ AI Providers & 100+ Models.
🤔 Why 9Router?
Stop wasting money, tokens and hitting limits:
- ❌ Subscription quota expires unused every month
- ❌ Rate limits stop you mid-coding
- ❌ Tool outputs (git diff, grep, ls...) burn tokens fast
- ❌ Expensive APIs ($20-50/month per provider)
9Router solves this:
- ✅ RTK Token Saver - Auto-compress tool_result, save 20-40% tokens
- ✅ Maximize subscriptions - Track quota, use every bit before reset
- ✅ Auto fallback - Subscription → Cheap → Free, zero downtime
- ✅ Multi-account - Round-robin between accounts per provider
- ✅ Universal - Works with any OpenAI/Claude-compatible CLI
⚡ Quick Start
Option 1 — npm (recommended for desktop):
npm install -g 9router
9router
# Or run directly with npx
npx 9router
Option 2 — Docker (server/VPS):
docker run -d --name 9router -p 20128:20128 \
-v "$HOME/.9router:/app/data" -e DATA_DIR=/app/data \
decolua/9router:latest
Published images: Docker Hub • GHCR (multi-platform amd64/arm64).
🎉 Dashboard opens at http://localhost:20128
2. Connect a FREE provider (no signup needed):
Dashboard → Providers → Connect Kiro AI (free Claude unlimited) or OpenCode Free (no auth) → Done!
3. Use in your CLI tool:
Claude Code/Codex/OpenClaw/Cursor/Cline Settings:
Endpoint: http://localhost:20128/v1
API Key: [copy from dashboard]
Model: kr/claude-sonnet-4.5
That's it! Start coding with FREE AI models.
🚀 CLI Options
9router # Start with default settings
9router --port 8080 # Custom port
9router --no-browser # Don't open browser
9router --skip-update # Skip auto-update check
9router --help # Show all options
Dashboard: http://localhost:20128/dashboard
🛠️ Supported CLI Tools
Claude-Code • OpenClaw • Codex • OpenCode • Cursor • Antigravity • Cline • Continue • Droid • Roo • Copilot • Kilo Code • Gemini CLI • Qwen Code • iFlow • Crush • Crusher • Aider
Any tool supporting OpenAI/Claude-compatible API works.
💾 Data Location
- macOS/Linux:
~/.9router/db/data.sqlite - Windows:
%APPDATA%/9router/db/data.sqlite - Docker:
/app/data/db/data.sqlite(mount$HOME/.9routerto persist)
📚 Documentation
Full docs, advanced setup, video tutorials & development guide:
- GitHub: https://github.com/decolua/9router
- Full README: https://github.com/decolua/9router/blob/main/app/README.md
- Website: https://9router.com
🙏 Acknowledgments
- CLIProxyAPI - Original Go implementation
📄 License
MIT License - see LICENSE for details.