mirrorneuron-cli 1.1.4__tar.gz → 1.2.5__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 (105) hide show
  1. {mirrorneuron_cli-1.1.4 → mirrorneuron_cli-1.2.5}/.github/workflows/ci.yml +2 -3
  2. {mirrorneuron_cli-1.1.4 → mirrorneuron_cli-1.2.5}/.github/workflows/release.yml +2 -13
  3. {mirrorneuron_cli-1.1.4 → mirrorneuron_cli-1.2.5}/.gitignore +17 -1
  4. mirrorneuron_cli-1.2.5/.python-version +1 -0
  5. mirrorneuron_cli-1.2.5/AGENTS.md +10 -0
  6. mirrorneuron_cli-1.2.5/PKG-INFO +53 -0
  7. mirrorneuron_cli-1.2.5/README.md +38 -0
  8. mirrorneuron_cli-1.2.5/mirrorneuron_cli.egg-info/PKG-INFO +53 -0
  9. {mirrorneuron_cli-1.1.4 → mirrorneuron_cli-1.2.5}/mirrorneuron_cli.egg-info/SOURCES.txt +41 -1
  10. {mirrorneuron_cli-1.1.4 → mirrorneuron_cli-1.2.5}/mirrorneuron_cli.egg-info/requires.txt +1 -0
  11. mirrorneuron_cli-1.2.5/mn_cli/banner.py +13 -0
  12. mirrorneuron_cli-1.2.5/mn_cli/config.py +38 -0
  13. {mirrorneuron_cli-1.1.4 → mirrorneuron_cli-1.2.5}/mn_cli/error_handler.py +19 -0
  14. mirrorneuron_cli-1.2.5/mn_cli/libs/artifacts.py +139 -0
  15. mirrorneuron_cli-1.2.5/mn_cli/libs/backup_cmds.py +574 -0
  16. mirrorneuron_cli-1.2.5/mn_cli/libs/blueprint_cmds.py +1859 -0
  17. mirrorneuron_cli-1.2.5/mn_cli/libs/blueprint_models.py +118 -0
  18. {mirrorneuron_cli-1.1.4 → mirrorneuron_cli-1.2.5}/mn_cli/libs/blueprint_observability.py +44 -14
  19. {mirrorneuron_cli-1.1.4 → mirrorneuron_cli-1.2.5}/mn_cli/libs/blueprint_repository.py +25 -16
  20. mirrorneuron_cli-1.2.5/mn_cli/libs/blueprint_resources.py +902 -0
  21. mirrorneuron_cli-1.2.5/mn_cli/libs/bundles.py +40 -0
  22. mirrorneuron_cli-1.2.5/mn_cli/libs/deployment_cmds.py +183 -0
  23. mirrorneuron_cli-1.2.5/mn_cli/libs/event_relay.py +41 -0
  24. mirrorneuron_cli-1.2.5/mn_cli/libs/job_cmds.py +624 -0
  25. mirrorneuron_cli-1.2.5/mn_cli/libs/model_cmds.py +542 -0
  26. mirrorneuron_cli-1.2.5/mn_cli/libs/resource_cmds.py +194 -0
  27. mirrorneuron_cli-1.2.5/mn_cli/libs/run_cmds.py +3230 -0
  28. {mirrorneuron_cli-1.1.4 → mirrorneuron_cli-1.2.5}/mn_cli/libs/run_logs.py +38 -5
  29. mirrorneuron_cli-1.2.5/mn_cli/libs/run_manifest.py +1131 -0
  30. mirrorneuron_cli-1.2.5/mn_cli/libs/runtime_health.py +322 -0
  31. mirrorneuron_cli-1.2.5/mn_cli/libs/schedule_cmds.py +290 -0
  32. mirrorneuron_cli-1.2.5/mn_cli/libs/service_cmds.py +121 -0
  33. mirrorneuron_cli-1.2.5/mn_cli/libs/skill_runtime.py +668 -0
  34. mirrorneuron_cli-1.2.5/mn_cli/libs/sys_cmds.py +354 -0
  35. mirrorneuron_cli-1.2.5/mn_cli/libs/ui.py +637 -0
  36. mirrorneuron_cli-1.2.5/mn_cli/libs/workflow_progress.py +318 -0
  37. mirrorneuron_cli-1.2.5/mn_cli/libs/workflow_validation.py +677 -0
  38. mirrorneuron_cli-1.2.5/mn_cli/main.py +169 -0
  39. mirrorneuron_cli-1.2.5/mn_cli/runtime_mode.py +66 -0
  40. mirrorneuron_cli-1.2.5/mn_cli/runtime_state.py +135 -0
  41. mirrorneuron_cli-1.2.5/mn_cli/schemas/workflow_manifest.schema.json +261 -0
  42. mirrorneuron_cli-1.2.5/mn_cli/sdk_path.py +12 -0
  43. mirrorneuron_cli-1.2.5/mn_cli/server_cmds.py +3539 -0
  44. mirrorneuron_cli-1.2.5/mn_cli/shared.py +56 -0
  45. mirrorneuron_cli-1.2.5/mn_cli/terminal.py +25 -0
  46. {mirrorneuron_cli-1.1.4 → mirrorneuron_cli-1.2.5}/mn_cli/update_cmds.py +138 -69
  47. {mirrorneuron_cli-1.1.4 → mirrorneuron_cli-1.2.5}/pyproject.toml +4 -0
  48. mirrorneuron_cli-1.2.5/tests/test_backup_cmds.py +208 -0
  49. mirrorneuron_cli-1.2.5/tests/test_blueprint_cmds.py +1268 -0
  50. {mirrorneuron_cli-1.1.4 → mirrorneuron_cli-1.2.5}/tests/test_blueprint_repository.py +6 -3
  51. mirrorneuron_cli-1.2.5/tests/test_blueprint_resources.py +251 -0
  52. mirrorneuron_cli-1.2.5/tests/test_deployment_cmds.py +79 -0
  53. mirrorneuron_cli-1.2.5/tests/test_docker_network_integration.py +101 -0
  54. mirrorneuron_cli-1.2.5/tests/test_job_cmds.py +57 -0
  55. mirrorneuron_cli-1.2.5/tests/test_main.py +178 -0
  56. mirrorneuron_cli-1.2.5/tests/test_model_cmds.py +356 -0
  57. mirrorneuron_cli-1.2.5/tests/test_resource_cmds.py +52 -0
  58. mirrorneuron_cli-1.2.5/tests/test_run_cmds.py +2283 -0
  59. mirrorneuron_cli-1.2.5/tests/test_run_helpers.py +1092 -0
  60. mirrorneuron_cli-1.2.5/tests/test_runtime_health.py +329 -0
  61. mirrorneuron_cli-1.2.5/tests/test_runtime_mode.py +57 -0
  62. mirrorneuron_cli-1.2.5/tests/test_runtime_state.py +84 -0
  63. mirrorneuron_cli-1.2.5/tests/test_schedule_cmds.py +100 -0
  64. mirrorneuron_cli-1.2.5/tests/test_server_cmds.py +2646 -0
  65. mirrorneuron_cli-1.2.5/tests/test_service_cmds.py +89 -0
  66. mirrorneuron_cli-1.2.5/tests/test_shared.py +242 -0
  67. mirrorneuron_cli-1.2.5/tests/test_sys_cmds.py +161 -0
  68. mirrorneuron_cli-1.2.5/tests/test_terminal.py +32 -0
  69. mirrorneuron_cli-1.2.5/tests/test_ui.py +49 -0
  70. mirrorneuron_cli-1.2.5/tests/test_update_cmds.py +273 -0
  71. mirrorneuron_cli-1.2.5/tests/test_workflow_validation.py +95 -0
  72. mirrorneuron_cli-1.2.5/uv.lock +393 -0
  73. mirrorneuron_cli-1.1.4/PKG-INFO +0 -202
  74. mirrorneuron_cli-1.1.4/README.md +0 -188
  75. mirrorneuron_cli-1.1.4/mirrorneuron_cli.egg-info/PKG-INFO +0 -202
  76. mirrorneuron_cli-1.1.4/mn_cli/config.py +0 -43
  77. mirrorneuron_cli-1.1.4/mn_cli/libs/blueprint_cmds.py +0 -569
  78. mirrorneuron_cli-1.1.4/mn_cli/libs/job_cmds.py +0 -225
  79. mirrorneuron_cli-1.1.4/mn_cli/libs/run_cmds.py +0 -541
  80. mirrorneuron_cli-1.1.4/mn_cli/libs/run_manifest.py +0 -253
  81. mirrorneuron_cli-1.1.4/mn_cli/libs/sys_cmds.py +0 -51
  82. mirrorneuron_cli-1.1.4/mn_cli/libs/ui.py +0 -162
  83. mirrorneuron_cli-1.1.4/mn_cli/main.py +0 -43
  84. mirrorneuron_cli-1.1.4/mn_cli/server_cmds.py +0 -344
  85. mirrorneuron_cli-1.1.4/mn_cli/shared.py +0 -13
  86. mirrorneuron_cli-1.1.4/tests/test_blueprint_cmds.py +0 -637
  87. mirrorneuron_cli-1.1.4/tests/test_job_cmds.py +0 -225
  88. mirrorneuron_cli-1.1.4/tests/test_run_cmds.py +0 -446
  89. mirrorneuron_cli-1.1.4/tests/test_run_helpers.py +0 -155
  90. mirrorneuron_cli-1.1.4/tests/test_server_cmds.py +0 -291
  91. mirrorneuron_cli-1.1.4/tests/test_sys_cmds.py +0 -89
  92. mirrorneuron_cli-1.1.4/tests/test_update_cmds.py +0 -113
  93. {mirrorneuron_cli-1.1.4 → mirrorneuron_cli-1.2.5}/LICENSE +0 -0
  94. {mirrorneuron_cli-1.1.4 → mirrorneuron_cli-1.2.5}/RELEASE.md +0 -0
  95. {mirrorneuron_cli-1.1.4 → mirrorneuron_cli-1.2.5}/mirrorneuron_cli.egg-info/dependency_links.txt +0 -0
  96. {mirrorneuron_cli-1.1.4 → mirrorneuron_cli-1.2.5}/mirrorneuron_cli.egg-info/entry_points.txt +0 -0
  97. {mirrorneuron_cli-1.1.4 → mirrorneuron_cli-1.2.5}/mirrorneuron_cli.egg-info/top_level.txt +0 -0
  98. {mirrorneuron_cli-1.1.4 → mirrorneuron_cli-1.2.5}/mn_cli/__init__.py +0 -0
  99. {mirrorneuron_cli-1.1.4 → mirrorneuron_cli-1.2.5}/mn_cli/libs/__init__.py +0 -0
  100. {mirrorneuron_cli-1.1.4 → mirrorneuron_cli-1.2.5}/mn_cli/logging_config.py +0 -0
  101. {mirrorneuron_cli-1.1.4 → mirrorneuron_cli-1.2.5}/scripts/check-release-artifacts.sh +0 -0
  102. {mirrorneuron_cli-1.1.4 → mirrorneuron_cli-1.2.5}/scripts/make-release-zip.sh +0 -0
  103. {mirrorneuron_cli-1.1.4 → mirrorneuron_cli-1.2.5}/scripts/validate-version-tag.sh +0 -0
  104. {mirrorneuron_cli-1.1.4 → mirrorneuron_cli-1.2.5}/setup.cfg +0 -0
  105. {mirrorneuron_cli-1.1.4 → mirrorneuron_cli-1.2.5}/tests/conftest.py +0 -0
