spctre-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.
- spctre_sdk-0.1.0/.gitignore +60 -0
- spctre_sdk-0.1.0/PKG-INFO +145 -0
- spctre_sdk-0.1.0/README.md +127 -0
- spctre_sdk-0.1.0/pyproject.toml +57 -0
- spctre_sdk-0.1.0/spctre/__init__.py +43 -0
- spctre_sdk-0.1.0/spctre/_generated/__init__.py +8 -0
- spctre_sdk-0.1.0/spctre/_generated/api/__init__.py +1 -0
- spctre_sdk-0.1.0/spctre/_generated/api/approvals/__init__.py +1 -0
- spctre_sdk-0.1.0/spctre/_generated/api/approvals/get_approval.py +174 -0
- spctre_sdk-0.1.0/spctre/_generated/api/auth/__init__.py +1 -0
- spctre_sdk-0.1.0/spctre/_generated/api/auth/refresh_token.py +203 -0
- spctre_sdk-0.1.0/spctre/_generated/api/auth/revoke_token.py +149 -0
- spctre_sdk-0.1.0/spctre/_generated/api/blueprint/__init__.py +1 -0
- spctre_sdk-0.1.0/spctre/_generated/api/blueprint/import_blueprint.py +217 -0
- spctre_sdk-0.1.0/spctre/_generated/api/bundle/__init__.py +1 -0
- spctre_sdk-0.1.0/spctre/_generated/api/bundle/get_bundle_latest.py +312 -0
- spctre_sdk-0.1.0/spctre/_generated/api/bundle/retain_latest_published_bundle.py +162 -0
- spctre_sdk-0.1.0/spctre/_generated/api/compliance/__init__.py +1 -0
- spctre_sdk-0.1.0/spctre/_generated/api/compliance/export_compliance.py +217 -0
- spctre_sdk-0.1.0/spctre/_generated/api/evidence/__init__.py +1 -0
- spctre_sdk-0.1.0/spctre/_generated/api/evidence/forensic_evidence_query.py +261 -0
- spctre_sdk-0.1.0/spctre/_generated/api/evidence/get_policy_content_artifact.py +192 -0
- spctre_sdk-0.1.0/spctre/_generated/api/evidence/ingest_evidence.py +206 -0
- spctre_sdk-0.1.0/spctre/_generated/api/evidence/ingest_git_checkpoint.py +210 -0
- spctre_sdk-0.1.0/spctre/_generated/api/evidence/retain_policy_content_artifact.py +211 -0
- spctre_sdk-0.1.0/spctre/_generated/api/gateway/__init__.py +1 -0
- spctre_sdk-0.1.0/spctre/_generated/api/gateway/gateway_decide.py +192 -0
- spctre_sdk-0.1.0/spctre/_generated/api/gateway/gateway_resolve.py +188 -0
- spctre_sdk-0.1.0/spctre/_generated/api/gateway/get_escalation_status.py +189 -0
- spctre_sdk-0.1.0/spctre/_generated/api/gateway/ingest_helicone_gateway_event.py +197 -0
- spctre_sdk-0.1.0/spctre/_generated/api/gateway/ingest_lite_llm_gateway_event.py +197 -0
- spctre_sdk-0.1.0/spctre/_generated/api/gateway/ingest_notion_gateway_event.py +201 -0
- spctre_sdk-0.1.0/spctre/_generated/api/gateway/ingest_portkey_gateway_event.py +197 -0
- spctre_sdk-0.1.0/spctre/_generated/api/gateway/list_escalations.py +178 -0
- spctre_sdk-0.1.0/spctre/_generated/api/gateway/register_agt_escalation_request.py +175 -0
- spctre_sdk-0.1.0/spctre/_generated/api/meta/__init__.py +1 -0
- spctre_sdk-0.1.0/spctre/_generated/api/meta/get_open_api_spec.py +136 -0
- spctre_sdk-0.1.0/spctre/_generated/api/policy/__init__.py +1 -0
- spctre_sdk-0.1.0/spctre/_generated/api/policy/import_policy.py +209 -0
- spctre_sdk-0.1.0/spctre/_generated/api/scim/__init__.py +1 -0
- spctre_sdk-0.1.0/spctre/_generated/api/scim/scim_create_user.py +180 -0
- spctre_sdk-0.1.0/spctre/_generated/api/scim/scim_list_users.py +210 -0
- spctre_sdk-0.1.0/spctre/_generated/api/simulation/__init__.py +1 -0
- spctre_sdk-0.1.0/spctre/_generated/api/simulation/evaluate_policy.py +193 -0
- spctre_sdk-0.1.0/spctre/_generated/api/trust/__init__.py +1 -0
- spctre_sdk-0.1.0/spctre/_generated/api/trust/evaluate_trust_governance.py +190 -0
- spctre_sdk-0.1.0/spctre/_generated/api/trust/ingest_context_budget_event.py +190 -0
- spctre_sdk-0.1.0/spctre/_generated/api/trust/ingest_trust_score.py +188 -0
- spctre_sdk-0.1.0/spctre/_generated/api/verification/__init__.py +1 -0
- spctre_sdk-0.1.0/spctre/_generated/api/verification/ingest_verification.py +183 -0
- spctre_sdk-0.1.0/spctre/_generated/api/verification/list_verifications.py +204 -0
- spctre_sdk-0.1.0/spctre/_generated/client.py +282 -0
- spctre_sdk-0.1.0/spctre/_generated/errors.py +16 -0
- spctre_sdk-0.1.0/spctre/_generated/models/__init__.py +317 -0
- spctre_sdk-0.1.0/spctre/_generated/models/api_error.py +100 -0
- spctre_sdk-0.1.0/spctre/_generated/models/api_error_issues_item.py +70 -0
- spctre_sdk-0.1.0/spctre/_generated/models/api_meta.py +78 -0
- spctre_sdk-0.1.0/spctre/_generated/models/approval_response.py +77 -0
- spctre_sdk-0.1.0/spctre/_generated/models/approval_response_approval.py +47 -0
- spctre_sdk-0.1.0/spctre/_generated/models/blueprint_import_request.py +74 -0
- spctre_sdk-0.1.0/spctre/_generated/models/blueprint_import_response.py +124 -0
- spctre_sdk-0.1.0/spctre/_generated/models/bundle_export_blocked_response.py +85 -0
- spctre_sdk-0.1.0/spctre/_generated/models/bundle_export_envelope.py +112 -0
- spctre_sdk-0.1.0/spctre/_generated/models/bundle_export_envelope_artifact_type_1.py +47 -0
- spctre_sdk-0.1.0/spctre/_generated/models/bundle_export_format.py +12 -0
- spctre_sdk-0.1.0/spctre/_generated/models/bundle_export_manifest.py +159 -0
- spctre_sdk-0.1.0/spctre/_generated/models/bundle_export_manifest_compatibility_level.py +10 -0
- spctre_sdk-0.1.0/spctre/_generated/models/bundle_export_manifest_provenance.py +148 -0
- spctre_sdk-0.1.0/spctre/_generated/models/bundle_export_manifest_provenance_target_stacks_item.py +47 -0
- spctre_sdk-0.1.0/spctre/_generated/models/bundle_export_preview_response.py +85 -0
- spctre_sdk-0.1.0/spctre/_generated/models/bundle_export_verification.py +91 -0
- spctre_sdk-0.1.0/spctre/_generated/models/bundle_export_verification_failed_response.py +95 -0
- spctre_sdk-0.1.0/spctre/_generated/models/bundle_response.py +50 -0
- spctre_sdk-0.1.0/spctre/_generated/models/compliance_export_response.py +305 -0
- spctre_sdk-0.1.0/spctre/_generated/models/compliance_export_response_approvals_item.py +47 -0
- spctre_sdk-0.1.0/spctre/_generated/models/compliance_export_response_artifact.py +47 -0
- spctre_sdk-0.1.0/spctre/_generated/models/compliance_export_response_escalations_item.py +47 -0
- spctre_sdk-0.1.0/spctre/_generated/models/compliance_export_response_framework_annotation_type_0.py +47 -0
- spctre_sdk-0.1.0/spctre/_generated/models/compliance_export_response_retention_plan_type_0.py +47 -0
- spctre_sdk-0.1.0/spctre/_generated/models/compliance_export_response_summary.py +126 -0
- spctre_sdk-0.1.0/spctre/_generated/models/compliance_export_response_timeline_item.py +47 -0
- spctre_sdk-0.1.0/spctre/_generated/models/compliance_export_response_verification_results_type_0.py +47 -0
- spctre_sdk-0.1.0/spctre/_generated/models/context_budget_ingest_request.py +192 -0
- spctre_sdk-0.1.0/spctre/_generated/models/context_budget_ingest_request_context_source_mix.py +47 -0
- spctre_sdk-0.1.0/spctre/_generated/models/context_budget_ingest_request_event_type.py +11 -0
- spctre_sdk-0.1.0/spctre/_generated/models/context_budget_ingest_request_governance_action.py +11 -0
- spctre_sdk-0.1.0/spctre/_generated/models/credential_grant.py +86 -0
- spctre_sdk-0.1.0/spctre/_generated/models/escalation_queue_item.py +194 -0
- spctre_sdk-0.1.0/spctre/_generated/models/escalation_status_response.py +257 -0
- spctre_sdk-0.1.0/spctre/_generated/models/escalation_status_response_approved_tool_parameters.py +52 -0
- spctre_sdk-0.1.0/spctre/_generated/models/escalation_status_response_resolution_outcome.py +10 -0
- spctre_sdk-0.1.0/spctre/_generated/models/escalation_status_response_status.py +11 -0
- spctre_sdk-0.1.0/spctre/_generated/models/evaluate_request.py +124 -0
- spctre_sdk-0.1.0/spctre/_generated/models/evaluate_request_tool_parameters.py +47 -0
- spctre_sdk-0.1.0/spctre/_generated/models/evaluate_response.py +150 -0
- spctre_sdk-0.1.0/spctre/_generated/models/evaluate_response_result.py +47 -0
- spctre_sdk-0.1.0/spctre/_generated/models/evaluate_trust_governance_response_200.py +47 -0
- spctre_sdk-0.1.0/spctre/_generated/models/evidence_ingest_request.py +567 -0
- spctre_sdk-0.1.0/spctre/_generated/models/evidence_ingest_request_execution_context.py +107 -0
- spctre_sdk-0.1.0/spctre/_generated/models/evidence_ingest_request_ingest_mode.py +9 -0
- spctre_sdk-0.1.0/spctre/_generated/models/evidence_ingest_request_orchestrator_ref.py +91 -0
- spctre_sdk-0.1.0/spctre/_generated/models/evidence_ingest_request_plugin_source.py +11 -0
- spctre_sdk-0.1.0/spctre/_generated/models/evidence_ingest_request_raw_evidence.py +47 -0
- spctre_sdk-0.1.0/spctre/_generated/models/evidence_ingest_request_skill_context.py +96 -0
- spctre_sdk-0.1.0/spctre/_generated/models/evidence_ingest_request_tool_parameters.py +47 -0
- spctre_sdk-0.1.0/spctre/_generated/models/evidence_ingest_response.py +112 -0
- spctre_sdk-0.1.0/spctre/_generated/models/evidence_ingest_response_evidence.py +47 -0
- spctre_sdk-0.1.0/spctre/_generated/models/evidence_ingest_response_gateway.py +109 -0
- spctre_sdk-0.1.0/spctre/_generated/models/evidence_ingest_response_gateway_outcome.py +10 -0
- spctre_sdk-0.1.0/spctre/_generated/models/evidence_ingest_response_gateway_risk_level.py +11 -0
- spctre_sdk-0.1.0/spctre/_generated/models/evidence_layer.py +9 -0
- spctre_sdk-0.1.0/spctre/_generated/models/export_compliance_format.py +9 -0
- spctre_sdk-0.1.0/spctre/_generated/models/export_compliance_framework.py +15 -0
- spctre_sdk-0.1.0/spctre/_generated/models/forensic_evidence_query_response_200.py +47 -0
- spctre_sdk-0.1.0/spctre/_generated/models/forensic_evidence_query_response_402.py +119 -0
- spctre_sdk-0.1.0/spctre/_generated/models/forensic_evidence_query_response_402_plan.py +8 -0
- spctre_sdk-0.1.0/spctre/_generated/models/gateway_decision.py +120 -0
- spctre_sdk-0.1.0/spctre/_generated/models/gateway_decision_outcome.py +10 -0
- spctre_sdk-0.1.0/spctre/_generated/models/gateway_decision_request.py +284 -0
- spctre_sdk-0.1.0/spctre/_generated/models/gateway_decision_request_risk_level.py +11 -0
- spctre_sdk-0.1.0/spctre/_generated/models/gateway_decision_request_tool_parameters.py +47 -0
- spctre_sdk-0.1.0/spctre/_generated/models/gateway_decision_response.py +109 -0
- spctre_sdk-0.1.0/spctre/_generated/models/gateway_decision_risk_level.py +11 -0
- spctre_sdk-0.1.0/spctre/_generated/models/gateway_ingest_response.py +92 -0
- spctre_sdk-0.1.0/spctre/_generated/models/gateway_resolve_request.py +94 -0
- spctre_sdk-0.1.0/spctre/_generated/models/gateway_resolve_request_resolution_outcome.py +10 -0
- spctre_sdk-0.1.0/spctre/_generated/models/gateway_resolve_response_200.py +83 -0
- spctre_sdk-0.1.0/spctre/_generated/models/gateway_webhook_request.py +50 -0
- spctre_sdk-0.1.0/spctre/_generated/models/get_open_api_spec_response_200.py +47 -0
- spctre_sdk-0.1.0/spctre/_generated/models/git_checkpoint_ingest_request.py +182 -0
- spctre_sdk-0.1.0/spctre/_generated/models/git_checkpoint_ingest_request_agent.py +70 -0
- spctre_sdk-0.1.0/spctre/_generated/models/git_checkpoint_ingest_request_checkpoint.py +132 -0
- spctre_sdk-0.1.0/spctre/_generated/models/git_checkpoint_ingest_request_checkpoint_diff.py +119 -0
- spctre_sdk-0.1.0/spctre/_generated/models/git_checkpoint_ingest_request_checkpoint_diff_files_item.py +91 -0
- spctre_sdk-0.1.0/spctre/_generated/models/git_checkpoint_ingest_request_checkpoint_diff_files_item_status.py +13 -0
- spctre_sdk-0.1.0/spctre/_generated/models/git_checkpoint_ingest_request_checkpoint_diff_format.py +10 -0
- spctre_sdk-0.1.0/spctre/_generated/models/git_checkpoint_ingest_request_checkpoint_repository.py +72 -0
- spctre_sdk-0.1.0/spctre/_generated/models/git_checkpoint_ingest_request_metadata.py +47 -0
- spctre_sdk-0.1.0/spctre/_generated/models/ingest_context_budget_event_response_201.py +47 -0
- spctre_sdk-0.1.0/spctre/_generated/models/ingest_trust_score_response_201.py +47 -0
- spctre_sdk-0.1.0/spctre/_generated/models/list_escalations_response_200.py +141 -0
- spctre_sdk-0.1.0/spctre/_generated/models/list_verifications_response_200.py +147 -0
- spctre_sdk-0.1.0/spctre/_generated/models/list_verifications_response_200_results_item.py +47 -0
- spctre_sdk-0.1.0/spctre/_generated/models/pagination.py +77 -0
- spctre_sdk-0.1.0/spctre/_generated/models/policy_content_artifact_retain_response.py +83 -0
- spctre_sdk-0.1.0/spctre/_generated/models/policy_import_request.py +127 -0
- spctre_sdk-0.1.0/spctre/_generated/models/policy_import_request_scope.py +11 -0
- spctre_sdk-0.1.0/spctre/_generated/models/policy_import_response.py +115 -0
- spctre_sdk-0.1.0/spctre/_generated/models/register_agt_escalation_request_body.py +69 -0
- spctre_sdk-0.1.0/spctre/_generated/models/retain_latest_published_bundle_response_201.py +91 -0
- spctre_sdk-0.1.0/spctre/_generated/models/runtime_decision_status.py +11 -0
- spctre_sdk-0.1.0/spctre/_generated/models/runtime_policy_context.py +115 -0
- spctre_sdk-0.1.0/spctre/_generated/models/runtime_policy_context_scope.py +12 -0
- spctre_sdk-0.1.0/spctre/_generated/models/runtime_stack.py +26 -0
- spctre_sdk-0.1.0/spctre/_generated/models/runtime_target.py +101 -0
- spctre_sdk-0.1.0/spctre/_generated/models/scim_create_user_body.py +47 -0
- spctre_sdk-0.1.0/spctre/_generated/models/scim_create_user_response_201.py +47 -0
- spctre_sdk-0.1.0/spctre/_generated/models/scim_create_user_response_402.py +117 -0
- spctre_sdk-0.1.0/spctre/_generated/models/scim_create_user_response_402_plan.py +8 -0
- spctre_sdk-0.1.0/spctre/_generated/models/scim_list_users_response_200.py +47 -0
- spctre_sdk-0.1.0/spctre/_generated/models/scim_list_users_response_402.py +117 -0
- spctre_sdk-0.1.0/spctre/_generated/models/scim_list_users_response_402_plan.py +8 -0
- spctre_sdk-0.1.0/spctre/_generated/models/token_refresh_request.py +61 -0
- spctre_sdk-0.1.0/spctre/_generated/models/token_refresh_response.py +104 -0
- spctre_sdk-0.1.0/spctre/_generated/models/trigger_kind.py +13 -0
- spctre_sdk-0.1.0/spctre/_generated/models/trust_evaluate_request.py +160 -0
- spctre_sdk-0.1.0/spctre/_generated/models/trust_evaluate_request_consequence_tier.py +11 -0
- spctre_sdk-0.1.0/spctre/_generated/models/trust_score_ingest_request.py +115 -0
- spctre_sdk-0.1.0/spctre/_generated/models/trust_score_ingest_request_source.py +12 -0
- spctre_sdk-0.1.0/spctre/_generated/models/verification_ingest_request.py +369 -0
- spctre_sdk-0.1.0/spctre/_generated/models/verification_ingest_request_compatibility_check_outcome.py +10 -0
- spctre_sdk-0.1.0/spctre/_generated/models/verification_ingest_request_escrow_verification_outcome.py +10 -0
- spctre_sdk-0.1.0/spctre/_generated/models/verification_ingest_request_outcome.py +10 -0
- spctre_sdk-0.1.0/spctre/_generated/models/verification_ingest_request_summary.py +47 -0
- spctre_sdk-0.1.0/spctre/_generated/models/verification_ingest_request_verification_type.py +13 -0
- spctre_sdk-0.1.0/spctre/_generated/models/verification_ingest_response.py +97 -0
- spctre_sdk-0.1.0/spctre/_generated/models/verification_ingest_response_outcome.py +10 -0
- spctre_sdk-0.1.0/spctre/_generated/types.py +54 -0
- spctre_sdk-0.1.0/spctre/_url.py +52 -0
- spctre_sdk-0.1.0/spctre/_version.py +1 -0
- spctre_sdk-0.1.0/spctre/client.py +313 -0
- spctre_sdk-0.1.0/spctre/errors.py +160 -0
- spctre_sdk-0.1.0/spctre/models.py +15 -0
- spctre_sdk-0.1.0/spctre/py.typed +0 -0
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Dependencies
|
|
2
|
+
node_modules/
|
|
3
|
+
.pnpm-store/
|
|
4
|
+
|
|
5
|
+
# pnpm is the sole package manager; reject stray npm/yarn lockfiles
|
|
6
|
+
package-lock.json
|
|
7
|
+
yarn.lock
|
|
8
|
+
|
|
9
|
+
# Environment and local secrets
|
|
10
|
+
.env
|
|
11
|
+
.env.*
|
|
12
|
+
!.env*.example
|
|
13
|
+
|
|
14
|
+
# Build outputs
|
|
15
|
+
dist/
|
|
16
|
+
target/
|
|
17
|
+
apps/web/.next/
|
|
18
|
+
apps/web/.source/
|
|
19
|
+
apps/worker/dist/
|
|
20
|
+
packages/cli/dist/
|
|
21
|
+
packages/mcp-server/dist/
|
|
22
|
+
packages/policy-schema/dist/
|
|
23
|
+
packages/policy-schema/native/target/
|
|
24
|
+
packages/ui/storybook-static/
|
|
25
|
+
|
|
26
|
+
# Caches and test output
|
|
27
|
+
.turbo/
|
|
28
|
+
.cache/
|
|
29
|
+
coverage/
|
|
30
|
+
cover.out
|
|
31
|
+
.nyc_output/
|
|
32
|
+
.pytest_cache/
|
|
33
|
+
.ruff_cache/
|
|
34
|
+
__pycache__/
|
|
35
|
+
*.tsbuildinfo
|
|
36
|
+
|
|
37
|
+
# Native artifacts
|
|
38
|
+
*.node
|
|
39
|
+
|
|
40
|
+
# Local tooling
|
|
41
|
+
.venv/
|
|
42
|
+
.agents/
|
|
43
|
+
.antigravitycli/
|
|
44
|
+
.claude/
|
|
45
|
+
.codex/
|
|
46
|
+
.impeccable/
|
|
47
|
+
.vscode/
|
|
48
|
+
.idea/
|
|
49
|
+
.spctre/
|
|
50
|
+
packages/cli/.spctre/
|
|
51
|
+
.polly/
|
|
52
|
+
.worktrees/
|
|
53
|
+
.pi/
|
|
54
|
+
|
|
55
|
+
# Local notes and OS files
|
|
56
|
+
concepts/
|
|
57
|
+
.DS_Store
|
|
58
|
+
.playwright-cli/
|
|
59
|
+
DESIGN.md
|
|
60
|
+
PRODUCT.md
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: spctre-sdk
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python SDK for the Spctre policy operations control plane.
|
|
5
|
+
Project-URL: Homepage, https://spctre.dev
|
|
6
|
+
Project-URL: Source, https://github.com/getspctre/spctre
|
|
7
|
+
Author-email: Spctre <support@spctre.dev>
|
|
8
|
+
License-Expression: Apache-2.0
|
|
9
|
+
Keywords: agents,governance,policy,spctre
|
|
10
|
+
Requires-Python: >=3.11
|
|
11
|
+
Requires-Dist: attrs>=22.2.0
|
|
12
|
+
Requires-Dist: httpx<0.29.0,>=0.23.0
|
|
13
|
+
Requires-Dist: python-dateutil>=2.8.0
|
|
14
|
+
Provides-Extra: test
|
|
15
|
+
Requires-Dist: pyrefly==1.1.0; extra == 'test'
|
|
16
|
+
Requires-Dist: pytest>=9.0.3; extra == 'test'
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
|
|
19
|
+
# spctre-sdk
|
|
20
|
+
|
|
21
|
+
Python client for the [Spctre](https://spctre.dev) policy operations control plane.
|
|
22
|
+
|
|
23
|
+
```sh
|
|
24
|
+
pip install spctre-sdk
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Requires Python 3.11+.
|
|
28
|
+
|
|
29
|
+
## Usage
|
|
30
|
+
|
|
31
|
+
```python
|
|
32
|
+
from spctre import SpctreClient
|
|
33
|
+
from spctre.models import GatewayDecisionRequest
|
|
34
|
+
|
|
35
|
+
client = SpctreClient(
|
|
36
|
+
base_url="https://app-staging.spctre.dev",
|
|
37
|
+
token=token,
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
decision = client.gateway.decide(
|
|
41
|
+
GatewayDecisionRequest(
|
|
42
|
+
decision_id="d-1",
|
|
43
|
+
artifact_hash="sha256:...",
|
|
44
|
+
policy_context=[...],
|
|
45
|
+
)
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
if decision.decision.outcome.value == "PROCEED":
|
|
49
|
+
... # act
|
|
50
|
+
client.evidence.ingest(record)
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
`base_url` is the deployment origin, not the API root — the client owns the
|
|
54
|
+
`/api/v1` path segment. Staging, production and self-hosted deployments differ
|
|
55
|
+
only in this value:
|
|
56
|
+
|
|
57
|
+
| Given | Requests go to |
|
|
58
|
+
| ------------------------------------- | ------------------------------------------------ |
|
|
59
|
+
| `https://app-staging.spctre.dev` | `https://app-staging.spctre.dev/api/v1/...` |
|
|
60
|
+
| `http://localhost:3000` | `http://localhost:3000/api/v1/...` |
|
|
61
|
+
| `https://internal.example.com/spctre` | `https://internal.example.com/spctre/api/v1/...` |
|
|
62
|
+
|
|
63
|
+
A URL that already ends in `/api/v1` is accepted as-is rather than doubled.
|
|
64
|
+
|
|
65
|
+
## Domains
|
|
66
|
+
|
|
67
|
+
The client is organized by product domain, following the control-plane loop:
|
|
68
|
+
policy changes become published controls, controls produce enforced decisions,
|
|
69
|
+
decisions produce evidence, evidence supports assurance.
|
|
70
|
+
|
|
71
|
+
| Domain | Operations |
|
|
72
|
+
| --------------------- | --------------------------------------------------------------------------------------- |
|
|
73
|
+
| `client.gateway` | `decide`, `resolve`, `escalation_status`, `list_escalations`, `register_agt_escalation` |
|
|
74
|
+
| `client.evidence` | `ingest`, `ingest_git_checkpoint`, `forensic_query` |
|
|
75
|
+
| `client.policy` | `import_policy` |
|
|
76
|
+
| `client.trust` | `ingest_score`, `evaluate`, `ingest_context_budget` |
|
|
77
|
+
| `client.verification` | `ingest`, `list` |
|
|
78
|
+
| `client.bundle` | `latest`, `retain_latest` |
|
|
79
|
+
| `client.compliance` | `export` |
|
|
80
|
+
| `client.approvals` | `get` |
|
|
81
|
+
|
|
82
|
+
Models come from `spctre.models`. Field names are snake_case in Python and are
|
|
83
|
+
serialized to the API's camelCase automatically.
|
|
84
|
+
|
|
85
|
+
Operations outside these domains — SCIM, auth token rotation, blueprint import,
|
|
86
|
+
simulation — are reachable through `spctre._generated`, which is regenerated
|
|
87
|
+
wholesale from the OpenAPI spec and carries no stability promise. Anything you
|
|
88
|
+
find yourself needing there is a reasonable thing to request as a facade
|
|
89
|
+
addition.
|
|
90
|
+
|
|
91
|
+
## Errors
|
|
92
|
+
|
|
93
|
+
Every failure is raised as a `SpctreError` subclass. Generated exceptions never
|
|
94
|
+
escape:
|
|
95
|
+
|
|
96
|
+
| Raised | When |
|
|
97
|
+
| ----------------------- | ------------------------------------------- |
|
|
98
|
+
| `SpctreAuthError` | 401: token missing, expired, or revoked |
|
|
99
|
+
| `SpctrePermissionError` | 403: token lacks the required scope |
|
|
100
|
+
| `SpctreRequestError` | other 4xx |
|
|
101
|
+
| `SpctreServerError` | 5xx |
|
|
102
|
+
| `SpctreTransportError` | no HTTP response at all (DNS, TLS, timeout) |
|
|
103
|
+
| `SpctreResponseError` | answered, but the response did not parse |
|
|
104
|
+
|
|
105
|
+
Each carries `status`, `body`, and `trace_id` lifted from the response
|
|
106
|
+
envelope's `meta.traceId` — quote it when reporting a problem.
|
|
107
|
+
|
|
108
|
+
`SpctreResponseError` is deliberately distinct from `SpctreTransportError`: it
|
|
109
|
+
means the client and the deployment disagree about the API contract, usually an
|
|
110
|
+
SDK too old or too new for the server. Reporting that as an unreachable host
|
|
111
|
+
would send debugging in the wrong direction.
|
|
112
|
+
|
|
113
|
+
## Testing
|
|
114
|
+
|
|
115
|
+
`transport=` accepts any `httpx.BaseTransport`, so tests need no network:
|
|
116
|
+
|
|
117
|
+
```python
|
|
118
|
+
import httpx
|
|
119
|
+
from spctre import SpctreClient
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def handler(request: httpx.Request) -> httpx.Response:
|
|
123
|
+
assert request.url.path == "/api/v1/gateway/decide"
|
|
124
|
+
return httpx.Response(200, json={...})
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
client = SpctreClient(
|
|
128
|
+
base_url="https://app.example.com",
|
|
129
|
+
token="test-token",
|
|
130
|
+
transport=httpx.MockTransport(handler),
|
|
131
|
+
)
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## How this package is built
|
|
135
|
+
|
|
136
|
+
`spctre._generated` is produced by
|
|
137
|
+
[openapi-python-client](https://github.com/openapi-generators/openapi-python-client)
|
|
138
|
+
from the control plane's OpenAPI spec, at a pinned generator version. Unlike
|
|
139
|
+
most generated code in this repository it is **checked in**, so installing or
|
|
140
|
+
building the package needs no code generator, and CI can assert the committed
|
|
141
|
+
client still matches the spec.
|
|
142
|
+
|
|
143
|
+
## License
|
|
144
|
+
|
|
145
|
+
Apache-2.0
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# spctre-sdk
|
|
2
|
+
|
|
3
|
+
Python client for the [Spctre](https://spctre.dev) policy operations control plane.
|
|
4
|
+
|
|
5
|
+
```sh
|
|
6
|
+
pip install spctre-sdk
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
Requires Python 3.11+.
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
from spctre import SpctreClient
|
|
15
|
+
from spctre.models import GatewayDecisionRequest
|
|
16
|
+
|
|
17
|
+
client = SpctreClient(
|
|
18
|
+
base_url="https://app-staging.spctre.dev",
|
|
19
|
+
token=token,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
decision = client.gateway.decide(
|
|
23
|
+
GatewayDecisionRequest(
|
|
24
|
+
decision_id="d-1",
|
|
25
|
+
artifact_hash="sha256:...",
|
|
26
|
+
policy_context=[...],
|
|
27
|
+
)
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
if decision.decision.outcome.value == "PROCEED":
|
|
31
|
+
... # act
|
|
32
|
+
client.evidence.ingest(record)
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
`base_url` is the deployment origin, not the API root — the client owns the
|
|
36
|
+
`/api/v1` path segment. Staging, production and self-hosted deployments differ
|
|
37
|
+
only in this value:
|
|
38
|
+
|
|
39
|
+
| Given | Requests go to |
|
|
40
|
+
| ------------------------------------- | ------------------------------------------------ |
|
|
41
|
+
| `https://app-staging.spctre.dev` | `https://app-staging.spctre.dev/api/v1/...` |
|
|
42
|
+
| `http://localhost:3000` | `http://localhost:3000/api/v1/...` |
|
|
43
|
+
| `https://internal.example.com/spctre` | `https://internal.example.com/spctre/api/v1/...` |
|
|
44
|
+
|
|
45
|
+
A URL that already ends in `/api/v1` is accepted as-is rather than doubled.
|
|
46
|
+
|
|
47
|
+
## Domains
|
|
48
|
+
|
|
49
|
+
The client is organized by product domain, following the control-plane loop:
|
|
50
|
+
policy changes become published controls, controls produce enforced decisions,
|
|
51
|
+
decisions produce evidence, evidence supports assurance.
|
|
52
|
+
|
|
53
|
+
| Domain | Operations |
|
|
54
|
+
| --------------------- | --------------------------------------------------------------------------------------- |
|
|
55
|
+
| `client.gateway` | `decide`, `resolve`, `escalation_status`, `list_escalations`, `register_agt_escalation` |
|
|
56
|
+
| `client.evidence` | `ingest`, `ingest_git_checkpoint`, `forensic_query` |
|
|
57
|
+
| `client.policy` | `import_policy` |
|
|
58
|
+
| `client.trust` | `ingest_score`, `evaluate`, `ingest_context_budget` |
|
|
59
|
+
| `client.verification` | `ingest`, `list` |
|
|
60
|
+
| `client.bundle` | `latest`, `retain_latest` |
|
|
61
|
+
| `client.compliance` | `export` |
|
|
62
|
+
| `client.approvals` | `get` |
|
|
63
|
+
|
|
64
|
+
Models come from `spctre.models`. Field names are snake_case in Python and are
|
|
65
|
+
serialized to the API's camelCase automatically.
|
|
66
|
+
|
|
67
|
+
Operations outside these domains — SCIM, auth token rotation, blueprint import,
|
|
68
|
+
simulation — are reachable through `spctre._generated`, which is regenerated
|
|
69
|
+
wholesale from the OpenAPI spec and carries no stability promise. Anything you
|
|
70
|
+
find yourself needing there is a reasonable thing to request as a facade
|
|
71
|
+
addition.
|
|
72
|
+
|
|
73
|
+
## Errors
|
|
74
|
+
|
|
75
|
+
Every failure is raised as a `SpctreError` subclass. Generated exceptions never
|
|
76
|
+
escape:
|
|
77
|
+
|
|
78
|
+
| Raised | When |
|
|
79
|
+
| ----------------------- | ------------------------------------------- |
|
|
80
|
+
| `SpctreAuthError` | 401: token missing, expired, or revoked |
|
|
81
|
+
| `SpctrePermissionError` | 403: token lacks the required scope |
|
|
82
|
+
| `SpctreRequestError` | other 4xx |
|
|
83
|
+
| `SpctreServerError` | 5xx |
|
|
84
|
+
| `SpctreTransportError` | no HTTP response at all (DNS, TLS, timeout) |
|
|
85
|
+
| `SpctreResponseError` | answered, but the response did not parse |
|
|
86
|
+
|
|
87
|
+
Each carries `status`, `body`, and `trace_id` lifted from the response
|
|
88
|
+
envelope's `meta.traceId` — quote it when reporting a problem.
|
|
89
|
+
|
|
90
|
+
`SpctreResponseError` is deliberately distinct from `SpctreTransportError`: it
|
|
91
|
+
means the client and the deployment disagree about the API contract, usually an
|
|
92
|
+
SDK too old or too new for the server. Reporting that as an unreachable host
|
|
93
|
+
would send debugging in the wrong direction.
|
|
94
|
+
|
|
95
|
+
## Testing
|
|
96
|
+
|
|
97
|
+
`transport=` accepts any `httpx.BaseTransport`, so tests need no network:
|
|
98
|
+
|
|
99
|
+
```python
|
|
100
|
+
import httpx
|
|
101
|
+
from spctre import SpctreClient
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def handler(request: httpx.Request) -> httpx.Response:
|
|
105
|
+
assert request.url.path == "/api/v1/gateway/decide"
|
|
106
|
+
return httpx.Response(200, json={...})
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
client = SpctreClient(
|
|
110
|
+
base_url="https://app.example.com",
|
|
111
|
+
token="test-token",
|
|
112
|
+
transport=httpx.MockTransport(handler),
|
|
113
|
+
)
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## How this package is built
|
|
117
|
+
|
|
118
|
+
`spctre._generated` is produced by
|
|
119
|
+
[openapi-python-client](https://github.com/openapi-generators/openapi-python-client)
|
|
120
|
+
from the control plane's OpenAPI spec, at a pinned generator version. Unlike
|
|
121
|
+
most generated code in this repository it is **checked in**, so installing or
|
|
122
|
+
building the package needs no code generator, and CI can assert the committed
|
|
123
|
+
client still matches the spec.
|
|
124
|
+
|
|
125
|
+
## License
|
|
126
|
+
|
|
127
|
+
Apache-2.0
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "spctre-sdk"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
description = "Python SDK for the Spctre policy operations control plane."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
# 3.11, not 3.10: the generated client parses timestamps with
|
|
11
|
+
# datetime.fromisoformat, which did not accept the trailing `Z` until 3.11, and
|
|
12
|
+
# every API response carries a required meta.ts. Matches the adapter packages.
|
|
13
|
+
requires-python = ">=3.11"
|
|
14
|
+
license = "Apache-2.0"
|
|
15
|
+
authors = [{ name = "Spctre", email = "support@spctre.dev" }]
|
|
16
|
+
keywords = ["spctre", "policy", "governance", "agents"]
|
|
17
|
+
dependencies = [
|
|
18
|
+
"httpx >= 0.23.0, < 0.29.0",
|
|
19
|
+
"attrs >= 22.2.0",
|
|
20
|
+
"python-dateutil >= 2.8.0",
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
[project.urls]
|
|
24
|
+
Homepage = "https://spctre.dev"
|
|
25
|
+
Source = "https://github.com/getspctre/spctre"
|
|
26
|
+
|
|
27
|
+
[project.optional-dependencies]
|
|
28
|
+
test = [
|
|
29
|
+
"pytest>=9.0.3",
|
|
30
|
+
"pyrefly==1.1.0",
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
# Single source of truth for the version, matching the adapter packages. The
|
|
34
|
+
# release workflow rewrites this file to stamp a .devN suffix for TestPyPI.
|
|
35
|
+
[tool.hatch.version]
|
|
36
|
+
path = "spctre/_version.py"
|
|
37
|
+
|
|
38
|
+
# One top-level package. spctre/_generated is generated by
|
|
39
|
+
# scripts/generate-python-sdk.sh but IS checked in, so building needs no code
|
|
40
|
+
# generator.
|
|
41
|
+
[tool.hatch.build.targets.wheel]
|
|
42
|
+
packages = ["spctre"]
|
|
43
|
+
|
|
44
|
+
[tool.hatch.build.targets.sdist]
|
|
45
|
+
include = ["spctre", "README.md"]
|
|
46
|
+
|
|
47
|
+
[tool.pytest.ini_options]
|
|
48
|
+
testpaths = ["tests"]
|
|
49
|
+
|
|
50
|
+
[tool.pyrefly]
|
|
51
|
+
# The generated tree is checked by its own generator's conventions, not ours;
|
|
52
|
+
# type-checking it would report the generator's style, which we cannot fix.
|
|
53
|
+
project-includes = ["spctre", "tests"]
|
|
54
|
+
project-excludes = ["spctre/_generated/**"]
|
|
55
|
+
# pytest puts the rootdir's tests/ directory on sys.path, which is how the
|
|
56
|
+
# test modules import shared helpers from conftest. Tell the checker the same.
|
|
57
|
+
search-path = [".", "tests"]
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"""Supported Python client for the Spctre control plane.
|
|
2
|
+
|
|
3
|
+
from spctre import SpctreClient
|
|
4
|
+
|
|
5
|
+
client = SpctreClient(base_url="https://app-staging.spctre.dev", token=token)
|
|
6
|
+
decision = client.gateway.decide(request)
|
|
7
|
+
|
|
8
|
+
Request and response models live in `spctre.models`. The full generated
|
|
9
|
+
surface, including operations the facade does not wrap, is under
|
|
10
|
+
`spctre._generated` — regenerated wholesale from the OpenAPI spec and not
|
|
11
|
+
covered by this package's stability promise.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from . import models
|
|
15
|
+
from ._url import API_PREFIX, normalize_base_url
|
|
16
|
+
from ._version import __version__
|
|
17
|
+
from .client import UNSET, SpctreClient, Unset
|
|
18
|
+
from .errors import (
|
|
19
|
+
SpctreAuthError,
|
|
20
|
+
SpctreError,
|
|
21
|
+
SpctrePermissionError,
|
|
22
|
+
SpctreRequestError,
|
|
23
|
+
SpctreResponseError,
|
|
24
|
+
SpctreServerError,
|
|
25
|
+
SpctreTransportError,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
__all__ = [
|
|
29
|
+
"API_PREFIX",
|
|
30
|
+
"SpctreAuthError",
|
|
31
|
+
"SpctreClient",
|
|
32
|
+
"SpctreError",
|
|
33
|
+
"SpctrePermissionError",
|
|
34
|
+
"SpctreRequestError",
|
|
35
|
+
"SpctreResponseError",
|
|
36
|
+
"SpctreServerError",
|
|
37
|
+
"SpctreTransportError",
|
|
38
|
+
"UNSET",
|
|
39
|
+
"Unset",
|
|
40
|
+
"__version__",
|
|
41
|
+
"models",
|
|
42
|
+
"normalize_base_url",
|
|
43
|
+
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Contains methods for accessing the API"""
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Contains endpoint functions for accessing the API"""
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
from http import HTTPStatus
|
|
2
|
+
from typing import Any
|
|
3
|
+
from urllib.parse import quote
|
|
4
|
+
|
|
5
|
+
import httpx
|
|
6
|
+
|
|
7
|
+
from ... import errors
|
|
8
|
+
from ...client import AuthenticatedClient, Client
|
|
9
|
+
from ...models.api_error import ApiError
|
|
10
|
+
from ...models.approval_response import ApprovalResponse
|
|
11
|
+
from ...types import Response
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _get_kwargs(
|
|
15
|
+
id: str,
|
|
16
|
+
) -> dict[str, Any]:
|
|
17
|
+
|
|
18
|
+
_kwargs: dict[str, Any] = {
|
|
19
|
+
"method": "get",
|
|
20
|
+
"url": "/approvals/{id}".format(
|
|
21
|
+
id=quote(str(id), safe=""),
|
|
22
|
+
),
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return _kwargs
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _parse_response(
|
|
29
|
+
*, client: AuthenticatedClient | Client, response: httpx.Response
|
|
30
|
+
) -> ApiError | ApprovalResponse | None:
|
|
31
|
+
if response.status_code == 200:
|
|
32
|
+
response_200 = ApprovalResponse.from_dict(response.json())
|
|
33
|
+
|
|
34
|
+
return response_200
|
|
35
|
+
|
|
36
|
+
if response.status_code == 401:
|
|
37
|
+
response_401 = ApiError.from_dict(response.json())
|
|
38
|
+
|
|
39
|
+
return response_401
|
|
40
|
+
|
|
41
|
+
if response.status_code == 404:
|
|
42
|
+
response_404 = ApiError.from_dict(response.json())
|
|
43
|
+
|
|
44
|
+
return response_404
|
|
45
|
+
|
|
46
|
+
if client.raise_on_unexpected_status:
|
|
47
|
+
raise errors.UnexpectedStatus(response.status_code, response.content)
|
|
48
|
+
else:
|
|
49
|
+
return None
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _build_response(
|
|
53
|
+
*, client: AuthenticatedClient | Client, response: httpx.Response
|
|
54
|
+
) -> Response[ApiError | ApprovalResponse]:
|
|
55
|
+
return Response(
|
|
56
|
+
status_code=HTTPStatus(response.status_code),
|
|
57
|
+
content=response.content,
|
|
58
|
+
headers=response.headers,
|
|
59
|
+
parsed=_parse_response(client=client, response=response),
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def sync_detailed(
|
|
64
|
+
id: str,
|
|
65
|
+
*,
|
|
66
|
+
client: AuthenticatedClient | Client,
|
|
67
|
+
) -> Response[ApiError | ApprovalResponse]:
|
|
68
|
+
"""Get an approval by ID
|
|
69
|
+
|
|
70
|
+
Returns the approval record for a specific revision approval ID.
|
|
71
|
+
|
|
72
|
+
Args:
|
|
73
|
+
id (str):
|
|
74
|
+
|
|
75
|
+
Raises:
|
|
76
|
+
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
|
77
|
+
httpx.TimeoutException: If the request takes longer than Client.timeout.
|
|
78
|
+
|
|
79
|
+
Returns:
|
|
80
|
+
Response[ApiError | ApprovalResponse]
|
|
81
|
+
"""
|
|
82
|
+
|
|
83
|
+
kwargs = _get_kwargs(
|
|
84
|
+
id=id,
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
response = client.get_httpx_client().request(
|
|
88
|
+
**kwargs,
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
return _build_response(client=client, response=response)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def sync(
|
|
95
|
+
id: str,
|
|
96
|
+
*,
|
|
97
|
+
client: AuthenticatedClient | Client,
|
|
98
|
+
) -> ApiError | ApprovalResponse | None:
|
|
99
|
+
"""Get an approval by ID
|
|
100
|
+
|
|
101
|
+
Returns the approval record for a specific revision approval ID.
|
|
102
|
+
|
|
103
|
+
Args:
|
|
104
|
+
id (str):
|
|
105
|
+
|
|
106
|
+
Raises:
|
|
107
|
+
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
|
108
|
+
httpx.TimeoutException: If the request takes longer than Client.timeout.
|
|
109
|
+
|
|
110
|
+
Returns:
|
|
111
|
+
ApiError | ApprovalResponse
|
|
112
|
+
"""
|
|
113
|
+
|
|
114
|
+
return sync_detailed(
|
|
115
|
+
id=id,
|
|
116
|
+
client=client,
|
|
117
|
+
).parsed
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
async def asyncio_detailed(
|
|
121
|
+
id: str,
|
|
122
|
+
*,
|
|
123
|
+
client: AuthenticatedClient | Client,
|
|
124
|
+
) -> Response[ApiError | ApprovalResponse]:
|
|
125
|
+
"""Get an approval by ID
|
|
126
|
+
|
|
127
|
+
Returns the approval record for a specific revision approval ID.
|
|
128
|
+
|
|
129
|
+
Args:
|
|
130
|
+
id (str):
|
|
131
|
+
|
|
132
|
+
Raises:
|
|
133
|
+
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
|
134
|
+
httpx.TimeoutException: If the request takes longer than Client.timeout.
|
|
135
|
+
|
|
136
|
+
Returns:
|
|
137
|
+
Response[ApiError | ApprovalResponse]
|
|
138
|
+
"""
|
|
139
|
+
|
|
140
|
+
kwargs = _get_kwargs(
|
|
141
|
+
id=id,
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
response = await client.get_async_httpx_client().request(**kwargs)
|
|
145
|
+
|
|
146
|
+
return _build_response(client=client, response=response)
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
async def asyncio(
|
|
150
|
+
id: str,
|
|
151
|
+
*,
|
|
152
|
+
client: AuthenticatedClient | Client,
|
|
153
|
+
) -> ApiError | ApprovalResponse | None:
|
|
154
|
+
"""Get an approval by ID
|
|
155
|
+
|
|
156
|
+
Returns the approval record for a specific revision approval ID.
|
|
157
|
+
|
|
158
|
+
Args:
|
|
159
|
+
id (str):
|
|
160
|
+
|
|
161
|
+
Raises:
|
|
162
|
+
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
|
163
|
+
httpx.TimeoutException: If the request takes longer than Client.timeout.
|
|
164
|
+
|
|
165
|
+
Returns:
|
|
166
|
+
ApiError | ApprovalResponse
|
|
167
|
+
"""
|
|
168
|
+
|
|
169
|
+
return (
|
|
170
|
+
await asyncio_detailed(
|
|
171
|
+
id=id,
|
|
172
|
+
client=client,
|
|
173
|
+
)
|
|
174
|
+
).parsed
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Contains endpoint functions for accessing the API"""
|