openyida 2026.10.8 → 2026.10.9-beta.2
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 +32 -9
- package/bin/yida.js +33 -11
- package/lib/agent/PROTOCOL.md +182 -0
- package/lib/agent/ask-human.js +151 -0
- package/lib/agent/cmd.js +405 -0
- package/lib/agent/command-receipt.js +84 -0
- package/lib/agent/connection-store.js +148 -0
- package/lib/agent/diagnostic-log.js +133 -0
- package/lib/agent/frozen-bundle.js +286 -0
- package/lib/agent/managed-command-map.js +36 -0
- package/lib/agent/managed-context.js +40 -0
- package/lib/agent/managed-run.js +85 -0
- package/lib/agent/mutation-receipt.js +82 -0
- package/lib/agent/private-directory.js +63 -0
- package/lib/agent/provider-preflight.js +257 -0
- package/lib/agent/providers.js +4 -0
- package/lib/agent/runtime-catalog.json +42 -0
- package/lib/agent/runtime-manifest.js +96 -0
- package/lib/agent/runtime-package.js +155 -0
- package/lib/agent/stdio.js +321 -0
- package/lib/app/app-list.js +0 -1
- package/lib/app/application-entry-urls.js +1 -27
- package/lib/app/canvas-compile.js +1 -7
- package/lib/app/canvas-theme-guard.js +10 -127
- package/lib/app/check-prd-completeness.js +15 -4
- package/lib/app/compile.js +2 -12
- package/lib/app/create-form/args.js +12 -37
- package/lib/app/create-form/batch.js +35 -41
- package/lib/app/create-form.js +57 -94
- package/lib/app/create-page.js +9 -5
- package/lib/app/custom-theme.js +37 -30
- package/lib/app/form-field-validator.js +36 -0
- package/lib/app/get-form-config.js +0 -6
- package/lib/app/get-schema.js +6 -1
- package/lib/app/publish.js +8 -69
- package/lib/app/services/canvas-page-compiler.js +1 -3
- package/lib/app/services/form-compiler.js +2 -0
- package/lib/app/theme-from-design.js +87 -77
- package/lib/app/update-app.js +26 -64
- package/lib/app/update-form-config.js +19 -9
- package/lib/auth/profile-lock.js +89 -0
- package/lib/auth/token-auth.js +57 -6
- package/lib/auth/token-store.js +26 -2
- package/lib/core/agent-capabilities.js +0 -1
- package/lib/core/cli-error.js +0 -1
- package/lib/core/command-manifest.js +231 -279
- package/lib/core/locales/en.js +52 -47
- package/lib/core/locales/zh.js +52 -47
- package/lib/core/managed-policy.js +81 -0
- package/lib/core/sample.js +14 -36
- package/lib/core/update.js +3 -1
- package/lib/core/utils.js +29 -0
- package/lib/design/check-design.js +6 -16
- package/lib/design/document.js +15 -37
- package/lib/design/icon-contrast.js +1 -1
- package/lib/design-plan/design-plan.js +8 -65
- package/lib/design-plan/entry-navigation.js +2 -19
- package/lib/design-plan/init.js +11 -19
- package/lib/design-plan/materialize.js +61 -93
- package/lib/design-plan/normalize.js +14 -13
- package/lib/design-plan/parallel.js +13 -62
- package/lib/design-plan/patch.js +7 -21
- package/lib/design-plan/preview.js +1 -4
- package/lib/design-plan/themes.js +1 -16
- package/lib/design-plan/validate.js +3 -18
- package/lib/design-plan/visual-policy.js +4 -29
- package/lib/page-config/save-share-config.js +16 -5
- package/lib/samples/openyida-scaffold/canvas-form-drawer.canvas.jsx +3 -3
- package/lib/samples/openyida-scaffold/canvas-nav/mixed.jsx +4 -6
- package/lib/samples/openyida-scaffold/canvas-nav/shared.jsx +5 -5
- package/lib/samples/openyida-scaffold/canvas-nav/side.jsx +2 -1
- package/lib/samples/openyida-scaffold/canvas-nav/sidebar.jsx +1 -1
- package/lib/samples/openyida-scaffold/canvas-nav/top.jsx +4 -5
- package/package.json +11 -3
- package/yida-skills/SKILL.md +25 -3
- package/yida-skills/references/local-agent-diagnostics.md +154 -0
- package/yida-skills/references/local-agent-preflight.md +56 -0
- package/yida-skills/references/official-example-schema-patterns.md +1 -1
- package/yida-skills/references/yida-api.md +0 -4
- package/yida-skills/skills/yida-app/SKILL.md +5 -16
- package/yida-skills/skills/yida-app/references/entry-navigation.md +9 -13
- package/yida-skills/skills/yida-app/workflow/plan/step-4-deliver.md +1 -3
- package/yida-skills/skills/yida-app/workflow/plan/workflow.md +5 -21
- 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-6-main-page.md +1 -1
- package/yida-skills/skills/yida-app/workflow/step-7-page-code.md +1 -1
- package/yida-skills/skills/yida-app/workflow/step-8-publish-navigation.md +5 -5
- package/yida-skills/skills/yida-app/workflow/step-9-output-finish.md +4 -27
- package/yida-skills/skills/yida-canvas-custom-page/SKILL.md +2 -3
- package/yida-skills/skills/yida-canvas-custom-page/references/canvas-style-implementation-guide.md +5 -6
- package/yida-skills/skills/yida-canvas-custom-page/references/canvas-theme-provider.md +2 -4
- package/yida-skills/skills/yida-canvas-custom-page/references/data-bridge-guide.md +1 -140
- 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/page-generation-guide.md +6 -6
- package/yida-skills/skills/yida-canvas-custom-page/scripts/build-canvas-theme.js +1 -2
- package/yida-skills/skills/yida-canvas-custom-page/scripts/canvas-theme-provider.template.jsx +1 -8
- package/yida-skills/skills/yida-canvas-data-binding/SKILL.md +0 -13
- package/yida-skills/skills/yida-create-form-page/SKILL.md +43 -36
- 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 +16 -20
- package/yida-skills/skills/yida-create-form-page/references/form-field-properties.md +42 -21
- package/yida-skills/skills/yida-create-page/SKILL.md +1 -1
- package/yida-skills/skills/yida-dashboard/SKILL.md +1 -1
- package/yida-skills/skills/yida-design/SKILL.md +13 -25
- package/yida-skills/skills/yida-design/references/application-theme-consistency.md +9 -96
- package/yida-skills/skills/yida-design/references/ask-human-interaction-contract.md +1 -1
- package/yida-skills/skills/yida-design/references/navigation-decision.md +6 -10
- package/yida-skills/skills/yida-design/references/page-quality-gates.md +4 -10
- package/yida-skills/skills/yida-design/references/theme/app-custom-theme-template.css +235 -596
- package/yida-skills/skills/yida-design/references/theme/theme-token-presets.md +1 -3
- package/yida-skills/skills/yida-design/references/theme-selection.md +11 -24
- package/yida-skills/skills/yida-design/references/visual-decision-engine.md +1 -1
- package/yida-skills/skills/yida-design/references/visual-scaffold-recipes.md +40 -13
- package/yida-skills/skills/yida-design/scripts/validate_design_themes.py +7 -52
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/SKILL.md +4 -16
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/references/build-plan-compact-schema.md +2 -6
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/references/build-plan-schema.md +3 -5
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/references/page-patterns.md +4 -6
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/references/visual-design.md +1 -3
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/references/visual-theme-selection.md +27 -26
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/scripts/render_build_plan.py +0 -2
- package/yida-skills/skills/yida-design/templates/design-themes/README.md +10 -20
- package/yida-skills/skills/yida-design/templates/design-themes/basic-tokens.json +7 -2
- package/yida-skills/skills/yida-design/templates/design-themes/{dark-inset-hairline/design.md → dark-inset-hairline.md} +59 -100
- package/yida-skills/skills/yida-design/templates/design-themes/{dark-rail-fine-lines/design.md → dark-rail-fine-lines.md} +53 -102
- package/yida-skills/skills/yida-design/templates/design-themes/{dark-rail-signal-panels/design.md → dark-rail-signal-panels.md} +13 -62
- package/yida-skills/skills/yida-design/templates/design-themes/{graphite-bevel-grid/design.md → graphite-bevel-grid.md} +11 -58
- package/yida-skills/skills/yida-design/templates/design-themes/{hairline-shared-bands/design.md → hairline-shared-bands.md} +13 -60
- package/yida-skills/skills/yida-design/templates/design-themes/{hairline-soft-blocks/design.md → hairline-soft-blocks.md} +13 -57
- package/yida-skills/skills/yida-design/templates/design-themes/index.json +35 -390
- package/yida-skills/skills/yida-design/templates/design-themes/{ink-glow-soft-panels/design.md → ink-glow-soft-panels.md} +9 -55
- package/yida-skills/skills/yida-design/templates/design-themes/{inset-frame-pixel-rhythm/design.md → inset-frame-pixel-rhythm.md} +10 -57
- package/yida-skills/skills/yida-design/templates/design-themes/{outlined-texture-duotone/design.md → outlined-texture-duotone.md} +13 -59
- package/yida-skills/skills/yida-design/templates/design-themes/{soft-inset-surfaces/design.md → soft-inset-surfaces.md} +12 -56
- package/yida-skills/skills/yida-design/templates/design-themes/{soft-outline-rhythm/design.md → soft-outline-rhythm.md} +26 -56
- package/yida-skills/skills/yida-design/templates/design-themes/{soft-rail-bold-band/design.md → soft-rail-bold-band.md} +55 -101
- package/yida-skills/skills/yida-design/templates/design-themes/soft-spectral-panels.md +337 -0
- package/yida-skills/skills/yida-design/templates/design-themes/{warm-canvas-contrast-panels/design.md → warm-canvas-contrast-panels.md} +14 -60
- package/yida-skills/skills/yida-design/templates/design-themes/{warm-rail-muted-panels/design.md → warm-rail-muted-panels.md} +54 -98
- package/yida-skills/skills/yida-design/workflow/output-design.md +8 -46
- package/yida-skills/skills/yida-design/workflow/step-2-theme-system.md +6 -21
- package/yida-skills/skills/yida-design/workflow/step-4-wireframe-interaction.md +5 -7
- package/yida-skills/skills/yida-design/workflow/step-5-visual-states.md +0 -1
- package/yida-skills/skills/yida-design/workflow/step-6-handoff.md +1 -3
- package/yida-skills/skills/yida-document-markdown/SKILL.md +6 -0
- package/yida-skills/skills/yida-export-conversation/SKILL.md +1 -0
- package/yida-skills/skills/yida-form-permission/SKILL.md +0 -6
- package/yida-skills/skills/yida-image-assets/SKILL.md +1 -3
- package/yida-skills/skills/yida-image-assets/references/manifest-contract.md +0 -2
- package/yida-skills/skills/yida-nav-shell/SKILL.md +3 -5
- package/yida-skills/skills/yida-nav-shell/references/nav-shell-patterns.md +0 -6
- package/yida-skills/skills/yida-page-config/SKILL.md +2 -11
- package/yida-skills/skills/yida-prd/SKILL.md +1 -4
- package/yida-skills/skills/yida-prd/references/app/role-journey.md +1 -1
- package/yida-skills/skills/yida-prd/references/plan-content.md +1 -1
- package/yida-skills/skills/yida-prd/workflow/output-prd.md +7 -7
- package/yida-skills/skills/yida-prd/workflow/plan-business.md +0 -2
- package/yida-skills/skills/yida-prd/workflow/step-2-information-architecture.md +4 -13
- package/yida-skills/skills/yida-publish-page/SKILL.md +18 -29
- package/yida-skills/skills/yida-requirement-analysis/references/experience-groups.md +12 -48
- package/yida-skills/skills/yida-requirement-analysis/references/handoff.md +6 -6
- package/yida-skills/skills/yida-requirement-analysis/workflow/prepare-brief.md +4 -17
- package/yida-skills/skills/yida-tingji/SKILL.md +4 -0
- package/yida-skills/skills-index.json +4 -4
- package/lib/app/app-entry.js +0 -144
- package/lib/app/application-style.js +0 -67
- package/lib/app/fix-theme.js +0 -34
- package/lib/app/theme-scope.js +0 -76
- package/lib/core/source-repair.js +0 -38
- package/lib/core/theme-brand-scale.js +0 -133
- package/lib/design/palette-contrast.js +0 -77
- package/lib/design-plan/confirmation.js +0 -13
- package/lib/design-plan/rebase.js +0 -69
- package/yida-skills/references/source-repair.md +0 -10
- package/yida-skills/skills/yida-canvas-custom-page/references/waf-safe-authoring.md +0 -45
- package/yida-skills/skills/yida-design/references/application-style-library.md +0 -93
- package/yida-skills/skills/yida-design/references/native-form-styles.md +0 -266
- package/yida-skills/skills/yida-design/references/task-driven-visual-design.md +0 -59
- package/yida-skills/skills/yida-design/references/theme/application-style-recipes.css +0 -58
- package/yida-skills/skills/yida-design/templates/design-themes/app-amber/app_theme.css +0 -809
- package/yida-skills/skills/yida-design/templates/design-themes/app-amber/design.md +0 -248
- package/yida-skills/skills/yida-design/templates/design-themes/app-amber/form-layout.json +0 -15
- package/yida-skills/skills/yida-design/templates/design-themes/app-editorial/app_theme.css +0 -809
- package/yida-skills/skills/yida-design/templates/design-themes/app-editorial/design.md +0 -241
- package/yida-skills/skills/yida-design/templates/design-themes/app-editorial/form-layout.json +0 -14
- package/yida-skills/skills/yida-design/templates/design-themes/app-executive/app_theme.css +0 -809
- package/yida-skills/skills/yida-design/templates/design-themes/app-executive/design.md +0 -241
- package/yida-skills/skills/yida-design/templates/design-themes/app-executive/form-layout.json +0 -13
- package/yida-skills/skills/yida-design/templates/design-themes/app-finance/app_theme.css +0 -809
- package/yida-skills/skills/yida-design/templates/design-themes/app-finance/design.md +0 -240
- package/yida-skills/skills/yida-design/templates/design-themes/app-finance/form-layout.json +0 -14
- package/yida-skills/skills/yida-design/templates/design-themes/app-glass/app_theme.css +0 -809
- package/yida-skills/skills/yida-design/templates/design-themes/app-glass/design.md +0 -241
- package/yida-skills/skills/yida-design/templates/design-themes/app-glass/form-layout.json +0 -14
- package/yida-skills/skills/yida-design/templates/design-themes/app-graphite/app_theme.css +0 -809
- package/yida-skills/skills/yida-design/templates/design-themes/app-graphite/design.md +0 -241
- package/yida-skills/skills/yida-design/templates/design-themes/app-graphite/form-layout.json +0 -14
- package/yida-skills/skills/yida-design/templates/design-themes/app-line/app_theme.css +0 -809
- package/yida-skills/skills/yida-design/templates/design-themes/app-line/design.md +0 -241
- package/yida-skills/skills/yida-design/templates/design-themes/app-line/form-layout.json +0 -13
- package/yida-skills/skills/yida-design/templates/design-themes/app-neon/app_theme.css +0 -809
- package/yida-skills/skills/yida-design/templates/design-themes/app-neon/design.md +0 -239
- package/yida-skills/skills/yida-design/templates/design-themes/app-neon/form-layout.json +0 -14
- package/yida-skills/skills/yida-design/templates/design-themes/app-nordic/app_theme.css +0 -809
- package/yida-skills/skills/yida-design/templates/design-themes/app-nordic/design.md +0 -241
- package/yida-skills/skills/yida-design/templates/design-themes/app-nordic/form-layout.json +0 -13
- package/yida-skills/skills/yida-design/templates/design-themes/app-paper/app_theme.css +0 -809
- package/yida-skills/skills/yida-design/templates/design-themes/app-paper/design.md +0 -239
- package/yida-skills/skills/yida-design/templates/design-themes/app-paper/form-layout.json +0 -13
- package/yida-skills/skills/yida-design/templates/design-themes/app-platinum/app_theme.css +0 -809
- package/yida-skills/skills/yida-design/templates/design-themes/app-platinum/design.md +0 -241
- package/yida-skills/skills/yida-design/templates/design-themes/app-platinum/form-layout.json +0 -14
- package/yida-skills/skills/yida-design/templates/design-themes/app-plum/app_theme.css +0 -809
- package/yida-skills/skills/yida-design/templates/design-themes/app-plum/design.md +0 -241
- package/yida-skills/skills/yida-design/templates/design-themes/app-plum/form-layout.json +0 -14
- package/yida-skills/skills/yida-design/templates/design-themes/app-pop/app_theme.css +0 -809
- package/yida-skills/skills/yida-design/templates/design-themes/app-pop/design.md +0 -241
- package/yida-skills/skills/yida-design/templates/design-themes/app-pop/form-layout.json +0 -14
- package/yida-skills/skills/yida-design/templates/design-themes/app-sage/app_theme.css +0 -809
- package/yida-skills/skills/yida-design/templates/design-themes/app-sage/design.md +0 -241
- package/yida-skills/skills/yida-design/templates/design-themes/app-sage/form-layout.json +0 -14
- package/yida-skills/skills/yida-design/templates/design-themes/app-teal/app_theme.css +0 -809
- package/yida-skills/skills/yida-design/templates/design-themes/app-teal/design.md +0 -241
- package/yida-skills/skills/yida-design/templates/design-themes/app-teal/form-layout.json +0 -14
- package/yida-skills/skills/yida-design/templates/design-themes/app-terminal/app_theme.css +0 -809
- package/yida-skills/skills/yida-design/templates/design-themes/app-terminal/design.md +0 -240
- package/yida-skills/skills/yida-design/templates/design-themes/app-terminal/form-layout.json +0 -14
- package/yida-skills/skills/yida-design/templates/design-themes/app-ticket/app_theme.css +0 -809
- package/yida-skills/skills/yida-design/templates/design-themes/app-ticket/design.md +0 -243
- package/yida-skills/skills/yida-design/templates/design-themes/app-ticket/form-layout.json +0 -13
- package/yida-skills/skills/yida-design/templates/design-themes/app-wire/app_theme.css +0 -809
- package/yida-skills/skills/yida-design/templates/design-themes/app-wire/design.md +0 -240
- package/yida-skills/skills/yida-design/templates/design-themes/app-wire/form-layout.json +0 -14
- package/yida-skills/skills/yida-design/templates/design-themes/dark-inset-hairline/app_theme.css +0 -748
- package/yida-skills/skills/yida-design/templates/design-themes/dark-inset-hairline/form-layout.json +0 -13
- package/yida-skills/skills/yida-design/templates/design-themes/dark-rail-fine-lines/app_theme.css +0 -756
- package/yida-skills/skills/yida-design/templates/design-themes/dark-rail-fine-lines/form-layout.json +0 -13
- package/yida-skills/skills/yida-design/templates/design-themes/dark-rail-signal-panels/app_theme.css +0 -754
- package/yida-skills/skills/yida-design/templates/design-themes/dark-rail-signal-panels/form-layout.json +0 -13
- package/yida-skills/skills/yida-design/templates/design-themes/free-creative/app_theme.css +0 -750
- package/yida-skills/skills/yida-design/templates/design-themes/free-creative/design.md +0 -204
- package/yida-skills/skills/yida-design/templates/design-themes/free-creative/form-layout.json +0 -13
- package/yida-skills/skills/yida-design/templates/design-themes/graphite-bevel-grid/app_theme.css +0 -750
- package/yida-skills/skills/yida-design/templates/design-themes/graphite-bevel-grid/form-layout.json +0 -13
- package/yida-skills/skills/yida-design/templates/design-themes/hairline-shared-bands/app_theme.css +0 -749
- package/yida-skills/skills/yida-design/templates/design-themes/hairline-shared-bands/form-layout.json +0 -13
- package/yida-skills/skills/yida-design/templates/design-themes/hairline-soft-blocks/app_theme.css +0 -749
- package/yida-skills/skills/yida-design/templates/design-themes/hairline-soft-blocks/form-layout.json +0 -13
- package/yida-skills/skills/yida-design/templates/design-themes/ink-glow-soft-panels/app_theme.css +0 -759
- package/yida-skills/skills/yida-design/templates/design-themes/ink-glow-soft-panels/form-layout.json +0 -13
- package/yida-skills/skills/yida-design/templates/design-themes/inset-frame-pixel-rhythm/app_theme.css +0 -749
- package/yida-skills/skills/yida-design/templates/design-themes/inset-frame-pixel-rhythm/form-layout.json +0 -13
- package/yida-skills/skills/yida-design/templates/design-themes/outlined-texture-duotone/app_theme.css +0 -756
- package/yida-skills/skills/yida-design/templates/design-themes/outlined-texture-duotone/form-layout.json +0 -13
- package/yida-skills/skills/yida-design/templates/design-themes/soft-inset-surfaces/app_theme.css +0 -749
- package/yida-skills/skills/yida-design/templates/design-themes/soft-inset-surfaces/form-layout.json +0 -13
- package/yida-skills/skills/yida-design/templates/design-themes/soft-outline-rhythm/app_theme.css +0 -748
- package/yida-skills/skills/yida-design/templates/design-themes/soft-outline-rhythm/form-layout.json +0 -13
- package/yida-skills/skills/yida-design/templates/design-themes/soft-rail-bold-band/app_theme.css +0 -756
- package/yida-skills/skills/yida-design/templates/design-themes/soft-rail-bold-band/form-layout.json +0 -13
- package/yida-skills/skills/yida-design/templates/design-themes/warm-canvas-contrast-panels/app_theme.css +0 -769
- package/yida-skills/skills/yida-design/templates/design-themes/warm-canvas-contrast-panels/form-layout.json +0 -13
- package/yida-skills/skills/yida-design/templates/design-themes/warm-rail-muted-panels/app_theme.css +0 -750
- package/yida-skills/skills/yida-design/templates/design-themes/warm-rail-muted-panels/form-layout.json +0 -13
package/README.md
CHANGED
|
@@ -118,7 +118,6 @@ 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 |
|
|
122
121
|
| [Cursor](https://cursor.com/) | Full support |
|
|
123
122
|
| [Visual Studio Code](https://code.visualstudio.com/) | Full support |
|
|
124
123
|
| QwenWork(千问办公) | Full support |
|
|
@@ -183,7 +182,7 @@ openyida get-schema APP_XXX FORM_XXX --compact --resolve-fields "Customer Name,S
|
|
|
183
182
|
openyida get-schema APP_XXX --all --output-dir .cache/schemas
|
|
184
183
|
```
|
|
185
184
|
|
|
186
|
-
|
|
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.
|
|
187
186
|
|
|
188
187
|
### Custom Page Development
|
|
189
188
|
|
|
@@ -393,15 +392,13 @@ Run `openyida --help` or `openyida <command> --help` for detailed usage.
|
|
|
393
392
|
|
|
394
393
|
| Command | Description |
|
|
395
394
|
|---------|-------------|
|
|
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 |
|
|
398
395
|
| `openyida app-list [--type managed\|created] [--page N] [--size N]` | Page through apps I manage or created |
|
|
399
396
|
| `openyida corp-efficiency [overview\|details\|detail\|groups\|notify] [options] [--open\|--no-open]` | Query enterprise efficiency overview and detail reports |
|
|
400
397
|
| `openyida create-app "<name>"\|--name <name> [options] [--locale zh_CN\|en_US\|ja_JP] [--open\|--no-open]` | Create a Yida app |
|
|
401
398
|
| `openyida design-plan catalog [--json]` | List available planning themes and page patterns |
|
|
402
399
|
| `openyida design-plan init <requirement-brief.json> [--theme-id <id>] [--output-dir <dir>] [--json]` | Initialize a plan draft from confirmed requirements |
|
|
403
400
|
| `openyida design-plan preview <build-plan.json> --part-file <module.json> [--json]` | Update plan drafts by module |
|
|
404
|
-
| `openyida design-plan materialize <build-plan.json> [--from-preview \| --business-file <json> --visual-file <json>] [--output-dir <dir>] [--
|
|
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 |
|
|
405
402
|
| `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 |
|
|
406
403
|
| `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 |
|
|
407
404
|
| `openyida app-online <appType> [--to-ding-app-center] [--show-app-center]` | Enable a Yida app |
|
|
@@ -417,7 +414,7 @@ Run `openyida --help` or `openyida <command> --help` for detailed usage.
|
|
|
417
414
|
| Command | Description |
|
|
418
415
|
|---------|-------------|
|
|
419
416
|
| `openyida create-form batch <appType> <plan.json> [--concurrency 1..4] [--check] [--json]` | Create forms concurrently by dependency |
|
|
420
|
-
| `openyida create-form create <appType> "<formTitle>" <fieldsJsonFile> [--
|
|
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 |
|
|
421
418
|
| `openyida create-form icons [--json]` | List available form navigation icons |
|
|
422
419
|
| `openyida create-form validate-fields <fieldsJsonOrFile> [--json]` | Validate form field JSON locally |
|
|
423
420
|
| `openyida create-form update <appType> <formUuid> (<changesJsonOrFile> \| --data-file <changesJsonOrFile>) [--locale zh_CN\|en_US\|ja_JP] [--open\|--no-open]` | Update a form page |
|
|
@@ -437,8 +434,8 @@ Run `openyida --help` or `openyida <command> --help` for detailed usage.
|
|
|
437
434
|
| `openyida create-page <appType> "<name>" [--mode dashboard] [--hide-nav] [--locale zh_CN\|en_US\|ja_JP] [--open\|--no-open]` | Create a custom display page |
|
|
438
435
|
| `openyida build-page <sourceFile> [--output file\|--write]` | Build Yida-compatible page source |
|
|
439
436
|
| `openyida check-page <src> [--compat] [--json]` | Check custom page standards |
|
|
440
|
-
| `openyida compile <src> [--canvas] [--compat] [--skip-lint] [--
|
|
441
|
-
| `openyida publish <src> <appType> <formUuid> [--health-check] [--force] [--canvas] [--compat] [--skip-lint] [--
|
|
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 |
|
|
442
439
|
| `openyida update-form-config <appType> <formUuid> <true\|false\|keep> "<title>" [--locale zh_CN\|en_US\|ja_JP]` | Update form configuration |
|
|
443
440
|
| `openyida get-form-config <appType> <formUuid> [--json]` | Query form configuration |
|
|
444
441
|
|
|
@@ -522,10 +519,11 @@ Run `openyida --help` or `openyida <command> --help` for detailed usage.
|
|
|
522
519
|
|---------|-------------|
|
|
523
520
|
| `openyida commands [--json]` | Output machine-readable command manifest |
|
|
524
521
|
| `openyida agent-capabilities [--json] [--summary-json\|--compact]` | Output one-shot agent capability snapshot |
|
|
522
|
+
| `openyida agent <doctor\|diagnose\|status\|connect\|run\|disconnect\|logout\|logs> [options]` | Connect a local Agent (Go runtime, development preview) |
|
|
525
523
|
| `openyida a2a <serve\|agent-card> [options]` | Start local read-only A2A adapter or print Agent Card |
|
|
526
524
|
| `openyida bridge start [--token <pair-token>] [--port 6736] [--origin https://demo.aliwork.com] [--open\|--no-open]` | Start OpenYida local web bridge service |
|
|
527
525
|
| `openyida copy [--force]` | Copy project working directory |
|
|
528
|
-
| `openyida sample [--list] [<skill> <name>] [--output <file
|
|
526
|
+
| `openyida sample [--list] [<skill> <name>] [--output <file>] [--var KEY=VALUE ...] [--design-file <design.md>]` | Output code samples/templates |
|
|
529
527
|
| `openyida doctor [--fix]` | Environment diagnostics & auto-fix |
|
|
530
528
|
| `openyida db-seq-fix [--fix]` | Detect and repair PostgreSQL sequence drift |
|
|
531
529
|
| `openyida formula evaluate <formula\|file> [--schema file]` | Static-check Yida formula syntax and field refs |
|
|
@@ -590,6 +588,31 @@ Form field definitions can include `alias` or `componentAlias` to populate Yida
|
|
|
590
588
|
|
|
591
589
|
`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.
|
|
592
590
|
|
|
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
|
+
|
|
593
616
|
## Agent Skills
|
|
594
617
|
|
|
595
618
|
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,6 +24,8 @@ 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;
|
|
27
29
|
|
|
28
30
|
function shouldUseEnvManagement(argsList) {
|
|
29
31
|
const subCommand = argsList[0];
|
|
@@ -571,6 +573,26 @@ function assertNoUnsupportedLegacyLoginFlags(...argLists) {
|
|
|
571
573
|
}
|
|
572
574
|
|
|
573
575
|
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);
|
|
574
596
|
applyQuietFlag();
|
|
575
597
|
applyGlobalEnvironmentFlags();
|
|
576
598
|
|
|
@@ -619,6 +641,12 @@ async function main() {
|
|
|
619
641
|
break;
|
|
620
642
|
}
|
|
621
643
|
|
|
644
|
+
case 'agent': {
|
|
645
|
+
const { run } = require('../lib/agent/cmd');
|
|
646
|
+
await run(args);
|
|
647
|
+
break;
|
|
648
|
+
}
|
|
649
|
+
|
|
622
650
|
case 'mcp': {
|
|
623
651
|
const { runStdioServer } = require('../lib/mcp/server');
|
|
624
652
|
runStdioServer();
|
|
@@ -733,12 +761,6 @@ async function main() {
|
|
|
733
761
|
break;
|
|
734
762
|
}
|
|
735
763
|
|
|
736
|
-
case 'app-entry': {
|
|
737
|
-
const { run } = require('../lib/app/app-entry');
|
|
738
|
-
await run(args);
|
|
739
|
-
break;
|
|
740
|
-
}
|
|
741
|
-
|
|
742
764
|
case 'app-list': {
|
|
743
765
|
const { run } = require('../lib/app/app-list');
|
|
744
766
|
await run(args);
|
|
@@ -894,8 +916,8 @@ async function main() {
|
|
|
894
916
|
if (args.length < 4) {
|
|
895
917
|
throwCliUsage(t('cli.form_config_usage'), t('cli.form_config_example'));
|
|
896
918
|
}
|
|
897
|
-
|
|
898
|
-
|
|
919
|
+
const { run: runUpdateFormConfig } = require('../lib/app/update-form-config');
|
|
920
|
+
await runUpdateFormConfig(args);
|
|
899
921
|
break;
|
|
900
922
|
}
|
|
901
923
|
|
|
@@ -1281,7 +1303,9 @@ async function main() {
|
|
|
1281
1303
|
}
|
|
1282
1304
|
|
|
1283
1305
|
main()
|
|
1306
|
+
.then(() => { commandReceipt.finish(process.exitCode || 0); })
|
|
1284
1307
|
.catch((err) => {
|
|
1308
|
+
try {commandReceipt?.finish(err && err.exitCode ? err.exitCode : 1);} catch { /* Intent remains recoverable. */ }
|
|
1285
1309
|
if (shouldUseStructuredErrorOutput(err, args)) {
|
|
1286
1310
|
console.error(JSON.stringify(toErrorPayload(err), null, 2));
|
|
1287
1311
|
} else if (isCliError(err)) {
|
|
@@ -1292,7 +1316,5 @@ main()
|
|
|
1292
1316
|
} else {
|
|
1293
1317
|
warn(t('cli.exec_failed', err.message));
|
|
1294
1318
|
}
|
|
1295
|
-
|
|
1296
|
-
// sent through pipes on macOS/Linux. process.exit() can truncate them.
|
|
1297
|
-
process.exitCode = err && err.exitCode ? err.exitCode : 1;
|
|
1319
|
+
process.exit(err && err.exitCode ? err.exitCode : 1);
|
|
1298
1320
|
});
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
# Local runtime launcher contract (development implementation)
|
|
2
|
+
|
|
3
|
+
This Node package contains no Go source or build/download fallback. The runtime
|
|
4
|
+
catalog is intentionally empty until release ownership, signing keys, platform
|
|
5
|
+
support and real package publication are approved. Internal test injection is not
|
|
6
|
+
a CLI trust-root option. Ordinary commands do not resolve or spawn this runtime.
|
|
7
|
+
|
|
8
|
+
## Production metadata and development mode
|
|
9
|
+
|
|
10
|
+
`runtime-manifest.js` is the strict v2 codec: no extra/duplicate JSON keys, malformed
|
|
11
|
+
UTF-8, unknown OS/CPU aliases, invalid protocol/state ranges, unsafe binary paths,
|
|
12
|
+
oversized metadata/binaries or invalid digests. The binary path is exactly
|
|
13
|
+
`bin/openyida-agent-runtime` (Windows: `.exe`). `sourceRevision` is a 40- or
|
|
14
|
+
64-character lowercase source commit digest, not a public artifact tag.
|
|
15
|
+
|
|
16
|
+
Detached Ed25519 signature: original UTF-8 `runtime-manifest.json` bytes, with no
|
|
17
|
+
re-serialization; `.sig` is canonical base64 for 64 signature bytes, optionally
|
|
18
|
+
one final LF. Trust roots live in the main package's `runtime-catalog.json`:
|
|
19
|
+
`keys[keyId] = {publicKey: <SPKI PEM>, revoked?: boolean}`. Unknown/revoked keys
|
|
20
|
+
fail closed; no TOFU, downloaded trust key, arbitrary URL or PATH fallback exists.
|
|
21
|
+
|
|
22
|
+
Each approved release entry is `{platform, arch, packageName, packageVersion,
|
|
23
|
+
runtimeVersion, manifestSha256}`. The main package must declare that exact
|
|
24
|
+
`optionalDependencies` version. Candidate naming is
|
|
25
|
+
`@openyida/local-agent-runtime-${process.platform}-${process.arch}`; this is not a
|
|
26
|
+
claim those names are registered or published. Platform metadata must match its
|
|
27
|
+
exact name/version and single-element `os`/`cpu`, and have no lifecycle scripts.
|
|
28
|
+
Main-package-relative Node resolution supports hoisting; platform packages must
|
|
29
|
+
make `package.json` resolvable. Missing optional packages block Local Agent only.
|
|
30
|
+
The current Beta production allowlist is macOS (`darwin`) and Windows (`win32`).
|
|
31
|
+
Linux remains a source/development target but must not appear in the production
|
|
32
|
+
catalog or optional dependencies until native acceptance is complete. Ordinary
|
|
33
|
+
non-Agent OpenYida CLI commands remain available on Linux.
|
|
34
|
+
|
|
35
|
+
Unsigned v1 requires CLI `--development-runtime` plus explicit absolute
|
|
36
|
+
`--runtime-path` and `--runtime-manifest`. Environment variables cannot enable it.
|
|
37
|
+
Node sends `developmentRuntime: true` only for that flag. v1 is not accepted as a
|
|
38
|
+
production compatibility fallback. Development binaries are normalized to the
|
|
39
|
+
bundle executable filename; their original manifest is preserved as evidence.
|
|
40
|
+
|
|
41
|
+
## Launch DTO (protocolVersion remains 1)
|
|
42
|
+
|
|
43
|
+
Go argv is `[command, "--stdio-config"]`; stdin is one JSON object plus LF, at
|
|
44
|
+
most 64 KiB. Business access/refresh tokens and ordinary OpenYida profile
|
|
45
|
+
identifiers are never included. The launch contains the control endpoint,
|
|
46
|
+
endpointId, providers and stateDir; web-first connect may additionally contain
|
|
47
|
+
one short-lived Enrollment token. `node.version` remains the OpenYida package
|
|
48
|
+
version, not the Node version.
|
|
49
|
+
|
|
50
|
+
CLI-first connect lets Go create a state/PKCE/device-key-bound Enrollment and open
|
|
51
|
+
the configured browser authorization page. Web-first connect receives the same
|
|
52
|
+
device result by claiming the single-use `--enroll` token. Both modes persist only
|
|
53
|
+
device connection credentials. For every accepted attempt, Python requests a
|
|
54
|
+
fixed-value Tianshu task grant with an initial 60-minute lease and Go injects it
|
|
55
|
+
only into that attempt's managed environment; Node never falls back to a normal
|
|
56
|
+
local profile. The authenticated Runtime heartbeat lets Python renew the lease
|
|
57
|
+
server-to-server while the authoritative Run remains active. Node has no task
|
|
58
|
+
RefreshToken and cannot renew the bearer itself. The grant is bound to the
|
|
59
|
+
application, not one form/page. An optional `formUuid` remains per-turn focus
|
|
60
|
+
context and may change between turns inside the same `appType`.
|
|
61
|
+
|
|
62
|
+
The device bearer is not sufficient by itself. Python sends a fresh nonce before
|
|
63
|
+
accepting the Runtime hello, and Go signs the device/endpoint/nonce binding with
|
|
64
|
+
the Ed25519 key created at Enrollment. Refresh requests similarly sign the
|
|
65
|
+
device/action/refresh-token digest/endpoint binding. A copied access or refresh
|
|
66
|
+
bearer therefore cannot connect or rotate without the enrolled private key.
|
|
67
|
+
|
|
68
|
+
For both `connect` and `run`, added fields are:
|
|
69
|
+
|
|
70
|
+
```json
|
|
71
|
+
{
|
|
72
|
+
"parentWatchFD": 3,
|
|
73
|
+
"node": {
|
|
74
|
+
"executable": "/external/node",
|
|
75
|
+
"nodeVersion": "v22.22.3",
|
|
76
|
+
"sha256": "NODE_BINARY_SHA256",
|
|
77
|
+
"version": "2026.9.7",
|
|
78
|
+
"packageRoot": "/private/bundles/BUNDLE_SHA256/openyida",
|
|
79
|
+
"cliEntry": "/private/bundles/BUNDLE_SHA256/openyida/bin/yida.js"
|
|
80
|
+
},
|
|
81
|
+
"bundle": {
|
|
82
|
+
"schemaVersion": 1,
|
|
83
|
+
"id": "BUNDLE_SHA256",
|
|
84
|
+
"root": "/private/bundles/BUNDLE_SHA256",
|
|
85
|
+
"manifestPath": "/private/bundles/BUNDLE_SHA256/bundle-manifest.json",
|
|
86
|
+
"manifestSha256": "BUNDLE_SHA256",
|
|
87
|
+
"runtimeVersion": "0.1.0-dev",
|
|
88
|
+
"skillsDir": "/private/bundles/BUNDLE_SHA256/openyida/yida-skills"
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
The launcher uses four stdio pipes and keeps fd 3 open independently from normal
|
|
94
|
+
configuration stdin EOF. Parent death/pipe EOF asks Go to stop admission and
|
|
95
|
+
cancel/drain, never to report successful completion. Node forwards SIGINT/TERM;
|
|
96
|
+
failure grants 15 seconds before forcing the Go wrapper down. Such timeout returns
|
|
97
|
+
`AGENT_RUNTIME_STOP_UNCONFIRMED` with `executionState: unknown`, not proof that
|
|
98
|
+
provider processes or in-flight business effects stopped.
|
|
99
|
+
|
|
100
|
+
## Bundle bytes, ownership and lifetime
|
|
101
|
+
|
|
102
|
+
`bundle-manifest.json` is `JSON.stringify(object) + "\n"`, encoded as UTF-8.
|
|
103
|
+
Its exact SHA-256 is the bundle ID; it is a local content manifest, not a publisher
|
|
104
|
+
signature. Fields (in emitted order) are:
|
|
105
|
+
|
|
106
|
+
```text
|
|
107
|
+
schemaVersion: 1
|
|
108
|
+
product: "openyida-local-agent-bundle"
|
|
109
|
+
openyidaVersion, runtimeVersion
|
|
110
|
+
launcherProtocol: 1
|
|
111
|
+
wireProtocol: 1
|
|
112
|
+
node: {version: <process.version>, sha256: <external Node binary hash>}
|
|
113
|
+
runtime: {executable: "runtime/bin/openyida-agent-runtime[.exe]",
|
|
114
|
+
manifest: "runtime/runtime-manifest.json",
|
|
115
|
+
signature: "runtime/runtime-manifest.sig" | null,
|
|
116
|
+
development: boolean}
|
|
117
|
+
cliEntry: "openyida/bin/yida.js"
|
|
118
|
+
skillsDir: "openyida/yida-skills"
|
|
119
|
+
files: [{path: <relative POSIX>, size: <integer>, sha256: <lowercase hex>,
|
|
120
|
+
mode: 292 | 365}]
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Files sort by path using UTF-8 byte order (not JavaScript UTF-16 comparison).
|
|
124
|
+
Mode 292 is octal 0444; 365 is 0555.
|
|
125
|
+
The manifest is not its own files entry. Unknown files and symlinks are rejected.
|
|
126
|
+
Go verifies the same raw manifest digest, file list/hash/size/mode, launch paths,
|
|
127
|
+
runtime version and external Node identity before accepting work and before
|
|
128
|
+
subsequent helper/new-turn use. Production publication trust is established by
|
|
129
|
+
Node's v2 signature and approved digest, not by pretending the local bundle hash
|
|
130
|
+
is a release signature.
|
|
131
|
+
|
|
132
|
+
Node copies only packaged CLI assets and the recursively resolved production
|
|
133
|
+
dependency/peer/available optional closure, preserving nested dependency
|
|
134
|
+
resolution and cycles. It omits root platform packages (verified runtime bytes
|
|
135
|
+
are copied separately), user projects, credentials, checkout metadata and
|
|
136
|
+
devDependencies. Source content/tree membership is checked again before publish;
|
|
137
|
+
missing dependencies or concurrent replacement fail closed. No install hook runs.
|
|
138
|
+
|
|
139
|
+
Unique private staging directories plus atomic compare-and-publish handle
|
|
140
|
+
concurrent materializers without a second execution lease. Contents are read-only;
|
|
141
|
+
on macOS the staging root must remain writable until rename, then becomes 0500.
|
|
142
|
+
Cache is 0700 on Unix. Same-user/root compromise is outside this protection;
|
|
143
|
+
Windows ACL/OS release acceptance remains unverified. Interrupted staging is not
|
|
144
|
+
an executable published bundle. Node does not repair a modified winner or GC
|
|
145
|
+
existing bundles, unknown journals or user files.
|
|
146
|
+
|
|
147
|
+
Go exclusively owns durable refs under
|
|
148
|
+
`stateDir/bundle-references/<bundleId>/...` for connection, session and attempt
|
|
149
|
+
lifetimes. Session identity no longer pins BundleID.
|
|
150
|
+
Node has no ref writer, owner lease, GC or automatic migration. Global npm upgrade
|
|
151
|
+
can produce a new bundle. After the connection drains and is restarted, a compatible
|
|
152
|
+
bundle may continue the same native session; an active attempt never switches. External Node and
|
|
153
|
+
provider binaries are not redistributed/frozen by this package; loss or an
|
|
154
|
+
incompatible change blocks new work rather than silently starting fresh.
|
|
155
|
+
|
|
156
|
+
For allowlisted Yida write commands only, Node writes one bounded Mutation Receipt
|
|
157
|
+
after an explicit success result in OPENYIDA_AGENT_RECEIPTS_DIR. Read-only, failed
|
|
158
|
+
and interrupted commands write nothing.
|
|
159
|
+
Go forwards a successful openyida_mutation through the authenticated Run stream;
|
|
160
|
+
Python adds requiresHostRefresh. No Go command gateway is involved.
|
|
161
|
+
Windows parent lifetime uses parentPID and an OS process handle, not parentWatchFD.
|
|
162
|
+
|
|
163
|
+
All current automated tests use generated signing keys and fake processes.
|
|
164
|
+
No real provider, user account, Yida write, public upload or npm publication is
|
|
165
|
+
part of this implementation checkpoint.
|
|
166
|
+
|
|
167
|
+
### Background startup receipt
|
|
168
|
+
|
|
169
|
+
A background `run` can carry a fresh `startupId` (safe identifier, generated per
|
|
170
|
+
spawn). The runtime atomically writes `startup-<startupId>.json` in `stateDir`
|
|
171
|
+
with mode 0600 after the control endpoint is bound, the cloud welcome is
|
|
172
|
+
validated, and recovery finishes. Its only fields are `protocolVersion: 1`,
|
|
173
|
+
`startupId`, and boolean `ready`. The launcher requires `ready: true`, then removes
|
|
174
|
+
the receipt and detaches. Provider logs are never a startup acknowledgement.
|
|
175
|
+
A startup failure/exit or the 60-second deadline rejects the launch; the launcher
|
|
176
|
+
terminates only its own new child, preserving credentials and journals.
|
|
177
|
+
|
|
178
|
+
This is an additive launch-config extension. Older launchers omit it. Older
|
|
179
|
+
runtimes reject unknown fields, so release the updated runtime and update the
|
|
180
|
+
CLI's exact runtime dependencies and signed catalog together before publishing
|
|
181
|
+
this launcher. Do not pair the updated launcher with a previously published
|
|
182
|
+
runtime and assume compatibility.
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const crypto = require('crypto');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const { CliError } = require('../core/cli-error');
|
|
7
|
+
const { readManagedContext } = require('./managed-context');
|
|
8
|
+
|
|
9
|
+
const SCHEMA = 'openyida.ask-human.v1';
|
|
10
|
+
const MAX_FILE_BYTES = 32 * 1024;
|
|
11
|
+
const USAGE = '用法:openyida agent ask-human --request-key <稳定业务键> --questions-file <相对路径> --json';
|
|
12
|
+
|
|
13
|
+
function fail(code, message) { throw new CliError(message, { code, details: { retryable: false } }); }
|
|
14
|
+
function text(value, max) { return typeof value === 'string' && value === value.trim() && value.length > 0 && Buffer.byteLength(value) <= max && !/\p{Cc}/u.test(value); }
|
|
15
|
+
|
|
16
|
+
function parseArgs(argv) {
|
|
17
|
+
if (argv.length === 1 && ['--help', '-h'].includes(argv[0])) { return { help: true }; }
|
|
18
|
+
const result = { json: false };
|
|
19
|
+
for (let i = 0; i < argv.length; i++) {
|
|
20
|
+
const name = argv[i];
|
|
21
|
+
if (name === '--json' && !result.json) { result.json = true; continue; }
|
|
22
|
+
const key = name === '--request-key' ? 'requestKey' : name === '--questions-file' ? 'questionsFile' : null;
|
|
23
|
+
if (!key || result[key] !== undefined || !argv[i + 1] || argv[i + 1].startsWith('--')) {
|
|
24
|
+
fail('AGENT_ASK_HUMAN_INVALID', USAGE);
|
|
25
|
+
}
|
|
26
|
+
result[key] = argv[++i];
|
|
27
|
+
}
|
|
28
|
+
if (!text(result.requestKey, 128) || !/^[A-Za-z0-9][A-Za-z0-9._:-]*$/.test(result.requestKey) ||
|
|
29
|
+
!text(result.questionsFile, 512) || path.isAbsolute(result.questionsFile)) {
|
|
30
|
+
fail('AGENT_ASK_HUMAN_INVALID', 'Ask Human 参数无效。');
|
|
31
|
+
}
|
|
32
|
+
return result;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function readQuestions(relativePath, cwd) {
|
|
36
|
+
let root;
|
|
37
|
+
let candidate;
|
|
38
|
+
let realCandidate;
|
|
39
|
+
let stat;
|
|
40
|
+
try {
|
|
41
|
+
root = fs.realpathSync(cwd);
|
|
42
|
+
candidate = path.resolve(root, relativePath);
|
|
43
|
+
stat = fs.lstatSync(candidate);
|
|
44
|
+
realCandidate = fs.realpathSync(candidate);
|
|
45
|
+
} catch { fail('AGENT_ASK_HUMAN_INVALID', '问题文件不可读取。'); }
|
|
46
|
+
if (candidate === root || !candidate.startsWith(root + path.sep) || realCandidate === root || !realCandidate.startsWith(root + path.sep)) {
|
|
47
|
+
fail('AGENT_ASK_HUMAN_INVALID', '问题文件必须位于当前工作目录。');
|
|
48
|
+
}
|
|
49
|
+
if (!stat.isFile() || stat.isSymbolicLink() || stat.size > MAX_FILE_BYTES) { fail('AGENT_ASK_HUMAN_INVALID', '问题文件必须是 32 KiB 以内的普通文件。'); }
|
|
50
|
+
let document;
|
|
51
|
+
try { document = JSON.parse(fs.readFileSync(candidate, 'utf8')); } catch { fail('AGENT_ASK_HUMAN_INVALID', '问题文件不是有效 JSON。'); }
|
|
52
|
+
if (!document || typeof document !== 'object' || Array.isArray(document) || !text(document.title, 256) ||
|
|
53
|
+
!Array.isArray(document.questions) || document.questions.length < 1 || document.questions.length > 3) {
|
|
54
|
+
fail('AGENT_ASK_HUMAN_INVALID', 'Ask Human 问题结构无效。');
|
|
55
|
+
}
|
|
56
|
+
const ids = new Set();
|
|
57
|
+
for (const question of document.questions) {
|
|
58
|
+
const allowed = new Set(['id', 'header', 'question', 'type', 'required', 'options']);
|
|
59
|
+
if (!question || typeof question !== 'object' || Array.isArray(question) || Object.keys(question).some(key => !allowed.has(key)) ||
|
|
60
|
+
!text(question.id, 128) || ids.has(question.id) || !text(question.header, 128) || !text(question.question, 2048) ||
|
|
61
|
+
!['text', 'single_select', 'multi_select'].includes(question.type) ||
|
|
62
|
+
(question.required !== undefined && typeof question.required !== 'boolean')) {
|
|
63
|
+
fail('AGENT_ASK_HUMAN_INVALID', 'Ask Human 问题结构无效。');
|
|
64
|
+
}
|
|
65
|
+
ids.add(question.id);
|
|
66
|
+
const options = question.options === undefined ? [] : question.options;
|
|
67
|
+
if (!Array.isArray(options)) { fail('AGENT_ASK_HUMAN_INVALID', 'Ask Human 选项结构无效。'); }
|
|
68
|
+
if ((question.type === 'text' && options.length) || (question.type !== 'text' && (!Array.isArray(options) || options.length < 1 || options.length > 12)) ||
|
|
69
|
+
options.some(option => !option || typeof option !== 'object' || Array.isArray(option) || Object.keys(option).some(key => !['label', 'description'].includes(key)) || !text(option.label, 512) ||
|
|
70
|
+
(option.description !== undefined && option.description !== '' && !text(option.description, 1024)))) {
|
|
71
|
+
fail('AGENT_ASK_HUMAN_INVALID', 'Ask Human 选项结构无效。');
|
|
72
|
+
}
|
|
73
|
+
if (new Set(options.map(option => option.label)).size !== options.length) { fail('AGENT_ASK_HUMAN_INVALID', 'Ask Human 选项不能重复。'); }
|
|
74
|
+
}
|
|
75
|
+
return { title: document.title, questions: document.questions.map(question => ({ ...question, required: question.required !== false })) };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function ensureReceiptDir(env) {
|
|
79
|
+
const value = env.OPENYIDA_AGENT_INTERACTIONS_DIR;
|
|
80
|
+
if (!value || !path.isAbsolute(value)) { fail('AGENT_ASK_HUMAN_UNAVAILABLE', '当前任务未启用 Ask Human。'); }
|
|
81
|
+
fs.mkdirSync(value, { recursive: true, mode: 0o700 });
|
|
82
|
+
const stat = fs.lstatSync(value);
|
|
83
|
+
if (!stat.isDirectory() || stat.isSymbolicLink()) { fail('AGENT_ASK_HUMAN_UNAVAILABLE', 'Ask Human 私有目录不可用。'); }
|
|
84
|
+
if (fs.readdirSync(value).filter(name => name.endsWith('.json')).length >= 64) { fail('AGENT_ASK_HUMAN_LIMIT', '本轮 Ask Human 请求过多。'); }
|
|
85
|
+
return value;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function retryWindowsFileOperation(operation) {
|
|
89
|
+
const delays = [20, 40, 80, 160, 320];
|
|
90
|
+
for (let attempt = 0; ; attempt++) {
|
|
91
|
+
try { return operation(); } catch (error) {
|
|
92
|
+
if (process.platform !== 'win32' || attempt === delays.length || !['EBUSY', 'EPERM', 'EACCES'].includes(error.code)) { throw error; }
|
|
93
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, delays[attempt]);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function writeReceipt(directory, requestId, receipt) {
|
|
99
|
+
const temporary = path.join(directory, `.${requestId}.tmp`);
|
|
100
|
+
const target = path.join(directory, `${requestId}.json`);
|
|
101
|
+
let fd;
|
|
102
|
+
try {
|
|
103
|
+
fd = fs.openSync(temporary, 'wx', 0o600);
|
|
104
|
+
fs.writeFileSync(fd, JSON.stringify(receipt));
|
|
105
|
+
fs.fsyncSync(fd);
|
|
106
|
+
fs.closeSync(fd);
|
|
107
|
+
fd = undefined;
|
|
108
|
+
retryWindowsFileOperation(() => fs.renameSync(temporary, target));
|
|
109
|
+
if (process.platform !== 'win32') {
|
|
110
|
+
const parent = fs.openSync(directory, 'r');
|
|
111
|
+
try { fs.fsyncSync(parent); } finally { fs.closeSync(parent); }
|
|
112
|
+
}
|
|
113
|
+
} catch (error) {
|
|
114
|
+
if (fd !== undefined) { try { fs.closeSync(fd); } catch { /* Preserve the original write failure if cleanup also fails. */ } }
|
|
115
|
+
if (fs.existsSync(temporary)) { try { retryWindowsFileOperation(() => fs.unlinkSync(temporary)); } catch { /* Preserve the original write failure if cleanup also fails. */ } }
|
|
116
|
+
throw error;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function run(argv = [], options = {}) {
|
|
121
|
+
const env = options.env || process.env;
|
|
122
|
+
const context = readManagedContext(env);
|
|
123
|
+
if (!context) { fail('AGENT_ASK_HUMAN_MANAGED_ONLY', 'Ask Human 仅可在宜搭云端发起的本地 Agent 任务中使用。'); }
|
|
124
|
+
const args = parseArgs(argv);
|
|
125
|
+
if (args.help) {
|
|
126
|
+
(options.stdout || process.stdout).write(`${USAGE}\n问题文件格式:{ "title": "...", "questions": [{ "id": "...", "header": "...", "question": "...", "type": "text|single_select|multi_select", "options": [{ "label": "...", "description": "..." }] }] }\n`);
|
|
127
|
+
return { ok: true, help: true };
|
|
128
|
+
}
|
|
129
|
+
const document = readQuestions(args.questionsFile, options.cwd || process.cwd());
|
|
130
|
+
const digest = crypto.createHash('sha256').update(JSON.stringify(document)).digest('hex');
|
|
131
|
+
const directory = ensureReceiptDir(env);
|
|
132
|
+
for (const name of fs.readdirSync(directory).filter(item => item.endsWith('.json'))) {
|
|
133
|
+
let prior;
|
|
134
|
+
try { prior = JSON.parse(fs.readFileSync(path.join(directory, name), 'utf8')); } catch { continue; }
|
|
135
|
+
if (prior.requestKey === args.requestKey) {
|
|
136
|
+
if (prior.contentDigest !== digest) { fail('AGENT_ASK_HUMAN_CONFLICT', '同一 request-key 不能提交不同问题。'); }
|
|
137
|
+
const output = { ok: true, requestId: prior.requestId, status: 'queued' };
|
|
138
|
+
(options.stdout || process.stdout).write(JSON.stringify(output) + '\n');
|
|
139
|
+
return output;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
const requestId = crypto.randomUUID();
|
|
143
|
+
const receipt = { schemaVersion: SCHEMA, requestId, requestKey: args.requestKey, contentDigest: digest,
|
|
144
|
+
runId: context.runId, attemptId: context.attemptId, createdAt: new Date().toISOString(), ...document };
|
|
145
|
+
writeReceipt(directory, requestId, receipt);
|
|
146
|
+
const output = { ok: true, requestId, status: 'queued' };
|
|
147
|
+
(options.stdout || process.stdout).write(JSON.stringify(output) + '\n');
|
|
148
|
+
return output;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
module.exports = { run, SCHEMA };
|