webbee 0.6.1__tar.gz → 0.6.3__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.1 → webbee-0.6.3}/CHANGELOG.md +12 -0
  2. {webbee-0.6.1 → webbee-0.6.3}/PKG-INFO +2 -2
  3. {webbee-0.6.1 → webbee-0.6.3}/pyproject.toml +2 -2
  4. webbee-0.6.3/src/webbee/__init__.py +1 -0
  5. {webbee-0.6.1 → webbee-0.6.3}/src/webbee/autoupdate.py +27 -7
  6. {webbee-0.6.1 → webbee-0.6.3}/tests/test_autoupdate.py +18 -6
  7. webbee-0.6.1/src/webbee/__init__.py +0 -1
  8. {webbee-0.6.1 → webbee-0.6.3}/.github/workflows/publish.yml +0 -0
  9. {webbee-0.6.1 → webbee-0.6.3}/.gitignore +0 -0
  10. {webbee-0.6.1 → webbee-0.6.3}/LICENSE +0 -0
  11. {webbee-0.6.1 → webbee-0.6.3}/README.md +0 -0
  12. {webbee-0.6.1 → webbee-0.6.3}/install.sh +0 -0
  13. {webbee-0.6.1 → webbee-0.6.3}/src/webbee/__main__.py +0 -0
  14. {webbee-0.6.1 → webbee-0.6.3}/src/webbee/_launch.py +0 -0
  15. {webbee-0.6.1 → webbee-0.6.3}/src/webbee/account.py +0 -0
  16. {webbee-0.6.1 → webbee-0.6.3}/src/webbee/active_sessions.py +0 -0
  17. {webbee-0.6.1 → webbee-0.6.3}/src/webbee/banner_art.py +0 -0
  18. {webbee-0.6.1 → webbee-0.6.3}/src/webbee/boot.py +0 -0
  19. {webbee-0.6.1 → webbee-0.6.3}/src/webbee/browser.py +0 -0
  20. {webbee-0.6.1 → webbee-0.6.3}/src/webbee/checkpoints.py +0 -0
  21. {webbee-0.6.1 → webbee-0.6.3}/src/webbee/cli.py +0 -0
  22. {webbee-0.6.1 → webbee-0.6.3}/src/webbee/clipboard.py +0 -0
  23. {webbee-0.6.1 → webbee-0.6.3}/src/webbee/clipboard_read.py +0 -0
  24. {webbee-0.6.1 → webbee-0.6.3}/src/webbee/clipboard_session.py +0 -0
  25. {webbee-0.6.1 → webbee-0.6.3}/src/webbee/coding_context.py +0 -0
  26. {webbee-0.6.1 → webbee-0.6.3}/src/webbee/commands.py +0 -0
  27. {webbee-0.6.1 → webbee-0.6.3}/src/webbee/config.py +0 -0
  28. {webbee-0.6.1 → webbee-0.6.3}/src/webbee/consent.py +0 -0
  29. {webbee-0.6.1 → webbee-0.6.3}/src/webbee/details.py +0 -0
  30. {webbee-0.6.1 → webbee-0.6.3}/src/webbee/events.py +0 -0
  31. {webbee-0.6.1 → webbee-0.6.3}/src/webbee/frames.py +0 -0
  32. {webbee-0.6.1 → webbee-0.6.3}/src/webbee/home.py +0 -0
  33. {webbee-0.6.1 → webbee-0.6.3}/src/webbee/home_view.py +0 -0
  34. {webbee-0.6.1 → webbee-0.6.3}/src/webbee/http.py +0 -0
  35. {webbee-0.6.1 → webbee-0.6.3}/src/webbee/instance_lock.py +0 -0
  36. {webbee-0.6.1 → webbee-0.6.3}/src/webbee/intel/__init__.py +0 -0
  37. {webbee-0.6.1 → webbee-0.6.3}/src/webbee/intel/graph.py +0 -0
  38. {webbee-0.6.1 → webbee-0.6.3}/src/webbee/intel/outline.py +0 -0
  39. {webbee-0.6.1 → webbee-0.6.3}/src/webbee/intel/query.py +0 -0
  40. {webbee-0.6.1 → webbee-0.6.3}/src/webbee/intel/service.py +0 -0
  41. {webbee-0.6.1 → webbee-0.6.3}/src/webbee/intel/store.py +0 -0
  42. {webbee-0.6.1 → webbee-0.6.3}/src/webbee/intel/vectors.py +0 -0
  43. {webbee-0.6.1 → webbee-0.6.3}/src/webbee/intel/watch.py +0 -0
  44. {webbee-0.6.1 → webbee-0.6.3}/src/webbee/media.py +0 -0
  45. {webbee-0.6.1 → webbee-0.6.3}/src/webbee/modals.py +0 -0
  46. {webbee-0.6.1 → webbee-0.6.3}/src/webbee/mode_store.py +0 -0
  47. {webbee-0.6.1 → webbee-0.6.3}/src/webbee/native_files.py +0 -0
  48. {webbee-0.6.1 → webbee-0.6.3}/src/webbee/native_read.py +0 -0
  49. {webbee-0.6.1 → webbee-0.6.3}/src/webbee/newtab_mode.py +0 -0
  50. {webbee-0.6.1 → webbee-0.6.3}/src/webbee/output_pane.py +0 -0
  51. {webbee-0.6.1 → webbee-0.6.3}/src/webbee/pricing_constants.py +0 -0
  52. {webbee-0.6.1 → webbee-0.6.3}/src/webbee/queue_panel.py +0 -0
  53. {webbee-0.6.1 → webbee-0.6.3}/src/webbee/reflow.py +0 -0
  54. {webbee-0.6.1 → webbee-0.6.3}/src/webbee/remote.py +0 -0
  55. {webbee-0.6.1 → webbee-0.6.3}/src/webbee/render.py +0 -0
  56. {webbee-0.6.1 → webbee-0.6.3}/src/webbee/repl.py +0 -0
  57. {webbee-0.6.1 → webbee-0.6.3}/src/webbee/repo.py +0 -0
  58. {webbee-0.6.1 → webbee-0.6.3}/src/webbee/selection.py +0 -0
  59. {webbee-0.6.1 → webbee-0.6.3}/src/webbee/session.py +0 -0
  60. {webbee-0.6.1 → webbee-0.6.3}/src/webbee/sessions.py +0 -0
  61. {webbee-0.6.1 → webbee-0.6.3}/src/webbee/sizing.py +0 -0
  62. {webbee-0.6.1 → webbee-0.6.3}/src/webbee/slots.py +0 -0
  63. {webbee-0.6.1 → webbee-0.6.3}/src/webbee/steer.py +0 -0
  64. {webbee-0.6.1 → webbee-0.6.3}/src/webbee/stream.py +0 -0
  65. {webbee-0.6.1 → webbee-0.6.3}/src/webbee/tab_store.py +0 -0
  66. {webbee-0.6.1 → webbee-0.6.3}/src/webbee/tabs.py +0 -0
  67. {webbee-0.6.1 → webbee-0.6.3}/src/webbee/thread.py +0 -0
  68. {webbee-0.6.1 → webbee-0.6.3}/src/webbee/tier_store.py +0 -0
  69. {webbee-0.6.1 → webbee-0.6.3}/src/webbee/todo_panel.py +0 -0
  70. {webbee-0.6.1 → webbee-0.6.3}/src/webbee/tokens.py +0 -0
  71. {webbee-0.6.1 → webbee-0.6.3}/src/webbee/tools.py +0 -0
  72. {webbee-0.6.1 → webbee-0.6.3}/src/webbee/tui.py +0 -0
  73. {webbee-0.6.1 → webbee-0.6.3}/src/webbee/update.py +0 -0
  74. {webbee-0.6.1 → webbee-0.6.3}/src/webbee/urlopen.py +0 -0
  75. {webbee-0.6.1 → webbee-0.6.3}/src/webbee/wallet.py +0 -0
  76. {webbee-0.6.1 → webbee-0.6.3}/src/webbee/worktrees.py +0 -0
  77. {webbee-0.6.1 → webbee-0.6.3}/tests/__init__.py +0 -0
  78. {webbee-0.6.1 → webbee-0.6.3}/tests/conftest.py +0 -0
  79. {webbee-0.6.1 → webbee-0.6.3}/tests/test_account.py +0 -0
  80. {webbee-0.6.1 → webbee-0.6.3}/tests/test_active_sessions.py +0 -0
  81. {webbee-0.6.1 → webbee-0.6.3}/tests/test_audit_2026_07_25_findings.py +0 -0
  82. {webbee-0.6.1 → webbee-0.6.3}/tests/test_checkpoints.py +0 -0
  83. {webbee-0.6.1 → webbee-0.6.3}/tests/test_cli.py +0 -0
  84. {webbee-0.6.1 → webbee-0.6.3}/tests/test_clipboard.py +0 -0
  85. {webbee-0.6.1 → webbee-0.6.3}/tests/test_clipboard_read.py +0 -0
  86. {webbee-0.6.1 → webbee-0.6.3}/tests/test_coding_context.py +0 -0
  87. {webbee-0.6.1 → webbee-0.6.3}/tests/test_commands.py +0 -0
  88. {webbee-0.6.1 → webbee-0.6.3}/tests/test_config.py +0 -0
  89. {webbee-0.6.1 → webbee-0.6.3}/tests/test_cpc_contract_stable.py +0 -0
  90. {webbee-0.6.1 → webbee-0.6.3}/tests/test_cpc_outline.py +0 -0
  91. {webbee-0.6.1 → webbee-0.6.3}/tests/test_cross_loop_token.py +0 -0
  92. {webbee-0.6.1 → webbee-0.6.3}/tests/test_details.py +0 -0
  93. {webbee-0.6.1 → webbee-0.6.3}/tests/test_events.py +0 -0
  94. {webbee-0.6.1 → webbee-0.6.3}/tests/test_freeze_fix.py +0 -0
  95. {webbee-0.6.1 → webbee-0.6.3}/tests/test_home.py +0 -0
  96. {webbee-0.6.1 → webbee-0.6.3}/tests/test_home_view.py +0 -0
  97. {webbee-0.6.1 → webbee-0.6.3}/tests/test_home_view_mouse.py +0 -0
  98. {webbee-0.6.1 → webbee-0.6.3}/tests/test_inspect_page.py +0 -0
  99. {webbee-0.6.1 → webbee-0.6.3}/tests/test_instance_lock.py +0 -0
  100. {webbee-0.6.1 → webbee-0.6.3}/tests/test_intel_chunker.py +0 -0
  101. {webbee-0.6.1 → webbee-0.6.3}/tests/test_intel_embed.py +0 -0
  102. {webbee-0.6.1 → webbee-0.6.3}/tests/test_intel_graph.py +0 -0
  103. {webbee-0.6.1 → webbee-0.6.3}/tests/test_intel_no_numpy.py +0 -0
  104. {webbee-0.6.1 → webbee-0.6.3}/tests/test_intel_query.py +0 -0
  105. {webbee-0.6.1 → webbee-0.6.3}/tests/test_intel_service.py +0 -0
  106. {webbee-0.6.1 → webbee-0.6.3}/tests/test_intel_store.py +0 -0
  107. {webbee-0.6.1 → webbee-0.6.3}/tests/test_intel_vectors.py +0 -0
  108. {webbee-0.6.1 → webbee-0.6.3}/tests/test_intel_watch.py +0 -0
  109. {webbee-0.6.1 → webbee-0.6.3}/tests/test_launch.py +0 -0
  110. {webbee-0.6.1 → webbee-0.6.3}/tests/test_marathon.py +0 -0
  111. {webbee-0.6.1 → webbee-0.6.3}/tests/test_modals_e2e.py +0 -0
  112. {webbee-0.6.1 → webbee-0.6.3}/tests/test_mode_store.py +0 -0
  113. {webbee-0.6.1 → webbee-0.6.3}/tests/test_native_files.py +0 -0
  114. {webbee-0.6.1 → webbee-0.6.3}/tests/test_newtab_mode.py +0 -0
  115. {webbee-0.6.1 → webbee-0.6.3}/tests/test_packaging.py +0 -0
  116. {webbee-0.6.1 → webbee-0.6.3}/tests/test_probe_cost.py +0 -0
  117. {webbee-0.6.1 → webbee-0.6.3}/tests/test_queue_manage_0337.py +0 -0
  118. {webbee-0.6.1 → webbee-0.6.3}/tests/test_reflow.py +0 -0
  119. {webbee-0.6.1 → webbee-0.6.3}/tests/test_render.py +0 -0
  120. {webbee-0.6.1 → webbee-0.6.3}/tests/test_repl.py +0 -0
  121. {webbee-0.6.1 → webbee-0.6.3}/tests/test_repo.py +0 -0
  122. {webbee-0.6.1 → webbee-0.6.3}/tests/test_resilient_http.py +0 -0
  123. {webbee-0.6.1 → webbee-0.6.3}/tests/test_session.py +0 -0
  124. {webbee-0.6.1 → webbee-0.6.3}/tests/test_sessions.py +0 -0
  125. {webbee-0.6.1 → webbee-0.6.3}/tests/test_sizing.py +0 -0
  126. {webbee-0.6.1 → webbee-0.6.3}/tests/test_slots.py +0 -0
  127. {webbee-0.6.1 → webbee-0.6.3}/tests/test_steer.py +0 -0
  128. {webbee-0.6.1 → webbee-0.6.3}/tests/test_stream.py +0 -0
  129. {webbee-0.6.1 → webbee-0.6.3}/tests/test_tab_bar_stability.py +0 -0
  130. {webbee-0.6.1 → webbee-0.6.3}/tests/test_tab_restore_0337.py +0 -0
  131. {webbee-0.6.1 → webbee-0.6.3}/tests/test_tabs.py +0 -0
  132. {webbee-0.6.1 → webbee-0.6.3}/tests/test_terminal_ux_0336.py +0 -0
  133. {webbee-0.6.1 → webbee-0.6.3}/tests/test_thread.py +0 -0
  134. {webbee-0.6.1 → webbee-0.6.3}/tests/test_tier_invalidation.py +0 -0
  135. {webbee-0.6.1 → webbee-0.6.3}/tests/test_tier_store.py +0 -0
  136. {webbee-0.6.1 → webbee-0.6.3}/tests/test_todo_panel.py +0 -0
  137. {webbee-0.6.1 → webbee-0.6.3}/tests/test_tokens.py +0 -0
  138. {webbee-0.6.1 → webbee-0.6.3}/tests/test_tools.py +0 -0
  139. {webbee-0.6.1 → webbee-0.6.3}/tests/test_tools_vision_websearch.py +0 -0
  140. {webbee-0.6.1 → webbee-0.6.3}/tests/test_tui.py +0 -0
  141. {webbee-0.6.1 → webbee-0.6.3}/tests/test_tui_hardening.py +0 -0
  142. {webbee-0.6.1 → webbee-0.6.3}/tests/test_update.py +0 -0
  143. {webbee-0.6.1 → webbee-0.6.3}/tests/test_update_badge_0340.py +0 -0
  144. {webbee-0.6.1 → webbee-0.6.3}/tests/test_urlopen.py +0 -0
  145. {webbee-0.6.1 → webbee-0.6.3}/tests/test_version.py +0 -0
  146. {webbee-0.6.1 → webbee-0.6.3}/tests/test_version_badge_corner.py +0 -0
  147. {webbee-0.6.1 → webbee-0.6.3}/tests/test_wallet.py +0 -0
  148. {webbee-0.6.1 → webbee-0.6.3}/tests/test_worktrees.py +0 -0
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.6.3
4
+
5
+ * **imperal-mcp 0.8.2 at least**: a shell command reaches the kernel with the folder it ran in, so the frame places a
6
+ `git` run without its own `cd` in the right repository. Works with the kernel of 2026-10-25 (package 0.8.4): memory
7
+ lookups are reads, parallel calls each keep their part of the history, one step numbering, WORLD's HEAD checked
8
+ against the code map.
9
+
10
+ ## 0.6.2
11
+
12
+ * **The upgrade at the start installs a release the moment it is out**: by its wheel's address on PyPI's file host
13
+ (the index pages trail a release by minutes), past every install cache.
14
+
3
15
  ## 0.6.1
