linc-cli-kit 0.1.4__tar.gz → 0.1.5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {linc_cli_kit-0.1.4 → linc_cli_kit-0.1.5}/.github/workflows/test.yml +2 -0
- {linc_cli_kit-0.1.4 → linc_cli_kit-0.1.5}/CHANGELOG.md +8 -0
- {linc_cli_kit-0.1.4 → linc_cli_kit-0.1.5}/PKG-INFO +1 -1
- {linc_cli_kit-0.1.4 → linc_cli_kit-0.1.5}/pyproject.toml +1 -1
- linc_cli_kit-0.1.5/tests/test_lock_floors.py +69 -0
- {linc_cli_kit-0.1.4 → linc_cli_kit-0.1.5}/.github/workflows/release.yml +0 -0
- {linc_cli_kit-0.1.4 → linc_cli_kit-0.1.5}/CONTRIBUTING.md +0 -0
- {linc_cli_kit-0.1.4 → linc_cli_kit-0.1.5}/LICENSE +0 -0
- {linc_cli_kit-0.1.4 → linc_cli_kit-0.1.5}/NOTICE +0 -0
- {linc_cli_kit-0.1.4 → linc_cli_kit-0.1.5}/README.md +0 -0
- {linc_cli_kit-0.1.4 → linc_cli_kit-0.1.5}/SECURITY.md +0 -0
- {linc_cli_kit-0.1.4 → linc_cli_kit-0.1.5}/src/linc_cli_kit/__init__.py +0 -0
- {linc_cli_kit-0.1.4 → linc_cli_kit-0.1.5}/src/linc_cli_kit/errors.py +0 -0
- {linc_cli_kit-0.1.4 → linc_cli_kit-0.1.5}/src/linc_cli_kit/logging.py +0 -0
- {linc_cli_kit-0.1.4 → linc_cli_kit-0.1.5}/src/linc_cli_kit/mount.py +0 -0
- {linc_cli_kit-0.1.4 → linc_cli_kit-0.1.5}/src/linc_cli_kit/output.py +0 -0
- {linc_cli_kit-0.1.4 → linc_cli_kit-0.1.5}/src/linc_cli_kit/registry.py +0 -0
- {linc_cli_kit-0.1.4 → linc_cli_kit-0.1.5}/src/linc_cli_kit/schema.py +0 -0
- {linc_cli_kit-0.1.4 → linc_cli_kit-0.1.5}/src/linc_cli_kit/testing.py +0 -0
- {linc_cli_kit-0.1.4 → linc_cli_kit-0.1.5}/src/linc_cli_kit/transport.py +0 -0
- {linc_cli_kit-0.1.4 → linc_cli_kit-0.1.5}/tests/__init__.py +0 -0
- {linc_cli_kit-0.1.4 → linc_cli_kit-0.1.5}/tests/conftest.py +0 -0
- {linc_cli_kit-0.1.4 → linc_cli_kit-0.1.5}/tests/test_errors.py +0 -0
- {linc_cli_kit-0.1.4 → linc_cli_kit-0.1.5}/tests/test_json_encoding.py +0 -0
- {linc_cli_kit-0.1.4 → linc_cli_kit-0.1.5}/tests/test_logging.py +0 -0
- {linc_cli_kit-0.1.4 → linc_cli_kit-0.1.5}/tests/test_mount.py +0 -0
- {linc_cli_kit-0.1.4 → linc_cli_kit-0.1.5}/tests/test_output.py +0 -0
- {linc_cli_kit-0.1.4 → linc_cli_kit-0.1.5}/tests/test_public_docs.py +0 -0
- {linc_cli_kit-0.1.4 → linc_cli_kit-0.1.5}/tests/test_registry.py +0 -0
- {linc_cli_kit-0.1.4 → linc_cli_kit-0.1.5}/tests/test_schema.py +0 -0
- {linc_cli_kit-0.1.4 → linc_cli_kit-0.1.5}/tests/test_startup.py +0 -0
- {linc_cli_kit-0.1.4 → linc_cli_kit-0.1.5}/tests/test_testing.py +0 -0
- {linc_cli_kit-0.1.4 → linc_cli_kit-0.1.5}/tests/test_transport.py +0 -0
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to linc-cli-kit are listed here.
|
|
4
4
|
|
|
5
|
+
## [0.1.5]
|
|
6
|
+
|
|
7
|
+
- Security: the lock moves PyJWT to 2.15.1 and certifi to 2026.10.23, clearing the open
|
|
8
|
+
advisories against the earlier versions. `tests/test_lock_floors.py` fails if a re-lock
|
|
9
|
+
drops either one back below its patched version.
|
|
10
|
+
- CI: the public `test.yml` workflow runs with a read-only `GITHUB_TOKEN`
|
|
11
|
+
(`permissions: contents: read`). No library changes.
|
|
12
|
+
|
|
5
13
|
## [0.1.4]
|
|
6
14
|
|
|
7
15
|
- `strict_tools(server, *, strict_types=False)`: rejects unknown MCP tool arguments. Every
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: linc-cli-kit
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.5
|
|
4
4
|
Summary: Reflect a FastMCP tool registry into Click commands under one agent-grade output contract
|
|
5
5
|
Project-URL: Homepage, https://github.com/LincForge/cli-kit
|
|
6
6
|
Project-URL: Issues, https://github.com/LincForge/cli-kit/issues
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""Security floors for locked dependencies and least-privilege CI permissions."""
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
import tomllib
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
import pytest
|
|
8
|
+
|
|
9
|
+
REPO = Path(__file__).resolve().parents[1]
|
|
10
|
+
LOCK = REPO / "uv.lock"
|
|
11
|
+
|
|
12
|
+
# Patched versions for open Dependabot advisories.
|
|
13
|
+
FLOORS = {
|
|
14
|
+
"pyjwt": (2, 15, 0),
|
|
15
|
+
"certifi": (2026, 10, 18),
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _version_tuple(version: str) -> tuple[int, ...]:
|
|
20
|
+
parts = []
|
|
21
|
+
for piece in version.split("."):
|
|
22
|
+
match = re.match(r"\d+", piece)
|
|
23
|
+
if match is None:
|
|
24
|
+
break
|
|
25
|
+
parts.append(int(match.group()))
|
|
26
|
+
if match.group() != piece:
|
|
27
|
+
break
|
|
28
|
+
return tuple(parts)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _workflows() -> Path:
|
|
32
|
+
# Private repo: templates under public/.github (the export maps them to .github).
|
|
33
|
+
public = REPO / "public" / ".github" / "workflows"
|
|
34
|
+
return public if public.is_dir() else REPO / ".github" / "workflows"
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _top_level_permissions(workflow: str) -> dict[str, str]:
|
|
38
|
+
lines = workflow.splitlines()
|
|
39
|
+
start = lines.index("permissions:")
|
|
40
|
+
perms = {}
|
|
41
|
+
for line in lines[start + 1 :]:
|
|
42
|
+
if not line.startswith(" "):
|
|
43
|
+
break
|
|
44
|
+
key, _, value = line.strip().partition(":")
|
|
45
|
+
perms[key] = value.strip()
|
|
46
|
+
return perms
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def test_locked_packages_meet_security_floors() -> None:
|
|
50
|
+
if not LOCK.exists():
|
|
51
|
+
pytest.skip("uv.lock is not part of the public mirror export")
|
|
52
|
+
locked = {
|
|
53
|
+
pkg["name"].lower(): pkg["version"] for pkg in tomllib.loads(LOCK.read_text())["package"]
|
|
54
|
+
}
|
|
55
|
+
for name, floor in FLOORS.items():
|
|
56
|
+
assert name in locked, f"{name} missing from uv.lock"
|
|
57
|
+
assert _version_tuple(locked[name]) >= floor, (
|
|
58
|
+
f"{name} {locked[name]} is below the patched floor {floor}"
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def test_test_workflow_token_is_read_only() -> None:
|
|
63
|
+
workflow = (_workflows() / "test.yml").read_text()
|
|
64
|
+
assert _top_level_permissions(workflow) == {"contents": "read"}
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def test_version_tuple_parses_prerelease_suffix() -> None:
|
|
68
|
+
assert _version_tuple("2.15.0rc1") == (2, 15, 0)
|
|
69
|
+
assert _version_tuple("2026.7.22") < (2026, 10, 18)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|