Merge branch 'hh2'
# Conflicts: # apps/server/src/modules/browser/service.ts # apps/web/app/components/browser/desktop-view.tsx # apps/web/app/components/browser/site-origin-view.tsx # packages/contracts/src/browser.ts
This commit is contained in:
commit
f41cd96fee
33 files changed
+1919
-302
No files matched your search
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { holderName, toViewport, waitingText } from "./coords.ts";
|
||||
import { agentActive, controllerLabel, holderName, toViewport, waitingText } from "./coords.ts";
|
||||
|
||||
describe("原站视图坐标换算", () => {
|
||||
const natural = { width: 1280, height: 800 };
|
||||
@@ -29,3 +29,18 @@ describe("原站视图坐标换算", () => {
|
||||
expect(holderName("admin_session:alice")).toBe("alice");
|
||||
});
|
||||
});
|
||||
|
||||
describe("当前控制文案", () => {
|
||||
const now = Date.parse("2026-09-29T10:00:00Z");
|
||||
it("自动化且没有最近动作 → 「自动化」;两分钟内有自动化动作 → 「Agent」", () => {
|
||||
expect(controllerLabel({ controller: "automation", lastAutomationAt: null }, now)).toBe("自动化");
|
||||
expect(controllerLabel({ controller: "automation" }, now)).toBe("自动化");
|
||||
expect(controllerLabel({ controller: "automation", lastAutomationAt: "2026-09-29T09:50:00Z" }, now)).toBe("自动化");
|
||||
expect(controllerLabel({ controller: "automation", lastAutomationAt: "2026-09-29T09:59:10Z" }, now)).toBe("Agent");
|
||||
expect(agentActive({ controller: "automation", lastAutomationAt: "2026-09-29T09:59:10Z" }, now)).toBe(true);
|
||||
});
|
||||
it("人工接管 / 等待处理不看自动化时间", () => {
|
||||
expect(controllerLabel({ controller: "human", lastAutomationAt: "2026-09-29T09:59:50Z" }, now)).toBe("人工接管");
|
||||
expect(controllerLabel({ controller: "waiting" }, now)).toBe("等待处理");
|
||||
});
|
||||
});
|
||||
@@ -17,12 +17,33 @@ export function toViewport(
|
||||
}
|
||||
|
||||
export const CONTROLLER_LABEL: Record<BrowserController, string> = {
|
||||
automation: "Agent",
|
||||
automation: "自动化",
|
||||
human: "人工接管",
|
||||
waiting: "等待处理",
|
||||
closed: "已关闭",
|
||||
};
|
||||
|
||||
/** 自动化动作在这段时间内发生过,算「Agent 正在操作」 */
|
||||
export const AGENT_ACTIVE_MS = 2 * 60_000;
|
||||
|
||||
/** Agent 是否正在用这个会话:控制者是自动化,且最近 AGENT_ACTIVE_MS 内有过自动化动作 */
|
||||
export function agentActive(
|
||||
s: { controller: BrowserController; lastAutomationAt?: string | null },
|
||||
now = Date.now(),
|
||||
): boolean {
|
||||
if (s.controller !== "automation" || !s.lastAutomationAt) return false;
|
||||
const t = new Date(s.lastAutomationAt).getTime();
|
||||
return Number.isFinite(t) && now - t < AGENT_ACTIVE_MS;
|
||||
}
|
||||
|
||||
/** 「当前控制」文案:无人操作时是「自动化」,Agent 正在操作才显示「Agent」 */
|
||||
export function controllerLabel(
|
||||
s: { controller: BrowserController; lastAutomationAt?: string | null },
|
||||
now = Date.now(),
|
||||
): string {
|
||||
return agentActive(s, now) ? "Agent" : CONTROLLER_LABEL[s.controller];
|
||||
}
|
||||
|
||||
const WAITING_TEXT: Record<string, string> = {
|
||||
captcha: "登录页需要验证码",
|
||||
challenge: "页面需要人工验证",
|
||||
|
||||
@@ -9,7 +9,7 @@ import { StatusBadge, type StatusTone } from "~/components/console/status-badge.
|
||||
import { Button } from "~/components/ui/button.tsx";
|
||||
import { apiData, errorMessage } from "~/lib/api.ts";
|
||||
import { qk } from "~/lib/query-keys.ts";
|
||||
import { CONTROLLER_LABEL, credentialBadge, holderName, waitingText } from "./coords.ts";
|
||||
import { agentActive, controllerLabel, credentialBadge, holderName, waitingText } from "./coords.ts";
|
||||
import { ReconnectPanel, ReloadNotice, useAutoReconnect } from "./reconnect.tsx";
|
||||
import { SiteOriginView } from "./site-origin-view.tsx";
|
||||
|
||||
@@ -153,7 +153,7 @@ function DesktopView({
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<StatusBadge tone={conn.tone}>{conn.text}</StatusBadge>
|
||||
<StatusBadge tone={CONTROLLER_TONE[s.controller] ?? "neutral"}>
|
||||
当前控制:{CONTROLLER_LABEL[s.controller]}
|
||||
当前控制:{controllerLabel(s)}
|
||||
{human && leaseHolder ? `(${leaseHolder})` : ""}
|
||||
</StatusBadge>
|
||||
{waiting ? <StatusBadge tone="warning">{waiting}</StatusBadge> : null}
|
||||
@@ -217,7 +217,9 @@ function DesktopView({
|
||||
<p className="text-caption text-muted-foreground">
|
||||
{mine
|
||||
? "直接在画面里点击和输入。中文、日文用本机输入法输入;复制粘贴用 Ctrl+C / Ctrl+V。"
|
||||
: "只读观看,Agent 可能正在操作。需要自己点或输入时先接管。"}
|
||||
: agentActive(s)
|
||||
? "只读观看,Agent 正在操作。需要自己点或输入时先接管,Agent 会暂停。"
|
||||
: "只读观看,目前没有 Agent 在操作。需要自己点或输入时先接管。"}
|
||||
</p>
|
||||
|
||||
<div className="pg-slab relative min-w-0 overflow-hidden rounded-xl">
|
||||
|
||||
@@ -34,7 +34,7 @@ import { StatusBadge, type StatusTone } from "~/components/console/status-badge.
|
||||
import { Button } from "~/components/ui/button.tsx";
|
||||
import { Input } from "~/components/ui/input.tsx";
|
||||
import { errorMessage } from "~/lib/api.ts";
|
||||
import { CONTROLLER_LABEL, credentialBadge, holderName, toViewport, waitingText } from "./coords.ts";
|
||||
import { controllerLabel, credentialBadge, holderName, toViewport, waitingText } from "./coords.ts";
|
||||
import { isBrowserGone, ReconnectPanel, ReloadNotice, useAutoReconnect } from "./reconnect.tsx";
|
||||
|
||||
// 原站视图(REQ §4.7.1、§4.7.3):同一个 camofox 会话的定时截图 + 人工输入映射。
|
||||
@@ -156,7 +156,7 @@ export function SiteOriginView({ site }: { site: SiteListItem }) {
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<StatusBadge tone={conn.tone}>{conn.text}</StatusBadge>
|
||||
<StatusBadge tone={CONTROLLER_TONE[s.controller] ?? "neutral"}>
|
||||
当前控制:{CONTROLLER_LABEL[s.controller]}
|
||||
当前控制:{controllerLabel(s)}
|
||||
{human && leaseHolder ? `(${leaseHolder})` : ""}
|
||||
</StatusBadge>
|
||||
{waiting ? <StatusBadge tone="warning">{waiting}</StatusBadge> : null}
|
||||
|
||||
Reference in new issue
Block a user