webbee 0.6.0__tar.gz → 0.6.2__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.0 → webbee-0.6.2}/CHANGELOG.md +10 -0
  2. {webbee-0.6.0 → webbee-0.6.2}/PKG-INFO +1 -1
  3. {webbee-0.6.0 → webbee-0.6.2}/pyproject.toml +1 -1
  4. webbee-0.6.2/src/webbee/__init__.py +1 -0
  5. {webbee-0.6.0 → webbee-0.6.2}/src/webbee/autoupdate.py +39 -20
  6. {webbee-0.6.0 → webbee-0.6.2}/tests/test_autoupdate.py +24 -12
  7. webbee-0.6.0/src/webbee/__init__.py +0 -1
  8. {webbee-0.6.0 → webbee-0.6.2}/.github/workflows/publish.yml +0 -0
  9. {webbee-0.6.0 → webbee-0.6.2}/.gitignore +0 -0
  10. {webbee-0.6.0 → webbee-0.6.2}/LICENSE +0 -0
  11. {webbee-0.6.0 → webbee-0.6.2}/README.md +0 -0
  12. {webbee-0.6.0 → webbee-0.6.2}/install.sh +0 -0
  13. {webbee-0.6.0 → webbee-0.6.2}/src/webbee/__main__.py +0 -0
  14. {webbee-0.6.0 → webbee-0.6.2}/src/webbee/_launch.py +0 -0
  15. {webbee-0.6.0 → webbee-0.6.2}/src/webbee/account.py +0 -0
  16. {webbee-0.6.0 → webbee-0.6.2}/src/webbee/active_sessions.py +0 -0
  17. {webbee-0.6.0 → webbee-0.6.2}/src/webbee/banner_art.py +0 -0
  18. {webbee-0.6.0 → webbee-0.6.2}/src/webbee/boot.py +0 -0
  19. {webbee-0.6.0 → webbee-0.6.2}/src/webbee/browser.py +0 -0
  20. {webbee-0.6.0 → webbee-0.6.2}/src/webbee/checkpoints.py +0 -0
  21. {webbee-0.6.0 → webbee-0.6.2}/src/webbee/cli.py +0 -0
  22. {webbee-0.6.0 → webbee-0.6.2}/src/webbee/clipboard.py +0 -0
  23. {webbee-0.6.0 → webbee-0.6.2}/src/webbee/clipboard_read.py +0 -0
  24. {webbee-0.6.0 → webbee-0.6.2}/src/webbee/clipboard_session.py +0 -0
  25. {webbee-0.6.0 → webbee-0.6.2}/src/webbee/coding_context.py +0 -0
  26. {webbee-0.6.0 → webbee-0.6.2}/src/webbee/commands.py +0 -0
  27. {webbee-0.6.0 → webbee-0.6.2}/src/webbee/config.py +0 -0
  28. {webbee-0.6.0 → webbee-0.6.2}/src/webbee/consent.py +0 -0
  29. {webbee-0.6.0 → webbee-0.6.2}/src/webbee/details.py +0 -0
  30. {webbee-0.6.0 → webbee-0.6.2}/src/webbee/events.py +0 -0
  31. {webbee-0.6.0 → webbee-0.6.2}/src/webbee/frames.py +0 -0
  32. {webbee-0.6.0 → webbee-0.6.2}/src/webbee/home.py +0 -0
  33. {webbee-0.6.0 → webbee-0.6.2}/src/webbee/home_view.py +0 -0
  34. {webbee-0.6.0 → webbee-0.6.2}/src/webbee/http.py +0 -0
  35. {webbee-0.6.0 → webbee-0.6.2}/src/webbee/instance_lock.py +0 -0
  36. {webbee-0.6.0 → webbee-0.6.2}/src/webbee/intel/__init__.py +0 -0
  37. {webbee-0.6.0 → webbee-0.6.2}/src/webbee/intel/graph.py +0 -0
  38. {webbee-0.6.0 → webbee-0.6.2}/src/webbee/intel/outline.py +0 -0
  39. {webbee-0.6.0 → webbee-0.6.2}/src/webbee/intel/query.py +0 -0
  40. {webbee-0.6.0 → webbee-0.6.2}/src/webbee/intel/service.py +0 -0
  41. {webbee-0.6.0 → webbee-0.6.2}/src/webbee/intel/store.py +0 -0
  42. {webbee-0.6.0 → webbee-0.6.2}/src/webbee/intel/vectors.py +0 -0
  43. {webbee-0.6.0 → webbee-0.6.2}/src/webbee/intel/watch.py +0 -0
  44. {webbee-0.6.0 → webbee-0.6.2}/src/webbee/media.py +0 -0
  45. {webbee-0.6.0 → webbee-0.6.2}/src/webbee/modals.py +0 -0
  46. {webbee-0.6.0 → webbee-0.6.2}/src/webbee/mode_store.py +0 -0
  47. {webbee-0.6.0 → webbee-0.6.2}/src/webbee/native_files.py +0 -0
  48. {webbee-0.6.0 → webbee-0.6.2}/src/webbee/native_read.py +0 -0
  49. {webbee-0.6.0 → webbee-0.6.2}/src/webbee/newtab_mode.py +0 -0
  50. {webbee-0.6.0 → webbee-0.6.2}/src/webbee/output_pane.py +0 -0
  51. {webbee-0.6.0 → webbee-0.6.2}/src/webbee/pricing_constants.py +0 -0
  52. {webbee-0.6.0 → webbee-0.6.2}/src/webbee/queue_panel.py +0 -0
  53. {webbee-0.6.0 → webbee-0.6.2}/src/webbee/reflow.py +0 -0
  54. {webbee-0.6.0 → webbee-0.6.2}/src/webbee/remote.py +0 -0
  55. {webbee-0.6.0 → webbee-0.6.2}/src/webbee/render.py +0 -0
  56. {webbee-0.6.0 → webbee-0.6.2}/src/webbee/repl.py +0 -0
  57. {webbee-0.6.0 → webbee-0.6.2}/src/webbee/repo.py +0 -0
  58. {webbee-0.6.0 → webbee-0.6.2}/src/webbee/selection.py +0 -0
  59. {webbee-0.6.0 → webbee-0.6.2}/src/webbee/session.py +0 -0
  60. {webbee-0.6.0 → webbee-0.6.2}/src/webbee/sessions.py +0 -0
  61. {webbee-0.6.0 → webbee-0.6.2}/src/webbee/sizing.py +0 -0
  62. {webbee-0.6.0 → webbee-0.6.2}/src/webbee/slots.py +0 -0
  63. {webbee-0.6.0 → webbee-0.6.2}/src/webbee/steer.py +0 -0
  64. {webbee-0.6.0 → webbee-0.6.2}/src/webbee/stream.py +0 -0
  65. {webbee-0.6.0 → webbee-0.6.2}/src/webbee/tab_store.py +0 -0
  66. {webbee-0.6.0 → webbee-0.6.2}/src/webbee/tabs.py +0 -0
  67. {webbee-0.6.0 → webbee-0.6.2}/src/webbee/thread.py +0 -0
  68. {webbee-0.6.0 → webbee-0.6.2}/src/webbee/tier_store.py +0 -0
  69. {webbee-0.6.0 → webbee-0.6.2}/src/webbee/todo_panel.py +0 -0
  70. {webbee-0.6.0 → webbee-0.6.2}/src/webbee/tokens.py +0 -0
  71. {webbee-0.6.0 → webbee-0.6.2}/src/webbee/tools.py +0 -0
  72. {webbee-0.6.0 → webbee-0.6.2}/src/webbee/tui.py +0 -0
  73. {webbee-0.6.0 → webbee-0.6.2}/src/webbee/update.py +0 -0
  74. {webbee-0.6.0 → webbee-0.6.2}/src/webbee/urlopen.py +0 -0
  75. {webbee-0.6.0 → webbee-0.6.2}/src/webbee/wallet.py +0 -0
  76. {webbee-0.6.0 → webbee-0.6.2}/src/webbee/worktrees.py +0 -0
  77. {webbee-0.6.0 → webbee-0.6.2}/tests/__init__.py +0 -0
  78. {webbee-0.6.0 → webbee-0.6.2}/tests/conftest.py +0 -0
  79. {webbee-0.6.0 → webbee-0.6.2}/tests/test_account.py +0 -0
  80. {webbee-0.6.0 → webbee-0.6.2}/tests/test_active_sessions.py +0 -0
  81. {webbee-0.6.0 → webbee-0.6.2}/tests/test_audit_2026_07_25_findings.py +0 -0
  82. {webbee-0.6.0 → webbee-0.6.2}/tests/test_checkpoints.py +0 -0
  83. {webbee-0.6.0 → webbee-0.6.2}/tests/test_cli.py +0 -0
  84. {webbee-0.6.0 → webbee-0.6.2}/tests/test_clipboard.py +0 -0
  85. {webbee-0.6.0 → webbee-0.6.2}/tests/test_clipboard_read.py +0 -0
  86. {webbee-0.6.0 → webbee-0.6.2}/tests/test_coding_context.py +0 -0
  87. {webbee-0.6.0 → webbee-0.6.2}/tests/test_commands.py +0 -0
  88. {webbee-0.6.0 → webbee-0.6.2}/tests/test_config.py +0 -0
  89. {webbee-0.6.0 → webbee-0.6.2}/tests/test_cpc_contract_stable.py +0 -0
  90. {webbee-0.6.0 → webbee-0.6.2}/tests/test_cpc_outline.py +0 -0
  91. {webbee-0.6.0 → webbee-0.6.2}/tests/test_cross_loop_token.py +0 -0
  92. {webbee-0.6.0 → webbee-0.6.2}/tests/test_details.py +0 -0
  93. {webbee-0.6.0 → webbee-0.6.2}/tests/test_events.py +0 -0
  94. {webbee-0.6.0 → webbee-0.6.2}/tests/test_freeze_fix.py +0 -0
  95. {webbee-0.6.0 → webbee-0.6.2}/tests/test_home.py +0 -0
  96. {webbee-0.6.0 → webbee-0.6.2}/tests/test_home_view.py +0 -0
  97. {webbee-0.6.0 → webbee-0.6.2}/tests/test_home_view_mouse.py +0 -0
  98. {webbee-0.6.0 → webbee-0.6.2}/tests/test_inspect_page.py +0 -0
  99. {webbee-0.6.0 → webbee-0.6.2}/tests/test_instance_lock.py +0 -0
  100. {webbee-0.6.0 → webbee-0.6.2}/tests/test_intel_chunker.py +0 -0
  101. {webbee-0.6.0 → webbee-0.6.2}/tests/test_intel_embed.py +0 -0
  102. {webbee-0.6.0 → webbee-0.6.2}/tests/test_intel_graph.py +0 -0
  103. {webbee-0.6.0 → webbee-0.6.2}/tests/test_intel_no_numpy.py +0 -0
  104. {webbee-0.6.0 → webbee-0.6.2}/tests/test_intel_query.py +0 -0
  105. {webbee-0.6.0 → webbee-0.6.2}/tests/test_intel_service.py +0 -0
  106. {webbee-0.6.0 → webbee-0.6.2}/tests/test_intel_store.py +0 -0
  107. {webbee-0.6.0 → webbee-0.6.2}/tests/test_intel_vectors.py +0 -0
  108. {webbee-0.6.0 → webbee-0.6.2}/tests/test_intel_watch.py +0 -0
  109. {webbee-0.6.0 → webbee-0.6.2}/tests/test_launch.py +0 -0
  110. {webbee-0.6.0 → webbee-0.6.2}/tests/test_marathon.py +0 -0
  111. {webbee-0.6.0 → webbee-0.6.2}/tests/test_modals_e2e.py +0 -0
  112. {webbee-0.6.0 → webbee-0.6.2}/tests/test_mode_store.py +0 -0
  113. {webbee-0.6.0 → webbee-0.6.2}/tests/test_native_files.py +0 -0
  114. {webbee-0.6.0 → webbee-0.6.2}/tests/test_newtab_mode.py +0 -0
  115. {webbee-0.6.0 → webbee-0.6.2}/tests/test_packaging.py +0 -0
  116. {webbee-0.6.0 → webbee-0.6.2}/tests/test_probe_cost.py +0 -0
  117. {webbee-0.6.0 → webbee-0.6.2}/tests/test_queue_manage_0337.py +0 -0
  118. {webbee-0.6.0 → webbee-0.6.2}/tests/test_reflow.py +0 -0
  119. {webbee-0.6.0 → webbee-0.6.2}/tests/test_render.py +0 -0
  120. {webbee-0.6.0 → webbee-0.6.2}/tests/test_repl.py +0 -0
  121. {webbee-0.6.0 → webbee-0.6.2}/tests/test_repo.py +0 -0
  122. {webbee-0.6.0 → webbee-0.6.2}/tests/test_resilient_http.py +0 -0
  123. {webbee-0.6.0 → webbee-0.6.2}/tests/test_session.py +0 -0
  124. {webbee-0.6.0 → webbee-0.6.2}/tests/test_sessions.py +0 -0
  125. {webbee-0.6.0 → webbee-0.6.2}/tests/test_sizing.py +0 -0
  126. {webbee-0.6.0 → webbee-0.6.2}/tests/test_slots.py +0 -0
  127. {webbee-0.6.0 → webbee-0.6.2}/tests/test_steer.py +0 -0
  128. {webbee-0.6.0 → webbee-0.6.2}/tests/test_stream.py +0 -0
  129. {webbee-0.6.0 → webbee-0.6.2}/tests/test_tab_bar_stability.py +0 -0
  130. {webbee-0.6.0 → webbee-0.6.2}/tests/test_tab_restore_0337.py +0 -0
  131. {webbee-0.6.0 → webbee-0.6.2}/tests/test_tabs.py +0 -0
  132. {webbee-0.6.0 → webbee-0.6.2}/tests/test_terminal_ux_0336.py +0 -0
  133. {webbee-0.6.0 → webbee-0.6.2}/tests/test_thread.py +0 -0
  134. {webbee-0.6.0 → webbee-0.6.2}/tests/test_tier_invalidation.py +0 -0
  135. {webbee-0.6.0 → webbee-0.6.2}/tests/test_tier_store.py +0 -0
  136. {webbee-0.6.0 → webbee-0.6.2}/tests/test_todo_panel.py +0 -0
  137. {webbee-0.6.0 → webbee-0.6.2}/tests/test_tokens.py +0 -0
  138. {webbee-0.6.0 → webbee-0.6.2}/tests/test_tools.py +0 -0
  139. {webbee-0.6.0 → webbee-0.6.2}/tests/test_tools_vision_websearch.py +0 -0
  140. {webbee-0.6.0 → webbee-0.6.2}/tests/test_tui.py +0 -0
  141. {webbee-0.6.0 → webbee-0.6.2}/tests/test_tui_hardening.py +0 -0
  142. {webbee-0.6.0 → webbee-0.6.2}/tests/test_update.py +0 -0
  143. {webbee-0.6.0 → webbee-0.6.2}/tests/test_update_badge_0340.py +0 -0
  144. {webbee-0.6.0 → webbee-0.6.2}/tests/test_urlopen.py +0 -0
  145. {webbee-0.6.0 → webbee-0.6.2}/tests/test_version.py +0 -0
  146. {webbee-0.6.0 → webbee-0.6.2}/tests/test_version_badge_corner.py +0 -0
  147. {webbee-0.6.0 → webbee-0.6.2}/tests/test_wallet.py +0 -0
  148. {webbee-0.6.0 → webbee-0.6.2}/tests/test_worktrees.py +0 -0
