mirrorneuron-api 1.0.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.
@@ -0,0 +1,192 @@
1
+ name: CI
2
+
3
+ on:
4
+ pull_request:
5
+ push:
6
+ branches:
7
+ - main
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ jobs:
13
+ ci:
14
+ name: Test and build
15
+ runs-on: ubuntu-latest
16
+
17
+ steps:
18
+ - name: Check out repository
19
+ uses: actions/checkout@v4
20
+ with:
21
+ fetch-depth: 0
22
+
23
+ - name: Detect project type
24
+ id: detect
25
+ shell: bash
26
+ run: |
27
+ set -euo pipefail
28
+
29
+ has_python_package="false"
30
+ has_python_tests="false"
31
+ has_npm="false"
32
+ has_elixir="false"
33
+ has_shell_scripts="false"
34
+
35
+ if find . -maxdepth 2 -name pyproject.toml -print -quit | grep -q .; then
36
+ has_python_package="true"
37
+ fi
38
+
39
+ if [[ -f pytest.ini || -f requirements.txt ]] || find . -path './.git' -prune -o -name 'test_*.py' -print -quit | grep -q .; then
40
+ has_python_tests="true"
41
+ fi
42
+
43
+ if [[ -f package.json ]]; then
44
+ has_npm="true"
45
+ fi
46
+
47
+ if [[ -f mix.exs ]]; then
48
+ has_elixir="true"
49
+ fi
50
+
51
+ if find . -path './.git' -prune -o -name '*.sh' -print -quit | grep -q .; then
52
+ has_shell_scripts="true"
53
+ fi
54
+
55
+ {
56
+ echo "has_python_package=$has_python_package"
57
+ echo "has_python_tests=$has_python_tests"
58
+ echo "has_npm=$has_npm"
59
+ echo "has_elixir=$has_elixir"
60
+ echo "has_shell_scripts=$has_shell_scripts"
61
+ } >> "$GITHUB_OUTPUT"
62
+
63
+ - name: Set up Python
64
+ if: steps.detect.outputs.has_python_package == 'true' || steps.detect.outputs.has_python_tests == 'true'
65
+ uses: actions/setup-python@v5
66
+ with:
67
+ python-version: "3.11"
68
+ cache: pip
69
+
70
+ - name: Set up Node.js
71
+ if: steps.detect.outputs.has_npm == 'true'
72
+ uses: actions/setup-node@v4
73
+ with:
74
+ node-version: "20"
75
+ cache: npm
76
+
77
+ - name: Set up Elixir
78
+ if: steps.detect.outputs.has_elixir == 'true'
79
+ uses: erlef/setup-beam@v1
80
+ with:
81
+ elixir-version: "1.16"
82
+ otp-version: "26"
83
+
84
+ - name: Install Python dependencies
85
+ if: steps.detect.outputs.has_python_package == 'true' || steps.detect.outputs.has_python_tests == 'true'
86
+ shell: bash
87
+ run: |
88
+ set -euo pipefail
89
+
90
+ python -m pip install --upgrade pip
91
+ python -m pip install build twine pytest
92
+
93
+ if [[ "$GITHUB_REPOSITORY" == "MirrorNeuronLab/mn-api" || "$GITHUB_REPOSITORY" == "MirrorNeuronLab/mn-cli" ]]; then
94
+ python -m pip install "mirrorneuron-python-sdk @ git+https://github.com/MirrorNeuronLab/mn-python-sdk.git"
95
+ fi
96
+
97
+ if [[ "$GITHUB_REPOSITORY" == "MirrorNeuronLab/mn-blueprints" ]]; then
98
+ python -m pip install "mirrorneuron-python-sdk @ git+https://github.com/MirrorNeuronLab/mn-python-sdk.git"
99
+ python -m pip install "mirrorneuron-blueprint-support-skill @ git+https://github.com/MirrorNeuronLab/mn-skills.git#subdirectory=blueprint_support_skill"
100
+ fi
101
+
102
+ if [[ "$GITHUB_REPOSITORY" == "MirrorNeuronLab/mn-system-tests" ]]; then
103
+ python -m pip install pytest pytest-cov requests
104
+ python -m pip install "mirrorneuron-python-sdk @ git+https://github.com/MirrorNeuronLab/mn-python-sdk.git"
105
+ python -m pip install "mn-cli @ git+https://github.com/MirrorNeuronLab/mn-cli.git"
106
+ elif [[ -f requirements.txt && "${{ steps.detect.outputs.has_python_package }}" != "true" ]]; then
107
+ python -m pip install -r requirements.txt
108
+ fi
109
+
110
+ mapfile -t pyprojects < <(find . -maxdepth 2 -name pyproject.toml | sort)
111
+ for pyproject in "${pyprojects[@]}"; do
112
+ package_dir="$(dirname "$pyproject")"
113
+ python -m pip install -e "$package_dir"
114
+ python -m pip install -e "$package_dir[test]"
115
+ done
116
+
117
+ - name: Run configured Python checks
118
+ if: steps.detect.outputs.has_python_package == 'true'
119
+ shell: bash
120
+ run: |
121
+ set -euo pipefail
122
+
123
+ if grep -R --include pyproject.toml -q '^\[tool\.ruff\]' .; then
124
+ python -m pip install ruff
125
+ python -m ruff check .
126
+ fi
127
+
128
+ if grep -R --include pyproject.toml -q '^\[tool\.mypy\]' .; then
129
+ python -m pip install mypy
130
+ python -m mypy .
131
+ fi
132
+
133
+ - name: Run Python tests
134
+ if: steps.detect.outputs.has_python_tests == 'true'
135
+ run: python -m pytest
136
+
137
+ - name: Build Python distributions
138
+ if: steps.detect.outputs.has_python_package == 'true'
139
+ shell: bash
140
+ run: |
141
+ set -euo pipefail
142
+
143
+ rm -rf dist
144
+ mkdir -p dist
145
+ mapfile -t pyprojects < <(find . -maxdepth 2 -name pyproject.toml | sort)
146
+ for pyproject in "${pyprojects[@]}"; do
147
+ package_dir="$(dirname "$pyproject")"
148
+ python -m build "$package_dir" --outdir dist
149
+ done
150
+
151
+ python -m twine check dist/*
152
+
153
+ - name: Install npm dependencies
154
+ if: steps.detect.outputs.has_npm == 'true'
155
+ run: npm ci
156
+
157
+ - name: Run npm lint
158
+ if: steps.detect.outputs.has_npm == 'true'
159
+ run: npm run lint --if-present
160
+
161
+ - name: Run npm tests
162
+ if: steps.detect.outputs.has_npm == 'true'
163
+ run: npm test --if-present
164
+
165
+ - name: Build npm project
166
+ if: steps.detect.outputs.has_npm == 'true'
167
+ run: npm run build --if-present
168
+
169
+ - name: Install Elixir dependencies
170
+ if: steps.detect.outputs.has_elixir == 'true'
171
+ run: mix deps.get
172
+
173
+ - name: Check Elixir formatting
174
+ if: steps.detect.outputs.has_elixir == 'true'
175
+ run: mix format --check-formatted
176
+
177
+ - name: Run Elixir tests
178
+ if: steps.detect.outputs.has_elixir == 'true'
179
+ run: mix test
180
+
181
+ - name: Build Elixir project
182
+ if: steps.detect.outputs.has_elixir == 'true'
183
+ run: mix compile --warnings-as-errors
184
+
185
+ - name: Check shell scripts
186
+ if: steps.detect.outputs.has_shell_scripts == 'true'
187
+ shell: bash
188
+ run: |
189
+ set -euo pipefail
190
+ while IFS= read -r -d '' script; do
191
+ bash -n "$script"
192
+ done < <(find . -path './.git' -prune -o -name '*.sh' -print0)
@@ -0,0 +1,389 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
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
13
+
14
+ permissions:
15
+ contents: read
16
+
17
+ jobs:
18
+ build:
19
+ name: Validate, test, and build
20
+ runs-on: ubuntu-latest
21
+ permissions:
22
+ contents: read
23
+ outputs:
24
+ tag_name: ${{ steps.version.outputs.tag_name }}
25
+ version: ${{ steps.version.outputs.version }}
26
+ is_prerelease: ${{ steps.version.outputs.is_prerelease }}
27
+ has_python_package: ${{ steps.detect.outputs.has_python_package }}
28
+
29
+ steps:
30
+ - name: Check out tag
31
+ uses: actions/checkout@v4
32
+ with:
33
+ fetch-depth: 0
34
+ ref: ${{ github.event_name == 'workflow_dispatch' && inputs.tag || github.ref }}
35
+
36
+ - name: Validate release tag
37
+ id: version
38
+ shell: bash
39
+ env:
40
+ INPUT_TAG: ${{ inputs.tag }}
41
+ run: |
42
+ set -euo pipefail
43
+ tag="$GITHUB_REF_NAME"
44
+ if [[ "$GITHUB_EVENT_NAME" == "workflow_dispatch" ]]; then
45
+ tag="$INPUT_TAG"
46
+ fi
47
+ scripts/validate-version-tag.sh "$tag"
48
+
49
+ - name: Detect project type
50
+ id: detect
51
+ shell: bash
52
+ run: |
53
+ set -euo pipefail
54
+
55
+ has_python_package="false"
56
+ has_python_tests="false"
57
+ has_npm="false"
58
+ has_elixir="false"
59
+ has_shell_scripts="false"
60
+
61
+ if find . -maxdepth 2 -name pyproject.toml -print -quit | grep -q .; then
62
+ has_python_package="true"
63
+ fi
64
+
65
+ if [[ -f pytest.ini || -f requirements.txt ]] || find . -path './.git' -prune -o -name 'test_*.py' -print -quit | grep -q .; then
66
+ has_python_tests="true"
67
+ fi
68
+
69
+ if [[ -f package.json ]]; then
70
+ has_npm="true"
71
+ fi
72
+
73
+ if [[ -f mix.exs ]]; then
74
+ has_elixir="true"
75
+ fi
76
+
77
+ if find . -path './.git' -prune -o -name '*.sh' -print -quit | grep -q .; then
78
+ has_shell_scripts="true"
79
+ fi
80
+
81
+ {
82
+ echo "has_python_package=$has_python_package"
83
+ echo "has_python_tests=$has_python_tests"
84
+ echo "has_npm=$has_npm"
85
+ echo "has_elixir=$has_elixir"
86
+ echo "has_shell_scripts=$has_shell_scripts"
87
+ } >> "$GITHUB_OUTPUT"
88
+
89
+ - name: Set up Python
90
+ if: steps.detect.outputs.has_python_package == 'true' || steps.detect.outputs.has_python_tests == 'true'
91
+ uses: actions/setup-python@v5
92
+ with:
93
+ python-version: "3.11"
94
+ cache: pip
95
+
96
+ - name: Set up Node.js
97
+ if: steps.detect.outputs.has_npm == 'true'
98
+ uses: actions/setup-node@v4
99
+ with:
100
+ node-version: "20"
101
+ cache: npm
102
+
103
+ - name: Set up Elixir
104
+ if: steps.detect.outputs.has_elixir == 'true'
105
+ uses: erlef/setup-beam@v1
106
+ with:
107
+ elixir-version: "1.16"
108
+ otp-version: "26"
109
+
110
+ - name: Install Python dependencies
111
+ if: steps.detect.outputs.has_python_package == 'true' || steps.detect.outputs.has_python_tests == 'true'
112
+ shell: bash
113
+ run: |
114
+ set -euo pipefail
115
+
116
+ python -m pip install --upgrade pip
117
+ python -m pip install build twine pytest packaging
118
+
119
+ if [[ "$GITHUB_REPOSITORY" == "MirrorNeuronLab/mn-api" || "$GITHUB_REPOSITORY" == "MirrorNeuronLab/mn-cli" ]]; then
120
+ python -m pip install "mirrorneuron-python-sdk @ git+https://github.com/MirrorNeuronLab/mn-python-sdk.git"
121
+ fi
122
+
123
+ if [[ "$GITHUB_REPOSITORY" == "MirrorNeuronLab/mn-blueprints" ]]; then
124
+ python -m pip install "mirrorneuron-python-sdk @ git+https://github.com/MirrorNeuronLab/mn-python-sdk.git"
125
+ python -m pip install "mirrorneuron-blueprint-support-skill @ git+https://github.com/MirrorNeuronLab/mn-skills.git#subdirectory=blueprint_support_skill"
126
+ fi
127
+
128
+ if [[ "$GITHUB_REPOSITORY" == "MirrorNeuronLab/mn-system-tests" ]]; then
129
+ python -m pip install pytest pytest-cov requests
130
+ python -m pip install "mirrorneuron-python-sdk @ git+https://github.com/MirrorNeuronLab/mn-python-sdk.git"
131
+ python -m pip install "mn-cli @ git+https://github.com/MirrorNeuronLab/mn-cli.git"
132
+ elif [[ -f requirements.txt && "${{ steps.detect.outputs.has_python_package }}" != "true" ]]; then
133
+ python -m pip install -r requirements.txt
134
+ fi
135
+
136
+ mapfile -t pyprojects < <(find . -maxdepth 2 -name pyproject.toml | sort)
137
+ for pyproject in "${pyprojects[@]}"; do
138
+ package_dir="$(dirname "$pyproject")"
139
+ python -m pip install -e "$package_dir"
140
+ python -m pip install -e "$package_dir[test]"
141
+ done
142
+
143
+ - name: Run configured Python checks
144
+ if: steps.detect.outputs.has_python_package == 'true'
145
+ shell: bash
146
+ run: |
147
+ set -euo pipefail
148
+
149
+ if grep -R --include pyproject.toml -q '^\[tool\.ruff\]' .; then
150
+ python -m pip install ruff
151
+ python -m ruff check .
152
+ fi
153
+
154
+ if grep -R --include pyproject.toml -q '^\[tool\.mypy\]' .; then
155
+ python -m pip install mypy
156
+ python -m mypy .
157
+ fi
158
+
159
+ - name: Run Python tests
160
+ if: steps.detect.outputs.has_python_tests == 'true'
161
+ run: python -m pytest
162
+
163
+ - name: Build Python distributions
164
+ if: steps.detect.outputs.has_python_package == 'true'
165
+ shell: bash
166
+ run: |
167
+ set -euo pipefail
168
+
169
+ rm -rf dist
170
+ mkdir -p dist
171
+ mapfile -t pyprojects < <(find . -maxdepth 2 -name pyproject.toml | sort)
172
+ for pyproject in "${pyprojects[@]}"; do
173
+ package_dir="$(dirname "$pyproject")"
174
+ python -m build "$package_dir" --outdir dist
175
+ done
176
+
177
+ python -m twine check dist/*
178
+
179
+ - name: Verify Python distribution versions
180
+ if: steps.detect.outputs.has_python_package == 'true'
181
+ shell: bash
182
+ run: |
183
+ set -euo pipefail
184
+
185
+ python - "$VERSION" <<'PY'
186
+ import email.parser
187
+ import glob
188
+ import pathlib
189
+ import sys
190
+ import tarfile
191
+ import zipfile
192
+
193
+ from packaging.version import Version
194
+
195
+ expected = str(Version(sys.argv[1]))
196
+ versions = {}
197
+
198
+ for wheel in glob.glob("dist/*.whl"):
199
+ with zipfile.ZipFile(wheel) as archive:
200
+ metadata_name = next(name for name in archive.namelist() if name.endswith(".dist-info/METADATA"))
201
+ metadata = email.parser.Parser().parsestr(archive.read(metadata_name).decode("utf-8"))
202
+ versions[pathlib.Path(wheel).name] = metadata["Version"]
203
+
204
+ for sdist in glob.glob("dist/*.tar.gz"):
205
+ with tarfile.open(sdist) as archive:
206
+ metadata_member = next(member for member in archive.getmembers() if member.name.endswith("/PKG-INFO"))
207
+ metadata_file = archive.extractfile(metadata_member)
208
+ if metadata_file is None:
209
+ raise SystemExit(f"Could not read PKG-INFO from {sdist}")
210
+ metadata = email.parser.Parser().parsestr(metadata_file.read().decode("utf-8"))
211
+ versions[pathlib.Path(sdist).name] = metadata["Version"]
212
+
213
+ if not versions:
214
+ raise SystemExit("No Python distributions were found in dist/.")
215
+
216
+ mismatches = {
217
+ name: version
218
+ for name, version in versions.items()
219
+ if str(Version(version)) != expected
220
+ }
221
+
222
+ if mismatches:
223
+ for name, version in mismatches.items():
224
+ print(f"{name}: expected {expected}, found {version}", file=sys.stderr)
225
+ raise SystemExit(1)
226
+
227
+ print(f"Python distribution versions match {expected}.")
228
+ PY
229
+
230
+ - name: Install npm dependencies
231
+ if: steps.detect.outputs.has_npm == 'true'
232
+ run: npm ci
233
+
234
+ - name: Apply tag version to npm metadata
235
+ if: steps.detect.outputs.has_npm == 'true'
236
+ run: npm version "$VERSION" --no-git-tag-version --allow-same-version
237
+
238
+ - name: Run npm lint
239
+ if: steps.detect.outputs.has_npm == 'true'
240
+ run: npm run lint --if-present
241
+
242
+ - name: Run npm tests
243
+ if: steps.detect.outputs.has_npm == 'true'
244
+ run: npm test --if-present
245
+
246
+ - name: Build npm project
247
+ if: steps.detect.outputs.has_npm == 'true'
248
+ run: npm run build --if-present
249
+
250
+ - name: Install Elixir dependencies
251
+ if: steps.detect.outputs.has_elixir == 'true'
252
+ run: mix deps.get
253
+
254
+ - name: Check Elixir formatting
255
+ if: steps.detect.outputs.has_elixir == 'true'
256
+ run: mix format --check-formatted
257
+
258
+ - name: Run Elixir tests
259
+ if: steps.detect.outputs.has_elixir == 'true'
260
+ run: mix test
261
+
262
+ - name: Build Elixir project
263
+ if: steps.detect.outputs.has_elixir == 'true'
264
+ env:
265
+ MIX_PROJECT_VERSION: ${{ steps.version.outputs.version }}
266
+ run: mix compile --warnings-as-errors
267
+
268
+ - name: Check shell scripts
269
+ if: steps.detect.outputs.has_shell_scripts == 'true'
270
+ shell: bash
271
+ run: |
272
+ set -euo pipefail
273
+ while IFS= read -r -d '' script; do
274
+ bash -n "$script"
275
+ done < <(find . -path './.git' -prune -o -name '*.sh' -print0)
276
+
277
+ - name: Create release ZIP
278
+ shell: bash
279
+ run: |
280
+ set -euo pipefail
281
+ mkdir -p dist/release-assets
282
+ scripts/make-release-zip.sh "$TAG_NAME" dist/release-assets
283
+
284
+ - name: Create SHA256 checksums
285
+ shell: bash
286
+ run: |
287
+ set -euo pipefail
288
+ mapfile -t artifacts < <(find dist dist/release-assets -maxdepth 1 -type f \( -name '*.zip' -o -name '*.whl' -o -name '*.tar.gz' \) | LC_ALL=C sort)
289
+ if [[ "${#artifacts[@]}" -eq 0 ]]; then
290
+ echo "No release artifacts found for checksums." >&2
291
+ exit 1
292
+ fi
293
+ sha256sum "${artifacts[@]}" > dist/SHA256SUMS.txt
294
+
295
+ - name: Validate release artifacts
296
+ shell: bash
297
+ run: |
298
+ set -euo pipefail
299
+ mapfile -t artifacts < <(find dist dist/release-assets -maxdepth 1 -type f \( -name '*.zip' -o -name '*.whl' -o -name '*.tar.gz' -o -name 'SHA256SUMS.txt' \) | LC_ALL=C sort)
300
+ scripts/check-release-artifacts.sh "${artifacts[@]}"
301
+
302
+ - name: Upload release artifacts
303
+ uses: actions/upload-artifact@v4
304
+ with:
305
+ name: release-assets
306
+ path: |
307
+ dist/release-assets/*.zip
308
+ dist/SHA256SUMS.txt
309
+ dist/*.whl
310
+ dist/*.tar.gz
311
+ if-no-files-found: error
312
+
313
+ - name: Upload Python distributions
314
+ if: steps.detect.outputs.has_python_package == 'true'
315
+ uses: actions/upload-artifact@v4
316
+ with:
317
+ name: python-dist
318
+ path: |
319
+ dist/*.whl
320
+ dist/*.tar.gz
321
+ if-no-files-found: error
322
+
323
+ github-release:
324
+ name: Create GitHub Release
325
+ runs-on: ubuntu-latest
326
+ needs: build
327
+ permissions:
328
+ contents: write
329
+
330
+ steps:
331
+ - name: Download release artifacts
332
+ uses: actions/download-artifact@v4
333
+ with:
334
+ name: release-assets
335
+ path: release-assets
336
+
337
+ - name: Create GitHub Release
338
+ shell: bash
339
+ env:
340
+ GH_TOKEN: ${{ github.token }}
341
+ GH_REPO: ${{ github.repository }}
342
+ TAG_NAME: ${{ needs.build.outputs.tag_name }}
343
+ IS_PRERELEASE: ${{ needs.build.outputs.is_prerelease }}
344
+ run: |
345
+ set -euo pipefail
346
+
347
+ if gh release view "$TAG_NAME" >/dev/null 2>&1; then
348
+ echo "Release $TAG_NAME already exists; refusing to overwrite it." >&2
349
+ exit 1
350
+ fi
351
+
352
+ mapfile -t assets < <(find release-assets -type f | LC_ALL=C sort)
353
+ if [[ "${#assets[@]}" -eq 0 ]]; then
354
+ echo "No release assets were downloaded." >&2
355
+ exit 1
356
+ fi
357
+
358
+ release_args=(release create "$TAG_NAME")
359
+ release_args+=("${assets[@]}")
360
+ release_args+=(--title "$TAG_NAME" --verify-tag --generate-notes)
361
+
362
+ if [[ "$IS_PRERELEASE" == "true" ]]; then
363
+ release_args+=(--prerelease)
364
+ fi
365
+
366
+ gh "${release_args[@]}"
367
+
368
+ publish-pypi:
369
+ name: Publish to PyPI
370
+ runs-on: ubuntu-latest
371
+ needs:
372
+ - build
373
+ - github-release
374
+ if: needs.build.outputs.has_python_package == 'true' && (needs.build.outputs.is_prerelease == 'false' || vars.PUBLISH_PRERELEASES_TO_PYPI == 'true')
375
+ permissions:
376
+ contents: read
377
+ id-token: write
378
+ environment:
379
+ name: pypi
380
+
381
+ steps:
382
+ - name: Download Python distributions
383
+ uses: actions/download-artifact@v4
384
+ with:
385
+ name: python-dist
386
+ path: dist
387
+
388
+ - name: Publish Python distributions to PyPI
389
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,34 @@
1
+ # Environments
2
+ .env
3
+ .venv
4
+ env/
5
+ venv/
6
+ ENV/
7
+ env.bak/
8
+ venv.bak/
9
+
10
+ # Build and Distribution
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ share/python-wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+
28
+ # Caches
29
+ __pycache__/
30
+ *.py[cod]
31
+ *$py.class
32
+ .pytest_cache/
33
+ .coverage
34
+ htmlcov/
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 MirrorNeuronLab
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,65 @@
1
+ Metadata-Version: 2.4
2
+ Name: mirrorneuron-api
3
+ Version: 1.0.0
4
+ Summary: MirrorNeuron REST API
5
+ License-Expression: MIT
6
+ Classifier: Programming Language :: Python :: 3
7
+ Requires-Python: >=3.9
8
+ Description-Content-Type: text/markdown
9
+ License-File: LICENSE
10
+ Requires-Dist: mirrorneuron-python-sdk
11
+ Requires-Dist: fastapi>=0.100.0
12
+ Requires-Dist: python-multipart>=0.0.9
13
+ Requires-Dist: uvicorn>=0.23.0
14
+ Provides-Extra: test
15
+ Requires-Dist: httpx>=0.27.0; extra == "test"
16
+ Dynamic: license-file
17
+
18
+ # MirrorNeuron API
19
+
20
+ The RESTful HTTP Gateway for the MirrorNeuron distributed runtime system.
21
+
22
+ ## Architecture
23
+ Built with FastAPI and Uvicorn, this component acts as an HTTP abstraction over the core `mn-python-sdk` gRPC Client. It allows external microservices, web dashboards, or non-Python applications to easily interact with the MirrorNeuron cluster without speaking Protobuf.
24
+
25
+ ## Installation
26
+ *Note: This API is installed automatically and symlinked globally as `mn-api` by the MirrorNeuron `install.sh` script.*
27
+
28
+ ```bash
29
+ pip install mirrorneuron-api
30
+ ```
31
+
32
+ ## Running the Server
33
+
34
+ ```bash
35
+ mn-api
36
+ ```
37
+ This runs the Uvicorn server on port `4001` locally.
38
+
39
+ ## Configuration
40
+
41
+ All overrides use `MN_` env vars:
42
+
43
+ - `MN_ENV=prod` requires `MN_API_TOKEN`.
44
+ - `MN_API_HOST`, `MN_API_PORT` control binding; default `localhost:4001`.
45
+ - `MN_CORE_HOST` controls the default core gRPC host; default `localhost`.
46
+ - `MN_GRPC_TARGET` or `MN_CORE_GRPC_TARGET` can override the full core gRPC target.
47
+ - `MN_GRPC_TIMEOUT_SECONDS` controls SDK calls.
48
+ - `MN_API_REQUEST_SIZE_LIMIT_BYTES` bounds request bodies.
49
+ - `MN_API_CORS_ALLOW_ORIGINS` is a comma-separated allowlist.
50
+
51
+ Protected endpoints accept `Authorization: Bearer <MN_API_TOKEN>`.
52
+
53
+ ## Endpoints
54
+
55
+ | Method | Route | Description |
56
+ |---|---|---|
57
+ | `GET` | `/api/v1/health` | Service health check |
58
+ | `GET` | `/api/v1/system/summary` | Cluster hardware and pool state |
59
+ | `GET` | `/api/v1/metrics` | Runtime metrics summary |
60
+ | `POST`| `/api/v1/jobs` | Submit a new workflow via JSON |
61
+ | `GET` | `/api/v1/jobs` | List recent workflows |
62
+ | `GET` | `/api/v1/jobs/{job_id}` | Fetch workflow status |
63
+ | `GET` | `/api/v1/jobs/{job_id}/events` | Fetch job events |
64
+ | `GET` | `/api/v1/jobs/{job_id}/dead-letters` | Inspect dead-letter events |
65
+ | `POST`| `/api/v1/jobs/{job_id}/cancel`| Cancel a running job |