openyida 2026.7.21 → 2026.7.23-1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -239,6 +239,14 @@ Examples:
239
239
  openyida export-conversation --list List available conversations
240
240
  `,
241
241
  unknown_command: 'Unknown command: {0}',
242
+ command_suggestion: 'Suggested command: {0}',
243
+ forbidden_alias_list_apps: '`{0}` is not an OpenYida command; use `{1}` for application discovery.',
244
+ forbidden_alias_get_app: '`{0}` is ambiguous; use `{1}` for app name search, `{2}` for form/schema lookup, or `{3}` for bound context preflight.',
245
+ forbidden_alias_create_app_json: '`{0}` is not a separate command contract; use canonical `{1}` output.',
246
+ forbidden_alias_create_page_app_type_option: '`{0}` takes appType as the first positional argument, not `{1}`.',
247
+ forbidden_alias_get_schema_app_type_option: '`{0}` takes appType as the first positional argument, not `{1}`.',
248
+ forbidden_alias_get_schema_form_uuid_option: '`{0}` takes formUuid as the second positional argument, not `{1}`.',
249
+ nearest_command_suggestion: 'Unknown OpenYida command root "{0}". Did you mean "{1}"?',
242
250
  run_help: 'Run openyida --help for usage',
243
251
  integration_help: 'Usage: openyida integration <create|list|enable|disable|check|diagnose> ...',
244
252
  integration_unknown: 'Unknown integration subcommand: {0}',
@@ -713,6 +721,7 @@ Examples:
713
721
  config_failed: ' ⚠️ Config update failed: {0}',
714
722
  schema_ok_config_failed: ' Schema saved, but config update failed',
715
723
  schema_saved_config_failed: ' Schema saved, but config update failed',
724
+ create_post_failure_retry_advice: 'Do not repeat create directly. First run openyida list-forms {0} --keyword "{1}" to check for an existing same-title form; if this run already created a blank/existing form, prefer create-form update or a future --resume-form-uuid flow.',
716
725
  error: '\n❌ Error: {0}',
717
726
  usage_create: 'Usage: openyida create-form create <appType> <formTitle> <fieldsJsonFile>',
718
727
  example_create: 'Example: openyida create-form create "APP_XXX" "Employee Info" .cache/openyida/forms/employee-fields.json',
@@ -1065,6 +1074,7 @@ Examples:
1065
1074
  unknown_template: 'Unknown page template: {0}',
1066
1075
  available_templates: 'Available templates: {0}',
1067
1076
  template_not_found: 'Template file not found: {0}',
1077
+ output_project_prefix_stripped: 'Current directory is already an OpenYida project; output path was adjusted from {0} to {1} to avoid project/project.',
1068
1078
  done: 'Page generated: {0}',
1069
1079
  hint: 'Next run openyida compile <file>, or pass --compile to compile immediately.',
1070
1080
  success: 'Page generation complete',
@@ -1201,6 +1211,7 @@ Examples:
1201
1211
  exception: '\n❌ Publish error: {0}',
1202
1212
  error: '\n❌ Publish error: {0}',
1203
1213
  source_not_found: '❌ Source file not found: {0}',
1214
+ source_path_hint: '💡 Try this source file path: {0}',
1204
1215
  usage: 'Usage: openyida publish <sourceFile> <appType> <formUuid> [--health-check] [--canvas]',
1205
1216
  example: 'Example: openyida publish pages/src/xxx.js APP_XXX FORM-XXX --health-check',
1206
1217
  },
@@ -240,6 +240,14 @@ openyida - 宜搭命令行工具
240
240
  openyida export-conversation --list 列出可用对话
241
241
  `,
242
242
  unknown_command: '未知命令: {0}',
243
+ command_suggestion: '建议命令: {0}',
244
+ forbidden_alias_list_apps: '`{0}` 不是 OpenYida 命令;请使用 `{1}` 查询应用。',
245
+ forbidden_alias_get_app: '`{0}` 含义不明确;按应用名称搜索请用 `{1}`,查询表单/字段结构请用 `{2}`,读取绑定上下文请用 `{3}`。',
246
+ forbidden_alias_create_app_json: '`{0}` 不是独立命令契约;请使用规范的 `{1}` 输出。',
247
+ forbidden_alias_create_page_app_type_option: '`{0}` 使用第一个位置参数传入 appType,不使用 `{1}`。',
248
+ forbidden_alias_get_schema_app_type_option: '`{0}` 使用第一个位置参数传入 appType,不使用 `{1}`。',
249
+ forbidden_alias_get_schema_form_uuid_option: '`{0}` 使用第二个位置参数传入 formUuid,不使用 `{1}`。',
250
+ nearest_command_suggestion: '未知 OpenYida 命令根「{0}」。你是不是想用「{1}」?',
243
251
  run_help: '运行 openyida --help 查看帮助',
244
252
  integration_help: '用法: openyida integration <create|list|enable|disable|check|diagnose> ...',
245
253
  integration_unknown: '未知的 integration 子命令: {0}',
@@ -685,6 +693,7 @@ openyida - 宜搭命令行工具
685
693
  config_failed: ' ⚠️ 配置更新失败: {0}',
686
694
  schema_ok_config_failed: ' Schema 已保存,但配置更新失败',
687
695
  schema_saved_config_failed: ' Schema 已保存,但配置更新失败',
696
+ create_post_failure_retry_advice: '不要直接重复 create。先运行 openyida list-forms {0} --keyword "{1}" 确认同名表单;若是本轮创建的空白/已有表单,优先使用 create-form update 或后续 --resume-form-uuid 复用。',
688
697
  error: '\n❌ 错误: {0}',
689
698
  usage_create: '用法: openyida create-form create <appType> <formTitle> <fieldsJsonFile>',
690
699
  example_create: '示例:openyida create-form create "APP_XXX" "员工信息登记" .cache/openyida/forms/employee-fields.json',
@@ -1059,6 +1068,7 @@ openyida - 宜搭命令行工具
1059
1068
  unknown_template: '未知页面模板:{0}',
1060
1069
  available_templates: '可用模板:{0}',
1061
1070
  template_not_found: '模板文件不存在:{0}',
1071
+ output_project_prefix_stripped: '检测到当前目录已是 OpenYida project,已将输出路径从 {0} 调整为 {1},避免生成 project/project。',
1062
1072
  done: '页面已生成:{0}',
1063
1073
  hint: '建议继续运行 openyida compile <file> 或使用 --compile 直接编译校验。',
1064
1074
  success: '页面生成完成',
@@ -1195,6 +1205,7 @@ openyida - 宜搭命令行工具
1195
1205
  exception: '\n❌ 发布异常: {0}',
1196
1206
  error: '\n❌ 发布异常: {0}',
1197
1207
  source_not_found: '❌ 源文件不存在:{0}',
1208
+ source_path_hint: '💡 可尝试使用源文件路径:{0}',
1198
1209
  usage: '用法: openyida publish <源文件路径> <appType> <formUuid> [--health-check] [--canvas]',
1199
1210
  example: '示例:openyida publish pages/src/xxx.js APP_XXX FORM-XXX --health-check',
1200
1211
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openyida",
3
- "version": "2026.7.21",
3
+ "version": "2026.7.23-1",
4
4
  "description": "OpenYida CLI - 宜搭低代码 AI 开发工具(安装即用,零配置)",