@@ -1,5 +1,15 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.6.2
4
+
5
+ * **The upgrade at the start installs a release the moment it is out**: by its wheel's address on PyPI's file host
6
+ (the index pages trail a release by minutes), past every install cache.
7
+
8
+ ## 0.6.1
9
+
10
+ * **The upgrade at the start finds a release minutes old**: it reads past pip's / uv's index cache (before, a fresh
11
+ release stayed invisible until the cache expired).
12
+
3
13
  ## 0.6.0
4
14
 
5
15
  * **The newest release before every start** (since 0.5.14) -- now never a needless reinstall: imperal-mcp's code extra
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: webbee
3
- Version: 0.6.0
3
+ Version: 0.6.2
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.0"
3
+ version = "0.6.2"
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.2"
@@ -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,19 +98,24 @@ 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
- return [light, "-m", "pip", "install", "--upgrade", spec]
87
- tool = tool_of(prefix)
106
+ return [light, "-m", "pip", "install", "--upgrade", "--no-cache-dir", spec]
107
+ return _install(tool_of(prefix), spec, uv, pipx, executable)
108
+
109
+
110
+ def _install(tool: str, spec: str, uv, pipx, python):
111
+ """``spec`` installed by ``tool`` -- past its index cache: a release minutes old is missing from a cached index page
112
+ (live 2026-10-25: pip listed nothing newer than 0.5.13 while PyPI served 0.6.0)."""
88
113
  if tool == "uv" and uv:
