bb-sync 1.9.0__tar.gz → 2.0.0__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.
Files changed (55) hide show
  1. {bb_sync-1.9.0 → bb_sync-2.0.0}/.github/workflows/ci.yml +4 -4
  2. {bb_sync-1.9.0 → bb_sync-2.0.0}/AGENTS.md +27 -12
  3. {bb_sync-1.9.0 → bb_sync-2.0.0}/CHANGELOG.md +55 -0
  4. {bb_sync-1.9.0 → bb_sync-2.0.0}/CONTRIBUTING.md +3 -3
  5. {bb_sync-1.9.0 → bb_sync-2.0.0}/PKG-INFO +15 -7
  6. {bb_sync-1.9.0 → bb_sync-2.0.0}/README.md +14 -6
  7. {bb_sync-1.9.0 → bb_sync-2.0.0}/docs/scripting.md +2 -2
  8. {bb_sync-1.9.0 → bb_sync-2.0.0}/pyproject.toml +1 -1
  9. {bb_sync-1.9.0 → bb_sync-2.0.0}/scripts/debug_course.py +4 -7
  10. {bb_sync-1.9.0 → bb_sync-2.0.0}/scripts/debug_courses.py +4 -7
  11. {bb_sync-1.9.0 → bb_sync-2.0.0}/scripts/debug_login.py +4 -7
  12. {bb_sync-1.9.0 → bb_sync-2.0.0}/src/bb_sync/__init__.py +1 -1
  13. {bb_sync-1.9.0 → bb_sync-2.0.0}/src/bb_sync/blackboard/login.py +3 -2
  14. bb_sync-2.0.0/src/bb_sync/browser/__init__.py +5 -0
  15. bb_sync-2.0.0/src/bb_sync/browser/launcher.py +424 -0
  16. {bb_sync-1.9.0 → bb_sync-2.0.0}/src/bb_sync/cli.py +24 -4
  17. bb_sync-2.0.0/src/bb_sync/commands/browser.py +90 -0
  18. {bb_sync-1.9.0 → bb_sync-2.0.0}/src/bb_sync/commands/course.py +4 -0
  19. {bb_sync-1.9.0 → bb_sync-2.0.0}/src/bb_sync/commands/doctor.py +1 -1
  20. {bb_sync-1.9.0 → bb_sync-2.0.0}/src/bb_sync/commands/due.py +5 -0
  21. {bb_sync-1.9.0 → bb_sync-2.0.0}/src/bb_sync/commands/run.py +5 -0
  22. {bb_sync-1.9.0 → bb_sync-2.0.0}/src/bb_sync/core/doctor.py +52 -81
  23. {bb_sync-1.9.0 → bb_sync-2.0.0}/src/bb_sync/core/errors.py +2 -2
  24. bb_sync-2.0.0/src/bb_sync/core/help.py +123 -0
  25. {bb_sync-1.9.0 → bb_sync-2.0.0}/src/bb_sync/core/service.py +88 -102
  26. {bb_sync-1.9.0 → bb_sync-2.0.0}/src/bb_sync/paths.py +1 -1
  27. {bb_sync-1.9.0 → bb_sync-2.0.0}/tests/conftest.py +7 -8
  28. {bb_sync-1.9.0 → bb_sync-2.0.0}/tests/test_cli_e2e.py +177 -2
  29. {bb_sync-1.9.0 → bb_sync-2.0.0}/tests/test_units.py +285 -232
  30. {bb_sync-1.9.0 → bb_sync-2.0.0}/uv.lock +1 -1
  31. bb_sync-1.9.0/src/bb_sync/browser/__init__.py +0 -5
  32. bb_sync-1.9.0/src/bb_sync/browser/steel.py +0 -511
  33. {bb_sync-1.9.0 → bb_sync-2.0.0}/.github/workflows/release.yml +0 -0
  34. {bb_sync-1.9.0 → bb_sync-2.0.0}/.gitignore +0 -0
  35. {bb_sync-1.9.0 → bb_sync-2.0.0}/.pre-commit-config.yaml +0 -0
  36. {bb_sync-1.9.0 → bb_sync-2.0.0}/LICENSE +0 -0
  37. {bb_sync-1.9.0 → bb_sync-2.0.0}/config.yaml +0 -0
  38. {bb_sync-1.9.0 → bb_sync-2.0.0}/docs/config.md +0 -0
  39. {bb_sync-1.9.0 → bb_sync-2.0.0}/src/bb_sync/__main__.py +0 -0
  40. {bb_sync-1.9.0 → bb_sync-2.0.0}/src/bb_sync/blackboard/__init__.py +0 -0
  41. {bb_sync-1.9.0 → bb_sync-2.0.0}/src/bb_sync/blackboard/downloader.py +0 -0
  42. {bb_sync-1.9.0 → bb_sync-2.0.0}/src/bb_sync/blackboard/models.py +0 -0
  43. {bb_sync-1.9.0 → bb_sync-2.0.0}/src/bb_sync/blackboard/scraper.py +0 -0
  44. {bb_sync-1.9.0 → bb_sync-2.0.0}/src/bb_sync/commands/__init__.py +0 -0
  45. {bb_sync-1.9.0 → bb_sync-2.0.0}/src/bb_sync/commands/auth.py +0 -0
  46. {bb_sync-1.9.0 → bb_sync-2.0.0}/src/bb_sync/commands/config.py +0 -0
  47. {bb_sync-1.9.0 → bb_sync-2.0.0}/src/bb_sync/context.py +0 -0
  48. {bb_sync-1.9.0 → bb_sync-2.0.0}/src/bb_sync/core/__init__.py +0 -0
  49. {bb_sync-1.9.0 → bb_sync-2.0.0}/src/bb_sync/core/config.py +0 -0
  50. {bb_sync-1.9.0 → bb_sync-2.0.0}/src/bb_sync/core/config_store.py +0 -0
  51. {bb_sync-1.9.0 → bb_sync-2.0.0}/src/bb_sync/core/due.py +0 -0
  52. {bb_sync-1.9.0 → bb_sync-2.0.0}/src/bb_sync/core/output.py +0 -0
  53. {bb_sync-1.9.0 → bb_sync-2.0.0}/src/bb_sync/core/update.py +0 -0
  54. {bb_sync-1.9.0 → bb_sync-2.0.0}/src/bb_sync/creds.py +0 -0
  55. {bb_sync-1.9.0 → bb_sync-2.0.0}/tests/test_update_e2e.py +0 -0
