codai-sdk 0.69.0__tar.gz → 0.71.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.
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/CHANGELOG.md +12 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/PKG-INFO +1 -1
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/pyproject.toml +1 -1
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/src/codai/__init__.py +5 -1
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/src/codai/_operations.py +1 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/src/codai/_types.py +59 -8
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/src/codai/resources/__init__.py +3 -1
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/src/codai/resources/productions.py +17 -6
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/src/codai/resources/vault.py +24 -2
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/tests/test_resources.py +1 -1
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/tests/test_vault.py +15 -3
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/.github/dependabot.yml +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/.github/pull_request_template.md +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/.github/workflows/publish.yml +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/.gitignore +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/CODE_OF_CONDUCT.md +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/CONTRIBUTING.md +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/LICENSE +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/README.md +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/SECURITY.md +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/examples/agent.py +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/examples/chat.py +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/examples/feedback.py +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/examples/streaming.py +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/scripts/gen-types.py +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/scripts/smoke_local.py +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/src/codai/_http.py +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/src/codai/_resolve_types.py +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/src/codai/resources/_base.py +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/src/codai/resources/abuse_reports.py +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/src/codai/resources/agents.py +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/src/codai/resources/batches.py +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/src/codai/resources/billing.py +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/src/codai/resources/brand_kits.py +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/src/codai/resources/cards.py +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/src/codai/resources/chat.py +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/src/codai/resources/core.py +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/src/codai/resources/creatives.py +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/src/codai/resources/environments.py +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/src/codai/resources/exports.py +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/src/codai/resources/lists.py +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/src/codai/resources/lyrics.py +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/src/codai/resources/media_ops.py +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/src/codai/resources/memory.py +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/src/codai/resources/messages.py +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/src/codai/resources/music_room.py +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/src/codai/resources/platform.py +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/src/codai/resources/prompts.py +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/src/codai/resources/provenance.py +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/src/codai/resources/referrals.py +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/src/codai/resources/research.py +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/src/codai/resources/resolve.py +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/src/codai/resources/responses.py +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/src/codai/resources/sandboxes.py +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/src/codai/resources/schedules.py +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/src/codai/resources/sessions.py +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/src/codai/resources/shares.py +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/src/codai/resources/templates.py +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/src/codai/resources/transcripts.py +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/src/codai/resources/triggers.py +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/src/codai/resources/usage_keys.py +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/src/codai/resources/voice_revocations.py +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/src/codai/resources/voices.py +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/tests/conftest.py +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/tests/test_abuse_reports.py +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/tests/test_batches.py +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/tests/test_billing_referrals.py +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/tests/test_brand_kits.py +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/tests/test_cards.py +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/tests/test_creatives.py +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/tests/test_creatives_cx07.py +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/tests/test_creatives_documents.py +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/tests/test_creatives_e11.py +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/tests/test_creatives_e11b.py +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/tests/test_environments_exec.py +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/tests/test_exports.py +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/tests/test_http.py +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/tests/test_lists.py +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/tests/test_lyrics.py +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/tests/test_media_ops.py +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/tests/test_memory.py +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/tests/test_music_room.py +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/tests/test_os_gate.py +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/tests/test_parity.py +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/tests/test_productions.py +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/tests/test_prompts.py +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/tests/test_provenance.py +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/tests/test_research.py +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/tests/test_resolve.py +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/tests/test_sandboxes.py +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/tests/test_settings.py +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/tests/test_shares_e12a.py +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/tests/test_templates.py +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/tests/test_transcripts.py +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/tests/test_voice_revocations.py +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/tests/test_voices.py +0 -0
- {codai_sdk-0.69.0 → codai_sdk-0.71.0}/uv.lock +0 -0
|
@@ -6,6 +6,18 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.71.0] - 2026-10-24
|
|
10
|
+
|
|
11
|
+
- `productions.create(kind=..., duration_s=...)`: 8 production kinds (video, image, deck, doc, sheet, audio,
|
|
12
|
+
carousel, print) and a 15-60 s target length for video; non-video responses carry `kind`, `output_asset_id` and
|
|
13
|
+
`outputs`.
|
|
14
|
+
|
|
15
|
+
## [0.70.0] - 2026-10-24
|
|
16
|
+
|
|
17
|
+
- `vault.resolve_challenge()` (`POST /v1/vault/resolve/challenge`) and a required `proof` on `vault.resolve()`: local
|
|
18
|
+
brokers prove an enrolled device key (`codai-vault/resolve/v1|<challenge>|<device_id>|<digest>`); helpers
|
|
19
|
+
`resolve_digest` / `resolve_message`. Device keys gain kind `cli_ed25519`, surface `cli`, attestation `software`.
|
|
20
|
+
|
|
9
21
|
## [0.69.0] - 2026-10-23
|
|
10
22
|
|
|
11
23
|
- `MusicRoom.listening`: what the Music Room critic heard (listening features, Audiobox Aesthetics, Gemini listener
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: codai-sdk
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.71.0
|
|
4
4
|
Summary: Official Python SDK for the codai AI gateway — chat, messages, responses, streaming, agents, sessions, hosts, tasks, account.
|
|
5
5
|
Project-URL: Homepage, https://codai.ro
|
|
6
6
|
Project-URL: Repository, https://github.com/codai-ro/codai-sdk-python
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "codai-sdk"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.71.0"
|
|
8
8
|
description = "Official Python SDK for the codai AI gateway — chat, messages, responses, streaming, agents, sessions, hosts, tasks, account."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
|
|
@@ -65,6 +65,8 @@ from .resources import (
|
|
|
65
65
|
VoiceRevocations,
|
|
66
66
|
Voices,
|
|
67
67
|
Vault,
|
|
68
|
+
vault_resolve_digest,
|
|
69
|
+
vault_resolve_message,
|
|
68
70
|
vault_unlock_message,
|
|
69
71
|
AbuseReports,
|
|
70
72
|
Provenance,
|
|
@@ -166,9 +168,11 @@ __all__ = [
|
|
|
166
168
|
"verify_resolve_webhook",
|
|
167
169
|
"settings_conflict_current",
|
|
168
170
|
"share_token_from",
|
|
171
|
+
"vault_resolve_digest",
|
|
172
|
+
"vault_resolve_message",
|
|
169
173
|
"vault_unlock_message",
|
|
170
174
|
]
|
|
171
|
-
__version__ = "0.
|
|
175
|
+
__version__ = "0.71.0"
|
|
172
176
|
class _CallableGroup:
|
|
173
177
|
"""A resource group that is also callable — keeps ``client.chat(...)``,
|
|
174
178
|
``client.embeddings(...)``, ``client.models()`` and ``client.feedback(...)``
|
|
@@ -139,6 +139,7 @@ OPERATION_METHODS: Dict[str, str] = {
|
|
|
139
139
|
"vaultUse": "vault.use",
|
|
140
140
|
"vaultHttp": "vault.http_call",
|
|
141
141
|
"vaultResolve": "vault.resolve",
|
|
142
|
+
"createVaultResolveChallenge": "vault.resolve_challenge",
|
|
142
143
|
"createStudioAbuseReport": "abuse_reports.create",
|
|
143
144
|
"checkProvenance": "provenance.check",
|
|
144
145
|
"createStudioBatch": "batches.create",
|
|
@@ -2518,14 +2518,20 @@ class MediaOpJob(TypedDict):
|
|
|
2518
2518
|
updated_at: str
|
|
2519
2519
|
|
|
2520
2520
|
|
|
2521
|
+
# What the production builds: `video` (MP4), `image` (PNG), `deck` (PPTX), `doc` (DOCX), `sheet` (XLSX), `audio`…
|
|
2522
|
+
StudioProductionKind = Literal['video', 'image', 'deck', 'doc', 'sheet', 'audio', 'carousel', 'print']
|
|
2523
|
+
|
|
2524
|
+
|
|
2521
2525
|
class _StudioProductionCreateRequired(TypedDict):
|
|
2522
2526
|
brief: str
|
|
2523
2527
|
|
|
2524
2528
|
|
|
2525
2529
|
class StudioProductionCreate(_StudioProductionCreateRequired, total=False):
|
|
2526
2530
|
"""Studio production request"""
|
|
2531
|
+
kind: StudioProductionKind
|
|
2527
2532
|
language: Literal['en', 'ro']
|
|
2528
2533
|
aspect_ratio: Literal['16:9', '9:16', '1:1']
|
|
2534
|
+
duration_s: int
|
|
2529
2535
|
starter: str
|
|
2530
2536
|
voice: str
|
|
2531
2537
|
narration: bool
|
|
@@ -2539,8 +2545,24 @@ class StudioProductionQa(TypedDict, total=False):
|
|
|
2539
2545
|
skipped: str
|
|
2540
2546
|
|
|
2541
2547
|
|
|
2542
|
-
class
|
|
2543
|
-
"""Studio
|
|
2548
|
+
class StudioProductionOutput(TypedDict):
|
|
2549
|
+
"""A built output of a non-video production: the regular Studio asset plus its `role`. `url` is a 1 h signed GET,…"""
|
|
2550
|
+
asset_id: str
|
|
2551
|
+
kind: AssetKind
|
|
2552
|
+
status: Literal['pending', 'ready', 'failed', 'deleted']
|
|
2553
|
+
model: str
|
|
2554
|
+
url: Optional[str]
|
|
2555
|
+
mime_type: str
|
|
2556
|
+
bytes: int
|
|
2557
|
+
duration_seconds: Optional[float]
|
|
2558
|
+
width: Optional[int]
|
|
2559
|
+
height: Optional[int]
|
|
2560
|
+
created_at: str
|
|
2561
|
+
expires_at: Optional[str]
|
|
2562
|
+
role: Literal['image', 'cover', 'document', 'pages', 'pdf', 'audio']
|
|
2563
|
+
|
|
2564
|
+
|
|
2565
|
+
class _StudioProductionRequired(TypedDict):
|
|
2544
2566
|
id: str
|
|
2545
2567
|
object: Literal['studio.production']
|
|
2546
2568
|
status: Literal['running', 'succeeded', 'failed']
|
|
@@ -2562,6 +2584,14 @@ class StudioProduction(TypedDict):
|
|
|
2562
2584
|
created_at: str
|
|
2563
2585
|
|
|
2564
2586
|
|
|
2587
|
+
class StudioProduction(_StudioProductionRequired, total=False):
|
|
2588
|
+
"""Studio production"""
|
|
2589
|
+
duration_s: Optional[int]
|
|
2590
|
+
kind: StudioProductionKind
|
|
2591
|
+
output_asset_id: Optional[str]
|
|
2592
|
+
outputs: List[StudioProductionOutput]
|
|
2593
|
+
|
|
2594
|
+
|
|
2565
2595
|
class _MusicRoomCreateRequired(TypedDict):
|
|
2566
2596
|
brief: str
|
|
2567
2597
|
|
|
@@ -5745,7 +5775,7 @@ class AccountPatchResponse(TypedDict):
|
|
|
5745
5775
|
training_opt_out: bool
|
|
5746
5776
|
consents: List[AccountConsent]
|
|
5747
5777
|
username_error: Optional[Literal['invalid', 'taken', 'change_limit']]
|
|
5748
|
-
referral_result: Optional[Literal['window_closed', 'none', 'invalid_code', 'self', 'already_referred', 'flagged', 'granted', 'cap_reached']]
|
|
5778
|
+
referral_result: Optional[Literal['window_closed', 'none', 'invalid_code', 'self', 'already_referred', 'flagged', 'pending_payment', 'granted', 'cap_reached']]
|
|
5749
5779
|
|
|
5750
5780
|
|
|
5751
5781
|
class BillingSubscription(TypedDict):
|
|
@@ -7090,11 +7120,11 @@ class VaultUseDecision(_VaultUseDecisionRequired, total=False):
|
|
|
7090
7120
|
|
|
7091
7121
|
class VaultDeviceKey(TypedDict):
|
|
7092
7122
|
id: str
|
|
7093
|
-
kind: Literal['android_keystore', 'windows_hello']
|
|
7123
|
+
kind: Literal['android_keystore', 'windows_hello', 'cli_ed25519']
|
|
7094
7124
|
name: str
|
|
7095
|
-
surface: Literal['phone', 'wear', 'desktop']
|
|
7125
|
+
surface: Literal['phone', 'wear', 'desktop', 'cli']
|
|
7096
7126
|
public_key: str
|
|
7097
|
-
attestation: Literal['android-strongbox', 'android-tee', 'tpm', 'hello-unattested']
|
|
7127
|
+
attestation: Literal['android-strongbox', 'android-tee', 'tpm', 'hello-unattested', 'software']
|
|
7098
7128
|
attestation_info: Dict[str, Any]
|
|
7099
7129
|
created_at: str
|
|
7100
7130
|
last_used_at: Optional[str]
|
|
@@ -7106,6 +7136,17 @@ class VaultDeviceChallenge(TypedDict):
|
|
|
7106
7136
|
expires_at: str
|
|
7107
7137
|
|
|
7108
7138
|
|
|
7139
|
+
class VaultResolveChallenge(TypedDict):
|
|
7140
|
+
challenge: str
|
|
7141
|
+
expires_at: str
|
|
7142
|
+
|
|
7143
|
+
|
|
7144
|
+
class VaultResolveProof(TypedDict):
|
|
7145
|
+
device_id: str
|
|
7146
|
+
challenge: str
|
|
7147
|
+
signature: str
|
|
7148
|
+
|
|
7149
|
+
|
|
7109
7150
|
class VaultDeviceEnrollAttestationOption1(TypedDict):
|
|
7110
7151
|
android_chain: List[str]
|
|
7111
7152
|
|
|
@@ -7121,9 +7162,9 @@ class VaultDeviceEnrollAttestationOption2(TypedDict):
|
|
|
7121
7162
|
|
|
7122
7163
|
class _VaultDeviceEnrollRequired(TypedDict):
|
|
7123
7164
|
device_id: str
|
|
7124
|
-
kind: Literal['android_keystore', 'windows_hello']
|
|
7165
|
+
kind: Literal['android_keystore', 'windows_hello', 'cli_ed25519']
|
|
7125
7166
|
name: str
|
|
7126
|
-
surface: Literal['phone', 'wear', 'desktop']
|
|
7167
|
+
surface: Literal['phone', 'wear', 'desktop', 'cli']
|
|
7127
7168
|
public_key: str
|
|
7128
7169
|
nonce: str
|
|
7129
7170
|
vault_signature: str
|
|
@@ -9708,9 +9749,14 @@ class VaultHttpBody(_VaultHttpBodyRequired, total=False):
|
|
|
9708
9749
|
VaultHttpResponse = Dict[str, Any]
|
|
9709
9750
|
|
|
9710
9751
|
|
|
9752
|
+
# POST /v1/vault/resolve/challenge — 200 response
|
|
9753
|
+
CreateVaultResolveChallengeResponse = VaultResolveChallenge
|
|
9754
|
+
|
|
9755
|
+
|
|
9711
9756
|
class _VaultResolveBodyRequired(TypedDict):
|
|
9712
9757
|
handles: List[str]
|
|
9713
9758
|
action: str
|
|
9759
|
+
proof: VaultResolveProof
|
|
9714
9760
|
|
|
9715
9761
|
|
|
9716
9762
|
class VaultResolveBody(_VaultResolveBodyRequired, total=False):
|
|
@@ -10006,6 +10052,7 @@ __all__ = [
|
|
|
10006
10052
|
'CreateTriggerResponse',
|
|
10007
10053
|
'CreateVaultBody',
|
|
10008
10054
|
'CreateVaultDeviceChallengeResponse',
|
|
10055
|
+
'CreateVaultResolveChallengeResponse',
|
|
10009
10056
|
'CreateVaultResponse',
|
|
10010
10057
|
'CreateVaultRuleBody',
|
|
10011
10058
|
'CreateVaultRuleResponse',
|
|
@@ -10815,6 +10862,8 @@ __all__ = [
|
|
|
10815
10862
|
'StudioPostCopyRequest',
|
|
10816
10863
|
'StudioProduction',
|
|
10817
10864
|
'StudioProductionCreate',
|
|
10865
|
+
'StudioProductionKind',
|
|
10866
|
+
'StudioProductionOutput',
|
|
10818
10867
|
'StudioProductionQa',
|
|
10819
10868
|
'StudioPublicDecisionRequest',
|
|
10820
10869
|
'StudioPublicNameRequest',
|
|
@@ -11018,6 +11067,8 @@ __all__ = [
|
|
|
11018
11067
|
'VaultItemLimits',
|
|
11019
11068
|
'VaultLockResult',
|
|
11020
11069
|
'VaultResolveBody',
|
|
11070
|
+
'VaultResolveChallenge',
|
|
11071
|
+
'VaultResolveProof',
|
|
11021
11072
|
'VaultResolveResponse',
|
|
11022
11073
|
'VaultUnlockChallenge',
|
|
11023
11074
|
'VaultUseBody',
|
|
@@ -13,7 +13,7 @@ from .media_ops import MEDIA_JOB_TERMINAL_STATUSES, MediaOps, is_media_op_job
|
|
|
13
13
|
from .lyrics import Lyrics
|
|
14
14
|
from .exports import Exports
|
|
15
15
|
from .research import RESEARCH_TERMINAL_STATUSES, Research
|
|
16
|
-
from .vault import Vault, vault_unlock_message
|
|
16
|
+
from .vault import Vault, vault_resolve_digest, vault_resolve_message, vault_unlock_message
|
|
17
17
|
from .batches import Batches
|
|
18
18
|
from .shares import Shares, share_token_from
|
|
19
19
|
from .voice_revocations import VoiceRevocations
|
|
@@ -141,6 +141,8 @@ __all__ = [
|
|
|
141
141
|
"VoiceRevocations",
|
|
142
142
|
"Voices",
|
|
143
143
|
"Vault",
|
|
144
|
+
"vault_resolve_digest",
|
|
145
|
+
"vault_resolve_message",
|
|
144
146
|
"vault_unlock_message",
|
|
145
147
|
"AbuseReports",
|
|
146
148
|
"Provenance",
|
|
@@ -1,10 +1,16 @@
|
|
|
1
|
-
"""``/v1/studio/productions`` — codai Studio brief → finished
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
"""``/v1/studio/productions`` — codai Studio brief → finished output (RC-20), one of 8 kinds (``kind``, default
|
|
2
|
+
``video``): ``video`` (MP4 render), ``image`` (PNG), ``deck`` (PPTX), ``doc`` (DOCX), ``sheet`` (XLSX), ``carousel``
|
|
3
|
+
(ZIP of PNG slides + cover PNG), ``print`` (A4/A5 PDF) and ``audio`` (MP3 jingle or song).
|
|
4
4
|
|
|
5
|
-
``
|
|
5
|
+
``video``: one model turn storyboards the brief on a built-in video starter, every scene gets an image and a voice-over
|
|
6
|
+
line, the result is saved as an editable creative and a draft render is queued; a vision review may fix text once.
|
|
7
|
+
Other kinds: one plan turn, generated images or music, an editable creative and the output built by the Studio
|
|
8
|
+
builders; the response adds ``kind``, ``output_asset_id`` and ``outputs`` (Studio assets with a ``role``).
|
|
9
|
+
``starter``, ``voice``, ``narration`` and ``aspect_ratio`` apply to ``video`` only.
|
|
10
|
+
|
|
11
|
+
``create`` returns 202 ``{id, status: "running", phase}`` after the output is queued (about a minute), ``retrieve``
|
|
6
12
|
advances and reads it, ``wait`` polls until ``succeeded`` / ``failed`` and ``run`` does ``create`` + ``wait``. The
|
|
7
|
-
|
|
13
|
+
plan and review turns are token-metered; images, music, voice-over and renders are billed at their usual prices;
|
|
8
14
|
``budget_micro_usd`` (default 3,000,000 = $3) caps the whole production.
|
|
9
15
|
"""
|
|
10
16
|
|
|
@@ -45,12 +51,17 @@ class Productions(Resource):
|
|
|
45
51
|
title: Optional[str] = None,
|
|
46
52
|
idempotency_key: Optional[str] = None,
|
|
47
53
|
ext: Optional[RequestExtensions] = None,
|
|
54
|
+
kind: Optional[str] = None,
|
|
55
|
+
duration_s: Optional[int] = None,
|
|
48
56
|
) -> Dict[str, Any]:
|
|
49
|
-
"""``POST /v1/studio/productions`` —
|
|
57
|
+
"""``POST /v1/studio/productions`` — plan + images / music + creative + the output being built
|
|
58
|
+
(``kind``: video | image | deck | doc | sheet | audio | carousel | print, default video)."""
|
|
50
59
|
body: Dict[str, Any] = {"brief": brief}
|
|
51
60
|
for k, v in (
|
|
61
|
+
("kind", kind),
|
|
52
62
|
("language", language),
|
|
53
63
|
("aspect_ratio", aspect_ratio),
|
|
64
|
+
("duration_s", duration_s),
|
|
54
65
|
("starter", starter),
|
|
55
66
|
("voice", voice),
|
|
56
67
|
("narration", narration),
|
|
@@ -5,12 +5,14 @@ client-side (bundles, envelopes, sealed keys); this resource moves those blobs.
|
|
|
5
5
|
|
|
6
6
|
from __future__ import annotations
|
|
7
7
|
|
|
8
|
+
import base64
|
|
9
|
+
import hashlib
|
|
8
10
|
from typing import Any, Dict, List, Optional
|
|
9
11
|
|
|
10
12
|
from .._http import HttpClient, RequestExtensions
|
|
11
13
|
from ._base import Resource, enc, list_of
|
|
12
14
|
|
|
13
|
-
__all__ = ["Vault", "VaultDevices", "vault_unlock_message"]
|
|
15
|
+
__all__ = ["Vault", "VaultDevices", "vault_resolve_digest", "vault_resolve_message", "vault_unlock_message"]
|
|
14
16
|
|
|
15
17
|
|
|
16
18
|
def vault_unlock_message(challenge: str, device_id_or_passkey: str) -> str:
|
|
@@ -18,6 +20,17 @@ def vault_unlock_message(challenge: str, device_id_or_passkey: str) -> str:
|
|
|
18
20
|
return f"codai-vault/unlock/v1|{challenge}|{device_id_or_passkey}"
|
|
19
21
|
|
|
20
22
|
|
|
23
|
+
def vault_resolve_digest(handles: List[str], action: str) -> str:
|
|
24
|
+
"""``base64url_nopad(SHA-256(handles joined by "\\n" + "\\n\\n" + action))``, handles and action exactly as sent."""
|
|
25
|
+
raw = hashlib.sha256(("\n".join(handles) + "\n\n" + action).encode("utf-8")).digest()
|
|
26
|
+
return base64.urlsafe_b64encode(raw).rstrip(b"=").decode("ascii")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def vault_resolve_message(challenge: str, device_id: str, digest: str) -> str:
|
|
30
|
+
"""The UTF-8 message a resolve proof covers: ``codai-vault/resolve/v1|<challenge>|<device_id>|<digest>``."""
|
|
31
|
+
return f"codai-vault/resolve/v1|{challenge}|{device_id}|{digest}"
|
|
32
|
+
|
|
33
|
+
|
|
21
34
|
def _seg(base: str, item_id: str, suffix: str = "") -> str:
|
|
22
35
|
return f"{base}/{enc(item_id)}{suffix}"
|
|
23
36
|
|
|
@@ -320,6 +333,15 @@ class Vault(Resource):
|
|
|
320
333
|
"""``POST /v1/vault/http`` — brokered HTTPS call (``handle``, ``url``, ...); HTTP 202 = approval required."""
|
|
321
334
|
return self._json("/v1/vault/http", ext=ext, method="POST", body=_clean(body), no_retry=True) or {}
|
|
322
335
|
|
|
336
|
+
def resolve_challenge(self, ext: Optional[RequestExtensions] = None) -> Dict[str, Any]:
|
|
337
|
+
"""``POST /v1/vault/resolve/challenge`` — codai local brokers only; single-use challenge, 120 seconds."""
|
|
338
|
+
return self._json("/v1/vault/resolve/challenge", ext=ext, method="POST", body={}, no_retry=True)
|
|
339
|
+
|
|
323
340
|
def resolve(self, body: Dict[str, Any], ext: Optional[RequestExtensions] = None) -> Dict[str, Any]:
|
|
324
|
-
"""``POST /v1/vault/resolve`` — codai local brokers only; HTTP 202 = approval required.
|
|
341
|
+
"""``POST /v1/vault/resolve`` — codai local brokers only; HTTP 202 = approval required.
|
|
342
|
+
|
|
343
|
+
``body["proof"]`` is required: ``{"device_id", "challenge", "signature"}`` where the device key signs
|
|
344
|
+
:func:`vault_resolve_message` over a fresh :meth:`resolve_challenge` (400 ``proof_required`` /
|
|
345
|
+
``invalid_challenge``, 403 ``invalid_device_proof``).
|
|
346
|
+
"""
|
|
325
347
|
return self._json("/v1/vault/resolve", ext=ext, method="POST", body=_clean(body), no_retry=True) or {}
|
|
@@ -4,7 +4,7 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import pytest
|
|
6
6
|
|
|
7
|
-
from codai import CodaiError, vault_unlock_message
|
|
7
|
+
from codai import CodaiError, vault_resolve_digest, vault_resolve_message, vault_unlock_message
|
|
8
8
|
|
|
9
9
|
ITEM = "0b7d1a2e-1111-4222-8333-444455556666"
|
|
10
10
|
DEVICE = {
|
|
@@ -88,9 +88,21 @@ def test_vault_broker_calls_and_approval_flow(client, stub):
|
|
|
88
88
|
assert r == {"approval_id": "a2"}
|
|
89
89
|
assert (stub.last.path, stub.last.json) == ("/v1/vault/http", {"handle": "github", "url": "https://api.github.com/user"})
|
|
90
90
|
|
|
91
|
+
stub.reply(200, {"challenge": "ch123", "expires_at": "2026-10-24T00:02:00Z"})
|
|
92
|
+
ch = client.vault.resolve_challenge()
|
|
93
|
+
assert (stub.last.method, stub.last.path, stub.last.json) == ("POST", "/v1/vault/resolve/challenge", {})
|
|
94
|
+
proof = {"device_id": "cli-abcd1234", "challenge": ch["challenge"], "signature": "sig"}
|
|
91
95
|
stub.reply(202, {"approvals": ["a3"]})
|
|
92
|
-
assert client.vault.resolve({"handles": ["db"], "action": "run"}) == {"approvals": ["a3"]}
|
|
93
|
-
assert stub.last.path == "/v1/vault/resolve"
|
|
96
|
+
assert client.vault.resolve({"handles": ["db"], "action": "run", "proof": proof}) == {"approvals": ["a3"]}
|
|
97
|
+
assert (stub.last.path, stub.last.json) == ("/v1/vault/resolve", {"handles": ["db"], "action": "run", "proof": proof})
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def test_vault_resolve_proof_message_matches_contract_vector():
|
|
101
|
+
digest = vault_resolve_digest(["github.token", "aws.secret"], "curl -H x https://api.github.com")
|
|
102
|
+
assert digest == "D_gl7EO9sGs-oo2tDZhv6JDMLE3vQ2Vo9jwdFPZRKdQ"
|
|
103
|
+
assert vault_resolve_message("ch123", "cli-abcd1234", digest) == (
|
|
104
|
+
"codai-vault/resolve/v1|ch123|cli-abcd1234|D_gl7EO9sGs-oo2tDZhv6JDMLE3vQ2Vo9jwdFPZRKdQ"
|
|
105
|
+
)
|
|
94
106
|
|
|
95
107
|
|
|
96
108
|
def test_vault_devices_enrol_list_rename_revoke(client, stub):
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|