openyida 2026.7.18 → 2026.7.21

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.
Files changed (200) hide show
  1. package/README.md +37 -2
  2. package/bin/yida.js +113 -18
  3. package/docs/capabilities.md +202 -0
  4. package/lib/agent-center/agent-center.js +2 -3
  5. package/lib/app/app-list.js +3 -4
  6. package/lib/app/build-page.js +13 -6
  7. package/lib/app/check-page.js +16 -6
  8. package/lib/app/compile.js +17 -7
  9. package/lib/app/create-app.js +14 -2
  10. package/lib/app/create-form/api-path.js +25 -0
  11. package/lib/app/create-form/args.js +13 -0
  12. package/lib/app/create-form/commands.js +4 -0
  13. package/lib/app/create-form/definition-reader.js +23 -23
  14. package/lib/app/create-form/field-normalizers.js +26 -7
  15. package/lib/app/create-form/schema-patch.js +4 -1
  16. package/lib/app/create-form.js +155 -68
  17. package/lib/app/create-page.js +47 -10
  18. package/lib/app/generate-page.js +20 -1
  19. package/lib/app/get-form-config.js +2 -1
  20. package/lib/app/get-schema.js +67 -15
  21. package/lib/app/import-app.js +94 -42
  22. package/lib/app/list-forms.js +4 -8
  23. package/lib/app/page-ir.js +140 -0
  24. package/lib/app/publish.js +289 -556
  25. package/lib/app/schema-field-resolution.js +370 -0
  26. package/lib/app/services/app-reader.js +85 -0
  27. package/lib/app/services/app-service.js +235 -0
  28. package/lib/app/services/canvas-page-compiler.js +91 -0
  29. package/lib/app/services/canvas-page-schema-builder.js +149 -0
  30. package/lib/app/services/field-bindings.js +68 -0
  31. package/lib/app/services/form-compiler.js +1922 -0
  32. package/lib/app/services/form-mode-service.js +182 -0
  33. package/lib/app/services/form-schema-patcher.js +333 -0
  34. package/lib/app/services/form-schema-reader.js +59 -0
  35. package/lib/app/services/form-service.js +379 -0
  36. package/lib/app/services/form-validation.js +562 -0
  37. package/lib/app/services/native-page-compiler.js +92 -0
  38. package/lib/app/services/native-page-schema-builder.js +428 -0
  39. package/lib/app/services/page-resource-service.js +185 -0
  40. package/lib/app/update-app.js +22 -9
  41. package/lib/app/update-form-config.js +4 -2
  42. package/lib/asset/asset-cmd.js +19 -6
  43. package/lib/auth/oauth-loopback.js +376 -15
  44. package/lib/auth/token-auth.js +34 -3
  45. package/lib/auth/token-store.js +22 -4
  46. package/lib/basic-info/basic-info.js +2 -1
  47. package/lib/bridge/bridge.js +43 -18
  48. package/lib/core/agent-capabilities.js +5 -3
  49. package/lib/core/command-errors.js +38 -0
  50. package/lib/core/command-manifest.js +59 -1
  51. package/lib/core/copy.js +12 -0
  52. package/lib/core/doctor.js +15 -4
  53. package/lib/core/env-cmd.js +1 -1
  54. package/lib/core/env.js +7 -6
  55. package/lib/core/i18n.js +66 -8
  56. package/lib/core/legacy-schema-guard.js +408 -0
  57. package/lib/core/locales/en.js +38 -46
  58. package/lib/core/locales/zh.js +30 -46
  59. package/lib/core/sample.js +12 -2
  60. package/lib/core/utils.js +334 -66
  61. package/lib/core/yida-client.js +108 -5
  62. package/lib/corp-efficiency/corp-efficiency.js +2 -2
  63. package/lib/corp-manager/corp-manager.js +3 -3
  64. package/lib/formula/evaluate.js +11 -68
  65. package/lib/formula/field-refs.js +83 -0
  66. package/lib/i18n-management/i18n-management.js +2 -3
  67. package/lib/process/configure-process.js +44 -1992
  68. package/lib/process/create-process.js +54 -87
  69. package/lib/process/preview-process.js +20 -8
  70. package/lib/process/services/process-compiler.js +2056 -0
  71. package/lib/process/services/process-reader.js +232 -0
  72. package/lib/process/services/process-resource-service.js +571 -0
  73. package/lib/process/services/process-service.js +122 -0
  74. package/lib/process/services/process-stage-checkpoint.js +246 -0
  75. package/lib/report/append.js +7 -26
  76. package/lib/report/http.js +31 -5
  77. package/lib/report/index.js +10 -2
  78. package/lib/samples/yida-canvas-custom-page/business-list.canvas.jsx +63 -14
  79. package/lib/samples/yida-canvas-custom-page/dashboard-overview.canvas.jsx +176 -32
  80. package/lib/samples/yida-canvas-custom-page/dashboard-starter.canvas.jsx +5 -2
  81. package/lib/samples/yida-canvas-custom-page/data-management.canvas.jsx +228 -14
  82. package/lib/samples/yida-canvas-custom-page/portal-shell-home.canvas.jsx +2 -2
  83. package/lib/samples/yida-canvas-custom-page/split-pane-detail.canvas.jsx +206 -10
  84. package/lib/samples/yida-custom-page/data-management.oyd.jsx +14 -3
  85. package/lib/samples/yida-custom-page/design-tokens.js +2 -2
  86. package/lib/samples/yida-custom-page/detail-profile.oyd.jsx +1 -1
  87. package/lib/samples/yida-custom-page/portal-shell-home.oyd.jsx +7 -2
  88. package/lib/samples/yida-custom-page/split-pane-detail.oyd.jsx +2 -1
  89. package/lib/schema/adapters/app-adapter.js +210 -0
  90. package/lib/schema/adapters/form-adapter.js +1362 -0
  91. package/lib/schema/adapters/keys.js +19 -0
  92. package/lib/schema/adapters/page-adapter.js +566 -0
  93. package/lib/schema/adapters/process-adapter.js +519 -0
  94. package/lib/schema/applier.js +1878 -0
  95. package/lib/schema/apply-store.js +1083 -0
  96. package/lib/schema/command.js +645 -0
  97. package/lib/schema/dependency-graph.js +83 -0
  98. package/lib/schema/errors.js +481 -0
  99. package/lib/schema/hash.js +9 -0
  100. package/lib/schema/manifest-limits.js +177 -0
  101. package/lib/schema/manifest-loader.js +309 -0
  102. package/lib/schema/manifest-schema-v1.json +193 -0
  103. package/lib/schema/normalize-manifest.js +281 -0
  104. package/lib/schema/page-canvas-foundation.js +447 -0
  105. package/lib/schema/page-data-source-builder.js +561 -0
  106. package/lib/schema/page-foundation.js +556 -0
  107. package/lib/schema/page-source-loader.js +314 -0
  108. package/lib/schema/planner.js +691 -0
  109. package/lib/schema/remote-dispatch-boundary.js +39 -0
  110. package/lib/schema/remote-missing.js +21 -0
  111. package/lib/schema/remote-reader.js +189 -0
  112. package/lib/schema/resource-registry.js +157 -0
  113. package/lib/schema/server-revision.js +71 -0
  114. package/lib/schema/sort.js +28 -0
  115. package/lib/schema/state-store.js +589 -0
  116. package/package.json +10 -8
  117. package/scripts/check-release-risks.js +254 -0
  118. package/scripts/generate-command-docs.js +6 -1
  119. package/scripts/i18n-baseline.json +11 -11
  120. package/scripts/postinstall.js +8 -12
  121. package/scripts/validate-ci.sh +15 -11
  122. package/scripts/validate-i18n.js +30 -9
  123. package/scripts/validate-package-size.js +5 -2
  124. package/scripts/validate-skills.js +4 -0
  125. package/scripts/validate-structure.js +15 -0
  126. package/yida-skills/SKILL.md +117 -30
  127. package/yida-skills/references/schema-as-code-phase1.md +99 -0
  128. package/yida-skills/references/setup-and-env.md +59 -59
  129. package/yida-skills/references/task-retrospective.md +6 -6
  130. package/yida-skills/skills/yida-app/SKILL.md +78 -17
  131. package/yida-skills/skills/yida-canvas-custom-page/SKILL.md +18 -7
  132. package/yida-skills/skills/yida-canvas-custom-page/references/data-bridge-guide.md +3 -3
  133. package/yida-skills/skills/yida-canvas-custom-page/references/page-generation-guide.md +15 -0
  134. package/yida-skills/skills/yida-create-app/SKILL.md +21 -3
  135. package/yida-skills/skills/yida-create-form-page/SKILL.md +19 -1
  136. package/yida-skills/skills/yida-create-page/SKILL.md +16 -3
  137. package/yida-skills/skills/yida-create-process/SKILL.md +100 -4
  138. package/yida-skills/skills/yida-custom-page/SKILL.md +19 -4
  139. package/yida-skills/skills/yida-dashboard/SKILL.md +1 -1
  140. package/yida-skills/skills/yida-dashboard/references/pitfalls.md +1 -1
  141. package/yida-skills/skills/yida-get-schema/SKILL.md +49 -5
  142. package/yida-skills/skills/yida-i18n/SKILL.md +1 -1
  143. package/yida-skills/skills/yida-integration/SKILL.md +7 -1
  144. package/yida-skills/skills/yida-login/SKILL.md +50 -99
  145. package/yida-skills/skills/yida-page-config/SKILL.md +7 -1
  146. package/yida-skills/skills/yida-page-uiux/references/app/blueprint.md +0 -1
  147. package/yida-skills/skills/yida-page-uiux/references/app/navigation-patterns.md +0 -1
  148. package/yida-skills/skills/yida-page-uiux/references/app/role-journey.md +0 -1
  149. package/yida-skills/skills/yida-page-uiux/references/dashboard/app-blueprint.md +0 -1
  150. package/yida-skills/skills/yida-page-uiux/references/dashboard/chart-contracts.md +0 -1
  151. package/yida-skills/skills/yida-page-uiux/references/dashboard/component-contracts.md +0 -1
  152. package/yida-skills/skills/yida-page-uiux/references/dashboard/layout-patterns.md +0 -1
  153. package/yida-skills/skills/yida-page-uiux/references/dashboard/theme-recipes.md +0 -1
  154. package/yida-skills/skills/yida-page-uiux/references/detail/object-narrative.md +0 -1
  155. package/yida-skills/skills/yida-page-uiux/references/detail/section-patterns.md +0 -1
  156. package/yida-skills/skills/yida-page-uiux/references/detail/timeline-and-related.md +0 -1
  157. package/yida-skills/skills/yida-page-uiux/references/landing/assets-workflow.md +0 -1
  158. package/yida-skills/skills/yida-page-uiux/references/landing/industry-playbooks.md +0 -1
  159. package/yida-skills/skills/yida-page-uiux/references/landing/research-levels.md +0 -1
  160. package/yida-skills/skills/yida-page-uiux/references/landing/section-patterns.md +0 -1
  161. package/yida-skills/skills/yida-page-uiux/references/list/drawer-detail.md +0 -1
  162. package/yida-skills/skills/yida-page-uiux/references/list/empty-loading-error.md +0 -1
  163. package/yida-skills/skills/yida-page-uiux/references/list/filter-patterns.md +0 -1
  164. package/yida-skills/skills/yida-page-uiux/references/list/table-patterns.md +0 -1
  165. package/yida-skills/skills/yida-page-uiux/references/scenes/screen.md +0 -1
  166. package/yida-skills/skills/yida-page-uiux/references/workbench/entry-patterns.md +0 -1
  167. package/yida-skills/skills/yida-page-uiux/references/workbench/portal-layouts.md +0 -1
  168. package/yida-skills/skills/yida-page-uiux/references/workbench/task-feed.md +0 -1
  169. package/yida-skills/skills/yida-page-uiux/workflow/output-decision-block.md +1 -1
  170. package/yida-skills/skills/yida-process-rule/SKILL.md +35 -1
  171. package/yida-skills/skills/yida-publish-page/SKILL.md +46 -4
  172. package/yida-skills/skills/yida-report/SKILL.md +9 -235
  173. package/yida-skills/skills/yida-report/references/schema-builder-details.md +239 -0
  174. package/yida-skills/skills/yida-skill-evaluator/SKILL.md +170 -0
  175. package/yida-skills/skills-index.json +238 -54
  176. package/lib/core/locales/ar.js +0 -834
  177. package/lib/core/locales/de.js +0 -834
  178. package/lib/core/locales/es.js +0 -834
  179. package/lib/core/locales/fr.js +0 -834
  180. package/lib/core/locales/hi.js +0 -834
  181. package/lib/core/locales/ja.js +0 -1227
  182. package/lib/core/locales/ko.js +0 -836
  183. package/lib/core/locales/pt.js +0 -834
  184. package/lib/core/locales/vi.js +0 -834
  185. package/lib/core/locales/zh-HK.js +0 -1263
  186. package/project/pages/src/demo-birthday-game.oyd.jsx +0 -832
  187. package/project/pages/src/demo-chip-insight.oyd.jsx +0 -983
  188. package/project/pages/src/demo-compat-smoke.oyd.jsx +0 -58
  189. package/project/pages/src/demo-crm-batch-entry.oyd.jsx +0 -805
  190. package/project/pages/src/demo-crm-dashboard.oyd.jsx +0 -677
  191. package/project/pages/src/demo-future-vision-2026.oyd.jsx +0 -1102
  192. package/project/pages/src/demo-ppt.oyd.jsx +0 -1192
  193. package/project/pages/src/demo-salary-calculator.oyd.jsx +0 -904
  194. package/project/pages/src/openyida-knowledge-doc.oyd.jsx +0 -1714
  195. package/yida-skills/skills/large-file-write/SKILL.md +0 -93
  196. package/yida-skills/skills/large-file-write/references/write-patterns.md +0 -147
  197. package/yida-skills/skills/large-file-write/scripts/write.js +0 -157
  198. package/yida-skills/skills/sls-log-workbench/SKILL.md +0 -134
  199. package/yida-skills/skills/sls-log-workbench/references/workbench-reference.md +0 -130
  200. package/yida-skills/skills/sls-log-workbench/sls-query.js +0 -295
