openyida 2026.9.23-beta.2 → 2026.9.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -32
- package/bin/yida.js +11 -33
- package/lib/app/app-entry.js +144 -0
- package/lib/app/app-list.js +1 -0
- package/lib/app/application-style.js +67 -0
- package/lib/app/canvas-compile.js +7 -1
- package/lib/app/canvas-theme-guard.js +127 -10
- package/lib/app/check-prd-completeness.js +4 -15
- package/lib/app/compile.js +12 -2
- package/lib/app/create-form/args.js +37 -12
- package/lib/app/create-form/batch.js +41 -35
- package/lib/app/create-form.js +94 -57
- package/lib/app/custom-theme.js +30 -37
- package/lib/app/fix-theme.js +34 -0
- package/lib/app/form-field-validator.js +0 -36
- package/lib/app/get-schema.js +1 -6
- package/lib/app/publish.js +49 -7
- package/lib/app/services/canvas-page-compiler.js +3 -1
- package/lib/app/services/form-compiler.js +0 -2
- package/lib/app/theme-from-design.js +77 -87
- package/lib/app/theme-scope.js +76 -0
- package/lib/app/update-app.js +64 -26
- package/lib/app/update-form-config.js +9 -19
- package/lib/auth/token-auth.js +6 -57
- package/lib/auth/token-store.js +2 -26
- package/lib/core/cli-error.js +1 -0
- package/lib/core/command-manifest.js +121 -48
- package/lib/core/locales/en.js +47 -52
- package/lib/core/locales/zh.js +47 -52
- package/lib/core/sample.js +36 -14
- package/lib/core/source-repair.js +38 -0
- package/lib/core/theme-brand-scale.js +133 -0
- package/lib/core/update.js +1 -3
- package/lib/core/utils.js +0 -29
- package/lib/design/check-design.js +16 -6
- package/lib/design/document.js +37 -15
- package/lib/design/icon-contrast.js +1 -1
- package/lib/design/palette-contrast.js +77 -0
- package/lib/design-plan/confirmation.js +13 -0
- package/lib/design-plan/design-plan.js +65 -8
- package/lib/design-plan/entry-navigation.js +10 -2
- package/lib/design-plan/init.js +17 -10
- package/lib/design-plan/materialize.js +88 -58
- package/lib/design-plan/normalize.js +13 -14
- package/lib/design-plan/parallel.js +62 -13
- package/lib/design-plan/patch.js +21 -7
- package/lib/design-plan/preview.js +4 -1
- package/lib/design-plan/rebase.js +69 -0
- package/lib/design-plan/themes.js +16 -1
- package/lib/design-plan/validate.js +18 -3
- package/lib/design-plan/visual-policy.js +29 -4
- package/lib/page-config/save-share-config.js +5 -16
- package/lib/samples/openyida-scaffold/canvas-form-drawer.canvas.jsx +3 -3
- package/lib/samples/openyida-scaffold/canvas-nav/mixed.jsx +6 -4
- package/lib/samples/openyida-scaffold/canvas-nav/shared.jsx +5 -5
- package/lib/samples/openyida-scaffold/canvas-nav/side.jsx +1 -2
- package/lib/samples/openyida-scaffold/canvas-nav/sidebar.jsx +1 -1
- package/lib/samples/openyida-scaffold/canvas-nav/top.jsx +5 -4
- package/package.json +3 -11
- package/yida-skills/SKILL.md +3 -25
- package/yida-skills/references/official-example-schema-patterns.md +1 -1
- package/yida-skills/references/source-repair.md +10 -0
- package/yida-skills/references/yida-api.md +4 -0
- package/yida-skills/skills/yida-app/SKILL.md +15 -4
- package/yida-skills/skills/yida-app/references/entry-navigation.md +11 -9
- package/yida-skills/skills/yida-app/workflow/plan/step-4-deliver.md +3 -1
- package/yida-skills/skills/yida-app/workflow/plan/workflow.md +21 -5
- package/yida-skills/skills/yida-app/workflow/step-2-design.md +4 -4
- package/yida-skills/skills/yida-app/workflow/step-3-create-or-reuse-app.md +2 -2
- package/yida-skills/skills/yida-app/workflow/step-8-publish-navigation.md +3 -3
- package/yida-skills/skills/yida-app/workflow/step-9-output-finish.md +25 -2
- package/yida-skills/skills/yida-canvas-custom-page/SKILL.md +3 -2
- package/yida-skills/skills/yida-canvas-custom-page/references/canvas-style-implementation-guide.md +1 -1
- package/yida-skills/skills/yida-canvas-custom-page/references/canvas-theme-provider.md +4 -2
- package/yida-skills/skills/yida-canvas-custom-page/references/data-bridge-guide.md +140 -1
- package/yida-skills/skills/yida-canvas-custom-page/references/navigation-and-entry-guide.md +1 -1
- package/yida-skills/skills/yida-canvas-custom-page/references/waf-safe-authoring.md +45 -0
- package/yida-skills/skills/yida-canvas-custom-page/scripts/build-canvas-theme.js +2 -1
- package/yida-skills/skills/yida-canvas-custom-page/scripts/canvas-theme-provider.template.jsx +8 -1
- package/yida-skills/skills/yida-canvas-data-binding/SKILL.md +7 -0
- package/yida-skills/skills/yida-create-form-page/SKILL.md +34 -43
- package/yida-skills/skills/yida-create-form-page/references/advanced-form-modes.md +5 -5
- package/yida-skills/skills/yida-create-form-page/references/association-form-field.md +4 -4
- package/yida-skills/skills/yida-create-form-page/references/batch-forms.md +2 -2
- package/yida-skills/skills/yida-create-form-page/references/field-definition-guide.md +4 -16
- package/yida-skills/skills/yida-create-form-page/references/form-field-properties.md +14 -37
- package/yida-skills/skills/yida-design/SKILL.md +16 -8
- package/yida-skills/skills/yida-design/references/application-style-library.md +93 -0
- package/yida-skills/skills/yida-design/references/application-theme-consistency.md +92 -7
- package/yida-skills/skills/yida-design/references/ask-human-interaction-contract.md +1 -1
- package/yida-skills/skills/yida-design/references/native-form-styles.md +208 -0
- package/yida-skills/skills/yida-design/references/navigation-decision.md +8 -6
- package/yida-skills/skills/yida-design/references/theme/app-custom-theme-template.css +136 -113
- package/yida-skills/skills/yida-design/references/theme/application-style-recipes.css +58 -0
- package/yida-skills/skills/yida-design/references/theme-selection.md +12 -11
- package/yida-skills/skills/yida-design/references/visual-decision-engine.md +1 -1
- package/yida-skills/skills/yida-design/scripts/validate_design_themes.py +52 -7
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/SKILL.md +15 -3
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/references/build-plan-compact-schema.md +6 -2
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/references/build-plan-schema.md +5 -3
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/references/visual-design.md +3 -1
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/references/visual-theme-selection.md +22 -23
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/scripts/render_build_plan.py +2 -0
- package/yida-skills/skills/yida-design/templates/design-themes/README.md +17 -10
- package/yida-skills/skills/yida-design/templates/design-themes/app-amber/app_theme.css +570 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-amber/design.md +238 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-amber/form-layout.json +15 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-editorial/app_theme.css +570 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-editorial/design.md +239 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-editorial/form-layout.json +14 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-executive/app_theme.css +570 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-executive/design.md +239 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-executive/form-layout.json +13 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-finance/app_theme.css +570 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-finance/design.md +238 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-finance/form-layout.json +14 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-glass/app_theme.css +570 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-glass/design.md +239 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-glass/form-layout.json +14 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-graphite/app_theme.css +570 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-graphite/design.md +239 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-graphite/form-layout.json +14 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-line/app_theme.css +570 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-line/design.md +239 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-line/form-layout.json +13 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-neon/app_theme.css +570 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-neon/design.md +237 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-neon/form-layout.json +14 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-nordic/app_theme.css +570 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-nordic/design.md +239 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-nordic/form-layout.json +13 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-paper/app_theme.css +570 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-paper/design.md +237 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-paper/form-layout.json +13 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-platinum/app_theme.css +570 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-platinum/design.md +239 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-platinum/form-layout.json +14 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-plum/app_theme.css +570 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-plum/design.md +239 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-plum/form-layout.json +14 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-pop/app_theme.css +570 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-pop/design.md +239 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-pop/form-layout.json +14 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-sage/app_theme.css +570 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-sage/design.md +239 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-sage/form-layout.json +14 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-teal/app_theme.css +570 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-teal/design.md +239 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-teal/form-layout.json +14 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-terminal/app_theme.css +570 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-terminal/design.md +238 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-terminal/form-layout.json +14 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-ticket/app_theme.css +570 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-ticket/design.md +241 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-ticket/form-layout.json +13 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-wire/app_theme.css +570 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-wire/design.md +238 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-wire/form-layout.json +14 -0
- package/yida-skills/skills/yida-design/templates/design-themes/basic-tokens.json +1 -7
- package/yida-skills/skills/yida-design/templates/design-themes/dark-inset-hairline/app_theme.css +509 -0
- package/yida-skills/skills/yida-design/templates/design-themes/{dark-inset-hairline.md → dark-inset-hairline/design.md} +97 -58
- package/yida-skills/skills/yida-design/templates/design-themes/dark-inset-hairline/form-layout.json +13 -0
- package/yida-skills/skills/yida-design/templates/design-themes/dark-rail-fine-lines/app_theme.css +517 -0
- package/yida-skills/skills/yida-design/templates/design-themes/{dark-rail-fine-lines.md → dark-rail-fine-lines/design.md} +98 -51
- package/yida-skills/skills/yida-design/templates/design-themes/dark-rail-fine-lines/form-layout.json +13 -0
- package/yida-skills/skills/yida-design/templates/design-themes/dark-rail-signal-panels/app_theme.css +515 -0
- package/yida-skills/skills/yida-design/templates/design-themes/{dark-rail-signal-panels.md → dark-rail-signal-panels/design.md} +59 -12
- package/yida-skills/skills/yida-design/templates/design-themes/dark-rail-signal-panels/form-layout.json +13 -0
- package/yida-skills/skills/yida-design/templates/design-themes/free-creative/app_theme.css +511 -0
- package/yida-skills/skills/yida-design/templates/design-themes/free-creative/design.md +202 -0
- package/yida-skills/skills/yida-design/templates/design-themes/free-creative/form-layout.json +13 -0
- package/yida-skills/skills/yida-design/templates/design-themes/graphite-bevel-grid/app_theme.css +511 -0
- package/yida-skills/skills/yida-design/templates/design-themes/{graphite-bevel-grid.md → graphite-bevel-grid/design.md} +55 -10
- package/yida-skills/skills/yida-design/templates/design-themes/graphite-bevel-grid/form-layout.json +13 -0
- package/yida-skills/skills/yida-design/templates/design-themes/hairline-shared-bands/app_theme.css +510 -0
- package/yida-skills/skills/yida-design/templates/design-themes/{hairline-shared-bands.md → hairline-shared-bands/design.md} +57 -12
- package/yida-skills/skills/yida-design/templates/design-themes/hairline-shared-bands/form-layout.json +13 -0
- package/yida-skills/skills/yida-design/templates/design-themes/hairline-soft-blocks/app_theme.css +510 -0
- package/yida-skills/skills/yida-design/templates/design-themes/{hairline-soft-blocks.md → hairline-soft-blocks/design.md} +52 -10
- package/yida-skills/skills/yida-design/templates/design-themes/hairline-soft-blocks/form-layout.json +13 -0
- package/yida-skills/skills/yida-design/templates/design-themes/index.json +399 -31
- package/yida-skills/skills/yida-design/templates/design-themes/ink-glow-soft-panels/app_theme.css +520 -0
- package/yida-skills/skills/yida-design/templates/design-themes/{ink-glow-soft-panels.md → ink-glow-soft-panels/design.md} +53 -9
- package/yida-skills/skills/yida-design/templates/design-themes/ink-glow-soft-panels/form-layout.json +13 -0
- package/yida-skills/skills/yida-design/templates/design-themes/inset-frame-pixel-rhythm/app_theme.css +510 -0
- package/yida-skills/skills/yida-design/templates/design-themes/{inset-frame-pixel-rhythm.md → inset-frame-pixel-rhythm/design.md} +55 -10
- package/yida-skills/skills/yida-design/templates/design-themes/inset-frame-pixel-rhythm/form-layout.json +13 -0
- package/yida-skills/skills/yida-design/templates/design-themes/outlined-texture-duotone/app_theme.css +517 -0
- package/yida-skills/skills/yida-design/templates/design-themes/{outlined-texture-duotone.md → outlined-texture-duotone/design.md} +56 -12
- package/yida-skills/skills/yida-design/templates/design-themes/outlined-texture-duotone/form-layout.json +13 -0
- package/yida-skills/skills/yida-design/templates/design-themes/soft-inset-surfaces/app_theme.css +510 -0
- package/yida-skills/skills/yida-design/templates/design-themes/{soft-inset-surfaces.md → soft-inset-surfaces/design.md} +54 -12
- package/yida-skills/skills/yida-design/templates/design-themes/soft-inset-surfaces/form-layout.json +13 -0
- package/yida-skills/skills/yida-design/templates/design-themes/soft-outline-rhythm/app_theme.css +509 -0
- package/yida-skills/skills/yida-design/templates/design-themes/{soft-outline-rhythm.md → soft-outline-rhythm/design.md} +54 -26
- package/yida-skills/skills/yida-design/templates/design-themes/soft-outline-rhythm/form-layout.json +13 -0
- package/yida-skills/skills/yida-design/templates/design-themes/soft-rail-bold-band/app_theme.css +517 -0
- package/yida-skills/skills/yida-design/templates/design-themes/{soft-rail-bold-band.md → soft-rail-bold-band/design.md} +94 -50
- package/yida-skills/skills/yida-design/templates/design-themes/soft-rail-bold-band/form-layout.json +13 -0
- package/yida-skills/skills/yida-design/templates/design-themes/soft-spectral-panels/app_theme.css +511 -0
- package/yida-skills/skills/yida-design/templates/design-themes/{soft-spectral-panels.md → soft-spectral-panels/design.md} +59 -13
- package/yida-skills/skills/yida-design/templates/design-themes/soft-spectral-panels/form-layout.json +13 -0
- package/yida-skills/skills/yida-design/templates/design-themes/warm-canvas-contrast-panels/app_theme.css +530 -0
- package/yida-skills/skills/yida-design/templates/design-themes/{warm-canvas-contrast-panels.md → warm-canvas-contrast-panels/design.md} +55 -11
- package/yida-skills/skills/yida-design/templates/design-themes/warm-canvas-contrast-panels/form-layout.json +13 -0
- package/yida-skills/skills/yida-design/templates/design-themes/warm-rail-muted-panels/app_theme.css +511 -0
- package/yida-skills/skills/yida-design/templates/design-themes/{warm-rail-muted-panels.md → warm-rail-muted-panels/design.md} +94 -52
- package/yida-skills/skills/yida-design/templates/design-themes/warm-rail-muted-panels/form-layout.json +13 -0
- package/yida-skills/skills/yida-design/workflow/output-design.md +44 -8
- package/yida-skills/skills/yida-design/workflow/step-2-theme-system.md +18 -5
- package/yida-skills/skills/yida-design/workflow/step-6-handoff.md +3 -1
- package/yida-skills/skills/yida-document-markdown/SKILL.md +0 -6
- package/yida-skills/skills/yida-export-conversation/SKILL.md +0 -1
- package/yida-skills/skills/yida-form-permission/SKILL.md +6 -0
- package/yida-skills/skills/yida-nav-shell/SKILL.md +5 -3
- package/yida-skills/skills/yida-nav-shell/references/nav-shell-patterns.md +6 -0
- package/yida-skills/skills/yida-page-config/SKILL.md +9 -0
- package/yida-skills/skills/yida-prd/SKILL.md +2 -1
- package/yida-skills/skills/yida-prd/workflow/output-prd.md +2 -2
- package/yida-skills/skills/yida-prd/workflow/step-2-information-architecture.md +1 -1
- package/yida-skills/skills/yida-publish-page/SKILL.md +22 -18
- package/yida-skills/skills/yida-requirement-analysis/references/experience-groups.md +45 -9
- package/yida-skills/skills/yida-requirement-analysis/references/handoff.md +6 -6
- package/yida-skills/skills/yida-requirement-analysis/workflow/prepare-brief.md +17 -4
- package/yida-skills/skills/yida-tingji/SKILL.md +0 -4
- package/yida-skills/skills-index.json +4 -4
- package/lib/agent/PROTOCOL.md +0 -165
- package/lib/agent/ask-human.js +0 -151
- package/lib/agent/cmd.js +0 -403
- package/lib/agent/command-receipt.js +0 -84
- package/lib/agent/connection-store.js +0 -148
- package/lib/agent/diagnostic-log.js +0 -133
- package/lib/agent/frozen-bundle.js +0 -286
- package/lib/agent/managed-command-map.js +0 -117
- package/lib/agent/managed-context.js +0 -40
- package/lib/agent/managed-run.js +0 -96
- package/lib/agent/mutation-receipt.js +0 -82
- package/lib/agent/private-directory.js +0 -63
- package/lib/agent/provider-preflight.js +0 -236
- package/lib/agent/providers.js +0 -4
- package/lib/agent/runtime-catalog.json +0 -42
- package/lib/agent/runtime-manifest.js +0 -96
- package/lib/agent/runtime-package.js +0 -155
- package/lib/agent/stdio.js +0 -265
- package/lib/auth/profile-lock.js +0 -89
- package/yida-skills/references/local-agent-diagnostics.md +0 -154
- package/yida-skills/references/local-agent-preflight.md +0 -56
package/README.md
CHANGED
|
@@ -118,6 +118,7 @@ The agent can then call OpenYida commands to create the application, generate so
|
|
|
118
118
|
| [Codex](https://openai.com/codex/) | Full support |
|
|
119
119
|
| [Claude Code](https://claude.ai/code) | Full support |
|
|
120
120
|
| [MuleRun](https://mulerun.com) | Full support |
|
|
121
|
+
| [OpenCode](https://opencode.ai) | Full support |
|
|
121
122
|
| [Cursor](https://cursor.com/) | Full support |
|
|
122
123
|
| [Visual Studio Code](https://code.visualstudio.com/) | Full support |
|
|
123
124
|
| QwenWork(千问办公) | Full support |
|
|
@@ -182,7 +183,7 @@ openyida get-schema APP_XXX FORM_XXX --compact --resolve-fields "Customer Name,S
|
|
|
182
183
|
openyida get-schema APP_XXX --all --output-dir .cache/schemas
|
|
183
184
|
```
|
|
184
185
|
|
|
185
|
-
|
|
186
|
+
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
187
|
|
|
187
188
|
### Custom Page Development
|
|
188
189
|
|
|
@@ -392,13 +393,15 @@ Run `openyida --help` or `openyida <command> --help` for detailed usage.
|
|
|
392
393
|
|
|
393
394
|
| Command | Description |
|
|
394
395
|
|---------|-------------|
|
|
396
|
+
| `openyida app-entry get <appType> [--json]` | Read frontend and management runtime entries |
|
|
397
|
+
| `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
398
|
| `openyida app-list [--type managed\|created] [--page N] [--size N]` | Page through apps I manage or created |
|
|
396
399
|
| `openyida corp-efficiency [overview\|details\|detail\|groups\|notify] [options] [--open\|--no-open]` | Query enterprise efficiency overview and detail reports |
|
|
397
400
|
| `openyida create-app "<name>"\|--name <name> [options] [--locale zh_CN\|en_US\|ja_JP] [--open\|--no-open]` | Create a Yida app |
|
|
398
401
|
| `openyida design-plan catalog [--json]` | List available planning themes and page patterns |
|
|
399
402
|
| `openyida design-plan init <requirement-brief.json> [--theme-id <id>] [--output-dir <dir>] [--json]` | Initialize a plan draft from confirmed requirements |
|
|
400
403
|
| `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 |
|
|
404
|
+
| `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
405
|
| `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
406
|
| `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
407
|
| `openyida app-online <appType> [--to-ding-app-center] [--show-app-center]` | Enable a Yida app |
|
|
@@ -414,7 +417,7 @@ Run `openyida --help` or `openyida <command> --help` for detailed usage.
|
|
|
414
417
|
| Command | Description |
|
|
415
418
|
|---------|-------------|
|
|
416
419
|
| `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 |
|
|
420
|
+
| `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
421
|
| `openyida create-form icons [--json]` | List available form navigation icons |
|
|
419
422
|
| `openyida create-form validate-fields <fieldsJsonOrFile> [--json]` | Validate form field JSON locally |
|
|
420
423
|
| `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 +437,8 @@ Run `openyida --help` or `openyida <command> --help` for detailed usage.
|
|
|
434
437
|
| `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
438
|
| `openyida build-page <sourceFile> [--output file\|--write]` | Build Yida-compatible page source |
|
|
436
439
|
| `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 |
|
|
440
|
+
| `openyida compile <src> [--canvas] [--compat] [--skip-lint] [--strict-theme\|--allow-fixed-brand] [--json]` | Compile custom page locally |
|
|
441
|
+
| `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
442
|
| `openyida update-form-config <appType> <formUuid> <true\|false\|keep> "<title>" [--locale zh_CN\|en_US\|ja_JP]` | Update form configuration |
|
|
440
443
|
| `openyida get-form-config <appType> <formUuid> [--json]` | Query form configuration |
|
|
441
444
|
|
|
@@ -519,11 +522,10 @@ Run `openyida --help` or `openyida <command> --help` for detailed usage.
|
|
|
519
522
|
|---------|-------------|
|
|
520
523
|
| `openyida commands [--json]` | Output machine-readable command manifest |
|
|
521
524
|
| `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
525
|
| `openyida a2a <serve\|agent-card> [options]` | Start local read-only A2A adapter or print Agent Card |
|
|
524
526
|
| `openyida bridge start [--token <pair-token>] [--port 6736] [--origin https://demo.aliwork.com] [--open\|--no-open]` | Start OpenYida local web bridge service |
|
|
525
527
|
| `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 |
|
|
528
|
+
| `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
529
|
| `openyida doctor [--fix]` | Environment diagnostics & auto-fix |
|
|
528
530
|
| `openyida db-seq-fix [--fix]` | Detect and repair PostgreSQL sequence drift |
|
|
529
531
|
| `openyida formula evaluate <formula\|file> [--schema file]` | Static-check Yida formula syntax and field refs |
|
|
@@ -588,31 +590,6 @@ Form field definitions can include `alias` or `componentAlias` to populate Yida
|
|
|
588
590
|
|
|
589
591
|
`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
592
|
|
|
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
593
|
## Agent Skills
|
|
617
594
|
|
|
618
595
|
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];
|
|
@@ -573,26 +571,6 @@ function assertNoUnsupportedLegacyLoginFlags(...argLists) {
|
|
|
573
571
|
}
|
|
574
572
|
|
|
575
573
|
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
574
|
applyQuietFlag();
|
|
597
575
|
applyGlobalEnvironmentFlags();
|
|
598
576
|
|
|
@@ -641,12 +619,6 @@ async function main() {
|
|
|
641
619
|
break;
|
|
642
620
|
}
|
|
643
621
|
|
|
644
|
-
case 'agent': {
|
|
645
|
-
const { run } = require('../lib/agent/cmd');
|
|
646
|
-
await run(args);
|
|
647
|
-
break;
|
|
648
|
-
}
|
|
649
|
-
|
|
650
622
|
case 'mcp': {
|
|
651
623
|
const { runStdioServer } = require('../lib/mcp/server');
|
|
652
624
|
runStdioServer();
|
|
@@ -761,6 +733,12 @@ async function main() {
|
|
|
761
733
|
break;
|
|
762
734
|
}
|
|
763
735
|
|
|
736
|
+
case 'app-entry': {
|
|
737
|
+
const { run } = require('../lib/app/app-entry');
|
|
738
|
+
await run(args);
|
|
739
|
+
break;
|
|
740
|
+
}
|
|
741
|
+
|
|
764
742
|
case 'app-list': {
|
|
765
743
|
const { run } = require('../lib/app/app-list');
|
|
766
744
|
await run(args);
|
|
@@ -916,8 +894,8 @@ async function main() {
|
|
|
916
894
|
if (args.length < 4) {
|
|
917
895
|
throwCliUsage(t('cli.form_config_usage'), t('cli.form_config_example'));
|
|
918
896
|
}
|
|
919
|
-
|
|
920
|
-
|
|
897
|
+
process.argv = [process.argv[0], process.argv[1], ...args];
|
|
898
|
+
require('../lib/app/update-form-config');
|
|
921
899
|
break;
|
|
922
900
|
}
|
|
923
901
|
|
|
@@ -1303,9 +1281,7 @@ async function main() {
|
|
|
1303
1281
|
}
|
|
1304
1282
|
|
|
1305
1283
|
main()
|
|
1306
|
-
.then(() => { commandReceipt.finish(process.exitCode || 0); })
|
|
1307
1284
|
.catch((err) => {
|
|
1308
|
-
try {commandReceipt?.finish(err && err.exitCode ? err.exitCode : 1);} catch { /* Intent remains recoverable. */ }
|
|
1309
1285
|
if (shouldUseStructuredErrorOutput(err, args)) {
|
|
1310
1286
|
console.error(JSON.stringify(toErrorPayload(err), null, 2));
|
|
1311
1287
|
} else if (isCliError(err)) {
|
|
@@ -1316,5 +1292,7 @@ main()
|
|
|
1316
1292
|
} else {
|
|
1317
1293
|
warn(t('cli.exec_failed', err.message));
|
|
1318
1294
|
}
|
|
1319
|
-
|
|
1295
|
+
// Let pending stdout/stderr writes drain, especially large JSON diagnostics
|
|
1296
|
+
// sent through pipes on macOS/Linux. process.exit() can truncate them.
|
|
1297
|
+
process.exitCode = err && err.exitCode ? err.exitCode : 1;
|
|
1320
1298
|
});
|
|
@@ -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 };
|
package/lib/app/app-list.js
CHANGED
|
@@ -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
|
-
|
|
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
|
|
|
@@ -2,8 +2,32 @@
|
|
|
2
2
|
|
|
3
3
|
const Babel = require('@babel/standalone');
|
|
4
4
|
const { CliError } = require('../core/cli-error');
|
|
5
|
+
const { warn } = require('../core/chalk');
|
|
5
6
|
const { t } = require('../core/i18n');
|
|
6
7
|
|
|
8
|
+
// Whether provably fixed brand overrides should be downgraded from a hard
|
|
9
|
+
// failure to a warning while preserving the source override.
|
|
10
|
+
function isTruthy(value) {
|
|
11
|
+
return ['1', 'true', 'yes', 'on'].includes(String(value || '').trim().toLowerCase());
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function allowFixedBrand(options = {}, env = process.env) {
|
|
15
|
+
return options.allowFixedBrand === true || isTruthy(env.OPENYIDA_CANVAS_ALLOW_FIXED_BRAND);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Whether fixed brand overrides should hard-block publishing. Off by default so
|
|
19
|
+
// the guard warns and retains fixed overrides. Teams
|
|
20
|
+
// that want CI enforcement opt in via --strict-theme / OPENYIDA_CANVAS_STRICT_THEME.
|
|
21
|
+
function strictFixedBrand(options = {}, env = process.env) {
|
|
22
|
+
return options.strictBrand === true || isTruthy(env.OPENYIDA_CANVAS_STRICT_THEME);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function emitThemeWarning(options, message, details) {
|
|
26
|
+
if (typeof options.onThemeWarning === 'function') { options.onThemeWarning(message, details); return; }
|
|
27
|
+
warn(message);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
|
|
7
31
|
// Check the maintained theme contract without executing business code.
|
|
8
32
|
// Dynamic component factories and runtime branches still need browser verification.
|
|
9
33
|
function assertCanvasThemeStructure(source, options = {}) {
|
|
@@ -23,6 +47,10 @@ function assertCanvasThemeStructure(source, options = {}) {
|
|
|
23
47
|
const traverse = Babel.packages.traverse.default || Babel.packages.traverse;
|
|
24
48
|
assertBrandOverrides(ast, traverse, options);
|
|
25
49
|
const functions = new Map();
|
|
50
|
+
const migrationSites = [];
|
|
51
|
+
const migrationEdges = [];
|
|
52
|
+
const migrationUses = options.requireThemeProvider
|
|
53
|
+
? new Set(collectBrandOverrides(ast, traverse).map(item => item.usageStart)) : null;
|
|
26
54
|
let program;
|
|
27
55
|
let defaultExport;
|
|
28
56
|
const fail = (kind, node) => {
|
|
@@ -64,6 +92,17 @@ function assertCanvasThemeStructure(source, options = {}) {
|
|
|
64
92
|
return null;
|
|
65
93
|
}
|
|
66
94
|
|
|
95
|
+
function insideProvider(p) {
|
|
96
|
+
let parent = p.isJSXOpeningElement() ? p.parentPath.parentPath : p.parentPath;
|
|
97
|
+
for (; parent && !parent.isFunction(); parent = parent.parentPath) {
|
|
98
|
+
if (parent.isJSXElement()) {
|
|
99
|
+
const component = resolveFunction(parent.get('openingElement.name'));
|
|
100
|
+
if (functions.get(component)?.name === 'CanvasThemeProvider') { return true; }
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
|
|
67
106
|
traverse(ast, {
|
|
68
107
|
CallExpression(p) {
|
|
69
108
|
const owner = functions.get(p.getFunctionParent()?.node);
|
|
@@ -75,6 +114,9 @@ function assertCanvasThemeStructure(source, options = {}) {
|
|
|
75
114
|
}
|
|
76
115
|
if (!owner) { return; }
|
|
77
116
|
if (target) { owner.calls.push(target); }
|
|
117
|
+
if (migrationUses && target) {
|
|
118
|
+
migrationEdges.push({ from: p.getFunctionParent().node, to: target, covered: insideProvider(p) });
|
|
119
|
+
}
|
|
78
120
|
const callee = p.node.callee;
|
|
79
121
|
if (callee.type === 'MemberExpression' && callee.object.name === 'React' && callee.property.name === 'createElement') {
|
|
80
122
|
const component = resolveFunction(p.get('arguments.0'));
|
|
@@ -85,11 +127,19 @@ function assertCanvasThemeStructure(source, options = {}) {
|
|
|
85
127
|
const owner = functions.get(p.getFunctionParent()?.node);
|
|
86
128
|
const component = resolveFunction(p.get('name'));
|
|
87
129
|
if (owner && component) { owner.renders.push(component); }
|
|
130
|
+
if (migrationUses) {
|
|
131
|
+
const from = p.getFunctionParent()?.node;
|
|
132
|
+
if (component && from) { migrationEdges.push({ from, to: component, covered: insideProvider(p) }); }
|
|
133
|
+
if (migrationUses.has(p.node.start)) { migrationSites.push({ owner: from, covered: insideProvider(p), node: p.node }); }
|
|
134
|
+
}
|
|
88
135
|
},
|
|
89
136
|
});
|
|
90
137
|
|
|
91
138
|
const entry = resolveFunction(program.scope.getBinding('YidaComp')?.path) || resolveFunction(defaultExport);
|
|
92
|
-
if (!entry) {
|
|
139
|
+
if (!entry) {
|
|
140
|
+
if (options.requireThemeProvider) { fail('provider_missing', defaultExport?.node); }
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
93
143
|
|
|
94
144
|
// Calling a hook before returning a Provider cannot read that Provider.
|
|
95
145
|
const directSeen = new Set();
|
|
@@ -118,14 +168,36 @@ function assertCanvasThemeStructure(source, options = {}) {
|
|
|
118
168
|
}
|
|
119
169
|
visit(entry);
|
|
120
170
|
const hasProvider = [...functions.values()].some(info => info.name === 'CanvasThemeProvider');
|
|
121
|
-
if (!providerMounted && (hasProvider || firstHook)) {
|
|
171
|
+
if (!providerMounted && (options.requireThemeProvider || hasProvider || firstHook)) {
|
|
122
172
|
fail('provider_missing', firstHook || entry);
|
|
123
173
|
}
|
|
174
|
+
if (migrationUses) {
|
|
175
|
+
// A sibling provider does not supply context to the migrated controls.
|
|
176
|
+
// Unknown/dynamic render paths are left for manual migration.
|
|
177
|
+
const checked = new Map();
|
|
178
|
+
const checkScope = (node, covered) => {
|
|
179
|
+
const states = checked.get(node) || new Set();
|
|
180
|
+
if (states.has(covered)) { return; }
|
|
181
|
+
states.add(covered); checked.set(node, states);
|
|
182
|
+
for (const site of migrationSites.filter(site => site.owner === node)) {
|
|
183
|
+
if (!covered && !site.covered) { fail('provider_missing', site.node); }
|
|
184
|
+
}
|
|
185
|
+
migrationEdges.filter(edge => edge.from === node).forEach(edge => checkScope(edge.to, covered || edge.covered));
|
|
186
|
+
};
|
|
187
|
+
checkScope(entry, false);
|
|
188
|
+
for (const site of migrationSites) {
|
|
189
|
+
if (!checked.has(site.owner)) { fail('provider_missing', site.node); }
|
|
190
|
+
}
|
|
191
|
+
}
|
|
124
192
|
}
|
|
125
193
|
|
|
126
|
-
//
|
|
127
|
-
// Semantic colors, layout tokens and dynamic theme resolvers
|
|
128
|
-
|
|
194
|
+
// Collect every provably fixed brand color passed to antd's ConfigProvider.
|
|
195
|
+
// Semantic colors, layout tokens and dynamic theme resolvers are ignored.
|
|
196
|
+
// Each entry keeps the offending property's char offsets so the theme codemod
|
|
197
|
+
// can surgically remove it while the guard reuses the same detection to fail.
|
|
198
|
+
function collectBrandOverrides(ast, traverse) {
|
|
199
|
+
const violations = [];
|
|
200
|
+
let usageStart;
|
|
129
201
|
const resolve = (p, seen = new Set()) => {
|
|
130
202
|
if (!p?.node || seen.has(p.node)) { return null; }
|
|
131
203
|
seen.add(p.node);
|
|
@@ -147,10 +219,12 @@ function assertBrandOverrides(ast, traverse, options) {
|
|
|
147
219
|
const color = val.isStringLiteral() ? val.node.value : val.node.quasis[0].value.cooked;
|
|
148
220
|
if (!color) { return; }
|
|
149
221
|
const line = val.node.loc?.start.line || 1;
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
222
|
+
// Remove the override at its ConfigProvider usage site, not the resolved literal.
|
|
223
|
+
const property = p?.parentPath?.isObjectProperty() ? p.parentPath.node : null;
|
|
224
|
+
violations.push({
|
|
225
|
+
field, color, line, usageStart,
|
|
226
|
+
start: property ? property.start : val.node.start,
|
|
227
|
+
end: property ? property.end : val.node.end,
|
|
154
228
|
});
|
|
155
229
|
};
|
|
156
230
|
const tokens = p => properties(p).forEach(item => {
|
|
@@ -167,6 +241,7 @@ function assertBrandOverrides(ast, traverse, options) {
|
|
|
167
241
|
if (!(imported.isImportSpecifier() && imported.node.imported.name === 'ConfigProvider')
|
|
168
242
|
&& !(imported.isImportNamespaceSpecifier() && name.node.property?.name === 'ConfigProvider')) { return; }
|
|
169
243
|
const attribute = p.get('attributes').find(attr => attr.isJSXAttribute() && attr.node.name.name === 'theme');
|
|
244
|
+
usageStart = p.node.start;
|
|
170
245
|
const theme = attribute?.get('value.expression');
|
|
171
246
|
tokens(prop(theme, 'token'));
|
|
172
247
|
const components = prop(theme, 'components');
|
|
@@ -179,6 +254,48 @@ function assertBrandOverrides(ast, traverse, options) {
|
|
|
179
254
|
}
|
|
180
255
|
},
|
|
181
256
|
});
|
|
257
|
+
return violations;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// Fixed brand colors passed to antd's ConfigProvider are relaxed by default:
|
|
261
|
+
// Warn and preserve the literal override; this does not install an app theme.
|
|
262
|
+
// Strict mode blocks these overrides; allow-fixed-brand explicitly preserves them.
|
|
263
|
+
function assertBrandOverrides(ast, traverse, options) {
|
|
264
|
+
const explicitlyAllowed = allowFixedBrand(options);
|
|
265
|
+
const strict = strictFixedBrand(options);
|
|
266
|
+
for (const { field, color, line } of collectBrandOverrides(ast, traverse)) {
|
|
267
|
+
const details = { code: 'OPENYIDA_CANVAS_THEME_FIXED_BRAND', sourcePath: options.sourcePath || '',
|
|
268
|
+
line, field, value: color, themeConsistency: 'fixed_override',
|
|
269
|
+
nextAction: { type: 'review_theme_override', sample: 'openyida sample openyida-page-template canvas-theme' } };
|
|
270
|
+
if (explicitlyAllowed) {
|
|
271
|
+
emitThemeWarning(options, t('publish.canvas_theme_fixed_brand_allowed', line, field), details);
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
if (!strict) {
|
|
275
|
+
emitThemeWarning(options, t('publish.canvas_theme_fixed_brand_relaxed', line, field), details);
|
|
276
|
+
continue;
|
|
277
|
+
}
|
|
278
|
+
throw new CliError(t('publish.canvas_theme_fixed_brand', line), {
|
|
279
|
+
code: 'OPENYIDA_CANVAS_THEME_FIXED_BRAND',
|
|
280
|
+
details: { stage: 'canvas_compile', sourcePath: options.sourcePath || '', line, field, value: color,
|
|
281
|
+
retryable: false, retrySafe: true, sideEffectState: 'none',
|
|
282
|
+
nextAction: { type: 'edit_source_then_recheck' },
|
|
283
|
+
sample: 'openyida sample openyida-page-template canvas-theme' },
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// Parse a canvas source and return removable fixed-brand overrides (with char
|
|
289
|
+
// offsets). Returns [] when the source has no theme markers or fails to parse,
|
|
290
|
+
// mirroring the guard's own gate so the codemod never acts on unrelated code.
|
|
291
|
+
function collectFixedBrandViolations(source) {
|
|
292
|
+
if (!/CanvasThemeProvider|useCanvasThemeContext|CanvasThemeContext|@canvas-theme-provider|antd/.test(source)) { return []; }
|
|
293
|
+
let ast;
|
|
294
|
+
try {
|
|
295
|
+
ast = Babel.packages.parser.parse(source, { sourceType: 'module', plugins: ['jsx', 'typescript'] });
|
|
296
|
+
} catch { return []; }
|
|
297
|
+
const traverse = Babel.packages.traverse.default || Babel.packages.traverse;
|
|
298
|
+
return collectBrandOverrides(ast, traverse);
|
|
182
299
|
}
|
|
183
300
|
|
|
184
|
-
module.exports = { assertCanvasThemeStructure };
|
|
301
|
+
module.exports = { assertCanvasThemeStructure, collectFixedBrandViolations };
|