webbee 0.6.6__tar.gz → 0.6.7__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.
Files changed (148) hide show
  1. {webbee-0.6.6 → webbee-0.6.7}/CHANGELOG.md +6 -0
  2. {webbee-0.6.6 → webbee-0.6.7}/PKG-INFO +1 -1
  3. {webbee-0.6.6 → webbee-0.6.7}/pyproject.toml +1 -1
  4. webbee-0.6.7/src/webbee/__init__.py +1 -0
  5. {webbee-0.6.6 → webbee-0.6.7}/src/webbee/boot.py +3 -0
  6. {webbee-0.6.6 → webbee-0.6.7}/src/webbee/intel/watch.py +27 -5
  7. {webbee-0.6.6 → webbee-0.6.7}/tests/test_intel_watch.py +26 -0
  8. webbee-0.6.6/src/webbee/__init__.py +0 -1
  9. {webbee-0.6.6 → webbee-0.6.7}/.github/workflows/publish.yml +0 -0
  10. {webbee-0.6.6 → webbee-0.6.7}/.gitignore +0 -0
  11. {webbee-0.6.6 → webbee-0.6.7}/LICENSE +0 -0
  12. {webbee-0.6.6 → webbee-0.6.7}/README.md +0 -0
  13. {webbee-0.6.6 → webbee-0.6.7}/install.sh +0 -0
  14. {webbee-0.6.6 → webbee-0.6.7}/src/webbee/__main__.py +0 -0
  15. {webbee-0.6.6 → webbee-0.6.7}/src/webbee/_launch.py +0 -0
  16. {webbee-0.6.6 → webbee-0.6.7}/src/webbee/account.py +0 -0
  17. {webbee-0.6.6 → webbee-0.6.7}/src/webbee/active_sessions.py +0 -0
  18. {webbee-0.6.6 → webbee-0.6.7}/src/webbee/autoupdate.py +0 -0
  19. {webbee-0.6.6 → webbee-0.6.7}/src/webbee/banner_art.py +0 -0
  20. {webbee-0.6.6 → webbee-0.6.7}/src/webbee/browser.py +0 -0
  21. {webbee-0.6.6 → webbee-0.6.7}/src/webbee/checkpoints.py +0 -0
  22. {webbee-0.6.6 → webbee-0.6.7}/src/webbee/cli.py +0 -0
  23. {webbee-0.6.6 → webbee-0.6.7}/src/webbee/clipboard.py +0 -0
  24. {webbee-0.6.6 → webbee-0.6.7}/src/webbee/clipboard_read.py +0 -0
  25. {webbee-0.6.6 → webbee-0.6.7}/src/webbee/clipboard_session.py +0 -0
  26. {webbee-0.6.6 → webbee-0.6.7}/src/webbee/coding_context.py +0 -0
  27. {webbee-0.6.6 → webbee-0.6.7}/src/webbee/commands.py +0 -0
  28. {webbee-0.6.6 → webbee-0.6.7}/src/webbee/config.py +0 -0
  29. {webbee-0.6.6 → webbee-0.6.7}/src/webbee/consent.py +0 -0
  30. {webbee-0.6.6 → webbee-0.6.7}/src/webbee/details.py +0 -0
  31. {webbee-0.6.6 → webbee-0.6.7}/src/webbee/events.py +0 -0
  32. {webbee-0.6.6 → webbee-0.6.7}/src/webbee/frames.py +0 -0
  33. {webbee-0.6.6 → webbee-0.6.7}/src/webbee/home.py +0 -0
  34. {webbee-0.6.6 → webbee-0.6.7}/src/webbee/home_view.py +0 -0
  35. {webbee-0.6.6 → webbee-0.6.7}/src/webbee/http.py +0 -0
  36. {webbee-0.6.6 → webbee-0.6.7}/src/webbee/instance_lock.py +0 -0
  37. {webbee-0.6.6 → webbee-0.6.7}/src/webbee/intel/__init__.py +0 -0
  38. {webbee-0.6.6 → webbee-0.6.7}/src/webbee/intel/graph.py +0 -0
  39. {webbee-0.6.6 → webbee-0.6.7}/src/webbee/intel/outline.py +0 -0
  40. {webbee-0.6.6 → webbee-0.6.7}/src/webbee/intel/query.py +0 -0
  41. {webbee-0.6.6 → webbee-0.6.7}/src/webbee/intel/service.py +0 -0
  42. {webbee-0.6.6 → webbee-0.6.7}/src/webbee/intel/store.py +0 -0
  43. {webbee-0.6.6 → webbee-0.6.7}/src/webbee/intel/vectors.py +0 -0
  44. {webbee-0.6.6 → webbee-0.6.7}/src/webbee/media.py +0 -0
  45. {webbee-0.6.6 → webbee-0.6.7}/src/webbee/modals.py +0 -0
  46. {webbee-0.6.6 → webbee-0.6.7}/src/webbee/mode_store.py +0 -0
  47. {webbee-0.6.6 → webbee-0.6.7}/src/webbee/native_files.py +0 -0
  48. {webbee-0.6.6 → webbee-0.6.7}/src/webbee/native_read.py +0 -0
  49. {webbee-0.6.6 → webbee-0.6.7}/src/webbee/newtab_mode.py +0 -0
  50. {webbee-0.6.6 → webbee-0.6.7}/src/webbee/output_pane.py +0 -0
  51. {webbee-0.6.6 → webbee-0.6.7}/src/webbee/pricing_constants.py +0 -0
  52. {webbee-0.6.6 → webbee-0.6.7}/src/webbee/queue_panel.py +0 -0
  53. {webbee-0.6.6 → webbee-0.6.7}/src/webbee/reflow.py +0 -0
  54. {webbee-0.6.6 → webbee-0.6.7}/src/webbee/remote.py +0 -0
  55. {webbee-0.6.6 → webbee-0.6.7}/src/webbee/render.py +0 -0
  56. {webbee-0.6.6 → webbee-0.6.7}/src/webbee/repl.py +0 -0
  57. {webbee-0.6.6 → webbee-0.6.7}/src/webbee/repo.py +0 -0
  58. {webbee-0.6.6 → webbee-0.6.7}/src/webbee/selection.py +0 -0
  59. {webbee-0.6.6 → webbee-0.6.7}/src/webbee/session.py +0 -0
  60. {webbee-0.6.6 → webbee-0.6.7}/src/webbee/sessions.py +0 -0
  61. {webbee-0.6.6 → webbee-0.6.7}/src/webbee/sizing.py +0 -0
  62. {webbee-0.6.6 → webbee-0.6.7}/src/webbee/slots.py +0 -0
  63. {webbee-0.6.6 → webbee-0.6.7}/src/webbee/steer.py +0 -0
  64. {webbee-0.6.6 → webbee-0.6.7}/src/webbee/stream.py +0 -0
  65. {webbee-0.6.6 → webbee-0.6.7}/src/webbee/tab_store.py +0 -0
  66. {webbee-0.6.6 → webbee-0.6.7}/src/webbee/tabs.py +0 -0
  67. {webbee-0.6.6 → webbee-0.6.7}/src/webbee/thread.py +0 -0
  68. {webbee-0.6.6 → webbee-0.6.7}/src/webbee/tier_store.py +0 -0
  69. {webbee-0.6.6 → webbee-0.6.7}/src/webbee/todo_panel.py +0 -0
  70. {webbee-0.6.6 → webbee-0.6.7}/src/webbee/tokens.py +0 -0
  71. {webbee-0.6.6 → webbee-0.6.7}/src/webbee/tools.py +0 -0
  72. {webbee-0.6.6 → webbee-0.6.7}/src/webbee/tui.py +0 -0
  73. {webbee-0.6.6 → webbee-0.6.7}/src/webbee/update.py +0 -0
  74. {webbee-0.6.6 → webbee-0.6.7}/src/webbee/urlopen.py +0 -0
  75. {webbee-0.6.6 → webbee-0.6.7}/src/webbee/wallet.py +0 -0
  76. {webbee-0.6.6 → webbee-0.6.7}/src/webbee/worktrees.py +0 -0
  77. {webbee-0.6.6 → webbee-0.6.7}/tests/__init__.py +0 -0
  78. {webbee-0.6.6 → webbee-0.6.7}/tests/conftest.py +0 -0
  79. {webbee-0.6.6 → webbee-0.6.7}/tests/test_account.py +0 -0
  80. {webbee-0.6.6 → webbee-0.6.7}/tests/test_active_sessions.py +0 -0
  81. {webbee-0.6.6 → webbee-0.6.7}/tests/test_audit_2026_07_25_findings.py +0 -0
  82. {webbee-0.6.6 → webbee-0.6.7}/tests/test_autoupdate.py +0 -0
  83. {webbee-0.6.6 → webbee-0.6.7}/tests/test_checkpoints.py +0 -0
  84. {webbee-0.6.6 → webbee-0.6.7}/tests/test_cli.py +0 -0
  85. {webbee-0.6.6 → webbee-0.6.7}/tests/test_clipboard.py +0 -0
  86. {webbee-0.6.6 → webbee-0.6.7}/tests/test_clipboard_read.py +0 -0
  87. {webbee-0.6.6 → webbee-0.6.7}/tests/test_coding_context.py +0 -0
  88. {webbee-0.6.6 → webbee-0.6.7}/tests/test_commands.py +0 -0
  89. {webbee-0.6.6 → webbee-0.6.7}/tests/test_config.py +0 -0
  90. {webbee-0.6.6 → webbee-0.6.7}/tests/test_cpc_contract_stable.py +0 -0
  91. {webbee-0.6.6 → webbee-0.6.7}/tests/test_cpc_outline.py +0 -0
  92. {webbee-0.6.6 → webbee-0.6.7}/tests/test_cross_loop_token.py +0 -0
  93. {webbee-0.6.6 → webbee-0.6.7}/tests/test_details.py +0 -0
  94. {webbee-0.6.6 → webbee-0.6.7}/tests/test_events.py +0 -0
  95. {webbee-0.6.6 → webbee-0.6.7}/tests/test_freeze_fix.py +0 -0
  96. {webbee-0.6.6 → webbee-0.6.7}/tests/test_home.py +0 -0
  97. {webbee-0.6.6 → webbee-0.6.7}/tests/test_home_view.py +0 -0
  98. {webbee-0.6.6 → webbee-0.6.7}/tests/test_home_view_mouse.py +0 -0
  99. {webbee-0.6.6 → webbee-0.6.7}/tests/test_inspect_page.py +0 -0
  100. {webbee-0.6.6 → webbee-0.6.7}/tests/test_instance_lock.py +0 -0
  101. {webbee-0.6.6 → webbee-0.6.7}/tests/test_intel_chunker.py +0 -0
  102. {webbee-0.6.6 → webbee-0.6.7}/tests/test_intel_embed.py +0 -0
  103. {webbee-0.6.6 → webbee-0.6.7}/tests/test_intel_graph.py +0 -0
  104. {webbee-0.6.6 → webbee-0.6.7}/tests/test_intel_no_numpy.py +0 -0
  105. {webbee-0.6.6 → webbee-0.6.7}/tests/test_intel_query.py +0 -0
  106. {webbee-0.6.6 → webbee-0.6.7}/tests/test_intel_service.py +0 -0
  107. {webbee-0.6.6 → webbee-0.6.7}/tests/test_intel_store.py +0 -0
  108. {webbee-0.6.6 → webbee-0.6.7}/tests/test_intel_vectors.py +0 -0
  109. {webbee-0.6.6 → webbee-0.6.7}/tests/test_launch.py +0 -0
  110. {webbee-0.6.6 → webbee-0.6.7}/tests/test_marathon.py +0 -0
  111. {webbee-0.6.6 → webbee-0.6.7}/tests/test_modals_e2e.py +0 -0
  112. {webbee-0.6.6 → webbee-0.6.7}/tests/test_mode_store.py +0 -0
  113. {webbee-0.6.6 → webbee-0.6.7}/tests/test_native_files.py +0 -0
  114. {webbee-0.6.6 → webbee-0.6.7}/tests/test_newtab_mode.py +0 -0
  115. {webbee-0.6.6 → webbee-0.6.7}/tests/test_packaging.py +0 -0
  116. {webbee-0.6.6 → webbee-0.6.7}/tests/test_probe_cost.py +0 -0
  117. {webbee-0.6.6 → webbee-0.6.7}/tests/test_queue_manage_0337.py +0 -0
  118. {webbee-0.6.6 → webbee-0.6.7}/tests/test_reflow.py +0 -0
  119. {webbee-0.6.6 → webbee-0.6.7}/tests/test_render.py +0 -0
  120. {webbee-0.6.6 → webbee-0.6.7}/tests/test_repl.py +0 -0
  121. {webbee-0.6.6 → webbee-0.6.7}/tests/test_repo.py +0 -0
  122. {webbee-0.6.6 → webbee-0.6.7}/tests/test_resilient_http.py +0 -0
  123. {webbee-0.6.6 → webbee-0.6.7}/tests/test_session.py +0 -0
  124. {webbee-0.6.6 → webbee-0.6.7}/tests/test_sessions.py +0 -0
  125. {webbee-0.6.6 → webbee-0.6.7}/tests/test_sizing.py +0 -0
  126. {webbee-0.6.6 → webbee-0.6.7}/tests/test_slots.py +0 -0
  127. {webbee-0.6.6 → webbee-0.6.7}/tests/test_steer.py +0 -0
  128. {webbee-0.6.6 → webbee-0.6.7}/tests/test_stream.py +0 -0
  129. {webbee-0.6.6 → webbee-0.6.7}/tests/test_tab_bar_stability.py +0 -0
  130. {webbee-0.6.6 → webbee-0.6.7}/tests/test_tab_restore_0337.py +0 -0
  131. {webbee-0.6.6 → webbee-0.6.7}/tests/test_tabs.py +0 -0
  132. {webbee-0.6.6 → webbee-0.6.7}/tests/test_terminal_ux_0336.py +0 -0
  133. {webbee-0.6.6 → webbee-0.6.7}/tests/test_thread.py +0 -0
  134. {webbee-0.6.6 → webbee-0.6.7}/tests/test_tier_invalidation.py +0 -0
  135. {webbee-0.6.6 → webbee-0.6.7}/tests/test_tier_store.py +0 -0
  136. {webbee-0.6.6 → webbee-0.6.7}/tests/test_todo_panel.py +0 -0
  137. {webbee-0.6.6 → webbee-0.6.7}/tests/test_tokens.py +0 -0
  138. {webbee-0.6.6 → webbee-0.6.7}/tests/test_tools.py +0 -0
  139. {webbee-0.6.6 → webbee-0.6.7}/tests/test_tools_vision_websearch.py +0 -0
  140. {webbee-0.6.6 → webbee-0.6.7}/tests/test_tui.py +0 -0
  141. {webbee-0.6.6 → webbee-0.6.7}/tests/test_tui_hardening.py +0 -0
  142. {webbee-0.6.6 → webbee-0.6.7}/tests/test_update.py +0 -0
  143. {webbee-0.6.6 → webbee-0.6.7}/tests/test_update_badge_0340.py +0 -0
  144. {webbee-0.6.6 → webbee-0.6.7}/tests/test_urlopen.py +0 -0
  145. {webbee-0.6.6 → webbee-0.6.7}/tests/test_version.py +0 -0
  146. {webbee-0.6.6 → webbee-0.6.7}/tests/test_version_badge_corner.py +0 -0
  147. {webbee-0.6.6 → webbee-0.6.7}/tests/test_wallet.py +0 -0
  148. {webbee-0.6.6 → webbee-0.6.7}/tests/test_worktrees.py +0 -0
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.6.7
4
+
5
+ * **The OS's file-watch limit never crashes webbee**: on a large tree (Linux `fs.inotify.max_user_watches`, a repo with
6
+ `node_modules`) the live re-index polls the tree every 3 s instead of dying, and the watcher's end is always
7
+ collected -- no "Task exception was never retrieved" when webbee quits.
8
+
3
9
  ## 0.6.6
