chartremotely 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 (66) hide show
  1. {chartremotely-0.3.3 → chartremotely-0.3.4}/PKG-INFO +2 -2
  2. {chartremotely-0.3.3 → chartremotely-0.3.4}/chartremotely/config.py +1 -1
  3. {chartremotely-0.3.3 → chartremotely-0.3.4}/chartremotely/keystore.py +2 -2
  4. {chartremotely-0.3.3 → chartremotely-0.3.4}/chartremotely/patron.py +2 -2
  5. {chartremotely-0.3.3 → chartremotely-0.3.4}/chartremotely/setup.py +6 -5
  6. {chartremotely-0.3.3 → chartremotely-0.3.4}/chartremotely.egg-info/PKG-INFO +2 -2
  7. {chartremotely-0.3.3 → chartremotely-0.3.4}/chartremotely.egg-info/requires.txt +1 -1
  8. {chartremotely-0.3.3 → chartremotely-0.3.4}/pyproject.toml +2 -2
  9. {chartremotely-0.3.3 → chartremotely-0.3.4}/tests/test_patron.py +3 -3
  10. {chartremotely-0.3.3 → chartremotely-0.3.4}/tests/test_setup.py +9 -9
  11. {chartremotely-0.3.3 → chartremotely-0.3.4}/LICENSE +0 -0
  12. {chartremotely-0.3.3 → chartremotely-0.3.4}/README.md +0 -0
  13. {chartremotely-0.3.3 → chartremotely-0.3.4}/chartremotely/__init__.py +0 -0
  14. {chartremotely-0.3.3 → chartremotely-0.3.4}/chartremotely/answer.py +0 -0
  15. {chartremotely-0.3.3 → chartremotely-0.3.4}/chartremotely/assets/shortcut.plist +0 -0
  16. {chartremotely-0.3.3 → chartremotely-0.3.4}/chartremotely/ax.py +0 -0
  17. {chartremotely-0.3.3 → chartremotely-0.3.4}/chartremotely/cli.py +0 -0
  18. {chartremotely-0.3.3 → chartremotely-0.3.4}/chartremotely/displays.py +0 -0
  19. {chartremotely-0.3.3 → chartremotely-0.3.4}/chartremotely/doctor.py +0 -0
  20. {chartremotely-0.3.3 → chartremotely-0.3.4}/chartremotely/guilock.py +0 -0
  21. {chartremotely-0.3.3 → chartremotely-0.3.4}/chartremotely/hearing.py +0 -0
  22. {chartremotely-0.3.3 → chartremotely-0.3.4}/chartremotely/holder.py +0 -0
  23. {chartremotely-0.3.3 → chartremotely-0.3.4}/chartremotely/layout.py +0 -0
  24. {chartremotely-0.3.3 → chartremotely-0.3.4}/chartremotely/mcpclient.py +0 -0
  25. {chartremotely-0.3.3 → chartremotely-0.3.4}/chartremotely/popups.py +0 -0
  26. {chartremotely-0.3.3 → chartremotely-0.3.4}/chartremotely/push.py +0 -0
  27. {chartremotely-0.3.3 → chartremotely-0.3.4}/chartremotely/recent.py +0 -0
  28. {chartremotely-0.3.3 → chartremotely-0.3.4}/chartremotely/registry.py +0 -0
  29. {chartremotely-0.3.3 → chartremotely-0.3.4}/chartremotely/relay.py +0 -0
  30. {chartremotely-0.3.3 → chartremotely-0.3.4}/chartremotely/requestlog.py +0 -0
  31. {chartremotely-0.3.3 → chartremotely-0.3.4}/chartremotely/resolve.py +0 -0
  32. {chartremotely-0.3.3 → chartremotely-0.3.4}/chartremotely/scales.py +0 -0
  33. {chartremotely-0.3.3 → chartremotely-0.3.4}/chartremotely/screen.py +0 -0
  34. {chartremotely-0.3.3 → chartremotely-0.3.4}/chartremotely/server.py +0 -0
  35. {chartremotely-0.3.3 → chartremotely-0.3.4}/chartremotely/services.py +0 -0
  36. {chartremotely-0.3.3 → chartremotely-0.3.4}/chartremotely/shortcut.py +0 -0
  37. {chartremotely-0.3.3 → chartremotely-0.3.4}/chartremotely/snapshot.py +0 -0
  38. {chartremotely-0.3.3 → chartremotely-0.3.4}/chartremotely/studies.py +0 -0
  39. {chartremotely-0.3.3 → chartremotely-0.3.4}/chartremotely/symbol.py +0 -0
  40. {chartremotely-0.3.3 → chartremotely-0.3.4}/chartremotely/tailnet.py +0 -0
  41. {chartremotely-0.3.3 → chartremotely-0.3.4}/chartremotely/terminal.py +0 -0
  42. {chartremotely-0.3.3 → chartremotely-0.3.4}/chartremotely/timeframe.py +0 -0
  43. {chartremotely-0.3.3 → chartremotely-0.3.4}/chartremotely/understand.py +0 -0
  44. {chartremotely-0.3.3 → chartremotely-0.3.4}/chartremotely/vocab.py +0 -0
  45. {chartremotely-0.3.3 → chartremotely-0.3.4}/chartremotely/voice.py +0 -0
  46. {chartremotely-0.3.3 → chartremotely-0.3.4}/chartremotely/window.py +0 -0
  47. {chartremotely-0.3.3 → chartremotely-0.3.4}/chartremotely.egg-info/SOURCES.txt +0 -0
  48. {chartremotely-0.3.3 → chartremotely-0.3.4}/chartremotely.egg-info/dependency_links.txt +0 -0
  49. {chartremotely-0.3.3 → chartremotely-0.3.4}/chartremotely.egg-info/entry_points.txt +0 -0
  50. {chartremotely-0.3.3 → chartremotely-0.3.4}/chartremotely.egg-info/top_level.txt +0 -0
  51. {chartremotely-0.3.3 → chartremotely-0.3.4}/setup.cfg +0 -0
  52. {chartremotely-0.3.3 → chartremotely-0.3.4}/tests/test_changelog.py +0 -0
  53. {chartremotely-0.3.3 → chartremotely-0.3.4}/tests/test_hearing.py +0 -0
  54. {chartremotely-0.3.3 → chartremotely-0.3.4}/tests/test_popups.py +0 -0
  55. {chartremotely-0.3.3 → chartremotely-0.3.4}/tests/test_push.py +0 -0
  56. {chartremotely-0.3.3 → chartremotely-0.3.4}/tests/test_recent.py +0 -0
  57. {chartremotely-0.3.3 → chartremotely-0.3.4}/tests/test_relay.py +0 -0
  58. {chartremotely-0.3.3 → chartremotely-0.3.4}/tests/test_requestlog.py +0 -0
  59. {chartremotely-0.3.3 → chartremotely-0.3.4}/tests/test_resolve.py +0 -0
  60. {chartremotely-0.3.3 → chartremotely-0.3.4}/tests/test_scales.py +0 -0
  61. {chartremotely-0.3.3 → chartremotely-0.3.4}/tests/test_screen.py +0 -0
  62. {chartremotely-0.3.3 → chartremotely-0.3.4}/tests/test_snapshot.py +0 -0
  63. {chartremotely-0.3.3 → chartremotely-0.3.4}/tests/test_stamp_changelog.py +0 -0
  64. {chartremotely-0.3.3 → chartremotely-0.3.4}/tests/test_timeframe.py +0 -0
  65. {chartremotely-0.3.3 → chartremotely-0.3.4}/tests/test_understand.py +0 -0
  66. {chartremotely-0.3.3 → chartremotely-0.3.4}/tests/test_voice.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: chartremotely
