openyida 2026.8.28 → 2026.8.30

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.
package/README.md CHANGED
@@ -199,6 +199,7 @@ openyida create-process APP_XXX "Purchase Request" .cache/openyida/process/field
199
199
  openyida configure-process APP_XXX FORM_XXX .cache/openyida/process/process.json
200
200
  openyida process preview APP_XXX PROC_INST_XXX --output .cache/openyida/process/process.html
201
201
  openyida data query form APP_XXX FORM_XXX --page 1 --size 20
202
+ openyida data query form APP_XXX FORM_XXX --dynamic-order '{"dateField_xxx":"-"}'
202
203
  openyida data create form APP_XXX FORM_XXX --data-file .cache/openyida/data-import/record.json
203
204
  openyida get-permission APP_XXX FORM_XXX
204
205
  ```
@@ -226,6 +227,7 @@ openyida get-permission APP_XXX FORM_XXX
226
227
  发布成功还必须精确回读同一 PUBLISHED `processId/processVersion` 的 `getProcessById` 平台视图,并验证可见节点的组件、名称、顺序和审批模式。只有输出 `verificationLevel: "PLATFORM_VIEW_VERIFIED"` 才表示平台 view 已验证;`PUBLISHED_UNVERIFIED` 表示发布可能已生效但回读不完整,不能宣称 `processJson` 已验证,也不能直接重放写请求。
227
228
 
228
229
  When creating or updating test data with `openyida data`, Yida date fields must use 13-digit millisecond timestamps, for example `"dateField_xxx": 1719705600000`. Do not submit `YYYY-MM-DD` strings for `DateField` or `CascadeDateField` values.
230
+ For deterministic query order, pass `--dynamic-order '{"fieldId":"+"}'` for ascending order or `--dynamic-order '{"fieldId":"-"}'` for descending order. Without `--dynamic-order`, `searchFormDatas` does not guarantee a stable result order; pagination, comparison, and pairing logic must not depend on the default order.
229
231
  Temporary JSON, CSV, and one-off import scripts should live under `.cache/openyida/` so generated run artifacts do not clutter the repository root.
230
232
 
231
233
  ### Real Environment E2E
@@ -305,6 +307,10 @@ OPENYIDA_E2E=1 npm run eval:e2e -- --skill yida-dashboard --screenshot --auto-sc
305
307
  # above, here the agent self-orchestrates.
306
308
  OPENYIDA_E2E=1 npm run eval:generate -- --screenshot
307
309
 
310
+ # Re-evaluate an archived real CLI trace against the latest scenario contract and
311
+ # current read-only platform state. This does not run an agent or mutate resources.
312
+ npm run eval:replay -- --report <generation-report.json> --scenario <scenario.json> --app-type APP_XXX
313
+
308
314
  # Run all three in one pass (routing + tool-pipeline baseline + real generation).
309
315
  OPENYIDA_E2E=1 npm run eval:all -- --skill yida-dashboard --screenshot
310
316
 
@@ -406,7 +412,7 @@ Run `openyida --help` or `openyida <command> --help` for detailed usage.
406
412
  | `openyida create-form add-option <appType> <formUuid> <fieldLabel> <option1> [option2] ...` | Update a form page |
407
413
  | `openyida list-forms <appType> [--keyword <text>]` | List forms/pages in an app |
408
414
  | `openyida aggregate-table <list\|create-empty\|inspect\|preview\|save\|publish\|status> <appType> ...` | Manage aggregate tables (virtualView) |
409
- | `openyida get-schema <appType> <formUuid\|--all> [--summary-json\|--field-map-json]` | Get one form Schema or all form Schemas |
415
+ | `openyida get-schema <appType> <formUuid\|--all> [--summary-json\|--field-map-json\|--analysis-json]` | Get one form Schema or all form Schemas |
410
416
  | `openyida check-prd-completeness <prd.md> --app-type <appType> [--build-manifest <file>] [--json]` | Check PRD page/resource count risk |
411
417
  | `openyida er <appType> [--format mermaid\|json] [--output file] [--include-system] [--include-pages]` | Export app entity relationship diagram |
412
418
  | `openyida create-page <appType> "<name>" [--mode dashboard] [--hide-nav] [--locale zh_CN\|en_US\|ja_JP] [--open\|--no-open]` | Create a custom display page |
@@ -424,13 +430,13 @@ Run `openyida --help` or `openyida <command> --help` for detailed usage.
424
430
 
425
431
  | Command | Description |
426
432
  |---------|-------------|
427
- | `openyida data <action> <resource> [args]` | Unified data management (form/process/task/subform) |
433
+ | `openyida data <query\|get\|create\|update\|delete> <form\|process\|subform\|tasks\|operation-records\|task> [args]` | Unified data management (form/process/task/subform) |
428
434
  | `openyida task-center <type> [options]` | Global task center (todo/processed/cc etc.) |
429
435
  | `openyida basic-info <overview\|commodity\|grant\|capacity\|quota\|abs-path\|dataflow\|i18n\|domain>` | Query organization basic info, capacity, quotas, and domain settings |
430
436
  | `openyida read-dingtalk-doc <docUrl> [--output <file>] [--json]` | Fetch Markdown content from a DingTalk document |
431
437
  | `openyida read-dingtalk-tingji <taskUuid> [--json]` | Fetch DingTalk Tingji details by task UUID |
432
- | `openyida get-permission <appType> <formUuid>` | Query form permission config |
433
- | `openyida save-permission <appType> <formUuid> ...` | Save form permission config |
438
+ | `openyida get-permission <appType> <formUuid> [--package-uuid <packageUuid>] [--json]` | Query form permission config |
439
+ | `openyida save-permission <appType> <formUuid> --package-uuid <packageUuid> [--data-permission <json>\|--action-permission <json>\|--field-permission <json>]` | Save form permission config |
434
440
  | `openyida corp-manager <search-user\|list\|add\|remove\|address-book> ...` | Manage platform admins and address book permissions |
435
441
  | `openyida agent-center <list\|create\|update\|cancel\|range\|search-user> ...` | Manage process and departure delegation |
436
442
 
@@ -485,7 +491,7 @@ Run `openyida --help` or `openyida <command> --help` for detailed usage.
485
491
  |---------|-------------|
486
492
  | `openyida integration create <appType> ... [--spec file.json]` | Create integration automation flow |
487
493
  | `openyida integration update <appType> <formUuid> <processCode> --spec <desired-spec.json> [--publish]` | Probe integration update capability (currently blocked without full readback) |
488
- | `openyida integration list <appType> [--form-uuid <uuid>] [--status y\|n] [--json]` | List integration automation flows |
494
+ | `openyida integration list <appType> [--flow-types 1,2,3,5,6] [--form-uuid <uuid>] [--status y\|n] [--json]` | List integration automation flows |
489
495
  | `openyida integration enable <appType> <formUuid> <processCode>` | Enable integration automation flow |
490
496
  | `openyida integration disable <appType> <formUuid> <processCode>` | Disable integration automation flow |
491
497
  | `openyida integration check <appType...>` | Check abnormal integration automation run logs |
package/bin/yida.js CHANGED
@@ -484,6 +484,45 @@ function printCommandUsage(...lines) {
484
484
  console.log(lines.filter(Boolean).join('\n'));
485
485
  }
486
486
 
487
+ const MANIFEST_HELP_PATHS = Object.freeze({
488
+ 'get-schema': ['get-schema'],
489
+ 'query-data': ['data'],
490
+ 'data-manage': ['data'],
491
+ data: ['data'],
492
+ report: ['report'],
493
+ 'create-process': ['create-process'],
494
+ 'create-report': ['create-report'],
495
+ 'save-share-config': ['save-share-config'],
496
+ 'verify-short-url': ['verify-short-url'],
497
+ 'integration-create': ['integration', 'create'],
498
+ 'save-permission': ['save-permission'],
499
+ 'get-permission': ['get-permission'],
500
+ });
501
+
502
+ function printManifestCommandHelp(commandName) {
503
+ const canonicalPath = MANIFEST_HELP_PATHS[commandName];
504
+ if (!canonicalPath) {
505
+ return false;
506
+ }
507
+
508
+ const manifest = buildCommandManifest({ t, version: currentVersion });
509
+ const matches = manifest.commands.filter((entry) =>
510
+ canonicalPath.every((token, index) => entry.path[index] === token)
511
+ );
512
+ const exact = matches.find((entry) => entry.path.length === canonicalPath.length);
513
+ const entries = exact ? [exact] : matches;
514
+ if (entries.length === 0) {
515
+ return false;
516
+ }
517
+
518
+ printCommandUsage(...entries.flatMap((entry) => [
519
+ entry.usage,
520
+ entry.description,
521
+ ...(entry.examples || []),
522
+ ]));
523
+ return true;
524
+ }
525
+
487
526
  function printLoginHelp() {
488
527
  printCommandUsage(t('cli.login_usage'), t('cli.login_example'));
489
528
  }
@@ -549,6 +588,10 @@ async function main() {
549
588
  return;
550
589
  }
551
590
 
591
+ if (hasHelpFlag(args) && printManifestCommandHelp(command)) {
592
+ return;
593
+ }
594
+
552
595
  switch (command) {
553
596
  case 'commands': {
554
597
  if (args[0] === 'validate' || args[0] === 'build') {
@@ -3,8 +3,8 @@
3
3
  *
4
4
  * 用法:
5
5
  * openyida get-schema <appType> <formUuid> [--compact] [--resolve-fields <labelOrFieldId,...>]
6
- * openyida get-schema <appType> <formUuid> [--summary-json|--field-map-json]
7
- * openyida get-schema <appType> --all [--summary-json] [--output-dir <dir>] [--concurrency N] [--retries N]
6
+ * openyida get-schema <appType> <formUuid> [--summary-json|--field-map-json|--analysis-json]
7
+ * openyida get-schema <appType> --all [--summary-json|--analysis-json] [--output-dir <dir>] [--concurrency N] [--retries N]
8
8
  */
9
9
 
10
10
  'use strict';
@@ -22,15 +22,27 @@ const { t } = require('../core/i18n');
22
22
  const { throwCommandError } = require('../core/command-errors');
23
23
  const { fetchFormPageList } = require('./form-navigation');
24
24
  const { buildFieldResolution } = require('./schema-field-resolution');
25
+ const { buildSemanticAnalysis } = require('./schema-semantic-analysis');
25
26
 
26
27
  // 需要在报表 fieldCode 中加 _value 后缀的字段类型
27
28
  const FIELD_TYPES_NEEDING_VALUE_SUFFIX = new Set([
28
29
  'SelectField',
30
+ 'MultiSelectField',
29
31
  'EmployeeField',
32
+ 'DepartmentSelectField',
30
33
  'RadioField',
31
34
  'CheckboxField',
32
35
  ]);
33
36
 
37
+ const FIELD_COMPONENT_NAMES = new Set([
38
+ 'TextField', 'TextareaField', 'SelectField', 'MultiSelectField',
39
+ 'DateField', 'CascadeDateField', 'NumberField', 'RadioField', 'CheckboxField',
40
+ 'EmployeeField', 'DepartmentSelectField', 'PhoneField', 'EmailField',
41
+ 'CascadeSelectField', 'CountrySelectField', 'CitySelectField', 'AddressField',
42
+ 'ImageField', 'AttachmentField', 'TableField', 'AssociationFormField',
43
+ 'RateField', 'SignatureField', 'SerialNumberField',
44
+ ]);
45
+
34
46
  /**
35
47
  * 从 Schema 中提取字段摘要,列出每个字段的真实 fieldId、组件别名和报表用 reportFieldCode。
36
48
  * @param {object} schemaResult - getFormSchema API 返回结果
@@ -44,12 +56,6 @@ function extractFieldSummary(schemaResult) {
44
56
  }
45
57
  const aliasMaps = buildComponentAliasMaps(schemaResult);
46
58
 
47
- const FIELD_COMPONENT_NAMES = new Set([
48
- 'TextField', 'TextareaField', 'SelectField', 'DateField', 'NumberField',
49
- 'RadioField', 'CheckboxField', 'EmployeeField', 'PhoneField', 'EmailField',
50
- 'CascadeSelectField', 'ImageField', 'AttachmentField', 'TableField',
51
- ]);
52
-
53
59
  function traverse(node) {
54
60
  if (!node) {
55
61
  return;
@@ -67,6 +73,15 @@ function extractFieldSummary(schemaResult) {
67
73
  if (fieldId) {
68
74
  const options = extractOptionSummary(props);
69
75
  const optionSource = getOptionSource(props) || [];
76
+ const complexDefault = props.complexValue && typeof props.complexValue === 'object'
77
+ ? props.complexValue.value
78
+ : undefined;
79
+ const defaultValue = firstPresent([complexDefault, props.defaultValue, props.value, props.default]);
80
+ const validationRules = Array.isArray(props.validation) ? props.validation : [];
81
+ const hasRequiredSignal = Object.prototype.hasOwnProperty.call(props, 'required')
82
+ || validationRules.some(rule => rule && (rule.type === 'required' || rule.required === true));
83
+ const required = props.required === true || props.required === 'true'
84
+ || validationRules.some(rule => rule && (rule.type === 'required' || rule.required === true));
70
85
  fields.push({
71
86
  label,
72
87
  componentName: node.componentName,
@@ -76,6 +91,10 @@ function extractFieldSummary(schemaResult) {
76
91
  options,
77
92
  optionCount: optionSource.length,
78
93
  optionsTruncated: optionSource.length > options.length,
94
+ ...(hasRequiredSignal
95
+ ? { required }
96
+ : {}),
97
+ ...(defaultValue !== undefined ? { defaultValue } : {}),
79
98
  });
80
99
  }
81
100
  }
@@ -321,6 +340,7 @@ function parseArgs(args) {
321
340
  field: '',
322
341
  json: false,
323
342
  summaryJson: false,
343
+ analysisJson: false,
324
344
  };
325
345
 
326
346
  for (let index = 1; index < args.length; index++) {
@@ -354,6 +374,8 @@ function parseArgs(args) {
354
374
  parsed.json = true;
355
375
  } else if (arg === '--summary-json' || arg === '--field-map-json') {
356
376
  parsed.summaryJson = true;
377
+ } else if (arg === '--analysis-json') {
378
+ parsed.analysisJson = true;
357
379
  } else if (!arg.startsWith('--') && !parsed.formUuid) {
358
380
  parsed.formUuid = arg;
359
381
  }
@@ -426,15 +448,6 @@ function collectFieldNodes(schemaResult) {
426
448
  const pages = schemaResult && schemaResult.content && schemaResult.content.pages;
427
449
  if (!pages || pages.length === 0) {return nodes;}
428
450
 
429
- const FIELD_COMPONENT_NAMES = new Set([
430
- 'TextField', 'TextareaField', 'SelectField', 'DateField', 'NumberField',
431
- 'RadioField', 'CheckboxField', 'EmployeeField', 'PhoneField', 'EmailField',
432
- 'CascadeSelectField', 'ImageField', 'AttachmentField', 'TableField',
433
- 'MultiSelectField', 'DepartmentSelectField', 'AssociationFormField',
434
- 'CountrySelectField', 'CitySelectField', 'RateField', 'SignatureField',
435
- 'SerialNumberField',
436
- ]);
437
-
438
451
  function traverse(node) {
439
452
  if (!node) {return;}
440
453
  if (FIELD_COMPONENT_NAMES.has(node.componentName)) {
@@ -486,7 +499,10 @@ async function fetchSchema(appType, formUuid, authRef) {
486
499
  }
487
500
 
488
501
  function isSuccessfulSchemaResult(result) {
489
- return !!(result && result.success !== false && !result.__needLogin);
502
+ const content = resolveSchemaContent(result);
503
+ const pages = content && content.pages;
504
+ return !!(result && result.success !== false && !result.__needLogin
505
+ && Array.isArray(pages));
490
506
  }
491
507
 
492
508
  function printFieldSummary(result) {
@@ -722,6 +738,16 @@ async function runSingle(parsed, authRef) {
722
738
  return;
723
739
  }
724
740
 
741
+ if (parsed.analysisJson) {
742
+ console.log(JSON.stringify(buildSemanticAnalysis(
743
+ parsed.appType,
744
+ parsed.formUuid,
745
+ result,
746
+ extractFieldSummary(result)
747
+ ), null, 2));
748
+ return;
749
+ }
750
+
725
751
  chalkSuccess(t('get_schema.success'));
726
752
  printFieldSummary(result);
727
753
  console.log(JSON.stringify(result, null, 2));
@@ -756,6 +782,14 @@ async function runBatch(parsed, authRef) {
756
782
 
757
783
  const records = await mapLimit(forms, parsed.concurrency, async (form) => {
758
784
  const record = await fetchSchemaRecord(parsed.appType, form, authRef, parsed.retries);
785
+ if (record.success && parsed.analysisJson) {
786
+ record.semanticAnalysis = buildSemanticAnalysis(
787
+ parsed.appType,
788
+ record.formUuid,
789
+ record.schema,
790
+ record.fieldSummary
791
+ );
792
+ }
759
793
  if (record.success) {
760
794
  info(` ✅ ${record.formName || record.formUuid}`);
761
795
  } else {
@@ -764,7 +798,9 @@ async function runBatch(parsed, authRef) {
764
798
  return record;
765
799
  });
766
800
 
767
- const outputRecords = writeBatchOutput(parsed.outputDir, records, { compact: parsed.summaryJson });
801
+ const outputRecords = writeBatchOutput(parsed.outputDir, records, {
802
+ compact: parsed.summaryJson || parsed.analysisJson,
803
+ });
768
804
  const successCount = records.filter(record => record.success).length;
769
805
  const failedCount = records.length - successCount;
770
806
 
@@ -780,6 +816,7 @@ async function runBatch(parsed, authRef) {
780
816
  successCount,
781
817
  failedCount,
782
818
  summaryOnly: parsed.summaryJson || undefined,
819
+ analysisOnly: parsed.analysisJson || undefined,
783
820
  outputDir: parsed.outputDir ? path.resolve(parsed.outputDir) : undefined,
784
821
  forms: outputRecords,
785
822
  }, null, 2));
@@ -800,6 +837,8 @@ module.exports = {
800
837
  extractFieldSummary,
801
838
  extractOptionSummary,
802
839
  buildSchemaSummary,
840
+ buildSemanticAnalysis,
841
+ isSuccessfulSchemaResult,
803
842
  buildComponentAliasMaps,
804
843
  parseArgs,
805
844
  appendFieldQueries,
@@ -0,0 +1,223 @@
1
+ 'use strict';
2
+
3
+ const crypto = require('crypto');
4
+
5
+ function parseObject(value) {
6
+ if (typeof value !== 'string') {
7
+ return value && typeof value === 'object' ? value : null;
8
+ }
9
+ try {
10
+ const parsed = JSON.parse(value);
11
+ return parsed && typeof parsed === 'object' ? parsed : null;
12
+ } catch {
13
+ return null;
14
+ }
15
+ }
16
+
17
+ function resolveContent(schemaResult) {
18
+ if (!schemaResult) {return null;}
19
+ return parseObject(schemaResult.content !== undefined ? schemaResult.content : schemaResult);
20
+ }
21
+
22
+ function labelText(label) {
23
+ if (label === null || label === undefined) {return '';}
24
+ if (typeof label === 'object') {
25
+ return String(label.zh_CN || label.zh_HK || label.en_US || label.text || label.label || '');
26
+ }
27
+ return String(label);
28
+ }
29
+
30
+ function unique(values) {
31
+ return [...new Set(values.filter(Boolean))].sort();
32
+ }
33
+
34
+ function isPresent(value) {
35
+ if (value === null || value === undefined || value === '') {return false;}
36
+ if (Array.isArray(value)) {return value.length > 0;}
37
+ if (typeof value === 'object') {return Object.keys(value).length > 0;}
38
+ return true;
39
+ }
40
+
41
+ function hasFormulaSemantics(value) {
42
+ if (!isPresent(value)) {return false;}
43
+ if (typeof value === 'string') {return value.trim() !== '';}
44
+ if (Array.isArray(value)) {return value.length > 0;}
45
+ if (typeof value !== 'object') {return true;}
46
+ if (['expression', 'source', 'value', 'formula'].some(key => isPresent(value[key]))) {
47
+ return true;
48
+ }
49
+ if (Array.isArray(value.data) && value.data.length > 0) {return true;}
50
+ if (value.event && typeof value.event === 'object') {
51
+ return Object.values(value.event).some(events => Array.isArray(events) ? events.length > 0 : isPresent(events));
52
+ }
53
+ return false;
54
+ }
55
+
56
+ function extractFunctionNames(source) {
57
+ const names = [];
58
+ const patterns = [
59
+ /export\s+(?:async\s+)?function\s+([A-Za-z_$][\w$]*)\s*\(/g,
60
+ /exports\.([A-Za-z_$][\w$]*)\s*=\s*/g,
61
+ ];
62
+ for (const pattern of patterns) {
63
+ let match;
64
+ while ((match = pattern.exec(source)) !== null) {
65
+ names.push(match[1]);
66
+ }
67
+ }
68
+ return unique(names);
69
+ }
70
+
71
+ function extractUrlParams(source) {
72
+ const keys = [];
73
+ const pattern = /urlParams(?:\.([A-Za-z_$][\w$]*)|\[['"]([^'"]+)['"]\])/g;
74
+ let match;
75
+ while ((match = pattern.exec(source)) !== null) {
76
+ keys.push(match[1] || match[2]);
77
+ }
78
+ return unique(keys);
79
+ }
80
+
81
+ function extractFieldMutations(source) {
82
+ const mutations = [];
83
+ const pattern = /this\.\$\(\s*['"]([^'"]+)['"]\s*\)\s*\.\s*(setValue|setBehavior|reset|validate|show|hide)\s*\(/g;
84
+ let match;
85
+ while ((match = pattern.exec(source)) !== null) {
86
+ mutations.push({ fieldId: match[1], operation: match[2] });
87
+ }
88
+ const seen = new Set();
89
+ return mutations.filter((item) => {
90
+ const key = `${item.fieldId}:${item.operation}`;
91
+ if (seen.has(key)) {return false;}
92
+ seen.add(key);
93
+ return true;
94
+ });
95
+ }
96
+
97
+ function bindingFromValue(value, path) {
98
+ if (!value || typeof value !== 'object' || value.type !== 'actionRef') {return null;}
99
+ return {
100
+ event: path[path.length - 1] || '',
101
+ actionName: value.name || value.id || '',
102
+ fieldId: path.find(part => typeof part === 'string' && /Field_/.test(part)) || '',
103
+ };
104
+ }
105
+
106
+ function buildSemanticAnalysis(appType, formUuid, schemaResult, fieldSummary = []) {
107
+ const content = resolveContent(schemaResult) || {};
108
+ const actionModule = content.actions && content.actions.module || {};
109
+ const source = typeof actionModule.source === 'string' ? actionModule.source : '';
110
+ const compiled = typeof actionModule.compiled === 'string' ? actionModule.compiled : '';
111
+ const fieldBehaviors = [];
112
+ const bindings = [];
113
+ let associationRuleCount = 0;
114
+
115
+ function visit(value, path = []) {
116
+ if (!value) {return;}
117
+ if (Array.isArray(value)) {
118
+ value.forEach((item, index) => visit(item, path.concat(index)));
119
+ return;
120
+ }
121
+ if (typeof value !== 'object') {return;}
122
+
123
+ const binding = bindingFromValue(value, path);
124
+ if (binding) {bindings.push(binding);}
125
+
126
+ const props = value.props || {};
127
+ if (Array.isArray(props.associationRules)) {
128
+ associationRuleCount += props.associationRules.length;
129
+ }
130
+ if (props.fieldId) {
131
+ const validation = Array.isArray(props.validation) ? props.validation : [];
132
+ const validationTypes = unique(validation.map(rule => rule && (rule.type || rule.name || (rule.required ? 'required' : ''))));
133
+ const behavior = props.behavior && props.behavior !== 'NORMAL' ? String(props.behavior) : '';
134
+ const formula = hasFormulaSemantics(props.formula)
135
+ || hasFormulaSemantics(props.defaultDataSource && props.defaultDataSource.formula);
136
+ const dataSourceUrl = props.remoteDataSource && props.remoteDataSource.url
137
+ || props.dataSource && !Array.isArray(props.dataSource) && props.dataSource.url
138
+ || props.dataSource && props.dataSource.searchConfig && props.dataSource.searchConfig.url
139
+ || '';
140
+ const hasDataSourceBinding = !!(
141
+ props.remoteDataSource
142
+ || dataSourceUrl
143
+ || props.dataSourceLinkage
144
+ || (props.dataSourceType && props.dataSourceType !== 'custom')
145
+ );
146
+ const association = props.associationForm || props.associationFormConfig || null;
147
+ if (behavior || validationTypes.length || formula || hasDataSourceBinding || association) {
148
+ fieldBehaviors.push({
149
+ fieldId: String(props.fieldId),
150
+ label: labelText(props.label),
151
+ componentName: value.componentName || '',
152
+ ...(behavior ? { behavior } : {}),
153
+ ...(validationTypes.length ? { validationTypes } : {}),
154
+ ...(formula ? { hasFormulaOrLinkage: true } : {}),
155
+ ...(hasDataSourceBinding ? {
156
+ dataSource: {
157
+ type: props.dataSourceType || (props.remoteDataSource ? 'remote' : 'linked'),
158
+ hasUrl: !!dataSourceUrl,
159
+ },
160
+ } : {}),
161
+ ...(association ? {
162
+ association: {
163
+ formUuid: association.formUuid || association.targetFormUuid || association.formId || '',
164
+ hasBackfill: !!(association.backfill || association.backfillRules || association.fillRules),
165
+ },
166
+ } : {}),
167
+ });
168
+ }
169
+ }
170
+
171
+ Object.keys(value).forEach((key) => {
172
+ if (key !== 'source' && key !== 'compiled') {
173
+ visit(value[key], path.concat(key));
174
+ }
175
+ });
176
+ }
177
+
178
+ visit(content.pages || []);
179
+
180
+ const actionList = content.actions && Array.isArray(content.actions.list)
181
+ ? content.actions.list
182
+ : [];
183
+ const actionEntries = actionList.map(item => ({
184
+ id: item.id || item.name || '',
185
+ name: item.name || item.title || item.id || '',
186
+ type: item.type || '',
187
+ relatedEventId: item.relatedEventId || '',
188
+ }));
189
+ const mutationFields = unique(extractFieldMutations(source).map(item => item.fieldId));
190
+
191
+ return {
192
+ kind: 'yida_schema_semantic_analysis',
193
+ contractVersion: 1,
194
+ resource: {
195
+ appType,
196
+ formUuid,
197
+ schemaHash: `sha256:${crypto.createHash('sha256').update(JSON.stringify(content)).digest('hex')}`,
198
+ },
199
+ fieldCount: fieldSummary.length,
200
+ fields: fieldSummary,
201
+ semantics: {
202
+ actions: {
203
+ sourceBytes: Buffer.byteLength(source, 'utf8'),
204
+ compiledBytes: Buffer.byteLength(compiled, 'utf8'),
205
+ functions: extractFunctionNames(source),
206
+ entries: actionEntries,
207
+ bindings,
208
+ urlParams: extractUrlParams(source),
209
+ fieldMutations: extractFieldMutations(source),
210
+ referencedMutationFields: mutationFields,
211
+ },
212
+ fieldBehaviors,
213
+ associationRuleCount,
214
+ },
215
+ };
216
+ }
217
+
218
+ module.exports = {
219
+ buildSemanticAnalysis,
220
+ extractFieldMutations,
221
+ extractFunctionNames,
222
+ extractUrlParams,
223
+ };
@@ -136,6 +136,10 @@ const BUILDER_CORE_COMMAND_IDS = Object.freeze([
136
136
  'app-list',
137
137
  'list-forms',
138
138
  'get-schema',
139
+ 'data',
140
+ 'nav-group',
141
+ 'get-permission',
142
+ 'save-permission',
139
143
  'create-app',
140
144
  'create-form.create',
141
145
  'create-page',
@@ -154,6 +158,7 @@ function commandBriefs(manifest, commandIds) {
154
158
  output: entry.output,
155
159
  permission_mode: entry.permission && entry.permission.mode,
156
160
  side_effect_kind: entry.side_effect && entry.side_effect.kind,
161
+ examples: entry.examples || [],
157
162
  }));
158
163
  }
159
164
 
@@ -920,7 +920,7 @@ const FORBIDDEN_ALIASES = Object.freeze([
920
920
  suggested_usage: 'openyida app-list [--size N]',
921
921
  alternative_command_ids: ['get-schema', 'agent-capabilities'],
922
922
  alternative_usages: [
923
- 'openyida get-schema <appType> <formUuid|--all> [--summary-json|--field-map-json]',
923
+ 'openyida get-schema <appType> <formUuid|--all> [--summary-json|--field-map-json|--analysis-json]',
924
924
  'openyida agent-capabilities --summary-json',
925
925
  ],
926
926
  message_key: 'cli.forbidden_alias_get_app',
@@ -958,7 +958,7 @@ const FORBIDDEN_ALIASES = Object.freeze([
958
958
  pattern: 'get-schema --app-type',
959
959
  matcher: { type: 'command_has_option', command: 'get-schema', option: '--app-type' },
960
960
  suggested_command_id: 'get-schema',
961
- suggested_usage: 'openyida get-schema <appType> <formUuid|--all> [--summary-json|--field-map-json]',
961
+ suggested_usage: 'openyida get-schema <appType> <formUuid|--all> [--summary-json|--field-map-json|--analysis-json]',
962
962
  message_key: 'cli.forbidden_alias_get_schema_app_type_option',
963
963
  message_args: ['get-schema', '--app-type'],
964
964
  }),
@@ -967,7 +967,7 @@ const FORBIDDEN_ALIASES = Object.freeze([
967
967
  pattern: 'get-schema --form-uuid',
968
968
  matcher: { type: 'command_has_option', command: 'get-schema', option: '--form-uuid' },
969
969
  suggested_command_id: 'get-schema',
970
- suggested_usage: 'openyida get-schema <appType> <formUuid|--all> [--summary-json|--field-map-json]',
970
+ suggested_usage: 'openyida get-schema <appType> <formUuid|--all> [--summary-json|--field-map-json|--analysis-json]',
971
971
  message_key: 'cli.forbidden_alias_get_schema_form_uuid_option',
972
972
  message_args: ['get-schema', '--form-uuid'],
973
973
  }),
@@ -1038,6 +1038,10 @@ const COMMAND_GROUPS = [
1038
1038
  command('nav-group', ['nav-group'], 'nav-group <list|create|rename|delete|move|order|auto-order|hide|show> <appType> ...', 'help.cmd_nav_group', {
1039
1039
  output: 'json',
1040
1040
  aliases: ['group'],
1041
+ examples: [
1042
+ 'openyida nav-group list APP_XXX --flat',
1043
+ 'openyida nav-group move APP_XXX FORM_XXX --to NAV_XXX',
1044
+ ],
1041
1045
  }),
1042
1046
  command('app-permission', ['app-permission'], 'app-permission <get|set|add|remove|search-user> ...', 'help.cmd_app_permission', {
1043
1047
  output: 'json',
@@ -1133,11 +1137,15 @@ const COMMAND_GROUPS = [
1133
1137
  command('add-validation', ['add-validation'], 'add-validation <appType> <formUuid> --field <labelOrId> --type <phone|regex|idCard|email|...> [--message <text>]', 'help.cmd_update_form'),
1134
1138
  command('create-form.bind-datasource', ['create-form', 'bind-datasource'], 'create-form bind-datasource <appType> <formUuid> <fieldLabelOrId> <dataSourceJsonOrFile> [--open|--no-open]', 'help.cmd_update_form'),
1135
1139
  command('create-form.add-option', ['create-form', 'add-option'], 'create-form add-option <appType> <formUuid> <fieldLabel> <option1> [option2] ...', 'help.cmd_update_form'),
1136
- command('list-forms', ['list-forms'], 'list-forms <appType> [--keyword <text>]', 'help.cmd_list_forms'),
1140
+ command('list-forms', ['list-forms'], 'list-forms <appType> [--keyword <text>]', 'help.cmd_list_forms', {
1141
+ examples: ['openyida list-forms APP_XXX --keyword 客户'],
1142
+ }),
1137
1143
  command('aggregate-table', ['aggregate-table'], 'aggregate-table <list|create-empty|inspect|preview|save|publish|status> <appType> ...', 'help.cmd_aggregate_table', {
1138
1144
  output: 'json',
1139
1145
  }),
1140
- command('get-schema', ['get-schema'], 'get-schema <appType> <formUuid|--all> [--summary-json|--field-map-json]', 'help.cmd_get_schema'),
1146
+ command('get-schema', ['get-schema'], 'get-schema <appType> <formUuid|--all> [--summary-json|--field-map-json|--analysis-json]', 'help.cmd_get_schema', {
1147
+ examples: ['openyida get-schema APP_XXX FORM_XXX --field-map-json'],
1148
+ }),
1141
1149
  command('check-prd-completeness', ['check-prd-completeness'], 'check-prd-completeness <prd.md> --app-type <appType> [--build-manifest <file>] [--json]', 'help.cmd_check_prd_completeness', {
1142
1150
  output: 'json',
1143
1151
  }),
@@ -1160,7 +1168,11 @@ const COMMAND_GROUPS = [
1160
1168
  id: 'data',
1161
1169
  titleKey: 'help.group_data',
1162
1170
  commands: [
1163
- command('data', ['data'], 'data <action> <resource> [args]', 'help.cmd_data'),
1171
+ command('data', ['data'], 'data <query|get|create|update|delete> <form|process|subform|tasks|operation-records|task> [args]', 'help.cmd_data', {
1172
+ examples: [
1173
+ 'openyida data create form APP_XXX FORM_XXX --data-json \'{"textField_name":"客户 A","dateField_followUp":1787932800000}\'',
1174
+ ],
1175
+ }),
1164
1176
  command('task-center', ['task-center'], 'task-center <type> [options]', 'help.cmd_task_center'),
1165
1177
  command('basic-info', ['basic-info'], 'basic-info <overview|commodity|grant|capacity|quota|abs-path|dataflow|i18n|domain>', 'help.cmd_basic_info', {
1166
1178
  output: 'json',
@@ -1171,8 +1183,13 @@ const COMMAND_GROUPS = [
1171
1183
  command('read-dingtalk-tingji', ['read-dingtalk-tingji'], 'read-dingtalk-tingji <taskUuid> [--json]', 'help.cmd_read_dingtalk_tingji', {
1172
1184
  output: 'json',
1173
1185
  }),
1174
- command('get-permission', ['get-permission'], 'get-permission <appType> <formUuid>', 'help.cmd_get_permission'),
1175
- command('save-permission', ['save-permission'], 'save-permission <appType> <formUuid> ...', 'help.cmd_save_permission'),
1186
+ command('get-permission', ['get-permission'], 'get-permission <appType> <formUuid> [--package-uuid <packageUuid>] [--json]', 'help.cmd_get_permission'),
1187
+ command('save-permission', ['save-permission'], 'save-permission <appType> <formUuid> --package-uuid <packageUuid> [--data-permission <json>|--action-permission <json>|--field-permission <json>]', 'help.cmd_save_permission', {
1188
+ examples: [
1189
+ 'openyida get-permission APP_XXX FORM_XXX --json',
1190
+ 'openyida save-permission APP_XXX FORM_XXX --package-uuid PACKAGE_XXX --data-permission \'{"dataRange":"ORIGINATOR"}\'',
1191
+ ],
1192
+ }),
1176
1193
  command('corp-manager', ['corp-manager'], 'corp-manager <search-user|list|add|remove|address-book> ...', 'help.cmd_corp_manager', { output: 'json' }),
1177
1194
  command('agent-center', ['agent-center'], 'agent-center <list|create|update|cancel|range|search-user> ...', 'help.cmd_agent_center', { output: 'json' }),
1178
1195
  ],
@@ -1240,7 +1257,7 @@ const COMMAND_GROUPS = [
1240
1257
  requiresLogin: false,
1241
1258
  output: 'json',
1242
1259
  }),
1243
- command('integration.list', ['integration', 'list'], 'integration list <appType> [--form-uuid <uuid>] [--status y|n] [--json]', 'help.cmd_integration_list', {
1260
+ command('integration.list', ['integration', 'list'], 'integration list <appType> [--flow-types 1,2,3,5,6] [--form-uuid <uuid>] [--status y|n] [--json]', 'help.cmd_integration_list', {
1244
1261
  output: 'json',
1245
1262
  }),
1246
1263
  command('integration.enable', ['integration', 'enable'], 'integration enable <appType> <formUuid> <processCode>', 'help.cmd_integration_enable', {
@@ -24,7 +24,7 @@ const { readFormMode } = require('../app/services/form-mode-service');
24
24
  const USAGE = `openyida data - Unified Yida data CLI
