mrmd-python 0.3.0__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.
- mrmd_python-0.3.0/.gitignore +44 -0
- mrmd_python-0.3.0/PKG-INFO +162 -0
- mrmd_python-0.3.0/README.md +131 -0
- mrmd_python-0.3.0/pyproject.toml +51 -0
- mrmd_python-0.3.0/src/mrmd_python/__init__.py +16 -0
- mrmd_python-0.3.0/src/mrmd_python/cli.py +225 -0
- mrmd_python-0.3.0/src/mrmd_python/runtime_client.py +459 -0
- mrmd_python-0.3.0/src/mrmd_python/runtime_daemon.py +437 -0
- mrmd_python-0.3.0/src/mrmd_python/server.py +760 -0
- mrmd_python-0.3.0/src/mrmd_python/subprocess_manager.py +470 -0
- mrmd_python-0.3.0/src/mrmd_python/subprocess_worker.py +844 -0
- mrmd_python-0.3.0/src/mrmd_python/types.py +245 -0
- mrmd_python-0.3.0/src/mrmd_python/worker.py +1525 -0
- mrmd_python-0.3.0/uv.lock +601 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.so
|
|
6
|
+
.Python
|
|
7
|
+
build/
|
|
8
|
+
develop-eggs/
|
|
9
|
+
dist/
|
|
10
|
+
downloads/
|
|
11
|
+
eggs/
|
|
12
|
+
.eggs/
|
|
13
|
+
lib/
|
|
14
|
+
lib64/
|
|
15
|
+
parts/
|
|
16
|
+
sdist/
|
|
17
|
+
var/
|
|
18
|
+
wheels/
|
|
19
|
+
*.egg-info/
|
|
20
|
+
.installed.cfg
|
|
21
|
+
*.egg
|
|
22
|
+
|
|
23
|
+
# Virtual environments
|
|
24
|
+
.venv/
|
|
25
|
+
venv/
|
|
26
|
+
ENV/
|
|
27
|
+
|
|
28
|
+
# IDE
|
|
29
|
+
.idea/
|
|
30
|
+
.vscode/
|
|
31
|
+
*.swp
|
|
32
|
+
*.swo
|
|
33
|
+
|
|
34
|
+
# Testing
|
|
35
|
+
.pytest_cache/
|
|
36
|
+
.coverage
|
|
37
|
+
htmlcov/
|
|
38
|
+
|
|
39
|
+
# Runtime assets (generated)
|
|
40
|
+
.mrmd-assets/
|
|
41
|
+
|
|
42
|
+
# OS
|
|
43
|
+
.DS_Store
|
|
44
|
+
Thumbs.db
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mrmd-python
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: Python runtime server implementing the MRMD Runtime Protocol (MRP)
|
|
5
|
+
Author: mrmd contributors
|
|
6
|
+
License: MIT
|
|
7
|
+
Keywords: ipython,mrmd,mrp,notebook,runtime
|
|
8
|
+
Classifier: Development Status :: 3 - Alpha
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Requires-Python: >=3.10
|
|
17
|
+
Requires-Dist: black>=25.12.0
|
|
18
|
+
Requires-Dist: httpx>=0.27.0
|
|
19
|
+
Requires-Dist: ipython>=8.0.0
|
|
20
|
+
Requires-Dist: jedi>=0.19.0
|
|
21
|
+
Requires-Dist: sse-starlette>=2.0.0
|
|
22
|
+
Requires-Dist: starlette>=0.40.0
|
|
23
|
+
Requires-Dist: uvicorn>=0.32.0
|
|
24
|
+
Provides-Extra: dev
|
|
25
|
+
Requires-Dist: httpx>=0.27.0; extra == 'dev'
|
|
26
|
+
Requires-Dist: pytest-asyncio>=0.24.0; extra == 'dev'
|
|
27
|
+
Requires-Dist: pytest>=8.0.0; extra == 'dev'
|
|
28
|
+
Provides-Extra: format
|
|
29
|
+
Requires-Dist: black>=24.0.0; extra == 'format'
|
|
30
|
+
Description-Content-Type: text/markdown
|
|
31
|
+
|
|
32
|
+
# mrmd-python
|
|
33
|
+
|
|
34
|
+
Independent Python runtime for MRMD notebooks. Runs as a daemon process with full GPU memory isolation.
|
|
35
|
+
|
|
36
|
+
## Features
|
|
37
|
+
|
|
38
|
+
- **Independent daemon process** - survives if parent dies, variables persist
|
|
39
|
+
- **GPU memory isolation** - kill daemon to release VRAM (critical for vLLM)
|
|
40
|
+
- **Auto venv detection** - uses current venv or `VIRTUAL_ENV`
|
|
41
|
+
- **Registry-based discovery** - find runtimes via `~/.mrmd/runtimes/`
|
|
42
|
+
- **Full MRP protocol** - execute, completions, inspect, variables, streaming
|
|
43
|
+
|
|
44
|
+
## Installation
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
# With uv
|
|
48
|
+
uv pip install mrmd-python
|
|
49
|
+
|
|
50
|
+
# Or run directly without installing
|
|
51
|
+
uvx mrmd-python
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Quick Start
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
# Start a daemon runtime (auto-detects venv)
|
|
58
|
+
mrmd-python
|
|
59
|
+
|
|
60
|
+
# The daemon runs in background. Use the API:
|
|
61
|
+
curl http://localhost:PORT/mrp/v1/capabilities
|
|
62
|
+
|
|
63
|
+
# List running runtimes
|
|
64
|
+
mrmd-python --list
|
|
65
|
+
|
|
66
|
+
# Kill when done (releases GPU memory)
|
|
67
|
+
mrmd-python --kill default
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## CLI Reference
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
# Start daemon
|
|
74
|
+
mrmd-python # Start with ID "default"
|
|
75
|
+
mrmd-python --id vllm # Start with custom ID
|
|
76
|
+
mrmd-python --venv /path/venv # Use specific venv
|
|
77
|
+
mrmd-python --port 8000 # Use specific port
|
|
78
|
+
|
|
79
|
+
# Management
|
|
80
|
+
mrmd-python --list # List all running runtimes
|
|
81
|
+
mrmd-python --info ID # Get runtime details
|
|
82
|
+
mrmd-python --kill ID # Kill a runtime
|
|
83
|
+
mrmd-python --kill-all # Kill all runtimes
|
|
84
|
+
|
|
85
|
+
# Development
|
|
86
|
+
mrmd-python --foreground # Run in foreground (no daemon)
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Virtual Environment Detection
|
|
90
|
+
|
|
91
|
+
When `--venv` is not specified, mrmd-python auto-detects:
|
|
92
|
+
|
|
93
|
+
1. Current venv (if running inside one via `sys.prefix`)
|
|
94
|
+
2. `VIRTUAL_ENV` environment variable
|
|
95
|
+
3. Falls back to system Python
|
|
96
|
+
|
|
97
|
+
## API Endpoints
|
|
98
|
+
|
|
99
|
+
All endpoints at `/mrp/v1/`:
|
|
100
|
+
|
|
101
|
+
| Endpoint | Method | Purpose |
|
|
102
|
+
|----------|--------|---------|
|
|
103
|
+
| `/capabilities` | GET | Runtime info and features |
|
|
104
|
+
| `/sessions` | GET/POST | List/create sessions |
|
|
105
|
+
| `/sessions/{id}` | GET/DELETE | Get/destroy session |
|
|
106
|
+
| `/execute` | POST | Run code |
|
|
107
|
+
| `/execute/stream` | POST | Run code with SSE streaming |
|
|
108
|
+
| `/complete` | POST | Get completions |
|
|
109
|
+
| `/inspect` | POST | Get symbol documentation |
|
|
110
|
+
| `/hover` | POST | Get hover tooltip |
|
|
111
|
+
| `/variables` | POST | List user variables |
|
|
112
|
+
| `/variables/{name}` | POST | Get variable details |
|
|
113
|
+
| `/is_complete` | POST | Check if code is complete |
|
|
114
|
+
| `/format` | POST | Format code with black |
|
|
115
|
+
|
|
116
|
+
## Architecture
|
|
117
|
+
|
|
118
|
+
```
|
|
119
|
+
~/.mrmd/
|
|
120
|
+
├── runtimes/
|
|
121
|
+
│ └── {id}.json # Registry: pid, port, url, venv, cwd
|
|
122
|
+
└── logs/
|
|
123
|
+
└── {id}.log # Daemon logs
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Each runtime is a fully independent process:
|
|
127
|
+
- Double-forked daemon (survives parent death)
|
|
128
|
+
- Own IPython shell with persistent variables
|
|
129
|
+
- HTTP server on auto-assigned port
|
|
130
|
+
- Registered in `~/.mrmd/runtimes/` for discovery
|
|
131
|
+
|
|
132
|
+
## GPU Memory Management
|
|
133
|
+
|
|
134
|
+
For vLLM and other GPU workloads, memory is only released when the process dies:
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
# Load model in runtime
|
|
138
|
+
mrmd-python --id vllm
|
|
139
|
+
# ... use the model ...
|
|
140
|
+
|
|
141
|
+
# Release GPU memory
|
|
142
|
+
mrmd-python --kill vllm
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Programmatic Usage
|
|
146
|
+
|
|
147
|
+
```python
|
|
148
|
+
from mrmd_python import create_app
|
|
149
|
+
import uvicorn
|
|
150
|
+
|
|
151
|
+
# Create app (daemon_mode=True for use inside daemon)
|
|
152
|
+
app = create_app(
|
|
153
|
+
cwd="/path/to/project",
|
|
154
|
+
venv="/path/to/venv",
|
|
155
|
+
daemon_mode=True,
|
|
156
|
+
)
|
|
157
|
+
uvicorn.run(app, host="localhost", port=8000)
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## Protocol
|
|
161
|
+
|
|
162
|
+
See [PROTOCOL.md](../mrmd-editor/PROTOCOL.md) for the full MRP specification.
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# mrmd-python
|
|
2
|
+
|
|
3
|
+
Independent Python runtime for MRMD notebooks. Runs as a daemon process with full GPU memory isolation.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Independent daemon process** - survives if parent dies, variables persist
|
|
8
|
+
- **GPU memory isolation** - kill daemon to release VRAM (critical for vLLM)
|
|
9
|
+
- **Auto venv detection** - uses current venv or `VIRTUAL_ENV`
|
|
10
|
+
- **Registry-based discovery** - find runtimes via `~/.mrmd/runtimes/`
|
|
11
|
+
- **Full MRP protocol** - execute, completions, inspect, variables, streaming
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# With uv
|
|
17
|
+
uv pip install mrmd-python
|
|
18
|
+
|
|
19
|
+
# Or run directly without installing
|
|
20
|
+
uvx mrmd-python
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Quick Start
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
# Start a daemon runtime (auto-detects venv)
|
|
27
|
+
mrmd-python
|
|
28
|
+
|
|
29
|
+
# The daemon runs in background. Use the API:
|
|
30
|
+
curl http://localhost:PORT/mrp/v1/capabilities
|
|
31
|
+
|
|
32
|
+
# List running runtimes
|
|
33
|
+
mrmd-python --list
|
|
34
|
+
|
|
35
|
+
# Kill when done (releases GPU memory)
|
|
36
|
+
mrmd-python --kill default
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## CLI Reference
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
# Start daemon
|
|
43
|
+
mrmd-python # Start with ID "default"
|
|
44
|
+
mrmd-python --id vllm # Start with custom ID
|
|
45
|
+
mrmd-python --venv /path/venv # Use specific venv
|
|
46
|
+
mrmd-python --port 8000 # Use specific port
|
|
47
|
+
|
|
48
|
+
# Management
|
|
49
|
+
mrmd-python --list # List all running runtimes
|
|
50
|
+
mrmd-python --info ID # Get runtime details
|
|
51
|
+
mrmd-python --kill ID # Kill a runtime
|
|
52
|
+
mrmd-python --kill-all # Kill all runtimes
|
|
53
|
+
|
|
54
|
+
# Development
|
|
55
|
+
mrmd-python --foreground # Run in foreground (no daemon)
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Virtual Environment Detection
|
|
59
|
+
|
|
60
|
+
When `--venv` is not specified, mrmd-python auto-detects:
|
|
61
|
+
|
|
62
|
+
1. Current venv (if running inside one via `sys.prefix`)
|
|
63
|
+
2. `VIRTUAL_ENV` environment variable
|
|
64
|
+
3. Falls back to system Python
|
|
65
|
+
|
|
66
|
+
## API Endpoints
|
|
67
|
+
|
|
68
|
+
All endpoints at `/mrp/v1/`:
|
|
69
|
+
|
|
70
|
+
| Endpoint | Method | Purpose |
|
|
71
|
+
|----------|--------|---------|
|
|
72
|
+
| `/capabilities` | GET | Runtime info and features |
|
|
73
|
+
| `/sessions` | GET/POST | List/create sessions |
|
|
74
|
+
| `/sessions/{id}` | GET/DELETE | Get/destroy session |
|
|
75
|
+
| `/execute` | POST | Run code |
|
|
76
|
+
| `/execute/stream` | POST | Run code with SSE streaming |
|
|
77
|
+
| `/complete` | POST | Get completions |
|
|
78
|
+
| `/inspect` | POST | Get symbol documentation |
|
|
79
|
+
| `/hover` | POST | Get hover tooltip |
|
|
80
|
+
| `/variables` | POST | List user variables |
|
|
81
|
+
| `/variables/{name}` | POST | Get variable details |
|
|
82
|
+
| `/is_complete` | POST | Check if code is complete |
|
|
83
|
+
| `/format` | POST | Format code with black |
|
|
84
|
+
|
|
85
|
+
## Architecture
|
|
86
|
+
|
|
87
|
+
```
|
|
88
|
+
~/.mrmd/
|
|
89
|
+
├── runtimes/
|
|
90
|
+
│ └── {id}.json # Registry: pid, port, url, venv, cwd
|
|
91
|
+
└── logs/
|
|
92
|
+
└── {id}.log # Daemon logs
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Each runtime is a fully independent process:
|
|
96
|
+
- Double-forked daemon (survives parent death)
|
|
97
|
+
- Own IPython shell with persistent variables
|
|
98
|
+
- HTTP server on auto-assigned port
|
|
99
|
+
- Registered in `~/.mrmd/runtimes/` for discovery
|
|
100
|
+
|
|
101
|
+
## GPU Memory Management
|
|
102
|
+
|
|
103
|
+
For vLLM and other GPU workloads, memory is only released when the process dies:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
# Load model in runtime
|
|
107
|
+
mrmd-python --id vllm
|
|
108
|
+
# ... use the model ...
|
|
109
|
+
|
|
110
|
+
# Release GPU memory
|
|
111
|
+
mrmd-python --kill vllm
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Programmatic Usage
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
from mrmd_python import create_app
|
|
118
|
+
import uvicorn
|
|
119
|
+
|
|
120
|
+
# Create app (daemon_mode=True for use inside daemon)
|
|
121
|
+
app = create_app(
|
|
122
|
+
cwd="/path/to/project",
|
|
123
|
+
venv="/path/to/venv",
|
|
124
|
+
daemon_mode=True,
|
|
125
|
+
)
|
|
126
|
+
uvicorn.run(app, host="localhost", port=8000)
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Protocol
|
|
130
|
+
|
|
131
|
+
See [PROTOCOL.md](../mrmd-editor/PROTOCOL.md) for the full MRP specification.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "mrmd-python"
|
|
3
|
+
version = "0.3.0"
|
|
4
|
+
description = "Python runtime server implementing the MRMD Runtime Protocol (MRP)"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.10"
|
|
7
|
+
license = { text = "MIT" }
|
|
8
|
+
authors = [{ name = "mrmd contributors" }]
|
|
9
|
+
keywords = ["notebook", "ipython", "runtime", "mrmd", "mrp"]
|
|
10
|
+
classifiers = [
|
|
11
|
+
"Development Status :: 3 - Alpha",
|
|
12
|
+
"Intended Audience :: Developers",
|
|
13
|
+
"License :: OSI Approved :: MIT License",
|
|
14
|
+
"Programming Language :: Python :: 3",
|
|
15
|
+
"Programming Language :: Python :: 3.10",
|
|
16
|
+
"Programming Language :: Python :: 3.11",
|
|
17
|
+
"Programming Language :: Python :: 3.12",
|
|
18
|
+
"Programming Language :: Python :: 3.13",
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
dependencies = [
|
|
22
|
+
"starlette>=0.40.0",
|
|
23
|
+
"uvicorn>=0.32.0",
|
|
24
|
+
"sse-starlette>=2.0.0",
|
|
25
|
+
"ipython>=8.0.0",
|
|
26
|
+
"jedi>=0.19.0",
|
|
27
|
+
"black>=25.12.0",
|
|
28
|
+
"httpx>=0.27.0", # For daemon runtime HTTP client
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
[project.optional-dependencies]
|
|
32
|
+
format = ["black>=24.0.0"]
|
|
33
|
+
dev = [
|
|
34
|
+
"pytest>=8.0.0",
|
|
35
|
+
"pytest-asyncio>=0.24.0",
|
|
36
|
+
"httpx>=0.27.0",
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
[project.scripts]
|
|
40
|
+
mrmd-python = "mrmd_python.cli:main"
|
|
41
|
+
|
|
42
|
+
[build-system]
|
|
43
|
+
requires = ["hatchling"]
|
|
44
|
+
build-backend = "hatchling.build"
|
|
45
|
+
|
|
46
|
+
[tool.hatch.build.targets.wheel]
|
|
47
|
+
packages = ["src/mrmd_python"]
|
|
48
|
+
|
|
49
|
+
[tool.pytest.ini_options]
|
|
50
|
+
asyncio_mode = "auto"
|
|
51
|
+
testpaths = ["tests"]
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""
|
|
2
|
+
mrmd-python: Python runtime server implementing the MRMD Runtime Protocol (MRP)
|
|
3
|
+
|
|
4
|
+
Usage:
|
|
5
|
+
mrmd-python [--host HOST] [--port PORT] [--cwd DIR]
|
|
6
|
+
|
|
7
|
+
Or programmatically:
|
|
8
|
+
from mrmd_python import create_app
|
|
9
|
+
app = create_app()
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from .server import create_app, MRPServer
|
|
13
|
+
from .worker import IPythonWorker
|
|
14
|
+
|
|
15
|
+
__version__ = "0.1.0"
|
|
16
|
+
__all__ = ["create_app", "MRPServer", "IPythonWorker", "__version__"]
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
"""
|
|
2
|
+
mrmd-python CLI
|
|
3
|
+
|
|
4
|
+
Independent Python runtime for MRMD notebooks.
|
|
5
|
+
|
|
6
|
+
Usage:
|
|
7
|
+
# Start a daemon runtime (default)
|
|
8
|
+
mrmd-python
|
|
9
|
+
mrmd-python --id my-session --venv /path/to/venv
|
|
10
|
+
|
|
11
|
+
# Management commands
|
|
12
|
+
mrmd-python --list # List running runtimes
|
|
13
|
+
mrmd-python --kill my-session # Kill a runtime
|
|
14
|
+
mrmd-python --kill-all # Kill all runtimes
|
|
15
|
+
mrmd-python --info my-session # Get runtime info
|
|
16
|
+
|
|
17
|
+
# Development/debugging
|
|
18
|
+
mrmd-python --foreground # Run in foreground (no daemon)
|
|
19
|
+
|
|
20
|
+
# Using with uvx (no install needed)
|
|
21
|
+
uvx mrmd-python
|
|
22
|
+
uvx mrmd-python --list
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
import argparse
|
|
26
|
+
import json
|
|
27
|
+
import os
|
|
28
|
+
import sys
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def main():
|
|
32
|
+
parser = argparse.ArgumentParser(
|
|
33
|
+
prog="mrmd-python",
|
|
34
|
+
description="Independent Python runtime for MRMD notebooks",
|
|
35
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
36
|
+
epilog="""
|
|
37
|
+
Examples:
|
|
38
|
+
mrmd-python Start default runtime (daemon mode)
|
|
39
|
+
mrmd-python --id vllm Start runtime named 'vllm'
|
|
40
|
+
mrmd-python --venv ~/.venv/ml Use specific virtual environment
|
|
41
|
+
mrmd-python --list List all running runtimes
|
|
42
|
+
mrmd-python --kill vllm Kill the 'vllm' runtime
|
|
43
|
+
mrmd-python --foreground Run in foreground (for debugging)
|
|
44
|
+
|
|
45
|
+
Virtual Environment Detection:
|
|
46
|
+
If --venv is not specified, mrmd-python auto-detects:
|
|
47
|
+
1. Current venv (if running inside one)
|
|
48
|
+
2. VIRTUAL_ENV environment variable
|
|
49
|
+
3. Falls back to system Python
|
|
50
|
+
|
|
51
|
+
Using with uvx (no install needed):
|
|
52
|
+
uvx mrmd-python Start default runtime
|
|
53
|
+
uvx mrmd-python --list List runtimes
|
|
54
|
+
""",
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
# Runtime configuration
|
|
58
|
+
parser.add_argument(
|
|
59
|
+
"--id",
|
|
60
|
+
default="default",
|
|
61
|
+
help="Runtime ID (default: 'default')",
|
|
62
|
+
)
|
|
63
|
+
parser.add_argument(
|
|
64
|
+
"--port",
|
|
65
|
+
type=int,
|
|
66
|
+
default=0,
|
|
67
|
+
help="Port to bind to (0 = auto-assign)",
|
|
68
|
+
)
|
|
69
|
+
parser.add_argument(
|
|
70
|
+
"--venv",
|
|
71
|
+
default=None,
|
|
72
|
+
help="Virtual environment path (auto-detected if not specified)",
|
|
73
|
+
)
|
|
74
|
+
parser.add_argument(
|
|
75
|
+
"--cwd",
|
|
76
|
+
default=None,
|
|
77
|
+
help="Working directory (default: current directory)",
|
|
78
|
+
)
|
|
79
|
+
parser.add_argument(
|
|
80
|
+
"--assets-dir",
|
|
81
|
+
default=None,
|
|
82
|
+
help="Directory for saving assets like plots",
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
# Execution mode
|
|
86
|
+
parser.add_argument(
|
|
87
|
+
"--foreground",
|
|
88
|
+
action="store_true",
|
|
89
|
+
help="Run in foreground instead of daemon mode",
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
# Management commands
|
|
93
|
+
parser.add_argument(
|
|
94
|
+
"--list",
|
|
95
|
+
action="store_true",
|
|
96
|
+
help="List all running runtimes",
|
|
97
|
+
)
|
|
98
|
+
parser.add_argument(
|
|
99
|
+
"--kill",
|
|
100
|
+
metavar="ID",
|
|
101
|
+
help="Kill a runtime by ID",
|
|
102
|
+
)
|
|
103
|
+
parser.add_argument(
|
|
104
|
+
"--kill-all",
|
|
105
|
+
action="store_true",
|
|
106
|
+
help="Kill all running runtimes",
|
|
107
|
+
)
|
|
108
|
+
parser.add_argument(
|
|
109
|
+
"--info",
|
|
110
|
+
metavar="ID",
|
|
111
|
+
help="Get detailed info about a runtime",
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
args = parser.parse_args()
|
|
115
|
+
|
|
116
|
+
# Import daemon functions (lazy import for fast --help)
|
|
117
|
+
from .runtime_daemon import (
|
|
118
|
+
list_runtimes,
|
|
119
|
+
kill_runtime,
|
|
120
|
+
read_runtime_info,
|
|
121
|
+
is_runtime_alive,
|
|
122
|
+
spawn_daemon,
|
|
123
|
+
run_daemon,
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
# Handle management commands
|
|
127
|
+
if args.list:
|
|
128
|
+
runtimes = list_runtimes()
|
|
129
|
+
if not runtimes:
|
|
130
|
+
print("No runtimes running")
|
|
131
|
+
print("\nStart one with: mrmd-python")
|
|
132
|
+
else:
|
|
133
|
+
print(f"{'ID':<20} {'STATUS':<8} {'PID':<10} {'PORT':<8} {'URL'}")
|
|
134
|
+
print("-" * 80)
|
|
135
|
+
for rt in runtimes:
|
|
136
|
+
status = "ALIVE" if rt.get("alive") else "DEAD"
|
|
137
|
+
print(f"{rt['id']:<20} {status:<8} {rt.get('pid', '-'):<10} {rt.get('port', '-'):<8} {rt.get('url', '-')}")
|
|
138
|
+
return
|
|
139
|
+
|
|
140
|
+
if args.kill:
|
|
141
|
+
if kill_runtime(args.kill):
|
|
142
|
+
print(f"Killed runtime '{args.kill}'")
|
|
143
|
+
else:
|
|
144
|
+
print(f"Runtime '{args.kill}' not found or already dead")
|
|
145
|
+
return
|
|
146
|
+
|
|
147
|
+
if args.kill_all:
|
|
148
|
+
runtimes = list_runtimes()
|
|
149
|
+
if not runtimes:
|
|
150
|
+
print("No runtimes to kill")
|
|
151
|
+
return
|
|
152
|
+
killed = 0
|
|
153
|
+
for rt in runtimes:
|
|
154
|
+
if kill_runtime(rt["id"]):
|
|
155
|
+
print(f"Killed '{rt['id']}'")
|
|
156
|
+
killed += 1
|
|
157
|
+
print(f"\nKilled {killed} runtime(s)")
|
|
158
|
+
return
|
|
159
|
+
|
|
160
|
+
if args.info:
|
|
161
|
+
info = read_runtime_info(args.info)
|
|
162
|
+
if info:
|
|
163
|
+
info["alive"] = is_runtime_alive(args.info)
|
|
164
|
+
print(json.dumps(info, indent=2))
|
|
165
|
+
else:
|
|
166
|
+
print(f"Runtime '{args.info}' not found")
|
|
167
|
+
return
|
|
168
|
+
|
|
169
|
+
# Determine venv
|
|
170
|
+
venv = args.venv
|
|
171
|
+
if not venv:
|
|
172
|
+
# Auto-detect venv
|
|
173
|
+
if sys.prefix != sys.base_prefix:
|
|
174
|
+
venv = sys.prefix
|
|
175
|
+
else:
|
|
176
|
+
venv = os.environ.get("VIRTUAL_ENV", sys.prefix)
|
|
177
|
+
|
|
178
|
+
# Determine cwd
|
|
179
|
+
cwd = args.cwd or os.getcwd()
|
|
180
|
+
|
|
181
|
+
# Check if runtime already exists
|
|
182
|
+
existing = read_runtime_info(args.id)
|
|
183
|
+
if existing and is_runtime_alive(args.id):
|
|
184
|
+
print(f"Runtime '{args.id}' is already running:")
|
|
185
|
+
print(f" URL: {existing.get('url')}")
|
|
186
|
+
print(f" PID: {existing.get('pid')}")
|
|
187
|
+
print(f" venv: {existing.get('venv')}")
|
|
188
|
+
print(f"\nTo kill it: mrmd-python --kill {args.id}")
|
|
189
|
+
return
|
|
190
|
+
|
|
191
|
+
if args.foreground:
|
|
192
|
+
# Run in foreground (blocking, for debugging)
|
|
193
|
+
print(f"Starting mrmd-python in foreground mode...")
|
|
194
|
+
print(f" ID: {args.id}")
|
|
195
|
+
print(f" venv: {venv}")
|
|
196
|
+
print(f" cwd: {cwd}")
|
|
197
|
+
print(f" port: {args.port or 'auto'}")
|
|
198
|
+
print(f"\nPress Ctrl+C to stop\n")
|
|
199
|
+
run_daemon(args.id, args.port, venv, cwd, args.assets_dir)
|
|
200
|
+
else:
|
|
201
|
+
# Spawn daemon (non-blocking)
|
|
202
|
+
print(f"Starting mrmd-python daemon...")
|
|
203
|
+
info = spawn_daemon(
|
|
204
|
+
runtime_id=args.id,
|
|
205
|
+
port=args.port,
|
|
206
|
+
venv=venv,
|
|
207
|
+
cwd=cwd,
|
|
208
|
+
assets_dir=args.assets_dir,
|
|
209
|
+
)
|
|
210
|
+
print(f"""
|
|
211
|
+
Runtime '{info['id']}' started:
|
|
212
|
+
URL: {info['url']}
|
|
213
|
+
PID: {info['pid']}
|
|
214
|
+
venv: {info['venv']}
|
|
215
|
+
cwd: {info['cwd']}
|
|
216
|
+
|
|
217
|
+
Management:
|
|
218
|
+
mrmd-python --list List all runtimes
|
|
219
|
+
mrmd-python --info {info['id']:<8} Get runtime info
|
|
220
|
+
mrmd-python --kill {info['id']:<8} Kill this runtime
|
|
221
|
+
""")
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
if __name__ == "__main__":
|
|
225
|
+
main()
|