webbee 0.3.3__tar.gz → 0.3.4__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 (76) hide show
  1. {webbee-0.3.3 → webbee-0.3.4}/CHANGELOG.md +8 -0
  2. {webbee-0.3.3 → webbee-0.3.4}/PKG-INFO +1 -1
  3. {webbee-0.3.3 → webbee-0.3.4}/pyproject.toml +1 -1
  4. webbee-0.3.4/src/webbee/__init__.py +1 -0
  5. {webbee-0.3.3 → webbee-0.3.4}/src/webbee/commands.py +3 -0
  6. webbee-0.3.4/src/webbee/remote.py +41 -0
  7. {webbee-0.3.3 → webbee-0.3.4}/src/webbee/repl.py +18 -0
  8. {webbee-0.3.3 → webbee-0.3.4}/tests/test_commands.py +12 -0
  9. {webbee-0.3.3 → webbee-0.3.4}/tests/test_repl.py +33 -0
  10. webbee-0.3.3/src/webbee/__init__.py +0 -1
  11. {webbee-0.3.3 → webbee-0.3.4}/.github/workflows/publish.yml +0 -0
  12. {webbee-0.3.3 → webbee-0.3.4}/.gitignore +0 -0
  13. {webbee-0.3.3 → webbee-0.3.4}/LICENSE +0 -0
  14. {webbee-0.3.3 → webbee-0.3.4}/README.md +0 -0
  15. {webbee-0.3.3 → webbee-0.3.4}/install.sh +0 -0
  16. {webbee-0.3.3 → webbee-0.3.4}/src/webbee/account.py +0 -0
  17. {webbee-0.3.3 → webbee-0.3.4}/src/webbee/banner_art.py +0 -0
  18. {webbee-0.3.3 → webbee-0.3.4}/src/webbee/checkpoints.py +0 -0
  19. {webbee-0.3.3 → webbee-0.3.4}/src/webbee/cli.py +0 -0
  20. {webbee-0.3.3 → webbee-0.3.4}/src/webbee/clipboard.py +0 -0
  21. {webbee-0.3.3 → webbee-0.3.4}/src/webbee/config.py +0 -0
  22. {webbee-0.3.3 → webbee-0.3.4}/src/webbee/details.py +0 -0
  23. {webbee-0.3.3 → webbee-0.3.4}/src/webbee/events.py +0 -0
  24. {webbee-0.3.3 → webbee-0.3.4}/src/webbee/frames.py +0 -0
  25. {webbee-0.3.3 → webbee-0.3.4}/src/webbee/intel/__init__.py +0 -0
  26. {webbee-0.3.3 → webbee-0.3.4}/src/webbee/intel/chunker.py +0 -0
  27. {webbee-0.3.3 → webbee-0.3.4}/src/webbee/intel/embed.py +0 -0
  28. {webbee-0.3.3 → webbee-0.3.4}/src/webbee/intel/graph.py +0 -0
  29. {webbee-0.3.3 → webbee-0.3.4}/src/webbee/intel/indexer.py +0 -0
  30. {webbee-0.3.3 → webbee-0.3.4}/src/webbee/intel/models.py +0 -0
  31. {webbee-0.3.3 → webbee-0.3.4}/src/webbee/intel/query.py +0 -0
  32. {webbee-0.3.3 → webbee-0.3.4}/src/webbee/intel/service.py +0 -0
  33. {webbee-0.3.3 → webbee-0.3.4}/src/webbee/intel/store.py +0 -0
  34. {webbee-0.3.3 → webbee-0.3.4}/src/webbee/intel/vectors.py +0 -0
  35. {webbee-0.3.3 → webbee-0.3.4}/src/webbee/intel/watch.py +0 -0
  36. {webbee-0.3.3 → webbee-0.3.4}/src/webbee/output_pane.py +0 -0
  37. {webbee-0.3.3 → webbee-0.3.4}/src/webbee/render.py +0 -0
  38. {webbee-0.3.3 → webbee-0.3.4}/src/webbee/repo.py +0 -0
  39. {webbee-0.3.3 → webbee-0.3.4}/src/webbee/session.py +0 -0
  40. {webbee-0.3.3 → webbee-0.3.4}/src/webbee/sessions.py +0 -0
  41. {webbee-0.3.3 → webbee-0.3.4}/src/webbee/stream.py +0 -0
  42. {webbee-0.3.3 → webbee-0.3.4}/src/webbee/tools.py +0 -0
  43. {webbee-0.3.3 → webbee-0.3.4}/src/webbee/tui.py +0 -0
  44. {webbee-0.3.3 → webbee-0.3.4}/src/webbee/update.py +0 -0
  45. {webbee-0.3.3 → webbee-0.3.4}/tests/__init__.py +0 -0
  46. {webbee-0.3.3 → webbee-0.3.4}/tests/test_account.py +0 -0
  47. {webbee-0.3.3 → webbee-0.3.4}/tests/test_checkpoints.py +0 -0
  48. {webbee-0.3.3 → webbee-0.3.4}/tests/test_cli.py +0 -0
  49. {webbee-0.3.3 → webbee-0.3.4}/tests/test_clipboard.py +0 -0
  50. {webbee-0.3.3 → webbee-0.3.4}/tests/test_config.py +0 -0
  51. {webbee-0.3.3 → webbee-0.3.4}/tests/test_cpc_contract_stable.py +0 -0
  52. {webbee-0.3.3 → webbee-0.3.4}/tests/test_details.py +0 -0
  53. {webbee-0.3.3 → webbee-0.3.4}/tests/test_events.py +0 -0
  54. {webbee-0.3.3 → webbee-0.3.4}/tests/test_freeze_fix.py +0 -0
  55. {webbee-0.3.3 → webbee-0.3.4}/tests/test_intel_chunker.py +0 -0
  56. {webbee-0.3.3 → webbee-0.3.4}/tests/test_intel_embed.py +0 -0
  57. {webbee-0.3.3 → webbee-0.3.4}/tests/test_intel_graph.py +0 -0
  58. {webbee-0.3.3 → webbee-0.3.4}/tests/test_intel_indexer.py +0 -0
  59. {webbee-0.3.3 → webbee-0.3.4}/tests/test_intel_no_numpy.py +0 -0
  60. {webbee-0.3.3 → webbee-0.3.4}/tests/test_intel_query.py +0 -0
  61. {webbee-0.3.3 → webbee-0.3.4}/tests/test_intel_service.py +0 -0
  62. {webbee-0.3.3 → webbee-0.3.4}/tests/test_intel_store.py +0 -0
  63. {webbee-0.3.3 → webbee-0.3.4}/tests/test_intel_vectors.py +0 -0
  64. {webbee-0.3.3 → webbee-0.3.4}/tests/test_intel_watch.py +0 -0
  65. {webbee-0.3.3 → webbee-0.3.4}/tests/test_marathon.py +0 -0
  66. {webbee-0.3.3 → webbee-0.3.4}/tests/test_packaging.py +0 -0
  67. {webbee-0.3.3 → webbee-0.3.4}/tests/test_render.py +0 -0
  68. {webbee-0.3.3 → webbee-0.3.4}/tests/test_repo.py +0 -0
  69. {webbee-0.3.3 → webbee-0.3.4}/tests/test_session.py +0 -0
  70. {webbee-0.3.3 → webbee-0.3.4}/tests/test_sessions.py +0 -0
  71. {webbee-0.3.3 → webbee-0.3.4}/tests/test_stream.py +0 -0
  72. {webbee-0.3.3 → webbee-0.3.4}/tests/test_tools.py +0 -0
  73. {webbee-0.3.3 → webbee-0.3.4}/tests/test_tui.py +0 -0
  74. {webbee-0.3.3 → webbee-0.3.4}/tests/test_tui_hardening.py +0 -0
  75. {webbee-0.3.3 → webbee-0.3.4}/tests/test_update.py +0 -0
  76. {webbee-0.3.3 → webbee-0.3.4}/tests/test_version.py +0 -0
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.4
4
+
5
+ - `/notify [tg|panel|both|off]` — mirror this coding session to Telegram
6
+ and/or the panel, and let either surface steer it back (approve/deny
7
+ prompts, follow-up turns). No argument shows the current routing. Talks to
8
+ the gateway's per-session remote-control state; a network hiccup notes
9
+ cleanly instead of crashing the dock.
10
+
3
11
  ## 0.3.3
