| |
| """ |
| 从运行中的 WorkBuddy 提取 Token(通过 CDP)。 |
| |
| 用法: |
| python extract_token.py # 默认 CDP 端口 9222 |
| python extract_token.py --port 9223 # 自定义端口 |
| python extract_token.py --save # 提取并保存到 data/token.json |
| |
| 前提: WorkBuddy 以 --remote-debugging-port=9222 启动。 |
| """ |
|
|
| import argparse |
| import asyncio |
| import json |
| import sys |
| from pathlib import Path |
|
|
| if sys.platform == "win32": |
| asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) |
|
|
| try: |
| import httpx |
| except ImportError: |
| sys.exit("请先安装依赖: pip install httpx") |
|
|
|
|
| async def extract(cdp_port: int = 9222) -> dict | None: |
| base = f"http://127.0.0.1:{cdp_port}" |
| async with httpx.AsyncClient() as client: |
| try: |
| resp = await client.get(f"{base}/json", timeout=5) |
| except httpx.ConnectError: |
| print(f"无法连接 CDP ({base}),请确认 WorkBuddy 已用调试模式启动") |
| return None |
| targets = resp.json() |
|
|
| ws_url = None |
| for t in targets: |
| if t.get("type") == "page" and "workbench" in t.get("url", ""): |
| ws_url = t.get("webSocketDebuggerUrl") |
| break |
| if not ws_url: |
| for t in targets: |
| if t.get("type") == "page": |
| ws_url = t.get("webSocketDebuggerUrl") |
| break |
| if not ws_url: |
| print("未找到可用的 CDP 页面目标") |
| return None |
|
|
| try: |
| import websockets |
| except ImportError: |
| sys.exit("请安装 websockets: pip install websockets") |
|
|
| async with websockets.connect(ws_url) as ws: |
| cmd = { |
| "id": 1, |
| "method": "Runtime.evaluate", |
| "params": { |
| "expression": """ |
| (async () => { |
| try { |
| const s = await window.vscode.ipcRenderer.invoke( |
| 'vscode:genie:auth:getSession' |
| ); |
| return JSON.stringify(s); |
| } catch(e) { |
| return JSON.stringify({error: e.message}); |
| } |
| })() |
| """, |
| "awaitPromise": True, |
| "returnByValue": True, |
| }, |
| } |
| await ws.send(json.dumps(cmd)) |
| result = json.loads(await asyncio.wait_for(ws.recv(), timeout=10)) |
|
|
| value = result.get("result", {}).get("result", {}).get("value", "") |
| if not value: |
| print("CDP 返回为空") |
| return None |
|
|
| session = json.loads(value) |
| if session.get("error"): |
| print(f"CDP 错误: {session['error']}") |
| return None |
|
|
| return session |
|
|
|
|
| def main(): |
| parser = argparse.ArgumentParser(description="从 WorkBuddy 提取 Token") |
| parser.add_argument("--port", type=int, default=9222, help="CDP 端口 (默认 9222)") |
| parser.add_argument("--save", action="store_true", help="保存到 data/token.json") |
| args = parser.parse_args() |
|
|
| session = asyncio.run(extract(args.port)) |
| if not session: |
| sys.exit(1) |
|
|
| |
| auth = session.get("auth", session) |
| access_token = auth.get("accessToken", "") |
| refresh_token = auth.get("refreshToken", "") |
|
|
| if not access_token: |
| print("未获取到 accessToken") |
| sys.exit(1) |
|
|
| print(f"accessToken: {access_token[:40]}... (len={len(access_token)})") |
| print(f"refreshToken: {refresh_token[:40]}... (len={len(refresh_token)})") |
|
|
| if args.save: |
| import time |
| out = Path(__file__).parent / "data" / "token.json" |
| out.parent.mkdir(parents=True, exist_ok=True) |
| out.write_text(json.dumps({ |
| "access_token": access_token, |
| "refresh_token": refresh_token, |
| "saved_at": time.strftime("%Y-%m-%d %H:%M:%S"), |
| }, indent=2)) |
| print(f"\n已保存到 {out}") |
|
|
|
|
| if __name__ == "__main__": |
| main() |
|
|