3
- Version: 0.3.3
3
+ Version: 0.3.4
4
4
  Summary: Remote control of desktop charting applications on macOS
5
5
  Author: Lonnie VanZandt
6
6
  License:
@@ -219,7 +219,7 @@ Requires-Dist: pyobjc-framework-Quartz>=10; extra == "macos"
219
219
  Requires-Dist: pyobjc-framework-Security>=10; extra == "macos"
220
220
  Requires-Dist: mlx-whisper>=0.4.3; (sys_platform == "darwin" and platform_machine == "arm64") and extra == "macos"
221
221
  Provides-Extra: setup
222
- Requires-Dist: tollbooth-dpyc[nostr]==0.102.1; extra == "setup"
222
+ Requires-Dist: tollbooth-dpyc[nostr]==0.103.0; extra == "setup"
223
223
  Provides-Extra: dev
224
224
  Requires-Dist: pytest>=8; extra == "dev"
225
225
  Requires-Dist: ruff>=0.6; extra == "dev"
@@ -35,7 +35,7 @@ DEFAULTS: dict = {
35
35
  "agent_id": None,
36
36
  "agent_secret": None,
37
37
  # Whose display this is: the npub voice commands are billed to. Its
38
- # sign-in (the dpop_token) lives in the login Keychain, never here.
38
+ # sign-in (this Mac's connection tokens) lives in the login Keychain, never here.
39
39
  "owner_npub": None,
40
40
  "token": None,
41
41
  "port": 8899,
@@ -1,8 +1,8 @@
1
1
  """Keep a patron's secrets in their login Keychain, never in this agent's files.
2
2
 
3
3
  Two kinds, never mixed: the Nostr key ``setup`` may make for the human, and
4
- the voice sign-in - the ``dpop_token`` the operator returns once the owner
5
- has proven their npub by DM - which the listener passes on each priced call.
4
+ the voice sign-in - this Mac's OAuth tokens on the operator, whose connection
5
+ holds the owner's npub once they have proven it by DM.
6
6
 
7
7
  A key made by ``setup`` belongs to the human. It goes into their login
8
8
  Keychain as an internet password for the ChartRemotely site, and it is
@@ -172,13 +172,13 @@ def renew(base: str, npub: str, sleep: Callable[[float], None] = time.sleep) ->
172
172
  def _renew(base: str, npub: str, sleep: Callable[[float], None]) -> None:
173
173
  try:
174
174
  sent = _call(base, npub, "chart_request_npub_proof", {"patron_npub": npub, "reason": REASON})
175
- phrase = sent.get("dpop_token")
175
+ phrase = sent.get("nonce")
176
176
  if not phrase:
177
177
  requestlog.outcome("serve", "renew", "", refusal(sent) or "no proof request was sent")
178
178
  return
179
179
  for delay in RENEW_DELAYS:
180
180
  sleep(delay)
181
- got = _call(base, npub, "chart_receive_npub_proof", {"patron_npub": npub, "dpop_token": phrase})
181
+ got = _call(base, npub, "chart_receive_npub_proof", {"patron_npub": npub, "nonce": phrase})
182
182
  if got.get("success") is not False and not got.get("error"):
183
183
  # Proven into this Mac's connection; nothing to keep here.
184
184
  requestlog.outcome("serve", "renew", "", None)
@@ -10,9 +10,10 @@ what is already done, so running setup again is always safe:
10
10
  A made key is kept for the human - their Keychain, then Safari's saved
11
11
  passwords - and used once, in memory, to sign the pairing. It is never
12
12
  written to this agent's config. Then **pairing**, without a code to copy: setup adopts this machine's code itself.
13
- and **voice sign-in**: the ``dpop_token`` from the owner's DM proof, kept in
14
- the login Keychain (their npub goes in the config), so the listener can
15
- call the operator's priced tools as that patron.
13
+ and **voice sign-in**: this Mac's connection to the operator, into which the
14
+ owner's DM proof puts their npub; its OAuth tokens are kept in the login
15
+ Keychain (their npub goes in the config), so the listener can call the
16
+ operator's priced tools as that patron.
16
17
  2. **Tailscale**: the Mac's tailnet address, with ``/chart`` forwarded to the agent.
17
18
  3. **Services**: the listener and the relay, under launchd.
18
19
  4. **Permissions**: Accessibility and Screen Recording, asked for by the
@@ -61,7 +62,7 @@ def prove_by_dm(client: mcpclient.Client, npub: str, ask: Ask, say: Say) -> None
61
62
  sent = client.call("chart_request_npub_proof", {
62
63
  "patron_npub": npub, "verify_at": SITE,
63
64
  "reason": "You asked to pair a Mac with ChartRemotely."})
64
- phrase = sent.get("dpop_token")
65
+ phrase = sent.get("nonce")
65
66
  if not phrase:
66
67
  raise SetupError(sent.get("error") or "the operator did not send a proof request")
67
68
  say(f"A Nostr DM is on its way to {npub[:12]}…")
@@ -70,7 +71,7 @@ def prove_by_dm(client: mcpclient.Client, npub: str, ask: Ask, say: Say) -> None
70
71
  say("may set cache_duration (30 days, or unlimited) so voice keeps working that")
71
72
  say("long; left alone, the sign-in lasts 2 hours.")
72
73
  ask("Press Return once you have replied… ")
73
- got = client.call("chart_receive_npub_proof", {"patron_npub": npub, "dpop_token": phrase})
74
+ got = client.call("chart_receive_npub_proof", {"patron_npub": npub, "nonce": phrase})
74
75
  if got.get("error"):
75
76
  raise SetupError(got["error"])
76
77
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: chartremotely
3
- Version: 0.3.3
3
+ Version: 0.3.4
4
4
  Summary: Remote control of desktop charting applications on macOS
5
5
  Author: Lonnie VanZandt
6
6
  License:
@@ -219,7 +219,7 @@ Requires-Dist: pyobjc-framework-Quartz>=10; extra == "macos"
219
219
  Requires-Dist: pyobjc-framework-Security>=10; extra == "macos"
220
220
  Requires-Dist: mlx-whisper>=0.4.3; (sys_platform == "darwin" and platform_machine == "arm64") and extra == "macos"
221
221
  Provides-Extra: setup
222
- Requires-Dist: tollbooth-dpyc[nostr]==0.102.1; extra == "setup"
222
+ Requires-Dist: tollbooth-dpyc[nostr]==0.103.0; extra == "setup"
223
223
  Provides-Extra: dev
224
224
  Requires-Dist: pytest>=8; extra == "dev"
225
225
  Requires-Dist: ruff>=0.6; extra == "dev"
@@ -13,4 +13,4 @@ pyobjc-framework-Security>=10
13
13
  mlx-whisper>=0.4.3
14
14
 
15
15
  [setup]
16
- tollbooth-dpyc[nostr]==0.102.1
16
+ tollbooth-dpyc[nostr]==0.103.0
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "chartremotely"
3
- version = "0.3.3"
3
+ version = "0.3.4"
4
4
  description = "Remote control of desktop charting applications on macOS"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"
@@ -25,7 +25,7 @@ macos = [
25
25
  ]
26
26
  # `chartremotely setup` makes and proves Nostr identities. The key and proof
27
27
  # code is the tollbooth-dpyc SDK's own, never rewritten here.
28
- setup = ["tollbooth-dpyc[nostr]==0.102.1"]
28
+ setup = ["tollbooth-dpyc[nostr]==0.103.0"]
29
29
  dev = ["pytest>=8", "ruff>=0.6"]
30
30
 
31
31
  [project.urls]
@@ -28,8 +28,8 @@ class FakeOperator:
28
28
  self.calls = []
29
29
  self.answers = {"chart_show_chart": {"ok": True, "display": "Desk", "result": "Showing PLTR"},
30
30
  "chart_read_chart": {"ok": True, "display": "Desk", "result": "PLTR at daily"},
31
- "chart_request_npub_proof": {"success": True, "dpop_token": "fresh-code"},
32
- "chart_receive_npub_proof": [{"success": True, "dpop_token": "fresh-code"}]}
31
+ "chart_request_npub_proof": {"success": True, "nonce": "fresh-code"},
32
+ "chart_receive_npub_proof": [{"success": True, "nonce": "fresh-code"}]}
33
33
  self.gate = threading.Event()
34
34
  self.gate.set()
35
35
  self.lock = threading.Lock()
@@ -168,7 +168,7 @@ def test_an_expired_sign_in_is_spoken_and_renewed_once(operator, monkeypatch):
168
168
  operator.answers["chart_receive_npub_proof"] = [
169
169
  {"success": False, "error_code": "courier_not_found", "error": "No reply found"},
170
170
  {"success": False, "error_code": "courier_not_found", "error": "No reply found"},
171
- {"success": True, "dpop_token": "fresh-code"}]
171
+ {"success": True, "nonce": "fresh-code"}]
172
172
  assert patron.send("set PLTR | half", "") == patron.EXPIRED
173
173
  assert patron.send("set PLTR | half", "") == patron.EXPIRED
174
174
  _wait_for_renewal()
@@ -199,8 +199,8 @@ class StubClient:
199
199
 
200
200
 
201
201
  def test_an_existing_npub_is_proven_by_dm_and_no_key_is_asked_for():
202
- client = StubClient({"chart_request_npub_proof": {"dpop_token": "seven-amber-door"},
203
- "chart_receive_npub_proof": {"success": True, "dpop_token": "seven-amber-door"}})
202
+ client = StubClient({"chart_request_npub_proof": {"nonce": "seven-amber-door"},
203
+ "chart_receive_npub_proof": {"success": True, "nonce": "seven-amber-door"}})
204
204
  asked, said = [], []
205
205
  # Proven into the connection `client` carries: nothing comes back to keep.
206
206
  assert setup.prove_by_dm(client, NPUB, lambda q: asked.append(q) or "", said.append) is None
@@ -211,7 +211,7 @@ def test_an_existing_npub_is_proven_by_dm_and_no_key_is_asked_for():
211
211
 
212
212
 
213
213
  def test_a_dm_that_was_not_answered_stops_setup():
214
- client = StubClient({"chart_request_npub_proof": {"dpop_token": "x"},
214
+ client = StubClient({"chart_request_npub_proof": {"nonce": "x"},
215
215
  "chart_receive_npub_proof": {"error": "no reply yet"}})
216
216
  with pytest.raises(setup.SetupError, match="no reply yet"):
217
217
  setup.prove_by_dm(client, NPUB, lambda q: "", lambda s: None)
@@ -316,8 +316,8 @@ def _stand_in_the_mac(monkeypatch, tmp_path, answers):
316
316
 
317
317
  def test_a_dm_proven_owner_keeps_the_sign_in_in_the_keychain_not_the_config(monkeypatch, tmp_path):
318
318
  vault = _stand_in_the_mac(monkeypatch, tmp_path, {
319
- "chart_request_npub_proof": {"dpop_token": "seven-amber-door"},
320
- "chart_receive_npub_proof": {"success": True, "dpop_token": "seven-amber-door"},
319
+ "chart_request_npub_proof": {"nonce": "seven-amber-door"},
320
+ "chart_receive_npub_proof": {"success": True, "nonce": "seven-amber-door"},
321
321
  "chart_pair_agent": {"ok": True, "display": "Desk", "agent_id": "a1"}})
322
322
  answers = iter(["", "1", NPUB, "", "Desk"])
323
323
  said = []
@@ -330,8 +330,8 @@ def test_a_dm_proven_owner_keeps_the_sign_in_in_the_keychain_not_the_config(monk
330
330
 
331
331
  def test_an_already_paired_mac_is_signed_in_for_voice_by_dm(monkeypatch, tmp_path):
332
332
  vault = _stand_in_the_mac(monkeypatch, tmp_path, {
333
- "chart_request_npub_proof": {"dpop_token": "seven-amber-door"},
334
- "chart_receive_npub_proof": {"success": True, "dpop_token": "seven-amber-door"}})
333
+ "chart_request_npub_proof": {"nonce": "seven-amber-door"},
334
+ "chart_receive_npub_proof": {"success": True, "nonce": "seven-amber-door"}})
335
335
  config.update(operator_url="https://op.test", agent_id="a1", agent_secret="agent-secret")
336
336
  answers = iter(["", NPUB, "y", ""])
337
337
  assert setup.run(ask=lambda q: next(answers), say=lambda s: None) == 0
@@ -341,8 +341,8 @@ def test_an_already_paired_mac_is_signed_in_for_voice_by_dm(monkeypatch, tmp_pat
341
341
 
342
342
  def test_setup_removes_a_retired_ask_shortcut_and_names_any_it_could_not(monkeypatch, tmp_path):
343
343
  _stand_in_the_mac(monkeypatch, tmp_path, {
344
- "chart_request_npub_proof": {"dpop_token": "seven-amber-door"},
345
- "chart_receive_npub_proof": {"success": True, "dpop_token": "seven-amber-door"}})
344
+ "chart_request_npub_proof": {"nonce": "seven-amber-door"},
345
+ "chart_receive_npub_proof": {"success": True, "nonce": "seven-amber-door"}})
346
346
  config.update(operator_url="https://op.test", agent_id="a1", agent_secret="agent-secret")
347
347
  monkeypatch.setattr(shortcut, "remove_retired", lambda: (["ChartRemotely Ask"], ["ChartRemotely Ask 2"]))
348
348
  answers = iter(["", NPUB, "y", ""])
File without changes
File without changes
File without changes