handler-ai 0.1.0__py3-none-any.whl

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.
Files changed (234) hide show
  1. handler_ai-0.1.0.dist-info/METADATA +60 -0
  2. handler_ai-0.1.0.dist-info/RECORD +234 -0
  3. handler_ai-0.1.0.dist-info/WHEEL +4 -0
  4. handler_ai-0.1.0.dist-info/entry_points.txt +6 -0
  5. handler_cli/__init__.py +1 -0
  6. handler_cli/account_commands.py +314 -0
  7. handler_cli/addon_commands.py +128 -0
  8. handler_cli/agent_commands.py +139 -0
  9. handler_cli/app_commands.py +175 -0
  10. handler_cli/app_window.py +85 -0
  11. handler_cli/automation_commands.py +309 -0
  12. handler_cli/bridge.py +42 -0
  13. handler_cli/cli.py +326 -0
  14. handler_cli/client.py +81 -0
  15. handler_cli/daemon_commands.py +108 -0
  16. handler_cli/image_commands.py +102 -0
  17. handler_cli/inbox_commands.py +33 -0
  18. handler_cli/output.py +12 -0
  19. handler_cli/project_commands.py +63 -0
  20. handler_cli/provider_commands.py +113 -0
  21. handler_cli/secret_commands.py +64 -0
  22. handler_cli/session_bridge.py +93 -0
  23. handler_cli/session_commands.py +485 -0
  24. handler_cli/setup.py +120 -0
  25. handler_cli/terminal.py +280 -0
  26. handler_cli/terminal_client.py +37 -0
  27. handler_contracts/__init__.py +642 -0
  28. handler_contracts/account.py +36 -0
  29. handler_contracts/acp_endpoint.py +69 -0
  30. handler_contracts/addon.py +147 -0
  31. handler_contracts/agent_spec.py +75 -0
  32. handler_contracts/automation.py +285 -0
  33. handler_contracts/common.py +229 -0
  34. handler_contracts/control.py +1208 -0
  35. handler_contracts/harness.py +300 -0
  36. handler_contracts/miniapp.py +141 -0
  37. handler_contracts/provider.py +82 -0
  38. handler_contracts/remote.py +167 -0
  39. handler_contracts/sandbox.py +199 -0
  40. handler_contracts/schemas.py +313 -0
  41. handler_contracts/terminal.py +32 -0
  42. handler_control/__init__.py +7 -0
  43. handler_control/access.py +62 -0
  44. handler_control/accounts.py +279 -0
  45. handler_control/addon_install.py +268 -0
  46. handler_control/addons.py +118 -0
  47. handler_control/agent_state.py +46 -0
  48. handler_control/agents.py +257 -0
  49. handler_control/api/__init__.py +137 -0
  50. handler_control/api/accounts.py +231 -0
  51. handler_control/api/acp.py +116 -0
  52. handler_control/api/addons.py +95 -0
  53. handler_control/api/agents.py +150 -0
  54. handler_control/api/apps.py +104 -0
  55. handler_control/api/automations.py +128 -0
  56. handler_control/api/bindings.py +105 -0
  57. handler_control/api/builtin_agents.py +22 -0
  58. handler_control/api/caches.py +79 -0
  59. handler_control/api/catalog.py +197 -0
  60. handler_control/api/common.py +205 -0
  61. handler_control/api/contexts.py +88 -0
  62. handler_control/api/folders.py +51 -0
  63. handler_control/api/images.py +164 -0
  64. handler_control/api/inbox.py +21 -0
  65. handler_control/api/projects.py +170 -0
  66. handler_control/api/providers.py +107 -0
  67. handler_control/api/remote.py +45 -0
  68. handler_control/api/secrets.py +177 -0
  69. handler_control/api/sessions.py +192 -0
  70. handler_control/api/skills.py +270 -0
  71. handler_control/app_api.py +84 -0
  72. handler_control/app_http.py +214 -0
  73. handler_control/app_remote.py +206 -0
  74. handler_control/apps.py +867 -0
  75. handler_control/auth.py +48 -0
  76. handler_control/automation_config.py +255 -0
  77. handler_control/automations.py +1134 -0
  78. handler_control/bindings.py +149 -0
  79. handler_control/builds.py +341 -0
  80. handler_control/builtin_agents.py +82 -0
  81. handler_control/caches.py +113 -0
  82. handler_control/checks.py +287 -0
  83. handler_control/compose.py +317 -0
  84. handler_control/config_files.py +41 -0
  85. handler_control/contexts.py +75 -0
  86. handler_control/daemon.py +64 -0
  87. handler_control/data/automation/bin/handler-llm +85 -0
  88. handler_control/data/automations/env-issues/automation.toml +17 -0
  89. handler_control/data/automations/env-issues/extract.py +73 -0
  90. handler_control/data/automations/env-issues/findings.schema.json +28 -0
  91. handler_control/data/automations/env-issues/prompt.md +19 -0
  92. handler_control/data/builtin-agents/skill-creator/agent.toml +10 -0
  93. handler_control/data/builtin-agents/skill-creator/instructions.md +5 -0
  94. handler_control/data/builtin-agents/skill-creator/skills/handler-skills/SKILL.md +44 -0
  95. handler_control/data/builtin-agents/skill-creator/skills/skill-creator/LICENSE.txt +202 -0
  96. handler_control/data/builtin-agents/skill-creator/skills/skill-creator/SKILL.md +485 -0
  97. handler_control/data/builtin-agents/skill-creator/skills/skill-creator/agents/analyzer.md +274 -0
  98. handler_control/data/builtin-agents/skill-creator/skills/skill-creator/agents/comparator.md +202 -0
  99. handler_control/data/builtin-agents/skill-creator/skills/skill-creator/agents/grader.md +223 -0
  100. handler_control/data/builtin-agents/skill-creator/skills/skill-creator/assets/eval_review.html +146 -0
  101. handler_control/data/builtin-agents/skill-creator/skills/skill-creator/eval-viewer/generate_review.py +471 -0
  102. handler_control/data/builtin-agents/skill-creator/skills/skill-creator/eval-viewer/viewer.html +1325 -0
  103. handler_control/data/builtin-agents/skill-creator/skills/skill-creator/references/schemas.md +430 -0
  104. handler_control/data/builtin-agents/skill-creator/skills/skill-creator/scripts/__init__.py +0 -0
  105. handler_control/data/builtin-agents/skill-creator/skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
  106. handler_control/data/builtin-agents/skill-creator/skills/skill-creator/scripts/generate_report.py +326 -0
  107. handler_control/data/builtin-agents/skill-creator/skills/skill-creator/scripts/improve_description.py +247 -0
  108. handler_control/data/builtin-agents/skill-creator/skills/skill-creator/scripts/package_skill.py +136 -0
  109. handler_control/data/builtin-agents/skill-creator/skills/skill-creator/scripts/quick_validate.py +103 -0
  110. handler_control/data/builtin-agents/skill-creator/skills/skill-creator/scripts/run_eval.py +310 -0
  111. handler_control/data/builtin-agents/skill-creator/skills/skill-creator/scripts/run_loop.py +328 -0
  112. handler_control/data/builtin-agents/skill-creator/skills/skill-creator/scripts/utils.py +47 -0
  113. handler_control/data/builtin-agents/skill-review/agent.toml +13 -0
  114. handler_control/data/builtin-agents/skill-review/instructions.md +30 -0
  115. handler_control/data/builtin-agents/skill-review/interface.json +83 -0
  116. handler_control/data/interfaces/llm/v1/interface.toml +17 -0
  117. handler_control/data/interfaces/llm/v1/tools.json +138 -0
  118. handler_control/data/templates/README.md +10 -0
  119. handler_control/data/templates/providers/anthropic-api.toml +18 -0
  120. handler_control/data/templates/providers/llamacpp.toml +210 -0
  121. handler_control/data/templates/providers/mistral-api.toml +14 -0
  122. handler_control/data/templates/providers/ollama.toml +10 -0
  123. handler_control/data/templates/providers/openai-api.toml +13 -0
  124. handler_control/data/templates/providers/openrouter.toml +10 -0
  125. handler_control/desktop.py +68 -0
  126. handler_control/display.py +62 -0
  127. handler_control/files.py +23 -0
  128. handler_control/folders.py +68 -0
  129. handler_control/git_read.py +39 -0
  130. handler_control/image_check.py +322 -0
  131. handler_control/image_updates.py +174 -0
  132. handler_control/inbox.py +70 -0
  133. handler_control/interfaces.py +151 -0
  134. handler_control/knowledge.py +237 -0
  135. handler_control/launcher.py +356 -0
  136. handler_control/llm.py +194 -0
  137. handler_control/migrations/__init__.py +62 -0
  138. handler_control/policy.py +305 -0
  139. handler_control/projects.py +266 -0
  140. handler_control/providers.py +180 -0
  141. handler_control/relay.py +133 -0
  142. handler_control/remote.py +888 -0
  143. handler_control/remote_crypto.py +97 -0
  144. handler_control/runtime.py +140 -0
  145. handler_control/sandbox_process.py +224 -0
  146. handler_control/schedules.py +58 -0
  147. handler_control/secrets.py +242 -0
  148. handler_control/service_router.py +180 -0
  149. handler_control/services.py +92 -0
  150. handler_control/session_files.py +429 -0
  151. handler_control/sessions.py +1116 -0
  152. handler_control/settings.py +129 -0
  153. handler_control/shortcuts.py +80 -0
  154. handler_control/skill_scan.py +88 -0
  155. handler_control/skill_sources.py +61 -0
  156. handler_control/skills.py +378 -0
  157. handler_control/terminal.py +79 -0
  158. handler_control/terminals.py +108 -0
  159. handler_control/transcript.py +174 -0
  160. handler_control/web-ui/assets/index-1w5RPsfA.js +190 -0
  161. handler_control/web-ui/assets/index-dWnzCLzQ.css +1 -0
  162. handler_control/web-ui/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
  163. handler_control/web-ui/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
  164. handler_control/web-ui/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
  165. handler_control/web-ui/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
  166. handler_control/web-ui/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
  167. handler_control/web-ui/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
  168. handler_control/web-ui/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
  169. handler_control/web-ui/favicon.svg +1 -0
  170. handler_control/web-ui/index.html +15 -0
  171. handler_control/webpush.py +90 -0
  172. handler_control/webui.py +105 -0
  173. handler_harness/__init__.py +13 -0
  174. handler_harness/addons.py +129 -0
  175. handler_harness/catalog.py +116 -0
  176. handler_harness/cli.py +109 -0
  177. handler_harness/data/addons/README.md +44 -0
  178. handler_harness/data/addons/miniapp/Containerfile +20 -0
  179. handler_harness/data/addons/miniapp/PROVISIONING.md +16 -0
  180. handler_harness/data/addons/miniapp/addon.toml +16 -0
  181. handler_harness/data/addons/miniapp/skills/miniapp/SKILL.md +140 -0
  182. handler_harness/data/addons/miniapp/skills/miniapp/template/.gitignore +2 -0
  183. handler_harness/data/addons/miniapp/skills/miniapp/template/app.toml +15 -0
  184. handler_harness/data/addons/miniapp/skills/miniapp/template/server/db.ts +30 -0
  185. handler_harness/data/addons/miniapp/skills/miniapp/template/server/deno.json +7 -0
  186. handler_harness/data/addons/miniapp/skills/miniapp/template/server/handler.ts +26 -0
  187. handler_harness/data/addons/miniapp/skills/miniapp/template/server/main.ts +18 -0
  188. handler_harness/data/addons/miniapp/skills/miniapp/template/server/main_test.ts +32 -0
  189. handler_harness/data/addons/miniapp/skills/miniapp/template/server/migrations/001_notes.sql +6 -0
  190. handler_harness/data/addons/miniapp/skills/miniapp/template/server/routes.ts +32 -0
  191. handler_harness/data/addons/miniapp/skills/miniapp/template/ui/deno.json +12 -0
  192. handler_harness/data/addons/miniapp/skills/miniapp/template/ui/index.html +12 -0
  193. handler_harness/data/addons/miniapp/skills/miniapp/template/ui/src/App.svelte +44 -0
  194. handler_harness/data/addons/miniapp/skills/miniapp/template/ui/src/api.ts +16 -0
  195. handler_harness/data/addons/miniapp/skills/miniapp/template/ui/src/app.css +24 -0
  196. handler_harness/data/addons/miniapp/skills/miniapp/template/ui/src/main.ts +5 -0
  197. handler_harness/data/addons/miniapp/skills/miniapp/template/ui/vite.config.ts +17 -0
  198. handler_harness/data/catalog/README.md +12 -0
  199. handler_harness/data/catalog/playwright/Containerfile +28 -0
  200. handler_harness/data/catalog/playwright/PROVISIONING.md +53 -0
  201. handler_harness/data/catalog/playwright/addon.toml +25 -0
  202. handler_harness/data/catalog/playwright/handler/playwright-mcp-check.mjs +23 -0
  203. handler_harness/data/catalog/playwright/handler/playwright-mcp.mjs +61 -0
  204. handler_harness/data/catalog/playwright/skills/browser/SKILL.md +41 -0
  205. handler_harness/data/catalog/shopify/Containerfile +22 -0
  206. handler_harness/data/catalog/shopify/PROVISIONING.md +49 -0
  207. handler_harness/data/catalog/shopify/addon.toml +39 -0
  208. handler_harness/data/catalog/shopify/skills/shopify/SKILL.md +41 -0
  209. handler_harness/data/handler-sandbox-env +30 -0
  210. handler_harness/data/harnesses/README.md +47 -0
  211. handler_harness/data/harnesses/claude-code/Containerfile +27 -0
  212. handler_harness/data/harnesses/claude-code/harness.toml +95 -0
  213. handler_harness/data/harnesses/codex/Containerfile +26 -0
  214. handler_harness/data/harnesses/codex/harness.toml +103 -0
  215. handler_harness/data/harnesses/pi/Containerfile +27 -0
  216. handler_harness/data/harnesses/pi/harness.toml +65 -0
  217. handler_harness/data/harnesses/vibe/Containerfile +19 -0
  218. handler_harness/data/harnesses/vibe/harness.toml +96 -0
  219. handler_harness/data/toolbox/Containerfile +56 -0
  220. handler_harness/data/toolbox/README.md +24 -0
  221. handler_harness/data/toolbox/toolbox.toml +41 -0
  222. handler_harness/images.py +62 -0
  223. handler_harness/providers.py +300 -0
  224. handler_harness/render.py +472 -0
  225. handler_sandbox_podman/__init__.py +1 -0
  226. handler_sandbox_podman/caches.py +97 -0
  227. handler_sandbox_podman/check.py +121 -0
  228. handler_sandbox_podman/cli.py +239 -0
  229. handler_sandbox_podman/egress.py +118 -0
  230. handler_sandbox_podman/images.py +199 -0
  231. handler_sandbox_podman/launch.py +474 -0
  232. handler_sandbox_podman/podman.py +108 -0
  233. handler_sandbox_podman/proxy/Containerfile +6 -0
  234. handler_sandbox_podman/proxy/addon.py +255 -0
