diff --git a/CHANGELOG.md b/CHANGELOG.md index d12d2d5..eb5b5a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,13 @@ - 小节正文写用户能看懂的变化(新能力、修复、注意事项),避免堆砌内部实现细节 - 打 tag 前**必须**为即将发布的版本新增一节;找不到匹配小节时,本次发布不会更新 changelog 字段 +## 1.0.77 + +- 固化 Action / CRUD **对称**表单字段类型契约:text、textarea/json、number、boolean、enum/lookup、sensitive、date、datetime_unix_seconds、url/email(薄层) +- 明确可编辑业务时间用 Unix **秒**读写;审计 `created_at`/`updated_at` 仍只读展示 +- 明确本版**不做**文件上传、多选/标签数组、复杂 nested 表单,避免宿主反复扩面 +- 更新 `SCHEMA.md`、`ACTIONS.md`、`skill-actions.schema.json`(`display_type`/`format`) + ## 1.0.76 - 固化数据管理「新增/编辑」表单契约:列 `description` 须在标签下展示;创建默认值取自 SQLite 列 `DEFAULT`(见 `references/SCHEMA.md`) diff --git a/SKILL.md b/SKILL.md index 86c398b..5e808b2 100644 --- a/SKILL.md +++ b/SKILL.md @@ -1,7 +1,7 @@ --- name: 技能开发模板(通用业务版) description: "OpenClaw 通用业务技能开发模板,供复制后定制新业务 skill。定制步骤见 development/DEVELOPMENT.md。" -version: 1.0.76 +version: 1.0.77 author: 深圳匠厂科技有限公司 metadata: openclaw: diff --git a/assets/schemas/skill-actions.schema.json b/assets/schemas/skill-actions.schema.json index 570fa01..55b9ef9 100644 --- a/assets/schemas/skill-actions.schema.json +++ b/assets/schemas/skill-actions.schema.json @@ -117,6 +117,16 @@ "minLength": 1, "pattern": "^[a-z][a-z0-9_]*$", "description": "可选。绑定业务表物理列名;replaces create/update 时宿主可复用该列的中文名与 lookup 下拉" + }, + "display_type": { + "type": "string", + "enum": ["textarea", "date", "datetime_unix_seconds", "url", "email"], + "description": "可选。表单控件提示;与 format 二选一即可。须与 CRUD display_type 语义对齐(见 ACTIONS.md / SCHEMA.md)。" + }, + "format": { + "type": "string", + "enum": ["textarea", "date", "datetime_unix_seconds", "url", "email"], + "description": "可选。display_type 别名,语义相同。" } } }, diff --git a/references/ACTIONS.md b/references/ACTIONS.md index 35d4b7f..e9cc58d 100644 --- a/references/ACTIONS.md +++ b/references/ACTIONS.md @@ -105,7 +105,7 @@ Action 是**可选能力**: | **bind** | `{ "tables": ["business_table"] }`;可选 `inputMapping`(行内/批量预填,见下节) | | **policy** | 可选;`enabled` + `entrypoint`(推荐 `policy` CLI);见 POLICY.md。未启用 = L0 | | **cellPresentation** | 可选;`{ "enabled": true }` 时宿主应加载单元格呈现;见 [`CELL_PRESENTATION.md`](CELL_PRESENTATION.md)。未启用 = P0 | -| inputSchema | 根 `type: object`;properties 使用 `string` / `number` / `boolean`;支持 `enum`、`default`、`required`、`sensitive`、`readOnly` | +| inputSchema | 根 `type: object`;properties 使用 `string` / `number` / `boolean`;支持 `enum`、`default`、`required`、`sensitive`、`readOnly`;可选 `display_type` / `format`(`textarea` / `date` / `datetime_unix_seconds` / `url` / `email`) | | confirmation | `{ "message": "..." }` — **风险文案**(confirm/form 弹窗黄条;agent 元数据);**不是**「要不要弹窗」的开关 | | **executionProfile** | `"sync"` \| `"async"`(**必须显式写出**,禁止依赖缺省) | | **interaction** | `"auto"` \| `"direct"` \| `"confirm"` \| `"form"`(可选;省略≈`auto`) | @@ -503,21 +503,52 @@ HINT: ## inputSchema 与参数表单 -当前宿主稳定支持: +### 表单字段类型对照(须与 CRUD 对称) -- `object` / `string` / `number` / `boolean` +Skill Action 弹窗与数据管理「新增 / 编辑」使用**同一套控件语义**。CRUD 侧配置见 [`SCHEMA.md`](SCHEMA.md)「表单与展示:稳定字段类型」;本表只写 Action 怎么配。 + +| 控件语义 | Action `inputSchema` 怎么写 | CRUD 怎么写(对照) | +|----------|----------------------------|---------------------| +| 单行文本 | `type: string` | `display_type: text` 或缺省 | +| 多行文本 | `display_type` 或 `format`: `textarea` | `display_type: textarea` | +| JSON 长文本 | `type: object`/`array`(以 JSON 字符串编辑)或 `textarea` | `display_type: json` / `textarea` | +| 数字 | `type: number` | `display_type: number` 或 INT/REAL/NUMERIC | +| 开关 | `type: boolean` | `display_type: boolean` | +| 枚举下拉 | `enum: [...]` | `options_json` 数组 | +| 关联下拉 | 绑列 lookup(`column` / `columnMapping`) | `display_type: lookup` | +| 密码 | `sensitive: true` | 列 `sensitive` | +| 日期(日) | `display_type` 或 `format`: `date` | `display_type: date` → 存 `YYYY-MM-DD` | +| 日期时间 | `display_type` 或 `format`: `datetime_unix_seconds` | 同左;**存/传 Unix 秒** | +| URL(薄层) | `display_type` 或 `format`: `url` | `display_type: url` | +| Email(薄层) | `display_type` 或 `format`: `email` | `display_type: email` | +| 说明灰字 | `description` | `_jiangchang_columns.description` | +| 默认值 | `default` | SQLite 列 `DEFAULT` | +| 只读 | `readOnly: true` | 列 `editable: 0` / 宿主只读规则 | + +**本版不做(勿宣传):** 文件上传、多选/标签数组、复杂 `oneOf` / nested object 专用表单。 + +日期时间约定: + +- Action 提交与 CRUD 写库统一用 Unix **秒**整数(与 SCHEMA 标准时间字段一致) +- `date` 只表示日历日,值为 `YYYY-MM-DD` +- 脏/无法归一的旧预设:日期控件可回退为普通文本,避免卡死表单 + +### 当前宿主稳定支持(摘要) + +- 根:`object`;属性:`string` / `number` / `boolean`(`object`/`array` 仅作 JSON 多行编辑兼容,新技能慎用) - `enum` / `default` / `required` / `sensitive` / `readOnly` +- `display_type` / `format`:`textarea` | `date` | `datetime_unix_seconds` | `url` | `email` - 属性级 `description`(表单标签下方灰字说明) - 简单模板替换(`{{fieldName}}`) **与数据管理「新增 / 编辑」表单的分工(勿混用配置入口):** -| 表单 | 短标题 | 说明灰字 | 默认值 | -|------|--------|----------|--------| -| Skill Action 弹窗 | `inputSchema.title` | `inputSchema.description` | `inputSchema.default` | -| 数据管理新增/编辑 | `_jiangchang_columns.display_name` | `_jiangchang_columns.description` | 物理列 SQLite `DEFAULT` | +| 表单 | 短标题 | 说明灰字 | 默认值 | 控件类型 | +|------|--------|----------|--------|----------| +| Skill Action 弹窗 | `inputSchema.title` | `inputSchema.description` | `inputSchema.default` | `type` + `display_type`/`format` | +| 数据管理新增/编辑 | `_jiangchang_columns.display_name` | `_jiangchang_columns.description` | 物理列 SQLite `DEFAULT` | `display_type` + 物理类型推断 | -CRUD 侧宿主义务与禁止事项见 [`SCHEMA.md`](SCHEMA.md)「新增/编辑表单:字段说明与默认值(宿主支持)」。同一业务含义两边文案应对齐,但分别配置。 +CRUD 侧宿主义务与禁止事项见 [`SCHEMA.md`](SCHEMA.md)「表单与展示:稳定字段类型」「新增/编辑表单:字段说明与默认值」。同一业务含义两边文案与控件应对齐,但分别配置。 示例: @@ -537,6 +568,23 @@ CRUD 侧宿主义务与禁止事项见 [`SCHEMA.md`](SCHEMA.md)「新增/编辑 "title": "返回数量", "description": "最多返回多少条;填 0 表示不限制(若业务支持)", "default": 50 + }, + "dueDate": { + "type": "string", + "title": "截止日期", + "display_type": "date", + "description": "日历日,格式 YYYY-MM-DD" + }, + "publishAt": { + "type": "number", + "title": "发布时间", + "display_type": "datetime_unix_seconds", + "description": "提交值为 Unix 秒" + }, + "homepage": { + "type": "string", + "title": "主页链接", + "display_type": "url" } } } @@ -561,6 +609,9 @@ CRUD 侧宿主义务与禁止事项见 [`SCHEMA.md`](SCHEMA.md)「新增/编辑 - `boolean` 在宿主中渲染为开关 - `enum` 在宿主中渲染为选择框 +- `type: number` 渲染为数字输入 +- `display_type`/`format: date` 渲染为日期(日);`datetime_unix_seconds` 渲染为日期时间并按 **Unix 秒**提交 +- `display_type`/`format: url` / `email` 为薄层输入类型(校验从宽,勿当强校验网关) - `required` 字段在提交前校验 - `sensitive` 字段不得出现在低信任入口 - `readOnly: true` 时数据管理表单只读展示该字段(仍随提交传入);适合行内/勾选预填的主键、编号列表 @@ -568,8 +619,7 @@ CRUD 侧宿主义务与禁止事项见 [`SCHEMA.md`](SCHEMA.md)「新增/编辑 - 需要确认的副作用操作使用 `confirmation.message` - 模板参数使用 `{{fieldName}}`,且必须在 `inputSchema.properties` 中声明 - `entrypoint.args` 项当前只允许 `string`、`number`、`boolean`;不允许对象或 `null` -- **不要**在当前模板中宣传 `nested object`、`oneOf`、复杂数组等尚未稳定支持的能力 - +- **不要**在当前模板中宣传文件上传、多选数组、`oneOf`、复杂 nested object 等尚未稳定支持的能力 ## 风险与入口配置 - 查询、诊断、版本检查可以放到 `skill-detail` 或 `agent` diff --git a/references/SCHEMA.md b/references/SCHEMA.md index 6f383ea..c9eaf95 100644 --- a/references/SCHEMA.md +++ b/references/SCHEMA.md @@ -190,11 +190,42 @@ upsert_table_metadata( | `visible` | 是 | 是否在数据管理表格中展示 | | `searchable` | 是 | 是否参与搜索 | | `editable` | 是 | `1` 业务字段允许宿主用于新增和编辑;`0` 系统/只读字段 | -| `display_type` | 否 | 字段渲染类型(如 `text`、`textarea`、`datetime_unix_seconds`) | +| `display_type` | 否 | 字段渲染类型;稳定取值见下节「表单与展示:稳定字段类型」 | | `options_json` | 否 | 枚举选项 JSON | 这两张表是**数据字典**,不是业务数据;宿主不会把它们当作普通业务表展示。 +#### 表单与展示:稳定字段类型(宿主支持,须与 Action 对称) + +数据管理「新增 / 编辑」表单与 Skill Action 参数表单应使用**同一套控件语义**(配置入口不同,见 [`ACTIONS.md`](ACTIONS.md))。新技能只使用下表;宿主须两边都实现,避免「Action 有、CRUD 没有」。 + +**本版稳定支持(须齐):** + +| `display_type`(CRUD) | 物理类型建议 | 列表展示 | 新增/编辑控件 | 存库约定 | +|------------------------|--------------|----------|---------------|----------| +| `text`(或缺省) | `TEXT` / 一般标量 | 原文 | 单行文本 | 原文 | +| `textarea` | `TEXT` | 原文(可截断) | 多行文本 | 原文 | +| `json` | `TEXT` | 原文 | 多行文本(JSON 字符串) | 原文;提交时宿主可做基础 JSON 合法性校验 | +| `number` | `INTEGER` / `REAL` / `NUMERIC` | 原文 | **数字输入**(`type=number`) | 数字;无 `display_type` 时,物理类型为 INT/REAL/NUMERIC 也应走数字控件 | +| `boolean` | `INTEGER` 0/1 或 BOOL | 开关语义展示 | 开关 | `0` / `1` | +| (枚举) | 任意 + `options_json` **数组** | 标签映射 | 下拉选择 | 选项 `value` | +| `lookup` | 外键式标量 + `options_json` **对象** | 标签映射 | 下拉选择(查关联表) | 关联键值 | +| (敏感) | 任意;列 `sensitive=1` | 掩码/隐藏策略由宿主定 | 新建可用密码框;编辑策略与既有宿主一致 | 原文(勿日志明文) | +| `date` | `TEXT` 推荐 `YYYY-MM-DD`,或可安全归一的值 | 日期 | **日期选择**(日) | `YYYY-MM-DD` | +| `datetime_unix_seconds` | `INTEGER` | `YYYY-MM-DD HH:mm:ss` | **日期时间选择**;提交写回 **Unix 秒** | Unix **秒**整数;禁止改成 ISO 字符串 | +| `url` | `TEXT` | 原文(可作链接) | 单行输入 `type=url`(薄层;校验从宽) | 原文 | +| `email` | `TEXT` | 原文 | 单行输入 `type=email`(薄层;校验从宽) | 原文 | + +说明灰字与创建默认值见下一小节;与 Action 的对照写在 [`ACTIONS.md`](ACTIONS.md)「表单字段类型对照」。 + +**明确不在本版稳定范围(勿在新技能宣传为已支持):** + +- 文件 / 图片上传 +- 多选、标签数组、复杂 `oneOf` / nested object 表单 +- 把业务可编辑时间存成毫秒、ISO 文本或 SQLite `datetime` 文本(审计字段规范见「标准时间字段」) + +`datetime_unix_milliseconds`:仅兼容**列表展示**旧数据;**新技能不要用**;可编辑时间一律 `datetime_unix_seconds`。 + #### 新增/编辑表单:字段说明与默认值(宿主支持) 数据管理「新增记录 / 编辑记录」与 Skill Action 弹窗是**两套表单**;CRUD 侧约定如下(交互对齐 Action 的 `inputSchema.description` / `default`,但**配置入口不同**)。 @@ -213,6 +244,7 @@ upsert_table_metadata( - 业务语义(如「0 = 不限制」)写在 `description` 里;宿主只展示,**不**解读业务含义 - 第一期**不**新增元数据「表单默认值」列;CRUD 默认值 = DDL `DEFAULT`,与 Action 的 `inputSchema.default` 分开配置 - 同一业务字段若同时出现在 Action 与新增表单,**文案应对齐**(短标题 + 说明),但分别写在 `actions.json` 与 `_jiangchang_columns` / DDL +- 数字类可编辑列推荐 `display_type: "number"`(或依赖 INT/REAL/NUMERIC 推断)并在 DDL 写清 `DEFAULT`(如条数上限默认 `0`) 示例(可编辑列种子 + DDL 默认): @@ -230,12 +262,14 @@ seed_column_metadata_batch( "column_name": "video_url", "display_name": "视频链接", "editable": 1, + "display_type": "url", "description": "填平台视频页链接;需包含可识别的视频路径片段。", }, { "column_name": "comment_limit", "display_name": "采集评论条数", "editable": 1, + "display_type": "number", "description": "最多采多少条。填 0 = 不限制;填正整数则按该数量封顶。", }, ), @@ -243,7 +277,7 @@ seed_column_metadata_batch( ) ``` -Action 参数表单的 `title` / `description` / `default` 见 [`ACTIONS.md`](ACTIONS.md);勿与本节 CRUD 配置入口混用。 +Action 参数表单的 `title` / `description` / `default` / `display_type` 见 [`ACTIONS.md`](ACTIONS.md);勿与本节 CRUD 配置入口混用。 ## editable 与 createable 的关系 @@ -349,10 +383,22 @@ readonly = 0 | 存储格式 | Unix 秒级整数 | | 展示元数据 `display_type` | `datetime_unix_seconds` | | 中文名 | `created_at` → **创建时间**;`updated_at` → **更新时间** | -| 用户编辑 | `editable = 0`(系统字段,宿主只展示格式化结果) | +| 用户编辑 | **审计字段** `editable = 0`(系统维护,列表只展示格式化结果,**不**进可编辑表单) | | 字段顺序 | 位于业务字段末尾:`… → created_at → updated_at` | | 排序 | **不使用** `display_order`;顺序仅来自 `PRAGMA table_info` | +### 业务可编辑时间 vs 审计时间 + +| 场景 | `editable` | 列表 | 新增/编辑表单 | +|------|------------|------|----------------| +| `created_at` / `updated_at` 等审计列 | `0` | 格式化为 `YYYY-MM-DD HH:mm:ss` | 不出现(或只读,由宿主既有规则) | +| 业务时间列(预约、截止、活动开始等) | `1` | 同上格式化 | **日期时间控件**;提交写入 Unix **秒**整数 | + +宿主对 `display_type = datetime_unix_seconds`: + +- **列表**:格式化为 `YYYY-MM-DD HH:mm:ss` 展示,**不**改库内原始整数 +- **可编辑表单**:提供日期时间选择;保存时写回 Unix **秒**(不要写 ISO 字符串、不要写毫秒) + ### 自动生成与维护 - **created_at**:`INTEGER NOT NULL DEFAULT (unixepoch())`;插入时可省略,由数据库生成。 @@ -365,8 +411,6 @@ readonly = 0 - 中文元数据种子:`scripts/db/display_metadata.py` - 自检:`scripts/db/display_metadata_validator.py` -宿主只负责把 `display_type = datetime_unix_seconds` 格式化为 `YYYY-MM-DD HH:mm:ss` 展示,**不修改**数据库原始值。 - ### 模板默认:`task_logs` `task_logs` 是技能内部**系统自动写入**的任务审计日志(由 `task_logs_repository` INSERT/UPDATE),不是供用户在宿主数据管理中手工维护的业务表。 diff --git a/scripts/util/constants.py b/scripts/util/constants.py index 17d7fe5..5cceecd 100644 --- a/scripts/util/constants.py +++ b/scripts/util/constants.py @@ -1,6 +1,6 @@ """技能标识、版本与平台公共库约束(复制后请修改 slug/version/logger)。""" SKILL_SLUG = "your-skill-slug" -SKILL_VERSION = "1.0.76" +SKILL_VERSION = "1.0.77" LOG_LOGGER_NAME = "openclaw.skill.your_skill_slug" PLATFORM_KIT_MIN_VERSION = "1.2.2" diff --git a/tests/test_docs_standards.py b/tests/test_docs_standards.py index fbffeb1..3d82da4 100644 --- a/tests/test_docs_standards.py +++ b/tests/test_docs_standards.py @@ -338,8 +338,12 @@ class TestDocsStandards(unittest.TestCase): "demo_items", "readonly", "新增/编辑表单:字段说明与默认值(宿主支持)", + "表单与展示:稳定字段类型(宿主支持,须与 Action 对称)", + "datetime_unix_seconds", + "display_type: \"number\"", "DEFAULT", "description", + "文件 / 图片上传", ): self.assertIn(marker, text, msg=f"SCHEMA.md missing {marker!r}") @@ -347,8 +351,10 @@ class TestDocsStandards(unittest.TestCase): text = self._read("references/ACTIONS.md") for marker in ( "inputSchema 与参数表单", + "表单字段类型对照(须与 CRUD 对称)", "数据管理新增/编辑", "_jiangchang_columns.description", + "datetime_unix_seconds", "SCHEMA.md", ): self.assertIn(marker, text, msg=f"ACTIONS.md missing {marker!r}")