A tiny run-once helper that asks AvalAI for its current chat-model catalog and adds those models to OpenCode as provider.avalai.
./run.shYou need Python 3, OpenCode, and an AvalAI API key.
cp .env.example .env
nano .envPut the real key in .env:
AVALAI_API_KEY='your-real-avalai-key'Then:
chmod 600 .env
./run.shThe real .env is covered by .gitignore. Do not commit it, paste it into an issue, or include it in screenshots.
The helper can read AVALAI_API_KEY from:
- This project's
.envfile - Your shell environment
- A hidden terminal prompt
When .env and the shell contain different keys, the helper asks which one to use. It never prints the key; it only displays its source and character count.
You can also skip .env for a one-off run:
AVALAI_API_KEY='your-real-key' ./run.shThe helper reads .env as data. It does not execute it as shell code.
The helper asks you to choose one of AvalAI's three officially documented API routes:
🌐 Choose an official AvalAI route
1) Primary Iran route — api.avalai.ir (default)
2) Iran fallback route — api.avalapis.ir
3) Cloudflare route — api.avalai.org
👉 Choice [1]:
Press Enter to use https://api.avalai.ir/v1.
During setup you choose:
🔐 How should OpenCode read the AvalAI key?
1) Environment variable — {env:AVALAI_API_KEY} (recommended)
2) Hard-code it in opencode.json (simpler, but less secure)
👉 Choice [1]:
OpenCode receives this setting:
"apiKey": "{env:AVALAI_API_KEY}"OpenCode must see the variable whenever it starts. From this project directory:
set -a
. ./.env
set +a
opencodeOr export the variable through your preferred shell/secret manager before running OpenCode.
OpenCode does not automatically load this project's
.env; the helper does.
The real key is written directly under provider.avalai.options.apiKey in:
~/.config/opencode/opencode.json
This is convenient, but less secure. The helper sets the config permission to 0600, meaning only your user should be able to read it. Backups may also contain the key after later runs, so keep them private.
The helper adds or replaces only:
provider.avalai
It leaves all other providers and unrelated settings semantically unchanged, including OpenAI, Anthropic, Google, OpenRouter, local providers, MCP servers, agents, permissions, plugins, model, and small_model.
A defensive check runs before writing: if anything outside provider.avalai changed in memory, the helper stops.
Before changing an existing config, the helper creates:
~/.config/opencode/opencode.json.backup-YYYYMMDD-HHMMSS
Example:
~/.config/opencode/opencode.json.backup-20260803-143012
The exact backup path is printed after a successful run.
Find the newest backup:
ls -1t ~/.config/opencode/opencode.json.backup-* 2>/dev/null | head -n 1Restore one:
cp ~/.config/opencode/opencode.json.backup-YYYYMMDD-HHMMSS \
~/.config/opencode/opencode.json
chmod 600 ~/.config/opencode/opencode.jsonFor a 401 Unauthorized, the helper now prints:
- the selected endpoint
- whether the key came from
.env, the shell, or the hidden prompt - AvalAI's short error message, when provided
It never prints the credential.
Common checks:
# Make sure the example placeholder is gone
grep '^AVALAI_API_KEY=' .env
# See whether your shell already has a different key
[[ -n ${AVALAI_API_KEY:-} ]] && echo 'shell key exists'When both sources differ, rerun ./run.sh and explicitly choose the correct one.
Run the same command again:
./run.shOnly provider.avalai is refreshed.