4
16
 
5
17
  * **The upgrade at the start finds a release minutes old**: it reads past pip's / uv's index cache (before, a fresh
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: webbee
3
- Version: 0.6.1
3
+ Version: 0.6.3
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
@@ -31,7 +31,7 @@ Requires-Python: >=3.7
31
31
  Requires-Dist: asyncssh>=2.14; python_version >= '3.10'
32
32
  Requires-Dist: httpx-sse>=0.4; python_version >= '3.10'
33
33
  Requires-Dist: httpx>=0.27; python_version >= '3.10'
34
- Requires-Dist: imperal-mcp>=0.8.1; python_version >= '3.10'
34
+ Requires-Dist: imperal-mcp>=0.8.2; python_version >= '3.10'
35
35
  Requires-Dist: imperal-sdk>=6.3.6; python_version >= '3.10'
36
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
37
  Requires-Dist: numpy>=2; python_version >= '3.10' and (sys_platform != 'darwin' or platform_machine != 'x86_64' or python_version < '3.13')
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "webbee"
3
- version = "0.6.1"
3
+ version = "0.6.3"
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"
@@ -13,7 +13,7 @@ dependencies = [
13
13
  # release on a Python 3.12 it sets up once with uv -- `uv` comes as a wheel on 3.8+,
14
14
  # 3.7 fetches it with uv's own installer.
15
15
  'uv>=0.4; python_version >= "3.8" and python_version < "3.10"',
16
- 'imperal-mcp>=0.8.1; python_version >= "3.10"', # raised to the newest at each release
16
+ 'imperal-mcp>=0.8.2; python_version >= "3.10"', # raised to the newest at each release
17
17
  'imperal-sdk>=6.3.6; python_version >= "3.10"',
18
18
  'asyncssh>=2.14; python_version >= "3.10"',
19
19
  'httpx>=0.27; python_version >= "3.10"',
@@ -0,0 +1 @@
1
+ __version__ = "0.6.3"
@@ -52,6 +52,26 @@ def latest(dist: str):
52
52
  return None
53
53
 
54
54
 
55
+ def wheel_url(dist: str, version: str):
56
+ """The release's own wheel on PyPI's file host -- there the moment it is published, while the index pages trail it
57
+ by minutes (live 2026-10-25: pip found no 0.6.1 a minute after PyPI served it) -- or None."""
58
+ try:
59
+ import httpx
60
+ r = httpx.get(PYPI.format(f"{dist}/{version}"), timeout=3.0)
61
+ r.raise_for_status()
62
+ wheels = [u["url"] for u in r.json().get("urls") or [] if u.get("packagetype") == "bdist_wheel"
63
+ and str(u.get("url") or "").startswith("https://files.pythonhosted.org/")]
64
+ return wheels[0] if wheels else None
65
+ except Exception:
66
+ return None
67
+
68
+
69
+ def _spec(name: str, dist: str, version: str, wheel) -> str:
70
+ """``name`` (with its extras) at ``version``: by its wheel's address when known, else by the index."""
71
+ url = wheel(dist, version) if wheel else None
72
+ return f"{name} @ {url}" if url else f"{name}=={version}"
73
+
74
+
55
75
  def _ver(s) -> tuple:
56
76
  try:
57
77
  return tuple(int(x) for x in str(s).strip().split("."))
@@ -78,9 +98,9 @@ def find_uv(which=safe_which):
78
98
 
79
99
 
80
100
  def self_command(version: str, prefix: str = sys.prefix, executable: str = sys.executable, env=os.environ,
81
- uv=None, pipx=None):
101
+ uv=None, pipx=None, wheel=None):
82
102
  """The command that installs webbee ``version`` where this webbee is installed -- or None."""
83
- spec = f"{DIST}=={version}"
103
+ spec = _spec(DIST, DIST, version, wheel)
84
104
  light = env.get(LIGHT_ENV)
85
105
  if light: # an old Python runs this release through its light install
86
106
  return [light, "-m", "pip", "install", "--upgrade", "--no-cache-dir", spec]
@@ -123,7 +143,7 @@ def mcp_installed(which=safe_which, run=subprocess.run) -> dict:
123
143
  return out
124
144
 
125
145
 
126
- def mcp_command(inst: dict, newest, uv, pipx):
146
+ def mcp_command(inst: dict, newest, uv, pipx, wheel=None):
127
147
  """The one command that makes the shell's imperal-mcp the newest with its code extra -- by the tool that installed
128
148
  it (uv, else pipx, for a new install) -- or None when it already is, or nothing can do it."""
129
149
  if inst and inst.get("version") and inst.get("version") == newest and inst.get("code"):
@@ -132,7 +152,7 @@ def mcp_command(inst: dict, newest, uv, pipx):
132
152
  return None # it did not answer: a working install is never replaced blind
133
153
  if inst and not newest and inst.get("code"):
134
154
  return None # PyPI unread: what works stays
135
- spec = f"{MCP_SPEC}=={newest}" if newest else (MCP_SPEC if inst else MCP_GIT_SPEC)
155
+ spec = _spec(MCP_SPEC, MCP, newest, wheel) if newest else (MCP_SPEC if inst else MCP_GIT_SPEC)
136
156
  tool = inst.get("tool") if inst else ("uv" if uv else "pipx" if pipx else "")
137
157
  return _install(tool, spec, uv, pipx, inst.get("python") if inst else None)
138
158
 
@@ -154,7 +174,7 @@ def _run(cmd, run=subprocess.run) -> bool:
154
174
 
155
175
 
156
176
  def upgrade_before_start(argv, current: str, say=print, fetch=latest, which=safe_which, run=subprocess.run,
157
- execv=os.execv, env=os.environ) -> list:
177
+ execv=os.execv, env=os.environ, wheel=wheel_url) -> list:
158
178
  """Bring webbee and the shell's imperal-mcp to their newest releases, then let the start go on; a self-upgrade
159
179
  starts webbee again on the new release (never returns then). Returns what it did, for the log. Never raises."""
