agentenv-framework-protocol 0.1.419__tar.gz → 0.1.421__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.
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/PKG-INFO +40 -6
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/README.md +39 -5
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/pyproject.toml +1 -1
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/src/agentenv_protocol/__init__.py +2 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/src/agentenv_protocol/agent_env_environment.py +35 -5
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/src/agentenv_protocol/client.py +68 -14
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/src/agentenv_protocol/computer/__init__.py +2 -1
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/src/agentenv_protocol/computer/app.py +142 -25
- agentenv_framework_protocol-0.1.421/src/agentenv_protocol/computer/app_data.py +217 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/src/agentenv_protocol/computer/app_runtime.py +21 -2
- agentenv_framework_protocol-0.1.421/src/agentenv_protocol/computer/apps.py +125 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/src/agentenv_protocol/computer/conformance/__init__.py +5 -3
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/src/agentenv_protocol/computer/conformance/__main__.py +8 -2
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/src/agentenv_protocol/computer/conformance/checks.py +33 -2
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/src/agentenv_protocol/computer/conformance/client.py +9 -6
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/src/agentenv_protocol/computer/dataplane.py +30 -21
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/src/agentenv_protocol/computer/driver.py +32 -5
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/src/agentenv_protocol/computer/runtime.py +4 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/src/agentenv_protocol/computer/serve_app.py +9 -3
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/src/agentenv_protocol/computer/session.py +33 -1
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/src/agentenv_protocol/types.py +3 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/tests/test_client.py +8 -0
- agentenv_framework_protocol-0.1.421/tests/test_computer_app_data.py +280 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/tests/test_computer_app_server.py +81 -1
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/tests/test_computer_conformance.py +37 -2
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/tests/test_computer_serve.py +6 -2
- agentenv_framework_protocol-0.1.421/tests/test_control_token.py +148 -0
- agentenv_framework_protocol-0.1.419/src/agentenv_protocol/computer/apps.py +0 -63
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/.gitignore +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/LICENSE +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/NOTICE +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/THIRD_PARTY_NOTICES.md +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/examples/__init__.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/examples/advanced_agent.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/examples/basic_agent.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/examples/custom_extensions_agent.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/examples/multimodal_agent.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/examples/reference_model.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/examples/streaming_agent.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/src/agentenv_protocol/a2a_agent/__init__.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/src/agentenv_protocol/a2a_agent/_trajectory_log.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/src/agentenv_protocol/a2a_agent/_triggers.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/src/agentenv_protocol/a2a_agent/extensions.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/src/agentenv_protocol/a2a_agent/framework.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/src/agentenv_protocol/a2a_agent/peer_agents.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/src/agentenv_protocol/a2a_agent/proxy.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/src/agentenv_protocol/a2a_agent/registry.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/src/agentenv_protocol/a2a_agent/staging.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/src/agentenv_protocol/a2a_agent/tasks/__init__.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/src/agentenv_protocol/a2a_agent/tasks/v1.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/src/agentenv_protocol/computer/_declare.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/src/agentenv_protocol/computer/_linux.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/src/agentenv_protocol/computer/_load.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/src/agentenv_protocol/computer/card.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/src/agentenv_protocol/computer/checks.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/src/agentenv_protocol/computer/conformance/fixture.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/src/agentenv_protocol/computer/conformance/mp4.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/src/agentenv_protocol/computer/conformance/report.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/src/agentenv_protocol/computer/errors.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/src/agentenv_protocol/computer/extensions.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/src/agentenv_protocol/computer/frames.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/src/agentenv_protocol/computer/keys.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/src/agentenv_protocol/computer/launch.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/src/agentenv_protocol/computer/manifest.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/src/agentenv_protocol/computer/operations.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/src/agentenv_protocol/computer/platform.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/src/agentenv_protocol/computer/queue.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/src/agentenv_protocol/computer/recording.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/src/agentenv_protocol/computer/results.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/src/agentenv_protocol/computer/run_install_step.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/src/agentenv_protocol/computer/serve.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/src/agentenv_protocol/computer/settings.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/src/agentenv_protocol/computer/skill.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/src/agentenv_protocol/computer/spaces.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/src/agentenv_protocol/computer/tools.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/src/agentenv_protocol/errors.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/src/agentenv_protocol/manifest.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/src/agentenv_protocol/preflight.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/src/agentenv_protocol/tool_access.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/src/agentenv_protocol/tool_client.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/src/agentenv_protocol/tool_context.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/src/agentenv_protocol/transfers.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/tests/computer_fake.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/tests/probe_fake.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/tests/test_a2a_agent.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/tests/test_a2a_agent_examples.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/tests/test_a2a_object_transfers.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/tests/test_a2a_peer_agents.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/tests/test_a2a_proxy.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/tests/test_a2a_staging.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/tests/test_a2a_trajectory_log.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/tests/test_agent_env_environment.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/tests/test_computer_conformance_checks.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/tests/test_computer_conformance_recording.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/tests/test_computer_dataplane.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/tests/test_computer_declare.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/tests/test_computer_driver.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/tests/test_computer_extensions.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/tests/test_computer_frames.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/tests/test_computer_queue.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/tests/test_computer_recording.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/tests/test_computer_spaces.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/tests/test_data_objects.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/tests/test_errors.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/tests/test_extension_methods.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/tests/test_intake.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/tests/test_manifest.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/tests/test_preflight.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/tests/test_tool_access.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/tests/test_tool_annotations.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/tests/test_tool_client.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/tests/test_tool_context.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/tests/test_tool_context_fastmcp.py +0 -0
- {agentenv_framework_protocol-0.1.419 → agentenv_framework_protocol-0.1.421}/tests/test_tool_context_injection.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: agentenv-framework-protocol
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.421
|
|
4
4
|
Summary: Open data-plane protocol and server SDK for agent environments.
|
|
5
5
|
Project-URL: Homepage, https://www.agentenvframework.com
|
|
6
6
|
Project-URL: Documentation, https://www.agentenvframework.com/docs
|
|
@@ -283,6 +283,23 @@ async def display_get(self) -> dict: ...
|
|
|
283
283
|
async def display_set(self, width: int, height: int) -> dict: ...
|
|
284
284
|
```
|
|
285
285
|
|
|
286
|
+
### Framework-only routes and the control token
|
|
287
|
+
|
|
288
|
+
Everything an environment serves but its MCP endpoint and its card is agent-env's own: the data
|
|
289
|
+
plane, every `@extension` route and the tool-access routes. When agent-env deploys an environment
|
|
290
|
+
that takes one, it boots it with a per-deploy token in `AGENTENV_CONTROL_TOKEN`
|
|
291
|
+
(`CONTROL_TOKEN_ENV`), and the SDK then refuses a call to those routes that doesn't carry it as
|
|
292
|
+
`Authorization: Bearer <token>`, answering 401 with the code `unauthorized` before the route runs.
|
|
293
|
+
The MCP endpoint and the card stay open, so an agent reaches the environment's tools as before and
|
|
294
|
+
nothing else. Without the variable, as when the environment runs on its own, every route is open.
|
|
295
|
+
`control_guard()` wraps a route an environment adds itself the same way, as a computer driver does
|
|
296
|
+
its `/svc/<app>/` routes.
|
|
297
|
+
|
|
298
|
+
On the client side each call takes `headers`, and `client.control_headers(token)` builds them.
|
|
299
|
+
Code that names none runs under `client.authorized({env_url: client.control_headers(token)})`:
|
|
300
|
+
within it, a call to an address under `env_url` sends those headers. agent-env runs every task
|
|
301
|
+
step, a verifier's `verify` included, under such a grant for each environment it deployed.
|
|
302
|
+
|
|
286
303
|
### Tool access without a gateway
|
|
287
304
|
|
|
288
305
|
An environment agents reach directly, with no gateway in front, can enforce per-role tool access
|
|
@@ -561,8 +578,8 @@ driver has them, through the `served_tools`, `served_extensions` and `served_ope
|
|
|
561
578
|
or a `transfers.ReadObject` read grant under the marker's `read`, and writes them atomically.
|
|
562
579
|
`data/get` returns the files under the `paths` it's given, relative to the home, or under the
|
|
563
580
|
card's `default_data_paths` (`Desktop`, `Documents`, `Downloads`) when it's given none, inline or
|
|
564
|
-
uploaded under a `write_namespace` grant
|
|
565
|
-
refuses them.
|
|
581
|
+
uploaded under a `write_namespace` grant, leaving out the paths its apps keep their data in. An
|
|
582
|
+
environment whose `@get_data` hook takes no `paths` refuses them.
|
|
566
583
|
- **The card** (the name from `ENVIRONMENT_NAME`, then `@computer_driver`'s), and per-role tool
|
|
567
584
|
access (`tool_access` is on).
|
|
568
585
|
|
|
@@ -644,7 +661,10 @@ report = await run_probe("http://localhost:18765", "full") # a ProbeReport
|
|
|
644
661
|
failed = [c for c in report.checks if c.status == "failed"]
|
|
645
662
|
```
|
|
646
663
|
|
|
647
|
-
The CLI exits 0 when no check failed, 1 when one did and 2 when it can't reach the driver.
|
|
664
|
+
The CLI exits 0 when no check failed, 1 when one did and 2 when it can't reach the driver. A
|
|
665
|
+
driver booted with a control token is probed with it: `--token`, by default
|
|
666
|
+
`$AGENTENV_CONTROL_TOKEN`, or `run_probe(..., token=...)`. Given one, the probe also checks that
|
|
667
|
+
`computer-ready`'s status refuses a call without it and with a wrong one.
|
|
648
668
|
|
|
649
669
|
| Level | Checks | Needs on the computer |
|
|
650
670
|
|---|---|---|
|
|
@@ -730,8 +750,10 @@ class MailApp(ComputerAppEnvironment):
|
|
|
730
750
|
naming one the app declares no install for is refused when the class is read. A step reads
|
|
731
751
|
`self.build_settings`; a deploy's `self.settings` aren't known at build, and reading one raises
|
|
732
752
|
`AttributeError` (`python -m agentenv_protocol.computer.run_install_step <file>:<Class> <method>`).
|
|
733
|
-
- **Its skill** is a file of its folder (`skill = "skill.md"`) or an `@agent_skill` method, not both
|
|
734
|
-
|
|
753
|
+
- **Its skill** is a file of its folder (`skill = "skill.md"`) or an `@agent_skill` method, not both.
|
|
754
|
+
- **Its data** is under the `paths` its OS's decorator names, relative to the session user's home,
|
|
755
|
+
none inside another (`@linux(..., paths=["Mail", ".config/mail/accounts.json"])`), or what its own
|
|
756
|
+
`@add_data`, `@get_data` and `@reset_data` hooks serve instead (below).
|
|
735
757
|
- **`Settings`** is typed; a computer hands each app the values a deploy gives it, as one JSON value
|
|
736
758
|
in `APP_SETTINGS_ENV` (`AGENTENV_APP_SETTINGS`, the apps' values by name), and the base class reads
|
|
737
759
|
its own, by `ENVIRONMENT_NAME`, as `self.settings`. A `SecretStr` field is a secret.
|
|
@@ -757,6 +779,18 @@ folder: the computer is ready once every app's server answers and each `boot` ap
|
|
|
757
779
|
no launch, says it's ready; a `demand` app counts once something launches it. What the server does
|
|
758
780
|
in the session (`agentenv_protocol.computer.session`) is Linux's.
|
|
759
781
|
|
|
782
|
+
The server serves the app's data plane and its skill. With `paths` alone, `data/add` writes the files
|
|
783
|
+
it's given under them and refuses any other path, or one a link leads out of them; `data/get`
|
|
784
|
+
returns the files there, a file tree; and `data/reset` puts them back as the server found them when
|
|
785
|
+
it first started on this boot, which is the app as installed. Hooks serve what they mark instead.
|
|
786
|
+
A hook's `data/add` parts come as they were sent, inline or as a read grant, and
|
|
787
|
+
`part_bytes(part)` returns a part's bytes either way. Either way the base class takes data calls and
|
|
788
|
+
launches one at a time, runs a load and a reset with the app stopped and relaunches it after,
|
|
789
|
+
follows a failed load with a reset, and fails a call that runs past `data_timeout_s` (300 s); a sync
|
|
790
|
+
hook runs in a thread, which the reset and relaunch after it wait for. The driver fronts its apps:
|
|
791
|
+
its card lists each one's card as a child environment, rooted at `/svc/<app>/`, where it forwards
|
|
792
|
+
requests to the app's server.
|
|
793
|
+
|
|
760
794
|
`MailApp.declaration()` returns what the class declares, an `AppDeclaration`, which a `computer_app`
|
|
761
795
|
env stores, and `python -m agentenv_protocol.computer._declare <folder> <file> <class>` prints it as
|
|
762
796
|
JSON, or a driver's `DriverDeclaration`, without the class's own dependencies installed.
|
|
@@ -227,6 +227,23 @@ async def display_get(self) -> dict: ...
|
|
|
227
227
|
async def display_set(self, width: int, height: int) -> dict: ...
|
|
228
228
|
```
|
|
229
229
|
|
|
230
|
+
### Framework-only routes and the control token
|
|
231
|
+
|
|
232
|
+
Everything an environment serves but its MCP endpoint and its card is agent-env's own: the data
|
|
233
|
+
plane, every `@extension` route and the tool-access routes. When agent-env deploys an environment
|
|
234
|
+
that takes one, it boots it with a per-deploy token in `AGENTENV_CONTROL_TOKEN`
|
|
235
|
+
(`CONTROL_TOKEN_ENV`), and the SDK then refuses a call to those routes that doesn't carry it as
|
|
236
|
+
`Authorization: Bearer <token>`, answering 401 with the code `unauthorized` before the route runs.
|
|
237
|
+
The MCP endpoint and the card stay open, so an agent reaches the environment's tools as before and
|
|
238
|
+
nothing else. Without the variable, as when the environment runs on its own, every route is open.
|
|
239
|
+
`control_guard()` wraps a route an environment adds itself the same way, as a computer driver does
|
|
240
|
+
its `/svc/<app>/` routes.
|
|
241
|
+
|
|
242
|
+
On the client side each call takes `headers`, and `client.control_headers(token)` builds them.
|
|
243
|
+
Code that names none runs under `client.authorized({env_url: client.control_headers(token)})`:
|
|
244
|
+
within it, a call to an address under `env_url` sends those headers. agent-env runs every task
|
|
245
|
+
step, a verifier's `verify` included, under such a grant for each environment it deployed.
|
|
246
|
+
|
|
230
247
|
### Tool access without a gateway
|
|
231
248
|
|
|
232
249
|
An environment agents reach directly, with no gateway in front, can enforce per-role tool access
|
|
@@ -505,8 +522,8 @@ driver has them, through the `served_tools`, `served_extensions` and `served_ope
|
|
|
505
522
|
or a `transfers.ReadObject` read grant under the marker's `read`, and writes them atomically.
|
|
506
523
|
`data/get` returns the files under the `paths` it's given, relative to the home, or under the
|
|
507
524
|
card's `default_data_paths` (`Desktop`, `Documents`, `Downloads`) when it's given none, inline or
|
|
508
|
-
uploaded under a `write_namespace` grant
|
|
509
|
-
refuses them.
|
|
525
|
+
uploaded under a `write_namespace` grant, leaving out the paths its apps keep their data in. An
|
|
526
|
+
environment whose `@get_data` hook takes no `paths` refuses them.
|
|
510
527
|
- **The card** (the name from `ENVIRONMENT_NAME`, then `@computer_driver`'s), and per-role tool
|
|
511
528
|
access (`tool_access` is on).
|
|
512
529
|
|
|
@@ -588,7 +605,10 @@ report = await run_probe("http://localhost:18765", "full") # a ProbeReport
|
|
|
588
605
|
failed = [c for c in report.checks if c.status == "failed"]
|
|
589
606
|
```
|
|
590
607
|
|
|
591
|
-
The CLI exits 0 when no check failed, 1 when one did and 2 when it can't reach the driver.
|
|
608
|
+
The CLI exits 0 when no check failed, 1 when one did and 2 when it can't reach the driver. A
|
|
609
|
+
driver booted with a control token is probed with it: `--token`, by default
|
|
610
|
+
`$AGENTENV_CONTROL_TOKEN`, or `run_probe(..., token=...)`. Given one, the probe also checks that
|
|
611
|
+
`computer-ready`'s status refuses a call without it and with a wrong one.
|
|
592
612
|
|
|
593
613
|
| Level | Checks | Needs on the computer |
|
|
594
614
|
|---|---|---|
|
|
@@ -674,8 +694,10 @@ class MailApp(ComputerAppEnvironment):
|
|
|
674
694
|
naming one the app declares no install for is refused when the class is read. A step reads
|
|
675
695
|
`self.build_settings`; a deploy's `self.settings` aren't known at build, and reading one raises
|
|
676
696
|
`AttributeError` (`python -m agentenv_protocol.computer.run_install_step <file>:<Class> <method>`).
|
|
677
|
-
- **Its skill** is a file of its folder (`skill = "skill.md"`) or an `@agent_skill` method, not both
|
|
678
|
-
|
|
697
|
+
- **Its skill** is a file of its folder (`skill = "skill.md"`) or an `@agent_skill` method, not both.
|
|
698
|
+
- **Its data** is under the `paths` its OS's decorator names, relative to the session user's home,
|
|
699
|
+
none inside another (`@linux(..., paths=["Mail", ".config/mail/accounts.json"])`), or what its own
|
|
700
|
+
`@add_data`, `@get_data` and `@reset_data` hooks serve instead (below).
|
|
679
701
|
- **`Settings`** is typed; a computer hands each app the values a deploy gives it, as one JSON value
|
|
680
702
|
in `APP_SETTINGS_ENV` (`AGENTENV_APP_SETTINGS`, the apps' values by name), and the base class reads
|
|
681
703
|
its own, by `ENVIRONMENT_NAME`, as `self.settings`. A `SecretStr` field is a secret.
|
|
@@ -701,6 +723,18 @@ folder: the computer is ready once every app's server answers and each `boot` ap
|
|
|
701
723
|
no launch, says it's ready; a `demand` app counts once something launches it. What the server does
|
|
702
724
|
in the session (`agentenv_protocol.computer.session`) is Linux's.
|
|
703
725
|
|
|
726
|
+
The server serves the app's data plane and its skill. With `paths` alone, `data/add` writes the files
|
|
727
|
+
it's given under them and refuses any other path, or one a link leads out of them; `data/get`
|
|
728
|
+
returns the files there, a file tree; and `data/reset` puts them back as the server found them when
|
|
729
|
+
it first started on this boot, which is the app as installed. Hooks serve what they mark instead.
|
|
730
|
+
A hook's `data/add` parts come as they were sent, inline or as a read grant, and
|
|
731
|
+
`part_bytes(part)` returns a part's bytes either way. Either way the base class takes data calls and
|
|
732
|
+
launches one at a time, runs a load and a reset with the app stopped and relaunches it after,
|
|
733
|
+
follows a failed load with a reset, and fails a call that runs past `data_timeout_s` (300 s); a sync
|
|
734
|
+
hook runs in a thread, which the reset and relaunch after it wait for. The driver fronts its apps:
|
|
735
|
+
its card lists each one's card as a child environment, rooted at `/svc/<app>/`, where it forwards
|
|
736
|
+
requests to the app's server.
|
|
737
|
+
|
|
704
738
|
`MailApp.declaration()` returns what the class declares, an `AppDeclaration`, which a `computer_app`
|
|
705
739
|
env stores, and `python -m agentenv_protocol.computer._declare <folder> <file> <class>` prints it as
|
|
706
740
|
JSON, or a driver's `DriverDeclaration`, without the class's own dependencies installed.
|
|
@@ -53,6 +53,7 @@ from .manifest import (
|
|
|
53
53
|
manifest_compatible,
|
|
54
54
|
)
|
|
55
55
|
from .types import (
|
|
56
|
+
CONTROL_TOKEN_ENV,
|
|
56
57
|
DATA_OBJECTS_EXTENSION_URI,
|
|
57
58
|
INTAKE_EXTENSION_URI,
|
|
58
59
|
MCP_PATH,
|
|
@@ -161,6 +162,7 @@ __all__ = [
|
|
|
161
162
|
"ExtensionCallError",
|
|
162
163
|
"PROTOCOL_VERSION",
|
|
163
164
|
"RPC_PATH",
|
|
165
|
+
"CONTROL_TOKEN_ENV",
|
|
164
166
|
"WELL_KNOWN_PATH",
|
|
165
167
|
"METHOD_RESET",
|
|
166
168
|
"METHOD_ADD",
|
|
@@ -5,6 +5,7 @@ import datetime
|
|
|
5
5
|
import decimal
|
|
6
6
|
import enum
|
|
7
7
|
import functools
|
|
8
|
+
import hmac
|
|
8
9
|
import inspect
|
|
9
10
|
import json
|
|
10
11
|
import logging
|
|
@@ -14,7 +15,8 @@ import sys
|
|
|
14
15
|
import types
|
|
15
16
|
import uuid
|
|
16
17
|
from abc import ABC, abstractmethod
|
|
17
|
-
from typing import Annotated, Any, Callable, ClassVar, Literal, NamedTuple, Union, get_args, get_origin,
|
|
18
|
+
from typing import (Annotated, Any, Callable, ClassVar, Literal, NamedTuple, Optional, Union, get_args, get_origin,
|
|
19
|
+
get_type_hints)
|
|
18
20
|
|
|
19
21
|
from pydantic import TypeAdapter, ValidationError
|
|
20
22
|
from pydantic.fields import FieldInfo
|
|
@@ -27,7 +29,7 @@ from .tool_access import DISABLE as TOOL_DISABLE, ENABLE as TOOL_ENABLE, PATHS a
|
|
|
27
29
|
from .tool_access import extensions as tool_access_extensions
|
|
28
30
|
from .tool_context import EMPTY, ToolContext, bound, context_from_http, context_from_mcp
|
|
29
31
|
from .transfers import WriteNamespaceGrant
|
|
30
|
-
from .types import DATA_OBJECTS_EXTENSION_URI, MCP_TRANSPORT, SKILL_EXTENSION_URI, METHOD_ADD, METHOD_GET, METHOD_RESET, RPC_PATH, WELL_KNOWN_PATH, AddDataRequest, AddDataResponse, EnvironmentCapabilities, EnvironmentCard, EnvironmentExtension, EnvironmentInterface, EnvironmentTool, GetDataResponse, ResetDataResponse, error_body
|
|
32
|
+
from .types import CONTROL_TOKEN_ENV, DATA_OBJECTS_EXTENSION_URI, MCP_TRANSPORT, SKILL_EXTENSION_URI, METHOD_ADD, METHOD_GET, METHOD_RESET, RPC_PATH, WELL_KNOWN_PATH, AddDataRequest, AddDataResponse, EnvironmentCapabilities, EnvironmentCard, EnvironmentExtension, EnvironmentInterface, EnvironmentTool, GetDataResponse, ResetDataResponse, error_body
|
|
31
33
|
|
|
32
34
|
logger = logging.getLogger(__name__)
|
|
33
35
|
|
|
@@ -342,10 +344,11 @@ class AgentEnvApplication(ABC):
|
|
|
342
344
|
if isinstance(self.handler, AgentEnvEnvironment):
|
|
343
345
|
self.handler._application = self
|
|
344
346
|
self.environment_card = self._card_for(app)
|
|
345
|
-
|
|
347
|
+
guard = control_guard()
|
|
348
|
+
self._add_route(app, rpc_url, ["POST"], guard(self._dispatch))
|
|
346
349
|
self._add_route(app, card_url, ["GET"], self._serve_card)
|
|
347
350
|
for path, method, handler in self._ext_routes:
|
|
348
|
-
self._add_route(app, path, [method], handler)
|
|
351
|
+
self._add_route(app, path, [method], guard(handler))
|
|
349
352
|
for descriptor, fn in self._tools:
|
|
350
353
|
self._register_tool(app, descriptor, fn)
|
|
351
354
|
for resource in self._resources:
|
|
@@ -376,7 +379,10 @@ class AgentEnvApplication(ABC):
|
|
|
376
379
|
return self.environment_card
|
|
377
380
|
|
|
378
381
|
async def _serve_card(self, request: Request) -> Response:
|
|
379
|
-
|
|
382
|
+
card = self.environment_card
|
|
383
|
+
if isinstance(self.handler, AgentEnvEnvironment) and (children := await self.handler.children_environments()):
|
|
384
|
+
card = card.model_copy(update={"children_environments": children})
|
|
385
|
+
return _json_response(card.model_dump())
|
|
380
386
|
|
|
381
387
|
async def update_tool(self, name: str, *, description: str) -> None:
|
|
382
388
|
"""Change a served tool's description after the mount: on the card and in tools/list alike."""
|
|
@@ -662,6 +668,11 @@ class AgentEnvEnvironment:
|
|
|
662
668
|
"""Declaration-only extensions to advertise, like ``intake/v1``: a promise with params and no handler."""
|
|
663
669
|
return []
|
|
664
670
|
|
|
671
|
+
async def children_environments(self) -> Optional[list[EnvironmentCard]]:
|
|
672
|
+
"""The environments this one fronts, as each serves its card with its paths under this one's, listed on the card
|
|
673
|
+
each time it's served; None for none."""
|
|
674
|
+
return None
|
|
675
|
+
|
|
665
676
|
def served_extensions(self, extensions: list[EnvironmentExtension]) -> list[EnvironmentExtension]:
|
|
666
677
|
"""The ``@extension`` handlers' extensions to serve, chosen at mount: one left out is not served, and of one served
|
|
667
678
|
with ``op``, only the methods its returned descriptor still lists."""
|
|
@@ -1535,6 +1546,25 @@ def _error_response(error: AgentEnvError) -> Response:
|
|
|
1535
1546
|
return _json_response(error_body(**error.fields()), error.status)
|
|
1536
1547
|
|
|
1537
1548
|
|
|
1549
|
+
def control_guard() -> Callable[[Callable], Callable]:
|
|
1550
|
+
"""What wraps a framework-only route, everything an env serves but its MCP endpoint and its card: with
|
|
1551
|
+
``CONTROL_TOKEN_ENV`` set, a call that doesn't carry that token as its bearer is refused before the route runs;
|
|
1552
|
+
without it, nothing."""
|
|
1553
|
+
token = os.environ.get(CONTROL_TOKEN_ENV)
|
|
1554
|
+
if not token:
|
|
1555
|
+
return lambda handler: handler
|
|
1556
|
+
expected = f"Bearer {token}".encode()
|
|
1557
|
+
|
|
1558
|
+
def guard(handler: Callable) -> Callable:
|
|
1559
|
+
async def guarded(request: Request) -> Response:
|
|
1560
|
+
if not hmac.compare_digest(request.headers.get("authorization", "").encode(), expected):
|
|
1561
|
+
return _error_response(AgentEnvError(
|
|
1562
|
+
"unauthorized", "this route is agent-env's own: it takes the env's control token", status=401))
|
|
1563
|
+
return await handler(request)
|
|
1564
|
+
return guarded
|
|
1565
|
+
return guard
|
|
1566
|
+
|
|
1567
|
+
|
|
1538
1568
|
def _tool_access_handler(access: ToolAccess, action: str) -> Callable:
|
|
1539
1569
|
async def handler(request: Request) -> Response:
|
|
1540
1570
|
raw = await request.body()
|
|
@@ -1,7 +1,12 @@
|
|
|
1
|
-
"""Data-plane wire protocol client (JSON-RPC) for servers implementing the AgentEnv protocol.
|
|
1
|
+
"""Data-plane wire protocol client (JSON-RPC) for servers implementing the AgentEnv protocol. A call to an env's
|
|
2
|
+
framework-only routes carries its control token: as the call's ``headers`` (``control_headers``), or, for code that
|
|
3
|
+
names none, from the grant it runs under (``authorized``)."""
|
|
2
4
|
from __future__ import annotations
|
|
3
5
|
|
|
6
|
+
import contextlib
|
|
4
7
|
import logging
|
|
8
|
+
from collections.abc import Iterator
|
|
9
|
+
from contextvars import ContextVar
|
|
5
10
|
|
|
6
11
|
import httpx
|
|
7
12
|
|
|
@@ -11,6 +16,9 @@ from .types import INTAKE_EXTENSION_URI, MCP_PATH, MCP_TRANSPORT, METHOD_ADD, ME
|
|
|
11
16
|
|
|
12
17
|
logger = logging.getLogger(__name__)
|
|
13
18
|
|
|
19
|
+
#: The headers that carry the control token of each env address the running code is granted, by address.
|
|
20
|
+
_GRANTS: ContextVar[dict[str, dict[str, str]]] = ContextVar("agentenv_control_grants", default={})
|
|
21
|
+
|
|
14
22
|
|
|
15
23
|
class ExtensionCallError(httpx.HTTPStatusError):
|
|
16
24
|
"""A non-2xx answer from an extension method, with the error fields the server sent. A body without them
|
|
@@ -35,10 +43,45 @@ def _error_fields(response: httpx.Response) -> dict:
|
|
|
35
43
|
return error if isinstance(error, dict) else {}
|
|
36
44
|
|
|
37
45
|
|
|
38
|
-
|
|
46
|
+
def control_headers(token: str | None) -> dict[str, str]:
|
|
47
|
+
"""The headers that carry an env's control token to its framework-only routes; none without one."""
|
|
48
|
+
return {"Authorization": f"Bearer {token}"} if token else {}
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@contextlib.contextmanager
|
|
52
|
+
def authorized(grants: dict[str | None, dict[str, str]]) -> Iterator[None]:
|
|
53
|
+
"""Within it, a call to an address under one of ``grants``, an env's address and the headers that carry its
|
|
54
|
+
control token (``control_headers``), sends those headers when the call names none of its own: how the steps
|
|
55
|
+
agent-env runs, and the verifiers they call, reach an env's framework-only routes as written."""
|
|
56
|
+
granted = _GRANTS.set({**_GRANTS.get(),
|
|
57
|
+
**{url.rstrip("/"): sent for url, sent in grants.items() if url and sent}})
|
|
58
|
+
try:
|
|
59
|
+
yield
|
|
60
|
+
finally:
|
|
61
|
+
_GRANTS.reset(granted)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def request_headers(url: str, headers: dict[str, str] | None = None) -> dict[str, str]:
|
|
65
|
+
"""What a call to ``url`` sends: ``headers`` when it names them, else the headers its grant gives
|
|
66
|
+
(``authorized``), if any."""
|
|
67
|
+
if headers is not None:
|
|
68
|
+
return headers
|
|
69
|
+
return granted_headers(url, _GRANTS.get())
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def granted_headers(url: str, grants: dict[str | None, dict[str, str]]) -> dict[str, str]:
|
|
73
|
+
"""What ``grants``, env addresses and the headers that carry their control tokens, give a call to ``url``: those of
|
|
74
|
+
the address it is or is under, and none anywhere else."""
|
|
75
|
+
return next((sent for base, sent in grants.items()
|
|
76
|
+
if base and (url == base.rstrip("/") or url.startswith(f"{base.rstrip('/')}/"))), {})
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
async def _rpc(base_url: str, method: str, params: dict, timeout: int, verify: bool,
|
|
80
|
+
headers: dict[str, str] | None) -> dict:
|
|
39
81
|
request = {"jsonrpc": "2.0", "id": 1, "method": method, "params": params}
|
|
82
|
+
url = f"{base_url}{RPC_PATH}"
|
|
40
83
|
async with httpx.AsyncClient(verify=verify) as client:
|
|
41
|
-
response = await client.post(
|
|
84
|
+
response = await client.post(url, json=request, timeout=timeout, headers=request_headers(url, headers))
|
|
42
85
|
response.raise_for_status()
|
|
43
86
|
body = response.json()
|
|
44
87
|
if "error" in body:
|
|
@@ -47,18 +90,20 @@ async def _rpc(base_url: str, method: str, params: dict, timeout: int, verify: b
|
|
|
47
90
|
return body["result"]
|
|
48
91
|
|
|
49
92
|
|
|
50
|
-
async def reset_data(base_url: str, timeout: int = 30, verify: bool = True
|
|
51
|
-
|
|
93
|
+
async def reset_data(base_url: str, timeout: int = 30, verify: bool = True, *,
|
|
94
|
+
headers: dict[str, str] | None = None) -> ResetDataResponse:
|
|
95
|
+
return ResetDataResponse.model_validate(await _rpc(base_url, METHOD_RESET, {}, timeout, verify, headers))
|
|
52
96
|
|
|
53
97
|
|
|
54
|
-
async def add_data(base_url: str, parts: list[Part], timeout: int = 120, verify: bool = True
|
|
98
|
+
async def add_data(base_url: str, parts: list[Part], timeout: int = 120, verify: bool = True, *,
|
|
99
|
+
headers: dict[str, str] | None = None) -> AddDataResponse:
|
|
55
100
|
params = {"parts": [p.model_dump(mode="json", exclude_none=True) for p in parts]}
|
|
56
|
-
return AddDataResponse.model_validate(await _rpc(base_url, METHOD_ADD, params, timeout, verify))
|
|
101
|
+
return AddDataResponse.model_validate(await _rpc(base_url, METHOD_ADD, params, timeout, verify, headers))
|
|
57
102
|
|
|
58
103
|
|
|
59
104
|
async def get_data(
|
|
60
105
|
base_url: str, timeout: int = 30, verify: bool = True, *, write_namespace: WriteNamespaceGrant | None = None,
|
|
61
|
-
paths: list[str] | None = None,
|
|
106
|
+
paths: list[str] | None = None, headers: dict[str, str] | None = None,
|
|
62
107
|
) -> GetDataResponse:
|
|
63
108
|
"""`data/get`. A server advertising `DATA_OBJECTS_EXTENSION_URI` may be handed `write_namespace` to upload
|
|
64
109
|
its export under; `uploaded_object_path` reads from the answer where it did. `paths` names what a computer
|
|
@@ -66,7 +111,7 @@ async def get_data(
|
|
|
66
111
|
params = {} if write_namespace is None else {"write_namespace": write_namespace.model_dump(mode="json", exclude_none=True)}
|
|
67
112
|
if paths is not None:
|
|
68
113
|
params["paths"] = paths
|
|
69
|
-
return GetDataResponse.model_validate(await _rpc(base_url, METHOD_GET, params, timeout, verify))
|
|
114
|
+
return GetDataResponse.model_validate(await _rpc(base_url, METHOD_GET, params, timeout, verify, headers))
|
|
70
115
|
|
|
71
116
|
|
|
72
117
|
async def get_card(base_url: str, timeout: int = 10, verify: bool = True) -> dict:
|
|
@@ -147,6 +192,12 @@ def intake_declaration(card: dict) -> dict | None:
|
|
|
147
192
|
return params if params else None
|
|
148
193
|
|
|
149
194
|
|
|
195
|
+
def operations(card: dict) -> list[str] | None:
|
|
196
|
+
"""The data-plane methods a card declares it serves (``capabilities.operations``), or None when it declares none,
|
|
197
|
+
as an older card doesn't."""
|
|
198
|
+
return (card.get("capabilities") or {}).get("operations")
|
|
199
|
+
|
|
200
|
+
|
|
150
201
|
def find_tool(card: dict, name: str) -> dict | None:
|
|
151
202
|
"""Return the MCP tool advertised under `name`, or None.
|
|
152
203
|
|
|
@@ -174,7 +225,8 @@ def find_child(card: dict, name: str) -> dict | None:
|
|
|
174
225
|
return None
|
|
175
226
|
|
|
176
227
|
|
|
177
|
-
async def get_interface_manifest(base_url: str, interface: str, endpoint: str = INTERFACE_MANIFEST_PATH, timeout: int = 30, verify: bool = True
|
|
228
|
+
async def get_interface_manifest(base_url: str, interface: str, endpoint: str = INTERFACE_MANIFEST_PATH, timeout: int = 30, verify: bool = True, *,
|
|
229
|
+
headers: dict[str, str] | None = None):
|
|
178
230
|
"""Fetch one interface's manifest from a server's manifest index endpoint.
|
|
179
231
|
|
|
180
232
|
``endpoint`` is the index path — pass the one advertised on the card under
|
|
@@ -185,12 +237,13 @@ async def get_interface_manifest(base_url: str, interface: str, endpoint: str =
|
|
|
185
237
|
"""
|
|
186
238
|
url = f"{base_url.rstrip('/')}{endpoint}/{interface}"
|
|
187
239
|
async with httpx.AsyncClient(verify=verify) as client:
|
|
188
|
-
response = await client.get(url, timeout=timeout)
|
|
240
|
+
response = await client.get(url, timeout=timeout, headers=request_headers(url, headers))
|
|
189
241
|
response.raise_for_status()
|
|
190
242
|
return response.json()
|
|
191
243
|
|
|
192
244
|
|
|
193
|
-
async def invoke_extension(base_url: str, card: dict, uri: str, params: dict | None = None, timeout: int = 30, verify: bool = True, *, method: str | None = None
|
|
245
|
+
async def invoke_extension(base_url: str, card: dict, uri: str, params: dict | None = None, timeout: int = 30, verify: bool = True, *, method: str | None = None,
|
|
246
|
+
headers: dict[str, str] | None = None):
|
|
194
247
|
"""Invoke a card extension via its advertised REST endpoint.
|
|
195
248
|
|
|
196
249
|
Reads the endpoint and HTTP verb of the extension method named `method` from the card, so the
|
|
@@ -209,11 +262,12 @@ async def invoke_extension(base_url: str, card: dict, uri: str, params: dict | N
|
|
|
209
262
|
if not op["endpoint"]:
|
|
210
263
|
raise ValueError(f"extension {uri} has no endpoint in params")
|
|
211
264
|
url = f"{base_url}{op['endpoint']}"
|
|
265
|
+
sent = request_headers(url, headers)
|
|
212
266
|
async with httpx.AsyncClient(verify=verify) as client:
|
|
213
267
|
if op["method"] == "GET":
|
|
214
|
-
response = await client.get(url, params=params or {}, timeout=timeout)
|
|
268
|
+
response = await client.get(url, params=params or {}, timeout=timeout, headers=sent)
|
|
215
269
|
else:
|
|
216
|
-
response = await client.request(op["method"], url, json=params or {}, timeout=timeout)
|
|
270
|
+
response = await client.request(op["method"], url, json=params or {}, timeout=timeout, headers=sent)
|
|
217
271
|
if response.status_code >= 400:
|
|
218
272
|
raise ExtensionCallError(response)
|
|
219
273
|
return response.json()
|
|
@@ -67,6 +67,7 @@ from .operations import (
|
|
|
67
67
|
type_text,
|
|
68
68
|
write_files,
|
|
69
69
|
)
|
|
70
|
+
from .dataplane import part_bytes
|
|
70
71
|
from .keys import PRINTABLE_KEYS
|
|
71
72
|
from .manifest import ManifestSegment, RecordingManifest
|
|
72
73
|
from .platform import Platform
|
|
@@ -107,7 +108,7 @@ __all__ = [
|
|
|
107
108
|
"ComputerAppEnvironment", "computer_app", "install", "linux", "install_step", "ready_check", "Linux", "Service",
|
|
108
109
|
"Launch", "Ready", "Packages", "AptSource", "Download", "RunScript",
|
|
109
110
|
"AppDeclaration", "AppOs",
|
|
110
|
-
"APP_SETTINGS_ENV",
|
|
111
|
+
"APP_SETTINGS_ENV", "part_bytes",
|
|
111
112
|
"Display", "Image", "Effect", "ExecResult", "ReadyCheck", "Platform", "ComputerSettings", "SETTINGS_ENV",
|
|
112
113
|
"PRINTABLE_KEYS",
|
|
113
114
|
"display", "set_display", "capture", "click", "drag", "scroll", "type_text", "press",
|