htcli 1.1.0__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 (140) hide show
  1. htcli-1.1.0.dist-info/METADATA +509 -0
  2. htcli-1.1.0.dist-info/RECORD +140 -0
  3. htcli-1.1.0.dist-info/WHEEL +4 -0
  4. htcli-1.1.0.dist-info/entry_points.txt +2 -0
  5. htcli-1.1.0.dist-info/licenses/LICENSE +21 -0
  6. src/__init__.py +0 -0
  7. src/htcli/__init__.py +5 -0
  8. src/htcli/client/__init__.py +338 -0
  9. src/htcli/client/extrinsics/__init__.py +26 -0
  10. src/htcli/client/extrinsics/base.py +487 -0
  11. src/htcli/client/extrinsics/consensus.py +79 -0
  12. src/htcli/client/extrinsics/governance.py +714 -0
  13. src/htcli/client/extrinsics/identity.py +490 -0
  14. src/htcli/client/extrinsics/node.py +1054 -0
  15. src/htcli/client/extrinsics/overwatch.py +401 -0
  16. src/htcli/client/extrinsics/staking.py +1504 -0
  17. src/htcli/client/extrinsics/subnet.py +2218 -0
  18. src/htcli/client/extrinsics/validator.py +203 -0
  19. src/htcli/client/extrinsics/wallet.py +323 -0
  20. src/htcli/client/offchain/__init__.py +10 -0
  21. src/htcli/client/offchain/backup.py +385 -0
  22. src/htcli/client/offchain/config.py +541 -0
  23. src/htcli/client/offchain/wallet.py +839 -0
  24. src/htcli/client/rpc/__init__.py +20 -0
  25. src/htcli/client/rpc/chain.py +568 -0
  26. src/htcli/client/rpc/node.py +783 -0
  27. src/htcli/client/rpc/overwatch.py +680 -0
  28. src/htcli/client/rpc/staking.py +216 -0
  29. src/htcli/client/rpc/subnet.py +2104 -0
  30. src/htcli/client/rpc/wallet.py +912 -0
  31. src/htcli/commands/__init__.py +31 -0
  32. src/htcli/commands/chain/__init__.py +66 -0
  33. src/htcli/commands/chain/display.py +204 -0
  34. src/htcli/commands/chain/handlers.py +260 -0
  35. src/htcli/commands/config/__init__.py +158 -0
  36. src/htcli/commands/config/display.py +353 -0
  37. src/htcli/commands/config/handlers.py +347 -0
  38. src/htcli/commands/config/prompts.py +357 -0
  39. src/htcli/commands/consensus/__init__.py +61 -0
  40. src/htcli/commands/consensus/handlers.py +100 -0
  41. src/htcli/commands/governance/__init__.py +49 -0
  42. src/htcli/commands/governance/handlers.py +81 -0
  43. src/htcli/commands/node/__init__.py +304 -0
  44. src/htcli/commands/node/display.py +749 -0
  45. src/htcli/commands/node/error_handling.py +470 -0
  46. src/htcli/commands/node/handlers.py +844 -0
  47. src/htcli/commands/node/prompts.py +346 -0
  48. src/htcli/commands/overwatch/__init__.py +219 -0
  49. src/htcli/commands/overwatch/display.py +396 -0
  50. src/htcli/commands/overwatch/error_handling.py +276 -0
  51. src/htcli/commands/overwatch/handlers.py +443 -0
  52. src/htcli/commands/overwatch/prompts.py +359 -0
  53. src/htcli/commands/stake/__init__.py +736 -0
  54. src/htcli/commands/stake/display.py +1103 -0
  55. src/htcli/commands/stake/error_handling.py +425 -0
  56. src/htcli/commands/stake/handlers.py +1902 -0
  57. src/htcli/commands/stake/prompts.py +1080 -0
  58. src/htcli/commands/subnet/__init__.py +639 -0
  59. src/htcli/commands/subnet/display.py +801 -0
  60. src/htcli/commands/subnet/error_handling.py +524 -0
  61. src/htcli/commands/subnet/handlers.py +2855 -0
  62. src/htcli/commands/subnet/prompts.py +1225 -0
  63. src/htcli/commands/validator/__init__.py +192 -0
  64. src/htcli/commands/validator/display.py +54 -0
  65. src/htcli/commands/validator/handlers.py +340 -0
  66. src/htcli/commands/wallet/__init__.py +546 -0
  67. src/htcli/commands/wallet/display.py +806 -0
  68. src/htcli/commands/wallet/error_handling.py +210 -0
  69. src/htcli/commands/wallet/handlers.py +3040 -0
  70. src/htcli/commands/wallet/prompts.py +1518 -0
  71. src/htcli/config.py +184 -0
  72. src/htcli/dependencies.py +186 -0
  73. src/htcli/errors/__init__.py +63 -0
  74. src/htcli/errors/base.py +141 -0
  75. src/htcli/errors/display.py +20 -0
  76. src/htcli/errors/handlers.py +710 -0
  77. src/htcli/main.py +343 -0
  78. src/htcli/models/__init__.py +21 -0
  79. src/htcli/models/enums/enum_types.py +35 -0
  80. src/htcli/models/errors.py +103 -0
  81. src/htcli/models/requests/__init__.py +197 -0
  82. src/htcli/models/requests/config.py +70 -0
  83. src/htcli/models/requests/consensus.py +19 -0
  84. src/htcli/models/requests/governance.py +38 -0
  85. src/htcli/models/requests/identity.py +51 -0
  86. src/htcli/models/requests/key.py +22 -0
  87. src/htcli/models/requests/node.py +91 -0
  88. src/htcli/models/requests/overwatch.py +64 -0
  89. src/htcli/models/requests/staking.py +580 -0
  90. src/htcli/models/requests/subnet.py +195 -0
  91. src/htcli/models/requests/validator.py +139 -0
  92. src/htcli/models/requests/wallet.py +118 -0
  93. src/htcli/models/responses/__init__.py +147 -0
  94. src/htcli/models/responses/base.py +18 -0
  95. src/htcli/models/responses/chain.py +39 -0
  96. src/htcli/models/responses/config.py +58 -0
  97. src/htcli/models/responses/identity.py +102 -0
  98. src/htcli/models/responses/overwatch.py +51 -0
  99. src/htcli/models/responses/staking.py +502 -0
  100. src/htcli/models/responses/subnet.py +856 -0
  101. src/htcli/models/responses/wallet.py +185 -0
  102. src/htcli/ui/__init__.py +87 -0
  103. src/htcli/ui/colors.py +309 -0
  104. src/htcli/ui/components/__init__.py +60 -0
  105. src/htcli/ui/components/panels.py +174 -0
  106. src/htcli/ui/components/progress.py +166 -0
  107. src/htcli/ui/components/spinners.py +92 -0
  108. src/htcli/ui/components/tables.py +809 -0
  109. src/htcli/ui/components/trees.py +721 -0
  110. src/htcli/ui/display.py +336 -0
  111. src/htcli/ui/prompts.py +870 -0
  112. src/htcli/utils/__init__.py +76 -0
  113. src/htcli/utils/blockchain/__init__.py +75 -0
  114. src/htcli/utils/blockchain/formatting.py +368 -0
  115. src/htcli/utils/blockchain/patches.py +286 -0
  116. src/htcli/utils/blockchain/peer_id.py +186 -0
  117. src/htcli/utils/blockchain/staking.py +448 -0
  118. src/htcli/utils/blockchain/type_registry.py +1373 -0
  119. src/htcli/utils/blockchain/validation.py +179 -0
  120. src/htcli/utils/cache.py +613 -0
  121. src/htcli/utils/constants.py +38 -0
  122. src/htcli/utils/legacy/__init__.py +12 -0
  123. src/htcli/utils/legacy/colors.py +311 -0
  124. src/htcli/utils/legacy/crypto.py +1176 -0
  125. src/htcli/utils/legacy/formatting.py +452 -0
  126. src/htcli/utils/legacy/interactive.py +306 -0
  127. src/htcli/utils/legacy/subnet_manifest.py +265 -0
  128. src/htcli/utils/legacy/validation.py +488 -0
  129. src/htcli/utils/logging.py +183 -0
  130. src/htcli/utils/network/__init__.py +20 -0
  131. src/htcli/utils/network/subnet.py +344 -0
  132. src/htcli/utils/prompts.py +27 -0
  133. src/htcli/utils/scale_codec.py +155 -0
  134. src/htcli/utils/validation/__init__.py +57 -0
  135. src/htcli/utils/validation/prompt_validators.py +267 -0
  136. src/htcli/utils/wallet/__init__.py +65 -0
  137. src/htcli/utils/wallet/auth.py +151 -0
  138. src/htcli/utils/wallet/core.py +1069 -0
  139. src/htcli/utils/wallet/crypto.py +1615 -0
  140. src/htcli/utils/wallet/migration.py +159 -0