4
12
 
5
13
  - Mouse-garbage / dead-keyboard fix (Linux, occasionally macOS): the dock now
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: webbee
3
- Version: 0.3.3
3
+ Version: 0.3.4
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.3.3"
3
+ version = "0.3.4"
4
4
  description = "Webbee 🐝 — the Imperal Cloud coding agent in your terminal"
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -0,0 +1 @@
1
+ __version__ = "0.3.4"
@@ -16,6 +16,7 @@ _HELP = """Commands:
16
16
  /sessions list active sessions (this + other devices)
17
17
  /sessions revoke <#> revoke a session by its number
18
18
  /logout-others sign out every session except this one
19
+ /notify [tg|panel|both|off] mirror this session to Telegram/panel & steer it back
19
20
  /exit (=/quit) quit"""
20
21
 
21
22
 
@@ -85,6 +86,8 @@ def dispatch(line: str, ctx: CommandContext) -> SlashResult:
85
86
  return SlashResult(handled=True, action="checkpoints")
86
87
  if cmd == "/rollback":
87
88
  return SlashResult(handled=True, action="rollback", arg=" ".join(args))
89
+ if cmd == "/notify":
90
+ return SlashResult(handled=True, action="notify", arg=(args[0].lower() if args else ""))
88
91
  if cmd == "/mode":
