multi-account selection: --account-name hint, exit 3 NEEDS_HUMAN
onboard-driver.py accepts --account-name and forwards to muse-signin.py. muse-signin.py detects Meta multi-account selection after OTP submit: with a hint it clicks the matching display-name button (never the +1 collapsed entry); without a hint (or no match) it exits 3 so the queue marks the job needs_human instead of stalling.
This commit is contained in:
@@ -58,6 +58,9 @@ def main():
|
|||||||
help='node name: CDP port comes from the NODES.md registry')
|
help='node name: CDP port comes from the NODES.md registry')
|
||||||
p.add_argument('--cdp-port', default=None,
|
p.add_argument('--cdp-port', default=None,
|
||||||
help='explicit CDP port (overrides --node lookup)')
|
help='explicit CDP port (overrides --node lookup)')
|
||||||
|
p.add_argument('--account-name', default=None,
|
||||||
|
help='display-name hint for Meta multi-account selection '
|
||||||
|
'(never the "+1" entry)')
|
||||||
args = p.parse_args()
|
args = p.parse_args()
|
||||||
|
|
||||||
if args.cdp_port:
|
if args.cdp_port:
|
||||||
@@ -155,6 +158,41 @@ def main():
|
|||||||
print(f"SUCCESS: Logged in as {args.email} (title: {title})")
|
print(f"SUCCESS: Logged in as {args.email} (title: {title})")
|
||||||
ws.close()
|
ws.close()
|
||||||
return 0
|
return 0
|
||||||
|
# Multi-account selection: Meta shows one button per matching
|
||||||
|
# account plus a "+1" collapsed entry. The "+1" is NOT a real
|
||||||
|
# account — click the button whose text contains the hinted
|
||||||
|
# display name. Without a hint (or no match), a human must pick.
|
||||||
|
if "Your email matches multiple accounts" in ev(
|
||||||
|
ws, "document.body.innerText.slice(0,2000)"):
|
||||||
|
if not args.account_name:
|
||||||
|
print("NEEDS_HUMAN: multiple accounts match and no "
|
||||||
|
"--account-name hint was given", file=sys.stderr)
|
||||||
|
ws.close()
|
||||||
|
sys.exit(3)
|
||||||
|
picked = ev(ws, """(async()=>{
|
||||||
|
const want=%s.toLowerCase();
|
||||||
|
const btns=[...document.querySelectorAll('button')];
|
||||||
|
const t=btns.find(b=>b.innerText.toLowerCase().includes(want)
|
||||||
|
&& !b.innerText.includes('+1'));
|
||||||
|
if(t){t.click();return true;} return false;
|
||||||
|
})()""" % json.dumps(args.account_name), True)
|
||||||
|
if not picked:
|
||||||
|
print(f"NEEDS_HUMAN: no account button matched "
|
||||||
|
f"'{args.account_name}'", file=sys.stderr)
|
||||||
|
ws.close()
|
||||||
|
sys.exit(3)
|
||||||
|
print(f"Selected account '{args.account_name}', waiting...")
|
||||||
|
time.sleep(5)
|
||||||
|
body = ev(ws, "document.body.innerText.slice(0,200)")
|
||||||
|
if "Connected" in body or "Chats" in body:
|
||||||
|
print(f"SUCCESS: Logged in as {args.email} "
|
||||||
|
f"(account: {args.account_name})")
|
||||||
|
ws.close()
|
||||||
|
return 0
|
||||||
|
print("WARNING: account selection did not land in chat",
|
||||||
|
file=sys.stderr)
|
||||||
|
ws.close()
|
||||||
|
sys.exit(1)
|
||||||
else:
|
else:
|
||||||
print(f"WARNING: Login may have failed. Title: {title}", file=sys.stderr)
|
print(f"WARNING: Login may have failed. Title: {title}", file=sys.stderr)
|
||||||
print(f"Body: {body[:100]}", file=sys.stderr)
|
print(f"Body: {body[:100]}", file=sys.stderr)
|
||||||
|
|||||||
@@ -8,7 +8,8 @@ Runs INSIDE the node's netns (via netvm-exec.sh). Reads the identifier
|
|||||||
onboard-driver.py --node muse --service muse --id-type email --step submit
|
onboard-driver.py --node muse --service muse --id-type email --step submit
|
||||||
|
|
||||||
Exit codes: 0 = step done, 2 = APPROVAL_NEEDED (code sent, awaiting OTP),
|
Exit codes: 0 = step done, 2 = APPROVAL_NEEDED (code sent, awaiting OTP),
|
||||||
1 = failed. The identifier/code are passed to the local signin script as
|
3 = NEEDS_HUMAN (multi-account selection needs a person), 1 = failed.
|
||||||
|
The identifier/code are passed to the local signin script as
|
||||||
argv (transient, same trust domain — bl is operator infrastructure);
|
argv (transient, same trust domain — bl is operator infrastructure);
|
||||||
they never cross a network boundary except inside the already-encrypted
|
they never cross a network boundary except inside the already-encrypted
|
||||||
VM->bl SSH stdin pipe.
|
VM->bl SSH stdin pipe.
|
||||||
@@ -49,6 +50,8 @@ def main():
|
|||||||
p.add_argument("--id-type", required=True)
|
p.add_argument("--id-type", required=True)
|
||||||
p.add_argument("--step", required=True, choices=["initiate", "submit"])
|
p.add_argument("--step", required=True, choices=["initiate", "submit"])
|
||||||
p.add_argument("--dry-run", action="store_true")
|
p.add_argument("--dry-run", action="store_true")
|
||||||
|
p.add_argument("--account-name", default=None,
|
||||||
|
help="display-name hint for multi-account selection")
|
||||||
args = p.parse_args()
|
args = p.parse_args()
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
lines = sys.stdin.read().splitlines()
|
||||||
@@ -79,6 +82,8 @@ def main():
|
|||||||
return 1
|
return 1
|
||||||
|
|
||||||
cmd = [sys.executable, SIGNIN, "--node", args.node, "--email", identifier]
|
cmd = [sys.executable, SIGNIN, "--node", args.node, "--email", identifier]
|
||||||
|
if args.account_name:
|
||||||
|
cmd += ["--account-name", args.account_name]
|
||||||
if args.step == "submit":
|
if args.step == "submit":
|
||||||
if not code:
|
if not code:
|
||||||
print("ERROR: no code on stdin", file=sys.stderr)
|
print("ERROR: no code on stdin", file=sys.stderr)
|
||||||
|
|||||||
Reference in New Issue
Block a user