diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml index 69e4574..8a4d5df 100644 --- a/.gitea/workflows/release.yml +++ b/.gitea/workflows/release.yml @@ -1,28 +1,30 @@ # Gitea Actions : pipeline de release (issue #42). # -# Sur chaque tag v* : construit les binaires de toutes les plateformes, -# archive + publie la release Gitea avec les artefacts. Les installateurs -# (install.sh / install.ps1) et 'am self-update' consomment ces artefacts. +# Sur chaque tag v* : construit les binaires de toutes les plateformes. +# Le job principal 'release' cross-compile Linux + Windows depuis un seul +# runner ubuntu-latest, donc il n'a pas besoin de runners Windows/macOS. +# Le job 'macos' est optionnel : s'il y a un runner macos-latest, il ajoute +# les archives macOS a la release existante ; sinon l'installateur retombe +# sur cargo install --git sur macOS. # -# Labels de runners attendus : ubuntu-latest, windows-latest, macos-latest. -# Un runner manquant laisse son job en attente ; le job linux couvre la -# majorite des cas (les artefacts windows/macos peuvent aussi etre produits -# par les scripts locaux documentes dans RELEASING.md). +# Les installateurs one-liner (install.sh / install.ps1) et 'am self-update' +# consomment les artefacts attaches a la release. # -# Prérequis du runner : pouvoir tirer les images docker.io (rust:1.94-alpine, -# ~300 Mo) et le secret GITEA_TOKEN (jeton avec droit 'write:release') -# positionné sur le dépôt. La release existante est écrasée -# (release_overwrite). +# Cross-compilation utilisee : +# - Linux x86_64 + aarch64 : cargo-zigbuild + musl (binaires statiques) +# - Windows x86_64 : mingw-w64 + x86_64-pc-windows-gnu +# +# Prerequis du runner : pouvoir tirer l'image docker.io (rust:1.94-bookworm, +# ~1,2 Go) et le secret GITEA_TOKEN (jeton avec droit 'write:release') +# positionne sur le depot. # # Note disque (runner avec espace limite) : -# - il faut ~1 Go libres sur / pour tirer l'image (rust:1.94-alpine) ; +# - il faut ~2 Go libres sur / pour tirer l'image (rust:1.94-bookworm) ; # en cas de 'no space left on device' : docker system prune -af --volumes # sur l'HOTE du runner (pas une autre machine !) ; # - les builds compilent dans CARGO_TARGET_DIR=/tmp (hors du volume # workspace) avec l'incremental desactive, donc rien ne persiste ; -# - le job linux utilise cargo-zigbuild : zig (~50 Mo) fournit le linker -# croise pour les DEUX cibles musl statiques dans une seule image legere -# (rust:1.94-alpine n'embarque pas de compilateur croise aarch64). +# - zig (~50 Mo) fournit le linker croise pour Linux. name: release on: @@ -35,41 +37,56 @@ concurrency: cancel-in-progress: true jobs: - linux: + release: runs-on: ubuntu-latest - container: rust:1.94-alpine + container: rust:1.94-bookworm env: CARGO_TARGET_DIR: /tmp/am-target CARGO_INCREMENTAL: "0" steps: # actions/checkout (et son post-step) s'execute via node, absent de - # l'image rust:alpine : sans nodejs le job echoue des le checkout. + # l'image rust:bookworm par defaut : sans nodejs le job echoue des le checkout. - name: Prepare container (node for checkout action) - run: apk add --no-cache nodejs + run: apt-get update && apt-get install -y nodejs - uses: actions/checkout@v4 - - name: Install build tools (zig + cargo-zigbuild) + - name: Install build tools (zig + cargo-zigbuild + mingw) run: | - apk add --no-cache build-base zip curl - rustup target add x86_64-unknown-linux-musl aarch64-unknown-linux-musl + apt-get update + apt-get install -y build-essential mingw-w64 zip curl + rustup target add \ + x86_64-unknown-linux-musl \ + aarch64-unknown-linux-musl \ + x86_64-pc-windows-gnu curl -fsSL https://ziglang.org/download/0.14.1/zig-x86_64-linux-0.14.1.tar.xz -o /tmp/zig.tar.xz tar -xf /tmp/zig.tar.xz -C /opt echo "/opt/zig-x86_64-linux-0.14.1" >> "$GITHUB_PATH" cargo install cargo-zigbuild --locked + # Contourne un bug de casse dans certaines crates Windows + # (ex. windows-sys peut emettre -lKernel32 au lieu de -lkernel32). + # Le linker MinGW sous Linux est sensible a la casse. + ln -sf /usr/x86_64-w64-mingw32/lib/libkernel32.a \ + /usr/x86_64-w64-mingw32/lib/libKernel32.a - name: Build linux x86_64 + aarch64 (static musl, zig) run: cargo zigbuild --release --locked --target x86_64-unknown-linux-musl --target aarch64-unknown-linux-musl + - name: Build windows x86_64 (mingw) + run: cargo build --release --locked --target x86_64-pc-windows-gnu + - name: Stage archives run: | mkdir -p dist cp "$CARGO_TARGET_DIR/x86_64-unknown-linux-musl/release/am" dist/am-linux-x86_64 cp "$CARGO_TARGET_DIR/aarch64-unknown-linux-musl/release/am" dist/am-linux-aarch64 + cp "$CARGO_TARGET_DIR/x86_64-pc-windows-gnu/release/am.exe" dist/am-windows-x86_64.exe cd dist tar -czf am-linux-x86_64.tar.gz am-linux-x86_64 tar -czf am-linux-aarch64.tar.gz am-linux-aarch64 - sha256sum am-linux-*.tar.gz > checksums.txt + zip -q am-windows-x86_64.zip am-windows-x86_64.exe + rm am-windows-x86_64.exe + sha256sum am-linux-*.tar.gz am-windows-*.zip > checksums.txt - name: Publish release uses: https://gitea.com/actions/gitea-release-action@v1 @@ -80,37 +97,16 @@ jobs: files: | dist/am-linux-x86_64.tar.gz dist/am-linux-aarch64.tar.gz - dist/checksums.txt - GITHUB_TOKEN: ${{ secrets.GITEA_TOKEN }} - release_overwrite: true - - windows: - runs-on: windows-latest - env: - CARGO_TARGET_DIR: C:/am-target - CARGO_INCREMENTAL: "0" - steps: - - uses: actions/checkout@v4 - - - name: Build windows x86_64 - run: cargo build --release --locked - - - name: Stage archive - run: | - New-Item -ItemType Directory -Force dist | Out-Null - Compress-Archive -Path C:/am-target/release/am.exe -DestinationPath dist/am-windows-x86_64.zip -Force - (Get-FileHash dist/am-windows-x86_64.zip -Algorithm SHA256).Hash.ToLower() | Out-File dist/checksums.txt - - - name: Publish release - uses: https://gitea.com/actions/gitea-release-action@v1 - with: - files: | dist/am-windows-x86_64.zip dist/checksums.txt GITHUB_TOKEN: ${{ secrets.GITEA_TOKEN }} release_overwrite: true macos: + # Optionnel : ajoute les archives macOS si un runner macos-latest est + # disponible. Sinon ce job reste en attente, mais le job release a deja + # publie Linux + Windows. + needs: release runs-on: macos-latest env: CARGO_TARGET_DIR: /tmp/am-target @@ -132,7 +128,7 @@ jobs: cd dist tar -czf am-macos-x86_64.tar.gz am-macos-x86_64 tar -czf am-macos-aarch64.tar.gz am-macos-aarch64 - shasum -a 256 am-*.tar.gz > checksums.txt + shasum -a 256 am-macos-*.tar.gz > checksums-macos.txt - name: Publish release uses: https://gitea.com/actions/gitea-release-action@v1 @@ -140,6 +136,8 @@ jobs: files: | dist/am-macos-x86_64.tar.gz dist/am-macos-aarch64.tar.gz - dist/checksums.txt + dist/checksums-macos.txt GITHUB_TOKEN: ${{ secrets.GITEA_TOKEN }} - release_overwrite: true + # false = ajoute les assets macOS a la release creee par le job + # principal, sans ecraser Linux + Windows. + release_overwrite: false diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 7e62b34..91e8200 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -131,6 +131,7 @@ src/ ├── app.rs 🧰 Contexte App (config, state, paths, logger, theme) ├── config.rs ⚙️ Schéma YAML, chargement, fusion, validation ├── catalog.rs 🔍 Index agents + recherche fuzzy + suggestions +├── catalog_remote.rs 🌐 Catalogues distants (fetch, cache, includes) ├── state.rs 💾 Base JSON des installations et annotations ├── events.rs 📝 Journal d'événements JSONL ├── runner.rs 🏃 Trait d'exécution système / mock @@ -149,15 +150,33 @@ src/ ├── tables.rs 📋 Rendu tabulaire ├── theme.rs 🎨 Thèmes de couleur (REPL et tableaux) ├── web.rs 🌐 Serveur HTTP local (127.0.0.1) + API JSON + frontend embarqué +├── serve.rs 🚀 API HTTP + WebSocket authentifiée (pilotage à distance, issue #80) ├── frontend/ 📄 index.html — dashboard web (HTML5 + CSS + JS vanilla, include_str!) ├── history.rs ⏪ Historique des commandes ├── sessions.rs 📅 Registre des sessions ├── projects.rs 🗂️️ Agrégation par projet ├── hooks.rs 🪝 Hooks de cycle de vie ├── secrets.rs 🔒 Gestion des secrets (keyring OS) +├── providers.rs 🏷️ Registre de providers LLM (base_url, modèles, défaut, @secret) +├── registry.rs 📦 Registre communautaire de catalogues (manifeste + sha256) +├── ask.rs 💬 Langage naturel → commandes am (règles locales + LLM optionnel) +├── sandbox.rs 🛡️ Profils sandbox par agent (allowlist, périmètre, réseau) +├── telemetry.rs 📈 Télémétrie anonyme opt-in (compteurs agrégés) +├── lab.rs 🧪 Benchmark d'agents (tâches YAML versionnables) +├── playbook.rs ▶️ Rejeu pas à pas d'une séquence d'historique +├── plugins.rs 🔌 Scripts d'extension sur les événements (contrat JSON) +├── models.rs 🤖 Inventaire des modèles locaux (ollama, llama.cpp, LM Studio) +├── sync.rs 🔄 Push git de l'état (journal, sessions, config) +├── agent_config.rs ⚙️ Adaptateurs de configuration post-install (TOML/YAML/JSON/key=value) +├── costs.rs 💰 Coûts estimés par agent (tokens in/out, modèles de prix) +├── automation.rs ⚙️ Services système + tâches planifiées (systemd/launchd/schtasks) +├── backup.rs 💾 Sauvegardes (update --rollback, migrate) +├── doctor.rs 🩺 Diagnostics environnement + --fix +├── nav.rs 🧭 Tables de navigation (ls/dir) +├── i18n.rs 🌍 Catalogue de traductions FR/EN (--lang, AM_LANG, LANG) ├── version.rs 🏷️ Informations de version -├── commands/ 📦 35 modules, un par commande -└── installers/ 📦 9 installateurs spécialisés +├── commands/ 📦 51 modules, un par commande +└── installers/ 📦 8 installateurs spécialisés ``` --- @@ -435,6 +454,25 @@ flowchart TB | `am version` | Version et build | | `am` (sans commande) | REPL interactif | +### 🤖 Copilote & plateforme (v0.7.0 / v1.0) + +| Commande | Description | +|----------|-------------| +| `am ask ""` | Langage naturel → commande(s) am : règles locales FR/EN hors-ligne, raffinement LLM optionnel (`settings.ask`), confirmation avant exécution | +| `am providers list/add/remove/set-token` | Registre LLM centralisé (base_url, modèles, clé par provider au keyring, résolution `@secret`) | +| `am registry publish/search/install` | Registre communautaire de catalogues (Gitea, manifeste + checksum sha256 vérifié) | +| `am serve --token [--port]` | API HTTP + WebSocket authentifiée : stats, run, start, stop, ask — rate limiting par IP, TLS derrière reverse proxy | +| `am web [--port]` | Dashboard web local en lecture seule (127.0.0.1), contrats `--json` réutilisés | +| `am lab --agents a,b --task ` | Benchmark comparatif (durée, exit, coût) sur tâches YAML versionnables | +| `am sync [--message]` | Sauvegarde git de l'état (journal, sessions, config — secrets exclus) | +| `am migrate export/import` | Bundle de transfert machine A → B (config + état + historique) | +| `am schedule add/list/remove/run` | Planification de commandes am (cron / Task Scheduler, issue #56) | +| `am service install ` | Service système (systemd / launchd / tâche Windows, autostart) | +| `am monitor [--json]` | TUI temps réel des processus gérés (CPU/RSS/uptime) + alertes de seuils | +| `am models [--prune]` | Inventaire des modèles locaux (ollama, llama.cpp, LM Studio) | +| `am audit` | Qui a modifié quoi, quand (checksums config + journal) | +| `am plugins [--test ]` | Scripts d'extension sur les événements (contrat JSON stdin/stdout) | + --- ## 🌐 Options globales diff --git a/Cargo.lock b/Cargo.lock index 5a5084b..28043d5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -21,7 +21,7 @@ dependencies = [ [[package]] name = "agent-manager" -version = "1.0.1" +version = "1.0.2" dependencies = [ "anyhow", "base64", diff --git a/Cargo.toml b/Cargo.toml index 88067d8..c1f7a8f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "agent-manager" -version = "1.0.1" +version = "1.0.2" edition = "2021" description = "Manage local AI coding agents: list, install, start, stop, update — with automatic dependency handling and a YAML-driven catalog." license = "MIT" diff --git a/README.md b/README.md index 3951f88..33c1285 100644 --- a/README.md +++ b/README.md @@ -78,11 +78,14 @@ plateforme, il compile automatiquement depuis les sources. | am list --all | tout le catalogue, avec l'état de chacun | | am search | recherche fuzzy : tolère les fautes de frappe, classe par pertinence, propose « vouliez-vous dire » | | am suggest | recommande un agent pour une demande en langage naturel (tags + usage réel) | +| am ask "" | langage naturel → commande(s) am : règles locales hors-ligne, raffinement LLM optionnel (settings.ask), confirmation avant exécution — ex: am ask "installe claude et lance-le" | | am info | fiche détaillée (installation, dépendances, site…) | | am status [agent] | état, version, PID, logs | | am models | inventaire des modèles locaux (ollama, llama.cpp, LM Studio) | | am models --prune | purge des modèles inutilisés (--dry-run : simulation) | +| am providers list / add / set-token | registre LLM centralisé : base_url, modèles par provider, clé dans le trousseau (jamais en clair, résolue via @secret) | | am catalog update / add | catalogue distant : rafraîchit l'officiel ou ajoute un catalogue d'équipe | +| am registry publish/search/install | registre communautaire de catalogues (Gitea) : publication, recherche, installation avec checksum sha256 vérifié et décision de confiance explicite | | am audit | qui a modifié quoi, quand (checksums config + journal) | | am sessions --export | export d'une session (métadonnées + commandes + extrait de log) | | am sessions --retention | purge des sessions terminées au-delà de N jours | @@ -207,6 +210,7 @@ Les variables d'environnement `AGENT_MANAGER_DATA`, `AGENT_MANAGER_STATE` et `AG | am config show / edit / add / set | gère votre configuration (hooks on_start/on_stop/… dans settings.hooks) | | am open | ouvre le répertoire d'installation dans l'explorateur | | am completion [--installed] | script de complétion (bash, zsh, fish, powershell, elvish) ; --installed complète dynamiquement les agents installés, alias et groupes | +| am serve --token [--port

] | API HTTP + WebSocket authentifiée pour piloter am à distance (stats, run, start, stop, ask…) — rate limiting par IP, TLS via reverse proxy | | am self-update | met à jour am lui-même (si configuré) | | am self-uninstall | désinstalle am et tout ce qu'il a créé (confirmation) | | am | shell interactif : bannière, passerelle système, Tab et historique | diff --git a/ROADMAP.md b/ROADMAP.md index 6cbe165..05b098f 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -1,6 +1,7 @@ # 🗺️ ROADMAP agent-manager — vers le « super outil » -> **Version du document : 2.2** · Statut : propositions, non engagées +> **Version du document : 2.3** · Statut : phases 0 à 3 livrées +> (v0.3.0 → v1.0.1), maintenance v1.0.2 > Base : analyse du code **v0.2.7** (Rust, 45+ tests, zéro dépendance runtime) > > ✅ **Phase 0 livrée en v0.3.0 (2026-08-17)** : issues #2 à #16 clôturées, @@ -18,8 +19,18 @@ > (#47–#75), 250+ tests. am web, i18n, services, schedule, lab, sync, > migrate, models, plugins d'événements… Binaires publiés sur Gitea. > -> 🔨 **Phase v0.7.0 en cours (priorité P0)** : providers & configuration -> automatisée (épique #87) — livrée AVANT la Phase 3 (v1.0), voir section 7 bis. +> ✅ **Phase v0.7.0 livrée (2026-08-19)** : 5/5 issues clôturées (#88–#92) — +> registre de providers LLM (am providers), secrets partagés par provider, +> flags --provider/--model/--no-config à l'install, configuration +> post-install (bloc config:). +> +> ✅ **Phase 3 livrée en v1.0.0 (2026-08-20)** : 5/5 issues clôturées +> (#76–#80) — télémétrie opt-in, am registry, am ask, profils sandbox, +> am serve (API HTTP + WebSocket). v1.0.1 : self-update décompresse les +> archives, prompt REPL avec shell actif. +> +> 🔧 **Maintenance v1.0.2 (2026-08-20)** : commandes REPL ask/serve/registry +> routées vers am (au lieu du shell système) ; docs alignées. --- @@ -509,5 +520,6 @@ configuration manuelle (tokens au keyring, provider/modèle par défaut). --- *Document généré à partir de l'analyse du code (v0.2.7, commit d886de3). -La phase 0 est livrée (v0.3.0) ; les découpages des phases 1 (v0.4.0), -2 (v0.5.0) et 3 (v1.0) sont en place sur Gitea (issues #25 à #80).* +Phases livrées : 0 (v0.3.0), 1 (v0.4.1), 2 (v0.6.0), v0.7.0 (providers, +2026-08-19), 3 (v1.0.0, #76–#80, 2026-08-20) ; maintenance v1.0.1/v1.0.2 +(self-update décompression, REPL).* diff --git a/man/am.1 b/man/am.1 index 91168f9..51518da 100644 --- a/man/am.1 +++ b/man/am.1 @@ -1,6 +1,6 @@ .ie \n(.g .ds Aq \(aq .el .ds Aq ' -.TH am 1 "am 1.0.0" +.TH am 1 "am 1.0.2" .SH NAME am \- agent\-manager (am) — manage local AI coding agents .SH SYNOPSIS @@ -244,4 +244,4 @@ Export the configuration and installation state (backup) am\-import(1) Import a previously exported configuration and state .SH VERSION -v1.0.0 +v1.0.2 diff --git a/scripts/build-release.ps1 b/scripts/build-release.ps1 index 06fa0ed..22b83ae 100644 --- a/scripts/build-release.ps1 +++ b/scripts/build-release.ps1 @@ -12,9 +12,18 @@ $zip = "dist/am-windows-x86_64.zip" Remove-Item $zip -ErrorAction SilentlyContinue Compress-Archive -Path target/release/am.exe -DestinationPath $zip +function Get-Sha256Net([string]$path) { + $stream = [System.IO.File]::OpenRead($path) + try { + $sha = [System.Security.Cryptography.SHA256]::Create() + $bytes = $sha.ComputeHash($stream) + return ([BitConverter]::ToString($bytes) -replace '-', '').ToLower() + } finally { $stream.Close() } +} + Write-Host "" Write-Host "Artefacts:" -Get-ChildItem dist | ForEach-Object { - $hash = (Get-FileHash $_.FullName -Algorithm SHA256).Hash.ToLower() +Get-ChildItem dist -File | ForEach-Object { + $hash = Get-Sha256Net $_.FullName Write-Host (" {0,-32} {1} sha256:{2}" -f $_.Name, $_.Length, $hash) } diff --git a/src/commands/tip_cmd.rs b/src/commands/tip_cmd.rs index 85f05e4..62c2d76 100644 --- a/src/commands/tip_cmd.rs +++ b/src/commands/tip_cmd.rs @@ -103,6 +103,12 @@ pub static SECTIONS: &[TipSection] = &[ options: &[("--json", "sortie machine-readable")], example: "suggest un agent pour du Python", }, + TipEntry { + usage: "ask ", + about: "langage naturel → commande(s) am : règles locales hors-ligne, raffinement LLM optionnel (issue #78)", + options: &[("--yes", "exécute la commande sans confirmation")], + example: "ask installe claude et lance-le", + }, TipEntry { usage: "models", about: "inventaire des modèles locaux (ollama, llama.cpp, LM Studio)", @@ -119,6 +125,15 @@ pub static SECTIONS: &[TipSection] = &[ options: &[("add ", "ajoute un catalogue d'équipe par URL")], example: "catalog add https://git.dracodev.net/team/agents.yaml", }, + TipEntry { + usage: "registry publish --source ", + about: "registre communautaire : publie, cherche ou installe un catalogue (manifeste + checksum sha256 vérifié)", + options: &[ + ("search ", "cherche dans le registre"), + ("install ", "installe après vérification du checksum et décision de confiance"), + ], + example: "registry search agents python", + }, TipEntry { usage: "audit", about: "qui a modifié quoi, quand (checksums config + journal d'événements)", @@ -251,6 +266,16 @@ pub static SECTIONS: &[TipSection] = &[ ], example: "web --port 9090", }, + TipEntry { + usage: "serve --token ", + about: "API HTTP + WebSocket authentifiée pour piloter am à distance (stats, run, start, stop, ask) (issue #80)", + options: &[ + ("--port

", "port d'écoute (défaut 8080)"), + ("--host ", "adresse d'écoute (défaut 127.0.0.1)"), + ("--rate-limit ", "requêtes par IP et par minute (défaut 120)"), + ], + example: "serve --token mon-token --port 9000", + }, TipEntry { usage: "sync", about: "pousse l'état (state.json, journal, historique) dans le dépôt git configuré (sync_repo)", diff --git a/src/repl.rs b/src/repl.rs index adaa62b..c9e02e6 100644 --- a/src/repl.rs +++ b/src/repl.rs @@ -113,6 +113,7 @@ const COMMAND_DESCRIPTIONS: &[(&str, &str)] = &[ ("registry", "publish, search and install agent catalogs (Gitea)"), ("providers", "manage the LLM provider registry (base URLs, models, default)"), ("suggest", "recommend an agent for a request"), + ("ask", "natural language → am commands (local rules first, optional LLM refinement)"), ("audit", "who changed what, when (config checksums)"), ("service", "register an agent as a system service (autostart)"), ("schedule", "plan am commands (daily) and check the fleet health"), @@ -798,7 +799,7 @@ pub fn banner_box( .to_string(), )); rows.push(inner( - " models models · models --prune · catalog · suggest · audit".to_string(), + " models models · models --prune · catalog · suggest · ask · audit".to_string(), )); rows.push(inner( " automate service install · schedule add · doctor --watch · monitor · sync · migrate · playbook".to_string(), @@ -2014,13 +2015,16 @@ pub fn is_am_command(word: &str) -> bool { | "tip" | "models" | "catalog" + | "registry" | "providers" | "suggest" + | "ask" | "audit" | "service" | "schedule" | "monitor" | "web" + | "serve" | "sync" | "migrate" | "playbook" @@ -2676,7 +2680,7 @@ mod tests { for cmd in [ "list", "status", "search", "info", "install", "uninstall", "update", "start", "stop", "restart", "run", "doctor", "config", "completion", "self-update", - "self-uninstall", "export", "import", + "self-uninstall", "export", "import", "ask", "serve", "registry", ] { assert!(is_am_command(cmd), "{cmd}"); }