openyida 2026.9.23-beta.2 → 2026.9.24-beta.0

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 (248) hide show
  1. package/README.md +18 -34
  2. package/bin/yida.js +26 -34
  3. package/lib/app/app-entry.js +144 -0
  4. package/lib/app/app-list.js +1 -0
  5. package/lib/app/application-style.js +67 -0
  6. package/lib/app/canvas-compile.js +7 -1
  7. package/lib/app/canvas-theme-guard.js +127 -10
  8. package/lib/app/check-prd-completeness.js +4 -15
  9. package/lib/app/compile.js +12 -2
  10. package/lib/app/create-form/args.js +37 -12
  11. package/lib/app/create-form/batch.js +41 -35
  12. package/lib/app/create-form.js +94 -57
  13. package/lib/app/custom-theme.js +30 -37
  14. package/lib/app/fix-theme.js +34 -0
  15. package/lib/app/form-field-validator.js +0 -36
  16. package/lib/app/get-schema.js +1 -6
  17. package/lib/app/publish.js +49 -7
  18. package/lib/app/services/canvas-page-compiler.js +3 -1
  19. package/lib/app/services/form-compiler.js +0 -2
  20. package/lib/app/theme-from-design.js +77 -87
  21. package/lib/app/theme-scope.js +76 -0
  22. package/lib/app/update-app.js +64 -26
  23. package/lib/app/update-form-config.js +9 -19
  24. package/lib/auth/oauth-device.js +311 -0
  25. package/lib/auth/token-auth.js +81 -79
  26. package/lib/auth/token-store.js +2 -26
  27. package/lib/core/cli-error.js +1 -0
  28. package/lib/core/command-manifest.js +122 -49
  29. package/lib/core/locales/en.js +61 -54
  30. package/lib/core/locales/zh.js +61 -54
  31. package/lib/core/sample.js +36 -14
  32. package/lib/core/source-repair.js +38 -0
  33. package/lib/core/theme-brand-scale.js +133 -0
  34. package/lib/core/update.js +1 -3
  35. package/lib/core/utils.js +0 -29
  36. package/lib/design/check-design.js +16 -6
  37. package/lib/design/document.js +37 -15
  38. package/lib/design/icon-contrast.js +1 -1
  39. package/lib/design/palette-contrast.js +77 -0
  40. package/lib/design-plan/confirmation.js +13 -0
  41. package/lib/design-plan/design-plan.js +65 -8
  42. package/lib/design-plan/entry-navigation.js +10 -2
  43. package/lib/design-plan/init.js +17 -10
  44. package/lib/design-plan/materialize.js +88 -58
  45. package/lib/design-plan/normalize.js +13 -14
  46. package/lib/design-plan/parallel.js +62 -13
  47. package/lib/design-plan/patch.js +21 -7
  48. package/lib/design-plan/preview.js +4 -1
  49. package/lib/design-plan/rebase.js +69 -0
  50. package/lib/design-plan/themes.js +16 -1
  51. package/lib/design-plan/validate.js +18 -3
  52. package/lib/design-plan/visual-policy.js +29 -4
  53. package/lib/page-config/save-share-config.js +5 -16
  54. package/lib/samples/openyida-scaffold/canvas-form-drawer.canvas.jsx +3 -3
  55. package/lib/samples/openyida-scaffold/canvas-nav/mixed.jsx +6 -4
  56. package/lib/samples/openyida-scaffold/canvas-nav/shared.jsx +5 -5
  57. package/lib/samples/openyida-scaffold/canvas-nav/side.jsx +1 -2
  58. package/lib/samples/openyida-scaffold/canvas-nav/sidebar.jsx +1 -1
  59. package/lib/samples/openyida-scaffold/canvas-nav/top.jsx +5 -4
  60. package/package.json +3 -11
  61. package/yida-skills/SKILL.md +3 -25
  62. package/yida-skills/references/official-example-schema-patterns.md +1 -1
  63. package/yida-skills/references/source-repair.md +10 -0
  64. package/yida-skills/references/yida-api.md +4 -0
  65. package/yida-skills/skills/yida-app/SKILL.md +15 -4
  66. package/yida-skills/skills/yida-app/references/entry-navigation.md +11 -9
  67. package/yida-skills/skills/yida-app/workflow/plan/step-4-deliver.md +3 -1
  68. package/yida-skills/skills/yida-app/workflow/plan/workflow.md +21 -5
  69. package/yida-skills/skills/yida-app/workflow/step-2-design.md +4 -4
  70. package/yida-skills/skills/yida-app/workflow/step-3-create-or-reuse-app.md +2 -2
  71. package/yida-skills/skills/yida-app/workflow/step-8-publish-navigation.md +3 -3
  72. package/yida-skills/skills/yida-app/workflow/step-9-output-finish.md +25 -2
  73. package/yida-skills/skills/yida-canvas-custom-page/SKILL.md +3 -2
  74. package/yida-skills/skills/yida-canvas-custom-page/references/canvas-style-implementation-guide.md +1 -1
  75. package/yida-skills/skills/yida-canvas-custom-page/references/canvas-theme-provider.md +4 -2
  76. package/yida-skills/skills/yida-canvas-custom-page/references/data-bridge-guide.md +140 -1
  77. package/yida-skills/skills/yida-canvas-custom-page/references/navigation-and-entry-guide.md +1 -1
  78. package/yida-skills/skills/yida-canvas-custom-page/references/waf-safe-authoring.md +45 -0
  79. package/yida-skills/skills/yida-canvas-custom-page/scripts/build-canvas-theme.js +2 -1
  80. package/yida-skills/skills/yida-canvas-custom-page/scripts/canvas-theme-provider.template.jsx +8 -1
  81. package/yida-skills/skills/yida-canvas-data-binding/SKILL.md +7 -0
  82. package/yida-skills/skills/yida-create-form-page/SKILL.md +34 -43
  83. package/yida-skills/skills/yida-create-form-page/references/advanced-form-modes.md +5 -5
  84. package/yida-skills/skills/yida-create-form-page/references/association-form-field.md +4 -4
  85. package/yida-skills/skills/yida-create-form-page/references/batch-forms.md +2 -2
  86. package/yida-skills/skills/yida-create-form-page/references/field-definition-guide.md +4 -16
  87. package/yida-skills/skills/yida-create-form-page/references/form-field-properties.md +14 -37
  88. package/yida-skills/skills/yida-design/SKILL.md +16 -8
  89. package/yida-skills/skills/yida-design/references/application-style-library.md +93 -0
  90. package/yida-skills/skills/yida-design/references/application-theme-consistency.md +92 -7
  91. package/yida-skills/skills/yida-design/references/ask-human-interaction-contract.md +1 -1
  92. package/yida-skills/skills/yida-design/references/native-form-styles.md +208 -0
  93. package/yida-skills/skills/yida-design/references/navigation-decision.md +8 -6
  94. package/yida-skills/skills/yida-design/references/theme/app-custom-theme-template.css +136 -113
  95. package/yida-skills/skills/yida-design/references/theme/application-style-recipes.css +58 -0
  96. package/yida-skills/skills/yida-design/references/theme-selection.md +12 -11
  97. package/yida-skills/skills/yida-design/references/visual-decision-engine.md +1 -1
  98. package/yida-skills/skills/yida-design/scripts/validate_design_themes.py +52 -7
  99. package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/SKILL.md +15 -3
  100. package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/references/build-plan-compact-schema.md +6 -2
  101. package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/references/build-plan-schema.md +5 -3
  102. package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/references/visual-design.md +3 -1
  103. package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/references/visual-theme-selection.md +22 -23
  104. package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/scripts/render_build_plan.py +2 -0
  105. package/yida-skills/skills/yida-design/templates/design-themes/README.md +17 -10
  106. package/yida-skills/skills/yida-design/templates/design-themes/app-amber/app_theme.css +570 -0
  107. package/yida-skills/skills/yida-design/templates/design-themes/app-amber/design.md +238 -0
  108. package/yida-skills/skills/yida-design/templates/design-themes/app-amber/form-layout.json +15 -0
  109. package/yida-skills/skills/yida-design/templates/design-themes/app-editorial/app_theme.css +570 -0
  110. package/yida-skills/skills/yida-design/templates/design-themes/app-editorial/design.md +239 -0
  111. package/yida-skills/skills/yida-design/templates/design-themes/app-editorial/form-layout.json +14 -0
  112. package/yida-skills/skills/yida-design/templates/design-themes/app-executive/app_theme.css +570 -0
  113. package/yida-skills/skills/yida-design/templates/design-themes/app-executive/design.md +239 -0
  114. package/yida-skills/skills/yida-design/templates/design-themes/app-executive/form-layout.json +13 -0
  115. package/yida-skills/skills/yida-design/templates/design-themes/app-finance/app_theme.css +570 -0
  116. package/yida-skills/skills/yida-design/templates/design-themes/app-finance/design.md +238 -0
  117. package/yida-skills/skills/yida-design/templates/design-themes/app-finance/form-layout.json +14 -0
  118. package/yida-skills/skills/yida-design/templates/design-themes/app-glass/app_theme.css +570 -0
  119. package/yida-skills/skills/yida-design/templates/design-themes/app-glass/design.md +239 -0
  120. package/yida-skills/skills/yida-design/templates/design-themes/app-glass/form-layout.json +14 -0
  121. package/yida-skills/skills/yida-design/templates/design-themes/app-graphite/app_theme.css +570 -0
  122. package/yida-skills/skills/yida-design/templates/design-themes/app-graphite/design.md +239 -0
  123. package/yida-skills/skills/yida-design/templates/design-themes/app-graphite/form-layout.json +14 -0
  124. package/yida-skills/skills/yida-design/templates/design-themes/app-line/app_theme.css +570 -0
  125. package/yida-skills/skills/yida-design/templates/design-themes/app-line/design.md +239 -0
  126. package/yida-skills/skills/yida-design/templates/design-themes/app-line/form-layout.json +13 -0
  127. package/yida-skills/skills/yida-design/templates/design-themes/app-neon/app_theme.css +570 -0
  128. package/yida-skills/skills/yida-design/templates/design-themes/app-neon/design.md +237 -0
  129. package/yida-skills/skills/yida-design/templates/design-themes/app-neon/form-layout.json +14 -0
  130. package/yida-skills/skills/yida-design/templates/design-themes/app-nordic/app_theme.css +570 -0
  131. package/yida-skills/skills/yida-design/templates/design-themes/app-nordic/design.md +239 -0
  132. package/yida-skills/skills/yida-design/templates/design-themes/app-nordic/form-layout.json +13 -0
  133. package/yida-skills/skills/yida-design/templates/design-themes/app-paper/app_theme.css +570 -0
  134. package/yida-skills/skills/yida-design/templates/design-themes/app-paper/design.md +237 -0
  135. package/yida-skills/skills/yida-design/templates/design-themes/app-paper/form-layout.json +13 -0
  136. package/yida-skills/skills/yida-design/templates/design-themes/app-platinum/app_theme.css +570 -0
  137. package/yida-skills/skills/yida-design/templates/design-themes/app-platinum/design.md +239 -0
  138. package/yida-skills/skills/yida-design/templates/design-themes/app-platinum/form-layout.json +14 -0
  139. package/yida-skills/skills/yida-design/templates/design-themes/app-plum/app_theme.css +570 -0
  140. package/yida-skills/skills/yida-design/templates/design-themes/app-plum/design.md +239 -0
  141. package/yida-skills/skills/yida-design/templates/design-themes/app-plum/form-layout.json +14 -0
  142. package/yida-skills/skills/yida-design/templates/design-themes/app-pop/app_theme.css +570 -0
  143. package/yida-skills/skills/yida-design/templates/design-themes/app-pop/design.md +239 -0
  144. package/yida-skills/skills/yida-design/templates/design-themes/app-pop/form-layout.json +14 -0
  145. package/yida-skills/skills/yida-design/templates/design-themes/app-sage/app_theme.css +570 -0
  146. package/yida-skills/skills/yida-design/templates/design-themes/app-sage/design.md +239 -0
  147. package/yida-skills/skills/yida-design/templates/design-themes/app-sage/form-layout.json +14 -0
  148. package/yida-skills/skills/yida-design/templates/design-themes/app-teal/app_theme.css +570 -0
  149. package/yida-skills/skills/yida-design/templates/design-themes/app-teal/design.md +239 -0
  150. package/yida-skills/skills/yida-design/templates/design-themes/app-teal/form-layout.json +14 -0
  151. package/yida-skills/skills/yida-design/templates/design-themes/app-terminal/app_theme.css +570 -0
  152. package/yida-skills/skills/yida-design/templates/design-themes/app-terminal/design.md +238 -0
  153. package/yida-skills/skills/yida-design/templates/design-themes/app-terminal/form-layout.json +14 -0
  154. package/yida-skills/skills/yida-design/templates/design-themes/app-ticket/app_theme.css +570 -0
  155. package/yida-skills/skills/yida-design/templates/design-themes/app-ticket/design.md +241 -0
  156. package/yida-skills/skills/yida-design/templates/design-themes/app-ticket/form-layout.json +13 -0
  157. package/yida-skills/skills/yida-design/templates/design-themes/app-wire/app_theme.css +570 -0
  158. package/yida-skills/skills/yida-design/templates/design-themes/app-wire/design.md +238 -0
  159. package/yida-skills/skills/yida-design/templates/design-themes/app-wire/form-layout.json +14 -0
  160. package/yida-skills/skills/yida-design/templates/design-themes/basic-tokens.json +1 -7
  161. package/yida-skills/skills/yida-design/templates/design-themes/dark-inset-hairline/app_theme.css +509 -0
  162. package/yida-skills/skills/yida-design/templates/design-themes/{dark-inset-hairline.md → dark-inset-hairline/design.md} +97 -58
  163. package/yida-skills/skills/yida-design/templates/design-themes/dark-inset-hairline/form-layout.json +13 -0
  164. package/yida-skills/skills/yida-design/templates/design-themes/dark-rail-fine-lines/app_theme.css +517 -0
  165. package/yida-skills/skills/yida-design/templates/design-themes/{dark-rail-fine-lines.md → dark-rail-fine-lines/design.md} +98 -51
  166. package/yida-skills/skills/yida-design/templates/design-themes/dark-rail-fine-lines/form-layout.json +13 -0
  167. package/yida-skills/skills/yida-design/templates/design-themes/dark-rail-signal-panels/app_theme.css +515 -0
  168. package/yida-skills/skills/yida-design/templates/design-themes/{dark-rail-signal-panels.md → dark-rail-signal-panels/design.md} +59 -12
  169. package/yida-skills/skills/yida-design/templates/design-themes/dark-rail-signal-panels/form-layout.json +13 -0
  170. package/yida-skills/skills/yida-design/templates/design-themes/free-creative/app_theme.css +511 -0
  171. package/yida-skills/skills/yida-design/templates/design-themes/free-creative/design.md +202 -0
  172. package/yida-skills/skills/yida-design/templates/design-themes/free-creative/form-layout.json +13 -0
  173. package/yida-skills/skills/yida-design/templates/design-themes/graphite-bevel-grid/app_theme.css +511 -0
  174. package/yida-skills/skills/yida-design/templates/design-themes/{graphite-bevel-grid.md → graphite-bevel-grid/design.md} +55 -10
  175. package/yida-skills/skills/yida-design/templates/design-themes/graphite-bevel-grid/form-layout.json +13 -0
  176. package/yida-skills/skills/yida-design/templates/design-themes/hairline-shared-bands/app_theme.css +510 -0
  177. package/yida-skills/skills/yida-design/templates/design-themes/{hairline-shared-bands.md → hairline-shared-bands/design.md} +57 -12
  178. package/yida-skills/skills/yida-design/templates/design-themes/hairline-shared-bands/form-layout.json +13 -0
  179. package/yida-skills/skills/yida-design/templates/design-themes/hairline-soft-blocks/app_theme.css +510 -0
  180. package/yida-skills/skills/yida-design/templates/design-themes/{hairline-soft-blocks.md → hairline-soft-blocks/design.md} +52 -10
  181. package/yida-skills/skills/yida-design/templates/design-themes/hairline-soft-blocks/form-layout.json +13 -0
  182. package/yida-skills/skills/yida-design/templates/design-themes/index.json +399 -31
  183. package/yida-skills/skills/yida-design/templates/design-themes/ink-glow-soft-panels/app_theme.css +520 -0
  184. package/yida-skills/skills/yida-design/templates/design-themes/{ink-glow-soft-panels.md → ink-glow-soft-panels/design.md} +53 -9
  185. package/yida-skills/skills/yida-design/templates/design-themes/ink-glow-soft-panels/form-layout.json +13 -0
  186. package/yida-skills/skills/yida-design/templates/design-themes/inset-frame-pixel-rhythm/app_theme.css +510 -0
  187. package/yida-skills/skills/yida-design/templates/design-themes/{inset-frame-pixel-rhythm.md → inset-frame-pixel-rhythm/design.md} +55 -10
  188. package/yida-skills/skills/yida-design/templates/design-themes/inset-frame-pixel-rhythm/form-layout.json +13 -0
  189. package/yida-skills/skills/yida-design/templates/design-themes/outlined-texture-duotone/app_theme.css +517 -0
  190. package/yida-skills/skills/yida-design/templates/design-themes/{outlined-texture-duotone.md → outlined-texture-duotone/design.md} +56 -12
  191. package/yida-skills/skills/yida-design/templates/design-themes/outlined-texture-duotone/form-layout.json +13 -0
  192. package/yida-skills/skills/yida-design/templates/design-themes/soft-inset-surfaces/app_theme.css +510 -0
  193. package/yida-skills/skills/yida-design/templates/design-themes/{soft-inset-surfaces.md → soft-inset-surfaces/design.md} +54 -12
  194. package/yida-skills/skills/yida-design/templates/design-themes/soft-inset-surfaces/form-layout.json +13 -0
  195. package/yida-skills/skills/yida-design/templates/design-themes/soft-outline-rhythm/app_theme.css +509 -0
  196. package/yida-skills/skills/yida-design/templates/design-themes/{soft-outline-rhythm.md → soft-outline-rhythm/design.md} +54 -26
  197. package/yida-skills/skills/yida-design/templates/design-themes/soft-outline-rhythm/form-layout.json +13 -0
  198. package/yida-skills/skills/yida-design/templates/design-themes/soft-rail-bold-band/app_theme.css +517 -0
  199. package/yida-skills/skills/yida-design/templates/design-themes/{soft-rail-bold-band.md → soft-rail-bold-band/design.md} +94 -50
  200. package/yida-skills/skills/yida-design/templates/design-themes/soft-rail-bold-band/form-layout.json +13 -0
  201. package/yida-skills/skills/yida-design/templates/design-themes/soft-spectral-panels/app_theme.css +511 -0
  202. package/yida-skills/skills/yida-design/templates/design-themes/{soft-spectral-panels.md → soft-spectral-panels/design.md} +59 -13
  203. package/yida-skills/skills/yida-design/templates/design-themes/soft-spectral-panels/form-layout.json +13 -0
  204. package/yida-skills/skills/yida-design/templates/design-themes/warm-canvas-contrast-panels/app_theme.css +530 -0
  205. package/yida-skills/skills/yida-design/templates/design-themes/{warm-canvas-contrast-panels.md → warm-canvas-contrast-panels/design.md} +55 -11
  206. package/yida-skills/skills/yida-design/templates/design-themes/warm-canvas-contrast-panels/form-layout.json +13 -0
  207. package/yida-skills/skills/yida-design/templates/design-themes/warm-rail-muted-panels/app_theme.css +511 -0
  208. package/yida-skills/skills/yida-design/templates/design-themes/{warm-rail-muted-panels.md → warm-rail-muted-panels/design.md} +94 -52
  209. package/yida-skills/skills/yida-design/templates/design-themes/warm-rail-muted-panels/form-layout.json +13 -0
  210. package/yida-skills/skills/yida-design/workflow/output-design.md +44 -8
  211. package/yida-skills/skills/yida-design/workflow/step-2-theme-system.md +18 -5
  212. package/yida-skills/skills/yida-design/workflow/step-6-handoff.md +3 -1
  213. package/yida-skills/skills/yida-document-markdown/SKILL.md +0 -6
  214. package/yida-skills/skills/yida-export-conversation/SKILL.md +0 -1
  215. package/yida-skills/skills/yida-form-permission/SKILL.md +6 -0
  216. package/yida-skills/skills/yida-nav-shell/SKILL.md +5 -3
  217. package/yida-skills/skills/yida-nav-shell/references/nav-shell-patterns.md +6 -0
  218. package/yida-skills/skills/yida-page-config/SKILL.md +9 -0
  219. package/yida-skills/skills/yida-prd/SKILL.md +2 -1
  220. package/yida-skills/skills/yida-prd/workflow/output-prd.md +2 -2
  221. package/yida-skills/skills/yida-prd/workflow/step-2-information-architecture.md +1 -1
  222. package/yida-skills/skills/yida-publish-page/SKILL.md +22 -18
  223. package/yida-skills/skills/yida-requirement-analysis/references/experience-groups.md +45 -9
  224. package/yida-skills/skills/yida-requirement-analysis/references/handoff.md +6 -6
  225. package/yida-skills/skills/yida-requirement-analysis/workflow/prepare-brief.md +17 -4
  226. package/yida-skills/skills/yida-tingji/SKILL.md +0 -4
  227. package/yida-skills/skills-index.json +4 -4
  228. package/lib/agent/PROTOCOL.md +0 -165
  229. package/lib/agent/ask-human.js +0 -151
  230. package/lib/agent/cmd.js +0 -403
  231. package/lib/agent/command-receipt.js +0 -84
  232. package/lib/agent/connection-store.js +0 -148
  233. package/lib/agent/diagnostic-log.js +0 -133
  234. package/lib/agent/frozen-bundle.js +0 -286
  235. package/lib/agent/managed-command-map.js +0 -117
  236. package/lib/agent/managed-context.js +0 -40
  237. package/lib/agent/managed-run.js +0 -96
  238. package/lib/agent/mutation-receipt.js +0 -82
  239. package/lib/agent/private-directory.js +0 -63
  240. package/lib/agent/provider-preflight.js +0 -236
  241. package/lib/agent/providers.js +0 -4
  242. package/lib/agent/runtime-catalog.json +0 -42
  243. package/lib/agent/runtime-manifest.js +0 -96
  244. package/lib/agent/runtime-package.js +0 -155
  245. package/lib/agent/stdio.js +0 -265
  246. package/lib/auth/profile-lock.js +0 -89
  247. package/yida-skills/references/local-agent-diagnostics.md +0 -154
  248. package/yida-skills/references/local-agent-preflight.md +0 -56