@@ -1,10 +1,9 @@
1
1
  name: CI
2
2
 
3
3
  on:
4
- pull_request:
5
4
  push:
6
- branches:
7
- - main
5
+ tags:
6
+ - "v[0-9]*.[0-9]*.[0-9]*"
8
7
 
9
8
  permissions:
10
9
  contents: read
@@ -3,13 +3,7 @@ name: Release
3
3
  on:
4
4
  push:
5
5
  tags:
6
- - "v*.*.*"
7
- workflow_dispatch:
8
- inputs:
9
- tag:
10
- description: "Existing release tag to publish, such as v1.0.1 or v1.0.1-rc.1"
11
- required: true
12
- type: string
6
+ - "v[0-9]*.[0-9]*.[0-9]*"
13
7
 
14
8
  permissions:
15
9
  contents: read
@@ -31,19 +25,14 @@ jobs:
31
25
  uses: actions/checkout@v4
32
26
  with:
33
27
  fetch-depth: 0
34
- ref: ${{ github.event_name == 'workflow_dispatch' && inputs.tag || github.ref }}
28
+ ref: ${{ github.ref }}
35
29
 
36
30
  - name: Validate release tag
37
31
  id: version
38
32
  shell: bash
39
- env:
40
- INPUT_TAG: ${{ inputs.tag }}
41
33
  run: |