@@ -0,0 +1,158 @@
1
+ """
2
+ Configuration management commands for the Hypertensor CLI.
3
+
4
+ Following the 3-step command pattern:
5
+ 1. Prompts - Collect user input
6
+ 2. Handlers - Execute business logic
7
+ 3. Display - Format and show results
8
+ """
9
+
10
+ from typing import Optional
11
+
12
+ import typer
13
+
14
+ from .handlers import (
15
+ edit_handler,
16
+ get_handler,
17
+ init_handler,
18
+ path_handler,
19
+ set_handler,
20
+ show_handler,
21
+ validate_handler,
22
+ )
23
+
24
+ app = typer.Typer(name="config", help="Configuration management")
25
+
26
+
27
+ @app.command()
28
+ def init(
29
+ config_file: Optional[str] = typer.Option(
30
+ None, "--config", "-c", help="Custom configuration file path"
31
+ ),
32
+ force: bool = typer.Option(
33
+ False, "--force", "-f", help="Overwrite existing configuration"
34
+ ),
35
+ endpoint: Optional[str] = typer.Option(
36
+ None, "--endpoint", "-e", help="RPC endpoint URL"
37
+ ),
38
+ ws_endpoint: Optional[str] = typer.Option(
39
+ None, "--ws-endpoint", "-w", help="WebSocket endpoint URL"
40
+ ),
41
+ timeout: Optional[int] = typer.Option(
42
+ None, "--timeout", "-t", help="Connection timeout in seconds"
43
+ ),
44
+ retry_attempts: Optional[int] = typer.Option(
45
+ None, "--retry-attempts", "-r", help="Number of retry attempts"
46
+ ),
47
+ output_format: Optional[str] = typer.Option(
48
+ None, "--output-format", "-o", help="Default output format (table/json/csv)"
49
+ ),
50
+ verbose: Optional[bool] = typer.Option(
51
+ None, "--verbose/--no-verbose", help="Enable verbose output"
52
+ ),
53
+ color: Optional[bool] = typer.Option(
54
+ None, "--color/--no-color", help="Enable colored output"
55
+ ),
56
+ wallet_path: Optional[str] = typer.Option(
57
+ None, "--wallet-path", "-p", help="Wallet storage path"
58
+ ),
59
+ default_wallet: Optional[str] = typer.Option(
60
+ None, "--default-wallet", "-d", help="Default wallet name"
61
+ ),
62
+ encryption_enabled: Optional[bool] = typer.Option(
63
+ None, "--encryption/--no-encryption", help="Enable wallet encryption"
64
+ ),
65
+ ):
66
+ """Initialize Hypertensor CLI configuration interactively."""
67
+ init_handler(
68
+ config_file=config_file,
69
+ force=force,
70
+ endpoint=endpoint,
71
+ ws_endpoint=ws_endpoint,
72
+ timeout=timeout,
73
+ retry_attempts=retry_attempts,
74
+ output_format=output_format,
75
+ verbose=verbose,
76
+ color=color,
77
+ wallet_path=wallet_path,
78
+ default_wallet=default_wallet,
79
+ encryption_enabled=encryption_enabled,
80
+ )
81
+
82
+
83
+ @app.command()
84
+ def show(
85
+ config_file: Optional[str] = typer.Option(
86
+ None, "--config", "-c", help="Custom configuration file path"
87
+ ),
88
+ format_type: str = typer.Option(
89
+ "table", "--format", "-f", help="Output format (table/yaml/json)"
90
+ ),
91
+ ):
92
+ """Show current configuration."""
93
+ show_handler(config_file=config_file, format_type=format_type)
94
+
95
+
96
+ @app.command()
97
+ def path(
98
+ config_file: Optional[str] = typer.Option(
99
+ None, "--config", "-c", help="Custom configuration file path"
100
+ ),
101
+ ):
102
+ """Show configuration file path."""
103
+ path_handler(config_file=config_file)
104
+
105
+
106
+ @app.command()
107
+ def edit(
108
+ config_file: Optional[str] = typer.Option(
109
+ None, "--config", "-c", help="Custom configuration file path"
110
+ ),
111
+ ):
112
+ """Edit configuration interactively in the terminal."""
113
+ edit_handler(config_file=config_file)
114
+
115
+
116
+ @app.command()
117
+ def validate(
118
+ config_file: Optional[str] = typer.Option(
119
+ None, "--config", "-c", help="Custom configuration file path"
120
+ ),
121
+ ):
122
+ """Validate configuration file."""
123
+ validate_handler(config_file=config_file)
124
+
125
+
126
+ @app.command()
127
+ def set(
128
+ key: Optional[str] = typer.Option(
129
+ None,
130
+ "--key",
131
+ "-k",
132
+ help="Configuration key (e.g., 'network.endpoint'). If omitted, you can select interactively.",
133
+ ),
134
+ value: Optional[str] = typer.Option(
135
+ None,
136
+ "--value",
137
+ "-v",
138
+ help="Value to set. If omitted, you will be prompted to enter a value.",
139
+ ),
140
+ config_file: Optional[str] = typer.Option(
141
+ None, "--config-file", "-c", help="Custom configuration file path"
142
+ ),
143
+ ):
144
+ """Set a specific configuration value."""
145
+ set_handler(key=key, value=value, config_file=config_file)
146
+
147
+
148
+ @app.command()
149
+ def get(
150
+ key: Optional[str] = typer.Option(
151
+ None, "--key", "-k", help="Configuration key (e.g., 'network.endpoint'). If omitted, you can select interactively."
152
+ ),
153
+ config_file: Optional[str] = typer.Option(
154
+ None, "--config-file", "-c", help="Custom configuration file path"
155
+ ),
156
+ ):
157
+ """Get a specific configuration value."""
158
+ get_handler(key=key, config_file=config_file)
@@ -0,0 +1,353 @@
1
+ """
2
+ Config command display logic.
3
+
4
+ Handles formatting and displaying results for configuration operations using HTCLI UI components.
5
+ """
6
+
7
+ from typing import Any
8
+
9
+ from ...models.responses import (
10
+ ConfigGetResponse,
11
+ ConfigInitResponse,
12
+ ConfigPathResponse,
13
+ ConfigSetResponse,
14
+ ConfigShowResponse,
15
+ ConfigValidateResponse,
16
+ )
17
+ from ...ui.colors import error, info, primary, success, warning
18
+ from ...ui.components import HTCLIPanel, HTCLITable
19
+ from ...ui.display import HTCLIConsole
20
+
21
+ console = HTCLIConsole()
22
+
23
+
24
+ def display_config_init_result(response: ConfigInitResponse) -> None:
25
+ """Display configuration initialization results."""
26
+ if response.created:
27
+ console.print(success("✅ Configuration created successfully!"))
28
+ else:
29
+ console.print(success("✅ Configuration updated successfully!"))
30
+
31
+ console.print()
32
+ console.print(f"[bold]Configuration file:[/bold] {response.config_path}")
33
+ console.print(f"[bold]Status:[/bold] {response.message}")
34
+
35
+ if response.created:
36
+ console.print()
37
+ console.print(info("💡 Next steps:"))
38
+ console.print(" • Use 'htcli config show' to view your configuration")
39
+ console.print(" • Use 'htcli config edit' to modify settings")
40
+ console.print(" • Use 'htcli wallet list' to check your wallets")
41
+
42
+
43
+ def display_config_show_result(response: ConfigShowResponse, format_type: str) -> None:
44
+ """Display configuration show results."""
45
+ console.print(f"[bold]Configuration file:[/bold] {response.config_path}")
46
+ console.print()
47
+
48
+ if not response.exists:
49
+ console.print(error("❌ Configuration file not found"))
50
+ console.print(info("💡 Run 'htcli config init' to create a configuration file"))
51
+ return
52
+
53
+ if format_type == "json":
54
+ console.print_json(data=response.config_data)
55
+ elif format_type == "yaml":
56
+ _display_config_as_yaml(response.config_data)
57
+ else: # table format
58
+ _display_config_as_table(response.config_data)
59
+
60
+
61
+ def _display_config_as_table(config_data: dict[str, Any]) -> None:
62
+ """Display configuration as formatted tables."""
63
+ # Network configuration table
64
+ if "network" in config_data:
65
+ network_table = HTCLITable(
66
+ title="[bold #60A5FA]🌐 Network Configuration[/bold #60A5FA]",
67
+ border_style="#60A5FA",
68
+ header_style="#60A5FA",
69
+ )
70
+ network_table.add_column("Setting", style="bold #60A5FA", width=25)
71
+ network_table.add_column("Value", width=50)
72
+
73
+ network = config_data["network"]
74
+ # Display all fields from default_config
75
+ # Alternate Value column colors: GRAY_200, GRAY_300, GRAY_200, GRAY_300
76
+ network_table.add_row(
77
+ "RPC Endpoint", f"[#E5E7EB]{network.get('endpoint', 'Not set')}[/]"
78
+ )
79
+ network_table.add_row(
80
+ "WebSocket Endpoint", f"[#D1D5DB]{network.get('ws_endpoint', 'Not set')}[/]"
81
+ )
82
+ network_table.add_row(
83
+ "Timeout", f"[#E5E7EB]{network.get('timeout', 'Not set')}s[/]"
84
+ )
85
+ network_table.add_row(
86
+ "Retry Attempts", f"[#D1D5DB]{network.get('retry_attempts', 'Not set')}[/]"
87
+ )
88
+ network_table.add_row(
89
+ "Chain Name", f"[#E5E7EB]{network.get('chain', 'Not set')}[/]"
90
+ )
91
+ network_table.render()
92
+ console.print() # Add spacing between tables
93
+
94
+ # Output configuration table
95
+ if "output" in config_data:
96
+ output_table = HTCLITable(
97
+ title="[bold #34D399]🎨 Output Configuration[/bold #34D399]",
98
+ border_style="#34D399",
99
+ header_style="#34D399",
100
+ )
101
+ output_table.add_column("Setting", style="bold #34D399", width=25)
102
+ output_table.add_column("Value", width=50)
103
+
104
+ output = config_data["output"]
105
+ # Only display fields that are prompted during config init
106
+ # Alternate Value column colors: GRAY_200, GRAY_300, GRAY_200, GRAY_300
107
+ output_table.add_row(
108
+ "Default Format", f"[#E5E7EB]{output.get('format', 'Not set')}[/]"
109
+ )
110
+ output_table.add_row(
111
+ "Verbose",
112
+ f"[#D1D5DB]{'Enabled' if output.get('verbose') else 'Disabled'}[/]",
113
+ )
114
+ output_table.add_row(
115
+ "Colors",
116
+ f"[#E5E7EB]{'Enabled' if output.get('color') else 'Disabled'}[/]",
117
+ )
118
+ output_table.render()
119
+ console.print() # Add spacing between tables
120
+ # Legacy display section support (backward compatibility)
121
+ elif "display" in config_data:
122
+ display_table = HTCLITable(
123
+ title="[bold #34D399]🎨 Display Configuration[/bold #34D399]",
124
+ border_style="#34D399",
125
+ header_style="#34D399",
126
+ )
127
+ display_table.add_column("Setting", style="bold #34D399", width=25)
128
+ display_table.add_column("Value", width=50)
129
+
130
+ display = config_data["display"]
131
+ # Alternate Value column colors: GRAY_200, GRAY_300, GRAY_200, GRAY_300
132
+ display_table.add_row(
133
+ "Default Format", f"[#E5E7EB]{display.get('format', 'Not set')}[/]"
134
+ )
135
+ display_table.add_row(
136
+ "Verbose",
137
+ f"[#D1D5DB]{'Enabled' if display.get('verbose') else 'Disabled'}[/]",
138
+ )
139
+ display_table.add_row(
140
+ "Colors",
141
+ f"[#E5E7EB]{'Enabled' if display.get('colors') else 'Disabled'}[/]",
142
+ )
143
+ display_table.add_row(
144
+ "Show Guidance",
145
+ f"[#D1D5DB]{'Enabled' if display.get('show_guidance') else 'Disabled'}[/]",
146
+ )
147
+ display_table.render()
148
+ console.print() # Add spacing between tables
149
+
150
+ # Wallet configuration table
151
+ if "wallet" in config_data:
152
+ wallet_table = HTCLITable(
153
+ title="[bold #FBBF24]💰 Wallet Configuration[/bold #FBBF24]",
154
+ border_style="#FBBF24",
155
+ header_style="#FBBF24",
156
+ )
157
+ wallet_table.add_column("Setting", style="bold #FBBF24", width=25)
158
+ wallet_table.add_column("Value", width=50)
159
+
160
+ wallet = config_data["wallet"]
161
+ # Only display fields that are prompted during config init
162
+ # Alternate Value column colors: GRAY_200, GRAY_300, GRAY_200
163
+ wallet_table.add_row(
164
+ "Wallet Path", f"[#E5E7EB]{wallet.get('path', 'Not set')}[/]"
165
+ )
166
+ wallet_table.add_row(
167
+ "Default Wallet", f"[#D1D5DB]{wallet.get('default_name', wallet.get('default_wallet', 'Not set'))}[/]"
168
+ )
169
+ wallet_table.add_row(
170
+ "Encryption Enabled",
171
+ f"[#E5E7EB]{'Enabled' if wallet.get('encryption_enabled') else 'Disabled'}[/]",
172
+ )
173
+ wallet_table.render()
174
+ console.print() # Add spacing between tables
175
+
176
+ # Security configuration table (legacy support for old configs)
177
+ if "security" in config_data:
178
+ security_table = HTCLITable(
179
+ title="[bold #F87171]🔒 Security Configuration[/bold #F87171]",
180
+ border_style="#F87171",
181
+ header_style="#F87171",
182
+ )
183
+ security_table.add_column("Setting", style="bold #F87171", width=25)
184
+ security_table.add_column("Value", width=50)
185
+
186
+ security = config_data["security"]
187
+ # Alternate Value column colors: GRAY_200, GRAY_300, GRAY_200
188
+ security_table.add_row(
189
+ "Password Timeout",
190
+ f"[#E5E7EB]{security.get('password_timeout', 'Not set')}s[/]",
191
+ )
192
+ security_table.add_row(
193
+ "Require Confirmation",
194
+ f"[#D1D5DB]{'Enabled' if security.get('require_confirmation') else 'Disabled'}[/]",
195
+ )
196
+ security_table.add_row(
197
+ "Log Transactions",
198
+ f"[#E5E7EB]{'Enabled' if security.get('log_transactions') else 'Disabled'}[/]",
199
+ )
200
+ security_table.render()
201
+
202
+
203
+ def _display_config_as_yaml(config_data: dict[str, Any]) -> None:
204
+ """Display configuration as formatted YAML."""
205
+ import yaml
206
+
207
+ # Format as YAML with comments
208
+ yaml_content = yaml.dump(
209
+ config_data, default_flow_style=False, indent=2, sort_keys=True
210
+ )
211
+
212
+ # Add syntax highlighting if possible
213
+ try:
214
+ from rich.syntax import Syntax
215
+
216
+ syntax = Syntax(yaml_content, "yaml", theme="monokai", line_numbers=True)
217
+ console.print(syntax)
218
+ except ImportError:
219
+ console.print(yaml_content)
220
+
221
+
222
+ def display_config_set_result(response: ConfigSetResponse) -> None:
223
+ """Display configuration set results."""
224
+ console.print(success(f"✅ Configuration updated: {response.key}"))
225
+ console.print()
226
+
227
+ # Create comparison table
228
+ table = HTCLITable(title="Configuration Change")
229
+ table.add_column("Key", style="htcli.value")
230
+ table.add_column("Old Value", style="htcli.error")
231
+ table.add_column("New Value", style="htcli.success")
232
+
233
+ table.add_row(response.key, str(response.old_value), str(response.new_value))
234
+
235
+ table.render()
236
+
237
+ console.print()
238
+ console.print(f"[bold]Configuration saved to:[/bold] {response.config_path}")
239
+ console.print(f"[bold]Status:[/bold] {response.message}")
240
+
241
+
242
+ def display_config_get_result(response: ConfigGetResponse) -> None:
243
+ """Display configuration get results."""
244
+ panel = HTCLIPanel(
245
+ f"[bold #60A5FA]Configuration Value[/bold #60A5FA]\n\n"
246
+ f"[bold]Key:[/bold] {response.key}\n"
247
+ f"[bold]Type:[/bold] {response.value_type}\n"
248
+ f"[bold]Value:[/bold] {response.value}\n\n"
249
+ f"{info('💡 Tip:')} Use 'htcli config set --key {response.key} --value <new_value>' to change this value",
250
+ title="Configuration Get",
251
+ )
252
+ panel.render()
253
+
254
+
255
+ def display_config_validate_result(response: ConfigValidateResponse) -> None:
256
+ """Display configuration validation results."""
257
+ if response.valid:
258
+ console.print(success("✅ Configuration file is valid!"))
259
+ console.print(
260
+ f"Configuration loaded successfully from: [bold]{response.config_path}[/bold]"
261
+ )
262
+ else:
263
+ console.print(error("❌ Configuration file is invalid!"))
264
+ console.print()
265
+
266
+ if response.errors:
267
+ console.print(error("Validation errors:"))
268
+ for error_msg in response.errors:
269
+ console.print(f" • {error_msg}")
270
+
271
+ console.print()
272
+ console.print(f"[bold]Status:[/bold] {response.message}")
273
+
274
+
275
+ def display_config_path_result(response: ConfigPathResponse) -> None:
276
+ """Display configuration path results."""
277
+ console.print(f"Configuration file path: [bold]{response.config_path}[/bold]")
278
+ console.print(f"Absolute path: [bold]{response.absolute_path}[/bold]")
279
+
280
+ if response.exists:
281
+ console.print(success("✅ File exists"))
282
+ else:
283
+ console.print(warning("⚠️ File does not exist"))
284
+ console.print()
285
+ console.print(
286
+ info("💡 Run 'htcli config init' to create the configuration file")
287
+ )
288
+
289
+
290
+ def display_config_changes_summary(
291
+ old_config: dict[str, Any], new_config: dict[str, Any]
292
+ ) -> None:
293
+ """Display configuration changes summary."""
294
+ console.print(primary("📋 Configuration Changes Summary"))
295
+ console.print()
296
+
297
+ changes_found = False
298
+
299
+ # Check network changes
300
+ if "network" in old_config and "network" in new_config:
301
+ network_changes = _get_section_changes(
302
+ "Network", old_config["network"], new_config["network"]
303
+ )
304
+ if network_changes:
305
+ changes_found = True
306
+ network_changes.render()
307
+
308
+ # Check output changes
309
+ if "output" in old_config and "output" in new_config:
310
+ output_changes = _get_section_changes(
311
+ "Output", old_config["output"], new_config["output"]
312
+ )
313
+ if output_changes:
314
+ changes_found = True
315
+ output_changes.render()
316
+
317
+ # Check wallet changes
318
+ if "wallet" in old_config and "wallet" in new_config:
319
+ wallet_changes = _get_section_changes(
320
+ "Wallet", old_config["wallet"], new_config["wallet"]
321
+ )
322
+ if wallet_changes:
323
+ changes_found = True
324
+ wallet_changes.render()
325
+
326
+ if not changes_found:
327
+ console.print(info("No changes detected"))
328
+
329
+
330
+ def _get_section_changes(
331
+ section_name: str, old_section: dict[str, Any], new_section: dict[str, Any]
332
+ ) -> HTCLITable:
333
+ """Get changes for a configuration section."""
334
+ table = HTCLITable(title=f"{section_name} Configuration Changes")
335
+ table.add_column("Setting", style="htcli.value")
336
+ table.add_column("Old Value", style="htcli.error")
337
+ table.add_column("New Value", style="htcli.success")
338
+ table.add_column("Changed", style="htcli.warning")
339
+
340
+ has_changes = False
341
+ for key in set(old_section.keys()) | set(new_section.keys()):
342
+ old_value = old_section.get(key, "Not set")
343
+ new_value = new_section.get(key, "Not set")
344
+ changed = "Yes" if old_value != new_value else "No"
345
+
346
+ if changed == "Yes":
347
+ has_changes = True
348
+
349
+ table.add_row(
350
+ key.title().replace("_", " "), str(old_value), str(new_value), changed
351
+ )
352
+
353
+ return table if has_changes else None