feat: purge legacy agent hosts and land vault Chrome extension path
Retire ACP/Hermes/OpenCode surfaces and rename hermes_tools to mnote_agent_tools so Page AI stays on Pi Lab only. Add Chrome vault extension + extension token route, pre-release purge design, and soft-retire legacy smokes for the small-group production cut.
This commit is contained in:
@@ -0,0 +1,929 @@
|
||||
# 12-3 [process] Chrome 扩展:站点登录/注册页保存到密码箱 v1
|
||||
|
||||
> 创建时间:2026-07-24
|
||||
> 状态:`PROCESS`
|
||||
> Owner:`12-vault`(扩展客户端 + mnote-web 鉴权/跨域适配;**不**改 tree kernel / Page Aggregate)
|
||||
> 建议 repo 落点:`design/12-vault/process/12-3-chrome-extension-vault-save-v1.md`
|
||||
>
|
||||
> 上位依据:
|
||||
> - `design/12-vault/process/12-1-password-vault-dedicated-crud-workbench-v1.md`(vault 真源、CRUD API、L0/L1、同站多账号)
|
||||
> - `design/12-vault/process/12-2-vaultd-local-token-agent-read-path-v1.md`(agent 读密通道;**本设计不复用 agent token 做写入口**)
|
||||
> - `/home/lix/.agent-infra/vault-policy.md` + `$mnote-vault`(AI 读密策略;扩展默认写**人用 vault**)
|
||||
> - `ARCHITECTURE.md` local-first;`AGENTS.md` 密码箱薄指针
|
||||
|
||||
---
|
||||
|
||||
## 1. Overview
|
||||
|
||||
### 1.1 一句话
|
||||
|
||||
> **Chrome MV3 扩展在用户完成 MNote 远程登录后,于外站注册/登录页弹出「保存到密码箱」;默认同时保存账号密码与浏览器登录态(Cookie);登录态以「每账号独立文件」落盘;Agent 经 `$mnote-vault` 的 `login` / `session` / `resolve --field session` 复用,与 `/vault` 同一真源。**
|
||||
|
||||
### 1.2 痛点
|
||||
|
||||
| 现状 | 问题 |
|
||||
|------|------|
|
||||
| 录入只靠 `/vault` 手填 | 跨站复制账号密码,摩擦高、易漏改 |
|
||||
| 12-1 Non-Goals 写明「不做 OS 级自动填表扩展」 | 产品一期聚焦工作台合理;**录入侧**仍缺浏览器入口 |
|
||||
| 12-2 agent token 只服务读密 | **不能**把 agent capability token 交给扩展当写凭证 |
|
||||
|
||||
### 1.3 与 12-1 / 12-2 的关系
|
||||
|
||||
| 维度 | 12-1 | 12-2 | **12-3(本文)** |
|
||||
|------|------|------|------------------|
|
||||
| 真源 | `{workspace}/.mnote/vault/**` | 不变 | **不变** |
|
||||
| 人用写入口 | `/vault` + `/api/vault/*` | 旁路 | **+ 浏览器扩展**(同 API 语义) |
|
||||
| Agent 读密 / 登录态 | HTTP(演进中) | vaultd + capability token;`login`/`session` 已有 | **不经扩展 resolve**;扩展只 **写入** session 文件;Agent 仍走 12-2 CLI |
|
||||
| 登录态存储 | frontmatter 内嵌 `loginSession`(现状) | 同左(AI 本) | **演进为每账号独立文件**(§5.6);兼容读 frontmatter |
|
||||
| 鉴权主体 | 浏览器 session cookie | agent token | **人会话 / 扩展专用 human token** |
|
||||
| 自动填表 | 非目标 | — | **P0 不做**;P2 可选 |
|
||||
|
||||
### 1.4 硬门闩(产品)
|
||||
|
||||
扩展在以下任一不满足时,**禁止**弹出保存、禁止自动提交 vault 写请求:
|
||||
|
||||
1. 已配置 **远程 MNote 基址**(`baseUrl`,如 `http://127.0.0.1:3000`)
|
||||
2. 已用 **MNote 账号/密码** 完成登录,且会话未过期
|
||||
3. 已选定可写 **workspace `rootUri`**(local_folder 的 `file://…`,与工作台一致)
|
||||
|
||||
未登录态 UI 只允许:Options 配置 +「连接密码箱」;工具栏徽章显示「未连接」。
|
||||
|
||||
---
|
||||
|
||||
## 2. Goals / Non-Goals
|
||||
|
||||
### 2.1 Goals
|
||||
|
||||
1. **MVP 保存闭环**:外站检测到 password 表单 → 用户确认 → 写入当前用户 vault(create 或更新同站条目)。
|
||||
2. **默认保存登录态**:确认保存时 **默认勾选**「同时保存登录态」;扩展用 `chrome.cookies` 抓取目标 origin 的 Cookie,写入 **每账号独立 session 文件**(§5.6)。
|
||||
3. **Options 引导**:远程地址 + 账号密码登录 + 选择 workspace(rootUri)+ 连接状态展示。
|
||||
4. **与 `/vault` 同真源**:`POST /api/vault/items`、`PATCH /api/vault/items/{id}`、`PUT …/session`、`GET /api/vault/list`;字段对齐 `VaultCreateInput` + session file schema。
|
||||
5. **安全默认**:密钥与 Cookie 只经 extension **background** 出站;content script **不**持长期 token / cookie 明文;审计无 value / cookieHeader。
|
||||
6. **可选同步 AI 本**:保存后二次确认才 `share-to-ai`;勾选时 **连同 session 文件** 复制到 AI 本(否则 Agent 无法 `login` 复用)。
|
||||
7. **Skill / Agent 可调用**:`$mnote-vault` + vault-policy 明确 `login` / `session` / `resolve --field session` 稳态(§6.5)。
|
||||
8. **可安装、可调试**:Manifest V3;本地 unpacked 加载;文档化 host_permissions(含 cookies)。
|
||||
|
||||
### 2.2 Non-Goals(本设计不承诺 / 明确不做)
|
||||
|
||||
| 项 | 说明 |
|
||||
|----|------|
|
||||
| P0 自动填充 | 不做 Bitwarden 式 autofill;仅「保存」 |
|
||||
| Agent capability token 复用 | 扩展 **禁止** 使用 `mnote-vault` agent token / vaultd UDS 写库 |
|
||||
| 改 kernel / tree.* | vault 仍非图节点 |
|
||||
| 云端跨机 vault 同步 | 仍 local-first;远程只是 mnote-web 可达地址 |
|
||||
| 完整密码管理器对标 | 无 TOTP 引擎、无浏览器原生 Password API 劫持(P2 再评估) |
|
||||
| Firefox/Safari | P0 只 Chrome/Chromium MV3;API 预留可移植 |
|
||||
| 在 content script 内直接 fetch vault API | 禁止(token 面扩大) |
|
||||
| 默认把所有保存项 share-to-ai | 禁止静默 |
|
||||
|
||||
### 2.3 成功标准(设计批准后实现门闩)
|
||||
|
||||
- [x] Options:填 baseUrl + 登录成功 → whoami 显示用户(扩展 CONNECT + API smoke 签发 mnext1)
|
||||
- [x] 未登录时打开任意站 password 表单 → **无**可保存(弹层提示去连接且禁用保存)
|
||||
- [ ] 登录后在示例站提交登录表单 → 弹层预填 url/username/password → 确认 → `/vault` 列表可见新条目(**Chrome 手工**)
|
||||
- [x] **默认勾选「同时保存登录态」** → API smoke 确认磁盘 `sessions/{credId}/{accountId}.json`
|
||||
- [x] 同 origin 已有条目 → 可选「追加账号」写入 `accounts[]`;**新账号自有 session 文件**(扩展 UI)
|
||||
- [ ] share-to-ai 勾选时 AI 本同时有 session 文件;Agent `mnote-vault login --id` 可 `mode=session` 复用(**Chrome 手工**)
|
||||
- [x] `$mnote-vault` skill 写明登录态调用(`login` / `session`);vault-policy 待同步
|
||||
- [x] audit 有 `session_put`;L0 **无** password / cookieHeader 明文(smoke 断言)
|
||||
- [x] 错误 baseUrl / 401 → 明确文案;token 吊销后 401(smoke)
|
||||
|
||||
---
|
||||
|
||||
## 3. 问题与方案选型
|
||||
|
||||
### 3.1 写入通道
|
||||
|
||||
| 方案 | 优点 | 缺点 | 结论 |
|
||||
|------|------|------|------|
|
||||
| A. 扩展 → mnote-web `/api/vault/*`(人会话) | 与工作台一致;已有 create/update | 需跨域鉴权 | **采用** |
|
||||
| B. 扩展 → Native Messaging → 本机写文件 | 不依赖 3000 | 无「远程地址」;安装重;与 local-first 桌面强绑定 | P2 备选「纯本机」 |
|
||||
| C. 扩展 → vaultd agent token | 不启 web | token 设计给 agent 读;无完整 human CRUD 策略 | **禁止** |
|
||||
|
||||
### 3.2 鉴权形态
|
||||
|
||||
| 方案 | 说明 | 推荐阶段 |
|
||||
|------|------|----------|
|
||||
| **E1. Session cookie 跨域** | `POST /api/auth` 后带 `mnote_session`;扩展 fetch `credentials: 'include'` | 本机 localhost 可试;跨站 SameSite 脆弱 |
|
||||
| **E2. Extension human token(推荐)** | 登录成功后服务端签发 **仅 vault.view + vault.edit** 的 Bearer;扩展存 `chrome.storage.session` | **MVP 主路径** |
|
||||
| **E3. 复用 `/api/auth/mnote-web-token`** | 若现网已有可给 API 的 token | 实现时审计 scope;不足则走 E2 |
|
||||
|
||||
**推荐默认(钉死):E2。**
|
||||
Cookie 路径(E1)可作为同机 dev 旁路,**不得**作为唯一生产假设。
|
||||
|
||||
E2 token 逻辑声明(与 12-2 agent token **分 aud/scope**,禁止混用):
|
||||
|
||||
```json
|
||||
{
|
||||
"v": 1,
|
||||
"iss": "mnote-web",
|
||||
"aud": "chrome-extension-vault",
|
||||
"sub": "user:<userId>",
|
||||
"scope": ["vault.view", "vault.edit"],
|
||||
"rootUriAllow": ["file:///…可选绑定…"],
|
||||
"iat": 0,
|
||||
"exp": 0,
|
||||
"jti": "uuid"
|
||||
}
|
||||
```
|
||||
|
||||
- **禁止** scope 含 `vault.resolve` / AI resolve。
|
||||
- **禁止** 与 `mnv1.*` agent token 同密钥、同 aud。
|
||||
- TTL 默认 **7d**(可 Options 续期 / 重新登录);revoke 按 jti。
|
||||
- 传输:`Authorization: Bearer <token>`。
|
||||
|
||||
### 3.3 远程地址与 rootUri
|
||||
|
||||
```text
|
||||
Options 配置:
|
||||
baseUrl = https://mnote.example 或 http://127.0.0.1:3000
|
||||
account = 用户账号/邮箱
|
||||
password = 仅登录瞬间使用;成功后**不**长期明文落盘(见 §7)
|
||||
rootUri = (可选)file://… 覆盖;默认自动解析
|
||||
sourceKind = local_folder(固定)
|
||||
```
|
||||
|
||||
- `baseUrl` 规范化:去尾 `/`;仅 `http:`/`https:`;禁止 `javascript:` 等。
|
||||
- **首次连接**后把 baseUrl 写入 `chrome.storage.sync`(可同步)或 `local`;token 只进 **session** storage。
|
||||
- **默认 rootUri(钉死)**:登录成功后 `POST /api/local-folder/workspaces/default` → 账号固定
|
||||
`…/users/<actor>/workspaces/my-space` 的 `file://` rootUri(与 web 壳默认 local_folder 一致)。
|
||||
Options **不必手填**;「高级」仅用于写到其它已授权 local_folder。
|
||||
- 多 workspace / 自定义目录:仍可覆盖 rootUri(P1 可做下拉列表)。
|
||||
|
||||
---
|
||||
|
||||
## 4. 目标架构
|
||||
|
||||
### 4.1 组件图
|
||||
|
||||
```text
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ 外站页面(任意 origin) │
|
||||
│ content script:侦测 password 字段 / 提交;发消息给 background │
|
||||
│ 不持 token;不直接打 MNote API │
|
||||
└────────────────────────────┬────────────────────────────────┘
|
||||
│ chrome.runtime.sendMessage
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ Extension Service Worker(background) │
|
||||
│ - 持 baseUrl + human token + rootUri │
|
||||
│ - 保存弹层协调 / 工具栏 badge │
|
||||
│ - fetch(`${baseUrl}/api/vault/...`) │
|
||||
│ - host_permissions: 用户配置的 baseUrl(optional 动态) │
|
||||
└────────────────────────────┬────────────────────────────────┘
|
||||
│ HTTPS + Bearer
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ mnote-web │
|
||||
│ POST /api/auth → 登录 │
|
||||
│ POST /api/vault/extension/token → 签发 E2(新) │
|
||||
│ GET /api/auth/whoami → 校验会话/token │
|
||||
│ GET /api/vault/list → 同站匹配 │
|
||||
│ POST /api/vault/items → create │
|
||||
│ PATCH /api/vault/items/{id} → update / 追加账号 │
|
||||
│ PUT /api/vault/items/{id}/session → 写每账号 session 文件 │
|
||||
│ POST /api/vault/items/{id}/share-to-ai → 可选(含 session)│
|
||||
└────────────────────────────┬────────────────────────────────┘
|
||||
│ 文件写
|
||||
▼
|
||||
{workspace}/.mnote/vault/
|
||||
entries/{credId}.md # 账号密码真源
|
||||
sessions/{credId}/{acc}.json # 每账号登录态(§5.6)
|
||||
```
|
||||
|
||||
### 4.2 仓库落点(建议)
|
||||
|
||||
| 路径 | 职责 |
|
||||
|------|------|
|
||||
| `extensions/mnote-vault/`(新建) | MV3 扩展源码:manifest、background、content、options、popup |
|
||||
| `rust/crates/mnote-web/src/routes/vault_extension.rs`(或并入 `vault.rs`) | extension token 签发/吊销;可选 match-by-url 查询 |
|
||||
| `browser/vault-workbench-runtime.js` | 可选:「复制连接信息到扩展」 |
|
||||
| `design/12-vault/process/12-3-…` | 本文 |
|
||||
| 文档 | `scripts/TESTING_REFERENCE.md` 增补 extension smoke |
|
||||
|
||||
**不**放入 `recycle/`;**不**改 `core-domain` 图模型。
|
||||
|
||||
### 4.3 Manifest V3 最小权限
|
||||
|
||||
```json
|
||||
{
|
||||
"manifest_version": 3,
|
||||
"name": "MNote Vault",
|
||||
"permissions": ["storage", "activeTab", "scripting", "alarms", "cookies"],
|
||||
"optional_host_permissions": ["http://*/*", "https://*/*"],
|
||||
"host_permissions": [],
|
||||
"background": { "service_worker": "background.js", "type": "module" },
|
||||
"action": { "default_popup": "popup.html" },
|
||||
"options_page": "options.html",
|
||||
"content_scripts": [{
|
||||
"matches": ["http://*/*", "https://*/*"],
|
||||
"js": ["content.js"],
|
||||
"run_at": "document_idle",
|
||||
"all_frames": false
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
说明:
|
||||
|
||||
- **`cookies` 权限**:默认保存登录态时,background 用 `chrome.cookies.getAll({ url })` 抓目标 origin Cookie(**禁止** content script 读 cookie)。
|
||||
- **optional_host_permissions**:用户在 Options 确认 baseUrl 后 `chrome.permissions.request` 只放行该 origin;目标站默认靠 content_scripts matches(仅读 DOM,不出站)。保存登录态时,对目标站 origin 也需 host 权限(`cookies` API 要求)。
|
||||
- 若政策要求更紧:content_scripts 改为 `activeTab` + 用户点工具栏再注入(牺牲「自动侦测」)。**P0 推荐:全站 content script + 保存必须用户点确认**(非静默上传)。
|
||||
|
||||
---
|
||||
|
||||
## 5. 用户流程
|
||||
|
||||
### 5.1 首次连接(Options)
|
||||
|
||||
```text
|
||||
1. 打开扩展 Options
|
||||
2. 输入 baseUrl →「测试连接」→ GET {baseUrl}/api/health 或 /api/auth/whoami(未登录可 401 但仍可达)
|
||||
3. 输入 MNote 账号/密码 →「登录」
|
||||
4. 扩展 background:
|
||||
POST {baseUrl}/api/auth { provider password, flow signIn, … }
|
||||
成功后 POST {baseUrl}/api/vault/extension/token (Cookie 或临时会话)
|
||||
或 auth 响应内嵌 extensionToken(实现可选合并)
|
||||
5. 选择/粘贴 rootUri →「保存配置」
|
||||
6. Badge:已连接 · 用户 email 缩写
|
||||
```
|
||||
|
||||
密码处理:
|
||||
|
||||
- Options 表单内存持有至登录成功;
|
||||
- **成功后立即清除** DOM 与内存中的 password;
|
||||
- **禁止** `chrome.storage` 持久化 MNote 登录密码;
|
||||
- 仅持久化:`baseUrl`、`rootUri`、`userId`/`email`(非敏感)、token 放 **session**(浏览器重启需重新登录或 refresh)。
|
||||
|
||||
### 5.2 外站保存(MVP)
|
||||
|
||||
```text
|
||||
触发(满足其一即可进入「候选」状态):
|
||||
T1. 页面存在 input[type=password],且用户焦点离开该字段 / 输入长度≥阈值
|
||||
T2. form submit 且 form 内含 password
|
||||
T3. 用户点击扩展工具栏「保存当前页账号」
|
||||
|
||||
门闩:
|
||||
未连接 → 仅 toast/小条:「打开 MNote Vault 扩展并登录」
|
||||
已连接 → 弹出确认层(扩展 page 或 iframe 隔离 UI,见 §5.4)
|
||||
|
||||
确认层字段:
|
||||
- title(默认:document.title 截断 80 或 hostname)
|
||||
- url(默认:location.origin + pathname 的 login 路径;可编辑)
|
||||
- username / email(从 autocomplete=username / email / name=user 启发式)
|
||||
- password(来自 password input;可显示切换)
|
||||
- 匹配结果:若 list 命中同站 → 「新建条目」|「追加到已有 · title」
|
||||
- **「同时保存登录态」checkbox:默认勾选**(用户可取消)
|
||||
- 可选 checkbox:同步到 AI 密码本(默认关;勾选时连同 session 文件)
|
||||
- 按钮:保存 / 取消
|
||||
|
||||
确认后 background:
|
||||
1) create 或 update(账号密码)
|
||||
2) 若「同时保存登录态」仍勾选:
|
||||
chrome.cookies.getAll({ url: pageUrl 或 origin })
|
||||
→ 拼 cookieHeader
|
||||
→ PUT /api/vault/items/{id}/session
|
||||
{ rootUri, accountId, cookieHeader, source: "chrome_extension" }
|
||||
3) 若勾选 share-to-ai → POST share-to-ai(服务端复制 credential + session 文件)
|
||||
4) toast 成功/失败(session 失败不回滚 credential,但提示「账号已存、登录态未写入」)
|
||||
```
|
||||
|
||||
### 5.3 同站匹配规则(P0)
|
||||
|
||||
服务端或客户端匹配(P0 可客户端 list 后过滤,条目量小时足够):
|
||||
|
||||
1. 取候选 URL 的 **registrable origin**(`https://github.com`)与 path 前缀(可选)。
|
||||
2. 对 list 中每条 credential 的 `url` + `urls[]`:
|
||||
- origin 相等 → 候选;
|
||||
- 多条 → 按 `updatedAt` 降序展示前 N=5。
|
||||
3. 用户选「追加到已有」:
|
||||
- `PATCH` 合并 `accounts[]` 新 slot(生成 `acc_*` id);
|
||||
- 顶层 `username`/`password` 是否更新:
|
||||
- **默认**:仅追加 slot,**不**改 primary 镜像(避免踩掉主账号);
|
||||
- 勾选「设为主账号」时再写 primary。
|
||||
|
||||
实现可新增只读辅助(P1):
|
||||
|
||||
```text
|
||||
GET /api/vault/match?rootUri=&url=https://example.com/login
|
||||
→ { items: [ L0 投影… ] }
|
||||
```
|
||||
|
||||
P0 无此 API 时用 `GET /api/vault/list` + 客户端过滤。
|
||||
|
||||
### 5.4 确认 UI 隔离
|
||||
|
||||
| 方式 | 说明 | 推荐 |
|
||||
|------|------|------|
|
||||
| Content script 注入 shadow DOM | 实现快;样式污染风险;密码进页面 JS 上下文 | MVP 可接受若确认层只读自 form |
|
||||
| `chrome.action` popup 预填 | 需用户点图标;最安全 | 与 T3 共用 |
|
||||
| 独立 extension 页 `chrome.windows.create` | 重 | 备选 |
|
||||
|
||||
**P0:shadow DOM 确认层 + 工具栏手动入口双轨。**
|
||||
密码值从 form 读入后立即通过 message 传 background,确认层可用 `type=password` 掩码展示;取消则丢弃。
|
||||
|
||||
### 5.5 注册页 vs 登录页
|
||||
|
||||
不强制区分 DOM:同一保存路径。
|
||||
启发式:URL/path 含 `signup|register|sign-up` 时 title 默认加「注册」前缀;不改变 schema。
|
||||
|
||||
### 5.6 每账号独立登录态文件(核心演进)
|
||||
|
||||
#### 5.6.1 为何不继续只靠 frontmatter
|
||||
|
||||
| 现状(12-1 / 12-2 已落地) | 问题 |
|
||||
|---------------------------|------|
|
||||
| `loginSession` 嵌在 `entries/{credId}.md` frontmatter | **整条 credential 只有一份** session;同站多账号互相覆盖 |
|
||||
| Cookie 与密码同文件 | 改 session 会 bump credential revision;审计/diff 面大 |
|
||||
| 扩展默认抓 cookie 后无处按账号落盘 | 无法满足「每个账号各自登录态」 |
|
||||
|
||||
**钉死:登录态 = 独立文件,按 credential + account 一文件。**
|
||||
frontmatter 内 `loginSession` **仅兼容读**(迁移期);新写入一律走 session 文件。
|
||||
|
||||
#### 5.6.2 磁盘布局
|
||||
|
||||
```text
|
||||
{workspace}/.mnote/vault/
|
||||
entries/
|
||||
{credId}.md # 账号/密码/accounts[] 真源(无 cookie 明文)
|
||||
sessions/
|
||||
{credId}/
|
||||
primary.json # 无 accounts[] 或主账号(accountId 省略 / "primary")
|
||||
{accountId}.json # 对应 accounts[].id,如 acc_01H…
|
||||
attachments/… # 既有附件(大 cookie 导出仍可走附件,非默认)
|
||||
audit.jsonl
|
||||
index.json
|
||||
```
|
||||
|
||||
- 路径一律相对 vault root;禁止 `..`。
|
||||
- 文件权限:与 vault 一致(建议 `0600` 文件 / `0700` 目录)。
|
||||
- **删除 credential** → 同步删除 `sessions/{credId}/` 整目录。
|
||||
- **删除 account slot** → 删除对应 `{accountId}.json`;`primary` 若被新主账号替换则按 merge 规则重命名/覆盖。
|
||||
- **share-to-ai** → 复制 `entries` 到 AI 本时 **一并复制** `sessions/{credId}/**` 到 AI vault 同相对路径;否则 Agent 无法 `login` 复用。
|
||||
|
||||
#### 5.6.3 Session 文件 schema
|
||||
|
||||
```json
|
||||
{
|
||||
"schema": "mnote.vault.session.v1",
|
||||
"credentialId": "cred_01H…",
|
||||
"accountId": "acc_01H…",
|
||||
"cookieHeader": "session=…; other=…",
|
||||
"cookies": [
|
||||
{
|
||||
"name": "session",
|
||||
"value": "…",
|
||||
"domain": ".example.com",
|
||||
"path": "/",
|
||||
"secure": true,
|
||||
"httpOnly": true,
|
||||
"sameSite": "lax",
|
||||
"expirationDate": 1735689600
|
||||
}
|
||||
],
|
||||
"origin": "https://example.com",
|
||||
"expiresAt": "2026-07-31T00:00:00Z",
|
||||
"lastLoginAt": "2026-07-24T12:00:00Z",
|
||||
"source": "chrome_extension",
|
||||
"updatedAt": "2026-07-24T12:00:00Z",
|
||||
"revision": 1
|
||||
}
|
||||
```
|
||||
|
||||
| 字段 | 必填 | 说明 |
|
||||
|------|------|------|
|
||||
| `schema` | 是 | 固定 `mnote.vault.session.v1` |
|
||||
| `credentialId` / `accountId` | 是 | 与路径一致;`accountId` 无多账号时用 `"primary"` |
|
||||
| `cookieHeader` | 推荐 | 拼好的 `Cookie` 请求头(Agent `login` 主消费) |
|
||||
| `cookies[]` | 推荐(扩展写) | 结构化;便于工作台展示/过期判断;与 cookieHeader 同步 |
|
||||
| `origin` | 推荐 | 抓取时的 origin |
|
||||
| `expiresAt` | 推荐 | 会话逻辑过期(默认 now+7d,`MNOTE_VAULT_SESSION_TTL_HOURS`) |
|
||||
| `source` | 是 | `chrome_extension` \| `api` \| `human_bridge` \| `browser` \| `login_api` |
|
||||
| `revision` | 是 | 乐观并发 |
|
||||
|
||||
**L0 投影**:list/get 只暴露 `hasLoginSession` / `sessionExpiresAt` / 可选 `sessionAccountIds[]`;**永不**返回 `cookieHeader` / `cookies[].value`。
|
||||
|
||||
#### 5.6.4 Core 读路径(兼容)
|
||||
|
||||
`mnote-vault-core` / `login` / `put_ai_vault_session` 演进顺序:
|
||||
|
||||
1. **写**:始终写 `sessions/{credId}/{accountId}.json`;可选同步清掉 frontmatter `loginSession`(或保留镜像一期,二期去掉)。
|
||||
2. **读(login 复用)**:
|
||||
a. 若指定 `--account-id` → 只读该文件;
|
||||
b. 否则读 `primary.json`,否则 `accounts[0].id.json`;
|
||||
c. **fallback**:frontmatter `loginSession`(旧数据)。
|
||||
3. **新鲜度**:同现网 `login_session_is_fresh`(`expiresAt` + TTL)。
|
||||
|
||||
#### 5.6.5 扩展抓 Cookie 规则
|
||||
|
||||
```text
|
||||
url = 确认层 url 或 tab.url 的 origin(至少 scheme+host+port)
|
||||
chrome.cookies.getAll({ url })
|
||||
→ 过滤:
|
||||
- 排除超大 value(单 cookie > 8KiB 截断并记 note)
|
||||
- 可选排除已知 tracking 名(P1 列表);P0 全量 origin cookies
|
||||
→ cookieHeader = cookies.map(c => `${c.name}=${c.value}`).join('; ')
|
||||
→ cookies[] 保留 domain/path/secure/httpOnly/sameSite/expirationDate
|
||||
```
|
||||
|
||||
- 仅 background 持有 cookie 明文;确认层 **不**展示完整 cookie 列表(可显示「已捕获 N 条 Cookie」)。
|
||||
- 用户取消「同时保存登录态」→ 跳过 PUT session。
|
||||
- 登录**前**表单提交瞬间可能尚无 session cookie:
|
||||
- P0:仍保存当时 cookies(可能为空或仅 CSRF);toast 可提示「若刚登录,建议登录成功后再点工具栏『更新登录态』」。
|
||||
- P1:`webNavigation` / 登录成功 URL 启发式后再二次捕获(另 checklist)。
|
||||
|
||||
#### 5.6.6 与 attachments 的关系
|
||||
|
||||
- 12-1 附件 `kind: cookies` **保留**为大文件/导出旁路。
|
||||
- **默认路径是 session 文件**,不是附件。
|
||||
- 禁止把 cookieHeader 再写进 notes_markdown。
|
||||
|
||||
---
|
||||
|
||||
## 6. API 合同
|
||||
|
||||
### 6.1 复用(已存在)
|
||||
|
||||
| 方法 | 路径 | 用途 | 鉴权 |
|
||||
|------|------|------|------|
|
||||
| POST | `/api/auth` | 登录拿会话 | 公开 |
|
||||
| GET | `/api/auth/whoami` 或 `/api/auth/session` | 校验身份 | session/token |
|
||||
| GET | `/api/vault/list?rootUri=&sourceKind=local_folder` | L0 列表 / 匹配 | view |
|
||||
| POST | `/api/vault/items` | 创建 | edit |
|
||||
| PATCH | `/api/vault/items/{id}` | 更新 / 追加账号 | edit |
|
||||
| POST | `/api/vault/items/{id}/share-to-ai` | 可选同步 AI 本(**须含 session 文件**) | edit |
|
||||
| POST | `/api/vault/ensure` | 确保目录 | 可写 |
|
||||
| PUT | `/api/vault/ai/items/{id}/session` | AI 本 session 回写(既有;演进写文件) | AI + session scope |
|
||||
|
||||
**Create body 示例(扩展 → mnote-web):**
|
||||
|
||||
```json
|
||||
{
|
||||
"rootUri": "file:///home/user/Notes",
|
||||
"sourceKind": "local_folder",
|
||||
"title": "GitHub",
|
||||
"url": "https://github.com/login",
|
||||
"username": "alice",
|
||||
"password": "plaintext-from-form",
|
||||
"email": "alice@example.com",
|
||||
"tags": ["from-extension"],
|
||||
"folderPath": "imported/browser",
|
||||
"notesMarkdown": "Saved via MNote Vault extension"
|
||||
}
|
||||
```
|
||||
|
||||
响应:与现网一致,L0 投影(password **masked**)。
|
||||
|
||||
**Update 追加账号(语义对齐 12-1 accounts[]):**
|
||||
|
||||
```json
|
||||
{
|
||||
"rootUri": "file:///…",
|
||||
"accounts": [
|
||||
{ "id": "acc_existing", "label": "work", "username": "…", "password": "…" },
|
||||
{ "id": "acc_new", "label": "from-extension", "username": "…", "password": "…" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
注意:`accounts` 在现网为 **全量替换**(`VaultUpdateInput.accounts: Option<Vec<…>>`)。扩展必须 **先 get 再 merge 再 patch**,禁止只发新 slot 导致清空旧账号。
|
||||
|
||||
### 6.2 新增(mnote-web,MVP 建议)
|
||||
|
||||
#### `POST /api/vault/extension/token`
|
||||
|
||||
**前置**:已登录 session(Cookie)或刚完成 auth 同请求链。
|
||||
|
||||
Request:
|
||||
|
||||
```json
|
||||
{
|
||||
"clientId": "chrome-extension",
|
||||
"extensionId": "optional-chrome-runtime-id",
|
||||
"ttlHours": 168
|
||||
}
|
||||
```
|
||||
|
||||
Response:
|
||||
|
||||
```json
|
||||
{
|
||||
"ok": true,
|
||||
"result": {
|
||||
"token": "mnext1.…",
|
||||
"expiresAt": "2026-07-31T00:00:00Z",
|
||||
"scope": ["vault.view", "vault.edit"],
|
||||
"userId": "…",
|
||||
"email": "…"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### `POST /api/vault/extension/token/revoke`
|
||||
|
||||
Body:`{ "jti": "…" }` 或吊销当前 Bearer。
|
||||
|
||||
#### `PUT /api/vault/items/{id}/session`(人用 / 扩展 · 新增)
|
||||
|
||||
**鉴权**:session cookie 或 E2 Bearer(`vault.edit`)。
|
||||
**写目标**:用户 vault(非 AI 本);路径 `sessions/{id}/{accountId}.json`。
|
||||
|
||||
Request:
|
||||
|
||||
```json
|
||||
{
|
||||
"rootUri": "file:///home/user/Notes",
|
||||
"sourceKind": "local_folder",
|
||||
"accountId": "primary",
|
||||
"cookieHeader": "session=abc; other=1",
|
||||
"cookies": [
|
||||
{
|
||||
"name": "session",
|
||||
"value": "abc",
|
||||
"domain": ".example.com",
|
||||
"path": "/",
|
||||
"secure": true,
|
||||
"httpOnly": true,
|
||||
"sameSite": "lax",
|
||||
"expirationDate": 1735689600
|
||||
}
|
||||
],
|
||||
"origin": "https://example.com",
|
||||
"expiresAt": "2026-07-31T00:00:00Z",
|
||||
"source": "chrome_extension",
|
||||
"expectedRevision": null
|
||||
}
|
||||
```
|
||||
|
||||
规则:
|
||||
|
||||
- `cookieHeader` 与 `cookies[]` 至少一个非空;两者皆有时以 `cookies[]` 重算 header 为准(防不一致)。
|
||||
- `accountId` 缺省 → `"primary"`。
|
||||
- 响应 L0:`{ ok, result: { credentialId, accountId, hasLoginSession: true, sessionExpiresAt, revision } }`,**无** cookie 明文。
|
||||
- audit:`action=session_put`,`source=chrome_extension`,无 value。
|
||||
|
||||
#### `GET /api/vault/items/{id}/session`(可选 P1)
|
||||
|
||||
- 默认 **不** 返回 cookie(仅 meta)。
|
||||
- 人用工作台「查看登录态元数据」用;扩展 P0 **不需要** GET。
|
||||
|
||||
#### share-to-ai 合同补强
|
||||
|
||||
`POST /api/vault/items/{id}/share-to-ai` 实现必须:
|
||||
|
||||
1. 复制 credential 到 AI 本(既有);
|
||||
2. **复制** `sessions/{id}/**` → AI vault 同相对路径;
|
||||
3. 若仅有 frontmatter `loginSession` 无文件 → 迁移写出 AI 侧 `primary.json` 再清/保留源侧策略按 core 统一。
|
||||
|
||||
否则 Agent 勾选同步后仍 `login` 不到 cookie。
|
||||
|
||||
#### 鉴权中间件扩展
|
||||
|
||||
`RequestContext` 识别:
|
||||
|
||||
1. 现有 `mnote_session` cookie
|
||||
2. **新增** `Authorization: Bearer mnext1.…`(extension human token)
|
||||
|
||||
映射到同一 `actor_id`;vault 路径 `require_authenticated` 通过即可。
|
||||
**reveal** 是否允许 extension token:
|
||||
|
||||
- **P0:允许**(扩展未来 autofill 需要);须审计 `source=extension`
|
||||
- 若产品收紧:P0 仅 list/create/update,reveal 仍要 web session
|
||||
|
||||
**推荐 P0:extension token 允许 list / create / update / ensure / **session put**;reveal 默认关(scope 不含 vault.reveal);P2 autofill 再加 `vault.reveal`。**
|
||||
E2 scope 逻辑声明可写 `["vault.view", "vault.edit"]`,`vault.edit` **包含** session 文件写入(不必单独 `vault.session`,避免 token 面碎片化)。
|
||||
|
||||
### 6.5 Agent / Skill 调用登录态(`$mnote-vault` 合同)
|
||||
|
||||
> **扩展只写入;Agent 只经 12-2 CLI / core 消费。** 禁止 Agent 为读 session 去扫 `.mnote/vault/sessions/**` 文件。
|
||||
|
||||
#### 6.5.1 稳态命令(AI 必须会)
|
||||
|
||||
```bash
|
||||
# 0. 环境(一次)
|
||||
export MNOTE_VAULT_TOKEN_FILE="${MNOTE_VAULT_TOKEN_FILE:-$HOME/.config/mnote/vault-tokens/default.token}"
|
||||
# 可选:MNOTE_AI_VAULT_ACTOR / MNOTE_VAULT_WORKSPACE
|
||||
|
||||
# 1. 选型(未知 id)
|
||||
mnote-vault list
|
||||
# L0 可见 hasLoginSession / sessionExpiresAt(无 cookie 明文)
|
||||
|
||||
# 2. 优先复用登录态(推荐主路径)
|
||||
mnote-vault login --id <credId>
|
||||
# 成功:mode=session | reused → 响应含 cookieHeader(仅 stdout 一次;勿贴聊天)
|
||||
# 过期/无 session:走 api_first 或 human_required
|
||||
|
||||
# 2b. 多账号时指定账号(session 文件按 accountId)
|
||||
mnote-vault login --id <credId> --account-id <accId>
|
||||
|
||||
# 3. 人机验证后回写(chrome-bridge / 扩展已写人用库后 share-to-ai)
|
||||
mnote-vault session --id <credId> --cookie-header 'name=value; …' --source human_bridge
|
||||
# 可选:--account-id <accId>
|
||||
|
||||
# 4. 只要 cookie 明文管道(实现演进;与 login 二选一)
|
||||
# mnote-vault resolve --id <credId> --field session [--account-id <accId>] [--raw]
|
||||
```
|
||||
|
||||
| 意图 | 命令 | 读哪里 |
|
||||
|------|------|--------|
|
||||
| 复用未过期 Cookie 访问外站 | `login` | `sessions/{id}/{account}.json` → fallback frontmatter |
|
||||
| 浏览器验证后落盘 | `session` | 写同上路径 |
|
||||
| 只要密码填表 | `resolve --field password` | credential md |
|
||||
| 只要 Cookie 不做登录流程 | `resolve --field session`(P1 落地)或 `login` 取 `cookieHeader` | session 文件 |
|
||||
|
||||
#### 6.5.2 Agent 行为规则(写入 skill)
|
||||
|
||||
1. **需要已登录外站状态时**:先 `login --id`,看 `mode`;`session`/`reused`/`ok` 用返回的 `cookieHeader` 调目标 API / 注入浏览器。
|
||||
2. **禁止** `Read`/`cat`/`grep` `.mnote/vault/sessions/**` 或 credential 内 cookie。
|
||||
3. **禁止** 把 `cookieHeader` / token 写入聊天、commit、issue。
|
||||
4. `human_required` → 用 chrome-bridge 完成验证 → `session` 回写;**不要**为 session 去起 mnote-web。
|
||||
5. 人用扩展写入的 session 在 **用户 vault**;Agent 默认读 **AI 本** → 须用户 **share-to-ai**(或显式配置 actor 指向人用本,非默认)。
|
||||
6. 多账号:list/get 看 `accounts[]` + `hasLoginSession`;`login --account-id` 与扩展写入的 `accountId` 对齐。
|
||||
|
||||
#### 6.5.3 文档落点(实现 Checklist 同步改)
|
||||
|
||||
| 文件 | 变更 |
|
||||
|------|------|
|
||||
| `/home/lix/.agent-infra/vault-policy.md` | Session 语义改为「每账号文件」;登录稳态表保留 |
|
||||
| `skills/mnote-vault/SKILL.md`(全局 symlink) | 增加「登录态文件 + 调用示例」专节 |
|
||||
| 12-2 | 交叉引用:session 存储演进见 12-3 §5.6;CLI 语义不变 |
|
||||
| 12-1 schema | 注明 `loginSession` frontmatter 兼容;新写走 `sessions/` |
|
||||
|
||||
#### CORS(仅 extension 需要时)
|
||||
|
||||
若 background 为 extension origin:
|
||||
|
||||
- 对 `/api/vault/*` 与 `/api/vault/extension/*`、`/api/auth`:
|
||||
- `Access-Control-Allow-Origin: chrome-extension://<id>` **或** 动态反射已登记 extensionId
|
||||
- `Access-Control-Allow-Headers: Authorization, Content-Type`
|
||||
- 不需要 `Allow-Credentials` 若纯 Bearer
|
||||
|
||||
**更干净:不靠 CORS,仅用 extension host_permissions 直连**(Chrome 扩展跨域 fetch 不受页面 CORS 限制)。
|
||||
**实现结论:MVP 以 host_permissions + Bearer 为主;服务端 CORS 仅在将来 Web 页面桥接时需要。**
|
||||
|
||||
### 6.3 错误码(扩展可分支)
|
||||
|
||||
| code | HTTP | 扩展动作 |
|
||||
|------|------|----------|
|
||||
| `vault_auth_required` | 401 | 清 token,引导 Options 登录 |
|
||||
| `vault_extension_token_invalid` | 401 | 重新签发 |
|
||||
| `vault_extension_token_expired` | 401 | 重新登录 |
|
||||
| `vault_root_required` / `vault_root_invalid` | 400 | Options 检查 rootUri |
|
||||
| `vault_permission_denied` | 403 | 提示 workspace 无写权限 |
|
||||
| `vault_revision_conflict` | 409 | re-get 后重试 merge |
|
||||
| `vault_mask_sentinel_rejected` | 400 | 勿提交掩码串 |
|
||||
| `network_unreachable` | — | 检查 baseUrl / 本机 3000 |
|
||||
|
||||
### 6.4 审计
|
||||
|
||||
沿用 `{workspace}/.mnote/vault/audit.jsonl`:
|
||||
|
||||
```json
|
||||
{"ts":"…","action":"create","actorId":"user_x","credentialId":"cred_…","requestId":"…","ok":true,"source":"chrome_extension"}
|
||||
{"ts":"…","action":"session_put","actorId":"user_x","credentialId":"cred_…","accountId":"primary","requestId":"…","ok":true,"source":"chrome_extension"}
|
||||
```
|
||||
|
||||
`source` 字段:实现时在 append_vault_audit 增加可选 `source`(无则兼容缺省)。**禁止** value / cookieHeader。
|
||||
|
||||
---
|
||||
|
||||
## 7. 安全设计
|
||||
|
||||
### 7.1 威胁与缓解
|
||||
|
||||
| 威胁 | 缓解 |
|
||||
|------|------|
|
||||
| 钓鱼站诱导保存到假 baseUrl | Options 显示完整 baseUrl;首次连接确认;可选证书钉扎不做 P0 |
|
||||
| 恶意页面读 extension 消息 | content 只发「候选字段」;token 永不下行 content;响应不回传 password / cookie |
|
||||
| XSS 页面窃取已输入密码 | 与浏览器密码管理器同类风险;确认层不把 token 注入页面 |
|
||||
| token 失窃 | session storage;短 TTL;revoke;scope 无 resolve |
|
||||
| Cookie 被 content 窃取 | 仅 background `chrome.cookies`;不回传 content |
|
||||
| 磁盘 session 文件泄露 | vault 目录权限;path deny 对 agent 通用工具;只经 CLI |
|
||||
| 扩展被恶意更新 | 自用 unpacked / 签名发布策略另案;P0 本地加载 |
|
||||
| 日志/崩溃转储 | background 不 console.log password / cookieHeader;错误上报脱敏 |
|
||||
| 静默批量上传 | **必须用户确认**;无「自动保存所有表单」默认开 |
|
||||
| AI 本扩大攻击面 | share-to-ai 默认关 + 二次确认;复制 session 须同确认 |
|
||||
|
||||
### 7.2 与 12-1 安全分级
|
||||
|
||||
| 级 | 扩展行为 |
|
||||
|----|----------|
|
||||
| L0 | list/match 元数据可缓存内存短时(TTL≤5min),**不**持久化到 disk 含 hint 以外 secret;session 仅 `hasLoginSession` |
|
||||
| L1 | create/update 提交明文 password;session put 提交 cookie **仅** HTTPS 到 baseUrl;扩展本地不落盘 |
|
||||
| L2 | **不提供** resolve;禁止 agent token;session **写**允许、**读明文**不对 extension |
|
||||
|
||||
### 7.3 与 chrome-bridge / QA 关系
|
||||
|
||||
- chrome-bridge 用于 agent **人机验证** session 回写(12-2),**不是**本扩展。
|
||||
- 本扩展是 **人** 的录入工具;QA 可用 Playwright 装 unpacked 扩展做 smoke,不与 Hermes QA 默认路径耦合。
|
||||
|
||||
---
|
||||
|
||||
## 8. 扩展模块地图
|
||||
|
||||
```text
|
||||
extensions/mnote-vault/
|
||||
manifest.json
|
||||
background/
|
||||
index.js # 消息路由、API 客户端、badge
|
||||
auth.js # login / token / whoami
|
||||
vault-api.js # list/create/update/session/share
|
||||
cookies.js # chrome.cookies → cookieHeader + cookies[]
|
||||
match.js # origin 匹配
|
||||
content/
|
||||
detect.js # password 表单侦测
|
||||
save-prompt.js # shadow DOM 确认层(默认勾选保存登录态)
|
||||
options/
|
||||
options.html
|
||||
options.js
|
||||
popup/
|
||||
popup.html # 状态 + 快捷「保存本页」+「更新登录态」+ Options
|
||||
popup.js
|
||||
shared/
|
||||
messages.js # 消息类型常量
|
||||
normalize-url.js
|
||||
README.md # 安装:chrome://extensions 开发者模式
|
||||
```
|
||||
|
||||
### 8.1 消息协议(内部)
|
||||
|
||||
```ts
|
||||
// content → background
|
||||
type Msg =
|
||||
| { type: "vault/candidate"; payload: { url: string; title: string; username?: string; email?: string; password: string; pageUrl: string } }
|
||||
| { type: "vault/ping" };
|
||||
|
||||
// background → content
|
||||
type Reply =
|
||||
| { type: "vault/status"; connected: boolean; email?: string }
|
||||
| { type: "vault/save-result"; ok: boolean; itemId?: string; sessionSaved?: boolean; message?: string };
|
||||
|
||||
// popup/options → background
|
||||
type Ctrl =
|
||||
| { type: "vault/login"; baseUrl: string; account: string; password: string }
|
||||
| { type: "vault/logout" }
|
||||
| { type: "vault/set-root"; rootUri: string }
|
||||
| { type: "vault/save-confirmed"; payload: SavePayload }
|
||||
| { type: "vault/refresh-session"; payload: { itemId: string; accountId?: string; pageUrl: string } };
|
||||
|
||||
type SavePayload = {
|
||||
title: string;
|
||||
url: string;
|
||||
username?: string;
|
||||
email?: string;
|
||||
password: string;
|
||||
matchItemId?: string; // 追加到已有
|
||||
setPrimary?: boolean;
|
||||
saveSession: boolean; // 默认 true
|
||||
shareToAi: boolean; // 默认 false
|
||||
pageUrl: string;
|
||||
};
|
||||
```
|
||||
|
||||
### 8.2 刷新模型
|
||||
|
||||
- **禁止** content/background `setInterval` 轮询 vault list。
|
||||
- 匹配:保存前 **单次** list/match;结果可 memory cache 至多 5 分钟或 rootUri 变更时失效。
|
||||
- 连接状态:登录/登出/401 时更新 badge;可用 `chrome.alarms` 做 token 到期前提醒(非轮询 vault)。
|
||||
|
||||
---
|
||||
|
||||
## 9. 分期
|
||||
|
||||
| 阶段 | 范围 | 依赖 |
|
||||
|------|------|------|
|
||||
| **P0 MVP** | Options 连接;Bearer;保存确认层;create;**默认保存登录态 → session 文件**;list 匹配;toolbar 手动保存;skill/policy 更新 | mnote-web token + session PUT;core 写 `sessions/` |
|
||||
| **P1** | get+merge `accounts[]` + 每账号 session;`login --account-id`;`/vault/match`;share-to-ai 复制 session;workbench「复制到扩展」;登录成功后二次抓 cookie | P0 |
|
||||
| **P2** | 自动填充(`vault.reveal`);`resolve --field session`;提交成功智能检测;Firefox | 安全评审 |
|
||||
| **P3** | Native Messaging 纯本机旁路;导入浏览器 CSV;TOTP;去掉 frontmatter `loginSession` 镜像 | 另设计 |
|
||||
|
||||
### 9.1 与 12-1 P2「协助录入」关系
|
||||
|
||||
12-1 §6 P2「协助录入确认流」可与本扩展合并叙事:**浏览器侧录入 = 12-3;工作台内粘贴助手可后置或不做。**
|
||||
|
||||
---
|
||||
|
||||
## 10. 实现 Checklist(批准设计后执行)
|
||||
|
||||
### PR-A — 设计与开关
|
||||
|
||||
- [x] 本文合入 `design/12-vault/process/`
|
||||
- [x] 12-1 / 12-2 交叉引用 session 文件演进(§5.6)
|
||||
- [ ] Feature flag(可选):`MNOTE_VAULT_EXTENSION=1` 控制 token 路由是否注册(P0 随 `MNOTE_VAULT=1`)
|
||||
|
||||
### PR-B — mnote-web + core(session 文件)
|
||||
|
||||
- [x] `POST /api/vault/extension/token` + revoke
|
||||
- [x] Bearer `mnext1` 解析接入 `RequestContext` / vault 鉴权
|
||||
- [x] scope:view+edit(含 session put);**无** resolve
|
||||
- [x] `PUT /api/vault/items/{id}/session` → 写 `sessions/{id}/{accountId}.json`
|
||||
- [x] core:`put_login_session` / `login` 读路径优先 session 文件,fallback frontmatter
|
||||
- [x] share-to-ai **复制** session 目录
|
||||
- [x] audit `source=chrome_extension`;`session_put` 无 cookie 明文
|
||||
- [x] 单测:签发、revoke、session 文件读写(`vault_extension_token` + `session_file_put_resolve_and_share_copy`)
|
||||
- [x] API smoke:`scripts/vault-extension-api-smoke.js`(auth → mnext1 → create → PUT session → disk → revoke)
|
||||
- [ ] (P1)`GET /api/vault/match`;`login --account-id`
|
||||
|
||||
### PR-C — 扩展 MVP
|
||||
|
||||
- [x] `extensions/mnote-vault` MV3 脚手架(含 `cookies` 权限)
|
||||
- [x] Options:baseUrl / 登录 / rootUri
|
||||
- [x] background vault-api + cookies 抓取
|
||||
- [x] content 侦测 + 确认层(**默认勾选保存登录态**)
|
||||
- [x] popup:状态 + 保存本页 + 更新登录态
|
||||
- [x] README 安装步骤
|
||||
|
||||
### PR-D — Skill / Policy
|
||||
|
||||
- [x] 更新 `/home/lix/.agent-infra/vault-policy.md`(每账号 session 文件 + 调用)
|
||||
- [x] 更新 `skills/mnote-vault/SKILL.md`(login/session 与文件语义)
|
||||
- [x] 确认全局 symlink(`.grok` / Codex)→ 仓库 `skills/mnote-vault`
|
||||
|
||||
### PR-E — 验收
|
||||
|
||||
- [x] API 层:`node scripts/vault-extension-api-smoke.js` 对 3000 通过
|
||||
- [ ] 本机:unpacked 扩展 Chrome 手工加载(用户验收)
|
||||
- [ ] 测试账号登录扩展 → 保存 example → `/vault` 可见条目 + `sessions/…json`
|
||||
- [ ] share-to-ai 后 `mnote-vault login --id` → `mode=session`
|
||||
- [x] 未登录 content 禁用保存(仅提示去 Options)
|
||||
- [ ] `TESTING_REFERENCE.md` 段落
|
||||
|
||||
### PR-F — P1 增强(可另 PR)
|
||||
|
||||
- [x] 同站 accounts 合并 + 每账号 session(扩展 MATCH_URL + append mode)
|
||||
- [ ] workbench 复制连接信息
|
||||
- [ ] 登录成功后二次抓 cookie
|
||||
- [ ] `resolve --field session`
|
||||
|
||||
---
|
||||
|
||||
## 11. 验收场景(手工 / smoke)
|
||||
|
||||
| # | 场景 | 期望 |
|
||||
|---|------|------|
|
||||
| S1 | 无配置打开 example.com 登录表 | 无保存弹层或仅「去连接」 |
|
||||
| S2 | 配置错误 baseUrl | 测试连接失败,不存 token |
|
||||
| S3 | 正确登录 + rootUri | badge 已连接 |
|
||||
| S4 | 填用户名密码 → 点保存 → 确认(默认保存登录态) | create 成功;`sessions/{id}/primary.json` 存在;password reveal 正确 |
|
||||
| S5 | 取消「同时保存登录态」 | 仅 credential,无新 session 文件 |
|
||||
| S6 | 同站再存另一账号选「追加」 | accounts 两个;**各有** session 文件(若均勾选) |
|
||||
| S7 | token 过期后保存 | 401 → 引导重新登录,不写半截 |
|
||||
| S8 | 勾选同步 AI | AI 本有 credential **+** session;`mnote-vault login` 可复用 |
|
||||
| S9 | 不勾选同步 AI | 仅人用库有 session;AI `login` 无该 id 或无 cookie |
|
||||
| S10 | 审计文件 | 有 create / session_put,无明文密码/cookie 行 |
|
||||
| S11 | skill 文档 | Agent 按 skill 用 `login`/`session`,不扫 vault 文件 |
|
||||
|
||||
---
|
||||
|
||||
## 12. Alternatives Considered
|
||||
|
||||
| 方案 | 结论 |
|
||||
|------|------|
|
||||
| 只做 bookmarklet 调 API | 无可靠 background;token 易进页面;否 |
|
||||
| 浏览器原生 Password Manager 导出再 import | 不解决「当下保存」;可作迁移旁路 |
|
||||
| 全部走 Native Host 写磁盘 | 无远程 baseUrl;与用户需求不符;P2 可选 |
|
||||
| 扩展直接读 `.mnote/vault` 文件 | 违反 vault path deny 与加密演进;禁止 |
|
||||
| 复用 agent `mnote-vault` token | 权限与 actor 错误;禁止 |
|
||||
| 继续只嵌 frontmatter `loginSession` | 同站多账号互踩;否;演进为每账号文件 |
|
||||
| session 只做 attachments 附件 | 路径长、list 难;默认 session 文件,附件作导出旁路 |
|
||||
|
||||
---
|
||||
|
||||
## 13. 待决问题(实现前钉死;推荐已标)
|
||||
|
||||
| # | 问题 | 推荐默认 |
|
||||
|---|------|----------|
|
||||
| D1 | 鉴权 E1 cookie vs E2 Bearer | **E2 Bearer**;dev 可附带 cookie |
|
||||
| D2 | content_scripts 全站 vs activeTab | **全站侦测 + 用户确认保存** |
|
||||
| D3 | extension token 是否 reveal | **P0 否**;P2 autofill 再开 |
|
||||
| D4 | rootUri 如何获得 | **P0 手动 / 工作台复制**;P1 列 directory grant |
|
||||
| D5 | 默认 folderPath | `imported/browser` |
|
||||
| D6 | 是否默认 tags | `["from-extension"]` |
|
||||
| D7 | 扩展目录 monorepo 还是独立 repo | **monorepo `extensions/mnote-vault`** |
|
||||
| D8 | 发布渠道 | P0 仅 unpacked / 自签;商店上架另案 |
|
||||
| D9 | 与 12-1「不做自动填表」文案 | 修订交叉引用,避免矛盾 |
|
||||
| D10 | 密码字段启发式失败 | 工具栏手动选中 / 用户可编辑确认层 |
|
||||
| D11 | 默认是否保存登录态 | **是(默认勾选)**;用户可取消 |
|
||||
| D12 | session 存 frontmatter vs 独立文件 | **独立文件** `sessions/{cred}/{acc}.json`;frontmatter 只兼容读 |
|
||||
| D13 | 无多账号时 accountId | **`primary`** |
|
||||
| D14 | 登录瞬间 cookie 可能不全 | P0 仍写 + 工具栏「更新登录态」;P1 登录成功二次抓 |
|
||||
|
||||
---
|
||||
|
||||
## 14. 修订记录
|
||||
|
||||
| 日期 | 变更 |
|
||||
|------|------|
|
||||
| 2026-07-24 | 初稿:Chrome MV3 保存扩展;E2 human token;复用 vault CRUD;与 12-1/12-2 边界钉死 |
|
||||
| 2026-07-24 | 增补:默认保存登录态;每账号独立 session 文件;PUT session API;skill/agent 调用合同 §6.5 |
|
||||
|
||||
---
|
||||
|
||||
## 15. 批准后下一步(给实现者)
|
||||
|
||||
1. 产品确认 §13 D1–D5、**D11–D14**(默认可直接开工)。
|
||||
2. 先 **PR-B**(token + session 文件 + 鉴权)再 **PR-C**(扩展),避免扩展对接空 API。
|
||||
3. 同步 **PR-D** skill/policy,保证 Agent 知道 `login`/`session` 读的是 session 文件。
|
||||
4. 不做 autofill 直到 P0 保存闭环(账号 + 登录态)绿 + 安全过目。
|
||||
5. 扩展是 **人用录入**;Agent 读密/读登录态 **仍只走** `$mnote-vault` CLI,**不是**扩展通道。
|
||||
|
||||
Reference in New Issue
Block a user