Skip to content

Commit d5c287c

Browse files
committed
fix: review
1 parent cee2ccb commit d5c287c

8 files changed

Lines changed: 53 additions & 55 deletions

File tree

.agents/skills/tinyengine-dsl-generator/SKILL.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ bash .agents/skills/tinyengine-dsl-generator/scripts/validate_all.sh <output-fil
113113

114114
| Stage | Script | Catches |
115115
| -------------- | ------------------------ | ------------------------------------------------------------------------------- |
116-
| Structure | validate_dsl.mjs | Required fields, Page/Block `componentName`, meta, `class` vs `className`, integer app id |
116+
| Structure | validate_dsl.mjs | Required fields, Page/Block `componentName`, meta, `class` vs `className`, app/page id types |
117117
| Event bindings | check_event_bindings.mjs | `JSFunction` on an event, or a function body in `JSExpression.value` |
118118
| CSS | check_css.mjs | Malformed `css` strings |
119119

@@ -125,7 +125,8 @@ bash .agents/skills/tinyengine-dsl-generator/scripts/validate_all.sh <output-fil
125125
- [ ] `modelValue` declares `model` (`true` for standard v-model)
126126
- [ ] `occupier` is `null`
127127
- [ ] All `id`s are unique; CSS classes use `className`, not `class`
128-
- [ ] App / page IDs are integers (`918`, not `"918"`)
128+
- [ ] **App schema** `id` and `meta.appId` are integers (`918`, not `"918"`) — apps.js persists `meta.appId` as string internally, keep the DSL integer
129+
- [ ] **Page** `app` reference is a string (`"918"`, not `918`) — pages.js queries with `appId.toString()`; a numeric `app` won't be found by `list()`. Page's own `id` is a NanoID string assigned by the server
129130

130131
## Component lookup
131132

.agents/skills/tinyengine-dsl-generator/references/protocol.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ interface IComponentMap {
4444
}
4545

