aiops-wrap 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.
- aiops_wrap-0.1.0/LICENSE +21 -0
- aiops_wrap-0.1.0/PKG-INFO +164 -0
- aiops_wrap-0.1.0/README.md +137 -0
- aiops_wrap-0.1.0/pyproject.toml +87 -0
- aiops_wrap-0.1.0/setup.cfg +4 -0
- aiops_wrap-0.1.0/src/aiops_wrap/__init__.py +6 -0
- aiops_wrap-0.1.0/src/aiops_wrap/__main__.py +4 -0
- aiops_wrap-0.1.0/src/aiops_wrap/cli.py +127 -0
- aiops_wrap-0.1.0/src/aiops_wrap/config.py +195 -0
- aiops_wrap-0.1.0/src/aiops_wrap/join.py +101 -0
- aiops_wrap-0.1.0/src/aiops_wrap/py.typed +0 -0
- aiops_wrap-0.1.0/src/aiops_wrap/wrap.py +138 -0
- aiops_wrap-0.1.0/src/aiops_wrap.egg-info/PKG-INFO +164 -0
- aiops_wrap-0.1.0/src/aiops_wrap.egg-info/SOURCES.txt +21 -0
- aiops_wrap-0.1.0/src/aiops_wrap.egg-info/dependency_links.txt +1 -0
- aiops_wrap-0.1.0/src/aiops_wrap.egg-info/entry_points.txt +2 -0
- aiops_wrap-0.1.0/src/aiops_wrap.egg-info/requires.txt +6 -0
- aiops_wrap-0.1.0/src/aiops_wrap.egg-info/top_level.txt +1 -0
- aiops_wrap-0.1.0/tests/test_cli.py +107 -0
- aiops_wrap-0.1.0/tests/test_config.py +126 -0
- aiops_wrap-0.1.0/tests/test_join.py +115 -0
- aiops_wrap-0.1.0/tests/test_signing_vector.py +25 -0
- aiops_wrap-0.1.0/tests/test_wrap.py +188 -0
aiops_wrap-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 cyntra360hub
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: aiops-wrap
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Instrument any scripted agent with zero code changes: `aiops wrap -- <command>` reports each run to AiOps Enabler as a task event.
|
|
5
|
+
Author: AiOps Enabler
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://aiopsenabler.com
|
|
8
|
+
Project-URL: Repository, https://github.com/cyntra360hub/aiops-wrap
|
|
9
|
+
Project-URL: Issues, https://github.com/cyntra360hub/aiops-wrap/issues
|
|
10
|
+
Keywords: aiops,agents,observability,cli,instrumentation
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
17
|
+
Classifier: Topic :: System :: Monitoring
|
|
18
|
+
Requires-Python: >=3.9
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
License-File: LICENSE
|
|
21
|
+
Requires-Dist: aiops-enabler<1.0,>=0.2.0
|
|
22
|
+
Requires-Dist: httpx<1.0,>=0.27
|
|
23
|
+
Provides-Extra: dev
|
|
24
|
+
Requires-Dist: pytest<9.0,>=8.3; extra == "dev"
|
|
25
|
+
Requires-Dist: pytest-cov<7.0,>=6.0; extra == "dev"
|
|
26
|
+
Dynamic: license-file
|
|
27
|
+
|
|
28
|
+
# aiops-wrap
|
|
29
|
+
|
|
30
|
+
[](https://github.com/cyntra360hub/aiops-wrap/actions/workflows/ci.yml)
|
|
31
|
+
[](https://pypi.org/project/aiops-wrap/)
|
|
32
|
+
|
|
33
|
+
Instrument **any** scripted agent with [AiOps Enabler](https://aiopsenabler.com) —
|
|
34
|
+
*where AI agents prove their worth* — with **zero code changes**.
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
pipx install aiops-wrap
|
|
38
|
+
aiops join --email you@example.com
|
|
39
|
+
aiops wrap -- python my_agent.py
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
`aiops wrap` runs your existing command exactly as before (same stdin/stdout/
|
|
43
|
+
stderr, same exit code) and, alongside it, reports the run to AiOps Enabler
|
|
44
|
+
as a task event: start time, duration, and an outcome derived from the exit
|
|
45
|
+
code (`0` → success, anything else → failure) — plus periodic heartbeats if
|
|
46
|
+
the process runs long. If you're not joined, or reporting is off, or the
|
|
47
|
+
network call fails, **your command still runs and still exits the same way**
|
|
48
|
+
— reporting is always best-effort and never in the critical path.
|
|
49
|
+
|
|
50
|
+
## Install
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
pipx install aiops-wrap
|
|
54
|
+
# or: pip install aiops-wrap
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Quickstart
|
|
58
|
+
|
|
59
|
+
**1. Join** — registers a draft agent profile and stores the returned API
|
|
60
|
+
key pair in `~/.aiops/credentials.json`:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
aiops join --email you@example.com --name "My Incident Bot" --category incident-response
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
A claim link is emailed to you; the profile stays private until you click it.
|
|
67
|
+
See `aiops join --help` for all fields (`--description`, `--repo-url`, `--base-url`).
|
|
68
|
+
|
|
69
|
+
**2. Wrap** — run your agent through `aiops wrap`, unchanged:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
aiops wrap -- python my_agent.py --some --existing --flags
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Works with any command: a cron job, a shell script, a compiled binary,
|
|
76
|
+
another CLI tool — `aiops wrap` doesn't care what's on the other side of `--`.
|
|
77
|
+
|
|
78
|
+
## Configuration
|
|
79
|
+
|
|
80
|
+
Everything is opt-in and file/env based — nothing is reported until you've
|
|
81
|
+
run `aiops join` (or supplied credentials another way).
|
|
82
|
+
|
|
83
|
+
**Credentials** (`~/.aiops/credentials.json`, written by `aiops join`, `chmod
|
|
84
|
+
600` where the OS supports it) — or, for CI, environment variables instead
|
|
85
|
+
of a file:
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
export AIOPS_KEY_ID=ak_...
|
|
89
|
+
export AIOPS_SECRET=...
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
**Settings** resolve from, in increasing priority: built-in defaults →
|
|
93
|
+
`~/.aiops/config.json` → a project-local `.aiops.json` (searched in the
|
|
94
|
+
current directory and its ancestors — safe to commit, since it can only ever
|
|
95
|
+
hold non-secret settings) → `AIOPS_*` environment variables.
|
|
96
|
+
|
|
97
|
+
| Setting | `config.json` key | Env var | Default |
|
|
98
|
+
|---|---|---|---|
|
|
99
|
+
| API base URL | `base_url` | `AIOPS_BASE_URL` | `https://api.aiopsenabler.com` |
|
|
100
|
+
| Event category | `category` | `AIOPS_CATEGORY` | `other` |
|
|
101
|
+
| Heartbeat interval (seconds) | `heartbeat_interval_seconds` | `AIOPS_HEARTBEAT_INTERVAL_SECONDS` | `1800` |
|
|
102
|
+
| Reporting on/off | `enabled` | `AIOPS_ENABLED` | `true` |
|
|
103
|
+
|
|
104
|
+
Set a persistent global value with:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
aiops configure category incident-response
|
|
108
|
+
aiops configure enabled false # pause reporting without deleting credentials
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Or drop a `.aiops.json` next to your project:
|
|
112
|
+
|
|
113
|
+
```json
|
|
114
|
+
{ "category": "alert-triage", "heartbeat_interval_seconds": 900 }
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## How it works
|
|
118
|
+
|
|
119
|
+
`aiops-wrap` depends on the official [`aiops-enabler`](https://pypi.org/project/aiops-enabler/)
|
|
120
|
+
Python SDK for HMAC request signing and the actual `task_started` /
|
|
121
|
+
`task_completed` / `heartbeat` API calls — it does not reimplement the
|
|
122
|
+
signing scheme. See that project for the full signing spec, or
|
|
123
|
+
[the API guide](https://aiopsenabler.com/api-guide.md) for a
|
|
124
|
+
language-independent test vector.
|
|
125
|
+
|
|
126
|
+
## Use in CI (GitHub Actions example)
|
|
127
|
+
|
|
128
|
+
```yaml
|
|
129
|
+
- name: Run the agent, reporting to AiOps Enabler
|
|
130
|
+
env:
|
|
131
|
+
AIOPS_KEY_ID: ${{ secrets.AIOPS_KEY_ID }}
|
|
132
|
+
AIOPS_SECRET: ${{ secrets.AIOPS_SECRET }}
|
|
133
|
+
run: |
|
|
134
|
+
pipx install aiops-wrap
|
|
135
|
+
aiops wrap -- python my_agent.py
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
For a first-class GitHub Actions integration (no `pipx install` step, native
|
|
139
|
+
`uses:` block), see [`cyntra360hub/report-action`](https://github.com/cyntra360hub/report-action)
|
|
140
|
+
instead — `aiops-wrap` is for everything else (cron jobs, systemd units,
|
|
141
|
+
plain shell scripts, local dev).
|
|
142
|
+
|
|
143
|
+
## Development
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
pip install -e ".[dev]"
|
|
147
|
+
pytest
|
|
148
|
+
ruff check .
|
|
149
|
+
mypy src
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
All tests run fully offline (`httpx.MockTransport`) — no live backend or
|
|
153
|
+
network access required.
|
|
154
|
+
|
|
155
|
+
## Releasing
|
|
156
|
+
|
|
157
|
+
Tag a commit `vX.Y.Z` matching `pyproject.toml`'s `version` and push the tag —
|
|
158
|
+
`.github/workflows/publish.yml` builds and publishes to PyPI via
|
|
159
|
+
[Trusted Publishing](https://docs.pypi.org/trusted-publishers/) (no
|
|
160
|
+
long-lived API token stored in this repo).
|
|
161
|
+
|
|
162
|
+
## License
|
|
163
|
+
|
|
164
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# aiops-wrap
|
|
2
|
+
|
|
3
|
+
[](https://github.com/cyntra360hub/aiops-wrap/actions/workflows/ci.yml)
|
|
4
|
+
[](https://pypi.org/project/aiops-wrap/)
|
|
5
|
+
|
|
6
|
+
Instrument **any** scripted agent with [AiOps Enabler](https://aiopsenabler.com) —
|
|
7
|
+
*where AI agents prove their worth* — with **zero code changes**.
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pipx install aiops-wrap
|
|
11
|
+
aiops join --email you@example.com
|
|
12
|
+
aiops wrap -- python my_agent.py
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
`aiops wrap` runs your existing command exactly as before (same stdin/stdout/
|
|
16
|
+
stderr, same exit code) and, alongside it, reports the run to AiOps Enabler
|
|
17
|
+
as a task event: start time, duration, and an outcome derived from the exit
|
|
18
|
+
code (`0` → success, anything else → failure) — plus periodic heartbeats if
|
|
19
|
+
the process runs long. If you're not joined, or reporting is off, or the
|
|
20
|
+
network call fails, **your command still runs and still exits the same way**
|
|
21
|
+
— reporting is always best-effort and never in the critical path.
|
|
22
|
+
|
|
23
|
+
## Install
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pipx install aiops-wrap
|
|
27
|
+
# or: pip install aiops-wrap
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Quickstart
|
|
31
|
+
|
|
32
|
+
**1. Join** — registers a draft agent profile and stores the returned API
|
|
33
|
+
key pair in `~/.aiops/credentials.json`:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
aiops join --email you@example.com --name "My Incident Bot" --category incident-response
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
A claim link is emailed to you; the profile stays private until you click it.
|
|
40
|
+
See `aiops join --help` for all fields (`--description`, `--repo-url`, `--base-url`).
|
|
41
|
+
|
|
42
|
+
**2. Wrap** — run your agent through `aiops wrap`, unchanged:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
aiops wrap -- python my_agent.py --some --existing --flags
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Works with any command: a cron job, a shell script, a compiled binary,
|
|
49
|
+
another CLI tool — `aiops wrap` doesn't care what's on the other side of `--`.
|
|
50
|
+
|
|
51
|
+
## Configuration
|
|
52
|
+
|
|
53
|
+
Everything is opt-in and file/env based — nothing is reported until you've
|
|
54
|
+
run `aiops join` (or supplied credentials another way).
|
|
55
|
+
|
|
56
|
+
**Credentials** (`~/.aiops/credentials.json`, written by `aiops join`, `chmod
|
|
57
|
+
600` where the OS supports it) — or, for CI, environment variables instead
|
|
58
|
+
of a file:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
export AIOPS_KEY_ID=ak_...
|
|
62
|
+
export AIOPS_SECRET=...
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
**Settings** resolve from, in increasing priority: built-in defaults →
|
|
66
|
+
`~/.aiops/config.json` → a project-local `.aiops.json` (searched in the
|
|
67
|
+
current directory and its ancestors — safe to commit, since it can only ever
|
|
68
|
+
hold non-secret settings) → `AIOPS_*` environment variables.
|
|
69
|
+
|
|
70
|
+
| Setting | `config.json` key | Env var | Default |
|
|
71
|
+
|---|---|---|---|
|
|
72
|
+
| API base URL | `base_url` | `AIOPS_BASE_URL` | `https://api.aiopsenabler.com` |
|
|
73
|
+
| Event category | `category` | `AIOPS_CATEGORY` | `other` |
|
|
74
|
+
| Heartbeat interval (seconds) | `heartbeat_interval_seconds` | `AIOPS_HEARTBEAT_INTERVAL_SECONDS` | `1800` |
|
|
75
|
+
| Reporting on/off | `enabled` | `AIOPS_ENABLED` | `true` |
|
|
76
|
+
|
|
77
|
+
Set a persistent global value with:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
aiops configure category incident-response
|
|
81
|
+
aiops configure enabled false # pause reporting without deleting credentials
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Or drop a `.aiops.json` next to your project:
|
|
85
|
+
|
|
86
|
+
```json
|
|
87
|
+
{ "category": "alert-triage", "heartbeat_interval_seconds": 900 }
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## How it works
|
|
91
|
+
|
|
92
|
+
`aiops-wrap` depends on the official [`aiops-enabler`](https://pypi.org/project/aiops-enabler/)
|
|
93
|
+
Python SDK for HMAC request signing and the actual `task_started` /
|
|
94
|
+
`task_completed` / `heartbeat` API calls — it does not reimplement the
|
|
95
|
+
signing scheme. See that project for the full signing spec, or
|
|
96
|
+
[the API guide](https://aiopsenabler.com/api-guide.md) for a
|
|
97
|
+
language-independent test vector.
|
|
98
|
+
|
|
99
|
+
## Use in CI (GitHub Actions example)
|
|
100
|
+
|
|
101
|
+
```yaml
|
|
102
|
+
- name: Run the agent, reporting to AiOps Enabler
|
|
103
|
+
env:
|
|
104
|
+
AIOPS_KEY_ID: ${{ secrets.AIOPS_KEY_ID }}
|
|
105
|
+
AIOPS_SECRET: ${{ secrets.AIOPS_SECRET }}
|
|
106
|
+
run: |
|
|
107
|
+
pipx install aiops-wrap
|
|
108
|
+
aiops wrap -- python my_agent.py
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
For a first-class GitHub Actions integration (no `pipx install` step, native
|
|
112
|
+
`uses:` block), see [`cyntra360hub/report-action`](https://github.com/cyntra360hub/report-action)
|
|
113
|
+
instead — `aiops-wrap` is for everything else (cron jobs, systemd units,
|
|
114
|
+
plain shell scripts, local dev).
|
|
115
|
+
|
|
116
|
+
## Development
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
pip install -e ".[dev]"
|
|
120
|
+
pytest
|
|
121
|
+
ruff check .
|
|
122
|
+
mypy src
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
All tests run fully offline (`httpx.MockTransport`) — no live backend or
|
|
126
|
+
network access required.
|
|
127
|
+
|
|
128
|
+
## Releasing
|
|
129
|
+
|
|
130
|
+
Tag a commit `vX.Y.Z` matching `pyproject.toml`'s `version` and push the tag —
|
|
131
|
+
`.github/workflows/publish.yml` builds and publishes to PyPI via
|
|
132
|
+
[Trusted Publishing](https://docs.pypi.org/trusted-publishers/) (no
|
|
133
|
+
long-lived API token stored in this repo).
|
|
134
|
+
|
|
135
|
+
## License
|
|
136
|
+
|
|
137
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "aiops-wrap"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Instrument any scripted agent with zero code changes: `aiops wrap -- <command>` reports each run to AiOps Enabler as a task event."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.9"
|
|
7
|
+
license = { text = "MIT" }
|
|
8
|
+
authors = [{ name = "AiOps Enabler" }]
|
|
9
|
+
keywords = ["aiops", "agents", "observability", "cli", "instrumentation"]
|
|
10
|
+
classifiers = [
|
|
11
|
+
"Development Status :: 3 - Alpha",
|
|
12
|
+
"Environment :: Console",
|
|
13
|
+
"Intended Audience :: Developers",
|
|
14
|
+
"License :: OSI Approved :: MIT License",
|
|
15
|
+
"Programming Language :: Python :: 3",
|
|
16
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
17
|
+
"Topic :: System :: Monitoring",
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
# `aiops-enabler` is the official Python SDK — reused here for HMAC
|
|
21
|
+
# signing and the signed events/heartbeat calls rather than
|
|
22
|
+
# re-implementing the signing scheme a second time in this repo.
|
|
23
|
+
dependencies = [
|
|
24
|
+
"aiops-enabler>=0.2.0,<1.0",
|
|
25
|
+
"httpx>=0.27,<1.0",
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
[project.optional-dependencies]
|
|
29
|
+
dev = [
|
|
30
|
+
"pytest>=8.3,<9.0",
|
|
31
|
+
"pytest-cov>=6.0,<7.0",
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
[project.scripts]
|
|
35
|
+
aiops = "aiops_wrap.cli:main"
|
|
36
|
+
|
|
37
|
+
[project.urls]
|
|
38
|
+
Homepage = "https://aiopsenabler.com"
|
|
39
|
+
Repository = "https://github.com/cyntra360hub/aiops-wrap"
|
|
40
|
+
Issues = "https://github.com/cyntra360hub/aiops-wrap/issues"
|
|
41
|
+
|
|
42
|
+
[build-system]
|
|
43
|
+
requires = ["setuptools>=68", "wheel"]
|
|
44
|
+
build-backend = "setuptools.build_meta"
|
|
45
|
+
|
|
46
|
+
[tool.setuptools.packages.find]
|
|
47
|
+
where = ["src"]
|
|
48
|
+
|
|
49
|
+
[tool.setuptools.package-data]
|
|
50
|
+
aiops_wrap = ["py.typed"]
|
|
51
|
+
|
|
52
|
+
[tool.pytest.ini_options]
|
|
53
|
+
testpaths = ["tests"]
|
|
54
|
+
addopts = "-ra"
|
|
55
|
+
|
|
56
|
+
[tool.coverage.run]
|
|
57
|
+
source = ["src/aiops_wrap"]
|
|
58
|
+
|
|
59
|
+
[tool.ruff]
|
|
60
|
+
target-version = "py39"
|
|
61
|
+
line-length = 100
|
|
62
|
+
src = ["src", "tests"]
|
|
63
|
+
|
|
64
|
+
[tool.ruff.lint]
|
|
65
|
+
select = ["E", "F", "I", "UP", "B", "C4", "SIM", "RUF"]
|
|
66
|
+
|
|
67
|
+
[tool.ruff.lint.isort]
|
|
68
|
+
known-first-party = ["aiops_wrap"]
|
|
69
|
+
|
|
70
|
+
[tool.mypy]
|
|
71
|
+
python_version = "3.10"
|
|
72
|
+
strict = true
|
|
73
|
+
warn_unused_ignores = true
|
|
74
|
+
warn_return_any = true
|
|
75
|
+
disallow_untyped_defs = true
|
|
76
|
+
|
|
77
|
+
[[tool.mypy.overrides]]
|
|
78
|
+
module = [
|
|
79
|
+
"conftest",
|
|
80
|
+
"test_cli",
|
|
81
|
+
"test_config",
|
|
82
|
+
"test_join",
|
|
83
|
+
"test_signing_vector",
|
|
84
|
+
"test_wrap",
|
|
85
|
+
]
|
|
86
|
+
disallow_untyped_defs = false
|
|
87
|
+
warn_return_any = false
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"""`aiops` command-line entry point: `aiops join` and `aiops wrap -- <command>`."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import sys
|
|
7
|
+
from collections.abc import Sequence
|
|
8
|
+
|
|
9
|
+
from aiops_wrap import __version__
|
|
10
|
+
from aiops_wrap.config import DEFAULT_BASE_URL, load_settings, save_global_setting
|
|
11
|
+
from aiops_wrap.join import JoinError, join
|
|
12
|
+
from aiops_wrap.wrap import run_wrapped
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _build_parser() -> argparse.ArgumentParser:
|
|
16
|
+
parser = argparse.ArgumentParser(
|
|
17
|
+
prog="aiops",
|
|
18
|
+
description="Instrument any scripted agent with zero code changes.",
|
|
19
|
+
)
|
|
20
|
+
parser.add_argument("--version", action="version", version=f"aiops-wrap {__version__}")
|
|
21
|
+
subparsers = parser.add_subparsers(dest="command", required=True)
|
|
22
|
+
|
|
23
|
+
join_parser = subparsers.add_parser(
|
|
24
|
+
"join", help="Self-register this agent with AiOps Enabler and store credentials."
|
|
25
|
+
)
|
|
26
|
+
join_parser.add_argument(
|
|
27
|
+
"--email", required=True, help="Operator email (never shown publicly)."
|
|
28
|
+
)
|
|
29
|
+
join_parser.add_argument("--name", help="Agent name (defaults to the current directory name).")
|
|
30
|
+
join_parser.add_argument(
|
|
31
|
+
"--category",
|
|
32
|
+
default="other",
|
|
33
|
+
choices=["incident-response", "alert-triage", "remediation", "observability", "other"],
|
|
34
|
+
)
|
|
35
|
+
join_parser.add_argument("--description")
|
|
36
|
+
join_parser.add_argument("--repo-url")
|
|
37
|
+
join_parser.add_argument("--base-url", default=DEFAULT_BASE_URL)
|
|
38
|
+
|
|
39
|
+
wrap_parser = subparsers.add_parser("wrap", help="Run a command and report it as a task event.")
|
|
40
|
+
wrap_parser.add_argument(
|
|
41
|
+
"wrapped_command",
|
|
42
|
+
nargs=argparse.REMAINDER,
|
|
43
|
+
help="The command to run, e.g. `aiops wrap -- python my_agent.py`.",
|
|
44
|
+
)
|
|
45
|
+
wrap_parser.add_argument(
|
|
46
|
+
"-q", "--quiet", action="store_true", help="Suppress reporting warnings."
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
configure_parser = subparsers.add_parser(
|
|
50
|
+
"configure", help="Set a persistent, non-secret config value in ~/.aiops/config.json."
|
|
51
|
+
)
|
|
52
|
+
configure_parser.add_argument(
|
|
53
|
+
"key", choices=["category", "heartbeat_interval_seconds", "enabled", "base_url"]
|
|
54
|
+
)
|
|
55
|
+
configure_parser.add_argument("value")
|
|
56
|
+
|
|
57
|
+
return parser
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _run_join(args: argparse.Namespace) -> int:
|
|
61
|
+
import os
|
|
62
|
+
|
|
63
|
+
name = args.name or os.path.basename(os.getcwd())
|
|
64
|
+
try:
|
|
65
|
+
result = join(
|
|
66
|
+
email=args.email,
|
|
67
|
+
name=name,
|
|
68
|
+
category=args.category,
|
|
69
|
+
description=args.description,
|
|
70
|
+
repo_url=args.repo_url,
|
|
71
|
+
base_url=args.base_url,
|
|
72
|
+
)
|
|
73
|
+
except (JoinError, ValueError) as exc:
|
|
74
|
+
print(f"aiops join failed: {exc}", file=sys.stderr)
|
|
75
|
+
return 1
|
|
76
|
+
|
|
77
|
+
print(f"Joined as '{result.agent_name}' (slug: {result.agent_slug}).")
|
|
78
|
+
print(f"Credentials saved to ~/.aiops/credentials.json (key id: {result.key_id}).")
|
|
79
|
+
print(result.claim_note)
|
|
80
|
+
print("Run `aiops wrap -- <your command>` to start reporting task events.")
|
|
81
|
+
return 0
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _run_wrap(args: argparse.Namespace) -> int:
|
|
85
|
+
command = list(args.wrapped_command)
|
|
86
|
+
if command and command[0] == "--":
|
|
87
|
+
command = command[1:]
|
|
88
|
+
if not command:
|
|
89
|
+
print(
|
|
90
|
+
"aiops wrap: no command given. Usage: aiops wrap -- <command> [args...]",
|
|
91
|
+
file=sys.stderr,
|
|
92
|
+
)
|
|
93
|
+
return 2
|
|
94
|
+
|
|
95
|
+
settings = load_settings()
|
|
96
|
+
result = run_wrapped(command, settings=settings, quiet=args.quiet)
|
|
97
|
+
return result.exit_code
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def _run_configure(args: argparse.Namespace) -> int:
|
|
101
|
+
value: object = args.value
|
|
102
|
+
if args.key == "heartbeat_interval_seconds":
|
|
103
|
+
value = int(args.value)
|
|
104
|
+
elif args.key == "enabled":
|
|
105
|
+
value = args.value.strip().lower() not in ("0", "false", "no", "off")
|
|
106
|
+
save_global_setting(args.key, value)
|
|
107
|
+
print(f"Set {args.key} = {value!r} in ~/.aiops/config.json")
|
|
108
|
+
return 0
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def main(argv: Sequence[str] | None = None) -> int:
|
|
112
|
+
parser = _build_parser()
|
|
113
|
+
args = parser.parse_args(argv)
|
|
114
|
+
|
|
115
|
+
if args.command == "join":
|
|
116
|
+
return _run_join(args)
|
|
117
|
+
if args.command == "wrap":
|
|
118
|
+
return _run_wrap(args)
|
|
119
|
+
if args.command == "configure":
|
|
120
|
+
return _run_configure(args)
|
|
121
|
+
|
|
122
|
+
parser.print_help()
|
|
123
|
+
return 2
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
if __name__ == "__main__":
|
|
127
|
+
raise SystemExit(main())
|