89
92
  if not args:
90
93
  return SlashResult(handled=True, action="mode", new_mode=None,
@@ -0,0 +1,41 @@
1
+ """Remote-control client — read/set this coding session's mirror+steer routing
2
+ (Telegram / panel) via the gateway. Best-effort: network errors never raise."""
3
+ from __future__ import annotations
4
+
5
+ _ARG_TO_STATE = {
6
+ "tg": {"enabled": True, "mirror": ["telegram"], "steer": ["telegram"]},
7
+ "panel": {"enabled": True, "mirror": ["panel"], "steer": []},
8
+ "both": {"enabled": True, "mirror": ["telegram", "panel"], "steer": ["telegram"]},
9
+ "off": {"enabled": False, "mirror": [], "steer": []},
10
+ }
11
+
12
+
13
+ async def get_remote(cfg, token_provider, session_id: str) -> dict:
14
+ import httpx
15
+ token = await token_provider()
16
+ async with httpx.AsyncClient(base_url=cfg.api_url, timeout=10) as c:
17
+ r = await c.get(f"/v1/agent/sessions/{session_id}/remote",
18
+ headers={"Authorization": f"Bearer {token}"})
19
+ r.raise_for_status()
20
+ return (r.json() or {}).get("state", {})
21
+
22
+
23
+ async def set_remote(cfg, token_provider, session_id: str, arg: str) -> dict:
24
+ import httpx
25
+ body = _ARG_TO_STATE.get(arg)
26
+ if body is None:
27
+ raise ValueError(arg)
28
+ token = await token_provider()
29
+ async with httpx.AsyncClient(base_url=cfg.api_url, timeout=10) as c:
30
+ r = await c.put(f"/v1/agent/sessions/{session_id}/remote", json=body,
31
+ headers={"Authorization": f"Bearer {token}"})
32
+ r.raise_for_status()
33
+ return (r.json() or {}).get("state", {})
34
+
35
+
36
+ def describe(state: dict) -> str:
37
+ if not state.get("enabled"):
38
+ return "Remote control: OFF."
39
+ m = ", ".join(state.get("mirror", [])) or "—"
40
+ s = ", ".join(state.get("steer", [])) or "—"
41
+ return f"Remote control: ON · mirror → {m} · steer ← {s}"
@@ -238,6 +238,24 @@ async def run_repl(cfg, mode: str = "default", *, once: bool = False, sink=None,
238
238
  _r = await asyncio.to_thread(shadow.rollback, res.arg)
239
239
  _sink.note(str(_r.get("content", "")))
240
240
  return "continue"
241
+ if res.action == "notify":
242
+ from webbee import remote as _remote
243
+ sid = getattr(agent, "session_id", "")
244
+ if not sid:
245
+ _sink.note("Start a coding turn first, then /notify to route it.")
246
+ return "continue"
247
+ try:
248
+ if res.arg in ("tg", "panel", "both", "off"):
249
+ st = await _remote.set_remote(cfg, token_provider, sid, res.arg)
250
+ elif res.arg:
251
+ _sink.note("Usage: /notify [tg|panel|both|off]")
252
+ return "continue"
253
+ else:
254
+ st = await _remote.get_remote(cfg, token_provider, sid)
255
+ _sink.note(_remote.describe(st))
256
+ except Exception as e:
257
+ _sink.note(f"Remote control unavailable: {type(e).__name__}")
258
+ return "continue"
241
259
  if res.action == "clear":
242
260
  _sink.clear()
243
261
  _sink.note(res.message)
@@ -140,3 +140,15 @@ def test_help_mentions_the_time_machine():
140
140
  from webbee.commands import dispatch
141
141
  res = dispatch("/help", _anyctx())
142
142
  assert "/checkpoints" in res.message and "/rollback" in res.message
143
+
144
+
145
+ def test_notify_command_dispatches():
146
+ from webbee.commands import dispatch
147
+ assert dispatch("/notify", _anyctx()).action == "notify"
148
+ r = dispatch("/notify tg", _anyctx())
149
+ assert r.action == "notify" and r.arg == "tg"
150
+
151
+
152
+ def test_help_mentions_notify():
153
+ from webbee.commands import dispatch
154
+ assert "/notify" in dispatch("/help", _anyctx()).message
@@ -404,6 +404,39 @@ def test_boot_skips_intel_when_disabled_in_config(monkeypatch):
404
404
  assert _SpyAgent.last_intel is None
405
405
 
406
406
 
407
+ # ── /notify remote control (Task 8) ───────────────────────────────────────────
408
+
409
+ def test_notify_sets_remote_and_shows_state(monkeypatch):
410
+ agent = StepAgent()
411
+
412
+ async def fake_set(cfg, tp, sid, arg):
413
+ assert sid == "sess-1" and arg == "tg"
414
+ return {"enabled": True, "mirror": ["telegram"], "steer": ["telegram"]}
415
+ monkeypatch.setattr("webbee.remote.set_remote", fake_set)
416
+
417
+ sink, agent = _run(read_line=_lines("/notify tg", "/exit"), agent=agent)
418
+ assert any("ON" in n for n in sink.notes)
419
+
420
+
421
+ def test_notify_without_session_prompts_to_start_a_turn():
422
+ sink, agent = _run(read_line=_lines("/notify tg", "/exit"), agent=StepAgent(session_id=""))
423
+ assert any("Start a coding turn first" in n for n in sink.notes)
424
+
425
+
426
+ def test_notify_bad_arg_shows_usage():
427
+ sink, agent = _run(read_line=_lines("/notify bogus", "/exit"), agent=StepAgent())
428
+ assert any("Usage: /notify" in n for n in sink.notes)
429
+
430
+
431
+ def test_notify_network_error_notes_cleanly(monkeypatch):
432
+ async def fake_set(cfg, tp, sid, arg):
433
+ raise RuntimeError("connection refused")
434
+ monkeypatch.setattr("webbee.remote.set_remote", fake_set)
435
+
436
+ sink, agent = _run(read_line=_lines("/notify tg", "/exit"), agent=StepAgent())
437
+ assert any("Remote control unavailable" in n for n in sink.notes)
438
+
439
+
407
440
  def test_watcher_started_at_intel_root_not_workspace(monkeypatch, tmp_path):
408
441
  """F1: IntelService.root is the discovered repo root, which can differ
409
442
  from the raw cwd (e.g. the REPL launched from a subdir). The watcher must
@@ -1 +0,0 @@
1
- __version__ = "0.3.3"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes