wimotek-chromium-sync 1.0.7 → 1.0.9

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.
package/README.md CHANGED
@@ -4,95 +4,197 @@
4
4
  [![npm version](https://img.shields.io/npm/v/wimotek-chromium-sync.svg?style=flat-square)](https://www.npmjs.com/package/wimotek-chromium-sync)
5
5
  [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg?style=flat-square)](LICENSE)
6
6
 
7
- 专为 Chromium / Chrome 设计的通用会话与 Cookie 穿透热同步工具。
7
+ 专为 Chromium / Chrome 设计的通用会话与 Cookie 集群同步调度工具。支持 1对1 与 1对多集群、本地直连免密、SSH 私钥内容/文件与密码认证、Cookie 有效期智能融合(Merge)以及轻量静态探针。
8
8
 
9
- 支持在本地容器与远程服务器容器之间双向热同步登录凭证(Cookie + LocalStorage),全端口可参数化自定义,内置智能流向识别与带心跳保活的 SSH 隧道自愈管理。
10
9
  ---
11
10
 
12
- ## 🌟 核心特性
11
+ ## 🌟 核心工程特性
13
12
 
14
- * 全端口自由定义:本地容器端口、远程实际端口、本地隧道映射端口及 SSH 端口均可自由配置或通过环境变量覆盖。
15
- * 智能双向流向探测 (auto):连通后自动探测两端 Cookie 数量与页面状态,支持智能推荐、人工交互确认或全自动执行。
16
- * 内置心跳保活隧道:内置 ServerAliveInterval=15 与端口占用检测,已存在则自动复用,不存在则自动拉起,绝不产生假死僵尸连接。
17
- * 无 TTY 环境自适应:在 Linux Crontab 定时任务、无图形后台或 CI 流程中自动回退,不因无键盘输入而崩溃。
18
- * 双生态支持:同时发布于 PyPI 与 npm,支持 Python 代码导入与命令行跨语言一键执行。
13
+ 1. **统一现代节点模型 (1对1 / 1对多集群)**:
14
+ * 原生支持任意规模的节点集群管理与多隧道并发映射。
15
+ * **本地节点免密直连**:配置本地端口即可直接直连 CDP 探针,跳过 SSH 隧道拉起。
19
16
 
20
- ## 📦 安装
17
+ 2. **原生私钥内容 (PEM String) / 路径与密码认证**:
18
+ * `ssh_key`: 原生支持**私钥文本内容字符串**(如以 `-----BEGIN` 开头的 PEM 文本)或**私钥文件绝对路径**(如 `~/.ssh/id_rsa`),调用方无需配置繁琐的环境变量中转。
19
+ * `ssh_password`: 直接支持 SSH 密码认证,支持集群级默认凭证与单节点重载。
20
+
21
+ 3. **结构化执行指标返回 (Structured Return Dict)**:
22
+ * `syncer.run()` 与 `await syncer.run_async()` 执行后返回标准指标字典,上层 Web 控制台可直接捕获迁移量、源端/目标端存量、耗时及明细,无需额外发送 CDP 探针度量。
23
+
24
+ 4. **双驱动入口:原生异步协程 + 同步阻塞**:
25
+ * 原生提供 `await syncer.run_async()` 与 `await syncer.inspect_async()`,适配 FastAPI、Tornado、aiohttp 等现代异步服务。
26
+ * 同步环境直接调用 `syncer.run()`,开箱即用。
21
27
 
22
- Python 环境 (PyPI)
28
+ 5. **暴露轻量静态探针工具方法 (无需实例化集群)**:
29
+ * 提供单点工具函数:`ChromiumSync.get_cookies(port=9222)`、`ChromiumSync.set_cookies(cookies, port=9222)`、`ChromiumSync.probe(port=9222)`,单点探测仅需 1 行代码。
23
30
 
31
+ 6. **全节点智能 Merge 算法**:
32
+ * 汇总所有节点 Cookie,针对冲突项**优先保留有效期最新(expires 更长 / 最新持久凭证)的 Cookie**,并写回热刷新。
33
+
34
+ ---
35
+
36
+ ## 📦 安装
37
+
38
+ ### Python (PyPI)
24
39
  ```bash
25
40
  pip install wimotek-chromium-sync
41
+ playwright install chromium
26
42
  ```
27
43
 
28
- Node.js 环境 (npm)
29
-
44
+ ### Node.js (npm)
30
45
  ```bash
31
- npm install wimotek-chromium-sync
46
+ npm install -g wimotek-chromium-sync
32
47
  ```
33
48
 
34
- ## 🚀 极简使用示例
49
+ ---
35
50
 
36
- ### 1. Python 代码调用(仅需 3 行)
51
+ ## 🚀 极简开发示例
37
52
 
53
+ ### 1. 轻量静态探针工具(无需实例化集群)
54
+ 单点探测某个节点时,直接 1 行静态调用:
38
55
  ```python
39
56
  from chromium_sync import ChromiumSync
40
57
 
41
- 自由定制两端的所有端口
58
+ # 1. 单点探测节点在线状态与页面信息
59
+ info = ChromiumSync.probe(port=9222)
60
+ print(info)
61
+ # {'online': True, 'cookie_count': 42, 'page_title': 'GitHub', 'page_url': 'https://github.com', ...}
42
62
 
43
- syncer = ChromiumSync( remote_ssh="root@xx.xx.xx", # 远程 SSH 服务器
44
- remote_ssh_port=22, # SSH 连接端口
45
- local_cdp_port=16002, # 本地容器的 CDP 端口
46
- remote_cdp_port=9222, # 远程容器的实际
47
- CDP 端口 remote_tunnel_port=9222, # 本地用于接收隧道的端口
48
- direction="auto", # 智能判断: 自动往缺少凭证的一端同步
49
- timeout=15 # 倒计时等待秒数 (0 为秒执行) )
63
+ # 2. 单点直接提取 Cookie 列表
64
+ cookies = ChromiumSync.get_cookies(port=9222)
50
65
 
51
- syncer.run()
66
+ # 3. 单点直接批量写入 Cookie 并热刷新页面
67
+ ChromiumSync.set_cookies(cookies, port=9223, reload=True)
52
68
  ```
53
69
 
54
- ### 2. 命令行使用 (CLI 模式)
70
+ ---
55
71
 
56
- 无论是通过 pip 安装还是 npm 安装,均可直接在终端中敲命令运行:
72
+ ### 2. 现代多节点集群同步与结构化返回
57
73
 
58
- ```bash
74
+ #### (1) Python 同步调用 (同步脚本 / Celery 任务)
75
+ ```python
76
+ from chromium_sync import ChromiumSync
77
+
78
+ syncer = ChromiumSync(
79
+ nodes=[
80
+ {"id": "local", "name": "本地容器", "cdp_port": 9223},
81
+ {
82
+ "id": "kr2",
83
+ "name": "韩国生产服",
84
+ "ssh_host": "kr2.wimotek.com",
85
+ "ssh_user": "root",
86
+ # 直接支持私钥文本内容 (PEM String) 或文件路径
87
+ "ssh_key": "-----BEGIN RSA PRIVATE KEY-----\nMIIEowIB...\n-----END RSA PRIVATE KEY-----",
88
+ "cdp_port": 9222
89
+ },
90
+ {
91
+ "id": "sg1",
92
+ "name": "新加坡服",
93
+ "ssh_host": "sg1.wimotek.com",
94
+ "ssh_user": "ubuntu",
95
+ # 直接支持密码认证
96
+ "ssh_password": "YourStrongPassword123",
97
+ "cdp_port": 9222
98
+ }
99
+ ],
100
+ mode="merge" # "merge" (合并最新凭据) 或 "sync" (单源广播)
101
+ )
102
+
103
+ # 执行同步并获取度量指标字典
104
+ result = syncer.run()
105
+ print(result)
106
+ ```
107
+ **结构化返回值示例**:
108
+ ```python
109
+ {
110
+ "status": "success", # "success" | "failed"
111
+ "mode": "merge", # 执行模式
112
+ "transferred_count": 45, # 实际迁移/融合生成的 Cookie 数量
113
+ "source_total": 80, # 源端/融合前凭证总数
114
+ "target_total": 45, # 目标端/融合后凭证总数
115
+ "nodes_synced": ["local", "kr2", "sg1"], # 成功同步的节点列表
116
+ "duration": 1.25, # 耗时(秒)
117
+ "details": "全节点 Merge 成功: 融合 45 条最新凭证至 ['本地容器', '韩国生产服', '新加坡服']",
118
+ "error": None
119
+ }
120
+ ```
59
121
 
60
- 查看所有端口与流向选项
122
+ #### (2) FastAPI / aiohttp 异步协程原生驱动
123
+ ```python
124
+ from fastapi import FastAPI
125
+ from chromium_sync import ChromiumSync
61
126
 
62
- chromium-sync --help
127
+ app = FastAPI()
128
+
129
+ @app.post("/api/sync")
130
+ async def trigger_sync():
131
+ syncer = ChromiumSync(
132
+ nodes=[
133
+ {"id": "local", "cdp_port": 9222},
134
+ {"id": "cloud", "ssh_host": "remote.host.com", "ssh_key": "~/.ssh/id_rsa", "cdp_port": 9222}
135
+ ],
136
+ mode="merge"
137
+ )
138
+ # 原生 asyncio 协程,零线程切换开销,高并发性能极佳
139
+ result = await syncer.run_async()
140
+ return result
141
+ ```
63
142
 
64
- 自由指定各端口全自动推送 (0 秒等待)
143
+ ---
65
144
 
66
- chromium-sync
67
- --remote-ssh root@xx.xx.xx
68
- --ssh-port 22
69
- --local-port 9222
70
- --tunnel-port 9222
71
- --direction push
72
- --timeout 0
145
+ ### 3. 命令行 CLI 执行
146
+
147
+ #### (1) 内联语法直接启动多节点 Merge
148
+ ```bash
149
+ chromium-sync \
150
+ --node id=local,port=9223 \
151
+ --node id=kr2,ssh=root@kr2.wimotek.com,port=9222,key=~/.ssh/id_rsa \
152
+ --node id=sg1,ssh=ubuntu@sg1.wimotek.com,port=9222,pass=SecretPass123 \
153
+ --mode merge
73
154
  ```
74
155
 
75
- ## ⚙️ 端口与参数对应说明
76
-
77
- | 参数名称 | 命令行选项 | 对应环境变量 | 默认值 | 详细说明 |
78
- | :------------------- | :------------- | :-------------------------- | :--------------------- | :------------------- |
79
- | remote\_ssh | \--remote-ssh | CHROMIUM\_REMOTE\_SSH | <root@xx.xx.xx> | 远程 SSH 连接主机 |
80
- | remote\_ssh\_port | \--ssh-port | CHROMIUM\_SSH\_PORT | 22 | 远程服务器的 SSH 端口 |
81
- | local\_cdp\_port | \--local-port | CHROMIUM\_LOCAL\_PORT | 16002 | 运行在本机的容器 CDP 端口 |
82
- | remote\_cdp\_port | \--remote-port | CHROMIUM\_REMOTE\_CDP\_PORT | 9222 | 运行在远端服务器上的容器实际端口 |
83
- | remote\_tunnel\_port | \--tunnel-port | CHROMIUM\_TUNNEL\_PORT | 9222 | SSH 隧道映射到本机的监听端口 |
84
- | direction | \--direction | CHROMIUM\_DIRECTION | push | 默认同步流向 (push 或 pull 或 auto) |
85
- | timeout | \--timeout | CHROMIUM\_TIMEOUT | 15 | 倒计时交互等待秒数,0 为免打扰秒执行 |
86
- | auto\_close\_tunnel | \--auto-close | \- | False | 任务完成后是否关闭创建的 SSH 隧道 |
87
-
88
- ## 🤖 自动化与定时任务配置示例
89
- 配合 Linux Crontab,每天凌晨自动将本地维护好的登录态推送到云端远程容器:
156
+ #### (2) 仅查看节点状态与 Cookie 数量 (ASCII 表格)
90
157
  ```bash
91
- 编辑定时任务
92
- crontab -e
93
- 添加此行:每天凌晨 3:00 自动执行同步且不弹任何提示
94
- 0 3 * * * /usr/local/bin/chromium-sync --direction push --timeout 0 --auto-close >> /tmp/chromium_sync.log 2>&1
158
+ chromium-sync --config cluster.json --status
95
159
  ```
160
+ 输出样例:
161
+ ```text
162
+ ==========================================================================================
163
+ 节点 ID | 节点名称 | 类型 | CDP端口/映射 | Cookie数 | 当前页面状态
164
+ ------------------------------------------------------------------------------------------
165
+ local | 本地容器 | 本地直连 | 9223 | 42 条 | [Google] https://google.com
166
+ kr2 | 韩国节点 | SSH远程 | 19222 | 38 条 | [GitHub] https://github.com
167
+ ==========================================================================================
168
+ ```
169
+
170
+ #### (3) 导出 Cookie 详情或执行结果为 JSON
171
+ ```bash
172
+ # 1. 导出所有节点及 Cookie 列表为 JSON
173
+ chromium-sync --config cluster.json --status --json > /tmp/cluster_data.json
174
+
175
+ # 2. 执行同步并将结构化指标以 JSON 打印 (方便 CI/CD 或流水线捕获)
176
+ chromium-sync --config cluster.json --mode merge --json
177
+ ```
178
+
179
+ ---
180
+
181
+ ## ⚙️ 节点参数配置规范 (NodeConfig)
182
+
183
+ | 字段 | 类型 | 默认值 | 详细说明 |
184
+ | :--- | :--- | :--- | :--- |
185
+ | `id` | str | 必填 | 节点唯一标识符 (例如 `local`, `kr2`) |
186
+ | `name` | str | `id` | 节点显示名称 |
187
+ | `is_local` | bool | 自动识别 | 是否为本地节点 (不填 `ssh_host` 或填回环地址自动判定为本地,免密直连) |
188
+ | `cdp_host` | str | `127.0.0.1` | 节点 CDP 服务监听的主机地址 |
189
+ | `cdp_port` | int | `9222` | 节点暴露的 CDP 端口 |
190
+ | `ssh_host` | str | `None` | 远程服务器 IP 或主机名 (提供该字段时识别为远程 SSH 节点) |
191
+ | `ssh_port` | int | `22` | 远程服务器 SSH 端口 |
192
+ | `ssh_user` | str | `root` | 远程 SSH 用户名 |
193
+ | `ssh_key` | str | `None` | **SSH 私钥 (同时支持文件绝对路径 或 私钥 PEM 文本字符串)** |
194
+ | `ssh_password` | str | `None` | SSH 登录密码 (自动适配 `sshpass` 与原生 `SSH_ASKPASS`) |
195
+ | `tunnel_port` | int | 自动分配 | 本地用于映射隧道的端口 (多节点自动分配防冲突) |
196
+
197
+ ---
96
198
 
97
199
  ## 📄 License
98
200
 
@@ -1,7 +1,8 @@
1
1
  """
2
- Chromium Sync Package
2
+ wimotek-chromium-sync Package
3
+ Universal Chromium Session & Cookie Synchronizer with SSH Tunnel and Cluster Support
3
4
  """
4
- from .core import ChromiumSync
5
+ from .core import ChromiumSync, NodeConfig, merge_cookies
5
6
 
6
- __version__ = "1.0.0"
7
- __all__ = ["ChromiumSync"]
7
+ __version__ = "1.2.0"
8
+ __all__ = ["ChromiumSync", "NodeConfig", "merge_cookies"]
@@ -1,33 +1,165 @@
1
1
  #!/usr/bin/env python3
2
2
  # -*- coding: utf-8 -*-
3
+ """
4
+ Chromium Sync CLI (现代多节点调度版)
5
+ """
3
6
 
4
7
  import argparse
5
- from .core import ChromiumSync
8
+ import json
9
+ import os
10
+ import sys
11
+ try:
12
+ from .core import ChromiumSync, NodeConfig
13
+ except (ImportError, ValueError):
14
+ from core import ChromiumSync, NodeConfig
15
+
16
+
17
+ def parse_node_str(val: str) -> dict:
18
+ """
19
+ 解析内联节点定义参数:
20
+ 例如:
21
+ --node id=local,port=9222
22
+ --node id=kr2,ssh=root@kr2.wimotek.com,port=9222,key=~/.ssh/id_rsa
23
+ --node id=sg1,ssh=ubuntu@sg1.wimotek.com,port=9222,pass=Secret123
24
+ --node '{"id": "local", "cdp_port": 9222}'
25
+ """
26
+ val = val.strip()
27
+ if val.startswith("{") and val.endswith("}"):
28
+ return json.loads(val)
29
+
30
+ res = {}
31
+ for part in val.split(","):
32
+ if "=" in part:
33
+ k, v = part.split("=", 1)
34
+ k = k.strip().lower()
35
+ v = v.strip()
36
+ if k in ("port", "cdp_port"):
37
+ res["cdp_port"] = int(v)
38
+ elif k in ("ssh_port",):
39
+ res["ssh_port"] = int(v)
40
+ elif k in ("tunnel_port",):
41
+ res["tunnel_port"] = int(v)
42
+ elif k in ("local", "is_local"):
43
+ res["is_local"] = v.lower() in ("true", "1", "yes")
44
+ elif k in ("host", "cdp_host"):
45
+ res["cdp_host"] = v
46
+ elif k in ("ssh", "ssh_host"):
47
+ if "@" in v:
48
+ user, host = v.split("@", 1)
49
+ res["ssh_user"] = user
50
+ res["ssh_host"] = host
51
+ else:
52
+ res["ssh_host"] = v
53
+ elif k in ("user", "ssh_user"):
54
+ res["ssh_user"] = v
55
+ elif k in ("key", "ssh_key", "ssh_key_path"):
56
+ res["ssh_key"] = v
57
+ elif k in ("pass", "password", "ssh_pass", "ssh_password"):
58
+ res["ssh_password"] = v
59
+ elif k in ("id", "name"):
60
+ res[k] = v
61
+ else:
62
+ res[k] = v
63
+
64
+ if "id" not in res:
65
+ res["id"] = res.get("name") or res.get("ssh_host") or "local"
66
+ return res
6
67
 
7
68
 
8
69
  def main():
9
- parser = argparse.ArgumentParser(description="Chromium 一体化会话热同步工具")
10
- parser.add_argument("--remote-ssh", default="root@kr2.wimotek.com", help="远程 SSH 地址")
11
- parser.add_argument("--ssh-port", type=int, default=22, help="远程 SSH 端口")
12
- parser.add_argument("--local-port", type=int, default=9223, help="本地容器 CDP 端口")
13
- parser.add_argument("--remote-port", type=int, default=9222, help="远程容器暴露的端口")
14
- parser.add_argument("--tunnel-port", type=int, default=9222, help="本地隧道端口")
15
- parser.add_argument("--direction", choices=["push", "pull", "auto"], default=None, help="同步方向")
16
- parser.add_argument("--timeout", type=int, default=5, help="倒计时秒数 (设为 0 即秒执行)")
17
- parser.add_argument("--auto-close", action="store_true", help="任务完成后关闭 SSH 隧道")
70
+ parser = argparse.ArgumentParser(
71
+ description="现代多节点 Chromium 会话同步与 Cookie 集群调度工具",
72
+ formatter_class=argparse.RawDescriptionHelpFormatter,
73
+ epilog="""
74
+ 使用示例:
75
+ # 1. 快速查看节点状态与 Cookie 数量 (ASCII 表格)
76
+ chromium-sync --node id=local,port=9222 --node id=kr2,ssh=root@kr2.wimotek.com,port=9222,key=~/.ssh/id_rsa --status
77
+
78
+ # 2. 导出节点状态与 Cookie 详情为 JSON (便于程序直接提取 Cookie 列表)
79
+ chromium-sync --config cluster.json --status --json
80
+
81
+ # 3. 全节点 Cookie 智能 Merge (合并所有节点并保留最新有效期)
82
+ chromium-sync --config cluster.json --mode merge
83
+
84
+ # 4. 指定单源节点广播同步并输出结构化指标 JSON
85
+ chromium-sync --config cluster.json --mode sync --source local --json
86
+
87
+ # 5. 极简命令行多节点直接同步
88
+ chromium-sync \\
89
+ --node id=local,port=9223 \\
90
+ --node id=kr2,ssh=root@kr2.wimotek.com,port=9222,key=~/.ssh/id_rsa \\
91
+ --mode merge
92
+ """
93
+ )
94
+
95
+ # 节点输入方式
96
+ parser.add_argument("--config", help="集群节点配置文件路径 (JSON 格式)")
97
+ parser.add_argument("--node", action="append", default=[], help="定义节点 (可多次传入,如 id=local,port=9222)")
98
+ parser.add_argument("--nodes", help="JSON 数组格式的节点配置字符串")
99
+
100
+ # 全局默认 SSH 凭据
101
+ parser.add_argument("--ssh-key", help="集群默认 SSH 私钥 (支持私钥文件绝对路径 或 私钥 PEM 文本内容)")
102
+ parser.add_argument("--ssh-password", help="集群默认 SSH 连接密码")
103
+
104
+ # 同步模式
105
+ parser.add_argument("--mode", choices=["sync", "merge"], default="sync",
106
+ help="同步方式: 'merge'(合并所有节点最新Cookie), 'sync'(单源广播同步,默认)")
107
+ parser.add_argument("--source", default=None, help="sync 模式下的源节点 ID (默认第一个节点)")
108
+ parser.add_argument("--targets", nargs="*", default=None, help="sync 模式下的目标节点 ID 列表 (默认其余所有节点)")
109
+
110
+ # 探测与数据查询
111
+ parser.add_argument("--status", action="store_true", help="仅探测并输出各节点的 Cookie 数与详情表格,不执行写入")
112
+ parser.add_argument("--json", action="store_true", help="以 JSON 格式输出节点数据或执行结果指标")
113
+
114
+ # 隧道与连接
115
+ parser.add_argument("--auto-close", action="store_true", help="任务完成后关闭拉起的 SSH 隧道")
116
+ parser.add_argument("-v", "--verbose", action="store_true", help="显示详细调试日志")
117
+
18
118
  args = parser.parse_args()
19
119
 
120
+ # 聚合节点配置
121
+ raw_nodes = []
122
+ if args.config:
123
+ if not os.path.exists(args.config):
124
+ sys.exit(f"[-] 配置文件不存在: {args.config}")
125
+ with open(args.config, "r", encoding="utf-8") as f:
126
+ raw_nodes.extend(json.load(f))
127
+
128
+ if args.nodes:
129
+ raw_nodes.extend(json.loads(args.nodes))
130
+
131
+ if args.node:
132
+ for n_str in args.node:
133
+ raw_nodes.append(parse_node_str(n_str))
134
+
135
+ # 默认兜底:若未传入任何节点参数,尝试使用标准默认本地节点
136
+ if not raw_nodes:
137
+ raw_nodes = [
138
+ {"id": "local", "name": "本地容器", "is_local": True, "cdp_port": 9222}
139
+ ]
140
+
20
141
  syncer = ChromiumSync(
21
- remote_ssh=args.remote_ssh,
22
- remote_ssh_port=args.ssh_port,
23
- local_cdp_port=args.local_port,
24
- remote_cdp_port=args.remote_port,
25
- remote_tunnel_port=args.tunnel_port,
26
- direction=args.direction or "auto",
27
- timeout=args.timeout,
28
- auto_close_tunnel=args.auto_close
142
+ nodes=raw_nodes,
143
+ mode=args.mode,
144
+ source=args.source,
145
+ targets=args.targets,
146
+ ssh_key=args.ssh_key,
147
+ ssh_password=args.ssh_password,
148
+ auto_close_tunnel=args.auto_close,
149
+ verbose=args.verbose
29
150
  )
30
- syncer.run()
151
+
152
+ if args.status:
153
+ report = syncer.inspect()
154
+ if args.json:
155
+ print(json.dumps(report, ensure_ascii=False, indent=2))
156
+ else:
157
+ print(syncer.get_summary_table(report))
158
+ return
159
+
160
+ result = syncer.run()
161
+ if args.json:
162
+ print(json.dumps(result, ensure_ascii=False, indent=2))
31
163
 
32
164
 
33
165
  if __name__ == "__main__":