harnesslayer 0.1.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.
- harnesslayer-0.1.0/.gitignore +10 -0
- harnesslayer-0.1.0/.python-version +1 -0
- harnesslayer-0.1.0/PKG-INFO +87 -0
- harnesslayer-0.1.0/README.md +68 -0
- harnesslayer-0.1.0/pyproject.toml +36 -0
- harnesslayer-0.1.0/src/harnesslayer/__init__.py +15 -0
- harnesslayer-0.1.0/src/harnesslayer/_internal/__init__.py +1 -0
- harnesslayer-0.1.0/src/harnesslayer/_internal/constants.py +7 -0
- harnesslayer-0.1.0/src/harnesslayer/_internal/transport.py +103 -0
- harnesslayer-0.1.0/src/harnesslayer/_internal/utils.py +21 -0
- harnesslayer-0.1.0/src/harnesslayer/client.py +67 -0
- harnesslayer-0.1.0/src/harnesslayer/exceptions.py +14 -0
- harnesslayer-0.1.0/src/harnesslayer/models/__init__.py +1 -0
- harnesslayer-0.1.0/src/harnesslayer/models/access_group.py +32 -0
- harnesslayer-0.1.0/src/harnesslayer/models/app.py +124 -0
- harnesslayer-0.1.0/src/harnesslayer/models/channel.py +123 -0
- harnesslayer-0.1.0/src/harnesslayer/models/conflict.py +38 -0
- harnesslayer-0.1.0/src/harnesslayer/models/instance.py +51 -0
- harnesslayer-0.1.0/src/harnesslayer/models/profile.py +61 -0
- harnesslayer-0.1.0/src/harnesslayer/models/response.py +12 -0
- harnesslayer-0.1.0/src/harnesslayer/models/session.py +33 -0
- harnesslayer-0.1.0/src/harnesslayer/models/version.py +39 -0
- harnesslayer-0.1.0/src/harnesslayer/py.typed +1 -0
- harnesslayer-0.1.0/src/harnesslayer/resources/__init__.py +1 -0
- harnesslayer-0.1.0/src/harnesslayer/resources/_base.py +18 -0
- harnesslayer-0.1.0/src/harnesslayer/resources/access_group.py +137 -0
- harnesslayer-0.1.0/src/harnesslayer/resources/app.py +183 -0
- harnesslayer-0.1.0/src/harnesslayer/resources/channel.py +229 -0
- harnesslayer-0.1.0/src/harnesslayer/resources/conflict.py +56 -0
- harnesslayer-0.1.0/src/harnesslayer/resources/instance.py +55 -0
- harnesslayer-0.1.0/src/harnesslayer/resources/profile.py +87 -0
- harnesslayer-0.1.0/src/harnesslayer/resources/session.py +113 -0
- harnesslayer-0.1.0/src/harnesslayer/resources/state.py +316 -0
- harnesslayer-0.1.0/src/harnesslayer/resources/version.py +298 -0
- harnesslayer-0.1.0/src/harnesslayer/version.py +1 -0
- harnesslayer-0.1.0/tests/claude/.claude/agent.json +14 -0
- harnesslayer-0.1.0/tests/claude/.claude/environment.json +9 -0
- harnesslayer-0.1.0/tests/claude/quick_start_claude.py +81 -0
- harnesslayer-0.1.0/tests/openclaw/.openclaw/openclaw.json +94 -0
- harnesslayer-0.1.0/tests/openclaw/.openclaw/workspace/AGENTS.md +212 -0
- harnesslayer-0.1.0/tests/openclaw/.openclaw/workspace/BOOTSTRAP.md +55 -0
- harnesslayer-0.1.0/tests/openclaw/.openclaw/workspace/HEARTBEAT.md +5 -0
- harnesslayer-0.1.0/tests/openclaw/.openclaw/workspace/IDENTITY.md +23 -0
- harnesslayer-0.1.0/tests/openclaw/.openclaw/workspace/SOUL.md +36 -0
- harnesslayer-0.1.0/tests/openclaw/.openclaw/workspace/TOOLS.md +40 -0
- harnesslayer-0.1.0/tests/openclaw/.openclaw/workspace/USER.md +17 -0
- harnesslayer-0.1.0/tests/openclaw/quick_start_openclaw.py +18 -0
- harnesslayer-0.1.0/tests/test_app_init.py +79 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.12
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: harnesslayer
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python SDK for the Harnesslayer API.
|
|
5
|
+
Project-URL: Homepage, https://www.harnesslayer.ai/
|
|
6
|
+
Author: Harnesslayer
|
|
7
|
+
Keywords: api-client,harnesslayer,sdk
|
|
8
|
+
Classifier: Development Status :: 3 - Alpha
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
16
|
+
Requires-Python: >=3.10
|
|
17
|
+
Requires-Dist: httpx<1.0,>=0.27
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
|
|
20
|
+
## Harnesslayer - Python
|
|
21
|
+
|
|
22
|
+
Python SDK for the Harnesslayer API.
|
|
23
|
+
|
|
24
|
+
### Install
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
pip install harnesslayer
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### Quick Start
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
from harnesslayer import Harnesslayer
|
|
34
|
+
|
|
35
|
+
client = Harnesslayer(api_key="your-api-key")
|
|
36
|
+
|
|
37
|
+
app = client.app.init("typedef-app",
|
|
38
|
+
type="claude",
|
|
39
|
+
version_path=".claude"
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
channel = app.channel.init("my-api",
|
|
43
|
+
type="api"
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
stream = channel.run(
|
|
47
|
+
session_id="my-custom-session-id",
|
|
48
|
+
user_id="stephen@pickaxe.co",
|
|
49
|
+
input="hello world"
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
for event in stream:
|
|
53
|
+
if event.done:
|
|
54
|
+
break
|
|
55
|
+
|
|
56
|
+
print(event)
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Using A Context Manager
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
from harnesslayer import Harnesslayer
|
|
64
|
+
|
|
65
|
+
with Harnesslayer(api_key="your-api-key") as client:
|
|
66
|
+
app = client.app.init("typedef-app",
|
|
67
|
+
type="claude",
|
|
68
|
+
version_path=".claude"
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
channel = app.channel.init("my-api",
|
|
72
|
+
type="api"
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
stream = channel.run(
|
|
76
|
+
session_id="my-custom-session-id",
|
|
77
|
+
user_id="stephen@pickaxe.co",
|
|
78
|
+
input="hello world"
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
for event in stream:
|
|
82
|
+
if event.done:
|
|
83
|
+
break
|
|
84
|
+
|
|
85
|
+
print(event)
|
|
86
|
+
|
|
87
|
+
```
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
## Harnesslayer - Python
|
|
2
|
+
|
|
3
|
+
Python SDK for the Harnesslayer API.
|
|
4
|
+
|
|
5
|
+
### Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install harnesslayer
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
### Quick Start
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
from harnesslayer import Harnesslayer
|
|
15
|
+
|
|
16
|
+
client = Harnesslayer(api_key="your-api-key")
|
|
17
|
+
|
|
18
|
+
app = client.app.init("typedef-app",
|
|
19
|
+
type="claude",
|
|
20
|
+
version_path=".claude"
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
channel = app.channel.init("my-api",
|
|
24
|
+
type="api"
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
stream = channel.run(
|
|
28
|
+
session_id="my-custom-session-id",
|
|
29
|
+
user_id="stephen@pickaxe.co",
|
|
30
|
+
input="hello world"
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
for event in stream:
|
|
34
|
+
if event.done:
|
|
35
|
+
break
|
|
36
|
+
|
|
37
|
+
print(event)
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Using A Context Manager
|
|
42
|
+
|
|
43
|
+
```python
|
|
44
|
+
from harnesslayer import Harnesslayer
|
|
45
|
+
|
|
46
|
+
with Harnesslayer(api_key="your-api-key") as client:
|
|
47
|
+
app = client.app.init("typedef-app",
|
|
48
|
+
type="claude",
|
|
49
|
+
version_path=".claude"
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
channel = app.channel.init("my-api",
|
|
53
|
+
type="api"
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
stream = channel.run(
|
|
57
|
+
session_id="my-custom-session-id",
|
|
58
|
+
user_id="stephen@pickaxe.co",
|
|
59
|
+
input="hello world"
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
for event in stream:
|
|
63
|
+
if event.done:
|
|
64
|
+
break
|
|
65
|
+
|
|
66
|
+
print(event)
|
|
67
|
+
|
|
68
|
+
```
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling>=1.27.0"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "harnesslayer"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
description = "Python SDK for the Harnesslayer API."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
dependencies = [
|
|
12
|
+
"httpx>=0.27,<1.0",
|
|
13
|
+
]
|
|
14
|
+
authors = [
|
|
15
|
+
{ name = "Harnesslayer" },
|
|
16
|
+
]
|
|
17
|
+
keywords = ["harnesslayer", "sdk", "api-client"]
|
|
18
|
+
classifiers = [
|
|
19
|
+
"Development Status :: 3 - Alpha",
|
|
20
|
+
"Intended Audience :: Developers",
|
|
21
|
+
"Programming Language :: Python :: 3",
|
|
22
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
23
|
+
"Programming Language :: Python :: 3.10",
|
|
24
|
+
"Programming Language :: Python :: 3.11",
|
|
25
|
+
"Programming Language :: Python :: 3.12",
|
|
26
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
[project.urls]
|
|
30
|
+
Homepage = "https://www.harnesslayer.ai/"
|
|
31
|
+
|
|
32
|
+
[tool.hatch.version]
|
|
33
|
+
path = "src/harnesslayer/version.py"
|
|
34
|
+
|
|
35
|
+
[tool.hatch.build.targets.wheel]
|
|
36
|
+
packages = ["src/harnesslayer"]
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from .client import Harnesslayer
|
|
2
|
+
from .exceptions import (
|
|
3
|
+
HarnesslayerAPIError,
|
|
4
|
+
HarnesslayerError,
|
|
5
|
+
HarnesslayerHTTPError,
|
|
6
|
+
HarnesslayerResponseError,
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
__all__ = [
|
|
10
|
+
"Harnesslayer",
|
|
11
|
+
"HarnesslayerError",
|
|
12
|
+
"HarnesslayerHTTPError",
|
|
13
|
+
"HarnesslayerResponseError",
|
|
14
|
+
"HarnesslayerAPIError",
|
|
15
|
+
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Internal Harnesslayer SDK implementation details."""
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any, Literal, cast
|
|
4
|
+
import httpx
|
|
5
|
+
|
|
6
|
+
from .utils import logger
|
|
7
|
+
from ..exceptions import (
|
|
8
|
+
HarnesslayerAPIError,
|
|
9
|
+
HarnesslayerHTTPError,
|
|
10
|
+
HarnesslayerResponseError
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
Method = Literal["GET", "POST", "PUT", "PATCH", "DELETE"]
|
|
14
|
+
|
|
15
|
+
class Transport:
|
|
16
|
+
def __init__(
|
|
17
|
+
self,
|
|
18
|
+
*,
|
|
19
|
+
base_url: str,
|
|
20
|
+
api_key: str,
|
|
21
|
+
timeout: float,
|
|
22
|
+
user_agent: str,
|
|
23
|
+
http_client: httpx.Client | None = None,
|
|
24
|
+
) -> None:
|
|
25
|
+
self.timeout = timeout
|
|
26
|
+
self._headers = {
|
|
27
|
+
"Authorization": f"Bearer {api_key}",
|
|
28
|
+
"Content-Type": "application/json",
|
|
29
|
+
"Accept": "application/json",
|
|
30
|
+
"User-Agent": user_agent,
|
|
31
|
+
}
|
|
32
|
+
self._owns_http_client = http_client is None
|
|
33
|
+
self._http = http_client or httpx.Client(
|
|
34
|
+
base_url=base_url,
|
|
35
|
+
timeout=timeout,
|
|
36
|
+
headers=self._headers,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
@property
|
|
40
|
+
def headers(self) -> dict[str, str]:
|
|
41
|
+
return self._headers.copy()
|
|
42
|
+
|
|
43
|
+
def close(self) -> None:
|
|
44
|
+
if self._owns_http_client:
|
|
45
|
+
self._http.close()
|
|
46
|
+
|
|
47
|
+
@staticmethod
|
|
48
|
+
def _parse_response(response: httpx.Response) -> dict[str, Any]:
|
|
49
|
+
try:
|
|
50
|
+
decoded = response.json()
|
|
51
|
+
except ValueError as exc:
|
|
52
|
+
raise HarnesslayerResponseError(
|
|
53
|
+
f"Harnesslayer API returned non-JSON response (status={response.status_code})"
|
|
54
|
+
) from exc
|
|
55
|
+
|
|
56
|
+
if not isinstance(decoded, dict):
|
|
57
|
+
raise HarnesslayerResponseError(
|
|
58
|
+
"Harnesslayer API returned an unexpected response payload"
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
return cast(dict[str, Any], decoded)
|
|
62
|
+
|
|
63
|
+
def request(
|
|
64
|
+
self,
|
|
65
|
+
method: Method,
|
|
66
|
+
path: str,
|
|
67
|
+
payload: dict[str, Any] | None = None,
|
|
68
|
+
) -> dict[str, Any]:
|
|
69
|
+
payload = payload or {}
|
|
70
|
+
|
|
71
|
+
try:
|
|
72
|
+
kwargs: dict[str, Any] = {
|
|
73
|
+
"method": method,
|
|
74
|
+
"url": path.lstrip("/"),
|
|
75
|
+
"headers": self._headers,
|
|
76
|
+
}
|
|
77
|
+
if method == "GET":
|
|
78
|
+
kwargs["params"] = payload
|
|
79
|
+
else:
|
|
80
|
+
kwargs["json"] = payload
|
|
81
|
+
|
|
82
|
+
response = self._http.request(**kwargs)
|
|
83
|
+
except httpx.HTTPError as exc:
|
|
84
|
+
raise HarnesslayerHTTPError(f"HTTP request failed: {exc}") from exc
|
|
85
|
+
|
|
86
|
+
data = self._parse_response(response)
|
|
87
|
+
|
|
88
|
+
logger.debug(
|
|
89
|
+
"Received response from Harnesslayer API (status=%d, data=%s)",
|
|
90
|
+
response.status_code,
|
|
91
|
+
data,
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
if response.status_code >= 400:
|
|
95
|
+
raise HarnesslayerAPIError(
|
|
96
|
+
f"Harnesslayer API returned error status {response.status_code}: {data}"
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
if data.get("success") is False:
|
|
100
|
+
message = str(data.get("error") or data.get("message") or "Request failed")
|
|
101
|
+
raise HarnesslayerAPIError(f"Harnesslayer API error: {message}")
|
|
102
|
+
|
|
103
|
+
return data
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import json
|
|
3
|
+
|
|
4
|
+
logger = logging.getLogger("harnesslayer")
|
|
5
|
+
logger.setLevel(logging.WARNING)
|
|
6
|
+
logger.addHandler(logging.NullHandler())
|
|
7
|
+
|
|
8
|
+
logging.basicConfig(
|
|
9
|
+
format="[%(funcName)s][%(asctime)s]: %(levelname)s %(message)s",
|
|
10
|
+
datefmt="%m/%d/%Y %H:%M:%S",
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
def set_debug(enabled: bool) -> None:
|
|
14
|
+
logger.setLevel(logging.INFO if enabled else logging.WARNING)
|
|
15
|
+
|
|
16
|
+
def safe_parse_json(raw: str):
|
|
17
|
+
try:
|
|
18
|
+
return json.loads(raw)
|
|
19
|
+
except Exception as e:
|
|
20
|
+
logger.error("Failed to parse JSON: %s", e)
|
|
21
|
+
return None
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any, Literal
|
|
4
|
+
|
|
5
|
+
import httpx
|
|
6
|
+
|
|
7
|
+
from ._internal.constants import DEFAULT_API_BASE_URL
|
|
8
|
+
from ._internal.utils import logger, set_debug
|
|
9
|
+
from ._internal.transport import Method, Transport
|
|
10
|
+
from .resources.app import HarnesslayerApp
|
|
11
|
+
from .version import __version__
|
|
12
|
+
|
|
13
|
+
class Harnesslayer:
|
|
14
|
+
def __init__(
|
|
15
|
+
self,
|
|
16
|
+
api_key: str,
|
|
17
|
+
*,
|
|
18
|
+
version: Literal["v1"] = "v1",
|
|
19
|
+
timeout: float = 30.0,
|
|
20
|
+
debug: bool = False,
|
|
21
|
+
http_client: httpx.Client | None = None,
|
|
22
|
+
) -> None:
|
|
23
|
+
self.debug = debug
|
|
24
|
+
set_debug(self.debug)
|
|
25
|
+
|
|
26
|
+
logger.info("Initializing Harnesslayer client with provided API key")
|
|
27
|
+
|
|
28
|
+
trimmed_key = api_key.strip()
|
|
29
|
+
if not trimmed_key:
|
|
30
|
+
raise ValueError("api_key must be provided")
|
|
31
|
+
if not trimmed_key.startswith("dk-"):
|
|
32
|
+
raise ValueError("Invalid Harnesslayer API key")
|
|
33
|
+
|
|
34
|
+
self.api_key = trimmed_key
|
|
35
|
+
self.version = version
|
|
36
|
+
self.timeout = timeout
|
|
37
|
+
self.base_url = f"{DEFAULT_API_BASE_URL}/{self.version}"
|
|
38
|
+
|
|
39
|
+
self._transport = Transport(
|
|
40
|
+
base_url=self.base_url,
|
|
41
|
+
api_key=self.api_key,
|
|
42
|
+
timeout=self.timeout,
|
|
43
|
+
user_agent=f"Harnesslayer-python/{__version__}",
|
|
44
|
+
http_client=http_client,
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
def close(self) -> None:
|
|
48
|
+
self._transport.close()
|
|
49
|
+
|
|
50
|
+
def __enter__(self) -> "Harnesslayer":
|
|
51
|
+
return self
|
|
52
|
+
|
|
53
|
+
def __exit__(self, *_: object) -> None:
|
|
54
|
+
self.close()
|
|
55
|
+
|
|
56
|
+
def _request(
|
|
57
|
+
self,
|
|
58
|
+
method: Method,
|
|
59
|
+
path: str,
|
|
60
|
+
payload: dict[str, Any] | None = None,
|
|
61
|
+
) -> dict[str, Any]:
|
|
62
|
+
return self._transport.request(method, path, payload)
|
|
63
|
+
|
|
64
|
+
@property
|
|
65
|
+
def app(self) -> HarnesslayerApp:
|
|
66
|
+
return HarnesslayerApp(self)
|
|
67
|
+
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
class HarnesslayerError(Exception):
|
|
2
|
+
"""Base exception for the Harnesslayer SDK."""
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class HarnesslayerHTTPError(HarnesslayerError):
|
|
6
|
+
"""Raised when the underlying HTTP request fails."""
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class HarnesslayerResponseError(HarnesslayerError):
|
|
10
|
+
"""Raised when an API response cannot be parsed as expected."""
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class HarnesslayerAPIError(HarnesslayerError):
|
|
14
|
+
"""Raised when the Harnesslayer API returns an error."""
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Harnesslayer SDK models."""
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from datetime import datetime
|
|
5
|
+
from typing import Any, Literal, Mapping
|
|
6
|
+
|
|
7
|
+
AccessGroupLimitReset = Literal["daily", "monthly", "yearly"]
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@dataclass(frozen=True, slots=True)
|
|
11
|
+
class HarnesslayerAccessGroupModel:
|
|
12
|
+
id: str
|
|
13
|
+
appId: str
|
|
14
|
+
name: str
|
|
15
|
+
limit: int
|
|
16
|
+
limitReset: AccessGroupLimitReset
|
|
17
|
+
limitMessage: str
|
|
18
|
+
createdAt: datetime
|
|
19
|
+
updatedAt: datetime
|
|
20
|
+
|
|
21
|
+
@classmethod
|
|
22
|
+
def _from_api(cls, payload: Mapping[str, Any]) -> "HarnesslayerAccessGroupModel":
|
|
23
|
+
return cls(
|
|
24
|
+
id=payload["id"],
|
|
25
|
+
appId=payload["appId"],
|
|
26
|
+
name=payload["name"],
|
|
27
|
+
limit=payload["limit"],
|
|
28
|
+
limitReset=payload["limitReset"],
|
|
29
|
+
limitMessage=payload["limitMessage"],
|
|
30
|
+
createdAt=datetime.fromisoformat(payload["createdAt"]),
|
|
31
|
+
updatedAt=datetime.fromisoformat(payload["updatedAt"]),
|
|
32
|
+
)
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from datetime import datetime
|
|
5
|
+
from typing import (
|
|
6
|
+
TYPE_CHECKING,
|
|
7
|
+
Any,
|
|
8
|
+
Literal,
|
|
9
|
+
Mapping,
|
|
10
|
+
Optional,
|
|
11
|
+
TypedDict,
|
|
12
|
+
Union,
|
|
13
|
+
Required,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
if TYPE_CHECKING:
|
|
17
|
+
from ..resources.access_group import HarnesslayerAccessGroup
|
|
18
|
+
from ..resources.version import HarnesslayerVersion
|
|
19
|
+
from ..resources.channel import HarnesslayerChannel
|
|
20
|
+
from ..resources.session import HarnesslayerSession
|
|
21
|
+
from ..resources.profile import HarnesslayerProfile
|
|
22
|
+
from ..resources.instance import HarnesslayerInstance
|
|
23
|
+
from ..resources.conflict import HarnesslayerConflict
|
|
24
|
+
|
|
25
|
+
ApplicationType = Literal["claude", "openclaw"]
|
|
26
|
+
|
|
27
|
+
class AppInitLimit(TypedDict):
|
|
28
|
+
name: Optional[str]
|
|
29
|
+
amount: Required[int]
|
|
30
|
+
interval: Required[Literal["daily", "weekly", "monthly"]]
|
|
31
|
+
message: Optional[str]
|
|
32
|
+
|
|
33
|
+
class AppInitCredentialMCPOAuthNoneParam(TypedDict):
|
|
34
|
+
type: Required[Literal["none"]]
|
|
35
|
+
|
|
36
|
+
class AppInitCredentialMCPOAuthBasicParam(TypedDict):
|
|
37
|
+
type: Required[Literal["client_secret_basic"]]
|
|
38
|
+
client_secret: Required[str]
|
|
39
|
+
|
|
40
|
+
class AppInitCredentialMCPOAuthPostParam(TypedDict):
|
|
41
|
+
type: Required[Literal["client_secret_post"]]
|
|
42
|
+
client_secret: Required[str]
|
|
43
|
+
|
|
44
|
+
class AppInitCredentialMCPOAuthRefresh(TypedDict):
|
|
45
|
+
client_id: Required[str]
|
|
46
|
+
refresh_token: Required[str]
|
|
47
|
+
token_endpoint: Required[str]
|
|
48
|
+
token_endpoint_auth: Required[Union[
|
|
49
|
+
AppInitCredentialMCPOAuthNoneParam,
|
|
50
|
+
AppInitCredentialMCPOAuthBasicParam,
|
|
51
|
+
AppInitCredentialMCPOAuthPostParam,
|
|
52
|
+
]]
|
|
53
|
+
resource: Optional[str]
|
|
54
|
+
scope: Optional[str]
|
|
55
|
+
|
|
56
|
+
class AppInitCredentialMCPOAuth(TypedDict):
|
|
57
|
+
access_token: Required[str]
|
|
58
|
+
mcp_server_url: Required[str]
|
|
59
|
+
type: Required[Literal["mcp_oauth"]]
|
|
60
|
+
expires_at: Optional[datetime]
|
|
61
|
+
refresh: Optional[AppInitCredentialMCPOAuthRefresh]
|
|
62
|
+
|
|
63
|
+
class AppInitCredentialMCPStaticBearer(TypedDict):
|
|
64
|
+
token: Required[str]
|
|
65
|
+
mcp_server_url: Required[str]
|
|
66
|
+
type: Required[Literal["mcp_oauth"]]
|
|
67
|
+
|
|
68
|
+
AppInitCredential = Union[AppInitCredentialMCPOAuth, AppInitCredentialMCPStaticBearer]
|
|
69
|
+
|
|
70
|
+
@dataclass(frozen=True, slots=True)
|
|
71
|
+
class HarnesslayerAppModel:
|
|
72
|
+
id: str
|
|
73
|
+
orgId: str
|
|
74
|
+
type: ApplicationType
|
|
75
|
+
name: str
|
|
76
|
+
slug: str
|
|
77
|
+
vaultId: str
|
|
78
|
+
defaultAccessGroupId: str
|
|
79
|
+
headVersionId: Optional[str]
|
|
80
|
+
metadata: Optional[dict[str, str]]
|
|
81
|
+
createdAt: datetime
|
|
82
|
+
updatedAt: datetime
|
|
83
|
+
|
|
84
|
+
version: HarnesslayerVersion
|
|
85
|
+
channel: HarnesslayerChannel
|
|
86
|
+
session: HarnesslayerSession
|
|
87
|
+
access_group: HarnesslayerAccessGroup
|
|
88
|
+
profile: HarnesslayerProfile
|
|
89
|
+
instance: HarnesslayerInstance
|
|
90
|
+
conflict: HarnesslayerConflict
|
|
91
|
+
|
|
92
|
+
@classmethod
|
|
93
|
+
def _from_api(
|
|
94
|
+
cls,
|
|
95
|
+
payload: Mapping[str, Any],
|
|
96
|
+
version: HarnesslayerVersion,
|
|
97
|
+
channel: HarnesslayerChannel,
|
|
98
|
+
session: HarnesslayerSession,
|
|
99
|
+
access_group: HarnesslayerAccessGroup,
|
|
100
|
+
profile: HarnesslayerProfile,
|
|
101
|
+
instance: HarnesslayerInstance,
|
|
102
|
+
conflict: HarnesslayerConflict,
|
|
103
|
+
) -> "HarnesslayerAppModel":
|
|
104
|
+
return cls(
|
|
105
|
+
id=payload["id"],
|
|
106
|
+
orgId=payload["orgId"],
|
|
107
|
+
type=payload["type"],
|
|
108
|
+
name=payload["name"],
|
|
109
|
+
slug=payload["slug"],
|
|
110
|
+
vaultId=payload["vaultId"],
|
|
111
|
+
defaultAccessGroupId=payload["defaultAccessGroupId"],
|
|
112
|
+
headVersionId=payload.get("headVersionId"),
|
|
113
|
+
metadata=payload.get("metadata"),
|
|
114
|
+
createdAt=datetime.fromisoformat(payload["createdAt"]),
|
|
115
|
+
updatedAt=datetime.fromisoformat(payload["updatedAt"]),
|
|
116
|
+
|
|
117
|
+
version=version,
|
|
118
|
+
channel=channel,
|
|
119
|
+
session=session,
|
|
120
|
+
access_group=access_group,
|
|
121
|
+
profile=profile,
|
|
122
|
+
instance=instance,
|
|
123
|
+
conflict=conflict,
|
|
124
|
+
)
|