From 0dd2e21683a13b6172b988173ffb704b25871913 Mon Sep 17 00:00:00 2001 From: opencode Date: Sat, 8 Aug 2026 20:20:40 +0300 Subject: [PATCH] env: .env file for API keys (gitignored), loaded by scripts and resolve_provider; key removed from bashrc --- .gitignore | 1 + .../console-2026-08-08T09-11-01-487Z.log | 9 ++++++ chat_map.png | Bin 2957 -> 2268 bytes scripts/run_bridge.sh | 8 +++++ scripts/run_chat.sh | 8 +++++ scripts/run_demo.sh | 8 +++++ scripts/run_live.sh | 8 +++++ testbed/llm_agent.py | 28 +++++++++++++++++- 8 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4c49bd7 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.env diff --git a/.playwright-mcp/console-2026-08-08T09-11-01-487Z.log b/.playwright-mcp/console-2026-08-08T09-11-01-487Z.log index eadf4d1..94fa150 100644 --- a/.playwright-mcp/console-2026-08-08T09-11-01-487Z.log +++ b/.playwright-mcp/console-2026-08-08T09-11-01-487Z.log @@ -7695,3 +7695,12 @@ [29252812ms] [ERROR] WebSocket connection to 'ws://127.0.0.1:8001/' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://127.0.0.1:8000/:40 [29257963ms] [ERROR] WebSocket connection to 'ws://127.0.0.1:8001/' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://127.0.0.1:8000/:40 [29260190ms] [ERROR] WebSocket connection to 'ws://127.0.0.1:8001/' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://127.0.0.1:8000/:40 +[29263568ms] [ERROR] WebSocket connection to 'ws://127.0.0.1:8001/' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://127.0.0.1:8000/:40 +[29268554ms] [ERROR] WebSocket connection to 'ws://127.0.0.1:8001/' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://127.0.0.1:8000/:40 +[29273559ms] [ERROR] WebSocket connection to 'ws://127.0.0.1:8001/' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://127.0.0.1:8000/:40 +[29275794ms] [ERROR] WebSocket connection to 'ws://127.0.0.1:8001/' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://127.0.0.1:8000/:40 +[29280000ms] [ERROR] WebSocket connection to 'ws://127.0.0.1:8001/' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://127.0.0.1:8000/:40 +[29284671ms] [ERROR] WebSocket connection to 'ws://127.0.0.1:8001/' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://127.0.0.1:8000/:40 +[29290218ms] [ERROR] WebSocket connection to 'ws://127.0.0.1:8001/' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://127.0.0.1:8000/:40 +[29294135ms] [ERROR] WebSocket connection to 'ws://127.0.0.1:8001/' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://127.0.0.1:8000/:40 +[29296881ms] [ERROR] WebSocket connection to 'ws://127.0.0.1:8001/' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://127.0.0.1:8000/:40 diff --git a/chat_map.png b/chat_map.png index a3b7a153c2f09e563726591da51f72a99858ad04..159b6b54a3b6cc979a2941ba25e83eb01c3570af 100644 GIT binary patch literal 2268 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 N&YyL(uCRh8{Tr_Iu*U!Z diff --git a/scripts/run_bridge.sh b/scripts/run_bridge.sh index 36692ca..b530633 100755 --- a/scripts/run_bridge.sh +++ b/scripts/run_bridge.sh @@ -3,6 +3,14 @@ # Usage: scripts/run_bridge.sh [port] (default 8765) # Note: the bridge keeps running until interrupted (Ctrl-C). set -euo pipefail + +# Load API keys from the project .env file (if present) +if [ -f .env ]; then + set -a + # shellcheck disable=SC1091 + . ./.env + set +a +fi cd "$(dirname "$0")/.." PORT="${1:-8765}" diff --git a/scripts/run_chat.sh b/scripts/run_chat.sh index e9cf3c8..7c4e280 100755 --- a/scripts/run_chat.sh +++ b/scripts/run_chat.sh @@ -3,6 +3,14 @@ # Open http://127.0.0.1:8000 to watch the capsule while you talk to it. # Usage: scripts/run_chat.sh [--model gemma4:e2b] set -euo pipefail + +# Load API keys from the project .env file (if present) +if [ -f .env ]; then + set -a + # shellcheck disable=SC1091 + . ./.env + set +a +fi cd "$(dirname "$0")/.." BRIDGE_PID="" LIVE_PID="" diff --git a/scripts/run_demo.sh b/scripts/run_demo.sh index 925a366..62b2dca 100755 --- a/scripts/run_demo.sh +++ b/scripts/run_demo.sh @@ -5,5 +5,13 @@ # scripts/run_demo.sh --agent scripted # scripts/run_demo.sh --agent llm --model gemma4:e2b set -euo pipefail + +# Load API keys from the project .env file (if present) +if [ -f .env ]; then + set -a + # shellcheck disable=SC1091 + . ./.env + set +a +fi cd "$(dirname "$0")/.." exec testbed/.venv/bin/python -m testbed.demo "$@" diff --git a/scripts/run_live.sh b/scripts/run_live.sh index a860c9e..e2c42c5 100755 --- a/scripts/run_live.sh +++ b/scripts/run_live.sh @@ -3,6 +3,14 @@ # demo while you watch. Open http://127.0.0.1:8000 in a browser. # Usage: scripts/run_live.sh [demo args...] e.g. --agent scripted set -euo pipefail + +# Load API keys from the project .env file (if present) +if [ -f .env ]; then + set -a + # shellcheck disable=SC1091 + . ./.env + set +a +fi cd "$(dirname "$0")/.." BRIDGE_PID="" LIVE_PID="" diff --git a/testbed/llm_agent.py b/testbed/llm_agent.py index 69426e0..57740cf 100644 --- a/testbed/llm_agent.py +++ b/testbed/llm_agent.py @@ -104,6 +104,31 @@ _MULTIMODAL_HINTS = ( ) +def load_dotenv() -> None: + """Load KEY=VALUE pairs from a .env file (project root or cwd) into + os.environ without overriding already-set variables.""" + import os + from pathlib import Path + + candidates = [ + Path.cwd() / ".env", + Path(__file__).resolve().parent.parent / ".env", + ] + for path in candidates: + if not path.is_file(): + continue + for line in path.read_text().splitlines(): + line = line.strip() + if not line or line.startswith("#") or "=" not in line: + continue + key, _, value = line.partition("=") + key = key.strip() + value = value.strip().strip('"').strip("'") + if key and key not in os.environ: + os.environ[key] = value + return + + def resolve_provider( *, provider: str | None, @@ -113,11 +138,12 @@ def resolve_provider( ) -> tuple[str, str, str]: """Resolve endpoint + key + model from a provider preset (or explicit args). - Keys come from the environment (POLZA_API_KEY / OPENAI_API_KEY) so nothing + Keys come from the environment or the project's .env file, so nothing secret lives in the repo or on the command line. """ import os + load_dotenv() if provider: preset = PROVIDERS.get(provider) if preset is None: