fix(webui): simplify automation source display
This commit is contained in:
@@ -160,7 +160,7 @@ def _origin_payload(
|
||||
|
||||
title = ""
|
||||
preview = ""
|
||||
if session_manager is not None:
|
||||
if channel == "websocket" and session_manager is not None:
|
||||
data = session_manager.read_session_file(session_key)
|
||||
if isinstance(data, dict):
|
||||
title = str(data.get("title") or "")
|
||||
|
||||
@@ -832,6 +832,14 @@ async def test_webui_automations_route_lists_all_jobs_and_allows_user_actions(
|
||||
message="Practice English",
|
||||
session_key="unified:default",
|
||||
)
|
||||
external_job = cron.add_job(
|
||||
name="WeChat quiz",
|
||||
schedule=CronSchedule(kind="every", every_ms=3_600_000),
|
||||
message="Send a quiz",
|
||||
session_key="weixin:wx-chat",
|
||||
origin_channel="weixin",
|
||||
origin_chat_id="wx-chat",
|
||||
)
|
||||
cron.register_system_job(
|
||||
CronJob(
|
||||
id="heartbeat",
|
||||
@@ -840,9 +848,13 @@ async def test_webui_automations_route_lists_all_jobs_and_allows_user_actions(
|
||||
payload=CronPayload(kind="system_event"),
|
||||
)
|
||||
)
|
||||
session_manager = _seed_session(tmp_path, key="websocket:abc")
|
||||
external_session = Session(key="weixin:wx-chat")
|
||||
external_session.add_message("user", "Scheduled cron job triggered")
|
||||
session_manager.save(external_session)
|
||||
channel = _ch(
|
||||
bus,
|
||||
session_manager=_seed_session(tmp_path, key="websocket:abc"),
|
||||
session_manager=session_manager,
|
||||
cron_service=cron,
|
||||
cron_pending_job_ids=lambda key: {user_job.id} if key == "websocket:abc" else set(),
|
||||
port=29932,
|
||||
@@ -870,6 +882,8 @@ async def test_webui_automations_route_lists_all_jobs_and_allows_user_actions(
|
||||
assert by_id[user_job.id]["origin"]["preview"] == "hi"
|
||||
assert by_id[legacy_job.id]["payload"]["session_key"] == "unified:default"
|
||||
assert by_id[legacy_job.id]["origin"] is None
|
||||
assert by_id[external_job.id]["origin"]["session_key"] == "weixin:wx-chat"
|
||||
assert by_id[external_job.id]["origin"]["preview"] == ""
|
||||
assert by_id["heartbeat"]["protected"] is True
|
||||
|
||||
disabled = await _http_get(
|
||||
|
||||
@@ -13,7 +13,6 @@ import {
|
||||
ArrowUpCircle,
|
||||
Bot,
|
||||
Brain,
|
||||
CalendarClock,
|
||||
Check,
|
||||
CircleAlert,
|
||||
ChevronDown,
|
||||
@@ -3390,23 +3389,14 @@ function AutomationsSettings({
|
||||
return (
|
||||
<div className="space-y-5">
|
||||
<section className="rounded-[24px] border border-border/50 bg-card/82 px-4 py-4 shadow-[0_18px_65px_rgba(15,23,42,0.07)] backdrop-blur-xl sm:px-5">
|
||||
<div className="flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between">
|
||||
<div className="min-w-0">
|
||||
<div className="flex items-center gap-2 text-[13px] font-medium text-muted-foreground">
|
||||
<CalendarClock className="h-4 w-4" aria-hidden />
|
||||
{tx("settings.automations.kicker", "Workspace automations")}
|
||||
</div>
|
||||
<h2 className="mt-2 text-[22px] font-normal leading-tight tracking-normal text-foreground">
|
||||
{tx("settings.automations.title", "Automations")}
|
||||
</h2>
|
||||
<p className="mt-2 max-w-[36rem] text-[13px] leading-6 text-muted-foreground">
|
||||
{tx(
|
||||
"settings.automations.description",
|
||||
"Review cron reminders, recurring agent turns, one-time jobs, and protected system automations in one place.",
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex shrink-0 gap-2">
|
||||
<div className="flex flex-col gap-3 lg:flex-row lg:items-center lg:justify-between">
|
||||
<p className="max-w-[38rem] text-[13px] leading-6 text-muted-foreground">
|
||||
{tx(
|
||||
"settings.automations.description",
|
||||
"Review cron reminders, recurring agent turns, one-time jobs, and protected system jobs.",
|
||||
)}
|
||||
</p>
|
||||
<div className="flex shrink-0 flex-wrap gap-2">
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
@@ -3711,6 +3701,11 @@ function AutomationDeleteDialog({
|
||||
}
|
||||
|
||||
function automationSearchText(job: SessionAutomationJob): string {
|
||||
const originText = job.origin
|
||||
? job.origin.channel === "websocket"
|
||||
? [job.origin.session_key, job.origin.title, job.origin.preview]
|
||||
: [job.origin.channel]
|
||||
: [];
|
||||
return [
|
||||
job.id,
|
||||
job.name,
|
||||
@@ -3718,9 +3713,7 @@ function automationSearchText(job: SessionAutomationJob): string {
|
||||
job.schedule.kind,
|
||||
job.schedule.expr,
|
||||
job.schedule.tz,
|
||||
job.origin?.session_key,
|
||||
job.origin?.title,
|
||||
job.origin?.preview,
|
||||
...originText,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(" ")
|
||||
@@ -3758,9 +3751,37 @@ function automationOriginLabel(
|
||||
return tx("settings.automations.origin.legacy", "Recreate in target chat");
|
||||
}
|
||||
if (!origin) return tx("settings.automations.origin.unknown", "No linked chat");
|
||||
if (origin.channel !== "websocket") return automationChannelLabel(origin.channel, tx);
|
||||
return origin.title || origin.preview || origin.session_key;
|
||||
}
|
||||
|
||||
function automationChannelLabel(
|
||||
channel: string,
|
||||
tx: (key: string, fallback: string, values?: Record<string, unknown>) => string,
|
||||
): string {
|
||||
const key = channel.trim().toLowerCase();
|
||||
const labels: Record<string, string> = {
|
||||
api: "API",
|
||||
cli: "CLI",
|
||||
dingtalk: "DingTalk",
|
||||
discord: "Discord",
|
||||
email: "Email",
|
||||
feishu: "Feishu",
|
||||
matrix: "Matrix",
|
||||
msteams: "Microsoft Teams",
|
||||
qq: "QQ",
|
||||
slack: "Slack",
|
||||
telegram: "Telegram",
|
||||
wechat: "WeChat",
|
||||
wecom: "WeCom",
|
||||
weixin: "WeChat",
|
||||
whatsapp: "WhatsApp",
|
||||
};
|
||||
return labels[key]
|
||||
? tx(`settings.automations.channels.${key}`, labels[key])
|
||||
: channel;
|
||||
}
|
||||
|
||||
function formatAutomationSchedule(
|
||||
job: SessionAutomationJob,
|
||||
locale: string,
|
||||
|
||||
@@ -466,7 +466,7 @@
|
||||
"automations": {
|
||||
"kicker": "Workspace automations",
|
||||
"title": "Automations",
|
||||
"description": "Review cron reminders, recurring agent turns, one-time jobs, and protected system automations in one place.",
|
||||
"description": "Review cron reminders, recurring agent turns, one-time jobs, and protected system jobs.",
|
||||
"refresh": "Refresh",
|
||||
"newInChat": "New in chat",
|
||||
"stats": {
|
||||
@@ -515,6 +515,23 @@
|
||||
"unknown": "No linked chat",
|
||||
"legacy": "Recreate in target chat"
|
||||
},
|
||||
"channels": {
|
||||
"api": "API",
|
||||
"cli": "CLI",
|
||||
"dingtalk": "DingTalk",
|
||||
"discord": "Discord",
|
||||
"email": "Email",
|
||||
"feishu": "Feishu",
|
||||
"matrix": "Matrix",
|
||||
"msteams": "Microsoft Teams",
|
||||
"qq": "QQ",
|
||||
"slack": "Slack",
|
||||
"telegram": "Telegram",
|
||||
"wechat": "WeChat",
|
||||
"wecom": "WeCom",
|
||||
"weixin": "WeChat",
|
||||
"whatsapp": "WhatsApp"
|
||||
},
|
||||
"schedule": {
|
||||
"at": "At {{time}}",
|
||||
"every": "Every {{duration}}",
|
||||
|
||||
@@ -466,7 +466,7 @@
|
||||
"automations": {
|
||||
"kicker": "Automatizaciones del espacio",
|
||||
"title": "Automatizaciones",
|
||||
"description": "Revisa recordatorios cron, turnos recurrentes del agente, automatizaciones de una vez y automatizaciones protegidas del sistema en un solo lugar.",
|
||||
"description": "Revisa recordatorios cron, turnos recurrentes del agente, tareas de una vez y tareas protegidas del sistema.",
|
||||
"refresh": "Actualizar",
|
||||
"newInChat": "Crear en chat",
|
||||
"stats": {
|
||||
@@ -515,6 +515,23 @@
|
||||
"unknown": "Sin chat vinculado",
|
||||
"legacy": "Recréala en el chat de destino"
|
||||
},
|
||||
"channels": {
|
||||
"api": "API",
|
||||
"cli": "CLI",
|
||||
"dingtalk": "DingTalk",
|
||||
"discord": "Discord",
|
||||
"email": "Email",
|
||||
"feishu": "Feishu",
|
||||
"matrix": "Matrix",
|
||||
"msteams": "Microsoft Teams",
|
||||
"qq": "QQ",
|
||||
"slack": "Slack",
|
||||
"telegram": "Telegram",
|
||||
"wechat": "WeChat",
|
||||
"wecom": "WeCom",
|
||||
"weixin": "WeChat",
|
||||
"whatsapp": "WhatsApp"
|
||||
},
|
||||
"schedule": {
|
||||
"at": "A las {{time}}",
|
||||
"every": "Cada {{duration}}",
|
||||
|
||||
@@ -466,7 +466,7 @@
|
||||
"automations": {
|
||||
"kicker": "Automatisations de l’espace",
|
||||
"title": "Automatisations",
|
||||
"description": "Passez en revue les rappels cron, tours récurrents de l’agent, automatisations ponctuelles et automatisations système protégées au même endroit.",
|
||||
"description": "Passez en revue les rappels cron, tours récurrents de l’agent, tâches ponctuelles et tâches système protégées.",
|
||||
"refresh": "Actualiser",
|
||||
"newInChat": "Créer dans le chat",
|
||||
"stats": {
|
||||
@@ -515,6 +515,23 @@
|
||||
"unknown": "Aucune discussion liée",
|
||||
"legacy": "Recréez-la dans la discussion cible"
|
||||
},
|
||||
"channels": {
|
||||
"api": "API",
|
||||
"cli": "CLI",
|
||||
"dingtalk": "DingTalk",
|
||||
"discord": "Discord",
|
||||
"email": "Email",
|
||||
"feishu": "Feishu",
|
||||
"matrix": "Matrix",
|
||||
"msteams": "Microsoft Teams",
|
||||
"qq": "QQ",
|
||||
"slack": "Slack",
|
||||
"telegram": "Telegram",
|
||||
"wechat": "WeChat",
|
||||
"wecom": "WeCom",
|
||||
"weixin": "WeChat",
|
||||
"whatsapp": "WhatsApp"
|
||||
},
|
||||
"schedule": {
|
||||
"at": "À {{time}}",
|
||||
"every": "Toutes les {{duration}}",
|
||||
|
||||
@@ -466,7 +466,7 @@
|
||||
"automations": {
|
||||
"kicker": "Otomasi ruang kerja",
|
||||
"title": "Otomasi",
|
||||
"description": "Tinjau pengingat cron, giliran agen berulang, otomasi sekali jalan, dan otomasi sistem terlindungi di satu tempat.",
|
||||
"description": "Tinjau pengingat cron, giliran agen berulang, tugas sekali jalan, dan tugas sistem terlindungi.",
|
||||
"refresh": "Segarkan",
|
||||
"newInChat": "Buat di chat",
|
||||
"stats": {
|
||||
@@ -515,6 +515,23 @@
|
||||
"unknown": "Tidak ada chat tertaut",
|
||||
"legacy": "Buat ulang di chat tujuan"
|
||||
},
|
||||
"channels": {
|
||||
"api": "API",
|
||||
"cli": "CLI",
|
||||
"dingtalk": "DingTalk",
|
||||
"discord": "Discord",
|
||||
"email": "Email",
|
||||
"feishu": "Feishu",
|
||||
"matrix": "Matrix",
|
||||
"msteams": "Microsoft Teams",
|
||||
"qq": "QQ",
|
||||
"slack": "Slack",
|
||||
"telegram": "Telegram",
|
||||
"wechat": "WeChat",
|
||||
"wecom": "WeCom",
|
||||
"weixin": "WeChat",
|
||||
"whatsapp": "WhatsApp"
|
||||
},
|
||||
"schedule": {
|
||||
"at": "Pada {{time}}",
|
||||
"every": "Setiap {{duration}}",
|
||||
|
||||
@@ -466,7 +466,7 @@
|
||||
"automations": {
|
||||
"kicker": "ワークスペースの自動タスク",
|
||||
"title": "自動タスク",
|
||||
"description": "cron リマインダー、定期的な agent ターン、一回限りの自動タスク、保護されたシステム自動タスクを一か所で確認できます。",
|
||||
"description": "cron リマインダー、定期的な agent ターン、一回限りのタスク、保護されたシステムタスクを確認できます。",
|
||||
"refresh": "更新",
|
||||
"newInChat": "チャットで作成",
|
||||
"stats": {
|
||||
@@ -515,6 +515,23 @@
|
||||
"unknown": "関連チャットなし",
|
||||
"legacy": "対象チャットで作り直してください"
|
||||
},
|
||||
"channels": {
|
||||
"api": "API",
|
||||
"cli": "CLI",
|
||||
"dingtalk": "DingTalk",
|
||||
"discord": "Discord",
|
||||
"email": "Email",
|
||||
"feishu": "Feishu",
|
||||
"matrix": "Matrix",
|
||||
"msteams": "Microsoft Teams",
|
||||
"qq": "QQ",
|
||||
"slack": "Slack",
|
||||
"telegram": "Telegram",
|
||||
"wechat": "WeChat",
|
||||
"wecom": "WeCom",
|
||||
"weixin": "WeChat",
|
||||
"whatsapp": "WhatsApp"
|
||||
},
|
||||
"schedule": {
|
||||
"at": "{{time}}",
|
||||
"every": "{{duration}} ごと",
|
||||
|
||||
@@ -466,7 +466,7 @@
|
||||
"automations": {
|
||||
"kicker": "작업 공간 자동화",
|
||||
"title": "자동화",
|
||||
"description": "cron 알림, 반복 agent 작업, 일회성 자동화, 보호된 시스템 자동화를 한곳에서 확인합니다.",
|
||||
"description": "cron 알림, 반복 agent 작업, 일회성 작업, 보호된 시스템 작업을 확인합니다.",
|
||||
"refresh": "새로 고침",
|
||||
"newInChat": "채팅에서 만들기",
|
||||
"stats": {
|
||||
@@ -515,6 +515,23 @@
|
||||
"unknown": "연결된 채팅 없음",
|
||||
"legacy": "대상 채팅에서 다시 만드세요"
|
||||
},
|
||||
"channels": {
|
||||
"api": "API",
|
||||
"cli": "CLI",
|
||||
"dingtalk": "DingTalk",
|
||||
"discord": "Discord",
|
||||
"email": "Email",
|
||||
"feishu": "Feishu",
|
||||
"matrix": "Matrix",
|
||||
"msteams": "Microsoft Teams",
|
||||
"qq": "QQ",
|
||||
"slack": "Slack",
|
||||
"telegram": "Telegram",
|
||||
"wechat": "WeChat",
|
||||
"wecom": "WeCom",
|
||||
"weixin": "WeChat",
|
||||
"whatsapp": "WhatsApp"
|
||||
},
|
||||
"schedule": {
|
||||
"at": "{{time}}",
|
||||
"every": "{{duration}}마다",
|
||||
|
||||
@@ -466,7 +466,7 @@
|
||||
"automations": {
|
||||
"kicker": "Tự động hóa không gian làm việc",
|
||||
"title": "Tự động hóa",
|
||||
"description": "Xem nhắc nhở cron, lượt agent định kỳ, tự động hóa một lần và tự động hóa hệ thống được bảo vệ tại một nơi.",
|
||||
"description": "Xem nhắc nhở cron, lượt agent định kỳ, tác vụ một lần và tác vụ hệ thống được bảo vệ.",
|
||||
"refresh": "Làm mới",
|
||||
"newInChat": "Tạo trong chat",
|
||||
"stats": {
|
||||
@@ -515,6 +515,23 @@
|
||||
"unknown": "Chưa liên kết cuộc trò chuyện",
|
||||
"legacy": "Tạo lại trong cuộc trò chuyện đích"
|
||||
},
|
||||
"channels": {
|
||||
"api": "API",
|
||||
"cli": "CLI",
|
||||
"dingtalk": "DingTalk",
|
||||
"discord": "Discord",
|
||||
"email": "Email",
|
||||
"feishu": "Feishu",
|
||||
"matrix": "Matrix",
|
||||
"msteams": "Microsoft Teams",
|
||||
"qq": "QQ",
|
||||
"slack": "Slack",
|
||||
"telegram": "Telegram",
|
||||
"wechat": "WeChat",
|
||||
"wecom": "WeCom",
|
||||
"weixin": "WeChat",
|
||||
"whatsapp": "WhatsApp"
|
||||
},
|
||||
"schedule": {
|
||||
"at": "Vào {{time}}",
|
||||
"every": "Mỗi {{duration}}",
|
||||
|
||||
@@ -466,7 +466,7 @@
|
||||
"automations": {
|
||||
"kicker": "工作区自动任务",
|
||||
"title": "自动任务",
|
||||
"description": "统一查看 cron 提醒、周期性 agent 任务、一次性自动任务和受保护的系统自动任务。",
|
||||
"description": "统一查看 cron 提醒、周期性 agent 任务、一次性任务和系统任务。",
|
||||
"refresh": "刷新",
|
||||
"newInChat": "在聊天中创建",
|
||||
"stats": {
|
||||
@@ -515,6 +515,23 @@
|
||||
"unknown": "未关联会话",
|
||||
"legacy": "请在目标会话中重新创建"
|
||||
},
|
||||
"channels": {
|
||||
"api": "API",
|
||||
"cli": "CLI",
|
||||
"dingtalk": "钉钉",
|
||||
"discord": "Discord",
|
||||
"email": "邮件",
|
||||
"feishu": "飞书",
|
||||
"matrix": "Matrix",
|
||||
"msteams": "Microsoft Teams",
|
||||
"qq": "QQ",
|
||||
"slack": "Slack",
|
||||
"telegram": "Telegram",
|
||||
"wechat": "微信",
|
||||
"wecom": "企业微信",
|
||||
"weixin": "微信",
|
||||
"whatsapp": "WhatsApp"
|
||||
},
|
||||
"schedule": {
|
||||
"at": "在 {{time}}",
|
||||
"every": "每 {{duration}}",
|
||||
|
||||
@@ -466,7 +466,7 @@
|
||||
"automations": {
|
||||
"kicker": "工作區自動任務",
|
||||
"title": "自動任務",
|
||||
"description": "集中查看 cron 提醒、週期性 agent 任務、一次性自動任務和受保護的系統自動任務。",
|
||||
"description": "集中查看 cron 提醒、週期性 agent 任務、一次性任務和系統任務。",
|
||||
"refresh": "重新整理",
|
||||
"newInChat": "在聊天中建立",
|
||||
"stats": {
|
||||
@@ -515,6 +515,23 @@
|
||||
"unknown": "未關聯會話",
|
||||
"legacy": "請在目標會話中重新建立"
|
||||
},
|
||||
"channels": {
|
||||
"api": "API",
|
||||
"cli": "CLI",
|
||||
"dingtalk": "釘釘",
|
||||
"discord": "Discord",
|
||||
"email": "電子郵件",
|
||||
"feishu": "飛書",
|
||||
"matrix": "Matrix",
|
||||
"msteams": "Microsoft Teams",
|
||||
"qq": "QQ",
|
||||
"slack": "Slack",
|
||||
"telegram": "Telegram",
|
||||
"wechat": "微信",
|
||||
"wecom": "企業微信",
|
||||
"weixin": "微信",
|
||||
"whatsapp": "WhatsApp"
|
||||
},
|
||||
"schedule": {
|
||||
"at": "於 {{time}}",
|
||||
"every": "每 {{duration}}",
|
||||
|
||||
@@ -385,6 +385,34 @@ describe("App layout", () => {
|
||||
},
|
||||
origin: null,
|
||||
},
|
||||
{
|
||||
id: "external-quiz",
|
||||
name: "WeChat quiz",
|
||||
enabled: true,
|
||||
protected: false,
|
||||
delete_after_run: false,
|
||||
schedule: { kind: "every", every_ms: 3_600_000 },
|
||||
payload: {
|
||||
message: "Send a quiz",
|
||||
kind: "agent_turn",
|
||||
session_key: "weixin:wx-chat",
|
||||
origin_channel: "weixin",
|
||||
origin_chat_id: "wx-chat",
|
||||
},
|
||||
state: {
|
||||
next_run_at_ms: Date.UTC(2026, 3, 17, 11, 30, 0),
|
||||
last_status: "ok",
|
||||
pending: false,
|
||||
run_history: [],
|
||||
},
|
||||
origin: {
|
||||
session_key: "weixin:wx-chat",
|
||||
channel: "weixin",
|
||||
chat_id: "wx-chat",
|
||||
title: "Scheduled cron job triggered",
|
||||
preview: "memory with dream state",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "heartbeat",
|
||||
name: "heartbeat",
|
||||
@@ -409,13 +437,17 @@ describe("App layout", () => {
|
||||
|
||||
fireEvent.click(automationsButton);
|
||||
|
||||
expect(await screen.findByText("Workspace automations")).toBeInTheDocument();
|
||||
expect(await screen.findByRole("heading", { name: "Automations" })).toBeInTheDocument();
|
||||
expect(screen.getByText("Daily repo check")).toBeInTheDocument();
|
||||
expect(screen.getByText("Check the repo status")).toBeInTheDocument();
|
||||
expect(screen.getByText("Release prep")).toBeInTheDocument();
|
||||
expect(screen.getByText("english-quiz")).toBeInTheDocument();
|
||||
expect(screen.getByText("Recreate in target chat")).toBeInTheDocument();
|
||||
expect(screen.queryByText("unified:default")).not.toBeInTheDocument();
|
||||
expect(screen.getByText("WeChat quiz")).toBeInTheDocument();
|
||||
expect(screen.getByText("WeChat")).toBeInTheDocument();
|
||||
expect(screen.queryByText("weixin:wx-chat")).not.toBeInTheDocument();
|
||||
expect(screen.queryByText("memory with dream state")).not.toBeInTheDocument();
|
||||
expect(screen.getByText("heartbeat")).toBeInTheDocument();
|
||||
expect(within(sidebar).getByRole("button", { name: "Automations" })).toHaveAttribute(
|
||||
"aria-current",
|
||||
@@ -473,9 +505,8 @@ describe("App layout", () => {
|
||||
const sidebar = screen.getByRole("navigation", { name: "侧边栏导航" });
|
||||
fireEvent.click(within(sidebar).getByRole("button", { name: "自动任务" }));
|
||||
|
||||
expect(await screen.findByText("工作区自动任务")).toBeInTheDocument();
|
||||
expect(screen.getAllByRole("heading", { name: "自动任务" }).length).toBeGreaterThan(0);
|
||||
expect(screen.getByText("统一查看 cron 提醒、周期性 agent 任务、一次性自动任务和受保护的系统自动任务。")).toBeInTheDocument();
|
||||
expect(await screen.findByRole("heading", { name: "自动任务" })).toBeInTheDocument();
|
||||
expect(screen.getByText("统一查看 cron 提醒、周期性 agent 任务、一次性任务和系统任务。")).toBeInTheDocument();
|
||||
expect(screen.getByRole("button", { name: "刷新" })).toBeInTheDocument();
|
||||
expect(screen.getByText("任务队列")).toBeInTheDocument();
|
||||
expect(screen.getByText("每日检查")).toBeInTheDocument();
|
||||
|
||||
Reference in New Issue
Block a user