diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e272f6..17cd344 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,18 @@ - 小节正文写用户能看懂的变化(新能力、修复、注意事项),避免堆砌内部实现细节 - 打 tag 前**必须**为即将发布的版本新增一节;找不到匹配小节时,本次发布不会更新 changelog 字段 +## 1.0.93 + +- 沉淀 Lookup **多选**契约:同一 `lookup` / `options_json` 对象上加 `multiple: true`(平行扩展单选,不另造 display_type) +- 多选存 `TEXT` 逗号分隔 id;列表展示映射为多个 label;控件由宿主按选项数量选择(短列表勾选 / 长列表 Chip+下拉) +- 更新 SCHEMA / ACTIONS / skill-actions.schema / lookup 示例与 options_json 校验 + +## 1.0.93 + +- 沉淀 Lookup **多选**契约:同一 `lookup` / `options_json` 对象加 `multiple: true`,不另造类型 +- 多选存 `TEXT` 逗号分隔 id;短列表勾选 / 长列表 Chip+下拉为宿主渲染策略 +- ACTIONS / SCHEMA / schema / 示例与文档守护对齐 + ## 1.0.92 - 沉淀数据管理 `replaces: import`:导入 UI 统一宿主面板,确认后由技能写库 diff --git a/SKILL.md b/SKILL.md index f72e42a..9159200 100644 --- a/SKILL.md +++ b/SKILL.md @@ -1,7 +1,7 @@ --- name: 技能开发模板(通用业务版) description: "OpenClaw 通用业务技能开发模板,供复制后定制新业务 skill。定制步骤见 development/DEVELOPMENT.md。" -version: 1.0.92 +version: 1.0.93 author: 深圳匠厂科技有限公司 metadata: openclaw: diff --git a/assets/examples/README.md b/assets/examples/README.md index 6190cf6..b205908 100644 --- a/assets/examples/README.md +++ b/assets/examples/README.md @@ -4,6 +4,6 @@ - `skill-policy-snapshot.*.example.json`:Data Policy Snapshot 形状参考(见 `references/POLICY.md`)。 - `skill-cell-presentation.example.json`:数据管理单元格呈现文档形状参考(见 `references/CELL_PRESENTATION.md`)。 - `render-html.action.example.json`:HTML 报告「重新生成」Action 形状参考(固定 id `render-html`;见 `references/ACTIONS.md`)。 -- `lookup-action.example.json`:Action 内联 Lookup 下拉形状参考(跨表引用勿填编号;见 `references/ACTIONS.md`「跨表引用:Lookup」)。 +- `lookup-action.example.json`:Action 内联 Lookup 形状参考(单选 + `multiple: true` 多选并列;跨表引用勿填编号;见 `references/ACTIONS.md`「跨表引用:Lookup」)。 - `visible-when-action.example.json`:Action 参数字段条件显隐 `visibleWhen` 形状参考(互斥模式联动;见 `references/ACTIONS.md`「字段条件显隐」;≠ Policy L3)。 - `replaces-create.action.example.json` / `replaces-update.action.example.json` / `replaces-delete.action.example.json` / `replaces-import.action.example.json`:数据管理默认按钮接管(`replaces`)形状参考;**import 时 UI 仍用宿主「导入数据」面板**,确认后调技能写库(见 `references/ACTIONS.md`「`replaces`」)。 diff --git a/assets/examples/lookup-action.example.json b/assets/examples/lookup-action.example.json index 6c90da4..9757f3b 100644 --- a/assets/examples/lookup-action.example.json +++ b/assets/examples/lookup-action.example.json @@ -1,12 +1,12 @@ { - "_comment": "形状参考:Action 参数引用同库另一张表时用 Lookup 下拉,勿让用户填内部编号。完整契约见 references/ACTIONS.md「跨表引用:Lookup」与 references/SCHEMA.md。", + "_comment": "形状参考:Action 参数引用同库另一张表时用 Lookup。单选与多选同一 lookup 对象,多选只加 multiple:true。完整契约见 references/ACTIONS.md「跨表引用:Lookup」与 references/SCHEMA.md。", "schemaVersion": 1, "skill": "your-skill-slug", "actions": [ { "id": "send-with-template", "label": "按模板发送(示例)", - "description": "演示内联 lookup:从 message_templates 选模板;-- 表示自动挑选。", + "description": "演示内联 lookup 单选:从 message_templates 选模板;-- 表示自动挑选。", "placements": ["toolbar"], "executionProfile": "async", "interaction": "form", @@ -43,6 +43,41 @@ } } } + }, + { + "id": "probe-with-engines", + "label": "按引擎抽检(多选示例)", + "description": "演示 lookup.multiple:从 engines 多选,提交为逗号分隔 id。", + "placements": ["toolbar"], + "executionProfile": "async", + "interaction": "form", + "bind": { + "tables": ["queries"] + }, + "entrypoint": { + "type": "cli", + "command": "query", + "args": ["batch-probe", "--engine-ids", "{{engineIds}}"] + }, + "inputSchema": { + "type": "object", + "properties": { + "engineIds": { + "type": "string", + "title": "将使用的引擎", + "description": "可多选;提交值为逗号分隔编号,如 1,2。", + "display_type": "lookup", + "lookup": { + "ref_table": "engines", + "value_column": "id", + "label_column": "display_name", + "filter_column": "enabled", + "filter_value": 1, + "multiple": true + } + } + } + } } ] } diff --git a/assets/schemas/skill-actions.schema.json b/assets/schemas/skill-actions.schema.json index e3d778a..d435663 100644 --- a/assets/schemas/skill-actions.schema.json +++ b/assets/schemas/skill-actions.schema.json @@ -214,9 +214,13 @@ "filter_value": { "type": ["string", "number", "boolean", "null"], "description": "可选。与 filter_column 配对的等值" + }, + "multiple": { + "type": "boolean", + "description": "可选。true=多选 Lookup(存/提交 TEXT 逗号分隔 id);省略或 false=单选(默认)。与 CRUD options_json lookup 同构。" } }, - "description": "同库 FK 式 Lookup。展示:value · label;有 extra 时 value · label(extra)。宿主亦接受 camelCase 键名。" + "description": "同库 FK 式 Lookup。展示:value · label;有 extra 时 value · label(extra)。multiple 时多选。宿主亦接受 camelCase 键名。" }, "inputSchema": { "type": "object", diff --git a/references/ACTIONS.md b/references/ACTIONS.md index 1086408..fb59b7c 100644 --- a/references/ACTIONS.md +++ b/references/ACTIONS.md @@ -733,7 +733,7 @@ HINT: | 当前表已有同含义列,且列上已是 CRUD lookup | `column` / `bind.columnMapping`,或属性名 = 列名 | toolbar/batch 弹窗与 create/update 抽屉都会复用该列已解析的选项 | | 当前表没有该列(如在「潜客」表上选「话术模板」) | `display_type: "lookup"` + 字段内联 `lookup` 对象 | 打开表单时宿主按当前技能库解析选项 | -内联 `lookup` 与列元数据 `options_json` **同构**(见 [`SCHEMA.md`](SCHEMA.md)「表关联 lookup」): +内联 `lookup` 与列元数据 `options_json` **同构**(见 [`SCHEMA.md`](SCHEMA.md)「跨表引用 lookup」): ```json { @@ -750,6 +750,20 @@ HINT: "filter_column": "is_active", "filter_value": 1 } + }, + "engineIds": { + "type": "string", + "title": "监测引擎", + "description": "可多选;提交为逗号分隔编号。", + "display_type": "lookup", + "lookup": { + "ref_table": "engines", + "value_column": "id", + "label_column": "display_name", + "filter_column": "enabled", + "filter_value": 1, + "multiple": true + } } } ``` @@ -761,16 +775,22 @@ HINT: | `label_column` | 是 | 主文案列 | | `extra_column` | 否 | 第三展示列(任意列名,不限「备注」) | | `filter_column` / `filter_value` | 否 | 等值过滤 | +| `multiple` | 否 | `true` = 多选;省略/`false` = 单选(默认) | -**下拉展示(宿主统一,技能只配列):** `{value} · {label}`;有非空 `extra_column` 时为 `{value} · {label}({extra})`。例:`2 · 通用私信-留资`、`2 · 通用私信-留资(dm)`。 +**下拉/选项展示(宿主统一,技能只配列):** 单选为 `{value} · {label}`;有非空 `extra_column` 时为 `{value} · {label}({extra})`。例:`2 · 通用私信-留资`、`2 · 通用私信-留资(dm)`。 -**可选「自动」:** 字段不要放进 `required`;不要用 `minimum: 1` 卡死留空。宿主会省略空的可选数字参数,避免「提交的数据无效」。 +**多选存值与模板:** Action / CRUD 存 **`TEXT` 逗号分隔 id**(如 `1,2,5`);`{{engineIds}}` 原样替换进 CLI。多选字段在 `inputSchema` 里用 `type: "string"`(逗号串),不要用尚未稳定的 JSON array 当业务控件。 + +**多选控件(宿主策略):** 选项少(建议 ≤6)用勾选列表;选项多用字段内 Chip + 可搜索下拉(不是再开业务弹窗)。 + +**可选「自动」(单选常见):** 字段不要放进 `required`;不要用 `minimum: 1` 卡死留空。宿主会省略空的可选数字参数,避免「提交的数据无效」。 **反模式(勿做):** - 标题写成「某某编号」,说明里教用户去左侧表抄 id - 用静态 `enum` 罗列会增长/变更的业务行(会过期) - 在技能里自己拼假 label 列代替宿主统一格式(难维护) +- 另造 `lookup_multi` / 第二套 display_type(应只加 `multiple: true`) 形状参考:[`assets/examples/lookup-action.example.json`](../assets/examples/lookup-action.example.json)。 @@ -786,7 +806,7 @@ Skill Action 弹窗与数据管理「新增 / 编辑」使用**同一套控件 | 数字 | `type: number` | `display_type: number` 或 INT/REAL/NUMERIC | | 开关 | `type: boolean` | `display_type: boolean` | | 枚举下拉 | `enum: [...]` | `options_json` 数组 | -| 关联下拉(Lookup) | 内联 `display_type: lookup` + `lookup`,或绑列 `column` / `columnMapping` | `display_type: lookup` + `options_json` 对象 | +| 关联下拉(Lookup) | 内联 `display_type: lookup` + `lookup`(可选 `multiple`),或绑列 | 同左;多选存 `TEXT` 逗号分隔 id | | 密码 | `sensitive: true` | 列 `sensitive` | | 日期(日) | `display_type` 或 `format`: `date` | `display_type: date` → 存 `YYYY-MM-DD` | | 日期时间 | `display_type` 或 `format`: `datetime_unix_seconds` | 同左;**存/传 Unix 秒** | @@ -798,7 +818,8 @@ Skill Action 弹窗与数据管理「新增 / 编辑」使用**同一套控件 | 只读 | `readOnly: true` | 列 `editable: 0` / 宿主只读规则 | | **条件显隐** | **`visibleWhen`**(见下节) | **本版 CRUD 无对称**;仅 Action / 定时任务参数表单 | -**本版不做(勿宣传):** 文件上传、多选/标签数组、复杂 `oneOf` / nested object 专用表单;`visibleWhen` 多条件 AND/OR 树、跨 Action 依赖、CRUD 新增/编辑对称显隐。 +**本版不做(勿宣传):** 文件上传、复杂 `oneOf` / nested object 专用表单;`visibleWhen` 多条件 AND/OR 树、跨 Action 依赖、CRUD 新增/编辑对称显隐。 +(**Lookup 多选**已支持:同一 `lookup` + `multiple: true`,见上文;不是任意标签数组。) 日期时间约定: @@ -811,7 +832,7 @@ Skill Action 弹窗与数据管理「新增 / 编辑」使用**同一套控件 - 根:`object`;属性:`string` / `number` / `boolean`(`object`/`array` 仅作 JSON 多行编辑兼容,新技能慎用) - `enum` / `default` / `required` / `sensitive` / `readOnly` - `display_type` / `format`:`textarea` | `date` | `datetime_unix_seconds` | `url` | `email` | **`lookup`** -- 字段内联 `lookup`(与 CRUD `options_json` lookup 对象同构;可选 `extra_column`) +- 字段内联 `lookup`(与 CRUD `options_json` lookup 对象同构;可选 `extra_column`、`multiple`) - 属性级 `description`(表单标签下方灰字说明) - 简单模板替换(`{{fieldName}}`) - 可选空数字字段提交时由宿主省略,避免 `""` 触发 number 校验失败 @@ -937,7 +958,7 @@ CRUD 侧宿主义务与禁止事项见 [`SCHEMA.md`](SCHEMA.md)「表单与展 - 短名看不懂或会覆盖数据时,用独立的 `hoverHint`;不要把确认黄条复用成悬停 - 模板参数使用 `{{fieldName}}`,且必须在 `inputSchema.properties` 中声明 - `entrypoint.args` 项当前只允许 `string`、`number`、`boolean`;不允许对象或 `null` -- **不要**在当前模板中宣传文件上传、多选数组、`oneOf`、复杂 nested object 等尚未稳定支持的能力 +- **不要**在当前模板中宣传文件上传、任意 JSON 多选数组、`oneOf`、复杂 nested object 等尚未稳定支持的能力;Lookup 多选请用 `lookup.multiple` ## 风险与入口配置 - 查询、诊断、版本检查可以放到 `skill-detail` 或 `agent` diff --git a/references/SCHEMA.md b/references/SCHEMA.md index ea23024..9e1cc6c 100644 --- a/references/SCHEMA.md +++ b/references/SCHEMA.md @@ -308,7 +308,7 @@ upsert_column_metadata( | `number` | `INTEGER` / `REAL` / `NUMERIC` | 原文 | **数字输入**(`type=number`) | 数字;无 `display_type` 时,物理类型为 INT/REAL/NUMERIC 也应走数字控件 | | `boolean` | `INTEGER` 0/1 或 BOOL | 开关语义展示 | 开关 | `0` / `1` | | (枚举) | 任意 + `options_json` **数组** | 标签映射 | 下拉选择 | 选项 `value` | -| `lookup` | 跨表引用 id 标量 + `options_json` **对象** | 标签映射 | 下拉选择(查关联表) | 业务 id(非 SQLite 外键) | +| `lookup` | 跨表引用 id;`options_json` **对象**;可选 `multiple` | 单选:标签映射;多选:多标签(如「豆包、元宝」) | 单选下拉;多选见下文 | 单选:业务 id 标量;多选:`TEXT` 逗号分隔 id(非 SQLite 外键) | | (敏感) | 任意;列 `sensitive=1` | 掩码/隐藏策略由宿主定 | 新建可用密码框;编辑策略与既有宿主一致 | 原文(勿日志明文) | | `date` | `TEXT` 推荐 `YYYY-MM-DD`,或可安全归一的值 | 日期 | **日期选择**(日) | `YYYY-MM-DD` | | `datetime_unix_seconds` | `INTEGER` | `YYYY-MM-DD HH:mm:ss` | **日期时间选择**;提交写回 **Unix 秒** | Unix **秒**整数;禁止改成 ISO 字符串 | @@ -320,9 +320,11 @@ upsert_column_metadata( **明确不在本版稳定范围(勿在新技能宣传为已支持):** - 文件 / 图片上传 -- 多选、标签数组、复杂 `oneOf` / nested object 表单 +- 复杂 `oneOf` / nested object 表单(任意 JSON 数组当业务多选控件) - 把业务可编辑时间存成毫秒、ISO 文本或 SQLite `datetime` 文本(审计字段规范见「标准时间字段」) +> **Lookup 多选**已纳入本版契约(见下文 `multiple`),与「任意多选/标签数组」不是同一能力。 + `datetime_unix_milliseconds`:仅兼容**列表展示**旧数据;**新技能不要用**;可编辑时间一律 `datetime_unix_seconds`。 #### 新增/编辑表单:字段说明与默认值(宿主支持) @@ -573,7 +575,9 @@ CREATE TABLE demo_items ( ### 2)跨表引用 lookup(对象,用于关联 id 字段) -当字段取值依赖同库另一张业务表时,`display_type` 设为 `lookup`,`options_json` 写对象(**不是**数组)。这是**展示与表单下拉**契约,**不是** SQLite 外键: +当字段取值依赖同库另一张业务表时,`display_type` 设为 `lookup`,`options_json` 写对象(**不是**数组)。这是**展示与表单选择**契约,**不是** SQLite 外键。 + +**单选(默认,与既有行为一致):** ```json { @@ -586,26 +590,54 @@ CREATE TABLE demo_items ( } ``` +**多选(平行扩展,仅多一个开关):** + +```json +{ + "ref_table": "engines", + "value_column": "id", + "label_column": "display_name", + "filter_column": "enabled", + "filter_value": 1, + "multiple": true +} +``` + | 字段 | 必填 | 说明 | |---|---|---| | `ref_table` | 是 | 同库被引用表名(snake_case 标识符) | | `value_column` | 是 | 写入当前字段的值列(业务 id / 编号) | -| `label_column` | 是 | 下拉主文案列(名称) | +| `label_column` | 是 | 选项主文案列(名称) | | `extra_column` | 否 | 第三展示列(不必叫「备注」);有值时拼进括号 | | `filter_column` / `filter_value` | 否 | 等值过滤(如只列出 `enabled=1`) | +| `multiple` | 否 | `true` = 多选 Lookup;省略或 `false` = **单选**(默认) | -宿主在加载表 schema 时查询 `ref_table`,把结果展开成 `{value,label}` 下拉选项;**物理列仍存标量**(如 `platform_key` 文本、`account_id` 整数),不做嵌套子表。 +宿主在加载表 schema 时查询 `ref_table`,把结果展开成 `{value,label}` 选项列表。 + +**存库约定:** + +| 模式 | 物理列建议 | 存什么 | +|------|------------|--------| +| 单选 | 整数或文本标量 | 一个业务 id(如 `2`、`douyin`) | +| 多选 | **`TEXT`** | 逗号分隔 id,如 `1,2,5`(与 `$selection.ids` 风格一致);**不要**建联接子表当 UI 多选 | **统一展示文案(宿主固定一种,技能只配列):** -- 默认:`{value} · {label}`,例如 `2 · 通用私信-留资`、`douyin · 抖音` +- 单选默认:`{value} · {label}`,例如 `2 · 通用私信-留资`、`douyin · 抖音` - 配置了 `extra_column` 且该行有非空值:`{value} · {label}({extra})` - 未配置 `extra_column`、或额外列为空:不加 `()` - `label` 为空或与 `value` 相同时:只显示 `value`(可再带额外括号) +- **多选列表格**:按 id 拆开后逐个映射为 label,再用顿号/逗号拼接(如 `豆包、元宝`);过长可截断并提示「+N」 -Action 弹窗内联 Lookup 与上表 **同构**(字段名一致),见 [`ACTIONS.md`](ACTIONS.md)「跨表引用:Lookup」。 +**控件呈现(宿主策略,非第二套契约):** -示例:账号表的平台字段 → 平台信息表;凭据表的账号编号 → 账号信息表;私信 Action 选话术 → `message_templates`。 +- 单选:下拉 Select(现状) +- 多选且选项较少(建议 ≤6):勾选列表 +- 多选且选项较多:字段内 Chip + 可搜索下拉(**不是**再开一层业务弹窗) + +Action 弹窗内联 Lookup 与上表 **同构**(字段名一致,含 `multiple`),见 [`ACTIONS.md`](ACTIONS.md)「跨表引用:Lookup」。 + +示例:账号表的平台字段 → 平台信息表;凭据表的账号编号 → 账号信息表;私信 Action 选话术 → `message_templates`;抽检 Action 多选引擎 → `engines` + `multiple: true`。 ## 幂等初始化标准 diff --git a/scripts/db/display_metadata_validator.py b/scripts/db/display_metadata_validator.py index 2d1c10b..0fe5ef3 100644 --- a/scripts/db/display_metadata_validator.py +++ b/scripts/db/display_metadata_validator.py @@ -219,6 +219,8 @@ def validate_options_json(options_json: str | None) -> tuple[list[str], list[str errors.append(f"options_json.{key} 必须是安全标识符,当前为 {raw!r}") if parsed.get("filter_column") not in (None, "") and "filter_value" not in parsed: warnings.append("options_json lookup 声明了 filter_column 但未提供 filter_value") + if "multiple" in parsed and not isinstance(parsed.get("multiple"), bool): + errors.append("options_json lookup.multiple 必须是布尔值") return errors, warnings if not isinstance(parsed, list): diff --git a/scripts/util/constants.py b/scripts/util/constants.py index 2b74577..ceb0929 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.92" +SKILL_VERSION = "1.0.93" LOG_LOGGER_NAME = "openclaw.skill.your_skill_slug" PLATFORM_KIT_MIN_VERSION = "1.2.11" diff --git a/tests/test_docs_standards.py b/tests/test_docs_standards.py index 31c8646..054f7af 100644 --- a/tests/test_docs_standards.py +++ b/tests/test_docs_standards.py @@ -353,6 +353,8 @@ class TestDocsStandards(unittest.TestCase): "表与表之间:禁止 SQLite 外键", "禁止 SQLite 外键", "跨表引用 lookup", + "\"multiple\": true", + "Lookup 多选", ): self.assertIn(marker, text, msg=f"SCHEMA.md missing {marker!r}") @@ -364,6 +366,7 @@ class TestDocsStandards(unittest.TestCase): "跨表引用:Lookup", "display_type: \"lookup\"", "extra_column", + "multiple", "数据管理新增/编辑", "_jiangchang_columns.description", "datetime_unix_seconds",