@@ -76,11 +76,11 @@ jobs:
76
76
  bb-sync --version
77
77
  # 注意:doctor 的诊断输出走 stderr(stdout 只留给 --json 的结构化结果),
78
78
  # 因此必须用 2>&1 才能把体检结果落进文件供 grep 断言。
79
- # 容器里没有 Node/浏览器,doctor 会报缺项(预期,退出码非 0),只断言基础项
80
- bb-sync doctor > doctor.txt 2>&1 || true
79
+ # 容器里没有浏览器,doctor 会报缺项(预期,退出码非 0),只断言基础项。
80
+ # 网络项探的是校园站点 bb.cuhk.edu.cn(校外需要 VPN),CI 上不作要求,故跳过。
81
+ bb-sync doctor --skip-network > doctor.txt 2>&1 || true
81
82
  grep -q "✓.*Python" doctor.txt
82
83
  grep -q "✓.*git" doctor.txt
83
- grep -q "✓.*网络" doctor.txt
84
84
  # CLI 结构断言:无参数出帮助且退出码 0
85
85
  bb-sync >/dev/null
86
86
  test $? -eq 0
@@ -91,7 +91,7 @@ jobs:
91
91
  bb-sync definitely-not-a-command >/dev/null 2>&1 || rc=$?
92
92
  test "$rc" -eq 2
93
93
  # --json 的 JSON 必须落在 stdout 且可被解析(诊断走 stderr 不污染)
94
- bb-sync --json doctor 2>/dev/null | python -c "import json,sys; json.load(sys.stdin)"
94
+ bb-sync --json doctor --skip-network 2>/dev/null | python -c "import json,sys; json.load(sys.stdin)"
95
95
 
96
96
  # 分支保护要求状态检查名为 `quality`;矩阵任务改名后由这个聚合门禁继续提供。
97
97
  quality:
@@ -13,7 +13,8 @@
13
13
  PowerShell 与 zsh / bash。
14
14
  - **技术栈**:Python ≥3.11;Typer + Click(CLI)、pydantic v2(配置)、PyYAML、rich(输出)、
15
15
  keyring(凭据)、Playwright(浏览器自动化)。
16
- - **浏览器后端**:Steel——本地 Node ≥22 服务,通过 CDP 接入,Chrome profile 持久化登录态。
16
+ - **浏览器后端**:直接驱动本机已装的 Chrome / Edge——分离进程 + CDP + 持久 profile,
17
+ 不引入 Node / npm 等额外运行时(也不要为此加回任何需要联网下载的浏览器依赖)。
17
18
  - **打包**:hatchling,`src/` 布局,入口 `bb-sync = bb_sync.cli:main`。
18
19
  - **语言**:代码注释、docstring、提交信息、用户文档一律中文。安装与路径示例同时覆盖
19
20
  Windows PowerShell 和 macOS zsh / bash。
@@ -26,10 +27,10 @@
26
27
  | `src/bb_sync/context.py` | 全局 Context(`--json/--quiet/--verbose/--config`),惰性导入 |
27
28
  | `src/bb_sync/paths.py` | `BB_SYNC_HOME`(默认 `~/.bb-sync`)与配置文件查找 |
28
29
  | `src/bb_sync/creds.py` | keyring 凭据读写 |
29
- | `src/bb_sync/commands/` | 子命令:run / doctor / config / auth / course |
30
+ | `src/bb_sync/commands/` | 子命令:run / due / doctor / config / auth / course / browser |
30
31
  | `src/bb_sync/core/` | config(分层加载)、config_store(行级 YAML 编辑)、service(同步编排)、doctor、output(Console)、errors(退出码) |
31
32
  | `src/bb_sync/blackboard/` | login(ADFS SSO)、scraper(列表 / 内容抓取)、downloader、models |
32
- | `src/bb_sync/browser/steel.py` | Steel 后端定位 / 安装 / 启动、CDP 会话、持久 profile |
33
+ | `src/bb_sync/browser/launcher.py` | 本机浏览器探测 / 分离启动 / CDP 接入、持久 profile |
33
34
  | `tests/` | 全部离线:`test_units.py`(纯逻辑)+ `test_cli_e2e.py`(进程内 CLI 契约) |
34
35
  | `scripts/debug_*.py` | 手工调试脚本,会真实登录 / 访问站点;不属于测试,不要在 CI 或非必要时运行 |
35
36
 
@@ -47,9 +48,9 @@ uv run bb-sync doctor # 环境体检(触网;诊断走 stderr)
47
48
  ```
48
49
 
49
50
  以上命令在 Windows PowerShell 与 macOS zsh / bash 中相同。macOS 前置依赖可用
50
- `brew install git node uv` 安装。
51
+ `brew install git uv` 安装。
51
52
 
52
- 只有真实运行 `run` / `course list` / `doctor`(未加 `--skip-network`)才需要 Node ≥22、
53
+ 只有真实运行 `run` / `course list` / `doctor`(未加 `--skip-network`)才需要本机的
53
54
  Chrome 或 Edge 和真实凭据;测试与质量检查完全离线。
54
55
 
55
56
  ### 提交前质量门禁(改动必须全绿)
@@ -90,17 +91,29 @@ CLI 参数 > `BB_SYNC_<KEY>` 环境变量 > config.yaml > 内置默认。
90
91
  `config set/unset` 通过 `core/config_store.py` 做**行级编辑**,必须保留注释与未知键;
91
92
  不要改成整份 YAML 重写。路径常量在 import 时快照,测试靠 monkeypatch 覆盖。
92
93
 
93
- ### 5. Steel 后端锚定快照
94
+ ### 5. 浏览器后端:分离进程 + 自有 profile
94
95
 
95
- `browser/steel.py` 的 `STEEL_REF` 固定到验证过的 commit,不追随上游 `main`;
96
- 升级方式 = 改 `STEEL_REF` 并在 `CHANGELOG.md` 记录。
97
- Steel 安装目录只能是 `BB_SYNC_HOME/.steel`,任何情况下不读写用户家目录的 `~/.steel`。
96
+ 浏览器就是用户机器上的 Chrome / Edge,由 `browser/launcher.py` 以
97
+ `--remote-debugging-port=0` 拉起,Playwright 只当 CDP 客户端。四条不能破的约束:
98
+
99
+ - **跑完自动关**:默认命令结束(成功或失败)就关掉浏览器,不留后台进程。
100
+ `browser_session` 的 finally 负责收尾,`--keep-open` 才保留实例供手动过 MFA
101
+ 或连续多次运行——不要在命令里留下没人管的浏览器。
102
+ - **分离**:进程独立于 CLI 存活(Windows 用 `CREATE_NO_WINDOW|CREATE_NEW_PROCESS_GROUP`,
103
+ POSIX 用 `start_new_session`),命令跑到一半终端被关掉也不会把它带走,
104
+ `--keep-open` 保留下来的实例下次运行直接复用。
105
+ - **端口自发现**:只认浏览器自己写下的 `DevToolsActivePort`,不要改成固定端口或自建注册表。
106
+ - **profile 隔离**:`--user-data-dir` 只能是 `BB_SYNC_HOME/.browser-profile/<chrome|edge>`,
107
+ 任何情况下不读不写用户日常使用的浏览器 profile。
108
+ - **关闭方式**:只能走 CDP 的 `Browser.close`(`stop_browser`),不要按 PID 杀进程——
109
+ 记录下来的 PID 可能已被系统回收。
98
110
 
99
111
  ## 测试要求
100
112
 
101
113
  - 默认测试套件必须离线:不登录、不下载、不起浏览器、不碰真实钥匙串。
102
- `tests/conftest.py` 自动隔离 `BB_SYNC_HOME`、CWD、keyring 与 Steel 路径——
103
- 新测试直接依赖既有 fixture(`cli`、`config_file` 等),不要连接真实环境。
114
+ `tests/conftest.py` 自动隔离 `BB_SYNC_HOME`、CWD、keyring 与浏览器 profile 路径——
115
+ 新测试直接依赖既有 fixture(`cli`、`config_file` 等),不要连接真实环境,
116
+ 更不要在测试里真的拉起浏览器进程。
104
117
  - 新行为要带测试:纯逻辑进 `tests/test_units.py`,CLI 契约 / 退出码 / 输出分流进 `tests/test_cli_e2e.py`。
105
118
  - 真实站点逻辑(登录、抓取、下载)无法离线覆盖时,在提交信息或 PR 里写清可重复的手工验证步骤。
106
119
 
@@ -126,5 +139,7 @@ Steel 安装目录只能是 `BB_SYNC_HOME/.steel`,任何情况下不读写用
126
139
  - GitHub Actions 的 run 默认 `set -e`,断言非零退出码需用 `|| true` 兜住(见 `ci.yml` 注释)。
127
140
  - Windows 与 macOS 是主要目标平台,CI 同时跑 Ubuntu、macOS 与 Windows;路径、后台进程和
128
141
  浏览器探测相关改动两个平台都要考虑。
129
- - 首次准备 Steel 需从 GitHub / npm 拉依赖,国内网络慢时可临时用代理或镜像,不要写永久全局配置。
142
+ - 浏览器后端不需要任何联网安装步骤;如果哪天又引入需要下载的组件,先想清楚国内网络下的代价。
143
+ - 关闭后台浏览器只能走 CDP(`browser stop`);直接 kill 浏览器进程会留下失效的
144
+ `DevToolsActivePort`,虽然下次启动会清掉,但 profile 可能没落盘。
130
145
  - 下载目录默认 `~/courses`,课程资料有版权;禁止提交或外传 `courses/`、`.workbuddy/` 内容。
@@ -2,6 +2,61 @@
2
2
 
3
3
  本项目遵循 [语义化版本](https://semver.org/lang/zh-CN/)。
4
4
 
5
+ ## [2.0.0] — 2026-10-14
6
+
7
+ ### 变更(破坏性)
8
+
9
+ - **移除 Steel 浏览器后端,改用本机已装的 Chrome / Edge**(没有 Chrome 时自动回退
10
+ Edge),通过 CDP 驱动。不再需要 Node.js / npm,首次运行也不再从 GitHub 下载
11
+ 约 90 MB 源码再跑 `npm install`——此前首次准备要几分钟、安装目录约 700 MB,
12
+ 现在这些全部省掉,且不再依赖 github.com 可达。
13
+ - 浏览器以**分离进程**运行——独立于 CLI 存活,命令跑到一半关掉终端也不会被带走——
14
+ 但**跑完会自动关闭**,成功或失败都不留后台进程。保留状态下的冷启动 0.3 秒、
15
+ 复用 0.01 秒(本机实测)。
16
+ - **登录态位置变更**:profile 改存 `~/.bb-sync/.browser-profile/<chrome|edge>`,
17
+ 登录态仍跨次运行保留;旧目录 `steel-chrome` 不再使用,升级后需要重新登录一次。
18
+ - `bb-sync doctor` 去掉 Node.js / npm 两项(现在只依赖浏览器),网络项改为探测
19
+ Blackboard 站点本身——原来探 github.com 只是为了下载 Steel。
20
+ - 旧版 `~/.bb-sync/.steel` 目录如仍存在,`bb-sync doctor` 会提示手动删除以释放空间。
21
+
22
+ ### 新增
23
+
24
+ - `--keep-open`(`run` / `due` / `course list`):跑完保留后台浏览器,供手动过 MFA、
25
+ 连续多次运行或调试;配套 `bb-sync browser status` 查看状态、`bb-sync browser stop`
26
+ 关闭(登录态保留在 profile,下次同步会自动重启)。
27
+ - 抹掉 `navigator.webdriver`:浏览器一旦开启远程调试端口,这个值就是 true,
28
+ 站点常据此判定「被自动化控制」;现在通过 init script 处理,与旧后端的反检测
29
+ 能力保持对齐。
30
+
31
+ ### 测试
32
+
33
+ - 删除 Steel 部署 / 回滚 / 锚点相关用例,新增浏览器探测、启动参数、端口文件解析、
34
+ 实例复用与显示模式切换、CDP 关闭等用例;`doctor` 的检查项回归防线同步更新。
35
+
36
+ ## [1.10.0] — 2026-10-14
37
+
38
+ ### 新增
39
+
40
+ - `--help` / `-h` 与无参数帮助支持彩色输出:章节标题与 `Usage:` 加粗,
41
+ 选项名、命令名用青色。颜色只在输出到终端时出现——管道或重定向时 Click 会
42
+ 自动剥掉 ANSI,脚本里 grep 帮助内容不受影响。
43
+ - `-h, --help` 与 `--version` 的说明文案本地化为中文。此前是 Click 硬编码的
44
+ `Show this message and exit.` / `Show the version and exit.`,与其余中文
45
+ 说明不一致。
46
+
47
+ ### 变更
48
+
49
+ - 子命令帮助不再走 Typer 自带的 Rich 面板,统一改用 Click 的 formatter。
50
+ 那套面板在终端里其实并不上色,而且 Rich 会二次处理彩色转义序列(`\x1b`
51
+ 被吃掉、只剩字面量 `[1m`)。换掉之后 `bb-sync --help` 不再导入 rich,
52
+ 帮助路径也随之变快。
53
+
54
+ ### 测试
55
+
56
+ - 新增彩色帮助的回归用例:管道下必须无 ANSI;伪终端下必须有 ANSI,且剥离
57
+ 转义后与管道版本逐字一致(守住列对齐没被样式破坏);`--help` 不得导入 rich;
58
+ `-h, --help` 的说明须为中文。
59
+
5
60
  ## [1.9.0] — 2026-10-14
6
61
 
7
62
  ### 变更
@@ -9,9 +9,9 @@
9
9
  - Python 3.11 或更高版本;
10
10
  - [uv](https://docs.astral.sh/uv/);
11
11
  - Git;
12
- - Node.js 22+ 与 npm
12
+ - Chrome 或 Edge(只有真实跑 `run` / `course list` 时才用到)
13
13
 
14
- macOS 可用 `brew install git node uv`;Windows 可按 README 用 `winget` 安装。
14
+ macOS 可用 `brew install git uv`;Windows 可按 README 用 `winget` 安装。
15
15
 
16
16
  初始化并安装开发依赖:
17
17
 
@@ -54,7 +54,7 @@ uv run pyright
54
54
  - pytest;
55
55
  - 干净容器中的安装冒烟测试。
56
56
 
57
- 启动 Steel、登录 Blackboard 和真实下载不属于默认离线测试范围。修改相关逻辑时,请在本地补充可重复的手工验证步骤。
57
+ 启动浏览器、登录 Blackboard 和真实下载不属于默认离线测试范围。修改相关逻辑时,请在本地补充可重复的手工验证步骤。
58
58
 
59
59
  ## 提交习惯
60
60
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: bb-sync
3
- Version: 1.9.0
3
+ Version: 2.0.0
4
4
  Summary: 港中深(CUHK-Shenzhen)Blackboard 课件下载与课程资源自动同步:登录 ADFS SSO,增量下载课件/作业/指导、公告与待办
5
5
  Project-URL: Homepage, https://github.com/asdukw/cuhk-shenzhen-blackboard-sync
6
6
  Project-URL: Repository, https://github.com/asdukw/cuhk-shenzhen-blackboard-sync
@@ -135,7 +135,7 @@ Blackboard 上的东西散在「课程内容」「作业」「公告」几个入
135
135
 
136
136
  ## 安装
137
137
 
138
- 需要 Git、Node.js 22+(含 npm)、uv 和 Chrome 或 Edge。bb-sync 会自动探测常见安装位置。
138
+ 需要 uv 和 Chrome 或 Edge。bb-sync 会自动探测常见安装位置,Windows 自带的 Edge 直接可用。
139
139
 
140
140
  ### Windows
141
141
 
@@ -143,10 +143,11 @@ Blackboard 上的东西散在「课程内容」「作业」「公告」几个入
143
143
 
144
144
  ```powershell
145
145
  winget install --id Git.Git -e
146
- winget install --id OpenJS.NodeJS.LTS -e
147
146
  powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
148
147
  ```
149
148
 
149
+ (Git 只在「从 GitHub 安装」时需要,走 PyPI 安装可以跳过。)
150
+
150
151
  Windows 自带的 Edge 即可使用;也可以自行安装 Google Chrome。
151
152
 
152
153
  ### macOS
@@ -154,7 +155,7 @@ Windows 自带的 Edge 即可使用;也可以自行安装 Google Chrome。
154
155
  推荐先安装 [Homebrew](https://brew.sh/),再执行:
155
156
 
156
157
  ```bash
157
- brew install git node uv
158
+ brew install git uv
158
159
  brew install --cask google-chrome
159
160
  ```
160
161
 
@@ -175,7 +176,7 @@ bb-sync doctor
175
176
  uv tool install git+https://github.com/asdukw/cuhk-shenzhen-blackboard-sync
176
177
  ```
177
178
 
178
- `bb-sync doctor` 用于检查环境;刚安装时凭据或浏览器后端显示“未配置”是正常的。如果提示找不到 `bb-sync`,执行 `uv tool update-shell` 后重开终端。校外使用 Blackboard 时,可能需要先连接学校 VPN。
179
+ `bb-sync doctor` 用于检查环境;刚安装时凭据显示“未配置”、浏览器后端显示“未运行”都是正常的。如果提示找不到 `bb-sync`,执行 `uv tool update-shell` 后重开终端。校外使用 Blackboard 时,可能需要先连接学校 VPN。
179
180
 
180
181
  ## 首次使用
181
182
 
@@ -193,12 +194,16 @@ bb-sync auth login
193
194
  bb-sync run --headed
194
195
  ```
195
196
 
196
- 首次运行会自动准备浏览器后端,可能需要几分钟。保持网络连接,不要提前关闭终端。之后同步直接运行:
197
+ 这条命令会用你机器上的 Chrome(没有则用 Edge)打开一个窗口,登录一次即可。之后同步直接运行:
197
198
 
198
199
  ```bash
199
200
  bb-sync run
200
201
  ```
201
202
 
203
+ 浏览器默认在**后台无窗口**运行,**跑完自动关闭**,不会在你机器上留下进程。登录态存在 `~/.bb-sync/.browser-profile/` 下、跨次运行保留,所以不必每次登录。
204
+
205
+ 需要把窗口留在屏幕上时(手动过 MFA、或想连续跑几条命令)加 `--keep-open`,之后再 `bb-sync browser stop` 关闭。
206
+
202
207
  ## 文件位置
203
208
 
204
209
  默认下载到用户主目录下的 `courses`:
@@ -230,12 +235,15 @@ Windows 也可以使用 `bb-sync config set root "D:/University/courses"`。更
230
235
  | `bb-sync run --headed` | 显示浏览器,适合首次登录或 MFA |
231
236
  | `bb-sync run --course CSC5010` | 只同步指定课程,可重复使用 |
232
237
  | `bb-sync run --dry-run` | 预览下载和待办,不写文件 |
238
+ | `bb-sync run --keep-open` | 跑完保留后台浏览器(手动过 MFA / 连着跑多条命令) |
233
239
  | `bb-sync due` | 扫描全部课程主页的 Due / To Do 与公告测验、考试,生成 `due.md` |
234
240
  | `bb-sync due --course CSC5010` | 只整理指定课程的待办 |
235
241
  | `bb-sync course list` | 列出账号下的课程 |
236
242
  | `bb-sync auth login` / `logout` / `status` | 录入、删除、查看已保存的凭据 |
237
243
  | `bb-sync config show` | 显示配置文件内容 |
238
244
  | `bb-sync config set <键> <值>` | 修改配置;另有 `get` / `unset` / `edit` / `path` / `init` |
245
+ | `bb-sync browser status` | 查看后台浏览器是否在运行(`--keep-open` 留下的实例) |
246
+ | `bb-sync browser stop` | 关闭后台浏览器(登录态保留) |
239
247
  | `bb-sync doctor` | 检查环境是否就绪 |
240
248
  | `bb-sync --help` | 查看完整帮助 |
241
249
 
@@ -243,7 +251,7 @@ Windows 也可以使用 `bb-sync config set root "D:/University/courses"`。更
243
251
 
244
252
  - **`bb-sync` 找不到**:重开终端,或执行 `uv tool update-shell`。
245
253
  - **环境检查不通过**:按 `bb-sync doctor` 的提示安装或配置缺失项。
246
- - **第一次运行很慢**:首次需要准备浏览器后端,耐心等待。
254
+ - **想留住浏览器窗口**:默认跑完会自动关;加 `--keep-open` 才会保留,之后用 `bb-sync browser status` 看状态、`bb-sync browser stop` 关闭(不影响登录态)。
247
255
  - **登录失败或需要 MFA**:运行 `bb-sync auth status` 查看凭据,再用 `bb-sync run --headed` 重试。
248
256
  - **更换密码**:重新运行 `bb-sync auth login`。
249
257
 
@@ -84,7 +84,7 @@ Blackboard 上的东西散在「课程内容」「作业」「公告」几个入
84
84
 
85
85
  ## 安装
86
86
 
87
- 需要 Git、Node.js 22+(含 npm)、uv 和 Chrome 或 Edge。bb-sync 会自动探测常见安装位置。
87
+ 需要 uv 和 Chrome 或 Edge。bb-sync 会自动探测常见安装位置,Windows 自带的 Edge 直接可用。
88
88
 
89
89
  ### Windows
90
90
 
@@ -92,10 +92,11 @@ Blackboard 上的东西散在「课程内容」「作业」「公告」几个入
92
92
 
93
93
  ```powershell
94
94
  winget install --id Git.Git -e
95
- winget install --id OpenJS.NodeJS.LTS -e
96
95
  powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
97
96
  ```
98
97
 
98
+ (Git 只在「从 GitHub 安装」时需要,走 PyPI 安装可以跳过。)
99
+
99
100
  Windows 自带的 Edge 即可使用;也可以自行安装 Google Chrome。
100
101
 
101
102
  ### macOS
@@ -103,7 +104,7 @@ Windows 自带的 Edge 即可使用;也可以自行安装 Google Chrome。
103
104
  推荐先安装 [Homebrew](https://brew.sh/),再执行:
104
105
 
105
106
  ```bash
106
- brew install git node uv
107
+ brew install git uv
107
108
  brew install --cask google-chrome
108
109
  ```
109
110
 
@@ -124,7 +125,7 @@ bb-sync doctor
124
125
  uv tool install git+https://github.com/asdukw/cuhk-shenzhen-blackboard-sync
125
126
  ```
126
127
 
127
- `bb-sync doctor` 用于检查环境;刚安装时凭据或浏览器后端显示“未配置”是正常的。如果提示找不到 `bb-sync`,执行 `uv tool update-shell` 后重开终端。校外使用 Blackboard 时,可能需要先连接学校 VPN。
128
+ `bb-sync doctor` 用于检查环境;刚安装时凭据显示“未配置”、浏览器后端显示“未运行”都是正常的。如果提示找不到 `bb-sync`,执行 `uv tool update-shell` 后重开终端。校外使用 Blackboard 时,可能需要先连接学校 VPN。
128
129
 
129
130
  ## 首次使用
130
131
 
@@ -142,12 +143,16 @@ bb-sync auth login
142
143
  bb-sync run --headed
143
144
  ```
144
145
 
145
- 首次运行会自动准备浏览器后端,可能需要几分钟。保持网络连接,不要提前关闭终端。之后同步直接运行:
146
+ 这条命令会用你机器上的 Chrome(没有则用 Edge)打开一个窗口,登录一次即可。之后同步直接运行:
146
147
 
147
148
  ```bash
148
149
  bb-sync run
149
150
  ```
150
151
 
152
+ 浏览器默认在**后台无窗口**运行,**跑完自动关闭**,不会在你机器上留下进程。登录态存在 `~/.bb-sync/.browser-profile/` 下、跨次运行保留,所以不必每次登录。
153
+
154
+ 需要把窗口留在屏幕上时(手动过 MFA、或想连续跑几条命令)加 `--keep-open`,之后再 `bb-sync browser stop` 关闭。
155
+
151
156
  ## 文件位置
152
157
 
153
158
  默认下载到用户主目录下的 `courses`:
@@ -179,12 +184,15 @@ Windows 也可以使用 `bb-sync config set root "D:/University/courses"`。更
179
184
  | `bb-sync run --headed` | 显示浏览器,适合首次登录或 MFA |
180
185
  | `bb-sync run --course CSC5010` | 只同步指定课程,可重复使用 |
181
186
  | `bb-sync run --dry-run` | 预览下载和待办,不写文件 |
187
+ | `bb-sync run --keep-open` | 跑完保留后台浏览器(手动过 MFA / 连着跑多条命令) |
182
188
  | `bb-sync due` | 扫描全部课程主页的 Due / To Do 与公告测验、考试,生成 `due.md` |
183
189
  | `bb-sync due --course CSC5010` | 只整理指定课程的待办 |
184
190
  | `bb-sync course list` | 列出账号下的课程 |
185
191
  | `bb-sync auth login` / `logout` / `status` | 录入、删除、查看已保存的凭据 |
186
192
  | `bb-sync config show` | 显示配置文件内容 |
187
193
  | `bb-sync config set <键> <值>` | 修改配置;另有 `get` / `unset` / `edit` / `path` / `init` |
194
+ | `bb-sync browser status` | 查看后台浏览器是否在运行(`--keep-open` 留下的实例) |
195
+ | `bb-sync browser stop` | 关闭后台浏览器(登录态保留) |
188
196
  | `bb-sync doctor` | 检查环境是否就绪 |
189
197
  | `bb-sync --help` | 查看完整帮助 |
190
198
 
@@ -192,7 +200,7 @@ Windows 也可以使用 `bb-sync config set root "D:/University/courses"`。更
192
200
 
193
201
  - **`bb-sync` 找不到**:重开终端,或执行 `uv tool update-shell`。
194
202
  - **环境检查不通过**:按 `bb-sync doctor` 的提示安装或配置缺失项。
195
- - **第一次运行很慢**:首次需要准备浏览器后端,耐心等待。
203
+ - **想留住浏览器窗口**:默认跑完会自动关;加 `--keep-open` 才会保留,之后用 `bb-sync browser status` 看状态、`bb-sync browser stop` 关闭(不影响登录态)。
196
204
  - **登录失败或需要 MFA**:运行 `bb-sync auth status` 查看凭据,再用 `bb-sync run --headed` 重试。
197
205
  - **更换密码**:重新运行 `bb-sync auth login`。
198
206
 
@@ -72,7 +72,7 @@ fi
72
72
  | 4 | 配置文件问题 |
73
73
  | 5 | 网络 / 代理 / 下载失败 |
74
74
  | 6 | 目标不存在(课程、配置键等) |
75
- | 7 | 环境依赖缺失(Node / 浏览器 / Steel) |
75
+ | 7 | 环境依赖缺失(未检测到浏览器、浏览器启动失败等) |
76
76
  | 8 | 用户中断 |
77
77
 
78
78
  > 未捕获的意外异常统一归为 1,并会把堆栈打到 stderr。
@@ -85,7 +85,7 @@ case $? in
85
85
  0) echo "同步完成" ;;
86
86
  3) echo "需要先 bb-sync auth login" >&2 ;;
87
87
  5) echo "网络/代理异常,检查 Clash 是否开启" >&2 ;;
88
- 7) echo "缺 Node/浏览器,先跑 bb-sync doctor" >&2 ;;
88
+ 7) echo "缺浏览器,先跑 bb-sync doctor" >&2 ;;
89
89
  *) echo "其它错误" >&2 ;;
90
90
  esac
91
91
  ```
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "bb-sync"
7
- version = "1.9.0"
7
+ version = "2.0.0"
8
8
  description = "港中深(CUHK-Shenzhen)Blackboard 课件下载与课程资源自动同步:登录 ADFS SSO,增量下载课件/作业/指导、公告与待办"
9
9
  readme = "README.md"
10
10
  license = { file = "LICENSE" }
@@ -14,7 +14,7 @@ from playwright.sync_api import sync_playwright
14
14
 
15
15
  from bb_sync.blackboard.login import ensure_login
16
16
  from bb_sync.blackboard.models import BASE
17
- from bb_sync.browser import steel
17
+ from bb_sync.browser import launcher
18
18
  from bb_sync.core.output import Console
19
19
  from bb_sync.paths import BB_SYNC_HOME
20
20
 
@@ -23,10 +23,9 @@ COURSE_ID = sys.argv[1] if len(sys.argv) > 1 else "_18482_1" # CSC5010
23
23
 
24
24
 
25
25
  def main() -> None:
26
- steel.ensure_server(console)
27
- session = steel.create_session(console=console)
26
+ session = launcher.ensure_browser(console)
28
27
  with sync_playwright() as p:
29
- browser, ctx, page = steel.connect(p, session)
28
+ browser, ctx, page = launcher.connect(p, session)
30
29
  ensure_login(page, console)
31
30
 
32
31
  for label, url in [
@@ -72,9 +71,7 @@ def main() -> None:
72
71
  for link in interesting[:20]:
73
72
  console.log(f" {link[:150]}")
74
73
 
75
- ctx.close()
76
- browser.close()
77
- steel.release_session(session, console)
74
+ # 调试脚本故意不关浏览器(方便接着看现场);用完 bb-sync browser stop 关闭
78
75
 
79
76
 
80
77
  if __name__ == "__main__":
@@ -13,7 +13,7 @@ from playwright.sync_api import sync_playwright
13
13
 
14
14
  from bb_sync.blackboard.login import ensure_login
15
15
  from bb_sync.blackboard.models import BASE
16
- from bb_sync.browser import steel
16
+ from bb_sync.browser import launcher
17
17
  from bb_sync.core.output import Console
18
18
  from bb_sync.paths import BB_SYNC_HOME
19
19
 
@@ -22,10 +22,9 @@ PORTAL = f"{BASE}/webapps/portal/execute/tabs/tabAction?tab_tab_group_id=_1_1"
22
22
 
23
23
 
24
24
  def main() -> None:
25
- steel.ensure_server(console)
26
- session = steel.create_session(console=console)
25
+ session = launcher.ensure_browser(console)
27
26
  with sync_playwright() as p:
28
- browser, ctx, page = steel.connect(p, session)
27
+ browser, ctx, page = launcher.connect(p, session)
29
28
  ensure_login(page, console)
30
29
  console.success("已登录")
31
30
 
@@ -69,9 +68,7 @@ def main() -> None:
69
68
  for link in links:
70
69
  console.log(f" {link[:140]}")
71
70
 
72
- ctx.close()
73
- browser.close()
74
- steel.release_session(session, console)
71
+ # 调试脚本故意不关浏览器(方便接着看现场);用完 bb-sync browser stop 关闭
75
72
 
76
73
 
77
74
  if __name__ == "__main__":
@@ -12,7 +12,7 @@ import re
12
12
  from playwright.sync_api import sync_playwright
13
13
 
14
14
  from bb_sync.blackboard.models import LOGIN_URL
15
- from bb_sync.browser import steel
15
+ from bb_sync.browser import launcher
16
16
  from bb_sync.core.output import Console
17
17
  from bb_sync.creds import load_credentials
18
18
  from bb_sync.paths import BB_SYNC_HOME
@@ -45,10 +45,9 @@ def dump(page, tag: str) -> None:
45
45
 
46
46
 
47
47
  def main() -> None:
48
- steel.ensure_server(console)
49
- session = steel.create_session(console=console)
48
+ session = launcher.ensure_browser(console)
50
49
  with sync_playwright() as p:
51
- browser, ctx, page = steel.connect(p, session)
50
+ browser, ctx, page = launcher.connect(p, session)
52
51
  page.goto(LOGIN_URL, wait_until="domcontentloaded")
53
52
  dump(page, "01_bb_login")
54
53
 
@@ -70,9 +69,7 @@ def main() -> None:
70
69
 
71
70
  page.wait_for_timeout(5000)
72
71
  dump(page, "07_final")
73
- ctx.close()
74
- browser.close()
75
- steel.release_session(session, console)
72
+ # 调试脚本故意不关浏览器(方便接着看现场);用完 bb-sync browser stop 关闭
76
73
 
77
74
 
78
75
  if __name__ == "__main__":
@@ -5,7 +5,7 @@
5
5
  - :mod:`bb_sync.cli` Typer 命令行入口
6
6
  - :mod:`bb_sync.core` 配置、输出、异常等基础设施
7
7
  - :mod:`bb_sync.blackboard` Blackboard 站点交互(登录 / 抓取 / 下载)
8
- - :mod:`bb_sync.browser` Steel 浏览器后端
8
+ - :mod:`bb_sync.browser` 本机浏览器后端(分离进程 + CDP)
9
9
  """
10
10
 
11
11
  from __future__ import annotations
@@ -87,7 +87,8 @@ def do_login(page: Page, username: str, password: str) -> None:
87
87
  raise AuthError(
88
88
  f"登录失败(当前 URL: {_safe_url(page)}):{detail}",
89
89
  f"已保存登录现场到 {home}(debug_login.png / debug_login.html)。"
90
- "若页面停在 ADFS / MFA / 验证码,请用 bb-sync run --headed 手动完成;"
90
+ "若页面停在 ADFS / MFA / 验证码,请用 bb-sync run --headed --keep-open 手动完成"
91
+ "(--keep-open 让浏览器在命令结束后留在屏幕上);"
91
92
  f"若怀疑是登录流程改版或 bb-sync 的问题,欢迎到 {ISSUES_URL} 提 issue,"
92
93
  "并附上 debug_login.html(以及上面这行 URL)。",
93
94
  ) from exc
@@ -161,7 +162,7 @@ def resolve_credentials(console: Console) -> Credentials:
161
162
  def ensure_login(page: Page, console: Console, *, headed: bool = False) -> None:
162
163
  """确保处于登录态(复用 profile → 静默登录 → 有头模式等人工过 MFA)。"""
163
164
  if is_logged_in(page):
164
- console.log("[login] 已有有效登录态(复用 Steel profile)")
165
+ console.log("[login] 已有有效登录态(复用浏览器 profile)")
165
166
  return
166
167
 
167
168
  creds = resolve_credentials(console)
@@ -0,0 +1,5 @@
1
+ """浏览器后端:直接驱动本机 Chrome / Edge(分离进程 + CDP)。"""
2
+
3
+ from __future__ import annotations
4
+
5
+ __all__: list[str] = []