Release v1.0.122: document Action enumNames and column-bound static enum labels
All checks were successful
技能自动化发布 / release (push) Successful in 9s
All checks were successful
技能自动化发布 / release (push) Successful in 9s
This commit is contained in:
@@ -9,6 +9,12 @@
|
||||
- 小节正文写用户能看懂的变化(新能力、修复、注意事项),避免堆砌内部实现细节
|
||||
- 打 tag 前**必须**为即将发布的版本新增一节;找不到匹配小节时,本次发布不会更新 changelog 字段
|
||||
|
||||
## 1.0.122
|
||||
|
||||
- 沉淀 Action **静态枚举中文 label**:`enum` + **`enumNames`**,或绑列复用列 `options_json` 数组
|
||||
- 约定宿主优先序:**绑列 options → enumNames → 纯 enum**;固定小枚举不要造假 Lookup
|
||||
- 更新 Schema / ACTIONS / SCHEMA / CHECKLIST A4-13;新增 `enum-names-action.example.json`
|
||||
|
||||
## 1.0.121
|
||||
|
||||
- 沉淀 Lookup **分页勾选小表**:`display_type: lookup` + `presentation: "paged_table"` + 可选 `page_size`(仍是 Lookup,不要新造控件类型)
|
||||
|
||||
2
SKILL.md
2
SKILL.md
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: 技能开发模板(通用业务版)
|
||||
description: "OpenClaw 通用业务技能开发模板,供复制后定制新业务 skill。定制步骤见 development/DEVELOPMENT.md。"
|
||||
version: 1.0.121
|
||||
version: 1.0.122
|
||||
author: 深圳匠厂科技有限公司
|
||||
metadata:
|
||||
openclaw:
|
||||
|
||||
51
assets/examples/enum-names-action.example.json
Normal file
51
assets/examples/enum-names-action.example.json
Normal file
@@ -0,0 +1,51 @@
|
||||
{
|
||||
"_comment": "形状参考:Action 静态枚举用 enum + enumNames 显示中文;提交仍是 enum value。有对应表列时优先 column 绑列复用 options_json。完整契约见 references/ACTIONS.md「静态枚举 label」。",
|
||||
"schemaVersion": 1,
|
||||
"skill": "your-skill-slug",
|
||||
"actions": [
|
||||
{
|
||||
"id": "create-ticket",
|
||||
"label": "新建工单(示例)",
|
||||
"description": "演示 enumNames:界面显示中文,CLI 仍收到英文枚举值。",
|
||||
"placements": ["toolbar", "agent"],
|
||||
"executionProfile": "sync",
|
||||
"interaction": "form",
|
||||
"bind": {
|
||||
"tables": ["tickets"]
|
||||
},
|
||||
"entrypoint": {
|
||||
"type": "cli",
|
||||
"command": "ticket-create",
|
||||
"args": [
|
||||
"--category", "{{category}}",
|
||||
"--priority", "{{priority}}",
|
||||
"--title", "{{title}}"
|
||||
]
|
||||
},
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"required": ["title", "category"],
|
||||
"properties": {
|
||||
"title": {
|
||||
"type": "string",
|
||||
"title": "标题"
|
||||
},
|
||||
"category": {
|
||||
"type": "string",
|
||||
"title": "类型",
|
||||
"column": "category",
|
||||
"enum": ["incident", "request", "consult", "other"],
|
||||
"enumNames": ["故障", "需求", "咨询", "其他"]
|
||||
},
|
||||
"priority": {
|
||||
"type": "string",
|
||||
"title": "优先级",
|
||||
"enum": ["low", "medium", "high", "urgent"],
|
||||
"enumNames": ["低", "中", "高", "紧急"],
|
||||
"description": "无对应列时仅靠 enumNames 展示中文"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -147,7 +147,13 @@
|
||||
"default": {},
|
||||
"enum": {
|
||||
"type": "array",
|
||||
"minItems": 1
|
||||
"minItems": 1,
|
||||
"description": "静态枚举取值(提交/存库用)。展示中文时配 enumNames,或绑列复用 options_json;勿用 Lookup 冒充固定小枚举。"
|
||||
},
|
||||
"enumNames": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"description": "可选。与 enum 一一对应的展示文案(JSON Schema / OpenAPI 风格)。缺项或空串时宿主回退为对应 enum 原文。有对应表列时优先绑列复用 options_json(见 ACTIONS.md「静态枚举 label」)。"
|
||||
},
|
||||
"sensitive": { "type": "boolean" },
|
||||
"readOnly": {
|
||||
@@ -162,7 +168,7 @@
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"pattern": "^[a-z][a-z0-9_]*$",
|
||||
"description": "可选。绑定业务表物理列名;Action 弹窗(toolbar/batch)与 replaces create/update 抽屉均可复用该列的中文名与 lookup 下拉"
|
||||
"description": "可选。绑定业务表物理列名;Action 弹窗(toolbar/batch)与 replaces create/update 抽屉可复用该列的中文名与 options(静态枚举 options_json 数组,或已解析的 lookup)。"
|
||||
},
|
||||
"display_type": {
|
||||
"type": "string",
|
||||
|
||||
@@ -115,6 +115,7 @@ checklist_version: 1.0
|
||||
- [ ] A4-10 **P0** 数据管理弹窗与任务失败文案:全中文产品用语;禁止在 `description` / `confirmation.message` / 参数 `description` / 用户向 `message` 写英文字段名、CLI 子命令、`async`/`pending`/`status=` 等(Agent 路由放 `SKILL.md`;见 `references/ACTIONS.md`「用户可见文案」)
|
||||
- [ ] A4-11 **P1** 若行操作应按业务状态显隐(如仅待确认可「采用」、仅已采用可「加入待写」):用 **`bind.visibleWhen`**(对当前行列值求值;存库原始值如 `approved`);勿指望 Policy L3;CLI 侧仍须校验状态(见 `references/ACTIONS.md`「行菜单显隐」)
|
||||
- [ ] A4-12 **P1** 若要在表单里翻页勾选一批业务行(文章、平台等):用 Lookup **`presentation: "paged_table"`**(仍是 `display_type: lookup`,勿新造类型);`skill_cli` 的 stdout 为一行 `{"items","total"}`,0 条也成功退出;该列表命令不要在入口加载整包 platform-kit 或 Playwright(见 `references/ACTIONS.md`「分页勾选小表」)
|
||||
- [ ] A4-13 **P1** 若 Action 有固定小枚举(状态/船司/优先级等)需中文展示:用 `enum` + **`enumNames`**,或绑列复用列 `options_json` 数组;宿主优先序为绑列 options → enumNames → 纯 enum;**禁止**为中文展示造假 Lookup(见 `references/ACTIONS.md`「静态枚举 label」)
|
||||
---
|
||||
|
||||
## 5. 账号与浏览器会话(网页 RPA 适用;其他形态标 N/A 或改测等价项)
|
||||
|
||||
@@ -110,7 +110,7 @@ Action 是否提供、挂哪些 `placements`,由本技能 **入口定位表**
|
||||
| **bind** | `{ "tables": ["business_table"] }`;可选 `inputMapping`、`columnMapping`、**`labelColumn`**、**`selectionMode`**、**`visibleWhen`**(行内/批量预填、选择摘要标签、勾选驱动模式、**行菜单显隐**,见下节) |
|
||||
| **policy** | 可选;`enabled` + `entrypoint`(推荐 `policy` CLI);见 POLICY.md。未启用 = L0 |
|
||||
| **cellPresentation** | 可选;`{ "enabled": true }` 时宿主应加载格子与 grid 呈现;见 [`CELL_PRESENTATION.md`](CELL_PRESENTATION.md)。未启用 = P0 |
|
||||
| inputSchema | 根 `type: object`;properties 使用 `string` / `number` / `boolean`;支持 `enum`、`default`、`required`、`sensitive`、`readOnly`、可选 **`revealInForm`**;可选 `display_type` / `format`(含 `file` / `directory` 等);可选 **`pathRoot`**(file/directory 起始目录,相对技能数据根);可选 **`visibleWhen`**(字段条件显隐,见专节) |
|
||||
| inputSchema | 根 `type: object`;properties 使用 `string` / `number` / `boolean`;支持 `enum`、可选 **`enumNames`**、`default`、`required`、`sensitive`、`readOnly`、可选 **`revealInForm`**;可选 `column`(绑列复用 options);可选 `display_type` / `format`(含 `file` / `directory` 等);可选 **`pathRoot`**(file/directory 起始目录,相对技能数据根);可选 **`visibleWhen`**(字段条件显隐,见专节) |
|
||||
| confirmation | `{ "message": "..." }` — **风险文案**(confirm/form 弹窗黄条;agent 元数据);**不是**「要不要弹窗」的开关 |
|
||||
| **hoverHint** | 可选字符串。数据管理**工具栏悬停**文案;与 `description` / `confirmation.message` **独立**。省略则不显示悬停,不回退。短名已够懂时不要写。需宿主已支持该字段;当前实现为顶栏技能按钮,不含宿主内置删除/刷新/新增/导入/导出 |
|
||||
| **visibleWhen** | 可选。写在 **property** 上(非 action 顶层):依赖同表单其它字段值决定是否展示;隐藏字段不提交、不校验 `required`。**勿与** [`POLICY.md`](POLICY.md) L3 行态 `visibleWhen` 混淆;也**勿**用它藏「已选择」已表达的映射主键(见「选择摘要」) |
|
||||
@@ -437,13 +437,14 @@ Action 是否提供、挂哪些 `placements`,由本技能 **入口定位表**
|
||||
- 点默认「导入」时**固定**打开宿主导入面板;确认后若有 `replaces:import` 则调用该 Action(`interaction` 不决定是否再弹技能表单)。
|
||||
- `update` / `delete` 可用 `inputMapping`(`$row.$pk`、`$selection.ids`)预填主键。
|
||||
- `import` 约定:`filePath`(或 `file`)+ 可选 `onError`(`skip`/`abort` 或上例中文枚举);技能自行解析文件与表头。
|
||||
- 整表 `_jiangchang_tables.readonly=1` 时仍隐藏写按钮(优先于 `replaces`)。
|
||||
- **列元数据复用(lookup)**:Action 表单(含 toolbar/batch 弹窗与 create/update 抽屉)可将 `inputSchema` 字段对齐业务列,复用中文名与 lookup 下拉:
|
||||
- 优先让属性名 = 物理列名(如 `platform_key`);
|
||||
- 或 `bind.columnMapping`:`{ "platform": "platform_key" }`;
|
||||
- 或字段上写 `"column": "platform_key"`。
|
||||
- 当前表没有对应列时,用字段内联 `lookup`(见下文「跨表引用:Lookup」)。
|
||||
- **不要**在 actions.json 里写死会过期的全量平台 `enum`,也**不要**让用户手填内部编号。
|
||||
- 整表 `_jiangchang_tables.readonly=1` 时仍隐藏宿主默认新增/编辑/导入与直写删除;**若声明了 `replaces: delete`,宿主仍显示「删除」并走技能 CLI**(不直写 SQLite)。
|
||||
- **列元数据复用**:Action 表单(含 toolbar/batch 弹窗与 create/update 抽屉)可将 `inputSchema` 字段对齐业务列,复用中文名与列上 **options**(静态枚举 `options_json` **数组**,或已解析的 lookup):
|
||||
- 优先让属性名 = 物理列名(如 `carrier_code` / `platform_key`);
|
||||
- 或 `bind.columnMapping`:`{ "carrierCode": "carrier_code" }`;
|
||||
- 或字段上写 `"column": "carrier_code"`(推荐:驼峰属性名时显式写出,避免只靠启发式)。
|
||||
- 当前表没有对应列、且选项是**固定小枚举**时:用 `enum` + **`enumNames`**(见下文「静态枚举 label」),**不要**为中文展示去造 Lookup。
|
||||
- 当前表没有对应列、且选项是**跨表/跨技能业务行**时:用字段内联 `lookup`(见下文「跨表引用:Lookup」)。
|
||||
- **不要**在 actions.json 里写死会过期的全量业务行 `enum`,也**不要**让用户手填内部编号。
|
||||
|
||||
## `interaction`(点击交互方式)
|
||||
|
||||
@@ -1141,7 +1142,7 @@ Skill Action 弹窗与数据管理「新增 / 编辑」使用**同一套控件
|
||||
| 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` 数组 |
|
||||
| 枚举下拉 | `enum: [...]` + 可选 **`enumNames`**;有对应列时再加 `column` / 属性名对齐,复用列 `options_json` | `options_json` **数组** `{value,label}` |
|
||||
| 关联下拉(Lookup) | 内联 `display_type: lookup` + `lookup`(`local_sql` / `skill_cli` / `external_api`;可选 `multiple`;可选 `presentation: "paged_table"`),或绑列 | 列侧仅同库 `local_sql`;多选存 `TEXT` 逗号分隔 id;分页小表只在 Action |
|
||||
| 密码 | `sensitive: true` | 列 `sensitive` |
|
||||
| 年份 | `display_type` 或 `format`: `year` | `display_type: year` → 存 `YYYY` |
|
||||
@@ -1162,6 +1163,33 @@ Skill Action 弹窗与数据管理「新增 / 编辑」使用**同一套控件
|
||||
**本版不做(勿宣传):** 浏览器内文件上传控件、复杂 `oneOf` / nested object 专用表单;`visibleWhen` 多条件 AND/OR 树、跨 Action 依赖、CRUD 新增/编辑对称显隐;Action 参数表单的列筛选对称。
|
||||
(**Lookup 多选**已支持:同一 `lookup` + `multiple: true`,见上文;不是任意标签数组。**分页勾选小表**已支持:同一 `lookup` + `presentation: "paged_table"`,见「分页勾选小表」。)
|
||||
(**本地路径选择**已支持:`file` / `directory` + 可选 `pathRoot`,见下节;提交值仍是本机绝对路径字符串,不是 multipart 上传。)
|
||||
(**静态枚举中文 label**已支持:Action `enumNames` 与列 `options_json` 数组对称,见下节「静态枚举 label」。)
|
||||
|
||||
### 静态枚举 label(`enum` / `enumNames` / 绑列)
|
||||
|
||||
固定小枚举(状态、船司代码、优先级等)**不要**走 Lookup。CRUD 用列 `options_json` 数组;Action 表单要与「新增记录」对齐中文展示时,按下面做。
|
||||
|
||||
**宿主解析优先序(高 → 低):**
|
||||
|
||||
1. **绑列 options**:字段通过 `column` / `columnMapping` / 属性名(含 camelCase→snake_case)对齐到当前表列,且该列已有解析后的 `options`(来自 `options_json` 数组或 lookup)→ 用列上的 `{value,label}`。
|
||||
2. **`enumNames`**:与 `enum` 下标一一对应的展示文案;缺项或空串回退为该位 `enum` 原文。
|
||||
3. **纯 `enum`**:无 label 时宿主只能显示代码值(体验差,新技能避免)。
|
||||
|
||||
```json
|
||||
"carrierCode": {
|
||||
"type": "string",
|
||||
"title": "船司",
|
||||
"column": "carrier_code",
|
||||
"enum": ["maersk", "hmm"],
|
||||
"enumNames": ["MAERSK 马士基", "HMM 现代商船"]
|
||||
}
|
||||
```
|
||||
|
||||
推荐:
|
||||
|
||||
- 表上已有同义枚举列 → **显式 `column`**(或属性名 = 列名)+ 列侧写好 `options_json`;`enum`/`enumNames` 可保留作无列上下文时的兜底(如 agent-only Action)。
|
||||
- 无对应列、仅 Action 表单需要 → 只写 `enum` + `enumNames`(见 [`assets/examples/enum-names-action.example.json`](../assets/examples/enum-names-action.example.json))。
|
||||
- **禁止**为了中文展示把固定枚举改成假 Lookup。
|
||||
|
||||
日期时间约定:
|
||||
|
||||
@@ -1235,7 +1263,8 @@ Skill Action 弹窗与数据管理「新增 / 编辑」使用**同一套控件
|
||||
### 当前宿主稳定支持(摘要)
|
||||
|
||||
- 根:`object`;属性:`string` / `number` / `boolean`(`object`/`array` 仅作 JSON 多行编辑兼容,新技能慎用)
|
||||
- `enum` / `default` / `required` / `sensitive` / `readOnly` / 可选 `revealInForm`
|
||||
- `enum` / 可选 **`enumNames`** / `default` / `required` / `sensitive` / `readOnly` / 可选 `revealInForm`
|
||||
- 可选 **`column`**:绑列复用 displayName 与 options(静态枚举或 lookup)
|
||||
- `display_type` / `format`:`textarea` | **`year`** | **`year_month`** / `month` | `date` | `datetime_unix_seconds` | `url` | `email` | **`lookup`** | **`file`** | **`directory`**
|
||||
- 可选 **`pathRoot`**(仅 `file` / `directory`):相对技能数据根的起始目录;省略默认 `{skill_data}/input`
|
||||
- 字段内联 `lookup`(与 CRUD `options_json` lookup 对象同构;可选 `extra_column`、`multiple`)
|
||||
@@ -1365,7 +1394,7 @@ CRUD 侧宿主义务与禁止事项见 [`SCHEMA.md`](SCHEMA.md)「表单与展
|
||||
约定:
|
||||
|
||||
- `boolean` 在宿主中渲染为开关
|
||||
- `enum` 在宿主中渲染为选择框
|
||||
- `enum` 在宿主中渲染为选择框(或短列表分段控件);**展示文案**优先绑列 options,其次 `enumNames`,再次纯 `enum` 值
|
||||
- `type: number` 渲染为数字输入
|
||||
- `display_type`/`format: year` 渲染为年份选择;`year_month` / `month` 渲染为年月选择
|
||||
- `display_type`/`format: date` 渲染为日期(日);`datetime_unix_seconds` 渲染为日期时间并按 **Unix 秒**提交
|
||||
|
||||
@@ -621,7 +621,7 @@ readonly = 0
|
||||
|
||||
| 层级 | 值 | 说明 |
|
||||
|------|-----|------|
|
||||
| 表级 `readonly` | `1` | 宿主数据管理只读展示,不提供新增/编辑/删除表单 |
|
||||
| 表级 `readonly` | `1` | 宿主数据管理只读展示,不提供新增/编辑/导入与直写删除;若技能声明 `replaces: delete` 仍可显示删除并走 CLI |
|
||||
| 相关业务字段 `editable` | `0` | 元数据权限只约束宿主 UI,不限制技能代码写库 |
|
||||
|
||||
模板**默认不建**此类审计表。过程看 `jiangchang.log`,进度/终态看任务中心,业务结果看业务表。
|
||||
@@ -687,6 +687,8 @@ CREATE TABLE demo_items (
|
||||
|
||||
宿主据此渲染选择框;技能侧负责保证 JSON 合法且与物理字段取值一致。
|
||||
|
||||
**与 Action 表单对称:** 数据管理「新增/编辑」读列 `options_json`;Skill Action 弹窗用 `enum` + 可选 **`enumNames`**,或字段绑列(`column` / 属性名对齐)直接复用本列 options。宿主优先序:**绑列 options → enumNames → 纯 enum**。详见 [`ACTIONS.md`](ACTIONS.md)「静态枚举 label」。固定小枚举**不要**改成 Lookup。
|
||||
|
||||
### 2)跨表引用 lookup(对象,用于关联 id 字段)
|
||||
|
||||
当字段取值依赖同库另一张业务表时,`display_type` 设为 `lookup`,`options_json` 写对象(**不是**数组)。这是**展示与表单选择**契约,**不是** SQLite 外键。
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"""技能标识、版本与平台公共库约束(复制后请修改 slug/version/logger)。"""
|
||||
|
||||
SKILL_SLUG = "your-skill-slug"
|
||||
SKILL_VERSION = "1.0.121"
|
||||
SKILL_VERSION = "1.0.122"
|
||||
LOG_LOGGER_NAME = "openclaw.skill.your_skill_slug"
|
||||
PLATFORM_KIT_MIN_VERSION = "1.2.11"
|
||||
|
||||
@@ -290,6 +290,29 @@ class TestActionsManifest(unittest.TestCase):
|
||||
action_props = schema["$defs"]["action"]["properties"]
|
||||
self.assertNotIn("visibleWhen", action_props)
|
||||
|
||||
def test_schema_defines_enum_names_on_input_property(self) -> None:
|
||||
schema = _load_actions_schema()
|
||||
props = schema["$defs"]["inputProperty"]["properties"]
|
||||
self.assertIn("enum", props)
|
||||
self.assertIn("enumNames", props)
|
||||
self.assertEqual(props["enumNames"].get("type"), "array")
|
||||
self.assertEqual(props["enumNames"].get("minItems"), 1)
|
||||
self.assertIn("column", props)
|
||||
|
||||
def test_enum_names_example_exists_and_aligns_labels(self) -> None:
|
||||
path = os.path.join(
|
||||
get_skill_root(), "assets", "examples", "enum-names-action.example.json"
|
||||
)
|
||||
self.assertTrue(os.path.isfile(path), msg="enum-names example must exist")
|
||||
with open(path, encoding="utf-8") as f:
|
||||
example = json.load(f)
|
||||
props = example["actions"][0]["inputSchema"]["properties"]
|
||||
category = props["category"]
|
||||
self.assertEqual(len(category["enum"]), len(category["enumNames"]))
|
||||
self.assertEqual(category.get("column"), "category")
|
||||
priority = props["priority"]
|
||||
self.assertEqual(len(priority["enum"]), len(priority["enumNames"]))
|
||||
|
||||
def test_default_manifest_omits_visible_when(self) -> None:
|
||||
for action in _load_actions_manifest()["actions"]:
|
||||
properties = ((action.get("inputSchema") or {}).get("properties")) or {}
|
||||
@@ -802,6 +825,9 @@ class TestActionsManifest(unittest.TestCase):
|
||||
"已选择",
|
||||
"revealInForm",
|
||||
"映射只读",
|
||||
"enumNames",
|
||||
"静态枚举 label",
|
||||
"绑列 options",
|
||||
):
|
||||
self.assertIn(marker, text, msg=f"ACTIONS.md missing {marker!r}")
|
||||
self.assertNotIn(
|
||||
|
||||
@@ -446,17 +446,27 @@ class TestDocsStandards(unittest.TestCase):
|
||||
),
|
||||
msg="lookup-paged-table.action.example.json missing",
|
||||
)
|
||||
self.assertTrue(
|
||||
os.path.isfile(
|
||||
os.path.join(get_skill_root(), "assets/examples/enum-names-action.example.json")
|
||||
),
|
||||
msg="enum-names-action.example.json missing",
|
||||
)
|
||||
checklist = self._read("development/CHECKLIST.md")
|
||||
self.assertIn("skill_cli", checklist)
|
||||
self.assertIn("A4-12", checklist)
|
||||
self.assertIn("A4-13", checklist)
|
||||
self.assertIn("enumNames", checklist)
|
||||
self.assertIn("paged_table", checklist)
|
||||
account_mgr = self._read("development/ACCOUNT_MANAGER.md")
|
||||
self.assertIn("skill_cli", account_mgr)
|
||||
schema = self._read("references/SCHEMA.md")
|
||||
self.assertIn("skill_cli", schema)
|
||||
self.assertIn("enumNames", schema)
|
||||
schema_json = self._read("assets/schemas/skill-actions.schema.json")
|
||||
self.assertIn("skill_cli", schema_json)
|
||||
self.assertIn("paged_table", schema_json)
|
||||
self.assertIn("enumNames", schema_json)
|
||||
self.assertIn("page_size", schema_json)
|
||||
self.assertIn('"skill"', schema_json)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user