embed-client-dev 4.0.78__py3-none-any.whl

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 (48) hide show
  1. embed_client/__init__.py +36 -0
  2. embed_client/__main__.py +15 -0
  3. embed_client/_version.py +3 -0
  4. embed_client/adapter_config_factory.py +213 -0
  5. embed_client/adapter_config_normalizer.py +135 -0
  6. embed_client/adapter_generator_bridge.py +166 -0
  7. embed_client/auth.py +189 -0
  8. embed_client/cli.py +483 -0
  9. embed_client/cli_core.py +427 -0
  10. embed_client/client_factory.py +515 -0
  11. embed_client/client_factory_examples.py +375 -0
  12. embed_client/client_factory_helpers.py +59 -0
  13. embed_client/config.py +408 -0
  14. embed_client/config_defaults.py +77 -0
  15. embed_client/config_examples.py +228 -0
  16. embed_client/config_generator.py +652 -0
  17. embed_client/config_generator_cli.py +151 -0
  18. embed_client/config_presets.py +245 -0
  19. embed_client/config_validator.py +246 -0
  20. embed_client/config_validator_cli.py +141 -0
  21. embed_client/constants.py +52 -0
  22. embed_client/demo_cli.py +78 -0
  23. embed_client/demo_security_cli.py +206 -0
  24. embed_client/embed_file_output_validate.py +348 -0
  25. embed_client/embed_file_status_store.py +80 -0
  26. embed_client/embedding_client.py +1929 -0
  27. embed_client/embedding_client_helpers.py +105 -0
  28. embed_client/example_async_usage.py +357 -0
  29. embed_client/example_async_usage_demo.py +282 -0
  30. embed_client/example_async_usage_ru.py +340 -0
  31. embed_client/example_async_usage_ru_demo.py +313 -0
  32. embed_client/exceptions.py +122 -0
  33. embed_client/identifier_types.py +56 -0
  34. embed_client/json_embed_file_preflight.py +128 -0
  35. embed_client/local_json_embed_file.py +73 -0
  36. embed_client/md_embed_file_prep.py +197 -0
  37. embed_client/response_normalizer.py +160 -0
  38. embed_client/response_parsers.py +219 -0
  39. embed_client/security_cli.py +200 -0
  40. embed_client/security_cli_core.py +227 -0
  41. embed_client/ssl_examples.py +278 -0
  42. embed_client/ssl_manager.py +141 -0
  43. embed_client_dev-4.0.78.dist-info/METADATA +155 -0
  44. embed_client_dev-4.0.78.dist-info/RECORD +48 -0
  45. embed_client_dev-4.0.78.dist-info/WHEEL +5 -0
  46. embed_client_dev-4.0.78.dist-info/entry_points.txt +4 -0
  47. embed_client_dev-4.0.78.dist-info/licenses/LICENSE +21 -0
  48. embed_client_dev-4.0.78.dist-info/top_level.txt +1 -0