160
180
  did: list = []
@@ -164,7 +184,7 @@ def upgrade_before_start(argv, current: str, say=print, fetch=latest, which=safe
164
184
  uv, pipx = find_uv(which), which("pipx")
165
185
  newest = fetch(DIST)
166
186
  if newest and _ver(newest) > _ver(current) and not env.get(DONE_ENV):
167
- cmd = self_command(newest, env=env, uv=uv, pipx=pipx)
187
+ cmd = self_command(newest, env=env, uv=uv, pipx=pipx, wheel=wheel)
168
188
  if cmd:
169
189
  say(f"🐝 updating webbee {current} -> {newest} ...")
170
190
  if _run(cmd, run):
@@ -178,7 +198,7 @@ def upgrade_before_start(argv, current: str, say=print, fetch=latest, which=safe
178
198
  say(f"🐝 webbee could not update itself -- run: {' '.join(cmd)}")
179
199
  mcp_newest = fetch(MCP)
180
200
  inst = mcp_installed(which, run)
181
- cmd = mcp_command(inst, mcp_newest, uv, pipx)
201
+ cmd = mcp_command(inst, mcp_newest, uv, pipx, wheel)
182
202
  if cmd:
183
203
  what = (f"imperal-mcp {inst.get('version') or '?'} -> {mcp_newest or 'source'}" if inst
184
204
  else f"imperal-mcp {mcp_newest or 'from source'}")
@@ -40,7 +40,7 @@ def test_webbee_upgrades_itself_by_its_own_tool():
40
40
  def test_a_newer_webbee_is_installed_and_started_again_before_anything_else():
41
41
  run, execd, env = _Run(), [], {}
42
42
  fetch = {"webbee": "0.6.0", "imperal-mcp": "0.8.2"}.get
43
- au.upgrade_before_start(["--mode", "plan"], "0.5.14", say=lambda m: None, fetch=fetch,
43
+ au.upgrade_before_start(["--mode", "plan"], "0.5.14", say=lambda m: None, fetch=fetch, wheel=None,
44
44
  which=_which({"pipx": "/bin/pipx"}), run=run, execv=lambda p, a: execd.append(a),
45
45
  env=env)
46
46
  assert run.calls[0][-1] == "webbee==0.6.0" and env[au.DONE_ENV] == "1"
@@ -53,7 +53,7 @@ def test_the_shell_s_imperal_mcp_is_brought_to_the_newest_with_its_code_extra(tm
53
53
  exe.write_text("")
54
54
  run = _Run(stdout="0.8.1 False") # an old release that lost its code extra
55
55
  monkeypatch.setattr(au, "find_uv", lambda which=None: "/bin/uv")
56
- did = au.upgrade_before_start([], "0.5.14", say=lambda m: None,
56
+ did = au.upgrade_before_start([], "0.5.14", say=lambda m: None, wheel=None,
57
57
  fetch={"webbee": "0.5.14", "imperal-mcp": "0.8.2"}.get,
58
58
  which=_which({"imperal-mcp": str(exe)}), run=run, env={})
59
59
  assert ["/bin/uv", "tool", "install", "--force", "--refresh", "imperal-mcp[code]==0.8.2"] in run.calls
@@ -67,7 +67,7 @@ def test_nothing_changes_when_all_is_newest_or_pypi_is_out_of_reach(tmp_path, mo
67
67
  monkeypatch.setattr(au, "find_uv", lambda which=None: None)
68
68
  for fetch in ({"webbee": "0.5.14", "imperal-mcp": "0.8.2"}.get, lambda d: None):
69
69
  run = _Run(stdout="0.8.2 True")
70
- au.upgrade_before_start([], "0.5.14", say=lambda m: None, fetch=fetch,
70
+ au.upgrade_before_start([], "0.5.14", wheel=lambda d, v: None, say=lambda m: None, fetch=fetch,
71
71
  which=_which({"imperal-mcp": str(exe), "pipx": "/bin/pipx"}), run=run, env={})
72
72
  assert not any("install" in c for c in run.calls)
73
73
 
@@ -81,13 +81,13 @@ def test_a_missing_imperal_mcp_is_installed_from_git_when_pypi_is_out_of_reach()
81
81
  def test_a_failed_or_switched_off_upgrade_never_stops_the_start(monkeypatch):
82
82
  said = []
83
83
  monkeypatch.setattr(au, "find_uv", lambda which=None: "/bin/uv")
84
- did = au.upgrade_before_start([], "0.5.14", say=said.append, fetch={"webbee": "0.6.0", "imperal-mcp": None}.get,
84
+ did = au.upgrade_before_start([], "0.5.14", wheel=lambda d, v: None, say=said.append, fetch={"webbee": "0.6.0", "imperal-mcp": None}.get,
85
85
  which=_which({}), run=_Run(rc=1), execv=lambda *a: 1 / 0,
86
86
  env={})
87
87
  assert did == [] and any("could not update itself" in s for s in said)
88
- assert au.upgrade_before_start([], "0.5.14", fetch=lambda d: 1 / 0, env={au.OFF_ENV: "1"}) == []
88
+ assert au.upgrade_before_start([], "0.5.14", wheel=lambda d, v: None, fetch=lambda d: 1 / 0, env={au.OFF_ENV: "1"}) == []
89
89
  run = _Run() # just restarted after a self-upgrade: never a second one
90
- au.upgrade_before_start([], "0.5.14", say=said.append, fetch={"webbee": "0.6.0"}.get, which=_which({}),
90
+ au.upgrade_before_start([], "0.5.14", wheel=lambda d, v: None, say=said.append, fetch={"webbee": "0.6.0"}.get, which=_which({}),
91
91
  run=run, execv=lambda *a: 1 / 0, env={au.DONE_ENV: "1"})
92
92
  assert not any("webbee==0.6.0" in c for c in run.calls)
93
93
 
@@ -112,3 +112,15 @@ def test_one_odd_answer_never_reinstalls_and_a_silent_install_is_left_alone(tmp_
112
112
  inst = au.mcp_installed(which=lambda n: str(exe), run=run)
113
113
  assert inst["code"] is True and au.mcp_command(inst, "0.8.1", "/bin/uv", None) is None
114
114
  assert au.mcp_command({"tool": "uv", "version": "", "code": False}, "0.8.2", "/bin/uv", None) is None
115
+
116
+
117
+ def test_a_release_is_installed_by_its_wheel_s_address_when_pypi_names_it():
118
+ url = "https://files.pythonhosted.org/packages/a8/1c/x/webbee-0.6.1-py3-none-any.whl"
119
+ wheel = lambda dist, v: url if dist == "webbee" else None # noqa: E731
120
+ cmd = au.self_command("0.6.1", prefix="/h/.local/pipx/venvs/webbee", env={}, pipx="/bin/pipx", wheel=wheel)
121
+ assert cmd[-1] == f"webbee @ {url}"
122
+ mcp_url = "https://files.pythonhosted.org/packages/b/imperal_mcp-0.8.2-py3-none-any.whl"
123
+ got = au.mcp_command({}, "0.8.2", "/bin/uv", None, wheel=lambda d, v: mcp_url)
124
+ assert got[-1] == f"imperal-mcp[code] @ {mcp_url}"
125
+ assert au.self_command("0.6.1", prefix="/v", executable="/v/bin/python", env={}, wheel=lambda d, v: None)[-1] \
126
+ == "webbee==0.6.1"
@@ -1 +0,0 @@
1
- __version__ = "0.6.1"
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
File without changes