# pi-web-scheduler

Scheduler generico e durevole per **pi / PI WEB**: job `cron` / `at` / `every` con pipeline di step
(`command`, `http`, `condition`, `agent`, `notify`), retry, policy di misfire, storico run e
**console web** (pannello *Scheduler* nel workspace PI WEB). Funziona su Linux, WSL e Windows (solo Node + filesystem).

Design e decisioni: [docs/adr/0001-architettura-scheduler.md](docs/adr/0001-architettura-scheduler.md).

> **Sperimentale.** Il plugin usa funzioni interne di PI WEB e può rompersi con nuove versioni. Gli step `command` e `agent` eseguono codice: abilita solo job di cui ti fidi.

## Installazione da npm

```sh
pi install npm:pi-web-scheduler   # companion extension
```

Per il plugin PI WEB vedi la sezione "Installazione in PI WEB" più sotto.

## Componenti

| Path | Cosa |
|---|---|
| `src/core/` | modello job, schedule (croner, timezone/DST), store su file, lease, engine, executor |
| `src/server.ts` | PI WEB server plugin: tick nel session daemon, peer API, run agent in sessioni visibili |
| `src/browser/` | pannello **Scheduler**: job attivi, storico, dettaglio run/step, esegui ora, pausa, riprogramma (con anteprima), modifica/duplica/elimina, misfire |
| `src/companion.ts` | estensione pi: tool `scheduler_create_job`, `scheduler_list_jobs`, `scheduler_manage_job`, `scheduler_job_runs`, comando `/jobs`, guardia allowlist tool |
| `src/cli.ts` | host di riserva: `pi-scheduler tick|daemon|list|run <id>|show <id>` |

Store: `$PI_SCHEDULER_HOME` o `~/.pi-scheduler` (`jobs/`, `runs/`, `locks/`, `bindings.json`).

## Modello job (sintesi)

```json
{
  "name": "Azure VM health 23:30",
  "schedule": { "cron": "30 23 * * *", "tz": "Europe/Rome" },
  "misfire": "skip | run-once | ask",
  "retry": { "maxAttempts": 5, "backoff": "2h", "factor": 1, "until": "2026-10-10T18:00:00+02:00" },
  "timeout": "20m",
  "steps": [
    { "id": "metrics", "type": "command", "argv": ["az", "..."], "parseJson": true },
    { "id": "gate", "type": "condition", "any": [{ "path": "steps.metrics.json.value[*].avg", "aggregate": "max", "op": ">", "value": 85 }], "onFalse": "continue | stop | retry" },
    { "id": "analyze", "type": "agent", "when": "gate", "prompt": "... {{steps.metrics.stdout}}", "tools": ["read", "bash"] },
    { "id": "alert", "type": "notify", "when": "gate", "message": "{{steps.analyze.text}}" }
  ]
}
```

Esempi completi in [`examples/`](examples/). Le condizioni sono dichiarative (nessuna `eval`), i comandi
usano `argv` senza shell, gli step `agent` hanno un'allowlist di tool.

## Sviluppo

```sh
npm install
npm test        # test del core (node:test + tsx)
npm run build   # dist/
```

## Installazione in PI WEB (attenzione: richiede restart del session daemon)

```sh
mkdir -p ~/.pi-web/plugins
ln -s <percorso-del-clone> ~/.pi-web/plugins/pi-web-scheduler
```

Poi abilitare in **Settings → PI WEB plugins** e **Settings → Pi packages** (per la companion extension),
riavviare il session daemon **da fuori dalle sessioni ospitate** (`systemctl --user restart pi-web-sessiond`:
interrompe sessioni e terminali attivi) e ricaricare il browser.

Impostazioni plugin (config PI WEB): `storeRoot`, `tickMs`, `keepRuns`, `allowedExecutables`, `piCommand`.

## Host di riserva (pi-web spento)

```sh
node dist/cli.js tick        # da cron / systemd timer / Windows Task Scheduler ogni minuto
node dist/cli.js daemon      # processo dedicato
```

Env: `PI_SCHEDULER_HOME`, `PI_SCHEDULER_PI` (comando pi), `PI_SCHEDULER_ALLOW` (eseguibili consentiti).
Lo stesso store è condiviso con il plugin; il lease per job evita doppie esecuzioni.

## Stato

- ✅ core + test (20), CLI verificata end-to-end
- ⏳ plugin server / pannello / companion: compilano contro le API installate (PI WEB 1.202610.1), **da validare in PI WEB** (richiede restart daemon)
- 🔜 notifiche intercom/mail, executor `mcp` diretto, editor visuale degli step, installer timer OS per la CLI

## Licenza

MIT, copyright (c) 2026 Vincenzo Moccia. Vedi [LICENSE](LICENSE).

## Stato pubblicazione (tracking locale)

- Preparazione locale completata; push GitHub e publish npm in attesa di ok esplicito dell'utente.
