Tengu Forge is a small, sharp control panel for running scripts on demand.
There’s no agent tree, no orchestration maze, no hidden magic. The model is simple:
API call → run a script → return the result.
The React front end gives you a clean dashboard to browse scripts, inspect metadata, run them with arguments/env, and review history. The FastAPI backend handles execution and exposes a minimal, predictable API.
-
🧰 Script Catalog
- List all available scripts with names, descriptions, tags, and categories.
- Supports Python-based scripts today; HTTP “actions” can be treated similarly.
-
▶️ On-Demand Execution- Run a script with structured arguments and optional environment overrides.
- Capture
stdout,stderr, exit code, start time, and duration. - Results flow straight into the UI’s output panel.
-
📜 Run History
- Per-script run history via
GET /scripts/{id}/runs. - See previous output, failures, and arguments used.
- Per-script run history via
-
🌐 HTTP Actions
- Create and store reusable HTTP actions (method, URL, headers, query params, body).
- Managed directly from the UI.
-
🔑 Configurable Backend
- API URL and API key are set in the top bar of the UI.
- Values are persisted on disk by the backend (no hard-coded keys).
Frontend (already built):
- React + TypeScript (Vite, Tailwind, shadcn)
- Lives under
src/ - Core pieces:
useBackendConfig– stores backend URL + API key and connection state.useApi– thin wrapper around the Tengu Forge backend API.Sidebar– lists scripts and lets you select one.ScriptDetails/RunTab/HistoryTab– show script metadata, run form, output, and history.HTTPActionsManager– CRUD for reusable HTTP actions.TopBar– config, connection, and theme toggle.
Backend (FastAPI, Python):
- Lives under
backend/ - Key modules:
backend/app/main.py– FastAPI app, routes, CORS, dependency wiring.backend/app/config.py– load/save API config (URL + key) frombackend/config/config.json.backend/app/models.py– Pydantic models for Script, RunResult, HTTPAction, backend config, etc.backend/app/script_registry.py– in-memory registry that describes which scripts exist and how to run them.backend/app/runs_store.py– in-memory run history store (can be swapped for a DB later).backend/app/http_actions_store.py– simple JSON-backed store for HTTP actions.backend/scripts/– actual executable scripts (e.g.hello_world.py).
Execution is intentionally simple: the backend maps a scriptId to a file/command, shells out with subprocess, and returns the captured result.
Current API surface (all paths relative to the configured backend URL).
All endpoints expect the API key in the header:
X-API-Key: YOUR_API_KEYGET /healthReturns a simple status so the frontend can verify connectivity and auth.
GET /scriptsReturns a list of Script objects (id, name, description, type, arguments, tags, etc.).
GET /scripts/{id}/runsReturns a list of RunResult records for the given script.
POST /scripts/{id}/run
Content-Type: application/json
{
"arguments": { ... },
"env": { "KEY": "VALUE", ... }
}Runs the script and returns a RunResult with stdout/stderr/exit code/duration.
GET /http-actionsReturns an array of saved HTTP actions.
POST /http-actions
Content-Type: application/json
{
"name": "My Action",
"description": "Optional description",
"method": "GET",
"url": "https://example.com",
"headers": [{ "key": "Authorization", "value": "Bearer ..." }],
"queryParams": [{ "key": "q", "value": "test" }],
"body": "{ "example": true }"
}Creates a new HTTP action and returns it with an id and createdAt.
The core layout (simplified):
tengu-forge/
├─ backend/
│ ├─ app/
│ │ ├─ main.py
│ │ ├─ config.py
│ │ ├─ models.py
│ │ ├─ script_registry.py
│ │ ├─ runs_store.py
│ │ └─ http_actions_store.py
│ ├─ scripts/
│ │ └─ hello_world.py
│ ├─ config/
│ │ └─ config.json # created on first save from the UI
│ ├─ data/ # runtime data (e.g. http-actions.json)
│ └─ requirements.txt
├─ src/
│ ├─ hooks/
│ │ ├─ useApi.ts
│ │ └─ useBackendConfig.ts
│ ├─ components/
│ │ ├─ TopBar.tsx
│ │ ├─ Sidebar.tsx
│ │ ├─ ScriptDetails.tsx
│ │ ├─ RunTab.tsx
│ │ ├─ HistoryTab.tsx
│ │ ├─ OutputPanel.tsx
│ │ └─ HTTPActionsManager.tsx
│ ├─ pages/
│ │ └─ Index.tsx
│ └─ types/
│ └─ tengu.ts
└─ README.md
- Node.js (LTS)
- npm or pnpm
- Python 3.10+
- Git
Clone the repo:
git clone https://github.com/TianJieHeng/tengu-forge.git
cd tengu-forgeCreate and activate a virtual environment:
cd backend
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activateInstall dependencies:
pip install -r requirements.txtRun the backend:
uvicorn backend.app.main:app --reload --host 0.0.0.0 --port 8000By default the app listens on http://localhost:8000.
Note: The backend expects an API key via
X-API-Key. You set and persist this through the UI, which will write it intobackend/config/config.jsonon first save.
From the repo root:
npm install
npm run devVite will print a local URL like http://localhost:5173. Open that in your browser.
- Start the backend (
uvicorn ...) and frontend (npm run dev). - Open the frontend in your browser.
- In the top bar:
- Set API URL to
http://localhost:8000 - Set API Key to any value you want (e.g.
tengu-dev-key)
- Set API URL to
- Click Save.
- Click Connect.
If the health check passes, the connection indicator will show that you’re connected, and the sidebar will populate with scripts (e.g. the Hello World example).
Scripts live in backend/scripts/ and are described in backend/app/script_registry.py.
Example: backend/scripts/hello_world.py
#!/usr/bin/env python3
import argparse
import time
def main():
parser = argparse.ArgumentParser()
parser.add_argument("--name", default="world")
args = parser.parse_args()
print(f"Hello, {args.name}! This is Tengu Forge.")
print(f"Timestamp: {int(time.time())}")
if __name__ == "__main__":
main()In backend/app/script_registry.py, you register it with an ID and metadata (name, description, arguments, etc.). The frontend reads that via GET /scripts and shows it automatically in the sidebar and script details panel.
From there you can:
- Select the script in the UI.
- Fill in arguments via the generated form.
- Click Run to execute and view the output + history.
- All API calls require
X-API-Key. The key is stored inbackend/config/config.json, written from the UI. - This is designed for trusted environments (your own Jetson, homelab, dev server).
- If you expose it over the internet, put it behind a proper reverse proxy with TLS and real auth.
- Optional persistent DB for run history.
- Per-script timeouts and resource limits.
- Better HTTP action execution + templating.
- Role-based restrictions for “dangerous” scripts.
Review LICENSE.txt