cycls 0.0.2.78__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.
- cycls/__init__.py +20 -0
- cycls/auth.py +4 -0
- cycls/cli.py +217 -0
- cycls/default-theme/assets/index-B0ZKcm_V.css +1 -0
- cycls/default-theme/assets/index-D5EDcI4J.js +422 -0
- cycls/default-theme/index.html +28 -0
- cycls/dev-theme/index.html +298 -0
- cycls/grpc/__init__.py +3 -0
- cycls/grpc/client.py +71 -0
- cycls/grpc/runtime.proto +18 -0
- cycls/grpc/runtime_pb2.py +40 -0
- cycls/grpc/runtime_pb2_grpc.py +100 -0
- cycls/grpc/server.py +60 -0
- cycls/runtime.py +465 -0
- cycls/sdk.py +181 -0
- cycls/web.py +131 -0
- cycls-0.0.2.78.dist-info/METADATA +274 -0
- cycls-0.0.2.78.dist-info/RECORD +20 -0
- cycls-0.0.2.78.dist-info/WHEEL +4 -0
- cycls-0.0.2.78.dist-info/entry_points.txt +3 -0
cycls/web.py
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import json, inspect
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from pydantic import BaseModel
|
|
4
|
+
from typing import Optional
|
|
5
|
+
|
|
6
|
+
class Config(BaseModel):
|
|
7
|
+
public_path: str = "theme"
|
|
8
|
+
header: str = ""
|
|
9
|
+
intro: str = ""
|
|
10
|
+
title: str = ""
|
|
11
|
+
prod: bool = False
|
|
12
|
+
auth: bool = False
|
|
13
|
+
plan: str = "free"
|
|
14
|
+
analytics: bool = False
|
|
15
|
+
org: Optional[str] = None
|
|
16
|
+
pk: str = ""
|
|
17
|
+
jwks: str = ""
|
|
18
|
+
|
|
19
|
+
async def openai_encoder(stream):
|
|
20
|
+
if inspect.isasyncgen(stream):
|
|
21
|
+
async for msg in stream:
|
|
22
|
+
if msg: yield f"data: {json.dumps({'choices': [{'delta': {'content': msg}}]})}\n\n"
|
|
23
|
+
else:
|
|
24
|
+
for msg in stream:
|
|
25
|
+
if msg: yield f"data: {json.dumps({'choices': [{'delta': {'content': msg}}]})}\n\n"
|
|
26
|
+
yield "data: [DONE]\n\n"
|
|
27
|
+
|
|
28
|
+
def sse(item):
|
|
29
|
+
if not item: return None
|
|
30
|
+
if not isinstance(item, dict): item = {"type": "text", "text": item}
|
|
31
|
+
return f"data: {json.dumps(item)}\n\n"
|
|
32
|
+
|
|
33
|
+
async def encoder(stream):
|
|
34
|
+
if inspect.isasyncgen(stream):
|
|
35
|
+
async for item in stream:
|
|
36
|
+
if msg := sse(item): yield msg
|
|
37
|
+
else:
|
|
38
|
+
for item in stream:
|
|
39
|
+
if msg := sse(item): yield msg
|
|
40
|
+
yield "data: [DONE]\n\n"
|
|
41
|
+
|
|
42
|
+
class Messages(list):
|
|
43
|
+
"""A list that provides text-only messages by default, with .raw for full data."""
|
|
44
|
+
def __init__(self, raw_messages):
|
|
45
|
+
self._raw = raw_messages
|
|
46
|
+
text_messages = []
|
|
47
|
+
for m in raw_messages:
|
|
48
|
+
text_content = "".join(
|
|
49
|
+
p.get("text", "") for p in m.get("parts", []) if p.get("type") == "text"
|
|
50
|
+
)
|
|
51
|
+
text_messages.append({
|
|
52
|
+
"role": m.get("role"),
|
|
53
|
+
"content": m.get("content") or text_content
|
|
54
|
+
})
|
|
55
|
+
super().__init__(text_messages)
|
|
56
|
+
|
|
57
|
+
@property
|
|
58
|
+
def raw(self):
|
|
59
|
+
return self._raw
|
|
60
|
+
|
|
61
|
+
def web(func, config):
|
|
62
|
+
from fastapi import FastAPI, Request, HTTPException, status, Depends
|
|
63
|
+
from fastapi.responses import StreamingResponse
|
|
64
|
+
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
|
|
65
|
+
import jwt
|
|
66
|
+
from jwt import PyJWKClient
|
|
67
|
+
from pydantic import EmailStr
|
|
68
|
+
from typing import List, Optional, Any
|
|
69
|
+
from fastapi.staticfiles import StaticFiles
|
|
70
|
+
|
|
71
|
+
if isinstance(config, dict):
|
|
72
|
+
config = Config(**config)
|
|
73
|
+
|
|
74
|
+
jwks = PyJWKClient(config.jwks)
|
|
75
|
+
|
|
76
|
+
class User(BaseModel):
|
|
77
|
+
id: str
|
|
78
|
+
name: Optional[str] = None
|
|
79
|
+
email: EmailStr
|
|
80
|
+
org: Optional[str] = None
|
|
81
|
+
plans: List[str] = []
|
|
82
|
+
|
|
83
|
+
class Context(BaseModel):
|
|
84
|
+
messages: Any
|
|
85
|
+
user: Optional[User] = None
|
|
86
|
+
|
|
87
|
+
app = FastAPI()
|
|
88
|
+
bearer_scheme = HTTPBearer()
|
|
89
|
+
|
|
90
|
+
def validate(bearer: HTTPAuthorizationCredentials = Depends(bearer_scheme)):
|
|
91
|
+
try:
|
|
92
|
+
key = jwks.get_signing_key_from_jwt(bearer.credentials)
|
|
93
|
+
decoded = jwt.decode(bearer.credentials, key.key, algorithms=["RS256"], leeway=10)
|
|
94
|
+
return {"type": "user",
|
|
95
|
+
"user": {"id": decoded.get("id"), "name": decoded.get("name"), "email": decoded.get("email"), "org": decoded.get("org"),
|
|
96
|
+
"plans": decoded.get("public", {}).get("plans", [])}}
|
|
97
|
+
except:
|
|
98
|
+
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid credentials", headers={"WWW-Authenticate": "Bearer"})
|
|
99
|
+
|
|
100
|
+
@app.post("/")
|
|
101
|
+
@app.post("/chat/cycls")
|
|
102
|
+
@app.post("/chat/completions")
|
|
103
|
+
async def back(request: Request, jwt: Optional[dict] = Depends(validate) if config.auth else None):
|
|
104
|
+
data = await request.json()
|
|
105
|
+
messages = data.get("messages")
|
|
106
|
+
user_data = jwt.get("user") if jwt else None
|
|
107
|
+
context = Context(messages = Messages(messages), user = User(**user_data) if user_data else None)
|
|
108
|
+
stream = await func(context) if inspect.iscoroutinefunction(func) else func(context)
|
|
109
|
+
if request.url.path == "/chat/completions":
|
|
110
|
+
stream = openai_encoder(stream)
|
|
111
|
+
elif request.url.path == "/chat/cycls":
|
|
112
|
+
stream = encoder(stream)
|
|
113
|
+
return StreamingResponse(stream, media_type="text/event-stream")
|
|
114
|
+
|
|
115
|
+
@app.get("/config")
|
|
116
|
+
async def get_config():
|
|
117
|
+
return config
|
|
118
|
+
|
|
119
|
+
if Path("public").is_dir():
|
|
120
|
+
app.mount("/public", StaticFiles(directory="public", html=True))
|
|
121
|
+
app.mount("/", StaticFiles(directory=config.public_path, html=True))
|
|
122
|
+
|
|
123
|
+
return app
|
|
124
|
+
|
|
125
|
+
def serve(func, config, name, port):
|
|
126
|
+
import uvicorn, logging
|
|
127
|
+
if isinstance(config, dict):
|
|
128
|
+
config = Config(**config)
|
|
129
|
+
logging.getLogger("uvicorn.error").addFilter(lambda r: "0.0.0.0" not in r.getMessage())
|
|
130
|
+
print(f"\n🔨 {name} => http://localhost:{port}\n")
|
|
131
|
+
uvicorn.run(web(func, config), host="0.0.0.0", port=port)
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cycls
|
|
3
|
+
Version: 0.0.2.78
|
|
4
|
+
Summary: Distribute Intelligence
|
|
5
|
+
Author: Mohammed J. AlRujayi
|
|
6
|
+
Author-email: mj@cycls.com
|
|
7
|
+
Requires-Python: >=3.9,<4.0
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
15
|
+
Provides-Extra: modal
|
|
16
|
+
Requires-Dist: cloudpickle (>=3.1.1,<4.0.0)
|
|
17
|
+
Requires-Dist: docker (>=7.1.0,<8.0.0)
|
|
18
|
+
Requires-Dist: fastapi (>=0.111.0,<0.112.0)
|
|
19
|
+
Requires-Dist: grpcio (>=1.76.0,<2.0.0)
|
|
20
|
+
Requires-Dist: httpx (>=0.27.0,<0.28.0)
|
|
21
|
+
Requires-Dist: modal (>=1.1.0,<2.0.0) ; extra == "modal"
|
|
22
|
+
Requires-Dist: protobuf (>=6.0,<7.0)
|
|
23
|
+
Requires-Dist: pyjwt (>=2.8.0,<3.0.0)
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
|
|
26
|
+
<h3 align="center">
|
|
27
|
+
Distribute Intelligence
|
|
28
|
+
</h3>
|
|
29
|
+
|
|
30
|
+
<h4 align="center">
|
|
31
|
+
<a href="https://cycls.com">Website</a> |
|
|
32
|
+
<a href="https://docs.cycls.com">Docs</a>
|
|
33
|
+
</h4>
|
|
34
|
+
|
|
35
|
+
<h4 align="center">
|
|
36
|
+
<a href="https://pypi.python.org/pypi/cycls"><img src="https://img.shields.io/pypi/v/cycls.svg?label=cycls+pypi&color=blueviolet" alt="cycls Python package on PyPi" /></a>
|
|
37
|
+
<a href="https://github.com/Cycls/cycls/actions/workflows/tests.yml"><img src="https://github.com/Cycls/cycls/actions/workflows/tests.yml/badge.svg" alt="Tests" /></a>
|
|
38
|
+
<a href="https://blog.cycls.com"><img src="https://img.shields.io/badge/newsletter-blueviolet.svg?logo=substack&label=cycls" alt="Cycls newsletter" /></a>
|
|
39
|
+
<a href="https://x.com/cyclsai">
|
|
40
|
+
<img src="https://img.shields.io/twitter/follow/CyclsAI" alt="Cycls Twitter" />
|
|
41
|
+
</a>
|
|
42
|
+
</h4>
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
# Cycls
|
|
47
|
+
|
|
48
|
+
The open-source SDK for distributing AI agents.
|
|
49
|
+
|
|
50
|
+
## Distribute Intelligence
|
|
51
|
+
|
|
52
|
+
Write a function. Deploy it as an API, a web interface, or both. Add authentication, analytics, and monetization with flags.
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
import cycls
|
|
56
|
+
|
|
57
|
+
cycls.api_key = "YOUR_CYCLS_API_KEY"
|
|
58
|
+
|
|
59
|
+
@cycls.agent(pip=["openai"])
|
|
60
|
+
async def agent(context):
|
|
61
|
+
from openai import AsyncOpenAI
|
|
62
|
+
client = AsyncOpenAI()
|
|
63
|
+
|
|
64
|
+
stream = await client.responses.create(
|
|
65
|
+
model="o3-mini",
|
|
66
|
+
input=context.messages,
|
|
67
|
+
stream=True,
|
|
68
|
+
reasoning={"effort": "medium", "summary": "auto"},
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
async for event in stream:
|
|
72
|
+
if event.type == "response.reasoning_summary_text.delta":
|
|
73
|
+
yield {"type": "thinking", "thinking": event.delta} # Renders as thinking bubble
|
|
74
|
+
elif event.type == "response.output_text.delta":
|
|
75
|
+
yield event.delta
|
|
76
|
+
|
|
77
|
+
agent.deploy() # Live at https://agent.cycls.ai
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Installation
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
pip install cycls
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Requires Docker.
|
|
87
|
+
|
|
88
|
+
## What You Get
|
|
89
|
+
|
|
90
|
+
- **Streaming API** - OpenAI-compatible `/chat/completions` endpoint
|
|
91
|
+
- **Web Interface** - Chat UI served automatically
|
|
92
|
+
- **Authentication** - `auth=True` enables JWT-based access control
|
|
93
|
+
- **Analytics** - `analytics=True` tracks usage
|
|
94
|
+
- **Monetization** - `plan="cycls_pass"` integrates with [Cycls Pass](https://cycls.ai) subscriptions
|
|
95
|
+
- **Native UI Components** - Render thinking bubbles, tables, code blocks in responses
|
|
96
|
+
|
|
97
|
+
## Running
|
|
98
|
+
|
|
99
|
+
```python
|
|
100
|
+
agent.local() # Development with hot-reload (localhost:8080)
|
|
101
|
+
agent.local(watch=False) # Development without hot-reload
|
|
102
|
+
agent.deploy() # Production: https://agent.cycls.ai
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Get an API key at [cycls.com](https://cycls.com).
|
|
106
|
+
|
|
107
|
+
## Authentication & Analytics
|
|
108
|
+
|
|
109
|
+
```python
|
|
110
|
+
@cycls.agent(pip=["openai"], auth=True, analytics=True)
|
|
111
|
+
async def agent(context):
|
|
112
|
+
# context.user available when auth=True
|
|
113
|
+
user = context.user # User(id, email, name, plans)
|
|
114
|
+
yield f"Hello {user.name}!"
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
| Flag | Description |
|
|
118
|
+
|------|-------------|
|
|
119
|
+
| `auth=True` | Universal user pool via Cycls Pass (Clerk-based). You can also use your own Clerk auth. |
|
|
120
|
+
| `analytics=True` | Rich usage metrics available on the Cycls dashboard. |
|
|
121
|
+
| `plan="cycls_pass"` | Monetization via Cycls Pass subscriptions. Enables both auth and analytics. |
|
|
122
|
+
|
|
123
|
+
## Native UI Components
|
|
124
|
+
|
|
125
|
+
Yield structured objects for rich streaming responses:
|
|
126
|
+
|
|
127
|
+
```python
|
|
128
|
+
@cycls.agent()
|
|
129
|
+
async def demo(context):
|
|
130
|
+
yield {"type": "thinking", "thinking": "Analyzing the request..."}
|
|
131
|
+
yield "Here's what I found:\n\n"
|
|
132
|
+
|
|
133
|
+
yield {"type": "table", "headers": ["Name", "Status"]}
|
|
134
|
+
yield {"type": "table", "row": ["Server 1", "Online"]}
|
|
135
|
+
yield {"type": "table", "row": ["Server 2", "Offline"]}
|
|
136
|
+
|
|
137
|
+
yield {"type": "code", "code": "result = analyze(data)", "language": "python"}
|
|
138
|
+
yield {"type": "callout", "callout": "Analysis complete!", "style": "success"}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
| Component | Streaming |
|
|
142
|
+
|-----------|-----------|
|
|
143
|
+
| `{"type": "thinking", "thinking": "..."}` | Yes |
|
|
144
|
+
| `{"type": "code", "code": "...", "language": "..."}` | Yes |
|
|
145
|
+
| `{"type": "table", "headers": [...]}` | Yes |
|
|
146
|
+
| `{"type": "table", "row": [...]}` | Yes |
|
|
147
|
+
| `{"type": "status", "status": "..."}` | Yes |
|
|
148
|
+
| `{"type": "callout", "callout": "...", "style": "..."}` | Yes |
|
|
149
|
+
| `{"type": "image", "src": "..."}` | Yes |
|
|
150
|
+
|
|
151
|
+
### Thinking Bubbles
|
|
152
|
+
|
|
153
|
+
The `{"type": "thinking", "thinking": "..."}` component renders as a collapsible thinking bubble in the UI. Each yield appends to the same bubble until a different component type is yielded:
|
|
154
|
+
|
|
155
|
+
```python
|
|
156
|
+
# Multiple yields build one thinking bubble
|
|
157
|
+
yield {"type": "thinking", "thinking": "Let me "}
|
|
158
|
+
yield {"type": "thinking", "thinking": "analyze this..."}
|
|
159
|
+
yield {"type": "thinking", "thinking": " Done thinking."}
|
|
160
|
+
|
|
161
|
+
# Then output the response
|
|
162
|
+
yield "Here's what I found..."
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
This works seamlessly with OpenAI's reasoning models - just map reasoning summaries to the thinking component.
|
|
166
|
+
|
|
167
|
+
## Context Object
|
|
168
|
+
|
|
169
|
+
```python
|
|
170
|
+
@cycls.agent()
|
|
171
|
+
async def chat(context):
|
|
172
|
+
context.messages # [{"role": "user", "content": "..."}]
|
|
173
|
+
context.messages.raw # Full data including UI component parts
|
|
174
|
+
context.user # User(id, email, name, plans) when auth=True
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## API Endpoints
|
|
178
|
+
|
|
179
|
+
| Endpoint | Format |
|
|
180
|
+
|----------|--------|
|
|
181
|
+
| `POST chat/cycls` | Cycls streaming protocol |
|
|
182
|
+
| `POST chat/completions` | OpenAI-compatible |
|
|
183
|
+
|
|
184
|
+
## Streaming Protocol
|
|
185
|
+
|
|
186
|
+
Cycls streams structured components over SSE:
|
|
187
|
+
|
|
188
|
+
```
|
|
189
|
+
data: {"type": "thinking", "thinking": "Let me "}
|
|
190
|
+
data: {"type": "thinking", "thinking": "analyze..."}
|
|
191
|
+
data: {"type": "text", "text": "Here's the answer"}
|
|
192
|
+
data: {"type": "callout", "callout": "Done!", "style": "success"}
|
|
193
|
+
data: [DONE]
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
See [docs/streaming-protocol.md](docs/streaming-protocol.md) for frontend integration.
|
|
197
|
+
|
|
198
|
+
## Declarative Infrastructure
|
|
199
|
+
|
|
200
|
+
Define your entire runtime in the decorator:
|
|
201
|
+
|
|
202
|
+
```python
|
|
203
|
+
@cycls.agent(
|
|
204
|
+
pip=["openai", "pandas", "numpy"],
|
|
205
|
+
apt=["ffmpeg", "libmagic1"],
|
|
206
|
+
copy=["./utils.py", "./models/", "/absolute/path/to/config.json"],
|
|
207
|
+
copy_public=["./assets/logo.png", "./static/"],
|
|
208
|
+
)
|
|
209
|
+
async def my_agent(context):
|
|
210
|
+
...
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### `pip` - Python Packages
|
|
214
|
+
|
|
215
|
+
Install any packages from PyPI. These are installed during the container build.
|
|
216
|
+
|
|
217
|
+
```python
|
|
218
|
+
pip=["openai", "pandas", "numpy", "transformers"]
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### `apt` - System Packages
|
|
222
|
+
|
|
223
|
+
Install system-level dependencies via apt-get. Need ffmpeg for audio processing? ImageMagick for images? Just declare it.
|
|
224
|
+
|
|
225
|
+
```python
|
|
226
|
+
apt=["ffmpeg", "imagemagick", "libpq-dev"]
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### `copy` - Bundle Files and Directories
|
|
230
|
+
|
|
231
|
+
Include local files and directories in your container. Works with both relative and absolute paths. Copies files and entire directory trees.
|
|
232
|
+
|
|
233
|
+
```python
|
|
234
|
+
copy=[
|
|
235
|
+
"./utils.py", # Single file, relative path
|
|
236
|
+
"./models/", # Entire directory
|
|
237
|
+
"/home/user/configs/app.json", # Absolute path
|
|
238
|
+
]
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
Then import them in your function:
|
|
242
|
+
|
|
243
|
+
```python
|
|
244
|
+
@cycls.agent(copy=["./utils.py"])
|
|
245
|
+
async def chat(context):
|
|
246
|
+
from utils import helper_function # Your bundled module
|
|
247
|
+
...
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
### `copy_public` - Static Files
|
|
251
|
+
|
|
252
|
+
Files and directories served at the `/public` endpoint. Perfect for images, downloads, or any static assets your agent needs to reference.
|
|
253
|
+
|
|
254
|
+
```python
|
|
255
|
+
copy_public=["./assets/logo.png", "./downloads/"]
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
Access them at `https://your-agent.cycls.ai/public/logo.png`.
|
|
259
|
+
|
|
260
|
+
---
|
|
261
|
+
|
|
262
|
+
### What You Get
|
|
263
|
+
|
|
264
|
+
- **One file** - Code, dependencies, configuration, and infrastructure together
|
|
265
|
+
- **Instant deploys** - Unchanged code deploys in seconds from cache
|
|
266
|
+
- **No drift** - What you see is what runs. Always.
|
|
267
|
+
- **Just works** - Closures, lambdas, dynamic imports - your function runs exactly as written
|
|
268
|
+
|
|
269
|
+
No YAML. No Dockerfiles. No infrastructure repo. The code is the deployment.
|
|
270
|
+
|
|
271
|
+
## License
|
|
272
|
+
|
|
273
|
+
MIT
|
|
274
|
+
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
cycls/__init__.py,sha256=vyI1d_8VP4XW7MliFuUs_P3O9KQxyCwQu-JkxrCyhPQ,597
|
|
2
|
+
cycls/auth.py,sha256=xkndHZyCfnlertMMEKerCJjf23N3fVcTRVTTSXTTuzg,247
|
|
3
|
+
cycls/cli.py,sha256=AKf0z7ZLau3GvBVR_IhB7agmq4nVaHkcuUafNyvv2_A,7978
|
|
4
|
+
cycls/default-theme/assets/index-B0ZKcm_V.css,sha256=wK9-NhEB8xPcN9Zv69zpOcfGTlFbMwyC9WqTmSKUaKw,6546
|
|
5
|
+
cycls/default-theme/assets/index-D5EDcI4J.js,sha256=sN4qRcAXa7DBd9JzmVcCoCwH4l8cNCM-U9QGUjBvWSo,1346506
|
|
6
|
+
cycls/default-theme/index.html,sha256=bM-yW_g0cGrV40Q5yY3ccY0fM4zI1Wuu5I8EtGFJIxs,828
|
|
7
|
+
cycls/dev-theme/index.html,sha256=QJBHkdNuMMiwQU7o8dN8__8YQeQB45D37D-NCXIWB2Q,11585
|
|
8
|
+
cycls/grpc/__init__.py,sha256=sr8UQMgJEHyBreBKV8xz8UCd0zDP5lhjXTnfkOB_yOY,63
|
|
9
|
+
cycls/grpc/client.py,sha256=zDFIBABXzuv_RUVn5LllppZ38C7k01RyAS8ZURBjudQ,2270
|
|
10
|
+
cycls/grpc/runtime.proto,sha256=B1AqrNIXOtr3Xsyzfc2Z1OCBepa6hsi4DJ4a3Pf33IQ,244
|
|
11
|
+
cycls/grpc/runtime_pb2.py,sha256=vEJo8FGP5aWPSDqzjZldfctduA2ojiyvoody7vpf-1w,1703
|
|
12
|
+
cycls/grpc/runtime_pb2_grpc.py,sha256=KFd8KqGbiNsKm8X39Q9_BPwXjeZUiDl8O_4aTlEys3k,3394
|
|
13
|
+
cycls/grpc/server.py,sha256=pfb4bo06NKDv0OpknqMSMjB9f8HUR41EZau1c6_XU5A,1911
|
|
14
|
+
cycls/runtime.py,sha256=8DqK14n5DLuyl3sHQrRqLOeCPzo6_zYP8iSiHbXH_yU,18038
|
|
15
|
+
cycls/sdk.py,sha256=B5_ZNGvXqqKcAGAvhk-tyr0YH8kfvCJPKl01rhetvFw,6588
|
|
16
|
+
cycls/web.py,sha256=_QNH8K55vTm90Z7tvcRKal5IybjkB1GY7Pf9p3qu3r8,4659
|
|
17
|
+
cycls-0.0.2.78.dist-info/METADATA,sha256=C7BGrOxmY41GTVXuZldlLvarXEn6ImBzfGI0DvXm6HE,8496
|
|
18
|
+
cycls-0.0.2.78.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
|
|
19
|
+
cycls-0.0.2.78.dist-info/entry_points.txt,sha256=vEhqUxFhhuzCKWtq02LbMnT3wpUqdfgcM3Yh-jjXom8,40
|
|
20
|
+
cycls-0.0.2.78.dist-info/RECORD,,
|