mirrorneuron-cli 1.2.15__tar.gz → 1.2.18__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.
- {mirrorneuron_cli-1.2.15/mirrorneuron_cli.egg-info → mirrorneuron_cli-1.2.18}/PKG-INFO +1 -1
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18/mirrorneuron_cli.egg-info}/PKG-INFO +1 -1
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/mirrorneuron_cli.egg-info/SOURCES.txt +2 -0
- mirrorneuron_cli-1.2.18/mirrorneuron_cli.egg-info/scm_file_list.json +83 -0
- mirrorneuron_cli-1.2.18/mirrorneuron_cli.egg-info/scm_version.json +8 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/mn_cli/libs/job_cmds.py +3 -9
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/mn_cli/libs/run_cmds.py +145 -1
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/mn_cli/server_cmds.py +82 -107
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/tests/test_job_cmds.py +4 -4
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/tests/test_run_cmds.py +93 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/tests/test_server_cmds.py +123 -73
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/tests/test_shared.py +12 -12
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/.github/workflows/ci.yml +0 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/.github/workflows/release.yml +0 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/.gitignore +0 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/.python-version +0 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/AGENTS.md +0 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/LICENSE +0 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/README.md +0 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/RELEASE.md +0 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/mirrorneuron_cli.egg-info/dependency_links.txt +0 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/mirrorneuron_cli.egg-info/entry_points.txt +0 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/mirrorneuron_cli.egg-info/requires.txt +0 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/mirrorneuron_cli.egg-info/top_level.txt +0 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/mn_cli/__init__.py +0 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/mn_cli/banner.py +0 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/mn_cli/config.py +0 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/mn_cli/error_handler.py +0 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/mn_cli/libs/__init__.py +0 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/mn_cli/libs/artifacts.py +0 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/mn_cli/libs/backup_cmds.py +0 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/mn_cli/libs/blueprint_cmds.py +0 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/mn_cli/libs/blueprint_models.py +0 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/mn_cli/libs/blueprint_observability.py +0 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/mn_cli/libs/blueprint_repository.py +0 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/mn_cli/libs/blueprint_resources.py +0 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/mn_cli/libs/bundles.py +0 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/mn_cli/libs/deployment_cmds.py +0 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/mn_cli/libs/event_relay.py +0 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/mn_cli/libs/model_cmds.py +0 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/mn_cli/libs/resource_cmds.py +0 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/mn_cli/libs/run_logs.py +0 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/mn_cli/libs/run_manifest.py +0 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/mn_cli/libs/runtime_health.py +0 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/mn_cli/libs/schedule_cmds.py +0 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/mn_cli/libs/service_cmds.py +0 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/mn_cli/libs/skill_dependencies.py +0 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/mn_cli/libs/skill_runtime.py +0 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/mn_cli/libs/sys_cmds.py +0 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/mn_cli/libs/ui.py +0 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/mn_cli/libs/workflow_progress.py +0 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/mn_cli/libs/workflow_validation.py +0 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/mn_cli/logging_config.py +0 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/mn_cli/main.py +0 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/mn_cli/runtime_mode.py +0 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/mn_cli/runtime_state.py +0 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/mn_cli/schemas/workflow_manifest.schema.json +0 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/mn_cli/sdk_path.py +0 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/mn_cli/shared.py +0 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/mn_cli/terminal.py +0 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/mn_cli/update_cmds.py +0 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/pyproject.toml +0 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/scripts/check-release-artifacts.sh +0 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/scripts/make-release-zip.sh +0 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/scripts/validate-version-tag.sh +0 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/setup.cfg +0 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/tests/conftest.py +0 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/tests/test_backup_cmds.py +0 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/tests/test_blueprint_cmds.py +0 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/tests/test_blueprint_repository.py +0 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/tests/test_blueprint_resources.py +0 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/tests/test_deployment_cmds.py +0 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/tests/test_docker_network_integration.py +0 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/tests/test_main.py +0 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/tests/test_model_cmds.py +0 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/tests/test_repo_hygiene.py +0 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/tests/test_resource_cmds.py +0 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/tests/test_run_helpers.py +0 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/tests/test_runtime_health.py +0 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/tests/test_runtime_mode.py +0 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/tests/test_runtime_state.py +0 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/tests/test_schedule_cmds.py +0 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/tests/test_service_cmds.py +0 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/tests/test_sys_cmds.py +0 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/tests/test_terminal.py +0 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/tests/test_ui.py +0 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/tests/test_update_cmds.py +0 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/tests/test_workflow_validation.py +0 -0
- {mirrorneuron_cli-1.2.15 → mirrorneuron_cli-1.2.18}/uv.lock +0 -0
|
@@ -13,6 +13,8 @@ mirrorneuron_cli.egg-info/SOURCES.txt
|
|
|
13
13
|
mirrorneuron_cli.egg-info/dependency_links.txt
|
|
14
14
|
mirrorneuron_cli.egg-info/entry_points.txt
|
|
15
15
|
mirrorneuron_cli.egg-info/requires.txt
|
|
16
|
+
mirrorneuron_cli.egg-info/scm_file_list.json
|
|
17
|
+
mirrorneuron_cli.egg-info/scm_version.json
|
|
16
18
|
mirrorneuron_cli.egg-info/top_level.txt
|
|
17
19
|
mn_cli/__init__.py
|
|
18
20
|
mn_cli/banner.py
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
{
|
|
2
|
+
"files": [
|
|
3
|
+
"README.md",
|
|
4
|
+
"uv.lock",
|
|
5
|
+
"RELEASE.md",
|
|
6
|
+
".python-version",
|
|
7
|
+
"LICENSE",
|
|
8
|
+
"pyproject.toml",
|
|
9
|
+
"AGENTS.md",
|
|
10
|
+
".gitignore",
|
|
11
|
+
"scripts/check-release-artifacts.sh",
|
|
12
|
+
"scripts/make-release-zip.sh",
|
|
13
|
+
"scripts/validate-version-tag.sh",
|
|
14
|
+
"mn_cli/runtime_state.py",
|
|
15
|
+
"mn_cli/__init__.py",
|
|
16
|
+
"mn_cli/error_handler.py",
|
|
17
|
+
"mn_cli/config.py",
|
|
18
|
+
"mn_cli/sdk_path.py",
|
|
19
|
+
"mn_cli/logging_config.py",
|
|
20
|
+
"mn_cli/banner.py",
|
|
21
|
+
"mn_cli/main.py",
|
|
22
|
+
"mn_cli/runtime_mode.py",
|
|
23
|
+
"mn_cli/server_cmds.py",
|
|
24
|
+
"mn_cli/terminal.py",
|
|
25
|
+
"mn_cli/shared.py",
|
|
26
|
+
"mn_cli/update_cmds.py",
|
|
27
|
+
"mn_cli/libs/backup_cmds.py",
|
|
28
|
+
"mn_cli/libs/model_cmds.py",
|
|
29
|
+
"mn_cli/libs/run_manifest.py",
|
|
30
|
+
"mn_cli/libs/__init__.py",
|
|
31
|
+
"mn_cli/libs/artifacts.py",
|
|
32
|
+
"mn_cli/libs/blueprint_cmds.py",
|
|
33
|
+
"mn_cli/libs/workflow_progress.py",
|
|
34
|
+
"mn_cli/libs/blueprint_models.py",
|
|
35
|
+
"mn_cli/libs/blueprint_observability.py",
|
|
36
|
+
"mn_cli/libs/deployment_cmds.py",
|
|
37
|
+
"mn_cli/libs/run_cmds.py",
|
|
38
|
+
"mn_cli/libs/skill_runtime.py",
|
|
39
|
+
"mn_cli/libs/event_relay.py",
|
|
40
|
+
"mn_cli/libs/resource_cmds.py",
|
|
41
|
+
"mn_cli/libs/skill_dependencies.py",
|
|
42
|
+
"mn_cli/libs/service_cmds.py",
|
|
43
|
+
"mn_cli/libs/run_logs.py",
|
|
44
|
+
"mn_cli/libs/runtime_health.py",
|
|
45
|
+
"mn_cli/libs/bundles.py",
|
|
46
|
+
"mn_cli/libs/blueprint_resources.py",
|
|
47
|
+
"mn_cli/libs/blueprint_repository.py",
|
|
48
|
+
"mn_cli/libs/schedule_cmds.py",
|
|
49
|
+
"mn_cli/libs/sys_cmds.py",
|
|
50
|
+
"mn_cli/libs/ui.py",
|
|
51
|
+
"mn_cli/libs/workflow_validation.py",
|
|
52
|
+
"mn_cli/libs/job_cmds.py",
|
|
53
|
+
"mn_cli/schemas/workflow_manifest.schema.json",
|
|
54
|
+
"tests/test_shared.py",
|
|
55
|
+
"tests/test_model_cmds.py",
|
|
56
|
+
"tests/test_deployment_cmds.py",
|
|
57
|
+
"tests/test_docker_network_integration.py",
|
|
58
|
+
"tests/test_service_cmds.py",
|
|
59
|
+
"tests/test_blueprint_resources.py",
|
|
60
|
+
"tests/test_backup_cmds.py",
|
|
61
|
+
"tests/test_server_cmds.py",
|
|
62
|
+
"tests/test_run_cmds.py",
|
|
63
|
+
"tests/test_blueprint_repository.py",
|
|
64
|
+
"tests/test_blueprint_cmds.py",
|
|
65
|
+
"tests/test_workflow_validation.py",
|
|
66
|
+
"tests/test_runtime_mode.py",
|
|
67
|
+
"tests/test_update_cmds.py",
|
|
68
|
+
"tests/test_schedule_cmds.py",
|
|
69
|
+
"tests/test_run_helpers.py",
|
|
70
|
+
"tests/test_sys_cmds.py",
|
|
71
|
+
"tests/conftest.py",
|
|
72
|
+
"tests/test_terminal.py",
|
|
73
|
+
"tests/test_job_cmds.py",
|
|
74
|
+
"tests/test_ui.py",
|
|
75
|
+
"tests/test_resource_cmds.py",
|
|
76
|
+
"tests/test_main.py",
|
|
77
|
+
"tests/test_runtime_health.py",
|
|
78
|
+
"tests/test_runtime_state.py",
|
|
79
|
+
"tests/test_repo_hygiene.py",
|
|
80
|
+
".github/workflows/release.yml",
|
|
81
|
+
".github/workflows/ci.yml"
|
|
82
|
+
]
|
|
83
|
+
}
|
|
@@ -98,11 +98,6 @@ def list_jobs(running_only: bool = typer.Option(False, "--running-only", help="O
|
|
|
98
98
|
def clear():
|
|
99
99
|
"""Remove all job records except running ones"""
|
|
100
100
|
try:
|
|
101
|
-
admin_token = str(getattr(client, "admin_token", "") or config.grpc_admin_token or "").strip()
|
|
102
|
-
if not admin_token:
|
|
103
|
-
console.print("[red]Error: No local gRPC admin token was found for job clear.[/red]")
|
|
104
|
-
console.print("Start or recreate the runtime so the shared grpc_admin.token file is generated.")
|
|
105
|
-
return
|
|
106
101
|
cleared_count = client.clear_jobs()
|
|
107
102
|
logger.info("Cleared %d non-running jobs", cleared_count)
|
|
108
103
|
print_success_confirmation(
|
|
@@ -115,11 +110,10 @@ def clear():
|
|
|
115
110
|
if e.code() == grpc.StatusCode.PERMISSION_DENIED and "MN_GRPC_ADMIN_TOKEN" in str(e.details()):
|
|
116
111
|
console.print("[red]Error: ClearJobs admin authorization failed.[/red]")
|
|
117
112
|
console.print(
|
|
118
|
-
"The
|
|
119
|
-
"
|
|
120
|
-
"or the running core has not been recreated with the shared token-file mount."
|
|
113
|
+
"The running core rejected the fixed gRPC admin token. "
|
|
114
|
+
"Recreate the runtime with the current fixed-token build."
|
|
121
115
|
)
|
|
122
|
-
console.print("
|
|
116
|
+
console.print("Retry after: mn runtime stop; mn runtime start")
|
|
123
117
|
return
|
|
124
118
|
handle_cli_error(e, console, 'clear')
|
|
125
119
|
except Exception as e:
|
|
@@ -61,14 +61,23 @@ from mn_cli.libs.workflow_validation import (
|
|
|
61
61
|
from mn_cli.libs.blueprint_observability import (
|
|
62
62
|
make_blueprint_run_id as _make_blueprint_run_id,
|
|
63
63
|
)
|
|
64
|
+
from mn_cli.libs.blueprint_models import BlueprintModelOps, blueprint_model_dependency_summary
|
|
65
|
+
from mn_cli.libs.model_cmds import install_model_entry, model_installed
|
|
64
66
|
from mn_cli.libs.blueprint_resources import cleanup_blueprint_host_hooks
|
|
65
67
|
from mn_cli.server_cmds import ensure_context_engine_runtime
|
|
66
68
|
from mn_cli.shared import console, client, logger
|
|
67
69
|
from mn_cli.terminal import use_progress
|
|
68
70
|
from mn_cli.error_handler import handle_cli_error
|
|
69
71
|
from mn_sdk import (
|
|
72
|
+
cluster_provided_model,
|
|
73
|
+
docker_model_name,
|
|
74
|
+
load_model_catalog,
|
|
75
|
+
load_model_ownership,
|
|
70
76
|
make_validation_report,
|
|
71
77
|
prepare_job_submission,
|
|
78
|
+
record_model_owner,
|
|
79
|
+
required_blueprint_models,
|
|
80
|
+
resolve_model_entry,
|
|
72
81
|
run_hardware_requirements_validation,
|
|
73
82
|
run_input_validation,
|
|
74
83
|
run_model_validation,
|
|
@@ -234,6 +243,119 @@ def _manifest_config(manifest: dict[str, Any]) -> dict[str, Any]:
|
|
|
234
243
|
return {}
|
|
235
244
|
|
|
236
245
|
|
|
246
|
+
def _prepare_runtime_models_for_run_or_exit(
|
|
247
|
+
bundle_dir: Path,
|
|
248
|
+
manifest: dict[str, Any],
|
|
249
|
+
*,
|
|
250
|
+
env_overrides: Optional[dict[str, str]] = None,
|
|
251
|
+
config_overrides: Optional[dict[str, Any]] = None,
|
|
252
|
+
force: bool = False,
|
|
253
|
+
) -> dict[str, Any]:
|
|
254
|
+
config = load_blueprint_config(bundle_dir, config_overrides=config_overrides) or {}
|
|
255
|
+
validation_manifest = _manifest_for_model_validation(manifest, config)
|
|
256
|
+
summary = blueprint_model_dependency_summary(
|
|
257
|
+
blueprint_id=_runtime_model_blueprint_id(bundle_dir, manifest, config),
|
|
258
|
+
blueprint_revision=_runtime_model_blueprint_revision(manifest, config),
|
|
259
|
+
bundle_root=bundle_dir,
|
|
260
|
+
manifest=validation_manifest,
|
|
261
|
+
config=config,
|
|
262
|
+
install_source=str(bundle_dir),
|
|
263
|
+
force=force,
|
|
264
|
+
ops=BlueprintModelOps(
|
|
265
|
+
load_model_catalog=load_model_catalog,
|
|
266
|
+
required_blueprint_models=required_blueprint_models,
|
|
267
|
+
load_model_ownership=load_model_ownership,
|
|
268
|
+
resolve_model_entry=resolve_model_entry,
|
|
269
|
+
docker_model_name=docker_model_name,
|
|
270
|
+
cluster_provided_model=cluster_provided_model,
|
|
271
|
+
record_model_owner=record_model_owner,
|
|
272
|
+
model_installed=model_installed,
|
|
273
|
+
install_model_entry=install_model_entry,
|
|
274
|
+
notify_model_install_start=_print_runtime_model_install_start,
|
|
275
|
+
),
|
|
276
|
+
)
|
|
277
|
+
_print_runtime_model_install_summary(summary)
|
|
278
|
+
if summary["errors"]:
|
|
279
|
+
raise typer.Exit(1)
|
|
280
|
+
return summary
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
def _runtime_model_blueprint_id(
|
|
284
|
+
bundle_dir: Path,
|
|
285
|
+
manifest: dict[str, Any],
|
|
286
|
+
config: dict[str, Any],
|
|
287
|
+
) -> str:
|
|
288
|
+
metadata = manifest.get("metadata") if isinstance(manifest.get("metadata"), dict) else {}
|
|
289
|
+
identity = config.get("identity") if isinstance(config.get("identity"), dict) else {}
|
|
290
|
+
for value in (
|
|
291
|
+
metadata.get("blueprint_id"),
|
|
292
|
+
metadata.get("blueprintId"),
|
|
293
|
+
identity.get("blueprint_id"),
|
|
294
|
+
identity.get("blueprintId"),
|
|
295
|
+
manifest.get("id"),
|
|
296
|
+
manifest.get("graph_id"),
|
|
297
|
+
manifest.get("job_name"),
|
|
298
|
+
):
|
|
299
|
+
text = str(value or "").strip()
|
|
300
|
+
if text:
|
|
301
|
+
return text
|
|
302
|
+
return bundle_dir.name
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
def _runtime_model_blueprint_revision(
|
|
306
|
+
manifest: dict[str, Any],
|
|
307
|
+
config: dict[str, Any],
|
|
308
|
+
) -> str | None:
|
|
309
|
+
metadata = manifest.get("metadata") if isinstance(manifest.get("metadata"), dict) else {}
|
|
310
|
+
identity = config.get("identity") if isinstance(config.get("identity"), dict) else {}
|
|
311
|
+
for value in (
|
|
312
|
+
metadata.get("blueprint_revision"),
|
|
313
|
+
metadata.get("blueprintRevision"),
|
|
314
|
+
identity.get("blueprint_revision"),
|
|
315
|
+
identity.get("blueprintRevision"),
|
|
316
|
+
manifest.get("revision"),
|
|
317
|
+
manifest.get("version"),
|
|
318
|
+
):
|
|
319
|
+
text = str(value or "").strip()
|
|
320
|
+
if text:
|
|
321
|
+
return text
|
|
322
|
+
return None
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
def _print_runtime_model_install_start(model: dict[str, Any]) -> None:
|
|
326
|
+
label = str(model.get("id") or model.get("model") or "runtime model")
|
|
327
|
+
docker_model = str(model.get("model") or "")
|
|
328
|
+
backend = str(model.get("backend") or "auto")
|
|
329
|
+
detail = f"{label} ({docker_model})" if docker_model and docker_model != label else label
|
|
330
|
+
console.print(
|
|
331
|
+
f"[yellow]Runtime model {detail} is not installed. "
|
|
332
|
+
f"Installing with backend {backend}; this may take a few minutes the first time.[/yellow]"
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
def _print_runtime_model_install_summary(summary: dict[str, Any]) -> None:
|
|
337
|
+
models = summary.get("models") or []
|
|
338
|
+
if not models:
|
|
339
|
+
return
|
|
340
|
+
|
|
341
|
+
prepared = [
|
|
342
|
+
item
|
|
343
|
+
for item in models
|
|
344
|
+
if str(item.get("status") or "")
|
|
345
|
+
in {"installed", "already_installed", "service_required", "cluster_provided"}
|
|
346
|
+
]
|
|
347
|
+
if prepared:
|
|
348
|
+
labels = ", ".join(
|
|
349
|
+
str(item.get("id") or item.get("model") or "runtime model")
|
|
350
|
+
for item in prepared[:4]
|
|
351
|
+
)
|
|
352
|
+
if len(prepared) > 4:
|
|
353
|
+
labels = f"{labels}, +{len(prepared) - 4} more"
|
|
354
|
+
console.print(f"[green]Runtime models ready:[/green] {labels}")
|
|
355
|
+
for error in summary.get("errors") or []:
|
|
356
|
+
console.print(f"[red]Runtime model install failed: {error}[/red]")
|
|
357
|
+
|
|
358
|
+
|
|
237
359
|
def _ensure_context_engine_for_run_if_needed(
|
|
238
360
|
bundle_dir: Path,
|
|
239
361
|
manifest: dict[str, Any],
|
|
@@ -1609,6 +1731,17 @@ def run_bundle(
|
|
|
1609
1731
|
env_overrides=env_overrides,
|
|
1610
1732
|
config_overrides=config_overrides,
|
|
1611
1733
|
)
|
|
1734
|
+
_print_launch_progress(
|
|
1735
|
+
"Prepare runtime models",
|
|
1736
|
+
"installing any missing Docker Model Runner models required by this blueprint.",
|
|
1737
|
+
)
|
|
1738
|
+
_prepare_runtime_models_for_run_or_exit(
|
|
1739
|
+
bundle_dir,
|
|
1740
|
+
manifest_dict,
|
|
1741
|
+
env_overrides=env_overrides,
|
|
1742
|
+
config_overrides=config_overrides,
|
|
1743
|
+
force=force,
|
|
1744
|
+
)
|
|
1612
1745
|
_validate_manifest_models_or_exit(
|
|
1613
1746
|
bundle_dir,
|
|
1614
1747
|
manifest_dict,
|
|
@@ -1623,7 +1756,18 @@ def run_bundle(
|
|
|
1623
1756
|
)
|
|
1624
1757
|
else:
|
|
1625
1758
|
console.print(
|
|
1626
|
-
"[yellow]Validation skipped because --force was provided; service checks, model checks, input checks, and non-hard runtime requirements will be bypassed
|
|
1759
|
+
"[yellow]Validation skipped because --force was provided; service checks, model checks, input checks, and non-hard runtime requirements will be bypassed, but required runtime models will still be prepared.[/yellow]"
|
|
1760
|
+
)
|
|
1761
|
+
_print_launch_progress(
|
|
1762
|
+
"Prepare runtime models",
|
|
1763
|
+
"installing any missing Docker Model Runner models required by this blueprint.",
|
|
1764
|
+
)
|
|
1765
|
+
_prepare_runtime_models_for_run_or_exit(
|
|
1766
|
+
bundle_dir,
|
|
1767
|
+
manifest_dict,
|
|
1768
|
+
env_overrides=env_overrides,
|
|
1769
|
+
config_overrides=config_overrides,
|
|
1770
|
+
force=force,
|
|
1627
1771
|
)
|
|
1628
1772
|
_print_launch_progress(
|
|
1629
1773
|
"Package workflow",
|
|
@@ -19,7 +19,7 @@ from urllib.parse import urlparse
|
|
|
19
19
|
import typer
|
|
20
20
|
from rich.console import Console
|
|
21
21
|
from rich.table import Table
|
|
22
|
-
from mn_sdk.blueprint_source import DEFAULT_BLUEPRINT_REPO
|
|
22
|
+
from mn_sdk.blueprint_source import DEFAULT_BLUEPRINT_REPO, normalize_blueprint_repo_value
|
|
23
23
|
from mn_cli.config import CliConfig
|
|
24
24
|
from mn_cli.libs.ui import print_confirmed, print_success_confirmation
|
|
25
25
|
from mn_cli.logging_config import configure_logging
|
|
@@ -36,6 +36,8 @@ logger = configure_logging("mn-cli", CliConfig.from_env().log_path)
|
|
|
36
36
|
GRPC_ADMIN_TOKEN_ENV = "MN_GRPC_ADMIN_TOKEN"
|
|
37
37
|
GRPC_AUTH_TOKEN_FILE_ENV = "MN_GRPC_AUTH_TOKEN_FILE"
|
|
38
38
|
GRPC_ADMIN_TOKEN_FILE_ENV = "MN_GRPC_ADMIN_TOKEN_FILE"
|
|
39
|
+
FIXED_GRPC_AUTH_TOKEN = "mirror_neuron_password"
|
|
40
|
+
FIXED_GRPC_ADMIN_TOKEN = "mirror_neuron_password_admin"
|
|
39
41
|
|
|
40
42
|
def _erl_aflags(dist_port: str | int) -> str:
|
|
41
43
|
return (
|
|
@@ -186,8 +188,6 @@ DEFAULT_BLUEPRINT_WEB_UI_PORT_ALLOCATION_MODE = "prepublished"
|
|
|
186
188
|
DEFAULT_CONTAINER_RUNS_ROOT = "/root/.mn/runs"
|
|
187
189
|
DEFAULT_CONTAINER_BLOB_STORE_ROOT = "/root/.mn/blobs"
|
|
188
190
|
DEFAULT_RUNTIME_SHARED_STORAGE_ROOT = "/root/.mn/shared"
|
|
189
|
-
DEFAULT_CONTAINER_GRPC_AUTH_TOKEN_FILE = "/root/.mn/grpc_auth.token"
|
|
190
|
-
DEFAULT_CONTAINER_GRPC_ADMIN_TOKEN_FILE = "/root/.mn/grpc_admin.token"
|
|
191
191
|
DEFAULT_REDIS_IMAGE = "redis:8"
|
|
192
192
|
LEGACY_GRPC_PORT = "50051"
|
|
193
193
|
LEGACY_API_PORT = "4001"
|
|
@@ -732,46 +732,10 @@ def _resolve_mn_cookie() -> str:
|
|
|
732
732
|
return generated_cookie
|
|
733
733
|
|
|
734
734
|
def _resolve_grpc_auth_token() -> str:
|
|
735
|
-
|
|
736
|
-
if env_token:
|
|
737
|
-
return env_token
|
|
738
|
-
|
|
739
|
-
token_file = DIR / "grpc_auth.token"
|
|
740
|
-
try:
|
|
741
|
-
existing_token = token_file.read_text().strip()
|
|
742
|
-
if existing_token:
|
|
743
|
-
return existing_token
|
|
744
|
-
except FileNotFoundError:
|
|
745
|
-
pass
|
|
746
|
-
|
|
747
|
-
DIR.mkdir(parents=True, exist_ok=True)
|
|
748
|
-
generated_token = secrets.token_hex(32)
|
|
749
|
-
_write_grpc_token_file(token_file, generated_token, "gRPC auth")
|
|
750
|
-
return generated_token
|
|
735
|
+
return FIXED_GRPC_AUTH_TOKEN
|
|
751
736
|
|
|
752
737
|
def _resolve_grpc_admin_token() -> str:
|
|
753
|
-
|
|
754
|
-
if env_token:
|
|
755
|
-
return env_token
|
|
756
|
-
|
|
757
|
-
token_file = DIR / "grpc_admin.token"
|
|
758
|
-
try:
|
|
759
|
-
existing_token = token_file.read_text().strip()
|
|
760
|
-
if existing_token:
|
|
761
|
-
return existing_token
|
|
762
|
-
except FileNotFoundError:
|
|
763
|
-
pass
|
|
764
|
-
|
|
765
|
-
DIR.mkdir(parents=True, exist_ok=True)
|
|
766
|
-
generated_token = secrets.token_hex(32)
|
|
767
|
-
_write_grpc_token_file(token_file, generated_token, "gRPC admin")
|
|
768
|
-
return generated_token
|
|
769
|
-
|
|
770
|
-
def _write_grpc_token_file(token_file: Path, token: str, label: str) -> None:
|
|
771
|
-
token = str(token or "").strip()
|
|
772
|
-
if not token:
|
|
773
|
-
return
|
|
774
|
-
write_private_text(token_file, f"{token}\n")
|
|
738
|
+
return FIXED_GRPC_ADMIN_TOKEN
|
|
775
739
|
|
|
776
740
|
def _resolve_api_token() -> str:
|
|
777
741
|
env_token = os.getenv("MN_API_TOKEN", "").strip()
|
|
@@ -802,57 +766,34 @@ def _ensure_runtime_api_token(env: dict[str, str], *, persist_compose: bool = Fa
|
|
|
802
766
|
|
|
803
767
|
def _ensure_runtime_grpc_tokens(env: dict[str, str], *, persist_compose: bool = False) -> dict[str, str]:
|
|
804
768
|
resolved = dict(env)
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
resolved[GRPC_ADMIN_TOKEN_ENV],
|
|
818
|
-
"gRPC admin",
|
|
819
|
-
)
|
|
769
|
+
fixed_tokens = {
|
|
770
|
+
"MN_GRPC_AUTH_TOKEN": FIXED_GRPC_AUTH_TOKEN,
|
|
771
|
+
GRPC_ADMIN_TOKEN_ENV: FIXED_GRPC_ADMIN_TOKEN,
|
|
772
|
+
}
|
|
773
|
+
stale_token_keys = {
|
|
774
|
+
"MN_MIRROR_NEURON_GRPC_ADMIN_TOKEN",
|
|
775
|
+
GRPC_AUTH_TOKEN_FILE_ENV,
|
|
776
|
+
GRPC_ADMIN_TOKEN_FILE_ENV,
|
|
777
|
+
}
|
|
778
|
+
for key in stale_token_keys:
|
|
779
|
+
resolved.pop(key, None)
|
|
780
|
+
resolved.update(fixed_tokens)
|
|
820
781
|
if persist_compose:
|
|
821
|
-
_write_env_file_values(
|
|
822
|
-
|
|
823
|
-
{
|
|
824
|
-
"MN_GRPC_AUTH_TOKEN": resolved["MN_GRPC_AUTH_TOKEN"],
|
|
825
|
-
GRPC_ADMIN_TOKEN_ENV: resolved[GRPC_ADMIN_TOKEN_ENV],
|
|
826
|
-
GRPC_AUTH_TOKEN_FILE_ENV: resolved[GRPC_AUTH_TOKEN_FILE_ENV],
|
|
827
|
-
GRPC_ADMIN_TOKEN_FILE_ENV: resolved[GRPC_ADMIN_TOKEN_FILE_ENV],
|
|
828
|
-
},
|
|
829
|
-
)
|
|
782
|
+
_write_env_file_values(RUNTIME_COMPOSE_ENV, fixed_tokens)
|
|
783
|
+
_remove_env_file_keys(RUNTIME_COMPOSE_ENV, stale_token_keys)
|
|
830
784
|
return resolved
|
|
831
785
|
|
|
832
786
|
def _grpc_tokens_from_handshake(handshake: Optional[dict]) -> dict[str, str]:
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
admin_token = str(handshake.get("grpc_admin_token") or "").strip()
|
|
838
|
-
if auth_token:
|
|
839
|
-
tokens["MN_GRPC_AUTH_TOKEN"] = auth_token
|
|
840
|
-
if admin_token:
|
|
841
|
-
tokens[GRPC_ADMIN_TOKEN_ENV] = admin_token
|
|
842
|
-
return tokens
|
|
787
|
+
return {
|
|
788
|
+
"MN_GRPC_AUTH_TOKEN": FIXED_GRPC_AUTH_TOKEN,
|
|
789
|
+
GRPC_ADMIN_TOKEN_ENV: FIXED_GRPC_ADMIN_TOKEN,
|
|
790
|
+
}
|
|
843
791
|
|
|
844
792
|
def _runtime_grpc_tokens_from_running_container() -> dict[str, str]:
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
if auth_token:
|
|
850
|
-
tokens["MN_GRPC_AUTH_TOKEN"] = auth_token
|
|
851
|
-
if admin_token:
|
|
852
|
-
tokens[GRPC_ADMIN_TOKEN_ENV] = admin_token
|
|
853
|
-
if tokens:
|
|
854
|
-
break
|
|
855
|
-
return tokens
|
|
793
|
+
return {
|
|
794
|
+
"MN_GRPC_AUTH_TOKEN": FIXED_GRPC_AUTH_TOKEN,
|
|
795
|
+
GRPC_ADMIN_TOKEN_ENV: FIXED_GRPC_ADMIN_TOKEN,
|
|
796
|
+
}
|
|
856
797
|
|
|
857
798
|
def _resolve_network_token(force_new: bool = False) -> str:
|
|
858
799
|
if force_new:
|
|
@@ -1245,8 +1186,8 @@ def _network_core_env(
|
|
|
1245
1186
|
"MN_RUNTIME_SHARED_STORAGE_ROOT": DEFAULT_RUNTIME_SHARED_STORAGE_ROOT,
|
|
1246
1187
|
"MN_DIST_PORT": str(dist_port),
|
|
1247
1188
|
"MN_COOKIE": _derive_network_secret(token, "cookie"),
|
|
1248
|
-
"MN_GRPC_AUTH_TOKEN":
|
|
1249
|
-
GRPC_ADMIN_TOKEN_ENV:
|
|
1189
|
+
"MN_GRPC_AUTH_TOKEN": FIXED_GRPC_AUTH_TOKEN,
|
|
1190
|
+
GRPC_ADMIN_TOKEN_ENV: FIXED_GRPC_ADMIN_TOKEN,
|
|
1250
1191
|
"ERL_EPMD_ADDRESS": "0.0.0.0",
|
|
1251
1192
|
"ERL_EPMD_PORT": str(epmd_port),
|
|
1252
1193
|
"ERL_AFLAGS": _erl_aflags(dist_port),
|
|
@@ -1652,9 +1593,6 @@ def _start_network_seed(
|
|
|
1652
1593
|
redis_public_host=redis_public_host,
|
|
1653
1594
|
redis_public_port=redis_public_port,
|
|
1654
1595
|
)
|
|
1655
|
-
env["MN_GRPC_AUTH_TOKEN"] = _resolve_grpc_auth_token()
|
|
1656
|
-
env[GRPC_ADMIN_TOKEN_ENV] = _resolve_grpc_admin_token()
|
|
1657
|
-
|
|
1658
1596
|
env = _ensure_runtime_grpc_tokens(env, persist_compose=runtime_compose_available())
|
|
1659
1597
|
|
|
1660
1598
|
console.print("=> Starting MirrorNeuron core-only exposed node...")
|
|
@@ -2029,11 +1967,10 @@ def ensure_context_engine_runtime(*, force: bool = False) -> dict[str, str]:
|
|
|
2029
1967
|
_remove_non_mirror_neuron_container(CONTEXT_ENGINE_CONTAINER)
|
|
2030
1968
|
_remove_non_mirror_neuron_container(CONTEXT_ENGINE_MODEL_CONTAINER)
|
|
2031
1969
|
_ensure_docker_model_runner()
|
|
2032
|
-
|
|
2033
|
-
model_status = "already_installed" if model_already_installed else "compose_pending"
|
|
1970
|
+
model_status = _install_context_engine_model(model)
|
|
2034
1971
|
|
|
2035
1972
|
already_running = _docker_container_running(CONTEXT_ENGINE_CONTAINER)
|
|
2036
|
-
if force or not already_running
|
|
1973
|
+
if force or not already_running:
|
|
2037
1974
|
compose_env = env
|
|
2038
1975
|
anonymous_docker_config: Path | None = None
|
|
2039
1976
|
if use_engine_image:
|
|
@@ -2066,8 +2003,6 @@ def ensure_context_engine_runtime(*, force: bool = False) -> dict[str, str]:
|
|
|
2066
2003
|
if anonymous_docker_config is not None:
|
|
2067
2004
|
shutil.rmtree(anonymous_docker_config, ignore_errors=True)
|
|
2068
2005
|
status = "restarted" if already_running and force else "started"
|
|
2069
|
-
if not model_already_installed:
|
|
2070
|
-
model_status = "installed" if _docker_model_inspect_ok(model) else "compose_managed"
|
|
2071
2006
|
else:
|
|
2072
2007
|
status = "already_running"
|
|
2073
2008
|
|
|
@@ -2091,6 +2026,37 @@ def _docker_model_inspect_ok(model: str) -> bool:
|
|
|
2091
2026
|
return False
|
|
2092
2027
|
return _docker_command_ok(["docker", "model", "inspect", model])
|
|
2093
2028
|
|
|
2029
|
+
def _install_context_engine_model(model: str) -> str:
|
|
2030
|
+
model = str(model or "").strip()
|
|
2031
|
+
if not model:
|
|
2032
|
+
return "skipped"
|
|
2033
|
+
if _docker_model_inspect_ok(model):
|
|
2034
|
+
return "already_installed"
|
|
2035
|
+
pull_result = subprocess.run(
|
|
2036
|
+
["docker", "model", "pull", model],
|
|
2037
|
+
capture_output=True,
|
|
2038
|
+
text=True,
|
|
2039
|
+
timeout=900,
|
|
2040
|
+
)
|
|
2041
|
+
if pull_result.returncode != 0 and not _docker_model_inspect_ok(model):
|
|
2042
|
+
output = _subprocess_error_output(pull_result)
|
|
2043
|
+
raise RuntimeError(f"Failed to install context engine model {model}: {output}")
|
|
2044
|
+
run_result = subprocess.run(
|
|
2045
|
+
["docker", "model", "run", "--detach", model],
|
|
2046
|
+
capture_output=True,
|
|
2047
|
+
text=True,
|
|
2048
|
+
timeout=300,
|
|
2049
|
+
)
|
|
2050
|
+
if run_result.returncode != 0:
|
|
2051
|
+
output = _subprocess_error_output(run_result)
|
|
2052
|
+
if "already" not in output.lower():
|
|
2053
|
+
raise RuntimeError(f"Failed to start context engine model {model}: {output}")
|
|
2054
|
+
return "installed"
|
|
2055
|
+
|
|
2056
|
+
def _subprocess_error_output(result: subprocess.CompletedProcess[str]) -> str:
|
|
2057
|
+
output = f"{result.stderr or ''}\n{result.stdout or ''}".strip()
|
|
2058
|
+
return output or f"exit code {result.returncode}"
|
|
2059
|
+
|
|
2094
2060
|
def _anonymous_public_gar_docker_env(env: dict[str, str], image: str) -> tuple[dict[str, str], Path | None]:
|
|
2095
2061
|
if not _is_public_gar_image(image):
|
|
2096
2062
|
return env, None
|
|
@@ -2454,10 +2420,13 @@ def _cluster_endpoint_host(env: dict[str, str], host: str) -> str:
|
|
|
2454
2420
|
return _native_endpoint_host(normalized)
|
|
2455
2421
|
|
|
2456
2422
|
def _runtime_blueprint_env_updates(env: dict[str, str]) -> dict[str, str]:
|
|
2423
|
+
runtime_env = str(env.get("MN_ENV") or os.getenv("MN_ENV") or "dev").strip().lower()
|
|
2457
2424
|
blueprint_source = str(env.get("MN_BLUEPRINT_SOURCE") or os.getenv("MN_BLUEPRINT_SOURCE") or "github").strip().lower()
|
|
2458
2425
|
if blueprint_source not in {"github", "local"}:
|
|
2459
2426
|
blueprint_source = "github"
|
|
2460
|
-
blueprint_repo =
|
|
2427
|
+
blueprint_repo = normalize_blueprint_repo_value(
|
|
2428
|
+
str(env.get("MN_BLUEPRINT_REPO") or os.getenv("MN_BLUEPRINT_REPO") or DEFAULT_BLUEPRINT_REPO).strip()
|
|
2429
|
+
)
|
|
2461
2430
|
blueprint_local = str(env.get("MN_BLUEPRINT_LOCAL") or os.getenv("MN_BLUEPRINT_LOCAL") or "").strip()
|
|
2462
2431
|
host_home_dir = str(
|
|
2463
2432
|
env.get("MN_HOST_HOME_DIR")
|
|
@@ -2500,6 +2469,7 @@ def _runtime_blueprint_env_updates(env: dict[str, str]) -> dict[str, str]:
|
|
|
2500
2469
|
or DEFAULT_RUNTIME_SHARED_STORAGE_ROOT
|
|
2501
2470
|
).strip()
|
|
2502
2471
|
updates: dict[str, str] = {
|
|
2472
|
+
"MN_ENV": runtime_env,
|
|
2503
2473
|
"MN_BLUEPRINT_SOURCE": blueprint_source,
|
|
2504
2474
|
"MN_BLUEPRINT_REPO": blueprint_repo,
|
|
2505
2475
|
"MN_BLUEPRINT_LOCAL": blueprint_local,
|
|
@@ -2636,6 +2606,11 @@ def _runtime_api_config_mismatches(env: dict[str, str], health: Optional[dict[st
|
|
|
2636
2606
|
if not health:
|
|
2637
2607
|
return []
|
|
2638
2608
|
mismatches: list[tuple[str, str, str]] = []
|
|
2609
|
+
expected_env = str(env.get("MN_ENV") or "dev").strip().lower()
|
|
2610
|
+
active_env = str(health.get("env") or health.get("mn_env") or "").strip().lower()
|
|
2611
|
+
if active_env and expected_env and active_env != expected_env:
|
|
2612
|
+
mismatches.append(("MN_ENV", active_env, expected_env))
|
|
2613
|
+
|
|
2639
2614
|
expected_blueprint = _expected_blueprint_location(env)
|
|
2640
2615
|
active_blueprint = str(
|
|
2641
2616
|
health.get("active_blueprint_location")
|
|
@@ -3632,10 +3607,8 @@ def _build_core_docker_run_command(
|
|
|
3632
3607
|
|
|
3633
3608
|
cmd.extend(["-e", f"MN_NODE_NAME={env['MN_NODE_NAME']}"])
|
|
3634
3609
|
cmd.extend(["-e", f"MN_COOKIE={env['MN_COOKIE']}"])
|
|
3635
|
-
cmd.extend(["-e", f"MN_GRPC_AUTH_TOKEN={env
|
|
3636
|
-
cmd.extend(["-e", f"{
|
|
3637
|
-
cmd.extend(["-e", f"{GRPC_ADMIN_TOKEN_ENV}={env[GRPC_ADMIN_TOKEN_ENV]}"])
|
|
3638
|
-
cmd.extend(["-e", f"{GRPC_ADMIN_TOKEN_FILE_ENV}={env[GRPC_ADMIN_TOKEN_FILE_ENV]}"])
|
|
3610
|
+
cmd.extend(["-e", f"MN_GRPC_AUTH_TOKEN={env.get('MN_GRPC_AUTH_TOKEN', FIXED_GRPC_AUTH_TOKEN)}"])
|
|
3611
|
+
cmd.extend(["-e", f"{GRPC_ADMIN_TOKEN_ENV}={env.get(GRPC_ADMIN_TOKEN_ENV, FIXED_GRPC_ADMIN_TOKEN)}"])
|
|
3639
3612
|
cmd.extend(["-e", f"MN_NETWORK_JOIN_TOKEN={env['MN_NETWORK_JOIN_TOKEN']}"])
|
|
3640
3613
|
cmd.extend(["-e", f"MN_NETWORK_ADVERTISE_HOST={env['MN_NETWORK_ADVERTISE_HOST']}"])
|
|
3641
3614
|
if env.get("MN_MODEL_SERVICE_NODE_NAME"):
|
|
@@ -3779,14 +3752,16 @@ def _start_server(
|
|
|
3779
3752
|
env = _ensure_runtime_api_token(env, persist_compose=compose_runtime)
|
|
3780
3753
|
if compose_runtime:
|
|
3781
3754
|
env = _ensure_compose_native_port_settings(env)
|
|
3782
|
-
|
|
3755
|
+
core_running = _docker_container_running("mirror-neuron-core")
|
|
3756
|
+
if not core_running:
|
|
3783
3757
|
console.print("=> MirrorNeuron Core is not running; starting Docker runtime (Compose)...")
|
|
3784
|
-
|
|
3785
|
-
|
|
3758
|
+
try:
|
|
3759
|
+
subprocess.run(runtime_compose_cmd("up", "-d"), check=True, stdout=subprocess.DEVNULL, env=env)
|
|
3760
|
+
if not core_running:
|
|
3786
3761
|
console.print(" [green][Started][/green] Docker runtime (Compose project: mirror-neuron)")
|
|
3787
|
-
|
|
3788
|
-
|
|
3789
|
-
|
|
3762
|
+
except (FileNotFoundError, subprocess.CalledProcessError):
|
|
3763
|
+
console.print("[red]Failed to start MirrorNeuron Docker runtime.[/red]")
|
|
3764
|
+
raise typer.Exit(1)
|
|
3790
3765
|
env.setdefault("MN_API_HOST", _api_host())
|
|
3791
3766
|
env.setdefault("MN_API_PORT", DEFAULT_API_PORT)
|
|
3792
3767
|
env.setdefault("MN_WEB_UI_HOST", _web_ui_host())
|
|
@@ -25,7 +25,7 @@ def _capture_console(monkeypatch):
|
|
|
25
25
|
return output
|
|
26
26
|
|
|
27
27
|
|
|
28
|
-
def
|
|
28
|
+
def test_clear_runs_without_local_admin_token_preflight(monkeypatch):
|
|
29
29
|
output = _capture_console(monkeypatch)
|
|
30
30
|
client = SimpleNamespace(admin_token="", clear_jobs=lambda: 1)
|
|
31
31
|
monkeypatch.setattr(job_cmds, "client", client)
|
|
@@ -34,8 +34,8 @@ def test_clear_preflights_missing_admin_token(monkeypatch):
|
|
|
34
34
|
job_cmds.clear()
|
|
35
35
|
|
|
36
36
|
rendered = output.getvalue()
|
|
37
|
-
assert "
|
|
38
|
-
assert "
|
|
37
|
+
assert "Job clear successful" in rendered
|
|
38
|
+
assert "Jobs cleared: 1 non-running" in rendered
|
|
39
39
|
|
|
40
40
|
|
|
41
41
|
def test_clear_reports_admin_token_mismatch(monkeypatch):
|
|
@@ -54,4 +54,4 @@ def test_clear_reports_admin_token_mismatch(monkeypatch):
|
|
|
54
54
|
|
|
55
55
|
rendered = output.getvalue()
|
|
56
56
|
assert "ClearJobs admin authorization failed" in rendered
|
|
57
|
-
assert "
|
|
57
|
+
assert "fixed gRPC admin token" in rendered
|