arclith-cli 0.19.0__tar.gz → 0.21.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. {arclith_cli-0.19.0 → arclith_cli-0.21.0}/PKG-INFO +41 -10
  2. {arclith_cli-0.19.0 → arclith_cli-0.21.0}/README.md +39 -8
  3. arclith_cli-0.21.0/arclith_cli/__init__.py +1 -0
  4. {arclith_cli-0.19.0 → arclith_cli-0.21.0}/arclith_cli/adapter_templates.py +2 -1
  5. {arclith_cli-0.19.0 → arclith_cli-0.21.0}/arclith_cli/capabilities.py +5 -0
  6. {arclith_cli-0.19.0 → arclith_cli-0.21.0}/arclith_cli/capability_models.py +41 -0
  7. arclith_cli-0.21.0/arclith_cli/catalogs/channels.py +197 -0
  8. {arclith_cli-0.19.0 → arclith_cli-0.21.0}/arclith_cli/catalogs/persistence.py +14 -79
  9. arclith_cli-0.21.0/arclith_cli/catalogs/repository_facets.py +94 -0
  10. arclith_cli-0.21.0/arclith_cli/catalogs/repository_postgresql.py +107 -0
  11. {arclith_cli-0.19.0 → arclith_cli-0.21.0}/arclith_cli/core_scaffold.py +155 -40
  12. {arclith_cli-0.19.0 → arclith_cli-0.21.0}/arclith_cli/init_project.py +1 -1
  13. {arclith_cli-0.19.0 → arclith_cli-0.21.0}/arclith_cli/main.py +41 -3
  14. {arclith_cli-0.19.0 → arclith_cli-0.21.0}/arclith_cli/project_paths.py +18 -3
  15. {arclith_cli-0.19.0 → arclith_cli-0.21.0}/arclith_cli/recipe.py +14 -1
  16. arclith_cli-0.21.0/arclith_cli/scaffold_interactive.py +105 -0
  17. arclith_cli-0.21.0/arclith_cli/scaffold_templates.py +140 -0
  18. {arclith_cli-0.19.0 → arclith_cli-0.21.0}/pyproject.toml +2 -2
  19. {arclith_cli-0.19.0 → arclith_cli-0.21.0}/tests/e2e_manual.sh +1 -1
  20. arclith_cli-0.21.0/tests/test_adapter_templates.py +20 -0
  21. {arclith_cli-0.19.0 → arclith_cli-0.21.0}/tests/test_add_adapter.py +172 -7
  22. {arclith_cli-0.19.0 → arclith_cli-0.21.0}/tests/test_capabilities.py +176 -0
  23. arclith_cli-0.21.0/tests/test_core_scaffold.py +566 -0
  24. {arclith_cli-0.19.0 → arclith_cli-0.21.0}/tests/test_e2e_scaffold.py +3 -1
  25. {arclith_cli-0.19.0 → arclith_cli-0.21.0}/tests/test_embedding_capability.py +3 -3
  26. {arclith_cli-0.19.0 → arclith_cli-0.21.0}/tests/test_project_paths.py +1 -0
  27. {arclith_cli-0.19.0 → arclith_cli-0.21.0}/tests/test_recipe.py +2 -2
  28. arclith_cli-0.21.0/tests/test_scaffold_cli.py +216 -0
  29. {arclith_cli-0.19.0 → arclith_cli-0.21.0}/tests/test_vector_store_capability.py +1 -1
  30. {arclith_cli-0.19.0 → arclith_cli-0.21.0}/uv.lock +4 -3
  31. arclith_cli-0.19.0/arclith_cli/__init__.py +0 -1
  32. arclith_cli-0.19.0/tests/test_core_scaffold.py +0 -248
  33. {arclith_cli-0.19.0 → arclith_cli-0.21.0}/.gitignore +0 -0
  34. {arclith_cli-0.19.0 → arclith_cli-0.21.0}/LICENSE +0 -0
  35. {arclith_cli-0.19.0 → arclith_cli-0.21.0}/Makefile +0 -0
  36. {arclith_cli-0.19.0 → arclith_cli-0.21.0}/arclith_cli/adapter_config.py +0 -0
  37. {arclith_cli-0.19.0 → arclith_cli-0.21.0}/arclith_cli/adapter_generator.py +0 -0
  38. {arclith_cli-0.19.0 → arclith_cli-0.21.0}/arclith_cli/adapter_parameters.py +0 -0
  39. {arclith_cli-0.19.0 → arclith_cli-0.21.0}/arclith_cli/adapter_rendering.py +0 -0
  40. {arclith_cli-0.19.0 → arclith_cli-0.21.0}/arclith_cli/adapter_selection.py +0 -0
  41. {arclith_cli-0.19.0 → arclith_cli-0.21.0}/arclith_cli/add_adapter.py +0 -0
  42. {arclith_cli-0.19.0 → arclith_cli-0.21.0}/arclith_cli/catalogs/__init__.py +0 -0
  43. {arclith_cli-0.19.0 → arclith_cli-0.21.0}/arclith_cli/catalogs/ai.py +0 -0
  44. {arclith_cli-0.19.0 → arclith_cli-0.21.0}/arclith_cli/catalogs/core.py +0 -0
  45. {arclith_cli-0.19.0 → arclith_cli-0.21.0}/arclith_cli/catalogs/observability.py +0 -0
  46. {arclith_cli-0.19.0 → arclith_cli-0.21.0}/arclith_cli/catalogs/security.py +0 -0
  47. {arclith_cli-0.19.0 → arclith_cli-0.21.0}/arclith_cli/catalogs/transports.py +0 -0
  48. {arclith_cli-0.19.0 → arclith_cli-0.21.0}/arclith_cli/entity_scanner.py +0 -0
  49. {arclith_cli-0.19.0 → arclith_cli-0.21.0}/arclith_cli/export_config.py +0 -0
  50. {arclith_cli-0.19.0 → arclith_cli-0.21.0}/arclith_cli/new_project.py +0 -0
  51. {arclith_cli-0.19.0 → arclith_cli-0.21.0}/arclith_cli/recipe_cli.py +0 -0
  52. {arclith_cli-0.19.0 → arclith_cli-0.21.0}/arclith_cli/recipe_models.py +0 -0
  53. {arclith_cli-0.19.0 → arclith_cli-0.21.0}/arclith_cli/rename.py +0 -0
  54. {arclith_cli-0.19.0 → arclith_cli-0.21.0}/arclith_cli/runtime_templates.py +0 -0
  55. {arclith_cli-0.19.0 → arclith_cli-0.21.0}/arclith_cli/scaffold.py +0 -0
  56. {arclith_cli-0.19.0 → arclith_cli-0.21.0}/arclith_cli/updater.py +0 -0
  57. {arclith_cli-0.19.0 → arclith_cli-0.21.0}/tests/__init__.py +0 -0
  58. {arclith_cli-0.19.0 → arclith_cli-0.21.0}/tests/test_rename.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: arclith-cli
