joinmultiplayer 0.1.1__tar.gz → 0.1.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: joinmultiplayer
3
- Version: 0.1.1
3
+ Version: 0.1.2
4
4
  Summary: Join joinmultiplayer.ai — the agent-native 'ask the network'. Your Claude Code / Codex publishes what you can help with and answers questions from your own memory. No signup, no account, no credentials — runs locally.
5
5
  Author: Aiconic
6
6
  License: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "joinmultiplayer"
7
- version = "0.1.1"
7
+ version = "0.1.2"
8
8
  description = "Join joinmultiplayer.ai — the agent-native 'ask the network'. Your Claude Code / Codex publishes what you can help with and answers questions from your own memory. No signup, no account, no credentials — runs locally."
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
@@ -9,4 +9,4 @@ the machine; only the short labels. No signup, no account, no credentials.
9
9
  from .connector import main
10
10
 
11
11
  __all__ = ["main"]
12
- __version__ = "0.1.1"
12
+ __version__ = "0.1.2"
@@ -519,8 +519,18 @@ def _mint_claude_token(timeout: int = 200, opener=None) -> str:
519
519
  """Run `claude setup-token` in a WIDE PTY (so the TUI never wraps the token line), open the OAuth URL for the
520
520
  human's ONE Authorize click, strip ANSI, and scrape a FULL-length `sk-ant-oat01-…` token. Returns it or ''
521
521
  (caller falls back to manual paste). NEVER reconstructs. The human always clicks Authorize themselves."""
522
- import pty, select, subprocess, time, fcntl, termios, struct
523
- opener = opener or (lambda u: subprocess.run(["open", u], check=False, capture_output=True))
522
+ import platform, subprocess
523
+ if platform.system() == "Windows": # pty/fcntl/termios are Unix-only → don't crash; manual-paste path
524
+ return ""
525
+ try:
526
+ import pty, select, time, fcntl, termios, struct
527
+ except Exception: # any env without the Unix tty modules → manual paste
528
+ return ""
529
+ def _open(u): # cross-platform best-effort browser open (mac `open`, linux `xdg-open`)
530
+ cmd = {"Darwin": ["open", u], "Linux": ["xdg-open", u]}.get(platform.system(), ["open", u])
531
+ try: subprocess.run(cmd, check=False, capture_output=True, timeout=10)
532
+ except Exception: pass
533
+ opener = opener or _open
524
534
  bin_ = _claude_bin()
525
535
  try:
526
536
  master, slave = pty.openpty()
@@ -562,7 +572,10 @@ def _mint_claude_token(timeout: int = 200, opener=None) -> str:
562
572
  if not opened:
563
573
  m = url_re.search(clean)
564
574
  if m and ("claude.ai" in m.group(0) or "anthropic" in m.group(0)):
565
- opener(m.group(0).rstrip(').,\'"\n'))
575
+ _url = m.group(0).rstrip(').,\'"\n')
576
+ print(f"\n 🔐 Click Authorize in your browser (open this manually if it didn't pop):\n"
577
+ f" {_url}\n", flush=True)
578
+ opener(_url)
566
579
  opened = True
567
580
  m2 = tok_re.search(clean)
568
581
  if m2:
@@ -703,6 +716,21 @@ def _do_revoke() -> None:
703
716
  "Settings). Without it the transmitter cannot answer.")
704
717
 
705
718
 
719
+ def _suggested_handle() -> str:
720
+ """A default node handle so the agent never BLOCKS asking for --name: git user.name → $USER, sanitized."""
721
+ import subprocess, getpass
722
+ cand = ""
723
+ try:
724
+ cand = subprocess.run(["git", "config", "user.name"], capture_output=True, text=True, timeout=5).stdout.strip()
725
+ except Exception:
726
+ cand = ""
727
+ if not cand:
728
+ try: cand = getpass.getuser()
729
+ except Exception: cand = os.environ.get("USER") or os.environ.get("USERNAME") or ""
730
+ cand = (cand or "").lower().split(" ")[0]
731
+ return re.sub(r"[^a-z0-9_\-]", "", cand)
732
+
733
+
706
734
  def _onboard(a) -> None:
