openyida 2026.5.25 → 2026.5.26
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 +3 -2
- package/bin/yida.js +15 -3
- package/lib/app/page-linter.js +10 -0
- package/lib/app/update-app.js +41 -3
- package/lib/core/command-manifest.js +2 -1
- package/lib/core/locales/ar.js +5 -0
- package/lib/core/locales/de.js +5 -0
- package/lib/core/locales/en.js +7 -4
- package/lib/core/locales/es.js +5 -0
- package/lib/core/locales/fr.js +5 -0
- package/lib/core/locales/hi.js +5 -0
- package/lib/core/locales/ja.js +5 -0
- package/lib/core/locales/ko.js +5 -0
- package/lib/core/locales/pt.js +5 -0
- package/lib/core/locales/vi.js +5 -0
- package/lib/core/locales/zh-HK.js +5 -0
- package/lib/core/locales/zh.js +7 -4
- package/lib/core/query-data.js +184 -5
- package/lib/core/utils.js +32 -6
- package/package.json +1 -1
- package/scripts/e2e-real/skill-coverage.js +1 -0
- package/yida-skills/SKILL.md +1 -0
- package/yida-skills/skills/yida-connector-safe-actions/SKILL.md +282 -0
package/README.md
CHANGED
|
@@ -297,7 +297,7 @@ For overseas apps, pass `--locale en_US` or `--locale ja_JP` on creation command
|
|
|
297
297
|
| `openyida app-list [--size N]` | List Yida applications |
|
|
298
298
|
| `openyida corp-efficiency [overview\|details\|detail\|groups\|notify] [options] [--open\|--no-open]` | Query enterprise efficiency metrics, detail report entries, and related notification actions |
|
|
299
299
|
| `openyida create-app "<name>"\|--name <name> [options] [--locale zh_CN\|en_US\|ja_JP] [--open\|--no-open]` | Create an application and output `appType` |
|
|
300
|
-
| `openyida update-app <appType> --name "..."` | Update application metadata |
|
|
300
|
+
| `openyida update-app <appType> [--name "..."] [--layout slide\|ver] [--theme deepBlue]` | Update application metadata and theme/layout fields |
|
|
301
301
|
| `openyida nav-group <list\|create\|rename\|delete\|move\|hide\|show> <appType> ...` | Manage sidebar navigation groups and move pages between groups |
|
|
302
302
|
| `openyida app-permission <get\|set\|add\|remove\|search-user> ...` | Manage app primary admins, data admins, and developer members |
|
|
303
303
|
| `openyida i18n <overview\|config\|languages\|list\|upsert\|delete\|translate\|translate-all\|upgrade> <appType> ...` | Manage app multilingual copy and language configuration |
|
|
@@ -330,7 +330,7 @@ For overseas apps, pass `--locale en_US` or `--locale ja_JP` on creation command
|
|
|
330
330
|
|
|
331
331
|
| Command | Description |
|
|
332
332
|
|---------|-------------|
|
|
333
|
-
| `openyida data <action> <resource> [args]` | Unified data management for forms, processes, tasks, and subforms |
|
|
333
|
+
| `openyida data <action> <resource> [args]` | Unified data management for forms, processes, tasks, and subforms; form queries support `--all` and `--form-uuid` subform hydration |
|
|
334
334
|
| `openyida data check <appType> <formUuid> <rules.json>` | Detect anomalous process-form records |
|
|
335
335
|
| `openyida task-center <type> [options]` | Query todo, created, processed, CC, or proxy-submitted tasks |
|
|
336
336
|
| `openyida agent-center <sub-command>` | Manage Yida process delegation and departure delegation |
|
|
@@ -372,6 +372,7 @@ For overseas apps, pass `--locale en_US` or `--locale ja_JP` on creation command
|
|
|
372
372
|
| `openyida sample [--list]` | Emit sample templates |
|
|
373
373
|
| `openyida bridge start [--token <pair-token>] [--port 6736] [--open]` | Start the local OpenYida web bridge for `https://demo.aliwork.com/s/openyida` |
|
|
374
374
|
| `openyida doctor [--fix]` | Diagnose and repair environment issues |
|
|
375
|
+
| `openyida db-seq-fix [--fix]` | Detect and repair PostgreSQL sequence drift |
|
|
375
376
|
| `openyida formula evaluate <formula\|file> [--schema file]` | Static-check formula syntax and field references |
|
|
376
377
|
| `openyida update` | Update OpenYida through npm |
|
|
377
378
|
| `openyida export-conversation [options]` | Export AI conversation history |
|
package/bin/yida.js
CHANGED
|
@@ -306,7 +306,7 @@ function shouldUsePlaywrightFallbackInAgentLogin() {
|
|
|
306
306
|
const { detectActiveTool } = require('../lib/core/utils');
|
|
307
307
|
const activeTool = detectActiveTool();
|
|
308
308
|
if (activeTool && activeTool.tool === 'wukong') {
|
|
309
|
-
return
|
|
309
|
+
return false;
|
|
310
310
|
}
|
|
311
311
|
return process.env.OPENYIDA_AGENT_PLAYWRIGHT_FALLBACK === '1';
|
|
312
312
|
}
|
|
@@ -477,6 +477,14 @@ async function main() {
|
|
|
477
477
|
if (cachedResult.status === 'ok') {
|
|
478
478
|
printLoginResult(cachedResult);
|
|
479
479
|
} else {
|
|
480
|
+
const { detectActiveTool } = require('../lib/core/utils');
|
|
481
|
+
const activeTool = detectActiveTool();
|
|
482
|
+
if (activeTool && activeTool.tool === 'wukong') {
|
|
483
|
+
const { codexLogin } = require('../lib/auth/codex-login');
|
|
484
|
+
const result = await codexLogin({ tool: 'wukong' });
|
|
485
|
+
printLoginResult(result);
|
|
486
|
+
break;
|
|
487
|
+
}
|
|
480
488
|
const { interactiveLogin } = require('../lib/auth/login');
|
|
481
489
|
const browserResult = interactiveLogin({
|
|
482
490
|
playwrightFallback: shouldUsePlaywrightFallbackInAgentLogin(),
|
|
@@ -486,8 +494,6 @@ async function main() {
|
|
|
486
494
|
} else {
|
|
487
495
|
// CDP/Playwright 失败后的兜底策略:
|
|
488
496
|
// QoderWork 有 in-app browser,优先使用 browser handoff;其余走终端二维码
|
|
489
|
-
const { detectActiveTool } = require('../lib/core/utils');
|
|
490
|
-
const activeTool = detectActiveTool();
|
|
491
497
|
if (activeTool && activeTool.tool === 'qoderwork') {
|
|
492
498
|
const { codexLogin } = require('../lib/auth/codex-login');
|
|
493
499
|
const result = await codexLogin({ tool: 'qoderwork' });
|
|
@@ -1065,6 +1071,12 @@ async function main() {
|
|
|
1065
1071
|
break;
|
|
1066
1072
|
}
|
|
1067
1073
|
|
|
1074
|
+
case 'db-seq-fix': {
|
|
1075
|
+
const { run: runDbSeqFix } = require('../lib/db/db-seq-fix');
|
|
1076
|
+
await runDbSeqFix(args);
|
|
1077
|
+
break;
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1068
1080
|
case 'update': {
|
|
1069
1081
|
const { runUpdate } = require('../lib/core/update');
|
|
1070
1082
|
await runUpdate(currentVersion);
|
package/lib/app/page-linter.js
CHANGED
|
@@ -604,6 +604,16 @@ function lintYidaSource(sourceCode, filePath) {
|
|
|
604
604
|
pushIssue(warnings, lineNumber, 'native-select-ui', t('publish.lint_native_select_ui'), disableMap);
|
|
605
605
|
}
|
|
606
606
|
|
|
607
|
+
const iframeSelfNavigationMatch = line.match(/<a\b(?=[^>]*(?:aliwork\.com|yidaapps\.com|\/preview\/|\/workbench))(?!(?=[^>]*\btarget=(['"]?)_top\1))(?!(?=[^>]*\btarget=(['"]?)_blank\2))[^>]*>/i);
|
|
608
|
+
if (iframeSelfNavigationMatch && !isInCommentOrString(line, iframeSelfNavigationMatch.index)) {
|
|
609
|
+
pushIssue(warnings, lineNumber, 'iframe-self-navigation', t('publish.lint_iframe_self_navigation'), disableMap);
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
const topLocationMatch = line.match(/window\.location\.href\s*=\s*[^;\n]*(?:aliwork\.com|yidaapps\.com|\/preview\/|\/workbench)/i);
|
|
613
|
+
if (topLocationMatch && !isInCommentOrString(line, topLocationMatch.index) && !line.includes('window.top.location')) {
|
|
614
|
+
pushIssue(warnings, lineNumber, 'iframe-self-navigation', t('publish.lint_iframe_self_navigation'), disableMap);
|
|
615
|
+
}
|
|
616
|
+
|
|
607
617
|
const pageSizeMatch = line.match(/\bpageSize\s*:\s*(\d+)/);
|
|
608
618
|
if (pageSizeMatch && Number(pageSizeMatch[1]) > 100 && !isInCommentOrString(line, pageSizeMatch.index)) {
|
|
609
619
|
pushIssue(errors, lineNumber, 'page-size-limit', t('publish.lint_page_size_limit', pageSizeMatch[1]), disableMap);
|
package/lib/app/update-app.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* update-app.js - 更新宜搭应用信息
|
|
3
3
|
*
|
|
4
|
-
* 用法:openyida update-app <appType> --name "新名称" [--desc "描述"] [--icon "图标"]
|
|
4
|
+
* 用法:openyida update-app <appType> --name "新名称" [--desc "描述"] [--icon "图标"] [--layout slide|ver]
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
'use strict';
|
|
@@ -29,6 +29,9 @@ function parseArgs(args) {
|
|
|
29
29
|
desc: null,
|
|
30
30
|
icon: null,
|
|
31
31
|
iconColor: null,
|
|
32
|
+
colour: null,
|
|
33
|
+
navTheme: null,
|
|
34
|
+
layoutDirection: null,
|
|
32
35
|
};
|
|
33
36
|
|
|
34
37
|
// 第一个参数是 appType
|
|
@@ -69,6 +72,28 @@ function parseArgs(args) {
|
|
|
69
72
|
i++;
|
|
70
73
|
}
|
|
71
74
|
break;
|
|
75
|
+
case '--theme':
|
|
76
|
+
case '--colour':
|
|
77
|
+
if (nextArg) {
|
|
78
|
+
result.colour = nextArg;
|
|
79
|
+
i++;
|
|
80
|
+
}
|
|
81
|
+
break;
|
|
82
|
+
case '--nav-theme':
|
|
83
|
+
case '--navTheme':
|
|
84
|
+
if (nextArg) {
|
|
85
|
+
result.navTheme = nextArg;
|
|
86
|
+
i++;
|
|
87
|
+
}
|
|
88
|
+
break;
|
|
89
|
+
case '--layout':
|
|
90
|
+
case '--layout-direction':
|
|
91
|
+
case '--layoutDirection':
|
|
92
|
+
if (nextArg) {
|
|
93
|
+
result.layoutDirection = nextArg;
|
|
94
|
+
i++;
|
|
95
|
+
}
|
|
96
|
+
break;
|
|
72
97
|
}
|
|
73
98
|
}
|
|
74
99
|
|
|
@@ -88,7 +113,7 @@ async function run(args) {
|
|
|
88
113
|
const params = parseArgs(args);
|
|
89
114
|
|
|
90
115
|
// 验证必填参数
|
|
91
|
-
const { c, banner, step, label, info, success: chalkSuccess, result: chalkResult, error: chalkError } = require('../core/chalk');
|
|
116
|
+
const { c, banner, step, label, info, warn, success: chalkSuccess, result: chalkResult, error: chalkError } = require('../core/chalk');
|
|
92
117
|
|
|
93
118
|
if (!params.appType) {
|
|
94
119
|
chalkError(t('update_app.missing_app_type'), { exit: false });
|
|
@@ -96,7 +121,7 @@ async function run(args) {
|
|
|
96
121
|
process.exit(1);
|
|
97
122
|
}
|
|
98
123
|
|
|
99
|
-
if (!params.name && !params.desc && !params.icon) {
|
|
124
|
+
if (!params.name && !params.desc && !params.icon && !params.colour && !params.navTheme && !params.layoutDirection) {
|
|
100
125
|
chalkError(t('update_app.missing_update_field'), { exit: false });
|
|
101
126
|
printUsage();
|
|
102
127
|
process.exit(1);
|
|
@@ -107,6 +132,9 @@ async function run(args) {
|
|
|
107
132
|
if (params.name) {label('Name', params.name);}
|
|
108
133
|
if (params.desc) {label('Desc', params.desc);}
|
|
109
134
|
if (params.icon) {label('Icon', `${params.icon} ${c.dim}(${params.iconColor || '#0089FF'})${c.reset}`);}
|
|
135
|
+
if (params.colour || params.navTheme || params.layoutDirection) {
|
|
136
|
+
label('Theme', `${params.colour || '-'} / ${params.navTheme || '-'} / ${params.layoutDirection || '-'}`);
|
|
137
|
+
}
|
|
110
138
|
|
|
111
139
|
// Step 1: 读取登录态
|
|
112
140
|
step(1, t('common.step_login', 1));
|
|
@@ -158,6 +186,13 @@ async function run(args) {
|
|
|
158
186
|
postDataObj.iconUrl = iconValue;
|
|
159
187
|
}
|
|
160
188
|
|
|
189
|
+
if (params.colour) {postDataObj.colour = params.colour;}
|
|
190
|
+
if (params.navTheme) {postDataObj.navTheme = params.navTheme;}
|
|
191
|
+
if (params.layoutDirection) {
|
|
192
|
+
postDataObj.layoutDirection = params.layoutDirection;
|
|
193
|
+
warn(t('update_app.layout_notice'));
|
|
194
|
+
}
|
|
195
|
+
|
|
161
196
|
const postData = querystring.stringify(postDataObj);
|
|
162
197
|
|
|
163
198
|
const response = await requestWithAutoLogin((auth) => {
|
|
@@ -182,6 +217,9 @@ async function run(args) {
|
|
|
182
217
|
name: params.name || undefined,
|
|
183
218
|
desc: params.desc || undefined,
|
|
184
219
|
icon: params.icon || undefined,
|
|
220
|
+
colour: params.colour || undefined,
|
|
221
|
+
navTheme: params.navTheme || undefined,
|
|
222
|
+
layoutDirection: params.layoutDirection || undefined,
|
|
185
223
|
},
|
|
186
224
|
}));
|
|
187
225
|
} else {
|
|
@@ -45,7 +45,7 @@ const COMMAND_GROUPS = [
|
|
|
45
45
|
output: 'json',
|
|
46
46
|
}),
|
|
47
47
|
command('create-app', ['create-app'], 'create-app "<name>"|--name <name> [options] [--locale zh_CN|en_US|ja_JP] [--open|--no-open]', 'help.cmd_create_app'),
|
|
48
|
-
command('update-app', ['update-app'], 'update-app <appType> --name "..."', 'help.cmd_update_app'),
|
|
48
|
+
command('update-app', ['update-app'], 'update-app <appType> [--name "..."] [--layout slide|ver] [--theme deepBlue]', 'help.cmd_update_app'),
|
|
49
49
|
command('nav-group', ['nav-group'], 'nav-group <list|create|rename|delete|move|hide|show> <appType> ...', 'help.cmd_nav_group', {
|
|
50
50
|
output: 'json',
|
|
51
51
|
}),
|
|
@@ -175,6 +175,7 @@ const COMMAND_GROUPS = [
|
|
|
175
175
|
command('copy', ['copy'], 'copy [--force]', 'help.cmd_copy', { requiresLogin: false }),
|
|
176
176
|
command('sample', ['sample'], 'sample [--list]', 'help.cmd_sample', { requiresLogin: false }),
|
|
177
177
|
command('doctor', ['doctor'], 'doctor [--fix]', 'help.cmd_doctor', { requiresLogin: false }),
|
|
178
|
+
command('db-seq-fix', ['db-seq-fix'], 'db-seq-fix [--fix]', 'help.cmd_db_seq_fix'),
|
|
178
179
|
command('formula.evaluate', ['formula', 'evaluate'], 'formula evaluate <formula|file> [--schema file]', 'help.cmd_formula_evaluate', {
|
|
179
180
|
requiresLogin: false,
|
|
180
181
|
output: 'text|json',
|
package/lib/core/locales/ar.js
CHANGED
|
@@ -81,6 +81,7 @@ module.exports = {
|
|
|
81
81
|
cmd_commands: 'Output machine-readable command manifest',
|
|
82
82
|
cmd_a2a: 'Start local read-only A2A adapter or print Agent Card',
|
|
83
83
|
cmd_bridge: 'Start OpenYida local web bridge service',
|
|
84
|
+
cmd_db_seq_fix: 'Detect and repair PostgreSQL sequence drift',
|
|
84
85
|
cmd_copy: 'نسخ دليل عمل project',
|
|
85
86
|
cmd_sample: 'إخراج أمثلة/قوالب الكود',
|
|
86
87
|
cmd_doctor: 'تشخيص البيئة والإصلاح التلقائي',
|
|
@@ -462,6 +463,7 @@ module.exports = {
|
|
|
462
463
|
lint_foreach_callback_function: '.forEach(function ...) may lose this in callbacks; prefer .forEach((item) => ...)',
|
|
463
464
|
lint_controlled_input: 'input uses controlled value mode. Yida pages should use defaultValue + onChange to update _customState',
|
|
464
465
|
lint_native_select_ui: 'Found a native select. User-facing custom page dropdowns should use a Tailwind-styled custom dropdown component for consistent visual quality',
|
|
466
|
+
lint_iframe_self_navigation: 'Yida custom pages run in an iframe. Use target="_top"/target="_blank" or window.top.location for Yida page navigation to avoid nested pages.',
|
|
465
467
|
lint_page_size_limit: 'pageSize={0} exceeds the Yida API limit of 100; use 100 or less',
|
|
466
468
|
lint_yida_api_catch: 'this.utils.yida API call has no detected .catch(); add error handling and toast the user',
|
|
467
469
|
lint_echarts_legacy_map_china: 'ECharts 5 no longer supports echarts/map/js/china.js. Load DataV GeoJSON and call echarts.registerMap("china", geoJson) instead',
|
|
@@ -675,6 +677,9 @@ module.exports = {
|
|
|
675
677
|
done_meeting: ' التعرف على الاجتماع: {0} حقول بيانات وصفية، {1} أقسام A1، {2} متحدثين',
|
|
676
678
|
},
|
|
677
679
|
},
|
|
680
|
+
update_app: {
|
|
681
|
+
layout_notice: 'Note: layoutDirection is consumed by the Yida app shell during creation/refresh. If the top action bar does not recover immediately after a backend switch, reopen the workbench or recreate the app with the target layout.',
|
|
682
|
+
},
|
|
678
683
|
codex_login: {
|
|
679
684
|
title: ' openyida login {0} - {1} Login Mode',
|
|
680
685
|
not_codex: 'Current environment is not detected as Codex / Qoder / Wukong; returning an in-app browser login handoff only.',
|
package/lib/core/locales/de.js
CHANGED
|
@@ -81,6 +81,7 @@ module.exports = {
|
|
|
81
81
|
cmd_commands: 'Maschinenlesbares Command Manifest ausgeben',
|
|
82
82
|
cmd_a2a: 'Lokalen schreibgeschützten A2A-Adapter starten oder Agent Card ausgeben',
|
|
83
83
|
cmd_bridge: 'Lokalen OpenYida Web-Bridge-Dienst starten',
|
|
84
|
+
cmd_db_seq_fix: 'Detect and repair PostgreSQL sequence drift',
|
|
84
85
|
cmd_copy: 'project-Arbeitsverzeichnis kopieren',
|
|
85
86
|
cmd_sample: 'Codebeispiele/Vorlagen ausgeben',
|
|
86
87
|
cmd_doctor: 'Umgebungsdiagnose & automatische Reparatur',
|
|
@@ -462,6 +463,7 @@ module.exports = {
|
|
|
462
463
|
lint_foreach_callback_function: '.forEach(function ...) may lose this in callbacks; prefer .forEach((item) => ...)',
|
|
463
464
|
lint_controlled_input: 'input uses controlled value mode. Yida pages should use defaultValue + onChange to update _customState',
|
|
464
465
|
lint_native_select_ui: 'Found a native select. User-facing custom page dropdowns should use a Tailwind-styled custom dropdown component for consistent visual quality',
|
|
466
|
+
lint_iframe_self_navigation: 'Yida custom pages run in an iframe. Use target="_top"/target="_blank" or window.top.location for Yida page navigation to avoid nested pages.',
|
|
465
467
|
lint_page_size_limit: 'pageSize={0} exceeds the Yida API limit of 100; use 100 or less',
|
|
466
468
|
lint_yida_api_catch: 'this.utils.yida API call has no detected .catch(); add error handling and toast the user',
|
|
467
469
|
lint_echarts_legacy_map_china: 'ECharts 5 no longer supports echarts/map/js/china.js. Load DataV GeoJSON and call echarts.registerMap("china", geoJson) instead',
|
|
@@ -675,6 +677,9 @@ module.exports = {
|
|
|
675
677
|
done_meeting: ' Meeting-Erkennung: {0} Metadaten, {1} A1-Abschnitte, {2} Sprecher',
|
|
676
678
|
},
|
|
677
679
|
},
|
|
680
|
+
update_app: {
|
|
681
|
+
layout_notice: 'Note: layoutDirection is consumed by the Yida app shell during creation/refresh. If the top action bar does not recover immediately after a backend switch, reopen the workbench or recreate the app with the target layout.',
|
|
682
|
+
},
|
|
678
683
|
codex_login: {
|
|
679
684
|
title: ' openyida login {0} - {1} Login Mode',
|
|
680
685
|
not_codex: 'Current environment is not detected as Codex / Qoder / Wukong; returning an in-app browser login handoff only.',
|
package/lib/core/locales/en.js
CHANGED
|
@@ -87,6 +87,7 @@ module.exports = {
|
|
|
87
87
|
cmd_copy: 'Copy project working directory',
|
|
88
88
|
cmd_sample: 'Output code samples/templates',
|
|
89
89
|
cmd_doctor: 'Environment diagnostics & auto-fix',
|
|
90
|
+
cmd_db_seq_fix: 'Detect and repair PostgreSQL sequence drift',
|
|
90
91
|
cmd_formula_evaluate: 'Static-check Yida formula syntax and field refs',
|
|
91
92
|
cmd_update: 'Check and update to latest version',
|
|
92
93
|
cmd_export_conversation: 'Export AI conversation records',
|
|
@@ -802,11 +803,11 @@ Examples:
|
|
|
802
803
|
|
|
803
804
|
// ── lib/update-app.js ─────────────────────────────
|
|
804
805
|
update_app: {
|
|
805
|
-
usage: 'Usage: openyida update-app <appType> --name "New Name" [--desc "Description"] [--
|
|
806
|
-
example: 'Example: openyida update-app APP_XXX --name "New App Name" --
|
|
807
|
-
options: 'Options:\n --name, -n App name (
|
|
806
|
+
usage: 'Usage: openyida update-app <appType> [--name "New Name"] [--desc "Description"] [--layout slide|ver] [--theme deepBlue]',
|
|
807
|
+
example: 'Example: openyida update-app APP_XXX --name "New App Name" --layout ver --theme deepBlue',
|
|
808
|
+
options: 'Options:\n --name, -n App name (specify at least one update field)\n --desc, -d App description\n --icon Icon name (e.g. xian-yingyong)\n --icon-color Icon color (default: #0089FF)\n --theme App theme colour\n --nav-theme Navigation theme navTheme\n --layout Layout direction layoutDirection (slide or ver)',
|
|
808
809
|
missing_app_type: 'Error: missing appType argument',
|
|
809
|
-
missing_update_field: 'Error: at least one update field must be specified (--name, --desc, --icon)',
|
|
810
|
+
missing_update_field: 'Error: at least one update field must be specified (--name, --desc, --icon, --theme, --nav-theme, --layout)',
|
|
810
811
|
title: ' update-app - Yida App Info Update Tool',
|
|
811
812
|
app_type: '\n App ID: {0}',
|
|
812
813
|
new_name: ' New name: {0}',
|
|
@@ -817,6 +818,7 @@ Examples:
|
|
|
817
818
|
app_type_label: ' appType: {0}',
|
|
818
819
|
name_label: ' App name: {0}',
|
|
819
820
|
failed: ' ❌ Update failed: {0}',
|
|
821
|
+
layout_notice: 'Note: layoutDirection is consumed by the Yida app shell during creation/refresh. If the top action bar does not recover immediately after a backend switch, reopen the workbench or recreate the app with the target layout.',
|
|
820
822
|
},
|
|
821
823
|
|
|
822
824
|
// ── lib/update-form-config.js ──────────────────────
|
|
@@ -960,6 +962,7 @@ Examples:
|
|
|
960
962
|
lint_foreach_callback_function: '.forEach(function ...) may lose this in callbacks; prefer .forEach((item) => ...)',
|
|
961
963
|
lint_controlled_input: 'input uses controlled value mode. Yida pages should use defaultValue + onChange to update _customState',
|
|
962
964
|
lint_native_select_ui: 'Found a native select. User-facing custom page dropdowns should use a Tailwind-styled custom dropdown component for consistent visual quality',
|
|
965
|
+
lint_iframe_self_navigation: 'Yida custom pages run in an iframe. Use target="_top"/target="_blank" or window.top.location for Yida page navigation to avoid nested pages.',
|
|
963
966
|
lint_page_size_limit: 'pageSize={0} exceeds the Yida API limit of 100; use 100 or less',
|
|
964
967
|
lint_yida_api_catch: 'this.utils.yida API call has no detected .catch(); add error handling and toast the user',
|
|
965
968
|
lint_echarts_legacy_map_china: 'ECharts 5 no longer supports echarts/map/js/china.js. Load DataV GeoJSON and call echarts.registerMap("china", geoJson) instead',
|
package/lib/core/locales/es.js
CHANGED
|
@@ -81,6 +81,7 @@ module.exports = {
|
|
|
81
81
|
cmd_commands: 'Emitir manifiesto de comandos legible por máquina',
|
|
82
82
|
cmd_a2a: 'Iniciar adaptador A2A local de solo lectura o imprimir Agent Card',
|
|
83
83
|
cmd_bridge: 'Iniciar servicio local OpenYida web bridge',
|
|
84
|
+
cmd_db_seq_fix: 'Detect and repair PostgreSQL sequence drift',
|
|
84
85
|
cmd_copy: 'Copiar directorio de trabajo project',
|
|
85
86
|
cmd_sample: 'Generar ejemplos/plantillas de código',
|
|
86
87
|
cmd_doctor: 'Diagnóstico de entorno y reparación automática',
|
|
@@ -462,6 +463,7 @@ module.exports = {
|
|
|
462
463
|
lint_foreach_callback_function: '.forEach(function ...) may lose this in callbacks; prefer .forEach((item) => ...)',
|
|
463
464
|
lint_controlled_input: 'input uses controlled value mode. Yida pages should use defaultValue + onChange to update _customState',
|
|
464
465
|
lint_native_select_ui: 'Found a native select. User-facing custom page dropdowns should use a Tailwind-styled custom dropdown component for consistent visual quality',
|
|
466
|
+
lint_iframe_self_navigation: 'Yida custom pages run in an iframe. Use target="_top"/target="_blank" or window.top.location for Yida page navigation to avoid nested pages.',
|
|
465
467
|
lint_page_size_limit: 'pageSize={0} exceeds the Yida API limit of 100; use 100 or less',
|
|
466
468
|
lint_yida_api_catch: 'this.utils.yida API call has no detected .catch(); add error handling and toast the user',
|
|
467
469
|
lint_echarts_legacy_map_china: 'ECharts 5 no longer supports echarts/map/js/china.js. Load DataV GeoJSON and call echarts.registerMap("china", geoJson) instead',
|
|
@@ -675,6 +677,9 @@ module.exports = {
|
|
|
675
677
|
done_meeting: ' Reconocimiento de reunion: {0} metadatos, {1} secciones A1, {2} ponentes',
|
|
676
678
|
},
|
|
677
679
|
},
|
|
680
|
+
update_app: {
|
|
681
|
+
layout_notice: 'Note: layoutDirection is consumed by the Yida app shell during creation/refresh. If the top action bar does not recover immediately after a backend switch, reopen the workbench or recreate the app with the target layout.',
|
|
682
|
+
},
|
|
678
683
|
codex_login: {
|
|
679
684
|
title: ' openyida login {0} - {1} Login Mode',
|
|
680
685
|
not_codex: 'Current environment is not detected as Codex / Qoder / Wukong; returning an in-app browser login handoff only.',
|
package/lib/core/locales/fr.js
CHANGED
|
@@ -81,6 +81,7 @@ module.exports = {
|
|
|
81
81
|
cmd_commands: 'Afficher le manifeste des commandes lisible par machine',
|
|
82
82
|
cmd_a2a: 'Démarrer l’adaptateur A2A local en lecture seule ou afficher l’Agent Card',
|
|
83
83
|
cmd_bridge: 'Démarrer le service web bridge local OpenYida',
|
|
84
|
+
cmd_db_seq_fix: 'Detect and repair PostgreSQL sequence drift',
|
|
84
85
|
cmd_copy: 'Copier le répertoire de travail project',
|
|
85
86
|
cmd_sample: 'Exporter des exemples/modèles de code',
|
|
86
87
|
cmd_doctor: 'Diagnostic d\'environnement et réparation auto',
|
|
@@ -462,6 +463,7 @@ module.exports = {
|
|
|
462
463
|
lint_foreach_callback_function: '.forEach(function ...) may lose this in callbacks; prefer .forEach((item) => ...)',
|
|
463
464
|
lint_controlled_input: 'input uses controlled value mode. Yida pages should use defaultValue + onChange to update _customState',
|
|
464
465
|
lint_native_select_ui: 'Found a native select. User-facing custom page dropdowns should use a Tailwind-styled custom dropdown component for consistent visual quality',
|
|
466
|
+
lint_iframe_self_navigation: 'Yida custom pages run in an iframe. Use target="_top"/target="_blank" or window.top.location for Yida page navigation to avoid nested pages.',
|
|
465
467
|
lint_page_size_limit: 'pageSize={0} exceeds the Yida API limit of 100; use 100 or less',
|
|
466
468
|
lint_yida_api_catch: 'this.utils.yida API call has no detected .catch(); add error handling and toast the user',
|
|
467
469
|
lint_echarts_legacy_map_china: 'ECharts 5 no longer supports echarts/map/js/china.js. Load DataV GeoJSON and call echarts.registerMap("china", geoJson) instead',
|
|
@@ -675,6 +677,9 @@ module.exports = {
|
|
|
675
677
|
done_meeting: ' Reconnaissance reunion : {0} metadonnees, {1} sections A1, {2} intervenants',
|
|
676
678
|
},
|
|
677
679
|
},
|
|
680
|
+
update_app: {
|
|
681
|
+
layout_notice: 'Note: layoutDirection is consumed by the Yida app shell during creation/refresh. If the top action bar does not recover immediately after a backend switch, reopen the workbench or recreate the app with the target layout.',
|
|
682
|
+
},
|
|
678
683
|
codex_login: {
|
|
679
684
|
title: ' openyida login {0} - {1} Login Mode',
|
|
680
685
|
not_codex: 'Current environment is not detected as Codex / Qoder / Wukong; returning an in-app browser login handoff only.',
|
package/lib/core/locales/hi.js
CHANGED
|
@@ -81,6 +81,7 @@ module.exports = {
|
|
|
81
81
|
cmd_commands: 'Output machine-readable command manifest',
|
|
82
82
|
cmd_a2a: 'Start local read-only A2A adapter or print Agent Card',
|
|
83
83
|
cmd_bridge: 'Start OpenYida local web bridge service',
|
|
84
|
+
cmd_db_seq_fix: 'Detect and repair PostgreSQL sequence drift',
|
|
84
85
|
cmd_copy: 'project कार्य निर्देशिका कॉपी करें',
|
|
85
86
|
cmd_sample: 'कोड सैंपल/टेम्पलेट आउटपुट करें',
|
|
86
87
|
cmd_doctor: 'वातावरण निदान और स्वचालित मरम्मत',
|
|
@@ -462,6 +463,7 @@ module.exports = {
|
|
|
462
463
|
lint_foreach_callback_function: '.forEach(function ...) may lose this in callbacks; prefer .forEach((item) => ...)',
|
|
463
464
|
lint_controlled_input: 'input uses controlled value mode. Yida pages should use defaultValue + onChange to update _customState',
|
|
464
465
|
lint_native_select_ui: 'Found a native select. User-facing custom page dropdowns should use a Tailwind-styled custom dropdown component for consistent visual quality',
|
|
466
|
+
lint_iframe_self_navigation: 'Yida custom pages run in an iframe. Use target="_top"/target="_blank" or window.top.location for Yida page navigation to avoid nested pages.',
|
|
465
467
|
lint_page_size_limit: 'pageSize={0} exceeds the Yida API limit of 100; use 100 or less',
|
|
466
468
|
lint_yida_api_catch: 'this.utils.yida API call has no detected .catch(); add error handling and toast the user',
|
|
467
469
|
lint_echarts_legacy_map_china: 'ECharts 5 no longer supports echarts/map/js/china.js. Load DataV GeoJSON and call echarts.registerMap("china", geoJson) instead',
|
|
@@ -675,6 +677,9 @@ module.exports = {
|
|
|
675
677
|
done_meeting: ' Meeting recognition: {0} metadata, {1} A1 sections, {2} speakers',
|
|
676
678
|
},
|
|
677
679
|
},
|
|
680
|
+
update_app: {
|
|
681
|
+
layout_notice: 'Note: layoutDirection is consumed by the Yida app shell during creation/refresh. If the top action bar does not recover immediately after a backend switch, reopen the workbench or recreate the app with the target layout.',
|
|
682
|
+
},
|
|
678
683
|
codex_login: {
|
|
679
684
|
title: ' openyida login {0} - {1} Login Mode',
|
|
680
685
|
not_codex: 'Current environment is not detected as Codex / Qoder / Wukong; returning an in-app browser login handoff only.',
|
package/lib/core/locales/ja.js
CHANGED
|
@@ -83,6 +83,7 @@ module.exports = {
|
|
|
83
83
|
cmd_commands: '機械可読コマンド manifest を出力',
|
|
84
84
|
cmd_a2a: 'ローカル読み取り専用 A2A Adapter を起動、または Agent Card を出力',
|
|
85
85
|
cmd_bridge: 'OpenYida ローカル Web ブリッジサービスを起動',
|
|
86
|
+
cmd_db_seq_fix: 'PostgreSQL Sequence ドリフトを検出・修復',
|
|
86
87
|
cmd_copy: 'project 作業ディレクトリをコピー',
|
|
87
88
|
cmd_sample: 'コードサンプル/テンプレートを出力',
|
|
88
89
|
cmd_doctor: '環境診断と自動修復',
|
|
@@ -882,6 +883,7 @@ openyida - Yida CLI ツール
|
|
|
882
883
|
lint_foreach_callback_function: '.forEach(function ...) may lose this in callbacks; prefer .forEach((item) => ...)',
|
|
883
884
|
lint_controlled_input: 'input uses controlled value mode. Yida pages should use defaultValue + onChange to update _customState',
|
|
884
885
|
lint_native_select_ui: 'Found a native select. User-facing custom page dropdowns should use a Tailwind-styled custom dropdown component for consistent visual quality',
|
|
886
|
+
lint_iframe_self_navigation: 'Yida custom pages run in an iframe. Use target="_top"/target="_blank" or window.top.location for Yida page navigation to avoid nested pages.',
|
|
885
887
|
lint_page_size_limit: 'pageSize={0} exceeds the Yida API limit of 100; use 100 or less',
|
|
886
888
|
lint_yida_api_catch: 'this.utils.yida API call has no detected .catch(); add error handling and toast the user',
|
|
887
889
|
lint_echarts_legacy_map_china: 'ECharts 5 no longer supports echarts/map/js/china.js. Load DataV GeoJSON and call echarts.registerMap("china", geoJson) instead',
|
|
@@ -1067,6 +1069,9 @@ openyida - Yida CLI ツール
|
|
|
1067
1069
|
done_meeting: ' 会議認識:メタ情報 {0} 項目、A1 要約 {1} セクション、発言者 {2} 名',
|
|
1068
1070
|
},
|
|
1069
1071
|
},
|
|
1072
|
+
update_app: {
|
|
1073
|
+
layout_notice: 'Note: layoutDirection is consumed by the Yida app shell during creation/refresh. If the top action bar does not recover immediately after a backend switch, reopen the workbench or recreate the app with the target layout.',
|
|
1074
|
+
},
|
|
1070
1075
|
codex_login: {
|
|
1071
1076
|
title: ' openyida login {0} - {1} Login Mode',
|
|
1072
1077
|
not_codex: 'Current environment is not detected as Codex / Qoder / Wukong; returning an in-app browser login handoff only.',
|
package/lib/core/locales/ko.js
CHANGED
|
@@ -81,6 +81,7 @@ module.exports = {
|
|
|
81
81
|
cmd_commands: 'Output machine-readable command manifest',
|
|
82
82
|
cmd_a2a: 'Start local read-only A2A adapter or print Agent Card',
|
|
83
83
|
cmd_bridge: 'Start OpenYida local web bridge service',
|
|
84
|
+
cmd_db_seq_fix: 'Detect and repair PostgreSQL sequence drift',
|
|
84
85
|
cmd_copy: 'project 작업 디렉토리 복사',
|
|
85
86
|
cmd_sample: '코드 샘플/템플릿 출력',
|
|
86
87
|
cmd_doctor: '환경 진단 및 자동 수정',
|
|
@@ -464,6 +465,7 @@ module.exports = {
|
|
|
464
465
|
lint_foreach_callback_function: '.forEach(function ...) may lose this in callbacks; prefer .forEach((item) => ...)',
|
|
465
466
|
lint_controlled_input: 'input uses controlled value mode. Yida pages should use defaultValue + onChange to update _customState',
|
|
466
467
|
lint_native_select_ui: 'Found a native select. User-facing custom page dropdowns should use a Tailwind-styled custom dropdown component for consistent visual quality',
|
|
468
|
+
lint_iframe_self_navigation: 'Yida custom pages run in an iframe. Use target="_top"/target="_blank" or window.top.location for Yida page navigation to avoid nested pages.',
|
|
467
469
|
lint_page_size_limit: 'pageSize={0} exceeds the Yida API limit of 100; use 100 or less',
|
|
468
470
|
lint_yida_api_catch: 'this.utils.yida API call has no detected .catch(); add error handling and toast the user',
|
|
469
471
|
lint_echarts_legacy_map_china: 'ECharts 5 no longer supports echarts/map/js/china.js. Load DataV GeoJSON and call echarts.registerMap("china", geoJson) instead',
|
|
@@ -677,6 +679,9 @@ module.exports = {
|
|
|
677
679
|
done_meeting: ' 회의 인식: 메타정보 {0}개, A1 요약 {1}개, 발언자 {2}명',
|
|
678
680
|
},
|
|
679
681
|
},
|
|
682
|
+
update_app: {
|
|
683
|
+
layout_notice: 'Note: layoutDirection is consumed by the Yida app shell during creation/refresh. If the top action bar does not recover immediately after a backend switch, reopen the workbench or recreate the app with the target layout.',
|
|
684
|
+
},
|
|
680
685
|
codex_login: {
|
|
681
686
|
title: ' openyida login {0} - {1} Login Mode',
|
|
682
687
|
not_codex: 'Current environment is not detected as Codex / Qoder / Wukong; returning an in-app browser login handoff only.',
|
package/lib/core/locales/pt.js
CHANGED
|
@@ -81,6 +81,7 @@ module.exports = {
|
|
|
81
81
|
cmd_commands: 'Emitir manifesto de comandos legível por máquina',
|
|
82
82
|
cmd_a2a: 'Iniciar adaptador A2A local somente leitura ou imprimir Agent Card',
|
|
83
83
|
cmd_bridge: 'Iniciar serviço local OpenYida web bridge',
|
|
84
|
+
cmd_db_seq_fix: 'Detect and repair PostgreSQL sequence drift',
|
|
84
85
|
cmd_copy: 'Copiar diretório de trabalho project',
|
|
85
86
|
cmd_sample: 'Gerar exemplos/modelos de código',
|
|
86
87
|
cmd_doctor: 'Diagnóstico de ambiente e reparo automático',
|
|
@@ -462,6 +463,7 @@ module.exports = {
|
|
|
462
463
|
lint_foreach_callback_function: '.forEach(function ...) may lose this in callbacks; prefer .forEach((item) => ...)',
|
|
463
464
|
lint_controlled_input: 'input uses controlled value mode. Yida pages should use defaultValue + onChange to update _customState',
|
|
464
465
|
lint_native_select_ui: 'Found a native select. User-facing custom page dropdowns should use a Tailwind-styled custom dropdown component for consistent visual quality',
|
|
466
|
+
lint_iframe_self_navigation: 'Yida custom pages run in an iframe. Use target="_top"/target="_blank" or window.top.location for Yida page navigation to avoid nested pages.',
|
|
465
467
|
lint_page_size_limit: 'pageSize={0} exceeds the Yida API limit of 100; use 100 or less',
|
|
466
468
|
lint_yida_api_catch: 'this.utils.yida API call has no detected .catch(); add error handling and toast the user',
|
|
467
469
|
lint_echarts_legacy_map_china: 'ECharts 5 no longer supports echarts/map/js/china.js. Load DataV GeoJSON and call echarts.registerMap("china", geoJson) instead',
|
|
@@ -675,6 +677,9 @@ module.exports = {
|
|
|
675
677
|
done_meeting: ' Reconhecimento de reuniao: {0} metadados, {1} secoes A1, {2} palestrantes',
|
|
676
678
|
},
|
|
677
679
|
},
|
|
680
|
+
update_app: {
|
|
681
|
+
layout_notice: 'Note: layoutDirection is consumed by the Yida app shell during creation/refresh. If the top action bar does not recover immediately after a backend switch, reopen the workbench or recreate the app with the target layout.',
|
|
682
|
+
},
|
|
678
683
|
codex_login: {
|
|
679
684
|
title: ' openyida login {0} - {1} Login Mode',
|
|
680
685
|
not_codex: 'Current environment is not detected as Codex / Qoder / Wukong; returning an in-app browser login handoff only.',
|
package/lib/core/locales/vi.js
CHANGED
|
@@ -81,6 +81,7 @@ module.exports = {
|
|
|
81
81
|
cmd_commands: 'Output machine-readable command manifest',
|
|
82
82
|
cmd_a2a: 'Start local read-only A2A adapter or print Agent Card',
|
|
83
83
|
cmd_bridge: 'Start OpenYida local web bridge service',
|
|
84
|
+
cmd_db_seq_fix: 'Detect and repair PostgreSQL sequence drift',
|
|
84
85
|
cmd_copy: 'Sao chép thư mục làm việc project',
|
|
85
86
|
cmd_sample: 'Xuất mẫu ví dụ/mẫu mã',
|
|
86
87
|
cmd_doctor: 'Chẩn đoán môi trường và sửa tự động',
|
|
@@ -462,6 +463,7 @@ module.exports = {
|
|
|
462
463
|
lint_foreach_callback_function: '.forEach(function ...) may lose this in callbacks; prefer .forEach((item) => ...)',
|
|
463
464
|
lint_controlled_input: 'input uses controlled value mode. Yida pages should use defaultValue + onChange to update _customState',
|
|
464
465
|
lint_native_select_ui: 'Found a native select. User-facing custom page dropdowns should use a Tailwind-styled custom dropdown component for consistent visual quality',
|
|
466
|
+
lint_iframe_self_navigation: 'Yida custom pages run in an iframe. Use target="_top"/target="_blank" or window.top.location for Yida page navigation to avoid nested pages.',
|
|
465
467
|
lint_page_size_limit: 'pageSize={0} exceeds the Yida API limit of 100; use 100 or less',
|
|
466
468
|
lint_yida_api_catch: 'this.utils.yida API call has no detected .catch(); add error handling and toast the user',
|
|
467
469
|
lint_echarts_legacy_map_china: 'ECharts 5 no longer supports echarts/map/js/china.js. Load DataV GeoJSON and call echarts.registerMap("china", geoJson) instead',
|
|
@@ -675,6 +677,9 @@ module.exports = {
|
|
|
675
677
|
done_meeting: ' Nhan dang cuoc hop: {0} thong tin, {1} phan A1, {2} nguoi phat bieu',
|
|
676
678
|
},
|
|
677
679
|
},
|
|
680
|
+
update_app: {
|
|
681
|
+
layout_notice: 'Note: layoutDirection is consumed by the Yida app shell during creation/refresh. If the top action bar does not recover immediately after a backend switch, reopen the workbench or recreate the app with the target layout.',
|
|
682
|
+
},
|
|
678
683
|
codex_login: {
|
|
679
684
|
title: ' openyida login {0} - {1} Login Mode',
|
|
680
685
|
not_codex: 'Current environment is not detected as Codex / Qoder / Wukong; returning an in-app browser login handoff only.',
|
|
@@ -83,6 +83,7 @@ module.exports = {
|
|
|
83
83
|
cmd_commands: '輸出機器可讀命令清單',
|
|
84
84
|
cmd_a2a: '啟動本機唯讀 A2A Adapter 或輸出 Agent Card',
|
|
85
85
|
cmd_bridge: '啟動 OpenYida 本機網頁橋接服務',
|
|
86
|
+
cmd_db_seq_fix: 'PostgreSQL Sequence 漂移偵測與修復',
|
|
86
87
|
cmd_copy: '複製 project 工作目錄',
|
|
87
88
|
cmd_sample: '輸出程式碼範例/模板',
|
|
88
89
|
cmd_doctor: '環境診斷與自動修復',
|
|
@@ -793,6 +794,7 @@ openyida - 宜搭命令列工具
|
|
|
793
794
|
lint_foreach_callback_function: '.forEach(function ...) 可能導致回調內 this 遺失,建議改為 .forEach((item) => ...)',
|
|
794
795
|
lint_controlled_input: 'input 使用了 value 受控模式,宜搭頁面應使用 defaultValue + onChange 寫入 _customState',
|
|
795
796
|
lint_native_select_ui: '偵測到原生 select。面向用戶的自訂頁面下拉互動應使用 Tailwind 風格的自訂下拉組件,避免瀏覽器原生控件觀感不一致',
|
|
797
|
+
lint_iframe_self_navigation: '宜搭自訂頁面運行在 iframe 中,跳轉宜搭頁面時請使用 target="_top"/target="_blank" 或 window.top.location,避免頁面套娃',
|
|
796
798
|
lint_page_size_limit: 'pageSize={0} 超過宜搭 API 上限 100,請改為 100 或更小',
|
|
797
799
|
lint_yida_api_catch: 'this.utils.yida API 調用未偵測到 .catch(),請補充錯誤處理並 toast 給用戶',
|
|
798
800
|
lint_echarts_legacy_map_china: 'ECharts 5 已廢棄 echarts/map/js/china.js,請載入 DataV GeoJSON 後調用 echarts.registerMap("china", geoJson)',
|
|
@@ -1103,6 +1105,9 @@ openyida - 宜搭命令列工具
|
|
|
1103
1105
|
node_end: '流程結束',
|
|
1104
1106
|
node_approval: '審批節點',
|
|
1105
1107
|
},
|
|
1108
|
+
update_app: {
|
|
1109
|
+
layout_notice: '提示:layoutDirection 由宜搭應用外殼在建立/重新整理時消費;若後台切換後頂部操作欄未立即恢復,請重新開啟工作台或使用目標 layout 重新建立應用。',
|
|
1110
|
+
},
|
|
1106
1111
|
codex_login: {
|
|
1107
1112
|
title: ' openyida login {0} - {1} 登入模式',
|
|
1108
1113
|
not_codex: '目前環境未偵測為 Codex / Qoder / 悟空,僅返回內建瀏覽器登入 handoff。',
|
package/lib/core/locales/zh.js
CHANGED
|
@@ -87,6 +87,7 @@ module.exports = {
|
|
|
87
87
|
cmd_copy: '复制 project 工作目录',
|
|
88
88
|
cmd_sample: '输出代码示例/模板',
|
|
89
89
|
cmd_doctor: '环境诊断与自动修复',
|
|
90
|
+
cmd_db_seq_fix: 'PostgreSQL Sequence 漂移检测与修复',
|
|
90
91
|
cmd_formula_evaluate: '静态检查宜搭公式语法和字段引用',
|
|
91
92
|
cmd_update: '检查并更新到最新版本',
|
|
92
93
|
cmd_export_conversation: '导出 AI 对话记录',
|
|
@@ -739,11 +740,11 @@ openyida - 宜搭命令行工具
|
|
|
739
740
|
|
|
740
741
|
// ── lib/update-app.js ─────────────────────────────
|
|
741
742
|
update_app: {
|
|
742
|
-
usage: '用法: openyida update-app <appType> --name "新名称" [--desc "描述"] [--
|
|
743
|
-
example: '示例: openyida update-app APP_XXX --name "新应用名称" --
|
|
744
|
-
options: '选项:\n --name, -n
|
|
743
|
+
usage: '用法: openyida update-app <appType> [--name "新名称"] [--desc "描述"] [--layout slide|ver] [--theme deepBlue]',
|
|
744
|
+
example: '示例: openyida update-app APP_XXX --name "新应用名称" --layout ver --theme deepBlue',
|
|
745
|
+
options: '选项:\n --name, -n 应用名称(至少指定一个更新字段)\n --desc, -d 应用描述\n --icon 图标名称(如: xian-yingyong)\n --icon-color 图标颜色(默认: #0089FF)\n --theme 应用主题 colour\n --nav-theme 导航主题 navTheme\n --layout 布局方向 layoutDirection(slide 或 ver)',
|
|
745
746
|
missing_app_type: '错误: 缺少 appType 参数',
|
|
746
|
-
missing_update_field: '错误: 至少需要指定一个更新字段(--name, --desc, --icon)',
|
|
747
|
+
missing_update_field: '错误: 至少需要指定一个更新字段(--name, --desc, --icon, --theme, --nav-theme, --layout)',
|
|
747
748
|
title: ' update-app - 宜搭应用信息更新工具',
|
|
748
749
|
app_type: '\n 应用 ID: {0}',
|
|
749
750
|
new_name: ' 新名称: {0}',
|
|
@@ -754,6 +755,7 @@ openyida - 宜搭命令行工具
|
|
|
754
755
|
app_type_label: ' appType: {0}',
|
|
755
756
|
name_label: ' 应用名称: {0}',
|
|
756
757
|
failed: ' ❌ 更新失败: {0}',
|
|
758
|
+
layout_notice: '提示:layoutDirection 由宜搭应用外壳在创建/刷新时消费;若后台切换后顶部操作栏未立即恢复,请重新打开工作台或使用目标 layout 重新创建应用。',
|
|
757
759
|
},
|
|
758
760
|
|
|
759
761
|
// ── lib/process/create-process.js ─────────────────
|
|
@@ -962,6 +964,7 @@ openyida - 宜搭命令行工具
|
|
|
962
964
|
lint_foreach_callback_function: '.forEach(function ...) 可能导致回调内 this 丢失,建议改为 .forEach((item) => ...)',
|
|
963
965
|
lint_controlled_input: 'input 使用了 value 受控模式,宜搭页面应使用 defaultValue + onChange 写入 _customState',
|
|
964
966
|
lint_native_select_ui: '检测到原生 select。面向用户的自定义页面下拉交互应使用 Tailwind 风格的自定义下拉组件,避免浏览器原生控件观感不一致',
|
|
967
|
+
lint_iframe_self_navigation: '宜搭自定义页面运行在 iframe 中,跳转宜搭页面时请使用 target="_top"/target="_blank" 或 window.top.location,避免页面套娃',
|
|
965
968
|
lint_page_size_limit: 'pageSize={0} 超过宜搭接口上限 100,请改为 100 或更小',
|
|
966
969
|
lint_yida_api_catch: 'this.utils.yida API 调用未检测到 .catch(),请补充错误处理并 toast 给用户',
|
|
967
970
|
lint_echarts_legacy_map_china: 'ECharts 5 已废弃 echarts/map/js/china.js,请加载 DataV GeoJSON 后调用 echarts.registerMap("china", geoJson)',
|
package/lib/core/query-data.js
CHANGED
|
@@ -29,8 +29,8 @@ const { warn } = require('./chalk');
|
|
|
29
29
|
const USAGE = `openyida data - Unified Yida data CLI
|
|
30
30
|
|
|
31
31
|
Usage:
|
|
32
|
-
openyida data query form <appType> <formUuid> [--page N] [--size N] [--search-json JSON|--search-file .cache/openyida/search.json] [--inst-id ID]
|
|
33
|
-
openyida data get form <appType> --inst-id <formInstId>
|
|
32
|
+
openyida data query form <appType> <formUuid> [--page N] [--size N] [--all] [--max-pages N] [--search-json JSON|--search-file .cache/openyida/search.json] [--inst-id ID] [--no-hydrate-subforms]
|
|
33
|
+
openyida data get form <appType> --inst-id <formInstId> [--form-uuid <formUuid>] [--no-hydrate-subforms]
|
|
34
34
|
openyida data create form <appType> <formUuid> (--data-json <JSON>|--data-file .cache/openyida/data.json) [--dept-id ID]
|
|
35
35
|
openyida data update form <appType> --inst-id <formInstId> (--data-json <JSON>|--data-file .cache/openyida/data.json) [--use-latest-version y]
|
|
36
36
|
openyida data query subform <appType> <formUuid> --inst-id <formInstId> --table-field-id <fieldId> [--page N] [--size N]
|
|
@@ -113,6 +113,17 @@ function clampPageSize(options, defaultSize = 20) {
|
|
|
113
113
|
options.page = page;
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
+
function parsePositiveInt(value, defaultValue, label) {
|
|
117
|
+
const parsed = Number.parseInt(value || `${defaultValue}`, 10);
|
|
118
|
+
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
119
|
+
if (label) {
|
|
120
|
+
parseError(`${label} 必须是正整数`);
|
|
121
|
+
}
|
|
122
|
+
return defaultValue;
|
|
123
|
+
}
|
|
124
|
+
return parsed;
|
|
125
|
+
}
|
|
126
|
+
|
|
116
127
|
function requirePositionals(positionals, count, names) {
|
|
117
128
|
if (positionals.length < count) {
|
|
118
129
|
parseError(`缺少必填参数 ${names.join(' ')}`);
|
|
@@ -230,6 +241,138 @@ function normalizeFormDatasResult(result) {
|
|
|
230
241
|
return result;
|
|
231
242
|
}
|
|
232
243
|
|
|
244
|
+
function getResultDataList(result) {
|
|
245
|
+
if (!result) {return [];}
|
|
246
|
+
if (Array.isArray(result.data)) {return result.data;}
|
|
247
|
+
if (result.content && Array.isArray(result.content.data)) {return result.content.data;}
|
|
248
|
+
if (result.content && Array.isArray(result.content.dataList)) {return result.content.dataList;}
|
|
249
|
+
return [];
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function getResultTotalCount(result) {
|
|
253
|
+
if (!result) {return 0;}
|
|
254
|
+
if (result.totalCount !== undefined) {return Number(result.totalCount) || 0;}
|
|
255
|
+
if (result.content && result.content.totalCount !== undefined) {return Number(result.content.totalCount) || 0;}
|
|
256
|
+
return 0;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function getFormDataContainer(result) {
|
|
260
|
+
if (!result || !result.success) {return null;}
|
|
261
|
+
const candidates = [
|
|
262
|
+
result.content,
|
|
263
|
+
result.data,
|
|
264
|
+
result.content && result.content.data,
|
|
265
|
+
result.content && result.content.formData,
|
|
266
|
+
];
|
|
267
|
+
|
|
268
|
+
for (const candidate of candidates) {
|
|
269
|
+
if (!candidate || typeof candidate !== 'object') {continue;}
|
|
270
|
+
if (candidate.formData && typeof candidate.formData === 'object') {
|
|
271
|
+
return candidate.formData;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
return null;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
function normalizeSubformResult(result) {
|
|
279
|
+
const normalized = normalizeFormDatasResult(result);
|
|
280
|
+
return {
|
|
281
|
+
...normalized,
|
|
282
|
+
data: getResultDataList(normalized),
|
|
283
|
+
totalCount: getResultTotalCount(normalized),
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
async function fetchAllPages(fetchPage, options, defaultSize = 100) {
|
|
288
|
+
const pageSize = Math.min(parsePositiveInt(options.size, defaultSize), 100);
|
|
289
|
+
const maxPages = parsePositiveInt(options.max_pages, 1000);
|
|
290
|
+
const firstPage = parsePositiveInt(options.page, 1);
|
|
291
|
+
const allData = [];
|
|
292
|
+
let page = firstPage;
|
|
293
|
+
let lastResult = null;
|
|
294
|
+
let pagesFetched = 0;
|
|
295
|
+
|
|
296
|
+
while (pagesFetched < maxPages) {
|
|
297
|
+
const result = normalizeFormDatasResult(await fetchPage(page, pageSize));
|
|
298
|
+
lastResult = result;
|
|
299
|
+
const data = getResultDataList(result);
|
|
300
|
+
allData.push(...data);
|
|
301
|
+
pagesFetched++;
|
|
302
|
+
|
|
303
|
+
const totalCount = getResultTotalCount(result);
|
|
304
|
+
if (totalCount && allData.length >= totalCount) {break;}
|
|
305
|
+
if (!data.length || data.length < pageSize) {break;}
|
|
306
|
+
page++;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
return {
|
|
310
|
+
...(lastResult || { success: true }),
|
|
311
|
+
data: allData,
|
|
312
|
+
totalCount: getResultTotalCount(lastResult) || allData.length,
|
|
313
|
+
currentPage: firstPage,
|
|
314
|
+
pageSize,
|
|
315
|
+
pagesFetched,
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
async function fetchAllSubformRows(session, appType, formUuid, formInstId, tableFieldId, options = {}) {
|
|
320
|
+
const result = await fetchAllPages((page, size) => sendGet(session, appType, `/dingtalk/web/${appType}/v1/form/listTableDataByFormInstIdAndTableId.json`, {
|
|
321
|
+
formUuid,
|
|
322
|
+
formInstanceId: formInstId,
|
|
323
|
+
tableFieldId,
|
|
324
|
+
currentPage: String(page),
|
|
325
|
+
pageSize: String(size),
|
|
326
|
+
}), {
|
|
327
|
+
page: 1,
|
|
328
|
+
size: options.size || 100,
|
|
329
|
+
max_pages: options.max_pages || 1000,
|
|
330
|
+
}, 100);
|
|
331
|
+
return normalizeSubformResult(result);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
async function hydrateTruncatedSubforms(result, context) {
|
|
335
|
+
if (!context || !context.formUuid || !context.formInstId || context.disabled) {
|
|
336
|
+
return result;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
const formData = getFormDataContainer(result);
|
|
340
|
+
if (!formData) {return result;}
|
|
341
|
+
|
|
342
|
+
const hydrated = [];
|
|
343
|
+
const entries = Object.entries(formData);
|
|
344
|
+
for (const [fieldId, value] of entries) {
|
|
345
|
+
if (!/^tableField_/.test(fieldId)) {continue;}
|
|
346
|
+
if (!Array.isArray(value) || value.length < 50) {continue;}
|
|
347
|
+
if (value.length > 0 && !value.every(item => item && typeof item === 'object')) {continue;}
|
|
348
|
+
|
|
349
|
+
const subformResult = await fetchAllSubformRows(
|
|
350
|
+
context.session,
|
|
351
|
+
context.appType,
|
|
352
|
+
context.formUuid,
|
|
353
|
+
context.formInstId,
|
|
354
|
+
fieldId,
|
|
355
|
+
context.options || {}
|
|
356
|
+
);
|
|
357
|
+
const rows = subformResult.data || [];
|
|
358
|
+
if (rows.length > value.length) {
|
|
359
|
+
formData[fieldId] = rows;
|
|
360
|
+
hydrated.push({
|
|
361
|
+
fieldId,
|
|
362
|
+
originalCount: value.length,
|
|
363
|
+
hydratedCount: rows.length,
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
if (hydrated.length > 0) {
|
|
369
|
+
const target = result.content && typeof result.content === 'object' ? result.content : result;
|
|
370
|
+
target._openyidaHydratedSubforms = hydrated;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
return result;
|
|
374
|
+
}
|
|
375
|
+
|
|
233
376
|
function printResult(result) {
|
|
234
377
|
const errorCode = result && result.errorCode;
|
|
235
378
|
const hasErrorCode = errorCode !== undefined && errorCode !== null && errorCode !== '' && errorCode !== 0 && errorCode !== '0';
|
|
@@ -250,13 +393,41 @@ function printFormDatasResult(result) {
|
|
|
250
393
|
async function queryForm(positionals, options, session) {
|
|
251
394
|
requirePositionals(positionals, 2, ['appType', 'formUuid']);
|
|
252
395
|
const [appType, formUuid] = positionals;
|
|
253
|
-
clampPageSize(options);
|
|
396
|
+
clampPageSize(options, options.all ? 100 : 20);
|
|
254
397
|
|
|
255
398
|
let result;
|
|
256
399
|
if (options.inst_id) {
|
|
257
400
|
result = await sendGet(session, appType, `/dingtalk/web/${appType}/v1/form/getFormDataById.json`, {
|
|
258
401
|
formInstId: options.inst_id,
|
|
259
402
|
});
|
|
403
|
+
result = await hydrateTruncatedSubforms(result, {
|
|
404
|
+
session,
|
|
405
|
+
appType,
|
|
406
|
+
formUuid,
|
|
407
|
+
formInstId: options.inst_id,
|
|
408
|
+
disabled: !!options.no_hydrate_subforms,
|
|
409
|
+
options,
|
|
410
|
+
});
|
|
411
|
+
} else if (options.all) {
|
|
412
|
+
const searchJson = readJsonOption(options, 'search_json', 'search_file', '查询条件');
|
|
413
|
+
result = await fetchAllPages((page, size) => {
|
|
414
|
+
const params = {
|
|
415
|
+
formUuid,
|
|
416
|
+
appType,
|
|
417
|
+
currentPage: String(page),
|
|
418
|
+
pageSize: String(size),
|
|
419
|
+
};
|
|
420
|
+
if (searchJson) {
|
|
421
|
+
params.searchFieldJson = searchJson;
|
|
422
|
+
}
|
|
423
|
+
for (const key of ['originator_id', 'create_from', 'create_to', 'modified_from', 'modified_to', 'dynamic_order']) {
|
|
424
|
+
if (options[key]) {params[snakeToCamel(key)] = options[key];}
|
|
425
|
+
}
|
|
426
|
+
const requestPath = options.ids_only
|
|
427
|
+
? `/dingtalk/web/${appType}/v1/form/searchFormDataIds.json`
|
|
428
|
+
: `/dingtalk/web/${appType}/v1/form/searchFormDatas.json`;
|
|
429
|
+
return sendGet(session, appType, requestPath, params);
|
|
430
|
+
}, options, 100);
|
|
260
431
|
} else {
|
|
261
432
|
const params = {
|
|
262
433
|
formUuid,
|
|
@@ -284,8 +455,16 @@ async function getForm(positionals, options, session) {
|
|
|
284
455
|
requirePositionals(positionals, 1, ['appType']);
|
|
285
456
|
requireOption(options, 'inst_id');
|
|
286
457
|
const [appType] = positionals;
|
|
287
|
-
|
|
458
|
+
const result = await sendGet(session, appType, `/dingtalk/web/${appType}/v1/form/getFormDataById.json`, {
|
|
459
|
+
formInstId: options.inst_id,
|
|
460
|
+
});
|
|
461
|
+
printResult(await hydrateTruncatedSubforms(result, {
|
|
462
|
+
session,
|
|
463
|
+
appType,
|
|
464
|
+
formUuid: options.form_uuid,
|
|
288
465
|
formInstId: options.inst_id,
|
|
466
|
+
disabled: !!options.no_hydrate_subforms,
|
|
467
|
+
options,
|
|
289
468
|
}));
|
|
290
469
|
}
|
|
291
470
|
|
|
@@ -299,7 +478,7 @@ async function createForm(positionals, options, session) {
|
|
|
299
478
|
formDataJson: dataJson,
|
|
300
479
|
};
|
|
301
480
|
if (options.dept_id) {params.deptId = options.dept_id;}
|
|
302
|
-
printResult(await sendPost(session, appType, `/
|
|
481
|
+
printResult(await sendPost(session, appType, `/alibaba/web/${appType}/_/saveFormData.json`, params));
|
|
303
482
|
}
|
|
304
483
|
|
|
305
484
|
async function updateForm(positionals, options, session) {
|
package/lib/core/utils.js
CHANGED
|
@@ -433,6 +433,10 @@ function isCsrfTokenExpired(responseJson) {
|
|
|
433
433
|
);
|
|
434
434
|
}
|
|
435
435
|
|
|
436
|
+
function isHttpRedirectStatus(statusCode) {
|
|
437
|
+
return [301, 302, 303, 307, 308].includes(Number(statusCode));
|
|
438
|
+
}
|
|
439
|
+
|
|
436
440
|
// ── base_url 解析 ─────────────────────────────────────
|
|
437
441
|
|
|
438
442
|
/**
|
|
@@ -460,6 +464,16 @@ function resolveBaseUrl(cookieData, defaultBaseUrl) {
|
|
|
460
464
|
|
|
461
465
|
// ── HTTP 请求工具 ─────────────────────────────────────
|
|
462
466
|
|
|
467
|
+
function collectResponseText(res, onEnd) {
|
|
468
|
+
const chunks = [];
|
|
469
|
+
res.on('data', (chunk) => {
|
|
470
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
471
|
+
});
|
|
472
|
+
res.on('end', () => {
|
|
473
|
+
onEnd(Buffer.concat(chunks).toString('utf8'));
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
|
|
463
477
|
/**
|
|
464
478
|
* 发送 HTTP POST 请求(application/x-www-form-urlencoded)
|
|
465
479
|
* @param {string} baseUrl
|
|
@@ -508,12 +522,18 @@ function httpPost(baseUrl, requestPath, postData, cookies, optionsOverride = {})
|
|
|
508
522
|
};
|
|
509
523
|
|
|
510
524
|
const req = requestModule.request(options, (res) => {
|
|
511
|
-
|
|
512
|
-
res.on('data', (chunk) => { data += chunk; });
|
|
513
|
-
res.on('end', () => {
|
|
525
|
+
collectResponseText(res, (data) => {
|
|
514
526
|
if (!optionsOverride.silentStatus) {
|
|
515
527
|
warn(t('common.http_status', res.statusCode));
|
|
516
528
|
}
|
|
529
|
+
if (isHttpRedirectStatus(res.statusCode)) {
|
|
530
|
+
resolve({
|
|
531
|
+
__needLogin: true,
|
|
532
|
+
__httpStatus: res.statusCode,
|
|
533
|
+
__location: res.headers.location || '',
|
|
534
|
+
});
|
|
535
|
+
return;
|
|
536
|
+
}
|
|
517
537
|
try {
|
|
518
538
|
const parsed = JSON.parse(data);
|
|
519
539
|
if (isLoginExpired(parsed)) {
|
|
@@ -593,12 +613,18 @@ function httpGet(baseUrl, requestPath, queryParams, cookies, optionsOverride = {
|
|
|
593
613
|
};
|
|
594
614
|
|
|
595
615
|
const req = requestModule.request(options, (res) => {
|
|
596
|
-
|
|
597
|
-
res.on('data', (chunk) => { data += chunk; });
|
|
598
|
-
res.on('end', () => {
|
|
616
|
+
collectResponseText(res, (data) => {
|
|
599
617
|
if (!optionsOverride.silentStatus) {
|
|
600
618
|
warn(t('common.http_status', res.statusCode));
|
|
601
619
|
}
|
|
620
|
+
if (isHttpRedirectStatus(res.statusCode)) {
|
|
621
|
+
resolve({
|
|
622
|
+
__needLogin: true,
|
|
623
|
+
__httpStatus: res.statusCode,
|
|
624
|
+
__location: res.headers.location || '',
|
|
625
|
+
});
|
|
626
|
+
return;
|
|
627
|
+
}
|
|
602
628
|
try {
|
|
603
629
|
const parsed = JSON.parse(data);
|
|
604
630
|
if (isLoginExpired(parsed)) {
|
package/package.json
CHANGED
|
@@ -17,6 +17,7 @@ const SKILL_COVERAGE = {
|
|
|
17
17
|
'yida-business-rule': { level: 'opt-in', reason: 'business association rules mutate form event configuration; validate in a dedicated real-form/UI stage before adding to deterministic shared E2E' },
|
|
18
18
|
'yida-chart': { level: 'real-e2e', stages: ['report', 'dashboard'], tests: ['report chart config generation'] },
|
|
19
19
|
'yida-connector': { level: 'offline', stages: ['connector-local'], commands: ['connector gen-template', 'connector parse-api'] },
|
|
20
|
+
'yida-connector-safe-actions': { level: 'offline', stages: ['connector-local'], commands: ['connector parse-api', 'connector test --action <operationId>'], reason: 'skill documents conservative HTTP connector action generation and repair workflow; shared E2E should validate local parsing without mutating tenant connectors' },
|
|
20
21
|
'yida-corp-efficiency': { level: 'offline-unit', tests: ['tests/corp-efficiency.test.js'], reason: 'enterprise efficiency queries and notify mutations are not safe for shared real org E2E' },
|
|
21
22
|
'yida-corp-manager': { level: 'offline-unit', tests: ['tests/corp-manager.test.js'], reason: 'enterprise admin mutations are not safe for shared real org E2E' },
|
|
22
23
|
'yida-create-app': { level: 'real-e2e', stages: ['app'], commands: ['create-app'] },
|
package/yida-skills/SKILL.md
CHANGED
|
@@ -186,6 +186,7 @@ openyida copy
|
|
|
186
186
|
| `yida-integration` | `skills/yida-integration/SKILL.md` | 集成自动化逻辑流(创建/列表/启停) | `openyida integration <create\|list\|enable\|disable> ...` |
|
|
187
187
|
| `yida-business-rule` | `skills/yida-business-rule/SKILL.md` | 表单业务关联规则高级函数(INSERT/UPDATE/DELETE/UPSERT) | 详见 SKILL.md |
|
|
188
188
|
| `yida-connector` | `skills/yida-connector/SKILL.md` | HTTP 连接器创建、测试与动作管理 | `openyida connector smart-create <配置>` |
|
|
189
|
+
| `yida-connector-safe-actions` | `skills/yida-connector-safe-actions/SKILL.md` | 从前后端接口安全生成 HTTP 连接器执行动作,并修复测试后动作消失问题 | `openyida connector add-action --operations <动作JSON> --connector-id <id> --confirm` |
|
|
189
190
|
| `sls-log-workbench` | `skills/sls-log-workbench/SKILL.md` | SLS 日志查询工作台排查(内部技术支持) | 详见 SKILL.md |
|
|
190
191
|
| `yida-dashboard` | `skills/yida-dashboard/SKILL.md` | 经营看板/驾驶舱/数据大屏完整产品化交付(单屏控制塔+宜搭待办连接器真实钉钉待办闭环+卡片截图+组织内短链) | 详见 SKILL.md |
|
|
191
192
|
| `yida-chart` | `skills/yida-chart/SKILL.md` | 报表可视化(ECharts 图表 + 数据聚合) | 详见 SKILL.md |
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: yida-connector-safe-actions
|
|
3
|
+
description: 宜搭 HTTP 连接器执行动作安全生成与修复。适用于从前端 API 文件、后端 Controller/接口定义生成 OpenYida 连接器操作,或修复“点击测试后报错、所有操作消失”的连接器动作配置问题。不适用于创建连接器本体(应使用 yida-connector),也不适用于配置集成自动化逻辑流(应使用 yida-integration)。
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# 宜搭 HTTP 连接器执行动作安全生成
|
|
7
|
+
|
|
8
|
+
## 适用场景
|
|
9
|
+
|
|
10
|
+
当用户需要把已有系统接口接入宜搭 HTTP 连接器时使用本技能,尤其适用于:
|
|
11
|
+
|
|
12
|
+
- 已有连接器,需要继续添加“执行动作”
|
|
13
|
+
- 用户提供前端 API 文件和后端 Controller/接口定义文件
|
|
14
|
+
- 从 Vue/React API wrapper、ASP.NET Controller、Spring Controller 等代码中提取接口
|
|
15
|
+
- 点击宜搭连接器测试面板时报错,刷新后动作列表为空
|
|
16
|
+
- `openyida connector list-actions <connector-id>` 返回 0 个动作,但连接器仍存在
|
|
17
|
+
- 需要说明“如何正确使用 OpenYida 生成 HTTP 连接器动作”
|
|
18
|
+
|
|
19
|
+
如果只是创建连接器、配置鉴权、管理连接器账号,优先使用 `yida-connector`。
|
|
20
|
+
|
|
21
|
+
## 核心原则
|
|
22
|
+
|
|
23
|
+
- 先读源码再生成动作,不要凭空编造接口路径、参数或 action-id。
|
|
24
|
+
- 默认只生成前端 API 文件实际暴露/调用的接口,除非用户明确要求覆盖后端全部接口。
|
|
25
|
+
- 对未知响应结构保持保守,先只输出根对象 `Response`,避免宜搭测试面板解析复杂输出结构时报错。
|
|
26
|
+
- Windows PowerShell 读取中文 JSON 时必须显式使用 UTF-8。
|
|
27
|
+
- 修改后必须执行“添加动作 -> 列表验证 -> CLI 测试 -> 再次列表验证”的闭环。
|
|
28
|
+
- 如果一次错误配置导致动作被清空,应重建完整动作列表,而不是只追加缺失动作。
|
|
29
|
+
|
|
30
|
+
## 推荐流程
|
|
31
|
+
|
|
32
|
+
### 1. 读取接口来源
|
|
33
|
+
|
|
34
|
+
同时读取用户提供的前端 API 文件和后端接口定义文件。
|
|
35
|
+
|
|
36
|
+
前端 API 文件用于确认“哪些接口真的要暴露给宜搭连接器”;后端文件用于确认 method、route、query/path/body 参数和默认值。
|
|
37
|
+
|
|
38
|
+
### 2. 查看连接器状态
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
openyida connector detail <connector-id>
|
|
42
|
+
openyida connector list-actions <connector-id>
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
记录以下信息:
|
|
46
|
+
|
|
47
|
+
- 连接器 ID
|
|
48
|
+
- 连接器域名、协议、基础路径、鉴权方式
|
|
49
|
+
- 当前已有动作列表
|
|
50
|
+
- 是否已经出现动作被清空
|
|
51
|
+
|
|
52
|
+
### 3. 生成动作 JSON 文件
|
|
53
|
+
|
|
54
|
+
建议放在当前项目:
|
|
55
|
+
|
|
56
|
+
```text
|
|
57
|
+
.cache/openyida/connector-actions/<业务名>-actions.json
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
动作 ID 建议使用顺序编号:
|
|
61
|
+
|
|
62
|
+
```json
|
|
63
|
+
"id": "operation-1"
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
动作调用名使用前端函数名或后端 Action 名:
|
|
67
|
+
|
|
68
|
+
```json
|
|
69
|
+
"operationId": "getUserDtuSns"
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### 4. 校验 JSON 编码
|
|
73
|
+
|
|
74
|
+
Windows PowerShell 必须加 `-Encoding UTF8`:
|
|
75
|
+
|
|
76
|
+
```powershell
|
|
77
|
+
Get-Content -Raw -Encoding UTF8 .cache\openyida\connector-actions\<业务名>-actions.json | ConvertFrom-Json | Out-Null
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
不要使用默认 `Get-Content` 校验中文 JSON,默认编码可能导致误判或乱码。
|
|
81
|
+
|
|
82
|
+
### 5. 添加动作
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
openyida connector add-action --operations .cache/openyida/connector-actions/<业务名>-actions.json --connector-id <connector-id> --confirm
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### 6. 验证动作存在
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
openyida connector list-actions <connector-id>
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### 7. 用 CLI 测试动作
|
|
95
|
+
|
|
96
|
+
CLI 测试时 `--action` 使用 `operationId`,不是顺序编号 `operation-1`:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
openyida connector test --connector-id <connector-id> --action <operationId>
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
测试后再次查询动作列表,确认动作没有被清空:
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
openyida connector list-actions <connector-id>
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## 安全动作格式
|
|
109
|
+
|
|
110
|
+
### 无参数 GET 动作
|
|
111
|
+
|
|
112
|
+
```json
|
|
113
|
+
{
|
|
114
|
+
"id": "operation-1",
|
|
115
|
+
"operationId": "getAccessToken",
|
|
116
|
+
"summary": "获取三色灯 Token",
|
|
117
|
+
"description": "获取三色灯 Token",
|
|
118
|
+
"url": "api/TriColorLamp/GetAccessToken",
|
|
119
|
+
"method": "get",
|
|
120
|
+
"inputs": [],
|
|
121
|
+
"parameters": {},
|
|
122
|
+
"responses": {
|
|
123
|
+
"type": "object",
|
|
124
|
+
"properties": {}
|
|
125
|
+
},
|
|
126
|
+
"outputs": [
|
|
127
|
+
{
|
|
128
|
+
"defaultValue": "{}",
|
|
129
|
+
"desc": "响应体结构",
|
|
130
|
+
"name": "Response",
|
|
131
|
+
"paramType": "Object",
|
|
132
|
+
"required": false
|
|
133
|
+
}
|
|
134
|
+
],
|
|
135
|
+
"origin": true
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### 带 Query 参数的 GET 动作
|
|
140
|
+
|
|
141
|
+
```json
|
|
142
|
+
{
|
|
143
|
+
"id": "operation-2",
|
|
144
|
+
"operationId": "getDtuSnData",
|
|
145
|
+
"summary": "获取单设备三色灯数据",
|
|
146
|
+
"description": "根据 dtuSn 和日期获取三色灯数据",
|
|
147
|
+
"url": "api/TriColorLamp/GetDtuSnData",
|
|
148
|
+
"method": "get",
|
|
149
|
+
"inputs": [
|
|
150
|
+
{
|
|
151
|
+
"childList": [
|
|
152
|
+
{
|
|
153
|
+
"componentName": "TextField",
|
|
154
|
+
"desc": "设备 dtuSn",
|
|
155
|
+
"name": "dtuSn",
|
|
156
|
+
"queryDefaultValue": {
|
|
157
|
+
"paramType": "fixedValue",
|
|
158
|
+
"defaultValue": ""
|
|
159
|
+
},
|
|
160
|
+
"required": true
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
"componentName": "TextField",
|
|
164
|
+
"desc": "查询日期,可为空",
|
|
165
|
+
"name": "date",
|
|
166
|
+
"queryDefaultValue": {
|
|
167
|
+
"paramType": "fixedValue",
|
|
168
|
+
"defaultValue": ""
|
|
169
|
+
},
|
|
170
|
+
"required": false
|
|
171
|
+
}
|
|
172
|
+
],
|
|
173
|
+
"desc": "请求参数",
|
|
174
|
+
"name": "Query",
|
|
175
|
+
"paramType": "Object",
|
|
176
|
+
"required": false
|
|
177
|
+
}
|
|
178
|
+
],
|
|
179
|
+
"parameters": {
|
|
180
|
+
"query": [
|
|
181
|
+
{
|
|
182
|
+
"name": "dtuSn",
|
|
183
|
+
"type": "string",
|
|
184
|
+
"required": true,
|
|
185
|
+
"description": "设备 dtuSn",
|
|
186
|
+
"queryDefaultValue": {
|
|
187
|
+
"paramType": "fixedValue",
|
|
188
|
+
"defaultValue": ""
|
|
189
|
+
}
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
"name": "date",
|
|
193
|
+
"type": "string",
|
|
194
|
+
"required": false,
|
|
195
|
+
"description": "查询日期,可为空",
|
|
196
|
+
"queryDefaultValue": {
|
|
197
|
+
"paramType": "fixedValue",
|
|
198
|
+
"defaultValue": ""
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
]
|
|
202
|
+
},
|
|
203
|
+
"responses": {
|
|
204
|
+
"type": "object",
|
|
205
|
+
"properties": {}
|
|
206
|
+
},
|
|
207
|
+
"outputs": [
|
|
208
|
+
{
|
|
209
|
+
"defaultValue": "{}",
|
|
210
|
+
"desc": "响应体结构",
|
|
211
|
+
"name": "Response",
|
|
212
|
+
"paramType": "Object",
|
|
213
|
+
"required": false
|
|
214
|
+
}
|
|
215
|
+
],
|
|
216
|
+
"origin": true
|
|
217
|
+
}
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
## 字段规则
|
|
221
|
+
|
|
222
|
+
| 字段 | 推荐写法 |
|
|
223
|
+
| --- | --- |
|
|
224
|
+
| `id` | 使用 `operation-1`、`operation-2` 等顺序编号 |
|
|
225
|
+
| `operationId` | 使用前端函数名或后端 Action 名,例如 `getDtuSnData` |
|
|
226
|
+
| `summary` | 中文短名称,用于宜搭界面展示 |
|
|
227
|
+
| `description` | 一句话说明动作用途 |
|
|
228
|
+
| `url` | 不带域名,只写连接器域名后的相对路径 |
|
|
229
|
+
| `method` | 小写,例如 `get`、`post`、`put` |
|
|
230
|
+
| `inputs` | GET 参数只放 `Query`,不要放 `Body` |
|
|
231
|
+
| `parameters.query` | 与 `inputs[].childList[]` 中的 query 参数保持一致 |
|
|
232
|
+
| `queryDefaultValue` | Query 参数建议在 `inputs` 和 `parameters` 两处都写 |
|
|
233
|
+
| `outputs` | 修复或首次生成时只保留根对象 `Response` |
|
|
234
|
+
|
|
235
|
+
## 避免测试面板崩溃
|
|
236
|
+
|
|
237
|
+
以下写法容易导致宜搭连接器测试面板解析异常,应谨慎使用:
|
|
238
|
+
|
|
239
|
+
- 未确认平台兼容时,在输出字段里展开复杂 `Code`、`Message`、`Data` 子字段
|
|
240
|
+
- 给输入/输出叶子节点添加非必要的 `label`
|
|
241
|
+
- GET 动作配置 `Body`
|
|
242
|
+
- `inputs` 与 `parameters` 中的 query 参数不一致
|
|
243
|
+
- 中文 JSON 未按 UTF-8 读取或保存
|
|
244
|
+
- 只追加部分动作,遗漏原有 Token/Test 动作,导致重建后动作列表不完整
|
|
245
|
+
|
|
246
|
+
当用户反馈“点击测试后操作都不见了”,优先按修复流程处理,不要继续追加同一份风险 JSON。
|
|
247
|
+
|
|
248
|
+
## ASP.NET Controller 映射规则
|
|
249
|
+
|
|
250
|
+
对于 ASP.NET Controller:
|
|
251
|
+
|
|
252
|
+
```csharp
|
|
253
|
+
[Route("api/[controller]/[action]")]
|
|
254
|
+
public class TriColorLampController : ControllerBase
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
路径映射为:
|
|
258
|
+
|
|
259
|
+
```text
|
|
260
|
+
api/TriColorLamp/<ActionName>
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
规则:
|
|
264
|
+
|
|
265
|
+
- `[HttpGet]`、`[HttpPost]`、`[HttpPut]` 映射到对应小写 method
|
|
266
|
+
- `[FromQuery]` 参数映射为 `Query`
|
|
267
|
+
- 有默认值或可空参数,例如 `string date = null`,映射为 `required: false`
|
|
268
|
+
- 无默认值的必填参数映射为 `required: true`
|
|
269
|
+
|
|
270
|
+
## 故障修复流程
|
|
271
|
+
|
|
272
|
+
当连接器动作被清空时:
|
|
273
|
+
|
|
274
|
+
1. 执行 `openyida connector detail <connector-id>`,确认连接器仍存在。
|
|
275
|
+
2. 执行 `openyida connector list-actions <connector-id>`,确认动作是否为 0。
|
|
276
|
+
3. 从前端 API 文件和后端 Controller 重新生成完整动作列表。
|
|
277
|
+
4. 如果原来有 Token/Test 动作,也要一起放回 JSON。
|
|
278
|
+
5. 使用保守输出结构,不展开复杂响应字段。
|
|
279
|
+
6. 执行 `add-action --confirm` 重建动作。
|
|
280
|
+
7. 执行 `list-actions` 验证动作数量。
|
|
281
|
+
8. 用 `connector test --action <operationId>` 测试至少一个无参数动作。
|
|
282
|
+
9. 测试后再次执行 `list-actions`,确认动作没有再次消失。
|