A local-first web control panel for scheduling, running, and auditing recurring Claude Code and script tasks.
This project started as a small AI chat interface and has evolved into a
desktop-style operations console for maintainers who keep lots of repeatable
work in Markdown task files, shell scripts, and local automation jobs. It runs
on your own machine, stores runtime state in SQLite, and is designed to stay on
127.0.0.1 unless you deliberately add your own authentication and network
controls.
- Create recurring tasks from local task step files.
- Run Markdown-based LLM tasks through the
claudeCLI. - Run local script tasks with shebang,
.py,.sh, or direct executable resolution. - Group related tasks into folders and collapse or expand each group in the queue.
- Switch the interface theme between system, light, and dark mode. The default is system.
- Assign stable task codes such as
T001,T002, andT003. - Schedule work with daily, interval, weekly, monthly, or cron expressions.
- Trigger tasks manually from the UI or CLI.
- Inspect recent runs, task-specific run history, stdout and stderr logs, and verification details.
- Retry failed LLM tasks through a watchdog flow.
- Resume Claude Code sessions from previous LLM runs.
- Manage macOS LaunchAgents from the same local console.
- View LaunchAgent stdout and stderr tails with explicit empty, missing, and unavailable log states.
- Protect local state-changing routes from cross-site writes.
The first screen is the actual task console, not a marketing page. It contains:
- A task queue with status filters, folder chips, and collapsible groups.
- A run log panel for the selected task.
- Scheduler, watchdog, and LaunchAgent status summaries.
- A task editor for LLM and script tasks.
- A macOS LaunchAgent inventory panel with search, scope filters, actions, and log viewing.
- A display-mode control for system, light, and dark theme selection.
- macOS, for the LaunchAgent management features.
- Node.js 18 or newer.
- npm.
- Claude Code CLI on
PATHif you want to run LLM tasks. - Any interpreters required by your script tasks, such as
python3,sh, or custom executables.
The basic web UI and SQLite-backed task storage work without Claude Code, but
LLM task execution requires the claude CLI to be available to the backend
process.
git clone https://github.com/Gatsby1s/ai-chat-interface.git
cd ai-chat-interface/backend
npm install
npm run db:init
npm run devOpen the local console:
http://127.0.0.1:3000
There is also a convenience script at the repository root:
./start.shThe script installs backend dependencies when needed, initializes the database when missing, seeds sample data on first setup, and starts the server.
The backend loads environment files in this order:
backend/.envbackend/.env.local, overriding values frombackend/.env
Create one of those files from the example:
cd backend
cp ../.env.example .envSupported variables:
| Variable | Default | Purpose |
|---|---|---|
HOST |
127.0.0.1 |
Interface bind host. Keep this loopback-only for normal use. |
PORT |
3000 |
Interface port. |
ANTHROPIC_BASE_URL |
empty | Optional override passed through the runtime environment. |
ANTHROPIC_AUTH_TOKEN |
empty | Optional auth token passed through the runtime environment. |
ANTHROPIC_MODEL |
empty | Optional model override for services that use it. |
Real .env* files are ignored by git. Do not commit local tokens, database
files, logs, or personal task paths.
Each task has:
execution_mode:llmorscripttask_path: a local Markdown task file or script pathgroup_name: optional folder label used by the UI- schedule fields
- status and recent run metadata
- an automatically assigned stable code
LLM tasks read a local task step file and pass its contents to the claude CLI.
The executor builds a prompt that includes:
- the source task file path
- the stable task code or task label
- the task file content
It then runs:
claude -p --session-id <generated-session-id> <prompt>The backend stores the generated Claude session ID and the source directory so the UI can offer a resume command later:
cd '<task-directory>' && claude --resume '<session-id>'LLM scheduled tasks are scanned by the running Node.js server once per minute.
Script tasks execute a local script or executable directly from the backend. Resolution order:
- Use the script shebang if present.
- Use
python3for.pyfiles. - Use
shfor.shfiles. - Execute the path directly for other files.
Script tasks can also define:
working_directoryscript_args, stored as an array
Scheduled script tasks are scanned by a macOS LaunchAgent once per minute. The app writes and reconciles this LaunchAgent automatically when script tasks are created, updated, enabled, disabled, or deleted.
The scheduler supports:
| Type | Payload field | Example |
|---|---|---|
daily |
daily_time |
09:00 |
interval |
interval_hours |
6 |
weekly |
weekly_days and daily_time |
Mondays, Wednesdays, and Fridays at 09:30 |
monthly |
monthly_day and daily_time |
day 1 at 08:00 |
cron |
cron_expr |
*/15 * * * * |
Weekly day values use numbers from 0 to 6. The CLI accepts comma-separated
weekdays in the same form used by the backend.
Run commands from the backend directory:
cd backendList tasks:
npm run tasks:cli -- listCreate a daily LLM task:
npm run tasks:cli -- create llm ~/tasks/check-panel.md daily 09:00Create a cron-based LLM task:
npm run tasks:cli -- create llm ~/tasks/check-panel.md cron "*/15 * * * *"Create a weekly script task:
npm run tasks:cli -- create script ~/bin/daily-report.sh weekly 1,3,5@09:30 ~/projects/report-job '["--dry-run"]'Update a task:
npm run tasks:cli -- update <taskId> script ~/bin/daily-report.sh cron "0 9 * * 1-5" ~ '["--env","prod"]'Run a task immediately:
npm run tasks:cli -- run <taskId>Toggle a task:
npm run tasks:cli -- toggle <taskId>Delete a task:
npm run tasks:cli -- delete <taskId>The CLI accepts an optional base URL as the first argument:
node src/cli/tasks-cli.js http://127.0.0.1:4000 listAll API routes are served under /api.
| Method | Route | Purpose |
|---|---|---|
GET |
/api/tasks |
List tasks. |
POST |
/api/tasks |
Create a task. |
PUT |
/api/tasks/:id |
Update a task. |
DELETE |
/api/tasks/:id |
Move a task to the recycle bin. |
POST |
/api/tasks/:id/restore |
Restore a deleted task. |
DELETE |
/api/tasks/:id/permanent |
Permanently delete a task. |
POST |
/api/tasks/:id/toggle |
Enable or disable a task. |
POST |
/api/tasks/:id/run |
Run a task immediately. |
POST |
/api/tasks/:id/open-session |
Open the latest resumable Claude session. |
GET |
/api/tasks/:id/runs |
List runs for one task. |
Example:
curl -s http://127.0.0.1:3000/api/tasks | jqCreate a task:
curl -s http://127.0.0.1:3000/api/tasks \
-H 'Content-Type: application/json' \
-d '{
"execution_mode": "llm",
"task_path": "~/tasks/check-panel.md",
"group_name": "Maintenance",
"schedule_type": "daily",
"daily_time": "09:00",
"enabled": true
}' | jq| Method | Route | Purpose |
|---|---|---|
GET |
/api/runs/recent?limit=10 |
List recent runs. |
GET |
/api/runs/:id |
Get one run. |
GET |
/api/runs/:id/log |
Read a run log and log status. |
POST |
/api/runs/:id/open-session |
Open a resumable Claude session for a run. |
GET |
/api/watchdog/status |
Read scheduler, watchdog, and app LaunchAgent state. |
POST |
/api/watchdog/run |
Trigger the watchdog immediately. |
GET |
/api/launchd/status |
Read the app-owned script scheduler LaunchAgent state. |
POST |
/api/launchd/reconcile |
Reconcile the app-owned script scheduler LaunchAgent. |
| Method | Route | Purpose |
|---|---|---|
GET |
/api/system-launch-agents?scope=user |
List LaunchAgents. |
GET |
/api/system-launch-agents/:label |
Inspect one LaunchAgent. |
GET |
/api/system-launch-agents/:label/log?type=stdout |
Read stdout or stderr tail. |
POST |
/api/system-launch-agents/:label/bootstrap |
Bootstrap a LaunchAgent. |
POST |
/api/system-launch-agents/:label/bootout |
Boot out a LaunchAgent. |
POST |
/api/system-launch-agents/:label/kickstart |
Kickstart a LaunchAgent. |
POST |
/api/system-launch-agents/:label/enable |
Enable a LaunchAgent. |
POST |
/api/system-launch-agents/:label/disable |
Disable a LaunchAgent. |
Scopes:
user:~/Library/LaunchAgentsglobal:/Library/LaunchAgentsall: both locations
The control panel protects its own app-related LaunchAgent labels. Destructive
actions against protected labels require force=true.
The application writes local runtime state under backend/data/.
| Path | Purpose |
|---|---|
backend/data/chat.db |
SQLite database. |
backend/data/logs/ |
Per-task logs and LaunchAgent logs. |
backend/data/.gitkeep |
Keeps the runtime directory in git. |
The database and logs are intentionally ignored by git.
.
+-- backend/
| +-- package.json
| +-- src/
| +-- cli/ # HTTP task CLI and script runner
| +-- config/ # env loading, paths, task file resolution
| +-- routes/ # Express API routes
| +-- services/ # schedulers, executors, LaunchAgent services
| +-- utils/ # SQLite initialization and schedule helpers
+-- frontend/
| +-- index.html
| +-- app.js
| +-- styles.css
+-- SETUP.md
+-- LICENSE
+-- README.md
This is a local control panel for running commands on your machine. Treat it as a privileged local operations tool.
Important defaults:
- The server binds to
127.0.0.1by default. - Cross-site state-changing requests are blocked using Origin, Referer, and Fetch Metadata checks.
- Runtime secrets and local state are ignored by git.
- The UI warns through structure rather than pretending LaunchAgent operations are harmless.
Do not expose this service directly to the public Internet. If you want remote access, put it behind authentication, TLS, and a network boundary you control.
Install dependencies:
cd backend
npm installInitialize or migrate the SQLite database:
npm run db:initStart the local server:
npm run devStatic frontend files are served directly from frontend/ by the Express
server. There is no separate frontend build step.
Useful syntax checks:
node --check src/server.js
node --check src/cli/tasks-cli.js
node --check ../frontend/app.jsIf port 3000 is already in use, either stop the existing server or change the
port:
PORT=4000 npm run devRun:
npm run db:initConfirm that the Claude Code CLI is installed and available to the backend process:
which claude
claude --versionAlso confirm that your local Claude credentials and environment are configured outside this repository.
Check the app-owned LaunchAgent status:
curl -s http://127.0.0.1:3000/api/launchd/status | jqThen reconcile it:
curl -s -X POST http://127.0.0.1:3000/api/launchd/reconcile | jqScheduled script tasks require macOS LaunchAgent support and at least one enabled script task.
Some app-owned labels are protected so the control panel does not accidentally
disable its own scheduler or host process. If you deliberately need to operate
on a protected label, pass force=true after reviewing the target.
- Authentication for remote deployment scenarios.
- Import and export of task definitions.
- Better cron preview and next-run explanations.
- Pluggable executors beyond Claude Code and local scripts.
- Built-in test fixtures for scheduler edge cases.
MIT. See LICENSE.