openyida 2026.8.28 → 2026.8.29
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 +9 -5
- package/bin/yida.js +43 -0
- package/lib/app/get-schema.js +58 -19
- package/lib/app/schema-semantic-analysis.js +223 -0
- package/lib/core/agent-capabilities.js +5 -0
- package/lib/core/command-manifest.js +26 -9
- package/lib/integration/integration-check.js +7 -6
- package/lib/integration/integration-list.js +9 -3
- package/lib/page-config/save-share-config.js +16 -5
- package/lib/permission/get-permission.js +3 -1
- package/lib/report/capability-registry.js +8 -0
- package/lib/report/chart-builder.js +51 -1
- package/lib/report/contract.js +71 -0
- package/lib/report/data-model.js +43 -2
- package/package.json +2 -1
- package/yida-skills/skills/yida-app/SKILL.md +2 -1
- package/yida-skills/skills/yida-app/workflow/step-4-forms-processes.md +3 -0
- package/yida-skills/skills/yida-app/workflow/step-9-output-finish.md +2 -0
- package/yida-skills/skills/yida-form-permission/SKILL.md +1 -0
- package/yida-skills/skills/yida-get-schema/SKILL.md +12 -2
- package/yida-skills/skills/yida-integration/SKILL.md +5 -2
- package/yida-skills/skills/yida-nav-group/SKILL.md +2 -0
- package/yida-skills/skills/yida-report/SKILL.md +8 -2
package/README.md
CHANGED
|
@@ -305,6 +305,10 @@ OPENYIDA_E2E=1 npm run eval:e2e -- --skill yida-dashboard --screenshot --auto-sc
|
|
|
305
305
|
# above, here the agent self-orchestrates.
|
|
306
306
|
OPENYIDA_E2E=1 npm run eval:generate -- --screenshot
|
|
307
307
|
|
|
308
|
+
# Re-evaluate an archived real CLI trace against the latest scenario contract and
|
|
309
|
+
# current read-only platform state. This does not run an agent or mutate resources.
|
|
310
|
+
npm run eval:replay -- --report <generation-report.json> --scenario <scenario.json> --app-type APP_XXX
|
|
311
|
+
|
|
308
312
|
# Run all three in one pass (routing + tool-pipeline baseline + real generation).
|
|
309
313
|
OPENYIDA_E2E=1 npm run eval:all -- --skill yida-dashboard --screenshot
|
|
310
314
|
|
|
@@ -406,7 +410,7 @@ Run `openyida --help` or `openyida <command> --help` for detailed usage.
|
|
|
406
410
|
| `openyida create-form add-option <appType> <formUuid> <fieldLabel> <option1> [option2] ...` | Update a form page |
|
|
407
411
|
| `openyida list-forms <appType> [--keyword <text>]` | List forms/pages in an app |
|
|
408
412
|
| `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 |
|
|
413
|
+
| `openyida get-schema <appType> <formUuid\|--all> [--summary-json\|--field-map-json\|--analysis-json]` | Get one form Schema or all form Schemas |
|
|
410
414
|
| `openyida check-prd-completeness <prd.md> --app-type <appType> [--build-manifest <file>] [--json]` | Check PRD page/resource count risk |
|
|
411
415
|
| `openyida er <appType> [--format mermaid\|json] [--output file] [--include-system] [--include-pages]` | Export app entity relationship diagram |
|
|
412
416
|
| `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 +428,13 @@ Run `openyida --help` or `openyida <command> --help` for detailed usage.
|
|
|
424
428
|
|
|
425
429
|
| Command | Description |
|
|
426
430
|
|---------|-------------|
|
|
427
|
-
| `openyida data <
|
|
431
|
+
| `openyida data <query\|get\|create\|update\|delete> <form\|process\|subform\|tasks\|operation-records\|task> [args]` | Unified data management (form/process/task/subform) |
|
|
428
432
|
| `openyida task-center <type> [options]` | Global task center (todo/processed/cc etc.) |
|
|
429
433
|
| `openyida basic-info <overview\|commodity\|grant\|capacity\|quota\|abs-path\|dataflow\|i18n\|domain>` | Query organization basic info, capacity, quotas, and domain settings |
|
|
430
434
|
| `openyida read-dingtalk-doc <docUrl> [--output <file>] [--json]` | Fetch Markdown content from a DingTalk document |
|
|
431
435
|
| `openyida read-dingtalk-tingji <taskUuid> [--json]` | Fetch DingTalk Tingji details by task UUID |
|
|
432
|
-
| `openyida get-permission <appType> <formUuid
|
|
433
|
-
| `openyida save-permission <appType> <formUuid>
|
|
436
|
+
| `openyida get-permission <appType> <formUuid> [--package-uuid <packageUuid>] [--json]` | Query form permission config |
|
|
437
|
+
| `openyida save-permission <appType> <formUuid> --package-uuid <packageUuid> [--data-permission <json>\|--action-permission <json>\|--field-permission <json>]` | Save form permission config |
|
|
434
438
|
| `openyida corp-manager <search-user\|list\|add\|remove\|address-book> ...` | Manage platform admins and address book permissions |
|
|
435
439
|
| `openyida agent-center <list\|create\|update\|cancel\|range\|search-user> ...` | Manage process and departure delegation |
|
|
436
440
|
|
|
@@ -485,7 +489,7 @@ Run `openyida --help` or `openyida <command> --help` for detailed usage.
|
|
|
485
489
|
|---------|-------------|
|
|
486
490
|
| `openyida integration create <appType> ... [--spec file.json]` | Create integration automation flow |
|
|
487
491
|
| `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 |
|
|
492
|
+
| `openyida integration list <appType> [--flow-types 1,2,3,5,6] [--form-uuid <uuid>] [--status y\|n] [--json]` | List integration automation flows |
|
|
489
493
|
| `openyida integration enable <appType> <formUuid> <processCode>` | Enable integration automation flow |
|
|
490
494
|
| `openyida integration disable <appType> <formUuid> <processCode>` | Disable integration automation flow |
|
|
491
495
|
| `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') {
|
package/lib/app/get-schema.js
CHANGED
|
@@ -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
|
-
|
|
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, {
|
|
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 <
|
|
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>
|
|
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', {
|
|
@@ -419,7 +419,7 @@ function buildAuthRef() {
|
|
|
419
419
|
}));
|
|
420
420
|
}
|
|
421
421
|
|
|
422
|
-
function appendFlow(result, group, flow) {
|
|
422
|
+
function appendFlow(result, group, flow, flowType = '') {
|
|
423
423
|
if (!flow || !flow.processCode || result.seenProcessCodes.has(flow.processCode)) {
|
|
424
424
|
return;
|
|
425
425
|
}
|
|
@@ -435,6 +435,7 @@ function appendFlow(result, group, flow) {
|
|
|
435
435
|
status: flow.status || '',
|
|
436
436
|
eventName: flow.eventName || '',
|
|
437
437
|
eventType: flow.eventType || group.eventType || null,
|
|
438
|
+
flowType: String(flow.flowType || flow.type || group.flowType || group.type || flowType || ''),
|
|
438
439
|
executeByOrder: flow.executeByOrder,
|
|
439
440
|
gmtModified: flow.gmtModified || '',
|
|
440
441
|
lastAction: flow.lastAction || '',
|
|
@@ -442,15 +443,15 @@ function appendFlow(result, group, flow) {
|
|
|
442
443
|
});
|
|
443
444
|
}
|
|
444
445
|
|
|
445
|
-
function collectFlowsFromListResponse(result, response) {
|
|
446
|
+
function collectFlowsFromListResponse(result, response, flowType = '') {
|
|
446
447
|
const groups = response.data || [];
|
|
447
448
|
for (const item of groups) {
|
|
448
449
|
if (Array.isArray(item.flowList)) {
|
|
449
450
|
for (const flow of item.flowList) {
|
|
450
|
-
appendFlow(result, item, flow);
|
|
451
|
+
appendFlow(result, item, flow, flowType);
|
|
451
452
|
}
|
|
452
453
|
} else {
|
|
453
|
-
appendFlow(result, {}, item);
|
|
454
|
+
appendFlow(result, {}, item, flowType);
|
|
454
455
|
}
|
|
455
456
|
}
|
|
456
457
|
}
|
|
@@ -478,7 +479,7 @@ async function listAllFlowsForForm(authRef, appType, formUuid, baseGroup, option
|
|
|
478
479
|
pageIndex,
|
|
479
480
|
pageSize,
|
|
480
481
|
});
|
|
481
|
-
collectFlowsFromListResponse(flows, response);
|
|
482
|
+
collectFlowsFromListResponse(flows, response, type);
|
|
482
483
|
totalCount = response.totalCount || totalCount;
|
|
483
484
|
pageIndex++;
|
|
484
485
|
if (!response.hasMore && (!totalCount || (pageIndex - 1) * pageSize >= totalCount)) {
|
|
@@ -527,7 +528,7 @@ async function listAllLogicflows(authRef, appType, options = {}) {
|
|
|
527
528
|
pageIndex,
|
|
528
529
|
pageSize,
|
|
529
530
|
});
|
|
530
|
-
collectFlowsFromListResponse(result, response);
|
|
531
|
+
collectFlowsFromListResponse(result, response, type);
|
|
531
532
|
for (const group of response.data || []) {
|
|
532
533
|
if (group && group.formUuid && group.hasMore) {
|
|
533
534
|
groupsWithMore.push(group);
|