arbiter-server 0.9.1.dev1__py3-none-any.whl

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,60 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ from pathlib import Path
5
+ import sys
6
+
7
+
8
+ class PluginStorage:
9
+ def __init__(self, *, plugin_name: str, root: Path) -> None:
10
+ self._plugin_name = plugin_name
11
+ self._root = plugin_data_dir(root, plugin_name)
12
+
13
+ @property
14
+ def plugin_name(self) -> str:
15
+ return self._plugin_name
16
+
17
+ def path(self, *parts: str) -> Path:
18
+ relative = Path(*parts)
19
+ if relative.is_absolute():
20
+ raise ValueError("plugin storage paths must be relative")
21
+ if any(part in {"", ".", ".."} for part in relative.parts):
22
+ raise ValueError("plugin storage paths must not traverse directories")
23
+ return self._root / relative
24
+
25
+ def ensure_dir(self, *parts: str) -> Path:
26
+ directory = self.path(*parts)
27
+ ensure_private_dir(directory)
28
+ return directory
29
+
30
+
31
+ def default_plugin_data_root() -> Path:
32
+ if sys.platform == "win32":
33
+ base = os.environ.get("LOCALAPPDATA")
34
+ if base:
35
+ return Path(base) / "Arbiter" / "server" / "plugins"
36
+ if sys.platform == "darwin":
37
+ return (
38
+ Path.home()
39
+ / "Library"
40
+ / "Application Support"
41
+ / "Arbiter"
42
+ / "server"
43
+ / "plugins"
44
+ )
45
+ base = os.environ.get("XDG_STATE_HOME")
46
+ if base:
47
+ return Path(base) / "arbiter" / "server" / "plugins"
48
+ return Path.home() / ".local" / "state" / "arbiter" / "server" / "plugins"
49
+
50
+
51
+ def plugin_data_dir(root: Path, plugin_name: str) -> Path:
52
+ if "/" in plugin_name or "\\" in plugin_name or plugin_name in {"", ".", ".."}:
53
+ raise ValueError(f"invalid plugin name for data directory: {plugin_name}")
54
+ return root / plugin_name
55
+
56
+
57
+ def ensure_private_dir(path: Path) -> None:
58
+ path.mkdir(mode=0o700, parents=True, exist_ok=True)
59
+ if os.name != "nt":
60
+ os.chmod(path, 0o700)
@@ -0,0 +1,135 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ import re
5
+ import subprocess
6
+ from dataclasses import dataclass
7
+ from importlib.metadata import PackageNotFoundError, version
8
+ from pathlib import Path
9
+
10
+
11
+ _VERSION_LINE_PATTERN = re.compile(r"^(?P<major>\d+)\.(?P<minor>\d+)(?:\.|$)")
12
+
13
+
14
+ @dataclass(frozen=True)
15
+ class SourceInfo:
16
+ commit: str | None
17
+ dirty: bool | None
18
+
19
+
20
+ def compatibility_line(value: str) -> str:
21
+ match = _VERSION_LINE_PATTERN.match(value)
22
+ if not match:
23
+ raise ValueError(f"version must start with MAJOR.MINOR: {value}")
24
+ return f"{match.group('major')}.{match.group('minor')}"
25
+
26
+
27
+ def _pyproject_version(distribution_name: str, package_file: str | Path) -> str | None:
28
+ start = Path(package_file).resolve()
29
+ current = start if start.is_dir() else start.parent
30
+ for src_dir in (
31
+ parent for parent in (current, *current.parents) if parent.name == "src"
32
+ ):
33
+ package_root = src_dir.parent
34
+ if not current.is_relative_to(src_dir):
35
+ continue
36
+ pyproject = package_root / "pyproject.toml"
37
+ if not pyproject.exists():
38
+ return None
39
+ text = pyproject.read_text(encoding="utf-8")
40
+ project = re.search(r"(?ms)^\[project\]\s*(.*?)(?=^\[|\Z)", text)
41
+ if project is None:
42
+ return None
43
+ project_text = project.group(1)
44
+ name = re.search(r'^name\s*=\s*"([^"]+)"\s*$', project_text, re.MULTILINE)
45
+ if name is None or name.group(1) != distribution_name:
46
+ return None
47
+ project_version = re.search(
48
+ r'^version\s*=\s*"([^"]+)"\s*$',
49
+ project_text,
50
+ re.MULTILINE,
51
+ )
52
+ if project_version is not None:
53
+ return project_version.group(1)
54
+ return None
55
+ return None
56
+
57
+
58
+ def distribution_version(
59
+ distribution_name: str,
60
+ *,
61
+ package_file: str | Path | None = None,
62
+ ) -> str:
63
+ if package_file is not None:
64
+ source_version = _pyproject_version(distribution_name, package_file)
65
+ if source_version is not None:
66
+ return source_version
67
+
68
+ try:
69
+ return version(distribution_name)
70
+ except PackageNotFoundError:
71
+ pass
72
+
73
+ return "unknown"
74
+
75
+
76
+ def arbiter_server_version() -> str:
77
+ return distribution_version("arbiter-server", package_file=__file__)
78
+
79
+
80
+ def server_api_version() -> str:
81
+ return compatibility_line(arbiter_server_version())
82
+
83
+
84
+ def _find_vcs_root(start: str | Path) -> Path | None:
85
+ current = Path(start).resolve()
86
+ if current.is_file():
87
+ current = current.parent
88
+ for src_dir in (
89
+ parent for parent in (current, *current.parents) if parent.name == "src"
90
+ ):
91
+ if not current.is_relative_to(src_dir):
92
+ continue
93
+ package_root = src_dir.parent
94
+ for parent in (package_root, *package_root.parents):
95
+ if (parent / ".sl").exists() or (parent / ".git").exists():
96
+ return parent
97
+ return None
98
+ return None
99
+
100
+
101
+ def _run_vcs_command(root: Path, args: list[str]) -> str | None:
102
+ env = os.environ.copy()
103
+ if args[0] == "sl":
104
+ env["CHGDISABLE"] = "1"
105
+ try:
106
+ result = subprocess.run(
107
+ args,
108
+ cwd=root,
109
+ env=env,
110
+ check=True,
111
+ capture_output=True,
112
+ text=True,
113
+ timeout=2,
114
+ )
115
+ except (OSError, subprocess.SubprocessError):
116
+ return None
117
+ return result.stdout.strip()
118
+
119
+
120
+ def source_info(package_file: str | Path = __file__) -> SourceInfo:
121
+ root = _find_vcs_root(package_file)
122
+ if root is None:
123
+ return SourceInfo(commit=None, dirty=None)
124
+
125
+ if (root / ".sl").exists():
126
+ commit = _run_vcs_command(root, ["sl", "log", "-r", ".", "-T", "{node|short}"])
127
+ status = _run_vcs_command(root, ["sl", "status"])
128
+ else:
129
+ commit = _run_vcs_command(root, ["git", "rev-parse", "--short", "HEAD"])
130
+ status = _run_vcs_command(root, ["git", "status", "--porcelain"])
131
+
132
+ return SourceInfo(
133
+ commit=commit or None,
134
+ dirty=None if status is None else bool(status),
135
+ )
@@ -0,0 +1,26 @@
1
+ Metadata-Version: 2.4
2
+ Name: arbiter-server
3
+ Version: 0.9.1.dev1
4
+ Summary: Policy-controlled MCP server for agent-accessible services
5
+ Author-email: Omry Yadan <omry@yadan.net>
6
+ Maintainer-email: Omry Yadan <omry@yadan.net>
7
+ License: MIT
8
+ Project-URL: Homepage, https://github.com/omry/arbiter
9
+ Project-URL: Repository, https://github.com/omry/arbiter
10
+ Keywords: agent,policy,mcp,access-control,security
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Programming Language :: Python :: 3.14
20
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
21
+ Requires-Python: <3.15,>=3.10
22
+ Description-Content-Type: text/markdown
23
+ Requires-Dist: hydra-core<2.0,>=1.3
24
+ Requires-Dist: mcp<2.0,>=1.9.4
25
+
26
+ Server package for Arbiter.
@@ -0,0 +1,22 @@
1
+ arbiter_server/__init__.py,sha256=RZjRBfM2N7yWEBWZzXcE1nj_q86-JvddrfthDCdnAvM,30
2
+ arbiter_server/__main__.py,sha256=lU6t3BklI-5H9EdprlceWLr0UcDmfXUu3Th0tjk2ANA,81
3
+ arbiter_server/app.py,sha256=F4JvylSK57jr1FOLG1lb1AT9lOHw5i2M_m1ZbDiQ178,1087
4
+ arbiter_server/artifacts.py,sha256=BielMQadzR2A3T6gYgpBU-ydcE3AOoBjtt7vjZlK3Sc,11514
5
+ arbiter_server/cli_errors.py,sha256=EA0UAEK1SjF8rRtaHJZrkxuEeV-bi72QGHD9EaBW81o,803
6
+ arbiter_server/config.py,sha256=9E2RsjHopBJcWMv7wNssSUPU80632hwUY3MQPNNxbgk,6606
7
+ arbiter_server/main.py,sha256=afGD71-pCPVQGkRYYnOUHR5sX1fgO7hv2ocZyVW-hZw,93164
8
+ arbiter_server/py.typed,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
9
+ arbiter_server/services.py,sha256=jdeCCg-XEVgCQLSsABj30Ctg1WajXjQdpKWkt-nPk_o,25318
10
+ arbiter_server/storage.py,sha256=siCrudgFF4EImI4faggjER25HmzbWjs-WBt7BNyM998,1892
11
+ arbiter_server/version.py,sha256=XM3p4SjNq-XO4FlaxpvK46dE8u1xmk9vIvo5SEvuptc,4078
12
+ arbiter_server/deploy/docker/arbiter-docker,sha256=Umln7WiXDmV1Tc0NSpjqcMVEzK3whhxHJXC_ryaUrHI,130605
13
+ arbiter_server/deploy/docker/compose.yaml,sha256=81zPbgSx0evax3voIrnRzlqm3bLNHKGmArbUAkVbtYk,6135
14
+ arbiter_server/file_protection/__init__.py,sha256=JERhFRL9UqwdrMx8c0740mIrNfewOp46j19gbVZaPac,499
15
+ arbiter_server/file_protection/posix.py,sha256=xYnPnuctbRt7WAPZvZTkJH4YAjT_WuLpMBcFQHxogfE,2430
16
+ arbiter_server/file_protection/windows.py,sha256=_fUzYlImvJcX0iCI0tQUhWer-ogydONLKaEWtJnLKpE,13037
17
+ arbiter_server/plugins/__init__.py,sha256=vAmekvOS-JX_PX2y1V-XAc4eLuVdjpeRHY1X4r4tmys,1091
18
+ arbiter_server-0.9.1.dev1.dist-info/METADATA,sha256=gTz4sEYc3VXuBv4dTQFtWyCIIddkDt3v1ncYHxkfZMM,1076
19
+ arbiter_server-0.9.1.dev1.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
20
+ arbiter_server-0.9.1.dev1.dist-info/entry_points.txt,sha256=o6Vja25caFpbYolfdGqBhIA6c4hN8Yzguc_IPVqXMpI,60
21
+ arbiter_server-0.9.1.dev1.dist-info/top_level.txt,sha256=FrZNLp36AB4tQL_A9MFn9b0Ol7LTnKpf0lHnsdOQG6U,15
22
+ arbiter_server-0.9.1.dev1.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ arbiter-server = arbiter_server.main:main
@@ -0,0 +1 @@
1
+ arbiter_server