@@ -2,6 +2,7 @@
2
2
  * get-schema.js - 宜搭表单 Schema 获取命令
3
3
  *
4
4
  * 用法:
5
+ * openyida get-schema <appType> <formUuid> [--compact] [--resolve-fields <labelOrFieldId,...>]
5
6
  * openyida get-schema <appType> <formUuid> [--summary-json|--field-map-json]
6
7
  * openyida get-schema <appType> --all [--summary-json] [--output-dir <dir>] [--concurrency N] [--retries N]
7
8
  */
@@ -18,7 +19,9 @@ const {
18
19
  requestWithAutoLogin,
19
20
  } = require('../core/utils');
20
21
  const { t } = require('../core/i18n');
22
+ const { throwCommandError } = require('../core/command-errors');
21
23
  const { fetchFormPageList } = require('./form-navigation');
24
+ const { buildFieldResolution } = require('./schema-field-resolution');
22
25
 
23
26
  // 需要在报表 fieldCode 中加 _value 后缀的字段类型
24
27
  const FIELD_TYPES_NEEDING_VALUE_SUFFIX = new Set([
@@ -201,8 +204,9 @@ function parseArgs(args) {
201
204
  concurrency: 3,
202
205
  retries: 1,
203
206
  keyword: '',
204
- // --field <labelOrFieldId>:单字段过滤模式,命中时跳过 runBatch 分支,
205
- // 仅返回目标字段的 {componentName, fieldId, label, props}
207
+ compact: false,
208
+ fields: [],
209
+ // 保留已有单字段参数,作为 --fields 的单值兼容别名。
206
210
  field: '',
207
211
  json: false,
208
212
  summaryJson: false,
@@ -224,7 +228,15 @@ function parseArgs(args) {
224
228
  } else if (arg === '--keyword' && args[index + 1]) {
225
229
  parsed.keyword = args[index + 1];
226
230
  index++;
227
- } else if (arg === '--field' && args[index + 1]) {
231
+ } else if (arg === '--compact') {
232
+ parsed.compact = true;
233
+ } else if (arg === '--resolve-fields') {
234
+ parsed.compact = true;
235
+ if (args[index + 1] && !args[index + 1].startsWith('--')) {
236
+ appendFieldQueries(parsed.fields, args[index + 1]);
237
+ index++;
238
+ }
239
+ } else if (arg === '--field' && args[index + 1] && !args[index + 1].startsWith('--')) {
228
240
  parsed.field = args[index + 1];
229
241
  index++;
230
242
  } else if (arg === '--json') {
@@ -239,15 +251,27 @@ function parseArgs(args) {
239
251
  return parsed;
240
252
  }
241
253
 
254
+ function appendFieldQueries(target, value) {
255
+ String(value || '')
256
+ .split(/[,,]/)
257
+ .map(item => item.trim())
258
+ .filter(Boolean)
259
+ .forEach((item) => {
260
+ if (!target.includes(item)) {
261
+ target.push(item);
262
+ }
263
+ });
264
+ }
265
+
242
266
  function ensureUsage(parsed) {
243
267
  if (!parsed.appType || (!parsed.all && !parsed.formUuid)) {
244
268
  const { error: chalkError } = require('../core/chalk');
245
269
  chalkError(t('get_schema.usage'), { hint: t('get_schema.example') });
246
270
  }
247
- // --field 模式必须同时提供 formUuid(单字段过滤不支持 --all 批量)
248
- if (parsed.field && parsed.all) {
271
+ // contract v1 resource 对应单个 form,不与 --all 批量模式混用。
272
+ if ((parsed.compact || parsed.field) && parsed.all) {
249
273
  const { error: chalkError } = require('../core/chalk');
250
- chalkError('--field 不支持与 --all 同时使用,请改为单 form 模式');
274
+ chalkError(t('get_schema.usage'), { hint: t('get_schema.example') });
251
275
  }
252
276
  }
253
277
 
@@ -502,21 +526,37 @@ async function runSingle(parsed, authRef) {
502
526
  banner(t('get_schema.title'));
503
527
  label('App', parsed.appType);
504
528
  label('Form', parsed.formUuid);
505
- if (parsed.field) {label('Field', parsed.field);}
529
+ if (parsed.field) {
530
+ label('Field', parsed.field);
531
+ } else if (parsed.fields.length > 0) {
532
+ label('Fields', parsed.fields.join(', '));
533
+ }
506
534
 
507
535
  // Step 2: 获取表单 Schema
508
536
  step(2, t('get_schema.step_get'));
509
537
  info(t('get_schema.sending'));
510
538
 
511
- const result = await fetchSchema(parsed.appType, parsed.formUuid, authRef);
539
+ let result;
540
+ try {
541
+ result = await fetchSchema(parsed.appType, parsed.formUuid, authRef);
542
+ } catch (error) {
543
+ if (!parsed.compact) {
544
+ throw error;
545
+ }
546
+ chalkResult(false, t('get_schema.failed', t('common.request_failed')));
547
+ process.exit(1);
548
+ return;
549
+ }
512
550
 
513
551
  if (!isSuccessfulSchemaResult(result)) {
514
- const errorMsg = result ? result.errorMsg || t('common.unknown_error') : t('common.request_failed');
552
+ const errorMsg = parsed.compact
553
+ ? t('common.request_failed')
554
+ : result ? result.errorMsg || t('common.unknown_error') : t('common.request_failed');
515
555
  chalkResult(false, t('get_schema.failed', errorMsg));
516
- process.exit(1);
556
+ throwCommandError(errorMsg);
517
557
  }
518
558
 
519
- // --field 模式:只返回单个字段的详细 props(含 SelectField.dataSource 等)
559
+ // 保留既有 --field 返回结构;新 contract 仅由 --compact / --resolve-fields 触发。
520
560
  if (parsed.field) {
521
561
  const allFieldNodes = collectFieldNodes(result);
522
562
  const aliasMaps = buildComponentAliasMaps(result);
@@ -530,18 +570,29 @@ async function runSingle(parsed, authRef) {
530
570
  }
531
571
  const props = matched.props || {};
532
572
  const labelRaw = props.label;
533
- const label = labelRaw
573
+ const fieldLabel = labelRaw
534
574
  ? (typeof labelRaw === 'object' ? (labelRaw.zh_CN || labelRaw.en_US || '') : String(labelRaw))
535
575
  : '';
536
- const compact = {
576
+ const legacyField = {
537
577
  componentName: matched.componentName,
538
578
  fieldId: props.fieldId || '',
539
579
  alias: aliasMaps.aliasByFieldId[props.fieldId] || '',
540
- label,
580
+ label: fieldLabel,
541
581
  props,
542
582
  };
543
583
  chalkSuccess(t('get_schema.success'));
544
- console.log(JSON.stringify(compact, null, 2));
584
+ console.log(JSON.stringify(legacyField, null, 2));
585
+ return;
586
+ }
587
+
588
+ if (parsed.compact) {
589
+ chalkSuccess(t('get_schema.success'));
590
+ console.log(JSON.stringify(buildFieldResolution(
591
+ parsed.appType,
592
+ parsed.formUuid,
593
+ result,
594
+ parsed.fields
595
+ ), null, 2));
545
596
  return;
546
597
  }
547
598
 
@@ -630,6 +681,7 @@ module.exports = {
630
681
  buildSchemaSummary,
631
682
  buildComponentAliasMaps,
632
683
  parseArgs,
684
+ appendFieldQueries,
633
685
  filterForms,
634
686
  mapLimit,
635
687
  fetchSchemaRecord,
@@ -15,31 +15,37 @@ const {
15
15
  httpPost,
16
16
  requestWithAutoLogin,
17
17
  } = require('../core/utils');
18
- const { createAuthRef } = require('../core/yida-client');
18
+ const { createAuthRef, isTokenAuthRef } = require('../core/yida-client');
19
19
  const { t } = require('../core/i18n');
20
20
  const { buildYidaI18n, resolveContentLocale } = require('../core/yida-i18n');
21
21
  const { banner, step, label, success, fail, warn, info, error, result, usage, hint } = require('../core/chalk');
22
+ const { throwUsage } = require('../core/command-errors');
23
+ const { getSchemaServerRevision } = require('../schema/server-revision');
24
+ const {
25
+ assertLegacyDirectWriteAllowed,
26
+ extractLegacyGuardArgs,
27
+ } = require('../core/legacy-schema-guard');
22
28
 
23
29
  // ── 创建新应用 ────────────────────────────────────────
24
30
 
25
31
  async function createApp(appName, authRef) {
26
32
  const contentLocale = resolveContentLocale({ baseUrl: authRef.baseUrl });
27
- const postData = querystring.stringify({
28
- appName: JSON.stringify(buildYidaI18n(appName, { en_US: appName, ja_JP: appName })),
29
- description: JSON.stringify(buildYidaI18n(appName, { en_US: appName, ja_JP: appName })),
30
- icon: 'xian-yingyong%%#0089FF',
31
- iconUrl: 'xian-yingyong%%#0089FF',
32
- colour: 'blue',
33
- defaultLanguage: contentLocale,
34
- openExclusive: 'n',
35
- openPhysicColumn: 'n',
36
- openIsolationDatabase: 'n',
37
- openExclusiveUnit: 'n',
38
- group: 'ALL',
39
- });
40
-
41
33
  const result = await requestWithAutoLogin((auth) => {
42
- return httpPost(auth.baseUrl, '/query/app/registerApp.json', postData);
34
+ const postData = querystring.stringify({
35
+ _csrf_token: auth.csrfToken,
36
+ appName: JSON.stringify(buildYidaI18n(appName, { en_US: appName, ja_JP: appName })),
37
+ description: JSON.stringify(buildYidaI18n(appName, { en_US: appName, ja_JP: appName })),
38
+ icon: 'xian-yingyong%%#0089FF',
39
+ iconUrl: 'xian-yingyong%%#0089FF',
40
+ colour: 'blue',
41
+ defaultLanguage: contentLocale,
42
+ openExclusive: 'n',
43
+ openPhysicColumn: 'n',
44
+ openIsolationDatabase: 'n',
45
+ openExclusiveUnit: 'n',
46
+ group: 'ALL',
47
+ });
48
+ return httpPost(auth.baseUrl, '/query/app/registerApp.json', postData, auth.cookies);
43
49
  }, authRef);
44
50
 
45
51
  if (!result || !result.success || !result.content) {
@@ -57,22 +63,30 @@ function normalizeImportFormType(formType) {
57
63
  return normalized;
58
64
  }
59
65
 
60
- function buildCreateFormPostData(formTitle, formType = 'receipt') {
61
- return querystring.stringify({
66
+ function buildCreateFormPayload(formTitle, formType = 'receipt') {
67
+ return {
62
68
  formType: normalizeImportFormType(formType),
63
69
  title: JSON.stringify(buildYidaI18n(formTitle, { en_US: formTitle, ja_JP: formTitle })),
64
- });
70
+ };
71
+ }
72
+
73
+ function buildCreateFormPostData(formTitle, formType = 'receipt') {
74
+ return querystring.stringify(buildCreateFormPayload(formTitle, formType));
65
75
  }
66
76
 
67
77
  // ── 创建空白表单/页面/报表 ──────────────────────────────
68
78
 
69
79
  async function createBlankForm(appType, formTitle, authRef, formType = 'receipt') {
70
- const postData = buildCreateFormPostData(formTitle, formType);
71
80
  const result = await requestWithAutoLogin((auth) => {
81
+ const postData = querystring.stringify({
82
+ _csrf_token: auth.csrfToken,
83
+ ...buildCreateFormPayload(formTitle, formType),
84
+ });
72
85
  return httpPost(
73
86
  auth.baseUrl,
74
87
  `/dingtalk/web/${appType}/query/formdesign/saveFormSchemaInfo.json`,
75
- postData
88
+ postData,
89
+ auth.cookies
76
90
  );
77
91
  }, authRef);
78
92
 
@@ -86,44 +100,72 @@ async function createBlankForm(appType, formTitle, authRef, formType = 'receipt'
86
100
 
87
101
  // ── 保存表单 Schema ───────────────────────────────────
88
102
 
89
- async function saveFormSchema(appType, formUuid, schema, authRef, formType = 'receipt') {
103
+ async function saveFormSchema(appType, formUuid, schema, authRef, formType = 'receipt', serverRevision) {
104
+ const tokenReady = isTokenAuthRef(authRef);
105
+ const cookieReady = !!(
106
+ authRef &&
107
+ typeof authRef.csrfToken === 'string' &&
108
+ authRef.csrfToken &&
109
+ Array.isArray(authRef.cookies)
110
+ );
111
+ if (!authRef || !authRef.baseUrl || (!tokenReady && !cookieReady)) {
112
+ const err = new Error('Import schema save requires an authenticated request context.');
113
+ err.code = 'IMPORT_SCHEMA_WRITE_PRECHECK_FAILED';
114
+ throw err;
115
+ }
116
+ if (!schema || typeof schema !== 'object' || !Array.isArray(schema.pages)) {
117
+ const err = new Error('Imported schema content must include a pages array before writing.');
118
+ err.code = 'SCHEMA_REMOTE_READ_FAILED';
119
+ throw err;
120
+ }
121
+ if (!Number.isFinite(serverRevision) || serverRevision < 0) {
122
+ const err = new Error('Imported schema revision is missing or invalid.');
123
+ err.code = 'SCHEMA_REMOTE_READ_FAILED';
124
+ throw err;
125
+ }
126
+
90
127
  const payload = {
91
128
  appType,
92
129
  formUuid,
93
130
  content: JSON.stringify(schema),
94
131
  schemaVersion: 'V5',
132
+ gmtModified: serverRevision,
95
133
  };
96
134
  if (normalizeImportFormType(formType) === 'report') {
97
135
  payload.importSchema = 'true';
98
136
  }
99
- const postData = querystring.stringify(payload);
100
-
101
- const result = await requestWithAutoLogin((auth) => {
102
- return httpPost(
103
- auth.baseUrl,
104
- `/alibaba/web/${appType}/_view/query/formdesign/saveFormSchema.json`,
105
- postData
106
- );
107
- }, authRef);
108
137
 
109
- return result;
138
+ const postData = querystring.stringify(Object.assign(
139
+ {},
140
+ authRef.csrfToken ? { _csrf_token: authRef.csrfToken } : {},
141
+ payload
142
+ ));
143
+ const authOptions = { projectRoot: authRef.projectRoot };
144
+ return httpPost(
145
+ authRef.baseUrl,
146
+ `/alibaba/web/${appType}/_view/query/formdesign/saveFormSchema.json`,
147
+ postData,
148
+ Array.isArray(authRef.cookies) ? authRef.cookies : authOptions,
149
+ Array.isArray(authRef.cookies) ? authOptions : undefined
150
+ );
110
151
  }
111
152
 
112
153
  // ── 更新表单配置(发布表单)─────────────────────────────
113
154
 
114
155
  async function updateFormConfig(appType, formUuid, authRef) {
115
- const postData = querystring.stringify({
116
- appType,
117
- formUuid,
118
- setting: JSON.stringify({ MINI_RESOURCE: 0 }),
119
- version: 1,
120
- });
121
-
122
156
  const result = await requestWithAutoLogin((auth) => {
157
+ const postData = querystring.stringify({
158
+ _csrf_token: auth.csrfToken,
159
+ appType,
160
+ formUuid,
161
+ setting: JSON.stringify({ MINI_RESOURCE: 0 }),
162
+ version: 1,
163
+ });
123
164
  return httpPost(
124
165
  auth.baseUrl,
125
166
  `/dingtalk/web/${appType}/query/formdesign/updateFormConfig.json`,
126
- postData
167
+ postData,
168
+ auth.cookies
127
169
  );
128
170
  }, authRef);
129
171
 
@@ -177,11 +219,13 @@ function extractSchemaContent(schemaResult) {
177
219
  // ── 主逻辑 ────────────────────────────────────────────
178
220
 
179
221
  async function run(args) {
222
+ const guardSplit = extractLegacyGuardArgs(args || []);
223
+ args = guardSplit.args;
180
224
  if (args.length < 1) {
181
225
  usage(t('import.usage'));
182
226
  hint(t('import.example1'));
183
227
  hint(t('import.example2'));
184
- process.exit(1);
228
+ throwUsage(t('import.usage'), `${t('import.example1')}\n${t('import.example2')}`);
185
229
  }
186
230
 
187
231
  const exportFilePath = path.resolve(args[0]);
@@ -215,6 +259,11 @@ async function run(args) {
215
259
  step(2, t('common.step_login_label'));
216
260
  const authRef = createAuthRef();
217
261
  success(t('common.login_ready', authRef.baseUrl));
262
+ assertLegacyDirectWriteAllowed({
263
+ command: 'import-app',
264
+ resourceType: 'app',
265
+ action: 'create',
266
+ }, { guardOptions: guardSplit.guardOptions });
218
267
 
219
268
  step(3, t('import.step_create_app'));
220
269
  let newAppType;
@@ -287,9 +336,10 @@ async function run(args) {
287
336
  oldFormUuid,
288
337
  newFormUuid
289
338
  );
339
+ const serverRevision = getSchemaServerRevision(formSchemaResult);
290
340
 
291
341
  // 4.3 保存 Schema
292
- const saveResult = await saveFormSchema(newAppType, newFormUuid, adaptedSchema, authRef, formType);
342
+ const saveResult = await saveFormSchema(newAppType, newFormUuid, adaptedSchema, authRef, formType, serverRevision);
293
343
  if (!saveResult || !saveResult.success) {
294
344
  const errorMsg = saveResult ? saveResult.errorMsg || t('common.unknown_error') : t('common.request_failed');
295
345
  fail(t('import.save_schema_failed', errorMsg));
@@ -368,7 +418,9 @@ module.exports = {
368
418
  run,
369
419
  __test__: {
370
420
  normalizeImportFormType,
421
+ buildCreateFormPayload,
371
422
  buildCreateFormPostData,
423
+ saveFormSchema,
372
424
  adaptSchemaIdentifiers,
373
425
  extractSchemaContent,
374
426
  },
@@ -3,6 +3,7 @@
3
3
  const { createAuthRef, isAuthRefReady } = require('../core/yida-client');
4
4
  const { t } = require('../core/i18n');
5
5
  const { fetchFormPageList } = require('./form-navigation');
6
+ const { throwCommandError, throwUsage } = require('../core/command-errors');
6
7
 
7
8
  function parseArgs(args) {
8
9
  const parsed = {
@@ -40,10 +41,7 @@ function filterForms(forms, keyword) {
40
41
  async function run(args) {
41
42
  const parsed = parseArgs(args);
42
43
  if (!parsed.appType) {
43
- console.error(t('list_forms.usage'));
44
- console.error(t('list_forms.example1'));
45
- console.error(t('list_forms.example2'));
46
- process.exit(1);
44
+ throwUsage(t('list_forms.usage'), `${t('list_forms.example1')}\n${t('list_forms.example2')}`);
47
45
  }
48
46
 
49
47
  const SEP = '='.repeat(50);
@@ -58,8 +56,7 @@ async function run(args) {
58
56
  console.error(t('common.step_login', 1));
59
57
  const authRef = createAuthRef();
60
58
  if (!isAuthRefReady(authRef)) {
61
- console.error(t('list_forms.no_login'));
62
- process.exit(1);
59
+ throwCommandError(t('list_forms.no_login'), { code: 'CLI_AUTH_REQUIRED' });
63
60
  }
64
61
  console.error(t('common.login_ready', authRef.baseUrl));
65
62
 
@@ -68,8 +65,7 @@ async function run(args) {
68
65
  try {
69
66
  forms = await fetchFormPageList(parsed.appType, authRef);
70
67
  } catch (error) {
71
- console.error(t('list_forms.failed', error.message));
72
- process.exit(1);
68
+ throwCommandError(t('list_forms.failed', error.message));
73
69
  }
74
70
 
75
71
  const filteredForms = filterForms(forms, parsed.keyword);
@@ -1168,6 +1168,137 @@ function normalizeMediaAssets(spec, defaults) {
1168
1168
  };
1169
1169
  }
1170
1170
 
1171
+ function hasValue(value) {
1172
+ if (Array.isArray(value)) {
1173
+ return value.some(hasValue);
1174
+ }
1175
+ if (isPlainObject(value)) {
1176
+ return Object.keys(value).some((key) => hasValue(value[key]));
1177
+ }
1178
+ return value !== undefined && value !== null && String(value).trim() !== '';
1179
+ }
1180
+
1181
+ function hasBlockItems(block) {
1182
+ return Boolean(block && Array.isArray(block.items) && block.items.some(hasValue));
1183
+ }
1184
+
1185
+ function isSameJson(a, b) {
1186
+ return JSON.stringify(a) === JSON.stringify(b);
1187
+ }
1188
+
1189
+ function normalizeDomainSpecificity(rawSpec, options, blocks, defaults, normalized, scene) {
1190
+ const spec = isPlainObject(rawSpec) ? rawSpec : {};
1191
+ const optionInputs = isPlainObject(options) ? options : {};
1192
+ const heroBlock = blocks.heroBlock || {};
1193
+ const featureBlock = blocks.featureBlock || {};
1194
+ const metricBlock = blocks.metricBlock || {};
1195
+ const roadmapBlock = blocks.roadmapBlock || {};
1196
+ const ctaBlock = blocks.ctaBlock || {};
1197
+ const sampleFallbacks = [];
1198
+ const missing = [];
1199
+
1200
+ const hasDomainText = hasValue([
1201
+ spec.requirement,
1202
+ spec.requirements,
1203
+ spec.prompt,
1204
+ spec.goal,
1205
+ spec.industry,
1206
+ spec.businessType,
1207
+ spec.business_type,
1208
+ spec.name,
1209
+ spec.title,
1210
+ spec.brandName,
1211
+ spec.appName,
1212
+ spec.appBlueprint,
1213
+ spec.app_blueprint,
1214
+ ]);
1215
+ const provided = {
1216
+ hero: hasValue(spec.brandName || spec.tagline || spec.heroText || heroBlock.brandName || heroBlock.tagline || heroBlock.text || heroBlock.heroText),
1217
+ features: hasValue(spec.features) || hasBlockItems(featureBlock),
1218
+ metrics: hasValue(spec.metrics) || hasBlockItems(metricBlock),
1219
+ roadmap: hasValue(spec.roadmap) || hasBlockItems(roadmapBlock),
1220
+ cta: hasValue(spec.ctaTitle || spec.ctaText || ctaBlock.title || ctaBlock.text),
1221
+ visualProfile: hasValue(optionInputs.visualProfile || spec.visualProfile || spec.visual_profile || spec.visual),
1222
+ appBlueprint: hasValue(optionInputs.appBlueprint || spec.appBlueprint || spec.app_blueprint || spec.pages || spec.roles || spec.navigation || spec.navGroups),
1223
+ interactionProfile: hasValue(optionInputs.interactionProfile || spec.interactionProfile || spec.interaction_profile || spec.interactions || spec.primaryAction || spec.detailMode || spec.bulkActions),
1224
+ insights: hasValue(optionInputs.insights || spec.insights || spec.insight),
1225
+ dataBinding: hasValue(optionInputs.dataBinding || spec.dataBinding || spec.data_binding || spec.formUuid || spec.form_uuid || spec.dataUrl || spec.data_url),
1226
+ assets: hasValue(spec.assets || spec.heroImage || spec.hero_image || spec.productImages || spec.images),
1227
+ };
1228
+
1229
+ if (normalized.meta.brandName === defaults.meta.brandName) {
1230
+ sampleFallbacks.push('brandName');
1231
+ }
1232
+ if (normalized.meta.tagline === defaults.meta.tagline) {
1233
+ sampleFallbacks.push('tagline');
1234
+ }
1235
+ if (normalized.meta.heroText === defaults.meta.heroText) {
1236
+ sampleFallbacks.push('heroText');
1237
+ }
1238
+ if (isSameJson(normalized.features, defaults.features)) {
1239
+ sampleFallbacks.push('features');
1240
+ }
1241
+ if (isSameJson(normalized.metrics, defaults.metrics)) {
1242
+ sampleFallbacks.push('metrics');
1243
+ }
1244
+ if (isSameJson(normalized.roadmap, defaults.roadmap)) {
1245
+ sampleFallbacks.push('roadmap');
1246
+ }
1247
+
1248
+ if (!provided.hero) {
1249
+ missing.push('页面名称、业务定位和首屏说明');
1250
+ }
1251
+ if (!provided.features) {
1252
+ missing.push('业务对象/模块,而不是模板卖点');
1253
+ }
1254
+ if (!provided.metrics && ['dashboard', 'screen', 'workbench'].includes(scene)) {
1255
+ missing.push('贴合业务的指标口径');
1256
+ }
1257
+ if (!provided.roadmap && scene !== 'landing') {
1258
+ missing.push('用户动作、处理路径或运营节奏');
1259
+ }
1260
+ if (!provided.interactionProfile && ['dashboard', 'list', 'detail', 'workbench'].includes(scene)) {
1261
+ missing.push('主操作、下钻方式、筛选/批量动作和空载错状态');
1262
+ }
1263
+ if (!provided.visualProfile) {
1264
+ missing.push('区别于 sample 的视觉方向');
1265
+ }
1266
+ if (scene === 'landing' && !provided.assets) {
1267
+ missing.push('真实素材或素材缺口说明');
1268
+ }
1269
+
1270
+ const weightedProvided = [
1271
+ provided.hero,
1272
+ provided.features,
1273
+ provided.metrics,
1274
+ provided.roadmap,
1275
+ provided.cta,
1276
+ provided.visualProfile,
1277
+ provided.appBlueprint,
1278
+ provided.interactionProfile,
1279
+ provided.insights,
1280
+ provided.dataBinding,
1281
+ provided.assets,
1282
+ ].filter(Boolean).length;
1283
+ const score = Math.round((weightedProvided / 11) * 100);
1284
+ const status = sampleFallbacks.length === 0 && missing.length <= 1
1285
+ ? 'domain-ready'
1286
+ : hasDomainText
1287
+ ? 'draft-needs-domain-spec'
1288
+ : 'sample-reference';
1289
+
1290
+ return {
1291
+ status,
1292
+ score,
1293
+ sampleFallbacks,
1294
+ missing,
1295
+ provided,
1296
+ guidance: status === 'domain-ready'
1297
+ ? 'Spec 已覆盖主要业务语义;sample 仅作为编译骨架。'
1298
+ : '补齐业务对象、指标口径、用户动作、视觉方向和素材/数据约束后再视为真实业务页;sample 只能作为可编译参考。',
1299
+ };
1300
+ }
1301
+
1171
1302
  function normalizeTodoItems(items, fallback) {
1172
1303
  const normalized = toList(items)
1173
1304
  .filter(isPlainObject)
@@ -1228,6 +1359,14 @@ function normalizeProductHomepageSpec(rawSpec, options, templateName = PRODUCT_H
1228
1359
  roadmapBlock.items || spec.roadmap,
1229
1360
  defaults.roadmap
1230
1361
  );
1362
+ const domainFidelity = normalizeDomainSpecificity(
1363
+ spec,
1364
+ options,
1365
+ { heroBlock, featureBlock, metricBlock, roadmapBlock, ctaBlock },
1366
+ defaults,
1367
+ { meta, features, metrics, roadmap },
1368
+ scene
1369
+ );
1231
1370
 
1232
1371
  return {
1233
1372
  irVersion: IR_VERSION,
@@ -1243,6 +1382,7 @@ function normalizeProductHomepageSpec(rawSpec, options, templateName = PRODUCT_H
1243
1382
  appBlueprint,
1244
1383
  interactionProfile,
1245
1384
  dataBinding,
1385
+ domainFidelity,
1246
1386
  meta,
1247
1387
  assets,
1248
1388
  insights,