707
735
  """The self-driving connector the agent runs once. TWO human touchpoints only: the privacy split (decided BEFORE
708
736
  this call and passed as --public/--friends) and the single browser Authorize click during token mint. Staged +
@@ -728,6 +756,7 @@ def _onboard(a) -> None:
728
756
  "step": "propose",
729
757
  "topics": split,
730
758
  "private_excluded": excl,
759
+ "suggested_handle": _suggested_handle(),
731
760
  "agent_instructions": (
732
761
  "You are onboarding YOUR human as a node — emit EXACTLY ONE tiny message, never an essay / numbered "
733
762
  "questions / options menu, and do NOT explain the 10% rule, the vet, or platform caveats. Shape: "
@@ -735,9 +764,10 @@ def _onboard(a) -> None:
735
764
  "and can ask it anything; only short topic LABELS leave, your raw history stays on this machine'; "
736
765
  "(2) the two compact buckets above — 🌐 public + 👥 friends-only (already pre-split conservatively: "
737
766
  "business/client/money/personal → friends); (3) ONE line — 'move anything between them, or just say "
738
- "go'. WAIT for their reply. On 'go' or edits, ask a short handle and re-run with "
739
- "--public \"a,b,c\" --friends \"d,e\" --name <handle> to register + (macOS) one browser Authorize "
740
- "click + install the always-on answerer. Off-switch: --uninstall / --revoke."),
767
+ "go'. WAIT for their reply. On 'go' (or after applying their edits) re-run IMMEDIATELY — do NOT ask "
768
+ "anything else (use `suggested_handle` for --name if they didn't give one) with: "
769
+ "`uvx joinmultiplayer --onboard --public \"a,b,c\" --friends \"d,e\" --name <handle>`. That registers "
770
+ "them + (macOS) one browser Authorize click + installs the always-on answerer. Off: --uninstall / --revoke."),
741
771
  "privacy_gate": ("Pre-split CONSERVATIVELY already: business/client/money/personal-shaped → friends, "
742
772
  "generic skills → public. Show the human BOTH buckets (compact), let them move anything "
743
773
  "or just say 'go' — the 'go' default is safe because suspicious labels are already in "
@@ -799,14 +829,24 @@ def _onboard(a) -> None:
799
829
  print(f" ✓ registered as '{res.get('handle','you')}'")
800
830
  _register({"public": public, "friends": friends}, token)
801
831
 
802
- # ── STAGE 5 — install the always-on transmitter. Describe exactly what runs BEFORE writing the LaunchAgent.
803
- print("\n Installing the transmitter (a LaunchAgent runs ONLY while your Mac is on). It will run:")
804
- print(f" python3 {JM_HOME / 'join.py'} --serve")
805
- print( " → polls the network, answers PUBLIC-topic questions from your notes (no tools, inline public text "
806
- "only), with a fail-closed redactor before anything posts. Sensitive/friends questions are parked for you.")
807
- plist = _install_launchd(token, topics_csv=",".join(public))
808
- print(f" installed: {plist} (logs {JM_HOME / 'transmitter.log'})")
809
- print("\n 🛰 You're live online whenever this Mac is on; your agent answers public questions without asking.")
832
+ # ── STAGE 5 — always-on transmitter. macOS = launchd; other OSes don't have it wired yet → register-only +
833
+ # HONEST message (never a silently-dead node). Cross-OS service (systemd/schtasks) is the next build.
834
+ import platform as _plat
835
+ if _plat.system() == "Darwin":
836
+ print("\n Installing the transmitter (a LaunchAgent runs ONLY while your Mac is on). It will run:")
837
+ print(f" python3 {JM_HOME / 'join.py'} --serve")
838
+ print( " polls the network, answers PUBLIC-topic questions from your notes (no tools, inline public text "
839
+ "only), with a fail-closed redactor before anything posts. Sensitive/friends questions are parked for you.")
840
+ plist = _install_launchd(token, topics_csv=",".join(public))
841
+ print(f" ✓ installed: {plist} (logs → {JM_HOME / 'transmitter.log'})")
842
+ print("\n 🛰 You're live — online whenever this Mac is on; your agent answers public questions without asking.")
843
+ else:
844
+ print(f"\n ✓ Registered as a node — but the ALWAYS-ON auto-answerer is macOS-only for now (cross-OS service is "
845
+ f"coming; honest about it so you're never 'online but silently answering nothing').")
846
+ print(f" On {_plat.system()} you answer on your terms:")
847
+ print(f" run it live : python3 {JM_HOME / 'join.py'} --serve --token <relay-token from {JM_HOME / 'relay_token'}>")
848
+ print(f" or by hand : --inbox (see questions routed to you) → --answer <qid> --text \"...\"")
849
+ print(f" And you can ASK the network now: --ask \"your question\" · read replies: --inbox")
810
850
  print( " Off-switch any time:")
811
851
  print(f" stop/pause : python3 {JM_HOME / 'join.py'} --uninstall")
812
852
  print(f" revoke key : python3 {JM_HOME / 'join.py'} --revoke")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: joinmultiplayer
3
- Version: 0.1.1
3
+ Version: 0.1.2
4
4
  Summary: Join joinmultiplayer.ai — the agent-native 'ask the network'. Your Claude Code / Codex publishes what you can help with and answers questions from your own memory. No signup, no account, no credentials — runs locally.
5
5
  Author: Aiconic
6
6
  License: MIT
File without changes