chore: auto release commit (2026-09-15 01:02:50)
All checks were successful
技能自动化发布 / release (push) Successful in 6s

This commit is contained in:
2026-09-15 01:02:50 +08:00
parent 9878f570d7
commit c21339ae44
4 changed files with 58 additions and 12 deletions

View File

@@ -2,6 +2,14 @@
本文件对应技能市场 **「更新日志」** Tab。 本文件对应技能市场 **「更新日志」** Tab。
## 1.1.21
- **修复登录完成检测盲区**:`_wait_for_manual_login` 现在监听 3 个信号判断「登录已完成」,任一触发即继续
1. URL 含 `/app/` 且稳定 2 秒(v1.1.20 旧逻辑)
2. URL 已离开 `/login` / `/signin` / `/auth/` 等登录路径
3. 页面出现 sidebar 元素 `a[href$='/hr/contracts']`
- 解决「用户已经登录但脚本还在傻等」的回归:之前只判 URL 变化,cookie 已生效复用 profile 场景或 SPA 不跳 URL 的场景会卡 180 秒
## 1.1.20 ## 1.1.20
- **登录流程重写**:第一跳改为 `https://sandbox.jc2009.com/`(根域,用户最初看到的页面),**不做任何操作**等 URL 稳定 - **登录流程重写**:第一跳改为 `https://sandbox.jc2009.com/`(根域,用户最初看到的页面),**不做任何操作**等 URL 稳定

View File

@@ -1,7 +1,7 @@
--- ---
name: 合同到期智能预警与续聘辅助 name: 合同到期智能预警与续聘辅助
description: "当用户说要扫描即将到期的合同、查 90/60/30 天预警名单、给某位教师生成续聘任务书、下发单位版续聘清单、从 SmartCampus HR 平台抓合同数据、解析续聘材料 PDF/Word、查看本月合同到期人员清单时,加载本技能。支持定时扫描(每日凌晨)+ 数据管理 + Agent 三入口,覆盖合同到期智能预警与续聘辅助全流程。" description: "当用户说要扫描即将到期的合同、查 90/60/30 天预警名单、给某位教师生成续聘任务书、下发单位版续聘清单、从 SmartCampus HR 平台抓合同数据、解析续聘材料 PDF/Word、查看本月合同到期人员清单时,加载本技能。支持定时扫描(每日凌晨)+ 数据管理 + Agent 三入口,覆盖合同到期智能预警与续聘辅助全流程。"
version: 1.1.20 version: 1.1.21
author: 深圳匠厂科技有限公司 author: 深圳匠厂科技有限公司
metadata: metadata:
openclaw: openclaw:

View File