@@ -0,0 +1,60 @@
1
+ Metadata-Version: 2.5
2
+ Name: handler-ai
3
+ Version: 0.1.0
4
+ Summary: Run coding agents (Claude Code, Codex, Vibe, pi) in rootless Podman sandboxes on Linux
5
+ Project-URL: Homepage, https://github.com/macray/handler-ai
6
+ Project-URL: Documentation, https://macray.github.io/handler-ai/
7
+ Project-URL: Changelog, https://github.com/macray/handler-ai/blob/main/CHANGELOG.md
8
+ Project-URL: Issues, https://github.com/macray/handler-ai/issues
9
+ License-Expression: Apache-2.0
10
+ Keywords: acp,agents,ai,claude-code,codex,podman,sandbox
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Environment :: Web Environment
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Operating System :: POSIX :: Linux
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Topic :: Software Development
17
+ Requires-Python: >=3.12
18
+ Requires-Dist: cryptography>=45
19
+ Requires-Dist: dulwich>=1.2
20
+ Requires-Dist: fastapi>=0.141
21
+ Requires-Dist: httpx>=0.28
22
+ Requires-Dist: jsonschema>=4.23
23
+ Requires-Dist: pydantic<3,>=2.13
24
+ Requires-Dist: pyyaml>=6
25
+ Requires-Dist: tomlkit>=0.13
26
+ Requires-Dist: uvicorn>=0.53
27
+ Requires-Dist: websockets>=17
28
+ Description-Content-Type: text/markdown
29
+
30
+ # Handler
31
+
32
+ **Handler**: every agent needs one.
33
+
34
+ Run coding agents in containers on your Linux machine. Each agent (Claude Code, Codex, Mistral
35
+ Vibe or pi) runs in its own rootless Podman container that sees only its project folder and reaches
36
+ only the hosts you allow. A daemon keeps the sessions, which you follow from a web UI, the
37
+ terminal, your editor (over ACP) or your phone.
38
+
39
+ ## Install
40
+
41
+ **You need:** Linux with rootless Podman, `passt` and a systemd user session, and about 3 GB of
42
+ disk for the images, built on your machine.
43
+
44
+ ```bash
45
+ uv tool install handler-ai # or: pipx install handler-ai
46
+ handler setup # starts handlerd as a user service, checks this machine, builds the images
47
+ handler ui # opens the web UI
48
+ ```
49
+
50
+ The first `handler setup` downloads and builds the images: from a few minutes to half an hour.
51
+ Without uv: `curl -fsSL https://github.com/macray/handler-ai/releases/latest/download/install.sh | sh`
52
+ installs it, then Handler. To update: `uv tool upgrade handler-ai`.
53
+
54
+ ## More
55
+
56
+ - [README](https://github.com/macray/handler-ai#readme): what Handler does, compared with other
57
+ sandboxes, and a first session.
58
+ - [Documentation](https://macray.github.io/handler-ai/)
59
+ - [Changelog](https://github.com/macray/handler-ai/blob/main/CHANGELOG.md) ·
60
+ [Issues](https://github.com/macray/handler-ai/issues)
@@ -0,0 +1,234 @@
1
+ handler_cli/__init__.py,sha256=1VgCiquupUdYG5B7rt6M3zbxntmKWM6ky6R8szDS1e0,98
2
+ handler_cli/account_commands.py,sha256=peiT-VCCN2DGr3UBgD-XYin__hUSDh8YTlXoJgpgq1A,13680
3
+ handler_cli/addon_commands.py,sha256=1SxEXbcUznwqkyOrbxFcE3dXegooK-Pbm4sLP0jcbdE,5356
4
+ handler_cli/agent_commands.py,sha256=rurfdJPx3ydFL8hUILflL361Xm0JqnACpPguTdB6Cqg,6164
5
+ handler_cli/app_commands.py,sha256=l0mLyPZjRBVA2hjAaytpwQULl5ebDF4fxIpL9ErFSa4,7001
6
+ handler_cli/app_window.py,sha256=gV29B8z-NJQtRj8pINKydZ_uOqN_3OAyqfnmH1Y8zaw,3275
7
+ handler_cli/automation_commands.py,sha256=RVhDqcjsQFDQRDGvDAbTMpHQkJIGgw9LCyaZcPFi1vI,12795
8
+ handler_cli/bridge.py,sha256=R6AdT9VvnkyufMl-tXtYTKrDk1OwrhFShjDvhJy9N90,1602
9
+ handler_cli/cli.py,sha256=xU2YmvYcmVddzfqAWX6HMW2sT1WN64r3rfCvuNP_j4g,14904
10
+ handler_cli/client.py,sha256=3IzRnG7GkSK2I0TOEInvIRdDIAYkSR_1fsOJ6OqqRL0,2812
11
+ handler_cli/daemon_commands.py,sha256=RkagiMchdlWtoD-ciDZDPvkLAtpv1kmFSqCEjdC1bck,3472
12
+ handler_cli/image_commands.py,sha256=PhgLBCyS9sZ13mmeVDUiWlZ3pA8vLWvTQEOhGGyq9O0,4119
13
+ handler_cli/inbox_commands.py,sha256=M7chRV-r2d99RRLH3FQW7CeRy2OoaHvm1n8TAZTjooA,1006
14
+ handler_cli/output.py,sha256=SJ6x6-ORzVk40Cilwee3Ln9tnQBX63jwifCR4TR0MfA,425
15
+ handler_cli/project_commands.py,sha256=tzoHRjL8k8I5NtUmbSkeK8tuLdNCxZGIVIfCvuDNkrc,2462
16
+ handler_cli/provider_commands.py,sha256=aSwuLC3_MCjL22ewM8NTQmQ2fAPj7RQyTktBsIuix9s,5482
17
+ handler_cli/secret_commands.py,sha256=I6g3ta5bhWigllmmb_eyr0kZ7k2sOW4QFloLtw8h6qs,2806
18
+ handler_cli/session_bridge.py,sha256=yMeeoZptr4MnGbVF5f6YRiSY--ieeMvCR7G1OxIaucM,4133
19
+ handler_cli/session_commands.py,sha256=E7fG3V2FvOcIbsOmsC27y7NLqRS_2K5KGiIWVkSBzDg,22123
20
+ handler_cli/setup.py,sha256=s_jqJ3MVL-GBy5sbZDWkzKgD_n4Dklj58_ZIw9VOPx4,4663
21
+ handler_cli/terminal.py,sha256=2pBB2lkeFbeJHfOJELAY5MhuYcdiWoDtd2PMoRrhIDA,12057
22
+ handler_cli/terminal_client.py,sha256=ocmCVyOrHRqT95v1vlklURjUsrGxsEzUZOGwMrf3TRw,1627
23
+ handler_contracts/__init__.py,sha256=uEdyK24gkQQaDXvNZX7LXpDP-BzvoQiXQjQiRDEt2Gs,14777
24
+ handler_contracts/account.py,sha256=pd-T_zCv8MVnQYLZpiTktNFJMcM4zR-nYpb5JN0gB7M,1758
25
+ handler_contracts/acp_endpoint.py,sha256=hEjzGWK8oBJxJ-jWGAOMTQWJhMyAHYbBh73Ay3ZsxyE,2311
26
+ handler_contracts/addon.py,sha256=7zq39o2oI79aE-K8XaG9b87m5GW9_p_ncehrjlYaOxQ,6527
27
+ handler_contracts/agent_spec.py,sha256=8ljmD4j2qd01cedU_5lXUzwK2RT2rDxaCXm8mD2WmnI,3157
28
+ handler_contracts/automation.py,sha256=8eeVoxMjlFbE0jkrQjARyaKOsZs8_Mr8De0LmS6xYhw,10864
29
+ handler_contracts/common.py,sha256=MHsph8XtcNsDv7jbRf96VA7XaEMvxR69sPXwHSPv9cs,8264
30
+ handler_contracts/control.py,sha256=vW_cX92NuzIJvoyFouviS5QVnLdk00756fSeuNBZNO4,47775
31
+ handler_contracts/harness.py,sha256=e4sfmPJGqcLprLGQ36RG2ow8FMVYzAlsIEmELENa0RI,13379
32
+ handler_contracts/miniapp.py,sha256=foDgXDvnYBabTMzwEFsL5toLk0X_XpoGYxaOra0pc3Y,5630
33
+ handler_contracts/provider.py,sha256=R3iy4_z-hapzTPZgfxLDHHST4-2kEJzX_3mgH0I1AdI,3754
34
+ handler_contracts/remote.py,sha256=NxbXZrjGmi2dNCYADxqZWa3sQQyp3KjOf4S6YQpnW6E,4886
35
+ handler_contracts/sandbox.py,sha256=MGqKpwQIBiA2bgPsCaEKUBaERJyE6EMj7comTDpE3ys,7299
36
+ handler_contracts/schemas.py,sha256=RagEVAohXA4sSTQGFN8UG2zHKu7OCblWZQmGT72QvI4,10715
37
+ handler_contracts/terminal.py,sha256=lEMfu5blpK0bYDoHbDYBmwGackoDtnnu6aOWMXxPcA4,1134
38
+ handler_control/__init__.py,sha256=AHQb0rB8Lb-tvHPl2PyZHhsUL7p2A-kaJ6hz2FvZht4,294
39
+ handler_control/access.py,sha256=hQ9IDPZyhnZXdwc9ZqAHB8zItMt8InJVeyWCTTwdxDM,2699
40
+ handler_control/accounts.py,sha256=KK9Q89YsYabZBDoW5LYXrn47Q_qnJT2OJ3YFuikZB9E,10459
41
+ handler_control/addon_install.py,sha256=9YXmKu3FemIzYn0EXNAD_7W9DGPOEH6C8cHlzv2esy8,10510
42
+ handler_control/addons.py,sha256=vk_OEjbbI0yFU1ON-hgsCWuiAsjEZ14AnGxv8e-px2I,5226
43
+ handler_control/agent_state.py,sha256=ECIkdH0Dvlw6Fys_2biZiwTOD4QEduZmKpWGSIzPpiA,2000
44
+ handler_control/agents.py,sha256=gdYkP0A10oDsJWXmzB3lE9qyUglP6icHAcsmBHOI3mU,10911
45
+ handler_control/app_api.py,sha256=QH06bi8NylItlhMdXzuNCoBOc2o4HxvOY8qlJ2HfEUU,3375
46
+ handler_control/app_http.py,sha256=I8dj_swK1omdEaPR8EsWsnnnbKDvkZB1swhI18tjiqg,9652
47
+ handler_control/app_remote.py,sha256=BUyBHEirnSzcNACW4IWBY4oGJ3DQ6triwO2Sq02rkfU,8891
48
+ handler_control/apps.py,sha256=9QCk1muB1lncTAnOIdpF1nrjjNnqROWnAMuZKU_0fCs,38403
49
+ handler_control/auth.py,sha256=s3vhQGoPqckaEfMqh34Hkwd-F4Fi2cJ7WAqzUj_tUQA,1619
50
+ handler_control/automation_config.py,sha256=XKWGtELZbiWpzn4ImEKHp5iNXOAYFDEFu-IeQmwHLw4,10160
51
+ handler_control/automations.py,sha256=kmNptFeNXR_fIl6xeuDy6zMi-5F7WmnToh5DHDHMON0,51445
52
+ handler_control/bindings.py,sha256=JvUD0cZi5MBR9HVED2v_vqjZgR6MNHdhC55_sT3fKxg,5572
53
+ handler_control/builds.py,sha256=VVid-YQmbef3qR1N7r-yevQYzPgRMveQotkag7Ffq2I,14879
54
+ handler_control/builtin_agents.py,sha256=9Fd_NZLuMdmYOVYLsuMLH33t2mDfJ-wRtek2XIHxlQk,3149
55
+ handler_control/caches.py,sha256=OYqGZvopS8S6Qk7pH_c1A9-hLEbsCD7ooSh53jfs6b0,4327
56
+ handler_control/checks.py,sha256=FX7zy_Ee6-QBpRIL2vIkQfzt-cHRSZ-31t4VII34F8c,12436
57
+ handler_control/compose.py,sha256=ahJiD1MW1L3SUuLSvoB4XUCZI8urR0sqonLFxQRAwV8,12006
58
+ handler_control/config_files.py,sha256=36P64ss5gvGfAalt4BKzTJDUFIb1bUquEqpoB1MY1MI,1412
59
+ handler_control/contexts.py,sha256=GTLbWYnMQ3xXTGAuN1lJsRnIBO0sgmEV8QuepFvRMnU,3137
60
+ handler_control/daemon.py,sha256=840RRUIP6x1PJIEUpMgufd-7cLkilp0QC__XovSDtss,2073
61
+ handler_control/desktop.py,sha256=esxpMAA_9t3GMxlKR-WRyL2KSHiIHiRrrL7ErmNmbgs,2699
62
+ handler_control/display.py,sha256=WA59tT1xmq_w0sjedPPiN9DIvRQ0AuFXBgmO0524an4,2371
63
+ handler_control/files.py,sha256=EEw44CyZTGoQ9EJu2q13iNfnUPOGXok0_H3LufNfmnI,733
64
+ handler_control/folders.py,sha256=3DSNrPF19PfEtUu48BVie-Eembsumo9PqXtH-Ty1snA,2740
65
+ handler_control/git_read.py,sha256=3dOP8trIWG5SjksVKENft4o3Wpki8MFTWrka48VEhrQ,1477
66
+ handler_control/image_check.py,sha256=Mxq4KTImuSMwnNw7krTUjkDhBVdbo0beDGWZjPI8U6Y,14329
67
+ handler_control/image_updates.py,sha256=7LJncW2_vE6aSVsHa98XEkGMVVSJdANwbfG2shwgUPg,7266
68
+ handler_control/inbox.py,sha256=aP7KNd7XzdtNCcexBnLWgT7BkQvqCRgUB_vlj5DkiXk,2718
69
+ handler_control/interfaces.py,sha256=o5EbP0gY45sZVnwUkbd-ot5FRAlLANWLGEXHAWZdE8U,6522
70
+ handler_control/knowledge.py,sha256=oedlumsLO6R2jSrEt3Hz0x-z9iunY6XQl6cED2d3_pw,9894
71
+ handler_control/launcher.py,sha256=OfkuSL6FzTRGxJCKhFn79u60klxeRbBHeHjcRiNWjag,14206
72
+ handler_control/llm.py,sha256=E9enxXXu3K_TM2ApfxtrW5C4Ga4DSaCvfey3CL1X0dw,8152
73
+ handler_control/policy.py,sha256=UYvniHObbrHawC8HTe1-pPZaXZeaexBSEjmUWHohzRQ,13097
74
+ handler_control/projects.py,sha256=VklzEM_8B8PI7zoCbAKDOtcvu_L_C4VvV23jlVUSa8k,10581
75
+ handler_control/providers.py,sha256=cU4S8tONF5WTVT-30pbCs8YdtYwllMnqZIXr1yZrzlk,6311
76
+ handler_control/relay.py,sha256=5KcF4qnTfw_HB761Wq8phNB2SvsXmwUPa3w4J5y3c0U,5156
77
+ handler_control/remote.py,sha256=AW3YwJYYzsSXVFiyNIo0t2d8sP6RVkP3Sf1XzUpA_s4,38297
78
+ handler_control/remote_crypto.py,sha256=VXZlQSkU1JiFWHH0b87r6MEbGBJrJhrlhw5DkVyDg9Q,3861
79
+ handler_control/runtime.py,sha256=Eh3t6xBQL0Y2Zv-pwuZixkYcp5dKTrXQ39bVlo1yqnk,5435
80
+ handler_control/sandbox_process.py,sha256=B565YLoH8pwwxZksGSlCi0rDtQWa0CkYmIaLDpjD4RA,8395
81
+ handler_control/schedules.py,sha256=Uf8rrigyg2kGP5caVPd4nm8Ci5jI2oG_qot044FCUEo,2708
82
+ handler_control/secrets.py,sha256=Sp1ENmXk6KeldY3iH-ckDLsxJCWym0T4yjq_w7iZ0b0,9261
83
+ handler_control/service_router.py,sha256=Y_y7fMuW2o1eDxesQOXVQt6A6DaWXAxJQBzjZIO0H_c,7812
84
+ handler_control/services.py,sha256=fDOHCUOocXBnLud1Q0oS6rd2vws6_a_Epu3H7CySsUQ,3530
85
+ handler_control/session_files.py,sha256=72Rh-toPeHzBLZKf_9MxL_j6Ed9TPmzWH56GPWfQe0M,16457
86
+ handler_control/sessions.py,sha256=xPRrAY6WzI8BC7NixJ-lVQDC63eMFE8aiOPD86AzTOY,50976
87
+ handler_control/settings.py,sha256=e9UG5lzGOc6W6Gi3mNEJkl9hqK_2exP1Z5KO8nvzkQk,6747
88
+ handler_control/shortcuts.py,sha256=hsFl5vrR67OAO8wKx3WfJOulrvV5SCxmjPNfQoR11f0,2716
89
+ handler_control/skill_scan.py,sha256=BjI-mEikMVGbPb-TLxGDfBXLu_uzuHdqkLcvatkPo14,3643
90
+ handler_control/skill_sources.py,sha256=WymoWDFCXjxVyqedo2Q3RhDDEmafrtfXo8cIb5kxYcI,2600
91
+ handler_control/skills.py,sha256=nhGfggLa2WTI4s7gvPvwFXA4kE_1kqzsN1kkonqL01E,15781
92
+ handler_control/terminal.py,sha256=Uhvc9bYNo-etJq1w8_4xD_iPvuLM0fY66ASflDnT7rw,2907
93
+ handler_control/terminals.py,sha256=lNEiqMc5STt-m62wMIa8g7p5yZGFNNv1y-qZOG6Wy-k,3867
94
+ handler_control/transcript.py,sha256=ZHzUNpgvg-Y1Pkj2rbVltF4NzTSm20qVD2BPFZAF71w,6953
95
+ handler_control/webpush.py,sha256=QyzaJySJL-c_qrCVhahlFdkzm9tp6rXV8HAvgv_Wtyo,3474
96
+ handler_control/webui.py,sha256=ALYd4PlibTbtnNKrJSljHH2ctMmGxtphtzlLYhO-juc,3989
97
+ handler_control/api/__init__.py,sha256=E-5vvIc6_-WI8zbzG4EAwFGyBvXOFZsALgywHdR-Ybc,5709
98
+ handler_control/api/accounts.py,sha256=Z17Q_Y7Ca9Afrhj6AUVu772sRfyH1es2AYliGYWymSE,9756
99
+ handler_control/api/acp.py,sha256=RERJW0ZSz4713XycMn6kV887hq6oJfSstCoeEbgn7rY,4622
100
+ handler_control/api/addons.py,sha256=oaxmq1ZDMMn9DeYwtXUnbHxcQiU8jFIUTRNgwwL1RnE,3971
101
+ handler_control/api/agents.py,sha256=h-sDv20DglAMc6UE_05Kc330WDYH0iMyEL266_LyCkg,6383
102
+ handler_control/api/apps.py,sha256=BgrOYx8hJvFkGxGOWUCUluSa6Rk7XDbB2A1s9slzjac,4179
103
+ handler_control/api/automations.py,sha256=Fa1k7gUGxBEozf7NIr4zL3rQwOQShn-pefCJSr00B0c,5468
104
+ handler_control/api/bindings.py,sha256=V7dtp9DllM8mDhqHkmqAvNlA_dhr1-GffzLTFF3SfqQ,4452
105
+ handler_control/api/builtin_agents.py,sha256=EQDJ2qM44KIkz964VjGXQxnKUweiPOzZWENA4X8BdbU,824
106
+ handler_control/api/caches.py,sha256=bxcKzdopHT5quNYfBPmDa419hvskeqdvRJPn7n3WdYM,3309
107
+ handler_control/api/catalog.py,sha256=9MgCLFUE6jCbSiPtoJPmrypWctDO3faCG0now2dZtKI,7527
108
+ handler_control/api/common.py,sha256=dzdQwW6zblD1auclynll4KC_XbzQXGNbqUVS9pYYXkk,7284
109
+ handler_control/api/contexts.py,sha256=ahUqhToNdLmeYEy8JR-k_Un9DJ1J6CJ2whdbrCOSEIU,3591
110
+ handler_control/api/folders.py,sha256=TbjyPa5-U3e0QdYqdnRcmtUAXh6IBz7PsB_I7O78bEQ,1846
111
+ handler_control/api/images.py,sha256=6VpC_dF6jOCH3AiEJFE3s47iEgATKDKIkRYM8kDJVa8,6290
112
+ handler_control/api/inbox.py,sha256=7UStolnSC88-P7EKWY-8Q3Zdxnl93O7fvVf2atz-Lyw,716
113
+ handler_control/api/projects.py,sha256=9ihmtUgyAkb2apE_47FpeIUHx-saoa8fiyW3XlCHM4I,7118
114
+ handler_control/api/providers.py,sha256=FerD5O4pailjNObAS2vVdUwB_dOOwGFkhgxgZpVhfYs,4460
115
+ handler_control/api/remote.py,sha256=zbVqzMoJPdW6YLmt2XY7R3jtlUD0_DObY6c2HxyhpfQ,1740
116
+ handler_control/api/secrets.py,sha256=spUEtbMhKAEtmyyNPWFWnZ2LUkdxbAdpPrrhpWmtGa8,7242
117
+ handler_control/api/sessions.py,sha256=plg9d0YDqCD8zinuJ9Uj6te8OnrbL6TNTDCT9ayjE0Y,8171
118
+ handler_control/api/skills.py,sha256=vR_6Dpin1XdPzMAXsbE1Tyd7PcXNhO2q8dXSYgbYnK0,11932
119
+ handler_control/data/automation/bin/handler-llm,sha256=6QWsiPUsCtwXfvovlDi5-vLi05A_-eKwppVXePGRaTc,3511
120
+ handler_control/data/automations/env-issues/automation.toml,sha256=kBv4ebS-is1D66WZ00z-FOm6UYjqTPFtIJDLaxXucjk,891
121
+ handler_control/data/automations/env-issues/extract.py,sha256=Me21j2Z7RQXIpDkGO8rNriiVYPZhzNVoRbvusbD_6Q4,2466
122
+ handler_control/data/automations/env-issues/findings.schema.json,sha256=5g-LAd6XTsjEynIBC9wR6MDtRXdnv6OWYivrUhiXFBU,622
123
+ handler_control/data/automations/env-issues/prompt.md,sha256=cKDaL9Cee4LKWNxAox1S4kBffGZqjhpqWngjabAfAV4,1187
124
+ handler_control/data/builtin-agents/skill-creator/agent.toml,sha256=zGr-NMM1Ok7LfBZEg29o43bOiPdMBYmh1ww5wwvYBVI,629
125
+ handler_control/data/builtin-agents/skill-creator/instructions.md,sha256=xJ5IBfGPrhv8I7AYa2etXOFk3U1r0WtTramDnhAcZ1Y,437
126
+ handler_control/data/builtin-agents/skill-creator/skills/handler-skills/SKILL.md,sha256=0II6NFtfz0gK2SmG6qlFwZVWeacF-s2vZGvrKUCPmeg,2449
127
+ handler_control/data/builtin-agents/skill-creator/skills/skill-creator/LICENSE.txt,sha256=vGs68vMxy8f7DaE0Tvssvlh3oxSYtNcNvHAA80BaE2I,11345
128
+ handler_control/data/builtin-agents/skill-creator/skills/skill-creator/SKILL.md,sha256=3NSAPmHpE-b8JylBhM06cfCfXpJP8gyKmiAXPns8K88,33168
129
+ handler_control/data/builtin-agents/skill-creator/skills/skill-creator/agents/analyzer.md,sha256=v2j0ysWlbGc6kowubWGVhsW5PqNkAmqzdUd3LLRaZjo,10376
130
+ handler_control/data/builtin-agents/skill-creator/skills/skill-creator/agents/comparator.md,sha256=_h_JeHxJXYZMXW6tpHOWR4VyMl_eGzOpbXi_S4Sbej4,7287
131
+ handler_control/data/builtin-agents/skill-creator/skills/skill-creator/agents/grader.md,sha256=VxNNoMGk7qM_vXShycRKqBTwfWvGTeMD7bWG-UHl0ho,9049
132
+ handler_control/data/builtin-agents/skill-creator/skills/skill-creator/assets/eval_review.html,sha256=zkd9zHTcHA0dM1JkankWe1pjY06TaxAZFgAlBll05FI,7058
133
+ handler_control/data/builtin-agents/skill-creator/skills/skill-creator/eval-viewer/generate_review.py,sha256=_J0bkkP-WrYBLr1Xm9dtADXeG3n9O5ad4RTe-rJkePs,16365
134
+ handler_control/data/builtin-agents/skill-creator/skills/skill-creator/eval-viewer/viewer.html,sha256=pTITQm7hEARB1wGjoNSc2nqEL5ktLDZGP008wCWFdfo,44998
135
+ handler_control/data/builtin-agents/skill-creator/skills/skill-creator/references/schemas.md,sha256=joh2GAqJibQGpNPt3fh1sEzf1YBcyGFmhtVSsRzkRV8,12061
136
+ handler_control/data/builtin-agents/skill-creator/skills/skill-creator/scripts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
137
+ handler_control/data/builtin-agents/skill-creator/skills/skill-creator/scripts/aggregate_benchmark.py,sha256=Ej7xKOpczAGksawhLvVWfyHpwT09JAYJeAvusyAMSao,14386
138
+ handler_control/data/builtin-agents/skill-creator/skills/skill-creator/scripts/generate_report.py,sha256=E99xGKPFDIPEwyUKYG1fKyCyWj1Ey8OSs9Zp7HUoFFM,12847
139
+ handler_control/data/builtin-agents/skill-creator/skills/skill-creator/scripts/improve_description.py,sha256=h9hkVwIgtpn6xS2jCdLW79sGBke_68dPdoEo5kasz4A,11116
140
+ handler_control/data/builtin-agents/skill-creator/skills/skill-creator/scripts/package_skill.py,sha256=GjMFmw2x73M3XUbVE-XqgTadLog4yXBZew1S3e-NHA8,4234
141
+ handler_control/data/builtin-agents/skill-creator/skills/skill-creator/scripts/quick_validate.py,sha256=Z89XA0AgE5Nsj7da1qGv7NiEHUXMXmBrY06wWCX942U,3972
142
+ handler_control/data/builtin-agents/skill-creator/skills/skill-creator/scripts/run_eval.py,sha256=Q-O4-A2_acNDlnunfiaPrpkdn6PtaLMqD_AlMs1IZX8,11464
143
+ handler_control/data/builtin-agents/skill-creator/skills/skill-creator/scripts/run_loop.py,sha256=e9b2dCAxaFIFF-7JTFX0k8DRVDObBhtNfA8NrRh9DyE,13605
144
+ handler_control/data/builtin-agents/skill-creator/skills/skill-creator/scripts/utils.py,sha256=OviuYsQMc6txIgdDag2amB6EXyXFpwQCKesYnMjkW7E,1661
145
+ handler_control/data/builtin-agents/skill-review/agent.toml,sha256=mHnQB_jXXzOP-jf_y-UcF6F4FXY-UZF1dLicvstT-tw,676
146
+ handler_control/data/builtin-agents/skill-review/instructions.md,sha256=mAj4TRdTq_rZYJHYWJd7VycOAohGWOzZB8_2zAcMzXo,2215
147
+ handler_control/data/builtin-agents/skill-review/interface.json,sha256=lc6B_tyC1B8eC8RcFswj6BEA9DaqhUpYowCpf0LRlQs,2689
148
+ handler_control/data/interfaces/llm/v1/interface.toml,sha256=QPP3ydQSmYaDCIXnSu5FGh9mnUyNgkIGSLleHnpeApw,780
149
+ handler_control/data/interfaces/llm/v1/tools.json,sha256=_S9q677uQUJolGYIC2sXfb0uD7kc7rP-n6xWT2wGvso,3011
150
+ handler_control/data/templates/README.md,sha256=1bqiDC1BHTFreB3GenMhRyOyYb7H5At9zfGxi3egFa4,534
151
+ handler_control/data/templates/providers/anthropic-api.toml,sha256=uIBXxdZ5lu_daN3ur4LfpfT1ppVSe1IEi7E6pPRwOrE,332
152
+ handler_control/data/templates/providers/llamacpp.toml,sha256=gviB-Qm8OHU3_YWrENa3E6WrHCBHCUf-4HVH7g-dQ_I,17552
153
+ handler_control/data/templates/providers/mistral-api.toml,sha256=fFu0rsWYyMFOdwCZFfyv7olcvNLWonlsN51Uq6ceS6k,276
154
+ handler_control/data/templates/providers/ollama.toml,sha256=DGZ0mLso7H6rov-zmFFGe4bT0ygFrEMDkRdKapvwwzs,289
155
+ handler_control/data/templates/providers/openai-api.toml,sha256=GFJqY8RcBITfg3P2JdeXqzc4zIYjqiMm_-YzeNz3p7w,256
156
+ handler_control/data/templates/providers/openrouter.toml,sha256=DyLuMB5UOSpfyAPeqBhOV94BTIiMxqFZJfFUXtR3Vdg,208
157
+ handler_control/migrations/__init__.py,sha256=RoB8DgI4wLaAXFzZC6VTRVR67KSHu4GxbtFE-3v3L9Y,2496
158
+ handler_control/web-ui/favicon.svg,sha256=l7nmpf_PxjUNKE_U6PhDkwPDc9fMgN6e8BYQvx3ZVps,774
159
+ handler_control/web-ui/index.html,sha256=EM6S5P_N4kPFw4xEnSR-2QJ7nDsKPipgxW1L5H7XhkQ,507
160
+ handler_control/web-ui/assets/index-1w5RPsfA.js,sha256=4M62_lok_9g52vXDA0SJ_rhdh8pLUf_p6oJcJh-0VlU,1009903
161
+ handler_control/web-ui/assets/index-dWnzCLzQ.css,sha256=Z2z7TQVOPrmaZYHeq5A3LHEys36KrQMhX3RmXuuKW0A,99761
162
+ handler_control/web-ui/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2,sha256=yhVwYzOaxK1BjyFPOr_tEZsHmKtNN3OGzlyeWnpDXr0,25960
163
+ handler_control/web-ui/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2,sha256=cdXuk8wenx1SCjqLZkVt4Yx4edjfCdV_zS6v91_vAHU,18748
164
+ handler_control/web-ui/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2,sha256=bp4CCiX5tW1BjywIWx08CXJaTaI_5pOltGMGRgZzIZA,11232
165
+ handler_control/web-ui/assets/inter-greek-wght-normal-CkhJZR-_.woff2,sha256=G-NEjikvvwX_4Xb-HkPxNQE9ULHn0yStGlWPYj07tvY,18996
166
+ handler_control/web-ui/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2,sha256=NLnFBMq3pz43t0Y0OkSRMuVs97VIGvLLgdx03P8lyVY,85068
167
+ handler_control/web-ui/assets/inter-latin-wght-normal-Dx4kXJAl.woff2,sha256=MQDndehhbNJhG-7PojpCY9cDdYZ4m0PwNSNqLm-9TGI,48256
168
+ handler_control/web-ui/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2,sha256=XGb54H6QxtSsSSLMaNYN4mwXsYWOZ3-15gP845UrP_I,10252
169
+ handler_harness/__init__.py,sha256=gcqXmlxXEJoFCWAz4esFMZXObAa8I0i-QSQQF39dEZc,605
170
+ handler_harness/addons.py,sha256=Y7CRbEB-ezO6oUWniF6kWrPL2_p4dljIGSlhavnysLg,4534
171
+ handler_harness/catalog.py,sha256=hm3pBm7L3-A7_sV6kal_Jguwp_kql8EYJvjaWgE8glE,4184
172
+ handler_harness/cli.py,sha256=tM2riQ7j3Y-O1UyxyS0KX6kG_npxxtAiugpDTJUimo0,4189
173
+ handler_harness/images.py,sha256=u35iolGdG0BfhX3iyDoA-jecfUqQ9P_89A9OYwM01fw,2423
174
+ handler_harness/providers.py,sha256=br3S7hsb1Zw_MxsnmOrKBYTnpp3R2ItM4FLiZyQpjD8,11667
175
+ handler_harness/render.py,sha256=SJpF8Gsd5mqiP-Q3oJpMFLSYwsH0Qg7QbkK4GOSl-kM,20540
176
+ handler_harness/data/handler-sandbox-env,sha256=KC9BsL52Tg8lPIZgybeE10p2HPSibW8QFP2R6hWGBus,1152
177
+ handler_harness/data/addons/README.md,sha256=01iz8vdkigS66fpQ-En0cyKSYWyCVp7R48wSM9k7Mc0,2642
178
+ handler_harness/data/addons/miniapp/Containerfile,sha256=g5sYRS3lbDsx1qkSyrNAZfYm9_DZNT5TS_4icDlxqrA,844
179
+ handler_harness/data/addons/miniapp/PROVISIONING.md,sha256=gJAQZYnoFyNxv-Et8ht330KRbXr58L7tVnemFOZL_uY,603
180
+ handler_harness/data/addons/miniapp/addon.toml,sha256=Hkq_KesAHtkuxkceofWsPGiZelJ1OnU-S1dpZf7QdCw,868
181
+ handler_harness/data/addons/miniapp/skills/miniapp/SKILL.md,sha256=6w-KWy8twsfaNGxFdRCkXanZUrs14j7SRMB1JZ6ERBU,8814
182
+ handler_harness/data/addons/miniapp/skills/miniapp/template/.gitignore,sha256=Ww_P8-8E2jmWEf6J-z_tsG6K0ZKOfRwc2XrPgirp_II,31
183
+ handler_harness/data/addons/miniapp/skills/miniapp/template/app.toml,sha256=LE_uJYWk_hvt1-wCSmxREjsEXIyGFQcQwGl_k0o2-zA,586
184
+ handler_harness/data/addons/miniapp/skills/miniapp/template/server/db.ts,sha256=eIdgPAY5OTOhEeZGIT1T3aja0eUKI7SE0INCT26vge0,1077
185
+ handler_harness/data/addons/miniapp/skills/miniapp/template/server/deno.json,sha256=GpeLDbbvO6QBf0vzVF-TeC4Jkyh3uf9iSAlgZP4VoO4,255
186
+ handler_harness/data/addons/miniapp/skills/miniapp/template/server/handler.ts,sha256=9vXpqdySnz185J8tVaQSc8ZwWZz2OG3_BZQZMmOLAAY,1185
187
+ handler_harness/data/addons/miniapp/skills/miniapp/template/server/main.ts,sha256=2K8s8Di1HWXlj_f2-1x-6qNzEZIrbJWKsaW655AF4rw,818
188
+ handler_harness/data/addons/miniapp/skills/miniapp/template/server/main_test.ts,sha256=FFCMemfxFEdCee-6O_FQ85u0G_GyBBdrJ0XYptbhFzI,1363
189
+ handler_harness/data/addons/miniapp/skills/miniapp/template/server/routes.ts,sha256=5ihC8GUw_FZCK631VKtwFEqfQlMsAertG8gusIF-TuE,1487
190
+ handler_harness/data/addons/miniapp/skills/miniapp/template/server/migrations/001_notes.sql,sha256=IFx5b26gHM0NJ4JHcxjHE0_r3TsrA-mgOBghRP08clA,171
191
+ handler_harness/data/addons/miniapp/skills/miniapp/template/ui/deno.json,sha256=7tnHhD96JpRI4snnkwZy60sb4lGPa5dBhsJP7dByoLk,291
192
+ handler_harness/data/addons/miniapp/skills/miniapp/template/ui/index.html,sha256=cS5eAJ9v3uNBuQHiA65jsRe5Rb43x_DEzdn2NryKCwY,288
193
+ handler_harness/data/addons/miniapp/skills/miniapp/template/ui/vite.config.ts,sha256=uRQMeTR2JDd4GFZc9mDz5Mx20WSnacpxtLOOxFPXNYw,675
194
+ handler_harness/data/addons/miniapp/skills/miniapp/template/ui/src/App.svelte,sha256=cszVVXfAM2_lZ226J-G-ZTxSFcho1vWvWDN7ZAEG7DE,1121
195
+ handler_harness/data/addons/miniapp/skills/miniapp/template/ui/src/api.ts,sha256=WV1TT_HW94xBD5mvL0ZC78wcZx8QQsNbw1EmwJ13Ldw,1035
196
+ handler_harness/data/addons/miniapp/skills/miniapp/template/ui/src/app.css,sha256=HPUcFTr42E4bKNhl26vtN09tdaVr4wemaxXMdLDyq08,289
197
+ handler_harness/data/addons/miniapp/skills/miniapp/template/ui/src/main.ts,sha256=62oaXZ_afOhKSVUO7pGeMDGAdxePQevyhX9Q9glHWRI,142
198
+ handler_harness/data/catalog/README.md,sha256=YseuqgRKTY5-vC-3D8ZKYkp333wPVmp_2jHKMxEh-do,883
199
+ handler_harness/data/catalog/playwright/Containerfile,sha256=FuXXP1Xoyl8D_qNVG8yza23RzRvBn8tc1hAWDF12Ees,1699
200
+ handler_harness/data/catalog/playwright/PROVISIONING.md,sha256=mVanfu4eQr4_ZLD-dpx0dxz7GOMgRC3Mte3aCE9rv2E,2153
201
+ handler_harness/data/catalog/playwright/addon.toml,sha256=x3c2iNnAMNFLK4ExpBW8sv2abLbkhB1bE7LSWrgQN1c,1312
202
+ handler_harness/data/catalog/playwright/handler/playwright-mcp-check.mjs,sha256=Attl0Zd0L-dn3UwH23K0VclQL8Jt70XdOi-bvnJyHx8,998
203
+ handler_harness/data/catalog/playwright/handler/playwright-mcp.mjs,sha256=qk0-o8j2FmmE6Mm6730DhwZ6JWQy1GkY4kCfWyzulOY,2801
204
+ handler_harness/data/catalog/playwright/skills/browser/SKILL.md,sha256=sFfU8fBUtOuuELSWNnv1Ooa_xfUGNQw08_AXKt0Yjzg,2192
205
+ handler_harness/data/catalog/shopify/Containerfile,sha256=w3m7fzYkMhhTON6h_P1sAB6ga6zP01ybzUMW_WOwZR4,1208
206
+ handler_harness/data/catalog/shopify/PROVISIONING.md,sha256=XgBVN5T6vVVB7BiJQj_RzTAMmFc8u9O28IliuRU4yH0,2110
207
+ handler_harness/data/catalog/shopify/addon.toml,sha256=Jxzy3HrcuNHq1OPCaeMX-y6C2cj1yQWkgYzIVe1v30U,1836
208
+ handler_harness/data/catalog/shopify/skills/shopify/SKILL.md,sha256=BNhDos63S7x5-w2arSsAffCO79Cx4NpkDpFmRXvaTBI,1971
209
+ handler_harness/data/harnesses/README.md,sha256=REl5xKr_Gb0UHRiAgsCXvYq_gsw4C-JC4OKPiAQ65_g,4083
210
+ handler_harness/data/harnesses/claude-code/Containerfile,sha256=NoB-o3ZsEgKG8zQAsPzgwF5aFrEtpN8yZxn2oI42IDw,1639
211
+ handler_harness/data/harnesses/claude-code/harness.toml,sha256=OHnL3rsjengn5Ww-mqLOKwbm1uyuS7faUseqUJJxpiQ,4955
212
+ handler_harness/data/harnesses/codex/Containerfile,sha256=eYzhS2LPOBZ4NnhrH4d4fvYlRKO0zkNAGxW-C4E5McU,1744
213
+ handler_harness/data/harnesses/codex/harness.toml,sha256=zcRex6oTmxTZRSqQNbWlzgJZLxeSzMMxBt6NdRNCuNk,5047
214
+ handler_harness/data/harnesses/pi/Containerfile,sha256=TX_D2KrebETpy_ATRSh87KHgitDTRPzwP8100I4oGqI,1653
215
+ handler_harness/data/harnesses/pi/harness.toml,sha256=LWxf77QcaCAV6it56lqWwVUlyvaOuf2KAOoVZsYMglU,3314
216
+ handler_harness/data/harnesses/vibe/Containerfile,sha256=FMdNXEvyAKXtZSVqJF4SQf1p8JrZUMrIN4stXkovPjc,884
217
+ handler_harness/data/harnesses/vibe/harness.toml,sha256=VJuUEwGC3Mvx5iIVv_KoM3rTeR1vTook3Jt3XJecIKg,4322
218
+ handler_harness/data/toolbox/Containerfile,sha256=PpH0e-JEpnr_eHV5hrggQQq-qrqSeFJ8lvDaJ90I1ck,2988
219
+ handler_harness/data/toolbox/README.md,sha256=bFy_D0Nv69dR159xvE-t-fAz9vjPHGEjFs2Rm1jsMms,1648
220
+ handler_harness/data/toolbox/toolbox.toml,sha256=W5lYuAxNQ_IQQuY41HPbn13u4L1v03o2KhC1loAktVc,1243
221
+ handler_sandbox_podman/__init__.py,sha256=mL_TJ6_6NuZHTBItyYSMYYzjS22Oosyc8kxLhTWqoz8,62
222
+ handler_sandbox_podman/caches.py,sha256=pld6dVa784TubQMCxcKzV22lPtwMxjrRJLvQTUFDlXI,3953
223
+ handler_sandbox_podman/check.py,sha256=i4QUc49CS669DA7dRf769GU0N4dCz0_eHpJchlfk0d0,4274
224
+ handler_sandbox_podman/cli.py,sha256=NFYBsmg-_Afa76HBR2ypQlUNtgRtK8ct1SqHGo3QpZc,10016
225
+ handler_sandbox_podman/egress.py,sha256=_xkMBp3bHkjtUigdC8TsvOBoa_pWAU_ylH6e_Iwo9rQ,4720
226
+ handler_sandbox_podman/images.py,sha256=dw8yeiWsi0X1Uw1F_zcuAMC2VHnQtDjY2CsA46Dmo4s,7192
227
+ handler_sandbox_podman/launch.py,sha256=Wvy5QOuP7S9_jLxQsaD-G3JTLG0gzGkZllU69Hwiokw,21528
228
+ handler_sandbox_podman/podman.py,sha256=Qnqfw4Dv_Shu7rxm5q4zV31QSqEQhkYHTsXdsdvBNAA,4445
229
+ handler_sandbox_podman/proxy/Containerfile,sha256=dF56zLSfELXw94CC2zJEYz8FVBRbz6zDWGbmv9Tzq4g,401
230
+ handler_sandbox_podman/proxy/addon.py,sha256=Pr8dXkFR7A5mrsJUAMij3lOQjOQpTVC-Jc2j-rydB0c,10659
231
+ handler_ai-0.1.0.dist-info/METADATA,sha256=IEmlmZf3Ul1i0qMWIv9SKtS-dbZkldfwCWl0vNEKgO0,2497
232
+ handler_ai-0.1.0.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
233
+ handler_ai-0.1.0.dist-info/entry_points.txt,sha256=BVznYHkhvLm9-vMiNdqoGKhZGx5guBFxIuLEuCNbNnA,222
234
+ handler_ai-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.32.4
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,6 @@
1
+ [console_scripts]
2
+ handler = handler_cli.cli:main
3
+ handler-ai = handler_cli.cli:main
4
+ handler-harness = handler_harness.cli:main
5
+ handler-sandbox-podman = handler_sandbox_podman.cli:main
6
+ handlerd = handler_control.daemon:main
@@ -0,0 +1 @@
1
+ """handler — command line client of handlerd (contracts C and D only; never touches podman)."""
@@ -0,0 +1,314 @@
1
+ """`handler account …` — accounts (contract D.9): a subscription one harness logs in with.
2
+
3
+ A token login (Claude Code): you run its sign-in yourself (`claude setup-token`, Anthropic's own,
4
+ in your browser) and give handler the token it prints, or tell handler where you keep it; handler
5
+ never runs that sign-in nor reads its output. A file login (Codex, Vibe): handler runs the harness's
6
+ login here and keeps the credential file it leaves.
7
+
8
+ handler account list accounts, their harness, plan, login and contexts
9
+ handler account add NAME HARNESS [--description TEXT] [--plan PLAN] [--context CONTEXT …]
10
+ [--token | --token-env VAR | --token-file PATH | --token-command CMD]
11
+ PLAN: its subscription's (e.g. pro, max for claude-code),
12
+ told to the harness, which cannot learn it from a token.
13
+ --context: the project contexts it is made for (Work,
14
+ Personal…, or any; none: it runs nowhere a project has one)
15
+ --token…: its token login at once (as `handler account token`)
16
+ handler account token NAME [--env VAR | --file PATH | --command CMD]
17
+ a token login: the token your own sign-in printed (asked
18
+ for, or read from stdin), kept in handler's secret store; or
19
+ where you keep it: an env var of handlerd, a file (its first
20
+ line), a command printing it (e.g. 'pass show claude-pro')
21
+ handler account login NAME [--keep-open] a file login: its harness's own login runs here, on this
22
+ computer (npx or uvx fetches it), its state in a folder of
23
+ its own; handler keeps the file it leaves. --keep-open: wait
24
+ for Enter at the end (a window the web UI opened for it)
25
+ handler account import NAME FILE or give a file login done elsewhere: the credential file it
26
+ left (e.g. ~/.codex/auth.json after `codex login`)
27
+ handler account rm NAME it and its login (refused while an agent uses it)
28
+ """
29
+
30
+ import argparse
31
+ import contextlib
32
+ import fcntl
33
+ import getpass
34
+ import os
35
+ import pty
36
+ import pwd
37
+ import select
38
+ import shlex
39
+ import shutil
40
+ import signal
41
+ import struct
42
+ import subprocess
43
+ import sys
44
+ import tempfile
45
+ import termios
46
+ import tty
47
+ from pathlib import Path
48
+ from typing import Any
49
+
50
+ from .client import ClientError, Daemon
51
+ from .output import table
52
+
53
+
54
+ def add_parser(sub: Any) -> None:
55
+ account = sub.add_parser("account", help="accounts", description=__doc__,
56
+ formatter_class=argparse.RawDescriptionHelpFormatter) # fmt: skip
57
+ a = account.add_subparsers(dest="account_cmd", required=True)
58
+ a.add_parser("list")
59
+ add = a.add_parser("add")
60
+ add.add_argument("name")
61
+ add.add_argument("harness")
62
+ add.add_argument("--description")
63
+ add.add_argument("--plan")
64
+ add.add_argument(
65
+ "--context",
66
+ action="append",
67
+ metavar="CONTEXT",
68
+ default=[],
69
+ help="a project context it is made for (Work, any…); repeatable",
70
+ )
71
+ given = add.add_mutually_exclusive_group()
72
+ given.add_argument("--token", action="store_true", help="ask for the token now")
73
+ given.add_argument("--token-env", metavar="VAR")
74
+ given.add_argument("--token-file", metavar="PATH")
75
+ given.add_argument("--token-command", metavar="CMD")
76
+ token = a.add_parser("token")
77
+ token.add_argument("name")
78
+ where = token.add_mutually_exclusive_group()
79
+ where.add_argument("--env", metavar="VAR")
80
+ where.add_argument("--file", metavar="PATH")
81
+ where.add_argument("--command", metavar="CMD")
82
+ log_in = a.add_parser("login")
83
+ log_in.add_argument("name")
84
+ log_in.add_argument("--keep-open", action="store_true", help="wait for Enter at the end")
85
+ imp = a.add_parser("import")
86
+ imp.add_argument("name")
87
+ imp.add_argument("file", type=Path)
88
+ a.add_parser("rm").add_argument("name")
89
+
90
+
91
+ def dispatch(args: argparse.Namespace) -> int:
92
+ d = Daemon()
93
+ if args.account_cmd == "list":
94
+ rows = [["NAME", "HARNESS", "PLAN", "LOGIN", "CONTEXTS", "AGENTS"]]
95
+ for a in d.call("GET", "/v1/accounts"):
96
+ harness, plan = a.get("harness") or "?", a.get("plan") or "-"
97
+ contexts = ", ".join(a.get("contexts", [])) or "-"
98
+ rows.append([a["name"], harness, plan, login_status(a), contexts,
99
+ ", ".join(a["agents"])]) # fmt: skip
100
+ table(rows) if len(rows) > 1 else print("no account")
101
+ return 0
102
+ if args.account_cmd == "add":
103
+ return add(d, args)
104
+ if args.account_cmd == "token":
105
+ return give_token(d, args.name, token_body(args.name, args.env, args.file, args.command))
106
+ if args.account_cmd == "import":
107
+ return import_login(d, args.name, args.file)
108
+ if args.account_cmd == "login":
109
+ return login_window(d, args.name, keep_open=args.keep_open)
110
+ d.call("DELETE", f"/v1/accounts/{args.name}")
111
+ print(f"{args.name}: removed")
112
+ return 0
113
+
114
+
115
+ def login_window(d: Daemon, name: str, *, keep_open: bool) -> int:
116
+ """`handler account login`, its errors said in its own terminal (a window may close at once)."""
117
+ try:
118
+ return login(d, name)
119
+ except ClientError as e:
120
+ print(f"handler: {e}")
121
+ return 1
122
+ finally:
123
+ if keep_open:
124
+ with contextlib.suppress(EOFError, KeyboardInterrupt):
125
+ input("\nPress Enter to close this window.")
126
+
127
+
128
+ def login_status(a: dict[str, Any]) -> str:
129
+ """Its login as `handler account list` says it: logged in (and how), or what is missing."""
130
+ if a["status"] == "invalid":
131
+ return a.get("error") or "invalid"
132
+ if a["status"] != "ready":
133
+ return "; ".join(a.get("problems") or ["not logged in"])
134
+ where = {"secret": "token in handler", "env": "token from an env var",
135
+ "file": "token from a file",
136
+ "command": "token from a command"}.get(a.get("token") or "", "logged in") # fmt: skip
137
+ expires = a.get("expiresAt")
138
+ return f"{where}, until {expires[:10]}" if expires else where
139
+
140
+
141
+ def add(d: Daemon, args: argparse.Namespace) -> int:
142
+ body = {
143
+ "name": args.name,
144
+ "harness": args.harness,
145
+ "description": args.description,
146
+ "contexts": args.context,
147
+ }
148
+ body |= {"plan": args.plan} if args.plan else {}
149
+ made = d.call("POST", "/v1/accounts", body)
150
+ print(f"{args.name}: created")
151
+ if args.token or args.token_env or args.token_file or args.token_command:
152
+ given = token_body(args.name, args.token_env, args.token_file, args.token_command)
153
+ return give_token(d, args.name, given)
154
+ if made.get("login") == "token":
155
+ print(f"log it in: run `{token_command(d, args.harness)}`, then: handler account token "
156
+ f"{args.name}") # fmt: skip
157
+ elif made.get("login") == "file":
158
+ print(f"log it in: handler account login {args.name}")
159
+ return 0
160
+
161
+
162
+ def token_command(d: Daemon, harness: str) -> str:
163
+ found = next((h for h in d.call("GET", "/v1/harnesses") if h["name"] == harness), None)
164
+ return ((found or {}).get("login") or {}).get("tokenCommand") or "its sign-in"
165
+
166
+
167
+ def token_body(name: str, env: str | None, file: str | None, command: str | None) -> dict[str, Any]:
168
+ """`PUT …/login`'s body: a reference to where the token is kept, else the token, asked for."""
169
+ if env:
170
+ return {"tokenRef": {"env": env}}
171
+ if file:
172
+ return {"tokenRef": {"file": str(Path(file).expanduser().resolve())}}
173
+ if command:
174
+ return {"tokenRef": {"command": shlex.split(command)}}
175
+ return {"token": ask_token(name)}
176
+
177
+
178
+ def give_token(d: Daemon, name: str, body: dict[str, Any]) -> int:
179
+ if not body.get("tokenRef") and not body.get("token"):
180
+ raise ClientError(f"no token given for {name}")
181
+ return save(d, name, body)
182
+
183
+
184
+ def import_login(d: Daemon, name: str, file: Path) -> int:
185
+ account = next((a for a in d.call("GET", "/v1/accounts") if a["name"] == name), None)
186
+ if account is None:
187
+ raise ClientError(f"no account '{name}' (handler account list)")
188
+ if account.get("login") != "file":
189
+ raise ClientError(f"the login of '{name}' is a token: handler account token {name}")
190
+ return save(d, name, {"file": file.read_text()})
191
+
192
+
193
+ def ask_token(name: str, what: str = "token") -> str:
194
+ prompt = f"{what} for {name}: "
195
+ return (getpass.getpass(prompt) if sys.stdin.isatty() else sys.stdin.readline()).strip()
196
+
197
+
198
+ def save(d: Daemon, name: str, body: dict[str, Any]) -> int:
199
+ made = d.call("PUT", f"/v1/accounts/{name}/login", body)
200
+ logged_in = made["status"] == "ready"
201
+ print(f"{name}: {'logged in' if logged_in else login_status(made)}")
202
+ return 0 if logged_in else 1
203
+
204
+
205
+ def login(d: Daemon, name: str) -> int:
206
+ """A file login (D.9) on this computer, on a terminal of its own relayed to this one: its
207
+ state in a folder given as `{dir}`, then the file it leaves there becomes the account's
208
+ login. A token login is the person's own to run: said, not run."""
209
+ account = next((a for a in d.call("GET", "/v1/accounts") if a["name"] == name), None)
210
+ if account is None:
211
+ raise ClientError(f"no account '{name}' (handler account list)")
212
+ harness = next(
213
+ (h for h in d.call("GET", "/v1/harnesses") if h["name"] == account.get("harness")), None
214
+ )
215
+ recipe = harness and harness.get("login")
216
+ if not recipe:
217
+ raise ClientError(f"'{name}': {account.get('error') or 'its harness has no login'}")
218
+ if recipe.get("tokenCommand"):
219
+ raise ClientError(
220
+ f"'{name}' logs in with a token you make: run `{recipe['tokenCommand']}`, "
221
+ f"then: handler account token {name}"
222
+ )
223
+ if not harness.get("loginFile"): # a handlerd older than this handler
224
+ raise ClientError("handlerd does not say where the login leaves its file: restart it")
225
+ path = search_path(recipe["command"][0], account["harness"])
226
+ argv = [str(shutil.which(recipe["command"][0], path=path)), *recipe["command"][1:]]
227
+ if recipe.get("help"):
228
+ print(recipe["help"])
229
+ with tempfile.TemporaryDirectory(prefix="handler-login-") as folder:
230
+ env = (
231
+ dict(os.environ)
232
+ | {"PATH": path}
233
+ | {k: v.replace("{dir}", folder) for k, v in (recipe.get("env") or {}).items()}
234
+ )
235
+ code = on_terminal(argv, env, folder)
236
+ file = Path(folder) / harness["loginFile"]
237
+ if not file.is_file() or not file.read_text(errors="replace").strip():
238
+ print(f"\n{name}: not logged in: it left no {file.name} (exit {code})")
239
+ return 1
240
+ return save(d, name, {"file": file.read_text()})
241
+
242
+
243
+ def search_path(program: str, harness: str) -> str:
244
+ """The PATH the login runs with: this one when it has the program, else the person's login
245
+ shell's (a window handlerd opened has handlerd's, a systemd service's: without ~/.local/bin…),
246
+ where what the program needs (node for npx) is too."""
247
+ for path in (os.environ.get("PATH", ""), login_path()):
248
+ if shutil.which(program, path=path):
249
+ return path
250
+ raise ClientError(
251
+ f"the {harness} login runs {program} on this computer: install it "
252
+ "(npx: Node.js and npm; uvx: uv)"
253
+ )
254
+
255
+
256
+ def login_path() -> str:
257
+ shell = pwd.getpwuid(os.getuid()).pw_shell or "/bin/sh"
258
+ with contextlib.suppress(OSError, subprocess.SubprocessError):
259
+ printed = subprocess.run(
260
+ [shell, "-lc", "printenv PATH"],
261
+ capture_output=True,
262
+ text=True,
263
+ timeout=10,
264
+ stdin=subprocess.DEVNULL,
265
+ check=False,
266
+ ).stdout.strip()
267
+ if printed:
268
+ return printed.splitlines()[-1] # after what its shell profile may print
269
+ return ""
270
+
271
+
272
+ def on_terminal(argv: list[str], env: dict[str, str], cwd: str) -> int:
273
+ """Run `argv` on a terminal of its own, relayed to this one (raw while it runs, resized with
274
+ it): its exit code."""
275
+ pid, master = pty.fork()
276
+ if pid == 0: # the child, on the new terminal
277
+ os.chdir(cwd)
278
+ os.execve(argv[0], argv, env)
279
+ interactive = sys.stdin.isatty()
280
+ saved = termios.tcgetattr(0) if interactive else None
281
+
282
+ def resize(*_: object) -> None:
283
+ cols, rows = shutil.get_terminal_size()
284
+ fcntl.ioctl(master, termios.TIOCSWINSZ, struct.pack("HHHH", rows, cols, 0, 0))
285
+
286
+ resize()
287
+ inputs = [0, master]
288
+ try:
289
+ if interactive:
290
+ tty.setraw(0)
291
+ signal.signal(signal.SIGWINCH, resize)
292
+ while True:
293
+ ready, _, _ = select.select(inputs, [], [])
294
+ if master in ready:
295
+ try:
296
+ data = os.read(master, 65536)
297
+ except OSError: # EIO: the command and all it started are gone
298
+ data = b""
299
+ if not data:
300
+ break
301
+ os.write(1, data)
302
+ if 0 in ready:
303
+ data = os.read(0, 4096)
304
+ if data:
305
+ os.write(master, data)
306
+ else: # stdin ended (piped): the command goes on without it
307
+ inputs.remove(0)
308
+ finally:
309
+ if saved is not None:
310
+ termios.tcsetattr(0, termios.TCSADRAIN, saved)
311
+ signal.signal(signal.SIGWINCH, signal.SIG_DFL)
312
+ os.close(master)
313
+ _, status = os.waitpid(pid, 0)
314
+ return os.waitstatus_to_exitcode(status)