Le manifeste de l'application (app.json) déclare l'identité, les ressources et les exigences de votre application. Il se trouve à la racine du dépôt — ou dans un sous-répertoire si l'entrée du registre en spécifie un.
| Champ | Type | Description |
|---|---|---|
name | string | Identifiant unique, en kebab-case (p. ex. "oncall-watchtower") |
version | string | Version Semver (p. ex. "1.0.0") |
displayName | string | Nom lisible affiché dans l'App Store |
description | string | Courte description de ce que fait l'application |
| Champ | Type | Description |
|---|---|---|
author | string | Nom de l'auteur ou de l'équipe |
license | string | Identifiant de licence |
minCrewVersion | string | Version minimale requise de Gateway |
tags | string[] | Étiquettes de découverte (p. ex. ["oncall", "monitoring"]) |
jobFamilies | string[] | Familles d'emplois auxquelles cette application s'applique |
| Champ | Type | Description |
|---|---|---|
agents | string[] | Chemins vers les fichiers JSON des agents (relatifs à la racine de l'application) |
skills | string[] | Chemins vers les répertoires de Skills |
sops | string[] | Chemins vers les fichiers SOP (procédure opérationnelle normalisée) |
mcpServers | object | Définitions des serveurs MCP (même format que mcp.json) |
| Champ | Type | Description |
|---|---|---|
repo | string | Nom du dépôt Git. Utilisé par le proxy blob pour servir les images |
iconPath | string | Chemin vers l'icône de l'application (PNG, carrée, min 256×256) |
screenshots | string[] | Chemins vers les captures d'écran (PNG/JPG, max 5) |
highlights | string[] | Points forts affichés sur la page de détails (max 10) |
L'illustration héros s'affiche sur les lignes de liste Discover, la mise en vedette, les cartes de fonctionnalités et la bannière de la page de détails.
| Champ | Objectif | Taille recommandée |
|---|---|---|
heroImage | Héros en thème clair | 1200×675 (16:9) |
heroImageDark | Variante en thème sombre | 1200×675 (16:9) |
heroImageDetail / heroImageDetailDark | Bannière de la page de détails uniquement (préférée à heroImage pour cet usage) | 1200×288 (25:6) |
Ordre de résolution sur chaque surface : illustration du thème actuel → celle du thème opposé → première capture d'écran → dégradé déterministe avec l'icône de l'application. L'absence d'image héros est gérée proprement.
La forme du chemin dépend de la distribution :
/apps/{name}/ui/ui/hero-light.svg); le registre la réécrit en URL de proxy blob{ "crons": [ { "name": "ticket-refresh", "every": 300, "message": "Check for new high-severity tickets" }, { "name": "daily-digest", "cron_expr": "0 9 * * 1-5", "message": "Generate daily digest", "agent": "digest-agent" } ] }
| Champ | Description |
|---|---|
name | Identifiant de la tâche |
every | Intervalle en secondes (s'exclut mutuellement avec cron_expr) |
cron_expr | Expression cron (s'exclut mutuellement avec every) |
message | Requête envoyée à l'agent à chaque exécution |
agent | Agent à exécuter (optionnel, utilise l'agent par défaut si omis) |
enabled | Par défaut true. Doit être un booléen JSON. false enregistre la tâche cron en pause (visible, reprenable) — pour les tâches qui nécessitent d'abord une configuration par l'utilisateur |
{ "ui": { "entry": "dist/index.mjs", "pages": [ { "route": "/apps/my-app", "label": "My App", "icon": "Shield", "entryPoint": "dist/page.mjs", "mountFunction": "mount" } ], "sidebar": { "section": "Apps", "order": 10 } } }
| Champ | Par défaut | Description |
|---|---|---|
ui.entry | Chemin vers le bundle ESM (relatif à la racine de l'application) | |
ui.pages[].route | Chemin URL de la page | |
ui.pages[].label | Étiquette dans la barre latérale | |
ui.pages[].icon | Nom d'icône Lucide (p. ex. "Shield", "Package") | |
ui.pages[].iconUrl | Chemin d'image d'icône personnalisée (relatif à ui/) | |
ui.pages[].entryPoint | Bundle ESM par page (remplace ui.entry) | |
ui.pages[].mountFunction | "mount" | Nom de la fonction exportée dans le bundle ESM |
ui.sidebar.section | "Apps" | Nom de la section dans la barre latérale |
ui.sidebar.order | 10 | Ordre de tri dans la section |
{ "backend": { "entryPoint": "backend/server.py", "port": "auto", "healthCheck": "/health", "routes": "/api/apps/oncall-watchtower" } }
| Champ | Par défaut | Description |
|---|---|---|
backend.entryPoint | Script à exécuter (relatif à la racine de l'application), ou un chemin de module Python en notation pointée lancé via python -m | |
backend.port | "auto" | Numéro de port ou "auto" pour l'attribution automatique |
backend.healthCheck | "/health" | Chemin du point de terminaison de vérification de santé |
backend.routes | Chemin de route de base pour le backend | |
backend.type | "" | Runtime : "python", "asgi", "node", "exec", ou "" (détection automatique à partir de entryPoint) |
Les backends d'application sont accessibles via le proxy inverse de la gateway à /apps/{name}/api/{path}, ce qui évite les problèmes CORS pour les pages UI du tableau de bord.
Au lieu d'un processus backend autonome (ou en complément), enregistrez des points d'entrée Python qui s'exécutent à l'intérieur du processus de la gateway :
{ "backend": { "hooks": { "routes": "backend.routes:register_routes", "on_startup": "backend.hooks:on_startup", "on_shutdown": "backend.hooks:on_shutdown" } } }
| Champ | Description |
|---|---|
backend.hooks.routes | Enregistre des gestionnaires dans le RouteRegistry interne de la gateway |
backend.hooks.on_startup | Invoqué lorsque les hooks de l'application sont branchés |
backend.hooks.on_shutdown | Invoqué lorsque l'application est désactivée |
Les hooks sont branchés à l'activation de l'application (via on_app_enable, également réexécuté au démarrage de la gateway) — aucun redémarrage de la gateway n'est requis.
{ "permissions": { "api": ["/api/crons", "/api/status", "/api/agents"], "events": ["notification", "slots"], "mcpTools": ["cron_add", "cron_list"], "storage": true, "cron": true, "memory": "app-scoped", "network": false } }
| Champ | Type | Description |
|---|---|---|
permissions.api | string[] | Préfixes de chemins API autorisés (appliqué actuellement) |
permissions.events | string[] | Types d'événements WebSocket autorisés |
permissions.mcpTools | string[] | Noms d'outils MCP autorisés |
permissions.storage | boolean | Peut utiliser le stockage propre à l'application |
permissions.cron | boolean | Peut créer des tâches cron |
permissions.memory | string | Accès à la mémoire : "", "app-scoped", ou "shared" |
permissions.network | boolean | Peut effectuer des requêtes réseau externes |
{ "setup": { "onInstall": "cd ui && npm install && npm run build", "onUninstall": "echo cleanup done", "onUpdate": "cd ui && npm install && npm run build", "onEnable": "echo enabled", "onDisable": "echo disabled", "configSchema": {} } }
| Champ | Par défaut | Description |
|---|---|---|
setup.onInstall | "" | Commande shell exécutée après l'installation |
setup.onUninstall | "" | Commande shell exécutée avant la désinstallation |
setup.onUpdate | "" | Commande shell exécutée après la mise à jour |
setup.onEnable | "" | Commande shell exécutée à l'activation de l'application |
setup.onDisable | "" | Commande shell exécutée à la désactivation de l'application |
setup.onEnableTimeout | 30 | Délai d'expiration en secondes pour onEnable |
setup.onDisableTimeout | 30 | Délai d'expiration en secondes pour onDisable |
setup.configSchema | {} | Schéma JSON pour la configuration de l'application |
Les scripts s'exécutent avec set -euo pipefail imposé par Crew. Les variables non définies et les échecs de pipe entraînent une sortie immédiate — aucune erreur silencieuse.
Limites de délai d'expiration : onInstall / onUpdate = 300 s, onUninstall = 120 s, onEnable / onDisable = configurable (30 s par défaut).
Si onEnable échoue, l'activation est annulée — l'application reste désactivée et toute ressource enregistrée est désenregistrée. Les échecs de onDisable sont consignés comme avertissements mais ne bloquent pas la désactivation.
onUninstall reçoit KEEP_DATA=1 ou KEEP_DATA=0 dans l'environnement — si l'utilisateur a choisi « Conserver les données de l'application », le script doit éviter de supprimer les répertoires de données utilisateur.
{ "dependencies": { "managedBy": "gateway", "capabilities": { "mcp": [ { "id": "some-mcp-server", "source": "registry" } ], "skills": [ { "id": "some-skill", "source": "registry" } ] }, "commands": ["jq", "node", "python3"] } }
| Champ | Par défaut | Description |
|---|---|---|
dependencies.managedBy | "gateway" | Qui gère le cycle de vie des dépendances : "gateway" ou "app" |
dependencies.capabilities.mcp | [] | Dépendances requises envers des serveurs MCP |
dependencies.capabilities.skills | [] | Dépendances requises envers des Skills |
dependencies.capabilities.agents | [] | Déprécié pour managedBy: "gateway" — toujours signalé comme non résolu |
dependencies.commands | [] | Commandes système qui doivent être sur le PATH (vérifiées via which) |
Les dépendances sont suivies dans un registre à comptage de références. À la désinstallation, Crew indique quelles dépendances peuvent être supprimées en toute sécurité par rapport à celles partagées avec d'autres applications.
| Champ | Par défaut | Description |
|---|---|---|
lifecycle | "gateway" | "gateway" (géré), "app" (autogéré), ou "locked" (ne peut être désinstallé) |
resources | "gateway" | "gateway" (Crew enregistre les agents/Skills/MCP) ou "app" (l'application gère les siennes) |
{ "platform": { "os": ["macos", "linux"], "arch": [], "installMode": "server", "clientInstall": { "shell": "curl -fsSL https://example.com/install.sh | bash", "postInstall": "open ~/Applications/MyApp.app" } } }
| Champ | Par défaut | Description |
|---|---|---|
platform.os | ["macos", "linux"] | Plateformes prises en charge |
platform.arch | [] (toutes) | Architectures prises en charge |
platform.installMode | "server" | "server" ou "client" |
platform.clientInstall.shell | Commande unique pour l'installation locale | |
platform.clientInstall.postInstall | Commande à exécuter après l'installation |
Lorsque installMode: "client", l'App Store affiche des instructions de terminal à copier-coller au lieu d'exécuter l'installation sur le serveur. À utiliser pour les applications qui doivent s'exécuter sur la machine locale de l'utilisateur (p. ex. les applications Electron lorsque Crew s'exécute sur un hôte distant).
{ "openCommand": "open ~/Applications/MyApp.app" }
Pour les applications qui s'exécutent hors du tableau de bord, openCommand déclare comment les lancer. POST /api/apps/{name}/open l'exécute en arrière-plan. Dans un environnement distant sans interface, le point de terminaison retourne plutôt la commande à exécuter localement.
Appliquées au moment de l'installation :
name doit correspondre à /^[a-z0-9]+(?:-[a-z0-9]+)*$/ (kebab-case)version doit correspondre au format semver (X.Y.Z)agents, skills, sops, ui.entry, ui.pages[].entryPoint, backend.entryPoint doivent être relatifs et rester à l'intérieur de la racine de l'application (les chemins absolus et la traversée .. sont rejetés via une résolution canonique et une vérification de confinement)backend.hooks.* sont vérifiés au niveau du format (module.path:callable, aucune traversée exprimable) et vérifiés en confinement au chargementmcpServers utilisent command / args / url / env — et non des chemins de fichiers relatifs à l'application — et ne sont pas vérifiées quant au cheminevery, soit cron_exprroute et un labelLes champs inconnus dans app.json sont préservés lors de l'analyse et retransmis intégralement via to_dict() / to_json(). Les nouvelles fonctionnalités du manifeste peuvent coexister avec des versions plus anciennes de Crew sans compromettre la validation.
{ "name": "oncall-watchtower", "version": "1.0.0", "displayName": "Oncall Watchtower", "description": "Monitor tickets, pipelines, and alarms for your on-call rotation", "author": "kirocrew", "tags": ["oncall", "monitoring"], "agents": ["agents/ticket-analyst.json"], "skills": ["skills/oncall-runbook"], "crons": [ { "name": "ticket-refresh", "every": 300, "message": "Check for new high-severity tickets" } ], "ui": { "entry": "dist/index.mjs", "pages": [ { "route": "/apps/oncall-watchtower", "label": "Oncall", "icon": "Shield" } ] }, "permissions": { "api": ["/api/crons", "/api/status"], "events": ["notification"] }, "platform": { "os": ["macos", "linux"] } }
Référence du manifeste