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.
Files changed (83) hide show
  1. caraer_cli/__init__.py +4 -0
  2. caraer_cli/api/__init__.py +1 -0
  3. caraer_cli/api/apps.py +78 -0
  4. caraer_cli/api/auth.py +53 -0
  5. caraer_cli/api/client.py +179 -0
  6. caraer_cli/api/functions.py +72 -0
  7. caraer_cli/api/installation.py +93 -0
  8. caraer_cli/api/integration_runtime.py +77 -0
  9. caraer_cli/api/projects.py +213 -0
  10. caraer_cli/api/sandboxes.py +29 -0
  11. caraer_cli/api/webhooks.py +59 -0
  12. caraer_cli/app_sync.py +697 -0
  13. caraer_cli/apps_local.py +274 -0
  14. caraer_cli/commands/__init__.py +1 -0
  15. caraer_cli/commands/apps.py +2025 -0
  16. caraer_cli/commands/auth.py +173 -0
  17. caraer_cli/commands/company.py +68 -0
  18. caraer_cli/commands/installation.py +182 -0
  19. caraer_cli/commands/profile.py +239 -0
  20. caraer_cli/commands/publish.py +40 -0
  21. caraer_cli/commands/sandbox.py +92 -0
  22. caraer_cli/commands/skill.py +165 -0
  23. caraer_cli/commands/webhooks.py +68 -0
  24. caraer_cli/completion.py +77 -0
  25. caraer_cli/completion_callbacks.py +162 -0
  26. caraer_cli/context.py +41 -0
  27. caraer_cli/errors.py +84 -0
  28. caraer_cli/formatters/__init__.py +1 -0
  29. caraer_cli/formatters/output.py +381 -0
  30. caraer_cli/formatters/timestamps.py +98 -0
  31. caraer_cli/install_completion.py +38 -0
  32. caraer_cli/local_app.py +135 -0
  33. caraer_cli/main.py +104 -0
  34. caraer_cli/models/__init__.py +1 -0
  35. caraer_cli/models/api.py +52 -0
  36. caraer_cli/project/__init__.py +17 -0
  37. caraer_cli/project/app_bars_sync.py +154 -0
  38. caraer_cli/project/app_manifest_template.py +68 -0
  39. caraer_cli/project/inbound_sync.py +192 -0
  40. caraer_cli/project/json_schemas.py +66 -0
  41. caraer_cli/project/lifecycle_sync.py +119 -0
  42. caraer_cli/project/local_dev.py +825 -0
  43. caraer_cli/project/manifest_edit.py +51 -0
  44. caraer_cli/project/marketplace_assemble.py +165 -0
  45. caraer_cli/project/oauth_providers_sync.py +180 -0
  46. caraer_cli/project/paths.py +102 -0
  47. caraer_cli/project/pricing_sync.py +81 -0
  48. caraer_cli/project/push_plan.py +590 -0
  49. caraer_cli/project/release.py +128 -0
  50. caraer_cli/project/scaffold.py +537 -0
  51. caraer_cli/project/schedules_sync.py +170 -0
  52. caraer_cli/project/schema.py +160 -0
  53. caraer_cli/project/settings_sync.py +77 -0
  54. caraer_cli/project/state.py +51 -0
  55. caraer_cli/project/sync.py +308 -0
  56. caraer_cli/project/typegen.py +278 -0
  57. caraer_cli/project/validate_app.py +1215 -0
  58. caraer_cli/project/webhooks_sync.py +233 -0
  59. caraer_cli/resolve.py +55 -0
  60. caraer_cli/schemas/app.caraer.schema.json +175 -0
  61. caraer_cli/schemas/function.caraer.schema.json +29 -0
  62. caraer_cli/schemas/inbound.caraer.schema.json +49 -0
  63. caraer_cli/schemas/lifecycle.caraer.schema.json +50 -0
  64. caraer_cli/schemas/schedule.caraer.schema.json +39 -0
  65. caraer_cli/schemas/webhook.caraer.schema.json +50 -0
  66. caraer_cli/skills/.gitkeep +2 -0
  67. caraer_cli/skills/caraer-apps/SKILL.md +172 -0
  68. caraer_cli/skills/caraer-apps/reference.md +163 -0
  69. caraer_cli/state/__init__.py +1 -0
  70. caraer_cli/state/config.py +148 -0
  71. caraer_cli/state/session.py +98 -0
  72. caraer_cli/utils.py +40 -0
  73. caraer_cli/wizard/__init__.py +9 -0
  74. caraer_cli/wizard/catalog.py +170 -0
  75. caraer_cli/wizard/marketplace.py +350 -0
  76. caraer_cli/wizard/prompts.py +99 -0
  77. caraer_cli/wizard/public_app.py +519 -0
  78. caraer_cli/wizard/scope_macros.py +214 -0
  79. caraer_cli-0.1.2.dist-info/METADATA +266 -0
  80. caraer_cli-0.1.2.dist-info/RECORD +83 -0
  81. caraer_cli-0.1.2.dist-info/WHEEL +4 -0
  82. caraer_cli-0.1.2.dist-info/entry_points.txt +2 -0
  83. 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
+ )