25
25
 
26
26
  Usage:
27
- openyida data query form <appType> <formUuid> [--page N] [--size N] [--all] [--max-pages N] [--search-json JSON|--search-file .cache/openyida/search.json] [--resolve-aliases] [--inst-id ID] [--no-hydrate-subforms]
27
+ openyida data query form <appType> <formUuid> [--page N] [--size N] [--all] [--max-pages N] [--search-json JSON|--search-file .cache/openyida/search.json] [--dynamic-order '{"fieldId":"+"}'] [--resolve-aliases] [--inst-id ID] [--no-hydrate-subforms]
28
28
  openyida data get form <appType> --inst-id <formInstId> [--form-uuid <formUuid>] [--no-hydrate-subforms]
29
29
  openyida data create form <appType> <formUuid> (--data-json <JSON>|--data-file .cache/openyida/data.json) [--dept-id ID] [--resolve-aliases]
30
30
  说明:若目标表单为流程表单,会自动使用 /v1/process/startInstance.json 发起流程。
@@ -41,6 +41,8 @@ Usage:
41
41
  openyida data query tasks <appType> --type <todo|done|submitted|cc> [--page N] [--size N] [--keyword TEXT] [--process-codes JSON] [--instance-status STATUS]
42
42
 
43
43
  Add --resolve-aliases when JSON keys use Yida component aliases instead of field IDs.
44
+ Use --dynamic-order '{"fieldId":"+"}' for ascending order or '{"fieldId":"-"}' for descending order.
45
+ When --dynamic-order is omitted, the API does not guarantee a stable result order.
44
46
  `;
45
47
 
46
48
  function fail(message) {