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 ADDED
@@ -0,0 +1,20 @@
1
+ import sys
2
+ from types import ModuleType
3
+ from .sdk import function, agent
4
+ from .runtime import Runtime
5
+
6
+ class _Module(ModuleType):
7
+ def __getattr__(self, name):
8
+ from . import sdk
9
+ if name in ("api_key", "base_url"):
10
+ return getattr(sdk, name)
11
+ raise AttributeError(f"module 'cycls' has no attribute '{name}'")
12
+
13
+ def __setattr__(self, name, value):
14
+ from . import sdk
15
+ if name in ("api_key", "base_url"):
16
+ setattr(sdk, name, value)
17
+ return
18
+ super().__setattr__(name, value)
19
+
20
+ sys.modules[__name__].__class__ = _Module
cycls/auth.py ADDED
@@ -0,0 +1,4 @@
1
+ JWKS_PROD = "https://clerk.cycls.ai/.well-known/jwks.json"
2
+ JWKS_TEST = "https://select-sloth-58.clerk.accounts.dev/.well-known/jwks.json"
3
+ PK_LIVE = "pk_live_Y2xlcmsuY3ljbHMuYWkk"
4
+ PK_TEST = "pk_test_c2VsZWN0LXNsb3RoLTU4LmNsZXJrLmFjY291bnRzLmRldiQ"
cycls/cli.py ADDED
@@ -0,0 +1,217 @@
1
+ import sys
2
+ import json
3
+ import time
4
+ import threading
5
+ import httpx
6
+
7
+ # ANSI codes
8
+ DIM = "\033[2m"
9
+ BOLD = "\033[1m"
10
+ RESET = "\033[0m"
11
+ CLEAR_LINE = "\r\033[K"
12
+ GREEN = "\033[32m"
13
+ YELLOW = "\033[33m"
14
+ BLUE = "\033[34m"
15
+ RED = "\033[31m"
16
+ CYAN = "\033[36m"
17
+ MAGENTA = "\033[35m"
18
+
19
+ CALLOUT_STYLES = {
20
+ "success": ("✓", GREEN),
21
+ "warning": ("⚠", YELLOW),
22
+ "info": ("ℹ", BLUE),
23
+ "error": ("✗", RED),
24
+ }
25
+
26
+ def format_time(seconds):
27
+ if seconds < 60:
28
+ return f"{seconds:.1f}s"
29
+ return f"{int(seconds // 60)}m {int(seconds % 60)}s"
30
+
31
+ def render_table(headers, rows):
32
+ if not headers:
33
+ return
34
+ widths = [len(str(h)) for h in headers]
35
+ for row in rows:
36
+ for i, cell in enumerate(row):
37
+ if i < len(widths):
38
+ widths[i] = max(widths[i], len(str(cell)))
39
+
40
+ top = "┌" + "┬".join("─" * (w + 2) for w in widths) + "┐"
41
+ sep = "├" + "┼".join("─" * (w + 2) for w in widths) + "┤"
42
+ bot = "└" + "┴".join("─" * (w + 2) for w in widths) + "┘"
43
+
44
+ def fmt_row(cells, bold=False):
45
+ parts = []
46
+ for i, w in enumerate(widths):
47
+ cell = str(cells[i]) if i < len(cells) else ""
48
+ if bold:
49
+ parts.append(f" {BOLD}{cell.ljust(w)}{RESET} ")
50
+ else:
51
+ parts.append(f" {cell.ljust(w)} ")
52
+ return "│" + "│".join(parts) + "│"
53
+
54
+ print(top)
55
+ print(fmt_row(headers, bold=True))
56
+ print(sep)
57
+ for row in rows:
58
+ print(fmt_row(row))
59
+ print(bot)
60
+
61
+ class Spinner:
62
+ def __init__(self):
63
+ self.active = False
64
+ self.thread = None
65
+ self.frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]
66
+
67
+ def start(self):
68
+ self.active = True
69
+ def spin():
70
+ i = 0
71
+ while self.active:
72
+ if not self.active:
73
+ break
74
+ print(f"\r{MAGENTA}{self.frames[i % len(self.frames)]}{RESET} ", end="", flush=True)
75
+ for _ in range(8): # Check active more frequently
76
+ if not self.active:
77
+ break
78
+ time.sleep(0.01)
79
+ i += 1
80
+ self.thread = threading.Thread(target=spin, daemon=True)
81
+ self.thread.start()
82
+
83
+ def stop(self):
84
+ self.active = False
85
+ if self.thread:
86
+ self.thread.join(timeout=0.2)
87
+ print(f"{CLEAR_LINE}", end="", flush=True)
88
+
89
+ def chat(url):
90
+ messages = []
91
+ endpoint = f"{url.rstrip('/')}/chat/cycls"
92
+
93
+ print(f"\n{MAGENTA}●{RESET} {BOLD}{url}{RESET}\n")
94
+
95
+ while True:
96
+ try:
97
+ user_input = input(f"{CYAN}❯{RESET} ")
98
+ if not user_input.strip():
99
+ continue
100
+
101
+ messages.append({"role": "user", "content": user_input})
102
+ print()
103
+
104
+ start_time = time.time()
105
+ in_thinking = False
106
+ table_headers = []
107
+ table_rows = []
108
+
109
+ with httpx.stream("POST", endpoint, json={"messages": messages}, timeout=None) as r:
110
+ for line in r.iter_lines():
111
+ if line.startswith("data: ") and line != "data: [DONE]":
112
+
113
+ data = json.loads(line[6:])
114
+ msg_type = data.get("type")
115
+
116
+ # Handle plain string or missing type
117
+ if msg_type is None:
118
+ # Could be OpenAI format or plain text
119
+ if isinstance(data, str):
120
+ print(data, end="", flush=True)
121
+ continue
122
+ elif "choices" in data:
123
+ # OpenAI format
124
+ content = data.get("choices", [{}])[0].get("delta", {}).get("content", "")
125
+ if content:
126
+ print(content, end="", flush=True)
127
+ continue
128
+ elif "text" in data:
129
+ print(data.get("text", ""), end="", flush=True)
130
+ continue
131
+ elif "content" in data:
132
+ print(data.get("content", ""), end="", flush=True)
133
+ continue
134
+ else:
135
+ # Debug: print raw data
136
+ print(f"[debug: {data}]", end="", flush=True)
137
+ continue
138
+
139
+ # Close thinking if switching
140
+ if msg_type != "thinking" and in_thinking:
141
+ print(f"</thinking>{RESET}\n", end="", flush=True)
142
+ in_thinking = False
143
+
144
+ # Flush table if switching
145
+ if msg_type != "table" and table_headers:
146
+ render_table(table_headers, table_rows)
147
+ table_headers = []
148
+ table_rows = []
149
+
150
+ if msg_type == "thinking":
151
+ if not in_thinking:
152
+ print(f"{DIM}<thinking>", end="", flush=True)
153
+ in_thinking = True
154
+ print(data.get("thinking", ""), end="", flush=True)
155
+
156
+ elif msg_type == "text":
157
+ print(data.get("text", ""), end="", flush=True)
158
+
159
+ elif msg_type == "code":
160
+ lang = data.get("language", "")
161
+ print(f"\n```{lang}\n{data.get('code', '')}\n```\n", end="", flush=True)
162
+
163
+ elif msg_type == "status":
164
+ print(f"{DIM}[{data.get('status', '')}]{RESET} ", end="", flush=True)
165
+
166
+ elif msg_type == "table":
167
+ if "headers" in data:
168
+ if table_headers:
169
+ render_table(table_headers, table_rows)
170
+ table_headers = data["headers"]
171
+ table_rows = []
172
+ elif "row" in data:
173
+ table_rows.append(data["row"])
174
+
175
+ elif msg_type == "callout":
176
+ style = data.get("style", "info")
177
+ icon, color = CALLOUT_STYLES.get(style, ("•", RESET))
178
+ title = data.get("title", "")
179
+ text = data.get("callout", "")
180
+ if title:
181
+ print(f"\n{color}{icon} {BOLD}{title}{RESET}")
182
+ print(f"{color} {text}{RESET}\n", end="", flush=True)
183
+ else:
184
+ print(f"\n{color}{icon} {text}{RESET}\n", end="", flush=True)
185
+
186
+ elif msg_type == "image":
187
+ print(f"{DIM}[image: {data.get('src', '')}]{RESET}", end="", flush=True)
188
+
189
+ # Flush remaining
190
+ if table_headers:
191
+ render_table(table_headers, table_rows)
192
+ if in_thinking:
193
+ print(f"</thinking>{RESET}", end="", flush=True)
194
+
195
+ elapsed = time.time() - start_time
196
+ print(f"\n\n{DIM}✦ {format_time(elapsed)}{RESET}\n")
197
+
198
+ except KeyboardInterrupt:
199
+ continue
200
+ except EOFError:
201
+ print(f"{RESET}\n👋")
202
+ break
203
+ except (httpx.ReadError, httpx.ConnectError):
204
+ print(f"{RESET}🔄 Reconnecting...", end="", flush=True)
205
+ time.sleep(1)
206
+ print(CLEAR_LINE, end="")
207
+ if messages:
208
+ messages.pop()
209
+
210
+ def main():
211
+ if len(sys.argv) < 3 or sys.argv[1] != "chat":
212
+ print("Usage: cycls chat <url>")
213
+ sys.exit(1)
214
+ chat(sys.argv[2])
215
+
216
+ if __name__ == "__main__":
217
+ main()
@@ -0,0 +1 @@
1
+ :root{--bg-primary: #ffffff;--bg-secondary: #f9fafb;--bg-tertiary: #f3f4f6;--bg-sidebar: rgba(255, 255, 255, .8);--bg-hover: rgba(0, 0, 0, .05);--bg-active: rgba(0, 0, 0, .07);--bg-overlay: rgba(0, 0, 0, .25);--text-primary: #0d0d0d;--text-secondary: #374151;--text-tertiary: #6b6b6b;--text-muted: #9ca3af;--border-primary: rgba(0, 0, 0, .1);--border-secondary: rgba(0, 0, 0, .06);--accent-primary: #10a37f;--accent-hover: #0d8a6c;--scrollbar-thumb: #d1d1d1;--scrollbar-thumb-hover: #b1b1b1;--shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, .05);--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, .1), 0 2px 4px -1px rgba(0, 0, 0, .06);--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, .1), 0 4px 6px -2px rgba(0, 0, 0, .05);--code-bg: #f3f4f6;--code-text: #1f2937;--msg-user-bg: #f3f4f6;--msg-assistant-bg: transparent;--input-bg: #ffffff;--input-border: #e5e7eb;--input-focus-border: #10a37f;--btn-primary-bg: #0d0d0d;--btn-primary-text: #ffffff;--btn-secondary-bg: transparent;--btn-secondary-text: #0d0d0d}.dark,[data-theme=dark]{--bg-primary: #212121;--bg-secondary: #171717;--bg-tertiary: #2f2f2f;--bg-sidebar: rgba(23, 23, 23, .95);--bg-hover: rgba(255, 255, 255, .08);--bg-active: rgba(255, 255, 255, .12);--bg-overlay: rgba(0, 0, 0, .5);--text-primary: #ececec;--text-secondary: #c5c5c5;--text-tertiary: #8e8e8e;--text-muted: #6b6b6b;--border-primary: rgba(255, 255, 255, .1);--border-secondary: rgba(255, 255, 255, .06);--accent-primary: #10a37f;--accent-hover: #1abc94;--scrollbar-thumb: #4a4a4a;--scrollbar-thumb-hover: #5a5a5a;--shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, .3);--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, .4), 0 2px 4px -1px rgba(0, 0, 0, .3);--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, .4), 0 4px 6px -2px rgba(0, 0, 0, .3);--code-bg: #2f2f2f;--code-text: #e5e7eb;--msg-user-bg: #2f2f2f;--msg-assistant-bg: transparent;--input-bg: #2f2f2f;--input-border: #424242;--input-focus-border: #10a37f;--btn-primary-bg: #ececec;--btn-primary-text: #0d0d0d;--btn-secondary-bg: transparent;--btn-secondary-text: #ececec}@media (prefers-color-scheme: dark){:root:not(.light):not([data-theme=light]){--bg-primary: #212121;--bg-secondary: #171717;--bg-tertiary: #2f2f2f;--bg-sidebar: rgba(23, 23, 23, .95);--bg-hover: rgba(255, 255, 255, .08);--bg-active: rgba(255, 255, 255, .12);--bg-overlay: rgba(0, 0, 0, .5);--text-primary: #ececec;--text-secondary: #c5c5c5;--text-tertiary: #8e8e8e;--text-muted: #6b6b6b;--border-primary: rgba(255, 255, 255, .1);--border-secondary: rgba(255, 255, 255, .06);--accent-primary: #10a37f;--accent-hover: #1abc94;--scrollbar-thumb: #4a4a4a;--scrollbar-thumb-hover: #5a5a5a;--shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, .3);--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, .4), 0 2px 4px -1px rgba(0, 0, 0, .3);--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, .4), 0 4px 6px -2px rgba(0, 0, 0, .3);--code-bg: #2f2f2f;--code-text: #e5e7eb;--msg-user-bg: #2f2f2f;--msg-assistant-bg: transparent;--input-bg: #2f2f2f;--input-border: #424242;--input-focus-border: #10a37f;--btn-primary-bg: #ececec;--btn-primary-text: #0d0d0d;--btn-secondary-bg: transparent;--btn-secondary-text: #ececec}}body{background-color:var(--bg-primary);color:var(--text-primary);transition:background-color .2s ease,color .2s ease}.cl-internal-p8bmz4{box-shadow:none!important;border:1px solid var(--border-primary);box-shadow:var(--shadow-sm)}.cl-drawerRoot{z-index:99!important}.cl-pricingTableCardFooterButton{padding:10px}.cl-pricingTableCardFee{font-size:2rem}.cl-pricingTableCardTitleContainer{margin-bottom:10px}.scrollbar-thin{scrollbar-width:thin;scrollbar-color:var(--scrollbar-thumb) transparent}.scrollbar-thin::-webkit-scrollbar{width:6px}.scrollbar-thin::-webkit-scrollbar-track{background:transparent}.scrollbar-thin::-webkit-scrollbar-thumb{background-color:var(--scrollbar-thumb);border-radius:3px}.scrollbar-thin::-webkit-scrollbar-thumb:hover{background-color:var(--scrollbar-thumb-hover)}.scrollbar-thin::-webkit-scrollbar-thumb{background-color:transparent}.scrollbar-thin:hover::-webkit-scrollbar-thumb{background-color:var(--scrollbar-thumb)}.sidebar-transition{transition:transform .3s ease-in-out,width .3s ease-in-out}.sidebar-overlay{z-index:40}.sidebar-panel{z-index:50}@keyframes slideDown{0%{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:translateY(0)}}.dropdown-menu{animation:slideDown .15s ease-out}.sidebar-item:focus-visible{outline:2px solid var(--text-primary);outline-offset:-2px;border-radius:8px}.user-profile-item:hover .user-avatar{transform:scale(1.02)}.chat-title-fade{mask-image:linear-gradient(to right,black 85%,transparent 100%);-webkit-mask-image:linear-gradient(to right,black 85%,transparent 100%)}.theme-toggle-icon{transition:transform .3s ease,opacity .2s ease}.theme-toggle:hover .theme-toggle-icon{transform:rotate(15deg)}.theme-transition{transition:background-color .2s ease,color .2s ease,border-color .2s ease,box-shadow .2s ease}.dark .prose,[data-theme=dark] .prose{--tw-prose-body: var(--text-primary);--tw-prose-headings: var(--text-primary);--tw-prose-lead: var(--text-secondary);--tw-prose-links: var(--accent-primary);--tw-prose-bold: var(--text-primary);--tw-prose-counters: var(--text-tertiary);--tw-prose-bullets: var(--text-tertiary);--tw-prose-hr: var(--border-primary);--tw-prose-quotes: var(--text-secondary);--tw-prose-quote-borders: var(--border-primary);--tw-prose-captions: var(--text-tertiary);--tw-prose-code: var(--text-primary);--tw-prose-pre-code: var(--code-text);--tw-prose-pre-bg: var(--code-bg);--tw-prose-th-borders: var(--border-primary);--tw-prose-td-borders: var(--border-secondary)}@media (prefers-color-scheme: dark){:root:not(.light):not([data-theme=light]) .prose{--tw-prose-body: var(--text-primary);--tw-prose-headings: var(--text-primary);--tw-prose-lead: var(--text-secondary);--tw-prose-links: var(--accent-primary);--tw-prose-bold: var(--text-primary);--tw-prose-counters: var(--text-tertiary);--tw-prose-bullets: var(--text-tertiary);--tw-prose-hr: var(--border-primary);--tw-prose-quotes: var(--text-secondary);--tw-prose-quote-borders: var(--border-primary);--tw-prose-captions: var(--text-tertiary);--tw-prose-code: var(--text-primary);--tw-prose-pre-code: var(--code-text);--tw-prose-pre-bg: var(--code-bg);--tw-prose-th-borders: var(--border-primary);--tw-prose-td-borders: var(--border-secondary)}}.dark pre code.hljs,[data-theme=dark] pre code.hljs{background:var(--code-bg)!important}@media (prefers-color-scheme: dark){:root:not(.light):not([data-theme=light]) pre code.hljs{background:var(--code-bg)!important}}.group button:hover{transform:scale(1.05)}