3
- Version: 0.19.0
3
+ Version: 0.21.0
4
4
  Summary: CLI scaffolding tool for arclith — hexagonal architecture framework
5
5
  Author-email: Killian KOPP <killiankopp@gmail.com>
6
6
  License: Apache License
@@ -186,7 +186,7 @@ License: Apache License
186
186
  License-File: LICENSE
187
187
  Keywords: arclith,cli,ddd,hexagonal-architecture,scaffold
188
188
  Requires-Python: >=3.13
189
- Requires-Dist: arclith>=0.22.0
189
+ Requires-Dist: arclith>=0.24.0
190
190
  Requires-Dist: httpx>=0.27.0
191
191
  Requires-Dist: rich>=13.0.0
192
192
  Requires-Dist: typer>=0.15.0
@@ -252,7 +252,10 @@ template est régénéré côté CLI pour appliquer le contrat `runtime/docker-i
252
252
 
253
253
  ### `add-entity` — Ajouter une entité métier
254
254
 
255
- Crée uniquement le fichier minimal d'une entité dans `src/<package>/domain/models/`.
255
+ Crée un squelette minimal et guidé dans `src/<package>/domain/models/`. Le fichier
256
+ signale où déclarer les champs et invariants, rappelle les champs déjà fournis par
257
+ `Entity` et renvoie vers les guides Arclith et Pydantic. L'exemple `Field(...)`
258
+ reste commenté, donc aucun import inutilisé n'est ajouté.
256
259
 
257
260
  ```bash
258
261
  cd my-recipe-service
@@ -265,21 +268,41 @@ Fichier généré :
265
268
  src/<package>/domain/models/shopping_item.py