package/README.md CHANGED
@@ -74,10 +74,17 @@ openyida login
74
74
 
75
75
  OpenYida login defaults to OAuth token mode. It opens the DingTalk OAuth authorization page, receives the local loopback callback, exchanges `code` / `authCode` with the Yida server, and stores `access_token` / `refresh_token` in the user auth store when available. If the user auth store is not writable, OpenYida explicitly falls back to the current project cache and reports that narrower persistence scope.
76
76
 
77
+ In a sandbox, headless shell, or CI dashboard where the loopback callback cannot complete, use the device authorization flow. Open the displayed verification URL on any device and enter the user code. `--env-hint` optionally pins the intended environment profile at authorization time:
78
+
79
+ ```bash
80
+ openyida login --device
81
+ openyida login --device --env-hint pre
82
+ ```
83
+
77
84
  When the user names a target Yida entry URL, pass it to the login command so OpenYida can select the matching environment and auth profile. For example:
78
85
 
79
86
  ```bash
80
- openyida login https://yida-group.alibaba-inc.com/
87
+ openyida login https://<your-yida-entry-url>/
81
88
  openyida login --alibaba
82
89
  ```
83
90
 
@@ -118,6 +125,7 @@ The agent can then call OpenYida commands to create the application, generate so
118
125
  | [Codex](https://openai.com/codex/) | Full support |
119
126
  | [Claude Code](https://claude.ai/code) | Full support |
120
127
  | [MuleRun](https://mulerun.com) | Full support |
128
+ | [OpenCode](https://opencode.ai) | Full support |
121
129
  | [Cursor](https://cursor.com/) | Full support |
122
130
  | [Visual Studio Code](https://code.visualstudio.com/) | Full support |
123
131
  | QwenWork(千问办公) | Full support |
@@ -182,7 +190,7 @@ openyida get-schema APP_XXX FORM_XXX --compact --resolve-fields "Customer Name,S
182
190
  openyida get-schema APP_XXX --all --output-dir .cache/schemas
183
191
  ```
184
192
 
185
- Form definitions support 19 business field types plus verified presentation/layout components. Prefer `Divider` for section titles and `ColumnContainer` (mapped to `ColumnsLayout` + `Column`) for multi-column layout; use `GroupContainer` / `PageSection` only when an actual grouping container is needed.
193
+ Native forms use a component tree: business fields capture data, tabs switch content, action groups trigger business actions, images establish visual focus, status areas provide feedback, and `Divider` plus `ColumnContainer` organize hierarchy and columns. `create-form` field definitions create 19 business field types plus `Divider` and `ColumnContainer` (mapped to `ColumnsLayout` + `Column`), while updates retain other existing components.
186
194
 
187
195
  ### Custom Page Development
188
196
 
@@ -382,7 +390,7 @@ Run `openyida --help` or `openyida <command> --help` for detailed usage.
382
390
 
383
391
  | Command | Description |
384
392
  |---------|-------------|
385
- | `openyida login [target-url] [--env <name>\|--intl\|--overseas\|--global\|--yidaapps\|--alibaba] [--client-id <clientId>] [--endpoint <url>] [--no-browser]` | Login with OAuth token mode |
393
+ | `openyida login [target-url] [--env <name>\|--intl\|--overseas\|--global\|--yidaapps\|--alibaba] [--client-id <clientId>] [--endpoint <url>] [--no-browser] [--device] [--env-hint <name>]` | Login with OAuth token mode |
386
394
  | `openyida logout` | Logout / unbind current project auth |
387
395
  | `openyida auth <status\|login\|refresh\|logout\|profiles\|profile switch>` | Token login state and profile management |
388
396
  | `openyida org <list\|switch> [--json] [--corp-id <corpId>]` | Organization management (list / switch existing profiles first) |
@@ -392,13 +400,15 @@ Run `openyida --help` or `openyida <command> --help` for detailed usage.
392
400
 
393
401
  | Command | Description |
394
402
  |---------|-------------|
403
+ | `openyida app-entry get <appType> [--json]` | Read frontend and management runtime entries |
404
+ | `openyida app-entry set <appType> [--frontend <url>\|--frontend-page <formUuid>] [--management <url>\|--management-page <formUuid>] [--clear-frontend] [--clear-management] [--json]` | Register application entries with partial update and readback |
395
405
  | `openyida app-list [--type managed\|created] [--page N] [--size N]` | Page through apps I manage or created |
396
406
  | `openyida corp-efficiency [overview\|details\|detail\|groups\|notify] [options] [--open\|--no-open]` | Query enterprise efficiency overview and detail reports |
397
407
  | `openyida create-app "<name>"\|--name <name> [options] [--locale zh_CN\|en_US\|ja_JP] [--open\|--no-open]` | Create a Yida app |
398
408
  | `openyida design-plan catalog [--json]` | List available planning themes and page patterns |
399
409
  | `openyida design-plan init <requirement-brief.json> [--theme-id <id>] [--output-dir <dir>] [--json]` | Initialize a plan draft from confirmed requirements |
400
410
  | `openyida design-plan preview <build-plan.json> --part-file <module.json> [--json]` | Update plan drafts by module |
401
- | `openyida design-plan materialize <build-plan.json> [--from-preview \| --business-file <json> --visual-file <json>] [--output-dir <dir>] [--check] [--json]` | Generate or update plan documents and theme with consistency checks |
411
+ | `openyida design-plan materialize <build-plan.json> [--from-preview \| --business-file <json> --visual-file <json>] [--output-dir <dir>] [--rebase-parts] [--check] [--json]` | Generate or update plan documents and theme with consistency checks |
402
412
  | `openyida design-plan patch <build-plan.json> --set <path=value> [--set <path=value> ...] [--materialize] [--output-dir <dir>] [--json]` | Patch plan fields and optionally sync documents and theme |
403
413
  | `openyida update-app <appType> [--name "..."] [--desc "..."] [--icon <name>] [--icon-color <color>] [--colour <key>] [--theme-color <color>] [--theme-file <css>] [--nav-theme light\|dark\|white\|gray] [--logo-source appIcon\|customImage] [--layout side\|top\|l_shape] [--hide-app-nav\|--show-app-nav]` | Update app info |
404
414
  | `openyida app-online <appType> [--to-ding-app-center] [--show-app-center]` | Enable a Yida app |
@@ -414,7 +424,7 @@ Run `openyida --help` or `openyida <command> --help` for detailed usage.
414
424
  | Command | Description |
415
425
  |---------|-------------|
416
426
  | `openyida create-form batch <appType> <plan.json> [--concurrency 1..4] [--check] [--json]` | Create forms concurrently by dependency |
417
- | `openyida create-form create <appType> "<formTitle>" <fieldsJsonFile> [--icon auto\|<iconName>] [--locale zh_CN\|en_US\|ja_JP] [--open\|--no-open]` | Create a form page |
427
+ | `openyida create-form create <appType> "<formTitle>" <fieldsJsonFile> [--layout single\|double\|card\|section] [--theme default\|compact\|comfortable] [--label-align top\|left\|right] [--icon auto\|<iconName>] [--locale zh_CN\|en_US\|ja_JP] [--open\|--no-open]` | Create a form page |
418
428
  | `openyida create-form icons [--json]` | List available form navigation icons |
419
429
  | `openyida create-form validate-fields <fieldsJsonOrFile> [--json]` | Validate form field JSON locally |
420
430
  | `openyida create-form update <appType> <formUuid> (<changesJsonOrFile> \| --data-file <changesJsonOrFile>) [--locale zh_CN\|en_US\|ja_JP] [--open\|--no-open]` | Update a form page |
@@ -434,8 +444,8 @@ Run `openyida --help` or `openyida <command> --help` for detailed usage.
434
444
  | `openyida create-page <appType> "<name>" [--mode dashboard] [--hide-nav] [--locale zh_CN\|en_US\|ja_JP] [--open\|--no-open]` | Create a custom display page |
435
445
  | `openyida build-page <sourceFile> [--output file\|--write]` | Build Yida-compatible page source |
436
446
  | `openyida check-page <src> [--compat] [--json]` | Check custom page standards |
437
- | `openyida compile <src> [--canvas] [--compat] [--skip-lint] [--json]` | Compile custom page locally |
438
- | `openyida publish <src> <appType> <formUuid> [--health-check] [--force] [--canvas] [--compat] [--skip-lint] [--auto-nav-order] [--open\|--no-open] [--json]` | Compile and publish custom page |
447
+ | `openyida compile <src> [--canvas] [--compat] [--skip-lint] [--strict-theme\|--allow-fixed-brand] [--json]` | Compile custom page locally |
448
+ | `openyida publish <src> <appType> <formUuid> [--health-check] [--force] [--canvas] [--compat] [--skip-lint] [--fix-theme] [--strict-theme] [--allow-fixed-brand] [--auto-nav-order] [--open\|--no-open] [--json]` | Compile and publish custom page |
439
449
  | `openyida update-form-config <appType> <formUuid> <true\|false\|keep> "<title>" [--locale zh_CN\|en_US\|ja_JP]` | Update form configuration |
440
450
  | `openyida get-form-config <appType> <formUuid> [--json]` | Query form configuration |
441
451
 
@@ -519,11 +529,10 @@ Run `openyida --help` or `openyida <command> --help` for detailed usage.
519
529
  |---------|-------------|
520
530
  | `openyida commands [--json]` | Output machine-readable command manifest |
521
531
  | `openyida agent-capabilities [--json] [--summary-json\|--compact]` | Output one-shot agent capability snapshot |
522
- | `openyida agent <doctor\|diagnose\|status\|connect\|run\|disconnect\|logout> [options]` | Connect a local Agent (Go runtime, development preview) |
523
532
  | `openyida a2a <serve\|agent-card> [options]` | Start local read-only A2A adapter or print Agent Card |
524
533
  | `openyida bridge start [--token <pair-token>] [--port 6736] [--origin https://demo.aliwork.com] [--open\|--no-open]` | Start OpenYida local web bridge service |
525
534
  | `openyida copy [--force]` | Copy project working directory |
526
- | `openyida sample [--list] [<skill> <name>] [--output <file>] [--var KEY=VALUE ...] [--design-file <design.md>]` | Output code samples/templates |
535
+ | `openyida sample [--list] [<skill> <name>] [--output <file-or-directory>] [--var KEY=VALUE ...] [--design-file <design.md>] [--style-id <themeId\|free-creative>]` | Output code samples/templates |
527
536
  | `openyida doctor [--fix]` | Environment diagnostics & auto-fix |
528
537
  | `openyida db-seq-fix [--fix]` | Detect and repair PostgreSQL sequence drift |
529
538
  | `openyida formula evaluate <formula\|file> [--schema file]` | Static-check Yida formula syntax and field refs |
@@ -588,31 +597,6 @@ Form field definitions can include `alias` or `componentAlias` to populate Yida
588
597
 
589
598
  `openyida integration create` supports form events (`insert`, `update`, `delete`, `comment`) and approval events (`processFinish`, `activityTask`; aliases: `approval`, `approvalNode`). Approval events require `--approval-actions agree,disagree,terminated`; `activityTask` also requires `--approval-node-ids <nodeId,...>`. Passing `--process-code` selects a full graph replacement and now requires explicit `--replace`; it is not a safe update. `integration update` currently performs capability detection only: because full platform `processJson` + `viewJson` readback is unproven, it writes a redacted local probe artifact and returns `PLATFORM_PROBE_REQUIRED` before authentication, spec reading, or remote writes. It does not edit a flow.
590
599
 
591
- ### Local Agent development preview
592
-
593
- `openyida agent` is a thin Node launcher for a separately supplied Go runtime. Node does not connect to the agent service or manage provider sessions. Platform runtime packages are **not published** in this preview; no Go executable is discovered on `PATH` and no provider is installed or invoked by Node.
594
-
595
- The Local Agent Beta production allowlist contains macOS and Windows only. Linux remains supported by the ordinary OpenYida CLI and by the explicit development-runtime workflow below, but Linux Local Agent packages are not published and production `connect`/`run` are rejected until native acceptance is complete.
596
-
597
- ```bash
598
- openyida agent doctor --json
599
- openyida agent doctor --development-runtime --runtime-path /absolute/path/openyida-runtime --runtime-manifest /absolute/path/runtime-manifest.json
600
- openyida agent connect --development-runtime --runtime-path /absolute/path/openyida-runtime --runtime-manifest /absolute/path/runtime-manifest.json --endpoint https://agent.example.test --endpoint-id configured-platform-id
601
- openyida agent run --development-runtime --runtime-path /absolute/path/openyida-runtime --runtime-manifest /absolute/path/runtime-manifest.json --endpoint https://agent.example.test --endpoint-id configured-platform-id --provider qoder --provider-path /absolute/path/qodercli
602
- ```
603
-
604
- Development manifest v1 specifies runtime version, protocol `1`, Node-style OS/architecture names, and `binary: {file, sha256}`; it requires the explicit `--development-runtime` flag. This is an integrity check, **not a publisher signature**. The production loader requires a strictly validated v2 manifest, exact approved platform-package version/digest, and an Ed25519 signature over the original manifest bytes using trust roots bundled in the Node host. The release catalog is currently empty: no production signing key or package is implied to exist. See the [launcher contract](lib/agent/PROTOCOL.md).
605
-
606
- `connect` supports two equivalent enrollment entries. CLI-first opens the Yida browser approval page and returns through a state/PKCE-protected loopback callback. Web-first copies one five-minute command containing `--enroll`; the token is single-use and carries no business authority. Neither path reads or uploads an ordinary OpenYida login profile.
607
-
608
- One installation may connect to multiple organizations. Node keeps only a shared Installation ID at the installation root; each organization authorization gets an isolated private Connection state directory, device credentials, journals, control socket, and frozen bundle. `agent connect` keeps the new Connection online in the foreground, while `agent run`, `status`, `disconnect`, and `logout` enumerate every valid Connection. Each Connection registers all locally discovered Qoder and Codex Runtimes, and the web UI selects computer, CLI, and model.
609
-
610
- For each attempt, Go injects `OPENYIDA_MANAGED_RUN=1`, the cloud user/corp/app/run/attempt context, and a fixed-value Tianshu task grant with an initial 60-minute lease. The CLI uses that grant instead of local auth and sends scope-correlation headers. While the authenticated device connection and server-side Run remain active, yida-agent can renew the same bearer server-to-server; the CLI cannot renew it and never receives a task refresh token. Tianshu independently checks the grant against the requested app and rejects application creation, identity/environment changes, arbitrary batches, and cross-application requests. `formUuid` remains optional per-turn page-focus context, so one app-bound session can work across pages in that application. Without `OPENYIDA_MANAGED_RUN`, existing login, token refresh, and command behavior are unchanged.
611
-
612
- Before `connect` or `run`, Node now freezes the verified runtime bytes, CLI, full production dependency closure, and packaged Skills into a private content-addressed bundle. Source changes during copying fail closed. Existing bundles are verified, never silently repaired or garbage-collected by Node, and survive replacement/removal of the original npm directory. Go owns active/session references and version activation; the external Node executable and provider installations are not copied or managed by npm. A separate parent-watch pipe and a 15-second stop grace prevent normal config EOF from being mistaken for parent death; an unconfirmed forced stop remains unknown.
613
-
614
- This is not an OS sandbox or an authorization boundary against arbitrary shell code. Production signing/release infrastructure, full Go/provider recovery and upgrade acceptance, and OS/architecture-specific provider E2E are not claimed by these fake-process tests. Auto-update is disabled for `agent` and managed invocations; ordinary CLI commands neither spawn nor download the runtime.
615
-
616
600
  ## Agent Skills
617
601
 
618
602
  The `yida-skills/` directory is the source skill library used by OpenYida during development and distributed with the npm package.
package/bin/yida.js CHANGED
@@ -24,8 +24,6 @@ const { COMMAND_GROUPS, buildCommandManifest, findCommandSuggestion } = require(
24
24
  const command = process.argv[2];
25
25
  const args = process.argv.slice(3);
26
26
  const rawArgs = process.argv.slice(3);
27
- const { beginCommandReceipt } = require('../lib/agent/command-receipt');
28
- let commandReceipt;
29
27
 
30
28
  function shouldUseEnvManagement(argsList) {
31
29
  const subCommand = argsList[0];
@@ -195,6 +193,7 @@ function printLoginResult(result) {
195
193
 
196
194
  if (!result) {
197
195
  console.log(JSON.stringify({
196
+ type: 'login_result',
198
197
  ok: false,
199
198
  status: 'login_failed',
200
199
  can_auto_use: false,
@@ -207,6 +206,7 @@ function printLoginResult(result) {
207
206
  ? !!result.ok
208
207
  : (result.can_auto_use !== false && !!result.access_token);
209
208
  const summary = {
209
+ type: 'login_result',
210
210
  ok: tokenUsable,
211
211
  status: result.status || (tokenUsable ? 'ok' : 'token_not_issued'),
212
212
  auth_mode: 'token',
@@ -281,6 +281,7 @@ function getFirstPositionalArg(cliArgs, startIndex = 0) {
281
281
  '--login-url',
282
282
  '--profile',
283
283
  '--user-id',
284
+ '--env-hint',
284
285
  ]);
285
286
  for (let index = startIndex; index < cliArgs.length; index++) {
286
287
  const arg = cliArgs[index];
@@ -388,6 +389,7 @@ function applyLoginEnvironmentFlags(cliArgs, options = {}) {
388
389
  '--client-id',
389
390
  '--profile',
390
391
  '--user-id',
392
+ '--env-hint',
391
393
  ]);
392
394
  const targetUrlFlags = new Set([
393
395
  '--endpoint',
@@ -539,13 +541,23 @@ function printLoginHelp() {
539
541
  }
540
542
 
541
543
  function buildTokenLoginOptions(loginArgs) {
544
+ const device = loginArgs.includes('--device');
542
545
  return {
543
546
  clientId: getArgValue(loginArgs, '--client-id'),
544
547
  corpId: getArgValue(loginArgs, '--corp-id'),
545
548
  userId: getArgValue(loginArgs, '--user-id'),
546
549
  authProfile: getArgValue(loginArgs, '--profile'),
547
550
  quiet: process.env.YIDA_QUIET === '1' || loginArgs.includes('--quiet'),
548
- noBrowser: loginArgs.includes('--no-browser'),
551
+ noBrowser: device || loginArgs.includes('--no-browser'),
552
+ device,
553
+ envHint: getArgValue(loginArgs, '--env-hint'),
554
+ // Machine-readable progress events for agent runtimes (sandbox browsers,
555
+ // CI dashboards). Emits one JSON line per state transition.
556
+ onDeviceState: (state) => {
557
+ if (device) {
558
+ process.stdout.write(JSON.stringify({ type: 'device_login_state', ...state }) + '\n');
559
+ }
560
+ },
549
561
  };
550
562
  }
551
563
 
@@ -573,26 +585,6 @@ function assertNoUnsupportedLegacyLoginFlags(...argLists) {
573
585
  }
574
586
 
575
587
  async function main() {
576
- const { assertManagedInvocation, resolveManagedHelp } = require('../lib/agent/managed-run');
577
- const { readManagedContext } = require('../lib/agent/managed-context');
578
- if (readManagedContext() && [command, ...rawArgs].some(arg => arg === '--help' || arg === '-h')) {
579
- assertManagedInvocation(command, rawArgs);
580
- const entries = resolveManagedHelp(command, rawArgs, { t, version: currentVersion });
581
- // Managed help is static and local: no Receipt, auth, update, first-run
582
- // guide or business command module may run on this path.
583
- commandReceipt = { finish() {} };
584
- if (rawArgs.includes('--json')) {
585
- console.log(JSON.stringify({ name: 'openyida', version: currentVersion, commands: entries }, null, 2));
586
- } else {
587
- printCommandUsage(...entries.flatMap(entry => [entry.usage, entry.description, ...(entry.examples || [])]));
588
- }
589
- return;
590
- }
591
- commandReceipt = beginCommandReceipt({ command, args: rawArgs });
592
- process.once('exit', code => {
593
- try {commandReceipt.finish(code);} catch {process.exitCode = 1;}
594
- });
595
- assertManagedInvocation(command, rawArgs);
596
588
  applyQuietFlag();
597
589
  applyGlobalEnvironmentFlags();
598
590
 
@@ -641,12 +633,6 @@ async function main() {
641
633
  break;
642
634
  }
643
635
 
644
- case 'agent': {
645
- const { run } = require('../lib/agent/cmd');
646
- await run(args);
647
- break;
648
- }
649
-
650
636
  case 'mcp': {
651
637
  const { runStdioServer } = require('../lib/mcp/server');
652
638
  runStdioServer();
@@ -761,6 +747,12 @@ async function main() {
761
747
  break;
762
748
  }
763
749
 
750
+ case 'app-entry': {
751
+ const { run } = require('../lib/app/app-entry');
752
+ await run(args);
753
+ break;
754
+ }
755
+
764
756
  case 'app-list': {
765
757
  const { run } = require('../lib/app/app-list');
766
758
  await run(args);
@@ -916,8 +908,8 @@ async function main() {
916
908
  if (args.length < 4) {
917
909
  throwCliUsage(t('cli.form_config_usage'), t('cli.form_config_example'));
918
910
  }
919
- const { run: runUpdateFormConfig } = require('../lib/app/update-form-config');
920
- await runUpdateFormConfig(args);
911
+ process.argv = [process.argv[0], process.argv[1], ...args];
912
+ require('../lib/app/update-form-config');
921
913
  break;
922
914
  }
923
915
 
@@ -1303,9 +1295,7 @@ async function main() {
1303
1295
  }
1304
1296
 
1305
1297
  main()
1306
- .then(() => { commandReceipt.finish(process.exitCode || 0); })
1307
1298
  .catch((err) => {
1308
- try {commandReceipt?.finish(err && err.exitCode ? err.exitCode : 1);} catch { /* Intent remains recoverable. */ }
1309
1299
  if (shouldUseStructuredErrorOutput(err, args)) {
1310
1300
  console.error(JSON.stringify(toErrorPayload(err), null, 2));
1311
1301
  } else if (isCliError(err)) {
@@ -1316,5 +1306,7 @@ main()
1316
1306
  } else {
1317
1307
  warn(t('cli.exec_failed', err.message));
1318
1308
  }
1319
- process.exit(err && err.exitCode ? err.exitCode : 1);
1309
+ // Let pending stdout/stderr writes drain, especially large JSON diagnostics
1310
+ // sent through pipes on macOS/Linux. process.exit() can truncate them.
1311
+ process.exitCode = err && err.exitCode ? err.exitCode : 1;
1320
1312
  });
@@ -0,0 +1,144 @@
1
+ 'use strict';
2
+
3
+ const { createAuthRef, createYidaClient, unwrapYidaResponse } = require('../core/yida-client');
4
+ const { t } = require('../core/i18n');
5
+ const { throwUsage } = require('../core/command-errors');
6
+ const { fetchFormPageList } = require('./form-navigation');
7
+
8
+ /** Keep complete runtime URLs; the server verifies tenant and resource ownership. */
9
+ function normalizeUrl(value, appType) {
10
+ const input = String(value || '').trim();
11
+ if (!input) { throw new Error(t('app_entry.invalid_url')); }
12
+ // eslint-disable-next-line no-control-regex -- reject control characters in persisted URLs
13
+ if (input.length > 2048 || /[\\\s\x00-\x1f\x7f]/.test(input)) {
14
+ throw new Error(t('app_entry.invalid_url'));
15
+ }
16
+ const match = /^https?:\/\/[^/?#]+(\/[^?#]*)(?:\?([^#]*))?(?:#(.*))?$/i.exec(input);
17
+ if (!match) { throw new Error(t('app_entry.invalid_url')); }
18
+ const url = new URL(input);
19
+ const route = match[1];
20
+ const prefix = `/${appType}/`;
21
+ const parts = route.startsWith(prefix) ? route.slice(prefix.length).split('/') : [];
22
+ const short = /^\/(?:s\/[A-Za-z0-9_-]+|o\/[A-Za-z0-9_-]+(?:\/FORM-[A-Za-z0-9_-]+)?)$/.test(route);
23
+ const runtime = ['custom', 'workbench', 'submission', 'manage'].includes(parts[0]) &&
24
+ ((parts.length === 1 && parts[0] === 'workbench') ||
25
+ (parts.length === 2 && /^[A-Za-z0-9_-]+$/.test(parts[1])));
26
+ if (url.username || url.password || url.pathname !== route || (!short && !runtime)) {
27
+ throw new Error(t('app_entry.invalid_url'));
28
+ }
29
+ if (match[2] !== undefined) {
30
+ const seen = new Set();
31
+ for (const parameter of match[2].split('&')) {
32
+ const pair = /^(corpid|locale|viewUuid|processCode|hideLeftNav|noShowTopBottom)=([A-Za-z0-9_-]+)$/.exec(parameter);
33
+ if (!pair || seen.has(pair[1]) ||
34
+ (['hideLeftNav', 'noShowTopBottom'].includes(pair[1]) && !/^(true|false|y|n|0|1)$/.test(pair[2]))) {
35
+ throw new Error(t('app_entry.invalid_url'));
36
+ }
37
+ seen.add(pair[1]);
38
+ }
39
+ }
40
+ if (match[3] !== undefined && !/^[A-Za-z0-9_/-]+$/.test(match[3])) {
41
+ throw new Error(t('app_entry.invalid_url'));
42
+ }
43
+ return input;
44
+ }
45
+
46
+ /** Resolve a page identifier into the canonical entry URL the platform expects. */
47
+ async function resolveEntryUrl(authRef, appType, key, formUuid) {
48
+ if (!/^[A-Za-z0-9_-]+$/.test(String(formUuid || ''))) {
49
+ throw new Error(t('app_entry.invalid_url'));
50
+ }
51
+ const forms = await fetchFormPageList(appType, authRef);
52
+ const form = Array.isArray(forms) ? forms.find((item) => item.formUuid === formUuid) : null;
53
+ if (!form) { throw new Error(t('app_entry.page_not_in_app', formUuid)); }
54
+ if (key === 'frontend') {
55
+ if (String(form.formType).toLowerCase() !== 'display') { throw new Error(t('app_entry.frontend_display_required', formUuid)); }
56
+ if (!form.pathName) { throw new Error(t('app_entry.page_offline', formUuid)); }
57
+ return normalizeUrl(`${authRef.baseUrl}/${appType}/custom/${formUuid}`, appType);
58
+ }
59
+ return normalizeUrl(`${authRef.baseUrl}/${appType}/workbench/${formUuid}`, appType);
60
+ }
61
+
62
+ /** Reject duplicate or ambiguous flags; omissions never clear stored entries. */
63
+ function parseArgs(args) {
64
+ const [action, appType, ...flags] = args;
65
+ if (!['get', 'set'].includes(action) || !/^[A-Za-z0-9_-]+$/.test(appType || '')) {
66
+ throwUsage(t('app_entry.usage'));
67
+ }
68
+ const values = {};
69
+ for (let index = 0; index < flags.length; index++) {
70
+ const flag = flags[index];
71
+ if (flag === '--json') {
72
+ continue;
73
+ }
74
+ const match = /^--(clear-)?(frontend|management)(-page)?$/.exec(flag);
75
+ if (action !== 'set' || !match || (match[1] && match[3]) ||
76
+ Object.prototype.hasOwnProperty.call(values, match[2])) {
77
+ throwUsage(t('app_entry.usage'));
78
+ }
79
+ const kind = match[1] ? 'clear' : (match[3] ? 'page' : 'url');
80
+ const value = kind === 'clear' ? null : flags[++index];
81
+ if (kind !== 'clear' && (!value || value.startsWith('--'))) {
82
+ throwUsage(t('app_entry.usage'));
83
+ }
84
+ values[match[2]] = { kind, value };
85
+ }
86
+ if (action === 'set' && !Object.keys(values).length) {
87
+ throwUsage(t('app_entry.usage'));
88
+ }
89
+ return { action, appType, values };
90
+ }
91
+
92
+ /** Read the revision used for a conditional partial write. */
93
+ async function readEntries(client, appType) {
94
+ const result = unwrapYidaResponse(await client.get(`/${appType}/query/app/getAccessEntries.json`));
95
+ if (!result || typeof result.revision !== 'string' || !result.revision ||
96
+ !result.accessEntries || typeof result.accessEntries !== 'object' || Array.isArray(result.accessEntries)) {
97
+ throw new Error(t('app_entry.invalid_response'));
98
+ }
99
+ return result;
100
+ }
101
+
102
+ /** Persist only supplied entries and verify them through a separate read. */
103
+ async function run(args = []) {
104
+ if (args.includes('--help') || args.includes('-h')) {
105
+ console.log(t('app_entry.usage'));
106
+ return;
107
+ }
108
+ const { action, appType, values } = parseArgs(args);
109
+ const authRef = createAuthRef();
110
+ const client = createYidaClient({ authRef });
111
+ const patch = {};
112
+ for (const [key, spec] of Object.entries(values)) {
113
+ if (spec.kind === 'clear') {
114
+ patch[key] = null;
115
+ } else if (spec.kind === 'page') {
116
+ patch[key] = { url: await resolveEntryUrl(authRef, appType, key, spec.value) };
117
+ } else {
118
+ const url = normalizeUrl(spec.value, appType);
119
+ patch[key] = { url };
120
+ }
121
+ }
122
+ let result = await readEntries(client, appType);
123
+ if (action === 'set') {
124
+ unwrapYidaResponse(await client.postForm(`/${appType}/query/app/saveAccessEntries.json`, {
125
+ accessEntries: JSON.stringify(patch), revision: result.revision,
126
+ }));
127
+ result = await readEntries(client, appType);
128
+ for (const [key, entry] of Object.entries(patch)) {
129
+ if ((result.accessEntries[key]?.url || '') !== (entry?.url || '')) {
130
+ throw new Error(t('app_entry.readback_failed'));
131
+ }
132
+ }
133
+ }
134
+ const urls = {};
135
+ for (const key of ['frontend', 'management']) {
136
+ if (result.accessEntries[key]?.url) {
137
+ urls[key] = normalizeUrl(result.accessEntries[key].url, appType);
138
+ }
139
+ }
140
+ console.log(JSON.stringify({ appType, ...result, urls }, null, 2));
141
+ return result;
142
+ }
143
+
144
+ module.exports = { run, parseArgs, normalizeUrl, resolveEntryUrl };
@@ -84,6 +84,7 @@ function formatApp(app, baseUrl) {
84
84
  appName: (app.appName && app.appName.zh_CN) || '',
85
85
  appType: app.appType || '',
86
86
  systemLink: app.systemLink || '',
87
+ ...(app.accessEntries ? { accessEntries: app.accessEntries } : {}),
87
88
  ...buildApplicationEntryUrls({ appType: app.appType, systemLink: app.systemLink, baseUrl }),
88
89
  };
89
90
  }
@@ -0,0 +1,67 @@
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const { CliError } = require('../core/cli-error');
6
+ const { t } = require('../core/i18n');
7
+ const { DESIGN_SKILL_ROOT, loadThemeIndex } = require('../design-plan/themes');
8
+ const START = '/* OPENYIDA APPLICATION STYLE RECIPES START';
9
+ const END = '/* OPENYIDA APPLICATION STYLE RECIPES END */';
10
+ const CREATIVE_TOKENS = [
11
+ '--pod-page-bg-color', '--pod-card-bg-color', '--color-text1-4',
12
+ '--form-element-medium-corner', '--form-element-medium-height',
13
+ '--pod-page-footer-bg-color', '--pod-sticky-footer-box-shadow',
14
+ '--oyd-content-width', '--oyd-content-padding', '--oyd-field-gap',
15
+ '--oyd-heading-font', '--oyd-heading-size', '--oyd-rule-style',
16
+ ];
17
+
18
+ function validateCreativeDirection(direction, tokens) {
19
+ const missing = ['businessRationale', 'composition', 'typography', 'material', 'formLayout']
20
+ .filter(key => typeof direction?.[key] !== 'string' || !direction[key].trim());
21
+ missing.push(...CREATIVE_TOKENS.filter(key => typeof tokens?.[key] !== 'string' || !tokens[key].trim()));
22
+ if (missing.length) {
23
+ throw new CliError(t('design_document.invalid', 'creativeDirection', missing.join(', ')), {
24
+ code: 'APPLICATION_STYLE_CREATIVE_INCOMPLETE', details: { missing },
25
+ });
26
+ }
27
+ }
28
+
29
+ function applyApplicationStyle(css, metadata, tokens) {
30
+ const style = metadata.applicationStyle;
31
+ if (style && (style.recipe !== 'application-style-v1' || !['template', 'creative'].includes(style.mode))) {
32
+ throw new CliError(t('design_document.invalid', 'applicationStyle', 'APPLICATION_STYLE_INVALID'), { code: 'APPLICATION_STYLE_INVALID' });
33
+ }
34
+ if (style?.mode === 'creative') {validateCreativeDirection(metadata.creativeDirection, tokens);}
35
+ const first = css.indexOf(START);
36
+ const last = css.indexOf(END);
37
+ if (first >= 0 && last < first) {
38
+ throw new CliError(t('design_document.invalid', 'app_theme.css', 'APPLICATION_STYLE_CSS_INVALID'), { code: 'APPLICATION_STYLE_CSS_INVALID' });
39
+ }
40
+ const recipe = style
41
+ ? fs.readFileSync(path.join(DESIGN_SKILL_ROOT, 'references/theme/application-style-recipes.css'), 'utf8').trim()
42
+ : '';
43
+ // Only the managed recipe block is replaced. User-authored rules stay intact.
44
+ if (first >= 0) {return css.slice(0, first) + recipe + css.slice(last + END.length);}
45
+ return recipe ? `${css.trimEnd()}\n\n${recipe}\n` : css;
46
+ }
47
+
48
+ function exportApplicationStyle(id, outputDirectory) {
49
+ const theme = loadThemeIndex().themes.find(item => item.themeId === id);
50
+ if (!theme) {throw new CliError(t('design_document.invalid', id || '--style-id', 'APPLICATION_STYLE_UNKNOWN'), { code: 'APPLICATION_STYLE_UNKNOWN' });}
51
+ const directory = path.resolve(outputDirectory);
52
+ const assets = [['design.md', theme.templatePath], ['app_theme.css', theme.cssTemplatePath], ['form-layout.json', theme.formLayoutPath]];
53
+ const outputs = assets.map(([name, relative]) => {
54
+ const source = path.resolve(DESIGN_SKILL_ROOT, relative);
55
+ const target = path.join(directory, name);
56
+ const root = path.resolve(DESIGN_SKILL_ROOT, 'templates/design-themes') + path.sep;
57
+ if (!source.startsWith(root) || fs.existsSync(target)) {
58
+ throw new CliError(t('design_document.invalid', target, 'APPLICATION_STYLE_OUTPUT_CONFLICT'), { code: 'APPLICATION_STYLE_OUTPUT_CONFLICT' });
59
+ }
60
+ return { source, target, content: fs.readFileSync(source, 'utf8') };
61
+ });
62
+ fs.mkdirSync(directory, { recursive: true });
63
+ for (const item of outputs) {fs.writeFileSync(item.target, item.content, { flag: 'wx' });}
64
+ return { success: true, mode: theme.mode, themeId: id, authoringRequired: true, outputs: outputs.map(item => item.target) };
65
+ }
66
+
67
+ module.exports = { applyApplicationStyle, validateCreativeDirection, CREATIVE_TOKENS, exportApplicationStyle };
@@ -976,7 +976,12 @@ function compileCanvasLocal(source, options = {}) {
976
976
  assertCanvasNavigationStructure(source, options);
977
977
  assertCanvasNavigationPaths(source, options);
978
978
  assertInlineCssStructure(source, options);
979
- assertCanvasThemeStructure(source, options);
979
+ const warnings = [];
980
+ assertCanvasThemeStructure(source, { ...options, onThemeWarning(message, details) {
981
+ warnings.push({ ...details, message });
982
+ if (options.onThemeWarning) { options.onThemeWarning(message, details); }
983
+ else { require('../core/chalk').warn(message); }
984
+ } });
980
985
 
981
986
  const importedModules = extractImportedModules(source);
982
987
 
@@ -1036,6 +1041,7 @@ function compileCanvasLocal(source, options = {}) {
1036
1041
  return {
1037
1042
  runtimeCode,
1038
1043
  importedModules: JSON.stringify(importedModules),
1044
+ warnings,
1039
1045
  };
1040
1046
  }
1041
1047