feat(browser): unify site credentials with browser profiles (BR-2)
- Inject the site account Cookie (manual + auto-login session, session wins)
into the camofox profile before the first tab opens; password accounts
without a session run refresh-login first; API Key sites are skipped.
- Cookie expired after injection -> credential.status=expired; cookie
accounts go to waiting/login_required ("log in once, applies site-wide").
- Write back site-domain cookies from the browser on handover, session close,
every 10 min of activity, and from refresh-login when the stored cookie is
invalid (verified before saving). user_info.cookieOrigin records
source=browser; source shown as manual/login/browser in the origin view.
- New camofox plugin umt-cookies (GET/POST /umt/sessions/:userId/cookies
under the access key); profile dir 0700/files 0600 in the desktop image.
- Single entry: sources-identity/service/credentials.ts.
- Real check: HHClub cookie injected into a fresh profile -> home page
logged in (logout link present), 5 cookies read back.
Requires rebuilding umt-browser-desktop (docker compose build camofox &&
docker compose up -d camofox) to take effect.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
901b01d1a1
commit
28974d1e1a
58 files changed
+2843
-1112
No files matched your search
@@ -286,6 +286,47 @@ export const BrowserProvidersQuery = z.object({
|
||||
export type BrowserProvidersQuery = z.infer<typeof BrowserProvidersQuery>;
|
||||
|
||||
/** 会话视图:会话 + 标签页 + 当前有效租约 + 界面可用能力 */
|
||||
// ── BR-2 凭据统一:站点账号 Cookie ⇄ 浏览器 profile ──
|
||||
|
||||
/** 浏览器 Cookie(Playwright 形状);value 是凭据,只在服务端与浏览器服务之间传,不进日志、模型与 API 响应 */
|
||||
export const BrowserCookie = z.object({
|
||||
name: z.string().min(1),
|
||||
value: z.string(),
|
||||
/** 主机名(host-only)或 .根域 */
|
||||
domain: z.string().min(1),
|
||||
path: z.string().default("/"),
|
||||
/** Unix 秒;-1 = 会话 Cookie */
|
||||
expires: z.number().default(-1),
|
||||
httpOnly: z.boolean().default(false),
|
||||
secure: z.boolean().default(false),
|
||||
sameSite: z.enum(["Strict", "Lax", "None"]).optional(),
|
||||
});
|
||||
export type BrowserCookie = z.infer<typeof BrowserCookie>;
|
||||
|
||||
/** 站点账号登录态(Cookie)来源:手填 / 账密自动登录 / 浏览器回写 */
|
||||
export const SiteCookieSource = z.enum(["manual", "login", "browser"]);
|
||||
export type SiteCookieSource = z.infer<typeof SiteCookieSource>;
|
||||
|
||||
/**
|
||||
* 会话打开时的凭据注入结果(存 recovery_state.credential):
|
||||
* injected = 已把账号 Cookie 注入浏览器 profile;none = 账号没有可注入的 Cookie;
|
||||
* not_applicable = 匿名会话或 API Key 类站点(网页登录态与 API Key 无关,不注入);
|
||||
* unsupported = 浏览器服务没有 Cookie 接口(镜像未带 umt-cookies 插件且上游导入口令不同);
|
||||
* failed = 注入请求失败;expired = 注入后首页仍是登录页(Cookie 过期),会话转"需要人工登录"。
|
||||
*/
|
||||
export const BrowserCredentialState = z.object({
|
||||
status: z.enum(["injected", "none", "not_applicable", "unsupported", "failed", "expired"]),
|
||||
source: SiteCookieSource.nullable(),
|
||||
/** 该来源最近一次写入凭据的时间 */
|
||||
sourceAt: Timestamp.nullable(),
|
||||
/** 注入的 Cookie 个数(不含值) */
|
||||
count: NonNegInt,
|
||||
message: z.string().nullable(),
|
||||
/** 最近一次从浏览器读回并比对的时间 */
|
||||
syncedAt: Timestamp.nullable(),
|
||||
});
|
||||
export type BrowserCredentialState = z.infer<typeof BrowserCredentialState>;
|
||||
|
||||
export const BrowserSessionView = BrowserSession.extend({
|
||||
tabs: z.array(BrowserTab),
|
||||
currentTabId: z.string().nullable(),
|
||||
@@ -294,6 +335,8 @@ export const BrowserSessionView = BrowserSession.extend({
|
||||
needsSnapshot: z.boolean(),
|
||||
/** 等待处理/接管原因(login_required / captcha / human_takeover / lease_expired …) */
|
||||
waitingReason: z.string().nullable(),
|
||||
/** 打开会话时的凭据注入与回写状态(BR-2);旧会话没有 */
|
||||
credential: BrowserCredentialState.nullable().optional(),
|
||||
});
|
||||
export type BrowserSessionView = z.infer<typeof BrowserSessionView>;
|
||||
|
||||
|
||||
Reference in new issue
Block a user