elevate-adapter: direct CreateSession for token refresh
Replace subprocess-based token refresh (superpowers run-session --child-session) with direct SEQPACKET CreateSession RPC to superpowerd. This fixes: 1. Session PID mismatch: old approach registered session to short-lived sh subprocess PID, causing immediate pidAlive() failure on next use 2. Child session failure: --child-session fails when superpowerd restarts (parent session gone) 3. Gentoo python-exec2c exec chain: replace os.execvp startup with direct CreateSession call The adapter now owns its session directly (peerPID = adapter PID), so the session stays valid for the adapters entire lifetime.
This commit is contained in:
parent
a65178f936
commit
058d7aed0d
|
|
@ -29,7 +29,6 @@ import os
|
|||
import signal
|
||||
import socket
|
||||
import struct
|
||||
import subprocess
|
||||
import threading
|
||||
|
||||
_token_lock = threading.Lock()
|
||||
|
|
@ -40,27 +39,74 @@ def _log(msg):
|
|||
print(f"{_LOG_PREFIX}: {msg}", file=sys.stderr, flush=True)
|
||||
|
||||
|
||||
def _read_master_token():
|
||||
"""Read the superpowerd master token from the standard location."""
|
||||
path = os.environ.get("SUPERPOWERD_MASTER_TOKEN_FILE") or \
|
||||
os.path.join(os.path.expanduser("~"), "Private", "superpowerd_master.token")
|
||||
try:
|
||||
return open(path).read().strip()
|
||||
except OSError as e:
|
||||
_log(f"cannot read master token at {path}: {e}")
|
||||
return None
|
||||
|
||||
|
||||
def refresh_token():
|
||||
"""Obtain a fresh session token from superpowerd via run-session.
|
||||
"""Create a fresh session directly on superpowerd via SEQPACKET.
|
||||
|
||||
Connects to superpowerd and calls CreateSession with the master token.
|
||||
Because WE are the connecting process, the session PID = our PID,
|
||||
so pidAlive() stays true for our entire lifetime.
|
||||
|
||||
Returns the new token string, or None on failure.
|
||||
Updates os.environ so subsequent calls use the fresh token.
|
||||
"""
|
||||
_log("refreshing session token")
|
||||
_log("refreshing session token via direct CreateSession")
|
||||
master_token = _read_master_token()
|
||||
if not master_token:
|
||||
_log("refresh failed: cannot read master token")
|
||||
return None
|
||||
|
||||
sp_path = sp_socket_path()
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["superpowers", "run-session", "--child-session", "--",
|
||||
"sh", "-c", "echo $SUPERPOWERD_SESSION_TOKEN"],
|
||||
capture_output=True, text=True, timeout=10,
|
||||
)
|
||||
token = result.stdout.strip()
|
||||
if token and result.returncode == 0:
|
||||
sp = socket.socket(socket.AF_UNIX, socket.SOCK_SEQPACKET)
|
||||
sp.connect(sp_path)
|
||||
except OSError as e:
|
||||
_log(f"refresh failed: connect {sp_path}: {e}")
|
||||
return None
|
||||
|
||||
try:
|
||||
req = json.dumps({
|
||||
"jsonrpc": "2.0",
|
||||
"method": "CreateSession",
|
||||
"id": 1,
|
||||
"params": {
|
||||
"auth": {"master_token": master_token},
|
||||
"env": dict(os.environ),
|
||||
"working_dir": os.getcwd(),
|
||||
},
|
||||
}).encode()
|
||||
sp.send(req)
|
||||
resp_data = sp.recv(1024 * 1024)
|
||||
sp.close()
|
||||
|
||||
resp = json.loads(resp_data)
|
||||
if "error" in resp and resp["error"]:
|
||||
msg = resp["error"].get("message", str(resp["error"]))
|
||||
_log(f"refresh failed: superpowerd error: {msg}")
|
||||
return None
|
||||
|
||||
token = resp.get("result", {}).get("token", "")
|
||||
if token:
|
||||
os.environ["SUPERPOWERD_SESSION_TOKEN"] = token
|
||||
_log("session token refreshed successfully")
|
||||
return token
|
||||
_log(f"token refresh returned exit={result.returncode} stdout={result.stdout!r:.80}")
|
||||
except (subprocess.TimeoutExpired, OSError) as e:
|
||||
_log(f"token refresh failed: {e}")
|
||||
_log(f"refresh failed: no token in response: {resp_data!r:.200}")
|
||||
except (OSError, json.JSONDecodeError, ValueError) as e:
|
||||
_log(f"refresh failed: {e}")
|
||||
try:
|
||||
sp.close()
|
||||
except OSError:
|
||||
pass
|
||||
return None
|
||||
|
||||
|
||||
|
|
@ -101,7 +147,7 @@ def _is_auth_error(sp_data):
|
|||
err = resp.get("error")
|
||||
if isinstance(err, dict):
|
||||
msg = err.get("message", "").lower()
|
||||
return "invalid session token" in msg or "session not found" in msg
|
||||
return "invalid session token" in msg or "session not found" in msg or "session expired" in msg or "parent session expired" in msg
|
||||
except (json.JSONDecodeError, ValueError):
|
||||
pass
|
||||
return False
|
||||
|
|
@ -302,8 +348,12 @@ def _finish_response(conn, sp_data):
|
|||
|
||||
def main():
|
||||
if "SUPERPOWERD_SESSION_TOKEN" not in os.environ:
|
||||
os.execvp("superpowers", ["superpowers", "run-session", "--",
|
||||
sys.executable, __file__] + sys.argv[1:])
|
||||
# Get a token directly from superpowerd instead of os.execvp through
|
||||
# superpowers run-session (which fails on Gentoo's python-exec2c).
|
||||
token = refresh_token()
|
||||
if not token:
|
||||
_log("fatal: cannot obtain session token")
|
||||
sys.exit(1)
|
||||
|
||||
sock_path = os.environ.get("OLLIE_ELEVATE_SOCKET") or default_socket_path()
|
||||
sock_path = os.path.expanduser(sock_path)
|
||||
|
|
|
|||
Loading…
Reference in New Issue