89
- return [uv, "tool", "install", "--force", spec]
114
+ return [uv, "tool", "install", "--force", "--refresh", spec]
90
115
  if tool == "pipx" and pipx:
91
- return [pipx, "install", "--force", spec]
92
- if tool == "pip":
93
- return [executable, "-m", "pip", "install", "--upgrade", spec]
116
+ return [pipx, "install", "--force", "--pip-args=--no-cache-dir", spec]
117
+ if tool == "pip" and python:
118
+ return [python, "-m", "pip", "install", "--upgrade", "--no-cache-dir", spec]
94
119
  return None
95
120
 
96
121
 
@@ -118,7 +143,7 @@ def mcp_installed(which=safe_which, run=subprocess.run) -> dict:
118
143
  return out
119
144
 
120
145
 
121
- def mcp_command(inst: dict, newest, uv, pipx):
146
+ def mcp_command(inst: dict, newest, uv, pipx, wheel=None):
122
147
  """The one command that makes the shell's imperal-mcp the newest with its code extra -- by the tool that installed
123
148
  it (uv, else pipx, for a new install) -- or None when it already is, or nothing can do it."""
124
149
  if inst and inst.get("version") and inst.get("version") == newest and inst.get("code"):
@@ -127,15 +152,9 @@ def mcp_command(inst: dict, newest, uv, pipx):
127
152
  return None # it did not answer: a working install is never replaced blind
