personal memory agent
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

add --port flag to sol service install, default 5015

sol service install and make install-service now accept a port for the
convey web server (default 5015). Previously the service used auto-port
which made the URL unpredictable across restarts.

make install-service # port 5015
make install-service PORT=8000 # custom port
sol service install --port 8000 # CLI equivalent

Also removes the stale backlog item for service install (already done)
and documents the service workflow in INSTALL.md, README, and the
coding skill environment reference.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

+96 -44
+2 -2
Makefile
··· 363 363 find . -type f -name ".DS_Store" -delete 364 364 rm -f .installed 365 365 366 - # Service management 366 + # Service management (override port: make install-service PORT=8000) 367 367 install-service: .installed 368 - $(VENV_BIN)/sol service install 368 + $(VENV_BIN)/sol service install --port $(or $(PORT),5015) 369 369 $(VENV_BIN)/sol service start 370 370 $(VENV_BIN)/sol service status 371 371
+4 -4
README.md
··· 78 78 # Add at minimum: GOOGLE_API_KEY=your-key 79 79 # See docs/PROVIDERS.md for all supported providers 80 80 81 - # Start the full stack 82 - sol supervisor 81 + # Install as a background service (starts on login, port 5015) 82 + make install-service 83 83 84 - # Open the web interface 85 - # (prints URL on startup, default http://localhost:<port>) 84 + # Or start manually for development 85 + sol supervisor 86 86 ``` 87 87 88 88 See [docs/INSTALL.md](docs/INSTALL.md) for platform-specific dependencies, detailed configuration, and first-run guidance.
-1
docs/BACKLOG.md
··· 25 25 26 26 ## Infrastructure 27 27 28 - - [ ] Create system/user service install for sol supervisor (remove terminal dependency) 29 28 - [ ] Health monitor and diagnostics agent (explore Claude Code SDK) 30 29 31 30 ## Indexer
+31 -7
docs/INSTALL.md
··· 132 132 133 133 ## First Run 134 134 135 - ### Start the Supervisor 135 + ### Install as a Background Service 136 + 137 + The recommended way to run solstone is as a system service that starts automatically on login: 136 138 137 - The supervisor manages all background services (capture, processing): 139 + ```bash 140 + make install-service 141 + ``` 142 + 143 + This installs, enables, and starts a systemd user service (Linux) or launchd agent (macOS) with convey on port 5015. To use a custom port: 138 144 139 145 ```bash 140 - sol supervisor 146 + make install-service PORT=8000 147 + ``` 148 + 149 + Manage the service with: 150 + 151 + ```bash 152 + sol service status # Check if running 153 + sol service restart # Restart 154 + sol service stop # Stop 155 + sol service logs -f # Follow logs 156 + sol up # Install + start (if not already) 157 + sol down # Stop 158 + ``` 159 + 160 + ### Start Manually (Development) 161 + 162 + For development, run the supervisor directly in a terminal: 163 + 164 + ```bash 165 + sol supervisor # Auto-selects an available port 166 + sol supervisor 8000 # Use a specific port 141 167 ``` 142 168 143 169 This starts: ··· 147 173 148 174 ### Verify Services 149 175 150 - In another terminal, check that services are running: 176 + Check that services are running: 151 177 152 178 ```bash 153 - pgrep -af "sol:observer|sol:sense|sol:supervisor" 179 + sol health 154 180 ``` 155 - 156 - You should see three processes. 157 181 158 182 --- 159 183
+5 -1
muse/coding/reference/environment.md
··· 2 2 3 3 ## Journal Path 4 4 5 - The journal lives at `journal/` in the project root. `get_journal()` from `think.utils` returns the path. For tests, set `_SOLSTONE_JOURNAL_OVERRIDE` to override. 5 + The journal lives at `journal/` in the project root. `get_journal()` from `think.utils` returns the path. For tests, set `_SOLSTONE_JOURNAL_OVERRIDE` to override. Do not set this in production service files — the default project-root resolution is correct. 6 + 7 + ## Service Installation 8 + 9 + `make install-service` installs solstone as a systemd user service (Linux) or launchd agent (macOS) with convey on port 5015. Override with `make install-service PORT=8000`. Managed via `sol service <install|start|stop|restart|status|logs>`. 6 10 7 11 ## API Keys 8 12
+54 -29
think/service.py
··· 4 4 """Cross-platform background service management for solstone. 5 5 6 6 Usage: 7 - sol service install Install solstone as a background service 8 - sol service uninstall Remove the background service 9 - sol service start Start the background service 10 - sol service stop Stop the background service 11 - sol service restart Restart the background service 12 - sol service status Show service installation and runtime status 13 - sol service logs View service logs 14 - sol service logs -f Follow service logs 7 + sol service install [--port PORT] Install solstone as a background service 8 + sol service uninstall Remove the background service 9 + sol service start Start the background service 10 + sol service stop Stop the background service 11 + sol service restart Restart the background service 12 + sol service status Show service installation and runtime status 13 + sol service logs View service logs 14 + sol service logs -f Follow service logs 15 + 16 + sol up Install (if needed), start, and show status 17 + sol down Stop the background service 15 18 16 - sol up Install (if needed), start, and show status 17 - sol down Stop the background service 19 + Default convey port for installed services is 5015. 18 20 """ 19 21 20 22 from __future__ import annotations ··· 28 30 29 31 SERVICE_LABEL = "org.solpbc.solstone" 30 32 SYSTEMD_UNIT = "solstone" 33 + DEFAULT_SERVICE_PORT = 5015 31 34 32 35 33 36 def _platform() -> str: ··· 98 101 return env 99 102 100 103 101 - def _generate_plist(env: dict[str, str]) -> bytes: 104 + def _generate_plist(env: dict[str, str], port: int = DEFAULT_SERVICE_PORT) -> bytes: 102 105 """Generate a launchd plist for the solstone supervisor.""" 103 106 journal_path = str(Path(get_journal()).resolve()) 104 107 sol = _sol_bin() 105 108 106 109 plist = { 107 110 "Label": SERVICE_LABEL, 108 - "ProgramArguments": [sol, "supervisor"], 111 + "ProgramArguments": [sol, "supervisor", str(port)], 109 112 "EnvironmentVariables": env, 110 113 "RunAtLoad": True, 111 114 "KeepAlive": True, ··· 115 118 return plistlib.dumps(plist) 116 119 117 120 118 - def _generate_systemd_unit(env: dict[str, str]) -> str: 121 + def _generate_systemd_unit(env: dict[str, str], port: int = DEFAULT_SERVICE_PORT) -> str: 119 122 """Generate a systemd user unit for the solstone supervisor.""" 120 123 sol = _sol_bin() 121 124 env_lines = "\n".join(f"Environment={k}={v}" for k, v in sorted(env.items())) ··· 127 130 f"\n" 128 131 f"[Service]\n" 129 132 f"Type=simple\n" 130 - f"ExecStart={sol} supervisor\n" 133 + f"ExecStart={sol} supervisor {port}\n" 131 134 f"Restart=on-failure\n" 132 135 f"RestartSec=5\n" 133 136 f"{env_lines}\n" ··· 156 159 pass 157 160 158 161 159 - def _install() -> int: 162 + def _install(port: int = DEFAULT_SERVICE_PORT) -> int: 160 163 platform = _platform() 161 164 env = _collect_env() 162 165 ··· 164 167 Path(journal_path, "health").mkdir(parents=True, exist_ok=True) 165 168 166 169 if platform == "darwin": 167 - plist_data = _generate_plist(env) 170 + plist_data = _generate_plist(env, port=port) 168 171 path = _plist_path() 169 172 path.parent.mkdir(parents=True, exist_ok=True) 170 173 ··· 188 191 print("Service loaded into launchd") 189 192 190 193 else: 191 - unit_content = _generate_systemd_unit(env) 194 + unit_content = _generate_systemd_unit(env, port=port) 192 195 path = _unit_path() 193 196 path.parent.mkdir(parents=True, exist_ok=True) 194 197 path.write_text(unit_content) ··· 414 417 return 0 415 418 416 419 417 - def _up() -> int: 420 + def _up(port: int = DEFAULT_SERVICE_PORT) -> int: 418 421 """Install if needed, start if not running, show status.""" 419 422 platform = _platform() 420 423 ··· 425 428 426 429 if not installed: 427 430 print("Installing service...") 428 - rc = _install() 431 + rc = _install(port=port) 429 432 if rc != 0: 430 433 return rc 431 434 ··· 460 463 461 464 462 465 _SUBCOMMANDS = { 463 - "install": _install, 464 466 "uninstall": _uninstall, 465 467 "start": _start, 466 468 "stop": _stop, 467 469 "restart": _restart, 468 470 "status": _status, 469 - "up": lambda: _up(), 470 - "down": lambda: _down(), 471 + "down": lambda **_kw: _down(), 471 472 } 473 + 474 + 475 + def _parse_port(args: list[str]) -> int: 476 + """Extract --port PORT from args, return DEFAULT_SERVICE_PORT if absent.""" 477 + for i, arg in enumerate(args): 478 + if arg == "--port" and i + 1 < len(args): 479 + try: 480 + return int(args[i + 1]) 481 + except ValueError: 482 + print(f"Error: invalid port '{args[i + 1]}'", file=sys.stderr) 483 + sys.exit(1) 484 + if arg.startswith("--port="): 485 + try: 486 + return int(arg.split("=", 1)[1]) 487 + except ValueError: 488 + print(f"Error: invalid port '{arg}'", file=sys.stderr) 489 + sys.exit(1) 490 + return DEFAULT_SERVICE_PORT 472 491 473 492 474 493 def main() -> None: ··· 481 500 482 501 if not args: 483 502 print("Usage: sol service <install|uninstall|start|stop|restart|status|logs>") 484 - print(" sol up (install + start + status)") 485 - print(" sol down (stop)") 503 + print(" sol service install [--port PORT] (default: 5015)") 504 + print(" sol up [--port PORT] (install + start + status)") 505 + print(" sol down (stop)") 486 506 sys.exit(1) 487 507 488 508 subcmd = args[0] 509 + rest = args[1:] 489 510 490 - if subcmd in _SUBCOMMANDS: 511 + if subcmd == "install": 512 + sys.exit(_install(port=_parse_port(rest))) 513 + elif subcmd == "up": 514 + sys.exit(_up(port=_parse_port(rest))) 515 + elif subcmd in _SUBCOMMANDS: 491 516 sys.exit(_SUBCOMMANDS[subcmd]()) 492 - 493 - print(f"Unknown subcommand: {subcmd}", file=sys.stderr) 494 - print("Available: install, uninstall, start, stop, restart, status, logs") 495 - sys.exit(1) 517 + else: 518 + print(f"Unknown subcommand: {subcmd}", file=sys.stderr) 519 + print("Available: install, uninstall, start, stop, restart, status, logs") 520 + sys.exit(1)