kotharcomputing 0.76.0__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.
- kotharcomputing/__init__.py +16 -0
- kotharcomputing/agents.py +186 -0
- kotharcomputing/ai.py +262 -0
- kotharcomputing/api_tokens.py +111 -0
- kotharcomputing/client.py +78 -0
- kotharcomputing/common.py +7 -0
- kotharcomputing/credits.py +80 -0
- kotharcomputing/errors.py +60 -0
- kotharcomputing/executions.py +312 -0
- kotharcomputing/fetch.py +572 -0
- kotharcomputing/files.py +186 -0
- kotharcomputing/jobs.py +186 -0
- kotharcomputing/py.typed +0 -0
- kotharcomputing/runtimes.py +26 -0
- kotharcomputing/service_prices.py +28 -0
- kotharcomputing/subscribe_user.py +40 -0
- kotharcomputing/subscribe_workspace.py +58 -0
- kotharcomputing/subscriptions.py +85 -0
- kotharcomputing/users.py +81 -0
- kotharcomputing/workspaces.py +79 -0
- kotharcomputing-0.76.0.dist-info/METADATA +74 -0
- kotharcomputing-0.76.0.dist-info/RECORD +25 -0
- kotharcomputing-0.76.0.dist-info/WHEEL +5 -0
- kotharcomputing-0.76.0.dist-info/licenses/LICENSE +179 -0
- kotharcomputing-0.76.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from .client import (
|
|
2
|
+
KotharClient,
|
|
3
|
+
KotharClientOptions,
|
|
4
|
+
RawResponse,
|
|
5
|
+
)
|
|
6
|
+
from .executions import ExecuteResult
|
|
7
|
+
from .errors import KOTHAR_ERROR_TYPE_BASE_URL, KotharApiError
|
|
8
|
+
|
|
9
|
+
__all__ = [
|
|
10
|
+
"KOTHAR_ERROR_TYPE_BASE_URL",
|
|
11
|
+
"KotharApiError",
|
|
12
|
+
"KotharClient",
|
|
13
|
+
"KotharClientOptions",
|
|
14
|
+
"RawResponse",
|
|
15
|
+
"ExecuteResult",
|
|
16
|
+
]
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Literal, NotRequired, TypeAlias, TypedDict, cast
|
|
4
|
+
|
|
5
|
+
from .fetch import ApiTransport
|
|
6
|
+
from .users import WithUser
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ActivityExecution(TypedDict):
|
|
10
|
+
type: Literal["execution"]
|
|
11
|
+
id: str
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ActivityJob(TypedDict):
|
|
15
|
+
type: Literal["job"]
|
|
16
|
+
id: str
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
Activity: TypeAlias = ActivityExecution | ActivityJob
|
|
20
|
+
|
|
21
|
+
AgentKind: TypeAlias = Literal["personal", "cloud"]
|
|
22
|
+
AgentStatus: TypeAlias = Literal["offline", "online"]
|
|
23
|
+
AgentCapabilities: TypeAlias = Literal["direct-execution", "job"]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class AgentSystemInfoCPU(TypedDict):
|
|
27
|
+
arch: str
|
|
28
|
+
parallelism: int
|
|
29
|
+
flags: list[str]
|
|
30
|
+
vendor: NotRequired[str]
|
|
31
|
+
model: NotRequired[str]
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class AgentSystemInfoMemory(TypedDict):
|
|
35
|
+
totalMB: int
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class AgentSystemInfo(TypedDict):
|
|
39
|
+
cpu: AgentSystemInfoCPU
|
|
40
|
+
memory: AgentSystemInfoMemory
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class AgentData(TypedDict):
|
|
44
|
+
systemInfo: AgentSystemInfo
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class AgentPersonal(WithUser):
|
|
48
|
+
id: str
|
|
49
|
+
name: str
|
|
50
|
+
status: AgentStatus
|
|
51
|
+
kind: Literal["personal"]
|
|
52
|
+
capabilities: list[AgentCapabilities]
|
|
53
|
+
activities: list[Activity]
|
|
54
|
+
ownerId: str
|
|
55
|
+
createdAt: str
|
|
56
|
+
ip: NotRequired[str]
|
|
57
|
+
version: NotRequired[str]
|
|
58
|
+
imageVersion: NotRequired[str]
|
|
59
|
+
lastSeen: NotRequired[str]
|
|
60
|
+
data: NotRequired[AgentData]
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class AgentCloudCPU(TypedDict):
|
|
64
|
+
parallelism: int
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class AgentCloudSystemInfo(TypedDict):
|
|
68
|
+
cpu: AgentCloudCPU
|
|
69
|
+
memory: AgentSystemInfoMemory
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class AgentCloudData(TypedDict):
|
|
73
|
+
systemInfo: AgentCloudSystemInfo
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class AgentCloud(WithUser):
|
|
77
|
+
id: str
|
|
78
|
+
name: str
|
|
79
|
+
status: AgentStatus
|
|
80
|
+
kind: Literal["cloud"]
|
|
81
|
+
capabilities: list[AgentCapabilities]
|
|
82
|
+
activities: list[Activity]
|
|
83
|
+
ownerId: str
|
|
84
|
+
tooltip: NotRequired[str]
|
|
85
|
+
timeout: NotRequired[str]
|
|
86
|
+
data: NotRequired[AgentCloudData]
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
Agent: TypeAlias = AgentPersonal | AgentCloud
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class WithAgent(TypedDict):
|
|
93
|
+
agentId: str
|
|
94
|
+
agentName: NotRequired[str]
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
class CreateAgentArgs(TypedDict):
|
|
98
|
+
name: str
|
|
99
|
+
kind: AgentKind
|
|
100
|
+
ownerId: str
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
class CreateAgentResult(TypedDict):
|
|
104
|
+
agent: Agent
|
|
105
|
+
token: str
|
|
106
|
+
cmdLine: str
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
class ResetAgentTokenResult(TypedDict):
|
|
110
|
+
agent: Agent
|
|
111
|
+
token: str
|
|
112
|
+
cmdLine: str
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class DeleteAgentResult(TypedDict, total=False):
|
|
116
|
+
cmdLine: str
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
class AgentsPage(TypedDict):
|
|
120
|
+
items: list[Agent]
|
|
121
|
+
next: NotRequired[str]
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
class AgentsClient:
|
|
125
|
+
def __init__(self, transport: ApiTransport) -> None:
|
|
126
|
+
self._transport = transport
|
|
127
|
+
|
|
128
|
+
def list(
|
|
129
|
+
self,
|
|
130
|
+
*,
|
|
131
|
+
for_user_id: str,
|
|
132
|
+
next: str | None = None,
|
|
133
|
+
with_usernames: bool | None = None,
|
|
134
|
+
) -> AgentsPage:
|
|
135
|
+
return cast(
|
|
136
|
+
AgentsPage,
|
|
137
|
+
self._transport.get_json(
|
|
138
|
+
"v1/agents",
|
|
139
|
+
search_params={
|
|
140
|
+
"forUserId": for_user_id,
|
|
141
|
+
"next": next,
|
|
142
|
+
"withUsernames": with_usernames,
|
|
143
|
+
},
|
|
144
|
+
),
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
def create(self, args: CreateAgentArgs) -> CreateAgentResult:
|
|
148
|
+
return cast(
|
|
149
|
+
CreateAgentResult, self._transport.post_json("v1/agents", json_data=args)
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
def id(self, agent_id: str) -> "AgentByIdClient":
|
|
153
|
+
return AgentByIdClient(self._transport, agent_id)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
class AgentByIdClient:
|
|
157
|
+
def __init__(self, transport: ApiTransport, agent_id: str) -> None:
|
|
158
|
+
self._transport = transport
|
|
159
|
+
self._agent_id = agent_id
|
|
160
|
+
|
|
161
|
+
def get(self, *, with_usernames: bool | None = None) -> Agent:
|
|
162
|
+
return cast(
|
|
163
|
+
Agent,
|
|
164
|
+
self._transport.get_json(
|
|
165
|
+
"v1/agents/:agentId",
|
|
166
|
+
path_params={"agentId": self._agent_id},
|
|
167
|
+
search_params={"withUsernames": with_usernames},
|
|
168
|
+
),
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
def reset_token(self) -> ResetAgentTokenResult:
|
|
172
|
+
return cast(
|
|
173
|
+
ResetAgentTokenResult,
|
|
174
|
+
self._transport.post_json(
|
|
175
|
+
"v1/agents/:agentId/$resetToken",
|
|
176
|
+
path_params={"agentId": self._agent_id},
|
|
177
|
+
),
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
def delete(self) -> DeleteAgentResult:
|
|
181
|
+
return cast(
|
|
182
|
+
DeleteAgentResult,
|
|
183
|
+
self._transport.delete_json(
|
|
184
|
+
"v1/agents/:agentId", path_params={"agentId": self._agent_id}
|
|
185
|
+
),
|
|
186
|
+
)
|
kotharcomputing/ai.py
ADDED
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Callable, Literal, NotRequired, TypeAlias, TypedDict, cast
|
|
4
|
+
|
|
5
|
+
from .common import JSONValue
|
|
6
|
+
from .errors import ProblemDetails
|
|
7
|
+
from .fetch import ApiTransport
|
|
8
|
+
from .users import WithUser
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class AIChatContentText(TypedDict):
|
|
12
|
+
type: Literal["text"]
|
|
13
|
+
text: str
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class AIChatFileLocation(TypedDict):
|
|
17
|
+
line: int
|
|
18
|
+
column: int
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class AIChatSelection(TypedDict):
|
|
22
|
+
start: AIChatFileLocation
|
|
23
|
+
end: AIChatFileLocation
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class AIChatContentEditorContextFile(TypedDict):
|
|
27
|
+
path: str
|
|
28
|
+
content: str
|
|
29
|
+
is_truncated: bool
|
|
30
|
+
language: NotRequired[str]
|
|
31
|
+
is_primary: NotRequired[bool]
|
|
32
|
+
cursor: NotRequired[AIChatFileLocation]
|
|
33
|
+
selection: NotRequired[AIChatSelection]
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class AIChatContentEditorContext(TypedDict):
|
|
37
|
+
type: Literal["editor_context"]
|
|
38
|
+
files: list[AIChatContentEditorContextFile]
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
AIChatMessageAssistantContent: TypeAlias = AIChatContentText
|
|
42
|
+
AIChatMessageUserContent: TypeAlias = AIChatContentText
|
|
43
|
+
AIChatMessageUserContentEdition: TypeAlias = (
|
|
44
|
+
AIChatContentText | AIChatContentEditorContext
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class AIChatToolDescriptionFunctionData(TypedDict):
|
|
49
|
+
name: str
|
|
50
|
+
description: NotRequired[str]
|
|
51
|
+
parameters: NotRequired[JSONValue]
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class AIChatToolDescriptionFunction(TypedDict):
|
|
55
|
+
type: Literal["function"]
|
|
56
|
+
function: AIChatToolDescriptionFunctionData
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
AIChatToolDescription: TypeAlias = AIChatToolDescriptionFunction
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class AIChatToolCallFunctionData(TypedDict):
|
|
63
|
+
name: str
|
|
64
|
+
arguments: str
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class AIChatToolCallFunction(TypedDict):
|
|
68
|
+
id: str
|
|
69
|
+
type: Literal["function"]
|
|
70
|
+
function: AIChatToolCallFunctionData
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
AIChatToolCall: TypeAlias = AIChatToolCallFunction
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class AbbreviatedAIChat(TypedDict):
|
|
77
|
+
id: str
|
|
78
|
+
createdAt: str
|
|
79
|
+
updatedAt: str
|
|
80
|
+
summary: NotRequired[str]
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class AIChatMessageApi(TypedDict):
|
|
84
|
+
role: Literal["api"]
|
|
85
|
+
aiChat: AbbreviatedAIChat
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class AIChatMessageUser(TypedDict):
|
|
89
|
+
role: Literal["user"]
|
|
90
|
+
content: list[AIChatMessageUserContent]
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class AIChatMessageAssistant(TypedDict):
|
|
94
|
+
role: Literal["assistant"]
|
|
95
|
+
content: NotRequired[list[AIChatMessageAssistantContent]]
|
|
96
|
+
toolCalls: NotRequired[list[AIChatToolCall]]
|
|
97
|
+
refusal: NotRequired[str]
|
|
98
|
+
reasoning: NotRequired[str]
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class AIChatMessageAssistantDelta(TypedDict):
|
|
102
|
+
role: Literal["assistant_delta"]
|
|
103
|
+
content: NotRequired[str]
|
|
104
|
+
refusal: NotRequired[str]
|
|
105
|
+
reasoning: NotRequired[str]
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
class AIChatMessageError(TypedDict):
|
|
109
|
+
role: Literal["error"]
|
|
110
|
+
error: ProblemDetails
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class AIChatMessageTool(TypedDict):
|
|
114
|
+
role: Literal["tool"]
|
|
115
|
+
toolCallId: str
|
|
116
|
+
content: str
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
AIChatMessage: TypeAlias = AIChatMessageApi | AIChatMessageUser | AIChatMessageAssistant
|
|
120
|
+
AIChatMessageWithStreaming: TypeAlias = (
|
|
121
|
+
AIChatMessage | AIChatMessageAssistantDelta | AIChatMessageError
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
class AIChat(WithUser):
|
|
126
|
+
id: str
|
|
127
|
+
createdAt: str
|
|
128
|
+
updatedAt: str
|
|
129
|
+
messages: list[AIChatMessage]
|
|
130
|
+
summary: NotRequired[str]
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
class AIChatsPage(TypedDict):
|
|
134
|
+
items: list[AbbreviatedAIChat]
|
|
135
|
+
next: NotRequired[str]
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
class AIModelInfo(TypedDict):
|
|
139
|
+
id: str
|
|
140
|
+
name: str
|
|
141
|
+
default: NotRequired[bool]
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
class AIChatMessageUserCreate(TypedDict):
|
|
145
|
+
role: Literal["user"]
|
|
146
|
+
content: list[AIChatMessageUserContentEdition]
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
AIChatMessageUserAppend: TypeAlias = AIChatMessageUserCreate | AIChatMessageTool
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
class AIChatByIdClient:
|
|
153
|
+
def __init__(
|
|
154
|
+
self, transport: ApiTransport, workspace_id: str, chat_id: str
|
|
155
|
+
) -> None:
|
|
156
|
+
self._transport = transport
|
|
157
|
+
self._workspace_id = workspace_id
|
|
158
|
+
self._chat_id = chat_id
|
|
159
|
+
|
|
160
|
+
def get(self, *, with_usernames: bool | None = None) -> AIChat:
|
|
161
|
+
return cast(
|
|
162
|
+
AIChat,
|
|
163
|
+
self._transport.get_json(
|
|
164
|
+
"v1/workspaces/:workspaceId/ai/chats/:chatId",
|
|
165
|
+
path_params={
|
|
166
|
+
"workspaceId": self._workspace_id,
|
|
167
|
+
"chatId": self._chat_id,
|
|
168
|
+
},
|
|
169
|
+
search_params={"withUsernames": with_usernames},
|
|
170
|
+
),
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
def append(
|
|
174
|
+
self,
|
|
175
|
+
*,
|
|
176
|
+
message: AIChatMessageUserAppend,
|
|
177
|
+
on: Callable[[AIChatMessage | AIChatMessageWithStreaming], None],
|
|
178
|
+
model: str | None = None,
|
|
179
|
+
tools: list[AIChatToolDescription] | None = None,
|
|
180
|
+
stream: bool | None = None,
|
|
181
|
+
) -> None:
|
|
182
|
+
self._transport.sse(
|
|
183
|
+
"put",
|
|
184
|
+
"v1/workspaces/:workspaceId/ai/chats/:chatId",
|
|
185
|
+
path_params={"workspaceId": self._workspace_id, "chatId": self._chat_id},
|
|
186
|
+
json_data={
|
|
187
|
+
"message": message,
|
|
188
|
+
"model": model,
|
|
189
|
+
"tools": tools,
|
|
190
|
+
"stream": stream,
|
|
191
|
+
},
|
|
192
|
+
callback=on,
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
def delete(self) -> None:
|
|
196
|
+
self._transport.raw(
|
|
197
|
+
"delete",
|
|
198
|
+
"v1/workspaces/:workspaceId/ai/chats/:chatId",
|
|
199
|
+
path_params={"workspaceId": self._workspace_id, "chatId": self._chat_id},
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
class AIChatsClient:
|
|
204
|
+
def __init__(self, transport: ApiTransport, workspace_id: str) -> None:
|
|
205
|
+
self._transport = transport
|
|
206
|
+
self._workspace_id = workspace_id
|
|
207
|
+
|
|
208
|
+
def list(self, *, next: str | None = None) -> AIChatsPage:
|
|
209
|
+
return cast(
|
|
210
|
+
AIChatsPage,
|
|
211
|
+
self._transport.get_json(
|
|
212
|
+
"v1/workspaces/:workspaceId/ai/chats",
|
|
213
|
+
path_params={"workspaceId": self._workspace_id},
|
|
214
|
+
search_params={"next": next},
|
|
215
|
+
),
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
def create(
|
|
219
|
+
self,
|
|
220
|
+
*,
|
|
221
|
+
message: AIChatMessageUserCreate,
|
|
222
|
+
on: Callable[[AIChatMessage | AIChatMessageWithStreaming], None],
|
|
223
|
+
model: str | None = None,
|
|
224
|
+
tools: list[AIChatToolDescription] | None = None,
|
|
225
|
+
stream: bool | None = None,
|
|
226
|
+
) -> None:
|
|
227
|
+
self._transport.sse(
|
|
228
|
+
"post",
|
|
229
|
+
"v1/workspaces/:workspaceId/ai/chats",
|
|
230
|
+
path_params={"workspaceId": self._workspace_id},
|
|
231
|
+
json_data={
|
|
232
|
+
"message": message,
|
|
233
|
+
"model": model,
|
|
234
|
+
"tools": tools,
|
|
235
|
+
"stream": stream,
|
|
236
|
+
},
|
|
237
|
+
callback=on,
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
def id(self, chat_id: str) -> AIChatByIdClient:
|
|
241
|
+
return AIChatByIdClient(self._transport, self._workspace_id, chat_id)
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
class AIModelsClient:
|
|
245
|
+
def __init__(self, transport: ApiTransport, workspace_id: str) -> None:
|
|
246
|
+
self._transport = transport
|
|
247
|
+
self._workspace_id = workspace_id
|
|
248
|
+
|
|
249
|
+
def list(self) -> list[AIModelInfo]:
|
|
250
|
+
return cast(
|
|
251
|
+
list[AIModelInfo],
|
|
252
|
+
self._transport.get_json(
|
|
253
|
+
"v1/workspaces/:workspaceId/ai/models",
|
|
254
|
+
path_params={"workspaceId": self._workspace_id},
|
|
255
|
+
),
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
class WorkspaceAIClient:
|
|
260
|
+
def __init__(self, transport: ApiTransport, workspace_id: str) -> None:
|
|
261
|
+
self.chats = AIChatsClient(transport, workspace_id)
|
|
262
|
+
self.models = AIModelsClient(transport, workspace_id)
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from datetime import date, datetime
|
|
4
|
+
from typing import Literal, NotRequired, TypeAlias, TypedDict, cast
|
|
5
|
+
|
|
6
|
+
from .fetch import ApiTransport, to_optional_iso
|
|
7
|
+
from .users import WithUser
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class ApiToken(WithUser):
|
|
11
|
+
createdAt: str
|
|
12
|
+
id: str
|
|
13
|
+
name: str
|
|
14
|
+
permissions: "ApiTokenPermissions"
|
|
15
|
+
workspaceId: str
|
|
16
|
+
description: NotRequired[str]
|
|
17
|
+
expiresAt: NotRequired[str]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
ApiTokenPermissionSubject: TypeAlias = Literal["agent", "execution", "file", "job"]
|
|
21
|
+
ApiTokenPermissionAction: TypeAlias = Literal[
|
|
22
|
+
"create", "read", "update", "delete", "manage"
|
|
23
|
+
]
|
|
24
|
+
ApiTokenPermissions: TypeAlias = list[
|
|
25
|
+
tuple[ApiTokenPermissionAction, ApiTokenPermissionSubject]
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class CreateApiTokenArgs(TypedDict):
|
|
30
|
+
name: str
|
|
31
|
+
permissions: ApiTokenPermissions
|
|
32
|
+
description: NotRequired[str]
|
|
33
|
+
expiresAt: NotRequired[str | date | datetime]
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class CreateApiTokenResult(TypedDict):
|
|
37
|
+
apiToken: ApiToken
|
|
38
|
+
token: str
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class ApiTokensPage(TypedDict):
|
|
42
|
+
items: list[ApiToken]
|
|
43
|
+
next: NotRequired[str]
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class ApiTokensByIdClient:
|
|
47
|
+
def __init__(
|
|
48
|
+
self, transport: ApiTransport, workspace_id: str, api_token_id: str
|
|
49
|
+
) -> None:
|
|
50
|
+
self._transport = transport
|
|
51
|
+
self._workspace_id = workspace_id
|
|
52
|
+
self._api_token_id = api_token_id
|
|
53
|
+
|
|
54
|
+
def get(self, *, with_usernames: bool | None = None) -> ApiToken:
|
|
55
|
+
return cast(
|
|
56
|
+
ApiToken,
|
|
57
|
+
self._transport.get_json(
|
|
58
|
+
"v1/workspaces/:workspaceId/api-tokens/:apiTokenId",
|
|
59
|
+
path_params={
|
|
60
|
+
"workspaceId": self._workspace_id,
|
|
61
|
+
"apiTokenId": self._api_token_id,
|
|
62
|
+
},
|
|
63
|
+
search_params={"withUsernames": with_usernames},
|
|
64
|
+
),
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
def delete(self) -> None:
|
|
68
|
+
self._transport.raw(
|
|
69
|
+
"delete",
|
|
70
|
+
"v1/workspaces/:workspaceId/api-tokens/:apiTokenId",
|
|
71
|
+
path_params={
|
|
72
|
+
"workspaceId": self._workspace_id,
|
|
73
|
+
"apiTokenId": self._api_token_id,
|
|
74
|
+
},
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class ApiTokensClient:
|
|
79
|
+
def __init__(self, transport: ApiTransport, workspace_id: str) -> None:
|
|
80
|
+
self._transport = transport
|
|
81
|
+
self._workspace_id = workspace_id
|
|
82
|
+
|
|
83
|
+
def list(
|
|
84
|
+
self, *, next: str | None = None, with_usernames: bool | None = None
|
|
85
|
+
) -> ApiTokensPage:
|
|
86
|
+
return cast(
|
|
87
|
+
ApiTokensPage,
|
|
88
|
+
self._transport.get_json(
|
|
89
|
+
"v1/workspaces/:workspaceId/api-tokens",
|
|
90
|
+
path_params={"workspaceId": self._workspace_id},
|
|
91
|
+
search_params={"next": next, "withUsernames": with_usernames},
|
|
92
|
+
),
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
def create(self, args: CreateApiTokenArgs) -> CreateApiTokenResult:
|
|
96
|
+
payload = dict(args)
|
|
97
|
+
expires_at = payload.get("expiresAt")
|
|
98
|
+
if isinstance(expires_at, (date, datetime)):
|
|
99
|
+
payload["expiresAt"] = to_optional_iso(expires_at)
|
|
100
|
+
|
|
101
|
+
return cast(
|
|
102
|
+
CreateApiTokenResult,
|
|
103
|
+
self._transport.post_json(
|
|
104
|
+
"v1/workspaces/:workspaceId/api-tokens",
|
|
105
|
+
path_params={"workspaceId": self._workspace_id},
|
|
106
|
+
json_data=payload,
|
|
107
|
+
),
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
def id(self, api_token_id: str) -> ApiTokensByIdClient:
|
|
111
|
+
return ApiTokensByIdClient(self._transport, self._workspace_id, api_token_id)
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import Any, Mapping
|
|
5
|
+
|
|
6
|
+
from .agents import AgentsClient
|
|
7
|
+
from .fetch import AccessTokenProvider, ApiTransport, QueryParamValue, RawResponse
|
|
8
|
+
from .runtimes import RuntimesClient
|
|
9
|
+
from .service_prices import ServicePricesClient
|
|
10
|
+
from .users import UsersClient
|
|
11
|
+
from .workspaces import WorkspacesClient
|
|
12
|
+
|
|
13
|
+
DEFAULT_BASE_URL = "https://api.kotharcomputing.com"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass(slots=True)
|
|
17
|
+
class KotharClientOptions:
|
|
18
|
+
base_url: str = DEFAULT_BASE_URL
|
|
19
|
+
access_token: AccessTokenProvider | None = None
|
|
20
|
+
timeout: float = 30.0
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class KotharClient:
|
|
24
|
+
def __init__(
|
|
25
|
+
self,
|
|
26
|
+
options: KotharClientOptions | None = None,
|
|
27
|
+
*,
|
|
28
|
+
base_url: str = DEFAULT_BASE_URL,
|
|
29
|
+
access_token: AccessTokenProvider | None = None,
|
|
30
|
+
timeout: float = 30.0,
|
|
31
|
+
) -> None:
|
|
32
|
+
if options is None:
|
|
33
|
+
options = KotharClientOptions(
|
|
34
|
+
base_url=base_url,
|
|
35
|
+
access_token=access_token,
|
|
36
|
+
timeout=timeout,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
self._transport = ApiTransport(
|
|
40
|
+
base_url=options.base_url,
|
|
41
|
+
access_token=options.access_token,
|
|
42
|
+
timeout=options.timeout,
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
self.agents = AgentsClient(self._transport)
|
|
46
|
+
self.runtimes = RuntimesClient(self._transport)
|
|
47
|
+
self.service_prices = ServicePricesClient(self._transport)
|
|
48
|
+
self.users = UsersClient(self._transport)
|
|
49
|
+
self.workspaces = WorkspacesClient(self._transport)
|
|
50
|
+
|
|
51
|
+
# TS-compat aliases.
|
|
52
|
+
self.servicePrices = self.service_prices
|
|
53
|
+
self.apiVersion = self.api_version
|
|
54
|
+
self._raw = self.raw
|
|
55
|
+
|
|
56
|
+
def api_version(self) -> dict[str, str]:
|
|
57
|
+
return self._transport.get_json("version")
|
|
58
|
+
|
|
59
|
+
def raw(
|
|
60
|
+
self,
|
|
61
|
+
method: str,
|
|
62
|
+
templated_url: str,
|
|
63
|
+
*,
|
|
64
|
+
path_params: Mapping[str, str | int | bool] | None = None,
|
|
65
|
+
search_params: Mapping[str, QueryParamValue] | None = None,
|
|
66
|
+
headers: Mapping[str, str | None] | None = None,
|
|
67
|
+
body: bytes | str | None = None,
|
|
68
|
+
json_data: Any | None = None,
|
|
69
|
+
) -> RawResponse:
|
|
70
|
+
return self._transport.raw(
|
|
71
|
+
method,
|
|
72
|
+
templated_url,
|
|
73
|
+
path_params=path_params,
|
|
74
|
+
search_params=search_params,
|
|
75
|
+
headers=headers,
|
|
76
|
+
body=body,
|
|
77
|
+
json_data=json_data,
|
|
78
|
+
)
|