4
10
 
5
11
  * **The upgrade's lock is the user's own**: it lives in webbee's data folder (only this user can write it), never in
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: webbee
3
- Version: 0.6.6
3
+ Version: 0.6.7
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
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "webbee"
3
- version = "0.6.6"
3
+ version = "0.6.7"
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"
@@ -0,0 +1 @@
1
+ __version__ = "0.6.7"
@@ -102,6 +102,9 @@ async def start_intel(cfg, workspace: str, intel_factory):
102
102
  await asyncio.to_thread(svc.build)
103
103
  from webbee.intel import watch
104
104
  watcher_task = asyncio.ensure_future(watch.watch_workspace(svc.root, svc.apply_changes))
105
+ # its end is always collected: an exception is never printed when webbee quits ("Task exception was never
106
+ # retrieved", live 2026-10-25)
107
+ watcher_task.add_done_callback(lambda t: t.cancelled() or t.exception())
105
108
  return svc, watcher_task
106
109
  except Exception:
107
110
  return None, None
@@ -17,14 +17,36 @@ def _ignored(path: str, root: str = "") -> bool:
17
17
  return ignored_path(root, path)
18
18
 
19
19
 
20
- async def watch_workspace(root: str, on_change) -> None:
20
+ # the OS's watch limit reached (Linux: fs.inotify.max_user_watches; live 2026-10-25 on a repo with node_modules: the
21
+ # watcher task died with "OS file watch limit reached" and its exception was printed when webbee quit): the tree is
22
+ # polled instead -- slower, never a crash
23
+ POLL_MS = 3000
24
+
25
+
26
+ async def watch_workspace(root: str, on_change, awatch=None) -> None:
21
27
  """Call on_change(set_of_relpaths) as files change. Fail-soft: if
22
- watchfiles is unavailable, return immediately (no watcher)."""
28
+ watchfiles is unavailable, return immediately (no watcher); if the OS cannot
29
+ watch the tree, poll it; any other watch failure ends the watcher alone."""
30
+ if awatch is None:
31
+ try:
32
+ from watchfiles import awatch
33
+ except ImportError:
34
+ return
23
35
  try:
24
- from watchfiles import awatch
25
- except ImportError:
36
+ await _follow(awatch(root), root, on_change)
37
+ except OSError as exc:
38
+ if "watch limit" not in str(exc) and "MaxFilesWatch" not in str(exc):
39
+ return
40
+ try:
41
+ await _follow(awatch(root, force_polling=True, poll_delay_ms=POLL_MS), root, on_change)
42
+ except Exception:
43
+ return
44
+ except Exception:
26
45
  return
27
- async for changes in awatch(root):
46
+
47
+
48
+ async def _follow(stream, root: str, on_change) -> None:
49
+ async for changes in stream:
28
50
  rels = set()
29
51
  for _chg, path in changes:
30
52
  if _ignored(path, root):
@@ -75,3 +75,29 @@ def test_missing_watchfiles_extra_returns_immediately(monkeypatch, tmp_path):
75
75
  monkeypatch.setattr(builtins, "__import__", _blocked_import)
76
76
  # Must return (not hang/raise) when the optional extra isn't installed.
77
77
  asyncio.run(watch.watch_workspace(str(tmp_path), lambda rels: None))
78
+
79
+
80
+ def test_the_os_watch_limit_falls_back_to_polling_and_never_raises(tmp_path):
81
+ """live 2026-10-25 (a repo with node_modules on Linux): "OS file watch limit reached" killed the watcher task and
82
+ its exception was printed when webbee quit -- the tree is polled instead."""
83
+ root = str(tmp_path)
84
+ calls, opened = [], []
85
+
86
+ def fake_awatch(_root, force_polling=False, poll_delay_ms=0):
87
+ opened.append(force_polling)
88
+
89
+ async def gen():
90
+ if not force_polling:
91
+ raise OSError('OS file watch limit reached. about ["/w"] (Error { kind: MaxFilesWatch })')
92
+ yield {("modified", os.path.join(root, "b.py"))}
93
+ return gen()
94
+
95
+ asyncio.run(watch.watch_workspace(root, calls.append, awatch=fake_awatch))
96
+ assert opened == [False, True] and calls == [{"b.py"}]
97
+
98
+ def broken(_root, **kw):
99
+ async def gen():
100
+ raise RuntimeError("anything else")
101
+ yield
102
+ return gen()
103
+ asyncio.run(watch.watch_workspace(root, calls.append, awatch=broken)) # ends quietly, never raises
@@ -1 +0,0 @@
1
- __version__ = "0.6.6"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes