diff --git a/CHANGELOG.md b/CHANGELOG.md index f457a43..8e272f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,12 @@ - 小节正文写用户能看懂的变化(新能力、修复、注意事项),避免堆砌内部实现细节 - 打 tag 前**必须**为即将发布的版本新增一节;找不到匹配小节时,本次发布不会更新 changelog 字段 +## 1.0.92 + +- 沉淀数据管理 `replaces: import`:导入 UI 统一宿主面板,确认后由技能写库 +- ACTIONS / schema / 守护测试对齐 create·update·delete·import;补充可抄示例 `replaces-*.action.example.json` +- 修正旧文档「宿主导入仍直写」的过时说明 + ## 1.0.91 - 沉淀「表与表之间禁止 SQLite 外键」契约:只存业务 id,关联与级联在 repository/service;界面用 `lookup` diff --git a/SKILL.md b/SKILL.md index 5063b50..f72e42a 100644 --- a/SKILL.md +++ b/SKILL.md @@ -1,7 +1,7 @@ --- name: 技能开发模板(通用业务版) description: "OpenClaw 通用业务技能开发模板,供复制后定制新业务 skill。定制步骤见 development/DEVELOPMENT.md。" -version: 1.0.91 +version: 1.0.92 author: 深圳匠厂科技有限公司 metadata: openclaw: diff --git a/assets/examples/README.md b/assets/examples/README.md index c7d5812..6190cf6 100644 --- a/assets/examples/README.md +++ b/assets/examples/README.md @@ -6,3 +6,4 @@ - `render-html.action.example.json`:HTML 报告「重新生成」Action 形状参考(固定 id `render-html`;见 `references/ACTIONS.md`)。 - `lookup-action.example.json`:Action 内联 Lookup 下拉形状参考(跨表引用勿填编号;见 `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/replaces-create.action.example.json b/assets/examples/replaces-create.action.example.json new file mode 100644 index 0000000..94b7ba6 --- /dev/null +++ b/assets/examples/replaces-create.action.example.json @@ -0,0 +1,28 @@ +{ + "id": "item-create", + "label": "新增记录", + "description": "新增业务记录并执行领域副作用(走技能 CLI,非宿主直写库)。", + "replaces": "create", + "placements": [], + "executionProfile": "sync", + "interaction": "form", + "bind": { + "tables": ["items"] + }, + "hostEffects": { + "onSuccess": ["reloadTable"] + }, + "inputSchema": { + "type": "object", + "required": ["name"], + "properties": { + "name": { "type": "string", "title": "名称" }, + "note": { "type": "string", "title": "备注" } + } + }, + "entrypoint": { + "type": "cli", + "command": "item", + "args": ["create", "--name", "{{name}}", "--note", "{{note}}"] + } +} diff --git a/assets/examples/replaces-delete.action.example.json b/assets/examples/replaces-delete.action.example.json new file mode 100644 index 0000000..390f758 --- /dev/null +++ b/assets/examples/replaces-delete.action.example.json @@ -0,0 +1,33 @@ +{ + "id": "item-delete", + "label": "删除记录", + "description": "删除业务记录并清理关联副作用。", + "replaces": "delete", + "placements": [], + "executionProfile": "sync", + "interaction": "confirm", + "confirmation": { + "message": "确定删除该记录?关联数据将一并清理。" + }, + "bind": { + "tables": ["items"], + "inputMapping": { + "id": "$row.$pk" + } + }, + "hostEffects": { + "onSuccess": ["reloadTable", "clearSelection"] + }, + "inputSchema": { + "type": "object", + "required": ["id"], + "properties": { + "id": { "type": "number", "title": "编号", "readOnly": true } + } + }, + "entrypoint": { + "type": "cli", + "command": "item", + "args": ["delete", "--id", "{{id}}"] + } +} diff --git a/assets/examples/replaces-import.action.example.json b/assets/examples/replaces-import.action.example.json new file mode 100644 index 0000000..16c4ce0 --- /dev/null +++ b/assets/examples/replaces-import.action.example.json @@ -0,0 +1,38 @@ +{ + "id": "item-import", + "label": "导入记录", + "description": "由宿主「导入数据」面板调用:解析 Excel/CSV 并写入业务表(含领域校验与派生字段)。", + "replaces": "import", + "placements": [], + "executionProfile": "sync", + "interaction": "form", + "bind": { + "tables": ["items"] + }, + "hostEffects": { + "onSuccess": ["reloadTable"] + }, + "inputSchema": { + "type": "object", + "required": ["filePath"], + "properties": { + "filePath": { + "type": "string", + "title": "导入文件", + "description": "由宿主导入面板传入的 Excel/CSV 路径。", + "format": "file" + }, + "onError": { + "type": "string", + "title": "导入规则(出错时)", + "enum": ["跳过该行,继续导入", "立即停止,不再导入后续行"], + "default": "跳过该行,继续导入" + } + } + }, + "entrypoint": { + "type": "cli", + "command": "item", + "args": ["import", "--file", "{{filePath}}", "--on-error", "{{onError}}"] + } +} diff --git a/assets/examples/replaces-update.action.example.json b/assets/examples/replaces-update.action.example.json new file mode 100644 index 0000000..c2f5b0b --- /dev/null +++ b/assets/examples/replaces-update.action.example.json @@ -0,0 +1,34 @@ +{ + "id": "item-update", + "label": "编辑记录", + "description": "编辑业务记录并重算派生字段。", + "replaces": "update", + "placements": [], + "executionProfile": "sync", + "interaction": "form", + "bind": { + "tables": ["items"], + "inputMapping": { + "id": "$row.$pk", + "name": "$row.name", + "note": "$row.note" + } + }, + "hostEffects": { + "onSuccess": ["reloadTable"] + }, + "inputSchema": { + "type": "object", + "required": ["id", "name"], + "properties": { + "id": { "type": "number", "title": "编号", "readOnly": true }, + "name": { "type": "string", "title": "名称" }, + "note": { "type": "string", "title": "备注" } + } + }, + "entrypoint": { + "type": "cli", + "command": "item", + "args": ["update", "--id", "{{id}}", "--name", "{{name}}", "--note", "{{note}}"] + } +} diff --git a/assets/examples/version-response.json b/assets/examples/version-response.json index d893e97..eef63c3 100644 --- a/assets/examples/version-response.json +++ b/assets/examples/version-response.json @@ -1,4 +1,4 @@ { - "version": "1.0.91", + "version": "1.0.92", "skill": "your-skill-slug" } diff --git a/assets/schemas/skill-actions.schema.json b/assets/schemas/skill-actions.schema.json index d0dd02a..e3d778a 100644 --- a/assets/schemas/skill-actions.schema.json +++ b/assets/schemas/skill-actions.schema.json @@ -84,8 +84,8 @@ }, "mutationKind": { "type": "string", - "enum": ["create", "update", "delete"], - "description": "可选。接管数据管理默认按钮:create=新增、update=编辑、delete=删除。须配合非空 bind.tables;无配置时宿主仍直写 SQLite。" + "enum": ["create", "update", "delete", "import"], + "description": "可选。接管数据管理默认按钮:create=新增、update=编辑、delete=删除、import=导入。须配合非空 bind.tables;无配置时宿主仍直写 SQLite。import 时 UI 仍用宿主「导入数据」面板,确认后调用本 Action 写库。" }, "scalarArg": { "type": ["string", "number", "boolean"] @@ -120,12 +120,12 @@ }, "display_type": { "type": "string", - "enum": ["textarea", "date", "datetime_unix_seconds", "url", "email", "lookup"], - "description": "可选。表单控件提示;与 format 二选一即可。lookup=关联下拉(须配 lookup 对象或绑到已有 lookup 列)。须与 CRUD display_type 语义对齐(见 ACTIONS.md / SCHEMA.md)。" + "enum": ["textarea", "date", "datetime_unix_seconds", "url", "email", "lookup", "directory", "file"], + "description": "可选。表单控件提示;与 format 二选一即可。lookup=关联下拉(须配 lookup 对象或绑到已有 lookup 列)。file/directory=本地路径选择。须与 CRUD display_type 语义对齐(见 ACTIONS.md / SCHEMA.md)。" }, "format": { "type": "string", - "enum": ["textarea", "date", "datetime_unix_seconds", "url", "email", "lookup"], + "enum": ["textarea", "date", "datetime_unix_seconds", "url", "email", "lookup", "directory", "file"], "description": "可选。display_type 别名,语义相同。" }, "lookup": { @@ -420,7 +420,8 @@ "skill-detail": { "type": "string", "enum": ["direct", "confirm", "form"] }, "create": { "type": "string", "enum": ["direct", "confirm", "form"] }, "update": { "type": "string", "enum": ["direct", "confirm", "form"] }, - "delete": { "type": "string", "enum": ["direct", "confirm", "form"] } + "delete": { "type": "string", "enum": ["direct", "confirm", "form"] }, + "import": { "type": "string", "enum": ["direct", "confirm", "form"] } }, "description": "按 placement / replaces 覆盖 interaction(只能写 concrete 模式,不能写 auto)。" }, diff --git a/development/DATA_PATHS.md b/development/DATA_PATHS.md index 0a72a97..cf6f2b1 100644 --- a/development/DATA_PATHS.md +++ b/development/DATA_PATHS.md @@ -67,7 +67,8 @@ D:\jiangchang-data\12500\download-video-baidu-haokan\ 宿主约定(与本模板对齐): -- 工具栏「导入」默认从 `input/` 取文件;「导出」默认写到 `output/` +- 工具栏「导入」**UI 一律**打开宿主「导入数据」面板(默认从 `input/` 选文件);「导出」默认写到 `output/` +- 若表声明了 `replaces: import`,确认导入后由技能 CLI 写库(见 [`../references/ACTIONS.md`](../references/ACTIONS.md)「`replaces`」);仅有 `replaces: create` 时宿主可按行调 create(兼容) - 数据管理扫描 `{skill_data}/html/*.html`(一层、非递归);`` → 侧栏显示名;「重新生成」调用 Action id **`render-html`**(见 [`../references/ACTIONS.md`](../references/ACTIONS.md)「HTML 页面报告」) --- diff --git a/development/PRODUCT_SURFACES.md b/development/PRODUCT_SURFACES.md index 145bfe6..a3c5383 100644 --- a/development/PRODUCT_SURFACES.md +++ b/development/PRODUCT_SURFACES.md @@ -56,7 +56,7 @@ 数据管理有两层,**同等重要**: -1. **执行面**:工具栏 / 行级 / 批量 / `replaces` 接管 CRUD —— 与对话一样触发同一 CLI,走完整业务闭环(传统用户可不聊天)。 +1. **执行面**:工具栏 / 行级 / 批量 / `replaces` 接管新增·编辑·删除·**导入** —— 与对话一样触发同一 CLI,走完整业务闭环(传统用户可不聊天)。导入按钮的 UX 始终是宿主「导入数据」面板;有 `replaces:import` 时确认后由技能写库。 2. **运营面**:业务表、状态流向、配置、input/output、日志、HTML —— 看清数据从哪来、到哪去、卡在哪。 **适合:** diff --git a/references/ACTIONS.md b/references/ACTIONS.md index b671cca..1086408 100644 --- a/references/ACTIONS.md +++ b/references/ACTIONS.md @@ -83,7 +83,7 @@ Action 是否提供、挂哪些 `placements`,由本技能 **入口定位表** | 字段 / 层 | 只决定什么 | 不决定什么 | |-----------|------------|------------| | **`placements`** | Action **出现在哪里**(toolbar / row / batch / cron / agent / skill-detail;`row`=行级入口,壳由宿主呈现) | 不决定 sync/async,不决定点了是否弹窗,不决定行级用操作列还是右键,不决定谁能点 | -| **`replaces`** | **可选**:接管数据管理默认「新增 / 编辑 / 删除」 | 不强制;无配置时宿主仍直写 SQLite | +| **`replaces`** | **可选**:接管数据管理默认「新增 / 编辑 / 删除 / **导入**」 | 不强制;无配置时宿主仍直写 SQLite;**import 时 UI 仍用宿主「导入数据」面板** | | **`bind.tables`** | 数据管理里出现在**哪些业务表** | 不决定业务实现 | | **`executionProfile`** | 宿主是**同步等待**还是**异步 Job** | 不限制可展示入口,不决定是否弹窗 | | **`interaction`** | 可点击入口(数据管理 toolbar/row/batch、默认 CRUD 接管、未来 skill-detail)**点了怎么交互** | 不决定 sync/async;**cron 触发与 agent 忽略** | @@ -105,7 +105,7 @@ Action 是否提供、挂哪些 `placements`,由本技能 **入口定位表** | action | `id`、`label`、`description`、`placements`、`entrypoint`、**`executionProfile`(必填)**;可选 `hoverHint`、`inputSchema`、`confirmation`、**`interaction`**、**`interactionByPlacement`**、**`bind`**、**`replaces`**、**`hostEffects`**、**`steps`**、**`authz`**(仅文档提示) | | entrypoint | `type = cli`、`command`、`args`(**必填** JSON 数组,项为 `string` / `number` / `boolean`) | | placements | `toolbar`、`row`、`batch`、`skill-detail`、`agent`、`cron`;仅 `replaces` 时可为空数组 | -| **replaces** | 可选 `"create"` \| `"update"` \| `"delete"` — 接管默认新增/编辑/删除(须 `bind.tables`) | +| **replaces** | 可选 `"create"` \| `"update"` \| `"delete"` \| `"import"` — 接管默认新增/编辑/删除/导入(须 `bind.tables`) | | **bind** | `{ "tables": ["business_table"] }`;可选 `inputMapping`(行内/批量预填,见下节) | | **policy** | 可选;`enabled` + `entrypoint`(推荐 `policy` CLI);见 POLICY.md。未启用 = L0 | | **cellPresentation** | 可选;`{ "enabled": true }` 时宿主应加载单元格呈现;见 [`CELL_PRESENTATION.md`](CELL_PRESENTATION.md)。未启用 = P0 | @@ -140,32 +140,166 @@ Action 是否提供、挂哪些 `placements`,由本技能 **入口定位表** - **`inputMapping`(可选)**:把行/勾选映射到 `inputSchema` 字段,例如 `"leadId": "$row.$pk"`、`"ids": "$selection.ids"`。适合 `row` / `batch`。宿主仍会提交这些字段;可用 `inputSchema.properties.*.readOnly: true` 在表单中只读展示。 - `concurrency` / `locks` 仍未稳定,**不要**写入新技能 manifest。 -## `replaces`(可选:接管默认新增 / 编辑 / 删除) +## `replaces`(可选:接管默认新增 / 编辑 / 删除 / 导入) -有领域副作用的表(建目录、加密、级联清理)不要依赖宿主直写 SQLite。此时可为该表声明: +有领域副作用的表(归一化字段、建目录、加密、级联清理)不要依赖宿主直写 SQLite。此时可为该表声明 `replaces`,**用户仍点宿主默认按钮**,由技能 CLI 完成写入。 + +### 怎么选 + +| 场景 | 建议 | +|------|------| +| 纯表字段、无副作用 | 不配 `replaces`,宿主直写即可 | +| 单条新增/编辑/删除有副作用 | `replaces: create` / `update` / `delete` | +| 批量导入也有副作用(推荐) | 另配 `replaces: import`(与 create 可同表并存) | +| 只有 create、暂无 import | 宿主「导入」面板确认后会**逐行调 create**(兼容);正式产品应补 `import` | + +### 导入分流(必读) + +点工具栏「导入」时: + +1. **UI 一律**打开宿主「导入数据」面板(下载模板 → 选文件 → 映射 → 出错规则 → 预览)。 +2. **写库**按表能力分流: + - 有 `replaces: import` → 把选中文件路径 + 出错规则交给该 Action(技能解析并写入) + - 仅有 `replaces: create` → 宿主按行调用 create(兼容) + - 都没有 → 宿主直写 SQLite + +**不要**再做一张技能自己的「开始执行」导入表单来冒充导入体验。`placements` 对 import 保持 `[]`;`description` 写明「由宿主导入面板调用」。 + +可抄文件见 [`assets/examples/`](../assets/examples/):`replaces-*.action.example.json`。 + +### 示例:create ```json { - "id": "account-add-web", - "label": "新增账号", - "description": "登记网页账号并初始化 profile 目录。", + "id": "item-create", + "label": "新增记录", + "description": "新增业务记录并执行领域副作用(走技能 CLI,非宿主直写库)。", "replaces": "create", "placements": [], "executionProfile": "sync", "interaction": "form", - "bind": { "tables": ["accounts"] }, + "bind": { "tables": ["items"] }, + "hostEffects": { "onSuccess": ["reloadTable"] }, "inputSchema": { "type": "object", - "required": ["platform_key", "account_label"], + "required": ["name"], "properties": { - "platform_key": { "type": "string", "title": "平台" }, - "account_label": { "type": "string", "title": "账号名称" } + "name": { "type": "string", "title": "名称" }, + "note": { "type": "string", "title": "备注" } } }, "entrypoint": { "type": "cli", - "command": "account", - "args": ["add-web", "--platform", "{{platform_key}}", "--label", "{{account_label}}"] + "command": "item", + "args": ["create", "--name", "{{name}}", "--note", "{{note}}"] + } +} +``` + +### 示例:update + +```json +{ + "id": "item-update", + "label": "编辑记录", + "description": "编辑业务记录并重算派生字段。", + "replaces": "update", + "placements": [], + "executionProfile": "sync", + "interaction": "form", + "bind": { + "tables": ["items"], + "inputMapping": { + "id": "$row.$pk", + "name": "$row.name", + "note": "$row.note" + } + }, + "hostEffects": { "onSuccess": ["reloadTable"] }, + "inputSchema": { + "type": "object", + "required": ["id", "name"], + "properties": { + "id": { "type": "number", "title": "编号", "readOnly": true }, + "name": { "type": "string", "title": "名称" }, + "note": { "type": "string", "title": "备注" } + } + }, + "entrypoint": { + "type": "cli", + "command": "item", + "args": ["update", "--id", "{{id}}", "--name", "{{name}}", "--note", "{{note}}"] + } +} +``` + +### 示例:delete + +```json +{ + "id": "item-delete", + "label": "删除记录", + "description": "删除业务记录并清理关联副作用。", + "replaces": "delete", + "placements": [], + "executionProfile": "sync", + "interaction": "confirm", + "confirmation": { "message": "确定删除该记录?关联数据将一并清理。" }, + "bind": { + "tables": ["items"], + "inputMapping": { "id": "$row.$pk" } + }, + "hostEffects": { "onSuccess": ["reloadTable", "clearSelection"] }, + "inputSchema": { + "type": "object", + "required": ["id"], + "properties": { + "id": { "type": "number", "title": "编号", "readOnly": true } + } + }, + "entrypoint": { + "type": "cli", + "command": "item", + "args": ["delete", "--id", "{{id}}"] + } +} +``` + +### 示例:import + +```json +{ + "id": "item-import", + "label": "导入记录", + "description": "由宿主「导入数据」面板调用:解析 Excel/CSV 并写入业务表(含领域校验与派生字段)。", + "replaces": "import", + "placements": [], + "executionProfile": "sync", + "interaction": "form", + "bind": { "tables": ["items"] }, + "hostEffects": { "onSuccess": ["reloadTable"] }, + "inputSchema": { + "type": "object", + "required": ["filePath"], + "properties": { + "filePath": { + "type": "string", + "title": "导入文件", + "description": "由宿主导入面板传入的 Excel/CSV 路径。", + "format": "file" + }, + "onError": { + "type": "string", + "title": "导入规则(出错时)", + "enum": ["跳过该行,继续导入", "立即停止,不再导入后续行"], + "default": "跳过该行,继续导入" + } + } + }, + "entrypoint": { + "type": "cli", + "command": "item", + "args": ["import", "--file", "{{filePath}}", "--on-error", "{{onError}}"] } } ``` @@ -173,12 +307,13 @@ Action 是否提供、挂哪些 `placements`,由本技能 **入口定位表** 规则: - **全可选**:无 `replaces` 时,宿主默认按钮仍直写库,行为与以前一致。 -- 取值:`create`(新增)/ `update`(编辑)/ `delete`(删除)。 +- 取值:`create`(新增)/ `update`(编辑)/ `delete`(删除)/ `import`(导入写库)。 - **必须**非空 `bind.tables`;同一技能同一表同一 `replaces` 至多一个 Action。 -- 与 `placements` 正交:可只接管默认按钮(`placements: []`),不必再挂 toolbar「第二个新增」。 -- 点默认按钮时走该 Action 的 `interaction`(create/update 常用 `form`;delete 常用 `confirm`)。 +- 与 `placements` 正交:可只接管默认按钮(`placements: []`),不必再挂 toolbar「第二个新增/导入」。 +- 点默认「新增/编辑/删除」时走该 Action 的 `interaction`(create/update 常用 `form`;delete 常用 `confirm`)。 +- 点默认「导入」时**固定**打开宿主导入面板;确认后若有 `replaces:import` 则调用该 Action(`interaction` 不决定是否再弹技能表单)。 - `update` / `delete` 可用 `inputMapping`(`$row.$pk`、`$selection.ids`)预填主键。 -- 宿主导入仍直写:有 `replaces:create` 的表慎用宿主导入,或另做 import Action。 +- `import` 约定:`filePath`(或 `file`)+ 可选 `onError`(`skip`/`abort` 或上例中文枚举);技能自行解析文件与表头。 - 整表 `_jiangchang_tables.readonly=1` 时仍隐藏写按钮(优先于 `replaces`)。 - **列元数据复用(lookup)**:Action 表单(含 toolbar/batch 弹窗与 create/update 抽屉)可将 `inputSchema` 字段对齐业务列,复用中文名与 lookup 下拉: - 优先让属性名 = 物理列名(如 `platform_key`); diff --git a/references/POLICY.md b/references/POLICY.md index 3df9a74..d0afea3 100644 --- a/references/POLICY.md +++ b/references/POLICY.md @@ -150,7 +150,7 @@ 4. `mode=open`:等价「L1 不限制按钮」,可与 L2 行列限制并存。 5. **`denylist` 不是 v1 合法值**;出现则 §6 fail-closed。 -`replaces` 接管的默认新增/编辑/删除:视为对应 Action 的 id 是否在 allowlist 中;不在则宿主隐藏该接管按钮。`mode=open` 时不按 id 限制 replaces。 +`replaces` 接管的默认新增/编辑/删除/导入:视为对应 Action 的 id 是否在 allowlist 中;不在则宿主隐藏该接管按钮(导入按钮是否可用还受 `hostCapabilities` 含 `import` 影响)。`mode=open` 时不按 id 限制 replaces。 ### 2.4 `hostCapabilities`(L1 可选冻) diff --git a/references/SCHEMA.md b/references/SCHEMA.md index 028f42d..ea23024 100644 --- a/references/SCHEMA.md +++ b/references/SCHEMA.md @@ -87,7 +87,7 @@ metadata: 不要把「外部来源只读」错误理解成「技能本地数据库一定只读」。技能通过 `_jiangchang_tables.readonly` 与 `_jiangchang_columns.editable` 声明本地表的实际写权限。 -若创建/更新/删除带有领域副作用(自动建目录、加密、级联清理),不要依赖宿主默认直写:在 `assets/actions.json` 用可选字段 `replaces: create|update|delete` 接管默认按钮,详见 [`ACTIONS.md`](ACTIONS.md)。 +若创建/更新/删除/导入带有领域副作用(自动建目录、加密、级联清理、归一化字段),不要依赖宿主默认直写:在 `assets/actions.json` 用可选字段 `replaces: create|update|delete|import` 接管默认按钮。其中 **import** 时用户仍使用宿主「导入数据」面板,确认后由技能写库。详见 [`ACTIONS.md`](ACTIONS.md) 与 `assets/examples/replaces-*.action.example.json`。 ## 表与表之间:禁止 SQLite 外键 diff --git a/scripts/util/constants.py b/scripts/util/constants.py index f97db33..2b74577 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.91" +SKILL_VERSION = "1.0.92" LOG_LOGGER_NAME = "openclaw.skill.your_skill_slug" PLATFORM_KIT_MIN_VERSION = "1.2.11" diff --git a/tests/test_actions_manifest.py b/tests/test_actions_manifest.py index 222735b..4cca492 100644 --- a/tests/test_actions_manifest.py +++ b/tests/test_actions_manifest.py @@ -188,7 +188,7 @@ class TestActionsManifest(unittest.TestCase): if replaces is not None: self.assertIn( replaces, - {"create", "update", "delete"}, + {"create", "update", "delete", "import"}, msg=f"{action_id} invalid replaces {replaces!r}", ) bind = action.get("bind") or {} @@ -236,7 +236,7 @@ class TestActionsManifest(unittest.TestCase): self.assertIn("interactionByPlacement", props) by_placement = props["interactionByPlacement"] self.assertIs(by_placement.get("additionalProperties"), False) - for key in ("toolbar", "row", "batch", "cron", "agent", "skill-detail", "create", "update", "delete"): + for key in ("toolbar", "row", "batch", "cron", "agent", "skill-detail", "create", "update", "delete", "import"): self.assertIn(key, by_placement.get("properties") or {}) self.assertEqual( set((by_placement["properties"][key].get("enum") or [])), @@ -246,7 +246,7 @@ class TestActionsManifest(unittest.TestCase): self.assertEqual(props["replaces"].get("$ref"), "#/$defs/mutationKind") self.assertEqual( set(schema["$defs"]["mutationKind"].get("enum") or []), - {"create", "update", "delete"}, + {"create", "update", "delete", "import"}, ) def test_schema_defines_hover_hint(self) -> None: