Skip to content

Commit 7dd12ff

Browse files
committed
feat(extension): 可安装打包 zip + Edge 安装实测与文档
- scripts/package_extension.py: extension/ → dist/AutoOffer-Extension-<版本>.zip (manifest 引用资源齐全性校验) - release 工作流把扩展 zip 挂上 Release 附件 - Edge(msedge 通道)实测 --load-extension 加载成功, SW 正常注册 - extension/README 重写至当前形态(M1 时代内容已过时); 主 README 快速上手补 Edge/zip 两种安装方式
1 parent f04d86d commit 7dd12ff

4 files changed

Lines changed: 111 additions & 27 deletions

File tree

‎.github/workflows/release.yml‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@ jobs:
5555
exit 1
5656
}
5757
58+
- name: Package browser extension
59+
shell: pwsh
60+
run: python scripts/package_extension.py
61+
5862
- name: Compute SHA256
5963
shell: pwsh
6064
run: |
@@ -70,3 +74,4 @@ jobs:
7074
files: |
7175
dist/AutoOffer-Setup.exe
7276
dist/AutoOffer-Setup.exe.sha256
77+
dist/AutoOffer-Extension-*.zip

‎README.md‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ AutoOffer 是一款基于多智能体(Multi-Agent)架构的**桌面软件**
1010

1111
| 特性 | 说明 |
1212
| --- | --- |
13-
| **浏览器插件直填(推荐)** | Chrome 加载 `extension/` 即用:站点适配器(智易/北森 Phoenix、Moka、牛客、智联 + Ant/Element 兜底)+ 本地标签评分直填(含学历下拉、自绘单选、日历/月份选择器、「至今」开关)+ 多条教育经历自动补块 + 附件注入 |
13+
| **浏览器插件直填(推荐)** | Edge/Chrome 加载 `extension/` 即用:站点适配器(智易/北森 Phoenix、Moka、牛客、智联 + Ant/Element 兜底)+ 本地标签评分直填(含学历下拉、自绘单选、日历/月份选择器、「至今」开关)+ 多条教育经历自动补块 + 附件注入 |
1414
| 规则优先、AI 兜底 | 两段式填写:第一段本地规则直填(零 LLM、零数据外发);未命中字段送 AI 标签映射(LLM 只见标签不见值),固定选项字段由 AI 选选项(级联逐层下钻) |
1515
| 通用表单泛化 | DOM 结构化感知 + 截图视觉辅助(Set-of-Marks),适配任意招聘网站,无需为单站写脚本(传统模式) |
1616
| 多智能体协作 | Planner(任务拆分)/ Actor(执行)/ Validator(校验)三角色循环(传统模式) |
@@ -58,7 +58,9 @@ AutoOffer 是一款基于多智能体(Multi-Agent)架构的**桌面软件**
5858
## 浏览器插件快速上手(推荐)
5959

6060
1. 启动本地服务:`python -m app.launcher`(健康检查 http://127.0.0.1:8765/api/v1/system/health)。
61-
2. Chrome → `chrome://extensions` → 开发者模式 → 「加载已解压的扩展程序」→ 选择仓库 `extension/` 目录。
61+
2. 安装插件(Edge / Chrome 通用,二选一):
62+
- **加载仓库目录**(开发推荐):Edge 打开 `edge://extensions`(Chrome 为 `chrome://extensions`)→ 开「开发人员模式」→「加载解压缩的扩展」→ 选择仓库 `extension/` 目录。
63+
- **安装打包 zip**:`python scripts/package_extension.py` 生成(或从 GitHub Release 附件下载)`dist/AutoOffer-Extension-<版本>.zip`,解压到固定目录后按上法加载该目录。
6264
3. 打开目标招聘表单页 → 点插件图标 → 「授权连接」→ 选档案 → 「开始填写」。只填不提交,检查无误后手动提交。
6365

6466
权限模型:`activeTab + scripting + storage`,站点源与本地服务源均在点击时按需申请;微信扫码/验证码类登录由用户人工完成。详见 [extension/README.md](extension/README.md)。

‎extension/README.md‎

Lines changed: 48 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,72 @@
1-
# AutoOffer 浏览器插件(MV3)—— 规则直填 M1
1+
# AutoOffer 浏览器插件(MV3)
22

3-
以浏览器插件为「手」、本地服务为「脑」的轻量实现:页面上能规则匹配的字段零 LLM
4-
直接写入,数据不出本机。
3+
以浏览器插件为「手」、本地服务为「脑」:页面上能规则匹配的字段零 LLM 直接写入,
4+
规则未命中的字段走本地服务的 AI 标签映射/选选项通道,档案值不出本机。
55

66
## 架构
77

88
```
9-
popup(选档案/授权) ──▶ background(SW)
10-
│ fetch http://127.0.0.1:8765
11-
│ GET /api/v1/profiles/{id}/flat[?sensitive=true]
12-
▼
13-
content.js(规则直填引擎)
14-
站点适配器(智易/Moka/北森/牛客/智联 + 框架兜底)
15-
标签扫描 → 本地评分(textMatchScore) + 硬否决
16-
(上传类 / 家庭域 / 值形冲突)
17-
→ setNativeValue 原生注入(React 兼容)
18-
→ 自定义下拉三段降级(点开 → portal 选项匹配 → 搜索框注入重试)
9+
popup(选档案/授权敏感字段) ──▶ background(SW) 两段式编排
10+
│ fetch http://127.0.0.1:8765
11+
│ GET /api/v1/profiles/{id}/flat[?sensitive=true]
12+
│ GET /api/v1/profiles/{id}/attachments/{idx}
13+
│ POST /api/v1/mapping (只见标签不见值)
14+
│ POST /api/v1/option-match (AI 选选项,≤3轮级联)
15+
▼
16+
content.js(规则直填引擎)
17+
站点适配器(智易/北森 Phoenix/Moka/牛客/智联 + Ant/Element 兜底)
18+
标签扫描 → 本地评分(textMatchScore) + 硬否决
19+
(上传类 / 家庭域 / 值形冲突)
20+
→ setNativeValue 原生注入(React 兼容)
21+
→ 自定义下拉(弹层叶子兜底 + 搜索框重试 + 选项收割)
22+
→ Phoenix 自绘单选(内部节点指针序列 + 选中态核验)
23+
→ 日历/月份选择器、「至今」开关(幂等)
24+
→ 多条教育经历自动补块(occurrence/itemIndex 配对)
25+
→ 附件 File+DataTransfer 注入
1926
```
2027

2128
- 评分与注入设计参考 OpenJobAutofill(MIT),源码归档于 `reference/OpenJobAutofill/`。
2229
- 敏感字段(身份证号、家庭情况等)默认不输出,弹窗勾选授权后才随 `sensitive=true` 下发。
2330
- 值形/标签语义冲突硬否决来自 Playwright 路线的实战修复(手机号形状的值禁止写入身高/日期类标签)。
31+
- AI 映射只送字段标签与档案键名,不送档案值;选选项通道送单个值到你自己配置的 LLM(与简历解析同一信任域)。
2432

25-
## 安装(开发者模式)
33+
## 安装到 Edge / Chrome
2634

27-
1. 启动本地服务:仓库根目录 `.venv/Scripts/python.exe -m app.launcher`
28-
(健康检查 http://127.0.0.1:8765/api/health)。
29-
2. Chrome 打开 `chrome://extensions` → 开启「开发者模式」→「加载已解压的扩展程序」→ 选择本目录。
30-
3. 打开目标招聘表单页 → 点击插件图标 → 「授权连接」本地服务 → 选择档案 → 「开始填写」。
35+
前提:本地服务已启动(`.venv/Scripts/python.exe -m app.launcher`,
36+
健康检查 http://127.0.0.1:8765/api/health)。
37+
38+
**方式一:加载解压缩目录(开发推荐,改动即生效)**
39+
40+
1. Edge 打开 `edge://extensions`(Chrome 为 `chrome://extensions`)。
41+
2. 打开左侧「开发人员模式」/「开发者模式」开关。
42+
3. 点「加载解压缩的扩展」/「加载已解压的扩展程序」,选择本仓库的 `extension/` 目录。
43+
44+
**方式二:安装打包 zip(`dist/AutoOffer-Extension-<版本>.zip`)**
45+
46+
1. `python scripts/package_extension.py` 生成 zip(或从 GitHub Release 下载附件)。
47+
2. 解压 zip 到任意固定目录(加载后目录不能删/移动)。
48+
3. 同方式一,加载解压缩的扩展时选中**解压出的目录**。
49+
50+
**使用**:打开目标招聘表单页 → 点插件图标 → 首次「授权连接」本地服务(一并申请站点源与
51+
API 源)→ 选择档案 → 按需勾选「包含敏感字段」→「开始填写」→ 查看填写报告(AI 映射/附件
52+
补填的字段带标注)。
3153

3254
权限模型:`activeTab + scripting + storage`,站点源与本地服务源均在点击时按需申请
3355
(`optional_host_permissions`),不常驻任何站点访问权。
3456

35-
## M1 已知边界
57+
## 已知边界
3658

37-
- repeat 档案区块(教育/实习/工作/项目经历)只匹配**第一条**;多条目「添加再填写」属 M3。
38-
- 附件上传字段一律跳过并提示手动上传(M3 处理)。
39-
- 无 AI 字段映射通道:规则分低于阈值的字段计入「跳过」(M2 在本地服务上加 `/api/v1/mapping`)。
40-
- 不触碰验证码/登录;自动提交不在插件范围内(沿用本地服务 auto_submit 设置语义,M3 接入)。
59+
- 两 pane 虚拟级联的职业大类(如北森「期望从事职业」46 类目)在自动化下仅部分可见,
60+
AI 会拒选而不是乱选,该类字段留人工。
61+
- 不触碰验证码/登录(微信扫码等由人工完成);不自动提交(沿用本地服务 auto_submit 设置)。
62+
- 招聘站普遍拒收 .md 附件,档案简历附件请用 PDF/Word
63+
(`python scripts/md_resume_to_pdf.py` 可把 .md 转 PDF 并更新档案)。
4164

4265
## 测试
4366

4467
```bash
45-
# 引擎集成测试(真实 Chromium + fixture 基准页注入 content.js)
4668
.venv/Scripts/python.exe -m pytest tests/integration/extension -q
47-
# 扁平档案接口
4869
.venv/Scripts/python.exe -m pytest tests/integration/server/test_profile_flat.py -q
4970
```
71+
72+
真实 Chromium 注入 `src/content.js` 跑本地基准页(Ant/Element/北森 Phoenix 单选/多块/附件上传)。

‎scripts/package_extension.py‎

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
"""把 extension/ 打包为可直接安装的浏览器扩展 zip(Edge/Chrome 通用)。
2+
3+
用法: python scripts/package_extension.py
4+
产物: dist/AutoOffer-Extension-<版本>.zip
5+
安装: edge://extensions 打开开发人员模式 → 解压 zip → 「加载解压缩的扩展」选中目录。
6+
"""
7+
8+
from __future__ import annotations
9+
10+
import json
11+
import sys
12+
import zipfile
13+
from pathlib import Path
14+
15+
ROOT = Path(__file__).resolve().parents[1]
16+
EXT = ROOT / "extension"
17+
DIST = ROOT / "dist"
18+
19+
20+
def main() -> int:
21+
for _stream in (sys.stdout, sys.stderr):
22+
if _stream.encoding and _stream.encoding.lower() not in ("utf-8", "utf8"):
23+
_stream.reconfigure(encoding="utf-8", errors="replace")
24+
25+
manifest = json.loads((EXT / "manifest.json").read_text(encoding="utf-8"))
26+
version = manifest["version"]
27+
28+
files = sorted(
29+
p for p in EXT.rglob("*") if p.is_file() and "__pycache__" not in p.parts
30+
)
31+
# manifest 引用的资源必须齐全
32+
referenced: list[str] = [
33+
manifest["action"]["default_popup"],
34+
manifest["background"]["service_worker"],
35+
]
36+
referenced += list(manifest["action"]["default_icon"].values())
37+
referenced += list(manifest["icons"].values())
38+
names = {str(p.relative_to(EXT)).replace("\\", "/") for p in files}
39+
missing = [r for r in referenced if r not in names]
40+
if missing:
41+
print(f"manifest 引用的文件缺失: {missing}")
42+
return 1
43+
44+
DIST.mkdir(parents=True, exist_ok=True)
45+
out = DIST / f"AutoOffer-Extension-{version}.zip"
46+
with zipfile.ZipFile(out, "w", zipfile.ZIP_DEFLATED) as zf:
47+
for p in files:
48+
zf.write(p, p.relative_to(EXT))
49+
print(f"已打包: {out} ({out.stat().st_size // 1024} KB, {len(files)} 个文件)")
50+
return 0
51+
52+
53+
if __name__ == "__main__":
54+
raise SystemExit(main())

0 commit comments

Comments
 (0)