42
34
  set -euo pipefail
43
35
  tag="$GITHUB_REF_NAME"
44
- if [[ "$GITHUB_EVENT_NAME" == "workflow_dispatch" ]]; then
45
- tag="$INPUT_TAG"
46
- fi
47
36
  scripts/validate-version-tag.sh "$tag"
48
37
 
49
38
  - name: Detect project type
@@ -1,6 +1,9 @@
1
1
  # Environments
2
2
  .env
3
+ .env.*
4
+ !.env.example
3
5
  .venv
6
+ .venv/
4
7
  env/
5
8
  venv/
6
9
  ENV/
@@ -30,5 +33,18 @@ __pycache__/
30
33
  *.py[cod]
31
34
  *$py.class
32
35
  .pytest_cache/
36
+ .ruff_cache/
37
+ .mypy_cache/
38
+ .pyre/
39
+ .hypothesis/
40
+ .tox/
41
+ .nox/
33
42
  .coverage
34
- htmlcov/
43
+ .coverage.*
44
+ coverage.xml
45
+ htmlcov/
46
+
47
+ # Local editor and OS files
48
+ .DS_Store
49
+ .idea/
50
+ .vscode/
@@ -0,0 +1 @@
1
+ 3.11.15
@@ -0,0 +1,10 @@
1
+ # AGENTS.md
2
+
3
+ Guidance for future coding agents working in this repository.
4
+
5
+ ## Issue Fixing Policy
6
+
7
+ - Unless the user explicitly asks for a temporary workaround, fix the root cause in the intended layer or contract.
8
+ - Avoid adding fallback paths, compatibility shims, feature flags, or temp solutions that mask a broken primary path.
9
+ - If fallback behavior is already product-specified, keep it narrow, documented, and tested; do not use it to avoid fixing the primary path.
10
+
@@ -0,0 +1,53 @@
1
+ Metadata-Version: 2.4
2
+ Name: mirrorneuron-cli
3
+ Version: 1.2.5
4
+ Summary: MirrorNeuron CLI
5
+ License-Expression: MIT
6
+ Classifier: Programming Language :: Python :: 3
7
+ Requires-Python: >=3.11
8
+ Description-Content-Type: text/markdown
9
+ License-File: LICENSE
10
+ Requires-Dist: mirrorneuron-python-sdk
11
+ Requires-Dist: jsonschema>=4.25
12
+ Requires-Dist: typer>=0.9.0
13
+ Requires-Dist: rich>=13.0.0
14
+ Dynamic: license-file
15
+
16
+ # MirrorNeuron CLI
17
+
18
+ `mn-cli` provides the `mn` command for validating and running blueprints,
19
+ inspecting runtime state, managing jobs, exporting artifacts, and starting local
20
+ services installed by `mn-deploy`.
21
+
22
+ ## Quick Start
23
+
24
+ Install locally and run tests:
25
+
26
+ ```bash
27
+ python3.11 -m venv .venv
28
+ . .venv/bin/activate
29
+ .venv/bin/python -m pip install -e .
30
+ .venv/bin/python -m pytest -q
31
+ ```
32
+
33
+ Try the CLI:
34
+
35
+ ```bash
36
+ mn --version
37
+ mn node list
38
+ mn blueprint run message_routing_trace
39
+ ```
40
+
41
+ ## Details
42
+
43
+ - [MirrorNeuron Component Guide](../mn-docs/component-guide.md#cli)
44
+ - [CLI Reference](../mn-docs/cli.md)
45
+ - [Environment Variables](../mn-docs/env_variables.md)
46
+ - [Monitor Guide](../mn-docs/monitor.md)
47
+
48
+ ## Notes
49
+
50
+ - A running MirrorNeuron core is required for live runtime commands.
51
+ - The default gRPC target comes from `MN_GRPC_TARGET`, then local deployment
52
+ settings, then `localhost:55051`.
53
+ - Use `mn blueprint validate` before `mn blueprint run --folder` when checking a local bundle.
@@ -0,0 +1,38 @@
1
+ # MirrorNeuron CLI
2
+
3
+ `mn-cli` provides the `mn` command for validating and running blueprints,
4
+ inspecting runtime state, managing jobs, exporting artifacts, and starting local
5
+ services installed by `mn-deploy`.
6
+
7
+ ## Quick Start
8
+
9
+ Install locally and run tests:
10
+
11
+ ```bash
12
+ python3.11 -m venv .venv
13
+ . .venv/bin/activate
14
+ .venv/bin/python -m pip install -e .
15
+ .venv/bin/python -m pytest -q
16
+ ```
17
+
18
+ Try the CLI:
19
+
20
+ ```bash
21
+ mn --version
22
+ mn node list
23
+ mn blueprint run message_routing_trace
24
+ ```
25
+
26
+ ## Details
27
+
28
+ - [MirrorNeuron Component Guide](../mn-docs/component-guide.md#cli)
29
+ - [CLI Reference](../mn-docs/cli.md)
30
+ - [Environment Variables](../mn-docs/env_variables.md)
31
+ - [Monitor Guide](../mn-docs/monitor.md)
32
+
33
+ ## Notes
34
+
35
+ - A running MirrorNeuron core is required for live runtime commands.
36
+ - The default gRPC target comes from `MN_GRPC_TARGET`, then local deployment
37
+ settings, then `localhost:55051`.
38
+ - Use `mn blueprint validate` before `mn blueprint run --folder` when checking a local bundle.
@@ -0,0 +1,53 @@
1
+ Metadata-Version: 2.4
2
+ Name: mirrorneuron-cli
3
+ Version: 1.2.5
4
+ Summary: MirrorNeuron CLI
5
+ License-Expression: MIT
6
+ Classifier: Programming Language :: Python :: 3
7
+ Requires-Python: >=3.11
8
+ Description-Content-Type: text/markdown
9
+ License-File: LICENSE
10
+ Requires-Dist: mirrorneuron-python-sdk
11
+ Requires-Dist: jsonschema>=4.25
12
+ Requires-Dist: typer>=0.9.0
13
+ Requires-Dist: rich>=13.0.0
14
+ Dynamic: license-file
15
+
16
+ # MirrorNeuron CLI
17
+
18
+ `mn-cli` provides the `mn` command for validating and running blueprints,
19
+ inspecting runtime state, managing jobs, exporting artifacts, and starting local
20
+ services installed by `mn-deploy`.
21
+
22
+ ## Quick Start
23
+
24
+ Install locally and run tests:
25
+
26
+ ```bash
27
+ python3.11 -m venv .venv
28
+ . .venv/bin/activate
29
+ .venv/bin/python -m pip install -e .
30
+ .venv/bin/python -m pytest -q
31
+ ```
32
+
33
+ Try the CLI:
34
+
35
+ ```bash
36
+ mn --version
37
+ mn node list
38
+ mn blueprint run message_routing_trace
39
+ ```
40
+
41
+ ## Details
42
+
43
+ - [MirrorNeuron Component Guide](../mn-docs/component-guide.md#cli)
44
+ - [CLI Reference](../mn-docs/cli.md)
45
+ - [Environment Variables](../mn-docs/env_variables.md)
46
+ - [Monitor Guide](../mn-docs/monitor.md)
47
+
48
+ ## Notes
49
+
50
+ - A running MirrorNeuron core is required for live runtime commands.
51
+ - The default gRPC target comes from `MN_GRPC_TARGET`, then local deployment
52
+ settings, then `localhost:55051`.
53
+ - Use `mn blueprint validate` before `mn blueprint run --folder` when checking a local bundle.
@@ -1,8 +1,11 @@
1
1
  .gitignore
2
+ .python-version
3
+ AGENTS.md
2
4
  LICENSE
3
5
  README.md
4
6
  RELEASE.md
5
7
  pyproject.toml
8
+ uv.lock
6
9
  .github/workflows/ci.yml
7
10
  .github/workflows/release.yml
8
11
  mirrorneuron_cli.egg-info/PKG-INFO
@@ -12,32 +15,69 @@ mirrorneuron_cli.egg-info/entry_points.txt
12
15
  mirrorneuron_cli.egg-info/requires.txt
13
16
  mirrorneuron_cli.egg-info/top_level.txt
14
17
  mn_cli/__init__.py
18
+ mn_cli/banner.py
15
19
  mn_cli/config.py
16
20
  mn_cli/error_handler.py
17
21
  mn_cli/logging_config.py
18
22
  mn_cli/main.py
23
+ mn_cli/runtime_mode.py
24
+ mn_cli/runtime_state.py
25
+ mn_cli/sdk_path.py
19
26
  mn_cli/server_cmds.py
20
27
  mn_cli/shared.py
28
+ mn_cli/terminal.py
21
29
  mn_cli/update_cmds.py
22
30
  mn_cli/libs/__init__.py
31
+ mn_cli/libs/artifacts.py
32
+ mn_cli/libs/backup_cmds.py
23
33
  mn_cli/libs/blueprint_cmds.py
34
+ mn_cli/libs/blueprint_models.py
24
35
  mn_cli/libs/blueprint_observability.py
25
36
  mn_cli/libs/blueprint_repository.py
37
+ mn_cli/libs/blueprint_resources.py
38
+ mn_cli/libs/bundles.py
39
+ mn_cli/libs/deployment_cmds.py
40
+ mn_cli/libs/event_relay.py
26
41
  mn_cli/libs/job_cmds.py
42
+ mn_cli/libs/model_cmds.py
43
+ mn_cli/libs/resource_cmds.py
27
44
  mn_cli/libs/run_cmds.py
28
45
  mn_cli/libs/run_logs.py
29
46
  mn_cli/libs/run_manifest.py
47
+ mn_cli/libs/runtime_health.py
48
+ mn_cli/libs/schedule_cmds.py
49
+ mn_cli/libs/service_cmds.py
50
+ mn_cli/libs/skill_runtime.py
30
51
  mn_cli/libs/sys_cmds.py
31
52
  mn_cli/libs/ui.py
53
+ mn_cli/libs/workflow_progress.py
54
+ mn_cli/libs/workflow_validation.py
55
+ mn_cli/schemas/workflow_manifest.schema.json
32
56
  scripts/check-release-artifacts.sh
33
57
  scripts/make-release-zip.sh
34
58
  scripts/validate-version-tag.sh
35
59
  tests/conftest.py
60
+ tests/test_backup_cmds.py
36
61
  tests/test_blueprint_cmds.py
37
62
  tests/test_blueprint_repository.py
63
+ tests/test_blueprint_resources.py
64
+ tests/test_deployment_cmds.py
65
+ tests/test_docker_network_integration.py
38
66
  tests/test_job_cmds.py
67
+ tests/test_main.py
68
+ tests/test_model_cmds.py
69
+ tests/test_resource_cmds.py
39
70
  tests/test_run_cmds.py
40
71
  tests/test_run_helpers.py
72
+ tests/test_runtime_health.py
73
+ tests/test_runtime_mode.py
74
+ tests/test_runtime_state.py
75
+ tests/test_schedule_cmds.py
41
76
  tests/test_server_cmds.py
77
+ tests/test_service_cmds.py
78
+ tests/test_shared.py
42
79
  tests/test_sys_cmds.py
43
- tests/test_update_cmds.py
80
+ tests/test_terminal.py
81
+ tests/test_ui.py
82
+ tests/test_update_cmds.py
83
+ tests/test_workflow_validation.py
@@ -1,3 +1,4 @@
1
1
  mirrorneuron-python-sdk
2
+ jsonschema>=4.25
2
3
  typer>=0.9.0
3
4
  rich>=13.0.0
@@ -0,0 +1,13 @@
1
+ MN_ASCII_ART = r"""
2
+ __ __ _ _ _
3
+ | \/ (_)_ __ _ __ ___ _ __| \ | | ___ _ _ _ __ ___ _ __
4
+ | |\/| | | '__| '__/ _ \| '__| \| |/ _ \ | | | '__/ _ \| '_ \
5
+ | | | | | | | | | (_) | | | |\ | __/ |_| | | | (_) | | | |
6
+ |_| |_|_|_| |_| \___/|_| |_| \_|\___|\__,_|_| \___/|_| |_|
7
+ """.strip("\n")
8
+
9
+
10
+ def format_banner(title: str | None = None) -> str:
11
+ if not title:
12
+ return MN_ASCII_ART
13
+ return f"{MN_ASCII_ART}\n\n => {title}"
@@ -0,0 +1,38 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ from dataclasses import dataclass
5
+ from pathlib import Path
6
+
7
+ from mn_cli.sdk_path import add_local_sdk_path
8
+
9
+ add_local_sdk_path("runtime_config.py")
10
+
11
+ from mn_sdk.runtime_config import RuntimeConfig, default_logs_root
12
+
13
+
14
+ @dataclass(frozen=True)
15
+ class CliConfig:
16
+ grpc_target: str = "localhost:55051"
17
+ grpc_timeout_seconds: float | None = 10.0
18
+ grpc_auth_token: str = ""
19
+ grpc_admin_token: str = ""
20
+ log_path: Path = default_logs_root() / "cli.log"
21
+ output_mode: str = "rich"
22
+
23
+ @classmethod
24
+ def from_env(cls) -> "CliConfig":
25
+ runtime_config = RuntimeConfig.from_env()
26
+ return cls(
27
+ grpc_target=runtime_config.grpc_target,
28
+ grpc_timeout_seconds=runtime_config.grpc_timeout_seconds,
29
+ grpc_auth_token=runtime_config.grpc_auth_token,
30
+ grpc_admin_token=runtime_config.grpc_admin_token,
31
+ log_path=Path(
32
+ os.getenv(
33
+ "MN_CLI_LOG_PATH",
34
+ str(default_logs_root() / "cli.log"),
35
+ )
36
+ ).expanduser(),
37
+ output_mode=os.getenv("MN_CLI_OUTPUT", "rich"),
38
+ )
@@ -14,8 +14,27 @@ CONTEXT_MESSAGES = {
14
14
  "cancel": "Error cancelling job",
15
15
  "pause": "Error pausing job",
16
16
  "resume": "Error resuming job",
17
+ "backup": "Error backing up job",
18
+ "restore": "Error restoring job",
17
19
  "nodes": "Error fetching nodes",
20
+ "reconcile-node": "Error reconciling node",
21
+ "drain-node": "Error draining node",
22
+ "undrain-node": "Error cancelling node drain",
23
+ "maintenance-node": "Error changing node maintenance",
18
24
  "metrics": "Error fetching metrics",
25
+ "resource list": "Error fetching resources",
26
+ "resource set": "Error setting resource limits",
27
+ "service list": "Error listing services",
28
+ "service resolve": "Error resolving service",
29
+ "service check": "Service validation failed",
30
+ "deploy": "Error deploying bundle",
31
+ "deployment list": "Error listing deployments",
32
+ "deployment status": "Error fetching deployment",
33
+ "deployment promote": "Error promoting deployment",
34
+ "deployment rollback": "Error rolling back deployment",
35
+ "deployment pause": "Error pausing deployment",
36
+ "deployment resume": "Error resuming deployment",
37
+ "deployment fail": "Error failing deployment",
19
38
  "dead_letters": "Error listing dead letters",
20
39
  "run bundle": "Error running bundle",
21
40
  "monitor stream": "Error fetching job",
@@ -0,0 +1,139 @@
1
+ from __future__ import annotations
2
+
3
+ import hashlib
4
+ import mimetypes
5
+ import os
6
+ import socket
7
+ from pathlib import Path
8
+ from typing import Any
9
+
10
+ from mn_sdk.runtime_config import resolve_mn_home
11
+
12
+ from mn_cli.runtime_state import read_env_file
13
+
14
+ DEFAULT_INLINE_PAYLOAD_MAX_BYTES = 1_048_576
15
+ DEFAULT_ARTIFACT_PORT = "55660"
16
+
17
+
18
+ def promote_large_payloads_to_blob_refs(
19
+ manifest: dict[str, Any],
20
+ payloads: dict[str, bytes],
21
+ *,
22
+ runtime_env: dict[str, str] | None = None,
23
+ ) -> list[dict[str, Any]]:
24
+ threshold = _inline_payload_max_bytes()
25
+ if threshold < 0:
26
+ return []
27
+
28
+ env = _runtime_env_file_values()
29
+ env.update(os.environ)
30
+ env.update(runtime_env or {})
31
+ root = _host_blob_store_root(env)
32
+ promoted: list[dict[str, Any]] = []
33
+
34
+ for rel_path, contents in list(payloads.items()):
35
+ if len(contents) <= threshold:
36
+ continue
37
+
38
+ blob_ref = _store_payload_blob(root, rel_path, contents, env)
39
+ promoted.append(blob_ref)
40
+ del payloads[rel_path]
41
+
42
+ if promoted:
43
+ metadata = manifest.setdefault("metadata", {})
44
+ artifacts = metadata.setdefault("mn_artifacts", {})
45
+ artifacts.setdefault("blob_refs", []).extend(promoted)
46
+
47
+ return promoted
48
+
49
+
50
+ def _store_payload_blob(
51
+ root: Path,
52
+ rel_path: str,
53
+ contents: bytes,
54
+ env: dict[str, str],
55
+ ) -> dict[str, Any]:
56
+ sha256 = hashlib.sha256(contents).hexdigest()
57
+ target = root / sha256[:2] / sha256
58
+ target.parent.mkdir(parents=True, exist_ok=True)
59
+
60
+ if not target.exists():
61
+ tmp = target.with_name(f"{target.name}.tmp-{os.getpid()}")
62
+ tmp.write_bytes(contents)
63
+ os.replace(tmp, target)
64
+
65
+ media_type, _encoding = mimetypes.guess_type(rel_path)
66
+ location = _blob_location(sha256, env)
67
+
68
+ blob_ref: dict[str, Any] = {
69
+ "type": "blob_ref",
70
+ "sha256": sha256,
71
+ "size_bytes": len(contents),
72
+ "media_type": media_type or "application/octet-stream",
73
+ "logical_name": Path(rel_path).name,
74
+ "scope": "job",
75
+ "payload_path": rel_path.replace("\\", "/"),
76
+ }
77
+
78
+ if location:
79
+ blob_ref["locations"] = [location]
80
+
81
+ return blob_ref
82
+
83
+
84
+ def _blob_location(sha256: str, env: dict[str, str]) -> dict[str, str] | None:
85
+ base_url = str(env.get("MN_ARTIFACT_ADVERTISE_URL") or os.getenv("MN_ARTIFACT_ADVERTISE_URL") or "").strip()
86
+ if not base_url:
87
+ host = (
88
+ str(env.get("MN_NETWORK_ADVERTISE_HOST") or os.getenv("MN_NETWORK_ADVERTISE_HOST") or "").strip()
89
+ or _detect_lan_ip()
90
+ )
91
+ port = str(env.get("MN_ARTIFACT_PORT") or os.getenv("MN_ARTIFACT_PORT") or DEFAULT_ARTIFACT_PORT).strip()
92
+ if not host or not port:
93
+ return None
94
+ base_url = f"http://{host}:{port}"
95
+
96
+ location = {
97
+ "url": f"{base_url.rstrip('/')}/blobs/{sha256}",
98
+ "status": "available",
99
+ }
100
+ node = str(env.get("MN_NODE_NAME") or os.getenv("MN_NODE_NAME") or "").strip()
101
+ if node:
102
+ location["node"] = node
103
+ return location
104
+
105
+
106
+ def _host_blob_store_root(env: dict[str, str]) -> Path:
107
+ configured = (
108
+ env.get("MN_HOST_BLOB_STORE_DIR")
109
+ or os.getenv("MN_HOST_BLOB_STORE_DIR")
110
+ or env.get("MN_BLOB_STORE_ROOT")
111
+ or os.getenv("MN_BLOB_STORE_ROOT")
112
+ )
113
+ if configured:
114
+ return Path(configured).expanduser()
115
+ return resolve_mn_home() / "blobs"
116
+
117
+
118
+ def _runtime_env_file_values() -> dict[str, str]:
119
+ env_file = resolve_mn_home() / "docker-compose.env"
120
+ return {key.strip(): value.strip() for key, value in read_env_file(env_file).items()}
121
+
122
+
123
+ def _inline_payload_max_bytes() -> int:
124
+ value = os.getenv("MN_INLINE_PAYLOAD_MAX_BYTES", str(DEFAULT_INLINE_PAYLOAD_MAX_BYTES))
125
+ try:
126
+ return int(value)
127
+ except (TypeError, ValueError):
128
+ return DEFAULT_INLINE_PAYLOAD_MAX_BYTES
129
+
130
+
131
+ def _detect_lan_ip() -> str:
132
+ probe = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
133
+ try:
134
+ probe.connect(("8.8.8.8", 80))
135
+ return probe.getsockname()[0]
136
+ except OSError:
137
+ return "127.0.0.1"
138
+ finally:
139
+ probe.close()