refactor: Major upgrade to v5.1b6 - AWS SSM support & Distributed Architecture

Core & Protocols:
- Native AWS SSM support added (aws ssm start-session).
- Improved Pexpect logic for ssm, kubectl, and docker.
- Cleaned connection success messages (omitting ports for non-IP protocols).

gRPC Layer:
- Migrated gRPC modules to 'connpy/grpc_layer/'.
- Implemented dynamic node naming (e.g. ssm-i-xxxx@aws) for accurate server-side logging.
- Added automatic sys.path resolution for gRPC generated modules.
- Enhanced InteractNode response with initial connection status.

Printer & Concurrency:
- Implemented ThreadLocalStream for isolated thread-safe output.
- Self-healing Console objects to prevent 'closed file' errors in test/async environments.
- Capture clean plugin output in remote executions.

AI & Services:
- Improved tool registration and debug visualization.
- Restored native dictionary returns for AI tools to fix Web UI rendering.
- Increased backup retention to 100 copies in SyncService.
- Silenced noisy auto-sync CLI messages.

Quality & Docs:
- Total tests: 267 (all passing).
- New test suites for gRPC layer and printer concurrency.
- Updated .gitignore to exclude internal planning docs.
- Full technical documentation regenerated with pdoc.
This commit is contained in:
2026-04-24 19:23:00 -03:00
parent 287acde1e4
commit 1c814eb9fd
94 changed files with 12656 additions and 22613 deletions
+47 -26
View File
@@ -3,7 +3,7 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
<meta name="generator" content="pdoc3 0.11.5">
<meta name="generator" content="pdoc3 0.11.6">
<title>connpy.services.node_service API documentation</title>
<meta name="description" content="">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/13.0.0/sanitize.min.css" integrity="sha512-y1dtMcuvtTMJc1yPgEqF0ZjQbhnc/bFhyvIyVNb9Zk5mIGtqVaAB1Ttl28su8AvFMOY0EwRbAe+HCLqj6W7/KA==" crossorigin>
@@ -123,10 +123,13 @@ el.replaceWith(d);
def get_node_details(self, unique_id):
&#34;&#34;&#34;Return full configuration dictionary for a specific node.&#34;&#34;&#34;
details = self.config.getitem(unique_id)
if not details:
try:
details = self.config.getitem(unique_id)
if not details:
raise NodeNotFoundError(f&#34;Node &#39;{unique_id}&#39; not found.&#34;)
return details
except (KeyError, TypeError):
raise NodeNotFoundError(f&#34;Node &#39;{unique_id}&#39; not found.&#34;)
return details
def explode_unique(self, unique_id):
&#34;&#34;&#34;Explode a unique ID into a dictionary of its parts.&#34;&#34;&#34;
@@ -136,6 +139,14 @@ el.replaceWith(d);
&#34;&#34;&#34;Generate and update the internal nodes cache.&#34;&#34;&#34;
self.config._generate_nodes_cache(nodes=nodes, folders=folders, profiles=profiles)
def validate_parent_folder(self, unique_id):
&#34;&#34;&#34;Check if parent folder exists for a given node unique ID.&#34;&#34;&#34;
node_folder = unique_id.partition(&#34;@&#34;)[2]
if node_folder:
parent_folder = f&#34;@{node_folder}&#34;
if parent_folder not in self.config._getallfolders():
raise NodeNotFoundError(f&#34;Folder &#39;{parent_folder}&#39; not found.&#34;)
def add_node(self, unique_id, data, is_folder=False):
&#34;&#34;&#34;Logic for adding a new node or folder to configuration.&#34;&#34;&#34;
@@ -154,9 +165,7 @@ el.replaceWith(d);
# Check if parent folder exists when creating a subfolder
if &#34;subfolder&#34; in uniques:
parent_folder = f&#34;@{uniques[&#39;folder&#39;]}&#34;
if parent_folder not in all_folders:
raise NodeNotFoundError(f&#34;Folder &#39;{parent_folder}&#39; not found.&#34;)
self.validate_parent_folder(unique_id)
self.config._folder_add(**uniques)
self.config._saveconfig(self.config.file)
@@ -165,11 +174,7 @@ el.replaceWith(d);
raise NodeAlreadyExistsError(f&#34;Node &#39;{unique_id}&#39; already exists.&#34;)
# Check if parent folder exists when creating a node in a folder
node_folder = unique_id.partition(&#34;@&#34;)[2]
if node_folder:
parent_folder = f&#34;@{node_folder}&#34;
if parent_folder not in all_folders:
raise NodeNotFoundError(f&#34;Folder &#39;{parent_folder}&#39; not found.&#34;)
self.validate_parent_folder(unique_id)
# Ensure &#39;id&#39; is in data for config._connections_add
if &#34;id&#34; not in data:
@@ -342,9 +347,7 @@ el.replaceWith(d);
# Check if parent folder exists when creating a subfolder
if &#34;subfolder&#34; in uniques:
parent_folder = f&#34;@{uniques[&#39;folder&#39;]}&#34;
if parent_folder not in all_folders:
raise NodeNotFoundError(f&#34;Folder &#39;{parent_folder}&#39; not found.&#34;)
self.validate_parent_folder(unique_id)
self.config._folder_add(**uniques)
self.config._saveconfig(self.config.file)
@@ -353,11 +356,7 @@ el.replaceWith(d);
raise NodeAlreadyExistsError(f&#34;Node &#39;{unique_id}&#39; already exists.&#34;)
# Check if parent folder exists when creating a node in a folder
node_folder = unique_id.partition(&#34;@&#34;)[2]
if node_folder:
parent_folder = f&#34;@{node_folder}&#34;
if parent_folder not in all_folders:
raise NodeNotFoundError(f&#34;Folder &#39;{parent_folder}&#39; not found.&#34;)
self.validate_parent_folder(unique_id)
# Ensure &#39;id&#39; is in data for config._connections_add
if &#34;id&#34; not in data:
@@ -540,10 +539,13 @@ el.replaceWith(d);
</summary>
<pre><code class="python">def get_node_details(self, unique_id):
&#34;&#34;&#34;Return full configuration dictionary for a specific node.&#34;&#34;&#34;
details = self.config.getitem(unique_id)
if not details:
raise NodeNotFoundError(f&#34;Node &#39;{unique_id}&#39; not found.&#34;)
return details</code></pre>
try:
details = self.config.getitem(unique_id)
if not details:
raise NodeNotFoundError(f&#34;Node &#39;{unique_id}&#39; not found.&#34;)
return details
except (KeyError, TypeError):
raise NodeNotFoundError(f&#34;Node &#39;{unique_id}&#39; not found.&#34;)</code></pre>
</details>
<div class="desc"><p>Return full configuration dictionary for a specific node.</p></div>
</dd>
@@ -690,6 +692,24 @@ el.replaceWith(d);
</details>
<div class="desc"><p>Explicitly update an existing node.</p></div>
</dd>
<dt id="connpy.services.node_service.NodeService.validate_parent_folder"><code class="name flex">
<span>def <span class="ident">validate_parent_folder</span></span>(<span>self, unique_id)</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def validate_parent_folder(self, unique_id):
&#34;&#34;&#34;Check if parent folder exists for a given node unique ID.&#34;&#34;&#34;
node_folder = unique_id.partition(&#34;@&#34;)[2]
if node_folder:
parent_folder = f&#34;@{node_folder}&#34;
if parent_folder not in self.config._getallfolders():
raise NodeNotFoundError(f&#34;Folder &#39;{parent_folder}&#39; not found.&#34;)</code></pre>
</details>
<div class="desc"><p>Check if parent folder exists for a given node unique ID.</p></div>
</dd>
</dl>
<h3>Inherited members</h3>
<ul class="hlist">
@@ -717,7 +737,7 @@ el.replaceWith(d);
<ul>
<li>
<h4><code><a title="connpy.services.node_service.NodeService" href="#connpy.services.node_service.NodeService">NodeService</a></code></h4>
<ul class="two-column">
<ul class="">
<li><code><a title="connpy.services.node_service.NodeService.add_node" href="#connpy.services.node_service.NodeService.add_node">add_node</a></code></li>
<li><code><a title="connpy.services.node_service.NodeService.bulk_add" href="#connpy.services.node_service.NodeService.bulk_add">bulk_add</a></code></li>
<li><code><a title="connpy.services.node_service.NodeService.connect_node" href="#connpy.services.node_service.NodeService.connect_node">connect_node</a></code></li>
@@ -731,6 +751,7 @@ el.replaceWith(d);
<li><code><a title="connpy.services.node_service.NodeService.list_nodes" href="#connpy.services.node_service.NodeService.list_nodes">list_nodes</a></code></li>
<li><code><a title="connpy.services.node_service.NodeService.move_node" href="#connpy.services.node_service.NodeService.move_node">move_node</a></code></li>
<li><code><a title="connpy.services.node_service.NodeService.update_node" href="#connpy.services.node_service.NodeService.update_node">update_node</a></code></li>
<li><code><a title="connpy.services.node_service.NodeService.validate_parent_folder" href="#connpy.services.node_service.NodeService.validate_parent_folder">validate_parent_folder</a></code></li>
</ul>
</li>
</ul>
@@ -739,7 +760,7 @@ el.replaceWith(d);
</nav>
</main>
<footer id="footer">
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.5</a>.</p>
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.6</a>.</p>
</footer>
</body>
</html>