webbee 0.5.4__tar.gz → 0.5.6__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.
- {webbee-0.5.4 → webbee-0.5.6}/CHANGELOG.md +23 -0
- {webbee-0.5.4 → webbee-0.5.6}/PKG-INFO +45 -23
- {webbee-0.5.4 → webbee-0.5.6}/README.md +26 -7
- {webbee-0.5.4 → webbee-0.5.6}/pyproject.toml +24 -17
- webbee-0.5.6/src/webbee/__init__.py +1 -0
- webbee-0.5.6/src/webbee/_launch.py +204 -0
- {webbee-0.5.4 → webbee-0.5.6}/src/webbee/steer.py +19 -14
- webbee-0.5.6/tests/test_launch.py +160 -0
- {webbee-0.5.4 → webbee-0.5.6}/tests/test_packaging.py +5 -1
- {webbee-0.5.4 → webbee-0.5.6}/tests/test_version.py +3 -1
- webbee-0.5.4/src/webbee/__init__.py +0 -1
- {webbee-0.5.4 → webbee-0.5.6}/.github/workflows/publish.yml +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/.gitignore +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/LICENSE +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/install.sh +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/src/webbee/__main__.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/src/webbee/account.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/src/webbee/active_sessions.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/src/webbee/banner_art.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/src/webbee/boot.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/src/webbee/browser.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/src/webbee/checkpoints.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/src/webbee/cli.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/src/webbee/clipboard.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/src/webbee/clipboard_read.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/src/webbee/clipboard_session.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/src/webbee/coding_context.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/src/webbee/commands.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/src/webbee/config.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/src/webbee/consent.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/src/webbee/details.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/src/webbee/events.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/src/webbee/frames.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/src/webbee/home.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/src/webbee/home_view.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/src/webbee/http.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/src/webbee/instance_lock.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/src/webbee/intel/__init__.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/src/webbee/intel/graph.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/src/webbee/intel/outline.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/src/webbee/intel/query.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/src/webbee/intel/service.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/src/webbee/intel/store.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/src/webbee/intel/vectors.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/src/webbee/intel/watch.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/src/webbee/media.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/src/webbee/modals.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/src/webbee/mode_store.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/src/webbee/native_files.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/src/webbee/native_read.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/src/webbee/newtab_mode.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/src/webbee/output_pane.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/src/webbee/pricing_constants.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/src/webbee/queue_panel.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/src/webbee/reflow.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/src/webbee/remote.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/src/webbee/render.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/src/webbee/repl.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/src/webbee/repo.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/src/webbee/selection.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/src/webbee/session.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/src/webbee/sessions.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/src/webbee/sizing.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/src/webbee/slots.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/src/webbee/stream.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/src/webbee/tab_store.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/src/webbee/tabs.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/src/webbee/thread.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/src/webbee/tier_store.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/src/webbee/todo_panel.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/src/webbee/tokens.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/src/webbee/tools.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/src/webbee/tui.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/src/webbee/update.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/src/webbee/urlopen.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/src/webbee/wallet.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/src/webbee/worktrees.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/tests/__init__.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/tests/conftest.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/tests/test_account.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/tests/test_active_sessions.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/tests/test_audit_2026_07_25_findings.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/tests/test_checkpoints.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/tests/test_cli.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/tests/test_clipboard.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/tests/test_clipboard_read.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/tests/test_coding_context.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/tests/test_commands.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/tests/test_config.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/tests/test_cpc_contract_stable.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/tests/test_cpc_outline.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/tests/test_cross_loop_token.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/tests/test_details.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/tests/test_events.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/tests/test_freeze_fix.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/tests/test_home.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/tests/test_home_view.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/tests/test_home_view_mouse.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/tests/test_inspect_page.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/tests/test_instance_lock.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/tests/test_intel_chunker.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/tests/test_intel_embed.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/tests/test_intel_graph.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/tests/test_intel_no_numpy.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/tests/test_intel_query.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/tests/test_intel_service.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/tests/test_intel_store.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/tests/test_intel_vectors.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/tests/test_intel_watch.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/tests/test_marathon.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/tests/test_modals_e2e.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/tests/test_mode_store.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/tests/test_native_files.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/tests/test_newtab_mode.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/tests/test_queue_manage_0337.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/tests/test_reflow.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/tests/test_render.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/tests/test_repl.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/tests/test_repo.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/tests/test_resilient_http.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/tests/test_session.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/tests/test_sessions.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/tests/test_sizing.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/tests/test_slots.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/tests/test_steer.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/tests/test_stream.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/tests/test_tab_bar_stability.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/tests/test_tab_restore_0337.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/tests/test_tabs.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/tests/test_terminal_ux_0336.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/tests/test_thread.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/tests/test_tier_invalidation.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/tests/test_tier_store.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/tests/test_todo_panel.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/tests/test_tokens.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/tests/test_tools.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/tests/test_tools_vision_websearch.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/tests/test_tui.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/tests/test_tui_hardening.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/tests/test_update.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/tests/test_update_badge_0340.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/tests/test_urlopen.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/tests/test_version_badge_corner.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/tests/test_wallet.py +0 -0
- {webbee-0.5.4 → webbee-0.5.6}/tests/test_worktrees.py +0 -0
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.5.6
|
|
4
|
+
|
|
5
|
+
* **`pip install webbee` works on Python 3.7, 3.8 and 3.9** (macOS ships 3.9): webbee's dependencies need
|
|
6
|
+
3.10+, so on an older Python the install is light and the `webbee` command (`webbee/_launch.py`, standard
|
|
7
|
+
library only, 3.7 syntax) sets up the same release on its own Python 3.12 once with uv, then hands over.
|
|
8
|
+
It is the full webbee -- code index and search by meaning included. uv comes as a wheel on 3.8+; on 3.7
|
|
9
|
+
the launcher downloads uv's release build from GitHub and checks its published SHA-256. On 3.10+ nothing
|
|
10
|
+
changes. Checked on clean Python 3.7 (arm64, x86_64), 3.8, 3.9 (incl. macOS's own with pip 21) and 3.12.
|
|
11
|
+
* **README**: one install section for every Python, and the errors people actually hit.
|
|
12
|
+
|
|
13
|
+
## 0.5.5
|
|
14
|
+
|
|
15
|
+
* **Installs on Python 3.10 again**: pip and pipx stopped with "conflicting dependencies / ResolutionImpossible"
|
|
16
|
+
because imperal-mcp up to 0.5.6 required Python 3.11 -- Ubuntu 22.04 and Pop!_OS 22.04 ship 3.10. webbee now
|
|
17
|
+
needs imperal-mcp >= 0.5.7, which runs on 3.10.
|
|
18
|
+
* **`/exit` no longer hangs on Python 3.10**: the steer poller asked `Task.cancelling()` (3.11+) whether a
|
|
19
|
+
turn had swallowed its cancel; on 3.10 the call failed, the poller kept looping and the terminal never
|
|
20
|
+
closed. A submitted turn now runs as its own task the poller waits on, so the cancel always reaches the
|
|
21
|
+
poller -- one path for every Python.
|
|
22
|
+
* **Python 3.10 is the stated floor** (was 3.9, which no dependency set could satisfy): an older Python now gets
|
|
23
|
+
pip's plain "requires a different Python" instead of a dependency conflict.
|
|
24
|
+
* **README installs through uv first** (it brings its own Python) and lists the install errors people hit.
|
|
25
|
+
|
|
3
26
|
## 0.5.4
|
|
4
27
|
|
|
5
28
|
* **Code search by meaning for repos synced before**: imperal-sdk >= 6.3.6 tells the code map this client
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: webbee
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.6
|
|
4
4
|
Summary: Webbee 🐝 — the Imperal Cloud coding agent in your terminal
|
|
5
5
|
Project-URL: Homepage, https://imperal.io
|
|
6
6
|
Project-URL: Documentation, https://docs.imperal.io
|
|
@@ -17,6 +17,8 @@ Classifier: Environment :: Console
|
|
|
17
17
|
Classifier: Intended Audience :: Developers
|
|
18
18
|
Classifier: Operating System :: OS Independent
|
|
19
19
|
Classifier: Programming Language :: Python :: 3
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.7
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
20
22
|
Classifier: Programming Language :: Python :: 3.9
|
|
21
23
|
Classifier: Programming Language :: Python :: 3.10
|
|
22
24
|
Classifier: Programming Language :: Python :: 3.11
|
|
@@ -25,21 +27,22 @@ Classifier: Programming Language :: Python :: 3.13
|
|
|
25
27
|
Classifier: Programming Language :: Python :: 3.14
|
|
26
28
|
Classifier: Topic :: Software Development
|
|
27
29
|
Classifier: Topic :: Utilities
|
|
28
|
-
Requires-Python: >=3.
|
|
29
|
-
Requires-Dist: asyncssh>=2.14
|
|
30
|
-
Requires-Dist: httpx-sse>=0.4
|
|
31
|
-
Requires-Dist: httpx>=0.27
|
|
32
|
-
Requires-Dist: imperal-mcp>=0.5.
|
|
33
|
-
Requires-Dist: imperal-sdk>=6.3.6
|
|
34
|
-
Requires-Dist: model2vec>=0.8.2; sys_platform != 'darwin' or platform_machine != 'x86_64' or python_version < '3.13'
|
|
35
|
-
Requires-Dist: numpy>=2; sys_platform != 'darwin' or platform_machine != 'x86_64' or python_version < '3.13'
|
|
36
|
-
Requires-Dist: prompt-toolkit>=3
|
|
37
|
-
Requires-Dist: rich>=13.7
|
|
38
|
-
Requires-Dist: tree-sitter-javascript>=0.23; sys_platform != 'darwin' or platform_machine != 'x86_64' or python_version < '3.13'
|
|
39
|
-
Requires-Dist: tree-sitter-python>=0.23; sys_platform != 'darwin' or platform_machine != 'x86_64' or python_version < '3.13'
|
|
40
|
-
Requires-Dist: tree-sitter-typescript>=0.23; sys_platform != 'darwin' or platform_machine != 'x86_64' or python_version < '3.13'
|
|
41
|
-
Requires-Dist: tree-sitter>=0.23; sys_platform != 'darwin' or platform_machine != 'x86_64' or python_version < '3.13'
|
|
42
|
-
Requires-Dist:
|
|
30
|
+
Requires-Python: >=3.7
|
|
31
|
+
Requires-Dist: asyncssh>=2.14; python_version >= '3.10'
|
|
32
|
+
Requires-Dist: httpx-sse>=0.4; python_version >= '3.10'
|
|
33
|
+
Requires-Dist: httpx>=0.27; python_version >= '3.10'
|
|
34
|
+
Requires-Dist: imperal-mcp>=0.5.7; python_version >= '3.10'
|
|
35
|
+
Requires-Dist: imperal-sdk>=6.3.6; python_version >= '3.10'
|
|
36
|
+
Requires-Dist: model2vec>=0.8.2; python_version >= '3.10' and (sys_platform != 'darwin' or platform_machine != 'x86_64' or python_version < '3.13')
|
|
37
|
+
Requires-Dist: numpy>=2; python_version >= '3.10' and (sys_platform != 'darwin' or platform_machine != 'x86_64' or python_version < '3.13')
|
|
38
|
+
Requires-Dist: prompt-toolkit>=3; python_version >= '3.10'
|
|
39
|
+
Requires-Dist: rich>=13.7; python_version >= '3.10'
|
|
40
|
+
Requires-Dist: tree-sitter-javascript>=0.23; python_version >= '3.10' and (sys_platform != 'darwin' or platform_machine != 'x86_64' or python_version < '3.13')
|
|
41
|
+
Requires-Dist: tree-sitter-python>=0.23; python_version >= '3.10' and (sys_platform != 'darwin' or platform_machine != 'x86_64' or python_version < '3.13')
|
|
42
|
+
Requires-Dist: tree-sitter-typescript>=0.23; python_version >= '3.10' and (sys_platform != 'darwin' or platform_machine != 'x86_64' or python_version < '3.13')
|
|
43
|
+
Requires-Dist: tree-sitter>=0.23; python_version >= '3.10' and (sys_platform != 'darwin' or platform_machine != 'x86_64' or python_version < '3.13')
|
|
44
|
+
Requires-Dist: uv>=0.4; python_version >= '3.8' and python_version < '3.10'
|
|
45
|
+
Requires-Dist: watchfiles>=0.24; python_version >= '3.10' and (sys_platform != 'darwin' or platform_machine != 'x86_64' or python_version < '3.13')
|
|
43
46
|
Provides-Extra: intel
|
|
44
47
|
Provides-Extra: intel-embed
|
|
45
48
|
Provides-Extra: intel-embed-faiss
|
|
@@ -79,21 +82,40 @@ Webbee is powered by **ICNLI** (*Infrastructure Contextual Natural Language Inte
|
|
|
79
82
|
|
|
80
83
|
## 📦 Installation
|
|
81
84
|
|
|
82
|
-
Install easily using **pipx** (recommended) or **uv**:
|
|
83
|
-
|
|
84
85
|
```sh
|
|
85
86
|
pipx install webbee
|
|
86
|
-
# or
|
|
87
|
-
|
|
87
|
+
# or: uv tool install webbee
|
|
88
|
+
# or: pip install webbee (inside a virtual environment)
|
|
88
89
|
```
|
|
89
90
|
|
|
90
|
-
|
|
91
|
+
No Python tools at hand? One line installs [uv](https://docs.astral.sh/uv/) (it brings its own Python) and webbee:
|
|
91
92
|
|
|
92
93
|
```sh
|
|
93
|
-
|
|
94
|
-
pip install webbee
|
|
94
|
+
curl -LsSf https://raw.githubusercontent.com/imperalcloud/webbee-code/main/install.sh | sh
|
|
95
95
|
```
|
|
96
96
|
|
|
97
|
+
Works with **any Python from 3.7**:
|
|
98
|
+
|
|
99
|
+
- **Python 3.10 or newer** — webbee runs directly on it.
|
|
100
|
+
- **Python 3.7 – 3.9** (macOS ships 3.9, Ubuntu 20.04 has 3.8) — the install is light, and the first
|
|
101
|
+
`webbee` command sets up the same release on its own Python 3.12, once (about 30–45 s, needs internet;
|
|
102
|
+
it fetches uv to do it). Every start after that is immediate, and it is the full webbee — code index
|
|
103
|
+
and search by meaning included. It lives in `~/.local/share/webbee/runtime`
|
|
104
|
+
(`%LOCALAPPDATA%\webbee\runtime` on Windows) and changes nothing else on your system.
|
|
105
|
+
|
|
106
|
+
<details>
|
|
107
|
+
<summary>Install problems</summary>
|
|
108
|
+
|
|
109
|
+
| You see | Why | Do |
|
|
110
|
+
|---------|-----|----|
|
|
111
|
+
| `Cannot install webbee==… because these package versions have conflicting dependencies` / `ResolutionImpossible` | a webbee release older than 0.5.6 on Python below 3.10 | `pipx install "webbee>=0.5.6"` (or `pip install -U webbee`) |
|
|
112
|
+
| `error: externally-managed-environment` | `pip install` into the system Python is blocked on Homebrew, Debian 12+, Ubuntu 23.04+ | `pipx install webbee` or `uv tool install webbee` |
|
|
113
|
+
| `webbee: command not found` after install | the tool folder is not on `PATH` | `pipx ensurepath` (or `uv tool update-shell`), then open a new terminal |
|
|
114
|
+
| `Could not set up webbee on Python 3.12` on the first start | Python 3.7–3.9 and no internet (or a proxy blocks github.com / pypi.org) | allow those hosts and start `webbee` again, or use the one-line installer above |
|
|
115
|
+
| `No matching distribution found for webbee` | Python 3.6 or older | the one-line installer above — it brings its own Python |
|
|
116
|
+
|
|
117
|
+
</details>
|
|
118
|
+
|
|
97
119
|
---
|
|
98
120
|
|
|
99
121
|
## 💡 Quick Start
|
|
@@ -27,21 +27,40 @@ Webbee is powered by **ICNLI** (*Infrastructure Contextual Natural Language Inte
|
|
|
27
27
|
|
|
28
28
|
## 📦 Installation
|
|
29
29
|
|
|
30
|
-
Install easily using **pipx** (recommended) or **uv**:
|
|
31
|
-
|
|
32
30
|
```sh
|
|
33
31
|
pipx install webbee
|
|
34
|
-
# or
|
|
35
|
-
|
|
32
|
+
# or: uv tool install webbee
|
|
33
|
+
# or: pip install webbee (inside a virtual environment)
|
|
36
34
|
```
|
|
37
35
|
|
|
38
|
-
|
|
36
|
+
No Python tools at hand? One line installs [uv](https://docs.astral.sh/uv/) (it brings its own Python) and webbee:
|
|
39
37
|
|
|
40
38
|
```sh
|
|
41
|
-
|
|
42
|
-
pip install webbee
|
|
39
|
+
curl -LsSf https://raw.githubusercontent.com/imperalcloud/webbee-code/main/install.sh | sh
|
|
43
40
|
```
|
|
44
41
|
|
|
42
|
+
Works with **any Python from 3.7**:
|
|
43
|
+
|
|
44
|
+
- **Python 3.10 or newer** — webbee runs directly on it.
|
|
45
|
+
- **Python 3.7 – 3.9** (macOS ships 3.9, Ubuntu 20.04 has 3.8) — the install is light, and the first
|
|
46
|
+
`webbee` command sets up the same release on its own Python 3.12, once (about 30–45 s, needs internet;
|
|
47
|
+
it fetches uv to do it). Every start after that is immediate, and it is the full webbee — code index
|
|
48
|
+
and search by meaning included. It lives in `~/.local/share/webbee/runtime`
|
|
49
|
+
(`%LOCALAPPDATA%\webbee\runtime` on Windows) and changes nothing else on your system.
|
|
50
|
+
|
|
51
|
+
<details>
|
|
52
|
+
<summary>Install problems</summary>
|
|
53
|
+
|
|
54
|
+
| You see | Why | Do |
|
|
55
|
+
|---------|-----|----|
|
|
56
|
+
| `Cannot install webbee==… because these package versions have conflicting dependencies` / `ResolutionImpossible` | a webbee release older than 0.5.6 on Python below 3.10 | `pipx install "webbee>=0.5.6"` (or `pip install -U webbee`) |
|
|
57
|
+
| `error: externally-managed-environment` | `pip install` into the system Python is blocked on Homebrew, Debian 12+, Ubuntu 23.04+ | `pipx install webbee` or `uv tool install webbee` |
|
|
58
|
+
| `webbee: command not found` after install | the tool folder is not on `PATH` | `pipx ensurepath` (or `uv tool update-shell`), then open a new terminal |
|
|
59
|
+
| `Could not set up webbee on Python 3.12` on the first start | Python 3.7–3.9 and no internet (or a proxy blocks github.com / pypi.org) | allow those hosts and start `webbee` again, or use the one-line installer above |
|
|
60
|
+
| `No matching distribution found for webbee` | Python 3.6 or older | the one-line installer above — it brings its own Python |
|
|
61
|
+
|
|
62
|
+
</details>
|
|
63
|
+
|
|
45
64
|
---
|
|
46
65
|
|
|
47
66
|
## 💡 Quick Start
|
|
@@ -1,20 +1,25 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "webbee"
|
|
3
|
-
version = "0.5.
|
|
3
|
+
version = "0.5.6"
|
|
4
4
|
description = "Webbee 🐝 — the Imperal Cloud coding agent in your terminal"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = "GPL-3.0-or-later"
|
|
7
7
|
license-files = ["LICENSE"]
|
|
8
|
-
requires-python = ">=3.
|
|
8
|
+
requires-python = ">=3.7"
|
|
9
9
|
authors = [{ name = "Valentin Scerbacov", email = "valentin@imperal.io" }, { name = "Imperal, Inc." }]
|
|
10
10
|
dependencies = [
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
11
|
+
# Every dependency below needs Python 3.10+ (the MCP SDK, numpy 2, tree-sitter). On 3.7-3.9
|
|
12
|
+
# pip installs none of them, and the `webbee` command (webbee/_launch.py) runs this same
|
|
13
|
+
# release on a Python 3.12 it sets up once with uv -- `uv` comes as a wheel on 3.8+,
|
|
14
|
+
# 3.7 fetches it with uv's own installer.
|
|
15
|
+
'uv>=0.4; python_version >= "3.8" and python_version < "3.10"',
|
|
16
|
+
'imperal-mcp>=0.5.7; python_version >= "3.10"',
|
|
17
|
+
'imperal-sdk>=6.3.6; python_version >= "3.10"',
|
|
18
|
+
'asyncssh>=2.14; python_version >= "3.10"',
|
|
19
|
+
'httpx>=0.27; python_version >= "3.10"',
|
|
20
|
+
'httpx-sse>=0.4; python_version >= "3.10"',
|
|
21
|
+
'rich>=13.7; python_version >= "3.10"',
|
|
22
|
+
'prompt_toolkit>=3; python_version >= "3.10"',
|
|
18
23
|
# --- code intelligence (CPC): the symbol/graph index is NOT optional ---
|
|
19
24
|
# Without these the indexer silently yields 0 symbols (boot.py degrades to
|
|
20
25
|
# intel=None), so repo_profile / graph_slice / search_code / impact_of_change
|
|
@@ -26,15 +31,15 @@ dependencies = [
|
|
|
26
31
|
# macosx_x86_64 wheel is cp312, and numpy stops there too). On that one
|
|
27
32
|
# platform the install stays green and intel fails soft to lexical+graph
|
|
28
33
|
# or off, exactly as tests/test_intel_no_numpy.py guarantees.
|
|
29
|
-
'tree-sitter>=0.23; sys_platform != "darwin" or platform_machine != "x86_64" or python_version < "3.13"',
|
|
30
|
-
'tree-sitter-python>=0.23; sys_platform != "darwin" or platform_machine != "x86_64" or python_version < "3.13"',
|
|
31
|
-
'tree-sitter-typescript>=0.23; sys_platform != "darwin" or platform_machine != "x86_64" or python_version < "3.13"',
|
|
32
|
-
'tree-sitter-javascript>=0.23; sys_platform != "darwin" or platform_machine != "x86_64" or python_version < "3.13"',
|
|
33
|
-
'watchfiles>=0.24; sys_platform != "darwin" or platform_machine != "x86_64" or python_version < "3.13"',
|
|
34
|
+
'tree-sitter>=0.23; python_version >= "3.10" and (sys_platform != "darwin" or platform_machine != "x86_64" or python_version < "3.13")',
|
|
35
|
+
'tree-sitter-python>=0.23; python_version >= "3.10" and (sys_platform != "darwin" or platform_machine != "x86_64" or python_version < "3.13")',
|
|
36
|
+
'tree-sitter-typescript>=0.23; python_version >= "3.10" and (sys_platform != "darwin" or platform_machine != "x86_64" or python_version < "3.13")',
|
|
37
|
+
'tree-sitter-javascript>=0.23; python_version >= "3.10" and (sys_platform != "darwin" or platform_machine != "x86_64" or python_version < "3.13")',
|
|
38
|
+
'watchfiles>=0.24; python_version >= "3.10" and (sys_platform != "darwin" or platform_machine != "x86_64" or python_version < "3.13")',
|
|
34
39
|
# Semantic arm: model2vec is pure-numpy (no ONNX toolchain), so it installs
|
|
35
40
|
# wherever wheels exist -- same platform carve-out as above.
|
|
36
|
-
'model2vec>=0.8.2; sys_platform != "darwin" or platform_machine != "x86_64" or python_version < "3.13"',
|
|
37
|
-
'numpy>=2; sys_platform != "darwin" or platform_machine != "x86_64" or python_version < "3.13"',
|
|
41
|
+
'model2vec>=0.8.2; python_version >= "3.10" and (sys_platform != "darwin" or platform_machine != "x86_64" or python_version < "3.13")',
|
|
42
|
+
'numpy>=2; python_version >= "3.10" and (sys_platform != "darwin" or platform_machine != "x86_64" or python_version < "3.13")',
|
|
38
43
|
]
|
|
39
44
|
keywords = ["webbee", "imperal", "icnli", "coding-agent", "ai-agent", "cli", "terminal", "llm"]
|
|
40
45
|
classifiers = [
|
|
@@ -42,6 +47,8 @@ classifiers = [
|
|
|
42
47
|
"Environment :: Console",
|
|
43
48
|
"Intended Audience :: Developers",
|
|
44
49
|
"Programming Language :: Python :: 3",
|
|
50
|
+
"Programming Language :: Python :: 3.7",
|
|
51
|
+
"Programming Language :: Python :: 3.8",
|
|
45
52
|
"Programming Language :: Python :: 3.9",
|
|
46
53
|
"Programming Language :: Python :: 3.10",
|
|
47
54
|
"Programming Language :: Python :: 3.11",
|
|
@@ -78,7 +85,7 @@ Issues = "https://github.com/imperalcloud/webbee-code/issues"
|
|
|
78
85
|
Protocol = "https://icnli.org"
|
|
79
86
|
|
|
80
87
|
[project.scripts]
|
|
81
|
-
webbee = "webbee.
|
|
88
|
+
webbee = "webbee._launch:main"
|
|
82
89
|
|
|
83
90
|
[build-system]
|
|
84
91
|
requires = ["hatchling"]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.5.6"
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
"""The `webbee` command on any Python from 3.7.
|
|
2
|
+
|
|
3
|
+
Webbee needs Python 3.10+ -- its dependencies do (the MCP SDK, numpy 2, tree-sitter). On
|
|
4
|
+
3.10+ this hands straight to webbee.cli. On an older Python pip installs webbee without those
|
|
5
|
+
dependencies, and this command runs the SAME webbee release on a Python 3.12 it sets up once
|
|
6
|
+
with uv in its own folder: the person gets the whole agent, never a reduced one.
|
|
7
|
+
|
|
8
|
+
Kept to Python 3.7 syntax and the standard library: it is the only module an old interpreter
|
|
9
|
+
ever runs. imperal-mcp carries the same launcher (imperal_mcp/_launch.py)."""
|
|
10
|
+
import hashlib
|
|
11
|
+
import io
|
|
12
|
+
import os
|
|
13
|
+
import platform
|
|
14
|
+
import shutil
|
|
15
|
+
import subprocess
|
|
16
|
+
import sys
|
|
17
|
+
import tarfile
|
|
18
|
+
import urllib.request
|
|
19
|
+
import zipfile
|
|
20
|
+
|
|
21
|
+
DIST = "webbee"
|
|
22
|
+
ENTRY_MODULE, ENTRY_FUNC = "webbee.cli", "main"
|
|
23
|
+
NATIVE = (3, 10) # the Python the dependencies need
|
|
24
|
+
RUNTIME_PYTHON = "3.12"
|
|
25
|
+
SPEC_ENV = "WEBBEE_RUNTIME_SPEC" # what to install instead of DIST==version (tests, mirrors)
|
|
26
|
+
HOME_ENV = "WEBBEE_RUNTIME_HOME"
|
|
27
|
+
_WINDOWS = os.name == "nt"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _version():
|
|
31
|
+
from webbee import __version__
|
|
32
|
+
return __version__ # the light install on an old Python and its runtime are one release
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _home():
|
|
36
|
+
if os.environ.get(HOME_ENV):
|
|
37
|
+
return os.environ[HOME_ENV]
|
|
38
|
+
if _WINDOWS:
|
|
39
|
+
base = os.environ.get("LOCALAPPDATA") or os.path.expanduser("~")
|
|
40
|
+
else:
|
|
41
|
+
base = os.environ.get("XDG_DATA_HOME") or os.path.join(os.path.expanduser("~"), ".local", "share")
|
|
42
|
+
return os.path.join(base, DIST, "runtime")
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _say(msg):
|
|
46
|
+
sys.stderr.write(msg + "\n")
|
|
47
|
+
sys.stderr.flush()
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _bin(prefix, name):
|
|
51
|
+
if _WINDOWS:
|
|
52
|
+
return os.path.join(prefix, "Scripts", name + ".exe")
|
|
53
|
+
return os.path.join(prefix, "bin", name)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _find_uv(home):
|
|
57
|
+
"""uv from the pip-installed `uv` package (Python 3.8+), PATH, the usual install folders,
|
|
58
|
+
or our own earlier bootstrap -- else None."""
|
|
59
|
+
try:
|
|
60
|
+
import uv # noqa: F401 -- the PyPI `uv` package ships the binary
|
|
61
|
+
found = uv.find_uv_bin()
|
|
62
|
+
if found and os.path.exists(found):
|
|
63
|
+
return found
|
|
64
|
+
except Exception:
|
|
65
|
+
pass
|
|
66
|
+
name = "uv.exe" if _WINDOWS else "uv"
|
|
67
|
+
candidates = [shutil.which("uv"), os.path.join(home, "uv", name)]
|
|
68
|
+
for d in (os.path.join("~", ".local", "bin"), os.path.join("~", ".cargo", "bin")):
|
|
69
|
+
candidates.append(os.path.join(os.path.expanduser(d), name))
|
|
70
|
+
for c in candidates:
|
|
71
|
+
if c and os.path.exists(c):
|
|
72
|
+
return c
|
|
73
|
+
return None
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
UV_RELEASES = "https://github.com/astral-sh/uv/releases/latest/download/"
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def _uv_target():
|
|
80
|
+
"""uv's release name for this machine, e.g. x86_64-unknown-linux-gnu."""
|
|
81
|
+
machine = platform.machine().lower()
|
|
82
|
+
arch = {"x86_64": "x86_64", "amd64": "x86_64", "aarch64": "aarch64", "arm64": "aarch64",
|
|
83
|
+
"i686": "i686", "i386": "i686", "armv7l": "armv7", "ppc64le": "powerpc64le",
|
|
84
|
+
"s390x": "s390x"}.get(machine)
|
|
85
|
+
if arch is None:
|
|
86
|
+
raise RuntimeError("no uv build for a %s machine" % machine)
|
|
87
|
+
if sys.platform == "darwin":
|
|
88
|
+
return arch + "-apple-darwin"
|
|
89
|
+
if _WINDOWS:
|
|
90
|
+
return arch + "-pc-windows-msvc"
|
|
91
|
+
libc = platform.libc_ver()[0]
|
|
92
|
+
if arch == "armv7":
|
|
93
|
+
return "armv7-unknown-linux-" + ("gnueabihf" if libc == "glibc" else "musleabihf")
|
|
94
|
+
return arch + "-unknown-linux-" + ("gnu" if libc == "glibc" else "musl")
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def _fetch(url):
|
|
98
|
+
req = urllib.request.Request(url, headers={"User-Agent": DIST + "-launcher"})
|
|
99
|
+
with urllib.request.urlopen(req, timeout=120) as r:
|
|
100
|
+
return r.read()
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _install_uv(home):
|
|
104
|
+
"""Python 3.7 has no `uv` wheel: download uv's release build for this machine from GitHub,
|
|
105
|
+
check it against the published SHA-256 and unpack it into our own folder. Standard library
|
|
106
|
+
only -- no curl, no installer script, no PATH or shell-profile changes."""
|
|
107
|
+
target = _uv_target()
|
|
108
|
+
archive = "uv-" + target + (".zip" if _WINDOWS else ".tar.gz")
|
|
109
|
+
_say("Fetching uv (it brings the Python %s %s runs on)..." % (RUNTIME_PYTHON, DIST))
|
|
110
|
+
data = _fetch(UV_RELEASES + archive)
|
|
111
|
+
expected = _fetch(UV_RELEASES + archive + ".sha256").decode().split()[0].lower()
|
|
112
|
+
if hashlib.sha256(data).hexdigest() != expected:
|
|
113
|
+
raise RuntimeError("uv download does not match its published SHA-256")
|
|
114
|
+
dest = os.path.join(home, "uv")
|
|
115
|
+
shutil.rmtree(dest, ignore_errors=True)
|
|
116
|
+
os.makedirs(dest)
|
|
117
|
+
name = "uv.exe" if _WINDOWS else "uv"
|
|
118
|
+
if _WINDOWS:
|
|
119
|
+
with zipfile.ZipFile(io.BytesIO(data)) as z:
|
|
120
|
+
member = next(m for m in z.namelist() if m.rsplit("/", 1)[-1] == name)
|
|
121
|
+
with open(os.path.join(dest, name), "wb") as f:
|
|
122
|
+
f.write(z.read(member))
|
|
123
|
+
else:
|
|
124
|
+
with tarfile.open(fileobj=io.BytesIO(data), mode="r:gz") as t:
|
|
125
|
+
member = next(m for m in t.getmembers() if m.isfile() and m.name.rsplit("/", 1)[-1] == name)
|
|
126
|
+
with open(os.path.join(dest, name), "wb") as f:
|
|
127
|
+
f.write(t.extractfile(member).read())
|
|
128
|
+
os.chmod(os.path.join(dest, name), 0o755)
|
|
129
|
+
return os.path.join(dest, name)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
class _Lock(object):
|
|
133
|
+
"""One setup at a time when two terminals start webbee together."""
|
|
134
|
+
|
|
135
|
+
def __init__(self, path):
|
|
136
|
+
self.path, self.f = path, None
|
|
137
|
+
|
|
138
|
+
def __enter__(self):
|
|
139
|
+
self.f = open(self.path, "a+")
|
|
140
|
+
try:
|
|
141
|
+
if _WINDOWS:
|
|
142
|
+
import msvcrt
|
|
143
|
+
msvcrt.locking(self.f.fileno(), msvcrt.LK_LOCK, 1)
|
|
144
|
+
else:
|
|
145
|
+
import fcntl
|
|
146
|
+
fcntl.flock(self.f.fileno(), fcntl.LOCK_EX)
|
|
147
|
+
except Exception:
|
|
148
|
+
pass # no lock beats no start
|
|
149
|
+
return self
|
|
150
|
+
|
|
151
|
+
def __exit__(self, *exc):
|
|
152
|
+
self.f.close()
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def runtime(version):
|
|
156
|
+
"""Path of the `DIST` command in a Python 3.12 environment holding this exact release;
|
|
157
|
+
set up on the first call, reused after."""
|
|
158
|
+
home = _home()
|
|
159
|
+
os.makedirs(home, exist_ok=True)
|
|
160
|
+
prefix = os.path.join(home, "py" + RUNTIME_PYTHON + "-" + version)
|
|
161
|
+
exe = _bin(prefix, DIST)
|
|
162
|
+
ready = os.path.join(prefix, ".ready")
|
|
163
|
+
if os.path.exists(ready) and os.path.exists(exe):
|
|
164
|
+
return exe
|
|
165
|
+
with _Lock(os.path.join(home, ".setup.lock")):
|
|
166
|
+
if os.path.exists(ready) and os.path.exists(exe):
|
|
167
|
+
return exe # the other terminal finished it meanwhile
|
|
168
|
+
_say("Python %d.%d is older than %s needs (%d.%d): setting up %s %s on Python %s "
|
|
169
|
+
"once, in %s ..." % (sys.version_info[0], sys.version_info[1], DIST, NATIVE[0], NATIVE[1],
|
|
170
|
+
DIST, version, RUNTIME_PYTHON, prefix))
|
|
171
|
+
uv = _find_uv(home) or _install_uv(home)
|
|
172
|
+
shutil.rmtree(prefix, ignore_errors=True) # a half-made one from an interrupted setup
|
|
173
|
+
subprocess.check_call([uv, "venv", "--quiet", "--python", RUNTIME_PYTHON, prefix],
|
|
174
|
+
stdout=sys.stderr)
|
|
175
|
+
spec = os.environ.get(SPEC_ENV) or "%s==%s" % (DIST, version)
|
|
176
|
+
subprocess.check_call([uv, "pip", "install", "--quiet", "--python", _bin(prefix, "python"), spec],
|
|
177
|
+
stdout=sys.stderr)
|
|
178
|
+
if not os.path.exists(exe):
|
|
179
|
+
raise RuntimeError("%s is missing after the install" % exe)
|
|
180
|
+
open(ready, "w").close()
|
|
181
|
+
for name in os.listdir(home): # earlier releases' environments
|
|
182
|
+
old = os.path.join(home, name)
|
|
183
|
+
if name.startswith("py") and old != prefix and os.path.isdir(old):
|
|
184
|
+
shutil.rmtree(old, ignore_errors=True)
|
|
185
|
+
_say("Done -- next starts are immediate.")
|
|
186
|
+
return exe
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def main():
|
|
190
|
+
if sys.version_info >= NATIVE:
|
|
191
|
+
import importlib
|
|
192
|
+
return getattr(importlib.import_module(ENTRY_MODULE), ENTRY_FUNC)()
|
|
193
|
+
try:
|
|
194
|
+
exe = runtime(_version())
|
|
195
|
+
except Exception as e:
|
|
196
|
+
_say("Could not set up %s on Python %s: %s\n"
|
|
197
|
+
"Install it with uv instead (it brings its own Python):\n"
|
|
198
|
+
" curl -LsSf https://astral.sh/uv/install.sh | sh && uv tool install %s"
|
|
199
|
+
% (DIST, RUNTIME_PYTHON, e, DIST))
|
|
200
|
+
sys.exit(1)
|
|
201
|
+
argv = [exe] + sys.argv[1:]
|
|
202
|
+
if _WINDOWS: # no exec on Windows: run it and pass its exit code on
|
|
203
|
+
sys.exit(subprocess.call(argv))
|
|
204
|
+
os.execv(exe, argv)
|
|
@@ -75,13 +75,22 @@ def _consume_mode(payload, on_mode) -> bool:
|
|
|
75
75
|
return True
|
|
76
76
|
|
|
77
77
|
|
|
78
|
-
def
|
|
79
|
-
"""
|
|
80
|
-
(repl._run_turn treats
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
78
|
+
async def _await_turn(coro) -> None:
|
|
79
|
+
"""Run a submitted turn and wait for it, letting a cancel of THIS task end the
|
|
80
|
+
poller even when the turn swallows CancelledError (repl._run_turn treats it as
|
|
81
|
+
a user interrupt). The turn runs as its own task and the poller only waits on
|
|
82
|
+
it, so the cancel lands in the poller's wait, never inside the turn: without
|
|
83
|
+
that the poller would sail past its own cancellation and asyncio.run's
|
|
84
|
+
shutdown would hang on a task that ignored it. Works on every Python
|
|
85
|
+
(Task.cancelling() would need 3.11)."""
|
|
86
|
+
turn = asyncio.ensure_future(coro)
|
|
87
|
+
try:
|
|
88
|
+
await asyncio.wait({turn})
|
|
89
|
+
except asyncio.CancelledError:
|
|
90
|
+
turn.cancel() # the turn ends as a user interrupt would
|
|
91
|
+
await asyncio.wait({turn})
|
|
92
|
+
raise
|
|
93
|
+
turn.result() # a turn's own error reaches the poller as before
|
|
85
94
|
|
|
86
95
|
|
|
87
96
|
async def poll_idle_steer(cfg, token_provider, *, workspace: str, is_busy,
|
|
@@ -251,9 +260,7 @@ async def poll_idle_steer(cfg, token_provider, *, workspace: str, is_busy,
|
|
|
251
260
|
if is_busy():
|
|
252
261
|
continue
|
|
253
262
|
last_active = now() # an attach pickup = activity
|
|
254
|
-
await attach_turn(attach)
|
|
255
|
-
if _cancel_absorbed():
|
|
256
|
-
raise asyncio.CancelledError
|
|
263
|
+
await _await_turn(attach_turn(attach))
|
|
257
264
|
failures = 0
|
|
258
265
|
continue
|
|
259
266
|
if not backlog:
|
|
@@ -266,10 +273,8 @@ async def poll_idle_steer(cfg, token_provider, *, workspace: str, is_busy,
|
|
|
266
273
|
backlog.appendleft(item) # a local line won the race -- defer, don't drop
|
|
267
274
|
continue
|
|
268
275
|
last_active = now() # an item arrived = activity
|
|
269
|
-
await submit(text, str(item.get("surface") or "telegram"),
|
|
270
|
-
|
|
271
|
-
if _cancel_absorbed():
|
|
272
|
-
raise asyncio.CancelledError
|
|
276
|
+
await _await_turn(submit(text, str(item.get("surface") or "telegram"),
|
|
277
|
+
str(item.get("iid") or "")))
|
|
273
278
|
except asyncio.CancelledError:
|
|
274
279
|
raise
|
|
275
280
|
except Exception:
|