stackgen-sdk 0.1.2__tar.gz → 0.1.5__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.
- {stackgen_sdk-0.1.2 → stackgen_sdk-0.1.5}/.gitignore +6 -0
- stackgen_sdk-0.1.5/PKG-INFO +92 -0
- stackgen_sdk-0.1.5/README.md +71 -0
- {stackgen_sdk-0.1.2 → stackgen_sdk-0.1.5}/pyproject.toml +4 -1
- {stackgen_sdk-0.1.2 → stackgen_sdk-0.1.5}/src/stackgen/__init__.py +7 -2
- {stackgen_sdk-0.1.2 → stackgen_sdk-0.1.5}/src/stackgen/_version.py +2 -2
- {stackgen_sdk-0.1.2 → stackgen_sdk-0.1.5}/src/stackgen/aiden/__init__.py +3 -1
- stackgen_sdk-0.1.5/src/stackgen/aiden/ask.py +168 -0
- stackgen_sdk-0.1.5/src/stackgen/aiden/namespace.py +78 -0
- {stackgen_sdk-0.1.2 → stackgen_sdk-0.1.5}/src/stackgen/aiden/runs.py +1 -1
- stackgen_sdk-0.1.5/src/stackgen/aiden/sessions.py +46 -0
- stackgen_sdk-0.1.5/src/stackgen/aiden/webhook.py +81 -0
- {stackgen_sdk-0.1.2 → stackgen_sdk-0.1.5}/src/stackgen/cli.py +9 -1
- {stackgen_sdk-0.1.2 → stackgen_sdk-0.1.5}/src/stackgen/config.py +14 -32
- {stackgen_sdk-0.1.2 → stackgen_sdk-0.1.5}/src/stackgen/generated/.openapi-generator/FILES +33 -0
- {stackgen_sdk-0.1.2 → stackgen_sdk-0.1.5}/src/stackgen/generated/__init__.py +22 -0
- {stackgen_sdk-0.1.2 → stackgen_sdk-0.1.5}/src/stackgen/generated/api/aiden_api.py +578 -0
- {stackgen_sdk-0.1.2 → stackgen_sdk-0.1.5}/src/stackgen/generated/configuration.py +3 -3
- {stackgen_sdk-0.1.2 → stackgen_sdk-0.1.5}/src/stackgen/generated/models/__init__.py +11 -0
- stackgen_sdk-0.1.5/src/stackgen/generated/models/ask_guild_request.py +119 -0
- stackgen_sdk-0.1.5/src/stackgen/generated/models/ask_guild_start_response.py +109 -0
- stackgen_sdk-0.1.5/src/stackgen/generated/models/candidate_type.py +40 -0
- stackgen_sdk-0.1.5/src/stackgen/generated/models/dag_edge.py +89 -0
- stackgen_sdk-0.1.5/src/stackgen/generated/models/dag_node.py +103 -0
- stackgen_sdk-0.1.5/src/stackgen/generated/models/entity_ref.py +90 -0
- stackgen_sdk-0.1.5/src/stackgen/generated/models/execution_meta.py +102 -0
- stackgen_sdk-0.1.5/src/stackgen/generated/models/execution_structured_output.py +93 -0
- stackgen_sdk-0.1.5/src/stackgen/generated/models/prior_session_policy.py +110 -0
- stackgen_sdk-0.1.5/src/stackgen/generated/models/qualified_candidate.py +92 -0
- stackgen_sdk-0.1.5/src/stackgen/generated/models/trace_dag_response.py +119 -0
- stackgen_sdk-0.1.2/PKG-INFO +0 -59
- stackgen_sdk-0.1.2/README.md +0 -41
- stackgen_sdk-0.1.2/src/stackgen/aiden/namespace.py +0 -26
- stackgen_sdk-0.1.2/src/stackgen/aiden/webhook.py +0 -185
- {stackgen_sdk-0.1.2 → stackgen_sdk-0.1.5}/src/stackgen/_http.py +0 -0
- {stackgen_sdk-0.1.2 → stackgen_sdk-0.1.5}/src/stackgen/client.py +0 -0
- {stackgen_sdk-0.1.2 → stackgen_sdk-0.1.5}/src/stackgen/errors.py +0 -0
- {stackgen_sdk-0.1.2 → stackgen_sdk-0.1.5}/src/stackgen/generated/.openapi-generator/VERSION +0 -0
- {stackgen_sdk-0.1.2 → stackgen_sdk-0.1.5}/src/stackgen/generated/.openapi-generator-ignore +0 -0
- {stackgen_sdk-0.1.2 → stackgen_sdk-0.1.5}/src/stackgen/generated/api/__init__.py +0 -0
- {stackgen_sdk-0.1.2 → stackgen_sdk-0.1.5}/src/stackgen/generated/api/sre_api.py +0 -0
- {stackgen_sdk-0.1.2 → stackgen_sdk-0.1.5}/src/stackgen/generated/api_client.py +0 -0
- {stackgen_sdk-0.1.2 → stackgen_sdk-0.1.5}/src/stackgen/generated/api_response.py +0 -0
- {stackgen_sdk-0.1.2 → stackgen_sdk-0.1.5}/src/stackgen/generated/exceptions.py +0 -0
- {stackgen_sdk-0.1.2 → stackgen_sdk-0.1.5}/src/stackgen/generated/models/alert_analysis_status.py +0 -0
- {stackgen_sdk-0.1.2 → stackgen_sdk-0.1.5}/src/stackgen/generated/models/alert_attention.py +0 -0
- {stackgen_sdk-0.1.2 → stackgen_sdk-0.1.5}/src/stackgen/generated/models/alert_categorization_summary.py +0 -0
- {stackgen_sdk-0.1.2 → stackgen_sdk-0.1.5}/src/stackgen/generated/models/alert_role.py +0 -0
- {stackgen_sdk-0.1.2 → stackgen_sdk-0.1.5}/src/stackgen/generated/models/alert_sort_by.py +0 -0
- {stackgen_sdk-0.1.2 → stackgen_sdk-0.1.5}/src/stackgen/generated/models/alert_status.py +0 -0
- {stackgen_sdk-0.1.2 → stackgen_sdk-0.1.5}/src/stackgen/generated/models/alert_summary.py +0 -0
- {stackgen_sdk-0.1.2 → stackgen_sdk-0.1.5}/src/stackgen/generated/models/alert_sync_run_status.py +0 -0
- {stackgen_sdk-0.1.2 → stackgen_sdk-0.1.5}/src/stackgen/generated/models/alert_v1.py +0 -0
- {stackgen_sdk-0.1.2 → stackgen_sdk-0.1.5}/src/stackgen/generated/models/artifact_info.py +0 -0
- {stackgen_sdk-0.1.2 → stackgen_sdk-0.1.5}/src/stackgen/generated/models/error_response.py +0 -0
- {stackgen_sdk-0.1.2 → stackgen_sdk-0.1.5}/src/stackgen/generated/models/investigate_alert_request.py +0 -0
- {stackgen_sdk-0.1.2 → stackgen_sdk-0.1.5}/src/stackgen/generated/models/investigate_response.py +0 -0
- {stackgen_sdk-0.1.2 → stackgen_sdk-0.1.5}/src/stackgen/generated/models/investigation.py +0 -0
- {stackgen_sdk-0.1.2 → stackgen_sdk-0.1.5}/src/stackgen/generated/models/investigation_evidence.py +0 -0
- {stackgen_sdk-0.1.2 → stackgen_sdk-0.1.5}/src/stackgen/generated/models/investigation_evidence_kind.py +0 -0
- {stackgen_sdk-0.1.2 → stackgen_sdk-0.1.5}/src/stackgen/generated/models/investigation_evidence_source.py +0 -0
- {stackgen_sdk-0.1.2 → stackgen_sdk-0.1.5}/src/stackgen/generated/models/investigation_hypothesis.py +0 -0
- {stackgen_sdk-0.1.2 → stackgen_sdk-0.1.5}/src/stackgen/generated/models/investigation_plain_summary.py +0 -0
- {stackgen_sdk-0.1.2 → stackgen_sdk-0.1.5}/src/stackgen/generated/models/investigation_prior_incident.py +0 -0
- {stackgen_sdk-0.1.2 → stackgen_sdk-0.1.5}/src/stackgen/generated/models/investigation_recommended_next_step.py +0 -0
- {stackgen_sdk-0.1.2 → stackgen_sdk-0.1.5}/src/stackgen/generated/models/investigation_ref.py +0 -0
- {stackgen_sdk-0.1.2 → stackgen_sdk-0.1.5}/src/stackgen/generated/models/investigation_status.py +0 -0
- {stackgen_sdk-0.1.2 → stackgen_sdk-0.1.5}/src/stackgen/generated/models/investigation_structured_hypothesis_entry.py +0 -0
- {stackgen_sdk-0.1.2 → stackgen_sdk-0.1.5}/src/stackgen/generated/models/investigation_structured_limitation_entry.py +0 -0
- {stackgen_sdk-0.1.2 → stackgen_sdk-0.1.5}/src/stackgen/generated/models/investigation_structured_rca.py +0 -0
- {stackgen_sdk-0.1.2 → stackgen_sdk-0.1.5}/src/stackgen/generated/models/investigation_triage_metadata.py +0 -0
- {stackgen_sdk-0.1.2 → stackgen_sdk-0.1.5}/src/stackgen/generated/models/json_error.py +0 -0
- {stackgen_sdk-0.1.2 → stackgen_sdk-0.1.5}/src/stackgen/generated/models/list_alerts_response.py +0 -0
- {stackgen_sdk-0.1.2 → stackgen_sdk-0.1.5}/src/stackgen/generated/models/list_investigations_response.py +0 -0
- {stackgen_sdk-0.1.2 → stackgen_sdk-0.1.5}/src/stackgen/generated/models/pagination.py +0 -0
- {stackgen_sdk-0.1.2 → stackgen_sdk-0.1.5}/src/stackgen/generated/models/schedule_run_status.py +0 -0
- {stackgen_sdk-0.1.2 → stackgen_sdk-0.1.5}/src/stackgen/generated/models/schedule_target_type.py +0 -0
- {stackgen_sdk-0.1.2 → stackgen_sdk-0.1.5}/src/stackgen/generated/models/session.py +0 -0
- {stackgen_sdk-0.1.2 → stackgen_sdk-0.1.5}/src/stackgen/generated/models/session_responder_kind.py +0 -0
- {stackgen_sdk-0.1.2 → stackgen_sdk-0.1.5}/src/stackgen/generated/models/signal_severity.py +0 -0
- {stackgen_sdk-0.1.2 → stackgen_sdk-0.1.5}/src/stackgen/generated/models/sync_response.py +0 -0
- {stackgen_sdk-0.1.2 → stackgen_sdk-0.1.5}/src/stackgen/generated/models/trigger_webhook202_response.py +0 -0
- {stackgen_sdk-0.1.2 → stackgen_sdk-0.1.5}/src/stackgen/generated/models/webhook_run.py +0 -0
- {stackgen_sdk-0.1.2 → stackgen_sdk-0.1.5}/src/stackgen/generated/models/webhook_run_detail.py +0 -0
- {stackgen_sdk-0.1.2 → stackgen_sdk-0.1.5}/src/stackgen/generated/models/webhook_run_list_response.py +0 -0
- {stackgen_sdk-0.1.2 → stackgen_sdk-0.1.5}/src/stackgen/generated/rest.py +0 -0
- {stackgen_sdk-0.1.2 → stackgen_sdk-0.1.5}/src/stackgen/sre/__init__.py +0 -0
- {stackgen_sdk-0.1.2 → stackgen_sdk-0.1.5}/src/stackgen/sre/namespace.py +0 -0
- {stackgen_sdk-0.1.2 → stackgen_sdk-0.1.5}/src/stackgen/vault/__init__.py +0 -0
- {stackgen_sdk-0.1.2 → stackgen_sdk-0.1.5}/src/stackgen/vault/namespace.py +0 -0
|
@@ -32,5 +32,11 @@ coverage.out
|
|
|
32
32
|
.env
|
|
33
33
|
.env.*
|
|
34
34
|
|
|
35
|
+
# Jekyll (only if native Ruby was used by mistake)
|
|
36
|
+
docs/_site/
|
|
37
|
+
docs/.jekyll-cache/
|
|
38
|
+
docs/.bundle/
|
|
39
|
+
docs/vendor/
|
|
40
|
+
|
|
35
41
|
# hatch-vcs generated version module (written at build time)
|
|
36
42
|
clients/python/src/stackgen/_version.py
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: stackgen-sdk
|
|
3
|
+
Version: 0.1.5
|
|
4
|
+
Summary: StackGen SDK — Aiden, SRE, and Vault from one client
|
|
5
|
+
Project-URL: Homepage, https://appcd-dev.github.io/stackgen-sdk/
|
|
6
|
+
Project-URL: Documentation, https://appcd-dev.github.io/stackgen-sdk/
|
|
7
|
+
Project-URL: Repository, https://github.com/appcd-dev/stackgen-sdk
|
|
8
|
+
Project-URL: Bug Tracker, https://github.com/appcd-dev/stackgen-sdk/issues
|
|
9
|
+
Author: StackGen
|
|
10
|
+
License-Expression: Apache-2.0
|
|
11
|
+
Keywords: aiden,openapi,sre,stackgen
|
|
12
|
+
Requires-Python: >=3.10
|
|
13
|
+
Requires-Dist: pydantic>=2.0
|
|
14
|
+
Requires-Dist: python-dateutil>=2.8
|
|
15
|
+
Requires-Dist: typing-extensions>=4.0
|
|
16
|
+
Requires-Dist: urllib3>=2.0
|
|
17
|
+
Provides-Extra: dev
|
|
18
|
+
Requires-Dist: build>=1.2; extra == 'dev'
|
|
19
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
|
|
22
|
+
# StackGen Python SDK
|
|
23
|
+
|
|
24
|
+
**Documentation:** [https://appcd-dev.github.io/stackgen-sdk/](https://appcd-dev.github.io/stackgen-sdk/) — Aiden guide, code samples, FAQ, and CI integration.
|
|
25
|
+
|
|
26
|
+
Install:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
pip install stackgen-sdk
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Import package name is `stackgen`. Construct the client with an explicit config:
|
|
33
|
+
|
|
34
|
+
```python
|
|
35
|
+
from pathlib import Path
|
|
36
|
+
from stackgen import StackgenClient, StackgenConfig
|
|
37
|
+
|
|
38
|
+
client = StackgenClient(
|
|
39
|
+
StackgenConfig(
|
|
40
|
+
base_url="https://azure-eu.cloud.stackgen.com",
|
|
41
|
+
api_token="stackgen_…",
|
|
42
|
+
org_id="<project-uuid>",
|
|
43
|
+
webhook_token="sg_aios_…", # webhook journeys only
|
|
44
|
+
)
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
result = client.aiden.run_webhook_and_download_report(
|
|
48
|
+
Path("alert.json").read_text(encoding="utf-8")
|
|
49
|
+
)
|
|
50
|
+
print(result.session_id, result.output_path)
|
|
51
|
+
|
|
52
|
+
ask = client.aiden.run_ask_and_download_artifact(
|
|
53
|
+
message="Run triage for checkout CPU alert",
|
|
54
|
+
entity_refs=[{"name": "observe-uber-triage", "type": "workflow"}],
|
|
55
|
+
)
|
|
56
|
+
print(ask.trace_id, ask.output_path)
|
|
57
|
+
|
|
58
|
+
alerts = client.sre.list_alerts()
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Default environment variables
|
|
62
|
+
|
|
63
|
+
`StackgenConfig.from_env()` reads **only** these (optional convenience):
|
|
64
|
+
|
|
65
|
+
| Variable | Purpose |
|
|
66
|
+
|----------|---------|
|
|
67
|
+
| `STACKGEN_URL` | Mothership URL (no path suffix) |
|
|
68
|
+
| `STACKGEN_TOKEN` | API token (`stackgen_…`) |
|
|
69
|
+
| `STACKGEN_PROJECT` | Project / org UUID (`orgId`) |
|
|
70
|
+
|
|
71
|
+
Pass `webhook_token`, `webhook_id`, timeouts, and artifact paths as `StackgenConfig` fields — not via env.
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
from dataclasses import replace
|
|
75
|
+
from stackgen import StackgenClient, StackgenConfig
|
|
76
|
+
|
|
77
|
+
client = StackgenClient(
|
|
78
|
+
replace(StackgenConfig.from_env(), webhook_token="sg_aios_…")
|
|
79
|
+
)
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Namespaces
|
|
83
|
+
|
|
84
|
+
- `client.aiden` — webhooks, Ask Guild, sessions, artifact download
|
|
85
|
+
- `client.sre` — alerts and investigations
|
|
86
|
+
- `client.vault` — secrets (not enabled in this release)
|
|
87
|
+
|
|
88
|
+
Prefer `StackgenClient` over `stackgen.generated`.
|
|
89
|
+
|
|
90
|
+
Pin versions in CI: `pip install stackgen-sdk==0.1.5`.
|
|
91
|
+
|
|
92
|
+
**Documentation:** [https://appcd-dev.github.io/stackgen-sdk/](https://appcd-dev.github.io/stackgen-sdk/) (Aiden guide, [FAQ](https://appcd-dev.github.io/stackgen-sdk/faq/), [code samples](https://appcd-dev.github.io/stackgen-sdk/code-samples/))
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# StackGen Python SDK
|
|
2
|
+
|
|
3
|
+
**Documentation:** [https://appcd-dev.github.io/stackgen-sdk/](https://appcd-dev.github.io/stackgen-sdk/) — Aiden guide, code samples, FAQ, and CI integration.
|
|
4
|
+
|
|
5
|
+
Install:
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install stackgen-sdk
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Import package name is `stackgen`. Construct the client with an explicit config:
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from stackgen import StackgenClient, StackgenConfig
|
|
16
|
+
|
|
17
|
+
client = StackgenClient(
|
|
18
|
+
StackgenConfig(
|
|
19
|
+
base_url="https://azure-eu.cloud.stackgen.com",
|
|
20
|
+
api_token="stackgen_…",
|
|
21
|
+
org_id="<project-uuid>",
|
|
22
|
+
webhook_token="sg_aios_…", # webhook journeys only
|
|
23
|
+
)
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
result = client.aiden.run_webhook_and_download_report(
|
|
27
|
+
Path("alert.json").read_text(encoding="utf-8")
|
|
28
|
+
)
|
|
29
|
+
print(result.session_id, result.output_path)
|
|
30
|
+
|
|
31
|
+
ask = client.aiden.run_ask_and_download_artifact(
|
|
32
|
+
message="Run triage for checkout CPU alert",
|
|
33
|
+
entity_refs=[{"name": "observe-uber-triage", "type": "workflow"}],
|
|
34
|
+
)
|
|
35
|
+
print(ask.trace_id, ask.output_path)
|
|
36
|
+
|
|
37
|
+
alerts = client.sre.list_alerts()
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Default environment variables
|
|
41
|
+
|
|
42
|
+
`StackgenConfig.from_env()` reads **only** these (optional convenience):
|
|
43
|
+
|
|
44
|
+
| Variable | Purpose |
|
|
45
|
+
|----------|---------|
|
|
46
|
+
| `STACKGEN_URL` | Mothership URL (no path suffix) |
|
|
47
|
+
| `STACKGEN_TOKEN` | API token (`stackgen_…`) |
|
|
48
|
+
| `STACKGEN_PROJECT` | Project / org UUID (`orgId`) |
|
|
49
|
+
|
|
50
|
+
Pass `webhook_token`, `webhook_id`, timeouts, and artifact paths as `StackgenConfig` fields — not via env.
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
from dataclasses import replace
|
|
54
|
+
from stackgen import StackgenClient, StackgenConfig
|
|
55
|
+
|
|
56
|
+
client = StackgenClient(
|
|
57
|
+
replace(StackgenConfig.from_env(), webhook_token="sg_aios_…")
|
|
58
|
+
)
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Namespaces
|
|
62
|
+
|
|
63
|
+
- `client.aiden` — webhooks, Ask Guild, sessions, artifact download
|
|
64
|
+
- `client.sre` — alerts and investigations
|
|
65
|
+
- `client.vault` — secrets (not enabled in this release)
|
|
66
|
+
|
|
67
|
+
Prefer `StackgenClient` over `stackgen.generated`.
|
|
68
|
+
|
|
69
|
+
Pin versions in CI: `pip install stackgen-sdk==0.1.5`.
|
|
70
|
+
|
|
71
|
+
**Documentation:** [https://appcd-dev.github.io/stackgen-sdk/](https://appcd-dev.github.io/stackgen-sdk/) (Aiden guide, [FAQ](https://appcd-dev.github.io/stackgen-sdk/faq/), [code samples](https://appcd-dev.github.io/stackgen-sdk/code-samples/))
|
|
@@ -19,7 +19,10 @@ dependencies = [
|
|
|
19
19
|
]
|
|
20
20
|
|
|
21
21
|
[project.urls]
|
|
22
|
-
Homepage = "https://
|
|
22
|
+
Homepage = "https://appcd-dev.github.io/stackgen-sdk/"
|
|
23
|
+
Documentation = "https://appcd-dev.github.io/stackgen-sdk/"
|
|
24
|
+
Repository = "https://github.com/appcd-dev/stackgen-sdk"
|
|
25
|
+
"Bug Tracker" = "https://github.com/appcd-dev/stackgen-sdk/issues"
|
|
23
26
|
|
|
24
27
|
[project.optional-dependencies]
|
|
25
28
|
dev = ["pytest>=8.0", "build>=1.2"]
|
|
@@ -4,8 +4,13 @@ Public entrypoint::
|
|
|
4
4
|
|
|
5
5
|
from stackgen import StackgenClient, StackgenConfig
|
|
6
6
|
|
|
7
|
-
client = StackgenClient
|
|
8
|
-
|
|
7
|
+
client = StackgenClient(
|
|
8
|
+
StackgenConfig(
|
|
9
|
+
base_url="https://azure-eu.cloud.stackgen.com",
|
|
10
|
+
api_token="stackgen_…",
|
|
11
|
+
org_id="<project-uuid>",
|
|
12
|
+
)
|
|
13
|
+
)
|
|
9
14
|
"""
|
|
10
15
|
|
|
11
16
|
from __future__ import annotations
|
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '0.1.
|
|
22
|
-
__version_tuple__ = version_tuple = (0, 1,
|
|
21
|
+
__version__ = version = '0.1.5'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 1, 5)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
"""Aiden (platform) namespace — webhooks, sessions, artifacts."""
|
|
1
|
+
"""Aiden (platform) namespace — webhooks, sessions, artifacts, Ask Guild."""
|
|
2
2
|
|
|
3
|
+
from stackgen.aiden.ask import AskArtifactResult
|
|
3
4
|
from stackgen.aiden.namespace import AidenNamespace
|
|
4
5
|
from stackgen.aiden.webhook import WebhookReportResult
|
|
5
6
|
|
|
6
7
|
__all__ = [
|
|
7
8
|
"AidenNamespace",
|
|
9
|
+
"AskArtifactResult",
|
|
8
10
|
"WebhookReportResult",
|
|
9
11
|
]
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
"""Aiden Ask → execution poll → artifact download journey."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import time
|
|
7
|
+
from dataclasses import dataclass
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
from stackgen import _http
|
|
12
|
+
from stackgen.aiden import sessions as session_helpers
|
|
13
|
+
from stackgen.config import StackgenConfig
|
|
14
|
+
from stackgen.errors import StackgenError, TimeoutError
|
|
15
|
+
|
|
16
|
+
_TERMINAL_STATUSES = frozenset({"completed", "error"})
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass(frozen=True)
|
|
20
|
+
class AskArtifactResult:
|
|
21
|
+
"""Result of Ask start → wait → download session artifact."""
|
|
22
|
+
|
|
23
|
+
trace_id: str
|
|
24
|
+
run_id: str
|
|
25
|
+
session_id: str
|
|
26
|
+
artifacts: tuple[dict[str, Any], ...]
|
|
27
|
+
output_path: Path
|
|
28
|
+
candidates: tuple[dict[str, Any], ...] = ()
|
|
29
|
+
plan: str = ""
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class AskArtifactJourney:
|
|
33
|
+
"""Start Ask Guild, poll execution trace, wait for artifact, download it.
|
|
34
|
+
|
|
35
|
+
Uses ``api_token`` and ``org_id`` only (no webhook token).
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
def __init__(self, config: StackgenConfig) -> None:
|
|
39
|
+
self._config = config
|
|
40
|
+
|
|
41
|
+
def run(
|
|
42
|
+
self,
|
|
43
|
+
message: str,
|
|
44
|
+
*,
|
|
45
|
+
entity_refs: list[dict[str, str]] | None = None,
|
|
46
|
+
artifact_name: str | None = None,
|
|
47
|
+
output_path: str | None = None,
|
|
48
|
+
inputs: dict[str, str] | None = None,
|
|
49
|
+
session_id: str | None = None,
|
|
50
|
+
source_app: str | None = None,
|
|
51
|
+
source_metadata: dict[str, str] | None = None,
|
|
52
|
+
) -> AskArtifactResult:
|
|
53
|
+
cfg = self._config
|
|
54
|
+
artifact = artifact_name or cfg.artifact_name
|
|
55
|
+
dest = output_path or cfg.output_path
|
|
56
|
+
|
|
57
|
+
start = self.start_ask(
|
|
58
|
+
message,
|
|
59
|
+
entity_refs=entity_refs,
|
|
60
|
+
inputs=inputs,
|
|
61
|
+
session_id=session_id,
|
|
62
|
+
source_app=source_app,
|
|
63
|
+
source_metadata=source_metadata,
|
|
64
|
+
)
|
|
65
|
+
trace_id = _http.text(start.get("trace_id"))
|
|
66
|
+
if not trace_id:
|
|
67
|
+
raise StackgenError(
|
|
68
|
+
"startAskGuild did not return trace_id; "
|
|
69
|
+
f"body={json.dumps(start)}"
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
run_id = _http.text(start.get("run_id"))
|
|
73
|
+
resolved_session = _http.text(start.get("session_id")) or _http.text(session_id)
|
|
74
|
+
plan = _http.text(start.get("plan"))
|
|
75
|
+
candidates = tuple(start.get("candidates") or ())
|
|
76
|
+
|
|
77
|
+
trace = self.wait_for_execution(trace_id)
|
|
78
|
+
if not resolved_session:
|
|
79
|
+
resolved_session = _http.text(trace.get("session_id"))
|
|
80
|
+
execution = trace.get("execution") or {}
|
|
81
|
+
if not resolved_session:
|
|
82
|
+
raise StackgenError(
|
|
83
|
+
f"execution {trace_id} settled without session_id; "
|
|
84
|
+
"cannot download artifacts"
|
|
85
|
+
)
|
|
86
|
+
if not run_id:
|
|
87
|
+
run_id = _http.text(execution.get("run_id"))
|
|
88
|
+
|
|
89
|
+
artifacts = session_helpers.wait_for_artifact(cfg, resolved_session, artifact)
|
|
90
|
+
path = session_helpers.download_artifact(cfg, resolved_session, artifact, dest)
|
|
91
|
+
return AskArtifactResult(
|
|
92
|
+
trace_id=trace_id,
|
|
93
|
+
run_id=run_id,
|
|
94
|
+
session_id=resolved_session,
|
|
95
|
+
artifacts=tuple(artifacts),
|
|
96
|
+
output_path=path,
|
|
97
|
+
candidates=candidates,
|
|
98
|
+
plan=plan,
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
def start_ask(
|
|
102
|
+
self,
|
|
103
|
+
message: str,
|
|
104
|
+
*,
|
|
105
|
+
entity_refs: list[dict[str, str]] | None = None,
|
|
106
|
+
inputs: dict[str, str] | None = None,
|
|
107
|
+
session_id: str | None = None,
|
|
108
|
+
source_app: str | None = None,
|
|
109
|
+
source_metadata: dict[str, str] | None = None,
|
|
110
|
+
) -> dict[str, Any]:
|
|
111
|
+
"""POST /guild/api/v1/guild/ask/start."""
|
|
112
|
+
body: dict[str, Any] = {"message": message}
|
|
113
|
+
if entity_refs:
|
|
114
|
+
body["entity_refs"] = entity_refs
|
|
115
|
+
if inputs:
|
|
116
|
+
body["inputs"] = inputs
|
|
117
|
+
if session_id:
|
|
118
|
+
body["session_id"] = session_id
|
|
119
|
+
if source_app:
|
|
120
|
+
body["source_app"] = source_app
|
|
121
|
+
if source_metadata:
|
|
122
|
+
body["source_metadata"] = source_metadata
|
|
123
|
+
|
|
124
|
+
url = (
|
|
125
|
+
f"{self._config.aiden_url()}/api/v1/guild/ask/start"
|
|
126
|
+
f"{_http.query(self._config.org_id)}"
|
|
127
|
+
)
|
|
128
|
+
data = _http.request(
|
|
129
|
+
url,
|
|
130
|
+
self._config.api_token,
|
|
131
|
+
method="POST",
|
|
132
|
+
body=json.dumps(body).encode("utf-8"),
|
|
133
|
+
content_type="application/json",
|
|
134
|
+
)
|
|
135
|
+
if not isinstance(data, dict):
|
|
136
|
+
raise StackgenError(f"unexpected startAskGuild payload: {data!r}")
|
|
137
|
+
|
|
138
|
+
inputs_required = data.get("inputs_required") or []
|
|
139
|
+
if inputs_required:
|
|
140
|
+
raise StackgenError(
|
|
141
|
+
"Ask requires additional inputs before execution can start: "
|
|
142
|
+
f"{inputs_required}"
|
|
143
|
+
)
|
|
144
|
+
return data
|
|
145
|
+
|
|
146
|
+
def wait_for_execution(self, trace_id: str) -> dict[str, Any]:
|
|
147
|
+
"""Poll GET /guild/api/v1/executions/{traceId} until settled."""
|
|
148
|
+
cfg = self._config
|
|
149
|
+
deadline = time.monotonic() + cfg.timeout_seconds
|
|
150
|
+
url = (
|
|
151
|
+
f"{cfg.aiden_url()}/api/v1/executions/{trace_id}"
|
|
152
|
+
f"{_http.query(cfg.org_id)}"
|
|
153
|
+
)
|
|
154
|
+
while time.monotonic() < deadline:
|
|
155
|
+
data = _http.request(url, cfg.api_token)
|
|
156
|
+
if not isinstance(data, dict):
|
|
157
|
+
raise StackgenError(f"unexpected getExecutionTrace payload: {data!r}")
|
|
158
|
+
execution = data.get("execution") or {}
|
|
159
|
+
status = _http.text(execution.get("status")).casefold()
|
|
160
|
+
trace_settled = execution.get("trace_settled") is True
|
|
161
|
+
if trace_settled and status in _TERMINAL_STATUSES:
|
|
162
|
+
if status == "error":
|
|
163
|
+
raise StackgenError(
|
|
164
|
+
f"execution {trace_id} ended with status error"
|
|
165
|
+
)
|
|
166
|
+
return data
|
|
167
|
+
time.sleep(cfg.poll_interval_seconds)
|
|
168
|
+
raise TimeoutError("execution", cfg.timeout_seconds)
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"""Aiden namespace — public methods for platform APIs."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from stackgen.aiden.ask import AskArtifactJourney, AskArtifactResult
|
|
8
|
+
from stackgen.aiden.webhook import WebhookReportJourney, WebhookReportResult
|
|
9
|
+
from stackgen.config import StackgenConfig
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class AidenNamespace:
|
|
13
|
+
"""Aiden platform APIs (sessions, webhooks, artifacts, Ask Guild)."""
|
|
14
|
+
|
|
15
|
+
def __init__(self, config: StackgenConfig) -> None:
|
|
16
|
+
self._config = config
|
|
17
|
+
self._webhook = WebhookReportJourney(config)
|
|
18
|
+
self._ask = AskArtifactJourney(config)
|
|
19
|
+
|
|
20
|
+
def run_webhook_and_download_report(
|
|
21
|
+
self,
|
|
22
|
+
payload: str,
|
|
23
|
+
*,
|
|
24
|
+
artifact_name: str | None = None,
|
|
25
|
+
output_path: str | None = None,
|
|
26
|
+
) -> WebhookReportResult:
|
|
27
|
+
"""Trigger a webhook, wait for the session artifact, download it."""
|
|
28
|
+
return self._webhook.run(
|
|
29
|
+
payload, artifact_name=artifact_name, output_path=output_path
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
def run_ask_and_download_artifact(
|
|
33
|
+
self,
|
|
34
|
+
message: str,
|
|
35
|
+
*,
|
|
36
|
+
entity_refs: list[dict[str, str]] | None = None,
|
|
37
|
+
artifact_name: str | None = None,
|
|
38
|
+
output_path: str | None = None,
|
|
39
|
+
inputs: dict[str, str] | None = None,
|
|
40
|
+
session_id: str | None = None,
|
|
41
|
+
source_app: str | None = None,
|
|
42
|
+
source_metadata: dict[str, str] | None = None,
|
|
43
|
+
) -> AskArtifactResult:
|
|
44
|
+
"""Start Ask Guild, poll execution, wait for artifact, download it."""
|
|
45
|
+
return self._ask.run(
|
|
46
|
+
message,
|
|
47
|
+
entity_refs=entity_refs,
|
|
48
|
+
artifact_name=artifact_name,
|
|
49
|
+
output_path=output_path,
|
|
50
|
+
inputs=inputs,
|
|
51
|
+
session_id=session_id,
|
|
52
|
+
source_app=source_app,
|
|
53
|
+
source_metadata=source_metadata,
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
def start_ask(
|
|
57
|
+
self,
|
|
58
|
+
message: str,
|
|
59
|
+
*,
|
|
60
|
+
entity_refs: list[dict[str, str]] | None = None,
|
|
61
|
+
inputs: dict[str, str] | None = None,
|
|
62
|
+
session_id: str | None = None,
|
|
63
|
+
source_app: str | None = None,
|
|
64
|
+
source_metadata: dict[str, str] | None = None,
|
|
65
|
+
) -> dict[str, Any]:
|
|
66
|
+
"""POST /guild/api/v1/guild/ask/start (advanced callers)."""
|
|
67
|
+
return self._ask.start_ask(
|
|
68
|
+
message,
|
|
69
|
+
entity_refs=entity_refs,
|
|
70
|
+
inputs=inputs,
|
|
71
|
+
session_id=session_id,
|
|
72
|
+
source_app=source_app,
|
|
73
|
+
source_metadata=source_metadata,
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
def wait_for_execution(self, trace_id: str) -> dict[str, Any]:
|
|
77
|
+
"""Poll GET /guild/api/v1/executions/{traceId} until settled."""
|
|
78
|
+
return self._ask.wait_for_execution(trace_id)
|
|
@@ -17,7 +17,7 @@ def pick_run(
|
|
|
17
17
|
Preference order:
|
|
18
18
|
1. Exact match on ``id`` / ``run_id`` to ``dispatch_run_id``
|
|
19
19
|
2. First run whose id is not in ``known_run_ids`` (new since pre-trigger snapshot)
|
|
20
|
-
3. If no known ids and runs exist, the first run (best-effort
|
|
20
|
+
3. If no known ids and runs exist, the first run (best-effort fallback)
|
|
21
21
|
"""
|
|
22
22
|
if dispatch_run_id:
|
|
23
23
|
for item in runs:
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""Shared session artifact wait/download helpers."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import time
|
|
6
|
+
import urllib.parse
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
from stackgen import _http
|
|
11
|
+
from stackgen.config import StackgenConfig
|
|
12
|
+
from stackgen.errors import TimeoutError
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def wait_for_artifact(
|
|
16
|
+
config: StackgenConfig, session_id: str, artifact_name: str
|
|
17
|
+
) -> list[dict[str, Any]]:
|
|
18
|
+
"""Poll session artifacts until ``artifact_name`` appears (case-insensitive)."""
|
|
19
|
+
deadline = time.monotonic() + config.timeout_seconds
|
|
20
|
+
url = (
|
|
21
|
+
f"{config.aiden_url()}/api/v1/sessions/{session_id}/artifacts"
|
|
22
|
+
f"{_http.query(config.org_id)}"
|
|
23
|
+
)
|
|
24
|
+
want = artifact_name.casefold()
|
|
25
|
+
artifacts: list[dict[str, Any]] = []
|
|
26
|
+
while time.monotonic() < deadline:
|
|
27
|
+
artifacts = _http.as_list(_http.request(url, config.api_token))
|
|
28
|
+
if any(_http.text(item.get("name")).casefold() == want for item in artifacts):
|
|
29
|
+
return artifacts
|
|
30
|
+
time.sleep(config.poll_interval_seconds)
|
|
31
|
+
raise TimeoutError(artifact_name, config.timeout_seconds)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def download_artifact(
|
|
35
|
+
config: StackgenConfig, session_id: str, artifact_name: str, output_path: str
|
|
36
|
+
) -> Path:
|
|
37
|
+
"""Download a session artifact to ``output_path``."""
|
|
38
|
+
encoded = urllib.parse.quote(artifact_name, safe="")
|
|
39
|
+
url = (
|
|
40
|
+
f"{config.aiden_url()}/api/v1/sessions/{session_id}/artifacts/"
|
|
41
|
+
f"{encoded}/download{_http.query(config.org_id)}"
|
|
42
|
+
)
|
|
43
|
+
raw = _http.request(url, config.api_token, expect_json=False)
|
|
44
|
+
dest = Path(output_path)
|
|
45
|
+
dest.write_bytes(raw)
|
|
46
|
+
return dest
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"""Aiden webhook → session-report journey."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
from stackgen import _http
|
|
11
|
+
from stackgen.aiden import sessions as session_helpers
|
|
12
|
+
from stackgen.config import StackgenConfig
|
|
13
|
+
from stackgen.errors import StackgenError
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass(frozen=True)
|
|
17
|
+
class WebhookReportResult:
|
|
18
|
+
"""Result of trigger → wait → download session-report."""
|
|
19
|
+
|
|
20
|
+
webhook_id: str
|
|
21
|
+
invocation_id: str
|
|
22
|
+
session_id: str
|
|
23
|
+
artifacts: tuple[dict[str, Any], ...]
|
|
24
|
+
output_path: Path
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class WebhookReportJourney:
|
|
28
|
+
"""Trigger a webhook, wait for the session artifact, and download it.
|
|
29
|
+
|
|
30
|
+
Uses ``webhook_token`` for POST trigger and ``api_token`` for poll/download.
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
def __init__(self, config: StackgenConfig) -> None:
|
|
34
|
+
self._config = config
|
|
35
|
+
|
|
36
|
+
def run(
|
|
37
|
+
self,
|
|
38
|
+
payload: str,
|
|
39
|
+
*,
|
|
40
|
+
artifact_name: str | None = None,
|
|
41
|
+
output_path: str | None = None,
|
|
42
|
+
) -> WebhookReportResult:
|
|
43
|
+
cfg = self._config
|
|
44
|
+
if not cfg.webhook_token:
|
|
45
|
+
raise ValueError("webhook_token is required for run_webhook_and_download_report")
|
|
46
|
+
|
|
47
|
+
artifact = artifact_name or cfg.artifact_name
|
|
48
|
+
dest = output_path or cfg.output_path
|
|
49
|
+
|
|
50
|
+
trigger = self._trigger(payload)
|
|
51
|
+
webhook_id = _http.text(trigger.get("webhook_id")) or _http.text(cfg.webhook_id)
|
|
52
|
+
invocation_id = _http.text(trigger.get("invocation_id"))
|
|
53
|
+
session_id = _http.text(trigger.get("session_id"))
|
|
54
|
+
|
|
55
|
+
if not session_id:
|
|
56
|
+
raise StackgenError(
|
|
57
|
+
f"trigger response missing session_id; body={json.dumps(trigger)}"
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
artifacts = session_helpers.wait_for_artifact(cfg, session_id, artifact)
|
|
61
|
+
path = session_helpers.download_artifact(cfg, session_id, artifact, dest)
|
|
62
|
+
return WebhookReportResult(
|
|
63
|
+
webhook_id=webhook_id,
|
|
64
|
+
invocation_id=invocation_id,
|
|
65
|
+
session_id=session_id,
|
|
66
|
+
artifacts=tuple(artifacts),
|
|
67
|
+
output_path=path,
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
def _trigger(self, payload: str) -> dict[str, Any]:
|
|
71
|
+
url = f"{self._config.aiden_url()}/api/v1/webhooks/trigger{_http.query(self._config.org_id)}"
|
|
72
|
+
data = _http.request(
|
|
73
|
+
url,
|
|
74
|
+
self._config.webhook_token,
|
|
75
|
+
method="POST",
|
|
76
|
+
body=payload.encode("utf-8"),
|
|
77
|
+
content_type="text/plain",
|
|
78
|
+
)
|
|
79
|
+
if not isinstance(data, dict):
|
|
80
|
+
raise StackgenError(f"unexpected trigger payload: {data!r}")
|
|
81
|
+
return data
|
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import os
|
|
5
6
|
import sys
|
|
7
|
+
from dataclasses import replace
|
|
6
8
|
from pathlib import Path
|
|
7
9
|
|
|
8
10
|
from stackgen.client import StackgenClient
|
|
@@ -20,7 +22,13 @@ def main(argv: list[str] | None = None) -> None:
|
|
|
20
22
|
sys.exit(1)
|
|
21
23
|
payload = path.read_text(encoding="utf-8")
|
|
22
24
|
try:
|
|
23
|
-
|
|
25
|
+
# CLI-only: webhook fields may be supplied alongside the three default env vars.
|
|
26
|
+
cfg = replace(
|
|
27
|
+
StackgenConfig.from_env(),
|
|
28
|
+
webhook_token=os.environ.get("WEBHOOK_TOKEN", "").strip(),
|
|
29
|
+
webhook_id=os.environ.get("WEBHOOK_ID", "").strip(),
|
|
30
|
+
)
|
|
31
|
+
client = StackgenClient(cfg)
|
|
24
32
|
result = client.aiden.run_webhook_and_download_report(payload)
|
|
25
33
|
except (StackgenError, ValueError) as err:
|
|
26
34
|
print(f"error: {err}", file=sys.stderr)
|