266
269
  ```
267
270
 
268
- La commande ne génère aucun CRUD, aucun port repository, aucun adapter et aucun endpoint. Elle pose seulement le point d'ancrage du modèle métier ; le développeur complète ensuite les champs et invariants de l'entité.
271
+ La commande ne génère aucun CRUD, aucun port repository, aucun adapter et aucun
272
+ endpoint. Elle pose seulement le point d'ancrage du modèle métier ; le
273
+ développeur complète ensuite les champs et invariants de l'entité.
269
274
 
270
275
  ---
271
276
 
272
277
  ### `add-usecase` — Ajouter un cas d'usage
273
278
 
274
- Crée le port inbound minimal dans `src/<package>/domain/ports/inbound/`, puis le fichier minimal du
275
- cas d'usage dans `src/<package>/application/use_cases/`.
279
+ Crée un port inbound guidé dans `src/<package>/domain/ports/inbound/`, puis le
280
+ cas d'usage dans `src/<package>/application/use_cases/`. Sans option de liaison,
281
+ la commande interactive propose les entités détectées par analyse AST, la
282
+ création d'une nouvelle entité ou un cas d'usage transverse.
276
283
 
277
284
  ```bash
278
285
  cd my-recipe-service
286
+
287
+ # Mode interactif : choisir une entité détectée, en créer une ou rester transverse
279
288
  arclith-cli add-usecase PlanShoppingList
280
- arclith-cli add-usecase find-by-name
289
+
290
+ # Modes directs, complets pour les agents et la CI
291
+ arclith-cli add-usecase CreateTodo --entity Todo
292
+ arclith-cli add-usecase CreateRecipe --new-entity Recipe
293
+ arclith-cli add-usecase RunMaintenance --no-entity
281
294
  ```
282
295
 
296
+ | Option | Effet |
297
+ |---|---|
298
+ | `--entity Todo` | lie le use case à une entité détectée et échoue si elle est absente |
299
+ | `--new-entity Todo` | crée l'entité si nécessaire, puis génère le use case lié |
300
+ | `--no-entity` | génère un `Command`, un `Result` et un use case transverse sans repository |
301
+
302
+ Ces options sont mutuellement exclusives. `--new-entity` réutilise une entité
303
+ valide déjà présente, mais refuse un fichier homonyme qui ne déclare pas la
304
+ classe `Entity` attendue.
305
+
283
306
  Fichier généré :
284
307
 
285
308
  ```text
@@ -287,9 +310,17 @@ src/<package>/domain/ports/inbound/plan_shopping_list.py
287
310
  src/<package>/application/use_cases/plan_shopping_list.py
288
311
  ```
289
312
 
290
- Le nom peut être fourni en PascalCase, snake_case ou kebab-case. Le suffixe `UseCase` est normalisé : `PlanShoppingListUseCase` et `plan-shopping-list-use-case` génèrent tous les deux `PlanShoppingListUseCase`.
291
-
292
- Comme `add-entity`, cette commande ne câble pas FastAPI, FastMCP, LangGraph, un repository ou un service. Les adapters se branchent ensuite explicitement avec `add-adapter` et devraient dépendre du port inbound généré.
313
+ Le nom peut être fourni en PascalCase, snake_case ou kebab-case. Le suffixe
314
+ `UseCase` est normalisé : `PlanShoppingListUseCase` et
315
+ `plan-shopping-list-use-case` génèrent tous les deux
316
+ `PlanShoppingListUseCase`.
317
+
318
+ Pour une entité principale, le squelette injecte explicitement
319
+ `Repository[Entity]` et type `execute` avec un `Command` Pydantic et l'entité en
320
+ retour. Le mode transverse génère plutôt un `Command` et un `Result` Pydantic,
321
+ sans repository implicite. Aucun mode ne câble FastAPI, FastMCP ou LangGraph.
322
+ Les exemples complets et les règles de séparation sont dans le
323
+ [deep dive du scaffold CLI](https://karned-rekipe.github.io/arclith/deep-dives/cli-scaffold/).
293
324
 
294
325
  ---
295
326
 
@@ -58,7 +58,10 @@ template est régénéré côté CLI pour appliquer le contrat `runtime/docker-i
58
58
 
59
59
  ### `add-entity` — Ajouter une entité métier
60
60
 
61
- Crée uniquement le fichier minimal d'une entité dans `src/<package>/domain/models/`.
61
+ Crée un squelette minimal et guidé dans `src/<package>/domain/models/`. Le fichier
62
+ signale où déclarer les champs et invariants, rappelle les champs déjà fournis par
63
+ `Entity` et renvoie vers les guides Arclith et Pydantic. L'exemple `Field(...)`
64
+ reste commenté, donc aucun import inutilisé n'est ajouté.
62
65
 
63
66
  ```bash
64
67
  cd my-recipe-service
@@ -71,21 +74,41 @@ Fichier généré :
71
74
  src/<package>/domain/models/shopping_item.py
72
75
  ```
73
76
 
74
- La commande ne génère aucun CRUD, aucun port repository, aucun adapter et aucun endpoint. Elle pose seulement le point d'ancrage du modèle métier ; le développeur complète ensuite les champs et invariants de l'entité.
77
+ La commande ne génère aucun CRUD, aucun port repository, aucun adapter et aucun
78
+ endpoint. Elle pose seulement le point d'ancrage du modèle métier ; le
79
+ développeur complète ensuite les champs et invariants de l'entité.
75
80
 
76
81
  ---
77
82
 
78
83
  ### `add-usecase` — Ajouter un cas d'usage
79
84
 
80
- Crée le port inbound minimal dans `src/<package>/domain/ports/inbound/`, puis le fichier minimal du
81
- cas d'usage dans `src/<package>/application/use_cases/`.
85
+ Crée un port inbound guidé dans `src/<package>/domain/ports/inbound/`, puis le
86
+ cas d'usage dans `src/<package>/application/use_cases/`. Sans option de liaison,
87
+ la commande interactive propose les entités détectées par analyse AST, la
88
+ création d'une nouvelle entité ou un cas d'usage transverse.
82
89
 
83
90
  ```bash
84
91
  cd my-recipe-service
92
+
93
+ # Mode interactif : choisir une entité détectée, en créer une ou rester transverse
85
94
  arclith-cli add-usecase PlanShoppingList
86
- arclith-cli add-usecase find-by-name
95
+
96
+ # Modes directs, complets pour les agents et la CI
97
+ arclith-cli add-usecase CreateTodo --entity Todo
98
+ arclith-cli add-usecase CreateRecipe --new-entity Recipe
99
+ arclith-cli add-usecase RunMaintenance --no-entity
87
100
  ```
88
101
 
102
+ | Option | Effet |
103
+ |---|---|
104
+ | `--entity Todo` | lie le use case à une entité détectée et échoue si elle est absente |
105
+ | `--new-entity Todo` | crée l'entité si nécessaire, puis génère le use case lié |
106
+ | `--no-entity` | génère un `Command`, un `Result` et un use case transverse sans repository |
107
+
108
+ Ces options sont mutuellement exclusives. `--new-entity` réutilise une entité
109
+ valide déjà présente, mais refuse un fichier homonyme qui ne déclare pas la
110
+ classe `Entity` attendue.
111
+
89
112
  Fichier généré :
90
113
 
91
114
  ```text
@@ -93,9 +116,17 @@ src/<package>/domain/ports/inbound/plan_shopping_list.py
93
116
  src/<package>/application/use_cases/plan_shopping_list.py
94
117
  ```
95
118
 
96
- Le nom peut être fourni en PascalCase, snake_case ou kebab-case. Le suffixe `UseCase` est normalisé : `PlanShoppingListUseCase` et `plan-shopping-list-use-case` génèrent tous les deux `PlanShoppingListUseCase`.
97
-
98
- Comme `add-entity`, cette commande ne câble pas FastAPI, FastMCP, LangGraph, un repository ou un service. Les adapters se branchent ensuite explicitement avec `add-adapter` et devraient dépendre du port inbound généré.
119
+ Le nom peut être fourni en PascalCase, snake_case ou kebab-case. Le suffixe
120
+ `UseCase` est normalisé : `PlanShoppingListUseCase` et
121
+ `plan-shopping-list-use-case` génèrent tous les deux
122
+ `PlanShoppingListUseCase`.
123
+
124
+ Pour une entité principale, le squelette injecte explicitement
125
+ `Repository[Entity]` et type `execute` avec un `Command` Pydantic et l'entité en
126
+ retour. Le mode transverse génère plutôt un `Command` et un `Result` Pydantic,
127
+ sans repository implicite. Aucun mode ne câble FastAPI, FastMCP ou LangGraph.
128
+ Les exemples complets et les règles de séparation sont dans le
129
+ [deep dive du scaffold CLI](https://karned-rekipe.github.io/arclith/deep-dives/cli-scaffold/).
99
130
 
100
131
  ---
101
132
 
@@ -0,0 +1 @@
1
+ __version__ = "0.21.0"
@@ -205,7 +205,8 @@ _repository_registry: RepositoryRegistry[{pascal}, {pascal}Repository] = (
205
205
 
206
206
 
207
207
  def build_{snake}_service(arclith: Arclith) -> tuple[{pascal}Service, Logger]:
208
- arclith.logger.info("🗄️ Repository adapter selected", adapter=arclith.config.adapters.repository)
208
+ adapter = arclith.config.adapters.repository_adapter_for({pascal})
209
+ arclith.logger.info("🗄️ Repository adapter selected", adapter=adapter)
209
210
  repo: {pascal}Repository = arclith.repository({pascal}, registry=_repository_registry)
210
211
  return {pascal}Service(repo, arclith.logger, arclith.config.soft_delete.retention_days), arclith.logger
211
212
  """
@@ -3,6 +3,7 @@ from __future__ import annotations
3
3
  from typing import Any
4
4
 
5
5
  from arclith_cli.capability_models import (
6
+ AdapterFacets,
6
7
  AdapterProfileSpec,
7
8
  AdapterSpec,
8
9
  CapabilitySpec,
@@ -18,6 +19,7 @@ from arclith_cli.catalogs.ai import (
18
19
  EMBEDDING_CAPABILITY,
19
20
  LLM_CAPABILITY,
20
21
  )
22
+ from arclith_cli.catalogs.channels import CHANNEL_CAPABILITY
21
23
  from arclith_cli.catalogs.core import (
22
24
  CACHE_CAPABILITY,
23
25
  LOGGER_CAPABILITY,
@@ -48,9 +50,11 @@ __all__ = [
48
50
  "AGENT_PERSISTENCE_CAPABILITY",
49
51
  "API_CAPABILITY",
50
52
  "AUTH_CAPABILITY",
53
+ "AdapterFacets",
51
54
  "AdapterProfileSpec",
52
55
  "AdapterSpec",
53
56
  "CACHE_CAPABILITY",
57
+ "CHANNEL_CAPABILITY",
54
58
  "CAPABILITY_CATALOG",
55
59
  "COMMAND_BUS_CAPABILITY",
56
60
  "CapabilitySpec",
@@ -91,6 +95,7 @@ CAPABILITY_CATALOG = (
91
95
  PROBE_CAPABILITY,
92
96
  HTTP_CAPABILITY,
93
97
  COMMAND_BUS_CAPABILITY,
98
+ CHANNEL_CAPABILITY,
94
99
  RUNTIME_CAPABILITY,
95
100
  AUTH_CAPABILITY,
96
101
  TENANT_CAPABILITY,
@@ -5,6 +5,16 @@ from typing import Any, Literal
5
5
 
6
6
  ParameterKind = Literal["string", "boolean"]
7
7
  LayerKind = Literal["inbound", "outbound", "bidirectional", "runtime"]
8
+ StorageModel = Literal[
9
+ "memory",
10
+ "document",
11
+ "relational_json",
12
+ "embedded_analytics",
13
+ "relational_structured",
14
+ ]
15
+ RuntimeKind = Literal["in_process", "file", "server"]
16
+ TransactionSupport = Literal["none", "limited", "strong"]
17
+ SchemaStrategy = Literal["flexible", "json_table", "structured_tables"]
8
18
 
9
19
 
10
20
  @dataclass(frozen=True)
@@ -70,6 +80,30 @@ class AdapterProfileSpec:
70
80
  return {"name": self.name, "parameters": dict(self.parameters)}
71
81
 
72
82
 
83
+ @dataclass(frozen=True)
84
+ class AdapterFacets:
85
+ storage_model: StorageModel
86
+ runtime: RuntimeKind
87
+ production_ready: bool
88
+ multi_process: bool
89
+ transactions: TransactionSupport
90
+ schema_strategy: SchemaStrategy
91
+ recommended_for: tuple[str, ...]
92
+ limits: tuple[str, ...]
93
+
94
+ def to_dict(self) -> dict[str, Any]:
95
+ return {
96
+ "storage_model": self.storage_model,
97
+ "runtime": self.runtime,
98
+ "production_ready": self.production_ready,
99
+ "multi_process": self.multi_process,
100
+ "transactions": self.transactions,
101
+ "schema_strategy": self.schema_strategy,
102
+ "recommended_for": list(self.recommended_for),
103
+ "limits": list(self.limits),
104
+ }
105
+
106
+
73
107
  @dataclass(frozen=True)
74
108
  class AdapterSpec:
75
109
  name: str
@@ -90,6 +124,7 @@ class AdapterSpec:
90
124
  profiles: tuple[AdapterProfileSpec, ...] = ()
91
125
  dependency_extra: str | None = None
92
126
  entity_scoped: bool = True
127
+ facets: AdapterFacets | None = None
93
128
 
94
129
  def has_config(self) -> bool:
95
130
  return self.config_path is not None and bool(self.config_template)
@@ -137,6 +172,7 @@ class AdapterSpec:
137
172
  "profiles": [profile.to_dict() for profile in self.profiles],
138
173
  "dependency_extra": self.dependency_extra,
139
174
  "entity_scoped": self.entity_scoped,
175
+ "facets": self.facets.to_dict() if self.facets is not None else None,
140
176
  }
141
177
 
142
178
 
@@ -169,6 +205,7 @@ class CapabilitySpec:
169
205
 
170
206
 
171
207
  __all__ = [
208
+ "AdapterFacets",
172
209
  "AdapterProfileSpec",
173
210
  "AdapterSpec",
174
211
  "CapabilitySpec",
@@ -176,5 +213,9 @@ __all__ = [
176
213
  "LayerKind",
177
214
  "ParameterKind",
178
215
  "ParameterSpec",
216
+ "RuntimeKind",
217
+ "SchemaStrategy",
179
218
  "SecretMappingSpec",
219
+ "StorageModel",
220
+ "TransactionSupport",
180
221
  ]
@@ -0,0 +1,197 @@
1
+ from arclith_cli.capability_models import (
2
+ AdapterSpec,
3
+ CapabilitySpec,
4
+ ParameterSpec,
5
+ SecretMappingSpec,
6
+ )
7
+
8
+ CHANNEL_CAPABILITY = CapabilitySpec(
9
+ name="channel",
10
+ layer="bidirectional",
11
+ description="Messages conversationnels entrants et sortants normalisés.",
12
+ activation_config_key=None,
13
+ adapters=(
14
+ AdapterSpec(
15
+ name="memory",
16
+ capability="channel",
17
+ layer="bidirectional",
18
+ description="Fake déterministe en mémoire pour tests et prototypes locaux.",
19
+ config_path="config/adapters/bidirectional/memory.yaml",
20
+ config_template="""\
21
+ enabled: true
22
+ """,
23
+ entity_scoped=False,
24
+ ),
25
+ AdapterSpec(
26
+ name="webhook",
27
+ capability="channel",
28
+ layer="bidirectional",
29
+ description="Webhook HTTP générique signé, idempotent et provider-neutral.",
30
+ config_path="config/adapters/bidirectional/webhook.yaml",
31
+ config_template="""\
32
+ enabled: true
33
+ path: "{path}"
34
+ secret: null
35
+ signature_header: "{signature_header}"
36
+ timestamp_header: "{timestamp_header}"
37
+ signature_tolerance_seconds: {signature_tolerance_seconds}
38
+ idempotency_header: "{idempotency_header}"
39
+ event_ttl_seconds: {event_ttl_seconds}
40
+ max_payload_bytes: {max_payload_bytes}
41
+ metadata_allowlist: []
42
+ response_mode: "{response_mode}"
43
+ callback_url: {callback_url}
44
+ callback_allowed_host: {callback_allowed_host}
45
+ callback_timeout_seconds: {callback_timeout_seconds}
46
+ """,
47
+ secret_mappings=(
48
+ SecretMappingSpec(
49
+ field_path="adapters.channel.webhook.secret",
50
+ secret_key="ARCLITH_WEBHOOK_SECRET",
51
+ ),
52
+ ),
53
+ parameters=(
54
+ ParameterSpec(
55
+ name="path",
56
+ kind="string",
57
+ prompt="Route HTTP statique du webhook",
58
+ default="/channels/webhook",
59
+ ),
60
+ ParameterSpec(
61
+ name="signature_header",
62
+ kind="string",
63
+ prompt="Header de signature HMAC",
64
+ default="X-Arclith-Signature",
65
+ ),
66
+ ParameterSpec(
67
+ name="timestamp_header",
68
+ kind="string",
69
+ prompt="Header du timestamp signé",
70
+ default="X-Arclith-Timestamp",
71
+ ),
72
+ ParameterSpec(
73
+ name="signature_tolerance_seconds",
74
+ kind="string",
75
+ prompt="Tolérance de fraîcheur HMAC en secondes",
76
+ default="300",
77
+ ),
78
+ ParameterSpec(
79
+ name="idempotency_header",
80
+ kind="string",
81
+ prompt="Header de l'identifiant stable de l'événement",
82
+ default="X-Arclith-Event-Id",
83
+ ),
84
+ ParameterSpec(
85
+ name="event_ttl_seconds",
86
+ kind="string",
87
+ prompt="Durée de déduplication en secondes",
88
+ default="86400",
89
+ ),
90
+ ParameterSpec(
91
+ name="max_payload_bytes",
92
+ kind="string",
93
+ prompt="Taille maximale du payload en octets",
94
+ default="1048576",
95
+ ),
96
+ ParameterSpec(
97
+ name="response_mode",
98
+ kind="string",
99
+ prompt="Mode de réponse HTTP",
100
+ default="sync",
101
+ choices=("sync", "accepted", "callback"),
102
+ ),
103
+ ParameterSpec(
104
+ name="callback_url",
105
+ kind="string",
106
+ prompt="URL HTTPS du callback côté serveur (null hors mode callback)",
107
+ default="null",
108
+ ),
109
+ ParameterSpec(
110
+ name="callback_allowed_host",
111
+ kind="string",
112
+ prompt="Hostname exact autorisé pour le callback (null hors mode callback)",
113
+ default="null",
114
+ ),
115
+ ParameterSpec(
116
+ name="callback_timeout_seconds",
117
+ kind="string",
118
+ prompt="Timeout du callback sortant en secondes",
119
+ default="5.0",
120
+ ),
121
+ ),
122
+ dependency_extra="channel",
123
+ entity_scoped=False,
124
+ ),
125
+ AdapterSpec(
126
+ name="slack",
127
+ capability="channel",
128
+ layer="bidirectional",
129
+ description="Slack Events API signé avec réponses chat.postMessage.",
130
+ config_path="config/adapters/bidirectional/slack.yaml",
131
+ config_template="""\
132
+ enabled: true
133
+ path: "{path}"
134
+ signing_secret: null
135
+ bot_token: null
136
+ workspace_id: {workspace_id}
137
+ allowed_channel_ids: []
138
+ signature_tolerance_seconds: {signature_tolerance_seconds}
139
+ event_ttl_seconds: {event_ttl_seconds}
140
+ max_payload_bytes: {max_payload_bytes}
141
+ request_timeout_seconds: {request_timeout_seconds}
142
+ """,
143
+ secret_mappings=(
144
+ SecretMappingSpec(
145
+ field_path="adapters.channel.slack.signing_secret",
146
+ secret_key="ARCLITH_SLACK_SIGNING_SECRET",
147
+ ),
148
+ SecretMappingSpec(
149
+ field_path="adapters.channel.slack.bot_token",
150
+ secret_key="ARCLITH_SLACK_BOT_TOKEN",
151
+ ),
152
+ ),
153
+ parameters=(
154
+ ParameterSpec(
155
+ name="path",
156
+ kind="string",
157
+ prompt="Route HTTP statique des événements Slack",
158
+ default="/channels/slack/events",
159
+ ),
160
+ ParameterSpec(
161
+ name="workspace_id",
162
+ kind="string",
163
+ prompt="Workspace Slack exact autorisé (null pour tout accepter)",
164
+ default="null",
165
+ ),
166
+ ParameterSpec(
167
+ name="signature_tolerance_seconds",
168
+ kind="string",
169
+ prompt="Tolérance de fraîcheur de signature en secondes",
170
+ default="300",
171
+ ),
172
+ ParameterSpec(
173
+ name="event_ttl_seconds",
174
+ kind="string",
175
+ prompt="Durée de déduplication des event_id en secondes",
176
+ default="86400",
177
+ ),
178
+ ParameterSpec(
179
+ name="max_payload_bytes",
180
+ kind="string",
181
+ prompt="Taille maximale du payload en octets",
182
+ default="1048576",
183
+ ),
184
+ ParameterSpec(
185
+ name="request_timeout_seconds",
186
+ kind="string",
187
+ prompt="Timeout de chat.postMessage en secondes",
188
+ default="5.0",
189
+ ),
190
+ ),
191
+ dependency_extra="channel",
192
+ entity_scoped=False,
193
+ ),
194
+ ),
195
+ )
196
+
197
+ __all__ = ["CHANNEL_CAPABILITY"]
@@ -6,6 +6,15 @@ from arclith_cli.capability_models import (
6
6
  ParameterSpec,
7
7
  SecretMappingSpec,
8
8
  )
9
+ from arclith_cli.catalogs.repository_facets import (
10
+ DUCKDB_FACETS,
11
+ MARIADB_FACETS,
12
+ MEMORY_FACETS,
13
+ MONGODB_FACETS,
14
+ )
15
+ from arclith_cli.catalogs.repository_postgresql import (
16
+ POSTGRESQL_REPOSITORY_ADAPTER,
17
+ )
9
18
 
10
19
  REPOSITORY_CAPABILITY = CapabilitySpec(
11
20
  name="repository",
@@ -18,6 +27,7 @@ REPOSITORY_CAPABILITY = CapabilitySpec(
18
27
  capability="repository",
19
28
  layer="outbound",
20
29
  description="Stockage volatile en mémoire pour dev, tests et smoke locaux.",
30
+ facets=MEMORY_FACETS,
21
31
  ),
22
32
  AdapterSpec(
23
33
  name="mongodb",
@@ -57,6 +67,7 @@ multitenant: {multitenant} # true = uri/db_name résolus par requête via JWT
57
67
  default=False,
58
68
  ),
59
69
  ),
70
+ facets=MONGODB_FACETS,
60
71
  ),
61
72
  AdapterSpec(
62
73
  name="duckdb",
@@ -76,6 +87,7 @@ path: {path}
76
87
  default="data/",
77
88
  ),
78
89
  ),
90
+ facets=DUCKDB_FACETS,
79
91
  ),
80
92
  AdapterSpec(
81
93
  name="mariadb",
@@ -142,86 +154,9 @@ multitenant: false
142
154
  default="",
143
155
  ),
144
156
  ),
157
+ facets=MARIADB_FACETS,
145
158
  ),
146
- AdapterSpec(
147
- name="postgresql",
148
- capability="repository",
149
- layer="outbound",
150
- description="Repository PostgreSQL async optionnel, pilote par SQLAlchemy et asyncpg, avec payload JSONB generique.",
151
- config_path="config/adapters/outbound/postgresql.yaml",
152
- config_template="""\
153
- url: null # à mapper via config/secrets.yaml ou resolver env/vault si vous fournissez une URL complète
154
- host: {host}
155
- port: {port}
156
- database: {database}
157
- user: {user}
158
- password: null # à mapper via config/secrets.yaml ou resolver env/vault
159
- schema: {schema}
160
- driver: {driver}
161
- table_prefix: "{table_prefix}"
162
- multitenant: {multitenant}
163
- """,
164
- secret_mappings=(
165
- SecretMappingSpec(
166
- field_path="adapters.postgresql.url",
167
- secret_key="POSTGRESQL_URL",
168
- ),
169
- SecretMappingSpec(
170
- field_path="adapters.postgresql.password",
171
- secret_key="POSTGRESQL_PASSWORD",
172
- ),
173
- ),
174
- parameters=(
175
- ParameterSpec(
176
- name="host",
177
- kind="string",
178
- prompt="host",
179
- default="127.0.0.1",
180
- ),
181
- ParameterSpec(
182
- name="port",
183
- kind="string",
184
- prompt="port",
185
- default="5432",
186
- ),
187
- ParameterSpec(
188
- name="database",
189
- kind="string",
190
- prompt="database",
191
- default_from_project_name=True,
192
- ),
193
- ParameterSpec(
194
- name="user",
195
- kind="string",
196
- prompt="user",
197
- default="app",
198
- ),
199
- ParameterSpec(
200
- name="schema",
201
- kind="string",
202
- prompt="schema",
203
- default="public",
204
- ),
205
- ParameterSpec(
206
- name="driver",
207
- kind="string",
208
- prompt="driver",
209
- default="asyncpg",
210
- ),
211
- ParameterSpec(
212
- name="table_prefix",
213
- kind="string",
214
- prompt="table_prefix",
215
- default="",
216
- ),
217
- ParameterSpec(
218
- name="multitenant",
219
- kind="boolean",
220
- prompt="multitenant",
221
- default=False,
222
- ),
223
- ),
224
- ),
159
+ POSTGRESQL_REPOSITORY_ADAPTER,
225
160
  ),
226
161
  )
227
162