diff --git a/README.md b/README.md index 90ba795..7dd8e11 100644 --- a/README.md +++ b/README.md @@ -240,6 +240,7 @@ redémarrages (volume `/data`). Variables d'environnement du conteneur : | `LOKI_MCP` | `rust` : les serveurs MCP — configuration, catalogue, état, pool de sessions stdio et HTTP — sont tenus par la façade Rust ; le Go lui demande les outils MCP de l'agent et leurs appels (expérimental, migration en cours) | Go | | `LOKI_BACKENDS` | `rust` : les backends llama.cpp — statut, vérification git, binaires précompilés, bascule fast/opt, backends custom, GPU vus par un moteur — et le job des moteurs sont tenus par la façade Rust ; les jobs du Go (compilation, moteur de l'image, Strata) lui rapportent leurs étapes (expérimental, migration en cours) | Go | | `LOKI_STRATA` | `rust` (avec `LOKI_BACKENDS=rust`) : l'écran de Strata — machine, ce qui y tient, recommandation, modèles installés —, ses réglages et son installation sont tenus par la façade Rust ; le lancement reste au Go (expérimental, migration en cours) | Go | +| `LOKI_TASKS` | `rust` : les tâches planifiées — liste, enregistrement, bascule, interrupteur maître, lancement, arrêt —, le planificateur et les tâches « script seul » sont tenus par la façade Rust ; une tâche IA reste exécutée par le Go, à la demande de la façade (expérimental, migration en cours) | Go | | `LOKI_ENGINE_IMAGE` | `rust` (avec `LOKI_BACKENDS=rust`) : le panneau « moteur de l'image » — version qui tourne, dernier build publié, version recommandée, mise à jour depuis le registre officiel avec essai à blanc, bascules, retour à la version précédente, vérification du rendu du gabarit — est tenu par la façade Rust (expérimental, migration en cours) | Go | | `LOKI_PROJECTS` | `rust` : le hub des projets (liste et comptes, création, renommage, description, suppression, bascule) est tenu par la façade Rust ; les discussions restent à qui tient la conversation (expérimental, migration en cours) | Go | | `LOKI_SYSTEM` | `rust` : les interrupteurs système — accès internet et Crawl4AI, pilotage du navigateur, clé de complétion, écoute réseau du moteur — sont tenus par la façade Rust ; le navigateur piloté reste au Go (expérimental, migration en cours) | Go | diff --git a/docs/superpowers/specs/2026-10-15-rust-migration-lot26-taches-design.md b/docs/superpowers/specs/2026-10-15-rust-migration-lot26-taches-design.md new file mode 100644 index 0000000..3576a04 --- /dev/null +++ b/docs/superpowers/specs/2026-10-15-rust-migration-lot26-taches-design.md @@ -0,0 +1,193 @@ +# Lot 26 — Tâches planifiées tenues par la façade + +## Contexte + +Une tâche planifiée tourne seule, sans utilisateur en face. Il en existe +deux sortes : + +- **une tâche IA** : une consigne, exécutée par l'agent dans un fil + éphémère ; +- **une tâche « script seul »** : un script du dossier protégé + (`$LOKI_HOME/scripts`), lancé sans modèle. Sa sortie devient le + compte-rendu. + +Le Go les tient dans plusieurs fichiers : + +- `tasks.go` : la base (`bkTasks`) et l'interrupteur maître ; +- `tasks_cron.go` : les fréquences ; +- `tasks_sched.go` : le planificateur, un tic par minute et une tâche due + par tic ; +- `tasks_run.go` et `tasks_script.go` : l'exécution ; +- `web_tasks.go` : sept routes. + +Les fréquences prennent quatre formes : + +- `@every 2h` : intervalle glissant d'au moins une minute ; +- `@every 3d@9:30` : tous les N jours, à une heure murale ; +- `@once 2026-10-01 16:54` : une seule fois ; +- une expression cron à cinq champs. + +L'heure murale est celle du fuseau de la tâche. Sans fuseau, ou avec un +fuseau inconnu, c'est l'heure locale du serveur. + +## Le lot + +Avec `LOKI_TASKS=rust` (expérimental), la façade (`rust/loki-rs/src/tasks`) +sert les routes `/api/tasks*` et fait tourner le planificateur, que le Go ne +démarre plus. Elle exécute elle-même les tâches script : + +- le shell de l'agent (`run_shell`), dans le dossier de la discussion + ouverte ; +- une limite de 10 min ; +- un arrêt par `/api/tasks/stop`. + +Une tâche IA passe par le verrou de génération et les outils de l'agent, qui +restent au Go. La façade la lui fait exécuter par `/internal/v1/tasks/run`, +que le Go traite comme son propre planificateur l'aurait fait : + +- preset épinglé ; +- projet forcé ; +- `RunAutonomous` ; +- état persisté ; +- notification. + +Moteur occupé ou modèle pas prêt : la tâche ne tourne pas, son état ne bouge +pas, et le tic suivant la réessaie. Le Go rend la main à la fin du passage. + +Les notifications des tâches script partent par la façade si elle tient les +notifications, sinon par le Go (`/internal/v1/push/send`). + +Les fréquences demandent l'heure murale d'un fuseau IANA. `src/tz.rs` porte +ce que le Go fait de la zoneinfo du système : + +- lecture des TZif v1 et v2, et de la règle POSIX qui prolonge les + transitions ; +- `time.Local` (`$TZ`, `/etc/localtime`) ; +- `time.Date`, avec la normalisation des débordements ; +- les heures qui sautent ou qui se répètent, résolues comme le Go. + +`tasks/cron.rs` porte `time.ParseDuration` du Go, messages d'erreur compris. + +Côté Go : + +- `facade_tasks.go` : le relais des routes reçues par le tunnel et les deux + routes internes ; +- `web_server.go` : le planificateur n'est pas démarré ; +- `tasksNow` : l'heure des tâches, remplaçable en test. + +## Correction de la façade + +Les appels de la façade à l'API interne du Go étaient tous bornés à 10 s. Un +outil exécuté par le Go (`/internal/v1/tool/exec`) qui dure plus longtemps +était coupé : + +- une page web lente ; +- le navigateur ; +- un serveur MCP tenu par le Go. + +Le résultat partait en erreur alors que l'outil continuait. Ces appels sont +désormais bornés à 30 min. Une tâche IA exécutée par le Go est attendue sans +autre limite que 24 h. + +## Ce que les mutations ont corrigé dans le port + +Le premier passage des mutations a laissé survivre des cas que le journal ne +voyait pas. Confrontés au Go, plusieurs étaient de vrais écarts : + +- **dates lointaines** : un instant en nanosecondes sur 64 bits s'arrête en + 2262. Le Go accepte `@once 2400-02-29 10:00`, ou un intervalle en jours + qui mène au-delà. Les instants des fréquences passent sur 128 bits ; +- **intervalle en jours démesuré** : `@every 200000d` déborde dans le Go + (`time.Duration`), qui rend une date passée. Le port déborde de même ; +- **l'heure qui saute** : `@every 1d@02:30` à Paris, le jour du passage à + l'heure d'été, tombe à 3 h 30. Le Go avance ensuite de jour en jour depuis + ce candidat (`AddDate`) et reste à 3 h 30. Le port repartait de 2 h 30 ; +- **compte-rendu tronqué** : le Go coupe à 4000 octets, au milieu d'un + caractère s'il le faut, et chaque octet orphelin devient U+FFFD à + l'encodage JSON. Le port reculait à une frontière de caractère ; +- **nom de fuseau** : le Go refuse tout nom qui contient `..`, pas seulement + un composant `..`. + +## Parité + +`internal/loki/parity_tasks_test.go` (Linux) tire du Go un journal rejoué +par `tests/tasks.rs`. L'heure des tâches est fixée pas à pas ; les +identifiants tirés au hasard deviennent `{{ID1}}`, `{{ID2}}`… dans l'ordre où +ils apparaissent. Le journal couvre : + +- **l'écran** : + - scripts du dossier (sous-dossiers compris) ; + - mode agent, interrupteur maître (rangé à « 0 » : pas suspendu) ; + - projets, mémoire coupée, internet armé ; + - la tâche en cours pendant un script ; +- **l'enregistrement** : + - corps illisible, sans nom, sans consigne ; + - script absent, dossier, remontée, chemin absolu, séparateur Windows, + caractère interdit ; + - chaque erreur de fréquence : vide, intervalle trop court, illisible ou + sans unité, jours ou heure invalides, cron à 4 champs, hors bornes, en + lettres, plage inversée, pas nul, jamais satisfait, date illisible ou + déjà passée ; + - chaque forme de fréquence, dans plusieurs fuseaux : Paris (dont l'heure + qui saute), New York, Calcutta, fuseau inconnu ; + - id inconnu gardé, projet inconnu remplacé par l'actif ; + - modification avec ou sans changement de fréquence ; +- **la bascule, l'interrupteur maître, la suppression** ; +- **les lancements** : + - tâche absente ; + - tâche IA avec moteur occupé (409) ou modèle pas prêt (503) ; + - script en succès, en échec, arrêté à la main ; + - script qui échoue après un succès, puis se rétablit ; + - tâche script rangée sans script, au nom refusé, au script disparu ; +- **le planificateur** : + - tic suspendu, tic pendant une optimisation ; + - une tâche IA due qui attend le modèle ; + - une tâche due par tic, dans l'ordre des noms ; + - la tâche « une fois » qui s'éteint ; + - une tâche sans prochain passage (posé sans la lancer) ; + - une fréquence devenue invalide ; + - deux tâches de même nom (rangées par id). + +Après chaque étape, le journal enregistre les tâches en base. + +Autres tests : + +- fuseaux et fréquences confrontés au Go : + - Paris aux deux changements d'heure, New York, Calcutta ; + - avant la première transition, pile à une transition, la table avant la + règle prolongée ; + - `$TZ` (vide, « : », chemin, nom), `/etc/localtime` ; + - noms refusés, « UTC » sans base ; + - mois hors bornes, années bissextiles, mois de 30 jours ; + - `ParseDuration` et ses débordements, cron (plages, pas, bornes, à 78 et + 364 jours) ; +- le compte-rendu tronqué, les noms de script ; +- l'amorçage au démarrage (tâches actives sans prochain passage) ; +- la durée d'un passage, à une horloge qui avance ; +- la tâche IA en cours vue par l'écran ; +- la tâche IA lancée à la main part au Go (`tache_ia_executee_par_le_go`) ; +- le planificateur tourne à son pas sans demande ; +- sans `LOKI_TASKS`, les routes partent au Go ; +- le Go (`TestFacadeTaches`) : + - les sept routes partent à la façade, et elles seules ; + - `/internal/v1/tasks/run` sur une tâche IA sans modèle (occupée, rien + d'enregistré) et sur une tâche script ; + - `/internal/v1/push/send`. + +## Mutations + +79 mutations du port Rust, dont 77 tuées. Les survivantes du premier passage +l'ont été par les tests et les étapes ajoutés ci-dessus. Restent deux +équivalentes : + +- le refus d'un nom de script absolu **après** nettoyage : sous Unix, un nom + qui ne commence pas par `/` ne le devient pas (le contrôle du Go vaut pour + Windows) ; +- `web_on` sans `internet_enabled` : la joignabilité refuse déjà un moteur + Crawl4AI sans adresse, seul cas où les deux diffèrent. + +## Écarts assumés + +- Pendant une tâche IA, le Go force le projet de la tâche. Une tâche + enregistrée sans projet à ce moment-là prend, côté façade, le projet actif + rangé. diff --git a/internal/loki/facade_chat.go b/internal/loki/facade_chat.go index 18dfd82..a897095 100644 --- a/internal/loki/facade_chat.go +++ b/internal/loki/facade_chat.go @@ -179,6 +179,8 @@ func withFacadeChat(path string, h http.HandlerFunc) http.HandlerFunc { held, unavailable = facadeEngineImage, "moteur de l'image indisponible : " case facadeStrataRoutes[path]: held, unavailable = facadeStrata, "Strata indisponible : " + case facadeTasksRoutes[path]: + held, unavailable = facadeTasks, "tâches indisponibles : " default: return h } diff --git a/internal/loki/facade_tasks.go b/internal/loki/facade_tasks.go new file mode 100644 index 0000000..93977bf --- /dev/null +++ b/internal/loki/facade_tasks.go @@ -0,0 +1,74 @@ +package loki + +import ( + "net/http" + "os" + "strings" +) + +// facade_tasks.go — tâches planifiées tenues par la façade Rust +// (LOKI_TASKS=rust, lot 26 de la migration ; expérimental, le Go reste le +// défaut) : les routes /api/tasks*, le planificateur et les tâches « script +// seul » (rust/loki-rs/src/tasks). Une tâche IA passe toujours par le verrou +// de génération et les outils de l'agent, qui sont ici : la façade la fait +// exécuter par le Go (/internal/v1/tasks/run). Le planificateur du Go ne +// démarre pas. + +const facadeTasksEnv = "LOKI_TASKS" + +// facadeTasks : la façade tient-elle les tâches ? +func facadeTasks() bool { + return strings.EqualFold(strings.TrimSpace(os.Getenv(facadeTasksEnv)), "rust") && + facadeAddr() != "" && internalToken() != "" +} + +// facadeTasksRoutes : les routes que la façade sert alors. +var facadeTasksRoutes = map[string]bool{ + "/api/tasks": true, + "/api/tasks/save": true, + "/api/tasks/delete": true, + "/api/tasks/toggle": true, + "/api/tasks/pause": true, + "/api/tasks/run": true, + "/api/tasks/stop": true, +} + +// handleInternalTaskRun (POST {id}) : exécute MAINTENANT la tâche IA d'id +// donné, comme le ferait le planificateur (preset épinglé, projet forcé, +// RunAutonomous, état persisté, notification), et rend la main à la fin. +// {busy:true} : le moteur était occupé ou le modèle pas prêt — la tâche n'a +// pas tourné et sera réessayée. +func handleInternalTaskRun(w http.ResponseWriter, r *http.Request) { + var in struct { + ID string `json:"id"` + } + if !readLeaseBody(w, r, &in) { + return + } + t, ok := getTask(in.ID) + if !ok { + sendJSON(w, http.StatusNotFound, map[string]any{"error": "tâche introuvable"}) + return + } + before := t.LastRun + runTask(t) + after, _ := getTask(in.ID) + sendJSON(w, http.StatusOK, map[string]any{"busy": after.LastRun == before}) +} + +// handleInternalPushSend (POST {title, body}) : une notification à tous les +// abonnés, envoyée en tâche de fond (les tâches script de la façade quand +// elle ne tient pas les notifications). +func handleInternalPushSend(w http.ResponseWriter, r *http.Request) { + var in struct { + Title string `json:"title"` + Body string `json:"body"` + } + if !readLeaseBody(w, r, &in) { + return + } + if hasPushSubs() { + go sendPushToAll(in.Title, in.Body) + } + w.WriteHeader(http.StatusNoContent) +} diff --git a/internal/loki/facade_tasks_test.go b/internal/loki/facade_tasks_test.go new file mode 100644 index 0000000..3c93419 --- /dev/null +++ b/internal/loki/facade_tasks_test.go @@ -0,0 +1,133 @@ +package loki + +import ( + "encoding/json" + "io" + "net/http" + "net/http/httptest" + "os" + "path/filepath" + "runtime" + "strings" + "sync" + "testing" +) + +// Les tâches partent à la façade avec LOKI_TASKS=rust (lot 26) ; le reste, +// non. Le Go exécute pour elle une tâche IA (/internal/v1/tasks/run) et +// envoie ses notifications (/internal/v1/push/send). +func TestFacadeTaches(t *testing.T) { + home := testHome(t) + var mu sync.Mutex + var calls []string + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Header.Get(internalTokenHeader) != jetonTest { + w.WriteHeader(403) + return + } + mu.Lock() + calls = append(calls, r.Method+" "+r.URL.Path) + mu.Unlock() + io.WriteString(w, `{"ok":true,"de":"facade"}`) + })) + defer srv.Close() + t.Setenv(internalTokenEnv, jetonTest) + t.Setenv(facadeAddrEnv, strings.TrimPrefix(srv.URL, "http://")) + local := func(w http.ResponseWriter, r *http.Request) { io.WriteString(w, "go") } + + t.Setenv(facadeTasksEnv, "") + if facadeTasks() { + t.Fatal("tenues sans LOKI_TASKS") + } + for p := range facadeTasksRoutes { + w := httptest.NewRecorder() + withFacadeChat(p, local)(w, httptest.NewRequest("POST", p, strings.NewReader("{}"))) + if w.Body.String() != "go" { + t.Fatalf("sans LOKI_TASKS, %s : %q", p, w.Body.String()) + } + } + t.Setenv(facadeTasksEnv, " RUST") + if len(facadeTasksRoutes) != 7 { + t.Fatalf("routes : %v", facadeTasksRoutes) + } + for p := range facadeTasksRoutes { + w := httptest.NewRecorder() + withFacadeChat(p, local)(w, httptest.NewRequest("POST", p, strings.NewReader("{}"))) + if !strings.Contains(w.Body.String(), "facade") { + t.Fatalf("%s non relayé : %q", p, w.Body.String()) + } + } + mu.Lock() + n := len(calls) + mu.Unlock() + if n != len(facadeTasksRoutes) { + t.Fatalf("relais : %v", calls) + } + w := httptest.NewRecorder() + withFacadeChat("/api/chat/stop", local)(w, httptest.NewRequest("POST", "/api/chat/stop", nil)) + if w.Body.String() != "go" { + t.Fatalf("l'arrêt du chat reste au Go : %q", w.Body.String()) + } + + // /internal/v1/tasks/run : la tâche IA, exécutée comme par le + // planificateur. Modèle pas prêt : elle ne tourne pas (busy). + run := func(body string) (int, map[string]any) { + rec := httptest.NewRecorder() + handleInternalTaskRun(rec, httptest.NewRequest("POST", "/internal/v1/tasks/run", strings.NewReader(body))) + var out map[string]any + _ = json.Unmarshal(rec.Body.Bytes(), &out) + return rec.Code, out + } + if code, _ := run(`{"id":"absente"}`); code != 404 { + t.Fatalf("tâche absente : %d", code) + } + rec := httptest.NewRecorder() + handleInternalTaskRun(rec, httptest.NewRequest("GET", "/internal/v1/tasks/run", nil)) + if rec.Code != 405 { + t.Fatalf("GET : %d", rec.Code) + } + if err := saveTask(Task{ID: "ia", Name: "IA", Prompt: "x", Schedule: "@every 1h", Enabled: true}); err != nil { + t.Fatal(err) + } + if code, out := run(`{"id":"ia"}`); code != 200 || out["busy"] != true { + t.Fatalf("tâche IA sans modèle : %d %v", code, out) + } + if tk, _ := getTask("ia"); tk.LastRun != 0 { + t.Fatalf("tâche IA sans modèle : passage enregistré %+v", tk) + } + // /internal/v1/push/send : sans abonné, rien ne part. + rec = httptest.NewRecorder() + handleInternalPushSend(rec, httptest.NewRequest("POST", "/internal/v1/push/send", strings.NewReader(`{"title":"t","body":"b"}`))) + if rec.Code != 204 { + t.Fatalf("push : %d", rec.Code) + } + rec = httptest.NewRecorder() + handleInternalPushSend(rec, httptest.NewRequest("POST", "/internal/v1/push/send", strings.NewReader(`{`))) + if rec.Code != 400 { + t.Fatalf("push illisible : %d", rec.Code) + } + + t.Setenv(internalTokenEnv, "") + if facadeTasks() { + t.Fatal("tenues sans jeton interne") + } + + // Une tâche script reçue tourne aussi (et n'est pas « occupée »). + if runtime.GOOS != "windows" { + if err := os.MkdirAll(filepath.Join(home, "scripts"), 0o755); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(filepath.Join(home, "scripts", "ok.sh"), []byte("#!/bin/sh\necho bonjour\n"), 0o755); err != nil { + t.Fatal(err) + } + if err := saveTask(Task{ID: "sc", Name: "Script", Kind: "script", Script: "ok.sh", Schedule: "@every 1h", Enabled: true}); err != nil { + t.Fatal(err) + } + if code, out := run(`{"id":"sc"}`); code != 200 || out["busy"] != false { + t.Fatalf("tâche script : %d %v", code, out) + } + if tk, _ := getTask("sc"); tk.LastRun == 0 || !tk.LastOK || !strings.Contains(tk.LastReport, "bonjour") { + t.Fatalf("tâche script : %+v", tk) + } + } +} diff --git a/internal/loki/parity_tasks_test.go b/internal/loki/parity_tasks_test.go new file mode 100644 index 0000000..0dffdc4 --- /dev/null +++ b/internal/loki/parity_tasks_test.go @@ -0,0 +1,361 @@ +//go:build linux + +package loki + +import ( + "encoding/json" + "net/http" + "net/http/httptest" + "os" + "regexp" + "strconv" + "strings" + "testing" + "time" +) + +// parity_tasks_test.go — journal de référence des tâches planifiées (lot 26 +// de la migration Rust) : l'enregistrement (chaque forme de fréquence, ses +// erreurs, les fuseaux et les changements d'heure), la bascule, l'interrupteur +// maître, la suppression, le lancement d'une tâche script (succès, échec, +// arrêt) ou IA (moteur occupé, modèle pas prêt) et les tics du planificateur, +// l'heure des tâches fixée pas à pas. Rangé dans +// rust/loki-rs/tests/parity_tasks/tasks.json ; tests/tasks.rs le rejoue par la +// façade. +// +// LOKI_PARITY_UPDATE=1 go test ./internal/loki -run TestPariteTaches + +const parityTasksFile = "../../rust/loki-rs/tests/parity_tasks/tasks.json" + +type parityTasksStep struct { + Name string `json:"name"` + Now string `json:"now,omitempty"` // RFC 3339 : l'heure des tâches + Files map[string]string `json:"files,omitempty"` + Config map[string]string `json:"config,omitempty"` + State map[string]string `json:"state,omitempty"` + Generating bool `json:"generating,omitempty"` + Tick bool `json:"tick,omitempty"` // un tic du planificateur, avant la requête + Tune bool `json:"tune,omitempty"` // une optimisation tient le moteur pendant l'étape + PutTask string `json:"put_task,omitempty"` // une tâche écrite telle quelle en base + Method string `json:"method,omitempty"` + Path string `json:"path,omitempty"` + Body string `json:"body,omitempty"` + WaitRunning string `json:"wait_running,omitempty"` // « {{IDn}} » : attendre qu'elle tourne + WaitDone string `json:"wait_done,omitempty"` // « {{IDn}} » : attendre la fin du passage + Status int `json:"status,omitempty"` + Out any `json:"out,omitempty"` + Tasks any `json:"tasks"` +} + +var ( + parityTaskDurRe = regexp.MustCompile(`exit: (-?[0-9]+) · [^\n"\\]*`) + parityTaskIDRe = regexp.MustCompile(`\b[0-9a-f]{12}\b`) + parityTaskCreatedRe = regexp.MustCompile(`"created":[0-9]+`) +) + +func TestPariteTaches(t *testing.T) { + home := testHome(t) + oldNow := tasksNow + var now time.Time + tasksNow = func() time.Time { return now } + t.Cleanup(func() { tasksNow = oldNow }) + + // Les identifiants tirés au hasard deviennent {{ID1}}, {{ID2}}… dans + // l'ordre où ils apparaissent. + ids := map[string]string{} + var order []string + mask := func(s string) string { + s = parityTaskIDRe.ReplaceAllStringFunc(s, func(id string) string { + if _, ok := ids[id]; !ok { + order = append(order, id) + ids[id] = "{{ID" + strconv.Itoa(len(order)) + "}}" + } + return ids[id] + }) + s = strings.ReplaceAll(s, home, "{{HOME}}") + // La date du projet par défaut, posée à la lecture (heure réelle). + s = parityTaskCreatedRe.ReplaceAllString(s, `"created":"{{T}}"`) + return parityTaskDurRe.ReplaceAllString(s, "exit: $1 · {{DUR}}") + } + unmask := func(s string) string { + for id, m := range ids { + s = strings.ReplaceAll(s, m, id) + } + return strings.ReplaceAll(s, "{{HOME}}", home) + } + handlers := map[string]http.HandlerFunc{ + "/api/tasks": handleTasks, + "/api/tasks/save": handleTaskSave, + "/api/tasks/delete": handleTaskDelete, + "/api/tasks/toggle": handleTaskToggle, + "/api/tasks/pause": handleTasksPause, + "/api/tasks/run": handleTaskRun, + "/api/tasks/stop": handleTaskStop, + } + call := func(method, path, body string) (int, any) { + rec := httptest.NewRecorder() + handlers[path](rec, httptest.NewRequest(method, path, strings.NewReader(body))) + var out any + if err := json.Unmarshal([]byte(mask(rec.Body.String())), &out); err != nil { + t.Fatalf("%s : corps illisible %q", path, rec.Body.String()) + } + return rec.Code, out + } + snapshot := func() any { + b, _ := json.Marshal(listTasks()) + var v any + _ = json.Unmarshal([]byte(mask(string(b))), &v) + return v + } + lastRun := func(id string) int64 { + tk, _ := getTask(id) + return tk.LastRun + } + + var steps []parityTasksStep + run := func(st parityTasksStep) { + if st.Now != "" { + n, err := time.Parse(time.RFC3339, st.Now) + if err != nil { + t.Fatal(err) + } + now = n + } + files := map[string]string{} + for k, v := range st.Files { + files[k] = unmask(v) + } + parityApplyFiles(t, home, files) + for k, v := range st.Config { + if err := SetConfigKey(k, v); err != nil { + t.Fatal(err) + } + } + for k, v := range st.State { + if err := putStr(bkState, k, v); err != nil { + t.Fatal(err) + } + } + if st.PutTask != "" { + var tk Task + if err := json.Unmarshal([]byte(st.PutTask), &tk); err != nil { + t.Fatal(err) + } + if err := putBytes(bkTasks, tk.ID, []byte(st.PutTask)); err != nil { + t.Fatal(err) + } + } + if st.Generating { + conv.mu.Lock() + conv.Generating = true + conv.mu.Unlock() + } + var lock *tuneLock + if st.Tune { + l, err := tuneLockAcquire("web", false) + if err != nil { + t.Fatal(err) + } + lock = l + } + if st.Tick { + tickTasks(tasksNow()) + } + var before int64 + if st.WaitDone != "" { + before = lastRun(unmask(st.WaitDone)) + } + if st.Path != "" { + st.Status, st.Out = call(st.Method, st.Path, unmask(st.Body)) + } + if lock != nil { + lock.release() + } + if st.Generating { + conv.mu.Lock() + conv.Generating = false + conv.mu.Unlock() + } + if st.WaitRunning != "" { + deadline := time.Now().Add(10 * time.Second) + for { + _, out := call("GET", "/api/tasks", "") + if out.(map[string]any)["running_id"] == st.WaitRunning { + break + } + if time.Now().After(deadline) { + t.Fatalf("%s : la tâche ne tourne pas", st.Name) + } + time.Sleep(10 * time.Millisecond) + } + } + if st.WaitDone != "" { + id := unmask(st.WaitDone) + deadline := time.Now().Add(20 * time.Second) + for lastRun(id) == before { + if time.Now().After(deadline) { + t.Fatalf("%s : le passage ne finit pas", st.Name) + } + time.Sleep(10 * time.Millisecond) + } + // Le registre des scripts est vidé juste après l'écriture. + for { + if sid, _ := scriptRunningAny(); sid == "" { + break + } + time.Sleep(5 * time.Millisecond) + } + } + st.Tasks = snapshot() + steps = append(steps, st) + } + + save := func(name, extra string) string { + return `{"name":"` + name + `","prompt":"Fais le point.","enabled":true` + extra + `}` + } + for _, st := range []parityTasksStep{ + {Name: "liste vide", Now: "2026-03-28T12:00:00Z", Files: map[string]string{ + "scripts/ok.sh": "#!/bin/sh\necho bonjour\n", + "scripts/echec.sh": "#!/bin/sh\necho oups >&2\nexit 2\n", + "scripts/lent.sh": "#!/bin/sh\nsleep 30\n", + "scripts/sous/rang.sh": "#!/bin/sh\necho rangé\n", + }, Method: "GET", Path: "/api/tasks"}, + {Name: "agent actif, interrupteur", State: map[string]string{"agent": "1"}, Method: "GET", Path: "/api/tasks"}, + {Name: "mémoire coupée, internet armé", Config: map[string]string{"MEM_MODE": "off"}, State: map[string]string{"internet": "1"}, Method: "GET", Path: "/api/tasks"}, + + // Enregistrement : erreurs. + {Name: "corps illisible", Method: "POST", Path: "/api/tasks/save", Body: `{"enabled":"oui"}`}, + {Name: "sans nom", Method: "POST", Path: "/api/tasks/save", Body: `{"name":" ","prompt":"x","schedule":"@every 1h"}`}, + {Name: "sans consigne", Method: "POST", Path: "/api/tasks/save", Body: `{"name":"x","schedule":"@every 1h"}`}, + {Name: "script sans nom de script", Method: "POST", Path: "/api/tasks/save", Body: `{"name":"x","kind":"script","schedule":"@every 1h"}`}, + {Name: "script absent", Method: "POST", Path: "/api/tasks/save", Body: `{"name":"x","kind":"script","script":"absent.sh","schedule":"@every 1h"}`}, + {Name: "script : dossier", Method: "POST", Path: "/api/tasks/save", Body: `{"name":"x","kind":"script","script":"sous","schedule":"@every 1h"}`}, + {Name: "script : remontée", Method: "POST", Path: "/api/tasks/save", Body: `{"name":"x","kind":"script","script":"../x.sh","schedule":"@every 1h"}`}, + {Name: "script : chemin absolu", Method: "POST", Path: "/api/tasks/save", Body: `{"name":"x","kind":"script","script":"/etc/x","schedule":"@every 1h"}`}, + {Name: "script : séparateur Windows", Method: "POST", Path: "/api/tasks/save", Body: `{"name":"x","kind":"script","script":"\\x.sh","schedule":"@every 1h"}`}, + {Name: "script : caractère interdit", Method: "POST", Path: "/api/tasks/save", Body: `{"name":"x","kind":"script","script":"a;b","schedule":"@every 1h"}`}, + {Name: "fréquence vide", Method: "POST", Path: "/api/tasks/save", Body: save("x", ``)}, + {Name: "intervalle trop court", Method: "POST", Path: "/api/tasks/save", Body: save("x", `,"schedule":"@every 30s"`)}, + {Name: "intervalle illisible", Method: "POST", Path: "/api/tasks/save", Body: save("x", `,"schedule":"@every deux heures"`)}, + {Name: "intervalle sans unité", Method: "POST", Path: "/api/tasks/save", Body: save("x", `,"schedule":"@every 5"`)}, + {Name: "jours invalides", Method: "POST", Path: "/api/tasks/save", Body: save("x", `,"schedule":"@every 0d"`)}, + {Name: "heure hors bornes", Method: "POST", Path: "/api/tasks/save", Body: save("x", `,"schedule":"@every 1d@24:00"`)}, + {Name: "heure sans deux-points", Method: "POST", Path: "/api/tasks/save", Body: save("x", `,"schedule":"@every 1d@9h"`)}, + {Name: "cron à 4 champs", Method: "POST", Path: "/api/tasks/save", Body: save("x", `,"schedule":"0 9 * *"`)}, + {Name: "cron : minute hors bornes", Method: "POST", Path: "/api/tasks/save", Body: save("x", `,"schedule":"61 9 * * *"`)}, + {Name: "cron : jour en lettres", Method: "POST", Path: "/api/tasks/save", Body: save("x", `,"schedule":"0 9 * * sun"`)}, + {Name: "cron : plage inversée", Method: "POST", Path: "/api/tasks/save", Body: save("x", `,"schedule":"0 9 5-1 * *"`)}, + {Name: "cron : pas nul", Method: "POST", Path: "/api/tasks/save", Body: save("x", `,"schedule":"*/0 * * * *"`)}, + {Name: "cron jamais satisfait", Method: "POST", Path: "/api/tasks/save", Body: save("x", `,"schedule":"0 0 30 2 *"`)}, + {Name: "une fois, date illisible", Method: "POST", Path: "/api/tasks/save", Body: save("x", `,"schedule":"@once 2026-04-31 10:00"`)}, + {Name: "une fois, déjà passé", Method: "POST", Path: "/api/tasks/save", Body: save("x", `,"schedule":"@once 2026-03-28 13:00","tz":"Europe/Paris"`)}, + + // Enregistrement : chaque forme. + {Name: "toutes les 2 h", Method: "POST", Path: "/api/tasks/save", Body: save("B toutes les 2 h", `,"schedule":"@every 2h"`)}, + {Name: "toutes les 1 h 30", Method: "POST", Path: "/api/tasks/save", Body: save("C une heure et demie", `,"schedule":"@EVERY 1.5h","enabled":false`)}, + {Name: "tous les jours à 23:00, Paris", Method: "POST", Path: "/api/tasks/save", Body: save("D chaque soir", `,"schedule":"@every 1d@23:00","tz":"Europe/Paris"`)}, + {Name: "tous les 3 jours à 9:30, New York", Method: "POST", Path: "/api/tasks/save", Body: save("E tous les trois jours", `,"schedule":"@every 3d@9:30","tz":"America/New_York"`)}, + {Name: "en semaine à 9 h, Calcutta", Method: "POST", Path: "/api/tasks/save", Body: save("F semaine", `,"schedule":"0 9 * * 1-5","tz":"Asia/Kolkata"`)}, + {Name: "chaque nuit à 2:30, Paris (heure qui saute)", Method: "POST", Path: "/api/tasks/save", Body: save("G nuit", `,"schedule":"30 2 * * *","tz":"Europe/Paris"`)}, + {Name: "toutes les 15 min, fuseau inconnu", Method: "POST", Path: "/api/tasks/save", Body: save("H quart d'heure", `,"schedule":"*/15 * * * *","tz":"Nulle/Part"`)}, + {Name: "le dimanche (7)", Method: "POST", Path: "/api/tasks/save", Body: save("I dimanche", `,"schedule":"0 8 * * 7"`)}, + {Name: "une seule fois", Method: "POST", Path: "/api/tasks/save", Body: save("J une fois", `,"schedule":"@once 2026-03-29 03:30","tz":"Europe/Paris"`)}, + {Name: "tâche script", Method: "POST", Path: "/api/tasks/save", Body: `{"name":"A script","kind":"script","script":" ok.sh ","schedule":"@every 1h","enabled":true}`}, + {Name: "tâche script en échec", Method: "POST", Path: "/api/tasks/save", Body: `{"name":"K échec","kind":"script","script":"echec.sh","schedule":"@every 1h","enabled":true}`}, + {Name: "tâche script lente", Method: "POST", Path: "/api/tasks/save", Body: `{"name":"L lente","kind":"script","script":"lent.sh","schedule":"@every 1h"}`}, + {Name: "script une seule fois", Method: "POST", Path: "/api/tasks/save", + Body: `{"name":"N script une fois","kind":"script","script":"ok.sh","schedule":"@once 2026-03-29 03:30","tz":"Europe/Paris","enabled":true}`}, + {Name: "tâche avec id inconnu", Method: "POST", Path: "/api/tasks/save", Body: `{"id":"zz","name":"M imposée","prompt":"x","schedule":"@every 1h","project":"inconnu","no_mem":true,"no_web":true}`}, + {Name: "liste", Method: "GET", Path: "/api/tasks"}, + + // Modification : la fréquence inchangée garde le prochain passage. + {Name: "renommer sans changer la fréquence", Now: "2026-03-28T12:30:00Z", Method: "POST", Path: "/api/tasks/save", + Body: `{"id":"{{ID1}}","name":"B renommée","prompt":"Autre.","schedule":"@every 2h","enabled":true}`}, + {Name: "changer la fréquence", Method: "POST", Path: "/api/tasks/save", + Body: `{"id":"{{ID1}}","name":"B renommée","prompt":"Autre.","schedule":"@every 3h","enabled":true}`}, + + // Bascule, interrupteur maître, suppression. + {Name: "bascule d'une tâche absente", Method: "POST", Path: "/api/tasks/toggle", Body: `{"id":"absente","on":true}`}, + {Name: "réactiver", Now: "2026-03-28T13:00:00Z", Method: "POST", Path: "/api/tasks/toggle", Body: `{"id":"{{ID2}}","on":true}`}, + {Name: "désactiver", Method: "POST", Path: "/api/tasks/toggle", Body: `{"id":"{{ID2}}","on":false}`}, + {Name: "suspendre tout", Method: "POST", Path: "/api/tasks/pause", Body: `{"on":true}`}, + {Name: "liste suspendue", Method: "GET", Path: "/api/tasks"}, + {Name: "tic suspendu", Now: "2026-03-28T23:00:00Z", Tick: true}, + {Name: "reprendre", Method: "POST", Path: "/api/tasks/pause", Body: `{"on":false}`}, + {Name: "interrupteur rangé à 0 : pas suspendu", State: map[string]string{"tasks_paused": "0"}, Method: "GET", Path: "/api/tasks"}, + {Name: "suppression sans id", Method: "POST", Path: "/api/tasks/delete", Body: `{}`}, + {Name: "suppression", Method: "POST", Path: "/api/tasks/delete", Body: `{"id":"zz"}`}, + + // Lancements. + {Name: "lancer une tâche absente", Method: "POST", Path: "/api/tasks/run", Body: `{"id":"absente"}`}, + {Name: "lancer une tâche IA, moteur occupé", Generating: true, Method: "POST", Path: "/api/tasks/run", Body: `{"id":"{{ID1}}"}`}, + {Name: "lancer une tâche IA, modèle pas prêt", Method: "POST", Path: "/api/tasks/run", Body: `{"id":"{{ID1}}"}`}, + {Name: "lancer le script", Method: "POST", Path: "/api/tasks/run", Body: `{"id":"{{ID10}}"}`, WaitDone: "{{ID10}}"}, + {Name: "lancer le script en échec", Method: "POST", Path: "/api/tasks/run", Body: `{"id":"{{ID11}}"}`, WaitDone: "{{ID11}}"}, + {Name: "lancer le script lent", Method: "POST", Path: "/api/tasks/run", Body: `{"id":"{{ID12}}"}`, WaitRunning: "{{ID12}}"}, + {Name: "liste pendant le script", Method: "GET", Path: "/api/tasks"}, + {Name: "arrêter le script", Method: "POST", Path: "/api/tasks/stop", Body: `{"id":"{{ID12}}"}`, WaitDone: "{{ID12}}"}, + {Name: "arrêter sans tâche script", Method: "POST", Path: "/api/tasks/stop", Body: `{}`}, + {Name: "le script qui réussissait échoue", Now: "2026-03-28T23:05:00Z", Files: map[string]string{"scripts/ok.sh": "#!/bin/sh\necho perdu\nexit 3\n"}, + Method: "POST", Path: "/api/tasks/run", Body: `{"id":"{{ID10}}"}`, WaitDone: "{{ID10}}"}, + {Name: "tâche script sans script", Now: "2026-03-28T23:01:00Z", PutTask: `{"id":"vide","name":"S vide","kind":"script","schedule":"@every 1h"}`, + Method: "POST", Path: "/api/tasks/run", Body: `{"id":"vide"}`, WaitDone: "vide"}, + {Name: "tâche script au nom refusé", PutTask: `{"id":"remonte","name":"S remonte","kind":"script","script":"../x.sh","schedule":"@every 1h"}`, + Method: "POST", Path: "/api/tasks/run", Body: `{"id":"remonte"}`, WaitDone: "remonte"}, + {Name: "tâche script au script disparu", PutTask: `{"id":"disparu","name":"S disparu","kind":"script","script":"parti.sh","schedule":"@every 1h"}`, + Method: "POST", Path: "/api/tasks/run", Body: `{"id":"disparu"}`, WaitDone: "disparu"}, + {Name: "puis réussit de nouveau", Now: "2026-03-28T23:10:00Z", Files: map[string]string{"scripts/ok.sh": "#!/bin/sh\necho bonjour\n"}, + Method: "POST", Path: "/api/tasks/run", Body: `{"id":"{{ID10}}"}`, WaitDone: "{{ID10}}"}, + + // Planificateur : les tâches IA (jamais exécutées sans modèle, elles + // bloqueraient la file) d'abord désactivées. + {Name: "tic : une tâche IA due attend le modèle", Now: "2026-03-28T16:00:00Z", Tick: true}, + } { + run(st) + } + for i := 1; i <= 9; i++ { + run(parityTasksStep{Name: "désactiver la tâche IA " + strconv.Itoa(i), Method: "POST", Path: "/api/tasks/toggle", + Body: `{"id":"{{ID` + strconv.Itoa(i) + `}}","on":false}`}) + } + for _, st := range []parityTasksStep{ + {Name: "tic pendant une optimisation : rien ne part", Now: "2026-03-29T00:30:00Z", Tune: true, Tick: true}, + {Name: "tic : la première tâche due tourne", Now: "2026-03-29T00:30:00Z", Tick: true}, + {Name: "tic : la suivante", Tick: true}, + {Name: "tic : rien de dû", Tick: true}, + {Name: "tic : de nouveau dû", Now: "2026-03-29T01:31:00Z", Tick: true}, + {Name: "tic : la suivante, encore", Tick: true}, + {Name: "tic : la tâche une fois tourne puis s'éteint", Tick: true}, + {Name: "tic : plus rien", Tick: true}, + {Name: "tâche sans prochain passage", Now: "2026-03-29T02:00:00Z", + PutTask: `{"id":"manuelle","name":"O manuelle","kind":"script","script":"ok.sh","schedule":"0 4 * * *","tz":"UTC","enabled":true}`, Tick: true}, + {Name: "tâche à la fréquence devenue invalide", PutTask: `{"id":"cassee","name":"P cassée","kind":"script","script":"ok.sh","schedule":"@every 10s","enabled":true}`, Tick: true}, + {Name: "même nom, ids dans l'ordre", PutTask: `{"id":"r2","name":"R double","kind":"script","script":"ok.sh","schedule":"@every 1h"}`}, + {Name: "même nom, second", PutTask: `{"id":"r1","name":"R double","kind":"script","script":"ok.sh","schedule":"@every 1h"}`}, + {Name: "liste finale", Method: "GET", Path: "/api/tasks"}, + } { + run(st) + } + + got, err := parityJSON(map[string]any{"steps": steps}) + if err != nil { + t.Fatal(err) + } + if os.Getenv("LOKI_PARITY_UPDATE") == "1" { + if err := os.MkdirAll("../../rust/loki-rs/tests/parity_tasks", 0o755); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(parityTasksFile, got, 0o644); err != nil { + t.Fatal(err) + } + return + } + want, err := os.ReadFile(parityTasksFile) + if err != nil { + t.Fatalf("journal de référence absent (%v) : LOKI_PARITY_UPDATE=1 pour le créer", err) + } + if string(got) != string(want) { + t.Errorf("les tâches ne produisent plus le journal rangé dans %s.\n"+ + "Si c'est voulu : LOKI_PARITY_UPDATE=1 go test ./internal/loki -run TestPariteTaches,\n"+ + "puis cargo test --test tasks dit si le port Rust suit.\n--- obtenu ---\n%s", parityTasksFile, got) + } +} diff --git a/internal/loki/tasks.go b/internal/loki/tasks.go index efef5e9..37ba263 100644 --- a/internal/loki/tasks.go +++ b/internal/loki/tasks.go @@ -143,6 +143,10 @@ func tasksPaused() bool { return getBool(bkState, "tasks_paused") } func setTasksPaused(on bool) error { return putBool(bkState, "tasks_paused", on) } +// tasksNow : l'heure des tâches (planification, passages), remplaçable par +// les tests (journal de parité des tâches). +var tasksNow = time.Now + // computeNextRun calcule le prochain déclenchement d'une tâche à partir de son // Schedule (dans le fuseau tz), relatif à `from`. Renvoie 0 si le schedule est // invalide (la tâche ne se déclenchera alors jamais — l'UI refuse déjà un diff --git a/internal/loki/tasks_cron.go b/internal/loki/tasks_cron.go index 29f87d1..f7b1ba3 100644 --- a/internal/loki/tasks_cron.go +++ b/internal/loki/tasks_cron.go @@ -129,7 +129,7 @@ func cutSuffix(s, suffix string) (string, bool) { // validateSchedule vérifie qu'un schedule est acceptable, sans calculer de date. func validateSchedule(schedule, tz string) error { - _, err := nextAfter(schedule, tz, time.Now()) + _, err := nextAfter(schedule, tz, tasksNow()) return err } diff --git a/internal/loki/tasks_run.go b/internal/loki/tasks_run.go index 771f780..c4fcd1b 100644 --- a/internal/loki/tasks_run.go +++ b/internal/loki/tasks_run.go @@ -155,7 +155,7 @@ const reportMax = 4000 // runTask exécute une tâche et met à jour son état persisté (dernier passage, // prochain passage, succès/échec, compte-rendu). func runTask(t Task) { - start := time.Now() + start := tasksNow() // Tâche « script seul » : aucun modèle à charger, aucun token consommé — on // lance le script du dossier protégé et sa sortie devient le compte-rendu. @@ -273,7 +273,7 @@ func ensureTaskPreset(id string) error { // tâche juste avant d'écrire pour ne pas écraser une modification concurrente // (bascule ou édition depuis l'interface pendant l'exécution). func recordTaskEnd(id string, start time.Time, report string, err error) { - now := time.Now() + now := tasksNow() cur, ok := getTask(id) if !ok { return // supprimée entre-temps : rien à écrire diff --git a/internal/loki/tasks_sched.go b/internal/loki/tasks_sched.go index e4027ac..7f2b3dc 100644 --- a/internal/loki/tasks_sched.go +++ b/internal/loki/tasks_sched.go @@ -27,7 +27,7 @@ func taskSchedLoop() { t := time.NewTicker(time.Minute) defer t.Stop() for range t.C { - tickTasks(time.Now()) + tickTasks(tasksNow()) } } @@ -35,7 +35,7 @@ func taskSchedLoop() { // relativement à maintenant — sinon une tâche à NextRun=0 serait considérée « due » // en permanence, ou jamais planifiée selon le sens de la comparaison. func primeNextRuns() { - now := time.Now() + now := tasksNow() for _, t := range listTasks() { if t.Enabled && t.NextRun == 0 { t.NextRun = computeNextRun(t.Schedule, t.TZ, now) diff --git a/internal/loki/tasks_script.go b/internal/loki/tasks_script.go index 75aa311..347cd1b 100644 --- a/internal/loki/tasks_script.go +++ b/internal/loki/tasks_script.go @@ -77,7 +77,7 @@ func scriptRunStop(id string) bool { // runScriptTask exécute le script d'une tâche et enregistre son état (comme runTask // pour une tâche IA), mais sans aucune inférence. func runScriptTask(t Task) { - start := time.Now() + start := tasksNow() name := strings.TrimSpace(t.Script) if name == "" { diff --git a/internal/loki/web_internal.go b/internal/loki/web_internal.go index 6d71694..6d715de 100644 --- a/internal/loki/web_internal.go +++ b/internal/loki/web_internal.go @@ -129,6 +129,10 @@ func withInternalAPI(next http.Handler) http.Handler { handleInternalProjectSwitch(w, r) case "/internal/v1/project/drop-convs": handleInternalProjectDropConvs(w, r) + case "/internal/v1/tasks/run": + handleInternalTaskRun(w, r) + case "/internal/v1/push/send": + handleInternalPushSend(w, r) default: http.NotFound(w, r) } diff --git a/internal/loki/web_server.go b/internal/loki/web_server.go index ac2297b..fd2a623 100644 --- a/internal/loki/web_server.go +++ b/internal/loki/web_server.go @@ -151,8 +151,11 @@ func newWebMux() *http.ServeMux { // attend le déverrouillage (voir handleMemUnlock). Idempotent. resumeMemMigration() // Planificateur des tâches : une goroutine, un tic par minute, dans le process - // qui détient la conversation et le modèle (idempotent, cf. sync.Once). - StartTaskScheduler() + // qui détient la conversation et le modèle (idempotent, cf. sync.Once) — sauf + // quand la façade tient les tâches (facade_tasks.go) : c'est elle qui planifie. + if !facadeTasks() { + StartTaskScheduler() + } // Jeton de tentative du décodage spéculatif automatique : effacé dès que le // moteur répond, sans dépendre d'une page ouverte (voir backend_serve_spec.go). startSpecAttemptWatch() diff --git a/internal/loki/web_tasks.go b/internal/loki/web_tasks.go index 7049275..8399682 100644 --- a/internal/loki/web_tasks.go +++ b/internal/loki/web_tasks.go @@ -6,7 +6,6 @@ import ( "encoding/json" "net/http" "strings" - "time" ) // handleTasks : GET → liste des tâches + état de l'interrupteur maître + si le @@ -134,7 +133,7 @@ func handleTaskSave(w http.ResponseWriter, r *http.Request) { t.NoMem, t.NoWeb = req.NoMem, req.NoWeb // Recalcule NextRun si la fréquence a changé (ou à la création). if t.Schedule != req.Schedule || t.NextRun == 0 { - t.NextRun = computeNextRun(req.Schedule, req.TZ, time.Now()) + t.NextRun = computeNextRun(req.Schedule, req.TZ, tasksNow()) } t.Schedule = req.Schedule if err := saveTask(t); err != nil { @@ -176,7 +175,7 @@ func handleTaskToggle(w http.ResponseWriter, r *http.Request) { } t.Enabled = req.On if req.On { - t.NextRun = computeNextRun(t.Schedule, t.TZ, time.Now()) + t.NextRun = computeNextRun(t.Schedule, t.TZ, tasksNow()) } if err := saveTask(t); err != nil { sendJSON(w, 500, map[string]any{"ok": false, "error": err.Error()}) diff --git a/rust/loki-rs/src/api.rs b/rust/loki-rs/src/api.rs index 248cf77..f212fdf 100644 --- a/rust/loki-rs/src/api.rs +++ b/rust/loki-rs/src/api.rs @@ -62,6 +62,9 @@ pub struct Ctx { /// Strata (écran, réglages, installation) tenu par la façade /// (`LOKI_STRATA=rust`, avec les backends). pub strata: Option>, + /// Tâches planifiées (routes, planificateur, tâches script) tenues par la + /// façade (`LOKI_TASKS=rust`). + pub tasks: Option>, /// Jeton de l'API interne : une requête qui le porte vient du Go. pub internal_token: String, } @@ -86,6 +89,7 @@ impl Ctx { || self.backends.is_some() || self.engineimg.is_some() || self.strata.is_some() + || self.tasks.is_some() } /// La requête vient du Go (jeton interne valide) : déjà authentifiée par diff --git a/rust/loki-rs/src/lib.rs b/rust/loki-rs/src/lib.rs index 0c25aad..fa08264 100644 --- a/rust/loki-rs/src/lib.rs +++ b/rust/loki-rs/src/lib.rs @@ -44,6 +44,8 @@ pub mod strata; pub mod sysinfo; pub mod system; pub mod sysupdate; +pub mod tasks; pub mod tools; +pub mod tz; pub mod ui; pub mod update; diff --git a/rust/loki-rs/src/main.rs b/rust/loki-rs/src/main.rs index b9cfb0e..102e89d 100644 --- a/rust/loki-rs/src/main.rs +++ b/rust/loki-rs/src/main.rs @@ -180,6 +180,9 @@ fn serve() -> ExitCode { if held_by_facade("LOKI_STRATA") && !backends_rust { eprintln!("[loki-rs] Strata tenu par le Go (LOKI_BACKENDS=rust requis)"); } + // LOKI_TASKS=rust : les tâches planifiées (routes, planificateur, + // tâches script) ; une tâche IA reste exécutée par le Go. + let tasks_rust = held_by_facade("LOKI_TASKS"); let engine_rust = held_by_facade("LOKI_ENGINE") && match loki_rs::enginectl::usable( std::env::var("LOKI_CONTAINER").ok().as_deref(), @@ -214,6 +217,7 @@ fn serve() -> ExitCode { ("LOKI_BACKENDS", backends_rust), ("LOKI_ENGINE_IMAGE", engineimg_rust), ("LOKI_STRATA", strata_rust), + ("LOKI_TASKS", tasks_rust), ]; for (var, on) in held { if on { @@ -345,6 +349,8 @@ fn serve() -> ExitCode { loki_rs::backends::probe::Probe::system(), )) }); + let tasks = tasks_rust + .then(|| std::sync::Arc::new(loki_rs::tasks::Tasks::new(engine_paths.home.clone()))); let cfg = proxy::Config { backend, internal_token: token, @@ -366,6 +372,7 @@ fn serve() -> ExitCode { backends, engineimg, strata, + tasks, mcp: mcp_rust.then(|| std::sync::Arc::new(loki_rs::mcp::Mcp::new(mem_home))), }; let res = proxy::serve(listener, cfg, shutdown_signal()).await; diff --git a/rust/loki-rs/src/memory.rs b/rust/loki-rs/src/memory.rs index 287617b..ca85f87 100644 --- a/rust/loki-rs/src/memory.rs +++ b/rust/loki-rs/src/memory.rs @@ -813,7 +813,7 @@ impl Memory { // --- mode mémoire --------------------------------------------------- /// `memMode` : mode du projet actif, repli sur `MEM_MODE`. - async fn mem_mode(store: &Store) -> &'static str { + pub(crate) async fn mem_mode(store: &Store) -> &'static str { let active = active_project(store).await; let projects = list_projects(store).await; if let Some(p) = projects.iter().find(|p| p.slug == active) diff --git a/rust/loki-rs/src/projects_api.rs b/rust/loki-rs/src/projects_api.rs index dee6559..71e8186 100644 --- a/rust/loki-rs/src/projects_api.rs +++ b/rust/loki-rs/src/projects_api.rs @@ -263,6 +263,14 @@ async fn delete_project(ctx: &Ctx, slug: &str) -> Result<(), Fail> { /// `projectDTOs` : le registre et ses comptes, plus le projet actif. async fn listing(ctx: &Ctx) -> Value { + let active = active_project(&ctx.store).await; + let projects = project_dtos(ctx).await; + json!({ "ok": true, "projects": projects, "active": active }) +} + +/// `projectDTOs` : les projets avec leurs compteurs (discussions, pages, +/// trackers) et le drapeau du projet actif. +pub(crate) async fn project_dtos(ctx: &Ctx) -> Vec { let store = &ctx.store; let active = active_project(store).await; let mut convs: std::collections::HashMap = std::collections::HashMap::new(); @@ -311,7 +319,7 @@ async fn listing(ctx: &Ctx) -> Value { d }) .collect(); - json!({ "ok": true, "projects": projects, "active": active }) + projects } /// Les routes servies par ce module. diff --git a/rust/loki-rs/src/proxy.rs b/rust/loki-rs/src/proxy.rs index 04fc2b9..17ca653 100644 --- a/rust/loki-rs/src/proxy.rs +++ b/rust/loki-rs/src/proxy.rs @@ -103,6 +103,20 @@ pub struct Config { /// Strata (machine, réglages, installation) tenu par la façade /// (`LOKI_STRATA=rust`) ; `None` : au Go. pub strata: Option>, + /// Tâches planifiées (routes, planificateur, tâches script) tenues par la + /// façade (`LOKI_TASKS=rust`) ; `None` : au Go. + pub tasks: Option>, +} + +/// Arrête la tâche de fond tenue à sa sortie de portée. +struct AbortOnDrop(Option>); + +impl Drop for AbortOnDrop { + fn drop(&mut self) { + if let Some(h) = &self.0 { + h.abort(); + } + } } struct Proxy { @@ -145,9 +159,18 @@ pub async fn serve( backends: cfg.backends, engineimg: cfg.engineimg, strata: cfg.strata, + tasks: cfg.tasks, internal_token: cfg.internal_token.clone(), }, }); + // Le planificateur des tâches tenues ici, arrêté avec la façade. + let _sched = AbortOnDrop( + proxy + .ctx + .tasks + .clone() + .map(|t| tokio::spawn(crate::tasks::sched_loop(proxy.ctx.clone(), t))), + ); tokio::pin!(shutdown); loop { tokio::select! { @@ -259,6 +282,11 @@ impl Proxy { { return crate::strata::api::serve(&self.ctx, s, req, from_go).await; } + if let Some(t) = &self.ctx.tasks + && crate::tasks::handles(path) + { + return crate::tasks::serve(&self.ctx, t, req, from_go).await; + } if let Some(b) = &self.ctx.backends && crate::backends::handles(path) { diff --git a/rust/loki-rs/src/store.rs b/rust/loki-rs/src/store.rs index a896ddd..8d74dc3 100644 --- a/rust/loki-rs/src/store.rs +++ b/rust/loki-rs/src/store.rs @@ -18,6 +18,12 @@ pub const BK_CONFIG: &str = "config"; pub const BK_PREFS: &str = "prefs"; pub const BK_STATE: &str = "state"; +/// Délai d'un appel à l'API interne (base, chiffrement, petites actions). +const CALL_LIMIT: Duration = Duration::from_secs(10); +/// Délai d'un outil exécuté par le Go : le plus long des siens (navigateur, +/// pages web, MCP) reste bien en deçà. +const TOOL_LIMIT: Duration = Duration::from_secs(30 * 60); + #[derive(Clone)] pub struct Store { client: Client>, @@ -39,6 +45,18 @@ impl Store { method: Method, path: &str, body: Vec, + ) -> Result<(StatusCode, Bytes), String> { + self.call_within(method, path, body, CALL_LIMIT).await + } + + /// Comme `call`, avec le délai donné (un appel qui dure : un outil, une + /// tâche IA exécutés par le Go). + async fn call_within( + &self, + method: Method, + path: &str, + body: Vec, + limit: Duration, ) -> Result<(StatusCode, Bytes), String> { let req = Request::builder() .method(method) @@ -57,7 +75,7 @@ impl Store { .to_bytes(); Ok((status, body)) }; - tokio::time::timeout(Duration::from_secs(10), fut) + tokio::time::timeout(limit, fut) .await .map_err(|_| "store : délai dépassé".to_string())? } @@ -104,6 +122,26 @@ impl Store { Ok((status, v)) } + /// POST JSON à l'API interne, sans autre délai que `limit` : un appel qui + /// dure (une tâche IA exécutée par le Go). + pub async fn internal_post_within( + &self, + path: &str, + body: &serde_json::Value, + limit: Duration, + ) -> Result<(StatusCode, serde_json::Value), String> { + let (status, bytes) = self + .call_within( + Method::POST, + path, + serde_json::to_vec(body).unwrap_or_default(), + limit, + ) + .await?; + let v = serde_json::from_slice(&bytes).unwrap_or(serde_json::Value::Null); + Ok((status, v)) + } + /// Scelle un blob avec la clé de la mémoire (gardée par le Go). Clair /// rendu tel quel si le chiffrement est coupé. pub async fn seal(&self, plain: Vec) -> Result, CryptError> { @@ -161,12 +199,25 @@ impl Store { args: &serde_json::Value, caps: &serde_json::Value, ) -> Result { - self.internal_json( - Method::POST, - "/internal/v1/tool/exec", - Some(&serde_json::json!({ "name": name, "args": args, "caps": caps })), - ) - .await + let body = serde_json::json!({ "name": name, "args": args, "caps": caps }); + let (status, bytes) = self + .call_within( + Method::POST, + "/internal/v1/tool/exec", + serde_json::to_vec(&body).unwrap_or_default(), + TOOL_LIMIT, + ) + .await?; + if !status.is_success() { + return Err(format!( + "/internal/v1/tool/exec : {status} {}", + String::from_utf8_lossy(&bytes) + )); + } + if bytes.is_empty() { + return Ok(serde_json::Value::Null); + } + serde_json::from_slice(&bytes).map_err(|e| e.to_string()) } /// Archive un bloc verbatim pour l'outil recall (`archiveRecallBlock`, diff --git a/rust/loki-rs/src/system.rs b/rust/loki-rs/src/system.rs index 59b1b1a..ba39da7 100644 --- a/rust/loki-rs/src/system.rs +++ b/rust/loki-rs/src/system.rs @@ -84,7 +84,7 @@ impl System { /// `crawlReachable` : le serveur Crawl4AI répond-il (moins de 500 sur /// `/health` ou `/`, deux secondes en tout) ? Le moteur intégré n'a rien à /// joindre. - async fn reachable(&self, cfg: &Cfg, key: &str) -> bool { + pub(crate) async fn reachable(&self, cfg: &Cfg, key: &str) -> bool { if web_engine(cfg) == ENGINE_GO { return true; } diff --git a/rust/loki-rs/src/tasks/cron.rs b/rust/loki-rs/src/tasks/cron.rs new file mode 100644 index 0000000..c315fa8 --- /dev/null +++ b/rust/loki-rs/src/tasks/cron.rs @@ -0,0 +1,693 @@ +//! Planification des tâches, résolution à la minute — port de +//! `tasks_cron.go`. Quatre formes : +//! +//! - « @every 30m », « @every 2h » : intervalle glissant (`time.ParseDuration` +//! du Go, borné à ≥ 1 minute) ; +//! - « @every 1d@23:00 » : tous les N jours, ancré à une heure murale ; +//! - « @once 2026-10-01 16:54 » : une seule fois ; +//! - expression cron 5 champs (*, a, a-b, a,b, */n, a-b/n). +//! +//! Les instants sont des nanosecondes Unix sur 128 bits (une date « @once » +//! peut dépasser 2262, que le Go accepte) ; l'heure murale est celle du +//! fuseau de l'utilisateur ([`crate::tz`], la zoneinfo du système comme le +//! Go). + +use std::collections::BTreeSet; + +use crate::gojson::quote; +use crate::tz::{Civil, Location}; + +const NS: i64 = 1_000_000_000; +const MINUTE: i64 = 60 * NS; +const HOUR: i64 = 60 * MINUTE; +const NS128: i128 = NS as i128; + +/// `loc` : fuseau IANA, vide ou introuvable → heure locale du serveur. +pub fn loc(tz: &str) -> Location { + loc_or(tz, Location::local) +} + +/// `loc`, l'heure locale donnée. +fn loc_or(tz: &str, local: fn() -> Location) -> Location { + if tz.is_empty() { + return local(); + } + Location::load(tz).unwrap_or_else(|_| local()) +} + +fn civil_of(l: &Location, ns: i128) -> Civil { + l.civil(ns.div_euclid(NS128) as i64) +} + +/// `cutPrefix` : préfixe insensible à la casse. +pub fn cut_prefix<'a>(s: &'a str, prefix: &str) -> Option<&'a str> { + if s.len() >= prefix.len() + && s.is_char_boundary(prefix.len()) + && s[..prefix.len()].eq_ignore_ascii_case(prefix) + { + Some(&s[prefix.len()..]) + } else { + None + } +} + +/// `isOnce`. +pub fn is_once(schedule: &str) -> bool { + cut_prefix(schedule.trim(), "@once").is_some() +} + +/// `nextAfter` : le prochain déclenchement (ns), strictement après `from` +/// (ns). +pub fn next_after(schedule: &str, tz: &str, from: i64) -> Result { + let from = i128::from(from); + let s = schedule.trim(); + if s.is_empty() { + return Err("fréquence vide".into()); + } + if let Some(rest) = cut_prefix(s, "@once") { + let l = loc(tz); + let Some(at) = parse_ymd_hm(rest.trim(), &l) else { + return Err("date invalide (attendu « @once AAAA-MM-JJ HH:MM »)".into()); + }; + if at <= from { + return Err(format!( + "{} est déjà passé (il est {})", + civil_of(&l, at).ymd_hm(), + civil_of(&l, from).ymd_hm() + )); + } + return Ok(at); + } + if let Some(rest) = cut_prefix(s, "@every") { + return next_every(rest.trim(), tz, from); + } + next_cron(s, tz, from) +} + +/// `time.ParseInLocation("2006-01-02 15:04", s, l)` : l'instant (ns), ou +/// rien si la chaîne ne suit pas la forme. +fn parse_ymd_hm(s: &str, l: &Location) -> Option { + let b = s.as_bytes(); + let digits = |r: &[u8]| r.iter().all(u8::is_ascii_digit); + // 2006 : 4 chiffres ; -01 et -02 : 2 chiffres ; 15 : 1 ou 2 ; :04 : 2. + if b.len() < 4 + 1 + 2 + 1 + 2 + 1 + 1 + 1 + 2 || !digits(&b[..4]) { + return None; + } + let year: i64 = s[..4].parse().ok()?; + let rest = s[4..].strip_prefix('-')?; + if rest.len() < 2 || !digits(&rest.as_bytes()[..2]) { + return None; + } + let month: i64 = rest[..2].parse().ok()?; + let rest = rest[2..].strip_prefix('-')?; + if rest.len() < 2 || !digits(&rest.as_bytes()[..2]) { + return None; + } + let day: i64 = rest[..2].parse().ok()?; + let rest = rest[2..].strip_prefix(' ')?; + let hl = rest.bytes().take(2).take_while(u8::is_ascii_digit).count(); + if hl == 0 { + return None; + } + let hour: i64 = rest[..hl].parse().ok()?; + let rest = rest[hl..].strip_prefix(':')?; + if rest.len() != 2 || !digits(rest.as_bytes()) { + return None; + } + let minute: i64 = rest.parse().ok()?; + if !(1..=12).contains(&month) || hour > 23 || minute > 59 { + return None; + } + let dim = days_in(month, year); + if day < 1 || day > dim { + return None; + } + Some(i128::from(l.date(year, month, day, hour, minute, 0)) * NS128) +} + +fn days_in(m: i64, y: i64) -> i64 { + match m { + 2 => { + if y % 4 == 0 && (y % 100 != 0 || y % 400 == 0) { + 29 + } else { + 28 + } + } + 4 | 6 | 9 | 11 => 30, + _ => 31, + } +} + +/// `nextEvery`. +fn next_every(spec: &str, tz: &str, from: i128) -> Result { + let (dur_part, time_part) = match spec.split_once('@') { + Some((d, t)) => (d.trim(), Some(t)), + None => (spec.trim(), None), + }; + if let Some(n) = dur_part.strip_suffix('d') { + let days = n + .parse::() + .ok() + .filter(|d| *d >= 1) + .ok_or_else(|| format!("nombre de jours invalide : {}", quote(dur_part)))?; + let Some(tp) = time_part else { + // time.Duration(days) * 24 * time.Hour : déborde comme le Go. + return Ok(from + i128::from(days.wrapping_mul(24).wrapping_mul(HOUR))); + }; + let (hh, mm) = parse_hhmm(tp)?; + let l = loc(tz); + let now = civil_of(&l, from); + let mut cand = l.date(now.year, now.month, now.day, hh, mm, 0); + // cand.AddDate(0, 0, days) : l'heure murale DU CANDIDAT, jour après + // jour (une heure qui saute au printemps reste décalée ensuite). + while i128::from(cand) * NS128 <= from { + let c = l.civil(cand); + cand = l.date(c.year, c.month, c.day + days, c.hour, c.minute, c.second); + } + return Ok(i128::from(cand) * NS128); + } + let d = parse_duration(dur_part) + .map_err(|e| format!("intervalle invalide (ex. « 2h », « 30m ») : {e}"))?; + if d < MINUTE { + return Err("intervalle minimum : 1 minute".into()); + } + Ok(from + i128::from(d)) +} + +/// `parseHHMM`. +fn parse_hhmm(s: &str) -> Result<(i64, i64), String> { + let Some((hs, ms)) = s.trim().split_once(':') else { + return Err(format!("heure invalide (attendu HH:MM) : {}", quote(s))); + }; + match (go_atoi(hs.trim()), go_atoi(ms.trim())) { + (Some(h), Some(m)) if (0..=23).contains(&h) && (0..=59).contains(&m) => Ok((h, m)), + _ => Err(format!("heure hors bornes : {}", quote(s))), + } +} + +/// `strconv.Atoi` (signe accepté). +fn go_atoi(s: &str) -> Option { + let digits = s.strip_prefix(['+', '-']).unwrap_or(s); + if digits.is_empty() || !digits.bytes().all(|c| c.is_ascii_digit()) { + return None; + } + s.parse().ok() +} + +/// `quote` du paquet time (messages de `ParseDuration`). +fn time_quote(s: &str) -> String { + let mut out = String::from("\""); + for c in s.chars() { + if (c as u32) >= 0x80 || c < ' ' { + let mut buf = [0u8; 4]; + for byte in c.encode_utf8(&mut buf).bytes() { + out.push_str(&format!("\\x{byte:02x}")); + } + } else { + if c == '"' || c == '\\' { + out.push('\\'); + } + out.push(c); + } + } + out.push('"'); + out +} + +/// `time.ParseDuration` du Go (nanosecondes). +pub fn parse_duration(orig: &str) -> Result { + let invalid = || format!("time: invalid duration {}", time_quote(orig)); + let mut s = orig.as_bytes(); + let mut neg = false; + if let Some(&c) = s.first() + && (c == b'-' || c == b'+') + { + neg = c == b'-'; + s = &s[1..]; + } + if s == b"0" { + return Ok(0); + } + if s.is_empty() { + return Err(invalid()); + } + let mut d: u64 = 0; + while !s.is_empty() { + if !(s[0] == b'.' || s[0].is_ascii_digit()) { + return Err(invalid()); + } + // [0-9]* + let pl = s.len(); + let mut v: u64 = 0; + let mut i = 0; + while i < s.len() && s[i].is_ascii_digit() { + if v > (1u64 << 63) / 10 { + return Err(invalid()); + } + v = v * 10 + (s[i] - b'0') as u64; + if v > 1u64 << 63 { + return Err(invalid()); + } + i += 1; + } + s = &s[i..]; + let pre = pl != s.len(); + // (\.[0-9]*)? + let mut post = false; + let (mut f, mut scale) = (0u64, 1f64); + if !s.is_empty() && s[0] == b'.' { + s = &s[1..]; + let pl = s.len(); + let mut overflow = false; + let mut i = 0; + while i < s.len() && s[i].is_ascii_digit() { + if !overflow { + if f > ((1u64 << 63) - 1) / 10 { + overflow = true; + } else { + let y = f * 10 + (s[i] - b'0') as u64; + if y > 1u64 << 63 { + overflow = true; + } else { + f = y; + scale *= 10.0; + } + } + } + i += 1; + } + s = &s[i..]; + post = pl != s.len(); + } + if !pre && !post { + return Err(invalid()); + } + // L'unité. + let mut i = 0; + while i < s.len() && !(s[i] == b'.' || s[i].is_ascii_digit()) { + i += 1; + } + if i == 0 { + return Err(format!( + "time: missing unit in duration {}", + time_quote(orig) + )); + } + let u = String::from_utf8_lossy(&s[..i]).into_owned(); + s = &s[i..]; + let unit: u64 = match u.as_str() { + "ns" => 1, + "us" | "\u{b5}s" | "\u{3bc}s" => 1_000, + "ms" => 1_000_000, + "s" => NS as u64, + "m" => MINUTE as u64, + "h" => HOUR as u64, + _ => { + return Err(format!( + "time: unknown unit {} in duration {}", + time_quote(&u), + time_quote(orig) + )); + } + }; + if v > (1u64 << 63) / unit { + return Err(invalid()); + } + v *= unit; + if f > 0 { + v += (f as f64 * (unit as f64 / scale)) as u64; + if v > 1u64 << 63 { + return Err(invalid()); + } + } + d += v; + if d > 1u64 << 63 { + return Err(invalid()); + } + } + if neg { + return Ok((d as i128).wrapping_neg() as i64); + } + if d > (1u64 << 63) - 1 { + return Err(invalid()); + } + Ok(d as i64) +} + +/// `cronSpec`. +pub struct CronSpec { + min: BTreeSet, + hour: BTreeSet, + dom: BTreeSet, + month: BTreeSet, + dow: BTreeSet, +} + +impl CronSpec { + fn matches(&self, c: &Civil) -> bool { + self.min.contains(&c.minute) + && self.hour.contains(&c.hour) + && self.dom.contains(&c.day) + && self.month.contains(&c.month) + && self.dow.contains(&c.weekday) + } +} + +/// `nextCron` : le prochain match à la minute, sur ~366 jours au plus. +fn next_cron(expr: &str, tz: &str, from: i128) -> Result { + let spec = parse_cron(expr)?; + let l = loc(tz); + let minute = i128::from(MINUTE); + // Truncate(time.Minute) se fait sur l'instant absolu. + let mut t = from.div_euclid(minute) * minute + minute; + let limit = t + 366 * 24 * i128::from(HOUR); + while t < limit { + if spec.matches(&civil_of(&l, t)) { + return Ok(t); + } + t += minute; + } + Err(format!("fréquence jamais satisfaite : {}", quote(expr))) +} + +/// `parseCron`. +pub fn parse_cron(expr: &str) -> Result { + let f: Vec<&str> = expr.split_whitespace().collect(); + if f.len() != 5 { + return Err(format!( + "cron : 5 champs attendus « min heure jour mois jour-semaine », reçu {}", + f.len() + )); + } + let field = |i: usize, lo, hi, name: &str| { + parse_field(f[i], lo, hi).map_err(|e| format!("champ {name} : {e}")) + }; + let min = field(0, 0, 59, "minute")?; + let hour = field(1, 0, 23, "heure")?; + let dom = field(2, 1, 31, "jour-du-mois")?; + let month = field(3, 1, 12, "mois")?; + let mut dow = field(4, 0, 7, "jour-semaine")?; + if dow.contains(&7) { + dow.insert(0); + } + Ok(CronSpec { + min, + hour, + dom, + month, + dow, + }) +} + +/// `parseField`. +fn parse_field(field: &str, lo: i64, hi: i64) -> Result, String> { + let mut out = BTreeSet::new(); + for part in field.split(',') { + let mut rng = part; + let mut step = 1; + if let Some((base, st)) = part.split_once('/') { + step = go_atoi(st) + .filter(|n| *n >= 1) + .ok_or_else(|| format!("pas invalide : {}", quote(part)))?; + rng = base; + } + let (mut start, mut end) = (lo, hi); + if rng != "*" { + if let Some((a, b)) = rng.split_once('-') { + start = atoi_range(a, lo, hi)?; + end = atoi_range(b, lo, hi)?; + } else { + let v = atoi_range(rng, lo, hi)?; + start = v; + end = v; + } + } + if start > end { + return Err(format!("plage inversée : {}", quote(part))); + } + let mut v = start; + while v <= end { + out.insert(v); + v += step; + } + } + if out.is_empty() { + return Err("champ vide".into()); + } + Ok(out) +} + +/// `atoiRange`. +fn atoi_range(s: &str, lo: i64, hi: i64) -> Result { + let v = go_atoi(s.trim()).ok_or_else(|| format!("valeur invalide : {}", quote(s)))?; + if v < lo || v > hi { + return Err(format!("valeur {v} hors bornes [{lo}-{hi}]")); + } + Ok(v) +} + +/// `computeNextRun` : le prochain déclenchement en ms Unix, 0 si la fréquence +/// est invalide. +pub fn compute_next_run(schedule: &str, tz: &str, from_ns: i64) -> i64 { + next_after(schedule, tz, from_ns).map_or(0, |t| t.div_euclid(1_000_000) as i64) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn durees_du_go() { + assert_eq!(parse_duration("2h"), Ok(2 * HOUR)); + assert_eq!(parse_duration("1h30m"), Ok(90 * MINUTE)); + assert_eq!(parse_duration("1.5h"), Ok(90 * MINUTE)); + assert_eq!(parse_duration(".5m"), Ok(30 * NS)); + assert_eq!(parse_duration("0"), Ok(0)); + assert_eq!(parse_duration("-2m"), Ok(-2 * MINUTE)); + assert_eq!(parse_duration("300ms"), Ok(300_000_000)); + assert_eq!(parse_duration("1µs"), Ok(1_000)); + assert_eq!( + parse_duration(""), + Err("time: invalid duration \"\"".into()) + ); + assert_eq!( + parse_duration("2"), + Err("time: missing unit in duration \"2\"".into()) + ); + assert_eq!( + parse_duration("2d"), + Err("time: unknown unit \"d\" in duration \"2d\"".into()) + ); + assert_eq!( + parse_duration("abc"), + Err("time: invalid duration \"abc\"".into()) + ); + assert_eq!( + parse_duration("é"), + Err("time: invalid duration \"\\xc3\\xa9\"".into()) + ); + assert_eq!( + parse_duration("9999999999h"), + Err("time: invalid duration \"9999999999h\"".into()) + ); + for big in ["18446744073709551619h", "9223372036854775808ns"] { + assert_eq!( + parse_duration(big), + Err(format!("time: invalid duration \"{big}\"")), + "{big}" + ); + } + assert_eq!(parse_duration("9223372036854775807ns"), Ok(i64::MAX)); + } + + #[test] + fn fuseau_de_la_tache() { + fn paris() -> Location { + Location::load("Europe/Paris").unwrap() + } + // Vide ou introuvable : l'heure locale ; connu : le sien. + assert_eq!(loc_or("", paris).offset_at(1_792_134_000), 7200); + assert_eq!(loc_or("Nulle/Part", paris).offset_at(1_792_134_000), 7200); + assert_eq!(loc_or("UTC", paris).offset_at(1_792_134_000), 0); + assert_eq!( + loc_or("America/New_York", paris).offset_at(1_792_134_000), + -4 * 3600 + ); + } + + /// Valeurs tirées du Go (`nextAfter`). + #[test] + fn bornes_du_calendrier_et_heure_murale() { + let u = Location::utc(); + let from = u.date(2026, 3, 28, 10, 0, 0) * NS; + let at = |s: &str, tz: &str, from: i64| next_after(s, tz, from).map(|n| (n / NS128) as i64); + let invalid = Err("date invalide (attendu « @once AAAA-MM-JJ HH:MM »)".to_string()); + assert_eq!(at("@once 2100-02-29 10:00", "UTC", from), invalid); + assert_eq!( + at("@once 2400-02-29 10:00", "UTC", from), + Ok(13_574_599_200) + ); + assert_eq!(at("@once 2028-02-29 10:00", "UTC", from), Ok(1_835_431_200)); + assert_eq!(at("@once 2026-09-31 10:00", "UTC", from), invalid); + assert_eq!(at("@once 2026-11-31 10:00", "UTC", from), invalid); + // À l'heure pile : le lendemain. + let pile = u.date(2026, 3, 28, 23, 0, 0) * NS; + assert_eq!(at("@every 1d@23:00", "UTC", pile), Ok(1_774_825_200)); + // Le changement d'heure de la nuit : 9 h murales, pas 24 h plus tard. + let paris = Location::load("Europe/Paris").unwrap(); + let matin = paris.date(2026, 3, 28, 10, 0, 0) * NS; + assert_eq!( + at("@every 1d@09:00", "Europe/Paris", matin), + Ok(1_774_767_600) + ); + // L'heure qui saute (2 h 30 → 3 h 30) : AddDate repart de 3 h 30. + let apres = paris.date(2026, 3, 29, 4, 0, 0) * NS; + assert_eq!( + at("@every 1d@02:30", "Europe/Paris", apres), + Ok(1_774_834_200) + ); + let avant = paris.date(2026, 3, 27, 4, 0, 0) * NS; + assert_eq!( + at("@every 2d@02:30", "Europe/Paris", avant), + Ok(1_774_747_800) + ); + // Débordements : la durée du Go, et des dates très lointaines. + assert_eq!( + compute_next_run("@every 200000d", "UTC", from), + 607_947_926_290 + ); + assert_eq!( + compute_next_run("@every 106751d", "UTC", from), + 10_997_978_400_000 + ); + assert_eq!( + compute_next_run("@once 9999-12-31 23:59", "UTC", from), + 253_402_300_740_000 + ); + assert_eq!( + compute_next_run("@every 99999999d@10:00", "UTC", from), + 8_641_774_605_600_000 + ); + // strconv.Atoi accepte un signe. + assert_eq!(at("@every 1d@+9:30", "UTC", from), Ok(1_774_776_600)); + assert_eq!(at("@every 1d@-0:30", "UTC", from), Ok(1_774_744_200)); + } + + #[test] + fn cron_et_erreurs() { + let err = |e: &str| parse_cron(e).err().unwrap_or_default(); + assert_eq!( + err("* * *"), + "cron : 5 champs attendus « min heure jour mois jour-semaine », reçu 3" + ); + assert_eq!( + err("60 * * * *"), + "champ minute : valeur 60 hors bornes [0-59]" + ); + assert_eq!(err("* x * * *"), "champ heure : valeur invalide : \"x\""); + assert_eq!( + err("* * 5-1 * *"), + "champ jour-du-mois : plage inversée : \"5-1\"" + ); + assert_eq!(err("* * * */0 *"), "champ mois : pas invalide : \"*/0\""); + assert_eq!( + err("* * 5-4 * *"), + "champ jour-du-mois : plage inversée : \"5-4\"" + ); + assert_eq!( + err("0 9 0 * *"), + "champ jour-du-mois : valeur 0 hors bornes [1-31]" + ); + assert_eq!(err("-1 9 * * *"), "champ minute : valeur invalide : \"\""); + assert!(parse_cron("0 9 * * 1-5").is_ok()); + let s = parse_cron("0 0 * * 7").unwrap(); + assert!(s.dow.contains(&0)); + } + + #[test] + fn prochains_declenchements() { + let u = Location::utc(); + let from = u.date(2026, 10, 15, 21, 10, 30) * NS; + let ns = |t: i64| i128::from(t) * NS128; + let from128 = i128::from(from); + assert_eq!( + next_after("@every 2h", "UTC", from), + Ok(from128 + i128::from(2 * HOUR)) + ); + assert_eq!( + next_after("@every 30s", "UTC", from), + Err("intervalle minimum : 1 minute".into()) + ); + assert_eq!( + next_after("@EVERY 1d", "UTC", from), + Ok(from128 + i128::from(24 * HOUR)) + ); + assert_eq!( + next_after("@every 1d@23:00", "UTC", from), + Ok(ns(u.date(2026, 10, 15, 23, 0, 0))) + ); + assert_eq!( + next_after("@every 2d@09:00", "UTC", from), + Ok(ns(u.date(2026, 10, 17, 9, 0, 0))) + ); + assert_eq!( + next_after("@every 0d", "UTC", from), + Err("nombre de jours invalide : \"0d\"".into()) + ); + assert_eq!( + next_after("@every 1d@25:00", "UTC", from), + Err("heure hors bornes : \"25:00\"".into()) + ); + assert_eq!( + next_after("@every 1d@9", "UTC", from), + Err("heure invalide (attendu HH:MM) : \"9\"".into()) + ); + assert_eq!( + next_after("0 9 * * 1-5", "UTC", from), + Ok(ns(u.date(2026, 10, 16, 9, 0, 0))) + ); + assert_eq!( + next_after("@once 2026-10-16 9:05", "UTC", from), + Ok(ns(u.date(2026, 10, 16, 9, 5, 0))) + ); + assert_eq!( + next_after("@once 2026-10-01 16:54", "UTC", from), + Err("2026-10-01 16:54 est déjà passé (il est 2026-10-15 21:10)".into()) + ); + for bad in [ + "@once 2026-02-30 10:00", + "@once 2026-10-16 24:00", + "@once 2026-10-16 23:60", + "@once 2026-13-01 10:00", + "@once 26-10-16 10:00", + "@once 2026-10-16 10:5", + ] { + assert_eq!( + next_after(bad, "UTC", from), + Err("date invalide (attendu « @once AAAA-MM-JJ HH:MM »)".into()), + "{bad}" + ); + } + assert_eq!(next_after(" ", "UTC", from), Err("fréquence vide".into())); + // Le prochain 1er janvier, à 78 jours ; le 14 octobre, à 364. + assert_eq!( + next_after("0 0 1 1 *", "UTC", from), + Ok(ns(u.date(2027, 1, 1, 0, 0, 0))) + ); + assert_eq!( + next_after("0 0 14 10 *", "UTC", from), + Ok(ns(u.date(2027, 10, 14, 0, 0, 0))) + ); + assert_eq!( + next_after("0 0 31 2 *", "UTC", from), + Err("fréquence jamais satisfaite : \"0 0 31 2 *\"".into()) + ); + assert!(is_once(" @Once 2026")); + assert_eq!( + next_after("@once 2026-10-16 7:00", "UTC", from), + Ok(ns(1_792_134_000)) + ); + assert_eq!(compute_next_run("bad", "UTC", from), 0); + } +} diff --git a/rust/loki-rs/src/tasks/mod.rs b/rust/loki-rs/src/tasks/mod.rs new file mode 100644 index 0000000..95bd017 --- /dev/null +++ b/rust/loki-rs/src/tasks/mod.rs @@ -0,0 +1,822 @@ +//! Tâches planifiées tenues par la façade (lot 26 de la migration, +//! `LOKI_TASKS=rust` ; expérimental, le Go reste le défaut) — port de +//! `tasks.go`, `tasks_sched.go`, `tasks_script.go` et `web_tasks.go`. +//! +//! - les routes `/api/tasks*` (liste, enregistrement, suppression, bascule, +//! interrupteur maître, lancement, arrêt) ; +//! - le planificateur : un tic par minute, une tâche due par tic ; +//! - les tâches « script seul » (un script du dossier protégé, sans modèle). +//! +//! Une tâche IA passe par le verrou de génération et les outils de l'agent, +//! restés au Go : la façade la fait exécuter par lui +//! (`/internal/v1/tasks/run`), qui tient son état comme son propre +//! planificateur. + +pub mod cron; + +use std::collections::BTreeMap; +use std::path::{Path, PathBuf}; +use std::sync::atomic::{AtomicU64, Ordering}; +use std::sync::{Arc, Mutex}; +use std::time::Duration; + +use http_body_util::{BodyExt, Limited}; +use hyper::body::Incoming; +use hyper::{Request, Response, StatusCode}; +use serde::{Deserialize, Serialize}; +use serde_json::{Value, json}; + +use crate::api::Ctx; +use crate::auth::json as send_json; +use crate::gojson::{self, Kind}; +use crate::proxy::Body; +use crate::store::{BK_STATE, Store}; + +/// `bkTasks`. +pub const BK_TASKS: &str = "tasks"; +/// `reportMax`. +pub const REPORT_MAX: usize = 4000; +/// `scriptTaskTimeout`. +pub const SCRIPT_TIMEOUT: Duration = Duration::from_secs(10 * 60); +/// Attente d'une tâche IA exécutée par le Go (qui ne la borne pas). +const AI_TASK_LIMIT: Duration = Duration::from_secs(24 * 3600); + +/// `Task` (mêmes champs JSON). +#[derive(Clone, Debug, Default, Deserialize, Serialize, PartialEq)] +pub struct Task { + #[serde(default)] + pub id: String, + #[serde(default)] + pub name: String, + #[serde(default)] + pub prompt: String, + #[serde(default)] + pub schedule: String, + #[serde(default)] + pub tz: String, + #[serde(default)] + pub preset: String, + #[serde(default, skip_serializing_if = "String::is_empty")] + pub project: String, + #[serde(default, skip_serializing_if = "String::is_empty")] + pub kind: String, + #[serde(default, skip_serializing_if = "String::is_empty")] + pub script: String, + #[serde(default)] + pub no_mem: bool, + #[serde(default)] + pub no_web: bool, + #[serde(default)] + pub enabled: bool, + #[serde(default)] + pub last_run: i64, + #[serde(default)] + pub next_run: i64, + #[serde(default)] + pub last_dur_ms: i64, + #[serde(default)] + pub last_ok: bool, + #[serde(default)] + pub last_error: String, + #[serde(default)] + pub last_report: String, +} + +/// Maintenant, en ns Unix. +pub fn now_ns() -> i64 { + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .map_or(0, |d| d.as_nanos() as i64) +} + +/// `newTaskID` : 6 octets aléatoires en hexadécimal. +pub fn new_task_id() -> String { + let mut b = [0u8; 6]; + let _ = getrandom::fill(&mut b); + hex::encode(b) +} + +/// Les tâches tenues ici : la base, et l'exécution en cours. +pub struct Tasks { + pub home: PathBuf, + /// La tâche IA que le Go exécute pour la façade (son id). + running: Mutex>, + /// Les tâches script en cours (id → signal d'arrêt) — `scriptRunning`. + scripts: Mutex>>, + /// Pas du planificateur (une minute). + pub tick: Duration, + /// L'heure (ns Unix) — remplaçable en test. + pub clock: fn() -> i64, + /// Un tic tout de suite (tests). + kick: tokio::sync::Notify, + /// Tics achevés (le premier passage compte pour un). + ticks: AtomicU64, + /// Joignabilité de Crawl4AI (cache de 30 s, comme le Go). + reach: crate::system::System, +} + +impl Tasks { + pub fn new(home: PathBuf) -> Self { + Self::with(home, Duration::from_secs(60), now_ns) + } + + /// Avec le pas du planificateur et l'horloge donnés. + pub fn with(home: PathBuf, tick: Duration, clock: fn() -> i64) -> Self { + Tasks { + home, + running: Mutex::new(None), + scripts: Mutex::new(BTreeMap::new()), + tick, + clock, + kick: tokio::sync::Notify::new(), + ticks: AtomicU64::new(0), + reach: crate::system::System::new(), + } + } + + pub fn now(&self) -> i64 { + (self.clock)() + } + + /// La tâche en cours vue par l'écran : l'IA d'abord, sinon un script. + pub fn running_id(&self) -> String { + if let Some(id) = self + .running + .lock() + .unwrap_or_else(|e| e.into_inner()) + .clone() + { + return id; + } + self.scripts + .lock() + .unwrap_or_else(|e| e.into_inner()) + .keys() + .next() + .cloned() + .unwrap_or_default() + } + + /// Une tâche script tourne-t-elle encore ? + pub fn script_running(&self) -> bool { + !self + .scripts + .lock() + .unwrap_or_else(|e| e.into_inner()) + .is_empty() + } + + fn set_running(&self, r: Option) { + *self.running.lock().unwrap_or_else(|e| e.into_inner()) = r; + } + + /// Demande un tic tout de suite. + pub fn kick(&self) { + self.kick.notify_one(); + } + + /// Le nombre de tics achevés. + pub fn ticks(&self) -> u64 { + self.ticks.load(Ordering::SeqCst) + } + + /// `scriptsDir`. + pub fn scripts_dir(&self) -> PathBuf { + self.home.join("scripts") + } +} + +/// `listTasks` : triées par nom, puis par id. +pub async fn list_tasks(store: &Store) -> Vec { + let mut out: Vec = store + .all(BK_TASKS) + .await + .unwrap_or_default() + .into_values() + .filter_map(|v| serde_json::from_str(&v).ok()) + .collect(); + out.sort_by(|a, b| a.name.cmp(&b.name).then_with(|| a.id.cmp(&b.id))); + out +} + +/// `getTask`. +pub async fn get_task(store: &Store, id: &str) -> Option { + let b = store.get(BK_TASKS, id).await.ok().flatten()?; + if b.is_empty() { + return None; + } + serde_json::from_slice(&b).ok() +} + +/// `saveTask`. +pub async fn save_task(store: &Store, t: &Task) -> Result<(), String> { + store + .put(BK_TASKS, &t.id, gojson::marshal(t).into_bytes()) + .await +} + +/// `tasksPaused`. +pub async fn tasks_paused(store: &Store) -> bool { + store.get_str(BK_STATE, "tasks_paused").await == "1" +} + +/// `scriptsPath` : un script DANS le dossier protégé — ni chemin absolu, ni +/// remontée, ni caractère hostile au shell. +pub fn scripts_path(dir: &Path, name: &str) -> Result { + let name = name.trim(); + if name.is_empty() { + return Err("nom de script vide".into()); + } + if name.starts_with('/') || name.starts_with('\\') { + return Err(format!( + "nom de script invalide (commence par un séparateur) : {name}" + )); + } + if let Some(i) = name.find([ + '"', '\'', '`', '$', ';', '&', '|', '<', '>', '*', '?', '\\', '\n', '\r', + ]) { + return Err(format!( + "nom de script invalide (caractère interdit {}) : {name}", + gojson::quote(&name[i..i + 1]) + )); + } + let clean = crate::paths::go_clean(name); + if clean.starts_with('/') || clean == ".." || clean.starts_with("../") { + return Err(format!( + "nom de script invalide (pas de chemin absolu ni de ../) : {name}" + )); + } + Ok(dir.join(clean)) +} + +/// `scriptExists`. +pub fn script_exists(dir: &Path, name: &str) -> Result<(), String> { + let full = scripts_path(dir, name)?; + match std::fs::metadata(&full) { + Err(_) => Err(format!("script introuvable : {name}")), + Ok(m) if m.is_dir() => Err(format!("{name} est un dossier, pas un script")), + Ok(_) => Ok(()), + } +} + +/// `listScripts` : les scripts du dossier (chemins relatifs, triés comme +/// `filepath.WalkDir`). +pub fn list_scripts(dir: &Path) -> Vec { + fn walk(root: &Path, p: &Path, out: &mut Vec) { + let Ok(rd) = std::fs::read_dir(p) else { + return; + }; + let mut entries: Vec<_> = rd.flatten().collect(); + entries.sort_by_key(|e| e.file_name()); + for e in entries { + let path = e.path(); + let Ok(ft) = e.file_type() else { continue }; + if ft.is_dir() { + walk(root, &path, out); + continue; + } + let rel = path + .strip_prefix(root) + .map(|r| r.to_string_lossy().replace('\\', "/")) + .unwrap_or_default(); + let size = e.metadata().map_or(0, |m| m.len()); + out.push(json!({ "name": rel, "size": size })); + } + } + let mut out = Vec::new(); + walk(dir, dir, &mut out); + out.sort_by(|a, b| a["name"].as_str().cmp(&b["name"].as_str())); + out +} + +// --------------------------------------------------------------------------- +// Exécution +// --------------------------------------------------------------------------- + +/// `ErrBusy`. +pub const ERR_BUSY: &str = "génération en cours"; +/// `errModelLoading`. +pub const ERR_MODEL_LOADING: &str = + "⏳ Le modèle est encore en train de charger — réessaie dans quelques secondes."; + +/// Fin d'une exécution. +pub enum Outcome { + Ok(String), + Err(String), + Canceled, +} + +/// `recordTaskEnd` : durée, dernier et prochain passage, succès ou échec, +/// compte-rendu — sur la tâche relue (une édition concurrente n'est pas +/// écrasée) ; puis la notification. +pub async fn record_task_end(ctx: &Ctx, tasks: &Tasks, id: &str, start_ns: i64, out: Outcome) { + let store = &ctx.store; + let now = tasks.now(); + let Some(mut cur) = get_task(store, id).await else { + return; + }; + cur.last_dur_ms = (now - start_ns).div_euclid(1_000_000); + cur.last_run = now.div_euclid(1_000_000); + cur.next_run = cron::compute_next_run(&cur.schedule, &cur.tz, now); + if cron::is_once(&cur.schedule) { + cur.enabled = false; + cur.next_run = 0; + } + let (failed, canceled) = match &out { + Outcome::Canceled => { + cur.last_ok = true; + cur.last_error.clear(); + cur.last_report = "(interrompue manuellement)".into(); + (false, true) + } + Outcome::Err(e) => { + cur.last_ok = false; + cur.last_error = e.clone(); + (true, false) + } + Outcome::Ok(report) => { + cur.last_ok = true; + cur.last_error.clear(); + cur.last_report = truncate_report(report); + (false, false) + } + }; + let _ = save_task(store, &cur).await; + if !canceled { + let title = if failed { + "Loki · tâche en échec" + } else { + "Loki · tâche terminée" + }; + push(ctx, title, &cur.name).await; + } +} + +/// Le compte-rendu borné à `reportMax` octets : `report[:n] + "…"` du Go, +/// qui peut couper un caractère — ses octets orphelins deviennent chacun +/// U+FFFD, comme à l'encodage JSON du Go. +fn truncate_report(r: &str) -> String { + if r.len() <= REPORT_MAX { + return r.to_string(); + } + format!("{}…", crate::system::go_lossy(&r.as_bytes()[..REPORT_MAX])) +} + +/// La notification de fin : par la façade si elle tient les notifications, +/// sinon par le Go. +async fn push(ctx: &Ctx, title: &str, body: &str) { + if let Some(p) = ctx.push.clone() { + let (store, t, b) = (ctx.store.clone(), title.to_string(), body.to_string()); + tokio::spawn(async move { p.send_all(&store, &t, &b).await }); + return; + } + let _ = ctx + .store + .internal_post( + "/internal/v1/push/send", + &json!({ "title": title, "body": body }), + ) + .await; +} + +/// `runTask` : une tâche script ici, une tâche IA par le Go. +pub async fn run_task(ctx: &Ctx, tasks: &Tasks, t: Task) { + if t.kind == "script" { + return run_script_task(ctx, tasks, t).await; + } + tasks.set_running(Some(t.id.clone())); + // Le Go rend la main à la fin du passage (une tâche IA dure). + let _ = ctx + .store + .internal_post_within( + "/internal/v1/tasks/run", + &json!({ "id": t.id }), + AI_TASK_LIMIT, + ) + .await; + tasks.set_running(None); +} + +/// `runScriptTask` : le script, sans modèle ; sa sortie fait le compte-rendu. +async fn run_script_task(ctx: &Ctx, tasks: &Tasks, t: Task) { + let start = tasks.now(); + let name = t.script.trim(); + if name.is_empty() { + let e = "aucun script associé à la tâche".to_string(); + return record_task_end(ctx, tasks, &t.id, start, Outcome::Err(e)).await; + } + let full = match scripts_path(&tasks.scripts_dir(), name) { + Ok(p) => p, + Err(e) => return record_task_end(ctx, tasks, &t.id, start, Outcome::Err(e)).await, + }; + if std::fs::metadata(&full).is_err() { + let e = format!("script introuvable : {name}"); + return record_task_end(ctx, tasks, &t.id, start, Outcome::Err(e)).await; + } + let (stop_tx, mut stop_rx) = tokio::sync::watch::channel(false); + tasks + .scripts + .lock() + .unwrap_or_else(|e| e.into_inner()) + .insert(t.id.clone(), stop_tx); + let (cwd, mem) = shell_dirs(ctx).await; + let cmd = script_run_command(&full); + let run = crate::tools::shell::run_shell(&cwd, &mem, &cmd, SCRIPT_TIMEOUT.as_secs() as i64); + let out = tokio::select! { + out = run => Some(out), + _ = stop_rx.wait_for(|s| *s) => None, + }; + let outcome = match out { + None => Outcome::Canceled, + Some(o) if o.starts_with("exit: 0") => Outcome::Ok(o), + Some(o) => Outcome::Err(o), + }; + record_task_end(ctx, tasks, &t.id, start, outcome).await; + tasks + .scripts + .lock() + .unwrap_or_else(|e| e.into_inner()) + .remove(&t.id); +} + +/// `scriptRunCommand` : le script lancé directement (son shebang choisit +/// l'interpréteur) ; sous Windows, un `.ps1` passe par PowerShell. Le nom est +/// déjà bridé par `scripts_path` : les guillemets suffisent. +fn script_run_command(path: &Path) -> String { + let q = format!("\"{}\"", path.to_string_lossy()); + let ps1 = path + .extension() + .is_some_and(|e| e.eq_ignore_ascii_case("ps1")); + if cfg!(windows) && ps1 { + return format!("powershell -NoProfile -ExecutionPolicy Bypass -File {q}"); + } + q +} + +/// Le dossier de travail de la discussion ouverte (là où `runShell` lance +/// ses commandes) et le dossier mémoire du projet actif (gardé du shell). +async fn shell_dirs(ctx: &Ctx) -> (PathBuf, PathBuf) { + let v = ctx + .store + .internal_json( + hyper::Method::POST, + "/internal/v1/conv/workspace", + Some(&json!({})), + ) + .await + .unwrap_or(Value::Null); + let cwd = v + .get("dir") + .and_then(Value::as_str) + .unwrap_or("") + .to_string(); + let active = crate::memory::active_project(&ctx.store).await; + let mem = crate::paths::project_memory_dir(&ctx.engine.home, &active); + (PathBuf::from(cwd), PathBuf::from(mem)) +} + +// --------------------------------------------------------------------------- +// Planificateur +// --------------------------------------------------------------------------- + +/// `primeNextRuns` : le prochain passage des tâches actives qui n'en ont pas. +pub async fn prime_next_runs(ctx: &Ctx, tasks: &Tasks) { + let now = tasks.now(); + for mut t in list_tasks(&ctx.store).await { + if t.enabled && t.next_run == 0 { + t.next_run = cron::compute_next_run(&t.schedule, &t.tz, now); + let _ = save_task(&ctx.store, &t).await; + } + } +} + +/// `tickTasks` : la première tâche due (une par tic), sauf interrupteur +/// maître ou optimisation en cours. +pub async fn tick_tasks(ctx: &Ctx, tasks: &Tasks) { + if tasks_paused(&ctx.store).await || crate::backends::tune_busy(&ctx.store).await.is_some() { + return; + } + let now = tasks.now(); + let now_ms = now.div_euclid(1_000_000); + for mut t in list_tasks(&ctx.store).await { + if !t.enabled { + continue; + } + if t.next_run == 0 { + t.next_run = cron::compute_next_run(&t.schedule, &t.tz, now); + let _ = save_task(&ctx.store, &t).await; + continue; + } + if t.next_run <= now_ms { + run_task(ctx, tasks, t).await; + return; + } + } +} + +/// La boucle : un premier passage tout de suite, puis un tic par minute (ou +/// sur demande). +pub async fn sched_loop(ctx: Ctx, tasks: Arc) { + prime_next_runs(&ctx, &tasks).await; + tasks.ticks.fetch_add(1, Ordering::SeqCst); + let mut iv = tokio::time::interval(tasks.tick); + iv.tick().await; + loop { + tokio::select! { + _ = iv.tick() => {} + _ = tasks.kick.notified() => {} + } + tick_tasks(&ctx, &tasks).await; + tasks.ticks.fetch_add(1, Ordering::SeqCst); + } +} + +// --------------------------------------------------------------------------- +// Routes +// --------------------------------------------------------------------------- + +const SAVE_REQ: &str = r#"struct { ID string "json:\"id\""; Name string "json:\"name\""; Prompt string "json:\"prompt\""; Schedule string "json:\"schedule\""; TZ string "json:\"tz\""; Preset string "json:\"preset\""; Project string "json:\"project\""; Kind string "json:\"kind\""; Script string "json:\"script\""; NoMem bool "json:\"no_mem\""; NoWeb bool "json:\"no_web\""; Enabled bool "json:\"enabled\"" }"#; +const ID_REQ: &str = r#"struct { ID string "json:\"id\"" }"#; +const TOGGLE_REQ: &str = r#"struct { ID string "json:\"id\""; On bool "json:\"on\"" }"#; +const ON_REQ: &str = r#"struct { On bool "json:\"on\"" }"#; + +/// Les routes servies par ce module. +pub fn handles(path: &str) -> bool { + matches!( + path, + "/api/tasks" + | "/api/tasks/save" + | "/api/tasks/delete" + | "/api/tasks/toggle" + | "/api/tasks/pause" + | "/api/tasks/run" + | "/api/tasks/stop" + ) +} + +fn fail(code: StatusCode, e: impl Into) -> Response { + send_json(code, &json!({ "ok": false, "error": e.into() })) +} + +/// `handleTasks`. +async fn overview(ctx: &Ctx, tasks: &Tasks) -> Value { + let store = &ctx.store; + let list = list_tasks(store).await; + let running_id = tasks.running_id(); + let cfg = store.config().await; + let order = crate::presets::parse_preset_order( + store + .get(BK_STATE, crate::presets::PRESET_ORDER_KEY) + .await + .ok() + .flatten() + .as_deref(), + ); + let presets: Vec = crate::presets::list_presets(&tasks.home, &cfg, &order) + .map(|l| { + l.into_iter() + .map(|p| json!({ "id": p.id, "name": p.name, "active": p.active })) + .collect() + }) + .unwrap_or_default(); + let switch_on = store.get_str(BK_STATE, "internet").await == "1"; + let key = store.get_str(BK_STATE, "crawl_key").await; + let web_on = + crate::system::internet_enabled(&cfg, switch_on) && tasks.reach.reachable(&cfg, &key).await; + json!({ + "ok": true, + "tasks": if list.is_empty() { Value::Null } else { serde_json::to_value(&list).unwrap_or(Value::Null) }, + "scripts": list_scripts(&tasks.scripts_dir()), + "scripts_dir": tasks.scripts_dir().to_string_lossy(), + "paused": tasks_paused(store).await, + "agent": store.get_str(BK_STATE, "agent").await == "1", + "running_id": running_id, + "presets": presets, + "projects": crate::projects_api::project_dtos(ctx).await, + "mem_on": crate::memory::Memory::mem_mode(store).await != "off", + "web_on": web_on, + }) +} + +/// `from_go` : remise par le Go (tunnel), déjà authentifiée par lui. +pub async fn serve( + ctx: &Ctx, + tasks: &Arc, + req: Request, + from_go: bool, +) -> Response { + let (parts, body) = req.into_parts(); + if !from_go && let Err(refus) = ctx.guard(&parts.headers).await { + return *refus; + } + let raw = Limited::new(body, 1 << 20) + .collect() + .await + .map(|b| b.to_bytes().to_vec()) + .unwrap_or_default(); + let store = &ctx.store; + match parts.uri.path() { + "/api/tasks" => send_json(StatusCode::OK, &overview(ctx, tasks).await), + "/api/tasks/save" => { + let d = gojson::decode_struct( + &raw, + SAVE_REQ, + &[ + ("id", Kind::Str), + ("name", Kind::Str), + ("prompt", Kind::Str), + ("schedule", Kind::Str), + ("tz", Kind::Str), + ("preset", Kind::Str), + ("project", Kind::Str), + ("kind", Kind::Str), + ("script", Kind::Str), + ("no_mem", Kind::Bool), + ("no_web", Kind::Bool), + ("enabled", Kind::Bool), + ], + ); + if let Some(e) = d.err.clone() { + return fail(StatusCode::BAD_REQUEST, e); + } + let name = d.str("name").trim().to_string(); + let prompt = d.str("prompt").trim().to_string(); + let schedule = d.str("schedule").trim().to_string(); + let kind = d.str("kind").trim().to_string(); + let script = d.str("script").trim().to_string(); + let tz = d.str("tz"); + if name.is_empty() { + return fail(StatusCode::BAD_REQUEST, "nom obligatoire"); + } + if kind == "script" { + if script.is_empty() { + return fail( + StatusCode::BAD_REQUEST, + "script obligatoire pour une tâche script", + ); + } + if let Err(e) = script_exists(&tasks.scripts_dir(), &script) { + return fail(StatusCode::BAD_REQUEST, e); + } + } else if prompt.is_empty() { + return fail(StatusCode::BAD_REQUEST, "consigne obligatoire"); + } + let now = tasks.now(); + if let Err(e) = cron::next_after(&schedule, &tz, now) { + return fail(StatusCode::BAD_REQUEST, format!("fréquence invalide : {e}")); + } + let id = d.str("id"); + let mut t = Task::default(); + if !id.is_empty() { + match get_task(store, &id).await { + Some(cur) => t = cur, + None => t.id = id, + } + } + if t.id.is_empty() { + t.id = new_task_id(); + } + t.name = name; + t.prompt = prompt; + t.enabled = d.bool("enabled"); + t.tz = tz.clone(); + t.preset = d.str("preset"); + t.kind = kind; + t.script = script; + let project = d.str("project").trim().to_string(); + let projects = crate::memory::list_projects(store).await; + if !project.is_empty() && projects.iter().any(|p| p.slug == project) { + t.project = project; + } else if t.project.is_empty() { + t.project = crate::memory::active_project(store).await; + } + t.no_mem = d.bool("no_mem"); + t.no_web = d.bool("no_web"); + if t.schedule != schedule || t.next_run == 0 { + t.next_run = cron::compute_next_run(&schedule, &tz, now); + } + t.schedule = schedule; + if let Err(e) = save_task(store, &t).await { + return fail(StatusCode::INTERNAL_SERVER_ERROR, e); + } + send_json(StatusCode::OK, &json!({ "ok": true, "id": t.id })) + } + "/api/tasks/delete" => { + let d = gojson::decode_struct(&raw, ID_REQ, &[("id", Kind::Str)]); + let id = d.str("id"); + if id.is_empty() { + return fail(StatusCode::BAD_REQUEST, "id manquant"); + } + if let Err(e) = store.del(BK_TASKS, &id).await { + return fail(StatusCode::INTERNAL_SERVER_ERROR, e); + } + send_json(StatusCode::OK, &json!({ "ok": true })) + } + "/api/tasks/toggle" => { + let d = + gojson::decode_struct(&raw, TOGGLE_REQ, &[("id", Kind::Str), ("on", Kind::Bool)]); + let Some(mut t) = get_task(store, &d.str("id")).await else { + return fail(StatusCode::NOT_FOUND, "tâche introuvable"); + }; + t.enabled = d.bool("on"); + if t.enabled { + t.next_run = cron::compute_next_run(&t.schedule, &t.tz, tasks.now()); + } + if let Err(e) = save_task(store, &t).await { + return fail(StatusCode::INTERNAL_SERVER_ERROR, e); + } + send_json(StatusCode::OK, &json!({ "ok": true })) + } + "/api/tasks/pause" => { + let d = gojson::decode_struct(&raw, ON_REQ, &[("on", Kind::Bool)]); + let res = if d.bool("on") { + store.put(BK_STATE, "tasks_paused", b"1".to_vec()).await + } else { + store.del(BK_STATE, "tasks_paused").await + }; + if let Err(e) = res { + return fail(StatusCode::INTERNAL_SERVER_ERROR, e); + } + send_json( + StatusCode::OK, + &json!({ "ok": true, "paused": tasks_paused(store).await }), + ) + } + "/api/tasks/stop" => { + let d = gojson::decode_struct(&raw, ID_REQ, &[("id", Kind::Str)]); + let id = d.str("id"); + let stop = (!id.is_empty()) + .then(|| { + tasks + .scripts + .lock() + .unwrap_or_else(|e| e.into_inner()) + .get(&id) + .cloned() + }) + .flatten(); + if let Some(s) = stop { + let _ = s.send(true); + return send_json(StatusCode::OK, &json!({ "ok": true })); + } + let _ = store + .internal_post("/internal/v1/engine/stop", &json!({})) + .await; + send_json(StatusCode::OK, &json!({ "ok": true })) + } + "/api/tasks/run" => { + let d = gojson::decode_struct(&raw, ID_REQ, &[("id", Kind::Str)]); + let Some(t) = get_task(store, &d.str("id")).await else { + return fail(StatusCode::NOT_FOUND, "tâche introuvable"); + }; + if t.kind != "script" { + if crate::enginectl::generating(ctx).await { + return fail(StatusCode::CONFLICT, ERR_BUSY); + } + if !crate::engstatus::health_check(&store.config().await).await { + return fail(StatusCode::SERVICE_UNAVAILABLE, ERR_MODEL_LOADING); + } + } + let (ctx2, tasks2) = (ctx.clone(), tasks.clone()); + tokio::spawn(async move { run_task(&ctx2, &tasks2, t).await }); + send_json(StatusCode::OK, &json!({ "ok": true })) + } + _ => fail(StatusCode::NOT_FOUND, "route inconnue"), + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn compte_rendu_borne() { + let pile = "x".repeat(REPORT_MAX); + assert_eq!(truncate_report(&pile), pile); + let long = format!("a{}", "é".repeat(2000)); + let want = format!("a{}\u{FFFD}…", "é".repeat(1999)); + assert_eq!(truncate_report(&long), want); + // Coupé entre deux caractères : rien à remplacer. + let net = format!("{}é", "x".repeat(REPORT_MAX)); + assert_eq!( + truncate_report(&net), + format!("{}…", "x".repeat(REPORT_MAX)) + ); + } + + #[test] + fn noms_de_script() { + let dir = Path::new("/srv/scripts"); + assert_eq!(scripts_path(dir, " sous/a.sh "), Ok(dir.join("sous/a.sh"))); + assert_eq!(scripts_path(dir, "./a.sh"), Ok(dir.join("a.sh"))); + assert_eq!(scripts_path(dir, " "), Err("nom de script vide".into())); + assert_eq!( + scripts_path(dir, "a/../../b"), + Err("nom de script invalide (pas de chemin absolu ni de ../) : a/../../b".into()) + ); + assert_eq!( + scripts_path(dir, "a\nb"), + Err("nom de script invalide (caractère interdit \"\\n\") : a\nb".into()) + ); + } +} diff --git a/rust/loki-rs/src/tz.rs b/rust/loki-rs/src/tz.rs new file mode 100644 index 0000000..4232756 --- /dev/null +++ b/rust/loki-rs/src/tz.rs @@ -0,0 +1,756 @@ +//! Fuseaux horaires à la Go (`time.LoadLocation`, `time.Local`) : les fichiers +//! TZif du système (`/usr/share/zoneinfo`…), lus comme le fait la bibliothèque +//! standard du Go — transitions 64 bits (v2+), règle POSIX du pied de fichier +//! pour les dates au-delà de la dernière transition — et la conversion d'une +//! heure murale en instant comme `time.Date`. +//! +//! Sert aux tâches planifiées (fréquences cron, « tous les jours à 23:00 », +//! « @once ») : la même zoneinfo que le Go, donc les mêmes déclenchements. + +use std::path::Path; + +/// Une zone : décalage UTC (s), heure d'été, abréviation. +#[derive(Clone, Debug, PartialEq)] +pub struct Zone { + pub name: String, + pub offset: i64, + pub is_dst: bool, +} + +/// Un fuseau chargé. +#[derive(Clone, Debug)] +pub struct Location { + pub name: String, + zones: Vec, + /// (instant, index de zone), triés. + tx: Vec<(i64, usize)>, + /// Règle POSIX au-delà de la dernière transition. + extend: String, +} + +const ALPHA: i64 = i64::MIN; +const OMEGA: i64 = i64::MAX; + +impl Location { + /// `time.UTC`. + pub fn utc() -> Self { + Location { + name: "UTC".into(), + zones: vec![Zone { + name: "UTC".into(), + offset: 0, + is_dst: false, + }], + tx: Vec::new(), + extend: String::new(), + } + } + + /// `time.LoadLocation` : "" ou "UTC" = UTC ; sinon le fichier TZif du + /// système. Erreur si introuvable ou illisible. + pub fn load(name: &str) -> Result { + if name.is_empty() || name == "UTC" { + return Ok(Self::utc()); + } + if name == "Local" { + return Ok(Self::local()); + } + let mut sources: Vec = Vec::new(); + if let Ok(z) = std::env::var("ZONEINFO") + && !z.is_empty() + { + sources.push(z); + } + sources.extend( + [ + "/usr/share/zoneinfo/", + "/usr/share/lib/zoneinfo/", + "/usr/lib/locale/TZ/", + "/etc/zoneinfo/", + ] + .iter() + .map(|s| s.to_string()), + ); + Self::load_in(name, &sources) + } + + /// `LoadLocation` sur les bases données (dans l'ordre). + fn load_in(name: &str, sources: &[String]) -> Result { + if name.is_empty() || name == "UTC" { + return Ok(Self::utc()); + } + if !valid_name(name) { + return Err("time: invalid location name".to_string()); + } + for src in sources { + let p = Path::new(src).join(name); + if let Ok(b) = std::fs::read(&p) { + return parse_tzif(name, &b); + } + } + Err(format!("unknown time zone {name}")) + } + + /// `time.Local` : `$TZ` (vide = UTC, « :chemin » accepté), sinon + /// /etc/localtime, sinon UTC. + pub fn local() -> Self { + Self::local_from(std::env::var("TZ").ok(), Path::new("/etc/localtime")) + } + + /// `initLocal` : `tz` la valeur de `$TZ` (absente : `None`), `etc` le + /// fuseau du système. + fn local_from(tz: Option, etc: &Path) -> Self { + match tz { + Some(tz) => { + let tz = tz.strip_prefix(':').unwrap_or(&tz).to_string(); + if tz.is_empty() || tz == "UTC" { + return Self::utc(); + } + if tz.starts_with('/') { + if let Ok(b) = std::fs::read(&tz) + && let Ok(mut l) = parse_tzif("Local", &b) + { + l.name = "Local".into(); + return l; + } + return Self::utc(); + } + match Self::load(&tz) { + Ok(mut l) => { + l.name = "Local".into(); + l + } + Err(_) => Self::utc(), + } + } + None => match std::fs::read(etc) + .ok() + .and_then(|b| parse_tzif("Local", &b).ok()) + { + Some(mut l) => { + l.name = "Local".into(); + l + } + None => { + let mut u = Self::utc(); + u.name = "Local".into(); + u + } + }, + } + } + + /// `(*Location).lookup` : la zone d'un instant et la période où elle + /// vaut, [start, end[. + pub fn lookup(&self, sec: i64) -> (Zone, i64, i64) { + if self.zones.is_empty() { + return ( + Zone { + name: "UTC".into(), + offset: 0, + is_dst: false, + }, + ALPHA, + OMEGA, + ); + } + if self.tx.is_empty() || sec < self.tx[0].0 { + let z = self.zones[self.lookup_first_zone()].clone(); + let end = self.tx.first().map_or(OMEGA, |t| t.0); + return (z, ALPHA, end); + } + // Dichotomie : la dernière transition <= sec. + let (mut lo, mut hi) = (0usize, self.tx.len()); + let mut end = OMEGA; + while hi - lo > 1 { + let m = lo + (hi - lo) / 2; + let lim = self.tx[m].0; + if sec < lim { + end = lim; + hi = m; + } else { + lo = m; + } + } + let zone = self.zones[self.tx[lo].1].clone(); + let start = self.tx[lo].0; + if lo == self.tx.len() - 1 + && !self.extend.is_empty() + && let Some((z, s, e)) = tzset(&self.extend, start, sec) + { + return (z, s, e); + } + (zone, start, end) + } + + /// `lookupFirstZone`. + fn lookup_first_zone(&self) -> usize { + if !self.first_zone_used() { + return 0; + } + if !self.tx.is_empty() && self.zones[self.tx[0].1].is_dst { + let mut zi = self.tx[0].1 as i64 - 1; + while zi >= 0 { + if !self.zones[zi as usize].is_dst { + return zi as usize; + } + zi -= 1; + } + } + for (i, z) in self.zones.iter().enumerate() { + if !z.is_dst { + return i; + } + } + 0 + } + + fn first_zone_used(&self) -> bool { + self.tx.iter().any(|t| t.1 == 0) + } + + /// Le décalage d'un instant. + pub fn offset_at(&self, sec: i64) -> i64 { + self.lookup(sec).0.offset + } + + /// `time.Date(y, m, d, h, mi, s, 0, loc)` : l'instant (s Unix) d'une heure + /// murale, normalisée comme le Go (débordements reportés). + pub fn date(&self, y: i64, m: i64, d: i64, h: i64, mi: i64, s: i64) -> i64 { + // Normalisation du mois puis des secondes du jour. + let (mut y, mut m) = (y, m - 1); + y += m.div_euclid(12); + m = m.rem_euclid(12); + let days = days_from_civil(y, m + 1, 1) + (d - 1); + let unix = days * 86400 + h * 3600 + mi * 60 + s; + let (z, start, end) = self.lookup(unix); + let mut offset = z.offset; + let mut out = unix; + if offset != 0 { + let utc = unix - offset; + if utc < start || utc >= end { + offset = self.lookup(utc).0.offset; + } + out -= offset; + } + out + } + + /// L'heure murale d'un instant : (année, mois, jour, heure, minute, + /// seconde, jour de la semaine 0 = dimanche). + pub fn civil(&self, sec: i64) -> Civil { + civil(sec + self.offset_at(sec)) + } +} + +/// Une heure murale. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct Civil { + pub year: i64, + pub month: i64, + pub day: i64, + pub hour: i64, + pub minute: i64, + pub second: i64, + pub weekday: i64, +} + +impl Civil { + /// `Format("2006-01-02 15:04")`. + pub fn ymd_hm(&self) -> String { + format!( + "{:04}-{:02}-{:02} {:02}:{:02}", + self.year, self.month, self.day, self.hour, self.minute + ) + } +} + +/// Les secondes « locales » (UTC + décalage) en heure murale. +pub fn civil(local: i64) -> Civil { + let days = local.div_euclid(86400); + let rem = local.rem_euclid(86400); + let (y, m, d) = civil_from_days(days); + Civil { + year: y, + month: m, + day: d, + hour: rem / 3600, + minute: rem % 3600 / 60, + second: rem % 60, + weekday: (days + 4).rem_euclid(7), + } +} + +/// Jours depuis 1970-01-01 (calendrier grégorien proleptique). +pub fn days_from_civil(y: i64, m: i64, d: i64) -> i64 { + let y = if m <= 2 { y - 1 } else { y }; + let era = y.div_euclid(400); + let yoe = y - era * 400; + let mp = (m + 9) % 12; + let doy = (153 * mp + 2) / 5 + d - 1; + let doe = yoe * 365 + yoe / 4 - yoe / 100 + doy; + era * 146097 + doe - 719468 +} + +/// L'inverse de [`days_from_civil`]. +pub fn civil_from_days(z: i64) -> (i64, i64, i64) { + let z = z + 719468; + let era = z.div_euclid(146097); + let doe = z - era * 146097; + let yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365; + let y = yoe + era * 400; + let doy = doe - (365 * yoe + yoe / 4 - yoe / 100); + let mp = (5 * doy + 2) / 153; + let d = doy - (153 * mp + 2) / 5 + 1; + let m = if mp < 10 { mp + 3 } else { mp - 9 }; + (if m <= 2 { y + 1 } else { y }, m, d) +} + +fn is_leap(y: i64) -> bool { + y % 4 == 0 && (y % 100 != 0 || y % 400 == 0) +} + +/// `containsDotDot` et consorts : un nom de zone sans « .. » ni chemin +/// absolu. +/// Ni `..` (n'importe où, comme `containsDotDot`), ni chemin absolu. +fn valid_name(name: &str) -> bool { + !(name.contains("..") || name.starts_with('/') || name.starts_with('\\')) +} + +fn be32(b: &[u8]) -> i64 { + i32::from_be_bytes([b[0], b[1], b[2], b[3]]) as i64 +} + +fn be64(b: &[u8]) -> i64 { + i64::from_be_bytes([b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]]) +} + +/// `LoadLocationFromTZData`. +pub fn parse_tzif(name: &str, data: &[u8]) -> Result { + let bad = || "malformed time zone information".to_string(); + if data.len() < 44 || &data[..4] != b"TZif" { + return Err(bad()); + } + let version = data[4]; + let counts = |off: usize| -> [usize; 6] { + let mut n = [0usize; 6]; + for (i, v) in n.iter_mut().enumerate() { + *v = u32::from_be_bytes([ + data[off + 20 + i * 4], + data[off + 21 + i * 4], + data[off + 22 + i * 4], + data[off + 23 + i * 4], + ]) as usize; + } + n + }; + // [isutcnt, isstdcnt, leapcnt, timecnt, typecnt, charcnt] + let mut n = counts(0); + let mut off = 44; + let mut wide = false; + if version >= b'2' { + // Saute le bloc v1. + let skip = n[3] * 4 + n[3] + n[4] * 6 + n[5] + n[2] * 8 + n[1] + n[0]; + let o2 = 44 + skip; + if data.len() < o2 + 44 || &data[o2..o2 + 4] != b"TZif" { + return Err(bad()); + } + n = counts(o2); + off = o2 + 44; + wide = true; + } + let tsz = if wide { 8 } else { 4 }; + let need = n[3] * tsz + n[3] + n[4] * 6 + n[5] + n[2] * (tsz + 4) + n[1] + n[0]; + if data.len() < off + need { + return Err(bad()); + } + let times = &data[off..off + n[3] * tsz]; + let idx = &data[off + n[3] * tsz..off + n[3] * tsz + n[3]]; + let zo = off + n[3] * tsz + n[3]; + let zdata = &data[zo..zo + n[4] * 6]; + let chars = &data[zo + n[4] * 6..zo + n[4] * 6 + n[5]]; + let after = zo + n[4] * 6 + n[5] + n[2] * (tsz + 4) + n[1] + n[0]; + let mut extend = String::new(); + if wide && data.len() > after && data[after] == b'\n' { + let rest = &data[after + 1..]; + if let Some(e) = rest.iter().position(|c| *c == b'\n') { + extend = String::from_utf8_lossy(&rest[..e]).into_owned(); + } + } + if n[4] == 0 { + return Err(bad()); + } + let mut zones = Vec::new(); + for i in 0..n[4] { + let z = &zdata[i * 6..i * 6 + 6]; + let off = be32(z); + let is_dst = z[4] != 0; + let di = z[5] as usize; + if di >= chars.len() { + return Err(bad()); + } + let end = chars[di..] + .iter() + .position(|c| *c == 0) + .unwrap_or(chars.len() - di); + zones.push(Zone { + name: String::from_utf8_lossy(&chars[di..di + end]).into_owned(), + offset: off, + is_dst, + }); + } + let mut tx = Vec::new(); + for i in 0..n[3] { + let t = if wide { + be64(×[i * 8..]) + } else { + be32(×[i * 4..]) + }; + let zi = idx[i] as usize; + if zi >= zones.len() { + return Err(bad()); + } + tx.push((t, zi)); + } + if tx.is_empty() { + // Comme le Go : une transition fictive au début des temps. + tx.push((ALPHA, 0)); + } + Ok(Location { + name: name.to_string(), + zones, + tx, + extend, + }) +} + +// --------------------------------------------------------------------------- +// Règle POSIX (tzset du Go) +// --------------------------------------------------------------------------- + +/// `tzset` : la zone d'un instant d'après une règle POSIX (« CET-1CEST,M3.5.0, +/// M10.5.0/3 »), et sa période. `None` : règle illisible. +fn tzset(s: &str, last_tx: i64, sec: i64) -> Option<(Zone, i64, i64)> { + let (std_name, s) = tzset_name(s)?; + let (std_off, s) = tzset_offset(s)?; + let std_offset = -std_off; + if s.is_empty() || s.starts_with(',') { + // Pas d'heure d'été. + return Some(( + Zone { + name: std_name, + offset: std_offset, + is_dst: false, + }, + last_tx, + OMEGA, + )); + } + let (dst_name, s) = tzset_name(s)?; + let (dst_offset, s) = if s.is_empty() || s.starts_with(',') { + (std_offset + 3600, s) + } else { + let (o, r) = tzset_offset(s)?; + (-o, r) + }; + let s = if s.is_empty() { ",M3.2.0,M11.1.0" } else { s }; + let s = s.strip_prefix(',')?; + let (start_rule, s) = tzset_rule(s)?; + let s = s.strip_prefix(',')?; + let (end_rule, s) = tzset_rule(s)?; + if !s.is_empty() { + return None; + } + let (year, yday) = { + let c = civil(sec); + let doy = days_from_civil(c.year, c.month, c.day) - days_from_civil(c.year, 1, 1); + (c.year, doy) + }; + let _ = yday; + let ysec = days_from_civil(year, 1, 1) * 86400; + let mut start = ysec + tzrule_time(year, &start_rule, std_offset); + let mut end = ysec + tzrule_time(year, &end_rule, dst_offset); + let (dst_is_dst, std_is_dst) = (true, false); + let (mut lo, mut hi) = ( + Zone { + name: std_name.clone(), + offset: std_offset, + is_dst: std_is_dst, + }, + Zone { + name: dst_name.clone(), + offset: dst_offset, + is_dst: dst_is_dst, + }, + ); + if end < start { + std::mem::swap(&mut start, &mut end); + std::mem::swap(&mut lo, &mut hi); + } + let yearend = days_from_civil(year + 1, 1, 1) * 86400; + if sec < start { + return Some((lo, ysec, start)); + } else if sec >= end { + return Some((lo, end, yearend)); + } + Some((hi, start, end)) +} + +fn tzset_name(s: &str) -> Option<(String, &str)> { + if s.is_empty() { + return None; + } + if let Some(r) = s.strip_prefix('<') { + let e = r.find('>')?; + return Some((r[..e].to_string(), &r[e + 1..])); + } + let e = s + .find(|c: char| c.is_ascii_digit() || c == ',' || c == '-' || c == '+') + .unwrap_or(s.len()); + if e < 3 { + return None; + } + Some((s[..e].to_string(), &s[e..])) +} + +fn tzset_offset(s: &str) -> Option<(i64, &str)> { + let (neg, s) = match s.as_bytes().first()? { + b'+' => (false, &s[1..]), + b'-' => (true, &s[1..]), + _ => (false, s), + }; + let (h, mut s) = tzset_num(s, 0, 24 * 7)?; + let mut off = h * 3600; + if let Some(r) = s.strip_prefix(':') { + let (m, r) = tzset_num(r, 0, 59)?; + off += m * 60; + s = r; + if let Some(r) = s.strip_prefix(':') { + let (sec, r) = tzset_num(r, 0, 59)?; + off += sec; + s = r; + } + } + Some((if neg { -off } else { off }, s)) +} + +fn tzset_num(s: &str, min: i64, max: i64) -> Option<(i64, &str)> { + let e = s.find(|c: char| !c.is_ascii_digit()).unwrap_or(s.len()); + if e == 0 { + return None; + } + let n: i64 = s[..e].parse().ok()?; + if n < min || n > max { + return None; + } + Some((n, &s[e..])) +} + +enum RuleKind { + Julian(i64), + Doy(i64), + Mwd(i64, i64, i64), +} + +struct Rule { + kind: RuleKind, + time: i64, +} + +fn tzset_rule(s: &str) -> Option<(Rule, &str)> { + let (kind, s) = if let Some(r) = s.strip_prefix('J') { + let (d, r) = tzset_num(r, 1, 365)?; + (RuleKind::Julian(d), r) + } else if let Some(r) = s.strip_prefix('M') { + let (m, r) = tzset_num(r, 1, 12)?; + let r = r.strip_prefix('.')?; + let (w, r) = tzset_num(r, 1, 5)?; + let r = r.strip_prefix('.')?; + let (d, r) = tzset_num(r, 0, 6)?; + (RuleKind::Mwd(m, w, d), r) + } else { + let (d, r) = tzset_num(s, 0, 365)?; + (RuleKind::Doy(d), r) + }; + let (time, s) = if let Some(r) = s.strip_prefix('/') { + tzset_offset(r)? + } else { + (2 * 3600, s) + }; + Some((Rule { kind, time }, s)) +} + +/// `tzruleTime` : secondes depuis le début de l'année (UTC) où la règle +/// s'applique, pour le décalage en vigueur avant elle. +fn tzrule_time(year: i64, r: &Rule, off: i64) -> i64 { + let s = match r.kind { + RuleKind::Julian(d) => { + let mut s = (d - 1) * 86400; + if is_leap(year) && d >= 60 { + s += 86400; + } + s + } + RuleKind::Doy(d) => d * 86400, + RuleKind::Mwd(mon, week, day) => { + // Zeller : jour de la semaine du 1er du mois. + let m1 = (mon + 9) % 12 + 1; + let mut yy0 = year; + if mon <= 2 { + yy0 -= 1; + } + let yy1 = yy0 / 100; + let yy2 = yy0 % 100; + let mut dow = ((26 * m1 - 2) / 10 + 1 + yy2 + yy2 / 4 + yy1 / 4 - 2 * yy1) % 7; + if dow < 0 { + dow += 7; + } + let mut d = day - dow; + if d < 0 { + d += 7; + } + for _ in 1..week { + if d + 7 >= days_in(mon, year) { + break; + } + d += 7; + } + let mut s = d * 86400; + for m in 1..mon { + s += days_in(m, year) * 86400; + } + s + } + }; + s + r.time - off +} + +fn days_in(m: i64, year: i64) -> i64 { + match m { + 2 => { + if is_leap(year) { + 29 + } else { + 28 + } + } + 4 | 6 | 9 | 11 => 30, + _ => 31, + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn heure_locale() { + let hiver = 1_767_268_800; // 2026-01-01 12:00 UTC + let etc = Path::new("/usr/share/zoneinfo/Europe/Paris"); + let off = |tz: Option<&str>, etc: &Path| { + Location::local_from(tz.map(str::to_string), etc).offset_at(hiver) + }; + assert_eq!(off(None, etc), 3600); + assert_eq!(off(None, Path::new("/nulle/part")), 0); + assert_eq!(off(Some(""), etc), 0); + assert_eq!(off(Some(":"), etc), 0); + assert_eq!(off(Some("UTC"), etc), 0); + assert_eq!(off(Some(":Asia/Kolkata"), etc), 19_800); + assert_eq!(off(Some("America/New_York"), etc), -18_000); + assert_eq!(off(Some(":/usr/share/zoneinfo/Asia/Kolkata"), etc), 19_800); + assert_eq!(off(Some("/nulle/part"), etc), 0); + assert_eq!(off(Some("Nulle/Part"), etc), 0); + } + + #[test] + fn noms_de_fuseau() { + let sys = vec!["/usr/share/zoneinfo/".to_string()]; + // « UTC » sans base : comme le Go, sans fichier. + assert_eq!(Location::load_in("UTC", &[]).unwrap().name, "UTC"); + assert_eq!(Location::load_in("", &[]).unwrap().offset_at(0), 0); + for bad in ["../etc/passwd", "a..b", "/etc/localtime", "\\x"] { + assert_eq!( + Location::load_in(bad, &sys).err().as_deref(), + Some("time: invalid location name"), + "{bad}" + ); + } + assert_eq!( + Location::load_in("Europe/Paris", &[]).err().as_deref(), + Some("unknown time zone Europe/Paris") + ); + assert!(Location::load_in("Europe/Paris", &sys).is_ok()); + } + + /// Valeurs tirées du Go. + #[test] + fn transitions_et_normalisation() { + let ny = Location::load("America/New_York").unwrap(); + let paris = Location::load("Europe/Paris").unwrap(); + // Avant la première transition : l'heure solaire de New York. + assert_eq!(ny.offset_at(-3_000_000_000), -17_762); + // Pas d'heure d'été en France en 1970 : la table, pas la règle. + assert_eq!(paris.offset_at(paris.date(1970, 7, 1, 14, 0, 0)), 3600); + assert_eq!(paris.offset_at(1_782_907_200), 7200); + // Pile à la transition : la nouvelle heure. + assert_eq!(paris.offset_at(828_234_000), 7200); + assert_eq!(paris.offset_at(828_233_999), 3600); + assert_eq!(paris.offset_at(846_378_000), 3600); + assert_eq!(paris.offset_at(846_377_999), 7200); + // L'heure qui saute, à l'ouest de Greenwich. + assert_eq!(ny.date(2026, 3, 8, 3, 30, 0), 1_772_955_000); + assert_eq!(ny.date(2026, 3, 8, 2, 30, 0), 1_772_951_400); + assert_eq!(ny.offset_at(1_782_907_200), -14_400); + // Mois hors bornes, reportés sur l'année. + let u = Location::utc(); + assert_eq!(u.date(2026, 0, 15, 10, 0, 0), 1_765_792_800); + assert_eq!(u.date(2026, -13, 15, 10, 0, 0), 1_731_664_800); + } + + #[test] + fn calendrier() { + assert_eq!(days_from_civil(1970, 1, 1), 0); + assert_eq!(civil_from_days(0), (1970, 1, 1)); + assert_eq!(days_from_civil(2026, 10, 15), 20741); + assert_eq!(civil_from_days(20741), (2026, 10, 15)); + assert_eq!(civil(0).weekday, 4, "jeudi"); + // Mois et jours hors bornes, normalisés comme time.Date. + let u = Location::utc(); + assert_eq!(u.date(2026, 13, 1, 0, 0, 0), u.date(2027, 1, 1, 0, 0, 0)); + assert_eq!(u.date(2026, 2, 30, 0, 0, 0), u.date(2026, 3, 2, 0, 0, 0)); + assert_eq!(u.date(2026, 1, 0, 0, 0, 0), u.date(2025, 12, 31, 0, 0, 0)); + } + + #[test] + fn paris() { + let Ok(p) = Location::load("Europe/Paris") else { + eprintln!("zoneinfo absente : test sauté"); + return; + }; + // Hiver : +1 h ; été : +2 h. + let jan = Location::utc().date(2026, 1, 15, 12, 0, 0); + assert_eq!(p.offset_at(jan), 3600); + let jul = Location::utc().date(2026, 7, 15, 12, 0, 0); + assert_eq!(p.offset_at(jul), 7200); + // Bien au-delà de la dernière transition du fichier : la règle POSIX. + let far = Location::utc().date(2090, 7, 15, 12, 0, 0); + assert_eq!(p.offset_at(far), 7200); + let far_w = Location::utc().date(2090, 12, 15, 12, 0, 0); + assert_eq!(p.offset_at(far_w), 3600); + // Heure murale → instant. + let t = p.date(2026, 7, 15, 23, 0, 0); + assert_eq!(p.civil(t).ymd_hm(), "2026-07-15 23:00"); + assert_eq!(Location::utc().civil(t).ymd_hm(), "2026-07-15 21:00"); + // Heure qui n'existe pas (passage à l'été) : comme le Go, 02:30 → 03:30. + let gap = p.date(2026, 3, 29, 2, 30, 0); + assert_eq!(p.civil(gap).ymd_hm(), "2026-03-29 03:30"); + assert_eq!(gap, 1774747800, "instant donné par le Go"); + // Heure double (passage à l'hiver) : la seconde, comme l'algorithme de + // time.Date (vérifié contre le Go). + let dbl = p.date(2026, 10, 25, 2, 30, 0); + assert_eq!((dbl, p.offset_at(dbl)), (1792891800, 3600)); + assert_eq!(t, 1784149200); + assert!(Location::load("Nulle/Part").is_err()); + assert!(Location::load("../etc/passwd").is_err()); + } +} diff --git a/rust/loki-rs/tests/common/fakego.rs b/rust/loki-rs/tests/common/fakego.rs index 5b7a92f..39abcee 100644 --- a/rust/loki-rs/tests/common/fakego.rs +++ b/rust/loki-rs/tests/common/fakego.rs @@ -99,6 +99,16 @@ pub struct FakeGo { pub crypt_mode: Mutex, /// Réponse de /internal/v1/conv/workspace (None : 404). pub conv_workspace: Mutex>, + /// /internal/v1/conv/workspace crée-t-il la discussion active dans + /// l'index, s'il est vide (`convEnsureActive`) ? + pub conv_ensure: Mutex, + /// /internal/v1/tasks/run reçus (ids) : la tâche IA ne tourne pas (le + /// modèle n'est jamais prêt ici). + pub task_runs: Mutex>, + /// Durée d'une tâche IA reçue (rien : rendue tout de suite). + pub task_run_delay: Mutex>, + /// /internal/v1/push/send reçus : (titre, corps). + pub push_sends: Mutex>, } impl FakeGo { @@ -472,9 +482,41 @@ async fn handle(st: Arc, req: Request) -> Response resp(204, "") } "/internal/v1/conv/workspace" => match st.conv_workspace.lock().unwrap().clone() { - Some(v) => resp(200, v.to_string()), + Some(v) => { + if *st.conv_ensure.lock().unwrap() && st.get("chat", "index").is_none() { + let id = v["id"].as_str().unwrap_or("actif"); + st.set( + "chat", + "index", + &serde_json::json!([{ "id": id }]).to_string(), + ); + } + resp(200, v.to_string()) + } None => resp(404, "404 page not found"), }, + "/internal/v1/tasks/run" => { + let body = req.into_body().collect().await.unwrap().to_bytes(); + let v: serde_json::Value = serde_json::from_slice(&body).unwrap_or_default(); + st.task_runs + .lock() + .unwrap() + .push(v["id"].as_str().unwrap_or("").to_string()); + let delay = *st.task_run_delay.lock().unwrap(); + if let Some(d) = delay { + tokio::time::sleep(d).await; + } + resp(200, r#"{"busy":true}"#) + } + "/internal/v1/push/send" => { + let body = req.into_body().collect().await.unwrap().to_bytes(); + let v: serde_json::Value = serde_json::from_slice(&body).unwrap_or_default(); + st.push_sends.lock().unwrap().push(( + v["title"].as_str().unwrap_or("").to_string(), + v["body"].as_str().unwrap_or("").to_string(), + )); + resp(204, "") + } "/internal/v1/tool/context" => match st.tool_ctx.lock().unwrap().clone() { Some(v) => resp(200, v.to_string()), None => resp(404, "404 page not found"), @@ -662,6 +704,10 @@ pub struct Opts { pub backends: Option>, pub engineimg: Option>, pub strata: Option>, + pub tasks: Option>, + /// Base de départ, posée avant que la façade démarre : (bucket, clé, + /// valeur). + pub seed: Vec<(String, String, String)>, } /// Comme [`start`], avec la conversation tenue par la façade @@ -697,6 +743,9 @@ pub async fn start_with( Option>, ) { let st = Arc::new(FakeGo::default()); + for (b, k, v) in &opts.seed { + st.set(b, k, v); + } if let Some(p) = opts.engine_port { st.set("config", "PORT", &p.to_string()); } @@ -766,6 +815,7 @@ pub async fn start_with( backends: opts.backends, engineimg: opts.engineimg, strata: opts.strata, + tasks: opts.tasks, }; tokio::spawn(loki_rs::proxy::serve(fl, cfg, async move { let _ = rx.await; diff --git a/rust/loki-rs/tests/lot2.rs b/rust/loki-rs/tests/lot2.rs index d3fc44f..2022452 100644 --- a/rust/loki-rs/tests/lot2.rs +++ b/rust/loki-rs/tests/lot2.rs @@ -109,6 +109,7 @@ async fn banc(cle: Option<&str>) -> Banc { backends: None, engineimg: None, strata: None, + tasks: None, }; tokio::spawn(loki_rs::proxy::serve(l, cfg, async move { let _ = rx.await; diff --git a/rust/loki-rs/tests/parity_tasks/tasks.json b/rust/loki-rs/tests/parity_tasks/tasks.json new file mode 100644 index 0000000..8652ace --- /dev/null +++ b/rust/loki-rs/tests/parity_tasks/tasks.json @@ -0,0 +1,18740 @@ +{ + "steps": [ + { + "name": "liste vide", + "now": "2026-03-28T12:00:00Z", + "files": { + "scripts/echec.sh": "#!/bin/sh\necho oups >&2\nexit 2\n", + "scripts/lent.sh": "#!/bin/sh\nsleep 30\n", + "scripts/ok.sh": "#!/bin/sh\necho bonjour\n", + "scripts/sous/rang.sh": "#!/bin/sh\necho rangé\n" + }, + "method": "GET", + "path": "/api/tasks", + "status": 200, + "out": { + "agent": false, + "mem_on": true, + "ok": true, + "paused": false, + "presets": [], + "projects": [ + { + "active": true, + "convs": 0, + "created": "{{T}}", + "name": "Générale", + "pages": 0, + "slug": "generale", + "trackers": 0 + } + ], + "running_id": "", + "scripts": [ + { + "name": "echec.sh", + "size": 31 + }, + { + "name": "lent.sh", + "size": 19 + }, + { + "name": "ok.sh", + "size": 23 + }, + { + "name": "sous/rang.sh", + "size": 22 + } + ], + "scripts_dir": "{{HOME}}/scripts", + "tasks": null, + "web_on": false + }, + "tasks": null + }, + { + "name": "agent actif, interrupteur", + "state": { + "agent": "1" + }, + "method": "GET", + "path": "/api/tasks", + "status": 200, + "out": { + "agent": true, + "mem_on": true, + "ok": true, + "paused": false, + "presets": [], + "projects": [ + { + "active": true, + "convs": 0, + "created": "{{T}}", + "name": "Générale", + "pages": 0, + "slug": "generale", + "trackers": 0 + } + ], + "running_id": "", + "scripts": [ + { + "name": "echec.sh", + "size": 31 + }, + { + "name": "lent.sh", + "size": 19 + }, + { + "name": "ok.sh", + "size": 23 + }, + { + "name": "sous/rang.sh", + "size": 22 + } + ], + "scripts_dir": "{{HOME}}/scripts", + "tasks": null, + "web_on": false + }, + "tasks": null + }, + { + "name": "mémoire coupée, internet armé", + "config": { + "MEM_MODE": "off" + }, + "state": { + "internet": "1" + }, + "method": "GET", + "path": "/api/tasks", + "status": 200, + "out": { + "agent": true, + "mem_on": false, + "ok": true, + "paused": false, + "presets": [], + "projects": [ + { + "active": true, + "convs": 0, + "created": "{{T}}", + "name": "Générale", + "pages": 0, + "slug": "generale", + "trackers": 0 + } + ], + "running_id": "", + "scripts": [ + { + "name": "echec.sh", + "size": 31 + }, + { + "name": "lent.sh", + "size": 19 + }, + { + "name": "ok.sh", + "size": 23 + }, + { + "name": "sous/rang.sh", + "size": 22 + } + ], + "scripts_dir": "{{HOME}}/scripts", + "tasks": null, + "web_on": true + }, + "tasks": null + }, + { + "name": "corps illisible", + "method": "POST", + "path": "/api/tasks/save", + "body": "{\"enabled\":\"oui\"}", + "status": 400, + "out": { + "error": "json: cannot unmarshal string into Go struct field .enabled of type bool", + "ok": false + }, + "tasks": null + }, + { + "name": "sans nom", + "method": "POST", + "path": "/api/tasks/save", + "body": "{\"name\":\" \",\"prompt\":\"x\",\"schedule\":\"@every 1h\"}", + "status": 400, + "out": { + "error": "nom obligatoire", + "ok": false + }, + "tasks": null + }, + { + "name": "sans consigne", + "method": "POST", + "path": "/api/tasks/save", + "body": "{\"name\":\"x\",\"schedule\":\"@every 1h\"}", + "status": 400, + "out": { + "error": "consigne obligatoire", + "ok": false + }, + "tasks": null + }, + { + "name": "script sans nom de script", + "method": "POST", + "path": "/api/tasks/save", + "body": "{\"name\":\"x\",\"kind\":\"script\",\"schedule\":\"@every 1h\"}", + "status": 400, + "out": { + "error": "script obligatoire pour une tâche script", + "ok": false + }, + "tasks": null + }, + { + "name": "script absent", + "method": "POST", + "path": "/api/tasks/save", + "body": "{\"name\":\"x\",\"kind\":\"script\",\"script\":\"absent.sh\",\"schedule\":\"@every 1h\"}", + "status": 400, + "out": { + "error": "script introuvable : absent.sh", + "ok": false + }, + "tasks": null + }, + { + "name": "script : dossier", + "method": "POST", + "path": "/api/tasks/save", + "body": "{\"name\":\"x\",\"kind\":\"script\",\"script\":\"sous\",\"schedule\":\"@every 1h\"}", + "status": 400, + "out": { + "error": "sous est un dossier, pas un script", + "ok": false + }, + "tasks": null + }, + { + "name": "script : remontée", + "method": "POST", + "path": "/api/tasks/save", + "body": "{\"name\":\"x\",\"kind\":\"script\",\"script\":\"../x.sh\",\"schedule\":\"@every 1h\"}", + "status": 400, + "out": { + "error": "nom de script invalide (pas de chemin absolu ni de ../) : ../x.sh", + "ok": false + }, + "tasks": null + }, + { + "name": "script : chemin absolu", + "method": "POST", + "path": "/api/tasks/save", + "body": "{\"name\":\"x\",\"kind\":\"script\",\"script\":\"/etc/x\",\"schedule\":\"@every 1h\"}", + "status": 400, + "out": { + "error": "nom de script invalide (commence par un séparateur) : /etc/x", + "ok": false + }, + "tasks": null + }, + { + "name": "script : séparateur Windows", + "method": "POST", + "path": "/api/tasks/save", + "body": "{\"name\":\"x\",\"kind\":\"script\",\"script\":\"\\\\x.sh\",\"schedule\":\"@every 1h\"}", + "status": 400, + "out": { + "error": "nom de script invalide (commence par un séparateur) : \\x.sh", + "ok": false + }, + "tasks": null + }, + { + "name": "script : caractère interdit", + "method": "POST", + "path": "/api/tasks/save", + "body": "{\"name\":\"x\",\"kind\":\"script\",\"script\":\"a;b\",\"schedule\":\"@every 1h\"}", + "status": 400, + "out": { + "error": "nom de script invalide (caractère interdit \";\") : a;b", + "ok": false + }, + "tasks": null + }, + { + "name": "fréquence vide", + "method": "POST", + "path": "/api/tasks/save", + "body": "{\"name\":\"x\",\"prompt\":\"Fais le point.\",\"enabled\":true}", + "status": 400, + "out": { + "error": "fréquence invalide : fréquence vide", + "ok": false + }, + "tasks": null + }, + { + "name": "intervalle trop court", + "method": "POST", + "path": "/api/tasks/save", + "body": "{\"name\":\"x\",\"prompt\":\"Fais le point.\",\"enabled\":true,\"schedule\":\"@every 30s\"}", + "status": 400, + "out": { + "error": "fréquence invalide : intervalle minimum : 1 minute", + "ok": false + }, + "tasks": null + }, + { + "name": "intervalle illisible", + "method": "POST", + "path": "/api/tasks/save", + "body": "{\"name\":\"x\",\"prompt\":\"Fais le point.\",\"enabled\":true,\"schedule\":\"@every deux heures\"}", + "status": 400, + "out": { + "error": "fréquence invalide : intervalle invalide (ex. « 2h », « 30m ») : time: invalid duration \"deux heures\"", + "ok": false + }, + "tasks": null + }, + { + "name": "intervalle sans unité", + "method": "POST", + "path": "/api/tasks/save", + "body": "{\"name\":\"x\",\"prompt\":\"Fais le point.\",\"enabled\":true,\"schedule\":\"@every 5\"}", + "status": 400, + "out": { + "error": "fréquence invalide : intervalle invalide (ex. « 2h », « 30m ») : time: missing unit in duration \"5\"", + "ok": false + }, + "tasks": null + }, + { + "name": "jours invalides", + "method": "POST", + "path": "/api/tasks/save", + "body": "{\"name\":\"x\",\"prompt\":\"Fais le point.\",\"enabled\":true,\"schedule\":\"@every 0d\"}", + "status": 400, + "out": { + "error": "fréquence invalide : nombre de jours invalide : \"0d\"", + "ok": false + }, + "tasks": null + }, + { + "name": "heure hors bornes", + "method": "POST", + "path": "/api/tasks/save", + "body": "{\"name\":\"x\",\"prompt\":\"Fais le point.\",\"enabled\":true,\"schedule\":\"@every 1d@24:00\"}", + "status": 400, + "out": { + "error": "fréquence invalide : heure hors bornes : \"24:00\"", + "ok": false + }, + "tasks": null + }, + { + "name": "heure sans deux-points", + "method": "POST", + "path": "/api/tasks/save", + "body": "{\"name\":\"x\",\"prompt\":\"Fais le point.\",\"enabled\":true,\"schedule\":\"@every 1d@9h\"}", + "status": 400, + "out": { + "error": "fréquence invalide : heure invalide (attendu HH:MM) : \"9h\"", + "ok": false + }, + "tasks": null + }, + { + "name": "cron à 4 champs", + "method": "POST", + "path": "/api/tasks/save", + "body": "{\"name\":\"x\",\"prompt\":\"Fais le point.\",\"enabled\":true,\"schedule\":\"0 9 * *\"}", + "status": 400, + "out": { + "error": "fréquence invalide : cron : 5 champs attendus « min heure jour mois jour-semaine », reçu 4", + "ok": false + }, + "tasks": null + }, + { + "name": "cron : minute hors bornes", + "method": "POST", + "path": "/api/tasks/save", + "body": "{\"name\":\"x\",\"prompt\":\"Fais le point.\",\"enabled\":true,\"schedule\":\"61 9 * * *\"}", + "status": 400, + "out": { + "error": "fréquence invalide : champ minute : valeur 61 hors bornes [0-59]", + "ok": false + }, + "tasks": null + }, + { + "name": "cron : jour en lettres", + "method": "POST", + "path": "/api/tasks/save", + "body": "{\"name\":\"x\",\"prompt\":\"Fais le point.\",\"enabled\":true,\"schedule\":\"0 9 * * sun\"}", + "status": 400, + "out": { + "error": "fréquence invalide : champ jour-semaine : valeur invalide : \"sun\"", + "ok": false + }, + "tasks": null + }, + { + "name": "cron : plage inversée", + "method": "POST", + "path": "/api/tasks/save", + "body": "{\"name\":\"x\",\"prompt\":\"Fais le point.\",\"enabled\":true,\"schedule\":\"0 9 5-1 * *\"}", + "status": 400, + "out": { + "error": "fréquence invalide : champ jour-du-mois : plage inversée : \"5-1\"", + "ok": false + }, + "tasks": null + }, + { + "name": "cron : pas nul", + "method": "POST", + "path": "/api/tasks/save", + "body": "{\"name\":\"x\",\"prompt\":\"Fais le point.\",\"enabled\":true,\"schedule\":\"*/0 * * * *\"}", + "status": 400, + "out": { + "error": "fréquence invalide : champ minute : pas invalide : \"*/0\"", + "ok": false + }, + "tasks": null + }, + { + "name": "cron jamais satisfait", + "method": "POST", + "path": "/api/tasks/save", + "body": "{\"name\":\"x\",\"prompt\":\"Fais le point.\",\"enabled\":true,\"schedule\":\"0 0 30 2 *\"}", + "status": 400, + "out": { + "error": "fréquence invalide : fréquence jamais satisfaite : \"0 0 30 2 *\"", + "ok": false + }, + "tasks": null + }, + { + "name": "une fois, date illisible", + "method": "POST", + "path": "/api/tasks/save", + "body": "{\"name\":\"x\",\"prompt\":\"Fais le point.\",\"enabled\":true,\"schedule\":\"@once 2026-04-31 10:00\"}", + "status": 400, + "out": { + "error": "fréquence invalide : date invalide (attendu « @once AAAA-MM-JJ HH:MM »)", + "ok": false + }, + "tasks": null + }, + { + "name": "une fois, déjà passé", + "method": "POST", + "path": "/api/tasks/save", + "body": "{\"name\":\"x\",\"prompt\":\"Fais le point.\",\"enabled\":true,\"schedule\":\"@once 2026-03-28 13:00\",\"tz\":\"Europe/Paris\"}", + "status": 400, + "out": { + "error": "fréquence invalide : 2026-03-28 13:00 est déjà passé (il est 2026-03-28 13:00)", + "ok": false + }, + "tasks": null + }, + { + "name": "toutes les 2 h", + "method": "POST", + "path": "/api/tasks/save", + "body": "{\"name\":\"B toutes les 2 h\",\"prompt\":\"Fais le point.\",\"enabled\":true,\"schedule\":\"@every 2h\"}", + "status": 200, + "out": { + "id": "{{ID1}}", + "ok": true + }, + "tasks": [ + { + "enabled": true, + "id": "{{ID1}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "B toutes les 2 h", + "next_run": 1774706400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 2h", + "tz": "" + } + ] + }, + { + "name": "toutes les 1 h 30", + "method": "POST", + "path": "/api/tasks/save", + "body": "{\"name\":\"C une heure et demie\",\"prompt\":\"Fais le point.\",\"enabled\":true,\"schedule\":\"@EVERY 1.5h\",\"enabled\":false}", + "status": 200, + "out": { + "id": "{{ID2}}", + "ok": true + }, + "tasks": [ + { + "enabled": true, + "id": "{{ID1}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "B toutes les 2 h", + "next_run": 1774706400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 2h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID2}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "C une heure et demie", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@EVERY 1.5h", + "tz": "" + } + ] + }, + { + "name": "tous les jours à 23:00, Paris", + "method": "POST", + "path": "/api/tasks/save", + "body": "{\"name\":\"D chaque soir\",\"prompt\":\"Fais le point.\",\"enabled\":true,\"schedule\":\"@every 1d@23:00\",\"tz\":\"Europe/Paris\"}", + "status": 200, + "out": { + "id": "{{ID3}}", + "ok": true + }, + "tasks": [ + { + "enabled": true, + "id": "{{ID1}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "B toutes les 2 h", + "next_run": 1774706400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 2h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID2}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "C une heure et demie", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@EVERY 1.5h", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID3}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "D chaque soir", + "next_run": 1774735200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 1d@23:00", + "tz": "Europe/Paris" + } + ] + }, + { + "name": "tous les 3 jours à 9:30, New York", + "method": "POST", + "path": "/api/tasks/save", + "body": "{\"name\":\"E tous les trois jours\",\"prompt\":\"Fais le point.\",\"enabled\":true,\"schedule\":\"@every 3d@9:30\",\"tz\":\"America/New_York\"}", + "status": 200, + "out": { + "id": "{{ID4}}", + "ok": true + }, + "tasks": [ + { + "enabled": true, + "id": "{{ID1}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "B toutes les 2 h", + "next_run": 1774706400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 2h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID2}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "C une heure et demie", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@EVERY 1.5h", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID3}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "D chaque soir", + "next_run": 1774735200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 1d@23:00", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID4}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "E tous les trois jours", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 3d@9:30", + "tz": "America/New_York" + } + ] + }, + { + "name": "en semaine à 9 h, Calcutta", + "method": "POST", + "path": "/api/tasks/save", + "body": "{\"name\":\"F semaine\",\"prompt\":\"Fais le point.\",\"enabled\":true,\"schedule\":\"0 9 * * 1-5\",\"tz\":\"Asia/Kolkata\"}", + "status": 200, + "out": { + "id": "{{ID5}}", + "ok": true + }, + "tasks": [ + { + "enabled": true, + "id": "{{ID1}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "B toutes les 2 h", + "next_run": 1774706400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 2h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID2}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "C une heure et demie", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@EVERY 1.5h", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID3}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "D chaque soir", + "next_run": 1774735200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 1d@23:00", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID4}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "E tous les trois jours", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 3d@9:30", + "tz": "America/New_York" + }, + { + "enabled": true, + "id": "{{ID5}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "F semaine", + "next_run": 1774841400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 9 * * 1-5", + "tz": "Asia/Kolkata" + } + ] + }, + { + "name": "chaque nuit à 2:30, Paris (heure qui saute)", + "method": "POST", + "path": "/api/tasks/save", + "body": "{\"name\":\"G nuit\",\"prompt\":\"Fais le point.\",\"enabled\":true,\"schedule\":\"30 2 * * *\",\"tz\":\"Europe/Paris\"}", + "status": 200, + "out": { + "id": "{{ID6}}", + "ok": true + }, + "tasks": [ + { + "enabled": true, + "id": "{{ID1}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "B toutes les 2 h", + "next_run": 1774706400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 2h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID2}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "C une heure et demie", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@EVERY 1.5h", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID3}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "D chaque soir", + "next_run": 1774735200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 1d@23:00", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID4}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "E tous les trois jours", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 3d@9:30", + "tz": "America/New_York" + }, + { + "enabled": true, + "id": "{{ID5}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "F semaine", + "next_run": 1774841400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 9 * * 1-5", + "tz": "Asia/Kolkata" + }, + { + "enabled": true, + "id": "{{ID6}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "G nuit", + "next_run": 1774830600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "30 2 * * *", + "tz": "Europe/Paris" + } + ] + }, + { + "name": "toutes les 15 min, fuseau inconnu", + "method": "POST", + "path": "/api/tasks/save", + "body": "{\"name\":\"H quart d'heure\",\"prompt\":\"Fais le point.\",\"enabled\":true,\"schedule\":\"*/15 * * * *\",\"tz\":\"Nulle/Part\"}", + "status": 200, + "out": { + "id": "{{ID7}}", + "ok": true + }, + "tasks": [ + { + "enabled": true, + "id": "{{ID1}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "B toutes les 2 h", + "next_run": 1774706400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 2h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID2}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "C une heure et demie", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@EVERY 1.5h", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID3}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "D chaque soir", + "next_run": 1774735200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 1d@23:00", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID4}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "E tous les trois jours", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 3d@9:30", + "tz": "America/New_York" + }, + { + "enabled": true, + "id": "{{ID5}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "F semaine", + "next_run": 1774841400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 9 * * 1-5", + "tz": "Asia/Kolkata" + }, + { + "enabled": true, + "id": "{{ID6}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "G nuit", + "next_run": 1774830600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "30 2 * * *", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID7}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "H quart d'heure", + "next_run": 1774700100000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "*/15 * * * *", + "tz": "Nulle/Part" + } + ] + }, + { + "name": "le dimanche (7)", + "method": "POST", + "path": "/api/tasks/save", + "body": "{\"name\":\"I dimanche\",\"prompt\":\"Fais le point.\",\"enabled\":true,\"schedule\":\"0 8 * * 7\"}", + "status": 200, + "out": { + "id": "{{ID8}}", + "ok": true + }, + "tasks": [ + { + "enabled": true, + "id": "{{ID1}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "B toutes les 2 h", + "next_run": 1774706400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 2h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID2}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "C une heure et demie", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@EVERY 1.5h", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID3}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "D chaque soir", + "next_run": 1774735200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 1d@23:00", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID4}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "E tous les trois jours", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 3d@9:30", + "tz": "America/New_York" + }, + { + "enabled": true, + "id": "{{ID5}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "F semaine", + "next_run": 1774841400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 9 * * 1-5", + "tz": "Asia/Kolkata" + }, + { + "enabled": true, + "id": "{{ID6}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "G nuit", + "next_run": 1774830600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "30 2 * * *", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID7}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "H quart d'heure", + "next_run": 1774700100000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "*/15 * * * *", + "tz": "Nulle/Part" + }, + { + "enabled": true, + "id": "{{ID8}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "I dimanche", + "next_run": 1774771200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 8 * * 7", + "tz": "" + } + ] + }, + { + "name": "une seule fois", + "method": "POST", + "path": "/api/tasks/save", + "body": "{\"name\":\"J une fois\",\"prompt\":\"Fais le point.\",\"enabled\":true,\"schedule\":\"@once 2026-03-29 03:30\",\"tz\":\"Europe/Paris\"}", + "status": 200, + "out": { + "id": "{{ID9}}", + "ok": true + }, + "tasks": [ + { + "enabled": true, + "id": "{{ID1}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "B toutes les 2 h", + "next_run": 1774706400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 2h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID2}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "C une heure et demie", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@EVERY 1.5h", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID3}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "D chaque soir", + "next_run": 1774735200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 1d@23:00", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID4}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "E tous les trois jours", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 3d@9:30", + "tz": "America/New_York" + }, + { + "enabled": true, + "id": "{{ID5}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "F semaine", + "next_run": 1774841400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 9 * * 1-5", + "tz": "Asia/Kolkata" + }, + { + "enabled": true, + "id": "{{ID6}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "G nuit", + "next_run": 1774830600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "30 2 * * *", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID7}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "H quart d'heure", + "next_run": 1774700100000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "*/15 * * * *", + "tz": "Nulle/Part" + }, + { + "enabled": true, + "id": "{{ID8}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "I dimanche", + "next_run": 1774771200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 8 * * 7", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID9}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "J une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@once 2026-03-29 03:30", + "tz": "Europe/Paris" + } + ] + }, + { + "name": "tâche script", + "method": "POST", + "path": "/api/tasks/save", + "body": "{\"name\":\"A script\",\"kind\":\"script\",\"script\":\" ok.sh \",\"schedule\":\"@every 1h\",\"enabled\":true}", + "status": 200, + "out": { + "id": "{{ID10}}", + "ok": true + }, + "tasks": [ + { + "enabled": true, + "id": "{{ID10}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "A script", + "next_run": 1774702800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "ok.sh", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID1}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "B toutes les 2 h", + "next_run": 1774706400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 2h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID2}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "C une heure et demie", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@EVERY 1.5h", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID3}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "D chaque soir", + "next_run": 1774735200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 1d@23:00", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID4}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "E tous les trois jours", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 3d@9:30", + "tz": "America/New_York" + }, + { + "enabled": true, + "id": "{{ID5}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "F semaine", + "next_run": 1774841400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 9 * * 1-5", + "tz": "Asia/Kolkata" + }, + { + "enabled": true, + "id": "{{ID6}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "G nuit", + "next_run": 1774830600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "30 2 * * *", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID7}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "H quart d'heure", + "next_run": 1774700100000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "*/15 * * * *", + "tz": "Nulle/Part" + }, + { + "enabled": true, + "id": "{{ID8}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "I dimanche", + "next_run": 1774771200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 8 * * 7", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID9}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "J une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@once 2026-03-29 03:30", + "tz": "Europe/Paris" + } + ] + }, + { + "name": "tâche script en échec", + "method": "POST", + "path": "/api/tasks/save", + "body": "{\"name\":\"K échec\",\"kind\":\"script\",\"script\":\"echec.sh\",\"schedule\":\"@every 1h\",\"enabled\":true}", + "status": 200, + "out": { + "id": "{{ID11}}", + "ok": true + }, + "tasks": [ + { + "enabled": true, + "id": "{{ID10}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "A script", + "next_run": 1774702800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "ok.sh", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID1}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "B toutes les 2 h", + "next_run": 1774706400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 2h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID2}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "C une heure et demie", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@EVERY 1.5h", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID3}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "D chaque soir", + "next_run": 1774735200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 1d@23:00", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID4}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "E tous les trois jours", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 3d@9:30", + "tz": "America/New_York" + }, + { + "enabled": true, + "id": "{{ID5}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "F semaine", + "next_run": 1774841400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 9 * * 1-5", + "tz": "Asia/Kolkata" + }, + { + "enabled": true, + "id": "{{ID6}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "G nuit", + "next_run": 1774830600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "30 2 * * *", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID7}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "H quart d'heure", + "next_run": 1774700100000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "*/15 * * * *", + "tz": "Nulle/Part" + }, + { + "enabled": true, + "id": "{{ID8}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "I dimanche", + "next_run": 1774771200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 8 * * 7", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID9}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "J une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@once 2026-03-29 03:30", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID11}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "K échec", + "next_run": 1774702800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "echec.sh", + "tz": "" + } + ] + }, + { + "name": "tâche script lente", + "method": "POST", + "path": "/api/tasks/save", + "body": "{\"name\":\"L lente\",\"kind\":\"script\",\"script\":\"lent.sh\",\"schedule\":\"@every 1h\"}", + "status": 200, + "out": { + "id": "{{ID12}}", + "ok": true + }, + "tasks": [ + { + "enabled": true, + "id": "{{ID10}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "A script", + "next_run": 1774702800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "ok.sh", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID1}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "B toutes les 2 h", + "next_run": 1774706400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 2h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID2}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "C une heure et demie", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@EVERY 1.5h", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID3}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "D chaque soir", + "next_run": 1774735200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 1d@23:00", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID4}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "E tous les trois jours", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 3d@9:30", + "tz": "America/New_York" + }, + { + "enabled": true, + "id": "{{ID5}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "F semaine", + "next_run": 1774841400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 9 * * 1-5", + "tz": "Asia/Kolkata" + }, + { + "enabled": true, + "id": "{{ID6}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "G nuit", + "next_run": 1774830600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "30 2 * * *", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID7}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "H quart d'heure", + "next_run": 1774700100000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "*/15 * * * *", + "tz": "Nulle/Part" + }, + { + "enabled": true, + "id": "{{ID8}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "I dimanche", + "next_run": 1774771200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 8 * * 7", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID9}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "J une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@once 2026-03-29 03:30", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID11}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "K échec", + "next_run": 1774702800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "echec.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID12}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "L lente", + "next_run": 1774702800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "lent.sh", + "tz": "" + } + ] + }, + { + "name": "script une seule fois", + "method": "POST", + "path": "/api/tasks/save", + "body": "{\"name\":\"N script une fois\",\"kind\":\"script\",\"script\":\"ok.sh\",\"schedule\":\"@once 2026-03-29 03:30\",\"tz\":\"Europe/Paris\",\"enabled\":true}", + "status": 200, + "out": { + "id": "{{ID13}}", + "ok": true + }, + "tasks": [ + { + "enabled": true, + "id": "{{ID10}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "A script", + "next_run": 1774702800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "ok.sh", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID1}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "B toutes les 2 h", + "next_run": 1774706400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 2h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID2}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "C une heure et demie", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@EVERY 1.5h", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID3}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "D chaque soir", + "next_run": 1774735200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 1d@23:00", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID4}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "E tous les trois jours", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 3d@9:30", + "tz": "America/New_York" + }, + { + "enabled": true, + "id": "{{ID5}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "F semaine", + "next_run": 1774841400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 9 * * 1-5", + "tz": "Asia/Kolkata" + }, + { + "enabled": true, + "id": "{{ID6}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "G nuit", + "next_run": 1774830600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "30 2 * * *", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID7}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "H quart d'heure", + "next_run": 1774700100000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "*/15 * * * *", + "tz": "Nulle/Part" + }, + { + "enabled": true, + "id": "{{ID8}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "I dimanche", + "next_run": 1774771200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 8 * * 7", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID9}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "J une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@once 2026-03-29 03:30", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID11}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "K échec", + "next_run": 1774702800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "echec.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID12}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "L lente", + "next_run": 1774702800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "lent.sh", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID13}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "N script une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@once 2026-03-29 03:30", + "script": "ok.sh", + "tz": "Europe/Paris" + } + ] + }, + { + "name": "tâche avec id inconnu", + "method": "POST", + "path": "/api/tasks/save", + "body": "{\"id\":\"zz\",\"name\":\"M imposée\",\"prompt\":\"x\",\"schedule\":\"@every 1h\",\"project\":\"inconnu\",\"no_mem\":true,\"no_web\":true}", + "status": 200, + "out": { + "id": "zz", + "ok": true + }, + "tasks": [ + { + "enabled": true, + "id": "{{ID10}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "A script", + "next_run": 1774702800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "ok.sh", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID1}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "B toutes les 2 h", + "next_run": 1774706400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 2h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID2}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "C une heure et demie", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@EVERY 1.5h", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID3}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "D chaque soir", + "next_run": 1774735200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 1d@23:00", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID4}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "E tous les trois jours", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 3d@9:30", + "tz": "America/New_York" + }, + { + "enabled": true, + "id": "{{ID5}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "F semaine", + "next_run": 1774841400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 9 * * 1-5", + "tz": "Asia/Kolkata" + }, + { + "enabled": true, + "id": "{{ID6}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "G nuit", + "next_run": 1774830600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "30 2 * * *", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID7}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "H quart d'heure", + "next_run": 1774700100000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "*/15 * * * *", + "tz": "Nulle/Part" + }, + { + "enabled": true, + "id": "{{ID8}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "I dimanche", + "next_run": 1774771200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 8 * * 7", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID9}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "J une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@once 2026-03-29 03:30", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID11}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "K échec", + "next_run": 1774702800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "echec.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID12}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "L lente", + "next_run": 1774702800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "lent.sh", + "tz": "" + }, + { + "enabled": false, + "id": "zz", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "M imposée", + "next_run": 1774702800000, + "no_mem": true, + "no_web": true, + "preset": "", + "project": "generale", + "prompt": "x", + "schedule": "@every 1h", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID13}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "N script une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@once 2026-03-29 03:30", + "script": "ok.sh", + "tz": "Europe/Paris" + } + ] + }, + { + "name": "liste", + "method": "GET", + "path": "/api/tasks", + "status": 200, + "out": { + "agent": true, + "mem_on": false, + "ok": true, + "paused": false, + "presets": [], + "projects": [ + { + "active": true, + "convs": 0, + "created": "{{T}}", + "name": "Générale", + "pages": 0, + "slug": "generale", + "trackers": 0 + } + ], + "running_id": "", + "scripts": [ + { + "name": "echec.sh", + "size": 31 + }, + { + "name": "lent.sh", + "size": 19 + }, + { + "name": "ok.sh", + "size": 23 + }, + { + "name": "sous/rang.sh", + "size": 22 + } + ], + "scripts_dir": "{{HOME}}/scripts", + "tasks": [ + { + "enabled": true, + "id": "{{ID10}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "A script", + "next_run": 1774702800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "ok.sh", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID1}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "B toutes les 2 h", + "next_run": 1774706400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 2h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID2}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "C une heure et demie", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@EVERY 1.5h", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID3}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "D chaque soir", + "next_run": 1774735200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 1d@23:00", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID4}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "E tous les trois jours", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 3d@9:30", + "tz": "America/New_York" + }, + { + "enabled": true, + "id": "{{ID5}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "F semaine", + "next_run": 1774841400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 9 * * 1-5", + "tz": "Asia/Kolkata" + }, + { + "enabled": true, + "id": "{{ID6}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "G nuit", + "next_run": 1774830600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "30 2 * * *", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID7}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "H quart d'heure", + "next_run": 1774700100000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "*/15 * * * *", + "tz": "Nulle/Part" + }, + { + "enabled": true, + "id": "{{ID8}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "I dimanche", + "next_run": 1774771200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 8 * * 7", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID9}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "J une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@once 2026-03-29 03:30", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID11}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "K échec", + "next_run": 1774702800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "echec.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID12}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "L lente", + "next_run": 1774702800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "lent.sh", + "tz": "" + }, + { + "enabled": false, + "id": "zz", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "M imposée", + "next_run": 1774702800000, + "no_mem": true, + "no_web": true, + "preset": "", + "project": "generale", + "prompt": "x", + "schedule": "@every 1h", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID13}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "N script une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@once 2026-03-29 03:30", + "script": "ok.sh", + "tz": "Europe/Paris" + } + ], + "web_on": true + }, + "tasks": [ + { + "enabled": true, + "id": "{{ID10}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "A script", + "next_run": 1774702800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "ok.sh", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID1}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "B toutes les 2 h", + "next_run": 1774706400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 2h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID2}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "C une heure et demie", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@EVERY 1.5h", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID3}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "D chaque soir", + "next_run": 1774735200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 1d@23:00", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID4}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "E tous les trois jours", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 3d@9:30", + "tz": "America/New_York" + }, + { + "enabled": true, + "id": "{{ID5}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "F semaine", + "next_run": 1774841400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 9 * * 1-5", + "tz": "Asia/Kolkata" + }, + { + "enabled": true, + "id": "{{ID6}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "G nuit", + "next_run": 1774830600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "30 2 * * *", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID7}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "H quart d'heure", + "next_run": 1774700100000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "*/15 * * * *", + "tz": "Nulle/Part" + }, + { + "enabled": true, + "id": "{{ID8}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "I dimanche", + "next_run": 1774771200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 8 * * 7", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID9}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "J une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@once 2026-03-29 03:30", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID11}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "K échec", + "next_run": 1774702800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "echec.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID12}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "L lente", + "next_run": 1774702800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "lent.sh", + "tz": "" + }, + { + "enabled": false, + "id": "zz", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "M imposée", + "next_run": 1774702800000, + "no_mem": true, + "no_web": true, + "preset": "", + "project": "generale", + "prompt": "x", + "schedule": "@every 1h", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID13}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "N script une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@once 2026-03-29 03:30", + "script": "ok.sh", + "tz": "Europe/Paris" + } + ] + }, + { + "name": "renommer sans changer la fréquence", + "now": "2026-03-28T12:30:00Z", + "method": "POST", + "path": "/api/tasks/save", + "body": "{\"id\":\"{{ID1}}\",\"name\":\"B renommée\",\"prompt\":\"Autre.\",\"schedule\":\"@every 2h\",\"enabled\":true}", + "status": 200, + "out": { + "id": "{{ID1}}", + "ok": true + }, + "tasks": [ + { + "enabled": true, + "id": "{{ID10}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "A script", + "next_run": 1774702800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "ok.sh", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID1}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "B renommée", + "next_run": 1774706400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Autre.", + "schedule": "@every 2h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID2}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "C une heure et demie", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@EVERY 1.5h", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID3}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "D chaque soir", + "next_run": 1774735200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 1d@23:00", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID4}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "E tous les trois jours", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 3d@9:30", + "tz": "America/New_York" + }, + { + "enabled": true, + "id": "{{ID5}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "F semaine", + "next_run": 1774841400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 9 * * 1-5", + "tz": "Asia/Kolkata" + }, + { + "enabled": true, + "id": "{{ID6}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "G nuit", + "next_run": 1774830600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "30 2 * * *", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID7}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "H quart d'heure", + "next_run": 1774700100000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "*/15 * * * *", + "tz": "Nulle/Part" + }, + { + "enabled": true, + "id": "{{ID8}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "I dimanche", + "next_run": 1774771200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 8 * * 7", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID9}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "J une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@once 2026-03-29 03:30", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID11}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "K échec", + "next_run": 1774702800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "echec.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID12}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "L lente", + "next_run": 1774702800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "lent.sh", + "tz": "" + }, + { + "enabled": false, + "id": "zz", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "M imposée", + "next_run": 1774702800000, + "no_mem": true, + "no_web": true, + "preset": "", + "project": "generale", + "prompt": "x", + "schedule": "@every 1h", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID13}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "N script une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@once 2026-03-29 03:30", + "script": "ok.sh", + "tz": "Europe/Paris" + } + ] + }, + { + "name": "changer la fréquence", + "method": "POST", + "path": "/api/tasks/save", + "body": "{\"id\":\"{{ID1}}\",\"name\":\"B renommée\",\"prompt\":\"Autre.\",\"schedule\":\"@every 3h\",\"enabled\":true}", + "status": 200, + "out": { + "id": "{{ID1}}", + "ok": true + }, + "tasks": [ + { + "enabled": true, + "id": "{{ID10}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "A script", + "next_run": 1774702800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "ok.sh", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID1}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "B renommée", + "next_run": 1774711800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Autre.", + "schedule": "@every 3h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID2}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "C une heure et demie", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@EVERY 1.5h", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID3}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "D chaque soir", + "next_run": 1774735200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 1d@23:00", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID4}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "E tous les trois jours", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 3d@9:30", + "tz": "America/New_York" + }, + { + "enabled": true, + "id": "{{ID5}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "F semaine", + "next_run": 1774841400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 9 * * 1-5", + "tz": "Asia/Kolkata" + }, + { + "enabled": true, + "id": "{{ID6}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "G nuit", + "next_run": 1774830600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "30 2 * * *", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID7}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "H quart d'heure", + "next_run": 1774700100000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "*/15 * * * *", + "tz": "Nulle/Part" + }, + { + "enabled": true, + "id": "{{ID8}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "I dimanche", + "next_run": 1774771200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 8 * * 7", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID9}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "J une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@once 2026-03-29 03:30", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID11}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "K échec", + "next_run": 1774702800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "echec.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID12}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "L lente", + "next_run": 1774702800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "lent.sh", + "tz": "" + }, + { + "enabled": false, + "id": "zz", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "M imposée", + "next_run": 1774702800000, + "no_mem": true, + "no_web": true, + "preset": "", + "project": "generale", + "prompt": "x", + "schedule": "@every 1h", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID13}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "N script une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@once 2026-03-29 03:30", + "script": "ok.sh", + "tz": "Europe/Paris" + } + ] + }, + { + "name": "bascule d'une tâche absente", + "method": "POST", + "path": "/api/tasks/toggle", + "body": "{\"id\":\"absente\",\"on\":true}", + "status": 404, + "out": { + "error": "tâche introuvable", + "ok": false + }, + "tasks": [ + { + "enabled": true, + "id": "{{ID10}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "A script", + "next_run": 1774702800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "ok.sh", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID1}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "B renommée", + "next_run": 1774711800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Autre.", + "schedule": "@every 3h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID2}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "C une heure et demie", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@EVERY 1.5h", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID3}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "D chaque soir", + "next_run": 1774735200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 1d@23:00", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID4}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "E tous les trois jours", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 3d@9:30", + "tz": "America/New_York" + }, + { + "enabled": true, + "id": "{{ID5}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "F semaine", + "next_run": 1774841400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 9 * * 1-5", + "tz": "Asia/Kolkata" + }, + { + "enabled": true, + "id": "{{ID6}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "G nuit", + "next_run": 1774830600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "30 2 * * *", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID7}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "H quart d'heure", + "next_run": 1774700100000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "*/15 * * * *", + "tz": "Nulle/Part" + }, + { + "enabled": true, + "id": "{{ID8}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "I dimanche", + "next_run": 1774771200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 8 * * 7", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID9}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "J une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@once 2026-03-29 03:30", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID11}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "K échec", + "next_run": 1774702800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "echec.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID12}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "L lente", + "next_run": 1774702800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "lent.sh", + "tz": "" + }, + { + "enabled": false, + "id": "zz", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "M imposée", + "next_run": 1774702800000, + "no_mem": true, + "no_web": true, + "preset": "", + "project": "generale", + "prompt": "x", + "schedule": "@every 1h", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID13}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "N script une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@once 2026-03-29 03:30", + "script": "ok.sh", + "tz": "Europe/Paris" + } + ] + }, + { + "name": "réactiver", + "now": "2026-03-28T13:00:00Z", + "method": "POST", + "path": "/api/tasks/toggle", + "body": "{\"id\":\"{{ID2}}\",\"on\":true}", + "status": 200, + "out": { + "ok": true + }, + "tasks": [ + { + "enabled": true, + "id": "{{ID10}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "A script", + "next_run": 1774702800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "ok.sh", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID1}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "B renommée", + "next_run": 1774711800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Autre.", + "schedule": "@every 3h", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID2}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "C une heure et demie", + "next_run": 1774708200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@EVERY 1.5h", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID3}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "D chaque soir", + "next_run": 1774735200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 1d@23:00", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID4}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "E tous les trois jours", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 3d@9:30", + "tz": "America/New_York" + }, + { + "enabled": true, + "id": "{{ID5}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "F semaine", + "next_run": 1774841400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 9 * * 1-5", + "tz": "Asia/Kolkata" + }, + { + "enabled": true, + "id": "{{ID6}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "G nuit", + "next_run": 1774830600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "30 2 * * *", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID7}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "H quart d'heure", + "next_run": 1774700100000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "*/15 * * * *", + "tz": "Nulle/Part" + }, + { + "enabled": true, + "id": "{{ID8}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "I dimanche", + "next_run": 1774771200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 8 * * 7", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID9}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "J une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@once 2026-03-29 03:30", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID11}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "K échec", + "next_run": 1774702800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "echec.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID12}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "L lente", + "next_run": 1774702800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "lent.sh", + "tz": "" + }, + { + "enabled": false, + "id": "zz", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "M imposée", + "next_run": 1774702800000, + "no_mem": true, + "no_web": true, + "preset": "", + "project": "generale", + "prompt": "x", + "schedule": "@every 1h", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID13}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "N script une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@once 2026-03-29 03:30", + "script": "ok.sh", + "tz": "Europe/Paris" + } + ] + }, + { + "name": "désactiver", + "method": "POST", + "path": "/api/tasks/toggle", + "body": "{\"id\":\"{{ID2}}\",\"on\":false}", + "status": 200, + "out": { + "ok": true + }, + "tasks": [ + { + "enabled": true, + "id": "{{ID10}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "A script", + "next_run": 1774702800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "ok.sh", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID1}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "B renommée", + "next_run": 1774711800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Autre.", + "schedule": "@every 3h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID2}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "C une heure et demie", + "next_run": 1774708200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@EVERY 1.5h", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID3}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "D chaque soir", + "next_run": 1774735200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 1d@23:00", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID4}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "E tous les trois jours", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 3d@9:30", + "tz": "America/New_York" + }, + { + "enabled": true, + "id": "{{ID5}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "F semaine", + "next_run": 1774841400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 9 * * 1-5", + "tz": "Asia/Kolkata" + }, + { + "enabled": true, + "id": "{{ID6}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "G nuit", + "next_run": 1774830600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "30 2 * * *", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID7}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "H quart d'heure", + "next_run": 1774700100000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "*/15 * * * *", + "tz": "Nulle/Part" + }, + { + "enabled": true, + "id": "{{ID8}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "I dimanche", + "next_run": 1774771200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 8 * * 7", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID9}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "J une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@once 2026-03-29 03:30", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID11}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "K échec", + "next_run": 1774702800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "echec.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID12}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "L lente", + "next_run": 1774702800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "lent.sh", + "tz": "" + }, + { + "enabled": false, + "id": "zz", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "M imposée", + "next_run": 1774702800000, + "no_mem": true, + "no_web": true, + "preset": "", + "project": "generale", + "prompt": "x", + "schedule": "@every 1h", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID13}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "N script une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@once 2026-03-29 03:30", + "script": "ok.sh", + "tz": "Europe/Paris" + } + ] + }, + { + "name": "suspendre tout", + "method": "POST", + "path": "/api/tasks/pause", + "body": "{\"on\":true}", + "status": 200, + "out": { + "ok": true, + "paused": true + }, + "tasks": [ + { + "enabled": true, + "id": "{{ID10}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "A script", + "next_run": 1774702800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "ok.sh", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID1}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "B renommée", + "next_run": 1774711800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Autre.", + "schedule": "@every 3h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID2}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "C une heure et demie", + "next_run": 1774708200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@EVERY 1.5h", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID3}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "D chaque soir", + "next_run": 1774735200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 1d@23:00", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID4}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "E tous les trois jours", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 3d@9:30", + "tz": "America/New_York" + }, + { + "enabled": true, + "id": "{{ID5}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "F semaine", + "next_run": 1774841400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 9 * * 1-5", + "tz": "Asia/Kolkata" + }, + { + "enabled": true, + "id": "{{ID6}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "G nuit", + "next_run": 1774830600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "30 2 * * *", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID7}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "H quart d'heure", + "next_run": 1774700100000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "*/15 * * * *", + "tz": "Nulle/Part" + }, + { + "enabled": true, + "id": "{{ID8}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "I dimanche", + "next_run": 1774771200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 8 * * 7", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID9}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "J une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@once 2026-03-29 03:30", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID11}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "K échec", + "next_run": 1774702800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "echec.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID12}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "L lente", + "next_run": 1774702800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "lent.sh", + "tz": "" + }, + { + "enabled": false, + "id": "zz", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "M imposée", + "next_run": 1774702800000, + "no_mem": true, + "no_web": true, + "preset": "", + "project": "generale", + "prompt": "x", + "schedule": "@every 1h", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID13}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "N script une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@once 2026-03-29 03:30", + "script": "ok.sh", + "tz": "Europe/Paris" + } + ] + }, + { + "name": "liste suspendue", + "method": "GET", + "path": "/api/tasks", + "status": 200, + "out": { + "agent": true, + "mem_on": false, + "ok": true, + "paused": true, + "presets": [], + "projects": [ + { + "active": true, + "convs": 0, + "created": "{{T}}", + "name": "Générale", + "pages": 0, + "slug": "generale", + "trackers": 0 + } + ], + "running_id": "", + "scripts": [ + { + "name": "echec.sh", + "size": 31 + }, + { + "name": "lent.sh", + "size": 19 + }, + { + "name": "ok.sh", + "size": 23 + }, + { + "name": "sous/rang.sh", + "size": 22 + } + ], + "scripts_dir": "{{HOME}}/scripts", + "tasks": [ + { + "enabled": true, + "id": "{{ID10}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "A script", + "next_run": 1774702800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "ok.sh", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID1}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "B renommée", + "next_run": 1774711800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Autre.", + "schedule": "@every 3h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID2}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "C une heure et demie", + "next_run": 1774708200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@EVERY 1.5h", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID3}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "D chaque soir", + "next_run": 1774735200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 1d@23:00", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID4}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "E tous les trois jours", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 3d@9:30", + "tz": "America/New_York" + }, + { + "enabled": true, + "id": "{{ID5}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "F semaine", + "next_run": 1774841400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 9 * * 1-5", + "tz": "Asia/Kolkata" + }, + { + "enabled": true, + "id": "{{ID6}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "G nuit", + "next_run": 1774830600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "30 2 * * *", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID7}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "H quart d'heure", + "next_run": 1774700100000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "*/15 * * * *", + "tz": "Nulle/Part" + }, + { + "enabled": true, + "id": "{{ID8}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "I dimanche", + "next_run": 1774771200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 8 * * 7", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID9}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "J une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@once 2026-03-29 03:30", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID11}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "K échec", + "next_run": 1774702800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "echec.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID12}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "L lente", + "next_run": 1774702800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "lent.sh", + "tz": "" + }, + { + "enabled": false, + "id": "zz", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "M imposée", + "next_run": 1774702800000, + "no_mem": true, + "no_web": true, + "preset": "", + "project": "generale", + "prompt": "x", + "schedule": "@every 1h", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID13}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "N script une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@once 2026-03-29 03:30", + "script": "ok.sh", + "tz": "Europe/Paris" + } + ], + "web_on": true + }, + "tasks": [ + { + "enabled": true, + "id": "{{ID10}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "A script", + "next_run": 1774702800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "ok.sh", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID1}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "B renommée", + "next_run": 1774711800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Autre.", + "schedule": "@every 3h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID2}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "C une heure et demie", + "next_run": 1774708200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@EVERY 1.5h", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID3}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "D chaque soir", + "next_run": 1774735200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 1d@23:00", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID4}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "E tous les trois jours", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 3d@9:30", + "tz": "America/New_York" + }, + { + "enabled": true, + "id": "{{ID5}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "F semaine", + "next_run": 1774841400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 9 * * 1-5", + "tz": "Asia/Kolkata" + }, + { + "enabled": true, + "id": "{{ID6}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "G nuit", + "next_run": 1774830600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "30 2 * * *", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID7}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "H quart d'heure", + "next_run": 1774700100000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "*/15 * * * *", + "tz": "Nulle/Part" + }, + { + "enabled": true, + "id": "{{ID8}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "I dimanche", + "next_run": 1774771200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 8 * * 7", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID9}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "J une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@once 2026-03-29 03:30", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID11}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "K échec", + "next_run": 1774702800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "echec.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID12}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "L lente", + "next_run": 1774702800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "lent.sh", + "tz": "" + }, + { + "enabled": false, + "id": "zz", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "M imposée", + "next_run": 1774702800000, + "no_mem": true, + "no_web": true, + "preset": "", + "project": "generale", + "prompt": "x", + "schedule": "@every 1h", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID13}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "N script une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@once 2026-03-29 03:30", + "script": "ok.sh", + "tz": "Europe/Paris" + } + ] + }, + { + "name": "tic suspendu", + "now": "2026-03-28T23:00:00Z", + "tick": true, + "tasks": [ + { + "enabled": true, + "id": "{{ID10}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "A script", + "next_run": 1774702800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "ok.sh", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID1}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "B renommée", + "next_run": 1774711800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Autre.", + "schedule": "@every 3h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID2}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "C une heure et demie", + "next_run": 1774708200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@EVERY 1.5h", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID3}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "D chaque soir", + "next_run": 1774735200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 1d@23:00", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID4}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "E tous les trois jours", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 3d@9:30", + "tz": "America/New_York" + }, + { + "enabled": true, + "id": "{{ID5}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "F semaine", + "next_run": 1774841400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 9 * * 1-5", + "tz": "Asia/Kolkata" + }, + { + "enabled": true, + "id": "{{ID6}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "G nuit", + "next_run": 1774830600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "30 2 * * *", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID7}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "H quart d'heure", + "next_run": 1774700100000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "*/15 * * * *", + "tz": "Nulle/Part" + }, + { + "enabled": true, + "id": "{{ID8}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "I dimanche", + "next_run": 1774771200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 8 * * 7", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID9}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "J une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@once 2026-03-29 03:30", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID11}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "K échec", + "next_run": 1774702800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "echec.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID12}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "L lente", + "next_run": 1774702800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "lent.sh", + "tz": "" + }, + { + "enabled": false, + "id": "zz", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "M imposée", + "next_run": 1774702800000, + "no_mem": true, + "no_web": true, + "preset": "", + "project": "generale", + "prompt": "x", + "schedule": "@every 1h", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID13}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "N script une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@once 2026-03-29 03:30", + "script": "ok.sh", + "tz": "Europe/Paris" + } + ] + }, + { + "name": "reprendre", + "method": "POST", + "path": "/api/tasks/pause", + "body": "{\"on\":false}", + "status": 200, + "out": { + "ok": true, + "paused": false + }, + "tasks": [ + { + "enabled": true, + "id": "{{ID10}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "A script", + "next_run": 1774702800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "ok.sh", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID1}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "B renommée", + "next_run": 1774711800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Autre.", + "schedule": "@every 3h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID2}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "C une heure et demie", + "next_run": 1774708200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@EVERY 1.5h", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID3}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "D chaque soir", + "next_run": 1774735200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 1d@23:00", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID4}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "E tous les trois jours", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 3d@9:30", + "tz": "America/New_York" + }, + { + "enabled": true, + "id": "{{ID5}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "F semaine", + "next_run": 1774841400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 9 * * 1-5", + "tz": "Asia/Kolkata" + }, + { + "enabled": true, + "id": "{{ID6}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "G nuit", + "next_run": 1774830600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "30 2 * * *", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID7}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "H quart d'heure", + "next_run": 1774700100000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "*/15 * * * *", + "tz": "Nulle/Part" + }, + { + "enabled": true, + "id": "{{ID8}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "I dimanche", + "next_run": 1774771200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 8 * * 7", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID9}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "J une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@once 2026-03-29 03:30", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID11}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "K échec", + "next_run": 1774702800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "echec.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID12}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "L lente", + "next_run": 1774702800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "lent.sh", + "tz": "" + }, + { + "enabled": false, + "id": "zz", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "M imposée", + "next_run": 1774702800000, + "no_mem": true, + "no_web": true, + "preset": "", + "project": "generale", + "prompt": "x", + "schedule": "@every 1h", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID13}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "N script une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@once 2026-03-29 03:30", + "script": "ok.sh", + "tz": "Europe/Paris" + } + ] + }, + { + "name": "interrupteur rangé à 0 : pas suspendu", + "state": { + "tasks_paused": "0" + }, + "method": "GET", + "path": "/api/tasks", + "status": 200, + "out": { + "agent": true, + "mem_on": false, + "ok": true, + "paused": false, + "presets": [], + "projects": [ + { + "active": true, + "convs": 0, + "created": "{{T}}", + "name": "Générale", + "pages": 0, + "slug": "generale", + "trackers": 0 + } + ], + "running_id": "", + "scripts": [ + { + "name": "echec.sh", + "size": 31 + }, + { + "name": "lent.sh", + "size": 19 + }, + { + "name": "ok.sh", + "size": 23 + }, + { + "name": "sous/rang.sh", + "size": 22 + } + ], + "scripts_dir": "{{HOME}}/scripts", + "tasks": [ + { + "enabled": true, + "id": "{{ID10}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "A script", + "next_run": 1774702800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "ok.sh", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID1}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "B renommée", + "next_run": 1774711800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Autre.", + "schedule": "@every 3h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID2}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "C une heure et demie", + "next_run": 1774708200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@EVERY 1.5h", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID3}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "D chaque soir", + "next_run": 1774735200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 1d@23:00", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID4}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "E tous les trois jours", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 3d@9:30", + "tz": "America/New_York" + }, + { + "enabled": true, + "id": "{{ID5}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "F semaine", + "next_run": 1774841400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 9 * * 1-5", + "tz": "Asia/Kolkata" + }, + { + "enabled": true, + "id": "{{ID6}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "G nuit", + "next_run": 1774830600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "30 2 * * *", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID7}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "H quart d'heure", + "next_run": 1774700100000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "*/15 * * * *", + "tz": "Nulle/Part" + }, + { + "enabled": true, + "id": "{{ID8}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "I dimanche", + "next_run": 1774771200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 8 * * 7", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID9}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "J une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@once 2026-03-29 03:30", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID11}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "K échec", + "next_run": 1774702800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "echec.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID12}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "L lente", + "next_run": 1774702800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "lent.sh", + "tz": "" + }, + { + "enabled": false, + "id": "zz", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "M imposée", + "next_run": 1774702800000, + "no_mem": true, + "no_web": true, + "preset": "", + "project": "generale", + "prompt": "x", + "schedule": "@every 1h", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID13}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "N script une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@once 2026-03-29 03:30", + "script": "ok.sh", + "tz": "Europe/Paris" + } + ], + "web_on": true + }, + "tasks": [ + { + "enabled": true, + "id": "{{ID10}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "A script", + "next_run": 1774702800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "ok.sh", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID1}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "B renommée", + "next_run": 1774711800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Autre.", + "schedule": "@every 3h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID2}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "C une heure et demie", + "next_run": 1774708200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@EVERY 1.5h", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID3}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "D chaque soir", + "next_run": 1774735200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 1d@23:00", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID4}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "E tous les trois jours", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 3d@9:30", + "tz": "America/New_York" + }, + { + "enabled": true, + "id": "{{ID5}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "F semaine", + "next_run": 1774841400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 9 * * 1-5", + "tz": "Asia/Kolkata" + }, + { + "enabled": true, + "id": "{{ID6}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "G nuit", + "next_run": 1774830600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "30 2 * * *", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID7}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "H quart d'heure", + "next_run": 1774700100000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "*/15 * * * *", + "tz": "Nulle/Part" + }, + { + "enabled": true, + "id": "{{ID8}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "I dimanche", + "next_run": 1774771200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 8 * * 7", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID9}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "J une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@once 2026-03-29 03:30", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID11}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "K échec", + "next_run": 1774702800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "echec.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID12}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "L lente", + "next_run": 1774702800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "lent.sh", + "tz": "" + }, + { + "enabled": false, + "id": "zz", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "M imposée", + "next_run": 1774702800000, + "no_mem": true, + "no_web": true, + "preset": "", + "project": "generale", + "prompt": "x", + "schedule": "@every 1h", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID13}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "N script une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@once 2026-03-29 03:30", + "script": "ok.sh", + "tz": "Europe/Paris" + } + ] + }, + { + "name": "suppression sans id", + "method": "POST", + "path": "/api/tasks/delete", + "body": "{}", + "status": 400, + "out": { + "error": "id manquant", + "ok": false + }, + "tasks": [ + { + "enabled": true, + "id": "{{ID10}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "A script", + "next_run": 1774702800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "ok.sh", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID1}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "B renommée", + "next_run": 1774711800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Autre.", + "schedule": "@every 3h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID2}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "C une heure et demie", + "next_run": 1774708200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@EVERY 1.5h", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID3}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "D chaque soir", + "next_run": 1774735200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 1d@23:00", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID4}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "E tous les trois jours", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 3d@9:30", + "tz": "America/New_York" + }, + { + "enabled": true, + "id": "{{ID5}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "F semaine", + "next_run": 1774841400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 9 * * 1-5", + "tz": "Asia/Kolkata" + }, + { + "enabled": true, + "id": "{{ID6}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "G nuit", + "next_run": 1774830600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "30 2 * * *", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID7}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "H quart d'heure", + "next_run": 1774700100000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "*/15 * * * *", + "tz": "Nulle/Part" + }, + { + "enabled": true, + "id": "{{ID8}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "I dimanche", + "next_run": 1774771200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 8 * * 7", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID9}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "J une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@once 2026-03-29 03:30", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID11}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "K échec", + "next_run": 1774702800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "echec.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID12}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "L lente", + "next_run": 1774702800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "lent.sh", + "tz": "" + }, + { + "enabled": false, + "id": "zz", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "M imposée", + "next_run": 1774702800000, + "no_mem": true, + "no_web": true, + "preset": "", + "project": "generale", + "prompt": "x", + "schedule": "@every 1h", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID13}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "N script une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@once 2026-03-29 03:30", + "script": "ok.sh", + "tz": "Europe/Paris" + } + ] + }, + { + "name": "suppression", + "method": "POST", + "path": "/api/tasks/delete", + "body": "{\"id\":\"zz\"}", + "status": 200, + "out": { + "ok": true + }, + "tasks": [ + { + "enabled": true, + "id": "{{ID10}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "A script", + "next_run": 1774702800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "ok.sh", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID1}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "B renommée", + "next_run": 1774711800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Autre.", + "schedule": "@every 3h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID2}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "C une heure et demie", + "next_run": 1774708200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@EVERY 1.5h", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID3}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "D chaque soir", + "next_run": 1774735200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 1d@23:00", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID4}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "E tous les trois jours", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 3d@9:30", + "tz": "America/New_York" + }, + { + "enabled": true, + "id": "{{ID5}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "F semaine", + "next_run": 1774841400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 9 * * 1-5", + "tz": "Asia/Kolkata" + }, + { + "enabled": true, + "id": "{{ID6}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "G nuit", + "next_run": 1774830600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "30 2 * * *", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID7}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "H quart d'heure", + "next_run": 1774700100000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "*/15 * * * *", + "tz": "Nulle/Part" + }, + { + "enabled": true, + "id": "{{ID8}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "I dimanche", + "next_run": 1774771200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 8 * * 7", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID9}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "J une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@once 2026-03-29 03:30", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID11}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "K échec", + "next_run": 1774702800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "echec.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID12}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "L lente", + "next_run": 1774702800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "lent.sh", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID13}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "N script une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@once 2026-03-29 03:30", + "script": "ok.sh", + "tz": "Europe/Paris" + } + ] + }, + { + "name": "lancer une tâche absente", + "method": "POST", + "path": "/api/tasks/run", + "body": "{\"id\":\"absente\"}", + "status": 404, + "out": { + "error": "tâche introuvable", + "ok": false + }, + "tasks": [ + { + "enabled": true, + "id": "{{ID10}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "A script", + "next_run": 1774702800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "ok.sh", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID1}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "B renommée", + "next_run": 1774711800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Autre.", + "schedule": "@every 3h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID2}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "C une heure et demie", + "next_run": 1774708200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@EVERY 1.5h", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID3}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "D chaque soir", + "next_run": 1774735200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 1d@23:00", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID4}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "E tous les trois jours", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 3d@9:30", + "tz": "America/New_York" + }, + { + "enabled": true, + "id": "{{ID5}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "F semaine", + "next_run": 1774841400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 9 * * 1-5", + "tz": "Asia/Kolkata" + }, + { + "enabled": true, + "id": "{{ID6}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "G nuit", + "next_run": 1774830600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "30 2 * * *", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID7}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "H quart d'heure", + "next_run": 1774700100000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "*/15 * * * *", + "tz": "Nulle/Part" + }, + { + "enabled": true, + "id": "{{ID8}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "I dimanche", + "next_run": 1774771200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 8 * * 7", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID9}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "J une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@once 2026-03-29 03:30", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID11}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "K échec", + "next_run": 1774702800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "echec.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID12}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "L lente", + "next_run": 1774702800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "lent.sh", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID13}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "N script une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@once 2026-03-29 03:30", + "script": "ok.sh", + "tz": "Europe/Paris" + } + ] + }, + { + "name": "lancer une tâche IA, moteur occupé", + "generating": true, + "method": "POST", + "path": "/api/tasks/run", + "body": "{\"id\":\"{{ID1}}\"}", + "status": 409, + "out": { + "error": "génération en cours", + "ok": false + }, + "tasks": [ + { + "enabled": true, + "id": "{{ID10}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "A script", + "next_run": 1774702800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "ok.sh", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID1}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "B renommée", + "next_run": 1774711800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Autre.", + "schedule": "@every 3h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID2}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "C une heure et demie", + "next_run": 1774708200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@EVERY 1.5h", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID3}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "D chaque soir", + "next_run": 1774735200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 1d@23:00", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID4}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "E tous les trois jours", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 3d@9:30", + "tz": "America/New_York" + }, + { + "enabled": true, + "id": "{{ID5}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "F semaine", + "next_run": 1774841400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 9 * * 1-5", + "tz": "Asia/Kolkata" + }, + { + "enabled": true, + "id": "{{ID6}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "G nuit", + "next_run": 1774830600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "30 2 * * *", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID7}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "H quart d'heure", + "next_run": 1774700100000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "*/15 * * * *", + "tz": "Nulle/Part" + }, + { + "enabled": true, + "id": "{{ID8}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "I dimanche", + "next_run": 1774771200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 8 * * 7", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID9}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "J une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@once 2026-03-29 03:30", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID11}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "K échec", + "next_run": 1774702800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "echec.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID12}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "L lente", + "next_run": 1774702800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "lent.sh", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID13}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "N script une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@once 2026-03-29 03:30", + "script": "ok.sh", + "tz": "Europe/Paris" + } + ] + }, + { + "name": "lancer une tâche IA, modèle pas prêt", + "method": "POST", + "path": "/api/tasks/run", + "body": "{\"id\":\"{{ID1}}\"}", + "status": 503, + "out": { + "error": "⏳ Le modèle est encore en train de charger — réessaie dans quelques secondes.", + "ok": false + }, + "tasks": [ + { + "enabled": true, + "id": "{{ID10}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "A script", + "next_run": 1774702800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "ok.sh", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID1}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "B renommée", + "next_run": 1774711800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Autre.", + "schedule": "@every 3h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID2}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "C une heure et demie", + "next_run": 1774708200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@EVERY 1.5h", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID3}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "D chaque soir", + "next_run": 1774735200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 1d@23:00", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID4}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "E tous les trois jours", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 3d@9:30", + "tz": "America/New_York" + }, + { + "enabled": true, + "id": "{{ID5}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "F semaine", + "next_run": 1774841400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 9 * * 1-5", + "tz": "Asia/Kolkata" + }, + { + "enabled": true, + "id": "{{ID6}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "G nuit", + "next_run": 1774830600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "30 2 * * *", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID7}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "H quart d'heure", + "next_run": 1774700100000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "*/15 * * * *", + "tz": "Nulle/Part" + }, + { + "enabled": true, + "id": "{{ID8}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "I dimanche", + "next_run": 1774771200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 8 * * 7", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID9}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "J une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@once 2026-03-29 03:30", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID11}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "K échec", + "next_run": 1774702800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "echec.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID12}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "L lente", + "next_run": 1774702800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "lent.sh", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID13}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "N script une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@once 2026-03-29 03:30", + "script": "ok.sh", + "tz": "Europe/Paris" + } + ] + }, + { + "name": "lancer le script", + "method": "POST", + "path": "/api/tasks/run", + "body": "{\"id\":\"{{ID10}}\"}", + "wait_done": "{{ID10}}", + "status": 200, + "out": { + "ok": true + }, + "tasks": [ + { + "enabled": true, + "id": "{{ID10}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": true, + "last_report": "exit: 0 · {{DUR}}\n\nstdout:\nbonjour\n", + "last_run": 1774738800000, + "name": "A script", + "next_run": 1774742400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "ok.sh", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID1}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "B renommée", + "next_run": 1774711800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Autre.", + "schedule": "@every 3h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID2}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "C une heure et demie", + "next_run": 1774708200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@EVERY 1.5h", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID3}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "D chaque soir", + "next_run": 1774735200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 1d@23:00", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID4}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "E tous les trois jours", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 3d@9:30", + "tz": "America/New_York" + }, + { + "enabled": true, + "id": "{{ID5}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "F semaine", + "next_run": 1774841400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 9 * * 1-5", + "tz": "Asia/Kolkata" + }, + { + "enabled": true, + "id": "{{ID6}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "G nuit", + "next_run": 1774830600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "30 2 * * *", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID7}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "H quart d'heure", + "next_run": 1774700100000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "*/15 * * * *", + "tz": "Nulle/Part" + }, + { + "enabled": true, + "id": "{{ID8}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "I dimanche", + "next_run": 1774771200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 8 * * 7", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID9}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "J une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@once 2026-03-29 03:30", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID11}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "K échec", + "next_run": 1774702800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "echec.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID12}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "L lente", + "next_run": 1774702800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "lent.sh", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID13}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "N script une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@once 2026-03-29 03:30", + "script": "ok.sh", + "tz": "Europe/Paris" + } + ] + }, + { + "name": "lancer le script en échec", + "method": "POST", + "path": "/api/tasks/run", + "body": "{\"id\":\"{{ID11}}\"}", + "wait_done": "{{ID11}}", + "status": 200, + "out": { + "ok": true + }, + "tasks": [ + { + "enabled": true, + "id": "{{ID10}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": true, + "last_report": "exit: 0 · {{DUR}}\n\nstdout:\nbonjour\n", + "last_run": 1774738800000, + "name": "A script", + "next_run": 1774742400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "ok.sh", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID1}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "B renommée", + "next_run": 1774711800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Autre.", + "schedule": "@every 3h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID2}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "C une heure et demie", + "next_run": 1774708200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@EVERY 1.5h", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID3}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "D chaque soir", + "next_run": 1774735200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 1d@23:00", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID4}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "E tous les trois jours", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 3d@9:30", + "tz": "America/New_York" + }, + { + "enabled": true, + "id": "{{ID5}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "F semaine", + "next_run": 1774841400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 9 * * 1-5", + "tz": "Asia/Kolkata" + }, + { + "enabled": true, + "id": "{{ID6}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "G nuit", + "next_run": 1774830600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "30 2 * * *", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID7}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "H quart d'heure", + "next_run": 1774700100000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "*/15 * * * *", + "tz": "Nulle/Part" + }, + { + "enabled": true, + "id": "{{ID8}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "I dimanche", + "next_run": 1774771200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 8 * * 7", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID9}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "J une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@once 2026-03-29 03:30", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID11}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "exit: 2 · {{DUR}}\n\nstderr:\noups\n", + "last_ok": false, + "last_report": "", + "last_run": 1774738800000, + "name": "K échec", + "next_run": 1774742400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "echec.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID12}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "L lente", + "next_run": 1774702800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "lent.sh", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID13}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "N script une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@once 2026-03-29 03:30", + "script": "ok.sh", + "tz": "Europe/Paris" + } + ] + }, + { + "name": "lancer le script lent", + "method": "POST", + "path": "/api/tasks/run", + "body": "{\"id\":\"{{ID12}}\"}", + "wait_running": "{{ID12}}", + "status": 200, + "out": { + "ok": true + }, + "tasks": [ + { + "enabled": true, + "id": "{{ID10}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": true, + "last_report": "exit: 0 · {{DUR}}\n\nstdout:\nbonjour\n", + "last_run": 1774738800000, + "name": "A script", + "next_run": 1774742400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "ok.sh", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID1}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "B renommée", + "next_run": 1774711800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Autre.", + "schedule": "@every 3h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID2}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "C une heure et demie", + "next_run": 1774708200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@EVERY 1.5h", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID3}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "D chaque soir", + "next_run": 1774735200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 1d@23:00", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID4}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "E tous les trois jours", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 3d@9:30", + "tz": "America/New_York" + }, + { + "enabled": true, + "id": "{{ID5}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "F semaine", + "next_run": 1774841400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 9 * * 1-5", + "tz": "Asia/Kolkata" + }, + { + "enabled": true, + "id": "{{ID6}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "G nuit", + "next_run": 1774830600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "30 2 * * *", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID7}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "H quart d'heure", + "next_run": 1774700100000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "*/15 * * * *", + "tz": "Nulle/Part" + }, + { + "enabled": true, + "id": "{{ID8}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "I dimanche", + "next_run": 1774771200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 8 * * 7", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID9}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "J une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@once 2026-03-29 03:30", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID11}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "exit: 2 · {{DUR}}\n\nstderr:\noups\n", + "last_ok": false, + "last_report": "", + "last_run": 1774738800000, + "name": "K échec", + "next_run": 1774742400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "echec.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID12}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "L lente", + "next_run": 1774702800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "lent.sh", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID13}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "N script une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@once 2026-03-29 03:30", + "script": "ok.sh", + "tz": "Europe/Paris" + } + ] + }, + { + "name": "liste pendant le script", + "method": "GET", + "path": "/api/tasks", + "status": 200, + "out": { + "agent": true, + "mem_on": false, + "ok": true, + "paused": false, + "presets": [], + "projects": [ + { + "active": true, + "convs": 1, + "created": "{{T}}", + "name": "Générale", + "pages": 0, + "slug": "generale", + "trackers": 0 + } + ], + "running_id": "{{ID12}}", + "scripts": [ + { + "name": "echec.sh", + "size": 31 + }, + { + "name": "lent.sh", + "size": 19 + }, + { + "name": "ok.sh", + "size": 23 + }, + { + "name": "sous/rang.sh", + "size": 22 + } + ], + "scripts_dir": "{{HOME}}/scripts", + "tasks": [ + { + "enabled": true, + "id": "{{ID10}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": true, + "last_report": "exit: 0 · {{DUR}}\n\nstdout:\nbonjour\n", + "last_run": 1774738800000, + "name": "A script", + "next_run": 1774742400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "ok.sh", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID1}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "B renommée", + "next_run": 1774711800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Autre.", + "schedule": "@every 3h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID2}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "C une heure et demie", + "next_run": 1774708200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@EVERY 1.5h", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID3}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "D chaque soir", + "next_run": 1774735200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 1d@23:00", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID4}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "E tous les trois jours", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 3d@9:30", + "tz": "America/New_York" + }, + { + "enabled": true, + "id": "{{ID5}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "F semaine", + "next_run": 1774841400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 9 * * 1-5", + "tz": "Asia/Kolkata" + }, + { + "enabled": true, + "id": "{{ID6}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "G nuit", + "next_run": 1774830600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "30 2 * * *", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID7}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "H quart d'heure", + "next_run": 1774700100000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "*/15 * * * *", + "tz": "Nulle/Part" + }, + { + "enabled": true, + "id": "{{ID8}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "I dimanche", + "next_run": 1774771200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 8 * * 7", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID9}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "J une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@once 2026-03-29 03:30", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID11}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "exit: 2 · {{DUR}}\n\nstderr:\noups\n", + "last_ok": false, + "last_report": "", + "last_run": 1774738800000, + "name": "K échec", + "next_run": 1774742400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "echec.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID12}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "L lente", + "next_run": 1774702800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "lent.sh", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID13}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "N script une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@once 2026-03-29 03:30", + "script": "ok.sh", + "tz": "Europe/Paris" + } + ], + "web_on": true + }, + "tasks": [ + { + "enabled": true, + "id": "{{ID10}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": true, + "last_report": "exit: 0 · {{DUR}}\n\nstdout:\nbonjour\n", + "last_run": 1774738800000, + "name": "A script", + "next_run": 1774742400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "ok.sh", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID1}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "B renommée", + "next_run": 1774711800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Autre.", + "schedule": "@every 3h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID2}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "C une heure et demie", + "next_run": 1774708200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@EVERY 1.5h", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID3}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "D chaque soir", + "next_run": 1774735200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 1d@23:00", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID4}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "E tous les trois jours", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 3d@9:30", + "tz": "America/New_York" + }, + { + "enabled": true, + "id": "{{ID5}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "F semaine", + "next_run": 1774841400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 9 * * 1-5", + "tz": "Asia/Kolkata" + }, + { + "enabled": true, + "id": "{{ID6}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "G nuit", + "next_run": 1774830600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "30 2 * * *", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID7}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "H quart d'heure", + "next_run": 1774700100000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "*/15 * * * *", + "tz": "Nulle/Part" + }, + { + "enabled": true, + "id": "{{ID8}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "I dimanche", + "next_run": 1774771200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 8 * * 7", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID9}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "J une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@once 2026-03-29 03:30", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID11}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "exit: 2 · {{DUR}}\n\nstderr:\noups\n", + "last_ok": false, + "last_report": "", + "last_run": 1774738800000, + "name": "K échec", + "next_run": 1774742400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "echec.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID12}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "L lente", + "next_run": 1774702800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "lent.sh", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID13}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "N script une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@once 2026-03-29 03:30", + "script": "ok.sh", + "tz": "Europe/Paris" + } + ] + }, + { + "name": "arrêter le script", + "method": "POST", + "path": "/api/tasks/stop", + "body": "{\"id\":\"{{ID12}}\"}", + "wait_done": "{{ID12}}", + "status": 200, + "out": { + "ok": true + }, + "tasks": [ + { + "enabled": true, + "id": "{{ID10}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": true, + "last_report": "exit: 0 · {{DUR}}\n\nstdout:\nbonjour\n", + "last_run": 1774738800000, + "name": "A script", + "next_run": 1774742400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "ok.sh", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID1}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "B renommée", + "next_run": 1774711800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Autre.", + "schedule": "@every 3h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID2}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "C une heure et demie", + "next_run": 1774708200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@EVERY 1.5h", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID3}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "D chaque soir", + "next_run": 1774735200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 1d@23:00", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID4}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "E tous les trois jours", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 3d@9:30", + "tz": "America/New_York" + }, + { + "enabled": true, + "id": "{{ID5}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "F semaine", + "next_run": 1774841400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 9 * * 1-5", + "tz": "Asia/Kolkata" + }, + { + "enabled": true, + "id": "{{ID6}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "G nuit", + "next_run": 1774830600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "30 2 * * *", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID7}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "H quart d'heure", + "next_run": 1774700100000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "*/15 * * * *", + "tz": "Nulle/Part" + }, + { + "enabled": true, + "id": "{{ID8}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "I dimanche", + "next_run": 1774771200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 8 * * 7", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID9}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "J une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@once 2026-03-29 03:30", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID11}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "exit: 2 · {{DUR}}\n\nstderr:\noups\n", + "last_ok": false, + "last_report": "", + "last_run": 1774738800000, + "name": "K échec", + "next_run": 1774742400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "echec.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID12}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": true, + "last_report": "(interrompue manuellement)", + "last_run": 1774738800000, + "name": "L lente", + "next_run": 1774742400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "lent.sh", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID13}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "N script une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@once 2026-03-29 03:30", + "script": "ok.sh", + "tz": "Europe/Paris" + } + ] + }, + { + "name": "arrêter sans tâche script", + "method": "POST", + "path": "/api/tasks/stop", + "body": "{}", + "status": 200, + "out": { + "ok": true + }, + "tasks": [ + { + "enabled": true, + "id": "{{ID10}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": true, + "last_report": "exit: 0 · {{DUR}}\n\nstdout:\nbonjour\n", + "last_run": 1774738800000, + "name": "A script", + "next_run": 1774742400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "ok.sh", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID1}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "B renommée", + "next_run": 1774711800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Autre.", + "schedule": "@every 3h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID2}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "C une heure et demie", + "next_run": 1774708200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@EVERY 1.5h", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID3}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "D chaque soir", + "next_run": 1774735200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 1d@23:00", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID4}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "E tous les trois jours", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 3d@9:30", + "tz": "America/New_York" + }, + { + "enabled": true, + "id": "{{ID5}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "F semaine", + "next_run": 1774841400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 9 * * 1-5", + "tz": "Asia/Kolkata" + }, + { + "enabled": true, + "id": "{{ID6}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "G nuit", + "next_run": 1774830600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "30 2 * * *", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID7}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "H quart d'heure", + "next_run": 1774700100000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "*/15 * * * *", + "tz": "Nulle/Part" + }, + { + "enabled": true, + "id": "{{ID8}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "I dimanche", + "next_run": 1774771200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 8 * * 7", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID9}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "J une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@once 2026-03-29 03:30", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID11}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "exit: 2 · {{DUR}}\n\nstderr:\noups\n", + "last_ok": false, + "last_report": "", + "last_run": 1774738800000, + "name": "K échec", + "next_run": 1774742400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "echec.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID12}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": true, + "last_report": "(interrompue manuellement)", + "last_run": 1774738800000, + "name": "L lente", + "next_run": 1774742400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "lent.sh", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID13}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "N script une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@once 2026-03-29 03:30", + "script": "ok.sh", + "tz": "Europe/Paris" + } + ] + }, + { + "name": "le script qui réussissait échoue", + "now": "2026-03-28T23:05:00Z", + "files": { + "scripts/ok.sh": "#!/bin/sh\necho perdu\nexit 3\n" + }, + "method": "POST", + "path": "/api/tasks/run", + "body": "{\"id\":\"{{ID10}}\"}", + "wait_done": "{{ID10}}", + "status": 200, + "out": { + "ok": true + }, + "tasks": [ + { + "enabled": true, + "id": "{{ID10}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "exit: 3 · {{DUR}}\n\nstdout:\nperdu\n", + "last_ok": false, + "last_report": "exit: 0 · {{DUR}}\n\nstdout:\nbonjour\n", + "last_run": 1774739100000, + "name": "A script", + "next_run": 1774742700000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "ok.sh", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID1}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "B renommée", + "next_run": 1774711800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Autre.", + "schedule": "@every 3h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID2}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "C une heure et demie", + "next_run": 1774708200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@EVERY 1.5h", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID3}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "D chaque soir", + "next_run": 1774735200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 1d@23:00", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID4}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "E tous les trois jours", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 3d@9:30", + "tz": "America/New_York" + }, + { + "enabled": true, + "id": "{{ID5}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "F semaine", + "next_run": 1774841400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 9 * * 1-5", + "tz": "Asia/Kolkata" + }, + { + "enabled": true, + "id": "{{ID6}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "G nuit", + "next_run": 1774830600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "30 2 * * *", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID7}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "H quart d'heure", + "next_run": 1774700100000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "*/15 * * * *", + "tz": "Nulle/Part" + }, + { + "enabled": true, + "id": "{{ID8}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "I dimanche", + "next_run": 1774771200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 8 * * 7", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID9}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "J une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@once 2026-03-29 03:30", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID11}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "exit: 2 · {{DUR}}\n\nstderr:\noups\n", + "last_ok": false, + "last_report": "", + "last_run": 1774738800000, + "name": "K échec", + "next_run": 1774742400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "echec.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID12}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": true, + "last_report": "(interrompue manuellement)", + "last_run": 1774738800000, + "name": "L lente", + "next_run": 1774742400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "lent.sh", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID13}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "N script une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@once 2026-03-29 03:30", + "script": "ok.sh", + "tz": "Europe/Paris" + } + ] + }, + { + "name": "tâche script sans script", + "now": "2026-03-28T23:01:00Z", + "put_task": "{\"id\":\"vide\",\"name\":\"S vide\",\"kind\":\"script\",\"schedule\":\"@every 1h\"}", + "method": "POST", + "path": "/api/tasks/run", + "body": "{\"id\":\"vide\"}", + "wait_done": "vide", + "status": 200, + "out": { + "ok": true + }, + "tasks": [ + { + "enabled": true, + "id": "{{ID10}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "exit: 3 · {{DUR}}\n\nstdout:\nperdu\n", + "last_ok": false, + "last_report": "exit: 0 · {{DUR}}\n\nstdout:\nbonjour\n", + "last_run": 1774739100000, + "name": "A script", + "next_run": 1774742700000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "ok.sh", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID1}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "B renommée", + "next_run": 1774711800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Autre.", + "schedule": "@every 3h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID2}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "C une heure et demie", + "next_run": 1774708200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@EVERY 1.5h", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID3}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "D chaque soir", + "next_run": 1774735200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 1d@23:00", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID4}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "E tous les trois jours", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 3d@9:30", + "tz": "America/New_York" + }, + { + "enabled": true, + "id": "{{ID5}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "F semaine", + "next_run": 1774841400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 9 * * 1-5", + "tz": "Asia/Kolkata" + }, + { + "enabled": true, + "id": "{{ID6}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "G nuit", + "next_run": 1774830600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "30 2 * * *", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID7}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "H quart d'heure", + "next_run": 1774700100000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "*/15 * * * *", + "tz": "Nulle/Part" + }, + { + "enabled": true, + "id": "{{ID8}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "I dimanche", + "next_run": 1774771200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 8 * * 7", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID9}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "J une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@once 2026-03-29 03:30", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID11}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "exit: 2 · {{DUR}}\n\nstderr:\noups\n", + "last_ok": false, + "last_report": "", + "last_run": 1774738800000, + "name": "K échec", + "next_run": 1774742400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "echec.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID12}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": true, + "last_report": "(interrompue manuellement)", + "last_run": 1774738800000, + "name": "L lente", + "next_run": 1774742400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "lent.sh", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID13}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "N script une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@once 2026-03-29 03:30", + "script": "ok.sh", + "tz": "Europe/Paris" + }, + { + "enabled": false, + "id": "vide", + "kind": "script", + "last_dur_ms": 0, + "last_error": "aucun script associé à la tâche", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S vide", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "tz": "" + } + ] + }, + { + "name": "tâche script au nom refusé", + "put_task": "{\"id\":\"remonte\",\"name\":\"S remonte\",\"kind\":\"script\",\"script\":\"../x.sh\",\"schedule\":\"@every 1h\"}", + "method": "POST", + "path": "/api/tasks/run", + "body": "{\"id\":\"remonte\"}", + "wait_done": "remonte", + "status": 200, + "out": { + "ok": true + }, + "tasks": [ + { + "enabled": true, + "id": "{{ID10}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "exit: 3 · {{DUR}}\n\nstdout:\nperdu\n", + "last_ok": false, + "last_report": "exit: 0 · {{DUR}}\n\nstdout:\nbonjour\n", + "last_run": 1774739100000, + "name": "A script", + "next_run": 1774742700000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "ok.sh", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID1}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "B renommée", + "next_run": 1774711800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Autre.", + "schedule": "@every 3h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID2}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "C une heure et demie", + "next_run": 1774708200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@EVERY 1.5h", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID3}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "D chaque soir", + "next_run": 1774735200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 1d@23:00", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID4}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "E tous les trois jours", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 3d@9:30", + "tz": "America/New_York" + }, + { + "enabled": true, + "id": "{{ID5}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "F semaine", + "next_run": 1774841400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 9 * * 1-5", + "tz": "Asia/Kolkata" + }, + { + "enabled": true, + "id": "{{ID6}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "G nuit", + "next_run": 1774830600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "30 2 * * *", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID7}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "H quart d'heure", + "next_run": 1774700100000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "*/15 * * * *", + "tz": "Nulle/Part" + }, + { + "enabled": true, + "id": "{{ID8}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "I dimanche", + "next_run": 1774771200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 8 * * 7", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID9}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "J une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@once 2026-03-29 03:30", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID11}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "exit: 2 · {{DUR}}\n\nstderr:\noups\n", + "last_ok": false, + "last_report": "", + "last_run": 1774738800000, + "name": "K échec", + "next_run": 1774742400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "echec.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID12}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": true, + "last_report": "(interrompue manuellement)", + "last_run": 1774738800000, + "name": "L lente", + "next_run": 1774742400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "lent.sh", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID13}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "N script une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@once 2026-03-29 03:30", + "script": "ok.sh", + "tz": "Europe/Paris" + }, + { + "enabled": false, + "id": "remonte", + "kind": "script", + "last_dur_ms": 0, + "last_error": "nom de script invalide (pas de chemin absolu ni de ../) : ../x.sh", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S remonte", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "script": "../x.sh", + "tz": "" + }, + { + "enabled": false, + "id": "vide", + "kind": "script", + "last_dur_ms": 0, + "last_error": "aucun script associé à la tâche", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S vide", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "tz": "" + } + ] + }, + { + "name": "tâche script au script disparu", + "put_task": "{\"id\":\"disparu\",\"name\":\"S disparu\",\"kind\":\"script\",\"script\":\"parti.sh\",\"schedule\":\"@every 1h\"}", + "method": "POST", + "path": "/api/tasks/run", + "body": "{\"id\":\"disparu\"}", + "wait_done": "disparu", + "status": 200, + "out": { + "ok": true + }, + "tasks": [ + { + "enabled": true, + "id": "{{ID10}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "exit: 3 · {{DUR}}\n\nstdout:\nperdu\n", + "last_ok": false, + "last_report": "exit: 0 · {{DUR}}\n\nstdout:\nbonjour\n", + "last_run": 1774739100000, + "name": "A script", + "next_run": 1774742700000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "ok.sh", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID1}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "B renommée", + "next_run": 1774711800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Autre.", + "schedule": "@every 3h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID2}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "C une heure et demie", + "next_run": 1774708200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@EVERY 1.5h", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID3}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "D chaque soir", + "next_run": 1774735200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 1d@23:00", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID4}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "E tous les trois jours", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 3d@9:30", + "tz": "America/New_York" + }, + { + "enabled": true, + "id": "{{ID5}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "F semaine", + "next_run": 1774841400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 9 * * 1-5", + "tz": "Asia/Kolkata" + }, + { + "enabled": true, + "id": "{{ID6}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "G nuit", + "next_run": 1774830600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "30 2 * * *", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID7}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "H quart d'heure", + "next_run": 1774700100000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "*/15 * * * *", + "tz": "Nulle/Part" + }, + { + "enabled": true, + "id": "{{ID8}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "I dimanche", + "next_run": 1774771200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 8 * * 7", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID9}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "J une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@once 2026-03-29 03:30", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID11}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "exit: 2 · {{DUR}}\n\nstderr:\noups\n", + "last_ok": false, + "last_report": "", + "last_run": 1774738800000, + "name": "K échec", + "next_run": 1774742400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "echec.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID12}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": true, + "last_report": "(interrompue manuellement)", + "last_run": 1774738800000, + "name": "L lente", + "next_run": 1774742400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "lent.sh", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID13}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "N script une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@once 2026-03-29 03:30", + "script": "ok.sh", + "tz": "Europe/Paris" + }, + { + "enabled": false, + "id": "disparu", + "kind": "script", + "last_dur_ms": 0, + "last_error": "script introuvable : parti.sh", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S disparu", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "script": "parti.sh", + "tz": "" + }, + { + "enabled": false, + "id": "remonte", + "kind": "script", + "last_dur_ms": 0, + "last_error": "nom de script invalide (pas de chemin absolu ni de ../) : ../x.sh", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S remonte", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "script": "../x.sh", + "tz": "" + }, + { + "enabled": false, + "id": "vide", + "kind": "script", + "last_dur_ms": 0, + "last_error": "aucun script associé à la tâche", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S vide", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "tz": "" + } + ] + }, + { + "name": "puis réussit de nouveau", + "now": "2026-03-28T23:10:00Z", + "files": { + "scripts/ok.sh": "#!/bin/sh\necho bonjour\n" + }, + "method": "POST", + "path": "/api/tasks/run", + "body": "{\"id\":\"{{ID10}}\"}", + "wait_done": "{{ID10}}", + "status": 200, + "out": { + "ok": true + }, + "tasks": [ + { + "enabled": true, + "id": "{{ID10}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": true, + "last_report": "exit: 0 · {{DUR}}\n\nstdout:\nbonjour\n", + "last_run": 1774739400000, + "name": "A script", + "next_run": 1774743000000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "ok.sh", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID1}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "B renommée", + "next_run": 1774711800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Autre.", + "schedule": "@every 3h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID2}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "C une heure et demie", + "next_run": 1774708200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@EVERY 1.5h", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID3}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "D chaque soir", + "next_run": 1774735200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 1d@23:00", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID4}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "E tous les trois jours", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 3d@9:30", + "tz": "America/New_York" + }, + { + "enabled": true, + "id": "{{ID5}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "F semaine", + "next_run": 1774841400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 9 * * 1-5", + "tz": "Asia/Kolkata" + }, + { + "enabled": true, + "id": "{{ID6}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "G nuit", + "next_run": 1774830600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "30 2 * * *", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID7}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "H quart d'heure", + "next_run": 1774700100000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "*/15 * * * *", + "tz": "Nulle/Part" + }, + { + "enabled": true, + "id": "{{ID8}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "I dimanche", + "next_run": 1774771200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 8 * * 7", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID9}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "J une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@once 2026-03-29 03:30", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID11}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "exit: 2 · {{DUR}}\n\nstderr:\noups\n", + "last_ok": false, + "last_report": "", + "last_run": 1774738800000, + "name": "K échec", + "next_run": 1774742400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "echec.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID12}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": true, + "last_report": "(interrompue manuellement)", + "last_run": 1774738800000, + "name": "L lente", + "next_run": 1774742400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "lent.sh", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID13}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "N script une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@once 2026-03-29 03:30", + "script": "ok.sh", + "tz": "Europe/Paris" + }, + { + "enabled": false, + "id": "disparu", + "kind": "script", + "last_dur_ms": 0, + "last_error": "script introuvable : parti.sh", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S disparu", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "script": "parti.sh", + "tz": "" + }, + { + "enabled": false, + "id": "remonte", + "kind": "script", + "last_dur_ms": 0, + "last_error": "nom de script invalide (pas de chemin absolu ni de ../) : ../x.sh", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S remonte", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "script": "../x.sh", + "tz": "" + }, + { + "enabled": false, + "id": "vide", + "kind": "script", + "last_dur_ms": 0, + "last_error": "aucun script associé à la tâche", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S vide", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "tz": "" + } + ] + }, + { + "name": "tic : une tâche IA due attend le modèle", + "now": "2026-03-28T16:00:00Z", + "tick": true, + "tasks": [ + { + "enabled": true, + "id": "{{ID10}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": true, + "last_report": "exit: 0 · {{DUR}}\n\nstdout:\nbonjour\n", + "last_run": 1774739400000, + "name": "A script", + "next_run": 1774743000000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "ok.sh", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID1}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "B renommée", + "next_run": 1774711800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Autre.", + "schedule": "@every 3h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID2}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "C une heure et demie", + "next_run": 1774708200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@EVERY 1.5h", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID3}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "D chaque soir", + "next_run": 1774735200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 1d@23:00", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID4}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "E tous les trois jours", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 3d@9:30", + "tz": "America/New_York" + }, + { + "enabled": true, + "id": "{{ID5}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "F semaine", + "next_run": 1774841400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 9 * * 1-5", + "tz": "Asia/Kolkata" + }, + { + "enabled": true, + "id": "{{ID6}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "G nuit", + "next_run": 1774830600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "30 2 * * *", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID7}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "H quart d'heure", + "next_run": 1774700100000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "*/15 * * * *", + "tz": "Nulle/Part" + }, + { + "enabled": true, + "id": "{{ID8}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "I dimanche", + "next_run": 1774771200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 8 * * 7", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID9}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "J une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@once 2026-03-29 03:30", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID11}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "exit: 2 · {{DUR}}\n\nstderr:\noups\n", + "last_ok": false, + "last_report": "", + "last_run": 1774738800000, + "name": "K échec", + "next_run": 1774742400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "echec.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID12}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": true, + "last_report": "(interrompue manuellement)", + "last_run": 1774738800000, + "name": "L lente", + "next_run": 1774742400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "lent.sh", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID13}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "N script une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@once 2026-03-29 03:30", + "script": "ok.sh", + "tz": "Europe/Paris" + }, + { + "enabled": false, + "id": "disparu", + "kind": "script", + "last_dur_ms": 0, + "last_error": "script introuvable : parti.sh", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S disparu", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "script": "parti.sh", + "tz": "" + }, + { + "enabled": false, + "id": "remonte", + "kind": "script", + "last_dur_ms": 0, + "last_error": "nom de script invalide (pas de chemin absolu ni de ../) : ../x.sh", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S remonte", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "script": "../x.sh", + "tz": "" + }, + { + "enabled": false, + "id": "vide", + "kind": "script", + "last_dur_ms": 0, + "last_error": "aucun script associé à la tâche", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S vide", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "tz": "" + } + ] + }, + { + "name": "désactiver la tâche IA 1", + "method": "POST", + "path": "/api/tasks/toggle", + "body": "{\"id\":\"{{ID1}}\",\"on\":false}", + "status": 200, + "out": { + "ok": true + }, + "tasks": [ + { + "enabled": true, + "id": "{{ID10}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": true, + "last_report": "exit: 0 · {{DUR}}\n\nstdout:\nbonjour\n", + "last_run": 1774739400000, + "name": "A script", + "next_run": 1774743000000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "ok.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID1}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "B renommée", + "next_run": 1774711800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Autre.", + "schedule": "@every 3h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID2}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "C une heure et demie", + "next_run": 1774708200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@EVERY 1.5h", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID3}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "D chaque soir", + "next_run": 1774735200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 1d@23:00", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID4}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "E tous les trois jours", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 3d@9:30", + "tz": "America/New_York" + }, + { + "enabled": true, + "id": "{{ID5}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "F semaine", + "next_run": 1774841400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 9 * * 1-5", + "tz": "Asia/Kolkata" + }, + { + "enabled": true, + "id": "{{ID6}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "G nuit", + "next_run": 1774830600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "30 2 * * *", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID7}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "H quart d'heure", + "next_run": 1774700100000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "*/15 * * * *", + "tz": "Nulle/Part" + }, + { + "enabled": true, + "id": "{{ID8}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "I dimanche", + "next_run": 1774771200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 8 * * 7", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID9}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "J une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@once 2026-03-29 03:30", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID11}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "exit: 2 · {{DUR}}\n\nstderr:\noups\n", + "last_ok": false, + "last_report": "", + "last_run": 1774738800000, + "name": "K échec", + "next_run": 1774742400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "echec.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID12}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": true, + "last_report": "(interrompue manuellement)", + "last_run": 1774738800000, + "name": "L lente", + "next_run": 1774742400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "lent.sh", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID13}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "N script une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@once 2026-03-29 03:30", + "script": "ok.sh", + "tz": "Europe/Paris" + }, + { + "enabled": false, + "id": "disparu", + "kind": "script", + "last_dur_ms": 0, + "last_error": "script introuvable : parti.sh", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S disparu", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "script": "parti.sh", + "tz": "" + }, + { + "enabled": false, + "id": "remonte", + "kind": "script", + "last_dur_ms": 0, + "last_error": "nom de script invalide (pas de chemin absolu ni de ../) : ../x.sh", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S remonte", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "script": "../x.sh", + "tz": "" + }, + { + "enabled": false, + "id": "vide", + "kind": "script", + "last_dur_ms": 0, + "last_error": "aucun script associé à la tâche", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S vide", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "tz": "" + } + ] + }, + { + "name": "désactiver la tâche IA 2", + "method": "POST", + "path": "/api/tasks/toggle", + "body": "{\"id\":\"{{ID2}}\",\"on\":false}", + "status": 200, + "out": { + "ok": true + }, + "tasks": [ + { + "enabled": true, + "id": "{{ID10}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": true, + "last_report": "exit: 0 · {{DUR}}\n\nstdout:\nbonjour\n", + "last_run": 1774739400000, + "name": "A script", + "next_run": 1774743000000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "ok.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID1}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "B renommée", + "next_run": 1774711800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Autre.", + "schedule": "@every 3h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID2}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "C une heure et demie", + "next_run": 1774708200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@EVERY 1.5h", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID3}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "D chaque soir", + "next_run": 1774735200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 1d@23:00", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID4}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "E tous les trois jours", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 3d@9:30", + "tz": "America/New_York" + }, + { + "enabled": true, + "id": "{{ID5}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "F semaine", + "next_run": 1774841400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 9 * * 1-5", + "tz": "Asia/Kolkata" + }, + { + "enabled": true, + "id": "{{ID6}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "G nuit", + "next_run": 1774830600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "30 2 * * *", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID7}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "H quart d'heure", + "next_run": 1774700100000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "*/15 * * * *", + "tz": "Nulle/Part" + }, + { + "enabled": true, + "id": "{{ID8}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "I dimanche", + "next_run": 1774771200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 8 * * 7", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID9}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "J une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@once 2026-03-29 03:30", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID11}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "exit: 2 · {{DUR}}\n\nstderr:\noups\n", + "last_ok": false, + "last_report": "", + "last_run": 1774738800000, + "name": "K échec", + "next_run": 1774742400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "echec.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID12}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": true, + "last_report": "(interrompue manuellement)", + "last_run": 1774738800000, + "name": "L lente", + "next_run": 1774742400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "lent.sh", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID13}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "N script une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@once 2026-03-29 03:30", + "script": "ok.sh", + "tz": "Europe/Paris" + }, + { + "enabled": false, + "id": "disparu", + "kind": "script", + "last_dur_ms": 0, + "last_error": "script introuvable : parti.sh", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S disparu", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "script": "parti.sh", + "tz": "" + }, + { + "enabled": false, + "id": "remonte", + "kind": "script", + "last_dur_ms": 0, + "last_error": "nom de script invalide (pas de chemin absolu ni de ../) : ../x.sh", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S remonte", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "script": "../x.sh", + "tz": "" + }, + { + "enabled": false, + "id": "vide", + "kind": "script", + "last_dur_ms": 0, + "last_error": "aucun script associé à la tâche", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S vide", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "tz": "" + } + ] + }, + { + "name": "désactiver la tâche IA 3", + "method": "POST", + "path": "/api/tasks/toggle", + "body": "{\"id\":\"{{ID3}}\",\"on\":false}", + "status": 200, + "out": { + "ok": true + }, + "tasks": [ + { + "enabled": true, + "id": "{{ID10}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": true, + "last_report": "exit: 0 · {{DUR}}\n\nstdout:\nbonjour\n", + "last_run": 1774739400000, + "name": "A script", + "next_run": 1774743000000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "ok.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID1}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "B renommée", + "next_run": 1774711800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Autre.", + "schedule": "@every 3h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID2}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "C une heure et demie", + "next_run": 1774708200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@EVERY 1.5h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID3}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "D chaque soir", + "next_run": 1774735200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 1d@23:00", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID4}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "E tous les trois jours", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 3d@9:30", + "tz": "America/New_York" + }, + { + "enabled": true, + "id": "{{ID5}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "F semaine", + "next_run": 1774841400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 9 * * 1-5", + "tz": "Asia/Kolkata" + }, + { + "enabled": true, + "id": "{{ID6}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "G nuit", + "next_run": 1774830600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "30 2 * * *", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID7}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "H quart d'heure", + "next_run": 1774700100000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "*/15 * * * *", + "tz": "Nulle/Part" + }, + { + "enabled": true, + "id": "{{ID8}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "I dimanche", + "next_run": 1774771200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 8 * * 7", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID9}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "J une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@once 2026-03-29 03:30", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID11}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "exit: 2 · {{DUR}}\n\nstderr:\noups\n", + "last_ok": false, + "last_report": "", + "last_run": 1774738800000, + "name": "K échec", + "next_run": 1774742400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "echec.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID12}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": true, + "last_report": "(interrompue manuellement)", + "last_run": 1774738800000, + "name": "L lente", + "next_run": 1774742400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "lent.sh", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID13}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "N script une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@once 2026-03-29 03:30", + "script": "ok.sh", + "tz": "Europe/Paris" + }, + { + "enabled": false, + "id": "disparu", + "kind": "script", + "last_dur_ms": 0, + "last_error": "script introuvable : parti.sh", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S disparu", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "script": "parti.sh", + "tz": "" + }, + { + "enabled": false, + "id": "remonte", + "kind": "script", + "last_dur_ms": 0, + "last_error": "nom de script invalide (pas de chemin absolu ni de ../) : ../x.sh", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S remonte", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "script": "../x.sh", + "tz": "" + }, + { + "enabled": false, + "id": "vide", + "kind": "script", + "last_dur_ms": 0, + "last_error": "aucun script associé à la tâche", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S vide", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "tz": "" + } + ] + }, + { + "name": "désactiver la tâche IA 4", + "method": "POST", + "path": "/api/tasks/toggle", + "body": "{\"id\":\"{{ID4}}\",\"on\":false}", + "status": 200, + "out": { + "ok": true + }, + "tasks": [ + { + "enabled": true, + "id": "{{ID10}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": true, + "last_report": "exit: 0 · {{DUR}}\n\nstdout:\nbonjour\n", + "last_run": 1774739400000, + "name": "A script", + "next_run": 1774743000000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "ok.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID1}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "B renommée", + "next_run": 1774711800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Autre.", + "schedule": "@every 3h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID2}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "C une heure et demie", + "next_run": 1774708200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@EVERY 1.5h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID3}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "D chaque soir", + "next_run": 1774735200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 1d@23:00", + "tz": "Europe/Paris" + }, + { + "enabled": false, + "id": "{{ID4}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "E tous les trois jours", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 3d@9:30", + "tz": "America/New_York" + }, + { + "enabled": true, + "id": "{{ID5}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "F semaine", + "next_run": 1774841400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 9 * * 1-5", + "tz": "Asia/Kolkata" + }, + { + "enabled": true, + "id": "{{ID6}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "G nuit", + "next_run": 1774830600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "30 2 * * *", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID7}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "H quart d'heure", + "next_run": 1774700100000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "*/15 * * * *", + "tz": "Nulle/Part" + }, + { + "enabled": true, + "id": "{{ID8}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "I dimanche", + "next_run": 1774771200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 8 * * 7", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID9}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "J une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@once 2026-03-29 03:30", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID11}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "exit: 2 · {{DUR}}\n\nstderr:\noups\n", + "last_ok": false, + "last_report": "", + "last_run": 1774738800000, + "name": "K échec", + "next_run": 1774742400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "echec.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID12}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": true, + "last_report": "(interrompue manuellement)", + "last_run": 1774738800000, + "name": "L lente", + "next_run": 1774742400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "lent.sh", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID13}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "N script une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@once 2026-03-29 03:30", + "script": "ok.sh", + "tz": "Europe/Paris" + }, + { + "enabled": false, + "id": "disparu", + "kind": "script", + "last_dur_ms": 0, + "last_error": "script introuvable : parti.sh", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S disparu", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "script": "parti.sh", + "tz": "" + }, + { + "enabled": false, + "id": "remonte", + "kind": "script", + "last_dur_ms": 0, + "last_error": "nom de script invalide (pas de chemin absolu ni de ../) : ../x.sh", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S remonte", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "script": "../x.sh", + "tz": "" + }, + { + "enabled": false, + "id": "vide", + "kind": "script", + "last_dur_ms": 0, + "last_error": "aucun script associé à la tâche", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S vide", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "tz": "" + } + ] + }, + { + "name": "désactiver la tâche IA 5", + "method": "POST", + "path": "/api/tasks/toggle", + "body": "{\"id\":\"{{ID5}}\",\"on\":false}", + "status": 200, + "out": { + "ok": true + }, + "tasks": [ + { + "enabled": true, + "id": "{{ID10}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": true, + "last_report": "exit: 0 · {{DUR}}\n\nstdout:\nbonjour\n", + "last_run": 1774739400000, + "name": "A script", + "next_run": 1774743000000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "ok.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID1}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "B renommée", + "next_run": 1774711800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Autre.", + "schedule": "@every 3h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID2}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "C une heure et demie", + "next_run": 1774708200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@EVERY 1.5h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID3}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "D chaque soir", + "next_run": 1774735200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 1d@23:00", + "tz": "Europe/Paris" + }, + { + "enabled": false, + "id": "{{ID4}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "E tous les trois jours", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 3d@9:30", + "tz": "America/New_York" + }, + { + "enabled": false, + "id": "{{ID5}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "F semaine", + "next_run": 1774841400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 9 * * 1-5", + "tz": "Asia/Kolkata" + }, + { + "enabled": true, + "id": "{{ID6}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "G nuit", + "next_run": 1774830600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "30 2 * * *", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID7}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "H quart d'heure", + "next_run": 1774700100000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "*/15 * * * *", + "tz": "Nulle/Part" + }, + { + "enabled": true, + "id": "{{ID8}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "I dimanche", + "next_run": 1774771200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 8 * * 7", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID9}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "J une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@once 2026-03-29 03:30", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID11}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "exit: 2 · {{DUR}}\n\nstderr:\noups\n", + "last_ok": false, + "last_report": "", + "last_run": 1774738800000, + "name": "K échec", + "next_run": 1774742400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "echec.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID12}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": true, + "last_report": "(interrompue manuellement)", + "last_run": 1774738800000, + "name": "L lente", + "next_run": 1774742400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "lent.sh", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID13}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "N script une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@once 2026-03-29 03:30", + "script": "ok.sh", + "tz": "Europe/Paris" + }, + { + "enabled": false, + "id": "disparu", + "kind": "script", + "last_dur_ms": 0, + "last_error": "script introuvable : parti.sh", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S disparu", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "script": "parti.sh", + "tz": "" + }, + { + "enabled": false, + "id": "remonte", + "kind": "script", + "last_dur_ms": 0, + "last_error": "nom de script invalide (pas de chemin absolu ni de ../) : ../x.sh", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S remonte", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "script": "../x.sh", + "tz": "" + }, + { + "enabled": false, + "id": "vide", + "kind": "script", + "last_dur_ms": 0, + "last_error": "aucun script associé à la tâche", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S vide", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "tz": "" + } + ] + }, + { + "name": "désactiver la tâche IA 6", + "method": "POST", + "path": "/api/tasks/toggle", + "body": "{\"id\":\"{{ID6}}\",\"on\":false}", + "status": 200, + "out": { + "ok": true + }, + "tasks": [ + { + "enabled": true, + "id": "{{ID10}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": true, + "last_report": "exit: 0 · {{DUR}}\n\nstdout:\nbonjour\n", + "last_run": 1774739400000, + "name": "A script", + "next_run": 1774743000000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "ok.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID1}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "B renommée", + "next_run": 1774711800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Autre.", + "schedule": "@every 3h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID2}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "C une heure et demie", + "next_run": 1774708200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@EVERY 1.5h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID3}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "D chaque soir", + "next_run": 1774735200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 1d@23:00", + "tz": "Europe/Paris" + }, + { + "enabled": false, + "id": "{{ID4}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "E tous les trois jours", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 3d@9:30", + "tz": "America/New_York" + }, + { + "enabled": false, + "id": "{{ID5}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "F semaine", + "next_run": 1774841400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 9 * * 1-5", + "tz": "Asia/Kolkata" + }, + { + "enabled": false, + "id": "{{ID6}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "G nuit", + "next_run": 1774830600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "30 2 * * *", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID7}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "H quart d'heure", + "next_run": 1774700100000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "*/15 * * * *", + "tz": "Nulle/Part" + }, + { + "enabled": true, + "id": "{{ID8}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "I dimanche", + "next_run": 1774771200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 8 * * 7", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID9}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "J une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@once 2026-03-29 03:30", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID11}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "exit: 2 · {{DUR}}\n\nstderr:\noups\n", + "last_ok": false, + "last_report": "", + "last_run": 1774738800000, + "name": "K échec", + "next_run": 1774742400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "echec.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID12}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": true, + "last_report": "(interrompue manuellement)", + "last_run": 1774738800000, + "name": "L lente", + "next_run": 1774742400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "lent.sh", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID13}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "N script une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@once 2026-03-29 03:30", + "script": "ok.sh", + "tz": "Europe/Paris" + }, + { + "enabled": false, + "id": "disparu", + "kind": "script", + "last_dur_ms": 0, + "last_error": "script introuvable : parti.sh", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S disparu", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "script": "parti.sh", + "tz": "" + }, + { + "enabled": false, + "id": "remonte", + "kind": "script", + "last_dur_ms": 0, + "last_error": "nom de script invalide (pas de chemin absolu ni de ../) : ../x.sh", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S remonte", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "script": "../x.sh", + "tz": "" + }, + { + "enabled": false, + "id": "vide", + "kind": "script", + "last_dur_ms": 0, + "last_error": "aucun script associé à la tâche", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S vide", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "tz": "" + } + ] + }, + { + "name": "désactiver la tâche IA 7", + "method": "POST", + "path": "/api/tasks/toggle", + "body": "{\"id\":\"{{ID7}}\",\"on\":false}", + "status": 200, + "out": { + "ok": true + }, + "tasks": [ + { + "enabled": true, + "id": "{{ID10}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": true, + "last_report": "exit: 0 · {{DUR}}\n\nstdout:\nbonjour\n", + "last_run": 1774739400000, + "name": "A script", + "next_run": 1774743000000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "ok.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID1}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "B renommée", + "next_run": 1774711800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Autre.", + "schedule": "@every 3h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID2}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "C une heure et demie", + "next_run": 1774708200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@EVERY 1.5h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID3}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "D chaque soir", + "next_run": 1774735200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 1d@23:00", + "tz": "Europe/Paris" + }, + { + "enabled": false, + "id": "{{ID4}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "E tous les trois jours", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 3d@9:30", + "tz": "America/New_York" + }, + { + "enabled": false, + "id": "{{ID5}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "F semaine", + "next_run": 1774841400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 9 * * 1-5", + "tz": "Asia/Kolkata" + }, + { + "enabled": false, + "id": "{{ID6}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "G nuit", + "next_run": 1774830600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "30 2 * * *", + "tz": "Europe/Paris" + }, + { + "enabled": false, + "id": "{{ID7}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "H quart d'heure", + "next_run": 1774700100000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "*/15 * * * *", + "tz": "Nulle/Part" + }, + { + "enabled": true, + "id": "{{ID8}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "I dimanche", + "next_run": 1774771200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 8 * * 7", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID9}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "J une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@once 2026-03-29 03:30", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID11}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "exit: 2 · {{DUR}}\n\nstderr:\noups\n", + "last_ok": false, + "last_report": "", + "last_run": 1774738800000, + "name": "K échec", + "next_run": 1774742400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "echec.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID12}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": true, + "last_report": "(interrompue manuellement)", + "last_run": 1774738800000, + "name": "L lente", + "next_run": 1774742400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "lent.sh", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID13}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "N script une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@once 2026-03-29 03:30", + "script": "ok.sh", + "tz": "Europe/Paris" + }, + { + "enabled": false, + "id": "disparu", + "kind": "script", + "last_dur_ms": 0, + "last_error": "script introuvable : parti.sh", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S disparu", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "script": "parti.sh", + "tz": "" + }, + { + "enabled": false, + "id": "remonte", + "kind": "script", + "last_dur_ms": 0, + "last_error": "nom de script invalide (pas de chemin absolu ni de ../) : ../x.sh", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S remonte", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "script": "../x.sh", + "tz": "" + }, + { + "enabled": false, + "id": "vide", + "kind": "script", + "last_dur_ms": 0, + "last_error": "aucun script associé à la tâche", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S vide", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "tz": "" + } + ] + }, + { + "name": "désactiver la tâche IA 8", + "method": "POST", + "path": "/api/tasks/toggle", + "body": "{\"id\":\"{{ID8}}\",\"on\":false}", + "status": 200, + "out": { + "ok": true + }, + "tasks": [ + { + "enabled": true, + "id": "{{ID10}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": true, + "last_report": "exit: 0 · {{DUR}}\n\nstdout:\nbonjour\n", + "last_run": 1774739400000, + "name": "A script", + "next_run": 1774743000000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "ok.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID1}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "B renommée", + "next_run": 1774711800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Autre.", + "schedule": "@every 3h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID2}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "C une heure et demie", + "next_run": 1774708200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@EVERY 1.5h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID3}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "D chaque soir", + "next_run": 1774735200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 1d@23:00", + "tz": "Europe/Paris" + }, + { + "enabled": false, + "id": "{{ID4}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "E tous les trois jours", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 3d@9:30", + "tz": "America/New_York" + }, + { + "enabled": false, + "id": "{{ID5}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "F semaine", + "next_run": 1774841400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 9 * * 1-5", + "tz": "Asia/Kolkata" + }, + { + "enabled": false, + "id": "{{ID6}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "G nuit", + "next_run": 1774830600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "30 2 * * *", + "tz": "Europe/Paris" + }, + { + "enabled": false, + "id": "{{ID7}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "H quart d'heure", + "next_run": 1774700100000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "*/15 * * * *", + "tz": "Nulle/Part" + }, + { + "enabled": false, + "id": "{{ID8}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "I dimanche", + "next_run": 1774771200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 8 * * 7", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID9}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "J une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@once 2026-03-29 03:30", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID11}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "exit: 2 · {{DUR}}\n\nstderr:\noups\n", + "last_ok": false, + "last_report": "", + "last_run": 1774738800000, + "name": "K échec", + "next_run": 1774742400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "echec.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID12}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": true, + "last_report": "(interrompue manuellement)", + "last_run": 1774738800000, + "name": "L lente", + "next_run": 1774742400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "lent.sh", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID13}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "N script une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@once 2026-03-29 03:30", + "script": "ok.sh", + "tz": "Europe/Paris" + }, + { + "enabled": false, + "id": "disparu", + "kind": "script", + "last_dur_ms": 0, + "last_error": "script introuvable : parti.sh", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S disparu", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "script": "parti.sh", + "tz": "" + }, + { + "enabled": false, + "id": "remonte", + "kind": "script", + "last_dur_ms": 0, + "last_error": "nom de script invalide (pas de chemin absolu ni de ../) : ../x.sh", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S remonte", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "script": "../x.sh", + "tz": "" + }, + { + "enabled": false, + "id": "vide", + "kind": "script", + "last_dur_ms": 0, + "last_error": "aucun script associé à la tâche", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S vide", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "tz": "" + } + ] + }, + { + "name": "désactiver la tâche IA 9", + "method": "POST", + "path": "/api/tasks/toggle", + "body": "{\"id\":\"{{ID9}}\",\"on\":false}", + "status": 200, + "out": { + "ok": true + }, + "tasks": [ + { + "enabled": true, + "id": "{{ID10}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": true, + "last_report": "exit: 0 · {{DUR}}\n\nstdout:\nbonjour\n", + "last_run": 1774739400000, + "name": "A script", + "next_run": 1774743000000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "ok.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID1}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "B renommée", + "next_run": 1774711800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Autre.", + "schedule": "@every 3h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID2}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "C une heure et demie", + "next_run": 1774708200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@EVERY 1.5h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID3}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "D chaque soir", + "next_run": 1774735200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 1d@23:00", + "tz": "Europe/Paris" + }, + { + "enabled": false, + "id": "{{ID4}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "E tous les trois jours", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 3d@9:30", + "tz": "America/New_York" + }, + { + "enabled": false, + "id": "{{ID5}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "F semaine", + "next_run": 1774841400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 9 * * 1-5", + "tz": "Asia/Kolkata" + }, + { + "enabled": false, + "id": "{{ID6}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "G nuit", + "next_run": 1774830600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "30 2 * * *", + "tz": "Europe/Paris" + }, + { + "enabled": false, + "id": "{{ID7}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "H quart d'heure", + "next_run": 1774700100000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "*/15 * * * *", + "tz": "Nulle/Part" + }, + { + "enabled": false, + "id": "{{ID8}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "I dimanche", + "next_run": 1774771200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 8 * * 7", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID9}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "J une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@once 2026-03-29 03:30", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID11}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "exit: 2 · {{DUR}}\n\nstderr:\noups\n", + "last_ok": false, + "last_report": "", + "last_run": 1774738800000, + "name": "K échec", + "next_run": 1774742400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "echec.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID12}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": true, + "last_report": "(interrompue manuellement)", + "last_run": 1774738800000, + "name": "L lente", + "next_run": 1774742400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "lent.sh", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID13}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "N script une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@once 2026-03-29 03:30", + "script": "ok.sh", + "tz": "Europe/Paris" + }, + { + "enabled": false, + "id": "disparu", + "kind": "script", + "last_dur_ms": 0, + "last_error": "script introuvable : parti.sh", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S disparu", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "script": "parti.sh", + "tz": "" + }, + { + "enabled": false, + "id": "remonte", + "kind": "script", + "last_dur_ms": 0, + "last_error": "nom de script invalide (pas de chemin absolu ni de ../) : ../x.sh", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S remonte", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "script": "../x.sh", + "tz": "" + }, + { + "enabled": false, + "id": "vide", + "kind": "script", + "last_dur_ms": 0, + "last_error": "aucun script associé à la tâche", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S vide", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "tz": "" + } + ] + }, + { + "name": "tic pendant une optimisation : rien ne part", + "now": "2026-03-29T00:30:00Z", + "tick": true, + "tune": true, + "tasks": [ + { + "enabled": true, + "id": "{{ID10}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": true, + "last_report": "exit: 0 · {{DUR}}\n\nstdout:\nbonjour\n", + "last_run": 1774739400000, + "name": "A script", + "next_run": 1774743000000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "ok.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID1}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "B renommée", + "next_run": 1774711800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Autre.", + "schedule": "@every 3h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID2}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "C une heure et demie", + "next_run": 1774708200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@EVERY 1.5h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID3}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "D chaque soir", + "next_run": 1774735200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 1d@23:00", + "tz": "Europe/Paris" + }, + { + "enabled": false, + "id": "{{ID4}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "E tous les trois jours", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 3d@9:30", + "tz": "America/New_York" + }, + { + "enabled": false, + "id": "{{ID5}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "F semaine", + "next_run": 1774841400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 9 * * 1-5", + "tz": "Asia/Kolkata" + }, + { + "enabled": false, + "id": "{{ID6}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "G nuit", + "next_run": 1774830600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "30 2 * * *", + "tz": "Europe/Paris" + }, + { + "enabled": false, + "id": "{{ID7}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "H quart d'heure", + "next_run": 1774700100000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "*/15 * * * *", + "tz": "Nulle/Part" + }, + { + "enabled": false, + "id": "{{ID8}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "I dimanche", + "next_run": 1774771200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 8 * * 7", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID9}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "J une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@once 2026-03-29 03:30", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID11}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "exit: 2 · {{DUR}}\n\nstderr:\noups\n", + "last_ok": false, + "last_report": "", + "last_run": 1774738800000, + "name": "K échec", + "next_run": 1774742400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "echec.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID12}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": true, + "last_report": "(interrompue manuellement)", + "last_run": 1774738800000, + "name": "L lente", + "next_run": 1774742400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "lent.sh", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID13}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "N script une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@once 2026-03-29 03:30", + "script": "ok.sh", + "tz": "Europe/Paris" + }, + { + "enabled": false, + "id": "disparu", + "kind": "script", + "last_dur_ms": 0, + "last_error": "script introuvable : parti.sh", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S disparu", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "script": "parti.sh", + "tz": "" + }, + { + "enabled": false, + "id": "remonte", + "kind": "script", + "last_dur_ms": 0, + "last_error": "nom de script invalide (pas de chemin absolu ni de ../) : ../x.sh", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S remonte", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "script": "../x.sh", + "tz": "" + }, + { + "enabled": false, + "id": "vide", + "kind": "script", + "last_dur_ms": 0, + "last_error": "aucun script associé à la tâche", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S vide", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "tz": "" + } + ] + }, + { + "name": "tic : la première tâche due tourne", + "now": "2026-03-29T00:30:00Z", + "tick": true, + "tasks": [ + { + "enabled": true, + "id": "{{ID10}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": true, + "last_report": "exit: 0 · {{DUR}}\n\nstdout:\nbonjour\n", + "last_run": 1774744200000, + "name": "A script", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "ok.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID1}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "B renommée", + "next_run": 1774711800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Autre.", + "schedule": "@every 3h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID2}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "C une heure et demie", + "next_run": 1774708200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@EVERY 1.5h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID3}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "D chaque soir", + "next_run": 1774735200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 1d@23:00", + "tz": "Europe/Paris" + }, + { + "enabled": false, + "id": "{{ID4}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "E tous les trois jours", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 3d@9:30", + "tz": "America/New_York" + }, + { + "enabled": false, + "id": "{{ID5}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "F semaine", + "next_run": 1774841400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 9 * * 1-5", + "tz": "Asia/Kolkata" + }, + { + "enabled": false, + "id": "{{ID6}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "G nuit", + "next_run": 1774830600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "30 2 * * *", + "tz": "Europe/Paris" + }, + { + "enabled": false, + "id": "{{ID7}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "H quart d'heure", + "next_run": 1774700100000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "*/15 * * * *", + "tz": "Nulle/Part" + }, + { + "enabled": false, + "id": "{{ID8}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "I dimanche", + "next_run": 1774771200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 8 * * 7", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID9}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "J une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@once 2026-03-29 03:30", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID11}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "exit: 2 · {{DUR}}\n\nstderr:\noups\n", + "last_ok": false, + "last_report": "", + "last_run": 1774738800000, + "name": "K échec", + "next_run": 1774742400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "echec.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID12}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": true, + "last_report": "(interrompue manuellement)", + "last_run": 1774738800000, + "name": "L lente", + "next_run": 1774742400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "lent.sh", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID13}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "N script une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@once 2026-03-29 03:30", + "script": "ok.sh", + "tz": "Europe/Paris" + }, + { + "enabled": false, + "id": "disparu", + "kind": "script", + "last_dur_ms": 0, + "last_error": "script introuvable : parti.sh", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S disparu", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "script": "parti.sh", + "tz": "" + }, + { + "enabled": false, + "id": "remonte", + "kind": "script", + "last_dur_ms": 0, + "last_error": "nom de script invalide (pas de chemin absolu ni de ../) : ../x.sh", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S remonte", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "script": "../x.sh", + "tz": "" + }, + { + "enabled": false, + "id": "vide", + "kind": "script", + "last_dur_ms": 0, + "last_error": "aucun script associé à la tâche", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S vide", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "tz": "" + } + ] + }, + { + "name": "tic : la suivante", + "tick": true, + "tasks": [ + { + "enabled": true, + "id": "{{ID10}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": true, + "last_report": "exit: 0 · {{DUR}}\n\nstdout:\nbonjour\n", + "last_run": 1774744200000, + "name": "A script", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "ok.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID1}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "B renommée", + "next_run": 1774711800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Autre.", + "schedule": "@every 3h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID2}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "C une heure et demie", + "next_run": 1774708200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@EVERY 1.5h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID3}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "D chaque soir", + "next_run": 1774735200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 1d@23:00", + "tz": "Europe/Paris" + }, + { + "enabled": false, + "id": "{{ID4}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "E tous les trois jours", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 3d@9:30", + "tz": "America/New_York" + }, + { + "enabled": false, + "id": "{{ID5}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "F semaine", + "next_run": 1774841400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 9 * * 1-5", + "tz": "Asia/Kolkata" + }, + { + "enabled": false, + "id": "{{ID6}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "G nuit", + "next_run": 1774830600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "30 2 * * *", + "tz": "Europe/Paris" + }, + { + "enabled": false, + "id": "{{ID7}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "H quart d'heure", + "next_run": 1774700100000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "*/15 * * * *", + "tz": "Nulle/Part" + }, + { + "enabled": false, + "id": "{{ID8}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "I dimanche", + "next_run": 1774771200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 8 * * 7", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID9}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "J une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@once 2026-03-29 03:30", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID11}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "exit: 2 · {{DUR}}\n\nstderr:\noups\n", + "last_ok": false, + "last_report": "", + "last_run": 1774744200000, + "name": "K échec", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "echec.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID12}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": true, + "last_report": "(interrompue manuellement)", + "last_run": 1774738800000, + "name": "L lente", + "next_run": 1774742400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "lent.sh", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID13}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "N script une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@once 2026-03-29 03:30", + "script": "ok.sh", + "tz": "Europe/Paris" + }, + { + "enabled": false, + "id": "disparu", + "kind": "script", + "last_dur_ms": 0, + "last_error": "script introuvable : parti.sh", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S disparu", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "script": "parti.sh", + "tz": "" + }, + { + "enabled": false, + "id": "remonte", + "kind": "script", + "last_dur_ms": 0, + "last_error": "nom de script invalide (pas de chemin absolu ni de ../) : ../x.sh", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S remonte", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "script": "../x.sh", + "tz": "" + }, + { + "enabled": false, + "id": "vide", + "kind": "script", + "last_dur_ms": 0, + "last_error": "aucun script associé à la tâche", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S vide", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "tz": "" + } + ] + }, + { + "name": "tic : rien de dû", + "tick": true, + "tasks": [ + { + "enabled": true, + "id": "{{ID10}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": true, + "last_report": "exit: 0 · {{DUR}}\n\nstdout:\nbonjour\n", + "last_run": 1774744200000, + "name": "A script", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "ok.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID1}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "B renommée", + "next_run": 1774711800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Autre.", + "schedule": "@every 3h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID2}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "C une heure et demie", + "next_run": 1774708200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@EVERY 1.5h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID3}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "D chaque soir", + "next_run": 1774735200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 1d@23:00", + "tz": "Europe/Paris" + }, + { + "enabled": false, + "id": "{{ID4}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "E tous les trois jours", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 3d@9:30", + "tz": "America/New_York" + }, + { + "enabled": false, + "id": "{{ID5}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "F semaine", + "next_run": 1774841400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 9 * * 1-5", + "tz": "Asia/Kolkata" + }, + { + "enabled": false, + "id": "{{ID6}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "G nuit", + "next_run": 1774830600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "30 2 * * *", + "tz": "Europe/Paris" + }, + { + "enabled": false, + "id": "{{ID7}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "H quart d'heure", + "next_run": 1774700100000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "*/15 * * * *", + "tz": "Nulle/Part" + }, + { + "enabled": false, + "id": "{{ID8}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "I dimanche", + "next_run": 1774771200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 8 * * 7", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID9}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "J une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@once 2026-03-29 03:30", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID11}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "exit: 2 · {{DUR}}\n\nstderr:\noups\n", + "last_ok": false, + "last_report": "", + "last_run": 1774744200000, + "name": "K échec", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "echec.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID12}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": true, + "last_report": "(interrompue manuellement)", + "last_run": 1774738800000, + "name": "L lente", + "next_run": 1774742400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "lent.sh", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID13}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "N script une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@once 2026-03-29 03:30", + "script": "ok.sh", + "tz": "Europe/Paris" + }, + { + "enabled": false, + "id": "disparu", + "kind": "script", + "last_dur_ms": 0, + "last_error": "script introuvable : parti.sh", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S disparu", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "script": "parti.sh", + "tz": "" + }, + { + "enabled": false, + "id": "remonte", + "kind": "script", + "last_dur_ms": 0, + "last_error": "nom de script invalide (pas de chemin absolu ni de ../) : ../x.sh", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S remonte", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "script": "../x.sh", + "tz": "" + }, + { + "enabled": false, + "id": "vide", + "kind": "script", + "last_dur_ms": 0, + "last_error": "aucun script associé à la tâche", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S vide", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "tz": "" + } + ] + }, + { + "name": "tic : de nouveau dû", + "now": "2026-03-29T01:31:00Z", + "tick": true, + "tasks": [ + { + "enabled": true, + "id": "{{ID10}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": true, + "last_report": "exit: 0 · {{DUR}}\n\nstdout:\nbonjour\n", + "last_run": 1774747860000, + "name": "A script", + "next_run": 1774751460000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "ok.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID1}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "B renommée", + "next_run": 1774711800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Autre.", + "schedule": "@every 3h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID2}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "C une heure et demie", + "next_run": 1774708200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@EVERY 1.5h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID3}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "D chaque soir", + "next_run": 1774735200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 1d@23:00", + "tz": "Europe/Paris" + }, + { + "enabled": false, + "id": "{{ID4}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "E tous les trois jours", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 3d@9:30", + "tz": "America/New_York" + }, + { + "enabled": false, + "id": "{{ID5}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "F semaine", + "next_run": 1774841400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 9 * * 1-5", + "tz": "Asia/Kolkata" + }, + { + "enabled": false, + "id": "{{ID6}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "G nuit", + "next_run": 1774830600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "30 2 * * *", + "tz": "Europe/Paris" + }, + { + "enabled": false, + "id": "{{ID7}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "H quart d'heure", + "next_run": 1774700100000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "*/15 * * * *", + "tz": "Nulle/Part" + }, + { + "enabled": false, + "id": "{{ID8}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "I dimanche", + "next_run": 1774771200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 8 * * 7", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID9}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "J une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@once 2026-03-29 03:30", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID11}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "exit: 2 · {{DUR}}\n\nstderr:\noups\n", + "last_ok": false, + "last_report": "", + "last_run": 1774744200000, + "name": "K échec", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "echec.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID12}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": true, + "last_report": "(interrompue manuellement)", + "last_run": 1774738800000, + "name": "L lente", + "next_run": 1774742400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "lent.sh", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID13}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "N script une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@once 2026-03-29 03:30", + "script": "ok.sh", + "tz": "Europe/Paris" + }, + { + "enabled": false, + "id": "disparu", + "kind": "script", + "last_dur_ms": 0, + "last_error": "script introuvable : parti.sh", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S disparu", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "script": "parti.sh", + "tz": "" + }, + { + "enabled": false, + "id": "remonte", + "kind": "script", + "last_dur_ms": 0, + "last_error": "nom de script invalide (pas de chemin absolu ni de ../) : ../x.sh", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S remonte", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "script": "../x.sh", + "tz": "" + }, + { + "enabled": false, + "id": "vide", + "kind": "script", + "last_dur_ms": 0, + "last_error": "aucun script associé à la tâche", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S vide", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "tz": "" + } + ] + }, + { + "name": "tic : la suivante, encore", + "tick": true, + "tasks": [ + { + "enabled": true, + "id": "{{ID10}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": true, + "last_report": "exit: 0 · {{DUR}}\n\nstdout:\nbonjour\n", + "last_run": 1774747860000, + "name": "A script", + "next_run": 1774751460000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "ok.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID1}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "B renommée", + "next_run": 1774711800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Autre.", + "schedule": "@every 3h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID2}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "C une heure et demie", + "next_run": 1774708200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@EVERY 1.5h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID3}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "D chaque soir", + "next_run": 1774735200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 1d@23:00", + "tz": "Europe/Paris" + }, + { + "enabled": false, + "id": "{{ID4}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "E tous les trois jours", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 3d@9:30", + "tz": "America/New_York" + }, + { + "enabled": false, + "id": "{{ID5}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "F semaine", + "next_run": 1774841400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 9 * * 1-5", + "tz": "Asia/Kolkata" + }, + { + "enabled": false, + "id": "{{ID6}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "G nuit", + "next_run": 1774830600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "30 2 * * *", + "tz": "Europe/Paris" + }, + { + "enabled": false, + "id": "{{ID7}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "H quart d'heure", + "next_run": 1774700100000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "*/15 * * * *", + "tz": "Nulle/Part" + }, + { + "enabled": false, + "id": "{{ID8}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "I dimanche", + "next_run": 1774771200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 8 * * 7", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID9}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "J une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@once 2026-03-29 03:30", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID11}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "exit: 2 · {{DUR}}\n\nstderr:\noups\n", + "last_ok": false, + "last_report": "", + "last_run": 1774747860000, + "name": "K échec", + "next_run": 1774751460000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "echec.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID12}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": true, + "last_report": "(interrompue manuellement)", + "last_run": 1774738800000, + "name": "L lente", + "next_run": 1774742400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "lent.sh", + "tz": "" + }, + { + "enabled": true, + "id": "{{ID13}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "N script une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@once 2026-03-29 03:30", + "script": "ok.sh", + "tz": "Europe/Paris" + }, + { + "enabled": false, + "id": "disparu", + "kind": "script", + "last_dur_ms": 0, + "last_error": "script introuvable : parti.sh", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S disparu", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "script": "parti.sh", + "tz": "" + }, + { + "enabled": false, + "id": "remonte", + "kind": "script", + "last_dur_ms": 0, + "last_error": "nom de script invalide (pas de chemin absolu ni de ../) : ../x.sh", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S remonte", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "script": "../x.sh", + "tz": "" + }, + { + "enabled": false, + "id": "vide", + "kind": "script", + "last_dur_ms": 0, + "last_error": "aucun script associé à la tâche", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S vide", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "tz": "" + } + ] + }, + { + "name": "tic : la tâche une fois tourne puis s'éteint", + "tick": true, + "tasks": [ + { + "enabled": true, + "id": "{{ID10}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": true, + "last_report": "exit: 0 · {{DUR}}\n\nstdout:\nbonjour\n", + "last_run": 1774747860000, + "name": "A script", + "next_run": 1774751460000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "ok.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID1}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "B renommée", + "next_run": 1774711800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Autre.", + "schedule": "@every 3h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID2}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "C une heure et demie", + "next_run": 1774708200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@EVERY 1.5h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID3}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "D chaque soir", + "next_run": 1774735200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 1d@23:00", + "tz": "Europe/Paris" + }, + { + "enabled": false, + "id": "{{ID4}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "E tous les trois jours", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 3d@9:30", + "tz": "America/New_York" + }, + { + "enabled": false, + "id": "{{ID5}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "F semaine", + "next_run": 1774841400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 9 * * 1-5", + "tz": "Asia/Kolkata" + }, + { + "enabled": false, + "id": "{{ID6}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "G nuit", + "next_run": 1774830600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "30 2 * * *", + "tz": "Europe/Paris" + }, + { + "enabled": false, + "id": "{{ID7}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "H quart d'heure", + "next_run": 1774700100000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "*/15 * * * *", + "tz": "Nulle/Part" + }, + { + "enabled": false, + "id": "{{ID8}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "I dimanche", + "next_run": 1774771200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 8 * * 7", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID9}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "J une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@once 2026-03-29 03:30", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID11}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "exit: 2 · {{DUR}}\n\nstderr:\noups\n", + "last_ok": false, + "last_report": "", + "last_run": 1774747860000, + "name": "K échec", + "next_run": 1774751460000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "echec.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID12}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": true, + "last_report": "(interrompue manuellement)", + "last_run": 1774738800000, + "name": "L lente", + "next_run": 1774742400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "lent.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID13}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": true, + "last_report": "exit: 0 · {{DUR}}\n\nstdout:\nbonjour\n", + "last_run": 1774747860000, + "name": "N script une fois", + "next_run": 0, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@once 2026-03-29 03:30", + "script": "ok.sh", + "tz": "Europe/Paris" + }, + { + "enabled": false, + "id": "disparu", + "kind": "script", + "last_dur_ms": 0, + "last_error": "script introuvable : parti.sh", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S disparu", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "script": "parti.sh", + "tz": "" + }, + { + "enabled": false, + "id": "remonte", + "kind": "script", + "last_dur_ms": 0, + "last_error": "nom de script invalide (pas de chemin absolu ni de ../) : ../x.sh", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S remonte", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "script": "../x.sh", + "tz": "" + }, + { + "enabled": false, + "id": "vide", + "kind": "script", + "last_dur_ms": 0, + "last_error": "aucun script associé à la tâche", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S vide", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "tz": "" + } + ] + }, + { + "name": "tic : plus rien", + "tick": true, + "tasks": [ + { + "enabled": true, + "id": "{{ID10}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": true, + "last_report": "exit: 0 · {{DUR}}\n\nstdout:\nbonjour\n", + "last_run": 1774747860000, + "name": "A script", + "next_run": 1774751460000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "ok.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID1}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "B renommée", + "next_run": 1774711800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Autre.", + "schedule": "@every 3h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID2}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "C une heure et demie", + "next_run": 1774708200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@EVERY 1.5h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID3}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "D chaque soir", + "next_run": 1774735200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 1d@23:00", + "tz": "Europe/Paris" + }, + { + "enabled": false, + "id": "{{ID4}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "E tous les trois jours", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 3d@9:30", + "tz": "America/New_York" + }, + { + "enabled": false, + "id": "{{ID5}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "F semaine", + "next_run": 1774841400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 9 * * 1-5", + "tz": "Asia/Kolkata" + }, + { + "enabled": false, + "id": "{{ID6}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "G nuit", + "next_run": 1774830600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "30 2 * * *", + "tz": "Europe/Paris" + }, + { + "enabled": false, + "id": "{{ID7}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "H quart d'heure", + "next_run": 1774700100000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "*/15 * * * *", + "tz": "Nulle/Part" + }, + { + "enabled": false, + "id": "{{ID8}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "I dimanche", + "next_run": 1774771200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 8 * * 7", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID9}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "J une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@once 2026-03-29 03:30", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID11}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "exit: 2 · {{DUR}}\n\nstderr:\noups\n", + "last_ok": false, + "last_report": "", + "last_run": 1774747860000, + "name": "K échec", + "next_run": 1774751460000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "echec.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID12}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": true, + "last_report": "(interrompue manuellement)", + "last_run": 1774738800000, + "name": "L lente", + "next_run": 1774742400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "lent.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID13}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": true, + "last_report": "exit: 0 · {{DUR}}\n\nstdout:\nbonjour\n", + "last_run": 1774747860000, + "name": "N script une fois", + "next_run": 0, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@once 2026-03-29 03:30", + "script": "ok.sh", + "tz": "Europe/Paris" + }, + { + "enabled": false, + "id": "disparu", + "kind": "script", + "last_dur_ms": 0, + "last_error": "script introuvable : parti.sh", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S disparu", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "script": "parti.sh", + "tz": "" + }, + { + "enabled": false, + "id": "remonte", + "kind": "script", + "last_dur_ms": 0, + "last_error": "nom de script invalide (pas de chemin absolu ni de ../) : ../x.sh", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S remonte", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "script": "../x.sh", + "tz": "" + }, + { + "enabled": false, + "id": "vide", + "kind": "script", + "last_dur_ms": 0, + "last_error": "aucun script associé à la tâche", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S vide", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "tz": "" + } + ] + }, + { + "name": "tâche sans prochain passage", + "now": "2026-03-29T02:00:00Z", + "tick": true, + "put_task": "{\"id\":\"manuelle\",\"name\":\"O manuelle\",\"kind\":\"script\",\"script\":\"ok.sh\",\"schedule\":\"0 4 * * *\",\"tz\":\"UTC\",\"enabled\":true}", + "tasks": [ + { + "enabled": true, + "id": "{{ID10}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": true, + "last_report": "exit: 0 · {{DUR}}\n\nstdout:\nbonjour\n", + "last_run": 1774747860000, + "name": "A script", + "next_run": 1774751460000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "ok.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID1}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "B renommée", + "next_run": 1774711800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Autre.", + "schedule": "@every 3h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID2}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "C une heure et demie", + "next_run": 1774708200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@EVERY 1.5h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID3}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "D chaque soir", + "next_run": 1774735200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 1d@23:00", + "tz": "Europe/Paris" + }, + { + "enabled": false, + "id": "{{ID4}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "E tous les trois jours", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 3d@9:30", + "tz": "America/New_York" + }, + { + "enabled": false, + "id": "{{ID5}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "F semaine", + "next_run": 1774841400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 9 * * 1-5", + "tz": "Asia/Kolkata" + }, + { + "enabled": false, + "id": "{{ID6}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "G nuit", + "next_run": 1774830600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "30 2 * * *", + "tz": "Europe/Paris" + }, + { + "enabled": false, + "id": "{{ID7}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "H quart d'heure", + "next_run": 1774700100000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "*/15 * * * *", + "tz": "Nulle/Part" + }, + { + "enabled": false, + "id": "{{ID8}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "I dimanche", + "next_run": 1774771200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 8 * * 7", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID9}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "J une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@once 2026-03-29 03:30", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID11}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "exit: 2 · {{DUR}}\n\nstderr:\noups\n", + "last_ok": false, + "last_report": "", + "last_run": 1774747860000, + "name": "K échec", + "next_run": 1774751460000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "echec.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID12}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": true, + "last_report": "(interrompue manuellement)", + "last_run": 1774738800000, + "name": "L lente", + "next_run": 1774742400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "lent.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID13}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": true, + "last_report": "exit: 0 · {{DUR}}\n\nstdout:\nbonjour\n", + "last_run": 1774747860000, + "name": "N script une fois", + "next_run": 0, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@once 2026-03-29 03:30", + "script": "ok.sh", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "manuelle", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "O manuelle", + "next_run": 1774756800000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "0 4 * * *", + "script": "ok.sh", + "tz": "UTC" + }, + { + "enabled": false, + "id": "disparu", + "kind": "script", + "last_dur_ms": 0, + "last_error": "script introuvable : parti.sh", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S disparu", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "script": "parti.sh", + "tz": "" + }, + { + "enabled": false, + "id": "remonte", + "kind": "script", + "last_dur_ms": 0, + "last_error": "nom de script invalide (pas de chemin absolu ni de ../) : ../x.sh", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S remonte", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "script": "../x.sh", + "tz": "" + }, + { + "enabled": false, + "id": "vide", + "kind": "script", + "last_dur_ms": 0, + "last_error": "aucun script associé à la tâche", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S vide", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "tz": "" + } + ] + }, + { + "name": "tâche à la fréquence devenue invalide", + "tick": true, + "put_task": "{\"id\":\"cassee\",\"name\":\"P cassée\",\"kind\":\"script\",\"script\":\"ok.sh\",\"schedule\":\"@every 10s\",\"enabled\":true}", + "tasks": [ + { + "enabled": true, + "id": "{{ID10}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": true, + "last_report": "exit: 0 · {{DUR}}\n\nstdout:\nbonjour\n", + "last_run": 1774747860000, + "name": "A script", + "next_run": 1774751460000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "ok.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID1}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "B renommée", + "next_run": 1774711800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Autre.", + "schedule": "@every 3h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID2}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "C une heure et demie", + "next_run": 1774708200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@EVERY 1.5h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID3}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "D chaque soir", + "next_run": 1774735200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 1d@23:00", + "tz": "Europe/Paris" + }, + { + "enabled": false, + "id": "{{ID4}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "E tous les trois jours", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 3d@9:30", + "tz": "America/New_York" + }, + { + "enabled": false, + "id": "{{ID5}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "F semaine", + "next_run": 1774841400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 9 * * 1-5", + "tz": "Asia/Kolkata" + }, + { + "enabled": false, + "id": "{{ID6}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "G nuit", + "next_run": 1774830600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "30 2 * * *", + "tz": "Europe/Paris" + }, + { + "enabled": false, + "id": "{{ID7}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "H quart d'heure", + "next_run": 1774700100000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "*/15 * * * *", + "tz": "Nulle/Part" + }, + { + "enabled": false, + "id": "{{ID8}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "I dimanche", + "next_run": 1774771200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 8 * * 7", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID9}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "J une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@once 2026-03-29 03:30", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID11}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "exit: 2 · {{DUR}}\n\nstderr:\noups\n", + "last_ok": false, + "last_report": "", + "last_run": 1774747860000, + "name": "K échec", + "next_run": 1774751460000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "echec.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID12}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": true, + "last_report": "(interrompue manuellement)", + "last_run": 1774738800000, + "name": "L lente", + "next_run": 1774742400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "lent.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID13}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": true, + "last_report": "exit: 0 · {{DUR}}\n\nstdout:\nbonjour\n", + "last_run": 1774747860000, + "name": "N script une fois", + "next_run": 0, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@once 2026-03-29 03:30", + "script": "ok.sh", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "manuelle", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "O manuelle", + "next_run": 1774756800000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "0 4 * * *", + "script": "ok.sh", + "tz": "UTC" + }, + { + "enabled": true, + "id": "cassee", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "P cassée", + "next_run": 0, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 10s", + "script": "ok.sh", + "tz": "" + }, + { + "enabled": false, + "id": "disparu", + "kind": "script", + "last_dur_ms": 0, + "last_error": "script introuvable : parti.sh", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S disparu", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "script": "parti.sh", + "tz": "" + }, + { + "enabled": false, + "id": "remonte", + "kind": "script", + "last_dur_ms": 0, + "last_error": "nom de script invalide (pas de chemin absolu ni de ../) : ../x.sh", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S remonte", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "script": "../x.sh", + "tz": "" + }, + { + "enabled": false, + "id": "vide", + "kind": "script", + "last_dur_ms": 0, + "last_error": "aucun script associé à la tâche", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S vide", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "tz": "" + } + ] + }, + { + "name": "même nom, ids dans l'ordre", + "put_task": "{\"id\":\"r2\",\"name\":\"R double\",\"kind\":\"script\",\"script\":\"ok.sh\",\"schedule\":\"@every 1h\"}", + "tasks": [ + { + "enabled": true, + "id": "{{ID10}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": true, + "last_report": "exit: 0 · {{DUR}}\n\nstdout:\nbonjour\n", + "last_run": 1774747860000, + "name": "A script", + "next_run": 1774751460000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "ok.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID1}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "B renommée", + "next_run": 1774711800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Autre.", + "schedule": "@every 3h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID2}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "C une heure et demie", + "next_run": 1774708200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@EVERY 1.5h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID3}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "D chaque soir", + "next_run": 1774735200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 1d@23:00", + "tz": "Europe/Paris" + }, + { + "enabled": false, + "id": "{{ID4}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "E tous les trois jours", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 3d@9:30", + "tz": "America/New_York" + }, + { + "enabled": false, + "id": "{{ID5}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "F semaine", + "next_run": 1774841400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 9 * * 1-5", + "tz": "Asia/Kolkata" + }, + { + "enabled": false, + "id": "{{ID6}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "G nuit", + "next_run": 1774830600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "30 2 * * *", + "tz": "Europe/Paris" + }, + { + "enabled": false, + "id": "{{ID7}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "H quart d'heure", + "next_run": 1774700100000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "*/15 * * * *", + "tz": "Nulle/Part" + }, + { + "enabled": false, + "id": "{{ID8}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "I dimanche", + "next_run": 1774771200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 8 * * 7", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID9}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "J une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@once 2026-03-29 03:30", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID11}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "exit: 2 · {{DUR}}\n\nstderr:\noups\n", + "last_ok": false, + "last_report": "", + "last_run": 1774747860000, + "name": "K échec", + "next_run": 1774751460000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "echec.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID12}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": true, + "last_report": "(interrompue manuellement)", + "last_run": 1774738800000, + "name": "L lente", + "next_run": 1774742400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "lent.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID13}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": true, + "last_report": "exit: 0 · {{DUR}}\n\nstdout:\nbonjour\n", + "last_run": 1774747860000, + "name": "N script une fois", + "next_run": 0, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@once 2026-03-29 03:30", + "script": "ok.sh", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "manuelle", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "O manuelle", + "next_run": 1774756800000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "0 4 * * *", + "script": "ok.sh", + "tz": "UTC" + }, + { + "enabled": true, + "id": "cassee", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "P cassée", + "next_run": 0, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 10s", + "script": "ok.sh", + "tz": "" + }, + { + "enabled": false, + "id": "r2", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "R double", + "next_run": 0, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "script": "ok.sh", + "tz": "" + }, + { + "enabled": false, + "id": "disparu", + "kind": "script", + "last_dur_ms": 0, + "last_error": "script introuvable : parti.sh", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S disparu", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "script": "parti.sh", + "tz": "" + }, + { + "enabled": false, + "id": "remonte", + "kind": "script", + "last_dur_ms": 0, + "last_error": "nom de script invalide (pas de chemin absolu ni de ../) : ../x.sh", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S remonte", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "script": "../x.sh", + "tz": "" + }, + { + "enabled": false, + "id": "vide", + "kind": "script", + "last_dur_ms": 0, + "last_error": "aucun script associé à la tâche", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S vide", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "tz": "" + } + ] + }, + { + "name": "même nom, second", + "put_task": "{\"id\":\"r1\",\"name\":\"R double\",\"kind\":\"script\",\"script\":\"ok.sh\",\"schedule\":\"@every 1h\"}", + "tasks": [ + { + "enabled": true, + "id": "{{ID10}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": true, + "last_report": "exit: 0 · {{DUR}}\n\nstdout:\nbonjour\n", + "last_run": 1774747860000, + "name": "A script", + "next_run": 1774751460000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "ok.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID1}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "B renommée", + "next_run": 1774711800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Autre.", + "schedule": "@every 3h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID2}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "C une heure et demie", + "next_run": 1774708200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@EVERY 1.5h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID3}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "D chaque soir", + "next_run": 1774735200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 1d@23:00", + "tz": "Europe/Paris" + }, + { + "enabled": false, + "id": "{{ID4}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "E tous les trois jours", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 3d@9:30", + "tz": "America/New_York" + }, + { + "enabled": false, + "id": "{{ID5}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "F semaine", + "next_run": 1774841400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 9 * * 1-5", + "tz": "Asia/Kolkata" + }, + { + "enabled": false, + "id": "{{ID6}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "G nuit", + "next_run": 1774830600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "30 2 * * *", + "tz": "Europe/Paris" + }, + { + "enabled": false, + "id": "{{ID7}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "H quart d'heure", + "next_run": 1774700100000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "*/15 * * * *", + "tz": "Nulle/Part" + }, + { + "enabled": false, + "id": "{{ID8}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "I dimanche", + "next_run": 1774771200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 8 * * 7", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID9}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "J une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@once 2026-03-29 03:30", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID11}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "exit: 2 · {{DUR}}\n\nstderr:\noups\n", + "last_ok": false, + "last_report": "", + "last_run": 1774747860000, + "name": "K échec", + "next_run": 1774751460000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "echec.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID12}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": true, + "last_report": "(interrompue manuellement)", + "last_run": 1774738800000, + "name": "L lente", + "next_run": 1774742400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "lent.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID13}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": true, + "last_report": "exit: 0 · {{DUR}}\n\nstdout:\nbonjour\n", + "last_run": 1774747860000, + "name": "N script une fois", + "next_run": 0, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@once 2026-03-29 03:30", + "script": "ok.sh", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "manuelle", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "O manuelle", + "next_run": 1774756800000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "0 4 * * *", + "script": "ok.sh", + "tz": "UTC" + }, + { + "enabled": true, + "id": "cassee", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "P cassée", + "next_run": 0, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 10s", + "script": "ok.sh", + "tz": "" + }, + { + "enabled": false, + "id": "r1", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "R double", + "next_run": 0, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "script": "ok.sh", + "tz": "" + }, + { + "enabled": false, + "id": "r2", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "R double", + "next_run": 0, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "script": "ok.sh", + "tz": "" + }, + { + "enabled": false, + "id": "disparu", + "kind": "script", + "last_dur_ms": 0, + "last_error": "script introuvable : parti.sh", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S disparu", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "script": "parti.sh", + "tz": "" + }, + { + "enabled": false, + "id": "remonte", + "kind": "script", + "last_dur_ms": 0, + "last_error": "nom de script invalide (pas de chemin absolu ni de ../) : ../x.sh", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S remonte", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "script": "../x.sh", + "tz": "" + }, + { + "enabled": false, + "id": "vide", + "kind": "script", + "last_dur_ms": 0, + "last_error": "aucun script associé à la tâche", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S vide", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "tz": "" + } + ] + }, + { + "name": "liste finale", + "method": "GET", + "path": "/api/tasks", + "status": 200, + "out": { + "agent": true, + "mem_on": false, + "ok": true, + "paused": false, + "presets": [], + "projects": [ + { + "active": true, + "convs": 1, + "created": "{{T}}", + "name": "Générale", + "pages": 0, + "slug": "generale", + "trackers": 0 + } + ], + "running_id": "", + "scripts": [ + { + "name": "echec.sh", + "size": 31 + }, + { + "name": "lent.sh", + "size": 19 + }, + { + "name": "ok.sh", + "size": 23 + }, + { + "name": "sous/rang.sh", + "size": 22 + } + ], + "scripts_dir": "{{HOME}}/scripts", + "tasks": [ + { + "enabled": true, + "id": "{{ID10}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": true, + "last_report": "exit: 0 · {{DUR}}\n\nstdout:\nbonjour\n", + "last_run": 1774747860000, + "name": "A script", + "next_run": 1774751460000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "ok.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID1}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "B renommée", + "next_run": 1774711800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Autre.", + "schedule": "@every 3h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID2}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "C une heure et demie", + "next_run": 1774708200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@EVERY 1.5h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID3}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "D chaque soir", + "next_run": 1774735200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 1d@23:00", + "tz": "Europe/Paris" + }, + { + "enabled": false, + "id": "{{ID4}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "E tous les trois jours", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 3d@9:30", + "tz": "America/New_York" + }, + { + "enabled": false, + "id": "{{ID5}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "F semaine", + "next_run": 1774841400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 9 * * 1-5", + "tz": "Asia/Kolkata" + }, + { + "enabled": false, + "id": "{{ID6}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "G nuit", + "next_run": 1774830600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "30 2 * * *", + "tz": "Europe/Paris" + }, + { + "enabled": false, + "id": "{{ID7}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "H quart d'heure", + "next_run": 1774700100000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "*/15 * * * *", + "tz": "Nulle/Part" + }, + { + "enabled": false, + "id": "{{ID8}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "I dimanche", + "next_run": 1774771200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 8 * * 7", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID9}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "J une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@once 2026-03-29 03:30", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID11}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "exit: 2 · {{DUR}}\n\nstderr:\noups\n", + "last_ok": false, + "last_report": "", + "last_run": 1774747860000, + "name": "K échec", + "next_run": 1774751460000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "echec.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID12}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": true, + "last_report": "(interrompue manuellement)", + "last_run": 1774738800000, + "name": "L lente", + "next_run": 1774742400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "lent.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID13}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": true, + "last_report": "exit: 0 · {{DUR}}\n\nstdout:\nbonjour\n", + "last_run": 1774747860000, + "name": "N script une fois", + "next_run": 0, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@once 2026-03-29 03:30", + "script": "ok.sh", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "manuelle", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "O manuelle", + "next_run": 1774756800000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "0 4 * * *", + "script": "ok.sh", + "tz": "UTC" + }, + { + "enabled": true, + "id": "cassee", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "P cassée", + "next_run": 0, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 10s", + "script": "ok.sh", + "tz": "" + }, + { + "enabled": false, + "id": "r1", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "R double", + "next_run": 0, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "script": "ok.sh", + "tz": "" + }, + { + "enabled": false, + "id": "r2", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "R double", + "next_run": 0, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "script": "ok.sh", + "tz": "" + }, + { + "enabled": false, + "id": "disparu", + "kind": "script", + "last_dur_ms": 0, + "last_error": "script introuvable : parti.sh", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S disparu", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "script": "parti.sh", + "tz": "" + }, + { + "enabled": false, + "id": "remonte", + "kind": "script", + "last_dur_ms": 0, + "last_error": "nom de script invalide (pas de chemin absolu ni de ../) : ../x.sh", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S remonte", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "script": "../x.sh", + "tz": "" + }, + { + "enabled": false, + "id": "vide", + "kind": "script", + "last_dur_ms": 0, + "last_error": "aucun script associé à la tâche", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S vide", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "tz": "" + } + ], + "web_on": true + }, + "tasks": [ + { + "enabled": true, + "id": "{{ID10}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": true, + "last_report": "exit: 0 · {{DUR}}\n\nstdout:\nbonjour\n", + "last_run": 1774747860000, + "name": "A script", + "next_run": 1774751460000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "ok.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID1}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "B renommée", + "next_run": 1774711800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Autre.", + "schedule": "@every 3h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID2}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "C une heure et demie", + "next_run": 1774708200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@EVERY 1.5h", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID3}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "D chaque soir", + "next_run": 1774735200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 1d@23:00", + "tz": "Europe/Paris" + }, + { + "enabled": false, + "id": "{{ID4}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "E tous les trois jours", + "next_run": 1774704600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@every 3d@9:30", + "tz": "America/New_York" + }, + { + "enabled": false, + "id": "{{ID5}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "F semaine", + "next_run": 1774841400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 9 * * 1-5", + "tz": "Asia/Kolkata" + }, + { + "enabled": false, + "id": "{{ID6}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "G nuit", + "next_run": 1774830600000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "30 2 * * *", + "tz": "Europe/Paris" + }, + { + "enabled": false, + "id": "{{ID7}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "H quart d'heure", + "next_run": 1774700100000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "*/15 * * * *", + "tz": "Nulle/Part" + }, + { + "enabled": false, + "id": "{{ID8}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "I dimanche", + "next_run": 1774771200000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "0 8 * * 7", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID9}}", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "J une fois", + "next_run": 1774747800000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "Fais le point.", + "schedule": "@once 2026-03-29 03:30", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "{{ID11}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "exit: 2 · {{DUR}}\n\nstderr:\noups\n", + "last_ok": false, + "last_report": "", + "last_run": 1774747860000, + "name": "K échec", + "next_run": 1774751460000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "echec.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID12}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": true, + "last_report": "(interrompue manuellement)", + "last_run": 1774738800000, + "name": "L lente", + "next_run": 1774742400000, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@every 1h", + "script": "lent.sh", + "tz": "" + }, + { + "enabled": false, + "id": "{{ID13}}", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": true, + "last_report": "exit: 0 · {{DUR}}\n\nstdout:\nbonjour\n", + "last_run": 1774747860000, + "name": "N script une fois", + "next_run": 0, + "no_mem": false, + "no_web": false, + "preset": "", + "project": "generale", + "prompt": "", + "schedule": "@once 2026-03-29 03:30", + "script": "ok.sh", + "tz": "Europe/Paris" + }, + { + "enabled": true, + "id": "manuelle", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "O manuelle", + "next_run": 1774756800000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "0 4 * * *", + "script": "ok.sh", + "tz": "UTC" + }, + { + "enabled": true, + "id": "cassee", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "P cassée", + "next_run": 0, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 10s", + "script": "ok.sh", + "tz": "" + }, + { + "enabled": false, + "id": "r1", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "R double", + "next_run": 0, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "script": "ok.sh", + "tz": "" + }, + { + "enabled": false, + "id": "r2", + "kind": "script", + "last_dur_ms": 0, + "last_error": "", + "last_ok": false, + "last_report": "", + "last_run": 0, + "name": "R double", + "next_run": 0, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "script": "ok.sh", + "tz": "" + }, + { + "enabled": false, + "id": "disparu", + "kind": "script", + "last_dur_ms": 0, + "last_error": "script introuvable : parti.sh", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S disparu", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "script": "parti.sh", + "tz": "" + }, + { + "enabled": false, + "id": "remonte", + "kind": "script", + "last_dur_ms": 0, + "last_error": "nom de script invalide (pas de chemin absolu ni de ../) : ../x.sh", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S remonte", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "script": "../x.sh", + "tz": "" + }, + { + "enabled": false, + "id": "vide", + "kind": "script", + "last_dur_ms": 0, + "last_error": "aucun script associé à la tâche", + "last_ok": false, + "last_report": "", + "last_run": 1774738860000, + "name": "S vide", + "next_run": 1774742460000, + "no_mem": false, + "no_web": false, + "preset": "", + "prompt": "", + "schedule": "@every 1h", + "tz": "" + } + ] + } + ] +} diff --git a/rust/loki-rs/tests/proxy.rs b/rust/loki-rs/tests/proxy.rs index 2b0a866..c9989ed 100644 --- a/rust/loki-rs/tests/proxy.rs +++ b/rust/loki-rs/tests/proxy.rs @@ -115,6 +115,7 @@ async fn facade(backend: SocketAddr) -> (SocketAddr, oneshot::Sender<()>) { backends: None, engineimg: None, strata: None, + tasks: None, }; tokio::spawn(loki_rs::proxy::serve(l, cfg, async move { let _ = rx.await; diff --git a/rust/loki-rs/tests/tasks.rs b/rust/loki-rs/tests/tasks.rs new file mode 100644 index 0000000..ee2e54e --- /dev/null +++ b/rust/loki-rs/tests/tasks.rs @@ -0,0 +1,497 @@ +//! Lot 26 : tâches planifiées tenues par la façade (`LOKI_TASKS=rust`). +//! +//! Le journal tiré du Go (`internal/loki/parity_tasks_test.go` → +//! `tests/parity_tasks/tasks.json`) est rejoué par une façade dont l'heure +//! des tâches est fixée pas à pas et dont les tics du planificateur sont +//! déclenchés à la demande. Le faux Go tient la base, le moteur occupé et la +//! discussion ouverte ; une tâche IA qu'il reçoit ne tourne pas (le modèle +//! n'est jamais prêt ici, comme dans le journal). + +#![cfg(unix)] + +mod common; + +use std::collections::HashMap; +use std::net::SocketAddr; +use std::path::PathBuf; +use std::sync::Arc; +use std::sync::atomic::{AtomicI64, Ordering}; +use std::time::Duration; + +use common::fakego; +use common::parity::apply_files; +use loki_rs::store::Store; +use loki_rs::tasks::{self, Tasks}; +use regex::Regex; +use serde_json::{Value, json}; + +fn fixture() -> Value { + serde_json::from_str(include_str!("parity_tasks/tasks.json")).expect("journal") +} + +fn s(v: &Value) -> &str { + v.as_str().unwrap_or("") +} + +const TUNE_BUSY: &str = "optimisation en cours (loki tune) : le moteur est réservé aux essais — attends la fin ou annule-la"; + +/// L'heure des tâches du rejeu (ns Unix). +static NOW: AtomicI64 = AtomicI64::new(0); + +fn clock() -> i64 { + NOW.load(Ordering::SeqCst) +} + +fn rfc3339_ns(v: &str) -> i64 { + let d = &v[..10]; + let t = &v[11..19]; + let p: Vec = d.split('-').map(|x| x.parse().unwrap()).collect(); + let h: Vec = t.split(':').map(|x| x.parse().unwrap()).collect(); + let utc = loki_rs::tz::Location::utc(); + utc.date(p[0], p[1], p[2], h[0], h[1], h[2]) * 1_000_000_000 +} + +struct Bench { + facade: SocketAddr, + go: Arc, + _stop: tokio::sync::oneshot::Sender<()>, + home: PathBuf, + tasks: Arc, + store: Store, + ids: HashMap, + order: Vec, + _tmp: tempfile::TempDir, +} + +async fn bench(clock: fn() -> i64) -> Bench { + let tmp = tempfile::tempdir().unwrap(); + let home = tmp.path().to_path_buf(); + let tasks = Arc::new(Tasks::with(home.clone(), Duration::from_secs(3600), clock)); + let (facade, go, stop, _) = fakego::start_with( + home.clone(), + fakego::Opts { + tasks: Some(tasks.clone()), + ..Default::default() + }, + ) + .await; + // Pas de modèle prêt : le port du moteur ne répond pas. + let port = std::net::TcpListener::bind("127.0.0.1:0") + .unwrap() + .local_addr() + .unwrap() + .port(); + go.set("config", "PORT", &port.to_string()); + let ws = home.join("workspace").join("actif"); + std::fs::create_dir_all(&ws).unwrap(); + *go.conv_workspace.lock().unwrap() = Some(json!({ + "dir": ws, "root": home.join("workspace"), "id": "actif", "title": "" + })); + *go.conv_ensure.lock().unwrap() = true; + let backend = go.backend.lock().unwrap().unwrap(); + let store = Store::new(backend, fakego::JETON.to_string()); + // Le premier passage du planificateur. + for _ in 0..500 { + if tasks.ticks() > 0 { + break; + } + tokio::time::sleep(Duration::from_millis(5)).await; + } + Bench { + facade, + go, + _stop: stop, + home, + tasks, + store, + ids: HashMap::new(), + order: Vec::new(), + _tmp: tmp, + } +} + +impl Bench { + /// Les identifiants tirés au hasard deviennent {{ID1}}, {{ID2}}… dans + /// l'ordre où ils apparaissent. + fn mask(&mut self, v: &str) -> String { + let id = Regex::new(r"\b[0-9a-f]{12}\b").unwrap(); + let dur = Regex::new(r#"exit: (-?[0-9]+) · [^\n"\\]*"#).unwrap(); + let created = Regex::new(r#""created":[0-9]+"#).unwrap(); + let mut out = String::new(); + let mut last = 0; + for m in id.find_iter(v) { + out.push_str(&v[last..m.start()]); + let k = m.as_str().to_string(); + if !self.ids.contains_key(&k) { + self.order.push(k.clone()); + self.ids + .insert(k.clone(), format!("{{{{ID{}}}}}", self.order.len())); + } + out.push_str(&self.ids[&k]); + last = m.end(); + } + out.push_str(&v[last..]); + let out = out.replace(&self.home.to_string_lossy().into_owned(), "{{HOME}}"); + let out = created.replace_all(&out, r#""created":"{{T}}""#); + dur.replace_all(&out, "exit: $1 · {{DUR}}").into_owned() + } + + fn unmask(&self, v: &str) -> String { + let mut v = v.to_string(); + // Les plus longs d'abord : {{ID10}} avant {{ID1}}. + let mut ids: Vec<_> = self.ids.iter().collect(); + ids.sort_by_key(|(_, m)| std::cmp::Reverse(m.len())); + for (id, m) in ids { + v = v.replace(m, id); + } + v.replace("{{HOME}}", &self.home.to_string_lossy()) + } + + async fn call(&mut self, method: &str, path: &str, body: &str) -> (u16, Value) { + let (code, b) = fakego::send(self.facade, method, path, &[], body).await; + let raw = String::from_utf8_lossy(&b).into_owned(); + let out = serde_json::from_str(&self.mask(&raw)) + .unwrap_or_else(|_| panic!("{path} : corps illisible {raw:?}")); + (code.as_u16(), out) + } + + async fn snapshot(&mut self) -> Value { + let list = tasks::list_tasks(&self.store).await; + if list.is_empty() { + return Value::Null; + } + let raw = serde_json::to_string(&list).unwrap(); + serde_json::from_str(&self.mask(&raw)).unwrap() + } + + async fn last_run(&self, id: &str) -> i64 { + tasks::get_task(&self.store, id) + .await + .map_or(0, |t| t.last_run) + } + + async fn tick(&self) { + let before = self.tasks.ticks(); + self.tasks.kick(); + for _ in 0..2000 { + if self.tasks.ticks() > before { + return; + } + tokio::time::sleep(Duration::from_millis(5)).await; + } + panic!("le tic ne finit pas"); + } +} + +#[tokio::test(flavor = "multi_thread", worker_threads = 4)] +async fn parite_des_taches() { + let fx = fixture(); + let steps = fx["steps"].as_array().unwrap(); + NOW.store(rfc3339_ns(s(&steps[0]["now"])), Ordering::SeqCst); + let mut bn = bench(clock).await; + let mut ia_runs = 0; + for st in steps { + let name = s(&st["name"]); + if let Some(n) = st["now"].as_str() { + NOW.store(rfc3339_ns(n), Ordering::SeqCst); + } + let files: serde_json::Map = st["files"] + .as_object() + .into_iter() + .flatten() + .map(|(k, v)| (k.clone(), Value::String(bn.unmask(s(v))))) + .collect(); + apply_files(&bn.home, &Value::Object(files)); + for (k, v) in st["config"].as_object().into_iter().flatten() { + bn.go.set("config", k, s(v)); + } + for (k, v) in st["state"].as_object().into_iter().flatten() { + bn.go.set("state", k, s(v)); + } + if let Some(t) = st["put_task"].as_str() { + let v: Value = serde_json::from_str(t).unwrap(); + bn.go.set("tasks", s(&v["id"]), t); + } + *bn.go.tune_busy.lock().unwrap() = + (st["tune"].as_bool() == Some(true)).then(|| TUNE_BUSY.to_string()); + let generating = st["generating"].as_bool() == Some(true); + *bn.go.generating.lock().unwrap() = generating; + if st["tick"].as_bool() == Some(true) { + bn.tick().await; + } + let before = match st["wait_done"].as_str() { + Some(id) => bn.last_run(&bn.unmask(id)).await, + None => 0, + }; + if let Some(path) = st["path"].as_str() { + let body = bn.unmask(s(&st["body"])); + let (code, out) = bn.call(s(&st["method"]), path, &body).await; + assert_eq!(code as i64, st["status"].as_i64().unwrap(), "{name}"); + assert_eq!(out, st["out"], "{name}"); + } + *bn.go.generating.lock().unwrap() = false; + *bn.go.tune_busy.lock().unwrap() = None; + if let Some(want) = st["wait_running"].as_str() { + let mut ok = false; + for _ in 0..1000 { + let (_, out) = bn.call("GET", "/api/tasks", "").await; + if out["running_id"] == json!(want) { + ok = true; + break; + } + tokio::time::sleep(Duration::from_millis(10)).await; + } + assert!(ok, "{name} : la tâche ne tourne pas"); + } + if let Some(id) = st["wait_done"].as_str() { + let id = bn.unmask(id); + let mut ok = false; + for _ in 0..2000 { + if bn.last_run(&id).await != before { + ok = true; + break; + } + tokio::time::sleep(Duration::from_millis(10)).await; + } + assert!(ok, "{name} : le passage ne finit pas"); + // Le registre des scripts est vidé juste après l'écriture. + while bn.tasks.script_running() { + tokio::time::sleep(Duration::from_millis(5)).await; + } + } + let snap = bn.snapshot().await; + assert_eq!(snap, st["tasks"], "{name} : tâches"); + // Une tâche IA due part au Go, qui ne la fait pas tourner (modèle pas + // prêt) : son état ne bouge pas. + let runs = bn.go.task_runs.lock().unwrap().clone(); + if runs.len() != ia_runs { + assert_eq!(name, "tic : une tâche IA due attend le modèle"); + assert_eq!(runs.len(), ia_runs + 1, "{name}"); + let masked = bn.mask(runs.last().unwrap()); + assert_eq!(masked, "{{ID1}}", "{name}"); + ia_runs = runs.len(); + } + } + assert_eq!(ia_runs, 1, "la tâche IA due n'est jamais partie au Go"); + // Les notifications des scripts partent au Go (la façade ne les tient + // pas) : succès, échec, puis les passages du planificateur. + let sends = bn.go.push_sends.lock().unwrap().clone(); + let titles: Vec<&str> = sends.iter().map(|(t, _)| t.as_str()).collect(); + assert_eq!( + titles.first().copied(), + Some("Loki · tâche terminée"), + "{sends:?}" + ); + assert!( + sends + .iter() + .any(|(t, b)| t == "Loki · tâche en échec" && b == "K échec"), + "{sends:?}" + ); + assert!( + !sends.iter().any(|(_, b)| b == "L lente"), + "un arrêt manuel ne notifie pas : {sends:?}" + ); +} + +/// Une tâche IA lancée à la main part au Go, qui l'exécute ; le moteur prêt +/// (preset externe), la façade ne la refuse pas. +#[tokio::test] +async fn tache_ia_executee_par_le_go() { + let mut bn = bench(tasks::now_ns).await; + bn.go.set("config", loki_rs::engstatus::EXT_KEY_FLAG, "1"); + let (code, out) = bn + .call( + "POST", + "/api/tasks/save", + r#"{"name":"IA","prompt":"Fais le point.","schedule":"@every 1h","enabled":true}"#, + ) + .await; + assert_eq!(code, 200, "{out}"); + let id = bn.unmask(s(&out["id"])); + // Le Go tient la tâche un moment : l'écran la montre en cours. + *bn.go.task_run_delay.lock().unwrap() = Some(Duration::from_millis(400)); + let (code, out) = bn + .call("POST", "/api/tasks/run", &json!({ "id": id }).to_string()) + .await; + assert_eq!((code, out), (200, json!({ "ok": true }))); + let masked = bn.mask(&id); + let mut seen = false; + for _ in 0..200 { + let (_, v) = bn.call("GET", "/api/tasks", "").await; + if v["running_id"] == json!(masked) { + seen = true; + break; + } + tokio::time::sleep(Duration::from_millis(5)).await; + } + assert!(seen, "la tâche IA n'est pas montrée en cours"); + for _ in 0..500 { + let (_, v) = bn.call("GET", "/api/tasks", "").await; + if v["running_id"] == json!("") { + break; + } + tokio::time::sleep(Duration::from_millis(5)).await; + } + let (_, v) = bn.call("GET", "/api/tasks", "").await; + assert_eq!(v["running_id"], json!("")); + assert_eq!(*bn.go.task_runs.lock().unwrap(), vec![id.clone()]); + // Le Go ne l'a pas fait tourner (occupé) : rien n'est enregistré ici. + let t = tasks::get_task(&bn.store, &id).await.unwrap(); + assert_eq!( + (t.last_run, t.last_ok, t.last_report.as_str()), + (0, false, "") + ); + // Arrêt d'une tâche IA : la génération du Go. + let (code, _) = bn + .call("POST", "/api/tasks/stop", &json!({ "id": id }).to_string()) + .await; + assert_eq!(code, 200); + assert_eq!(*bn.go.lease_log.lock().unwrap(), vec!["stop".to_string()]); + let (code, _) = bn + .call("POST", "/api/tasks/run", r#"{"id":"absente"}"#) + .await; + assert_eq!(code, 404); +} + +/// Sans demande, le planificateur tourne à son pas. +#[tokio::test] +async fn tics_au_pas_du_planificateur() { + let tmp = tempfile::tempdir().unwrap(); + let t = Arc::new(Tasks::with( + tmp.path().to_path_buf(), + Duration::from_millis(20), + tasks::now_ns, + )); + let (_facade, _go, _stop, _) = fakego::start_with( + tmp.path().to_path_buf(), + fakego::Opts { + tasks: Some(t.clone()), + ..Default::default() + }, + ) + .await; + for _ in 0..500 { + if t.ticks() >= 4 { + return; + } + tokio::time::sleep(Duration::from_millis(5)).await; + } + panic!("le planificateur ne tourne pas : {} tics", t.ticks()); +} + +/// Sans la façade pour les tâches, leurs routes partent au Go. +#[tokio::test] +async fn sans_la_facade() { + let tmp = tempfile::tempdir().unwrap(); + let (facade, go, _stop) = fakego::start(tmp.path().to_path_buf()).await; + let (c, b) = fakego::send(facade, "GET", "/api/tasks", &[], "").await; + assert_eq!(c.as_u16(), 200); + assert_eq!(String::from_utf8_lossy(&b), "go:/api/tasks"); + assert!( + go.relayed + .lock() + .unwrap() + .contains(&"/api/tasks".to_string()) + ); +} + +/// Une horloge qui avance d'une seconde et demie à chaque lecture. +static TICKING: AtomicI64 = AtomicI64::new(1_774_738_800_000_000_000); + +fn ticking() -> i64 { + TICKING.fetch_add(1_500_000_000, Ordering::SeqCst) +} + +/// La durée d'un passage : de son début à sa fin, à l'horloge des tâches. +#[tokio::test] +async fn duree_du_passage() { + let mut bn = bench(ticking).await; + apply_files( + &bn.home, + &json!({ "scripts/ok.sh": "#!/bin/sh\necho bonjour\n" }), + ); + let (code, out) = bn + .call( + "POST", + "/api/tasks/save", + r#"{"name":"S","kind":"script","script":"ok.sh","schedule":"@every 1h","enabled":true}"#, + ) + .await; + assert_eq!(code, 200, "{out}"); + let id = bn.unmask(s(&out["id"])); + let (code, _) = bn + .call("POST", "/api/tasks/run", &json!({ "id": id }).to_string()) + .await; + assert_eq!(code, 200); + let mut t = None; + for _ in 0..1000 { + t = tasks::get_task(&bn.store, &id) + .await + .filter(|t| t.last_run > 0); + if t.is_some() { + break; + } + tokio::time::sleep(Duration::from_millis(10)).await; + } + let t = t.expect("le passage ne finit pas"); + assert!(t.last_ok, "{t:?}"); + assert!( + t.last_dur_ms > 0 && t.last_dur_ms % 1500 == 0, + "durée : {}", + t.last_dur_ms + ); +} + +/// Au démarrage, le prochain passage des tâches actives qui n'en ont pas +/// (`primeNextRuns`) ; les autres ne bougent pas. +#[tokio::test] +async fn amorce_au_demarrage() { + let tmp = tempfile::tempdir().unwrap(); + let t = Arc::new(Tasks::with( + tmp.path().to_path_buf(), + Duration::from_secs(3600), + tasks::now_ns, + )); + let task = |id: &str, enabled: bool, next: i64| { + ( + "tasks".to_string(), + id.to_string(), + json!({ "id": id, "name": id, "kind": "script", "script": "ok.sh", + "schedule": "@every 1h", "enabled": enabled, "next_run": next }) + .to_string(), + ) + }; + let (_facade, go, _stop, _) = fakego::start_with( + tmp.path().to_path_buf(), + fakego::Opts { + tasks: Some(t.clone()), + seed: vec![ + task("active", true, 0), + task("eteinte", false, 0), + task("posee", true, 42), + ], + ..Default::default() + }, + ) + .await; + for _ in 0..500 { + if t.ticks() > 0 { + break; + } + tokio::time::sleep(Duration::from_millis(5)).await; + } + assert!(t.ticks() > 0, "pas de premier passage"); + let next = |id: &str| -> i64 { + let v: Value = serde_json::from_str(&go.get("tasks", id).unwrap()).unwrap(); + v["next_run"].as_i64().unwrap() + }; + let now_ms = tasks::now_ns() / 1_000_000; + let a = next("active"); + assert!( + (now_ms + 3_590_000..=now_ms + 3_600_000).contains(&a), + "active : {a}" + ); + assert_eq!(next("eteinte"), 0); + assert_eq!(next("posee"), 42); +}