128
153
  if inst and not newest and inst.get("code"):
129
154
  return None # PyPI unread: what works stays
130
- 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)
131
156
  tool = inst.get("tool") if inst else ("uv" if uv else "pipx" if pipx else "")
132
- if tool == "uv" and uv:
133
- return [uv, "tool", "install", "--force", spec]
134
- if tool == "pipx" and pipx:
135
- return [pipx, "install", "--force", spec]
136
- if tool == "pip" and inst.get("python"):
137
- return [inst["python"], "-m", "pip", "install", "--upgrade", spec]
138
- return None
157
+ return _install(tool, spec, uv, pipx, inst.get("python") if inst else None)
139
158
 
140
159
 
141
160
  def restart_command(argv, env=os.environ, executable: str = sys.executable):
@@ -155,7 +174,7 @@ def _run(cmd, run=subprocess.run) -> bool:
155
174
 
156
175
 
157
176
  def upgrade_before_start(argv, current: str, say=print, fetch=latest, which=safe_which, run=subprocess.run,
158
- execv=os.execv, env=os.environ) -> list:
177
+ execv=os.execv, env=os.environ, wheel=wheel_url) -> list:
159
178
  """Bring webbee and the shell's imperal-mcp to their newest releases, then let the start go on; a self-upgrade
160
179
  starts webbee again on the new release (never returns then). Returns what it did, for the log. Never raises."""
