openyida 2026.8.5 → 2026.8.12-1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -3
- package/bin/yida.js +8 -1
- package/lib/app/app-lifecycle.js +128 -0
- package/lib/app/canvas-compile.js +0 -19
- package/lib/app/get-schema.js +123 -2
- package/lib/app/page-linter.js +0 -5
- package/lib/core/agent-capabilities.js +75 -20
- package/lib/core/command-manifest.js +9 -3
- package/lib/core/locales/en.js +15 -1
- package/lib/core/locales/zh.js +15 -1
- package/package.json +1 -1
- package/yida-skills/SKILL.md +8 -23
- package/yida-skills/skills/yida-app/SKILL.md +17 -29
- package/yida-skills/skills/yida-app-lifecycle/SKILL.md +56 -0
- package/yida-skills/skills/yida-canvas-custom-page/references/canvas-style-implementation-guide.md +2 -2
- package/yida-skills/skills/yida-canvas-custom-page/references/page-generation-guide.md +4 -4
- package/yida-skills/skills/yida-design/SKILL.md +1 -1
- package/yida-skills/skills/yida-design/references/asset-workflow.md +1 -1
- package/yida-skills/skills/yida-design/references/page-quality-gates.md +4 -4
- package/yida-skills/skills/yida-design/references/style-design-selection.md +48 -29
- package/yida-skills/skills/yida-design/references/style-designs/registry.md +56 -30
- package/yida-skills/skills/yida-design/references/visual-scaffold-recipes.md +2 -2
- package/yida-skills/skills/yida-design/workflow/output-prd.md +3 -3
- package/yida-skills/skills/yida-design/workflow/step-3-information-architecture.md +2 -2
- package/yida-skills/skills/yida-design/workflow/step-4-wireframe-interaction.md +4 -4
- package/yida-skills/skills/yida-design/workflow/step-5-visual-states.md +2 -2
- package/yida-skills/skills/yida-design/workflow/step-6-handoff.md +1 -1
- package/yida-skills/skills-index.json +38 -0
- package/lib/app/page-richness-guard.js +0 -108
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
OpenYida connects AI coding agents with Yida's low-code platform, so developers can create apps, forms, workflows, custom pages, reports, integrations, and deployment configuration from a normal chat-driven development workflow.
|
|
10
10
|
|
|
11
|
-
[Quick Start](#quick-start) · [
|
|
11
|
+
[Quick Start](#quick-start) · [帮助网站&文档](https://demo.aliwork.com/o/openyida/helpCenter) · [Capabilities](#capabilities) · [Full Capability List](https://demo.aliwork.com/o/openyida/helpCenter?openyidaPath=capabilities) · [CLI Reference](https://demo.aliwork.com/o/openyida/helpCenter?openyidaPath=features/skills) · [Examples](https://demo.aliwork.com/o/openyida/helpCenter?openyidaRoute=showcase) · [Contributing](./CONTRIBUTING.md) · [Changelog](https://demo.aliwork.com/o/openyida/helpCenter?openyidaRoute=changelog)
|
|
12
12
|
|
|
13
13
|
[](https://www.npmjs.com/package/openyida)
|
|
14
14
|
[](https://www.npmjs.com/package/openyida)
|
|
@@ -16,7 +16,7 @@ OpenYida connects AI coding agents with Yida's low-code platform, so developers
|
|
|
16
16
|
[](./LICENSE)
|
|
17
17
|
[](https://nodejs.org)
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
**帮助网站&文档:** [帮助网站&文档](https://demo.aliwork.com/o/openyida/helpCenter)
|
|
20
20
|
|
|
21
21
|
[English README](./README.md) · [简体中文 README](./README_zhCN.md)
|
|
22
22
|
|
|
@@ -163,7 +163,7 @@ openyida/
|
|
|
163
163
|
|
|
164
164
|
## Capabilities
|
|
165
165
|
|
|
166
|
-
For a user-facing list of supported features and matching CLI commands, see [OpenYida 功能完整列表](
|
|
166
|
+
For a user-facing list of supported features and matching CLI commands, see [OpenYida 功能完整列表](https://demo.aliwork.com/o/openyida/helpCenter?openyidaPath=capabilities).
|
|
167
167
|
|
|
168
168
|
### Application and Form Management
|
|
169
169
|
|
|
@@ -194,6 +194,8 @@ Custom page source is authored directly from PRD and design guidance. Code Canva
|
|
|
194
194
|
|
|
195
195
|
For member, department, attachment, and image upload components, choose the page chain first. Code Canvas pages should follow `yida-canvas-custom-page/references/native-components-bridge.md` for feature detection, fallback, and value normalization. 普通自定义页面 JSX/Jsx pages should use `.oyd.jsx`, read `yida-custom-page/references/component-jsx-guide.md`, and read `attachment-upload-guide.md` when upload fields are involved.
|
|
196
196
|
|
|
197
|
+
For the current AI custom page native-component support matrix, see [Yida native components supported by AI custom pages](https://demo.aliwork.com/o/openyida/sample/canvas-native-components).
|
|
198
|
+
|
|
197
199
|
### Workflow, Data, and Permissions
|
|
198
200
|
|
|
199
201
|
```bash
|
|
@@ -369,6 +371,8 @@ Run `openyida --help` or `openyida <command> --help` for detailed usage.
|
|
|
369
371
|
| `openyida corp-efficiency [overview\|details\|detail\|groups\|notify] [options] [--open\|--no-open]` | Query enterprise efficiency overview and detail reports |
|
|
370
372
|
| `openyida create-app "<name>"\|--name <name> [options] [--locale zh_CN\|en_US\|ja_JP] [--open\|--no-open]` | Create a Yida app |
|
|
371
373
|
| `openyida update-app <appType> [--name "..."] [--layout slide\|ver] [--theme deepBlue]` | Update app info |
|
|
374
|
+
| `openyida app-online <appType> [--to-ding-app-center] [--show-app-center]` | Enable a Yida app |
|
|
375
|
+
| `openyida app-offline <appType> [--to-ding-app-center] [--show-app-center]` | Disable a Yida app |
|
|
372
376
|
| `openyida nav-group <list\|create\|rename\|delete\|move\|order\|auto-order\|hide\|show> <appType> ...` | Manage app sidebar navigation groups |
|
|
373
377
|
| `openyida app-permission <get\|set\|add\|remove\|search-user> ...` | Manage app primary, data, and developer admins |
|
|
374
378
|
| `openyida i18n <overview\|config\|languages\|list\|upsert\|delete\|translate\|translate-all\|upgrade> <appType> ...` | Manage app multilingual copy and language config |
|
|
@@ -637,7 +641,9 @@ Latest contributors: [DDlixin1](https://github.com/DDlixin1), [fcloud](https://g
|
|
|
637
641
|
<p>
|
|
638
642
|
<a href="https://github.com/yize"><img src="https://github.com/yize.png?size=48" width="48" height="48" alt="九神" title="九神" /></a>
|
|
639
643
|
<a href="https://github.com/alex-mm"><img src="https://github.com/alex-mm.png?size=48" width="48" height="48" alt="天晟" title="天晟" /></a>
|
|
644
|
+
<a href="https://github.com/fryaninc"><img src="https://github.com/fryaninc.png?size=48" width="48" height="48" alt="New Core architecture, Core development, user issue support, skill maintenance and Help & Show Case Center 构建者" title="New Core architecture, Core development, user issue support, skill maintenance and Help & Show Case Center 构建者" /></a>
|
|
640
645
|
<a href="https://github.com/DDlixin1"><img src="https://github.com/DDlixin1.png?size=48" width="48" height="48" alt="DDlixin1" title="DDlixin1" /></a>
|
|
646
|
+
<a href="https://github.com/xhy-ali"><img src="https://github.com/xhy-ali.png?size=48" width="48" height="48" alt="登录态开发者" title="登录态开发者" /></a>
|
|
641
647
|
<a href="https://github.com/fcloud"><img src="https://github.com/fcloud.png?size=48" width="48" height="48" alt="Aiden Wu (fcloud)" title="Aiden Wu (fcloud)" /></a>
|
|
642
648
|
<a href="https://github.com/nicky1108"><img src="https://github.com/nicky1108.png?size=48" width="48" height="48" alt="nicky1108" title="nicky1108" /></a>
|
|
643
649
|
<a href="https://github.com/angelinheys"><img src="https://github.com/angelinheys.png?size=48" width="48" height="48" alt="angelinheys" title="angelinheys" /></a>
|
|
@@ -654,6 +660,8 @@ Latest contributors: [DDlixin1](https://github.com/DDlixin1), [fcloud](https://g
|
|
|
654
660
|
<a href="https://github.com/key-668"><img src="https://github.com/key-668.png?size=48" width="48" height="48" alt="再不喝汽水" title="再不喝汽水" /></a>
|
|
655
661
|
<a href="https://github.com/dongbeixiaohuo"><img src="https://github.com/dongbeixiaohuo.png?size=48" width="48" height="48" alt="dongbeixiaohuo" title="dongbeixiaohuo" /></a>
|
|
656
662
|
<a href="https://github.com/nandanadileep"><img src="https://github.com/nandanadileep.png?size=48" width="48" height="48" alt="nandanadileep" title="nandanadileep" /></a>
|
|
663
|
+
<a href="https://github.com/hj837990198"><img src="https://github.com/hj837990198.png?size=48" width="48" height="48" alt="skill 测试" title="skill 测试" /></a>
|
|
664
|
+
<a href="https://github.com/chengminchao-create"><img src="https://github.com/chengminchao-create.png?size=48" width="48" height="48" alt="skill共建" title="skill共建" /></a>
|
|
657
665
|
</p>
|
|
658
666
|
|
|
659
667
|
<!-- openyida-contributors:end -->
|
package/bin/yida.js
CHANGED
|
@@ -143,7 +143,7 @@ function printHelp() {
|
|
|
143
143
|
console.log(` ${DIM}${RESET} openyida create-form create APP_XXX "${t('help.quickstart_form_name')}" .cache/openyida/forms/fields.json`);
|
|
144
144
|
console.log(` ${DIM}${RESET} openyida dws contact user search --keyword "张三"`);
|
|
145
145
|
console.log('');
|
|
146
|
-
console.log(` ${DIM}${t('help.docs')} https://
|
|
146
|
+
console.log(` ${DIM}${t('help.docs')} https://demo.aliwork.com/o/openyida/helpCenter · https://github.com/openyida/openyida${RESET}`);
|
|
147
147
|
console.log('');
|
|
148
148
|
}
|
|
149
149
|
|
|
@@ -841,6 +841,13 @@ async function main() {
|
|
|
841
841
|
break;
|
|
842
842
|
}
|
|
843
843
|
|
|
844
|
+
case 'app-online':
|
|
845
|
+
case 'app-offline': {
|
|
846
|
+
const { run: runAppLifecycle } = require('../lib/app/app-lifecycle');
|
|
847
|
+
await runAppLifecycle(command === 'app-online' ? 'online' : 'offline', args);
|
|
848
|
+
break;
|
|
849
|
+
}
|
|
850
|
+
|
|
844
851
|
case 'nav-group':
|
|
845
852
|
case 'group': {
|
|
846
853
|
const { run: runNavGroup } = require('../lib/app/nav-group');
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* app-lifecycle.js - 启用或停用宜搭应用
|
|
3
|
+
*
|
|
4
|
+
* 用法:
|
|
5
|
+
* openyida app-online <appType> [--to-ding-app-center] [--show-app-center]
|
|
6
|
+
* openyida app-offline <appType> [--to-ding-app-center] [--show-app-center]
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
const querystring = require('querystring');
|
|
12
|
+
const { httpPost, requestWithAutoLogin } = require('../core/utils');
|
|
13
|
+
const { createAuthRef } = require('../core/yida-client');
|
|
14
|
+
const { t } = require('../core/i18n');
|
|
15
|
+
const { throwCommandError, throwUsage } = require('../core/command-errors');
|
|
16
|
+
|
|
17
|
+
const ACTIONS = Object.freeze({
|
|
18
|
+
online: Object.freeze({ endpoint: 'onlineApp', api: 'App.goOnline' }),
|
|
19
|
+
offline: Object.freeze({ endpoint: 'offlineApp', api: 'App.goOffline' }),
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
function parseArgs(args = []) {
|
|
23
|
+
const params = {
|
|
24
|
+
appType: null,
|
|
25
|
+
toDingAppCenter: false,
|
|
26
|
+
showAppCenter: false,
|
|
27
|
+
help: false,
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
for (const arg of args) {
|
|
31
|
+
if (arg === '--help' || arg === '-h') {
|
|
32
|
+
params.help = true;
|
|
33
|
+
} else if (arg === '--to-ding-app-center') {
|
|
34
|
+
params.toDingAppCenter = true;
|
|
35
|
+
} else if (arg === '--show-app-center') {
|
|
36
|
+
params.showAppCenter = true;
|
|
37
|
+
} else if (!arg.startsWith('-') && !params.appType) {
|
|
38
|
+
params.appType = arg;
|
|
39
|
+
} else {
|
|
40
|
+
throwUsage(t('app_lifecycle.invalid_argument', arg));
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return params;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function buildRequestPath(action, appType, stamp = Date.now()) {
|
|
48
|
+
const config = ACTIONS[action];
|
|
49
|
+
if (!config) {
|
|
50
|
+
throw new Error(t('app_lifecycle.invalid_action', action));
|
|
51
|
+
}
|
|
52
|
+
return `/dingtalk/web/${encodeURIComponent(appType)}/query/app/${config.endpoint}.json` +
|
|
53
|
+
`?_api=${encodeURIComponent(config.api)}&_mock=false&_stamp=${stamp}`;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function buildPostData(params, authRef) {
|
|
57
|
+
return querystring.stringify({
|
|
58
|
+
_csrf_token: authRef.csrfToken || '',
|
|
59
|
+
_locale_time_zone_offset: '28800000',
|
|
60
|
+
isToDingAppCenter: params.toDingAppCenter ? 'y' : 'n',
|
|
61
|
+
showAppCenter: params.showAppCenter ? 'y' : 'n',
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async function changeAppLifecycle(action, params, authRef = createAuthRef()) {
|
|
66
|
+
const response = await requestWithAutoLogin((auth) => httpPost(
|
|
67
|
+
auth.baseUrl,
|
|
68
|
+
buildRequestPath(action, params.appType),
|
|
69
|
+
buildPostData(params, auth),
|
|
70
|
+
auth.cookies
|
|
71
|
+
), authRef);
|
|
72
|
+
|
|
73
|
+
if (!response || response.success !== true || response.content !== true) {
|
|
74
|
+
const errorMsg = response && (response.errorMsg || response.message || response.errorCode);
|
|
75
|
+
throwCommandError(errorMsg || t('app_lifecycle.request_failed'), {
|
|
76
|
+
code: action === 'online' ? 'APP_ONLINE_FAILED' : 'APP_OFFLINE_FAILED',
|
|
77
|
+
details: { action, appType: params.appType },
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
success: true,
|
|
83
|
+
action,
|
|
84
|
+
appType: params.appType,
|
|
85
|
+
isToDingAppCenter: params.toDingAppCenter,
|
|
86
|
+
showAppCenter: params.showAppCenter,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function printUsage(action) {
|
|
91
|
+
const { usage } = require('../core/chalk');
|
|
92
|
+
usage(t(`app_lifecycle.${action}_usage`), t(`app_lifecycle.${action}_example`));
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async function run(action, args = []) {
|
|
96
|
+
if (!ACTIONS[action]) {
|
|
97
|
+
throwCommandError(t('app_lifecycle.invalid_action', action), {
|
|
98
|
+
code: 'APP_LIFECYCLE_INVALID_ACTION',
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const params = parseArgs(args);
|
|
103
|
+
if (params.help) {
|
|
104
|
+
printUsage(action);
|
|
105
|
+
return { success: true, help: true };
|
|
106
|
+
}
|
|
107
|
+
if (!params.appType) {
|
|
108
|
+
printUsage(action);
|
|
109
|
+
throwUsage(t('app_lifecycle.missing_app_type'), t(`app_lifecycle.${action}_usage`), {
|
|
110
|
+
code: 'APP_LIFECYCLE_USAGE',
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const output = await changeAppLifecycle(action, params);
|
|
115
|
+
const { result } = require('../core/chalk');
|
|
116
|
+
result(true, t(`app_lifecycle.${action}_success`), [['appType', params.appType]]);
|
|
117
|
+
console.log(JSON.stringify(output));
|
|
118
|
+
return output;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
module.exports = {
|
|
122
|
+
ACTIONS,
|
|
123
|
+
buildPostData,
|
|
124
|
+
buildRequestPath,
|
|
125
|
+
changeAppLifecycle,
|
|
126
|
+
parseArgs,
|
|
127
|
+
run,
|
|
128
|
+
};
|
|
@@ -46,10 +46,6 @@ const {
|
|
|
46
46
|
findFormDetailLinkIssues,
|
|
47
47
|
formatFormDetailLinkMessage,
|
|
48
48
|
} = require('./form-detail-link-guard');
|
|
49
|
-
const {
|
|
50
|
-
findPageRichnessIssues,
|
|
51
|
-
formatPageRichnessMessage,
|
|
52
|
-
} = require('./page-richness-guard');
|
|
53
49
|
|
|
54
50
|
/**
|
|
55
51
|
* 依赖 → window 别名白名单,用于把 import 改写到运行时全局对象。
|
|
@@ -621,21 +617,6 @@ function compileCanvasLocal(source, options = {}) {
|
|
|
621
617
|
},
|
|
622
618
|
});
|
|
623
619
|
}
|
|
624
|
-
const richnessIssues = findPageRichnessIssues(source);
|
|
625
|
-
if (richnessIssues.length > 0) {
|
|
626
|
-
const firstIssue = richnessIssues[0];
|
|
627
|
-
throw new CliError(formatPageRichnessMessage(firstIssue), {
|
|
628
|
-
code: 'OPENYIDA_CANVAS_CONTENT_BLOCKS_TOO_FEW',
|
|
629
|
-
details: {
|
|
630
|
-
stage: 'canvas_compile',
|
|
631
|
-
sourcePath: options.sourcePath || '',
|
|
632
|
-
line: firstIssue.line,
|
|
633
|
-
scene: firstIssue.scene,
|
|
634
|
-
count: firstIssue.count,
|
|
635
|
-
min: firstIssue.min,
|
|
636
|
-
},
|
|
637
|
-
});
|
|
638
|
-
}
|
|
639
620
|
assertNoBareDependencyGlobals(source, options);
|
|
640
621
|
assertNoManualDependencyGlobals(source, options);
|
|
641
622
|
|
package/lib/app/get-schema.js
CHANGED
|
@@ -187,6 +187,117 @@ function buildComponentAliasMaps(schemaResult) {
|
|
|
187
187
|
return { aliasByFieldId, fieldIdByAlias };
|
|
188
188
|
}
|
|
189
189
|
|
|
190
|
+
function parseJsonObject(value) {
|
|
191
|
+
if (typeof value !== 'string') {
|
|
192
|
+
return value && typeof value === 'object' ? value : null;
|
|
193
|
+
}
|
|
194
|
+
try {
|
|
195
|
+
const parsed = JSON.parse(value);
|
|
196
|
+
return parsed && typeof parsed === 'object' ? parsed : null;
|
|
197
|
+
} catch {
|
|
198
|
+
return null;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function resolveSchemaContent(schemaResult) {
|
|
203
|
+
if (!schemaResult) {
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
206
|
+
const content = schemaResult.content !== undefined ? schemaResult.content : schemaResult;
|
|
207
|
+
return parseJsonObject(content);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function addImportedModules(target, value) {
|
|
211
|
+
let modules = value;
|
|
212
|
+
if (typeof modules === 'string') {
|
|
213
|
+
const trimmed = modules.trim();
|
|
214
|
+
if (!trimmed) {
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
try {
|
|
218
|
+
modules = JSON.parse(trimmed);
|
|
219
|
+
} catch {
|
|
220
|
+
modules = [trimmed];
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
if (!Array.isArray(modules)) {
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
modules
|
|
227
|
+
.map(item => String(item || '').trim())
|
|
228
|
+
.filter(Boolean)
|
|
229
|
+
.forEach((item) => {
|
|
230
|
+
if (!target.includes(item)) {
|
|
231
|
+
target.push(item);
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function codeBytes(value) {
|
|
237
|
+
return typeof value === 'string' ? Buffer.byteLength(value, 'utf8') : 0;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function isComponentInstance(node) {
|
|
241
|
+
return !!(node && typeof node === 'object' && (node.id || node.props || node.children));
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function extractDisplayPageSummary(schemaResult) {
|
|
245
|
+
const content = resolveSchemaContent(schemaResult);
|
|
246
|
+
if (!content || typeof content !== 'object') {
|
|
247
|
+
return null;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const displayPage = {
|
|
251
|
+
hasYidaCodeCanvas: false,
|
|
252
|
+
hasNativeJsx: false,
|
|
253
|
+
runtimeCodeBytes: 0,
|
|
254
|
+
sourceCodeBytes: 0,
|
|
255
|
+
compiledCodeBytes: 0,
|
|
256
|
+
importedModules: [],
|
|
257
|
+
componentCount: 0,
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
function traverse(node) {
|
|
261
|
+
if (!node) {
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
if (Array.isArray(node)) {
|
|
265
|
+
node.forEach(traverse);
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
if (typeof node !== 'object') {
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if (node.componentName === 'YidaCodeCanvas' && isComponentInstance(node)) {
|
|
273
|
+
const props = node.props || {};
|
|
274
|
+
displayPage.hasYidaCodeCanvas = true;
|
|
275
|
+
displayPage.componentCount++;
|
|
276
|
+
displayPage.runtimeCodeBytes += codeBytes(props.runtimeCode);
|
|
277
|
+
displayPage.sourceCodeBytes += codeBytes(props.code);
|
|
278
|
+
addImportedModules(displayPage.importedModules, props.importedModules);
|
|
279
|
+
} else if (node.componentName === 'Jsx' && isComponentInstance(node)) {
|
|
280
|
+
displayPage.hasNativeJsx = true;
|
|
281
|
+
displayPage.componentCount++;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
Object.keys(node).forEach((key) => traverse(node[key]));
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
traverse(content.pages || content);
|
|
288
|
+
|
|
289
|
+
const module = content.actions && content.actions.module;
|
|
290
|
+
if (displayPage.hasNativeJsx && module && typeof module === 'object') {
|
|
291
|
+
displayPage.sourceCodeBytes += codeBytes(module.source);
|
|
292
|
+
displayPage.compiledCodeBytes += codeBytes(module.compiled);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
if (!displayPage.hasYidaCodeCanvas && !displayPage.hasNativeJsx) {
|
|
296
|
+
return null;
|
|
297
|
+
}
|
|
298
|
+
return displayPage;
|
|
299
|
+
}
|
|
300
|
+
|
|
190
301
|
function parsePositiveInt(value, fallback, min, max) {
|
|
191
302
|
const parsed = Number.parseInt(value, 10);
|
|
192
303
|
if (!Number.isFinite(parsed) || parsed < min) {
|
|
@@ -402,7 +513,7 @@ function printFieldSummary(result) {
|
|
|
402
513
|
|
|
403
514
|
function buildSchemaSummary(appType, formUuid, schemaResult, meta = {}) {
|
|
404
515
|
const fields = extractFieldSummary(schemaResult);
|
|
405
|
-
|
|
516
|
+
const summary = {
|
|
406
517
|
success: true,
|
|
407
518
|
appType,
|
|
408
519
|
formUuid,
|
|
@@ -410,6 +521,11 @@ function buildSchemaSummary(appType, formUuid, schemaResult, meta = {}) {
|
|
|
410
521
|
fieldCount: fields.length,
|
|
411
522
|
fields,
|
|
412
523
|
};
|
|
524
|
+
const displayPage = extractDisplayPageSummary(schemaResult);
|
|
525
|
+
if (displayPage) {
|
|
526
|
+
summary.displayPage = displayPage;
|
|
527
|
+
}
|
|
528
|
+
return summary;
|
|
413
529
|
}
|
|
414
530
|
|
|
415
531
|
function filterForms(forms, keyword) {
|
|
@@ -454,7 +570,7 @@ async function fetchSchemaRecord(appType, form, authRef, retries) {
|
|
|
454
570
|
try {
|
|
455
571
|
const result = await fetchSchema(appType, form.formUuid, authRef);
|
|
456
572
|
if (isSuccessfulSchemaResult(result)) {
|
|
457
|
-
|
|
573
|
+
const record = {
|
|
458
574
|
formUuid: form.formUuid,
|
|
459
575
|
formName: form.formName,
|
|
460
576
|
formType: form.formType,
|
|
@@ -464,6 +580,11 @@ async function fetchSchemaRecord(appType, form, authRef, retries) {
|
|
|
464
580
|
fieldSummary: extractFieldSummary(result),
|
|
465
581
|
schema: result,
|
|
466
582
|
};
|
|
583
|
+
const displayPage = extractDisplayPageSummary(result);
|
|
584
|
+
if (displayPage) {
|
|
585
|
+
record.displayPage = displayPage;
|
|
586
|
+
}
|
|
587
|
+
return record;
|
|
467
588
|
}
|
|
468
589
|
lastError = new Error(result ? result.errorMsg || t('common.unknown_error') : t('common.request_failed'));
|
|
469
590
|
} catch (error) {
|
package/lib/app/page-linter.js
CHANGED
|
@@ -9,7 +9,6 @@ const {
|
|
|
9
9
|
const { findBareCjkJsxTextIdentifiers } = require('./jsx-text-guard');
|
|
10
10
|
const { findDirectFormOpenIssues } = require('./form-open-guard');
|
|
11
11
|
const { findFormDetailLinkIssues } = require('./form-detail-link-guard');
|
|
12
|
-
const { findPageRichnessIssues } = require('./page-richness-guard');
|
|
13
12
|
const Babel = require('@babel/standalone');
|
|
14
13
|
|
|
15
14
|
const THEN_CALLBACK_LINE_LIMIT = 50;
|
|
@@ -866,10 +865,6 @@ function lintYidaSource(sourceCode, filePath) {
|
|
|
866
865
|
findFormDetailLinkIssues(sourceCode, { parserOptions: PARSER_OPTIONS }).forEach((issue) => {
|
|
867
866
|
pushIssue(errors, issue.line, 'form-detail-link', t('publish.lint_form_detail_link'), disableMap);
|
|
868
867
|
});
|
|
869
|
-
findPageRichnessIssues(sourceCode).forEach((issue) => {
|
|
870
|
-
pushIssue(errors, issue.line, 'content-blocks-too-few', t('publish.lint_content_blocks_too_few', issue.count, issue.min), disableMap);
|
|
871
|
-
});
|
|
872
|
-
|
|
873
868
|
return { errors, warnings };
|
|
874
869
|
}
|
|
875
870
|
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const crypto = require('crypto');
|
|
4
|
+
const fs = require('fs');
|
|
4
5
|
const path = require('path');
|
|
5
6
|
const { version } = require('../../package.json');
|
|
6
7
|
const { t } = require('./i18n');
|
|
7
8
|
const { buildCommandManifest } = require('./command-manifest');
|
|
8
9
|
const { buildEnvironmentSnapshot } = require('./env');
|
|
9
|
-
const { getAuthStatus } = require('./utils');
|
|
10
|
+
const { findProjectRoot, getAuthStatus } = require('./utils');
|
|
10
11
|
|
|
11
12
|
function redactLogin(login) {
|
|
12
13
|
const redacted = { ...login };
|
|
@@ -35,6 +36,21 @@ function hasEnvTokenCredential(env = process.env) {
|
|
|
35
36
|
);
|
|
36
37
|
}
|
|
37
38
|
|
|
39
|
+
function hasRuntimeProvisionedAccessToken(env = process.env) {
|
|
40
|
+
return isTruthyEnv(env.YIDA_AUTH_ENABLED) &&
|
|
41
|
+
!!String(env.OPENYIDA_ACCESS_TOKEN || '').trim();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function buildRuntimeProvisionedLoginStatus() {
|
|
45
|
+
return {
|
|
46
|
+
ok: true,
|
|
47
|
+
auth_mode: 'token',
|
|
48
|
+
auth_source: 'env',
|
|
49
|
+
status: 'ok',
|
|
50
|
+
can_auto_use: true,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
38
54
|
const BUILDER_CORE_COMMAND_IDS = Object.freeze([
|
|
39
55
|
'agent-capabilities',
|
|
40
56
|
'commands',
|
|
@@ -84,8 +100,10 @@ function buildAuthPath(login, env = process.env) {
|
|
|
84
100
|
const envTokenPresent = hasEnvTokenCredential(env);
|
|
85
101
|
const authSource = login.auth_source || (hostInjectedTokenMode || envTokenPresent ? 'env' : 'token_session');
|
|
86
102
|
const hostTokenEnvDetected = hostInjectedTokenMode || envTokenPresent || authSource === 'env';
|
|
87
|
-
|
|
88
|
-
|
|
103
|
+
const runtimeAuthProvisioned = hasRuntimeProvisionedAccessToken(env) &&
|
|
104
|
+
authSource === 'env' &&
|
|
105
|
+
login.status === 'ok';
|
|
106
|
+
const authPath = {
|
|
89
107
|
mode: login.auth_mode || 'token',
|
|
90
108
|
source: authSource,
|
|
91
109
|
can_auto_use: login.can_auto_use === true,
|
|
@@ -109,6 +127,10 @@ function buildAuthPath(login, env = process.env) {
|
|
|
109
127
|
? 'STOP_AND_REQUEST_HOST_TOKEN'
|
|
110
128
|
: 'RUN_OPENYIDA_LOGIN_IF_USER_APPROVES',
|
|
111
129
|
};
|
|
130
|
+
if (runtimeAuthProvisioned) {
|
|
131
|
+
authPath.runtime_auth_provisioned = true;
|
|
132
|
+
}
|
|
133
|
+
return authPath;
|
|
112
134
|
}
|
|
113
135
|
|
|
114
136
|
function buildBuilderPath(login, projectRoot, manifest, env = process.env) {
|
|
@@ -218,26 +240,30 @@ function compactBuilderPath(builderPath) {
|
|
|
218
240
|
const commandContract = builderPath.command_contract;
|
|
219
241
|
const resourceContext = builderPath.resource_context_resolution;
|
|
220
242
|
const paths = builderPath.paths;
|
|
243
|
+
const auth = {
|
|
244
|
+
mode: builderPath.auth.mode,
|
|
245
|
+
source: builderPath.auth.source,
|
|
246
|
+
can_auto_use: builderPath.auth.can_auto_use,
|
|
247
|
+
host_injected_token_mode: builderPath.auth.host_injected_token_mode,
|
|
248
|
+
host_token_env_detected: builderPath.auth.host_token_env_detected,
|
|
249
|
+
env_token_present: builderPath.auth.env_token_present,
|
|
250
|
+
interactive_login_allowed: builderPath.auth.interactive_login_allowed,
|
|
251
|
+
browser_session_auth_allowed: builderPath.auth.browser_session_auth_allowed,
|
|
252
|
+
auth_runtime: builderPath.auth.auth_runtime,
|
|
253
|
+
cookie_auth_supported: builderPath.auth.cookie_auth_supported,
|
|
254
|
+
cookie_check_required: builderPath.auth.cookie_check_required,
|
|
255
|
+
playwright_cookie_check_required: builderPath.auth.playwright_cookie_check_required,
|
|
256
|
+
qr_login_required: builderPath.auth.qr_login_required,
|
|
257
|
+
missing_token_action: builderPath.auth.missing_token_action,
|
|
258
|
+
};
|
|
259
|
+
if (builderPath.auth.runtime_auth_provisioned === true) {
|
|
260
|
+
auth.runtime_auth_provisioned = true;
|
|
261
|
+
}
|
|
221
262
|
|
|
222
263
|
return {
|
|
223
264
|
schema_version: builderPath.schema_version,
|
|
224
265
|
preflight: builderPath.preflight,
|
|
225
|
-
auth
|
|
226
|
-
mode: builderPath.auth.mode,
|
|
227
|
-
source: builderPath.auth.source,
|
|
228
|
-
can_auto_use: builderPath.auth.can_auto_use,
|
|
229
|
-
host_injected_token_mode: builderPath.auth.host_injected_token_mode,
|
|
230
|
-
host_token_env_detected: builderPath.auth.host_token_env_detected,
|
|
231
|
-
env_token_present: builderPath.auth.env_token_present,
|
|
232
|
-
interactive_login_allowed: builderPath.auth.interactive_login_allowed,
|
|
233
|
-
browser_session_auth_allowed: builderPath.auth.browser_session_auth_allowed,
|
|
234
|
-
auth_runtime: builderPath.auth.auth_runtime,
|
|
235
|
-
cookie_auth_supported: builderPath.auth.cookie_auth_supported,
|
|
236
|
-
cookie_check_required: builderPath.auth.cookie_check_required,
|
|
237
|
-
playwright_cookie_check_required: builderPath.auth.playwright_cookie_check_required,
|
|
238
|
-
qr_login_required: builderPath.auth.qr_login_required,
|
|
239
|
-
missing_token_action: builderPath.auth.missing_token_action,
|
|
240
|
-
},
|
|
266
|
+
auth,
|
|
241
267
|
environment_check_simplification: {
|
|
242
268
|
minimal_probe_commands: environment.minimal_probe_commands,
|
|
243
269
|
can_skip_default_exploration_when_summary_ok: environment.can_skip_default_exploration_when_summary_ok,
|
|
@@ -319,8 +345,37 @@ function buildCommandManifestDigest(manifest) {
|
|
|
319
345
|
}
|
|
320
346
|
|
|
321
347
|
function buildAgentCapabilitiesSummary() {
|
|
322
|
-
const envSnapshot = buildEnvironmentSnapshot();
|
|
323
348
|
const manifest = buildCommandManifest({ t, version });
|
|
349
|
+
|
|
350
|
+
if (hasRuntimeProvisionedAccessToken(process.env)) {
|
|
351
|
+
const projectRoot = findProjectRoot();
|
|
352
|
+
const loginStatus = buildRuntimeProvisionedLoginStatus();
|
|
353
|
+
const builderPath = compactBuilderPath(
|
|
354
|
+
buildBuilderPath(loginStatus, projectRoot, manifest)
|
|
355
|
+
);
|
|
356
|
+
|
|
357
|
+
return {
|
|
358
|
+
schema_version: 1,
|
|
359
|
+
name: 'openyida-agent-capabilities-summary',
|
|
360
|
+
version,
|
|
361
|
+
login: compactLogin(loginStatus),
|
|
362
|
+
precheck: {
|
|
363
|
+
skipped: true,
|
|
364
|
+
reason: 'runtime_auth_provisioned',
|
|
365
|
+
},
|
|
366
|
+
workdir: projectRoot,
|
|
367
|
+
workdir_exists: fs.existsSync(projectRoot),
|
|
368
|
+
cache_dir: path.join(projectRoot, '.cache'),
|
|
369
|
+
openyida_task_cache_dir: path.join(projectRoot, '.cache', 'openyida'),
|
|
370
|
+
command_manifest_digest: buildCommandManifestDigest(manifest),
|
|
371
|
+
command_manifest_digest_algorithm: 'sha256',
|
|
372
|
+
command_count: manifest.summary.command_count,
|
|
373
|
+
full_capabilities_command: 'openyida agent-capabilities --json',
|
|
374
|
+
builder_path: builderPath,
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
const envSnapshot = buildEnvironmentSnapshot();
|
|
324
379
|
const projectRoot = envSnapshot.active.projectRoot;
|
|
325
380
|
const loginStatus = getAuthStatus({ projectRoot, includeSecrets: false });
|
|
326
381
|
const builderPath = compactBuilderPath(
|
|
@@ -430,6 +430,8 @@ const COMMAND_SIDE_EFFECTS = new Map([
|
|
|
430
430
|
|
|
431
431
|
...sideEffectEntries([
|
|
432
432
|
'add-validation',
|
|
433
|
+
'app-offline',
|
|
434
|
+
'app-online',
|
|
433
435
|
'append-chart',
|
|
434
436
|
'cdn-refresh',
|
|
435
437
|
'cdn-upload',
|
|
@@ -596,7 +598,7 @@ const COMMAND_SIDE_EFFECTS = new Map([
|
|
|
596
598
|
mutates_yida: true,
|
|
597
599
|
mutates_local: false,
|
|
598
600
|
read_actions: ['list'],
|
|
599
|
-
mutating_actions: ['create', 'rename', 'delete', 'move', 'order', 'hide', 'show'],
|
|
601
|
+
mutating_actions: ['create', 'rename', 'delete', 'move', 'order', 'auto-order', 'hide', 'show'],
|
|
600
602
|
})],
|
|
601
603
|
['org', sideEffect('mixed', {
|
|
602
604
|
mutates_yida: false,
|
|
@@ -667,6 +669,7 @@ const COMMAND_PERMISSIONS = new Map([
|
|
|
667
669
|
|
|
668
670
|
...permissionEntries([
|
|
669
671
|
'add-validation',
|
|
672
|
+
'app-online',
|
|
670
673
|
'append-chart',
|
|
671
674
|
'build-page',
|
|
672
675
|
'cdn-config',
|
|
@@ -727,10 +730,11 @@ const COMMAND_PERMISSIONS = new Map([
|
|
|
727
730
|
})),
|
|
728
731
|
|
|
729
732
|
...permissionEntries([
|
|
733
|
+
'app-offline',
|
|
730
734
|
'connector.delete',
|
|
731
735
|
'connector.delete-action',
|
|
732
736
|
], permission('ask', 'destructive', {
|
|
733
|
-
reason: 'Deletes or
|
|
737
|
+
reason: 'Deletes, removes, or disables existing remote state.',
|
|
734
738
|
})),
|
|
735
739
|
|
|
736
740
|
...permissionEntries([
|
|
@@ -884,7 +888,7 @@ const COMMAND_PERMISSIONS = new Map([
|
|
|
884
888
|
...permissionEntries([
|
|
885
889
|
'nav-group',
|
|
886
890
|
], actionDependentPermission({
|
|
887
|
-
preauthorized_actions: ['create', 'rename', 'move', 'order', 'hide', 'show'],
|
|
891
|
+
preauthorized_actions: ['create', 'rename', 'move', 'order', 'auto-order', 'hide', 'show'],
|
|
888
892
|
ask_actions: ['delete'],
|
|
889
893
|
})),
|
|
890
894
|
|
|
@@ -1032,6 +1036,8 @@ const COMMAND_GROUPS = [
|
|
|
1032
1036
|
}),
|
|
1033
1037
|
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'),
|
|
1034
1038
|
command('update-app', ['update-app'], 'update-app <appType> [--name "..."] [--layout slide|ver] [--theme deepBlue]', 'help.cmd_update_app'),
|
|
1039
|
+
command('app-online', ['app-online'], 'app-online <appType> [--to-ding-app-center] [--show-app-center]', 'help.cmd_app_online'),
|
|
1040
|
+
command('app-offline', ['app-offline'], 'app-offline <appType> [--to-ding-app-center] [--show-app-center]', 'help.cmd_app_offline'),
|
|
1035
1041
|
command('nav-group', ['nav-group'], 'nav-group <list|create|rename|delete|move|order|auto-order|hide|show> <appType> ...', 'help.cmd_nav_group', {
|
|
1036
1042
|
output: 'json',
|
|
1037
1043
|
aliases: ['group'],
|
package/lib/core/locales/en.js
CHANGED
|
@@ -23,6 +23,8 @@ module.exports = {
|
|
|
23
23
|
cmd_corp_efficiency: 'Query enterprise efficiency overview and detail reports',
|
|
24
24
|
cmd_create_app: 'Create a Yida app',
|
|
25
25
|
cmd_update_app: 'Update app info',
|
|
26
|
+
cmd_app_online: 'Enable a Yida app',
|
|
27
|
+
cmd_app_offline: 'Disable a Yida app',
|
|
26
28
|
cmd_nav_group: 'Manage app sidebar navigation groups',
|
|
27
29
|
cmd_app_permission: 'Manage app primary, data, and developer admins',
|
|
28
30
|
cmd_i18n: 'Manage app multilingual copy and language config',
|
|
@@ -960,6 +962,19 @@ Examples:
|
|
|
960
962
|
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.',
|
|
961
963
|
},
|
|
962
964
|
|
|
965
|
+
app_lifecycle: {
|
|
966
|
+
online_usage: 'Usage: openyida app-online <appType> [--to-ding-app-center] [--show-app-center]',
|
|
967
|
+
online_example: 'Example: openyida app-online APP_XXX',
|
|
968
|
+
offline_usage: 'Usage: openyida app-offline <appType> [--to-ding-app-center] [--show-app-center]',
|
|
969
|
+
offline_example: 'Example: openyida app-offline APP_XXX',
|
|
970
|
+
missing_app_type: 'Error: missing appType argument',
|
|
971
|
+
invalid_argument: 'Unsupported argument: {0}',
|
|
972
|
+
invalid_action: 'Unsupported app lifecycle action: {0}',
|
|
973
|
+
request_failed: 'App lifecycle operation failed',
|
|
974
|
+
online_success: 'App enabled',
|
|
975
|
+
offline_success: 'App disabled',
|
|
976
|
+
},
|
|
977
|
+
|
|
963
978
|
// ── lib/update-form-config.js ──────────────────────
|
|
964
979
|
update_form_config: {
|
|
965
980
|
usage: 'Usage: openyida update-form-config <appType> <formUuid> <isRenderNav> <title>',
|
|
@@ -1134,7 +1149,6 @@ Examples:
|
|
|
1134
1149
|
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.',
|
|
1135
1150
|
lint_form_open_container: 'Opening Yida form submission/detail pages from a custom page must use FormOpenContainer: a 50vw drawer iframe on desktop, and full/new page only on mobile. Button handlers should call openForm({ type: "submission" | "detail", ... }).',
|
|
1136
1151
|
lint_form_detail_link: 'Yida form detail pages must use a real formInstId: read row.formInstId first, and disable or warn when the instance id is missing instead of opening a formDetail link with an empty formInstId.',
|
|
1137
|
-
lint_content_blocks_too_few: 'Display pages must declare @openyida-content-blocks and include at least {1} business-purpose content blocks; KPI groups, quick-entry groups, and list groups each count as one block. Current count: {0}.',
|
|
1138
1152
|
lint_page_size_limit: 'pageSize={0} exceeds the Yida API limit of 100; use 100 or less',
|
|
1139
1153
|
lint_page_size_recommend: 'pageSize={0} is large; the recommended value is 50 (best performance, max 100). Prefer 10/20/50',
|
|
1140
1154
|
lint_searchformdata_http_path: 'A direct searchFormDatas.json call must use /dingtalk/web/<appType>/v1/form/searchFormDatas.json; /query/form/searchFormDatas.json is not a valid form data endpoint',
|