cueapi-sdk 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.
- cueapi_sdk-0.1.0/.env.example +6 -0
- cueapi_sdk-0.1.0/.github/ISSUE_TEMPLATE/bug_report.md +21 -0
- cueapi_sdk-0.1.0/.github/ISSUE_TEMPLATE/feature_request.md +11 -0
- cueapi_sdk-0.1.0/.github/workflows/publish.yml +28 -0
- cueapi_sdk-0.1.0/.github/workflows/sdk-integration.yml +72 -0
- cueapi_sdk-0.1.0/.gitignore +13 -0
- cueapi_sdk-0.1.0/CHANGELOG.md +17 -0
- cueapi_sdk-0.1.0/PKG-INFO +297 -0
- cueapi_sdk-0.1.0/README.md +272 -0
- cueapi_sdk-0.1.0/SECURITY.md +18 -0
- cueapi_sdk-0.1.0/cueapi/__init__.py +31 -0
- cueapi_sdk-0.1.0/cueapi/client.py +152 -0
- cueapi_sdk-0.1.0/cueapi/exceptions.py +58 -0
- cueapi_sdk-0.1.0/cueapi/models/__init__.py +10 -0
- cueapi_sdk-0.1.0/cueapi/models/cue.py +59 -0
- cueapi_sdk-0.1.0/cueapi/payload.py +56 -0
- cueapi_sdk-0.1.0/cueapi/resources/__init__.py +3 -0
- cueapi_sdk-0.1.0/cueapi/resources/cues.py +220 -0
- cueapi_sdk-0.1.0/cueapi/resources/executions.py +142 -0
- cueapi_sdk-0.1.0/cueapi/webhook.py +81 -0
- cueapi_sdk-0.1.0/examples/basic_usage.py +52 -0
- cueapi_sdk-0.1.0/examples/webhook_handler.py +37 -0
- cueapi_sdk-0.1.0/examples/worker_setup.py +20 -0
- cueapi_sdk-0.1.0/pyproject.toml +41 -0
- cueapi_sdk-0.1.0/tests/__init__.py +0 -0
- cueapi_sdk-0.1.0/tests/conftest.py +34 -0
- cueapi_sdk-0.1.0/tests/test_cues.py +120 -0
- cueapi_sdk-0.1.0/tests/test_executions_resource.py +166 -0
- cueapi_sdk-0.1.0/tests/test_payload_builder.py +48 -0
- cueapi_sdk-0.1.0/tests/test_webhook.py +120 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Bug report
|
|
3
|
+
about: Something is not working as expected
|
|
4
|
+
labels: bug
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
**What happened?**
|
|
8
|
+
|
|
9
|
+
**What did you expect to happen?**
|
|
10
|
+
|
|
11
|
+
**Steps to reproduce**
|
|
12
|
+
|
|
13
|
+
**Environment**
|
|
14
|
+
- cueapi-sdk version:
|
|
15
|
+
- Python version:
|
|
16
|
+
- OS:
|
|
17
|
+
|
|
18
|
+
**Code sample**
|
|
19
|
+
```python
|
|
20
|
+
# paste relevant code here
|
|
21
|
+
```
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
publish:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
permissions:
|
|
12
|
+
id-token: write
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v4
|
|
15
|
+
|
|
16
|
+
- name: Set up Python
|
|
17
|
+
uses: actions/setup-python@v5
|
|
18
|
+
with:
|
|
19
|
+
python-version: "3.12"
|
|
20
|
+
|
|
21
|
+
- name: Install build tools
|
|
22
|
+
run: pip install hatchling build
|
|
23
|
+
|
|
24
|
+
- name: Build package
|
|
25
|
+
run: python -m build
|
|
26
|
+
|
|
27
|
+
- name: Publish to PyPI
|
|
28
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
name: SDK Integration Tests
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
sdk-integration:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
|
|
13
|
+
services:
|
|
14
|
+
postgres:
|
|
15
|
+
image: postgres:15-alpine
|
|
16
|
+
env:
|
|
17
|
+
POSTGRES_DB: cueapi_test
|
|
18
|
+
POSTGRES_USER: runner
|
|
19
|
+
POSTGRES_PASSWORD: ""
|
|
20
|
+
POSTGRES_HOST_AUTH_METHOD: trust
|
|
21
|
+
ports:
|
|
22
|
+
- 5432:5432
|
|
23
|
+
options: >-
|
|
24
|
+
--health-cmd pg_isready
|
|
25
|
+
--health-interval 2s
|
|
26
|
+
--health-timeout 3s
|
|
27
|
+
--health-retries 10
|
|
28
|
+
|
|
29
|
+
redis:
|
|
30
|
+
image: redis:7-alpine
|
|
31
|
+
ports:
|
|
32
|
+
- 6379:6379
|
|
33
|
+
options: >-
|
|
34
|
+
--health-cmd "redis-cli ping"
|
|
35
|
+
--health-interval 2s
|
|
36
|
+
--health-timeout 3s
|
|
37
|
+
--health-retries 10
|
|
38
|
+
|
|
39
|
+
steps:
|
|
40
|
+
- name: Checkout cueapi-python SDK
|
|
41
|
+
uses: actions/checkout@v4
|
|
42
|
+
|
|
43
|
+
- name: Checkout cueapi-core
|
|
44
|
+
uses: actions/checkout@v4
|
|
45
|
+
with:
|
|
46
|
+
repository: cueapi/cueapi-core
|
|
47
|
+
path: cueapi-core
|
|
48
|
+
token: ${{ secrets.GITHUB_TOKEN }}
|
|
49
|
+
|
|
50
|
+
- name: Set up Python 3.12
|
|
51
|
+
uses: actions/setup-python@v5
|
|
52
|
+
with:
|
|
53
|
+
python-version: "3.12"
|
|
54
|
+
|
|
55
|
+
- name: Install cueapi-core dependencies
|
|
56
|
+
run: pip install -r cueapi-core/requirements.txt
|
|
57
|
+
|
|
58
|
+
- name: Install cueapi-python SDK (editable)
|
|
59
|
+
run: pip install -e .
|
|
60
|
+
|
|
61
|
+
- name: Copy SDK integration test into cueapi-core test dir
|
|
62
|
+
run: cp cueapi-core/tests/test_sdk_integration.py /tmp/test_sdk_integration.py
|
|
63
|
+
|
|
64
|
+
- name: Run SDK integration tests (cross-layer)
|
|
65
|
+
env:
|
|
66
|
+
DATABASE_URL: postgresql+asyncpg://runner@localhost:5432/cueapi_test
|
|
67
|
+
REDIS_URL: redis://localhost:6379
|
|
68
|
+
SESSION_SECRET: test-session-secret-32-chars-minimum!!
|
|
69
|
+
ENV: test
|
|
70
|
+
RESEND_API_KEY: ""
|
|
71
|
+
working-directory: cueapi-core
|
|
72
|
+
run: pytest tests/test_sdk_integration.py -v --tb=short
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to cueapi-sdk will be documented here.
|
|
4
|
+
|
|
5
|
+
## [0.1.0] - 2025-03-28
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- Initial release of the official Python SDK for CueAPI
|
|
9
|
+
- CueAPI client with full cues CRUD (create, list, get, update, delete, pause, resume)
|
|
10
|
+
- Executions client (list, get)
|
|
11
|
+
- Webhook transport support
|
|
12
|
+
- Worker transport support via cueapi-worker
|
|
13
|
+
- Webhook signature verification (verify_webhook) using HMAC-SHA256
|
|
14
|
+
- Typed exceptions: AuthenticationError, CueLimitExceededError, CueNotFoundError, InvalidScheduleError, RateLimitError, CueAPIServerError
|
|
15
|
+
- Python 3.9+ support
|
|
16
|
+
- 40 tests via pytest
|
|
17
|
+
- Working examples: basic_usage.py, webhook_handler.py, worker_setup.py
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cueapi-sdk
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: The official Python SDK for CueAPI — scheduling infrastructure for agents
|
|
5
|
+
Project-URL: Homepage, https://cueapi.ai
|
|
6
|
+
Project-URL: Documentation, https://docs.cueapi.ai
|
|
7
|
+
Project-URL: Repository, https://github.com/cueapi/cueapi-python
|
|
8
|
+
Author-email: CueAPI <support@cueapi.ai>
|
|
9
|
+
License: MIT
|
|
10
|
+
Keywords: ai-agents,cron,cueapi,scheduling,sdk,webhooks
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
21
|
+
Requires-Python: >=3.9
|
|
22
|
+
Requires-Dist: httpx>=0.24.0
|
|
23
|
+
Requires-Dist: pydantic>=2.0.0
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
|
|
26
|
+
> **For AI agents:** This is the official Python SDK for CueAPI. Run `pip install cueapi-sdk`. Your API key is in `CUEAPI_API_KEY`. Create a client: `client = CueAPI(os.environ["CUEAPI_API_KEY"])`. Schedule a task: `client.cues.create(name="task-name", cron="0 9 * * *", callback="https://your-handler.com", payload={})`. For agents without a public URL: set `transport="worker"` and run `pip install cueapi-worker`, then define a handler with `@worker.handler("task-name")` returning `{"status": "success"}` or `{"status": "failure", "error": "reason"}`. List executions: `client.executions.list()`. Get one: `client.executions.get("exec_id")`. Do not retry on `AuthenticationError` or `CueLimitExceededError`. Wait `e.retry_after` seconds on `RateLimitError`.
|
|
27
|
+
|
|
28
|
+
# cueapi-sdk
|
|
29
|
+
|
|
30
|
+
Your agents are failing silently. CueAPI tells you when and why.
|
|
31
|
+
|
|
32
|
+
**Cron has no concept of success. Cue does.**
|
|
33
|
+
|
|
34
|
+
[](https://pypi.org/project/cueapi-sdk/)
|
|
35
|
+
[](https://pypi.org/project/cueapi-sdk/)
|
|
36
|
+
[](LICENSE)
|
|
37
|
+
[](.github/workflows)
|
|
38
|
+
[](https://docs.cueapi.ai)
|
|
39
|
+
|
|
40
|
+
The official Python SDK for [CueAPI](https://cueapi.ai). Open source execution accountability for AI agents.
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Install
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
pip install cueapi-sdk
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## Quickstart
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
from cueapi import CueAPI
|
|
56
|
+
|
|
57
|
+
client = CueAPI("cue_sk_your_key")
|
|
58
|
+
|
|
59
|
+
# Schedule an agent to run every morning
|
|
60
|
+
cue = client.cues.create(
|
|
61
|
+
name="morning-agent-brief",
|
|
62
|
+
cron="0 9 * * *",
|
|
63
|
+
timezone="America/Los_Angeles",
|
|
64
|
+
callback="https://your-agent.com/run",
|
|
65
|
+
payload={"task": "daily_brief"},
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
print(f"Scheduled. Next run: {cue.next_run}")
|
|
69
|
+
# Scheduled. Next run: 2026-03-28T09:00:00-08:00
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## Why CueAPI over cron?
|
|
75
|
+
|
|
76
|
+
Your agent ran at 3am. Did it succeed? Cron does not know.
|
|
77
|
+
|
|
78
|
+
CueAPI tracks every execution separately from delivery, so you always know what happened.
|
|
79
|
+
|
|
80
|
+
```python
|
|
81
|
+
# Check what actually happened
|
|
82
|
+
execution = client.executions.get("exec_01HX...")
|
|
83
|
+
print(execution.outcome) # "success" or "failure" -- reported by your handler
|
|
84
|
+
print(execution.attempts) # 1 (or 2, 3 if it had to retry)
|
|
85
|
+
print(execution.delivered_at) # exactly when it was delivered
|
|
86
|
+
print(execution.status) # "delivered", "failed", "retrying"
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
**What you get that cron cannot give you:**
|
|
90
|
+
|
|
91
|
+
- Execution history with outcome tracking
|
|
92
|
+
- Automatic retries with exponential backoff (1, 5, 15 min)
|
|
93
|
+
- Email + webhook alerts when all retries exhaust
|
|
94
|
+
- Worker transport with no public URL needed
|
|
95
|
+
- Signed webhook payloads
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## Two transport modes
|
|
100
|
+
|
|
101
|
+
### Webhook (default)
|
|
102
|
+
|
|
103
|
+
CueAPI POSTs a signed payload to your URL when a cue fires:
|
|
104
|
+
|
|
105
|
+
```python
|
|
106
|
+
cue = client.cues.create(
|
|
107
|
+
name="data-sync",
|
|
108
|
+
cron="0 */6 * * *",
|
|
109
|
+
callback="https://your-app.com/webhook",
|
|
110
|
+
payload={"pipeline": "sync"},
|
|
111
|
+
)
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Your handler receives the payload, processes it, and CueAPI records the outcome.
|
|
115
|
+
|
|
116
|
+
### Worker (no public URL needed)
|
|
117
|
+
|
|
118
|
+
For agents running locally, in private networks, or behind firewalls. Your daemon polls CueAPI instead of waiting for inbound requests.
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
pip install cueapi-worker
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
```python
|
|
125
|
+
from cueapi_worker import Worker
|
|
126
|
+
|
|
127
|
+
worker = Worker(api_key="cue_sk_your_key")
|
|
128
|
+
|
|
129
|
+
@worker.handler("run-agent")
|
|
130
|
+
def handle(payload):
|
|
131
|
+
result = run_my_agent(payload["task"])
|
|
132
|
+
return {"status": "success", "summary": result}
|
|
133
|
+
|
|
134
|
+
worker.start() # polls continuously, no inbound firewall rules needed
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Create the cue with `transport="worker"`:
|
|
138
|
+
|
|
139
|
+
```python
|
|
140
|
+
cue = client.cues.create(
|
|
141
|
+
name="nightly-pipeline",
|
|
142
|
+
cron="0 2 * * *",
|
|
143
|
+
transport="worker",
|
|
144
|
+
payload={"pipeline": "etl"},
|
|
145
|
+
)
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## Webhook verification
|
|
151
|
+
|
|
152
|
+
Always verify incoming webhook signatures before processing:
|
|
153
|
+
|
|
154
|
+
```python
|
|
155
|
+
from cueapi import verify_webhook
|
|
156
|
+
|
|
157
|
+
@app.post("/webhook")
|
|
158
|
+
def handle_cue(request: Request):
|
|
159
|
+
is_valid = verify_webhook(
|
|
160
|
+
payload=request.body,
|
|
161
|
+
signature=request.headers["X-CueAPI-Signature"],
|
|
162
|
+
timestamp=request.headers["X-CueAPI-Timestamp"],
|
|
163
|
+
secret="whsec_your_secret",
|
|
164
|
+
tolerance=300, # seconds, default
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
if not is_valid:
|
|
168
|
+
return {"error": "invalid signature"}, 401
|
|
169
|
+
|
|
170
|
+
data = request.json()
|
|
171
|
+
run_task(data["payload"])
|
|
172
|
+
return {"outcome": "success"}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Signatures use HMAC-SHA256 in `v1={hex}` format. The `tolerance` parameter rejects replayed requests older than 5 minutes.
|
|
176
|
+
|
|
177
|
+
---
|
|
178
|
+
|
|
179
|
+
## Error handling
|
|
180
|
+
|
|
181
|
+
```python
|
|
182
|
+
from cueapi import (
|
|
183
|
+
CueAPI,
|
|
184
|
+
AuthenticationError,
|
|
185
|
+
CueLimitExceededError,
|
|
186
|
+
CueNotFoundError,
|
|
187
|
+
InvalidScheduleError,
|
|
188
|
+
RateLimitError,
|
|
189
|
+
CueAPIServerError,
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
try:
|
|
193
|
+
cue = client.cues.create(
|
|
194
|
+
name="my-agent",
|
|
195
|
+
cron="0 9 * * *",
|
|
196
|
+
callback="https://example.com/run",
|
|
197
|
+
)
|
|
198
|
+
except CueLimitExceededError:
|
|
199
|
+
print("Plan limit reached. Upgrade at cueapi.ai")
|
|
200
|
+
except InvalidScheduleError as e:
|
|
201
|
+
print(f"Bad cron expression: {e}")
|
|
202
|
+
except AuthenticationError:
|
|
203
|
+
print("Invalid API key")
|
|
204
|
+
except RateLimitError as e:
|
|
205
|
+
print(f"Rate limited. Retry after {e.retry_after}s")
|
|
206
|
+
except CueAPIServerError:
|
|
207
|
+
print("Server error. CueAPI status at status.cueapi.ai")
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
| Exception | HTTP | When |
|
|
211
|
+
|---|---|---|
|
|
212
|
+
| `AuthenticationError` | 401 | Invalid or missing API key |
|
|
213
|
+
| `CueLimitExceededError` | 403 | Plan cue limit reached |
|
|
214
|
+
| `CueNotFoundError` | 404 | Cue ID does not exist |
|
|
215
|
+
| `InvalidScheduleError` | 400/422 | Bad cron expression or request body |
|
|
216
|
+
| `RateLimitError` | 429 | Too many requests |
|
|
217
|
+
| `CueAPIServerError` | 5xx | Server error |
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
## Full method reference
|
|
222
|
+
|
|
223
|
+
### `CueAPI(api_key, *, base_url, timeout)`
|
|
224
|
+
|
|
225
|
+
```python
|
|
226
|
+
client = CueAPI(
|
|
227
|
+
api_key="cue_sk_...", # or set CUEAPI_API_KEY env var
|
|
228
|
+
base_url="https://api.cueapi.ai", # default
|
|
229
|
+
timeout=30, # seconds, default
|
|
230
|
+
)
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### `client.cues.create(**fields)`
|
|
234
|
+
|
|
235
|
+
| Parameter | Type | Description |
|
|
236
|
+
|---|---|---|
|
|
237
|
+
| `name` | `str` | Required. Unique name. |
|
|
238
|
+
| `cron` | `str` | Cron expression for recurring schedules. |
|
|
239
|
+
| `at` | `str or datetime` | ISO 8601 for one-time schedules. |
|
|
240
|
+
| `timezone` | `str` | IANA timezone (default `"UTC"`). |
|
|
241
|
+
| `callback` | `str` | Webhook URL (required if `transport="webhook"`). |
|
|
242
|
+
| `transport` | `str` | `"webhook"` (default) or `"worker"`. |
|
|
243
|
+
| `payload` | `dict` | JSON payload included in each execution. |
|
|
244
|
+
| `description` | `str` | Optional description. |
|
|
245
|
+
| `retry` | `dict` | `{"max_attempts": 3, "backoff_minutes": [1, 5, 15]}` |
|
|
246
|
+
| `on_failure` | `dict` | `{"email": true, "webhook": null, "pause": false}` |
|
|
247
|
+
|
|
248
|
+
Returns a `Cue` object.
|
|
249
|
+
|
|
250
|
+
### Other cue methods
|
|
251
|
+
|
|
252
|
+
```python
|
|
253
|
+
client.cues.list(limit=20, offset=0, status="active") # CueList
|
|
254
|
+
client.cues.get("cue_abc123") # Cue
|
|
255
|
+
client.cues.update("cue_abc123", cron="0 10 * * *") # Cue
|
|
256
|
+
client.cues.pause("cue_abc123") # Cue
|
|
257
|
+
client.cues.resume("cue_abc123") # Cue
|
|
258
|
+
client.cues.delete("cue_abc123") # None
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### Executions
|
|
262
|
+
|
|
263
|
+
```python
|
|
264
|
+
client.executions.list(cue_id="cue_abc123", limit=20) # ExecutionList
|
|
265
|
+
client.executions.get("exec_01HX...") # Execution
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
---
|
|
269
|
+
|
|
270
|
+
## Examples
|
|
271
|
+
|
|
272
|
+
See [`/examples`](examples/) for working code:
|
|
273
|
+
|
|
274
|
+
- [`basic_usage.py`](examples/basic_usage.py) - create, list, pause, delete cues
|
|
275
|
+
- [`webhook_handler.py`](examples/webhook_handler.py) - FastAPI handler with signature verification
|
|
276
|
+
- [`worker_setup.py`](examples/worker_setup.py) - worker daemon for private network agents
|
|
277
|
+
|
|
278
|
+
---
|
|
279
|
+
|
|
280
|
+
## Links
|
|
281
|
+
|
|
282
|
+
- [Dashboard](https://dashboard.cueapi.ai) - manage cues and view executions
|
|
283
|
+
- [Documentation](https://docs.cueapi.ai) - full guides and API reference
|
|
284
|
+
- [API Reference](https://docs.cueapi.ai/api-reference/overview) - all endpoints
|
|
285
|
+
- [cueapi-core](https://github.com/cueapi/cueapi-core) - open source server
|
|
286
|
+
- [cueapi.ai](https://cueapi.ai) - hosted service, free tier available
|
|
287
|
+
- [Changelog](CHANGELOG.md) - full version history
|
|
288
|
+
|
|
289
|
+
---
|
|
290
|
+
|
|
291
|
+
## License
|
|
292
|
+
|
|
293
|
+
MIT. See [LICENSE](LICENSE).
|
|
294
|
+
|
|
295
|
+
---
|
|
296
|
+
|
|
297
|
+
*Built by [Vector Apps](https://cueapi.ai/about)*
|