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.
- arbiter_server/__init__.py +1 -0
- arbiter_server/__main__.py +5 -0
- arbiter_server/app.py +44 -0
- arbiter_server/artifacts.py +344 -0
- arbiter_server/cli_errors.py +31 -0
- arbiter_server/config.py +231 -0
- arbiter_server/deploy/docker/arbiter-docker +4477 -0
- arbiter_server/deploy/docker/compose.yaml +101 -0
- arbiter_server/file_protection/__init__.py +20 -0
- arbiter_server/file_protection/posix.py +70 -0
- arbiter_server/file_protection/windows.py +379 -0
- arbiter_server/main.py +2843 -0
- arbiter_server/plugins/__init__.py +36 -0
- arbiter_server/py.typed +1 -0
- arbiter_server/services.py +706 -0
- arbiter_server/storage.py +60 -0
- arbiter_server/version.py +135 -0
- arbiter_server-0.9.1.dev1.dist-info/METADATA +26 -0
- arbiter_server-0.9.1.dev1.dist-info/RECORD +22 -0
- arbiter_server-0.9.1.dev1.dist-info/WHEEL +5 -0
- arbiter_server-0.9.1.dev1.dist-info/entry_points.txt +2 -0
- arbiter_server-0.9.1.dev1.dist-info/top_level.txt +1 -0
|
@@ -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 @@
|
|
|
1
|
+
arbiter_server
|