caraer-cli 0.1.2__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.
- caraer_cli/__init__.py +4 -0
- caraer_cli/api/__init__.py +1 -0
- caraer_cli/api/apps.py +78 -0
- caraer_cli/api/auth.py +53 -0
- caraer_cli/api/client.py +179 -0
- caraer_cli/api/functions.py +72 -0
- caraer_cli/api/installation.py +93 -0
- caraer_cli/api/integration_runtime.py +77 -0
- caraer_cli/api/projects.py +213 -0
- caraer_cli/api/sandboxes.py +29 -0
- caraer_cli/api/webhooks.py +59 -0
- caraer_cli/app_sync.py +697 -0
- caraer_cli/apps_local.py +274 -0
- caraer_cli/commands/__init__.py +1 -0
- caraer_cli/commands/apps.py +2025 -0
- caraer_cli/commands/auth.py +173 -0
- caraer_cli/commands/company.py +68 -0
- caraer_cli/commands/installation.py +182 -0
- caraer_cli/commands/profile.py +239 -0
- caraer_cli/commands/publish.py +40 -0
- caraer_cli/commands/sandbox.py +92 -0
- caraer_cli/commands/skill.py +165 -0
- caraer_cli/commands/webhooks.py +68 -0
- caraer_cli/completion.py +77 -0
- caraer_cli/completion_callbacks.py +162 -0
- caraer_cli/context.py +41 -0
- caraer_cli/errors.py +84 -0
- caraer_cli/formatters/__init__.py +1 -0
- caraer_cli/formatters/output.py +381 -0
- caraer_cli/formatters/timestamps.py +98 -0
- caraer_cli/install_completion.py +38 -0
- caraer_cli/local_app.py +135 -0
- caraer_cli/main.py +104 -0
- caraer_cli/models/__init__.py +1 -0
- caraer_cli/models/api.py +52 -0
- caraer_cli/project/__init__.py +17 -0
- caraer_cli/project/app_bars_sync.py +154 -0
- caraer_cli/project/app_manifest_template.py +68 -0
- caraer_cli/project/inbound_sync.py +192 -0
- caraer_cli/project/json_schemas.py +66 -0
- caraer_cli/project/lifecycle_sync.py +119 -0
- caraer_cli/project/local_dev.py +825 -0
- caraer_cli/project/manifest_edit.py +51 -0
- caraer_cli/project/marketplace_assemble.py +165 -0
- caraer_cli/project/oauth_providers_sync.py +180 -0
- caraer_cli/project/paths.py +102 -0
- caraer_cli/project/pricing_sync.py +81 -0
- caraer_cli/project/push_plan.py +590 -0
- caraer_cli/project/release.py +128 -0
- caraer_cli/project/scaffold.py +537 -0
- caraer_cli/project/schedules_sync.py +170 -0
- caraer_cli/project/schema.py +160 -0
- caraer_cli/project/settings_sync.py +77 -0
- caraer_cli/project/state.py +51 -0
- caraer_cli/project/sync.py +308 -0
- caraer_cli/project/typegen.py +278 -0
- caraer_cli/project/validate_app.py +1215 -0
- caraer_cli/project/webhooks_sync.py +233 -0
- caraer_cli/resolve.py +55 -0
- caraer_cli/schemas/app.caraer.schema.json +175 -0
- caraer_cli/schemas/function.caraer.schema.json +29 -0
- caraer_cli/schemas/inbound.caraer.schema.json +49 -0
- caraer_cli/schemas/lifecycle.caraer.schema.json +50 -0
- caraer_cli/schemas/schedule.caraer.schema.json +39 -0
- caraer_cli/schemas/webhook.caraer.schema.json +50 -0
- caraer_cli/skills/.gitkeep +2 -0
- caraer_cli/skills/caraer-apps/SKILL.md +172 -0
- caraer_cli/skills/caraer-apps/reference.md +163 -0
- caraer_cli/state/__init__.py +1 -0
- caraer_cli/state/config.py +148 -0
- caraer_cli/state/session.py +98 -0
- caraer_cli/utils.py +40 -0
- caraer_cli/wizard/__init__.py +9 -0
- caraer_cli/wizard/catalog.py +170 -0
- caraer_cli/wizard/marketplace.py +350 -0
- caraer_cli/wizard/prompts.py +99 -0
- caraer_cli/wizard/public_app.py +519 -0
- caraer_cli/wizard/scope_macros.py +214 -0
- caraer_cli-0.1.2.dist-info/METADATA +266 -0
- caraer_cli-0.1.2.dist-info/RECORD +83 -0
- caraer_cli-0.1.2.dist-info/WHEEL +4 -0
- caraer_cli-0.1.2.dist-info/entry_points.txt +2 -0
- caraer_cli-0.1.2.dist-info/licenses/LICENSE +8 -0
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import zipfile
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from caraer_cli.api.client import CaraerApiClient
|
|
8
|
+
from caraer_cli.project.schema import ProjectConfig
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def create_or_get_project(client: CaraerApiClient, app_uuid: str, name: str) -> dict[str, Any]:
|
|
12
|
+
return client.request(
|
|
13
|
+
"POST",
|
|
14
|
+
"/api/v2/developer-projects",
|
|
15
|
+
json_body={"appUuid": app_uuid, "name": name, "label": name},
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def get_project(client: CaraerApiClient, project_uuid: str) -> dict[str, Any]:
|
|
20
|
+
return client.request("GET", f"/api/v2/developer-projects/{project_uuid}")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def pack_project_archive(root: Path, config: ProjectConfig) -> Path:
|
|
24
|
+
src = root / config.srcDir
|
|
25
|
+
out = root / ".caraer" / "upload.zip"
|
|
26
|
+
out.parent.mkdir(parents=True, exist_ok=True)
|
|
27
|
+
with zipfile.ZipFile(out, "w", compression=zipfile.ZIP_DEFLATED) as zf:
|
|
28
|
+
workspace_json = root / "caraer.json"
|
|
29
|
+
if workspace_json.is_file():
|
|
30
|
+
zf.write(workspace_json, arcname="caraer.json")
|
|
31
|
+
legacy = root / "caraer.project.json"
|
|
32
|
+
if legacy.is_file() and not workspace_json.is_file():
|
|
33
|
+
zf.write(legacy, arcname="caraer.json")
|
|
34
|
+
if src.is_dir():
|
|
35
|
+
for path in src.rglob("*"):
|
|
36
|
+
if path.is_file():
|
|
37
|
+
zf.write(path, arcname=str(path.relative_to(root)))
|
|
38
|
+
return out
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def create_build(
|
|
42
|
+
client: CaraerApiClient,
|
|
43
|
+
project_uuid: str,
|
|
44
|
+
archive_path: Path,
|
|
45
|
+
*,
|
|
46
|
+
target: str = "production",
|
|
47
|
+
version: str | None = None,
|
|
48
|
+
release_notes: str | None = None,
|
|
49
|
+
) -> dict[str, Any]:
|
|
50
|
+
# Prefer multipart when available; fall back to base64 JSON for simpler clients.
|
|
51
|
+
import base64
|
|
52
|
+
|
|
53
|
+
encoded = base64.b64encode(archive_path.read_bytes()).decode("ascii")
|
|
54
|
+
body: dict[str, Any] = {
|
|
55
|
+
"target": target,
|
|
56
|
+
"archiveBase64": encoded,
|
|
57
|
+
"filename": archive_path.name,
|
|
58
|
+
}
|
|
59
|
+
if version:
|
|
60
|
+
body["version"] = version
|
|
61
|
+
if release_notes is not None:
|
|
62
|
+
body["releaseNotes"] = release_notes
|
|
63
|
+
return client.request(
|
|
64
|
+
"POST",
|
|
65
|
+
f"/api/v2/developer-projects/{project_uuid}/builds",
|
|
66
|
+
json_body=body,
|
|
67
|
+
timeout_seconds=max(client.context.timeout_seconds, 300.0),
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def list_builds(client: CaraerApiClient, project_uuid: str) -> dict[str, Any]:
|
|
72
|
+
return client.request("GET", f"/api/v2/developer-projects/{project_uuid}/builds")
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def get_build(client: CaraerApiClient, project_uuid: str, build_uuid: str) -> dict[str, Any]:
|
|
76
|
+
return client.request(
|
|
77
|
+
"GET", f"/api/v2/developer-projects/{project_uuid}/builds/{build_uuid}"
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def deploy_build(
|
|
82
|
+
client: CaraerApiClient,
|
|
83
|
+
project_uuid: str,
|
|
84
|
+
build_uuid: str,
|
|
85
|
+
*,
|
|
86
|
+
target: str = "production",
|
|
87
|
+
prune: bool = False,
|
|
88
|
+
) -> dict[str, Any]:
|
|
89
|
+
body: dict[str, Any] = {"target": target}
|
|
90
|
+
if prune:
|
|
91
|
+
body["prune"] = True
|
|
92
|
+
return client.request(
|
|
93
|
+
"POST",
|
|
94
|
+
f"/api/v2/developer-projects/{project_uuid}/builds/{build_uuid}/deploy",
|
|
95
|
+
json_body=body,
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def list_deploys(client: CaraerApiClient, project_uuid: str) -> dict[str, Any]:
|
|
100
|
+
return client.request("GET", f"/api/v2/developer-projects/{project_uuid}/deploys")
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def get_function_logs(
|
|
104
|
+
client: CaraerApiClient,
|
|
105
|
+
app_uuid: str,
|
|
106
|
+
function_uuid: str,
|
|
107
|
+
*,
|
|
108
|
+
since: str = "1h",
|
|
109
|
+
limit: int = 100,
|
|
110
|
+
) -> dict[str, Any]:
|
|
111
|
+
return client.request(
|
|
112
|
+
"GET",
|
|
113
|
+
f"/api/v2/apps/{app_uuid}/serverless-functions/{function_uuid}/logs",
|
|
114
|
+
params={"since": since, "limit": limit},
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def get_runtime_logs(
|
|
119
|
+
client: CaraerApiClient,
|
|
120
|
+
app_uuid: str,
|
|
121
|
+
*,
|
|
122
|
+
since: str = "1h",
|
|
123
|
+
limit: int = 100,
|
|
124
|
+
) -> dict[str, Any]:
|
|
125
|
+
"""Fetch app-level V2 container logs (no function UUID required)."""
|
|
126
|
+
return client.request(
|
|
127
|
+
"GET",
|
|
128
|
+
f"/api/v2/apps/{app_uuid}/runtime/logs",
|
|
129
|
+
params={"since": since, "limit": limit},
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def stream_runtime_logs(
|
|
134
|
+
client: CaraerApiClient,
|
|
135
|
+
app_uuid: str,
|
|
136
|
+
*,
|
|
137
|
+
since: str = "1h",
|
|
138
|
+
):
|
|
139
|
+
"""Yield SSE JSON payloads from the runtime log stream endpoint.
|
|
140
|
+
|
|
141
|
+
Falls back by raising ApiError (caller should poll) when unavailable.
|
|
142
|
+
"""
|
|
143
|
+
import json
|
|
144
|
+
|
|
145
|
+
import httpx
|
|
146
|
+
|
|
147
|
+
headers: dict[str, str] = {"Accept": "text/event-stream"}
|
|
148
|
+
if client.context.token:
|
|
149
|
+
headers["Authorization"] = f"Bearer {client.context.token}"
|
|
150
|
+
if client.context.company_uuid:
|
|
151
|
+
headers["X-Caraer-Company-Uuid"] = client.context.company_uuid
|
|
152
|
+
if client.context.sandbox_uuid:
|
|
153
|
+
headers["X-Caraer-Sandbox-Uuid"] = client.context.sandbox_uuid
|
|
154
|
+
|
|
155
|
+
url = (
|
|
156
|
+
client.context.base_url.rstrip("/")
|
|
157
|
+
+ f"/api/v2/apps/{app_uuid}/runtime/logs/stream"
|
|
158
|
+
)
|
|
159
|
+
params = {"since": since}
|
|
160
|
+
with httpx.Client(
|
|
161
|
+
timeout=httpx.Timeout(None, connect=30.0),
|
|
162
|
+
verify=client.context.verify_ssl,
|
|
163
|
+
) as http:
|
|
164
|
+
with http.stream("GET", url, headers=headers, params=params) as response:
|
|
165
|
+
if response.status_code >= 400:
|
|
166
|
+
from caraer_cli.errors import parse_api_error
|
|
167
|
+
|
|
168
|
+
payload = None
|
|
169
|
+
try:
|
|
170
|
+
payload = response.json()
|
|
171
|
+
except Exception: # noqa: BLE001
|
|
172
|
+
payload = None
|
|
173
|
+
raise parse_api_error(response.status_code, payload)
|
|
174
|
+
event_name = "message"
|
|
175
|
+
data_lines: list[str] = []
|
|
176
|
+
for line in response.iter_lines():
|
|
177
|
+
if line is None:
|
|
178
|
+
continue
|
|
179
|
+
if line.startswith(":"):
|
|
180
|
+
continue
|
|
181
|
+
if line.startswith("event:"):
|
|
182
|
+
event_name = line[len("event:") :].strip() or "message"
|
|
183
|
+
continue
|
|
184
|
+
if line.startswith("data:"):
|
|
185
|
+
data_lines.append(line[len("data:") :].lstrip())
|
|
186
|
+
continue
|
|
187
|
+
if line == "" and data_lines:
|
|
188
|
+
raw = "\n".join(data_lines)
|
|
189
|
+
data_lines = []
|
|
190
|
+
try:
|
|
191
|
+
payload = json.loads(raw)
|
|
192
|
+
except json.JSONDecodeError:
|
|
193
|
+
payload = {"message": raw, "event": event_name}
|
|
194
|
+
if isinstance(payload, dict):
|
|
195
|
+
payload.setdefault("event", event_name)
|
|
196
|
+
yield payload
|
|
197
|
+
event_name = "message"
|
|
198
|
+
if isinstance(payload, dict) and payload.get("event") == "done":
|
|
199
|
+
return
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def sample_payload(
|
|
203
|
+
client: CaraerApiClient,
|
|
204
|
+
app_uuid: str,
|
|
205
|
+
*,
|
|
206
|
+
record_uuid: str,
|
|
207
|
+
event_type: str,
|
|
208
|
+
) -> dict[str, Any]:
|
|
209
|
+
return client.request(
|
|
210
|
+
"POST",
|
|
211
|
+
f"/api/v2/apps/{app_uuid}/serverless-functions/sample-payload",
|
|
212
|
+
json_body={"recordUuid": record_uuid, "eventType": event_type},
|
|
213
|
+
)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from caraer_cli.api.client import CaraerApiClient
|
|
6
|
+
|
|
7
|
+
# Always operate against the owner company (never send X-Caraer-Sandbox-Uuid).
|
|
8
|
+
_OWNER_ONLY = {"send_sandbox_header": False}
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def create_sandbox(client: CaraerApiClient, payload: dict[str, Any]) -> dict[str, Any]:
|
|
12
|
+
return client.request(
|
|
13
|
+
"POST",
|
|
14
|
+
"/api/v2/developer-sandboxes",
|
|
15
|
+
json_body=payload,
|
|
16
|
+
**_OWNER_ONLY,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def list_sandboxes(client: CaraerApiClient) -> dict[str, Any]:
|
|
21
|
+
return client.request("GET", "/api/v2/developer-sandboxes", **_OWNER_ONLY)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def get_sandbox(client: CaraerApiClient, sandbox_uuid: str) -> dict[str, Any]:
|
|
25
|
+
return client.request(
|
|
26
|
+
"GET",
|
|
27
|
+
f"/api/v2/developer-sandboxes/{sandbox_uuid}",
|
|
28
|
+
**_OWNER_ONLY,
|
|
29
|
+
)
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from caraer_cli.api.client import CaraerApiClient
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def list_webhooks(client: CaraerApiClient, app_uuid: str, page: int, limit: int) -> dict[str, Any]:
|
|
9
|
+
return client.request(
|
|
10
|
+
"POST",
|
|
11
|
+
f"/api/v2/apps/{app_uuid}/webhooks/index",
|
|
12
|
+
json_body={
|
|
13
|
+
"filters": [],
|
|
14
|
+
"sort": [{"key": "createdAt", "direction": "desc"}],
|
|
15
|
+
"show": [],
|
|
16
|
+
"limit": limit,
|
|
17
|
+
"page": page,
|
|
18
|
+
"query": "",
|
|
19
|
+
},
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def get_formats(client: CaraerApiClient, app_uuid: str) -> dict[str, Any]:
|
|
24
|
+
return client.request("GET", f"/api/v2/apps/{app_uuid}/webhooks/formats")
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def get_events(client: CaraerApiClient, app_uuid: str) -> dict[str, Any]:
|
|
28
|
+
return client.request("GET", f"/api/v2/apps/{app_uuid}/webhooks/events")
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def get_webhook(client: CaraerApiClient, app_uuid: str, webhook_uuid: str) -> dict[str, Any]:
|
|
32
|
+
return client.request("GET", f"/api/v2/apps/{app_uuid}/webhooks/{webhook_uuid}")
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def create_webhook(client: CaraerApiClient, app_uuid: str, payload: dict[str, Any]) -> dict[str, Any]:
|
|
36
|
+
return client.request("POST", f"/api/v2/apps/{app_uuid}/webhooks", json_body=payload)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def update_webhook(
|
|
40
|
+
client: CaraerApiClient, app_uuid: str, webhook_uuid: str, payload: dict[str, Any]
|
|
41
|
+
) -> dict[str, Any]:
|
|
42
|
+
return client.request(
|
|
43
|
+
"PUT",
|
|
44
|
+
f"/api/v2/apps/{app_uuid}/webhooks/{webhook_uuid}",
|
|
45
|
+
json_body=payload,
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def delete_webhook(client: CaraerApiClient, app_uuid: str, webhook_uuid: str) -> dict[str, Any]:
|
|
50
|
+
return client.request("DELETE", f"/api/v2/apps/{app_uuid}/webhooks/{webhook_uuid}")
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def test_webhook(
|
|
54
|
+
client: CaraerApiClient, app_uuid: str, webhook_uuid: str, record_uuid: str, event: str
|
|
55
|
+
) -> dict[str, Any]:
|
|
56
|
+
return client.request(
|
|
57
|
+
"POST",
|
|
58
|
+
f"/api/v2/apps/{app_uuid}/webhooks/test/{webhook_uuid}/{record_uuid}/{event}",
|
|
59
|
+
)
|