4646
interface IAppMeta {
47-
appId: string | number // 建议使用整数 (e.g., 918),字符串会被强制转换
47+
appId: string | number // App Schema 中建议使用整数 (e.g., 918);服务端持久化时会转成字符串
4848
name: string
4949
description: string
5050
creator: string
@@ -55,13 +55,13 @@ interface IAppMeta {
5555
}
5656

5757
/**
58-
* App ID 格式建议 (统一使用整数):
58+
* App ID 格式建议:
5959
* - App Schema 文件中的 `id` 字段: 整数类型 (e.g., 918)
6060
* - App Schema 文件中的 `meta.appId` 字段: 整数类型 (e.g., 918)
6161
* - App Metadata 文件中的 `id` 字段: 整数类型 (e.g., 918)
62-
* - Page 文件中的 `app` 字段: 整数类型 (e.g., 918)
62+
* - Page 文件中的 `app` 字段: 字符串类型 (e.g., "918")
6363
*
64-
* 注意: 虽然字符串格式会被自动转换,但建议统一使用整数以保持一致性
64+
* 注意: pages.js 使用 appId.toString() 查询页面,直接落盘的 Page 文件必须用字符串 app 引用。
6565
*/
6666

6767
interface IAppConfig {

.agents/skills/tinyengine-dsl-generator/scripts/check_css.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ export class BasicCssChecker {
130130
}
131131
}
132132

133-
// 可用模式表(与 Python 版的 checkers 字典对应,仅保留 basic)
133+
// 可用模式表(仅保留 basic)
134134
const CHECKERS = { basic: BasicCssChecker };
135135

136136
function main() {

.agents/skills/tinyengine-dsl-generator/scripts/validate_dsl.mjs

Lines changed: 24 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -11,32 +11,22 @@ import fs from 'node:fs';
1111
import path from 'node:path';
1212
import { fileURLToPath } from 'node:url';
1313

14-
/**
15-
* 把 JS 值的类型名映射为 Python 风格(int/str/bool/list/dict/NoneType),
16-
* 用于 app id / meta.appId 告警里的 "got: <类型>" 描述。
17-
*/
18-
function pyTypeName(value) {
19-
if (value === null) return 'NoneType';
20-
if (Array.isArray(value)) return 'list';
14+
/** 把 JS 值映射为类型名,用于告警里的 "got: <类型>" 描述。 */
15+
function typeName(value) {
16+
if (value === null) return 'null';
17+
if (Array.isArray(value)) return 'array';
2118
switch (typeof value) {
2219
case 'string':
23-
return 'str';
20+
return 'string';
2421
case 'boolean':
25-
return 'bool';
22+
return 'boolean';
2623
case 'number':
27-
return Number.isInteger(value) ? 'int' : 'float';
24+
return Number.isInteger(value) ? 'integer' : 'number';
2825
default:
29-
return 'dict';
26+
return 'object';
3027
}
3128
}
3229

33-
/**
34-
* 等价于 Python 的 isinstance(x, int):Python 中 bool 也是 int,因此布尔值不算"非整数"。
35-
*/
36-
function isPythonInt(value) {
37-
return typeof value === 'boolean' || (typeof value === 'number' && Number.isInteger(value));
38-
}
39-
4030
/** 普通对象判定(非 null、非数组) */
4131
function isPlainObject(value) {
4232
return typeof value === 'object' && value !== null && !Array.isArray(value);
@@ -89,7 +79,7 @@ export class TinyEngineValidator {
8979
const cn = this.dsl.componentName;
9080
if (cn === 'Page') return 'page';
9181
if (cn === 'Block') return 'block';
92-
// 其它 componentName 落到 unknown(与 Python 一致:不进入 app 分支)
82+
// 其它 componentName 落到 unknown(不进入 app 分支)
9383
} else if ('componentsTree' in this.dsl || 'version' in this.dsl) {
9484
return 'app';
9585
}
@@ -114,6 +104,17 @@ export class TinyEngineValidator {
114104
this.errors.push(`Page componentName must be 'Page', got: ${this.dsl.componentName}`);
115105
}
116106

107+
// Page 外层包装的 app 引用必须是字符串:pages.js list()/create() 均用 appId.toString()
108+
// 查询;写成数字会导致直接落盘的 page 文件查不到。
109+
if (this._fromWrapper && isPlainObject(this.original)) {
110+
const appRef = this.original.app;
111+
if (appRef !== undefined && typeof appRef !== 'string') {
112+
this.warnings.push(
113+
`Page wrapper 'app' should be string, got: ${typeName(appRef)} (pages.js queries with appId.toString(); numeric app ref won't be found by list())`
114+
);
115+
}
116+
}
117+
117118
// 原始页面协议(IPageSchema)要求 meta;但外层包装格式把 meta 等元信息上提到包装层,
118119
// page_content 内不再含 meta。因此仅在校验"裸"页面 DSL 时强制要求 meta。
119120
if ('meta' in this.dsl) {
@@ -157,16 +158,16 @@ export class TinyEngineValidator {
157158
// 验证 app ID 格式
158159
if ('id' in this.dsl) {
159160
const rootId = this.dsl.id;
160-
if (!isPythonInt(rootId)) {
161-
this.warnings.push(`App Schema 'id' should be integer, got: ${pyTypeName(rootId)} (will be coerced)`);
161+
if (!Number.isInteger(rootId)) {
162+
this.warnings.push(`App Schema 'id' should be integer, got: ${typeName(rootId)} (will be coerced)`);
162163
}
163164
}
164165

165166
// 验证 meta.appId 格式
166167
if ('meta' in this.dsl && 'appId' in this.dsl.meta) {
167168
const appId = this.dsl.meta.appId;
168-
if (!isPythonInt(appId)) {
169-
this.warnings.push(`meta.appId should be integer, got: ${pyTypeName(appId)} (will be coerced)`);
169+
if (!Number.isInteger(appId)) {
170+
this.warnings.push(`meta.appId should be integer, got: ${typeName(appId)} (will be coerced)`);
170171
}
171172
}
172173

.agents/skills/tinyengine-dsl-generator/scripts/validate_page.mjs

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -21,24 +21,19 @@ function isPlainObject(value) {
2121
return typeof value === 'object' && value !== null && !Array.isArray(value);
2222
}
2323

24-
/** 等价于 Python 的 isinstance(x, int):bool 也算 int。 */
25-
function isPythonInt(value) {
26-
return typeof value === 'boolean' || (typeof value === 'number' && Number.isInteger(value));
27-
}
28-
29-
/** JS 值 → Python 类型名(与 type(x).__name__ 对齐)。 */
30-
function pyTypeName(value) {
31-
if (value === null) return 'NoneType';
32-
if (Array.isArray(value)) return 'list';
24+
/** 把 JS 值映射为类型名,用于告警里的 "got: <类型>" 描述。 */
25+
function typeName(value) {
26+
if (value === null) return 'null';
27+
if (Array.isArray(value)) return 'array';
3328
switch (typeof value) {
3429
case 'string':
35-
return 'str';
30+
return 'string';
3631
case 'boolean':
37-
return 'bool';
32+
return 'boolean';
3833
case 'number':
39-
return Number.isInteger(value) ? 'int' : 'float';
34+
return Number.isInteger(value) ? 'integer' : 'number';
4035
default:
41-
return 'dict';
36+
return 'object';
4237
}
4338
}
4439

@@ -83,12 +78,13 @@ function validatePageWrapper(filePath) {
8378
}
8479
}
8580

86-
// 验证 app 字段格式(建议使用整数)
81+
// 验证 app 字段格式:页面外层 app 引用必须是字符串。
82+
// pages.js list()/create() 均用 appId.toString() 查询,数字 app 会导致直接落盘的页面查不到。
8783
if ('app' in data) {
8884
const appField = data.app;
89-
if (!isPythonInt(appField)) {
85+
if (typeof appField !== 'string') {
9086
console.log(
91-
`⚠️ WARNING: 'app' field should be integer, got: ${pyTypeName(appField)} (will be coerced)`
87+
`⚠️ WARNING: 'app' field should be string, got: ${typeName(appField)} (pages.js queries with appId.toString(); numeric app ref won't be found by list())`
9288
);
9389
}
9490
}
@@ -142,7 +138,7 @@ function checkClassNameUsage(filePath) {
142138
try {
143139
data = readJson(filePath);
144140
} catch (e) {
145-
// JSON 语法错误或文件读取问题(FileNotFoundError 属于 OSError)由其他检查负责报告;
141+
// JSON 语法错误或文件读取问题(如 ENOENT)由其他检查负责报告;
146142
// 此处不掩盖其他意外运行错误。
147143
if (e instanceof SyntaxError || (e && typeof e.code === 'string')) {
148144
return true;

docs/advanced-features/using-skill-to-integrate-local-ai-agent.md

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ TinyEngine 现在支持将 **File 存储模式** 与一份标准化的 **DSL Ski
5757

5858
- **文件即数据**:File 模式下,MockServer 直接扫描 `mockServer/data/` 目录的 JSON 文件,文件名优先取 `name || label`(冲突时自动追加后缀)。
5959
- **可编辑性**:页面 / 区块的 `occupier` 必须为 `null`,否则进入画布后会提示被占用而无法编辑。
60-
- **命名与关联**:页面通过 `app` 字段关联所属应用,`route` 需在应用内唯一;应用与页面的 ID 统一使用整数(字符串格式也兼容)
60+
- **命名与关联**:页面通过 `app` 字段关联所属应用,`route` 需在应用内唯一;应用 ID 使用整数,Page 文件的 `app` 引用使用字符串
6161

6262
## 前置条件
6363

@@ -162,7 +162,7 @@ Agent 会遵循 Skill 内定义的标准流程:
162162
{
163163
"name": "Login",
164164
"id": "a1b2c3d4e5f6g7h8",
165-
"app": 1,
165+
"app": "1",
166166
"route": "Login",
167167
"page_content": {
168168
"componentName": "Page",
@@ -222,7 +222,7 @@ Agent 会遵循 Skill 内定义的标准流程:
222222

223223
- 文件名优先使用记录的 `name || label`;同名冲突时 MockServer 会自动追加随机后缀。
224224
- 页面的 `route` 需在所属应用内唯一,否则会被唯一性约束拦截。
225-
- 页面的 `app` 字段必须引用所属应用的 `id`二者类型保持一致(推荐统一使用整数,也兼容字符串格式)
225+
- 页面的 `app` 字段必须以字符串形式引用所属应用的 `id`例如应用 `id``1` 时页面写 `"app": "1"`
226226

227227
## 示例:生成一个登录页
228228

@@ -241,7 +241,7 @@ Agent 产出的页面核心 DSL(节选):
241241
{
242242
"name": "Login",
243243
"id": "a1b2c3d4e5f6g7h8",
244-
"app": 1,
244+
"app": "1",
245245
"route": "Login",
246246
"page_content": {
247247
"componentName": "Page",
@@ -363,4 +363,3 @@ node .agents/skills/tinyengine-dsl-generator/scripts/check_css.mjs mockServer/da
363363
## 相关文档
364364

365365
- [DSL Skill](https://github.com/opentiny/tiny-engine/blob/develop/.agents/skills/tinyengine-dsl-generator/SKILL.md)
366-

eslint.config.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ export default defineConfigWithVueTs(
6767
}
6868
},
6969
{
70-
files: ['scripts/**/*', 'skills/**/*'],
70+
files: ['scripts/**/*', '.agents/skills/**/*', '.claude/skills/**/*'],
7171
rules: {
7272
'no-console': 'off',
7373
'@typescript-eslint/no-require-imports': 'off'

mockServer/src/services/apps.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,12 +77,13 @@ export default class AppsService {
7777
}
7878

7979
async create(params) {
80-
let mockId = this.appList.length > 0 ? Math.max(...this.appList.map((item) => item.id)) + 1 : 3
80+
const all = await this.store.find({})
81+
const mockId = all.length > 0 ? Math.max(...all.map((item) => Number(item.id) || 0)) + 1 : 3
8182
const newApp = {
8283
...defaultApp,
8384
created_at: new Date().toISOString(),
8485
updated_at: new Date().toISOString(),
85-
id: mockId++,
86+
id: mockId,
8687
...params
8788
}
8889
await this.store.insert(newApp)

0 commit comments

Comments
 (0)