harbor-aiops 0.2.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.
Files changed (67) hide show
  1. harbor_aiops-0.2.0/.claude-plugin/plugin.json +22 -0
  2. harbor_aiops-0.2.0/.dockerignore +7 -0
  3. harbor_aiops-0.2.0/.github/workflows/ci.yml +28 -0
  4. harbor_aiops-0.2.0/.github/workflows/mcp-publish.yml +57 -0
  5. harbor_aiops-0.2.0/.github/workflows/publish.yml +28 -0
  6. harbor_aiops-0.2.0/.gitignore +23 -0
  7. harbor_aiops-0.2.0/.mcp.json +13 -0
  8. harbor_aiops-0.2.0/CHANGELOG.md +13 -0
  9. harbor_aiops-0.2.0/Dockerfile +6 -0
  10. harbor_aiops-0.2.0/LICENSE +21 -0
  11. harbor_aiops-0.2.0/PKG-INFO +60 -0
  12. harbor_aiops-0.2.0/README.md +44 -0
  13. harbor_aiops-0.2.0/SECURITY.md +7 -0
  14. harbor_aiops-0.2.0/docs/VERIFICATION.md +15 -0
  15. harbor_aiops-0.2.0/glama.json +6 -0
  16. harbor_aiops-0.2.0/harbor_aiops/__init__.py +8 -0
  17. harbor_aiops-0.2.0/harbor_aiops/cli/__init__.py +9 -0
  18. harbor_aiops-0.2.0/harbor_aiops/cli/_common.py +122 -0
  19. harbor_aiops-0.2.0/harbor_aiops/cli/_root.py +446 -0
  20. harbor_aiops-0.2.0/harbor_aiops/cli/secret.py +106 -0
  21. harbor_aiops-0.2.0/harbor_aiops/cli/undo.py +66 -0
  22. harbor_aiops-0.2.0/harbor_aiops/config.py +95 -0
  23. harbor_aiops-0.2.0/harbor_aiops/connection.py +188 -0
  24. harbor_aiops-0.2.0/harbor_aiops/governance/__init__.py +50 -0
  25. harbor_aiops-0.2.0/harbor_aiops/governance/audit.py +521 -0
  26. harbor_aiops-0.2.0/harbor_aiops/governance/budget.py +222 -0
  27. harbor_aiops-0.2.0/harbor_aiops/governance/decorators.py +574 -0
  28. harbor_aiops-0.2.0/harbor_aiops/governance/outcome.py +104 -0
  29. harbor_aiops-0.2.0/harbor_aiops/governance/paths.py +23 -0
  30. harbor_aiops-0.2.0/harbor_aiops/governance/patterns.py +378 -0
  31. harbor_aiops-0.2.0/harbor_aiops/governance/policy.py +67 -0
  32. harbor_aiops-0.2.0/harbor_aiops/governance/sanitize.py +79 -0
  33. harbor_aiops-0.2.0/harbor_aiops/governance/undo.py +257 -0
  34. harbor_aiops-0.2.0/harbor_aiops/mcp_server/__init__.py +1 -0
  35. harbor_aiops-0.2.0/harbor_aiops/mcp_server/_annotations.py +66 -0
  36. harbor_aiops-0.2.0/harbor_aiops/mcp_server/_shared.py +87 -0
  37. harbor_aiops-0.2.0/harbor_aiops/mcp_server/server.py +14 -0
  38. harbor_aiops-0.2.0/harbor_aiops/mcp_server/tools/__init__.py +1 -0
  39. harbor_aiops-0.2.0/harbor_aiops/mcp_server/tools/registry.py +313 -0
  40. harbor_aiops-0.2.0/harbor_aiops/mcp_server/tools/undo.py +204 -0
  41. harbor_aiops-0.2.0/harbor_aiops/mcp_server/tools/writes.py +46 -0
  42. harbor_aiops-0.2.0/harbor_aiops/ops/__init__.py +1 -0
  43. harbor_aiops-0.2.0/harbor_aiops/ops/registry.py +258 -0
  44. harbor_aiops-0.2.0/harbor_aiops/ops/writes.py +117 -0
  45. harbor_aiops-0.2.0/harbor_aiops/secretstore.py +306 -0
  46. harbor_aiops-0.2.0/openclaw.plugin.json +24 -0
  47. harbor_aiops-0.2.0/package.json +20 -0
  48. harbor_aiops-0.2.0/pyproject.toml +61 -0
  49. harbor_aiops-0.2.0/server.json +21 -0
  50. harbor_aiops-0.2.0/skills/harbor-aiops/SKILL.md +24 -0
  51. harbor_aiops-0.2.0/skills/harbor-aiops/references/agent-guardrails.md +13 -0
  52. harbor_aiops-0.2.0/skills/harbor-aiops/references/capabilities.md +5 -0
  53. harbor_aiops-0.2.0/skills/harbor-aiops/references/cli-reference.md +13 -0
  54. harbor_aiops-0.2.0/skills/harbor-aiops/references/setup-guide.md +7 -0
  55. harbor_aiops-0.2.0/tests/test_audit_guardrails.py +209 -0
  56. harbor_aiops-0.2.0/tests/test_cli_audited.py +48 -0
  57. harbor_aiops-0.2.0/tests/test_cli_governed_exit.py +18 -0
  58. harbor_aiops-0.2.0/tests/test_gov_audit.py +292 -0
  59. harbor_aiops-0.2.0/tests/test_gov_decorators.py +433 -0
  60. harbor_aiops-0.2.0/tests/test_gov_patterns.py +417 -0
  61. harbor_aiops-0.2.0/tests/test_gov_policy.py +56 -0
  62. harbor_aiops-0.2.0/tests/test_governance_persistence.py +331 -0
  63. harbor_aiops-0.2.0/tests/test_package_layout.py +62 -0
  64. harbor_aiops-0.2.0/tests/test_registry.py +361 -0
  65. harbor_aiops-0.2.0/tests/test_secretstore.py +99 -0
  66. harbor_aiops-0.2.0/tests/test_smoke.py +28 -0
  67. harbor_aiops-0.2.0/tests/test_tool_annotations.py +139 -0
@@ -0,0 +1,22 @@
1
+ {
2
+ "$schema": "https://anthropic.com/claude-code/plugin.schema.json",
3
+ "name": "harbor-aiops",
4
+ "displayName": "Harbor AIops",
5
+ "version": "0.2.0",
6
+ "description": "Governed Harbor registry operations — 39 MCP tools with audit.",
7
+ "author": {
8
+ "name": "Wei Zhou",
9
+ "email": "zhouwei008@gmail.com",
10
+ "url": "https://github.com/AIops-tools"
11
+ },
12
+ "homepage": "https://github.com/AIops-tools/Harbor-AIops",
13
+ "repository": "https://github.com/AIops-tools/Harbor-AIops",
14
+ "license": "MIT",
15
+ "keywords": [
16
+ "harbor",
17
+ "container-registry",
18
+ "oci",
19
+ "mcp",
20
+ "aiops"
21
+ ]
22
+ }
@@ -0,0 +1,7 @@
1
+ *
2
+ !Dockerfile
3
+ !pyproject.toml
4
+ !README.md
5
+ !LICENSE
6
+ !harbor_aiops/
7
+ !harbor_aiops/**
@@ -0,0 +1,28 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [bootstrap, main]
6
+ pull_request:
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ jobs:
12
+ quality:
13
+ runs-on: ubuntu-latest
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+ - uses: astral-sh/setup-uv@v5
17
+ - name: Install Python and dependencies
18
+ run: |
19
+ uv python install 3.11
20
+ uv sync --dev --python 3.11
21
+ - name: Tests
22
+ run: uv run pytest -q
23
+ - name: Lint
24
+ run: uv run ruff check .
25
+ - name: Security scan
26
+ run: uv run bandit -r harbor_aiops -ll -q
27
+ - name: Build
28
+ run: uv build
@@ -0,0 +1,57 @@
1
+ name: mcp-publish
2
+
3
+ # Registry publication follows the Harbor wheel's first appearance on PyPI.
4
+
5
+ on:
6
+ workflow_dispatch:
7
+ release:
8
+ types: [published]
9
+
10
+ permissions:
11
+ id-token: write
12
+ contents: read
13
+
14
+ jobs:
15
+ publish-mcp:
16
+ runs-on: ubuntu-latest
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+ - name: Wait for PyPI
20
+ # The release event also triggers the PyPI publish workflow; the MCP
21
+ # registry validates that the package version exists on PyPI, so poll
22
+ # until it has propagated (every 15s, up to 10 minutes).
23
+ run: |
24
+ python3 - <<'EOF'
25
+ import json
26
+ import sys
27
+ import time
28
+ import urllib.error
29
+ import urllib.request
30
+
31
+ with open("server.json", encoding="utf-8") as f:
32
+ package = json.load(f)["packages"][0]
33
+ name, version = package["identifier"], package["version"]
34
+ url = f"https://pypi.org/pypi/{name}/{version}/json"
35
+ deadline = time.monotonic() + 600
36
+ while True:
37
+ try:
38
+ with urllib.request.urlopen(url, timeout=10):
39
+ print(f"{name}=={version} is available on PyPI.")
40
+ sys.exit(0)
41
+ except (urllib.error.URLError, OSError) as exc:
42
+ print(f"{name}=={version} not on PyPI yet ({exc}); "
43
+ "retrying in 15s...")
44
+ if time.monotonic() >= deadline:
45
+ sys.exit(f"Timed out after 10 minutes waiting for "
46
+ f"{name}=={version} to appear on PyPI. "
47
+ "Check the PyPI publish workflow, then re-run "
48
+ "this workflow via workflow_dispatch.")
49
+ time.sleep(15)
50
+ EOF
51
+ - name: Install mcp-publisher
52
+ run: |
53
+ curl -sL "https://github.com/modelcontextprotocol/registry/releases/latest/download/mcp-publisher_$(uname -s | tr '[:upper:]' '[:lower:]')_$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/').tar.gz" | tar xz mcp-publisher
54
+ - name: Login to MCP Registry (GitHub OIDC)
55
+ run: ./mcp-publisher login github-oidc
56
+ - name: Publish server.json
57
+ run: ./mcp-publisher publish
@@ -0,0 +1,28 @@
1
+ name: Publish to PyPI
2
+
3
+ # First public package release uses the Harbor-specific pending PyPI publisher.
4
+
5
+ # Trusted Publishing (OIDC) — publishes from GitHub's runners with no API token,
6
+ # sidestepping the local-IP / account new-project rate limit. Configure a matching
7
+ # "trusted publisher" for this package on PyPI (see the repo release notes).
8
+ on:
9
+ release:
10
+ types: [published]
11
+ workflow_dispatch:
12
+
13
+ permissions:
14
+ contents: read
15
+
16
+ jobs:
17
+ publish:
18
+ runs-on: ubuntu-latest
19
+ permissions:
20
+ id-token: write # required for PyPI Trusted Publishing (OIDC)
21
+ steps:
22
+ - uses: actions/checkout@v4
23
+ - name: Set up uv
24
+ uses: astral-sh/setup-uv@v5
25
+ - name: Build sdist + wheel
26
+ run: uv build
27
+ - name: Publish to PyPI (Trusted Publishing)
28
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,23 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ .pytest_cache/
6
+ .ruff_cache/
7
+
8
+ # Virtual envs / build
9
+ .venv/
10
+ dist/
11
+ build/
12
+
13
+ # uv
14
+ uv.lock
15
+
16
+ # Local config / secrets (never commit)
17
+ *.env
18
+ .env
19
+ config.yaml
20
+
21
+ # OS
22
+ .DS_Store
23
+ .coverage
@@ -0,0 +1,13 @@
1
+ {
2
+ "mcpServers": {
3
+ "harbor-aiops": {
4
+ "type": "stdio",
5
+ "command": "uvx",
6
+ "args": [
7
+ "--from",
8
+ "harbor-aiops==0.2.0",
9
+ "harbor-aiops-mcp"
10
+ ]
11
+ }
12
+ }
13
+ }
@@ -0,0 +1,13 @@
1
+ # Changelog
2
+
3
+ ## v0.2.0 — 2026-11-05
4
+
5
+ - Expand to 39 governed MCP/CLI tools for project, repository, artifact, tag, vulnerability, quota, GC, replication and retention operations.
6
+ - Add bounded job logs, measured pagination truncation and explicit failed probes.
7
+ - Add guarded artifact scan, tag creation and exact mutable-tag deletion; preserve unknown outcomes on asynchronous work or lost responses.
8
+ - Limit Docker build context and image copies to package source and packaging files.
9
+ - Add GitHub CI for tests, lint, security scan and build.
10
+
11
+ ## 0.1.0
12
+
13
+ - Initial local development version; not published.
@@ -0,0 +1,6 @@
1
+ FROM python:3.13-slim
2
+ WORKDIR /app
3
+ COPY pyproject.toml README.md LICENSE /app/
4
+ COPY harbor_aiops /app/harbor_aiops
5
+ RUN pip install --no-cache-dir /app
6
+ ENTRYPOINT ["harbor-aiops-mcp"]
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 wei <zhouwei008@gmail.com>
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,60 @@
1
+ Metadata-Version: 2.5
2
+ Name: harbor-aiops
3
+ Version: 0.2.0
4
+ Summary: Governed Harbor registry diagnosis for health, storage, GC, replication and retention.
5
+ Author-email: Wei Zhou <zhouwei008@gmail.com>
6
+ License-Expression: MIT
7
+ License-File: LICENSE
8
+ Requires-Python: >=3.11
9
+ Requires-Dist: cryptography>=42.0
10
+ Requires-Dist: httpx<1.0,>=0.27
11
+ Requires-Dist: mcp[cli]<3.0,>=2.0
12
+ Requires-Dist: pyyaml<7.0,>=6.0
13
+ Requires-Dist: rich<16.0,>=13.0
14
+ Requires-Dist: typer<1.0,>=0.12
15
+ Description-Content-Type: text/markdown
16
+
17
+ <!-- mcp-name: io.github.AIops-tools/harbor-aiops -->
18
+ # Harbor AIops
19
+
20
+ Governed Harbor container-registry operations with 39 MCP tools and corresponding CLI commands. Every call is audited and budgeted by the bundled governance harness. Artifact scan, tag creation and high-risk tag deletion have dry-run previews; uncertain results are reported as `outcomeUnknown`.
21
+
22
+ ## What it checks
23
+
24
+ - Component health and system volume use.
25
+ - Projects, repository inventory, artifacts, tags, vulnerability reports, quotas and project audit logs.
26
+ - Registry statistics, project members, tag immutability rules, labels and robot metadata (secrets redacted).
27
+ - Latest registry-wide and scheduled scan metrics, plus bounded artifact scan report logs.
28
+ - Guarded deletion of one proven, mutable tag with an absence post-check.
29
+ - Garbage collection execution history.
30
+ - GC execution details and bounded plain-text logs.
31
+ - Replication policies, executions, tasks and bounded task logs.
32
+ - Retention policy, execution, tasks and bounded task logs by actual IDs.
33
+ - `harbor_diagnose` combines health, volume, GC and replication observations. Failed probes appear as errors with `data: null`.
34
+
35
+ Lists have `items`, `returned`, `limit`, and measured `truncated` fields. They are capped at 500 records per call.
36
+
37
+ ## Setup from source
38
+
39
+ Requires Python 3.11+, `uv`, and a Harbor instance. Install locally with `uv sync --dev`. Run `uv run harbor-aiops init` to configure an HTTPS target and encrypt its password, or create `~/.harbor-aiops/config.yaml` with a `targets` list:
40
+
41
+ ```yaml
42
+ targets:
43
+ - name: primary
44
+ host: harbor.example.com
45
+ username: robot$ops
46
+ scheme: https
47
+ port: 443
48
+ verify_ssl: true
49
+ timeout: 30
50
+ ```
51
+
52
+ Run `uv run harbor-aiops secret set primary`, then `uv run harbor-aiops doctor` and `uv run harbor-aiops diagnose`. The MCP entry point is `uv run harbor-aiops-mcp`. Set `HARBOR_AIOPS_MASTER_PASSWORD` for noninteractive secret-store access. TLS verification is enabled by default; `ca_file` accepts a custom CA PEM.
53
+
54
+ ## Scope and verification
55
+
56
+ The `scan-artifact` command starts an asynchronous scan and reports an unknown final outcome until its status is checked. `create-tag` proves the tag was absent and checks that it appeared after the write. `delete-tag` requires an exact mutable tag on the specified digest, previews the target and confirms absence after deletion. Neither tag write mints an undo token because immutability and later artifact changes can make the inverse unsafe. Retention execution, replication restart, GC start/stop, artifact deletion and policy changes remain outside this release until their actual outcomes and recovery paths can be verified on disposable Harbor. See [verification](docs/VERIFICATION.md). Missing operations and live-environment feedback are welcome as issues or PRs.
57
+
58
+ ## Development
59
+
60
+ `uv run pytest -q`, `uv run ruff check .`, and `uv run bandit -r harbor_aiops -ll` are local checks. Root handbook gates must pass before release.
@@ -0,0 +1,44 @@
1
+ <!-- mcp-name: io.github.AIops-tools/harbor-aiops -->
2
+ # Harbor AIops
3
+
4
+ Governed Harbor container-registry operations with 39 MCP tools and corresponding CLI commands. Every call is audited and budgeted by the bundled governance harness. Artifact scan, tag creation and high-risk tag deletion have dry-run previews; uncertain results are reported as `outcomeUnknown`.
5
+
6
+ ## What it checks
7
+
8
+ - Component health and system volume use.
9
+ - Projects, repository inventory, artifacts, tags, vulnerability reports, quotas and project audit logs.
10
+ - Registry statistics, project members, tag immutability rules, labels and robot metadata (secrets redacted).
11
+ - Latest registry-wide and scheduled scan metrics, plus bounded artifact scan report logs.
12
+ - Guarded deletion of one proven, mutable tag with an absence post-check.
13
+ - Garbage collection execution history.
14
+ - GC execution details and bounded plain-text logs.
15
+ - Replication policies, executions, tasks and bounded task logs.
16
+ - Retention policy, execution, tasks and bounded task logs by actual IDs.
17
+ - `harbor_diagnose` combines health, volume, GC and replication observations. Failed probes appear as errors with `data: null`.
18
+
19
+ Lists have `items`, `returned`, `limit`, and measured `truncated` fields. They are capped at 500 records per call.
20
+
21
+ ## Setup from source
22
+
23
+ Requires Python 3.11+, `uv`, and a Harbor instance. Install locally with `uv sync --dev`. Run `uv run harbor-aiops init` to configure an HTTPS target and encrypt its password, or create `~/.harbor-aiops/config.yaml` with a `targets` list:
24
+
25
+ ```yaml
26
+ targets:
27
+ - name: primary
28
+ host: harbor.example.com
29
+ username: robot$ops
30
+ scheme: https
31
+ port: 443
32
+ verify_ssl: true
33
+ timeout: 30
34
+ ```
35
+
36
+ Run `uv run harbor-aiops secret set primary`, then `uv run harbor-aiops doctor` and `uv run harbor-aiops diagnose`. The MCP entry point is `uv run harbor-aiops-mcp`. Set `HARBOR_AIOPS_MASTER_PASSWORD` for noninteractive secret-store access. TLS verification is enabled by default; `ca_file` accepts a custom CA PEM.
37
+
38
+ ## Scope and verification
39
+
40
+ The `scan-artifact` command starts an asynchronous scan and reports an unknown final outcome until its status is checked. `create-tag` proves the tag was absent and checks that it appeared after the write. `delete-tag` requires an exact mutable tag on the specified digest, previews the target and confirms absence after deletion. Neither tag write mints an undo token because immutability and later artifact changes can make the inverse unsafe. Retention execution, replication restart, GC start/stop, artifact deletion and policy changes remain outside this release until their actual outcomes and recovery paths can be verified on disposable Harbor. See [verification](docs/VERIFICATION.md). Missing operations and live-environment feedback are welcome as issues or PRs.
41
+
42
+ ## Development
43
+
44
+ `uv run pytest -q`, `uv run ruff check .`, and `uv run bandit -r harbor_aiops -ll` are local checks. Root handbook gates must pass before release.
@@ -0,0 +1,7 @@
1
+ # Security
2
+
3
+ Report vulnerabilities privately through the repository's GitHub security advisory flow when the repository is available. Do not put credentials, registry URLs with embedded secrets, or private image details into public issues.
4
+
5
+ Credentials are stored in the encrypted local `secrets.enc` store. The config file contains only target location and username. TLS verification is on by default, and environment proxy settings are not inherited for registry requests. The bundled governance harness records audit metadata and enforces budgets. Use a Harbor account with the least server-side privileges needed for the requested queries and optional scan/tag writes. Scan, tag creation and high-risk single-tag deletion use dry-run; deletion requires exact target and mutable-status checks, CLI double confirmation, and a post-read. A lost response or failed post-read is marked unknown. Job logs and vulnerability details may contain sensitive data, so share their output only with intended operators.
6
+
7
+ The Docker build context and image copy include only packaging files and `harbor_aiops/`; local config, encrypted secrets, audit databases and virtual environments are excluded.
@@ -0,0 +1,15 @@
1
+ # Verification — Harbor AIops
2
+
3
+ ## Source-backed API surface
4
+
5
+ The Harbor [v2.0 Swagger schema](https://github.com/goharbor/harbor/blob/main/api/v2.0/swagger.yaml), checked 2026-11-04, defines the project/repository/artifact/tag, quota, GC, replication, retention and project-log paths used here, plus `/statistics`, project members, immutable-tag rules, robot accounts, labels, both scan-metrics endpoints and artifact scan logs. It explicitly requires a slash in a `repository_name` path argument to be encoded twice. GC, replication-task and retention-task logs produce `text/plain`. Artifact scan is `POST .../scan` with HTTP 202; tag creation is `POST .../tags` with HTTP 201. The schema defines exact tag deletion as `DELETE .../tags/{tag_name}` with HTTP 200. Collection responses use `page` and `page_size`; the implementation reads an extra row to measure truncation.
6
+
7
+ ## Local verification
8
+
9
+ `tests/test_registry.py` uses a strict HTTP transport fake based on these paths and a loopback HTTP server for proxy isolation. It covers authentication, multi-page GC history, exact-limit results, 403 responses, repository double encoding, bounded plain-text logs, dry-run tag creation, post-read tag verification, exact mutable-tag deletion with dry-run and unknown post-check outcomes, robot-secret redaction, new read endpoint shapes, async scan uncertainty, lost write response, HTTP 500 ambiguity, redirect rejection and a partially failing diagnosis. Governance invariant tests come from the current tool line. Current result: 146 passing tests, Ruff clean, Bandit 0 Medium+ and wheel/sdist build, rerun on 2026-11-05.
10
+
11
+ The current 0.2.0 candidate wheel was rebuilt and installed into a new environment outside the source tree; its MCP registry reports 39 tools. Its `delete-tag` CLI ran preview and real calls against a strict loopback Harbor HTTP stub; preview made no DELETE, the real call made exactly one DELETE and reported success only after the tag GET returned 404. An earlier 0.2.0 candidate wheel was also installed outside the source tree; `harbor_aiops.__file__` resolved under `site-packages`. Its CLI ran four cases against a strict loopback HTTP stub: health, project listing, tag preview and real tag creation. The stub saw exactly one POST; the real create returned success only after a confirming GET. This validates packaging and request shape, not a real Harbor server.
12
+
13
+ ## Not yet live verified
14
+
15
+ No Harbor instance was available: Docker is installed but its daemon was not running. No real Harbor API response, permissions matrix, asynchronous job completion, GC failure, retention dry-run, or write cycle has been verified. These writes record no undo token; deleting a tag cannot be safely reversed after it may be rebound or become immutable. Do not describe the package as production validated. The next gate is a disposable Harbor deployment with deliberately failed replication/retention and comparison to Harbor's own UI/API.
@@ -0,0 +1,6 @@
1
+ {
2
+ "$schema": "https://glama.ai/mcp/schemas/server.json",
3
+ "maintainers": [
4
+ "zw008"
5
+ ]
6
+ }
@@ -0,0 +1,8 @@
1
+ """Harbor AIops: governed container registry operations for AI agents."""
2
+
3
+ from importlib.metadata import PackageNotFoundError, version
4
+
5
+ try:
6
+ __version__ = version("harbor-aiops")
7
+ except PackageNotFoundError:
8
+ __version__ = "0.0.0+unknown"
@@ -0,0 +1,9 @@
1
+ """CLI package for harbor-aiops.
2
+
3
+ Re-exports ``app`` so the pyproject entry point
4
+ ``harbor-aiops = "harbor_aiops.cli:app"`` works unchanged.
5
+ """
6
+
7
+ from harbor_aiops.cli._root import app
8
+
9
+ __all__ = ["app"]
@@ -0,0 +1,122 @@
1
+ """Shared helpers for harbor-aiops CLI sub-modules."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import functools
6
+ from collections.abc import Callable
7
+ from typing import Annotated, Any
8
+
9
+ import typer
10
+ from rich.console import Console
11
+ from rich.markup import escape
12
+
13
+ console = Console()
14
+
15
+ # ─── Shared Option types ───────────────────────────────────────────────────
16
+
17
+ TargetOption = Annotated[
18
+ str | None, typer.Option("--target", "-t", help="Target name from config")
19
+ ]
20
+ DryRunOption = Annotated[
21
+ bool, typer.Option("--dry-run", help="Print the API call without executing")
22
+ ]
23
+
24
+
25
+ def _cli_error_types() -> tuple[type[BaseException], ...]:
26
+ """Exceptions translated to a one-line teaching error instead of a traceback.
27
+
28
+ ``BudgetExceeded`` (and the retained ``PolicyDenied`` type) are raised by
29
+ ``@governed_tool`` OUTSIDE the tool body, so ``tool_errors`` never sees them
30
+ and they never arrive as an ``{"error": ...}`` dict. Their message is the
31
+ teaching text (e.g. which budget was hit) — without them here such a refusal
32
+ reaches the CLI as a traceback instead.
33
+ """
34
+ from harbor_aiops.connection import HarborApiError
35
+ from harbor_aiops.governance import BudgetExceeded, PolicyDenied
36
+ from harbor_aiops.secretstore import SecretStoreError
37
+
38
+ return (HarborApiError, PolicyDenied, BudgetExceeded, SecretStoreError, KeyError, OSError,
39
+ ValueError)
40
+
41
+
42
+ def cli_errors(fn: Callable) -> Callable:
43
+ """Translate known exceptions into one red line + exit code 1."""
44
+
45
+ @functools.wraps(fn)
46
+ def wrapper(*args: Any, **kwargs: Any) -> Any:
47
+ try:
48
+ return fn(*args, **kwargs)
49
+ except (typer.Exit, typer.Abort):
50
+ raise
51
+ except _cli_error_types() as e:
52
+ message = str(e)
53
+ if isinstance(e, KeyError):
54
+ message = f"Missing required key or environment variable: {message}"
55
+ # Upstream text can contain "[/bold]"; unescaped, rich raises on it and the
56
+ # real error is replaced by a MarkupError traceback.
57
+ console.print(f"[red]Error: {escape(message)}[/]")
58
+ raise typer.Exit(1) from e
59
+
60
+ return wrapper
61
+
62
+
63
+ #: Exit status for a write whose outcome could not be determined — kept distinct
64
+ #: from 0 (confirmed) and 1 (failed) so a script can tell all three apart.
65
+ EXIT_UNDETERMINED = 2
66
+
67
+
68
+ def governed(result: Any) -> dict:
69
+ """Return a governed tool's result, or print its error and exit 1.
70
+
71
+ The ``harbor_aiops.mcp_server.tools`` twins never raise: ``@tool_errors`` flattens every
72
+ failure — an unreachable service, a refused login, a bad argument — into
73
+ ``{"error": ...}``. A CLI command that drops that on the floor prints an empty
74
+ table and exits 0 as if the service had nothing to report. Route every governed
75
+ call through here.
76
+ """
77
+ # ``outcomeUnknown`` is judged BEFORE ``error``, matching the harness: a
78
+ # write whose response was lost carries BOTH keys, and it is audited
79
+ # `unknown` precisely because it may have taken effect. Reporting that as a
80
+ # plain failure would tell a script the change did not happen and invite the
81
+ # double-apply the payload's own note warns about.
82
+ if isinstance(result, dict) and result.get("outcomeUnknown"):
83
+ console.print(f"[yellow]Outcome undetermined: {escape(str(result.get('note') or ''))}[/]")
84
+ raise typer.Exit(EXIT_UNDETERMINED)
85
+ if isinstance(result, dict) and result.get("error"):
86
+ console.print(f"[red]Error: {escape(str(result['error']))}[/]")
87
+ raise typer.Exit(1)
88
+ return result if isinstance(result, dict) else {}
89
+
90
+
91
+ def dry_run_print(*, operation: str, api_call: str, parameters: dict | None = None) -> None:
92
+ """Print a dry-run preview of the API call that would be made."""
93
+ console.print("\n[bold magenta][DRY-RUN] No changes will be made.[/]")
94
+ console.print(f"[magenta] Operation: {operation}[/]")
95
+ console.print(f"[magenta] API Call: {api_call}[/]")
96
+ for k, v in (parameters or {}).items():
97
+ console.print(f"[magenta] Param: {k} = {v}[/]")
98
+ console.print("[magenta] Run without --dry-run to execute.[/]\n")
99
+
100
+
101
+ def double_confirm(action: str, resource: str) -> None:
102
+ """Require two confirmations for a destructive operation."""
103
+ console.print(f"[bold yellow]⚠️ About to: {action} '{resource}'[/]")
104
+ typer.confirm(f"Confirm 1/2: {action} '{resource}'?", abort=True)
105
+ typer.confirm(
106
+ f"Confirm 2/2: really {action} '{resource}'? This may be irreversible.",
107
+ abort=True,
108
+ )
109
+
110
+
111
+ def print_truncation_note(result: dict, noun: str) -> None:
112
+ """Print a visible warning when a listing envelope was capped.
113
+
114
+ The JSON already carries ``truncated``, but a human scrolling a long dump
115
+ will not spot it — and neither will a model summarising the terminal. Say
116
+ it in words, on the last line, where it cannot be missed.
117
+ """
118
+ if isinstance(result, dict) and result.get("truncated"):
119
+ console.print(
120
+ f"[yellow]… showing {result.get('returned')} of more {noun} — "
121
+ f"truncated, re-run with a higher --limit[/]"
122
+ )