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.
Files changed (29) hide show
  1. package/README.md +11 -3
  2. package/bin/yida.js +8 -1
  3. package/lib/app/app-lifecycle.js +128 -0
  4. package/lib/app/canvas-compile.js +0 -19
  5. package/lib/app/get-schema.js +123 -2
  6. package/lib/app/page-linter.js +0 -5
  7. package/lib/core/agent-capabilities.js +75 -20
  8. package/lib/core/command-manifest.js +9 -3
  9. package/lib/core/locales/en.js +15 -1
  10. package/lib/core/locales/zh.js +15 -1
  11. package/package.json +1 -1
  12. package/yida-skills/SKILL.md +8 -23
  13. package/yida-skills/skills/yida-app/SKILL.md +17 -29
  14. package/yida-skills/skills/yida-app-lifecycle/SKILL.md +56 -0
  15. package/yida-skills/skills/yida-canvas-custom-page/references/canvas-style-implementation-guide.md +2 -2
  16. package/yida-skills/skills/yida-canvas-custom-page/references/page-generation-guide.md +4 -4
  17. package/yida-skills/skills/yida-design/SKILL.md +1 -1
  18. package/yida-skills/skills/yida-design/references/asset-workflow.md +1 -1
  19. package/yida-skills/skills/yida-design/references/page-quality-gates.md +4 -4
  20. package/yida-skills/skills/yida-design/references/style-design-selection.md +48 -29
  21. package/yida-skills/skills/yida-design/references/style-designs/registry.md +56 -30
  22. package/yida-skills/skills/yida-design/references/visual-scaffold-recipes.md +2 -2
  23. package/yida-skills/skills/yida-design/workflow/output-prd.md +3 -3
  24. package/yida-skills/skills/yida-design/workflow/step-3-information-architecture.md +2 -2
  25. package/yida-skills/skills/yida-design/workflow/step-4-wireframe-interaction.md +4 -4
  26. package/yida-skills/skills/yida-design/workflow/step-5-visual-states.md +2 -2
  27. package/yida-skills/skills/yida-design/workflow/step-6-handoff.md +1 -1
  28. package/yida-skills/skills-index.json +38 -0
  29. 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) · [Help Center](https://xmtrf1.aliwork.com/o/openyida/help?lang=en) · [Capabilities](#capabilities) · [Full Capability List](./docs/capabilities.md) · [CLI Reference](#cli-reference) · [Examples](#examples) · [Contributing](./CONTRIBUTING.md) · [Changelog](./CHANGELOG.md)
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
  [![npm version](https://img.shields.io/npm/v/openyida?color=brightgreen&label=npm)](https://www.npmjs.com/package/openyida)
14
14
  [![npm downloads](https://img.shields.io/npm/dm/openyida?color=blue)](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: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](./LICENSE)
17
17
  [![Node.js >=18](https://img.shields.io/badge/node-%3E%3D18-brightgreen)](https://nodejs.org)
18
18
 
19
- **Help Center:** [简体中文](https://xmtrf1.aliwork.com/o/openyida/help?lang=zh) · [繁體中文(中國台灣)](https://xmtrf1.aliwork.com/o/openyida/help?lang=zh-Hant) · [繁體中文(中國香港)](https://xmtrf1.aliwork.com/o/openyida/help?lang=zh-HK) · [English](https://xmtrf1.aliwork.com/o/openyida/help?lang=en) · [日本語](https://xmtrf1.aliwork.com/o/openyida/help?lang=ja) · [한국어](https://xmtrf1.aliwork.com/o/openyida/help?lang=ko) · [Tiếng Việt](https://xmtrf1.aliwork.com/o/openyida/help?lang=vi) · [ไทย](https://xmtrf1.aliwork.com/o/openyida/help?lang=th) · [Bahasa Indonesia](https://xmtrf1.aliwork.com/o/openyida/help?lang=id) · [Bahasa Melayu](https://xmtrf1.aliwork.com/o/openyida/help?lang=ms) · [Français](https://xmtrf1.aliwork.com/o/openyida/help?lang=fr) · [Português](https://xmtrf1.aliwork.com/o/openyida/help?lang=pt) · [Türkçe](https://xmtrf1.aliwork.com/o/openyida/help?lang=tr) · [Español](https://xmtrf1.aliwork.com/o/openyida/help?lang=es) · [Русский](https://xmtrf1.aliwork.com/o/openyida/help?lang=ru)
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 功能完整列表](./docs/capabilities.md).
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://xmtrf1.aliwork.com/o/openyida/help · https://github.com/openyida/openyida${RESET}`);
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
 
@@ -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
- return {
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
- return {
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) {
@@ -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
- return {
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 removes existing configuration.',
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'],
@@ -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',