diff --git a/src/neuronetz_gateway/cli/manage.py b/src/neuronetz_gateway/cli/manage.py index a6e2be7..1832e79 100644 --- a/src/neuronetz_gateway/cli/manage.py +++ b/src/neuronetz_gateway/cli/manage.py @@ -448,6 +448,27 @@ def probe_ollama( typer.secho("upstream reachable and authenticated.", fg=typer.colors.GREEN, bold=True) +def _emit_paste_fallback(path: Path, line: str, exc: BaseException) -> None: + """Print a clean fallback message when --write-env can't write the file. + + The bootstrap CLI usually runs inside the gateway container (uid 10001), + but the host-mounted .env is typically owned by the host user — so the + in-container process can't write it. Instead of crashing with a traceback, + print the line the user can paste, and tell them how to retry with the + right privileges. + """ + typer.secho(f"✗ could not write {path}: {type(exc).__name__}", fg=typer.colors.RED) + typer.echo(f" ({exc})") + typer.echo("") + typer.echo("Two ways forward:") + typer.echo(" 1. Re-run the same command with `docker exec -u root …` so the in-container") + typer.echo(" process can write to the host-mounted .env.") + typer.echo(" 2. Paste this into your .env manually, then `docker compose up -d gateway`:") + typer.echo("") + typer.echo(f" OLLAMA_BACKENDS={line}") + typer.echo("") + + @app.command("list-backends") def list_backends() -> None: """Show the configured Ollama backends (tokens redacted). @@ -588,7 +609,11 @@ def add_backend( line = serialize(updated) if write_env is not None: path = Path(write_env) - update_env_file(path, line) + try: + update_env_file(path, line) + except (PermissionError, OSError) as exc: + _emit_paste_fallback(path, line, exc) + raise typer.Exit(code=1) from exc typer.echo(f"✓ updated {path}") typer.echo(" recreate the gateway: docker compose up -d gateway") else: @@ -643,7 +668,12 @@ def remove_backend( return if write_env is not None: - update_env_file(Path(write_env), line) + path = Path(write_env) + try: + update_env_file(path, line) + except (PermissionError, OSError) as exc: + _emit_paste_fallback(path, line, exc) + raise typer.Exit(code=1) from exc typer.echo(f"✓ updated {write_env}") typer.echo(" recreate the gateway: docker compose up -d gateway") else: