arclith-cli 0.13.0__tar.gz → 0.15.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.
- {arclith_cli-0.13.0 → arclith_cli-0.15.0}/PKG-INFO +2 -2
- {arclith_cli-0.13.0 → arclith_cli-0.15.0}/README.md +5 -3
- arclith_cli-0.15.0/arclith_cli/__init__.py +1 -0
- {arclith_cli-0.13.0 → arclith_cli-0.15.0}/arclith_cli/adapter_templates.py +46 -4
- {arclith_cli-0.13.0 → arclith_cli-0.15.0}/arclith_cli/add_adapter.py +8 -0
- {arclith_cli-0.13.0 → arclith_cli-0.15.0}/arclith_cli/capabilities.py +329 -3
- {arclith_cli-0.13.0 → arclith_cli-0.15.0}/pyproject.toml +2 -2
- {arclith_cli-0.13.0 → arclith_cli-0.15.0}/tests/e2e_manual.sh +1 -1
- {arclith_cli-0.13.0 → arclith_cli-0.15.0}/tests/test_add_adapter.py +558 -171
- {arclith_cli-0.13.0 → arclith_cli-0.15.0}/tests/test_capabilities.py +316 -53
- {arclith_cli-0.13.0 → arclith_cli-0.15.0}/tests/test_core_scaffold.py +1 -1
- {arclith_cli-0.13.0 → arclith_cli-0.15.0}/uv.lock +14 -3
- arclith_cli-0.13.0/arclith_cli/__init__.py +0 -1
- {arclith_cli-0.13.0 → arclith_cli-0.15.0}/.gitignore +0 -0
- {arclith_cli-0.13.0 → arclith_cli-0.15.0}/LICENSE +0 -0
- {arclith_cli-0.13.0 → arclith_cli-0.15.0}/Makefile +0 -0
- {arclith_cli-0.13.0 → arclith_cli-0.15.0}/arclith_cli/core_scaffold.py +0 -0
- {arclith_cli-0.13.0 → arclith_cli-0.15.0}/arclith_cli/entity_scanner.py +0 -0
- {arclith_cli-0.13.0 → arclith_cli-0.15.0}/arclith_cli/export_config.py +0 -0
- {arclith_cli-0.13.0 → arclith_cli-0.15.0}/arclith_cli/init_project.py +0 -0
- {arclith_cli-0.13.0 → arclith_cli-0.15.0}/arclith_cli/main.py +0 -0
- {arclith_cli-0.13.0 → arclith_cli-0.15.0}/arclith_cli/project_paths.py +0 -0
- {arclith_cli-0.13.0 → arclith_cli-0.15.0}/arclith_cli/rename.py +0 -0
- {arclith_cli-0.13.0 → arclith_cli-0.15.0}/arclith_cli/runtime_templates.py +0 -0
- {arclith_cli-0.13.0 → arclith_cli-0.15.0}/arclith_cli/scaffold.py +0 -0
- {arclith_cli-0.13.0 → arclith_cli-0.15.0}/arclith_cli/updater.py +0 -0
- {arclith_cli-0.13.0 → arclith_cli-0.15.0}/tests/__init__.py +0 -0
- {arclith_cli-0.13.0 → arclith_cli-0.15.0}/tests/test_e2e_scaffold.py +0 -0
- {arclith_cli-0.13.0 → arclith_cli-0.15.0}/tests/test_project_paths.py +0 -0
- {arclith_cli-0.13.0 → arclith_cli-0.15.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.
|
|
3
|
+
Version: 0.15.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.
|
|
189
|
+
Requires-Dist: arclith>=0.18.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
|
|
@@ -141,7 +141,7 @@ arclith-cli add-adapter --adapter mariadb --entity Recipe --param database=my_re
|
|
|
141
141
|
arclith-cli add-adapter --capability api --adapter fastapi --param port=8080 --yes
|
|
142
142
|
arclith-cli add-adapter --capability mcp --adapter fastmcp --param port=8081 --yes
|
|
143
143
|
arclith-cli add-adapter --capability llm --adapter lmstudio --param model_name=qwen/qwen3.5-9b --yes
|
|
144
|
-
arclith-cli add-adapter --capability agent --adapter langgraph --param graph_name=recipe_agent --yes
|
|
144
|
+
arclith-cli add-adapter --capability agent --adapter langgraph --param graph_name=recipe_agent --param stream_mode=updates,custom --yes
|
|
145
145
|
arclith-cli add-adapter --capability observability --adapter langsmith
|
|
146
146
|
arclith-cli add-adapter --capability observability --adapter opentelemetry --param service_name=my_recipe_service --yes
|
|
147
147
|
arclith-cli add-adapter --capability runtime --adapter docker-image --yes
|
|
@@ -166,7 +166,7 @@ arclith-cli add-adapter --capability repository --adapter memory --entity Recipe
|
|
|
166
166
|
- `lmstudio` → `model_name`, `base_url`, `api_key`
|
|
167
167
|
- `openai` → `model_name`, `base_url`, `OPENAI_API_KEY`
|
|
168
168
|
- `anthropic` → `model_name`, `ANTHROPIC_API_KEY`
|
|
169
|
-
- `langgraph` → `graph_name`
|
|
169
|
+
- `langgraph` → `graph_name`, `stream_mode`
|
|
170
170
|
- `langsmith` → `tracing`, `project`, `endpoint`, `LANGSMITH_API_KEY`
|
|
171
171
|
- `opentelemetry` → `service_name`, `endpoint`, `traces_endpoint`, `metrics_endpoint`, `protocol`, `traces`, `metrics`, `instrument_fastapi`
|
|
172
172
|
- `command-bus/rabbitmq` → `url`, `exchange`, `exchange_type`, `queue`, `routing_key`, `prefetch`, `consumer_name`, `concurrency`, `publisher_confirms`, `durable`, `retry_enabled`, `retry_requeue`, `dead_letter_exchange`, `dead_letter_routing_key`
|
|
@@ -240,7 +240,9 @@ l'environnement, un fichier local de secrets ou Vault selon le resolver choisi.
|
|
|
240
240
|
L'adapter `agent/langgraph` génère `langgraph.json`, `config/adapters/inbound/langgraph.yaml` et
|
|
241
241
|
`src/<package>/adapters/inbound/langgraph/agent.py`. Le projet ne modifie ensuite que ce fichier pour
|
|
242
242
|
son agent. Comme `fastapi` et `fastmcp`, LangGraph est configuré par son nom produit dans
|
|
243
|
-
`AppConfig.langgraph`, sans `adapters.agent`.
|
|
243
|
+
`AppConfig.langgraph`, sans `adapters.agent`. `stream_mode` vaut `updates` par défaut et accepte une
|
|
244
|
+
liste CSV comme `updates,custom` pour exposer les sorties de nodes et les événements
|
|
245
|
+
`get_stream_writer()`. L'adapter `observability/langsmith` génère
|
|
244
246
|
`config/adapters/outbound/langsmith.yaml`, l'ajoute à `observability.enabled`, met
|
|
245
247
|
à jour `.env` et ajoute `.env` au `.gitignore` si besoin. LangSmith Studio devient l'endroit standard
|
|
246
248
|
pour tester les agents. Une `LANGSMITH_API_KEY` déjà présente est conservée si aucune nouvelle valeur
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.15.0"
|
|
@@ -66,6 +66,20 @@ class MariaDB{pascal}Repository(MariaDBRepository[{pascal}], {pascal}Repository)
|
|
|
66
66
|
|
|
67
67
|
# TODO: add custom query methods here
|
|
68
68
|
""",
|
|
69
|
+
"postgresql": """\
|
|
70
|
+
from arclith.adapters.outbound.postgresql.config import PostgreSQLConfig
|
|
71
|
+
from arclith.adapters.outbound.postgresql.repository import PostgreSQLRepository
|
|
72
|
+
from arclith.domain.ports.outbound.logger import Logger
|
|
73
|
+
from {domain_import}.models.{snake} import {pascal}
|
|
74
|
+
from {domain_import}.ports.outbound.{snake}_repository import {pascal}Repository
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class PostgreSQL{pascal}Repository(PostgreSQLRepository[{pascal}], {pascal}Repository):
|
|
78
|
+
def __init__(self, config: PostgreSQLConfig, logger: Logger) -> None:
|
|
79
|
+
super().__init__(config, {pascal}, logger)
|
|
80
|
+
|
|
81
|
+
# TODO: add custom query methods here
|
|
82
|
+
""",
|
|
69
83
|
}
|
|
70
84
|
|
|
71
85
|
# ── repository.py re-export template ─────────────────────────────────────────
|
|
@@ -90,6 +104,11 @@ __all__ = ["DuckDB{pascal}Repository"]
|
|
|
90
104
|
from {adapters_import}.outbound.mariadb.repositories.{snake}_repository import MariaDB{pascal}Repository
|
|
91
105
|
|
|
92
106
|
__all__ = ["MariaDB{pascal}Repository"]
|
|
107
|
+
""",
|
|
108
|
+
"postgresql": """\
|
|
109
|
+
from {adapters_import}.outbound.postgresql.repositories.{snake}_repository import PostgreSQL{pascal}Repository
|
|
110
|
+
|
|
111
|
+
__all__ = ["PostgreSQL{pascal}Repository"]
|
|
93
112
|
""",
|
|
94
113
|
}
|
|
95
114
|
|
|
@@ -152,6 +171,29 @@ def _build_mariadb(cfg: AppConfig, _entity_class: type[{pascal}], log: Logger) -
|
|
|
152
171
|
log,
|
|
153
172
|
)
|
|
154
173
|
|
|
174
|
+
""",
|
|
175
|
+
"postgresql": """\
|
|
176
|
+
def _build_postgresql(cfg: AppConfig, _entity_class: type[{pascal}], log: Logger) -> {pascal}Repository:
|
|
177
|
+
from {adapters_import}.outbound.postgresql.repository import PostgreSQL{pascal}Repository
|
|
178
|
+
from arclith.adapters.outbound.postgresql.config import PostgreSQLConfig
|
|
179
|
+
postgresql = cfg.adapters.postgresql
|
|
180
|
+
if postgresql is None:
|
|
181
|
+
raise ValueError("PostgreSQL settings are required when repository=postgresql")
|
|
182
|
+
return PostgreSQL{pascal}Repository(
|
|
183
|
+
PostgreSQLConfig(
|
|
184
|
+
url=postgresql.url,
|
|
185
|
+
host=postgresql.host,
|
|
186
|
+
port=postgresql.port,
|
|
187
|
+
database=postgresql.database,
|
|
188
|
+
user=postgresql.user,
|
|
189
|
+
password=postgresql.password,
|
|
190
|
+
schema=postgresql.schema_name,
|
|
191
|
+
driver=postgresql.driver,
|
|
192
|
+
table_prefix=postgresql.table_prefix,
|
|
193
|
+
),
|
|
194
|
+
log,
|
|
195
|
+
)
|
|
196
|
+
|
|
155
197
|
""",
|
|
156
198
|
}
|
|
157
199
|
|
|
@@ -169,7 +211,9 @@ def build_{snake}_service(arclith: Arclith) -> tuple[{pascal}Service, Logger]:
|
|
|
169
211
|
"""
|
|
170
212
|
|
|
171
213
|
|
|
172
|
-
def render_container(
|
|
214
|
+
def render_container(
|
|
215
|
+
pascal: str, snake: str, installed_adapters: list[str], import_vars: dict[str, str]
|
|
216
|
+
) -> str:
|
|
173
217
|
"""Generate the full container file content for a given entity and its adapters."""
|
|
174
218
|
# memory is always included (arclith built-in, needs no extra files)
|
|
175
219
|
adapters = list(dict.fromkeys(["memory"] + installed_adapters))
|
|
@@ -182,9 +226,7 @@ def render_container(pascal: str, snake: str, installed_adapters: list[str], imp
|
|
|
182
226
|
if a in _CONTAINER_FACTORY
|
|
183
227
|
)
|
|
184
228
|
registrations = "\n".join(
|
|
185
|
-
f
|
|
186
|
-
for a in adapters
|
|
187
|
-
if a in _CONTAINER_FACTORY
|
|
229
|
+
f' .register("{a}", _build_{a})' for a in adapters if a in _CONTAINER_FACTORY
|
|
188
230
|
)
|
|
189
231
|
footer = _CONTAINER_FOOTER.format(**vars, registrations=registrations)
|
|
190
232
|
return header + factories + footer
|
|
@@ -686,11 +686,19 @@ def _file_template_vars(
|
|
|
686
686
|
"package_path": package_path,
|
|
687
687
|
"langgraph_entrypoint": langgraph_entrypoint,
|
|
688
688
|
"graph_name": graph_name,
|
|
689
|
+
"stream_mode_yaml": _langgraph_stream_mode_yaml(str(params.get("stream_mode") or "updates")),
|
|
689
690
|
"secret_template_yaml": _secret_template_yaml(str(params.get("field_path") or "")),
|
|
690
691
|
"secret_chain_yaml": _secret_chain_yaml(str(params.get("resolvers") or "")),
|
|
691
692
|
}
|
|
692
693
|
|
|
693
694
|
|
|
695
|
+
def _langgraph_stream_mode_yaml(stream_mode: str) -> str:
|
|
696
|
+
values = _split_csv_values(stream_mode)
|
|
697
|
+
if len(values) == 1:
|
|
698
|
+
return f'"{values[0]}"'
|
|
699
|
+
return yaml.safe_dump(values, default_flow_style=True, sort_keys=False).strip()
|
|
700
|
+
|
|
701
|
+
|
|
694
702
|
def _secret_template_yaml(field_path: str) -> str:
|
|
695
703
|
keys = [key for key in field_path.split(".") if key]
|
|
696
704
|
if not keys:
|
|
@@ -108,10 +108,16 @@ class AdapterSpec:
|
|
|
108
108
|
"layer": self.layer,
|
|
109
109
|
"description": self.description,
|
|
110
110
|
"config_path": self.config_path,
|
|
111
|
-
"merge_config_templates": [
|
|
111
|
+
"merge_config_templates": [
|
|
112
|
+
file_template.to_dict() for file_template in self.merge_config_templates
|
|
113
|
+
],
|
|
112
114
|
"env_path": self.env_path,
|
|
113
|
-
"file_templates": [
|
|
114
|
-
|
|
115
|
+
"file_templates": [
|
|
116
|
+
file_template.to_dict() for file_template in self.file_templates
|
|
117
|
+
],
|
|
118
|
+
"secret_mappings": [
|
|
119
|
+
secret_mapping.to_dict() for secret_mapping in self.secret_mappings
|
|
120
|
+
],
|
|
115
121
|
"secret_resolver": self.secret_resolver,
|
|
116
122
|
"secret_config_template": bool(self.secret_config_template),
|
|
117
123
|
"gitignore_entries": list(self.gitignore_entries),
|
|
@@ -284,6 +290,305 @@ multitenant: false
|
|
|
284
290
|
),
|
|
285
291
|
),
|
|
286
292
|
),
|
|
293
|
+
AdapterSpec(
|
|
294
|
+
name="postgresql",
|
|
295
|
+
capability="repository",
|
|
296
|
+
layer="outbound",
|
|
297
|
+
description="Repository PostgreSQL async optionnel, pilote par SQLAlchemy et asyncpg, avec payload JSONB generique.",
|
|
298
|
+
config_path="config/adapters/outbound/postgresql.yaml",
|
|
299
|
+
config_template="""\
|
|
300
|
+
url: null # à mapper via config/secrets.yaml ou resolver env/vault si vous fournissez une URL complète
|
|
301
|
+
host: {host}
|
|
302
|
+
port: {port}
|
|
303
|
+
database: {database}
|
|
304
|
+
user: {user}
|
|
305
|
+
password: null # à mapper via config/secrets.yaml ou resolver env/vault
|
|
306
|
+
schema: {schema}
|
|
307
|
+
driver: {driver}
|
|
308
|
+
table_prefix: "{table_prefix}"
|
|
309
|
+
multitenant: {multitenant}
|
|
310
|
+
""",
|
|
311
|
+
secret_mappings=(
|
|
312
|
+
SecretMappingSpec(
|
|
313
|
+
field_path="adapters.postgresql.url",
|
|
314
|
+
secret_key="POSTGRESQL_URL",
|
|
315
|
+
),
|
|
316
|
+
SecretMappingSpec(
|
|
317
|
+
field_path="adapters.postgresql.password",
|
|
318
|
+
secret_key="POSTGRESQL_PASSWORD",
|
|
319
|
+
),
|
|
320
|
+
),
|
|
321
|
+
parameters=(
|
|
322
|
+
ParameterSpec(
|
|
323
|
+
name="host",
|
|
324
|
+
kind="string",
|
|
325
|
+
prompt="host",
|
|
326
|
+
default="127.0.0.1",
|
|
327
|
+
),
|
|
328
|
+
ParameterSpec(
|
|
329
|
+
name="port",
|
|
330
|
+
kind="string",
|
|
331
|
+
prompt="port",
|
|
332
|
+
default="5432",
|
|
333
|
+
),
|
|
334
|
+
ParameterSpec(
|
|
335
|
+
name="database",
|
|
336
|
+
kind="string",
|
|
337
|
+
prompt="database",
|
|
338
|
+
default_from_project_name=True,
|
|
339
|
+
),
|
|
340
|
+
ParameterSpec(
|
|
341
|
+
name="user",
|
|
342
|
+
kind="string",
|
|
343
|
+
prompt="user",
|
|
344
|
+
default="app",
|
|
345
|
+
),
|
|
346
|
+
ParameterSpec(
|
|
347
|
+
name="schema",
|
|
348
|
+
kind="string",
|
|
349
|
+
prompt="schema",
|
|
350
|
+
default="public",
|
|
351
|
+
),
|
|
352
|
+
ParameterSpec(
|
|
353
|
+
name="driver",
|
|
354
|
+
kind="string",
|
|
355
|
+
prompt="driver",
|
|
356
|
+
default="asyncpg",
|
|
357
|
+
),
|
|
358
|
+
ParameterSpec(
|
|
359
|
+
name="table_prefix",
|
|
360
|
+
kind="string",
|
|
361
|
+
prompt="table_prefix",
|
|
362
|
+
default="",
|
|
363
|
+
),
|
|
364
|
+
ParameterSpec(
|
|
365
|
+
name="multitenant",
|
|
366
|
+
kind="boolean",
|
|
367
|
+
prompt="multitenant",
|
|
368
|
+
default=False,
|
|
369
|
+
),
|
|
370
|
+
),
|
|
371
|
+
),
|
|
372
|
+
),
|
|
373
|
+
)
|
|
374
|
+
|
|
375
|
+
STORAGE_CAPABILITY = CapabilitySpec(
|
|
376
|
+
name="storage",
|
|
377
|
+
layer="outbound",
|
|
378
|
+
description="Stockage de fichiers et blobs derrière un port FileStoragePort.",
|
|
379
|
+
activation_config_key=None,
|
|
380
|
+
adapters=(
|
|
381
|
+
AdapterSpec(
|
|
382
|
+
name="filesystem",
|
|
383
|
+
capability="storage",
|
|
384
|
+
layer="outbound",
|
|
385
|
+
description="Stockage fichier local, typiquement monté dans Docker via un volume.",
|
|
386
|
+
config_path="config/adapters/outbound/storage.yaml",
|
|
387
|
+
config_template="""\
|
|
388
|
+
adapter: filesystem
|
|
389
|
+
root_path: "{root_path}"
|
|
390
|
+
prefix: "{prefix}"
|
|
391
|
+
create_root: {create_root}
|
|
392
|
+
multitenant: {multitenant}
|
|
393
|
+
""",
|
|
394
|
+
parameters=(
|
|
395
|
+
ParameterSpec(
|
|
396
|
+
name="root_path",
|
|
397
|
+
kind="string",
|
|
398
|
+
prompt="Chemin conteneur du dossier de fichiers",
|
|
399
|
+
default="/data/files",
|
|
400
|
+
),
|
|
401
|
+
ParameterSpec(
|
|
402
|
+
name="prefix",
|
|
403
|
+
kind="string",
|
|
404
|
+
prompt="Préfixe d'objets",
|
|
405
|
+
default="",
|
|
406
|
+
),
|
|
407
|
+
ParameterSpec(
|
|
408
|
+
name="create_root",
|
|
409
|
+
kind="boolean",
|
|
410
|
+
prompt="Créer le dossier racine au démarrage",
|
|
411
|
+
default=True,
|
|
412
|
+
),
|
|
413
|
+
ParameterSpec(
|
|
414
|
+
name="multitenant",
|
|
415
|
+
kind="boolean",
|
|
416
|
+
prompt="Résoudre le stockage par tenant",
|
|
417
|
+
default=False,
|
|
418
|
+
),
|
|
419
|
+
),
|
|
420
|
+
entity_scoped=False,
|
|
421
|
+
),
|
|
422
|
+
AdapterSpec(
|
|
423
|
+
name="s3",
|
|
424
|
+
capability="storage",
|
|
425
|
+
layer="outbound",
|
|
426
|
+
description="Stockage objet AWS S3 compatible MinIO via endpoint custom.",
|
|
427
|
+
config_path="config/adapters/outbound/storage.yaml",
|
|
428
|
+
config_template="""\
|
|
429
|
+
adapter: s3
|
|
430
|
+
# Credentials: chaîne AWS par défaut, variables d'environnement, rôle IAM ou TenantContext.
|
|
431
|
+
bucket_name: "{bucket_name}"
|
|
432
|
+
prefix: "{prefix}"
|
|
433
|
+
region_name: "{region_name}"
|
|
434
|
+
endpoint_url: {endpoint_url}
|
|
435
|
+
force_path_style: {force_path_style}
|
|
436
|
+
multitenant: {multitenant}
|
|
437
|
+
""",
|
|
438
|
+
parameters=(
|
|
439
|
+
ParameterSpec(
|
|
440
|
+
name="bucket_name",
|
|
441
|
+
kind="string",
|
|
442
|
+
prompt="Bucket S3",
|
|
443
|
+
default="my-bucket",
|
|
444
|
+
),
|
|
445
|
+
ParameterSpec(
|
|
446
|
+
name="prefix",
|
|
447
|
+
kind="string",
|
|
448
|
+
prompt="Préfixe d'objets",
|
|
449
|
+
default="",
|
|
450
|
+
),
|
|
451
|
+
ParameterSpec(
|
|
452
|
+
name="region_name",
|
|
453
|
+
kind="string",
|
|
454
|
+
prompt="Région AWS",
|
|
455
|
+
default="eu-west-3",
|
|
456
|
+
),
|
|
457
|
+
ParameterSpec(
|
|
458
|
+
name="endpoint_url",
|
|
459
|
+
kind="string",
|
|
460
|
+
prompt="Endpoint S3 custom ou null",
|
|
461
|
+
default="null",
|
|
462
|
+
),
|
|
463
|
+
ParameterSpec(
|
|
464
|
+
name="force_path_style",
|
|
465
|
+
kind="boolean",
|
|
466
|
+
prompt="Forcer le path-style S3",
|
|
467
|
+
default=False,
|
|
468
|
+
),
|
|
469
|
+
ParameterSpec(
|
|
470
|
+
name="multitenant",
|
|
471
|
+
kind="boolean",
|
|
472
|
+
prompt="Résoudre le stockage par tenant",
|
|
473
|
+
default=False,
|
|
474
|
+
),
|
|
475
|
+
),
|
|
476
|
+
entity_scoped=False,
|
|
477
|
+
),
|
|
478
|
+
AdapterSpec(
|
|
479
|
+
name="azure-blob",
|
|
480
|
+
capability="storage",
|
|
481
|
+
layer="outbound",
|
|
482
|
+
description="Stockage objet Azure Blob Storage.",
|
|
483
|
+
config_path="config/adapters/outbound/storage.yaml",
|
|
484
|
+
config_template="""\
|
|
485
|
+
adapter: azure-blob
|
|
486
|
+
account_url: "{account_url}"
|
|
487
|
+
container_name: "{container_name}"
|
|
488
|
+
prefix: "{prefix}"
|
|
489
|
+
connection_string: null
|
|
490
|
+
account_key: null
|
|
491
|
+
sas_token: null
|
|
492
|
+
use_default_credential: {use_default_credential}
|
|
493
|
+
multitenant: {multitenant}
|
|
494
|
+
# Credentials: mappez connection_string, account_key ou sas_token via
|
|
495
|
+
# config/secrets.yaml, Vault, ou TenantContext. use_default_credential=true
|
|
496
|
+
# délègue à DefaultAzureCredential du SDK Azure.
|
|
497
|
+
""",
|
|
498
|
+
secret_mappings=(
|
|
499
|
+
SecretMappingSpec(
|
|
500
|
+
field_path="adapters.storage.connection_string",
|
|
501
|
+
secret_key="AZURE_STORAGE_CONNECTION_STRING",
|
|
502
|
+
),
|
|
503
|
+
SecretMappingSpec(
|
|
504
|
+
field_path="adapters.storage.account_key",
|
|
505
|
+
secret_key="AZURE_STORAGE_ACCOUNT_KEY",
|
|
506
|
+
),
|
|
507
|
+
SecretMappingSpec(
|
|
508
|
+
field_path="adapters.storage.sas_token",
|
|
509
|
+
secret_key="AZURE_STORAGE_SAS_TOKEN",
|
|
510
|
+
),
|
|
511
|
+
),
|
|
512
|
+
parameters=(
|
|
513
|
+
ParameterSpec(
|
|
514
|
+
name="account_url",
|
|
515
|
+
kind="string",
|
|
516
|
+
prompt="URL du compte Azure Blob",
|
|
517
|
+
default="https://<account>.blob.core.windows.net",
|
|
518
|
+
),
|
|
519
|
+
ParameterSpec(
|
|
520
|
+
name="container_name",
|
|
521
|
+
kind="string",
|
|
522
|
+
prompt="Container Azure Blob",
|
|
523
|
+
default="my-container",
|
|
524
|
+
),
|
|
525
|
+
ParameterSpec(
|
|
526
|
+
name="prefix",
|
|
527
|
+
kind="string",
|
|
528
|
+
prompt="Préfixe d'objets",
|
|
529
|
+
default="",
|
|
530
|
+
),
|
|
531
|
+
ParameterSpec(
|
|
532
|
+
name="use_default_credential",
|
|
533
|
+
kind="boolean",
|
|
534
|
+
prompt="Utiliser DefaultAzureCredential",
|
|
535
|
+
default=False,
|
|
536
|
+
),
|
|
537
|
+
ParameterSpec(
|
|
538
|
+
name="multitenant",
|
|
539
|
+
kind="boolean",
|
|
540
|
+
prompt="Résoudre le stockage par tenant",
|
|
541
|
+
default=False,
|
|
542
|
+
),
|
|
543
|
+
),
|
|
544
|
+
entity_scoped=False,
|
|
545
|
+
),
|
|
546
|
+
AdapterSpec(
|
|
547
|
+
name="gcs",
|
|
548
|
+
capability="storage",
|
|
549
|
+
layer="outbound",
|
|
550
|
+
description="Stockage objet Google Cloud Storage.",
|
|
551
|
+
config_path="config/adapters/outbound/storage.yaml",
|
|
552
|
+
config_template="""\
|
|
553
|
+
adapter: gcs
|
|
554
|
+
bucket_name: "{bucket_name}"
|
|
555
|
+
prefix: "{prefix}"
|
|
556
|
+
project_id: {project_id}
|
|
557
|
+
credentials_path: null
|
|
558
|
+
credentials_json: null
|
|
559
|
+
credentials_json_b64: null
|
|
560
|
+
multitenant: {multitenant}
|
|
561
|
+
# Credentials: use ADC/GOOGLE_APPLICATION_CREDENTIALS, config/secrets.yaml,
|
|
562
|
+
# Vault, or TenantContext. Never commit service account JSON.
|
|
563
|
+
""",
|
|
564
|
+
parameters=(
|
|
565
|
+
ParameterSpec(
|
|
566
|
+
name="bucket_name",
|
|
567
|
+
kind="string",
|
|
568
|
+
prompt="Bucket Google Cloud Storage",
|
|
569
|
+
default="my-bucket",
|
|
570
|
+
),
|
|
571
|
+
ParameterSpec(
|
|
572
|
+
name="prefix",
|
|
573
|
+
kind="string",
|
|
574
|
+
prompt="Préfixe d'objets",
|
|
575
|
+
default="",
|
|
576
|
+
),
|
|
577
|
+
ParameterSpec(
|
|
578
|
+
name="project_id",
|
|
579
|
+
kind="string",
|
|
580
|
+
prompt="Project ID GCP ou null",
|
|
581
|
+
default="null",
|
|
582
|
+
),
|
|
583
|
+
ParameterSpec(
|
|
584
|
+
name="multitenant",
|
|
585
|
+
kind="boolean",
|
|
586
|
+
prompt="Résoudre le stockage par tenant",
|
|
587
|
+
default=False,
|
|
588
|
+
),
|
|
589
|
+
),
|
|
590
|
+
entity_scoped=False,
|
|
591
|
+
),
|
|
287
592
|
),
|
|
288
593
|
)
|
|
289
594
|
|
|
@@ -1263,6 +1568,7 @@ name: "{graph_name}"
|
|
|
1263
1568
|
graph: "{graph_name}"
|
|
1264
1569
|
entrypoint: "{langgraph_entrypoint}"
|
|
1265
1570
|
env: ".env"
|
|
1571
|
+
stream_mode: {stream_mode_yaml}
|
|
1266
1572
|
""",
|
|
1267
1573
|
file_templates=(
|
|
1268
1574
|
FileTemplateSpec(
|
|
@@ -1287,6 +1593,7 @@ env: ".env"
|
|
|
1287
1593
|
from typing import Any, TypedDict
|
|
1288
1594
|
|
|
1289
1595
|
from arclith import Arclith
|
|
1596
|
+
from langgraph.config import get_stream_writer
|
|
1290
1597
|
from langgraph.graph import END, START
|
|
1291
1598
|
|
|
1292
1599
|
|
|
@@ -1300,6 +1607,8 @@ arclith = Arclith("config")
|
|
|
1300
1607
|
# Template minimal volontaire: remplacer AgentState, run_agent et les edges par
|
|
1301
1608
|
# l'état, les noeuds et les transitions propres au projet.
|
|
1302
1609
|
async def run_agent(state: AgentState) -> AgentState:
|
|
1610
|
+
writer = get_stream_writer()
|
|
1611
|
+
writer({{"kind": "progress", "stage": "agent.started", "message": "Agent node started."}})
|
|
1303
1612
|
return state
|
|
1304
1613
|
|
|
1305
1614
|
|
|
@@ -1320,6 +1629,22 @@ agent = arclith.langgraph(AgentState, register_agent, name="{graph_name}")
|
|
|
1320
1629
|
prompt="Nom du graphe LangGraph",
|
|
1321
1630
|
default="agent",
|
|
1322
1631
|
),
|
|
1632
|
+
ParameterSpec(
|
|
1633
|
+
name="stream_mode",
|
|
1634
|
+
kind="string",
|
|
1635
|
+
prompt="Mode(s) de streaming LangGraph",
|
|
1636
|
+
default="updates",
|
|
1637
|
+
choices=(
|
|
1638
|
+
"values",
|
|
1639
|
+
"updates",
|
|
1640
|
+
"custom",
|
|
1641
|
+
"messages",
|
|
1642
|
+
"checkpoints",
|
|
1643
|
+
"tasks",
|
|
1644
|
+
"debug",
|
|
1645
|
+
),
|
|
1646
|
+
csv_choices=True,
|
|
1647
|
+
),
|
|
1323
1648
|
),
|
|
1324
1649
|
entity_scoped=False,
|
|
1325
1650
|
),
|
|
@@ -1478,6 +1803,7 @@ OTEL_EXPORTER_OTLP_HEADERS={headers}
|
|
|
1478
1803
|
|
|
1479
1804
|
CAPABILITY_CATALOG = (
|
|
1480
1805
|
REPOSITORY_CAPABILITY,
|
|
1806
|
+
STORAGE_CAPABILITY,
|
|
1481
1807
|
CACHE_CAPABILITY,
|
|
1482
1808
|
LOGGER_CAPABILITY,
|
|
1483
1809
|
SECRETS_CAPABILITY,
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "arclith-cli"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.15.0"
|
|
8
8
|
description = "CLI scaffolding tool for arclith — hexagonal architecture framework"
|
|
9
9
|
license = { file = "LICENSE" }
|
|
10
10
|
requires-python = ">=3.13"
|
|
@@ -16,7 +16,7 @@ dependencies = [
|
|
|
16
16
|
"typer>=0.15.0",
|
|
17
17
|
"rich>=13.0.0",
|
|
18
18
|
"httpx>=0.27.0",
|
|
19
|
-
"arclith>=0.
|
|
19
|
+
"arclith>=0.18.0",
|
|
20
20
|
]
|
|
21
21
|
|
|
22
22
|
[tool.uv.sources]
|
|
@@ -39,7 +39,7 @@ if grep -q '\[tool\.uv\.sources\]' pyproject.toml; then
|
|
|
39
39
|
fi
|
|
40
40
|
|
|
41
41
|
ARCLITH_VERSION=$(grep 'arclith\[' pyproject.toml | grep -oE '[0-9]+\.[0-9]+\.[0-9]+')
|
|
42
|
-
MIN_ARCLITH_VERSION="0.
|
|
42
|
+
MIN_ARCLITH_VERSION="0.18.0"
|
|
43
43
|
if version_lt "$ARCLITH_VERSION" "$MIN_ARCLITH_VERSION"; then
|
|
44
44
|
echo -e "${RED}✗ FAIL: arclith version < $MIN_ARCLITH_VERSION (found: $ARCLITH_VERSION)${NC}"
|
|
45
45
|
exit 1
|