hobots 1.0.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.
- hobots-1.0.0/.gitignore +245 -0
- hobots-1.0.0/LICENSE +21 -0
- hobots-1.0.0/PKG-INFO +405 -0
- hobots-1.0.0/README.md +356 -0
- hobots-1.0.0/docs/README.md +32 -0
- hobots-1.0.0/docs/api-reference.md +309 -0
- hobots-1.0.0/docs/concepts.md +202 -0
- hobots-1.0.0/docs/getting-started.md +136 -0
- hobots-1.0.0/docs/protocol.md +189 -0
- hobots-1.0.0/docs/tasks.md +241 -0
- hobots-1.0.0/docs/telemetry.md +221 -0
- hobots-1.0.0/examples/README.md +62 -0
- hobots-1.0.0/examples/config.py +21 -0
- hobots-1.0.0/examples/full.py +80 -0
- hobots-1.0.0/examples/tasks/01_basic.py +61 -0
- hobots-1.0.0/examples/tasks/02_logs_cancel.py +73 -0
- hobots-1.0.0/examples/tasks/03_concurrency.py +62 -0
- hobots-1.0.0/examples/tasks/04_idempotency.py +80 -0
- hobots-1.0.0/examples/tasks/05_shutdown.py +86 -0
- hobots-1.0.0/examples/tasks/06_attachments.py +104 -0
- hobots-1.0.0/examples/telemetry/01_errors.py +84 -0
- hobots-1.0.0/examples/telemetry/02_scope.py +80 -0
- hobots-1.0.0/examples/telemetry/03_transactions.py +80 -0
- hobots-1.0.0/examples/telemetry/04_heartbeat.py +46 -0
- hobots-1.0.0/examples/telemetry/05_advanced.py +65 -0
- hobots-1.0.0/pyproject.toml +83 -0
- hobots-1.0.0/src/hobots/__about__.py +3 -0
- hobots-1.0.0/src/hobots/__init__.py +159 -0
- hobots-1.0.0/src/hobots/_client.py +206 -0
- hobots-1.0.0/src/hobots/_config.py +126 -0
- hobots-1.0.0/src/hobots/_core.py +405 -0
- hobots-1.0.0/src/hobots/_errors.py +48 -0
- hobots-1.0.0/src/hobots/_eventbuilder.py +86 -0
- hobots-1.0.0/src/hobots/_heartbeat.py +61 -0
- hobots-1.0.0/src/hobots/_http.py +195 -0
- hobots-1.0.0/src/hobots/_instrument.py +134 -0
- hobots-1.0.0/src/hobots/_scope.py +90 -0
- hobots-1.0.0/src/hobots/_scrub.py +28 -0
- hobots-1.0.0/src/hobots/_stacktrace.py +78 -0
- hobots-1.0.0/src/hobots/_transaction.py +282 -0
- hobots-1.0.0/src/hobots/_transport.py +208 -0
- hobots-1.0.0/src/hobots/_types.py +137 -0
- hobots-1.0.0/src/hobots/_utils.py +149 -0
- hobots-1.0.0/src/hobots/protocol.py +235 -0
- hobots-1.0.0/src/hobots/py.typed +0 -0
- hobots-1.0.0/src/hobots/tasks/__init__.py +1 -0
- hobots-1.0.0/src/hobots/tasks/_agent.py +444 -0
- hobots-1.0.0/src/hobots/tasks/_api.py +208 -0
- hobots-1.0.0/src/hobots/tasks/_attachments.py +98 -0
- hobots-1.0.0/src/hobots/tasks/_context.py +659 -0
- hobots-1.0.0/src/hobots/tasks/_run_context.py +49 -0
- hobots-1.0.0/src/hobots/tasks/_shutdown.py +93 -0
- hobots-1.0.0/src/hobots/tasks/_types.py +15 -0
- hobots-1.0.0/src/hobots/tasks/protocol.py +188 -0
- hobots-1.0.0/tests/conftest.py +224 -0
- hobots-1.0.0/tests/test_agent.py +353 -0
- hobots-1.0.0/tests/test_attachments.py +375 -0
- hobots-1.0.0/tests/test_client.py +309 -0
- hobots-1.0.0/tests/test_config.py +106 -0
- hobots-1.0.0/tests/test_protocol.py +44 -0
- hobots-1.0.0/tests/test_scrub.py +33 -0
- hobots-1.0.0/tests/test_stacktrace.py +143 -0
- hobots-1.0.0/tests/test_transaction.py +248 -0
- hobots-1.0.0/tests/test_transport.py +175 -0
hobots-1.0.0/.gitignore
ADDED
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
|
|
2
|
+
# Created by https://www.gitignore.io/api/node,macos,intellij,sublimetext,visualstudiocode
|
|
3
|
+
|
|
4
|
+
### Intellij ###
|
|
5
|
+
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
|
|
6
|
+
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
|
7
|
+
|
|
8
|
+
# Elastic Beanstalk Files
|
|
9
|
+
.elasticbeanstalk/*
|
|
10
|
+
!.elasticbeanstalk/*.cfg.yml
|
|
11
|
+
!.elasticbeanstalk/*.global.yml
|
|
12
|
+
.serverless
|
|
13
|
+
|
|
14
|
+
**/*.pdf
|
|
15
|
+
|
|
16
|
+
.turbo
|
|
17
|
+
.turbo/**
|
|
18
|
+
|
|
19
|
+
.next
|
|
20
|
+
.next/**
|
|
21
|
+
|
|
22
|
+
dist
|
|
23
|
+
dist/**
|
|
24
|
+
|
|
25
|
+
uploads
|
|
26
|
+
|
|
27
|
+
env.json
|
|
28
|
+
.env.*
|
|
29
|
+
!.env.example
|
|
30
|
+
|
|
31
|
+
.lock
|
|
32
|
+
yarn.lock
|
|
33
|
+
package-lock.json
|
|
34
|
+
|
|
35
|
+
# User-specific stuff:
|
|
36
|
+
.idea/**/workspace.xml
|
|
37
|
+
.idea/**/tasks.xml
|
|
38
|
+
.idea/dictionaries
|
|
39
|
+
./scripts/seed.sql
|
|
40
|
+
|
|
41
|
+
# Sensitive or high-churn files:
|
|
42
|
+
.idea/**/dataSources/
|
|
43
|
+
.idea/**/dataSources.ids
|
|
44
|
+
.idea/**/dataSources.xml
|
|
45
|
+
.idea/**/dataSources.local.xml
|
|
46
|
+
.idea/**/sqlDataSources.xml
|
|
47
|
+
.idea/**/dynamic.xml
|
|
48
|
+
.idea/**/uiDesigner.xml
|
|
49
|
+
|
|
50
|
+
# Gradle:
|
|
51
|
+
.idea/**/gradle.xml
|
|
52
|
+
.idea/**/libraries
|
|
53
|
+
|
|
54
|
+
# CMake
|
|
55
|
+
cmake-build-debug/
|
|
56
|
+
|
|
57
|
+
# Mongo Explorer plugin:
|
|
58
|
+
.idea/**/mongoSettings.xml
|
|
59
|
+
|
|
60
|
+
## File-based project format:
|
|
61
|
+
*.iws
|
|
62
|
+
|
|
63
|
+
## Plugin-specific files:
|
|
64
|
+
|
|
65
|
+
# IntelliJ
|
|
66
|
+
/out/
|
|
67
|
+
|
|
68
|
+
# mpeltonen/sbt-idea plugin
|
|
69
|
+
.idea_modules/
|
|
70
|
+
|
|
71
|
+
# JIRA plugin
|
|
72
|
+
atlassian-ide-plugin.xml
|
|
73
|
+
|
|
74
|
+
# Cursive Clojure plugin
|
|
75
|
+
.idea/replstate.xml
|
|
76
|
+
|
|
77
|
+
# Ruby plugin and RubyMine
|
|
78
|
+
/.rakeTasks
|
|
79
|
+
|
|
80
|
+
# Crashlytics plugin (for Android Studio and IntelliJ)
|
|
81
|
+
com_crashlytics_export_strings.xml
|
|
82
|
+
crashlytics.properties
|
|
83
|
+
crashlytics-build.properties
|
|
84
|
+
fabric.properties
|
|
85
|
+
|
|
86
|
+
### Intellij Patch ###
|
|
87
|
+
# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
|
|
88
|
+
|
|
89
|
+
# *.iml
|
|
90
|
+
# modules.xml
|
|
91
|
+
# .idea/misc.xml
|
|
92
|
+
# *.ipr
|
|
93
|
+
|
|
94
|
+
# Sonarlint plugin
|
|
95
|
+
.idea/sonarlint
|
|
96
|
+
|
|
97
|
+
### macOS ###
|
|
98
|
+
*.DS_Store
|
|
99
|
+
.AppleDouble
|
|
100
|
+
.LSOverride
|
|
101
|
+
|
|
102
|
+
# Icon must end with two \r
|
|
103
|
+
Icon
|
|
104
|
+
|
|
105
|
+
# Thumbnails
|
|
106
|
+
._*
|
|
107
|
+
|
|
108
|
+
# Files that might appear in the root of a volume
|
|
109
|
+
.DocumentRevisions-V100
|
|
110
|
+
.fseventsd
|
|
111
|
+
.Spotlight-V100
|
|
112
|
+
.TemporaryItems
|
|
113
|
+
.Trashes
|
|
114
|
+
.VolumeIcon.icns
|
|
115
|
+
.com.apple.timemachine.donotpresent
|
|
116
|
+
|
|
117
|
+
# Directories potentially created on remote AFP share
|
|
118
|
+
.AppleDB
|
|
119
|
+
.AppleDesktop
|
|
120
|
+
Network Trash Folder
|
|
121
|
+
Temporary Items
|
|
122
|
+
.apdisk
|
|
123
|
+
|
|
124
|
+
### Node ###
|
|
125
|
+
# Logs
|
|
126
|
+
logs
|
|
127
|
+
*.log
|
|
128
|
+
npm-debug.log*
|
|
129
|
+
yarn-debug.log*
|
|
130
|
+
yarn-error.log*
|
|
131
|
+
|
|
132
|
+
# Runtime data
|
|
133
|
+
pids
|
|
134
|
+
*.pid
|
|
135
|
+
*.seed
|
|
136
|
+
*.pid.lock
|
|
137
|
+
|
|
138
|
+
# Directory for instrumented libs generated by jscoverage/JSCover
|
|
139
|
+
lib-cov
|
|
140
|
+
|
|
141
|
+
# Coverage directory used by tools like istanbul
|
|
142
|
+
coverage
|
|
143
|
+
|
|
144
|
+
# nyc test coverage
|
|
145
|
+
.nyc_output
|
|
146
|
+
|
|
147
|
+
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
|
148
|
+
.grunt
|
|
149
|
+
|
|
150
|
+
# Bower dependency directory (https://bower.io/)
|
|
151
|
+
bower_components
|
|
152
|
+
|
|
153
|
+
# node-waf configuration
|
|
154
|
+
.lock-wscript
|
|
155
|
+
|
|
156
|
+
# Compiled binary addons (http://nodejs.org/api/addons.html)
|
|
157
|
+
build/Release
|
|
158
|
+
|
|
159
|
+
# Dependency directories
|
|
160
|
+
node_modules/
|
|
161
|
+
jspm_packages/
|
|
162
|
+
|
|
163
|
+
# Typescript v1 declaration files
|
|
164
|
+
typings/
|
|
165
|
+
|
|
166
|
+
# Optional npm cache directory
|
|
167
|
+
.npm
|
|
168
|
+
|
|
169
|
+
# Optional eslint cache
|
|
170
|
+
.eslintcache
|
|
171
|
+
|
|
172
|
+
# Optional REPL history
|
|
173
|
+
.node_repl_history
|
|
174
|
+
|
|
175
|
+
# Output of 'npm pack'
|
|
176
|
+
*.tgz
|
|
177
|
+
|
|
178
|
+
# Yarn Integrity file
|
|
179
|
+
.yarn-integrity
|
|
180
|
+
|
|
181
|
+
# dotenv environment variables file
|
|
182
|
+
.env
|
|
183
|
+
.env.test
|
|
184
|
+
|
|
185
|
+
### SublimeText ###
|
|
186
|
+
# cache files for sublime text
|
|
187
|
+
*.tmlanguage.cache
|
|
188
|
+
*.tmPreferences.cache
|
|
189
|
+
*.stTheme.cache
|
|
190
|
+
|
|
191
|
+
# workspace files are user-specific
|
|
192
|
+
*.sublime-workspace
|
|
193
|
+
|
|
194
|
+
# project files should be checked into the repository, unless a significant
|
|
195
|
+
# proportion of contributors will probably not be using SublimeText
|
|
196
|
+
# *.sublime-project
|
|
197
|
+
|
|
198
|
+
# sftp configuration file
|
|
199
|
+
sftp-config.json
|
|
200
|
+
|
|
201
|
+
# Package control specific files
|
|
202
|
+
Package Control.last-run
|
|
203
|
+
Package Control.ca-list
|
|
204
|
+
Package Control.ca-bundle
|
|
205
|
+
Package Control.system-ca-bundle
|
|
206
|
+
Package Control.cache/
|
|
207
|
+
Package Control.ca-certs/
|
|
208
|
+
Package Control.merged-ca-bundle
|
|
209
|
+
Package Control.user-ca-bundle
|
|
210
|
+
oscrypto-ca-bundle.crt
|
|
211
|
+
bh_unicode_properties.cache
|
|
212
|
+
|
|
213
|
+
# Sublime-github package stores a github token in this file
|
|
214
|
+
# https://packagecontrol.io/packages/sublime-github
|
|
215
|
+
GitHub.sublime-settings
|
|
216
|
+
|
|
217
|
+
### VisualStudioCode ###
|
|
218
|
+
.vscode/*
|
|
219
|
+
!.vscode/settings.json
|
|
220
|
+
!.vscode/tasks.json
|
|
221
|
+
!.vscode/launch.json
|
|
222
|
+
!.vscode/extensions.json
|
|
223
|
+
.history
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
# End of https://www.gitignore.io/api/node,macos,intellij,sublimetext,visualstudiocode
|
|
227
|
+
|
|
228
|
+
### Python (packages/hobots-sdk-python) ###
|
|
229
|
+
__pycache__/
|
|
230
|
+
*.py[cod]
|
|
231
|
+
*$py.class
|
|
232
|
+
*.egg-info/
|
|
233
|
+
.eggs/
|
|
234
|
+
build/
|
|
235
|
+
.venv/
|
|
236
|
+
venv/
|
|
237
|
+
ENV/
|
|
238
|
+
.pytest_cache/
|
|
239
|
+
.mypy_cache/
|
|
240
|
+
.ruff_cache/
|
|
241
|
+
.tox/
|
|
242
|
+
.coverage
|
|
243
|
+
.coverage.*
|
|
244
|
+
htmlcov/
|
|
245
|
+
# `dist` (usado pelo build do Python também) já é ignorado acima
|
hobots-1.0.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Hobots
|
|
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.
|
hobots-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: hobots
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: SDK Python da Hobots — telemetria (erros, transactions e heartbeats) e execução remota de tarefas sob demanda (tasks) para RPAs e integrações. Um único import e um único init().
|
|
5
|
+
Project-URL: Homepage, https://hobots.app
|
|
6
|
+
Author: Hobots
|
|
7
|
+
License: MIT License
|
|
8
|
+
|
|
9
|
+
Copyright (c) 2026 Hobots
|
|
10
|
+
|
|
11
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
12
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
13
|
+
in the Software without restriction, including without limitation the rights
|
|
14
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
15
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
16
|
+
furnished to do so, subject to the following conditions:
|
|
17
|
+
|
|
18
|
+
The above copyright notice and this permission notice shall be included in all
|
|
19
|
+
copies or substantial portions of the Software.
|
|
20
|
+
|
|
21
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
22
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
23
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
24
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
25
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
26
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
27
|
+
SOFTWARE.
|
|
28
|
+
License-File: LICENSE
|
|
29
|
+
Keywords: automation,hobots,monitoring,rpa,tasks,telemetry
|
|
30
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
31
|
+
Classifier: Intended Audience :: Developers
|
|
32
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
33
|
+
Classifier: Operating System :: OS Independent
|
|
34
|
+
Classifier: Programming Language :: Python :: 3
|
|
35
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
36
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
37
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
38
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
39
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
40
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
41
|
+
Classifier: Topic :: System :: Monitoring
|
|
42
|
+
Classifier: Typing :: Typed
|
|
43
|
+
Requires-Python: >=3.9
|
|
44
|
+
Provides-Extra: dev
|
|
45
|
+
Requires-Dist: mypy>=1.10; extra == 'dev'
|
|
46
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
47
|
+
Requires-Dist: ruff>=0.6; extra == 'dev'
|
|
48
|
+
Description-Content-Type: text/markdown
|
|
49
|
+
|
|
50
|
+
# hobots
|
|
51
|
+
|
|
52
|
+
SDK Python da **Hobots** para instrumentar agentes (RPAs) e integrações.
|
|
53
|
+
|
|
54
|
+
- **Telemetria** — o que o programa **emite** sozinho: erros com stack trace (módulo **Issues** do app), performance (transactions/etapas) e heartbeats de uptime (módulo **Agentes**).
|
|
55
|
+
- **Tasks** — execução **sob demanda**: o agente faz polling no app da Hobots, executa handlers registrados e reporta status e logs (módulo **Solicitações**).
|
|
56
|
+
|
|
57
|
+
**Um único import e um único `init()`** cobrem as duas capabilities. Cliente HTTP puro, **sem dependências de runtime** (só a stdlib). Requer **Python 3.9+**.
|
|
58
|
+
|
|
59
|
+
A API é **síncrona**: handlers são funções normais (`def`), e o SDK cuida das threads de fundo. É o que combina com Selenium, Playwright síncrono, pyautogui e código legado.
|
|
60
|
+
|
|
61
|
+
> **Todas as durações são em milissegundos**, como no SDK Node: `poll_interval=5000` são 5 segundos, `ctx.sleep(2000)` dorme 2 segundos.
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## Instalação
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
pip install hobots
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
```python
|
|
72
|
+
import hobots # um import só — telemetria e tasks
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Um SDK, duas capabilities, um `init()`
|
|
78
|
+
|
|
79
|
+
As duas capabilities são autenticadas pelo **mesmo Client Secret do agente** e configuradas em um único `init()`:
|
|
80
|
+
|
|
81
|
+
| Capability | Como habilita | Para quê | Ciclo de vida |
|
|
82
|
+
| --- | --- | --- | --- |
|
|
83
|
+
| **Telemetria** | sempre ativa no `init()` | O que o programa emite sozinho: erros, transactions/etapas, heartbeat. | Threads **daemon** — **não** seguram o processo. Chame `close()` no fim. |
|
|
84
|
+
| **Tasks** | `tasks=True` (ou opções) no `init()` | Execução remota sob demanda via polling. | `start()` **segura o processo vivo** até `stop()`/`close()`. |
|
|
85
|
+
|
|
86
|
+
Você usa **uma, outra ou as duas**:
|
|
87
|
+
|
|
88
|
+
- Só saber se o robô quebrou, quanto demora e se está vivo? → `init(client_secret=..., instance_id=..., heartbeat=True)`.
|
|
89
|
+
- Disparar o robô sob demanda a partir de um formulário no app? → `init(..., tasks=True)` + handlers + `run_forever()`.
|
|
90
|
+
- Ambos? Passe `heartbeat` e `tasks` no mesmo `init()` — o `instance_id` é a identidade única, e os dois canais convergem em **um agente** no painel. Com `tasks=True`, o heartbeat é **opcional**: o próprio poll de tasks já marca a presença do agente.
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
import hobots
|
|
94
|
+
|
|
95
|
+
hobots.init(
|
|
96
|
+
client_secret="hb_<64 hex>",
|
|
97
|
+
instance_id="vm-cliente-01",
|
|
98
|
+
heartbeat=True, # telemetria: "estou vivo" a cada 30s
|
|
99
|
+
tasks={"poll_interval": 5_000}, # execução sob demanda
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
@hobots.task("processar-nfe")
|
|
104
|
+
def processar_nfe(params, ctx):
|
|
105
|
+
ctx.log.info(f"processando o lote {params['lote']}…")
|
|
106
|
+
ctx.log.success("12 notas processadas") # o retorno é ignorado — reporte por log
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
hobots.run_forever() # inicia o polling e bloqueia até Ctrl-C/SIGTERM
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## O Client Secret
|
|
115
|
+
|
|
116
|
+
Cada agente tem **um único Client Secret** — a mesma credencial autentica a telemetria **e** as tasks:
|
|
117
|
+
|
|
118
|
+
```
|
|
119
|
+
hb_<64 hex>
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
A credencial é **secreta** e viaja sempre no header `Authorization: Bearer` — nunca na URL, e nunca no upload de anexo para o S3. Copie o Client Secret no app, em **Configurações → Agentes**.
|
|
123
|
+
|
|
124
|
+
O SDK fala com a API oficial `https://api2.hobots.app` automaticamente; em desenvolvimento, aponte para outro host com a env `HOBOTS_API_URL`:
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
HOBOTS_API_URL=http://localhost:3001 python meu_robo.py
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
O secret nunca aparece em `repr()` nem em serialização do `Client`, do `InitOptions` ou do `ResolvedConfig` — só no acesso explícito (`get_client().config.key`).
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## Telemetria
|
|
135
|
+
|
|
136
|
+
Todas as funções abaixo são **no-op seguras** se `hobots.init()` não foi chamado.
|
|
137
|
+
|
|
138
|
+
### `init(**options)`
|
|
139
|
+
|
|
140
|
+
Inicialize **uma vez**, o mais cedo possível no processo. Cria o cliente, instala handlers globais de erro (a menos que `capture_unhandled=False`) e inicia o heartbeat se configurado.
|
|
141
|
+
|
|
142
|
+
```python
|
|
143
|
+
hobots.init(
|
|
144
|
+
client_secret="hb_<64 hex>",
|
|
145
|
+
instance_id="vm-cliente-01",
|
|
146
|
+
environment="production",
|
|
147
|
+
release="robo-nfe@1.4.2",
|
|
148
|
+
tags={"squad": "automacoes"},
|
|
149
|
+
heartbeat=True,
|
|
150
|
+
)
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
| Opção | Tipo | Default | Descrição |
|
|
154
|
+
| --- | --- | --- | --- |
|
|
155
|
+
| `client_secret` | `str` | **obrigatório** | o Client Secret do agente (`hb_<64 hex>`) |
|
|
156
|
+
| `instance_id` | `str` | **obrigatório** | identidade única da instância — heartbeat e poll de tasks convergem nela |
|
|
157
|
+
| `environment` | `str` | `'production'` | ambiente lógico |
|
|
158
|
+
| `release` | `str` | — | versão do programa, ex. `robo-nfe@1.4.2` |
|
|
159
|
+
| `tags` | `dict[str, str]` | — | tags aplicadas a todos os eventos |
|
|
160
|
+
| `sample_rate` | `float` | `1.0` | amostragem de erros (0..1) |
|
|
161
|
+
| `traces_sample_rate` | `float` | `1.0` | amostragem de transactions (0..1) |
|
|
162
|
+
| `max_breadcrumbs` | `int` | `100` | tamanho do buffer de breadcrumbs |
|
|
163
|
+
| `capture_unhandled` | `bool` | `True` | handlers globais de erro (`sys.excepthook`, `threading.excepthook`, `atexit`) |
|
|
164
|
+
| `heartbeat` | `bool \| HeartbeatOptions \| dict` | `False` | heartbeat periódico |
|
|
165
|
+
| `tasks` | `bool \| TasksOptions \| dict` | `False` | habilita a execução sob demanda |
|
|
166
|
+
| `before_send` | `(event) -> event \| None` | — | edita/descarta evento (retorne `None` para descartar) |
|
|
167
|
+
| `debug` | `bool` | `False` | loga atividade do SDK |
|
|
168
|
+
| `flush_interval` | `int` (ms) | `2000` | intervalo do flush automático do transport |
|
|
169
|
+
| `max_batch_size` | `int` | `10` | itens por envelope antes do flush imediato |
|
|
170
|
+
|
|
171
|
+
### Captura de erros e mensagens
|
|
172
|
+
|
|
173
|
+
```python
|
|
174
|
+
try:
|
|
175
|
+
processar_nota(nota)
|
|
176
|
+
except Exception as error:
|
|
177
|
+
hobots.capture_exception(
|
|
178
|
+
error,
|
|
179
|
+
{
|
|
180
|
+
"level": "error",
|
|
181
|
+
"tags": {"cliente": "acme"},
|
|
182
|
+
"extra": {"numero": nota.numero},
|
|
183
|
+
"fingerprint": ["sefaz-timeout"], # agrupa manualmente
|
|
184
|
+
},
|
|
185
|
+
)
|
|
186
|
+
raise
|
|
187
|
+
|
|
188
|
+
hobots.capture_message("Lote processado com sucesso", "success")
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
As duas devolvem o `event_id` (ou `None` se o SDK não foi inicializado). O hint aceita `level`, `tags`, `extra` e `fingerprint`.
|
|
192
|
+
|
|
193
|
+
Erros **não tratados** são capturados e flushados automaticamente antes do processo sair, preservando o comportamento padrão do Python (traceback + exit 1). Exceções em threads também são capturadas (`threading.excepthook`). `KeyboardInterrupt` e `SystemExit` **não** são capturados — são intenção do operador, não defeito. Desligue tudo com `capture_unhandled=False`.
|
|
194
|
+
|
|
195
|
+
`Severity = 'fatal' | 'error' | 'warning' | 'info' | 'debug' | 'success'`. Use `'success'` para reportar itens concluídos com êxito — contam como eventos, mas **não** viram issue nem disparam alertas.
|
|
196
|
+
|
|
197
|
+
### Breadcrumbs
|
|
198
|
+
|
|
199
|
+
A trilha do que aconteceu **antes** do erro. Não são enviados sozinhos: viajam anexados ao próximo evento.
|
|
200
|
+
|
|
201
|
+
```python
|
|
202
|
+
hobots.add_breadcrumb(message="abriu o portal", category="nav")
|
|
203
|
+
hobots.add_breadcrumb({"message": "login efetuado", "level": "info"})
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### Scope
|
|
207
|
+
|
|
208
|
+
Dados que acompanham os próximos eventos:
|
|
209
|
+
|
|
210
|
+
```python
|
|
211
|
+
hobots.set_tag("cliente", "acme")
|
|
212
|
+
hobots.set_tags({"regiao": "sudeste", "turno": "noite"})
|
|
213
|
+
hobots.set_user({"id": "42", "username": "operador"})
|
|
214
|
+
hobots.set_context("portal", {"url": "https://portal.exemplo"})
|
|
215
|
+
hobots.set_extra("tentativas", 3)
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
Precedência de tags: `init.tags` → scope → evento → `hint`. `set_context(nome, None)` apaga o grupo; `set_user(None)` remove o usuário.
|
|
219
|
+
|
|
220
|
+
Para isolar alterações num bloco, use o context manager:
|
|
221
|
+
|
|
222
|
+
```python
|
|
223
|
+
with hobots.with_scope() as scope:
|
|
224
|
+
scope.set_tag("lote", "2026-08")
|
|
225
|
+
hobots.capture_message("processando")
|
|
226
|
+
# a tag `lote` não existe mais aqui
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
O isolamento é por **thread** (`contextvars`): um `with_scope()` numa thread não afeta as outras.
|
|
230
|
+
|
|
231
|
+
### Transactions (performance)
|
|
232
|
+
|
|
233
|
+
```python
|
|
234
|
+
with hobots.start_transaction("sincronizar-notas", "job") as tx:
|
|
235
|
+
with tx.start_child("browser", "login no portal") as login:
|
|
236
|
+
login.set_data("usuario", "operador")
|
|
237
|
+
with tx.start_child("http", "GET /notas") as download:
|
|
238
|
+
download.set_data("quantidade", 12)
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
Também funciona na forma explícita, com `.finish()`:
|
|
242
|
+
|
|
243
|
+
```python
|
|
244
|
+
tx = hobots.start_transaction("sincronizar-notas", "job")
|
|
245
|
+
step = tx.start_child("http", "GET /notas")
|
|
246
|
+
step.finish()
|
|
247
|
+
tx.finish() # nada é enviado antes daqui
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
Steps aninham em profundidade arbitrária (`step.start_child(...)`) e o `finish()` da transaction fecha as que ficaram abertas. `StepStatus = 'ok' | 'warning' | 'error' | 'cancelled'`.
|
|
251
|
+
|
|
252
|
+
### Heartbeat
|
|
253
|
+
|
|
254
|
+
```python
|
|
255
|
+
hobots.init(
|
|
256
|
+
client_secret="hb_<64 hex>",
|
|
257
|
+
instance_id="vm-cliente-01",
|
|
258
|
+
heartbeat={"interval": 30_000, "name": "VM 01", "metadata": {"regiao": "sudeste"}},
|
|
259
|
+
)
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
Bate **imediatamente** no `init()` e depois a cada `interval` ms. O valor vai no payload e define quanto silêncio o servidor tolera antes de marcar a instância offline.
|
|
263
|
+
|
|
264
|
+
### Encerramento
|
|
265
|
+
|
|
266
|
+
```python
|
|
267
|
+
hobots.flush(5_000) # empurra a fila; o SDK continua utilizável
|
|
268
|
+
hobots.close(5_000) # encerra tudo: tasks → heartbeat → flush final
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
As threads da telemetria são **daemon** e não seguram o processo — sempre chame `close()` antes de sair, ou você perde o que estava em buffer.
|
|
272
|
+
|
|
273
|
+
---
|
|
274
|
+
|
|
275
|
+
## Tasks (execução sob demanda)
|
|
276
|
+
|
|
277
|
+
Habilite com `tasks=True` (ou opções) no `init()`, registre handlers e chame `run_forever()`.
|
|
278
|
+
|
|
279
|
+
```python
|
|
280
|
+
hobots.init(
|
|
281
|
+
client_secret="hb_<64 hex>",
|
|
282
|
+
instance_id="vm-cliente-01",
|
|
283
|
+
tasks={"poll_interval": 5_000, "concurrency": 2},
|
|
284
|
+
)
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
@hobots.task("processar-nfe") # o task_slug do formulário no app
|
|
288
|
+
def processar_nfe(params, ctx):
|
|
289
|
+
ctx.log.info(f"lote {params['lote']}")
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
hobots.run_forever()
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
O decorator é equivalente a `hobots.register("processar-nfe", processar_nfe)`.
|
|
296
|
+
|
|
297
|
+
| Opção de `tasks` | Tipo | Default | Descrição |
|
|
298
|
+
| --- | --- | --- | --- |
|
|
299
|
+
| `poll_interval` | `int` (ms) | `5000` (mín. `1000`) | intervalo entre polls **sem trabalho**; com trabalho na fila o próximo poll é imediato |
|
|
300
|
+
| `concurrency` | `int` | `1` | runs simultâneas (clamp 1..5), cada uma na sua thread |
|
|
301
|
+
| `task_timeout` | `int` (ms) | sem timeout | teto client-side por run — **não mata** o handler |
|
|
302
|
+
| `log_flush_interval` | `int` (ms) | `2000` | intervalo do envio incremental de logs (ou na hora, ao acumular 20) |
|
|
303
|
+
| `cancel_check_interval` | `int` (ms) | `5000` (clamp `1000`..`15000`) | detecta cancelamento **e** mantém o sinal de vida da run |
|
|
304
|
+
| `install_exit_handlers` | `bool` | `True` | handlers de SIGINT/SIGTERM que reportam as runs ativas como `AgentTerminated` |
|
|
305
|
+
|
|
306
|
+
### O `ctx` (RunContext)
|
|
307
|
+
|
|
308
|
+
```python
|
|
309
|
+
ctx.run_id # id da execução
|
|
310
|
+
ctx.task # nome da task
|
|
311
|
+
ctx.log # .debug/.info/.warn/.error/.success(mensagem)
|
|
312
|
+
ctx.tx # a transaction desta run — NÃO chame finish() nela
|
|
313
|
+
ctx.cancel_event # threading.Event que dispara no cancelamento/timeout/shutdown
|
|
314
|
+
ctx.is_cancelled() # -> bool
|
|
315
|
+
ctx.raise_if_cancelled() # levanta RunCanceled
|
|
316
|
+
ctx.sleep(ms) # espera cancelável (levanta RunCanceled)
|
|
317
|
+
ctx.attach(imagem, ...) # anexa um print, bloqueando até subir -> AttachResult
|
|
318
|
+
ctx.attach_nowait(imagem, ...) # idem, sem esperar o upload
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
**O retorno do handler é ignorado** — o protocolo não transporta resultado (dados de negócio podem ser sensíveis e não são coletados). Reporte o que importa com `ctx.log`.
|
|
322
|
+
|
|
323
|
+
### Cancelamento é cooperativo
|
|
324
|
+
|
|
325
|
+
O SDK não consegue interromper código Python de fora. Ele dispara o `cancel_event`; o handler precisa parar:
|
|
326
|
+
|
|
327
|
+
```python
|
|
328
|
+
@hobots.task("lote-longo")
|
|
329
|
+
def lote_longo(params, ctx):
|
|
330
|
+
for item in itens:
|
|
331
|
+
ctx.raise_if_cancelled() # ponto de parada de uma linha
|
|
332
|
+
processar(item)
|
|
333
|
+
ctx.sleep(1_000) # espera cancelável, no lugar de time.sleep
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
> ⚠️ **Nunca** capture o `RunCanceled` para retornar normalmente — uma run que retorna sem erro é reportada como `succeeded`. Se precisar fazer limpeza, capture, limpe e **re-levante**.
|
|
337
|
+
|
|
338
|
+
### Anexos (screenshots)
|
|
339
|
+
|
|
340
|
+
```python
|
|
341
|
+
@hobots.task("capturar-tela")
|
|
342
|
+
def capturar(params, ctx):
|
|
343
|
+
ctx.attach(page.screenshot(), caption="portal após o login")
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
Aceita `bytes`, `bytearray`, `memoryview` ou um caminho de arquivo (`str`/`os.PathLike`). A linha de log entra **antes** de qualquer I/O, então a ordem dos logs fica intacta. `attach()` bloqueia até o upload terminar; `attach_nowait()` volta na hora e o SDK drena os uploads pendentes (até 15s) antes de fechar a run.
|
|
347
|
+
|
|
348
|
+
**Nunca levanta**: toda falha vira `AttachResult(ok=False, reason=...)` e um aviso no log da run. `reason` é um de `'unsupported'`, `'too-large'`, `'quota'`, `'read-failed'`, `'unsupported-format'`, `'upload-failed'`, `'canceled'`.
|
|
349
|
+
|
|
350
|
+
Limites (aplicados no SDK **e** no servidor): PNG/JPEG/WEBP, **5 MB** por imagem, **20** por execução, **4** por linha de log.
|
|
351
|
+
|
|
352
|
+
### Encerramento
|
|
353
|
+
|
|
354
|
+
```python
|
|
355
|
+
hobots.run_forever(timeout=15_000) # bloqueia até SIGINT/SIGTERM e encerra
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
Se precisar da main thread para outra coisa, use `hobots.start()` (volta na hora) e encerre você mesmo:
|
|
359
|
+
|
|
360
|
+
```python
|
|
361
|
+
hobots.start()
|
|
362
|
+
...
|
|
363
|
+
hobots.close(15_000) # para as tasks (aguardando as runs) + heartbeat + flush
|
|
364
|
+
hobots.stop(30_000) # para SÓ as tasks; a telemetria continua
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
> ⚠️ O `timeout` do `close()` é gasto **duas vezes, em sequência** — uma esperando as runs, outra no flush final. `close(15_000)` pode levar até ~30s no pior caso. Dimensione pelo prazo do seu SIGTERM (Kubernetes, systemd).
|
|
368
|
+
|
|
369
|
+
---
|
|
370
|
+
|
|
371
|
+
## Segurança
|
|
372
|
+
|
|
373
|
+
- O Client Secret viaja **só** no header `Authorization: Bearer`, nunca na URL, e nunca no PUT do anexo para o S3.
|
|
374
|
+
- Mensagens de log, captions, mensagens de erro e stacks passam por `scrub_secrets()` antes de sair do processo — client secrets `hb_…`, tokens Bearer, chaves `sk-…` e credenciais embutidas em URL são redigidos.
|
|
375
|
+
- O secret nunca aparece em `repr()`/serialização do `Client`, `InitOptions` ou `ResolvedConfig`.
|
|
376
|
+
- **Logs, erros e prints são visíveis no app.** Não coloque segredos ou dados sensíveis neles.
|
|
377
|
+
|
|
378
|
+
---
|
|
379
|
+
|
|
380
|
+
## Documentação
|
|
381
|
+
|
|
382
|
+
| Documento | Conteúdo |
|
|
383
|
+
| --- | --- |
|
|
384
|
+
| [Getting started](docs/getting-started.md) | instalação, primeiro robô, encerramento |
|
|
385
|
+
| [Conceitos](docs/concepts.md) | evento, tag, breadcrumb, run, transaction, fila… |
|
|
386
|
+
| [Telemetria](docs/telemetry.md) | erros, scope, performance, heartbeat |
|
|
387
|
+
| [Tasks](docs/tasks.md) | polling, handlers, cancelamento, concorrência, anexos |
|
|
388
|
+
| [Referência de API](docs/api-reference.md) | todas as assinaturas, opções e prazos |
|
|
389
|
+
| [Protocolo & transporte](docs/protocol.md) | endpoints, wire format, retries |
|
|
390
|
+
| [Exemplos](examples/) | 11 scripts executáveis, um por funcionalidade |
|
|
391
|
+
|
|
392
|
+
## Desenvolvimento
|
|
393
|
+
|
|
394
|
+
```bash
|
|
395
|
+
pip install -e ".[dev]"
|
|
396
|
+
pytest # 123 testes, sem rede (servidor HTTP local nas fixtures)
|
|
397
|
+
ruff check . && ruff format --check .
|
|
398
|
+
mypy src
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
Publicação: ajuste `__version__` em `src/hobots/__about__.py`, rode `python -m build` (ou `uv build`) e `twine upload dist/*`.
|
|
402
|
+
|
|
403
|
+
## Licença
|
|
404
|
+
|
|
405
|
+
MIT
|