# Setting up Volcanic Secretary on Windows This guide takes you from a fresh checkout to a Telegram bot that answers you. No Docker involved. Budget about ten minutes. ## What you need - Windows 10 or 11. - Node.js 24 or newer, from https://nodejs.org. Check with `node -v` in a terminal. - Git for Windows at its default location, which gives the assistant a `bash` for running scripts. - Pi installed and able to reach a model. If typing `pi` in a terminal and sending `hi` gets an answer, you are set; the bot reuses the same credentials from `~/.pi/agent`. - A Telegram account on your phone or desktop. ## Step 1: Create the bot and get its token 1. In Telegram, open a chat with **@BotFather**. 2. Send `/newbot`. 3. Give it a display name (for example `Volcanic Secretary`) and then a username that ends in `bot` (for example `volcanic_secretary_bot`). Usernames must be unique across Telegram, so add digits if the first try is taken. 4. BotFather replies with a token that looks like `123456789:AAFxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx`. Copy the whole thing. Treat the token like a password. Anyone who has it can send messages as your bot. ## Step 2: Find your own Telegram user id The bot only talks to one person, identified by a number, not a username. 1. Open a chat with **@userinfobot** and send `/start`. 2. It replies with a line such as `Id: 987654321`. That number is your user id. Any other user who messages the bot is ignored. Their id is written to the bot's log once, which is also how you can double check your own id later. ## Step 3: Create and fill in `.env` 1. Double-click `run.bat` in the project folder. On the first run it copies `.env.example` to `.env`, opens it in Notepad, and stops. 2. Edit the two required lines: ``` TELEGRAM_BOT_TOKEN=123456789:AAFxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx TELEGRAM_OWNER_ID=987654321 ``` No quotes, no spaces around `=`. 3. Optional lines, leave them as they are unless you want the feature: | Line | What it does | | --- | --- | | `OPENAI_API_KEY=` | Enables voice notes. Paste an OpenAI API key and voice messages get transcribed before the assistant sees them. Without it, voice notes get a polite "not configured" reply. | | `STT_MODEL=gpt-4o-transcribe` | The OpenAI transcription model. Fine as is. | | `PI_MODEL=` | Forces a model. Empty means the first model Pi has credentials for; with the Claude Code subscription package that is `pi-claude-code-provider/sonnet`. To use Opus through that package set `PI_MODEL=pi-claude-code-provider/opus`. Ids such as `anthropic/claude-opus-4-7` belong to Pi's built-in Anthropic provider and only work with an `ANTHROPIC_API_KEY`. `pi --list-models` prints every id Pi knows. | | `SECRETARY_TIMEZONE=` | IANA zone for reminders and cron jobs, for example `Europe/Berlin`. Empty means the Windows system zone. | | `SECRETARY_SHELL=` | Full path to `bash.exe` if Git is not installed at `C:\Program Files\Git`. | | `WORKSPACE_DIR=./workspace` | Where the assistant keeps its memory file, scripts, and files you send. | | `LOG_LEVEL=info` | Set to `debug` to see more in the console. | | `FORTRESS_ENABLED=` | Set to `true` to give the assistant the Fortress stealth browser, for reading pages that block normal browsers. Needs Python: open a terminal and run `pip install "tilion[mcp]" "mcp<2"` once. The browser engine (about 200 MB) downloads the first time it is used. | | `FORTRESS_COMMAND=` | Leave empty. If the console says `fortress bridge failed to start` with `ENOENT`, set it to `python -m tilion.mcp`. | 4. Save the file and close Notepad. ## Step 4: Start the bot Double-click `run.bat` again. It installs dependencies the first time, builds, and starts. The console should show lines like these: ``` workspace D:\...\VolcanicSecretary\workspace telegram bot @volcanic_secretary_bot agent ready: anthropic/claude-opus-4-8 scheduler ready with 0 job(s) polling started ``` Within a second or two your Telegram chat with the bot receives `🟢 Online.` followed by a short status block. Send `hi`. You will see a `⚙️ Working…` message that updates while the assistant works, then the reply. Useful commands in the chat: | Command | Effect | | --- | --- | | `/status` | Busy or idle, model, context usage, number of scheduled jobs | | `/new` | Start a fresh conversation, the old one stays on disk | | `/stop` | Abort what the assistant is doing right now | | `/jobs` | List scheduled jobs | | `/cancel ` | Delete a scheduled job by its id from `/jobs` | | `/compact` | Shrink the conversation context | ## Step 5: Stop the bot Press Ctrl+C in the console window. The bot shuts down cleanly. Windows then asks `Terminate batch job (Y/N)?`, answer `Y`. Closing the window also works. ## Everyday use - Photos are shown to the model. Files you send land in `workspace\inbox\` and the assistant is told the path. - The assistant writes durable facts to `workspace\MEMORY.md`. You can edit that file yourself; the bot reads it fresh every turn. - `workspace\AGENTS.md` holds standing instructions and the persona. Edit it to change how the assistant behaves; changes apply after `/new` or a restart. - Scheduled jobs are stored in `workspace\.secretary\jobs.json` and survive restarts. - After pulling code updates, delete the `node_modules` folder or run `npm install` once so the launcher picks up new dependencies. ## Keeping it running in the background `run.bat` is meant for a visible console window. To run the bot unattended, install pm2 once and register it: ```powershell npm install -g pm2 npm run build pm2 start "node --env-file-if-exists=.env dist/main.js" --name secretary --cwd "D:\Projects\Names\VolcanicSecretary" pm2 save ``` Alternatively, Windows Task Scheduler can start `run.bat` at logon, but any startup error then waits on the `pause` prompt inside a window you may not see. ## Troubleshooting | What you see | Cause | Fix | | --- | --- | --- | | `Config error: TELEGRAM_BOT_TOKEN is required` | `.env` is missing or the line is empty | Run `run.bat`, fill in the token, save | | `.env still contains the placeholder bot token` | The example value was never replaced | Paste the real token from BotFather | | `Startup failed: GrammyError: Call to 'getMe' failed! (401: Unauthorized)` | The token is wrong or was revoked | Copy it again from BotFather, or send `/revoke` there and use the new one | | `Startup failed: Error: Model "anthropic/..." (from PI_MODEL) has no credentials for provider "anthropic"` | `PI_MODEL` names a model from a provider you have no key for | Use one of the ids the message lists, for example `pi-claude-code-provider/opus`, or leave `PI_MODEL` empty | | `Startup failed: Error: No model available` or `No model has credentials at all` | Pi has no working model credentials | Run `pi` in a terminal and log in, or set `PI_MODEL` plus the provider's API key in `.env` | | Bot says `🟢 Online.` but never answers your messages | `TELEGRAM_OWNER_ID` is not your id | Look in the console for `ignoring update from user 987654321`, put that number in `.env`, restart | | `409: Conflict` in the console | A second copy of the bot is running with the same token | Stop the other one (another window, pm2, or Docker) | | Voice notes reply "Voice transcription is not configured" | No OpenAI key | Add `OPENAI_API_KEY` to `.env` and restart | | A scheduled script fails with `failed to start bash` | Git for Windows is not at the default path | Set `SECRETARY_SHELL` to the full path of `bash.exe` | | Replies arrive as raw text with `` tags | Telegram rejected the formatting, the bot fell back to plain text | Harmless; report the message content if it repeats | | `fortress bridge failed to start` with `ENOENT` | `tilion-mcp` is not on PATH | Set `FORTRESS_COMMAND=python -m tilion.mcp` in `.env` | | `fortress bridge failed to start` with `ModuleNotFoundError: No module named 'mcp.server.fastmcp'` | pip installed the 2.x MCP library, which Fortress doesn't support yet | Run `pip install "mcp<2"` | | The window closes before you can read anything | An error happened before the launcher's pause | Open a terminal in the folder and run `.\run.bat` from there | If the console shows a message not in this table, start the bot with `LOG_LEVEL=debug` in `.env` and read the last few lines before the exit.