161
180
  did: list = []
@@ -165,7 +184,7 @@ def upgrade_before_start(argv, current: str, say=print, fetch=latest, which=safe
165
184
  uv, pipx = find_uv(which), which("pipx")
166
185
  newest = fetch(DIST)
167
186
  if newest and _ver(newest) > _ver(current) and not env.get(DONE_ENV):
168
- cmd = self_command(newest, env=env, uv=uv, pipx=pipx)
187
+ cmd = self_command(newest, env=env, uv=uv, pipx=pipx, wheel=wheel)
169
188
  if cmd:
170
189
  say(f"🐝 updating webbee {current} -> {newest} ...")
171
190
  if _run(cmd, run):
@@ -179,7 +198,7 @@ def upgrade_before_start(argv, current: str, say=print, fetch=latest, which=safe
179
198
  say(f"🐝 webbee could not update itself -- run: {' '.join(cmd)}")
180
199
  mcp_newest = fetch(MCP)
181
200
  inst = mcp_installed(which, run)
182
- cmd = mcp_command(inst, mcp_newest, uv, pipx)
201
+ cmd = mcp_command(inst, mcp_newest, uv, pipx, wheel)
183
202
  if cmd:
184
203
  what = (f"imperal-mcp {inst.get('version') or '?'} -> {mcp_newest or 'source'}" if inst
185
204
  else f"imperal-mcp {mcp_newest or 'from source'}")
@@ -28,19 +28,19 @@ def test_the_installer_is_read_from_the_install_s_path():
28
28
 
29
29
  def test_webbee_upgrades_itself_by_its_own_tool():
30
30
  uv = au.self_command("0.6.0", prefix="/u/.local/share/uv/tools/webbee", env={}, uv="/bin/uv")
31
- assert uv == ["/bin/uv", "tool", "install", "--force", "webbee==0.6.0"]
31
+ assert uv == ["/bin/uv", "tool", "install", "--force", "--refresh", "webbee==0.6.0"]
32
32
  px = au.self_command("0.6.0", prefix="/h/.local/pipx/venvs/webbee", env={}, pipx="/bin/pipx")
33
- assert px == ["/bin/pipx", "install", "--force", "webbee==0.6.0"]
33
+ assert px == ["/bin/pipx", "install", "--force", "--pip-args=--no-cache-dir", "webbee==0.6.0"]
34
34
  pip = au.self_command("0.6.0", prefix="/venv", executable="/venv/bin/python", env={})
35
- assert pip == ["/venv/bin/python", "-m", "pip", "install", "--upgrade", "webbee==0.6.0"]
35
+ assert pip == ["/venv/bin/python", "-m", "pip", "install", "--upgrade", "--no-cache-dir", "webbee==0.6.0"]
36
36
  light = au.self_command("0.6.0", env={au.LIGHT_ENV: "/usr/bin/python3.8"}) # an old Python's light install
37
- assert light == ["/usr/bin/python3.8", "-m", "pip", "install", "--upgrade", "webbee==0.6.0"]
37
+ assert light == ["/usr/bin/python3.8", "-m", "pip", "install", "--upgrade", "--no-cache-dir", "webbee==0.6.0"]
38
38
 
39
39
 
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,10 +53,10 @@ 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
- assert ["/bin/uv", "tool", "install", "--force", "imperal-mcp[code]==0.8.2"] in run.calls
59
+ assert ["/bin/uv", "tool", "install", "--force", "--refresh", "imperal-mcp[code]==0.8.2"] in run.calls
60
60
  assert did == ["imperal-mcp 0.8.1 -> 0.8.2"]
61
61
 
62
62
 
@@ -67,13 +67,13 @@ 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
 
74
74
 
75
75
  def test_a_missing_imperal_mcp_is_installed_from_git_when_pypi_is_out_of_reach():
76
- assert au.mcp_command({}, None, "/bin/uv", None) == ["/bin/uv", "tool", "install", "--force", au.MCP_GIT_SPEC]
76
+ assert au.mcp_command({}, None, "/bin/uv", None) == ["/bin/uv", "tool", "install", "--force", "--refresh", au.MCP_GIT_SPEC]
77
77
  assert au.mcp_command({}, "0.8.2", None, "/bin/pipx")[-1] == "imperal-mcp[code]==0.8.2"
78
78
  assert au.mcp_command({}, "0.8.2", None, None) is None # no uv, no pipx: nothing to install with
79
79
 
@@ -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.0"
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