@@ -168,15 +168,19 @@ def _is_logged_in(page) -> bool:
def _wait_for_manual_login(page, wait_sec: int = HUMAN_WAIT_TIMEOUT) -> bool: def _wait_for_manual_login(page, wait_sec: int = HUMAN_WAIT_TIMEOUT) -> bool:
"""手工登录 fallback(v1.1.19 重写):等用户在浏览器输完账号密码提交。 """手工登录 fallback(v1.1.19 重写,v1.1.20 加强):等用户在浏览器完成登录。
判定「登录完成」的多个信号(满足任一即视为完成):
1. URL 含 `/app/` 且稳定 URL_STABLE_REQUIRED_S 秒(v1.1.19 旧逻辑)
2. URL 已离开 `/login` / `/signin` / `/auth/` 等登录路径(v1.1.20 新增)
—— 即便 sandbox 未跳到 /app/,只要离开登录页就认为登录成功
3. 页面出现 sidebar 元素 `a[href$='/hr/contracts']`(v1.1.20 新增)
—— 说明已加载到 SPA 主页
与之前轮询 _is_logged_in 不同的设计:
- **不做任何 goto / reload / click**,避免打断用户输入 - **不做任何 goto / reload / click**,避免打断用户输入
- 每 HUMAN_PROMPT_INTERVAL 秒打印一次剩余时间(默认 15s) - 每 HUMAN_PROMPT_INTERVAL 秒打印一次剩余时间(默认 15s)
- 每 HUMAN_POLL_INTERVAL 秒(默认 5s)只检查 URL,不做任何操作 - 每 HUMAN_POLL_INTERVAL 秒检查一次(默认 5s)
- 用户完成登录(URL 跳到 /app/...)后 **等 URL 真正稳定**,再返回 True - 超时抛 LoginTimeoutError。
超时抛 LoginTimeoutError。
""" """
print( print(
f"[登录] 请在浏览器中完成登录" f"[登录] 请在浏览器中完成登录"
@@ -187,12 +191,24 @@ def _wait_for_manual_login(page, wait_sec: int = HUMAN_WAIT_TIMEOUT) -> bool:
last_url = page.url last_url = page.url
last_url_change_at = time.monotonic() last_url_change_at = time.monotonic()
def _is_login_page_url(url: str) -> bool:
u = url.lower()
return (
"/login" in u or "/signin" in u or "/auth/" in u
or u.rstrip("/").endswith("sandbox.jc2009.com")
)
def _sidebar_present() -> bool:
try:
return page.locator("a[href$='/hr/contracts']").count() > 0
except Exception:
return False
while True: while True:
remaining = deadline - time.monotonic() remaining = deadline - time.monotonic()
if remaining <= 0: if remaining <= 0:
break break
# 用户输入完提交后,URL 会从 /login 跳到 /app/...
cur_url = page.url cur_url = page.url
if cur_url != last_url: if cur_url != last_url:
logger.info( logger.info(
@@ -201,16 +217,37 @@ def _wait_for_manual_login(page, wait_sec: int = HUMAN_WAIT_TIMEOUT) -> bool:
last_url = cur_url last_url = cur_url
last_url_change_at = time.monotonic() last_url_change_at = time.monotonic()
# 信号 1:URL 跳到 /app/... 并稳定
if _is_logged_in(page): if _is_logged_in(page):
# URL 已跳到 /app/...:等 URL 稳定 URL_STABLE_REQUIRED_S 秒再返回
stable_for = time.monotonic() - last_url_change_at stable_for = time.monotonic() - last_url_change_at
if stable_for >= URL_STABLE_REQUIRED_S: if stable_for >= URL_STABLE_REQUIRED_S:
print(f"[登录] ✅ 检测到登录完成(URL={cur_url}, 稳定 {stable_for:.1f}s)") print(
f"[登录] ✅ 检测到登录完成(URL 含 /app/,稳定 {stable_for:.1f}s)"
)
return True return True
logger.info( logger.info(
"platform_collector: 登录后 URL 还在过渡,等稳定 " "platform_collector: URL 含 /app/ 但还在过渡,等稳定 "
"(current=%s, stable_for=%.1fs)", cur_url, stable_for, "(current=%s, stable_for=%.1fs)", cur_url, stable_for,
) )
# 信号 2:URL 已离开登录页(即使不在 /app/,比如卡在中间过渡页也算)
elif not _is_login_page_url(cur_url):
stable_for = time.monotonic() - last_url_change_at
if stable_for >= URL_STABLE_REQUIRED_S:
print(
f"[登录] ✅ 检测到登录完成(URL 已离开登录页 → {cur_url},"
f"稳定 {stable_for:.1f}s)"
)
return True
logger.info(
"platform_collector: URL 已离开登录页但还没稳定,等 "
"(current=%s, stable_for=%.1fs)", cur_url, stable_for,
)
# 信号 3:sidebar 已出现
elif _sidebar_present():
print(
f"[登录] ✅ 检测到登录完成(sidebar 已出现,URL={cur_url})"
)
return True
else: else:
# 还在登录页:每 HUMAN_PROMPT_INTERVAL 秒提醒用户一次 # 还在登录页:每 HUMAN_PROMPT_INTERVAL 秒提醒用户一次
now = time.monotonic() now = time.monotonic()
@@ -225,6 +262,7 @@ def _wait_for_manual_login(page, wait_sec: int = HUMAN_WAIT_TIMEOUT) -> bool:
raise LoginTimeoutError( raise LoginTimeoutError(
f"自动登录失败,{wait_sec}s 内用户在浏览器中也未完成登录" f"自动登录失败,{wait_sec}s 内用户在浏览器中也未完成登录"
f"(最后 URL={page.url})"
) )

View File

@@ -6,7 +6,7 @@
""" """
SKILL_SLUG = "alert-contract-expiring-hr" SKILL_SLUG = "alert-contract-expiring-hr"
SKILL_VERSION = "1.1.20" SKILL_VERSION = "1.1.21"
LOG_LOGGER_NAME = "openclaw.skill.alert_contract_expiring_hr" LOG_LOGGER_NAME = "openclaw.skill.alert_contract_expiring_hr"
PLATFORM_KIT_MIN_VERSION = "1.2.11" PLATFORM_KIT_MIN_VERSION = "1.2.11"