@@ -0,0 +1,652 @@
1
+ """
2
+ Configuration Generator for embed-client.
3
+
4
+ Uses mcp_proxy_adapter SimpleConfigGenerator as base when available.
5
+ Generates client configurations for all security modes:
6
+ - http
7
+ - http + token
8
+ - http + token + roles
9
+ - https
10
+ - https + token
11
+ - https + token + roles
12
+ - mtls
13
+ - mtls + roles
14
+
15
+ Author: Vasiliy Zdanovskiy
16
+ email: vasilyvz@gmail.com
17
+ """
18
+
19
+ import json
20
+ import logging
21
+ from pathlib import Path
22
+ from typing import Dict, Any, Optional, List
23
+
24
+ logger = logging.getLogger(__name__)
25
+
26
+ try:
27
+ from embed_client.adapter_generator_bridge import generate_client_config_via_adapter
28
+
29
+ _ADAPTER_GEN_AVAILABLE = True
30
+ except ImportError:
31
+ _ADAPTER_GEN_AVAILABLE = False
32
+ generate_client_config_via_adapter = None # type: ignore[misc, assignment]
33
+
34
+
35
+ class ClientConfigGenerator:
36
+ """Generator for embed-client configurations."""
37
+
38
+ def __init__(self):
39
+ """Initialize configuration generator."""
40
+ self.default_tokens = {
41
+ "admin": "admin-secret-key",
42
+ "user": "user-secret-key",
43
+ "readonly": "readonly-secret-key",
44
+ }
45
+ self.default_roles = {
46
+ "admin": ["read", "write", "delete", "admin"],
47
+ "user": ["read", "write"],
48
+ "readonly": ["read"],
49
+ }
50
+
51
+ def _generate_via_adapter(
52
+ self,
53
+ protocol: str,
54
+ host: str = "localhost",
55
+ port: int = 8001,
56
+ cert_file: Optional[str] = None,
57
+ key_file: Optional[str] = None,
58
+ ca_cert_file: Optional[str] = None,
59
+ crl_file: Optional[str] = None,
60
+ token: Optional[str] = None,
61
+ use_token: bool = False,
62
+ use_roles: bool = False,
63
+ tokens: Optional[Dict[str, Any]] = None,
64
+ roles: Optional[Dict[str, Any]] = None,
65
+ output_path: Optional[Path] = None,
66
+ ) -> Optional[Dict[str, Any]]:
67
+ """Generate config using adapter as base; return dict or None if adapter unavailable."""
68
+ if not _ADAPTER_GEN_AVAILABLE or generate_client_config_via_adapter is None:
69
+ return None
70
+ try:
71
+ return generate_client_config_via_adapter(
72
+ protocol=protocol,
73
+ host=host,
74
+ port=port,
75
+ cert_file=cert_file,
76
+ key_file=key_file,
77
+ ca_cert_file=ca_cert_file,
78
+ crl_file=crl_file,
79
+ token=token,
80
+ use_token=use_token,
81
+ use_roles=use_roles,
82
+ tokens=tokens,
83
+ roles=roles,
84
+ output_path=output_path,
85
+ )
86
+ except (ValueError, RuntimeError) as e:
87
+ logger.debug("Adapter generator failed (%s), using fallback", e)
88
+ return None
89
+
90
+ def _create_base_config(
91
+ self,
92
+ host: str = "localhost",
93
+ port: int = 8001,
94
+ protocol: str = "http",
95
+ token: Optional[str] = None,
96
+ cert_file: Optional[str] = None,
97
+ key_file: Optional[str] = None,
98
+ ca_cert_file: Optional[str] = None,
99
+ crl_file: Optional[str] = None,
100
+ ) -> Dict[str, Any]:
101
+ """
102
+ Create base configuration with all fields.
103
+
104
+ Args:
105
+ host: Server host (default value)
106
+ port: Server port (default value)
107
+ protocol: Protocol type (http, https, mtls)
108
+ token: Optional authentication token
109
+ cert_file: Optional client certificate file
110
+ key_file: Optional client key file
111
+ ca_cert_file: Optional CA certificate file
112
+ crl_file: Optional CRL file
113
+
114
+ Returns:
115
+ Base configuration dictionary with all fields
116
+ """
117
+ # Determine SSL settings based on protocol
118
+ ssl_enabled = protocol in ("https", "mtls")
119
+ verify_mode = "CERT_REQUIRED" if protocol == "mtls" else "CERT_NONE"
120
+
121
+ # Base configuration with all fields
122
+ config: Dict[str, Any] = {
123
+ "server": {
124
+ "host": host, # Default value
125
+ "port": port, # Default value
126
+ },
127
+ "client": {
128
+ "timeout": 30.0,
129
+ },
130
+ "protocol": protocol, # Always present
131
+ "auth": {
132
+ "method": "none",
133
+ "api_key": {
134
+ "key": token, # Optional, can be null
135
+ "header": "X-API-Key",
136
+ },
137
+ "certificate": {
138
+ "enabled": protocol == "mtls",
139
+ "cert_file": cert_file if protocol == "mtls" else None,
140
+ "key_file": key_file if protocol == "mtls" else None,
141
+ "ca_cert_file": ca_cert_file if protocol == "mtls" else None,
142
+ },
143
+ },
144
+ "ssl": {
145
+ "enabled": ssl_enabled,
146
+ "verify": protocol != "http",
147
+ "verify_mode": verify_mode,
148
+ "check_hostname": False,
149
+ "check_expiry": False,
150
+ "cert_file": cert_file if ssl_enabled else None,
151
+ "key_file": key_file if ssl_enabled else None,
152
+ "ca_cert_file": ca_cert_file if ssl_enabled else None,
153
+ "crl_file": crl_file, # Optional, can be null
154
+ "client_cert_required": protocol == "mtls",
155
+ },
156
+ "security": {
157
+ "enabled": False,
158
+ },
159
+ }
160
+
161
+ return config
162
+
163
+ def generate_http_config(
164
+ self,
165
+ host: str = "localhost",
166
+ port: int = 8001,
167
+ token: Optional[str] = None,
168
+ crl_file: Optional[str] = None,
169
+ output_path: Optional[Path] = None,
170
+ ) -> Dict[str, Any]:
171
+ """
172
+ Generate HTTP configuration without authentication.
173
+
174
+ Uses adapter SimpleConfigGenerator as base when available.
175
+ """
176
+ config = self._generate_via_adapter(
177
+ protocol="http",
178
+ host=host,
179
+ port=port,
180
+ crl_file=crl_file,
181
+ output_path=output_path,
182
+ )
183
+ if config is not None:
184
+ return config
185
+ config = self._create_base_config(
186
+ host=host,
187
+ port=port,
188
+ protocol="http",
189
+ token=token,
190
+ crl_file=crl_file,
191
+ )
192
+ if output_path:
193
+ self._save_config(config, output_path)
194
+ return config
195
+
196
+ def generate_http_token_config(
197
+ self,
198
+ host: str = "localhost",
199
+ port: int = 8001,
200
+ api_key: Optional[str] = None,
201
+ token: Optional[str] = None,
202
+ crl_file: Optional[str] = None,
203
+ output_path: Optional[Path] = None,
204
+ ) -> Dict[str, Any]:
205
+ """
206
+ Generate HTTP configuration with token authentication.
207
+
208
+ Args:
209
+ host: Server host (default value)
210
+ port: Server port (default value)
211
+ api_key: API key for authentication (deprecated, use token)
212
+ token: Authentication token
213
+ crl_file: Optional CRL file
214
+ output_path: Optional output file path
215
+
216
+ Returns:
217
+ Complete configuration dictionary with all fields
218
+ """
219
+ token_value = token or api_key or self.default_tokens["user"]
220
+ config = self._create_base_config(
221
+ host=host,
222
+ port=port,
223
+ protocol="http",
224
+ token=token_value,
225
+ crl_file=crl_file,
226
+ )
227
+ config["auth"]["method"] = "api_key"
228
+ config["auth"]["api_key"]["key"] = token_value
229
+ config["security"]["enabled"] = True
230
+ config["security"]["tokens"] = {"user": token_value}
231
+ if output_path:
232
+ self._save_config(config, output_path)
233
+ return config
234
+
235
+ def generate_http_token_roles_config(
236
+ self,
237
+ host: str = "localhost",
238
+ port: int = 8001,
239
+ api_key: Optional[str] = None,
240
+ token: Optional[str] = None,
241
+ tokens: Optional[Dict[str, str]] = None,
242
+ roles: Optional[Dict[str, List[str]]] = None,
243
+ crl_file: Optional[str] = None,
244
+ output_path: Optional[Path] = None,
245
+ ) -> Dict[str, Any]:
246
+ """
247
+ Generate HTTP configuration with token authentication and roles.
248
+
249
+ Args:
250
+ host: Server host (default value)
251
+ port: Server port (default value)
252
+ api_key: API key for authentication (deprecated, use token)
253
+ token: Authentication token
254
+ tokens: Dictionary of tokens for different roles
255
+ roles: Dictionary of roles and their permissions
256
+ crl_file: Optional CRL file
257
+ output_path: Optional output file path
258
+
259
+ Returns:
260
+ Complete configuration dictionary with all fields
261
+ """
262
+ tokens_dict = tokens or self.default_tokens
263
+ roles_dict = roles or self.default_roles
264
+ token_value = token or api_key or tokens_dict.get("admin", "admin-secret-key")
265
+ config = self._create_base_config(
266
+ host=host,
267
+ port=port,
268
+ protocol="http",
269
+ token=token_value,
270
+ crl_file=crl_file,
271
+ )
272
+ config["auth"]["method"] = "api_key"
273
+ config["auth"]["api_key"]["key"] = token_value
274
+ config["security"]["enabled"] = True
275
+ config["security"]["tokens"] = tokens_dict
276
+ config["security"]["roles"] = roles_dict
277
+ if output_path:
278
+ self._save_config(config, output_path)
279
+ return config
280
+
281
+ def generate_https_config(
282
+ self,
283
+ host: str = "localhost",
284
+ port: int = 8001,
285
+ token: Optional[str] = None,
286
+ cert_file: Optional[str] = None,
287
+ key_file: Optional[str] = None,
288
+ ca_cert_file: Optional[str] = None,
289
+ crl_file: Optional[str] = None,
290
+ output_path: Optional[Path] = None,
291
+ ) -> Dict[str, Any]:
292
+ """
293
+ Generate HTTPS configuration without authentication.
294
+
295
+ Args:
296
+ host: Server host (default value)
297
+ port: Server port (default value)
298
+ token: Optional token (not used, but included in config)
299
+ cert_file: Optional client certificate file
300
+ key_file: Optional client key file
301
+ ca_cert_file: Optional CA certificate file
302
+ crl_file: Optional CRL file
303
+ output_path: Optional output file path
304
+
305
+ Returns:
306
+ Complete configuration dictionary with all fields
307
+ """
308
+ config = self._create_base_config(
309
+ host=host,
310
+ port=port,
311
+ protocol="https",
312
+ token=token,
313
+ cert_file=cert_file,
314
+ key_file=key_file,
315
+ ca_cert_file=ca_cert_file,
316
+ crl_file=crl_file,
317
+ )
318
+ if output_path:
319
+ self._save_config(config, output_path)
320
+ return config
321
+
322
+ def generate_https_token_config(
323
+ self,
324
+ host: str = "localhost",
325
+ port: int = 8001,
326
+ api_key: Optional[str] = None,
327
+ token: Optional[str] = None,
328
+ cert_file: Optional[str] = None,
329
+ key_file: Optional[str] = None,
330
+ ca_cert_file: Optional[str] = None,
331
+ crl_file: Optional[str] = None,
332
+ output_path: Optional[Path] = None,
333
+ ) -> Dict[str, Any]:
334
+ """
335
+ Generate HTTPS configuration with token authentication.
336
+
337
+ Args:
338
+ host: Server host (default value)
339
+ port: Server port (default value)
340
+ api_key: API key for authentication (deprecated, use token)
341
+ token: Authentication token
342
+ cert_file: Optional client certificate file
343
+ key_file: Optional client key file
344
+ ca_cert_file: Optional CA certificate file
345
+ crl_file: Optional CRL file
346
+ output_path: Optional output file path
347
+
348
+ Returns:
349
+ Complete configuration dictionary with all fields
350
+ """
351
+ token_value = token or api_key or self.default_tokens["user"]
352
+ config = self._create_base_config(
353
+ host=host,
354
+ port=port,
355
+ protocol="https",
356
+ token=token_value,
357
+ cert_file=cert_file,
358
+ key_file=key_file,
359
+ ca_cert_file=ca_cert_file,
360
+ crl_file=crl_file,
361
+ )
362
+ config["auth"]["method"] = "api_key"
363
+ config["auth"]["api_key"]["key"] = token_value
364
+ config["security"]["enabled"] = True
365
+ config["security"]["tokens"] = {"user": token_value}
366
+ if output_path:
367
+ self._save_config(config, output_path)
368
+ return config
369
+
370
+ def generate_https_token_roles_config(
371
+ self,
372
+ host: str = "localhost",
373
+ port: int = 8001,
374
+ api_key: Optional[str] = None,
375
+ token: Optional[str] = None,
376
+ tokens: Optional[Dict[str, str]] = None,
377
+ roles: Optional[Dict[str, List[str]]] = None,
378
+ cert_file: Optional[str] = None,
379
+ key_file: Optional[str] = None,
380
+ ca_cert_file: Optional[str] = None,
381
+ crl_file: Optional[str] = None,
382
+ output_path: Optional[Path] = None,
383
+ ) -> Dict[str, Any]:
384
+ """
385
+ Generate HTTPS configuration with token authentication and roles.
386
+
387
+ Args:
388
+ host: Server host (default value)
389
+ port: Server port (default value)
390
+ api_key: API key for authentication (deprecated, use token)
391
+ token: Authentication token
392
+ tokens: Dictionary of tokens for different roles
393
+ roles: Dictionary of roles and their permissions
394
+ cert_file: Optional client certificate file
395
+ key_file: Optional client key file
396
+ ca_cert_file: Optional CA certificate file
397
+ crl_file: Optional CRL file
398
+ output_path: Optional output file path
399
+
400
+ Returns:
401
+ Complete configuration dictionary with all fields
402
+ """
403
+ tokens_dict = tokens or self.default_tokens
404
+ roles_dict = roles or self.default_roles
405
+ token_value = token or api_key or tokens_dict.get("admin", "admin-secret-key")
406
+ config = self._create_base_config(
407
+ host=host,
408
+ port=port,
409
+ protocol="https",
410
+ token=token_value,
411
+ cert_file=cert_file,
412
+ key_file=key_file,
413
+ ca_cert_file=ca_cert_file,
414
+ crl_file=crl_file,
415
+ )
416
+ config["auth"]["method"] = "api_key"
417
+ config["auth"]["api_key"]["key"] = token_value
418
+ config["security"]["enabled"] = True
419
+ config["security"]["tokens"] = tokens_dict
420
+ config["security"]["roles"] = roles_dict
421
+ if output_path:
422
+ self._save_config(config, output_path)
423
+ return config
424
+
425
+ def generate_mtls_config(
426
+ self,
427
+ host: str = "localhost",
428
+ port: int = 8001,
429
+ token: Optional[str] = None,
430
+ cert_file: Optional[str] = None,
431
+ key_file: Optional[str] = None,
432
+ ca_cert_file: Optional[str] = None,
433
+ crl_file: Optional[str] = None,
434
+ output_path: Optional[Path] = None,
435
+ ) -> Dict[str, Any]:
436
+ """
437
+ Generate mTLS configuration with client certificates.
438
+
439
+ Uses adapter SimpleConfigGenerator as base when available.
440
+ """
441
+ config = self._generate_via_adapter(
442
+ protocol="mtls",
443
+ host=host,
444
+ port=port,
445
+ cert_file=cert_file,
446
+ key_file=key_file,
447
+ ca_cert_file=ca_cert_file,
448
+ crl_file=crl_file,
449
+ token=token,
450
+ output_path=output_path,
451
+ )
452
+ if config is not None:
453
+ return config
454
+ config = self._create_base_config(
455
+ host=host,
456
+ port=port,
457
+ protocol="mtls",
458
+ token=token,
459
+ cert_file=cert_file,
460
+ key_file=key_file,
461
+ ca_cert_file=ca_cert_file,
462
+ crl_file=crl_file,
463
+ )
464
+ config["auth"]["method"] = "certificate"
465
+ if cert_file:
466
+ config["auth"]["certificate"]["cert_file"] = cert_file
467
+ if key_file:
468
+ config["auth"]["certificate"]["key_file"] = key_file
469
+ if ca_cert_file:
470
+ config["auth"]["certificate"]["ca_cert_file"] = ca_cert_file
471
+ if output_path:
472
+ self._save_config(config, output_path)
473
+ return config
474
+
475
+ def generate_mtls_roles_config(
476
+ self,
477
+ host: str = "localhost",
478
+ port: int = 8001,
479
+ token: Optional[str] = None,
480
+ cert_file: Optional[str] = None,
481
+ key_file: Optional[str] = None,
482
+ ca_cert_file: Optional[str] = None,
483
+ crl_file: Optional[str] = None,
484
+ roles: Optional[Dict[str, List[str]]] = None,
485
+ output_path: Optional[Path] = None,
486
+ ) -> Dict[str, Any]:
487
+ """
488
+ Generate mTLS configuration with client certificates and roles.
489
+
490
+ Args:
491
+ host: Server host (default value)
492
+ port: Server port (default value)
493
+ token: Optional token (not used for mTLS, but included in config)
494
+ cert_file: Client certificate file (required for mTLS)
495
+ key_file: Client key file (required for mTLS)
496
+ ca_cert_file: CA certificate file (required for mTLS)
497
+ crl_file: Optional CRL file
498
+ roles: Dictionary of roles and their permissions
499
+ output_path: Optional output file path
500
+
501
+ Returns:
502
+ Complete configuration dictionary with all fields
503
+ """
504
+ roles_dict = roles or self.default_roles
505
+ config = self._create_base_config(
506
+ host=host,
507
+ port=port,
508
+ protocol="mtls",
509
+ token=token,
510
+ cert_file=cert_file,
511
+ key_file=key_file,
512
+ ca_cert_file=ca_cert_file,
513
+ crl_file=crl_file,
514
+ )
515
+ config["auth"]["method"] = "certificate"
516
+ # For mTLS, certificates are required
517
+ if cert_file:
518
+ config["auth"]["certificate"]["cert_file"] = cert_file
519
+ if key_file:
520
+ config["auth"]["certificate"]["key_file"] = key_file
521
+ if ca_cert_file:
522
+ config["auth"]["certificate"]["ca_cert_file"] = ca_cert_file
523
+ config["security"]["enabled"] = True
524
+ config["security"]["roles"] = roles_dict
525
+ if output_path:
526
+ self._save_config(config, output_path)
527
+ return config
528
+
529
+ def generate_all_configs(
530
+ self,
531
+ host: str = "localhost",
532
+ port: int = 8001,
533
+ output_dir: Optional[Path] = None,
534
+ cert_file: Optional[str] = None,
535
+ key_file: Optional[str] = None,
536
+ ca_cert_file: Optional[str] = None,
537
+ crl_file: Optional[str] = None,
538
+ token: Optional[str] = None,
539
+ ) -> Dict[str, Dict[str, Any]]:
540
+ """
541
+ Generate all 8 security mode configurations.
542
+
543
+ Args:
544
+ host: Server host (default value for all configs)
545
+ port: Server port (default value for all configs)
546
+ output_dir: Optional output directory
547
+ cert_file: Optional client certificate file
548
+ key_file: Optional client key file
549
+ ca_cert_file: Optional CA certificate file
550
+ crl_file: Optional CRL file
551
+ token: Optional authentication token
552
+
553
+ Returns:
554
+ Dictionary of all generated configurations
555
+ """
556
+ configs = {}
557
+
558
+ # 1. HTTP
559
+ configs["http"] = self.generate_http_config(
560
+ host=host,
561
+ port=port,
562
+ token=token,
563
+ crl_file=crl_file,
564
+ output_path=output_dir / "http.json" if output_dir else None,
565
+ )
566
+
567
+ # 2. HTTP + Token
568
+ configs["http_token"] = self.generate_http_token_config(
569
+ host=host,
570
+ port=port,
571
+ token=token,
572
+ crl_file=crl_file,
573
+ output_path=output_dir / "http_token.json" if output_dir else None,
574
+ )
575
+
576
+ # 3. HTTP + Token + Roles
577
+ configs["http_token_roles"] = self.generate_http_token_roles_config(
578
+ host=host,
579
+ port=port,
580
+ token=token,
581
+ crl_file=crl_file,
582
+ output_path=output_dir / "http_token_roles.json" if output_dir else None,
583
+ )
584
+
585
+ # 4. HTTPS
586
+ configs["https"] = self.generate_https_config(
587
+ host=host,
588
+ port=port,
589
+ token=token,
590
+ cert_file=cert_file,
591
+ key_file=key_file,
592
+ ca_cert_file=ca_cert_file,
593
+ crl_file=crl_file,
594
+ output_path=output_dir / "https.json" if output_dir else None,
595
+ )
596
+
597
+ # 5. HTTPS + Token
598
+ configs["https_token"] = self.generate_https_token_config(
599
+ host=host,
600
+ port=port,
601
+ token=token,
602
+ cert_file=cert_file,
603
+ key_file=key_file,
604
+ ca_cert_file=ca_cert_file,
605
+ crl_file=crl_file,
606
+ output_path=output_dir / "https_token.json" if output_dir else None,
607
+ )
608
+
609
+ # 6. HTTPS + Token + Roles
610
+ configs["https_token_roles"] = self.generate_https_token_roles_config(
611
+ host=host,
612
+ port=port,
613
+ token=token,
614
+ cert_file=cert_file,
615
+ key_file=key_file,
616
+ ca_cert_file=ca_cert_file,
617
+ crl_file=crl_file,
618
+ output_path=output_dir / "https_token_roles.json" if output_dir else None,
619
+ )
620
+
621
+ # 7. mTLS
622
+ configs["mtls"] = self.generate_mtls_config(
623
+ host=host,
624
+ port=port,
625
+ token=token,
626
+ cert_file=cert_file,
627
+ key_file=key_file,
628
+ ca_cert_file=ca_cert_file,
629
+ crl_file=crl_file,
630
+ output_path=output_dir / "mtls.json" if output_dir else None,
631
+ )
632
+
633
+ # 8. mTLS + Roles
634
+ configs["mtls_roles"] = self.generate_mtls_roles_config(
635
+ host=host,
636
+ port=port,
637
+ token=token,
638
+ cert_file=cert_file,
639
+ key_file=key_file,
640
+ ca_cert_file=ca_cert_file,
641
+ crl_file=crl_file,
642
+ output_path=output_dir / "mtls_roles.json" if output_dir else None,
643
+ )
644
+
645
+ return configs
646
+
647
+ def _save_config(self, config: Dict[str, Any], output_path: Path) -> None:
648
+ """Save configuration to file."""
649
+ output_path.parent.mkdir(parents=True, exist_ok=True)
650
+ with open(output_path, "w", encoding="utf-8") as f:
651
+ json.dump(config, f, indent=2, ensure_ascii=False)
652
+ logger.info(f"Configuration saved to {output_path}")