Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 49e4d6e9a6 | |||
| b27dd5b5ba |
@@ -31,6 +31,9 @@ STEP_DELAY_MAX=3.0 # 单位:秒;默认与最短等待组
|
|||||||
# 人工处理超时:遇到滑块、验证码、二次验证时,等待人工处理的最长时间
|
# 人工处理超时:遇到滑块、验证码、二次验证时,等待人工处理的最长时间
|
||||||
HUMAN_WAIT_TIMEOUT=180 # 单位:秒;来不及处理时可调大,如 300
|
HUMAN_WAIT_TIMEOUT=180 # 单位:秒;来不及处理时可调大,如 300
|
||||||
|
|
||||||
|
# 浏览器启动超时(可选):限制 launch_persistent_browser 最长时间;空=不限制(默认)
|
||||||
|
# OPENCLAW_BROWSER_LAUNCH_TIMEOUT_MS=60000 # 单位:毫秒;慢机器/限频时可开启并调大
|
||||||
|
|
||||||
# 以下为可选目录配置。一般不用改;取消注释并填写后,才覆盖默认子文件夹。
|
# 以下为可选目录配置。一般不用改;取消注释并填写后,才覆盖默认子文件夹。
|
||||||
# 填写相对路径时,相对于本技能的数据目录(不是当前工作目录)。
|
# 填写相对路径时,相对于本技能的数据目录(不是当前工作目录)。
|
||||||
# 用户可见只有 input(上行)与 output(下行);见 development/DATA_PATHS.md。
|
# 用户可见只有 input(上行)与 output(下行);见 development/DATA_PATHS.md。
|
||||||
|
|||||||
14
CHANGELOG.md
14
CHANGELOG.md
@@ -9,6 +9,20 @@
|
|||||||
- 小节正文写用户能看懂的变化(新能力、修复、注意事项),避免堆砌内部实现细节
|
- 小节正文写用户能看懂的变化(新能力、修复、注意事项),避免堆砌内部实现细节
|
||||||
- 打 tag 前**必须**为即将发布的版本新增一节;找不到匹配小节时,本次发布不会更新 changelog 字段
|
- 打 tag 前**必须**为即将发布的版本新增一节;找不到匹配小节时,本次发布不会更新 changelog 字段
|
||||||
|
|
||||||
|
## 1.0.79
|
||||||
|
|
||||||
|
- 沉淀数据管理表级「默认展示列」契约:`_jiangchang_tables.default_visible_columns_json`
|
||||||
|
- 已配置则宿主默认只显示名单列;未配置全显;用户改过「列」后以本机偏好为准
|
||||||
|
- 与 `_jiangchang_columns.visible=0` 硬隐藏区分:默认隐藏仍可在「列」面板勾回
|
||||||
|
- `upsert_table_metadata` / 旧库幂等 `ALTER` 已支持该字段(见 `references/SCHEMA.md`)
|
||||||
|
|
||||||
|
## 1.0.78
|
||||||
|
|
||||||
|
- 反哺 RPA 有界就绪等待约定:状态等待统一用 kit `poll_until`(需 `jiangchang-platform-kit>=1.2.11`)
|
||||||
|
- 错误码表补充 `UI_NOT_READY` / `BROWSER_START_TIMEOUT` / `ENV_DEGRADED`,明确慢机器勿误判为配置错或频控
|
||||||
|
- 模板默认 `platform_kit_min_version` 上调至 `1.2.11`;`.env.example` 增加可选 `OPENCLAW_BROWSER_LAUNCH_TIMEOUT_MS`
|
||||||
|
- `examples/real_browser_rpa` 搜索结果等待改为 `poll_until` 示例写法
|
||||||
|
|
||||||
## 1.0.77
|
## 1.0.77
|
||||||
|
|
||||||
- 固化 Action / CRUD **对称**表单字段类型契约:text、textarea/json、number、boolean、enum/lookup、sensitive、date、datetime_unix_seconds、url/email(薄层)
|
- 固化 Action / CRUD **对称**表单字段类型契约:text、textarea/json、number、boolean、enum/lookup、sensitive、date、datetime_unix_seconds、url/email(薄层)
|
||||||
|
|||||||
6
SKILL.md
6
SKILL.md
@@ -1,12 +1,12 @@
|
|||||||
---
|
---
|
||||||
name: 技能开发模板(通用业务版)
|
name: 技能开发模板(通用业务版)
|
||||||
description: "OpenClaw 通用业务技能开发模板,供复制后定制新业务 skill。定制步骤见 development/DEVELOPMENT.md。"
|
description: "OpenClaw 通用业务技能开发模板,供复制后定制新业务 skill。定制步骤见 development/DEVELOPMENT.md。"
|
||||||
version: 1.0.77
|
version: 1.0.79
|
||||||
author: 深圳匠厂科技有限公司
|
author: 深圳匠厂科技有限公司
|
||||||
metadata:
|
metadata:
|
||||||
openclaw:
|
openclaw:
|
||||||
slug: your-skill-slug
|
slug: your-skill-slug
|
||||||
platform_kit_min_version: "1.2.2"
|
platform_kit_min_version: "1.2.11"
|
||||||
emoji: "📦"
|
emoji: "📦"
|
||||||
category: "通用"
|
category: "通用"
|
||||||
developer_ids:
|
developer_ids:
|
||||||
@@ -83,7 +83,7 @@ python {baseDir}/scripts/main.py config-path
|
|||||||
python {baseDir}/scripts/main.py init-db
|
python {baseDir}/scripts/main.py init-db
|
||||||
```
|
```
|
||||||
|
|
||||||
- 公共库来自共享 venv 的 `jiangchang-platform-kit>=1.2.2`(包名路径 `jiangchang_skill_core`);**不要 vendor** `scripts/jiangchang_skill_core/`。
|
- 公共库来自共享 venv 的 `jiangchang-platform-kit>=1.2.11`(包名路径 `jiangchang_skill_core`);**不要 vendor** `scripts/jiangchang_skill_core/`。
|
||||||
- 根目录 `requirements.txt` **只声明技能特有**依赖;不要写 `jiangchang-platform-kit` / `playwright`。
|
- 根目录 `requirements.txt` **只声明技能特有**依赖;不要写 `jiangchang-platform-kit` / `playwright`。
|
||||||
- `metadata.openclaw.platform_kit_min_version` 是宿主兼容声明,**不是** pip 依赖。
|
- `metadata.openclaw.platform_kit_min_version` 是宿主兼容声明,**不是** pip 依赖。
|
||||||
- `health` 用 `collect_runtime_diagnostics` 做只读诊断。
|
- `health` 用 `collect_runtime_diagnostics` 做只读诊断。
|
||||||
|
|||||||
@@ -141,6 +141,13 @@ Agent 与编排层必须确认:
|
|||||||
|
|
||||||
页面加载、控件可用、弹窗消失、保存成功、下载完成等必须等待**真实条件**并设置超时。固定等待不能代替状态判断。
|
页面加载、控件可用、弹窗消失、保存成功、下载完成等必须等待**真实条件**并设置超时。固定等待不能代替状态判断。
|
||||||
|
|
||||||
|
自 **platform-kit 1.2.11** / 本模板 **1.0.78** 起:
|
||||||
|
|
||||||
|
- 统一使用 `jiangchang_skill_core.rpa.poll_until`(或 `poll_until_sync`),内部已接 `interruptible_sleep`。
|
||||||
|
- 先有界等待就绪,再区分「加载慢」与「页不对」;超时优先 `ERROR:UI_NOT_READY`。
|
||||||
|
- 不要把慢机器症状映射成配置错误、网络错误或频控,除非有明确证据。
|
||||||
|
- 详细表与纪律见 [`RPA.md`](RPA.md) §0.3 / §4。
|
||||||
|
|
||||||
### 操作节奏
|
### 操作节奏
|
||||||
|
|
||||||
真实业务环境中,两个正常业务动作之间默认随机等待 **1~3 秒**:
|
真实业务环境中,两个正常业务动作之间默认随机等待 **1~3 秒**:
|
||||||
|
|||||||
@@ -104,6 +104,9 @@ STEP_DELAY_MAX=3.0 # 单位:秒;默认与最短等待组
|
|||||||
|
|
||||||
# 人工处理超时:遇到滑块、验证码、二次验证时,等待人工处理的最长时间
|
# 人工处理超时:遇到滑块、验证码、二次验证时,等待人工处理的最长时间
|
||||||
HUMAN_WAIT_TIMEOUT=180 # 单位:秒;来不及处理时可调大,如 300
|
HUMAN_WAIT_TIMEOUT=180 # 单位:秒;来不及处理时可调大,如 300
|
||||||
|
|
||||||
|
# 浏览器启动超时(可选):限制 launch 最长时间;注释掉或不设=不限制
|
||||||
|
# OPENCLAW_BROWSER_LAUNCH_TIMEOUT_MS=60000 # 单位:毫秒;慢机器时可开启
|
||||||
```
|
```
|
||||||
|
|
||||||
四档运行模式的**实现与测试约定**见 [`ADAPTER.md`](ADAPTER.md);录屏管线见 [`RPA.md`](RPA.md)。这些内容**不要**写回 `.env.example` 注释。
|
四档运行模式的**实现与测试约定**见 [`ADAPTER.md`](ADAPTER.md);录屏管线见 [`RPA.md`](RPA.md)。这些内容**不要**写回 `.env.example` 注释。
|
||||||
@@ -167,7 +170,8 @@ config.get_float("STEP_DELAY_MIN", 1.0)
|
|||||||
### 空值、行尾注释与假默认(硬规则)
|
### 空值、行尾注释与假默认(硬规则)
|
||||||
|
|
||||||
- **留空即未配置**:`KEY=` 或 `KEY= # 说明` 必须读成空字符串,再走技能自己的「自动 / ensure」逻辑;**禁止**把示例说明、`# 可填…` 当成真实配置值。
|
- **留空即未配置**:`KEY=` 或 `KEY= # 说明` 必须读成空字符串,再走技能自己的「自动 / ensure」逻辑;**禁止**把示例说明、`# 可填…` 当成真实配置值。
|
||||||
- **需要 platform-kit ≥ 1.2.2**:旧版解析曾把「空值 + 行尾 `#`」误当成 `# 可填…` 这种假值。技能若依赖正确空值语义,须在 `SKILL.md` 声明 `platform_kit_min_version: "1.2.2"`(或更高)。
|
- **需要 platform-kit ≥ 1.2.2**:旧版解析曾把「空值 + 行尾 `#`」误当成 `# 可填…` 这种假值。技能若依赖正确空值语义,须在 `SKILL.md` 声明不低于该版本。
|
||||||
|
- **需要 platform-kit ≥ 1.2.11(本模板默认)**:RPA 有界就绪等待 `poll_until`、可选 `OPENCLAW_BROWSER_LAUNCH_TIMEOUT_MS`、health 中只读 `machine_power`。新技能直接声明 `"1.2.11"`。
|
||||||
- **网页账号配置语义**(有则用):
|
- **网页账号配置语义**(有则用):
|
||||||
- `DEFAULT_ACCOUNT_ID`:仅**纯数字**视为账号主键;校验指定记录后仍必须取得该账号租约,不能只 `account get`
|
- `DEFAULT_ACCOUNT_ID`:仅**纯数字**视为账号主键;校验指定记录后仍必须取得该账号租约,不能只 `account get`
|
||||||
- `DEFAULT_LOGIN_ID`:手机号 / 用户名等;留空则按平台 `ensure-web`;非数字 `DEFAULT_ACCOUNT_ID` 可兼容为 login_id,但不推荐
|
- `DEFAULT_LOGIN_ID`:手机号 / 用户名等;留空则按平台 `ensure-web`;非数字 `DEFAULT_ACCOUNT_ID` 可兼容为 login_id,但不推荐
|
||||||
|
|||||||
@@ -127,7 +127,7 @@ scripts/
|
|||||||
作用:常量、日志、路径、时间工具、通用帮助函数
|
作用:常量、日志、路径、时间工具、通用帮助函数
|
||||||
`util/logging_config.py` 是 `jiangchang_skill_core.unified_logging` 的**薄封装**,业务代码应通过它获取 logger
|
`util/logging_config.py` 是 `jiangchang_skill_core.unified_logging` 的**薄封装**,业务代码应通过它获取 logger
|
||||||
|
|
||||||
公共能力(config、logging、runtime_env、rpa、media_assets、video_session、runtime_diagnostics、activity/SRCP)从共享 runtime 的 `jiangchang-platform-kit>=1.2.2` import,**不得**在 `scripts/` 下保留 `jiangchang_skill_core/` 副本。
|
公共能力(config、logging、runtime_env、rpa、media_assets、video_session、runtime_diagnostics、activity/SRCP)从共享 runtime 的 `jiangchang-platform-kit>=1.2.11` import,**不得**在 `scripts/` 下保留 `jiangchang_skill_core/` 副本。
|
||||||
|
|
||||||
## 3.2 开发 RPA 类 skill
|
## 3.2 开发 RPA 类 skill
|
||||||
|
|
||||||
@@ -170,7 +170,7 @@ scripts/
|
|||||||
技能根目录的 `requirements.txt` 是**标准文件**,用于声明本技能**特有** Python 三方依赖。
|
技能根目录的 `requirements.txt` 是**标准文件**,用于声明本技能**特有** Python 三方依赖。
|
||||||
|
|
||||||
- **公共依赖**(`jiangchang-platform-kit`、`playwright`、config、runtime diagnostics、RPA 公共能力等)由**宿主共享 runtime** 提供,**不要**写入技能 `requirements.txt`。
|
- **公共依赖**(`jiangchang-platform-kit`、`playwright`、config、runtime diagnostics、RPA 公共能力等)由**宿主共享 runtime** 提供,**不要**写入技能 `requirements.txt`。
|
||||||
- `SKILL.md` 的 `metadata.openclaw.platform_kit_min_version`(当前 `1.2.2`)是运行契约/兼容性声明,供宿主安装与启用时校验,**不是** pip 依赖声明。
|
- `SKILL.md` 的 `metadata.openclaw.platform_kit_min_version`(当前 `1.2.11`)是运行契约/兼容性声明,供宿主安装与启用时校验,**不是** pip 依赖声明。
|
||||||
- 匠厂宿主安装/更新技能后,会将技能 `requirements.txt` 安装到共享 venv:`{JIANGCHANG_DATA_ROOT}/python-runtime/.venv`。
|
- 匠厂宿主安装/更新技能后,会将技能 `requirements.txt` 安装到共享 venv:`{JIANGCHANG_DATA_ROOT}/python-runtime/.venv`。
|
||||||
- **不要**在业务代码中 `subprocess` / `pip install`;缺依赖由 `health` 报错,由宿主负责安装。
|
- **不要**在业务代码中 `subprocess` / `pip install`;缺依赖由 `health` 报错,由宿主负责安装。
|
||||||
- **版本约束尽量收窄**,降低多技能共享 venv 时的冲突风险。推荐范围写法:
|
- **版本约束尽量收窄**,降低多技能共享 venv 时的冲突风险。推荐范围写法:
|
||||||
|
|||||||
@@ -65,7 +65,7 @@
|
|||||||
| RPA 阶段日志是否「够细」(每个控件) | development/LOGGING.md §3.2 | hard 已要求里程碑事件词与域模块有日志;逐步粒度靠 sample 契约与验收 |
|
| RPA 阶段日志是否「够细」(每个控件) | development/LOGGING.md §3.2 | hard 已要求里程碑事件词与域模块有日志;逐步粒度靠 sample 契约与验收 |
|
||||||
| adapter 四档契约测试覆盖 timeout/unauthorized 等 | development/ADAPTER.md §contract tests | 需业务实现后人工补测 |
|
| adapter 四档契约测试覆盖 timeout/unauthorized 等 | development/ADAPTER.md §contract tests | 需业务实现后人工补测 |
|
||||||
| `SKILL.md` / `constants.SKILL_SLUG` 一致性 | development/DEVELOPMENT.md §16 | 已有 `tests/test_skill_metadata.py` |
|
| `SKILL.md` / `constants.SKILL_SLUG` 一致性 | development/DEVELOPMENT.md §16 | 已有 `tests/test_skill_metadata.py` |
|
||||||
| platform_kit_min_version >= 1.2.2 | development/RUNTIME.md | 已有 `tests/test_platform_import.py` |
|
| platform_kit_min_version >= 1.2.11 | development/RUNTIME.md | 已有 `tests/test_platform_import.py` |
|
||||||
| 宿主按 `bind.tables` 过滤 toolbar 按钮 | references/ACTIONS.md | 待宿主升级后完整生效;模板侧已强制显式绑定 |
|
| 宿主按 `bind.tables` 过滤 toolbar 按钮 | references/ACTIONS.md | 待宿主升级后完整生效;模板侧已强制显式绑定 |
|
||||||
| Data Policy 未启用(L0)时宿主不得拉 Snapshot / 不得改按钮与行列行为 | references/POLICY.md | soft;宿主实现契约 |
|
| Data Policy 未启用(L0)时宿主不得拉 Snapshot / 不得改按钮与行列行为 | references/POLICY.md | soft;宿主实现契约 |
|
||||||
| Data Policy 启用后 fail-closed(禁止回退 L0 全开);写路径 domain 必须再鉴权 | references/POLICY.md §6–§7 | soft;`POLICY-SKILL-AUTHZ-*` 硬门禁可后续加 |
|
| Data Policy 启用后 fail-closed(禁止回退 L0 全开);写路径 domain 必须再鉴权 | references/POLICY.md §6–§7 | soft;`POLICY-SKILL-AUTHZ-*` 硬门禁可后续加 |
|
||||||
|
|||||||
@@ -99,6 +99,16 @@ async def open_login(page):
|
|||||||
|
|
||||||
**状态等待优先:**页面加载、控件可用、弹窗消失、保存成功、下载完成和业务结果出现,必须等待真实条件并设置超时;禁止用固定 `sleep(3)` 假设成功。
|
**状态等待优先:**页面加载、控件可用、弹窗消失、保存成功、下载完成和业务结果出现,必须等待真实条件并设置超时;禁止用固定 `sleep(3)` 假设成功。
|
||||||
|
|
||||||
|
**有界就绪等待(platform-kit ≥ 1.2.11 必用):**
|
||||||
|
|
||||||
|
- 业务技能的状态等待统一使用共享 API:`jiangchang_skill_core.rpa.poll_until`(async)或 `poll_until_sync`。
|
||||||
|
- **禁止**在 service 主路径再发明平行的 `deadline + sleep` / 私有 `_wait_*` 轮询作为标准答案(HITL 专用等待除外,仍可用 kit 的 `wait_for_login` / `wait_for_captcha_pass`)。
|
||||||
|
- 必须区分:**还在加载**(超时内继续等)vs **真的不是目标页/目标态**(有界等待失败后再判)。
|
||||||
|
- 超时默认映射 `ERROR:UI_NOT_READY`(或业务等价就绪码);**不要**把「慢」直接判成 `PAGE_TYPE_MISMATCH`、配置门禁失败或 `ERROR:RATE_LIMITED`。
|
||||||
|
- 超时秒数须允许环境变量 / `.env` 覆盖(慢机器、限频、轻量电源场景可调大)。
|
||||||
|
- 可选浏览器启动超时:`launch_persistent_browser(..., timeout_ms=...)` 或 `OPENCLAW_BROWSER_LAUNCH_TIMEOUT_MS`;超时码 `ERROR:BROWSER_START_TIMEOUT`。默认不开启,与历史行为兼容。
|
||||||
|
- `health` 可带只读 `machine_power`(供电/限频提示);**不得**仅凭该信号 hard-fail 全技能。
|
||||||
|
|
||||||
**操作节奏:**
|
**操作节奏:**
|
||||||
|
|
||||||
- 两个正常业务动作之间默认随机等待 `1~3` 秒。
|
- 两个正常业务动作之间默认随机等待 `1~3` 秒。
|
||||||
@@ -359,11 +369,20 @@ skill 退出/抛错统一用 `ERROR:` 前缀 + 稳定码,方便宿主与上层
|
|||||||
| `ERROR:AMBIGUOUS_ACCOUNT` | `--login-id` / `--label` 匹配到多条 | 改用数字 id 或更精确标识 |
|
| `ERROR:AMBIGUOUS_ACCOUNT` | `--login-id` / `--label` 匹配到多条 | 改用数字 id 或更精确标识 |
|
||||||
| `ERROR:RATE_LIMITED` | 触发频控 | 退避后重试 |
|
| `ERROR:RATE_LIMITED` | 触发频控 | 退避后重试 |
|
||||||
| `ERROR:MISSING_BROWSER` | 未检测到 Chrome/Edge | 提示安装 |
|
| `ERROR:MISSING_BROWSER` | 未检测到 Chrome/Edge | 提示安装 |
|
||||||
|
| `ERROR:BROWSER_START_TIMEOUT` | 浏览器启动超时(可选 timeout) | 可重试;检查机器负载/供电;可调大 `OPENCLAW_BROWSER_LAUNCH_TIMEOUT_MS` |
|
||||||
|
| `ERROR:UI_NOT_READY` | 有界等待内页面/控件未就绪 | **默认可重试**;勿与「页类型错误/配置未开」混用;勿轻易标 session fatal |
|
||||||
|
| `ERROR:ENV_DEGRADED` | 环境降级提示类(限频/供电不足等,技能选用) | 可重试;提示接电源/退出省电/减小并发;**不是**配置错误 |
|
||||||
| `ERROR:DEVICE_NOT_READY` | 手机未连接/未授权 | 检查 USB/ADB |
|
| `ERROR:DEVICE_NOT_READY` | 手机未连接/未授权 | 检查 USB/ADB |
|
||||||
| `ERROR:WINDOW_NOT_FOUND` | 桌面目标窗口未找到 | 检查程序是否启动 |
|
| `ERROR:WINDOW_NOT_FOUND` | 桌面目标窗口未找到 | 检查程序是否启动 |
|
||||||
|
|
||||||
业务空结果(如「本页无评论」)用业务码或成功空列表表达,**不要**复用上表账号/登录错误码。
|
业务空结果(如「本页无评论」)用业务码或成功空列表表达,**不要**复用上表账号/登录错误码。
|
||||||
|
|
||||||
|
**归因纪律(防误诊):**
|
||||||
|
|
||||||
|
- 「未开 `ALLOW_REAL_RPA` / 档位门禁」必须用技能自己的配置类错误码,**禁止**与真实启动失败、启动超时共用一个 hint。
|
||||||
|
- 页面慢导致的超时优先 `UI_NOT_READY` / 业务就绪码;仅当有明确频控 DOM/文案时才用 `RATE_LIMITED`。
|
||||||
|
- 是否列入技能 `SESSION_FATAL` / `RETRYABLE` 由技能自定,但 **UI 未就绪 / 启动超时默认不应整批不可重试终止**(除非会话已明确不可恢复,如未登录、验证码人工超时)。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 5. 存证与录屏规范
|
## 5. 存证与录屏规范
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
## 共享 Python Runtime
|
## 共享 Python Runtime
|
||||||
|
|
||||||
**skill-template** 及复制出的新技能,公共能力均来自宿主匠厂安装的共享 Python Runtime(`jiangchang-platform-kit>=1.2.2` 及其传递依赖,含 `playwright`)。`jiangchang_skill_core` **不得**在技能仓库内 vendored,应由共享 venv 的 site-packages 提供。
|
**skill-template** 及复制出的新技能,公共能力均来自宿主匠厂安装的共享 Python Runtime(`jiangchang-platform-kit>=1.2.11` 及其传递依赖,含 `playwright`)。`jiangchang_skill_core` **不得**在技能仓库内 vendored,应由共享 venv 的 site-packages 提供。
|
||||||
|
|
||||||
技能根目录 `requirements.txt` **只声明技能特有依赖**;**不要**重复声明 `jiangchang-platform-kit` 或 `playwright`。`SKILL.md` 的 `platform_kit_min_version` 是运行契约,**不是** pip 依赖声明。
|
技能根目录 `requirements.txt` **只声明技能特有依赖**;**不要**重复声明 `jiangchang-platform-kit` 或 `playwright`。`SKILL.md` 的 `platform_kit_min_version` 是运行契约,**不是** pip 依赖声明。
|
||||||
|
|
||||||
@@ -12,7 +12,7 @@ kit **源码仓**(可 clone 学习,勿 vendor)与其它公开公共仓登
|
|||||||
|
|
||||||
| 情况 | 做法 |
|
| 情况 | 做法 |
|
||||||
|------|------|
|
|------|------|
|
||||||
| 技能依赖 kit 某次修复/新 API(如 `.env` 空值+行尾 `#` 解析、新 RPA helper) | 把 `metadata.openclaw.platform_kit_min_version` 调到**需要的最低版本**(例如 `"1.2.2"`) |
|
| 技能依赖 kit 某次修复/新 API(如 `.env` 空值+行尾 `#` 解析、`poll_until`、启动超时) | 把 `metadata.openclaw.platform_kit_min_version` 调到**需要的最低版本**(模板默认 `"1.2.11"`) |
|
||||||
| PyPI/私有源刚发了更新,但本技能行为未依赖 | **不必**仅为「追新」抬版本 |
|
| PyPI/私有源刚发了更新,但本技能行为未依赖 | **不必**仅为「追新」抬版本 |
|
||||||
| 宿主升级时机 | 宿主在技能/宿主声明的最低版本**未满足**时按需升级共享 venv;**不会**仅因源上有更新就后台拉 latest |
|
| 宿主升级时机 | 宿主在技能/宿主声明的最低版本**未满足**时按需升级共享 venv;**不会**仅因源上有更新就后台拉 latest |
|
||||||
|
|
||||||
@@ -39,7 +39,7 @@ Unix(未注入时):
|
|||||||
|
|
||||||
共享解释器通常位于 `{JIANGCHANG_DATA_ROOT}/python-runtime/.venv`。数据根由宿主注入(见 `jiangchang_skill_core.runtime_env`)。
|
共享解释器通常位于 `{JIANGCHANG_DATA_ROOT}/python-runtime/.venv`。数据根由宿主注入(见 `jiangchang_skill_core.runtime_env`)。
|
||||||
|
|
||||||
## Runtime 诊断(platform-kit 1.2.2+)
|
## Runtime 诊断(platform-kit 1.2.11+;含既有 1.2.2+ 字段)
|
||||||
|
|
||||||
`health` 命令通过 **`jiangchang_skill_core.collect_runtime_diagnostics`** 输出共享 runtime 诊断,**不在技能内重复实现**。典型字段:
|
`health` 命令通过 **`jiangchang_skill_core.collect_runtime_diagnostics`** 输出共享 runtime 诊断,**不在技能内重复实现**。典型字段:
|
||||||
|
|
||||||
@@ -48,6 +48,7 @@ Unix(未注入时):
|
|||||||
- `JIANGCHANG_DATA_ROOT` / `resolved_data_root`
|
- `JIANGCHANG_DATA_ROOT` / `resolved_data_root`
|
||||||
- `media_assets_root`、`ffmpeg_path`、`background_music_*`
|
- `media_assets_root`、`ffmpeg_path`、`background_music_*`
|
||||||
- `record_video_enabled`、`runtime_issue[warning|error]`
|
- `record_video_enabled`、`runtime_issue[warning|error]`
|
||||||
|
- `machine_power` — 只读供电/限频探测(探不到则 `probed=false`);**不得**单独作为 hard-fail 依据
|
||||||
|
|
||||||
`health` 是**只读诊断**,**不会**触发 media-assets 下载或修复。并补充 `env_path` / `env_exists` / `example_path` 配置路径字段。
|
`health` 是**只读诊断**,**不会**触发 media-assets 下载或修复。并补充 `env_path` / `env_exists` / `example_path` 配置路径字段。
|
||||||
|
|
||||||
@@ -57,7 +58,7 @@ Unix(未注入时):
|
|||||||
- 用户实际 `.env`:`{JIANGCHANG_DATA_ROOT}/{JIANGCHANG_USER_ID}/{skill_slug}/.env`。
|
- 用户实际 `.env`:`{JIANGCHANG_DATA_ROOT}/{JIANGCHANG_USER_ID}/{skill_slug}/.env`。
|
||||||
- `scripts/main.py` 与 `cli.app.main()` 启动时调用 `util.config_bootstrap.bootstrap_skill_config()`。
|
- `scripts/main.py` 与 `cli.app.main()` 启动时调用 `util.config_bootstrap.bootstrap_skill_config()`。
|
||||||
- 配置优先级:**进程环境变量** > **用户 `.env`** > **`.env.example` 默认值**。
|
- 配置优先级:**进程环境变量** > **用户 `.env`** > **`.env.example` 默认值**。
|
||||||
- 公共 `config` / `merge_missing_env_keys` 来自共享 runtime 的 `jiangchang-platform-kit>=1.2.2`,**不得** vendored `scripts/jiangchang_skill_core/`。
|
- 公共 `config` / `merge_missing_env_keys` 来自共享 runtime 的 `jiangchang-platform-kit>=1.2.11`,**不得** vendored `scripts/jiangchang_skill_core/`。
|
||||||
|
|
||||||
## media-assets / ffmpeg / 背景音乐
|
## media-assets / ffmpeg / 背景音乐
|
||||||
|
|
||||||
|
|||||||
@@ -265,7 +265,7 @@ Golden fixture 流程同理([`tests/samples/test_golden_cases.py.sample`](../t
|
|||||||
|
|
||||||
- [ ] `requirements.txt` **不含** `jiangchang-platform-kit` / `playwright`
|
- [ ] `requirements.txt` **不含** `jiangchang-platform-kit` / `playwright`
|
||||||
- [ ] 无 `scripts/jiangchang_skill_core/` vendored 副本
|
- [ ] 无 `scripts/jiangchang_skill_core/` vendored 副本
|
||||||
- [ ] `platform_kit_min_version` **>= 1.2.2**(`SKILL.md` + `constants.py`)
|
- [ ] `platform_kit_min_version` **>= 1.2.11**(`SKILL.md` + `constants.py`)
|
||||||
- [ ] `health` 能输出 `platform_kit_version_ok`(或等价诊断行)
|
- [ ] `health` 能输出 `platform_kit_version_ok`(或等价诊断行)
|
||||||
- [ ] `config-path` 可输出用户 `.env` 路径 JSON
|
- [ ] `config-path` 可输出用户 `.env` 路径 JSON
|
||||||
- [ ] `pytest.ini` 存在且 `python_files` 只收集 `test_*.py` / `*_test.py`
|
- [ ] `pytest.ini` 存在且 `python_files` 只收集 `test_*.py` / `*_test.py`
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ from typing import Any, Callable, Dict, List, Optional, Tuple
|
|||||||
|
|
||||||
from jiangchang_skill_core import config
|
from jiangchang_skill_core import config
|
||||||
from jiangchang_skill_core.activity import interruptible_sleep
|
from jiangchang_skill_core.activity import interruptible_sleep
|
||||||
|
from jiangchang_skill_core.rpa import poll_until
|
||||||
|
|
||||||
from service.browser_session import close_browser_context, get_start_url, start_browser_session
|
from service.browser_session import close_browser_context, get_start_url, start_browser_session
|
||||||
from service.human_verification import (
|
from service.human_verification import (
|
||||||
@@ -236,17 +237,20 @@ async def _find_search_button(page):
|
|||||||
|
|
||||||
|
|
||||||
async def _wait_search_results(page, timeout_sec: float = 45.0) -> bool:
|
async def _wait_search_results(page, timeout_sec: float = 45.0) -> bool:
|
||||||
deadline = time.monotonic() + timeout_sec
|
"""有界等待搜索结果容器出现(kit poll_until;区分加载中与超时未就绪)。"""
|
||||||
while time.monotonic() < deadline:
|
|
||||||
|
async def _ready() -> bool:
|
||||||
try:
|
try:
|
||||||
url = page.url or ""
|
url = page.url or ""
|
||||||
if "/search" in url or "search" in url.lower():
|
if "/search" in url or "search" in url.lower():
|
||||||
if await page.locator(RESULT_CONTAINER_SELECTOR).count() > 0:
|
if await page.locator(RESULT_CONTAINER_SELECTOR).count() > 0:
|
||||||
return True
|
return True
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
return False
|
||||||
await interruptible_sleep(0.8)
|
return False
|
||||||
return False
|
|
||||||
|
result = await poll_until(_ready, timeout_sec=timeout_sec, interval_sec=0.8)
|
||||||
|
return result.ok
|
||||||
|
|
||||||
|
|
||||||
async def _has_end_marker(page) -> bool:
|
async def _has_end_marker(page) -> bool:
|
||||||
|
|||||||
@@ -473,6 +473,7 @@ HINT: <next step>
|
|||||||
- 臆造 `sortOrder` / `order` / `priority` 等字段指望宿主排序(模板 Schema `additionalProperties: false` 会拒收)。
|
- 臆造 `sortOrder` / `order` / `priority` 等字段指望宿主排序(模板 Schema `additionalProperties: false` 会拒收)。
|
||||||
- 依赖或宣传表顶栏「更多操作」溢出菜单(宿主已取消该能力)。
|
- 依赖或宣传表顶栏「更多操作」溢出菜单(宿主已取消该能力)。
|
||||||
- 把「表格行数据默认排序」与按钮顺序混淆——行排序见 [`SCHEMA.md`](SCHEMA.md) 的 `default_sort_json`。
|
- 把「表格行数据默认排序」与按钮顺序混淆——行排序见 [`SCHEMA.md`](SCHEMA.md) 的 `default_sort_json`。
|
||||||
|
- 把「默认展示列」与 `visible=0` 硬隐藏混淆——默认展示列见 [`SCHEMA.md`](SCHEMA.md) 的 `default_visible_columns_json`。
|
||||||
- 新增 `row-context-menu` 等 placement,或要求技能再维护一份与 `row` 平行的菜单清单。
|
- 新增 `row-context-menu` 等 placement,或要求技能再维护一份与 `row` 平行的菜单清单。
|
||||||
|
|
||||||
### Action 类型 × placements(常见示例,不是限制)
|
### Action 类型 × placements(常见示例,不是限制)
|
||||||
|
|||||||
@@ -139,6 +139,7 @@ metadata:
|
|||||||
| `visible` | 是 | `1` 在宿主数据管理中展示 / `0` 隐藏 |
|
| `visible` | 是 | `1` 在宿主数据管理中展示 / `0` 隐藏 |
|
||||||
| `readonly` | 是 | `1` 整张表只读;`0` 允许宿主根据字段权限执行写操作 |
|
| `readonly` | 是 | `1` 整张表只读;`0` 允许宿主根据字段权限执行写操作 |
|
||||||
| `default_sort_json` | 否 | 数据管理**默认行排序**(见下节);空则宿主用内置默认 |
|
| `default_sort_json` | 否 | 数据管理**默认行排序**(见下节);空则宿主用内置默认 |
|
||||||
|
| `default_visible_columns_json` | 否 | 数据管理**默认展示列**(见下节);空则全显 |
|
||||||
|
|
||||||
#### 表级默认排序 `default_sort_json`(宿主支持)
|
#### 表级默认排序 `default_sort_json`(宿主支持)
|
||||||
|
|
||||||
@@ -171,12 +172,49 @@ upsert_table_metadata(
|
|||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### 表级默认展示列 `default_visible_columns_json`(宿主支持)
|
||||||
|
|
||||||
|
技能可声明该表在数据管理表格中的**默认展示列**(软默认,不是硬隐藏):
|
||||||
|
|
||||||
|
- **已配置**:首次打开(用户尚未改过「列」)只显示名单中的列;其余列仍在「列」面板中,用户可勾回
|
||||||
|
- **未配置 / 空 / 无效**:显示全部 schema 可见列(与今日行为一致)
|
||||||
|
- **用户改过「列」或表头「隐藏此列」后**:以本机偏好为准;「恢复默认列布局」后重新回到技能默认(若有)
|
||||||
|
|
||||||
|
JSON 数组(物理列名;也可写 `{"column":"col"}` 对象,宿主同样接受):
|
||||||
|
|
||||||
|
```json
|
||||||
|
["stat_type","period","stage","ops_count","timeout_ratio"]
|
||||||
|
```
|
||||||
|
|
||||||
|
规则:
|
||||||
|
|
||||||
|
- 列名必须是该表真实列,且 `_jiangchang_columns.visible=1`(硬隐藏列本来就不进表格)
|
||||||
|
- 与 `visible=0` **不同**:`visible=0` = 列从数据管理 schema /「列」面板移除;本字段只影响默认勾选状态
|
||||||
|
- 名单为空或全部无效时,宿主 fail-open:全显
|
||||||
|
- 通过 `upsert_table_metadata(..., default_visible_columns_json=...)` 写入;旧库由 `init_display_metadata_tables` 幂等 `ALTER` 补列
|
||||||
|
|
||||||
|
示例(时效统计:默认不展示编号与时间戳列):
|
||||||
|
|
||||||
|
```python
|
||||||
|
upsert_table_metadata(
|
||||||
|
cursor,
|
||||||
|
table_name="timeliness_stats",
|
||||||
|
display_name="时效统计",
|
||||||
|
readonly=1,
|
||||||
|
default_visible_columns_json=(
|
||||||
|
'["stat_type","period","stage","ops_count",'
|
||||||
|
'"timeout_open","timeout_done","timeout_total","timeout_ratio"]'
|
||||||
|
),
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
禁止:
|
禁止:
|
||||||
|
|
||||||
- 使用中文真实表名
|
- 使用中文真实表名
|
||||||
- 使用 `_schema_meta` 作为新的正式契约
|
- 使用 `_schema_meta` 作为新的正式契约
|
||||||
- 只写英文物理名而不写中文 `display_name`
|
- 只写英文物理名而不写中文 `display_name`
|
||||||
- 让元数据引用不存在的表
|
- 让元数据引用不存在的表
|
||||||
|
- 用 `visible=0` 假装「默认不展示但用户还能勾出来」(那是硬隐藏,勾不回来)
|
||||||
|
|
||||||
### `_jiangchang_columns` 字段级标准
|
### `_jiangchang_columns` 字段级标准
|
||||||
|
|
||||||
@@ -187,7 +225,7 @@ upsert_table_metadata(
|
|||||||
| `display_name` | 是 | 用户可见的中文字段名(表头与表单标签);保持简短 |
|
| `display_name` | 是 | 用户可见的中文字段名(表头与表单标签);保持简短 |
|
||||||
| `description` | 否 | 字段说明(给人看的短文案);**宿主在新增/编辑表单标签下方展示**(见下节);不要塞进 `display_name` |
|
| `description` | 否 | 字段说明(给人看的短文案);**宿主在新增/编辑表单标签下方展示**(见下节);不要塞进 `display_name` |
|
||||||
| `display_order` | 否 | 兼容字段,默认保持 `NULL` |
|
| `display_order` | 否 | 兼容字段,默认保持 `NULL` |
|
||||||
| `visible` | 是 | 是否在数据管理表格中展示 |
|
| `visible` | 是 | 是否进入数据管理 schema /「列」面板(`0` = **硬隐藏**,用户无法勾回;默认不展示但可勾回请用表级 `default_visible_columns_json`) |
|
||||||
| `searchable` | 是 | 是否参与搜索 |
|
| `searchable` | 是 | 是否参与搜索 |
|
||||||
| `editable` | 是 | `1` 业务字段允许宿主用于新增和编辑;`0` 系统/只读字段 |
|
| `editable` | 是 | `1` 业务字段允许宿主用于新增和编辑;`0` 系统/只读字段 |
|
||||||
| `display_type` | 否 | 字段渲染类型;稳定取值见下节「表单与展示:稳定字段类型」 |
|
| `display_type` | 否 | 字段渲染类型;稳定取值见下节「表单与展示:稳定字段类型」 |
|
||||||
|
|||||||
@@ -25,7 +25,8 @@ CREATE TABLE IF NOT EXISTS _jiangchang_tables (
|
|||||||
sort_order INTEGER,
|
sort_order INTEGER,
|
||||||
visible INTEGER NOT NULL DEFAULT 1,
|
visible INTEGER NOT NULL DEFAULT 1,
|
||||||
readonly INTEGER NOT NULL DEFAULT 0,
|
readonly INTEGER NOT NULL DEFAULT 0,
|
||||||
default_sort_json TEXT
|
default_sort_json TEXT,
|
||||||
|
default_visible_columns_json TEXT
|
||||||
)
|
)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -89,6 +90,10 @@ def _ensure_jiangchang_tables_columns(cursor) -> None:
|
|||||||
cols = {str(row[1]) for row in cursor.fetchall()}
|
cols = {str(row[1]) for row in cursor.fetchall()}
|
||||||
if "default_sort_json" not in cols:
|
if "default_sort_json" not in cols:
|
||||||
cursor.execute("ALTER TABLE _jiangchang_tables ADD COLUMN default_sort_json TEXT")
|
cursor.execute("ALTER TABLE _jiangchang_tables ADD COLUMN default_sort_json TEXT")
|
||||||
|
if "default_visible_columns_json" not in cols:
|
||||||
|
cursor.execute(
|
||||||
|
"ALTER TABLE _jiangchang_tables ADD COLUMN default_visible_columns_json TEXT"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def init_display_metadata_tables(cursor) -> None:
|
def init_display_metadata_tables(cursor) -> None:
|
||||||
@@ -106,27 +111,42 @@ def upsert_table_metadata(
|
|||||||
visible: int = 1,
|
visible: int = 1,
|
||||||
readonly: int = 0,
|
readonly: int = 0,
|
||||||
default_sort_json: str | None = None,
|
default_sort_json: str | None = None,
|
||||||
|
default_visible_columns_json: str | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""写入表级元数据。
|
"""写入表级元数据。
|
||||||
|
|
||||||
default_sort_json: 宿主数据管理默认排序,JSON 数组,例如
|
default_sort_json: 宿主数据管理默认排序,JSON 数组,例如
|
||||||
'[{"column":"sent_at","direction":"desc"},{"column":"id","direction":"desc"}]'
|
'[{"column":"sent_at","direction":"desc"},{"column":"id","direction":"desc"}]'
|
||||||
为空则宿主回退到 updated_at → created_at → 主键。
|
为空则宿主回退到 updated_at → created_at → 主键。
|
||||||
|
|
||||||
|
default_visible_columns_json: 宿主数据管理默认展示列,JSON 字符串数组,例如
|
||||||
|
'["stat_type","period","stage"]'
|
||||||
|
为空则全显;用户改过「列」后以本机偏好为准(软默认,区别于 visible=0 硬隐藏)。
|
||||||
"""
|
"""
|
||||||
cursor.execute(
|
cursor.execute(
|
||||||
"""
|
"""
|
||||||
INSERT INTO _jiangchang_tables (
|
INSERT INTO _jiangchang_tables (
|
||||||
table_name, display_name, description, visible, readonly, default_sort_json
|
table_name, display_name, description, visible, readonly,
|
||||||
|
default_sort_json, default_visible_columns_json
|
||||||
)
|
)
|
||||||
VALUES (?, ?, ?, ?, ?, ?)
|
VALUES (?, ?, ?, ?, ?, ?, ?)
|
||||||
ON CONFLICT(table_name) DO UPDATE SET
|
ON CONFLICT(table_name) DO UPDATE SET
|
||||||
display_name = excluded.display_name,
|
display_name = excluded.display_name,
|
||||||
description = excluded.description,
|
description = excluded.description,
|
||||||
visible = excluded.visible,
|
visible = excluded.visible,
|
||||||
readonly = excluded.readonly,
|
readonly = excluded.readonly,
|
||||||
default_sort_json = excluded.default_sort_json
|
default_sort_json = excluded.default_sort_json,
|
||||||
|
default_visible_columns_json = excluded.default_visible_columns_json
|
||||||
""",
|
""",
|
||||||
(table_name, display_name, description, visible, readonly, default_sort_json),
|
(
|
||||||
|
table_name,
|
||||||
|
display_name,
|
||||||
|
description,
|
||||||
|
visible,
|
||||||
|
readonly,
|
||||||
|
default_sort_json,
|
||||||
|
default_visible_columns_json,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
"""技能标识、版本与平台公共库约束(复制后请修改 slug/version/logger)。"""
|
"""技能标识、版本与平台公共库约束(复制后请修改 slug/version/logger)。"""
|
||||||
|
|
||||||
SKILL_SLUG = "your-skill-slug"
|
SKILL_SLUG = "your-skill-slug"
|
||||||
SKILL_VERSION = "1.0.77"
|
SKILL_VERSION = "1.0.78"
|
||||||
LOG_LOGGER_NAME = "openclaw.skill.your_skill_slug"
|
LOG_LOGGER_NAME = "openclaw.skill.your_skill_slug"
|
||||||
PLATFORM_KIT_MIN_VERSION = "1.2.2"
|
PLATFORM_KIT_MIN_VERSION = "1.2.11"
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ def get_skill_root() -> str:
|
|||||||
return _SKILL_ROOT
|
return _SKILL_ROOT
|
||||||
|
|
||||||
|
|
||||||
def platform_kit_version_patch(version: str = "1.2.2"):
|
def platform_kit_version_patch(version: str = "1.2.11"):
|
||||||
"""Mock installed jiangchang-platform-kit version for health/diagnostics tests."""
|
"""Mock installed jiangchang-platform-kit version for health/diagnostics tests."""
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
|||||||
@@ -507,6 +507,54 @@ metadata:
|
|||||||
self.assertIn("seed_column_metadata_batch", text)
|
self.assertIn("seed_column_metadata_batch", text)
|
||||||
self.assertIn("valid_column_names", text)
|
self.assertIn("valid_column_names", text)
|
||||||
|
|
||||||
|
def test_default_visible_columns_json_upsert_and_migrate(self) -> None:
|
||||||
|
conn = sqlite3.connect(":memory:")
|
||||||
|
try:
|
||||||
|
from db.display_metadata import (
|
||||||
|
init_display_metadata_tables,
|
||||||
|
upsert_table_metadata,
|
||||||
|
)
|
||||||
|
|
||||||
|
cur = conn.cursor()
|
||||||
|
# Simulate pre-1.0.79 table without the new column.
|
||||||
|
cur.execute(
|
||||||
|
"""
|
||||||
|
CREATE TABLE _jiangchang_tables (
|
||||||
|
table_name TEXT PRIMARY KEY,
|
||||||
|
display_name TEXT NOT NULL,
|
||||||
|
description TEXT,
|
||||||
|
sort_order INTEGER,
|
||||||
|
visible INTEGER NOT NULL DEFAULT 1,
|
||||||
|
readonly INTEGER NOT NULL DEFAULT 0,
|
||||||
|
default_sort_json TEXT
|
||||||
|
)
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
init_display_metadata_tables(cur)
|
||||||
|
cur.execute("PRAGMA table_info('_jiangchang_tables')")
|
||||||
|
cols = {str(row[1]) for row in cur.fetchall()}
|
||||||
|
self.assertIn("default_visible_columns_json", cols)
|
||||||
|
|
||||||
|
payload = '["stat_type","period","stage"]'
|
||||||
|
upsert_table_metadata(
|
||||||
|
cur,
|
||||||
|
table_name="timeliness_stats",
|
||||||
|
display_name="时效统计",
|
||||||
|
readonly=1,
|
||||||
|
default_visible_columns_json=payload,
|
||||||
|
)
|
||||||
|
conn.commit()
|
||||||
|
cur.execute(
|
||||||
|
"""
|
||||||
|
SELECT default_visible_columns_json
|
||||||
|
FROM _jiangchang_tables
|
||||||
|
WHERE table_name = 'timeliness_stats'
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
self.assertEqual(cur.fetchone()[0], payload)
|
||||||
|
finally:
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|||||||
@@ -344,6 +344,8 @@ class TestDocsStandards(unittest.TestCase):
|
|||||||
"DEFAULT",
|
"DEFAULT",
|
||||||
"description",
|
"description",
|
||||||
"文件 / 图片上传",
|
"文件 / 图片上传",
|
||||||
|
"default_visible_columns_json",
|
||||||
|
"默认展示列",
|
||||||
):
|
):
|
||||||
self.assertIn(marker, text, msg=f"SCHEMA.md missing {marker!r}")
|
self.assertIn(marker, text, msg=f"SCHEMA.md missing {marker!r}")
|
||||||
|
|
||||||
|
|||||||
@@ -87,12 +87,12 @@ class TestPlatformImportSource(unittest.TestCase):
|
|||||||
from jiangchang_skill_core import version_ge
|
from jiangchang_skill_core import version_ge
|
||||||
from util.constants import PLATFORM_KIT_MIN_VERSION
|
from util.constants import PLATFORM_KIT_MIN_VERSION
|
||||||
|
|
||||||
self.assertEqual(PLATFORM_KIT_MIN_VERSION, "1.2.2")
|
self.assertEqual(PLATFORM_KIT_MIN_VERSION, "1.2.11")
|
||||||
|
|
||||||
md_path = os.path.join(get_skill_root(), "SKILL.md")
|
md_path = os.path.join(get_skill_root(), "SKILL.md")
|
||||||
with open(md_path, encoding="utf-8") as f:
|
with open(md_path, encoding="utf-8") as f:
|
||||||
md = f.read()
|
md = f.read()
|
||||||
self.assertEqual(_parse_platform_kit_min_version(md), "1.2.2")
|
self.assertEqual(_parse_platform_kit_min_version(md), "1.2.11")
|
||||||
|
|
||||||
req_path = os.path.join(get_skill_root(), "requirements.txt")
|
req_path = os.path.join(get_skill_root(), "requirements.txt")
|
||||||
with open(req_path, encoding="utf-8") as f:
|
with open(req_path, encoding="utf-8") as f:
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ FORBIDDEN_PHRASES = (
|
|||||||
|
|
||||||
POSITIVE_MARKERS = (
|
POSITIVE_MARKERS = (
|
||||||
"jiangchang-platform-kit",
|
"jiangchang-platform-kit",
|
||||||
"1.2.2",
|
"1.2.11",
|
||||||
"共享 runtime",
|
"共享 runtime",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user