5
5
  "bin": {
6
6
  "openyida": "bin/yida.js",
@@ -259,6 +259,10 @@ openyida copy
259
259
 
260
260
  fast_build 页面源码默认不得使用 \`this.dataSourceMap.*\`,除非本轮已经明确创建并绑定设计器数据源;默认使用入口型页面或 \`this.utils.yida.*\` 查询已创建表单。
261
261
 
262
+ fast_build 创建/解析多个表单后,页面阶段需要字段映射时,对每个目标表单默认只执行一次 \`openyida get-schema <appType> <formUuid> --field-map-json\`,读取完整 JSON 并写入/复用 \`.cache/<项目名>-schema.json\`;不要用 \`head\` / \`tail\` / \`grep\` 截断 schema stdout 后重复拉取。
263
+
264
+ Canvas 页面实现二选一:走模板路径时先写业务化 \`page-spec.json\` 再 \`openyida generate-page ... --spec ... --compile\`,之后只做必要小范围 Edit/patch;如果已经明确最终页面结构,跳过 \`generate-page\`,直接 Write 最终 \`.canvas.jsx\`。不要 generate-page 后马上 Read 大段源码并全量 Write 覆盖同一路径。
265
+
262
266
  不要默认加载 \`yida-page-uiux\`、\`yida-data-source-connectors\`、\`yida-data-management\`、\`yida-nav-group\`、\`yida-dashboard\`,也不要默认做示例数据、导航整理、截图验收、公开访问、长 PRD 或深读 references;这些只在用户明确要求或 \`full_demo\` / \`deep_design\` 时执行。
263
267
 
264
268
  ## 子技能目录
@@ -260,6 +260,77 @@ function validatePermissions(commands) {
260
260
  }
261
261
  }
262
262
 
263
+ function validateForbiddenAliases() {
264
+ const manifest = require('../lib/core/command-manifest').buildCommandManifest();
265
+ const commandIds = new Set(flattenCommandManifest().map(entry => entry.id));
266
+ const allowedMatcherTypes = new Set([
267
+ 'argv_prefix',
268
+ 'command_has_option',
269
+ ]);
270
+ const seenPatterns = new Set();
271
+
272
+ if (!manifest.forbidden_alias_schema || manifest.forbidden_alias_schema.version !== 1) {
273
+ errors.push('Command manifest is missing forbidden_alias_schema.version 1');
274
+ }
275
+ if (!Array.isArray(manifest.forbidden_aliases)) {
276
+ errors.push('Command manifest forbidden_aliases must be an array');
277
+ return;
278
+ }
279
+
280
+ for (const entry of manifest.forbidden_aliases) {
281
+ if (!entry || typeof entry !== 'object') {
282
+ errors.push('Forbidden alias entries must be objects');
283
+ continue;
284
+ }
285
+ if (typeof entry.id !== 'string' || !entry.id.trim()) {
286
+ errors.push('Forbidden alias entry is missing id');
287
+ }
288
+ if (typeof entry.pattern !== 'string' || !entry.pattern.trim()) {
289
+ errors.push(`Forbidden alias "${entry.id || '(missing id)'}" is missing pattern`);
290
+ } else if (seenPatterns.has(entry.pattern)) {
291
+ errors.push(`Duplicate forbidden alias pattern: ${entry.pattern}`);
292
+ } else {
293
+ seenPatterns.add(entry.pattern);
294
+ }
295
+ if (!entry.matcher || typeof entry.matcher !== 'object' || Array.isArray(entry.matcher)) {
296
+ errors.push(`Forbidden alias "${entry.id}" matcher must be an object`);
297
+ } else if (!allowedMatcherTypes.has(entry.matcher.type)) {
298
+ errors.push(`Forbidden alias "${entry.id}" has invalid matcher type "${entry.matcher.type}"`);
299
+ } else if (entry.matcher.type === 'argv_prefix') {
300
+ if (!Array.isArray(entry.matcher.tokens) || entry.matcher.tokens.length === 0) {
301
+ errors.push(`Forbidden alias "${entry.id}" argv_prefix matcher.tokens must be a non-empty array`);
302
+ }
303
+ } else if (entry.matcher.type === 'command_has_option') {
304
+ if (typeof entry.matcher.command !== 'string' || !entry.matcher.command.trim()) {
305
+ errors.push(`Forbidden alias "${entry.id}" command_has_option matcher.command must be a non-empty string`);
306
+ }
307
+ if (typeof entry.matcher.option !== 'string' || !entry.matcher.option.startsWith('--')) {
308
+ errors.push(`Forbidden alias "${entry.id}" command_has_option matcher.option must be a --option string`);
309
+ }
310
+ }
311
+ if (!commandIds.has(entry.suggested_command_id)) {
312
+ errors.push(`Forbidden alias "${entry.id}" references unknown suggested_command_id "${entry.suggested_command_id}"`);
313
+ }
314
+ if (typeof entry.suggested_usage !== 'string' || !entry.suggested_usage.startsWith('openyida ')) {
315
+ errors.push(`Forbidden alias "${entry.id}" suggested_usage must start with "openyida "`);
316
+ }
317
+ if (typeof entry.message_key !== 'string' || !entry.message_key.trim()) {
318
+ errors.push(`Forbidden alias "${entry.id}" message_key must be a non-empty string`);
319
+ }
320
+ if (entry.message_args !== undefined && !Array.isArray(entry.message_args)) {
321
+ errors.push(`Forbidden alias "${entry.id}" message_args must be an array when present`);
322
+ }
323
+ if (entry.message !== undefined && (typeof entry.message !== 'string' || !entry.message.trim())) {
324
+ errors.push(`Forbidden alias "${entry.id}" message must be a non-empty string when present`);
325
+ }
326
+ for (const commandId of entry.alternative_command_ids || []) {
327
+ if (!commandIds.has(commandId)) {
328
+ errors.push(`Forbidden alias "${entry.id}" references unknown alternative_command_id "${commandId}"`);
329
+ }
330
+ }
331
+ }
332
+ }
333
+
263
334
  function collectPatternCovers(patterns) {
264
335
  if (!Array.isArray(patterns)) {
265
336
  return [];
@@ -316,6 +387,7 @@ function run() {
316
387
  validateReadmeCoverage(commands);
317
388
  validateSideEffects(commands);
318
389
  validatePermissions(commands);
390
+ validateForbiddenAliases();
319
391
 
320
392
  if (errors.length > 0) {
321
393
  console.error('Command manifest validation failed:');
@@ -8,7 +8,7 @@ description: >
8
8
 
9
9
  # 宜搭 AI 应用开发指南
10
10
 
11
- 通过有 AI Coding 能力的智能体(悟空/Claude/Open Code 等)+ 宜搭低代码平台,实现一句话搭建或修改完整应用。所有操作通过 **`openyida`** CLI 统一执行。IF 未设置 `YIDA_AUTH_ENABLED=true`:默认 OAuth token,token 不可用才执行 `openyida login`。IF `YIDA_AUTH_ENABLED=true`:进入宿主注入 token 模式,仅使用 `OPENYIDA_ACCESS_TOKEN` / `OPENYIDA_REFRESH_TOKEN` 等 token env;缺 token 必须 STOP 回宿主,禁止触发 OAuth,禁止读 `.cache/cookies*.json`。
11
+ 通过有 AI Coding 能力的智能体(悟空/Claude/Open Code 等)+ 宜搭低代码平台,实现一句话搭建或修改完整应用。所有操作通过 **`openyida`** CLI 统一执行。登录态分流必须以 `openyida agent-capabilities --summary-json` 或 `openyida login --check-only --json` 返回的 auth snapshot 为准;只有 snapshot 明确返回 `login.auth_source=env` 或 `failure_reason=env_token_missing` 时,才按宿主注入 token 模式处理。其他未登录 token 场景走默认 OAuth token 登录,不要根据 agent 名称、宿主类型或手写环境判断自行分流;禁止读取 `.cache/cookies*.json`。
12
12
 
13
13
  ---
14
14
 
@@ -38,8 +38,9 @@ description: >
38
38
  |---------|------|
39
39
  | 命令跑不了(`command not found`) | openyida 未安装 → `npm install -g openyida` |
40
40
  | Node/npm 版本不达标 | 先升级 Node(≥16)再装/升级 openyida |
41
- | `login.auth_mode=token` 且未登录,且未开启 `YIDA_AUTH_ENABLED` | `openyida login`(指定入口带 URL 或 flag) |
42
- | `login.auth_mode=token` 且 `auth_source=env` / `failure_reason=env_token_missing` | STOP;宿主必须注入 `OPENYIDA_ACCESS_TOKEN` 或 `OPENYIDA_REFRESH_TOKEN`;禁止触发 OAuth;禁止读 `.cache/cookies*.json` |
41
+ | `login.auth_mode=token` 且 `status=ok` / `can_auto_use=true` | 继续执行业务命令 |
42
+ | snapshot 返回 `login.auth_source=env` / `failure_reason=env_token_missing` | STOP;宿主必须注入 `OPENYIDA_ACCESS_TOKEN` 或 `OPENYIDA_REFRESH_TOKEN`;禁止触发 OAuth;禁止读 `.cache/cookies*.json` |
43
+ | `login.auth_mode=token` 且未登录,且 snapshot 未返回 env 注入模式 | `openyida login`(指定入口带 URL 或 flag),完成后再 `openyida login --check-only --json` 验证 |
43
44
  | `workdir_exists` / `active.projectRootExists` 为 false | 无工作目录 → `openyida copy` 初始化 |
44
45
 
45
46
  **👉 环境异常、登录失败、悟空降级、OAuth token 登录异常等特殊分支 → [references/setup-and-env.md](references/setup-and-env.md)。正常 `agent-capabilities` 通过时不要默认读取该 reference。**
@@ -148,6 +149,10 @@ schema-managed create/update 必须等待用户对当前 `planId` 显式批准
148
149
 
149
150
  **Canvas 数据边界**:完整应用/真实交付页如果展示列表、看板或详情记录,必须优先把本轮真实 `appType/formUuid/fieldId` 写入 `page-spec.json` 的 `dataBinding.mode=form`;需要演示记录时先写入真实表单再读取。未接真实表单且未写入 demo records 时,页面展示空态/入口,不用前端 seedRows 冒充业务数据。
150
151
 
152
+ **Schema 获取去重**:完整应用创建/解析多个表单后,页面阶段需要字段映射时,对每个目标表单默认只执行一次 `openyida get-schema <appType> <formUuid> --field-map-json`,读取完整 JSON 并合并到 `.cache/<项目名>-schema.json` 复用。不要用 `head`/`tail`/`grep` 截断 get-schema stdout 作为字段证据,也不要因此对同一表单重复拉取多轮 schema。
153
+
154
+ **Canvas 生成路径二选一**:走模板路径时,先写业务化 `page-spec.json` 再 `openyida generate-page ... --spec ... --compile`,后续只读 manifest/摘要并小范围 Edit;不要立即 Read 大段源码再全量 Write 覆盖同一路径。若已经明确最终页面结构,跳过 `generate-page` 直接 Write 最终 `.canvas.jsx`。
155
+
151
156
  **doneWhen**:`yida-app` 发布主页面成功并输出可访问 URL。到这里默认完成;不要发布后继续 TaskCreate、重复读技能或继续规划。
152
157
 
153
158
  **optionalAfterDone**:导航整理、示例数据、公开访问、截图验证、深度视觉方向、数据源/连接器深度接入、报表/大屏,只在用户明确要求或 `yida-app` 模式为 `full_demo` / `deep_design` 时执行。
@@ -221,16 +226,18 @@ schema-managed create/update 必须等待用户对当前 `planId` 显式批准
221
226
  ### 致命规则(FATAL,违反即失败/报错)
222
227
 
223
228
  1. **技能加载唯一入口**:执行任何子技能前,支持 `use_skill` 的宿主必须调用 `use_skill("<技能名>", "<本阶段目的>")` 加载对应技能;不要用 `Read` / `read_file` / `cat` 读取 SKILL.md 路径,不凭记忆猜参数格式。
224
- 2. **corpId 一致性检查**:创建或发布页面前对比 prd/resource context 与当前 auth context(默认 token session;`YIDA_AUTH_ENABLED=true` 时为宿主注入 token)的 corpId,不一致必须询问用户(重新登录到目标组织,或确认在当前组织继续操作已解析资源/缺失资源)。
229
+ 2. **corpId 一致性检查**:创建或发布页面前对比 prd/resource context 与当前 auth snapshot(本地 OAuth token session 或 snapshot 明确返回的宿主注入 env token)中的 corpId,不一致必须询问用户(重新登录到目标组织,或确认在当前组织继续操作已解析资源/缺失资源)。
225
230
  3. **发布前本地校验**:普通自定义页面 `.oyd.jsx` / `.jsx` 发布前跑 `openyida check-page` + `openyida compile`;Code Canvas `.canvas.jsx` 不跑这两个普通自定义页面检查,改由 `openyida publish` 的 Canvas 编译阶段或 `compileCanvasLocal` 快检校验;JSON 配置写盘后先解析校验,再调用平台命令。
226
231
  4. **页面源码修改必须发布闭环**:只要本轮 Write/Edit/Create 了页面源码 `project/pages/src/*.{canvas.jsx,canvas.tsx,oyd.jsx,jsx,tsx}`(含完整搭建、补齐、已有页面 update path、单点优化),final 前必须看到成功的 `openyida publish <source> <appType> <displayPageFormUuid>` 命令结果;本地文件编辑、diff、本地校验或编译只证明源码可发布,不等于远端页面已更新。若没有 publish 成功证据,final 只能说“源码已修改,尚未发布”,禁止说“页面已更新 / 已重新发布 / 已上线”。
227
232
  5. **命令输入文件禁止 shell 写入**:当 OpenYida 命令需要 JSON/YAML/CSV/config/script 文件参数时,先使用当前 agent 运行时提供的结构化文件写入工具(如 create_file / Write / file edit tool)创建文件,再把路径传给命令;禁止用 shell heredoc、`cat`/`echo`/`printf`/`tee` 加输出重定向,或把命令 stdout 重定向成业务文件。
233
+ 6. **读文件少用 Bash 噪声**:读取或定位 workspace 文件优先用宿主的 Read / Glob / Grep;OpenYida CLI 已返回成功 JSON、URL 或 `formUuid/appType` 时,不要再用 Bash `cat`/`ls` 做无意义复核。
234
+ 7. **OpenYida CLI 不吞诊断**:不要给 `openyida` 命令加 `2>/dev/null`;失败时保留 stdout/stderr(必要时用 `2>&1` 合并诊断)。遇到 DENIED 或同一命令重复失败,先换策略、改输入或重做只读确认,不要盲目微调后重跑。
228
235
 
229
236
  ### 重要规则(IMPORTANT,影响质量/性能/可维护性)
230
237
 
231
238
  1. **按阶段加载必要技能**:按意图选 1 个主技能;完整应用按阶段加载当下唯一需要的子技能,禁止并发批量读取多个 `SKILL.md` 或预读未来阶段技能。
232
239
  2. **Resource-First**:任何 legacy 写操作前先解析本轮显式资源、agent bound context、workspace cache/config、历史上下文;已有目标资源时默认修改/补齐/发布,只有目标缺失且意图允许创建时才加载 create 类技能。
233
- 3. **优先复用 direct 映射**:仅对 direct/standalone 资源,已有 `.cache/<项目名>-schema.json` 中可确认新鲜的 `appType`/`formUuid`/`fieldId` 可复用;该文件不是 Schema-as-Code state,也不是远端真相。字段缺失、重名或结构变化时执行 `get-schema --compact --resolve-fields`,不得猜测。
240
+ 3. **优先复用 direct 映射**:仅对 direct/standalone 资源,已有 `.cache/<项目名>-schema.json` 中可确认新鲜的 `appType`/`formUuid`/`fieldId` 可复用;该文件不是 Schema-as-Code state,也不是远端真相。字段缺失、重名或结构变化时执行 `get-schema --compact --resolve-fields`;完整应用页面需要多字段/多表单映射时每表单一次性执行 `get-schema --field-map-json` 并缓存完整字段摘要。不得猜测字段 ID,也不要用 `head`/`tail`/`grep` 截断 schema stdout 当证据。
234
241
  4. **模板优先**:复杂产物先用 `openyida sample` 或现有示例生成骨架,再做最小改动。
235
242
  5. **配置承载优先于代码**:字段/公式/联动/报表/审批/集成交给对应技能,自定义页面只做展示与胶水。
236
243
  6. **数据性能优先**:统计聚合用 `yida-report` 服务端聚合,不在前端拉全量后自行聚合。
@@ -15,8 +15,10 @@ openyida login --check-only --json
15
15
 
16
16
  ## Auth Mode
17
17
 
18
- - IF `login.auth_mode=token`: OAuth token mode.
19
- - IF `YIDA_AUTH_ENABLED=true`: host-injected token mode; the host must provide token env such as `OPENYIDA_ACCESS_TOKEN` or `OPENYIDA_REFRESH_TOKEN`.
18
+ - Do not infer auth mode from agent name, host product, workspace path, or a guessed environment variable.
19
+ - First use `openyida agent-capabilities --summary-json`; fallback to `openyida login --check-only --json` only when the compact snapshot is unavailable or insufficient.
20
+ - If the snapshot reports `login.auth_source=env` or `failure_reason=env_token_missing`, treat it as host-injected token mode; the host must provide token env such as `OPENYIDA_ACCESS_TOKEN` or `OPENYIDA_REFRESH_TOKEN`.
21
+ - Otherwise, `login.auth_mode=token` uses the default OAuth token session flow.
20
22
  - NEVER infer auth from `.cache/cookies*.json`.
21
23
 
22
24
  ## Decision Table
@@ -26,13 +28,13 @@ openyida login --check-only --json
26
28
  | command not found | install/update `openyida`; do not create resources |
27
29
  | `workdir_exists=false` or `active.projectRootExists=false` | run `openyida copy`; do not create resources before workspace exists |
28
30
  | `auth_mode=token`, `status=ok` or `can_auto_use=true` | continue |
29
- | `auth_mode=token`, `failure_reason=env_token_missing` | STOP; host must inject `OPENYIDA_ACCESS_TOKEN` or `OPENYIDA_REFRESH_TOKEN`; do not run OAuth |
30
- | `auth_mode=token`, not logged in, `YIDA_AUTH_ENABLED` is not true | run `openyida login`; verify with `openyida login --check-only --json` |
31
- | `auth_mode=token`, access token expired | run `openyida auth refresh`; if still failed and `YIDA_AUTH_ENABLED` is not true, run `openyida login` |
31
+ | snapshot reports `auth_source=env` / `failure_reason=env_token_missing` | Treat as host-injected token mode; if token is missing, STOP and ask host to inject `OPENYIDA_ACCESS_TOKEN` or `OPENYIDA_REFRESH_TOKEN`; do not run OAuth |
32
+ | `auth_mode=token`, not logged in, and snapshot does not report env injection | run `openyida login`; verify with `openyida login --check-only --json` |
33
+ | `auth_mode=token`, access token expired | run `openyida auth refresh`; if still failed and snapshot does not report env injection, run `openyida login` |
32
34
 
33
35
  ## Token Mode Commands
34
36
 
35
- Use OAuth login only when `YIDA_AUTH_ENABLED` is not true.
37
+ Use OAuth login only when the auth snapshot does not report env injection.
36
38
 
37
39
  ```bash
38
40
  openyida login
@@ -54,7 +56,7 @@ Overseas / international / global / Japan / Global YiDA => add `--intl` or equiv
54
56
 
55
57
  ## Host-Injected Token Mode Commands
56
58
 
57
- Use only when `YIDA_AUTH_ENABLED=true`.
59
+ Use only after the auth snapshot reports `auth_source=env` or `failure_reason=env_token_missing`.
58
60
 
59
61
  ```bash
60
62
  openyida agent-capabilities --summary-json
@@ -75,11 +77,11 @@ Allowed result:
75
77
  }
76
78
  ```
77
79
 
78
- If the host did not inject token env, failure result includes `failure_reason=env_token_missing`; stop the task and go back to the host. Do not launch OAuth from this mode.
80
+ If the host did not inject token env, the snapshot includes `failure_reason=env_token_missing`; stop the task and go back to the host. Do not launch OAuth from this mode.
79
81
 
80
82
  ## NEVER
81
83
 
82
- - Never run `openyida login` in host-injected token mode.
84
+ - Never run `openyida login` after the snapshot reports host-injected token mode.
83
85
  - Never read `.cache/cookies*.json` as yida-agent auth.
84
86
  - Never ask the user to export browser Cookie.
85
87
  - Never print Cookie, CSRF, `access_token`, or `refresh_token`.
@@ -87,5 +89,5 @@ If the host did not inject token env, failure result includes `failure_reason=en
87
89
  ## Wukong / Codex
88
90
 
89
91
  - Same auth mode rules as above.
90
- - Do not special-case Wukong or Codex into OAuth login when `YIDA_AUTH_ENABLED=true`.
92
+ - Do not special-case Wukong, Codex, yida-agent, or any host identity into an auth branch; follow the OpenYida auth snapshot.
91
93
  - Do not create app/page/form/publish until auth snapshot is usable.
@@ -30,6 +30,15 @@ description: 宜搭完整应用开发编排技能。对普通 OpenYida 应用做
30
30
  - 已有流程表单或 `processCode` 时,流程诉求走 `yida-process-rule`;只有没有表单/流程且用户要新建审批表单时才进入 `yida-create-process`。
31
31
  - 多个同优先级候选、当前轮显式资源冲突或目标不明时才问用户;不要因为 cache 和历史里同时存在资源就默认打断。
32
32
 
33
+ ### 阶段 0 命令选择(不要猜命令)
34
+
35
+ - 已有显式 `appType`、应用 URL 或 agent bound `appType` 且能唯一解析时,直接复用该 app;不要调用 `app-list` 做存在性确认。
36
+ - 只有用户只给应用名称、存在多个候选、resource context 冲突,或需要诊断目标 app 访问失败时,才运行 `openyida app-list [--size N]`。
37
+ - 已知 `appType` 后,查询该应用下表单/页面用 `openyida list-forms <appType> [--keyword <text>]`;选择页面发布目标时只用 `formType=display`。
38
+ - 查询表单/页面 Schema、字段 ID 或批量字段摘要用 `openyida get-schema <appType> <formUuid|--all> ...`。
39
+ - 完整应用页面阶段如果需要多个表单的字段映射,默认对每个目标业务表单执行一次 `openyida get-schema <appType> <formUuid> --field-map-json`,读取完整 JSON 并合并到 `.cache/<项目名>-schema.json`;不要对同一表单用 `tail/head/grep` 截断 stdout 后再重复拉取。
40
+ - 阶段 0 禁止编造 `list-apps` / `get-app`;也不要把 `--app-type` / `--form-uuid` 当成 `list-forms` 或 `get-schema` 的参数。按目的在 `app-list`、`list-forms`、`get-schema` 三者中选择。
41
+
33
42
  该阶段只决定普通 OpenYida resource context;schema-managed 路径仍以 schema CLI 的 validate/plan/apply 结果为准。schema-managed create/update 必须停在当前 `planId`,等待用户显式批准后才可执行 `apply`;`nextAction`、错误恢复或本技能判断都不能授予 `mixed/write`。Phase 1 中 report、automation、page config、delete、pull 不从 Manifest fallback 到本技能的 legacy workflow。
34
43
 
35
44
  ## 阶段 1:resolve app name / rename placeholder app
@@ -59,6 +68,11 @@ description: 宜搭完整应用开发编排技能。对普通 OpenYida 应用做
59
68
 
60
69
  遵循根入口的只读预检结果。若当前会话还没做预检,先按根入口执行一次只读校验;只有登录态可用后,才执行会创建、修改或发布宜搭资源的命令。不要在每个阶段重复跑 env/help/login 探测。
61
70
 
71
+ ## 路径与文件读取口径
72
+
73
+ - 页面源码路径按当前 Bash cwd 选择:从仓库根执行时用 `project/pages/src/...`;如果 cwd 已是 `<workspace>/project`,用 `pages/src/...`,不要传 `project/pages/src/...` 导致 `project/project`。
74
+ - 读取 PRD、字段 JSON、页面源码或 schema 文件时优先用宿主 Read / Glob / Grep;OpenYida CLI 成功输出已经是操作证据,不要再 Bash `cat`/`ls` 复核。
75
+
62
76
  ## 标准执行流
63
77
 
64
78
  ```text
@@ -78,6 +92,7 @@ description: 宜搭完整应用开发编排技能。对普通 OpenYida 应用做
78
92
  ↓
79
93
  [Step 6] 编写自定义页面代码 → 默认 use_skill("yida-canvas-custom-page", "生成 Code Canvas 主页面")
80
94
  ↓ 先写业务化 page-spec.json,再 openyida generate-page <模板> --theme-profile yida-app-theme --theme-scope page --spec <page-spec.json> --compile
95
+ ↓ 字段映射来自 `.cache/<项目名>-schema.json`;同一表单不要重复 get-schema,除非刚修改字段或缓存不完整
81
96
  ↓ 本轮已创建/解析业务表单且页面需要列表/看板/详情数据时,必须在 spec.dataBinding 写 mode=form + 真实 appType/formUuid/fieldId;深度接入再加载 yida-canvas-data-binding
82
97
  ↓ 明确要求普通自定义页面 JSX/Jsx 组件链路,或强依赖 this.$ / this.utils.yida.* / this.dataSourceMap 等实例桥时选择 yida-custom-page
83
98
  ↓
@@ -141,11 +156,16 @@ UI 不是独立替代主流程的步骤,而是按模式插入到页面生成
141
156
  | `yida-data-management` | `openyida sample yida-data-management form-field-template` | 表单字段定义和数据插入 |
142
157
  | `yida-create-app` | `openyida sample yida-create-app ipd-app-template` | 完整应用创建示例 |
143
158
 
144
- 代码生成前必须:
159
+ 页面实现必须二选一:
160
+
161
+ - **模板路径**:先写业务化 `page-spec.json`,再执行 `openyida generate-page ... --spec <page-spec.json> --compile`。生成后只读取 `.openyida-page.json` / CLI 摘要判断 `domainFidelity` 和 dataBinding 状态;若需要补业务语义或样式,基于生成文件做小范围 Edit/patch。禁止在 `generate-page` 后立即 Read 500+ 行源码再全量 Write 覆盖同一路径。
162
+ - **手写路径**:如果已经明确最终页面结构、数据桥和视觉细节,跳过 `generate-page`,直接 Write 最终 `.canvas.jsx`,再做本地快检和 publish。不要先生成模板再把模板完全覆盖。
163
+
164
+ 选择模板路径时必须:
145
165
 
146
166
  1. 先从 PRD 提炼当前业务自己的 page spec;
147
167
  2. 再执行对应的 `openyida generate-page` 命令生成 Code Canvas 骨架;
148
- 3. 读取 manifest 的 `domainFidelity`,若仍是 sample-reference / draft,则补 spec 或改源码;
168
+ 3. 读取 manifest / CLI 摘要的 `domainFidelity`,若仍是 sample-reference / draft,则补 spec 或小范围改源码;
149
169
  4. 以模板为基础扩展交互和真实数据;
150
170
  5. 验证所有参数名称与 CLI 一致。
151
171
 
@@ -158,7 +178,7 @@ UI 不是独立替代主流程的步骤,而是按模式插入到页面生成
158
178
  | 0. 解析资源上下文 | 无 | 合并本轮显式资源、agent bound context、workspace config/cache、会话历史;本轮显式目标覆盖 bound context;判定 app/page/form/process 的 `source` 和 `allowCreate` | 明确复用、创建缺口或需要 ask_human |
159
179
  | 1. resolve app + app name | `yida-create-app` 仅在 app 缺失且允许创建时加载;`openyida update-app` 仅用于预创建占位 app 改名 | 已有 `appType`/应用 URL/bound app 时直接复用;若 bound/precreated app 仍是占位名,在语义名稳定后执行 `openyida update-app <appType> --name "<语义应用名>"`;否则创建应用并提取真实 `appType` | 拿到真实目标 `appType`,预创建占位 app 已改成语义名或明确跳过,且不会重复创建同类 app |
160
180
  | 2. 记录最小需求 | 无 | 写 `prd/<项目名>.md`:只记录 MVP 假设、核心表单/页面、完成标准;写/更新 `.cache/<项目名>-schema.json` standalone 映射;不要写长 PRD | 业务语义和 ID 存储位置明确 |
161
- | 3. resolve forms | `yida-create-form-page` | 已有目标表单时 update/patch/rule/bind-datasource;缺少支撑 MVP 的核心表单且允许创建时才 create;字段配置文件写入 `.cache/openyida/<项目名>/` | 拿到或确认表单 `formUuid` 和真实 `fieldId` |
181
+ | 3. resolve forms | `yida-create-form-page` | 已有目标表单时 update/patch/rule/bind-datasource;缺少支撑 MVP 的核心表单且允许创建时才 create;字段配置文件写入 `.cache/openyida/<项目名>/`;创建/解析多个表单后,对每个目标表单最多一次性获取完整 `--field-map-json` 并合并写回 `.cache/<项目名>-schema.json` | 拿到或确认表单 `formUuid` 和真实 `fieldId` |
162
182
  | 4. resolve main page | `yida-create-page` 仅在主页面缺失且允许创建时加载 | 已有页面 URL / `formUuid` / bound page 时直接作为主页面;否则创建一个用户主入口 display page | 拿到真实目标页面 `formUuid`,且不会重复创建页面 |
163
183
  | 5. 编写/更新页面 | 默认 `yida-canvas-custom-page`;明确要求 JSX/Jsx 组件链路或实例桥强依赖时选择 `yida-custom-page` | 生成或修改主页面源码;只实现 MVP 首屏和核心操作。可用已解析表单链接、真实空态、表单入口和轻量指标口径完成主页面;若展示业务列表/看板/详情记录,必须接本轮真实表单 `dataBinding.mode=form`,或先写入 demo records 后再读取;不要加载视觉/密度/报表/数据源等额外技能 | 本地源码通过对应页面技能的基础校验;未执行 publish 时仍是“源码已修改,尚未发布” |
164
184
  | 6. 发布页面 | `yida-publish-page` | 按页面链路校验后发布到已解析主页面:Canvas `.canvas.jsx` 使用 `openyida publish` 的 Canvas 编译阶段或 `compileCanvasLocal` 快检;普通自定义页面 `.oyd.jsx` / `.jsx` 跑 `check-page` / `compile`;再执行 `openyida publish <source> <appType> <displayPageFormUuid>` 发布主页面 | 发布成功并获得可访问 URL |
@@ -259,6 +279,7 @@ UI 不是独立替代主流程的步骤,而是按模式插入到页面生成
259
279
  ## 错误处理
260
280
 
261
281
  - 不编造 `appType`、`formUuid`、`fieldId`、`reportId`。
282
+ - OpenYida CLI 不要加 `2>/dev/null`;失败时保留 stdout/stderr 诊断。遇到 DENIED 或同一命令重复失败,先换策略、修改输入文件/参数/登录态/组织或重新只读取证,再重试。
262
283
  - 同一命令失败后,必须改变登录态、组织、参数、输入文件或字段 ID 后才能重试;禁止无修改连续重试。
263
284
  - corpId 与目标组织不一致时先停下,让用户选择重新登录或在当前组织继续。
264
285
  - 已有目标 app/page/form/process 时默认复用;只有用户明确要求新建另一个同类资源,或目标缺失且本次意图允许创建时,才加载 create 类子技能。
@@ -22,6 +22,7 @@ Code Canvas 是宜搭的代码画布自定义页面链路:以 `YidaCodeCanvas`
22
22
  ## 运行时事实
23
23
 
24
24
  - Canvas 源码写成 `.canvas.jsx` / `.canvas.tsx`,`openyida publish` 会自动走 Canvas 链路。
25
+ - 页面源码路径按 Bash cwd 选择:从仓库根执行命令时用 `project/pages/src/...`;如果 cwd 已是 `<workspace>/project`,用 `pages/src/...`,不要写成 `project/pages/src/...`。
25
26
  - `runtimeCode` 在宿主页真实 `window` 中执行,入口必须返回 `YidaComp` / `YidaComp.default` / 组件函数。
26
27
  - Canvas 组件没有普通页面实例上下文;数据读写通过 fetch、开放 API、连接器代理或显式 props 数据桥完成。
27
28
  - 第三方依赖走白名单;React、antd、ahooks、d3、recharts、Radix、framer-motion 等可按规则 import。
@@ -104,6 +105,7 @@ openyida sample yida-canvas-custom-page portal-native-components --output projec
104
105
  8. **门户运行态组件要补必需 props 和局部降级**:`QuickAccessCard` / `RecentlyUsedCard` 必须传 `theme="row-white"` 等必需 props,避免运行态读取 `theme.includes(...)` 报错;所有门户/字段/上传增强组件外层加局部 ErrorBoundary,单个组件不兼容时只降级该块,不让整页进入 Canvas 错误态。
105
106
  9. **自定义主题必须页面内注入**:`--theme` 只接受平台预置 key;如果页面设计使用非预置主题(例如活力橙、深玫红、自定义暗黑金),Canvas 页面必须在自身源码中注入 `style#yida-global-theme` 或等价 scoped CSS vars,并在根节点设置 `data-theme-scope="page"`。官方 sample 每个页面都要做,避免宿主应用 `black` 主题把页面染成黑灰。
106
107
  10. **真实交付不使用前端 seed 冒充业务数据**:`openyida sample` 原样发布可以保留 sample/seed 数据,但必须在页面上标注为 sample/seed。完整应用或真实交付页只要需要列表、看板、详情记录,并且本轮已经创建/解析业务表单,就必须在 `page-spec.json` 写入 `dataBinding.mode=form`、真实 `appType/formUuid` 和字段映射,让页面从表单读取。若需要演示数据,先通过表单数据写入链路创建 demo/mock records,再由 Canvas 读取这些真实表单记录;未写入 demo records 且没有真实数据时展示空态、表单入口、刷新/登记按钮。
108
+ 11. **页面生成二选一**:选择模板路径时,`openyida generate-page ... --spec ... --compile` 之后只读取 CLI 摘要或 `.openyida-page.json` 判断 `domainFidelity` / dataBinding,并对生成源码做小范围 Edit/patch;禁止立刻 Read 大段源码后全量 Write 覆盖同一路径。选择手写路径时,直接 Write 最终 `.canvas.jsx` 并快检/发布,不要先跑 `generate-page` 再完全覆盖。
107
109
 
108
110
  ## 数据真实性边界
109
111
 
@@ -154,6 +156,8 @@ npx jest tests/canvas-compile.test.js tests/generate-page.test.js --runInBand
154
156
 
155
157
  ## 开发流程
156
158
 
159
+ 下面命令以仓库根为视角;如果当前 cwd 已经是 `<workspace>/project`,把 `project/pages/src/...` 改成 `pages/src/...`。读取生成文件、Schema 或校验产物时优先用宿主 Read / Glob / Grep,不要在 CLI 成功后 Bash `cat`/`ls` 复核。
160
+
157
161
  ```bash
158
162
  # 1. 只读检查环境和登录态;真实创建资源前必须通过
159
163
  openyida env --json
@@ -162,12 +166,14 @@ openyida login --check-only --json
162
166
  # 2. 如需新页面,先创建空白自定义页拿 formUuid
163
167
  openyida create-page <appType> "<页面名>"
164
168
 
165
- # 3. 生成或复制 Canvas 源码
169
+ # 3. 生成或编写 Canvas 源码
170
+ # 模板路径:生成后基于 manifest/摘要和小范围 patch 演进,不全量覆盖生成文件。
166
171
  openyida generate-page workbench-home --theme-profile yida-app-theme --theme-scope page --output project/pages/src/workbench-home.canvas.jsx --compile
167
172
  openyida generate-page dashboard-overview --theme-profile yida-app-theme --theme-scope page --output project/pages/src/dashboard-overview.canvas.jsx --compile
168
173
  openyida generate-page portal-shell-home --theme-profile yida-app-theme --theme-scope page --output project/pages/src/portal-shell-home.canvas.jsx --compile
169
174
  openyida sample yida-canvas-custom-page native-components-smoke --output project/pages/src/native-components-smoke.canvas.jsx
170
175
  openyida sample yida-canvas-custom-page portal-native-components --output project/pages/src/portal-native-components.canvas.jsx
176
+ # 手写路径:已明确最终页面结构时,跳过 generate-page,直接 Write 最终 .canvas.jsx。
171
177
 
172
178
  # 4. 本地 Canvas 快检
173
179
  node -e "const fs=require('fs'); const {compileCanvasLocal}=require('./lib/app/canvas-compile'); const src=fs.readFileSync('project/pages/src/<页面名>.canvas.jsx','utf8'); console.log(compileCanvasLocal(src).importedModules)"
@@ -175,8 +181,8 @@ node -e "const fs=require('fs'); const {compileCanvasLocal}=require('./lib/app/c
175
181
  # 5. 发布(本轮修改源码后的远端完成证据)
176
182
  openyida publish project/pages/src/<页面名>.canvas.jsx <appType> <formUuid>
177
183
 
178
- # 6. 发布后回读 Schema 验收
179
- openyida get-schema <appType> <formUuid> > .cache/openyida/<页面名>-schema.json
184
+ # 6. 发布后回读字段摘要验收;如需留证,用结构化文件写入工具保存 stdout,不用 shell 重定向
185
+ openyida get-schema <appType> <formUuid> --field-map-json
180
186
  ```
181
187
 
182
188
  `openyida check-page` / `openyida compile` 当前面向普通自定义页面 `.oyd.jsx` / `.jsx`;Canvas 以 `compileCanvasLocal` 和 `openyida publish .canvas.jsx` 的 Canvas 编译阶段为准。`compileCanvasLocal` 是发布前快检,不能替代 `openyida publish` 的远端写入证据。
@@ -21,7 +21,11 @@
21
21
  | framer-motion | `FramerMotion` | `${cdn}/.../framerMotion.js` |
22
22
  | yida-plugin-markdown | `YidaMarkdown` | moduleFederation 0.0.4 |
23
23
 
24
- 新增依赖必须同时满足:① 编译能把 import 抽进 `importedModules` 并映射到 windowAlias(见 `canvas-compile.js` 的 `MODULE_ALIAS_MAP`);② 上表或平台运行时能把依赖加载到 window;③ `runtimeCode` 引用的变量名与 windowAlias 一致;④ CSS 资源可加载,否则组件可能渲染但样式/弹层异常。白名单外的包(yida-utils、`@ali/deep`、原生字段组件等)不能 `import`。
24
+ 新增依赖必须同时满足:① 编译能把 import 抽进 `importedModules` 并映射到 windowAlias(见 `canvas-compile.js` 的 `MODULE_ALIAS_MAP`);② 上表或平台运行时能把依赖加载到 window;③ `runtimeCode` 引用的变量名与 windowAlias 一致;④ CSS 资源可加载,否则组件可能渲染但样式/弹层异常。白名单外的包(yida-utils、`@ali/deep`、原生字段组件等)不能 `import`;带绑定的非白名单裸包 import 会在本地编译阶段硬失败。宜搭平台运行态全局对象必须显式使用 `window.Deep`、`window.DeepYida`、`window.YidaNativeComponents` 等 `window.*` 访问,不要从包中导入。
25
+
26
+ 如果宜搭物料依赖表已经先于 OpenYida CLI 升级,且你已经确认运行时确实会注入某个新裸包,可以临时设置 `OPENYIDA_CANVAS_ALLOW_UNSUPPORTED_IMPORTS=1` 退回 legacy `window["pkg"]` 映射发布;这只是白名单漂移逃生舱,不应用来绕过 `useDataBinding` 这类不存在的 hook 或未验证依赖。
27
+
28
+ Canvas 没有官方 `useDataBinding` hook,不得从任何包 `import { useDataBinding }`。真实表单数据绑定使用页面内本地 `useYidaData(binding)`、`DataBridge` 与同源 `fetch` 实现。
25
29
 
26
30
  编译位置:OpenYida CLI **本地用 Babel** 把源码转译为 `runtimeCode` + `importedModules`(`import`→`window.<别名>`、`export default`→`YidaComp`、依赖名正则抽取),不调用任何在线编译服务,因此不依赖登录态、不经过风控。别名映射逐条镜像自 `dependencies.ts` 的 `getModuleAliasMap()`;运行时消费契约见 `factory.tsx`(`new Function` 执行 `runtimeCode` 取 `YidaComp`)。
27
31
 
@@ -8,6 +8,8 @@
8
8
 
9
9
  `generate-page` 的模板只用于选定运行时契约、数据桥、主题变量和首版 primitives;它不是最终视觉稿。生成真实页面时,必须结合 `yida-page-uiux` 的视觉方向决策块重写区块顺序、信息层级、局部构图、文案和样式节奏。可以保留模板的编译安全结构和必要 primitive class,但不要照搬默认 Hero、卡片网格、三段式卖点或库存文案。
10
10
 
11
+ 页面生成路径必须二选一:走模板路径时,先写业务化 `page-spec.json` 并执行 `openyida generate-page ... --spec ... --compile`,之后只读取 CLI 摘要或 `.openyida-page.json`,再对生成源码做小范围 Edit/patch;不要立刻 Read 大段源码后全量 Write 覆盖同一路径。若已经明确最终页面结构、数据桥和视觉细节,走手写路径,跳过 `generate-page`,直接 Write 最终 `.canvas.jsx`。
12
+
11
13
  生成器会在 `.openyida-page.json` 中写入 `domainFidelity`,并在 CLI 输出中提示当前页面是否还依赖 sample fallback:
12
14
 
13
15
  - `domain-ready`:主要业务语义已覆盖,sample 只剩编译骨架。
@@ -16,6 +16,7 @@ Code Canvas 运行时是标准 React 组件环境,组件没有普通宜搭自
16
16
  - `YidaCodeCanvas` 物料只透传 `code / runtimeCode / importedModules / pageType`。
17
17
  - 组件内没有 `this` 上下文,也没有 `dataSourceMap`。
18
18
  - `this.utils.yida.*`、`didMount()`、`_customState` 等普通页面契约不可用。
19
+ - Canvas 没有官方 `useDataBinding` hook,不得从任何包 `import { useDataBinding }`;真实表单数据绑定用页面内本地 `useYidaData(binding)`、`DataBridge` 和同源 `fetch` 实现。
19
20
  - Cookie 由浏览器同源请求自动携带,前端代码不能硬编码 Cookie、appSecret、accessKey 或外部密钥。
20
21
  - 调宜搭同源端点时,请求必须带 `credentials: 'include'`。
21
22
  - CSRF 优先从 `window.g_config._csrf_token` 或 `window.g_config.csrfToken` 读取;内部端点常同时需要 `_csrf_token` 参数和 `global_csrf_token` 请求头。
@@ -24,6 +24,7 @@ description: 表单页面创建与更新,支持 19 种业务字段和 Divider
24
24
  - 不要在 update / patch / rule / validation / bind-datasource 模式中使用猜测的 fieldId,必须先用 `yida-get-schema` 获取
25
25
  - 不要用此命令操作数据记录(增删改查),应使用 `yida-data-management`
26
26
  - 不要用 shell heredoc、`cat`/`echo`/`printf`/`tee` 或重定向生成字段、变更、补丁、规则、数据源 JSON 文件
27
+ - OpenYida CLI 不要加 `2>/dev/null`;失败时保留 stdout/stderr 诊断,遇到 DENIED 或重复失败必须换策略
27
28
  - 已有目标表单且用户是改字段/联动/属性时,不要创建新表单;必须走 update/patch/rule/bind-datasource。
28
29
  - 不要用 `GroupContainer` / `PageSection` 承载普通业务分组;普通分组必须优先用 `Divider`
29
30
 
@@ -120,6 +121,15 @@ openyida create-form create <appType> <formTitle> <fieldsJsonOrFile> [--layout d
120
121
  {"success":true,"formUuid":"FORM-XXX","formTitle":"用户信息表","appType":"APP_xxx","fieldCount":4,"url":"{base_url}/APP_xxx/workbench/FORM-XXX"}
121
122
  ```
122
123
 
124
+ ### create 失败恢复决策树
125
+
126
+ create 命令失败后,不要立刻重复同一条 create:
127
+
128
+ 1. 先确认字段 JSON 文件存在,且内容是结构化写入后的最终字段数组/对象,不是半截 JSON、update changes 或 shell 拼接残留。
129
+ 2. 运行 `openyida list-forms <appType> --keyword "<表单名>"` 查同名表单;若本轮刚创建过空白表单或已有同名目标表单,优先走 `create-form update` / `patch` / 后续显式 resume 能力复用,不再 create。
130
+ 3. 只有确认远端没有同名目标表单,并且已经修改输入文件、参数、登录态或组织后,才重试 create。
131
+ 4. 同一 create 命令最多重试 2 次;仍失败时停止并带上完整 stdout/stderr、字段文件路径、appType、表单名和已发现的 formUuid 给用户。
132
+
123
133
  ## update 模式
124
134
 
125
135
  已有 `formUuid` / 表单 URL / bound form 时优先使用本模式;修改字段前必须用 `openyida get-schema` 确认字段 ID 和当前结构。
@@ -16,6 +16,7 @@ description: 宜搭普通自定义页面 JSX / Jsx 组件开发规范(React 16
16
16
  - 完整应用 `fast_build` 如果已有 bound app/page,主页面源码直接落到该页面;只在缺少主入口 display page 且用户意图允许新增时创建页面容器。
17
17
  - 用户只说“优化这个页面 URL / 修改现有页面 / 重新发布”时,本技能与 `yida-publish-page` 配合即可完成,不创建 app/page。
18
18
  - 如果用户给的是普通表单 `formUuid`,页面源码只能把它作为数据源或入口链接使用;不能把数据表单 ID 当作发布目标。
19
+ - 页面源码路径按 Bash cwd 选择:从仓库根执行命令时用 `project/pages/src/...`;如果 cwd 已是 `<workspace>/project`,用 `pages/src/...`,不要写成 `project/pages/src/...`。
19
20
 
20
21
  ## 核心规则
21
22
 
@@ -85,6 +86,8 @@ description: 宜搭普通自定义页面 JSX / Jsx 组件开发规范(React 16
85
86
 
86
87
  以开发「员工信息查询页」为例,完整流程如下:
87
88
 
89
+ 下面命令以仓库根为视角;如果当前 cwd 已经是 `<workspace>/project`,把 `project/pages/src/...` 改成 `pages/src/...`。读取生成文件和 Schema 时优先用宿主 Read / Glob / Grep,不要在 CLI 成功后 Bash `cat`/`ls` 复核。
90
+
88
91
  1. 获取表单 Schema,确认字段 ID:
89
92
 
90
93
  ```bash
@@ -15,12 +15,16 @@ description: 确定性解析表单字段 ID(fieldId)和子表路径;agent
15
15
  - 不要缓存过期的 Schema 信息,表单结构变更后必须重新获取
16
16
  - 不要把进程内状态或 CLI 自动派生索引当作跨调用缓存;查询新字段时允许重新拉取完整 Schema
17
17
  - 不要把 `openyida get-schema` 的 stdout 通过 shell 重定向保存成 JSON,也不要用 heredoc、`cat`/`echo`/`printf`/`tee` 生成 Schema 文件
18
+ - 不要把 `openyida get-schema` 的 stdout 再接 `head`、`tail`、`grep`、`sed`、`awk` 等截断/筛选命令作为 Schema 证据;这会丢字段、选项或子表路径,导致后续重复拉取
19
+ - 不要在同一阶段对同一个 `formUuid` 连续执行 `--compact`、`--field-map-json`、完整 Schema 等多轮“探一段 stdout”式查询;除非表单刚被修改、上次命令失败/不完整,或排障需要完整组件 props
18
20
 
19
21
  ## 严格要求 (MUST DO)
20
22
 
21
23
  - **凡是需要用到字段 ID(fieldId)的操作,必须先执行此命令**,不得跳过
22
24
  - 页面开发、数据查询、报表配置或流程规则只需要字段身份时,先执行 `openyida get-schema <appType> <formUuid> --compact --resolve-fields "<字段1,字段2>"`,不要拉取完整 Schema
23
25
  - 页面开发默认使用 compact 输出,只读取必要字段契约,不内联完整 Schema
26
+ - 完整应用页面、看板、列表或详情页需要一个表单的大部分字段,或需要跨多个表单建立 `dataBinding` 时,优先对每个表单执行一次 `openyida get-schema <appType> <formUuid> --field-map-json`,消费完整 JSON 后解析所需字段,不用 shell 截断 stdout
27
+ - 多表单场景同一阶段同一 `formUuid` 默认最多拉取一次字段映射;把 `appType`、`formUuid`、`fieldId`、`label`、`componentName`、`options` 等合并写入 `<projectRoot>/.cache/<项目名>-schema.json`,后续页面 spec 和源码复用该 standalone ID 映射
24
28
  - 执行 compact 查询后,只消费唯一命中的 `fields[]`;`missingFields` 或 `ambiguousFields` 非空时停止,不得猜测或继续写操作
25
29
  - 只有用户明确需要完整组件 props、布局结构、字段数据源配置,或 compact/summary 无法排障时,才执行不带 `--compact`/`--summary-json` 的完整 Schema 输出;拿到完整 Schema 后只读取必要片段,不内联完整 Schema
26
30
  - 已有 `<projectRoot>/.cache/<项目名>-schema.json` 等 standalone ID 映射文件可显式复用;目标字段缺失、重名、结构已变或无法确认新鲜度时,必须重新执行 compact 查询
@@ -74,13 +78,15 @@ openyida get-schema <appType> --all [--summary-json] [--output-dir <dir>] [--key
74
78
 
75
79
  ```bash
76
80
  openyida get-schema APP_XXX FORM-XXX --compact --resolve-fields "访客姓名,状态"
77
- openyida get-schema APP_XXX FORM-XXX --summary-json
81
+ openyida get-schema APP_XXX FORM-XXX --field-map-json
78
82
  ```
79
83
 
80
84
  Agent 只需要少量字段 ID 时,默认使用 `--compact --resolve-fields`,读取 `fields[].label`、`fields[].fieldId`、`fields[].componentType`、`fields[].valueType`、`fields[].path`、`fields[].labelPath` 和 `fields[].parentFieldId`。`path` 是稳定的 fieldId 数组,`labelPath` 是可读路径;所有可用语言的 label 都参与精确匹配。同名字段会进入 `ambiguousFields[].matches`,必须使用完整 `labelPath`、稳定 `path` 或已返回的 fieldId 重新精确选择,禁止取第一个。
81
85
 
82
86
  需要全量字段摘要和选项时继续使用 `--summary-json`。只有需要组件完整 props、布局结构、字段数据源配置或排障时,才执行不带 compact/summary 参数的完整 Schema 输出。
83
87
 
88
+ 消费输出时读取完整 JSON,再由 agent / 脚本解析字段;不要用 `tail -20`、`head -30` 或 `grep` 只看局部 stdout。局部查看可以作为人工调试,但不能作为后续写页面、写数据或配置流程的字段证据。
89
+
84
90
  如需复用输出,使用 agent 的结构化文件写入工具创建:
85
91
 
86
92
  ```text
@@ -167,7 +173,7 @@ Agent compact 模式输出共享 contract,不包含完整 Schema 或 props:
167
173
  |---------|----------|
168
174
  | 命令返回失败 | 确认 appType 和 formUuid 正确,检查登录态 |
169
175
  | 输出被终端截断 | 优先改用 `--summary-json`;确需完整 Schema 时,再将 stdout 通过结构化文件写入工具保存到 `<projectRoot>/.cache/openyida/<项目名或任务名>/<表单名>-schema.json`;不要使用 shell 重定向 |
170
- | 需要多个表单字段 ID | 使用批量 compact 模式:`openyida get-schema <appType> --all --summary-json --output-dir .cache/openyida/<项目名或任务名>/schemas`,默认只读 `index.json` 字段摘要 |
176
+ | 需要多个表单字段 ID | 使用批量摘要或每表单完整字段映射:`openyida get-schema <appType> --all --summary-json --output-dir .cache/openyida/<项目名或任务名>/schemas`,或对目标表单逐个执行一次 `--field-map-json`;默认只读完整 JSON / `index.json` 字段摘要,不用 `head`/`tail`/`grep` 截断 |
171
177
  | 批量部分失败 | 查看 stdout 的 `failedCount` 和 `forms[].errorMsg`,必要时提高 `--retries` 或缩小 `--keyword` 范围 |
172
178
  | 找不到目标字段 | 查看 `missingFields`,确认字段已创建后重新查询;不能手写猜测 fieldId |
173
179
  | 同名字段无法唯一确定 | 查看 `ambiguousFields[].matches[].labelPath` 和稳定 `path`,使用完整路径或 fieldId 重新查询;不得默认取第一个 |