openyida 2026.9.21 → 2026.9.22-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -20
- package/bin/yida.js +6 -11
- package/lib/app/app-list.js +3 -7
- package/lib/app/canvas-compile.js +12 -22
- package/lib/app/check-prd-completeness.js +0 -7
- package/lib/app/create-app.js +1 -4
- package/lib/app/create-form/args.js +12 -37
- package/lib/app/create-form/batch.js +11 -86
- package/lib/app/create-form.js +86 -193
- package/lib/app/create-page.js +17 -84
- package/lib/app/custom-theme.js +0 -11
- package/lib/app/form-field-validator.js +41 -26
- package/lib/app/form-open-guard.js +10 -97
- package/lib/app/get-form-config.js +0 -1
- package/lib/app/inline-css-guard.js +31 -1
- package/lib/app/services/form-compiler.js +4 -2
- package/lib/app/theme-from-design.js +41 -144
- package/lib/app/theme-presets.js +1 -1
- package/lib/app/update-app.js +13 -63
- package/lib/asset/ai-image.js +9 -59
- package/lib/asset/asset-cmd.js +7 -18
- package/lib/asset/asset-plan.js +9 -102
- package/lib/asset/asset-resolve.js +10 -55
- package/lib/asset/asset-status.js +4 -5
- package/lib/asset/host-capabilities.js +2 -11
- package/lib/asset/url-verify.js +4 -19
- package/lib/auth/dws-sync.js +145 -0
- package/lib/auth/token-auth.js +73 -9
- package/lib/auth/token-store.js +50 -0
- package/lib/connector/connector-test.js +5 -19
- package/lib/connector/contract.js +1 -3
- package/lib/core/agent-capabilities.js +16 -19
- package/lib/core/command-manifest.js +40 -329
- package/lib/core/locales/en.js +17 -74
- package/lib/core/locales/zh.js +16 -73
- package/lib/core/redact.js +10 -15
- package/lib/core/sample.js +30 -72
- package/lib/core/utils.js +4 -0
- package/lib/design-plan/design-plan.js +4 -51
- package/lib/design-plan/init.js +26 -121
- package/lib/design-plan/materialize.js +348 -451
- package/lib/design-plan/normalize.js +29 -74
- package/lib/design-plan/parallel.js +16 -71
- package/lib/design-plan/patch.js +16 -63
- package/lib/design-plan/preview.js +8 -6
- package/lib/design-plan/validate.js +11 -71
- package/lib/process/create-process.js +50 -84
- package/lib/samples/openyida-scaffold/canvas-form-drawer.canvas.jsx +90 -20
- package/lib/samples/openyida-scaffold/canvas-nav/content.jsx +13 -93
- package/lib/samples/openyida-scaffold/canvas-nav/data.jsx +18 -100
- 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 +9 -15
- package/lib/samples/yida-canvas-table-form/table-form-batch-submit.canvas.jsx +83 -9
- package/lib/samples/yida-rechart/trend-combo.canvas.jsx +87 -14
- package/package.json +1 -3
- package/scripts/postinstall.js +3 -3
- package/yida-skills/SKILL.md +16 -19
- 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 +23 -46
- package/yida-skills/skills/yida-app/workflow/parallel-work.md +6 -15
- package/yida-skills/skills/yida-app/workflow/plan/step-1-understand.md +1 -1
- package/yida-skills/skills/yida-app/workflow/plan/step-2-confirm.md +1 -1
- package/yida-skills/skills/yida-app/workflow/plan/step-4-deliver.md +15 -29
- package/yida-skills/skills/yida-app/workflow/plan/workflow.md +11 -29
- package/yida-skills/skills/yida-app/workflow/step-1-resource-context.md +2 -4
- package/yida-skills/skills/yida-app/workflow/step-2-design.md +9 -23
- package/yida-skills/skills/yida-app/workflow/step-3-create-or-reuse-app.md +4 -6
- package/yida-skills/skills/yida-app/workflow/step-4-forms-processes.md +4 -15
- package/yida-skills/skills/yida-app/workflow/step-6-main-page.md +4 -6
- package/yida-skills/skills/yida-app/workflow/step-7-page-code.md +9 -15
- package/yida-skills/skills/yida-app/workflow/step-8-publish-navigation.md +11 -25
- package/yida-skills/skills/yida-app/workflow/step-9-output-finish.md +28 -68
- package/yida-skills/skills/yida-canvas-custom-page/SKILL.md +43 -42
- package/yida-skills/skills/yida-canvas-custom-page/references/canvas-style-implementation-guide.md +44 -60
- package/yida-skills/skills/yida-canvas-custom-page/references/canvas-theme-provider.md +50 -72
- package/yida-skills/skills/yida-canvas-custom-page/references/component-library-guide.md +1 -13
- 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 +72 -101
- package/yida-skills/skills/yida-canvas-custom-page/references/page-generation-guide.md +32 -35
- package/yida-skills/skills/yida-canvas-custom-page/scripts/build-canvas-theme.js +3 -29
- package/yida-skills/skills/yida-canvas-custom-page/scripts/canvas-theme-provider.template.jsx +6 -69
- package/yida-skills/skills/yida-canvas-data-binding/SKILL.md +0 -26
- package/yida-skills/skills/yida-create-app/SKILL.md +0 -2
- package/yida-skills/skills/yida-create-form-page/SKILL.md +58 -72
- 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 +5 -6
- package/yida-skills/skills/yida-create-form-page/references/field-definition-guide.md +19 -7
- package/yida-skills/skills/yida-create-form-page/references/form-field-properties.md +44 -62
- package/yida-skills/skills/yida-create-page/SKILL.md +6 -14
- package/yida-skills/skills/yida-create-process/SKILL.md +2 -13
- package/yida-skills/skills/yida-custom-page/SKILL.md +0 -2
- package/yida-skills/skills/yida-custom-page/references/design-system.md +1 -1
- package/yida-skills/skills/yida-dashboard/SKILL.md +4 -5
- package/yida-skills/skills/yida-data-management/SKILL.md +2 -4
- package/yida-skills/skills/yida-data-management/references/data-format-guide.md +4 -4
- package/yida-skills/skills/yida-design/SKILL.md +14 -29
- package/yida-skills/skills/yida-design/references/app/blueprint.md +70 -0
- package/yida-skills/skills/yida-design/references/app/navigation-patterns.md +43 -0
- package/yida-skills/skills/yida-design/references/app/role-journey.md +30 -0
- package/yida-skills/skills/yida-design/references/ask-human-interaction-contract.md +42 -67
- package/yida-skills/skills/yida-design/references/asset-workflow.md +2 -6
- package/yida-skills/skills/yida-design/references/design-mode.md +4 -4
- package/yida-skills/skills/yida-design/references/page-quality-gates.md +38 -26
- package/yida-skills/skills/yida-design/references/style-design-selection.md +137 -0
- package/yida-skills/skills/yida-design/references/style-designs/_design-md-template.md +185 -0
- package/yida-skills/skills/yida-design/references/style-designs/aqua-service-progress-dashboard.md +242 -0
- package/yida-skills/skills/yida-design/references/style-designs/blue-insight-operations-dashboard.md +243 -0
- package/yida-skills/skills/yida-design/references/style-designs/blue-productivity-insight-workbench.md +242 -0
- package/yida-skills/skills/yida-design/references/style-designs/command-filter-card-console.md +304 -0
- package/yida-skills/skills/yida-design/references/style-designs/contrast-command-analytics-workbench.md +261 -0
- package/yida-skills/skills/yida-design/references/style-designs/dark-stage-analytic-dashboard.md +369 -0
- package/yida-skills/skills/yida-design/references/style-designs/filterable-card-catalog.md +354 -0
- package/yida-skills/skills/yida-design/references/style-designs/green-timeline-progress-workbench.md +250 -0
- package/yida-skills/skills/yida-design/references/style-designs/registry.md +117 -0
- package/yida-skills/skills/yida-design/references/style-designs/soft-analytic-workbench.md +348 -0
- package/yida-skills/skills/yida-design/references/style-designs/soft-blue-grid-analytic-dashboard.md +658 -0
- package/yida-skills/skills/yida-design/references/style-designs/soft-bordered-analytic-workbench.md +385 -0
- package/yida-skills/skills/yida-design/references/style-designs/soft-curated-filter-gallery.md +300 -0
- package/yida-skills/skills/yida-design/references/style-designs/soft-modular-analytic-workbench.md +404 -0
- package/yida-skills/skills/yida-design/references/style-designs/soft-progress-analytics-workbench.md +382 -0
- package/yida-skills/skills/yida-design/references/style-designs/soft-timeline-analytics-workbench.md +367 -0
- package/yida-skills/skills/yida-design/references/style-designs/teal-rail-analytics-workbench.md +341 -0
- package/yida-skills/skills/yida-design/references/theme/app-custom-theme-template.css +432 -350
- package/yida-skills/skills/yida-design/references/visual-decision-engine.md +143 -19
- package/yida-skills/skills/yida-design/references/visual-scaffold-recipes.md +27 -15
- package/yida-skills/skills/yida-design/sub_skill/page-design/SKILL.md +0 -4
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/SKILL.md +8 -22
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/assets/README.md +10 -14
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/assets/build-plan-template.html +4 -9
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/references/build-plan-compact-schema.md +27 -49
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/references/build-plan-schema.md +154 -33
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/references/visual-design.md +127 -48
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/references/visual-theme-selection.md +51 -37
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/scripts/render_build_plan.py +48 -43
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/scripts/validate_design_themes.py +190 -0
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/README.md +98 -0
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/airy-media-grid.md +339 -0
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/airy-modular-clarity.md +348 -0
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/airy-structured-clarity.md +344 -0
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/ambient-halo-layered.md +345 -0
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/dark-focus-layered.md +355 -0
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/dark-luminous-modular.md +377 -0
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/deep-stage-duotone.md +352 -0
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/hairline-runway-clarity.md +389 -0
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/high-contrast-modular.md +364 -0
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/index.json +329 -0
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/interlocked-vivid-modules.md +348 -0
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/matrix-grid-focus.md +345 -0
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/media-rail-inspector.md +379 -0
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/mist-layered-signal.md +347 -0
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/modular-rail-signal.md +352 -0
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/mono-grid-signal.md +386 -0
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/ribbon-ledger-lift.md +384 -0
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/segmented-meter-clarity.md +388 -0
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/split-rail-precision.md +341 -0
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/status-framed-media-grid.md +361 -0
- package/yida-skills/skills/yida-design/workflow/output-design.md +375 -117
- package/yida-skills/skills/yida-design/workflow/output-prd.md +185 -0
- package/yida-skills/skills/yida-design/workflow/step-1-read-brief.md +1 -1
- package/yida-skills/skills/yida-design/workflow/step-2-theme-system.md +29 -33
- package/yida-skills/skills/yida-design/workflow/step-4-wireframe-interaction.md +1 -3
- package/yida-skills/skills/yida-design/workflow/step-5-visual-states.md +106 -22
- package/yida-skills/skills/yida-design/workflow/step-6-handoff.md +22 -15
- package/yida-skills/skills/yida-form-permission/SKILL.md +0 -6
- package/yida-skills/skills/yida-image-assets/SKILL.md +50 -73
- package/yida-skills/skills/yida-image-assets/references/manifest-contract.md +52 -82
- package/yida-skills/skills/yida-image-assets/references/source-policy.md +15 -14
- package/yida-skills/skills/yida-integration/SKILL.md +0 -1
- package/yida-skills/skills/yida-login/SKILL.md +0 -2
- package/yida-skills/skills/yida-nav-group/SKILL.md +2 -4
- package/yida-skills/skills/yida-nav-shell/SKILL.md +12 -36
- package/yida-skills/skills/yida-nav-shell/references/nav-shell-patterns.md +136 -79
- package/yida-skills/skills/yida-page-config/SKILL.md +4 -13
- package/yida-skills/skills/yida-prd/SKILL.md +3 -5
- package/yida-skills/skills/yida-prd/references/app/blueprint.md +4 -4
- package/yida-skills/skills/yida-prd/references/app/navigation-patterns.md +3 -6
- package/yida-skills/skills/yida-prd/references/app/role-journey.md +3 -5
- package/yida-skills/skills/yida-prd/references/plan-content.md +0 -4
- package/yida-skills/skills/yida-prd/workflow/output-prd.md +11 -18
- package/yida-skills/skills/yida-prd/workflow/plan-business.md +4 -4
- package/yida-skills/skills/yida-prd/workflow/step-2-information-architecture.md +8 -11
- package/yida-skills/skills/yida-publish-page/SKILL.md +1 -9
- package/yida-skills/skills/yida-requirement-analysis/SKILL.md +39 -10
- package/yida-skills/skills/yida-requirement-analysis/workflow/prepare-brief.md +71 -40
- package/yida-skills/skills-index.json +16 -36
- package/lib/app/application-entry-urls.js +0 -31
- package/lib/app/application-style.js +0 -69
- package/lib/app/canvas-icon-exports.json +0 -6160
- package/lib/app/canvas-icon-guard.js +0 -87
- package/lib/app/canvas-navigation-guard.js +0 -84
- package/lib/app/canvas-path-guard.js +0 -78
- package/lib/app/canvas-theme-guard.js +0 -184
- package/lib/app/form-data-response-guard.js +0 -57
- package/lib/asset/asset-execution.js +0 -79
- package/lib/core/css-structure.js +0 -37
- package/lib/design/check-design.js +0 -51
- package/lib/design/document.js +0 -458
- package/lib/design/icon-contrast.js +0 -85
- package/lib/design/page-content.js +0 -11
- package/lib/design-plan/artifact-updates.js +0 -117
- package/lib/design-plan/confirmation.js +0 -13
- package/lib/design-plan/entry-navigation.js +0 -97
- package/lib/design-plan/navigation-policy.js +0 -38
- package/lib/design-plan/rebase.js +0 -60
- package/lib/design-plan/themes.js +0 -101
- package/lib/design-plan/visual-policy.js +0 -42
- package/lib/samples/openyida-scaffold/canvas-admin-entry.jsx +0 -62
- package/lib/samples/openyida-scaffold/canvas-navigation.jsx +0 -56
- package/lib/samples/openyida-scaffold/canvas-view-state.jsx +0 -74
- package/yida-skills/skills/yida-app/references/entry-navigation.md +0 -193
- package/yida-skills/skills/yida-canvas-custom-page/references/view-state-recovery.md +0 -52
- package/yida-skills/skills/yida-canvas-data-binding/references/business-action-permissions.md +0 -31
- package/yida-skills/skills/yida-design/references/application-style-library.md +0 -93
- package/yida-skills/skills/yida-design/references/application-theme-consistency.md +0 -150
- package/yida-skills/skills/yida-design/references/native-form-styles.md +0 -208
- package/yida-skills/skills/yida-design/references/navigation-decision.md +0 -53
- package/yida-skills/skills/yida-design/references/page-continuity.md +0 -42
- package/yida-skills/skills/yida-design/references/theme/application-style-recipes.css +0 -58
- package/yida-skills/skills/yida-design/references/theme-selection.md +0 -67
- package/yida-skills/skills/yida-design/scripts/validate_design_themes.py +0 -302
- package/yida-skills/skills/yida-design/templates/application-styles.json +0 -398
- package/yida-skills/skills/yida-design/templates/design-themes/README.md +0 -73
- package/yida-skills/skills/yida-design/templates/design-themes/app-amber/app_theme.css +0 -748
- package/yida-skills/skills/yida-design/templates/design-themes/app-amber/design.md +0 -274
- 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 -748
- package/yida-skills/skills/yida-design/templates/design-themes/app-editorial/design.md +0 -274
- 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 -748
- package/yida-skills/skills/yida-design/templates/design-themes/app-executive/design.md +0 -274
- 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 -748
- package/yida-skills/skills/yida-design/templates/design-themes/app-finance/design.md +0 -274
- 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 -748
- package/yida-skills/skills/yida-design/templates/design-themes/app-glass/design.md +0 -274
- 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 -748
- package/yida-skills/skills/yida-design/templates/design-themes/app-graphite/design.md +0 -274
- 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 -748
- package/yida-skills/skills/yida-design/templates/design-themes/app-line/design.md +0 -274
- 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 -748
- package/yida-skills/skills/yida-design/templates/design-themes/app-neon/design.md +0 -274
- 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 -754
- package/yida-skills/skills/yida-design/templates/design-themes/app-nordic/design.md +0 -279
- 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 -748
- package/yida-skills/skills/yida-design/templates/design-themes/app-paper/design.md +0 -274
- 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 -748
- package/yida-skills/skills/yida-design/templates/design-themes/app-platinum/design.md +0 -274
- 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 -748
- package/yida-skills/skills/yida-design/templates/design-themes/app-plum/design.md +0 -274
- 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 -758
- package/yida-skills/skills/yida-design/templates/design-themes/app-pop/design.md +0 -281
- 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 -748
- package/yida-skills/skills/yida-design/templates/design-themes/app-sage/design.md +0 -274
- 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 -748
- package/yida-skills/skills/yida-design/templates/design-themes/app-teal/design.md +0 -274
- 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 -754
- package/yida-skills/skills/yida-design/templates/design-themes/app-terminal/design.md +0 -279
- 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 -752
- package/yida-skills/skills/yida-design/templates/design-themes/app-ticket/design.md +0 -278
- 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 -748
- package/yida-skills/skills/yida-design/templates/design-themes/app-wire/design.md +0 -274
- 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/basic-tokens.json +0 -118
- package/yida-skills/skills/yida-design/templates/design-themes/dark-inset-hairline/app_theme.css +0 -690
- package/yida-skills/skills/yida-design/templates/design-themes/dark-inset-hairline/design.md +0 -382
- 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 -691
- package/yida-skills/skills/yida-design/templates/design-themes/dark-rail-fine-lines/design.md +0 -380
- 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 -696
- package/yida-skills/skills/yida-design/templates/design-themes/dark-rail-signal-panels/design.md +0 -378
- 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 -689
- package/yida-skills/skills/yida-design/templates/design-themes/free-creative/design.md +0 -238
- 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 -692
- package/yida-skills/skills/yida-design/templates/design-themes/graphite-bevel-grid/design.md +0 -375
- 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 -691
- package/yida-skills/skills/yida-design/templates/design-themes/hairline-shared-bands/design.md +0 -362
- 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 -691
- package/yida-skills/skills/yida-design/templates/design-themes/hairline-soft-blocks/design.md +0 -409
- 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/index.json +0 -464
- package/yida-skills/skills/yida-design/templates/design-themes/ink-glow-soft-panels/app_theme.css +0 -701
- package/yida-skills/skills/yida-design/templates/design-themes/ink-glow-soft-panels/design.md +0 -379
- 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 -691
- package/yida-skills/skills/yida-design/templates/design-themes/inset-frame-pixel-rhythm/design.md +0 -404
- 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 -698
- package/yida-skills/skills/yida-design/templates/design-themes/outlined-texture-duotone/design.md +0 -389
- 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 -691
- package/yida-skills/skills/yida-design/templates/design-themes/soft-inset-surfaces/design.md +0 -372
- 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 -690
- package/yida-skills/skills/yida-design/templates/design-themes/soft-outline-rhythm/design.md +0 -455
- 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 -698
- package/yida-skills/skills/yida-design/templates/design-themes/soft-rail-bold-band/design.md +0 -394
- 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/soft-spectral-panels/app_theme.css +0 -692
- package/yida-skills/skills/yida-design/templates/design-themes/soft-spectral-panels/design.md +0 -391
- package/yida-skills/skills/yida-design/templates/design-themes/soft-spectral-panels/form-layout.json +0 -13
- package/yida-skills/skills/yida-design/templates/design-themes/warm-canvas-contrast-panels/app_theme.css +0 -711
- package/yida-skills/skills/yida-design/templates/design-themes/warm-canvas-contrast-panels/design.md +0 -408
- 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 -692
- package/yida-skills/skills/yida-design/templates/design-themes/warm-rail-muted-panels/design.md +0 -383
- package/yida-skills/skills/yida-design/templates/design-themes/warm-rail-muted-panels/form-layout.json +0 -13
- package/yida-skills/skills/yida-design/templates/navigation-styles.json +0 -769
- package/yida-skills/skills/yida-requirement-analysis/references/experience-groups.md +0 -79
- package/yida-skills/skills/yida-requirement-analysis/references/handoff.md +0 -68
|
@@ -10,8 +10,6 @@ const { minimumSize, prepareAssetPlan } = require('./asset-plan');
|
|
|
10
10
|
const { readImageMetadata, parseImageMetadata } = require('./image-metadata');
|
|
11
11
|
const { MAX_IMAGE_BYTES, uploadAttachment } = require('./attachment-upload');
|
|
12
12
|
const { getAssetStatus } = require('./asset-status');
|
|
13
|
-
const { t } = require('../core/i18n');
|
|
14
|
-
const { verifyImageUrl } = require('./url-verify');
|
|
15
13
|
|
|
16
14
|
const ALLOWED_STOCK_IMAGE_PROVIDERS = Object.freeze({
|
|
17
15
|
unsplash: Object.freeze(['images.unsplash.com']),
|
|
@@ -44,20 +42,8 @@ function getCollectedImageProvider(rawUrl) {
|
|
|
44
42
|
return null;
|
|
45
43
|
}
|
|
46
44
|
|
|
47
|
-
function
|
|
48
|
-
|
|
49
|
-
const stock = (ctx.assetSource || 'search') === 'search';
|
|
50
|
-
return {
|
|
51
|
-
hotlinkAllowed: ctx.hotlinkAllowed ?? (isHttpUrl(rawUrl) && stock && !!ALLOWED_STOCK_IMAGE_PROVIDERS[provider]),
|
|
52
|
-
rehostAllowed: ctx.rehostAllowed ?? !(stock && provider === 'unsplash'),
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function getExternalDeliveryPolicy(rawUrl, ctx = {}) {
|
|
57
|
-
if (!isHttpUrl(rawUrl)) {return 'standard';}
|
|
58
|
-
const rights = deliveryRights(rawUrl, ctx);
|
|
59
|
-
if (rights.hotlinkAllowed && ctx.deliveryMode !== 'upload') {return 'original-url';}
|
|
60
|
-
return rights.rehostAllowed ? 'yida-attachment' : 'blocked';
|
|
45
|
+
function getExternalDeliveryPolicy(rawUrl) {
|
|
46
|
+
return isHttpUrl(rawUrl) ? 'yida-attachment' : 'standard';
|
|
61
47
|
}
|
|
62
48
|
|
|
63
49
|
function extensionFromUrl(rawUrl) {
|
|
@@ -194,29 +180,15 @@ async function resolveOne(candidate, ctx = {}) {
|
|
|
194
180
|
allowedProviders: Object.keys(ALLOWED_STOCK_IMAGE_PROVIDERS),
|
|
195
181
|
};
|
|
196
182
|
}
|
|
197
|
-
const deliveryPolicy = getExternalDeliveryPolicy(raw
|
|
198
|
-
const rights = deliveryRights(raw, ctx);
|
|
183
|
+
const deliveryPolicy = getExternalDeliveryPolicy(raw);
|
|
199
184
|
const common = { provider: inputProvider || ctx.provider || assetSource };
|
|
200
|
-
const original =
|
|
201
|
-
if (!rights.hotlinkAllowed) {return { ...common, resolved: false, reason };}
|
|
185
|
+
const original = metadata => {
|
|
202
186
|
const failure = sizeFailure(metadata, ctx);
|
|
203
187
|
if (failure) {return { ...common, resolved: false, reason: failure };}
|
|
204
188
|
return { ...common, ...metadata, imageType: metadata.type, resolved: true, url: raw,
|
|
205
|
-
source: 'external', reason, deliveryPolicy: 'original-url' };
|
|
189
|
+
source: 'external', reason: 'ASSET_TOO_LARGE', deliveryPolicy: 'original-url' };
|
|
206
190
|
};
|
|
207
|
-
if (deliveryPolicy === 'blocked') {
|
|
208
|
-
return { ...common, resolved: false, reason: 'REHOST_NOT_ALLOWED', deliveryPolicy };
|
|
209
|
-
}
|
|
210
|
-
if (deliveryPolicy === 'original-url') {
|
|
211
|
-
const verified = await (ctx.verifyFn || verifyImageUrl)(raw, {
|
|
212
|
-
timeout: ctx.timeout, minWidth: ctx.minWidth, minHeight: ctx.minHeight,
|
|
213
|
-
allowUrl: assetSource === 'search' ? url => getCollectedImageProvider(url) === inputProvider : undefined,
|
|
214
|
-
});
|
|
215
|
-
if (!verified.ok) {return { ...common, resolved: false, reason: verified.reason };}
|
|
216
|
-
return original({ ...verified, type: verified.imageType }, 'OK_DIRECT');
|
|
217
|
-
}
|
|
218
191
|
let tempPath = '';
|
|
219
|
-
let verifiedMetadata;
|
|
220
192
|
try {
|
|
221
193
|
tempPath = await (ctx.downloadFn || downloadUrlToTemp)(raw, { timeout: ctx.timeout, stockOnly: assetSource === 'search' });
|
|
222
194
|
if (fs.statSync(tempPath).size > MAX_IMAGE_BYTES) {return original(readImageMetadata(tempPath));}
|
|
@@ -224,20 +196,18 @@ async function resolveOne(candidate, ctx = {}) {
|
|
|
224
196
|
const failure = sizeFailure(downloaded, ctx);
|
|
225
197
|
if (failure) {return { resolved: false, url: raw, source: 'external', reason: failure };}
|
|
226
198
|
Object.assign(common, downloaded, { contentHash: fileHash(tempPath), imageType: downloaded.type });
|
|
227
|
-
verifiedMetadata = { ...common, type: downloaded.type };
|
|
228
199
|
const reused = await reuseDelivery(raw, common, ctx);
|
|
229
200
|
if (reused) {return reused;}
|
|
230
|
-
if (!ctx.canUpload) {return
|
|
201
|
+
if (!ctx.canUpload) {return { resolved: false, url: raw, source: 'external', reason: 'YIDA_ATTACHMENT_UNAVAILABLE' };}
|
|
231
202
|
const results = await ctx.uploadFn([tempPath]);
|
|
232
203
|
const hit = (results || []).find(result => result.success && result.originalPath === tempPath)
|
|
233
204
|
|| (results || []).find(result => result.success);
|
|
234
205
|
if (hit && isHttpUrl(hit.cdnUrl)) {
|
|
235
206
|
return { ...common, resolved: true, url: hit.cdnUrl, source: 'yida-attachment', reason: 'OK', deliveryPolicy };
|
|
236
207
|
}
|
|
237
|
-
return
|
|
208
|
+
return { ...common, resolved: false, url: raw, source: 'external', reason: ((results || [])[0] || {}).error || 'UPLOAD_FAILED' };
|
|
238
209
|
} catch (error) {
|
|
239
210
|
if (error.message === 'ASSET_TOO_LARGE') {return original(error.metadata || {});}
|
|
240
|
-
if (verifiedMetadata) {return original(verifiedMetadata, error.code || error.message || 'UPLOAD_FAILED');}
|
|
241
211
|
return { ...common, resolved: false, url: raw, source: 'external', reason: error.code || error.message || 'UPLOAD_FAILED' };
|
|
242
212
|
} finally {
|
|
243
213
|
if (tempPath) {
|
|
@@ -260,7 +230,6 @@ async function resolveOne(candidate, ctx = {}) {
|
|
|
260
230
|
allowedProviders: Object.keys(ALLOWED_STOCK_IMAGE_PROVIDERS),
|
|
261
231
|
};
|
|
262
232
|
}
|
|
263
|
-
if (!deliveryRights(raw, ctx).rehostAllowed) {return { resolved: false, reason: 'REHOST_NOT_ALLOWED' };}
|
|
264
233
|
const metadata = readImageMetadata(raw);
|
|
265
234
|
const failure = sizeFailure(metadata, ctx);
|
|
266
235
|
if (failure) {
|
|
@@ -289,9 +258,7 @@ async function resolveOne(candidate, ctx = {}) {
|
|
|
289
258
|
}
|
|
290
259
|
}
|
|
291
260
|
|
|
292
|
-
return { resolved: false, url: raw, source: 'unknown', reason: 'NOT_FOUND'
|
|
293
|
-
message: t('asset.localFileUnavailable', path.resolve(raw)),
|
|
294
|
-
details: { input: raw, resolvedPath: path.resolve(raw), baseDir: process.cwd(), pathBase: 'cwd' } };
|
|
261
|
+
return { resolved: false, url: raw, source: 'unknown', reason: 'NOT_FOUND' };
|
|
295
262
|
}
|
|
296
263
|
|
|
297
264
|
function normalizeSource(source, fallback) {
|
|
@@ -317,7 +284,7 @@ function metadataGaps(asset) {
|
|
|
317
284
|
}
|
|
318
285
|
|
|
319
286
|
async function resolveAssets(assets = [], options = {}) {
|
|
320
|
-
const plan = prepareAssetPlan(assets, options.assetStrategy
|
|
287
|
+
const plan = prepareAssetPlan(assets, options.assetStrategy);
|
|
321
288
|
const entries = plan.entries;
|
|
322
289
|
const online = options.online !== false;
|
|
323
290
|
const status = options.status || getAssetStatus({ online, env: options.env, runtime: options.runtime });
|
|
@@ -345,14 +312,10 @@ async function resolveAssets(assets = [], options = {}) {
|
|
|
345
312
|
previous: asset,
|
|
346
313
|
assetSource: source,
|
|
347
314
|
provider: asset.provider,
|
|
348
|
-
hotlinkAllowed: asset.hotlinkAllowed,
|
|
349
|
-
rehostAllowed: asset.rehostAllowed,
|
|
350
|
-
deliveryMode: options.uploadAssets ? 'upload' : asset.deliveryMode,
|
|
351
315
|
online,
|
|
352
316
|
canUpload,
|
|
353
317
|
timeout: options.timeout,
|
|
354
318
|
downloadFn: options.downloadFn,
|
|
355
|
-
verifyFn: options.verifyFn,
|
|
356
319
|
uploadFn,
|
|
357
320
|
...size,
|
|
358
321
|
});
|
|
@@ -371,8 +334,6 @@ async function resolveAssets(assets = [], options = {}) {
|
|
|
371
334
|
slotId,
|
|
372
335
|
pageId: asset.pageId || '',
|
|
373
336
|
input,
|
|
374
|
-
deliveryMode: options.uploadAssets ? 'upload' : asset.deliveryMode || 'auto',
|
|
375
|
-
...Object.fromEntries(['hotlinkAllowed', 'rehostAllowed'].filter(key => asset[key] !== undefined).map(key => [key, asset[key]])),
|
|
376
337
|
...minimumSize(asset),
|
|
377
338
|
...(asset.minSize !== undefined ? { minSize: asset.minSize } : {}),
|
|
378
339
|
usage: asset.usage || '',
|
|
@@ -381,13 +342,9 @@ async function resolveAssets(assets = [], options = {}) {
|
|
|
381
342
|
localPath: isLocalFile(input) ? input : '',
|
|
382
343
|
source,
|
|
383
344
|
provider,
|
|
384
|
-
assetId: asset.assetId || '',
|
|
385
345
|
sourcePage: asset.sourcePage || '',
|
|
386
346
|
creator: asset.creator || '',
|
|
387
347
|
license: asset.license || '',
|
|
388
|
-
licenseUrl: asset.licenseUrl || '',
|
|
389
|
-
licenseCheckedAt: asset.licenseCheckedAt || '',
|
|
390
|
-
authorizationEvidence: [...(asset.authorizationEvidence || [])],
|
|
391
348
|
attribution: asset.attribution || '',
|
|
392
349
|
downloadLocation: asset.downloadLocation || '',
|
|
393
350
|
downloadTracked: asset.downloadTracked === true,
|
|
@@ -406,8 +363,7 @@ async function resolveAssets(assets = [], options = {}) {
|
|
|
406
363
|
slotId: slotId || `slot-${index + 1}`,
|
|
407
364
|
pageId: output.pageId,
|
|
408
365
|
code: result.reason,
|
|
409
|
-
message:
|
|
410
|
-
...(result.details ? { details: result.details } : {}),
|
|
366
|
+
message: `图片未落地:${result.reason}`,
|
|
411
367
|
});
|
|
412
368
|
gaps.push(...output.gaps.map(gap => ({ ...gap })));
|
|
413
369
|
return;
|
|
@@ -445,7 +401,6 @@ async function resolveAssets(assets = [], options = {}) {
|
|
|
445
401
|
capabilityEvidence: {
|
|
446
402
|
host: status.hostCapabilities,
|
|
447
403
|
cdnConfigured: status.cdnConfigured,
|
|
448
|
-
deliveryPreference: 'verified-direct-url',
|
|
449
404
|
uploadTarget: 'yida-attachment',
|
|
450
405
|
maxUploadBytes: MAX_IMAGE_BYTES,
|
|
451
406
|
online,
|
|
@@ -41,8 +41,8 @@ function getAssetStatus(options = {}) {
|
|
|
41
41
|
|
|
42
42
|
let recommendedStrategy;
|
|
43
43
|
if (online) {
|
|
44
|
-
recommendedStrategy = STRATEGY.
|
|
45
|
-
reasons.push('
|
|
44
|
+
recommendedStrategy = STRATEGY.YIDA_ATTACHMENT;
|
|
45
|
+
reasons.push('图片默认上传宜搭附件,需要登录态和 appType;超过 20 MiB 的外链保留原链接。');
|
|
46
46
|
} else {
|
|
47
47
|
recommendedStrategy = STRATEGY.LOW_FIDELITY;
|
|
48
48
|
reasons.push('离线模式不校验外链,也不上传图片。');
|
|
@@ -56,9 +56,8 @@ function getAssetStatus(options = {}) {
|
|
|
56
56
|
cdnConfigured: valid,
|
|
57
57
|
canUpload: online,
|
|
58
58
|
uploadTarget: 'yida-attachment',
|
|
59
|
-
requiresLogin:
|
|
60
|
-
requiresAppType:
|
|
61
|
-
uploadRequirements: { requiresLogin: true, requiresAppType: true },
|
|
59
|
+
requiresLogin: true,
|
|
60
|
+
requiresAppType: true,
|
|
62
61
|
canGenerate: generator.available,
|
|
63
62
|
hostCapabilities,
|
|
64
63
|
recommendedStrategy,
|
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const { getAssetExecutionContract, selectAssetExecutionMode } = require('./asset-execution');
|
|
4
|
-
|
|
5
3
|
const CAPABILITY_ENV = Object.freeze({
|
|
6
4
|
onlineSearch: 'OPENYIDA_AGENT_ONLINE_SEARCH',
|
|
7
5
|
imageSearch: 'OPENYIDA_AGENT_IMAGE_SEARCH',
|
|
8
6
|
imageGeneration: 'OPENYIDA_AGENT_IMAGE_GENERATION',
|
|
9
|
-
backgroundAgent: 'OPENYIDA_AGENT_BACKGROUND_AGENT',
|
|
10
|
-
backgroundShell: 'OPENYIDA_AGENT_BACKGROUND_SHELL',
|
|
11
7
|
});
|
|
12
8
|
|
|
13
9
|
function readDeclaredCapability(value) {
|
|
@@ -67,9 +63,7 @@ function buildHostAssetCapabilities(options = {}) {
|
|
|
67
63
|
const onlineSearch = resolveCapability(env[CAPABILITY_ENV.onlineSearch], searchDefault());
|
|
68
64
|
const imageSearch = resolveCapability(env[CAPABILITY_ENV.imageSearch], searchDefault());
|
|
69
65
|
const imageGeneration = resolveCapability(env[CAPABILITY_ENV.imageGeneration], generationDefault);
|
|
70
|
-
const
|
|
71
|
-
const backgroundShell = readDeclaredCapability(env[CAPABILITY_ENV.backgroundShell]);
|
|
72
|
-
const capabilities = [onlineSearch, imageSearch, imageGeneration, backgroundAgent, backgroundShell];
|
|
66
|
+
const capabilities = [onlineSearch, imageSearch, imageGeneration];
|
|
73
67
|
|
|
74
68
|
return {
|
|
75
69
|
schema_version: 1,
|
|
@@ -82,13 +76,10 @@ function buildHostAssetCapabilities(options = {}) {
|
|
|
82
76
|
online_search: onlineSearch,
|
|
83
77
|
image_search: imageSearch,
|
|
84
78
|
image_generation: imageGeneration,
|
|
85
|
-
background_agent: backgroundAgent,
|
|
86
|
-
background_shell: backgroundShell,
|
|
87
|
-
execution: { selected_mode: selectAssetExecutionMode(backgroundAgent, backgroundShell), ...getAssetExecutionContract() },
|
|
88
79
|
requires_host_tool_inventory_check: capabilities.some(item => item.status === 'unknown'),
|
|
89
80
|
declaration_env: { ...CAPABILITY_ENV },
|
|
90
81
|
detection_policy:
|
|
91
|
-
'Explicit declarations override runtime defaults. QwenWork provides image generation, and verified non-cloud runtimes provide online and image search. Managed-cloud or unknown runtimes use the host tool inventory.
|
|
82
|
+
'Explicit declarations override runtime defaults. QwenWork provides image generation, and verified non-cloud runtimes provide online and image search. Managed-cloud or unknown runtimes use the host tool inventory.',
|
|
92
83
|
};
|
|
93
84
|
}
|
|
94
85
|
|
package/lib/asset/url-verify.js
CHANGED
|
@@ -43,12 +43,6 @@ function requestOnce(rawUrl, options = {}) {
|
|
|
43
43
|
...(method === 'GET' ? { Range: `bytes=0-${MAX_HEADER_BYTES - 1}` } : {}),
|
|
44
44
|
},
|
|
45
45
|
}, (res) => {
|
|
46
|
-
// Redirects and denied requests need no response-body download.
|
|
47
|
-
if (res.statusCode < 200 || res.statusCode >= 300) {
|
|
48
|
-
resolve({ status: res.statusCode || 0, headers: res.headers || {}, body: Buffer.alloc(0) });
|
|
49
|
-
res.destroy();
|
|
50
|
-
return;
|
|
51
|
-
}
|
|
52
46
|
if (method !== 'GET') {
|
|
53
47
|
resolve({ status: res.statusCode || 0, headers: res.headers || {}, body: Buffer.alloc(0) });
|
|
54
48
|
return;
|
|
@@ -78,10 +72,7 @@ function requestOnce(rawUrl, options = {}) {
|
|
|
78
72
|
});
|
|
79
73
|
res.on('end', finish);
|
|
80
74
|
res.on('error', reject);
|
|
81
|
-
res.on('aborted', () => reject(new Error('DOWNLOAD_ABORTED')));
|
|
82
75
|
});
|
|
83
|
-
const timer = setTimeout(() => req.destroy(new Error('TIMEOUT')), timeout);
|
|
84
|
-
req.on('close', () => clearTimeout(timer));
|
|
85
76
|
req.setTimeout(timeout, () => req.destroy(new Error('TIMEOUT')));
|
|
86
77
|
req.on('error', reject);
|
|
87
78
|
req.end();
|
|
@@ -89,12 +80,8 @@ function requestOnce(rawUrl, options = {}) {
|
|
|
89
80
|
}
|
|
90
81
|
|
|
91
82
|
async function requestFollow(rawUrl, options, depth = 0) {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
if (timeout <= 0) {throw new Error('TIMEOUT');}
|
|
95
|
-
const response = await requestOnce(rawUrl, { ...options, timeout });
|
|
96
|
-
if (response.status >= 300 && response.status < 400 && response.headers.location) {
|
|
97
|
-
if (depth >= MAX_REDIRECTS) {throw new Error('TOO_MANY_REDIRECTS');}
|
|
83
|
+
const response = await requestOnce(rawUrl, options);
|
|
84
|
+
if (response.status >= 300 && response.status < 400 && response.headers.location && depth < MAX_REDIRECTS) {
|
|
98
85
|
const next = new URL(response.headers.location, rawUrl).toString();
|
|
99
86
|
return requestFollow(next, options, depth + 1);
|
|
100
87
|
}
|
|
@@ -121,9 +108,7 @@ async function verifyImageUrl(rawUrl, options = {}) {
|
|
|
121
108
|
|
|
122
109
|
let response;
|
|
123
110
|
try {
|
|
124
|
-
response = await requestFollow(trimmed, {
|
|
125
|
-
method: 'GET', deadline: Date.now() + (options.timeout || DEFAULT_TIMEOUT), allowUrl: options.allowUrl,
|
|
126
|
-
});
|
|
111
|
+
response = await requestFollow(trimmed, { method: 'GET', timeout: options.timeout });
|
|
127
112
|
} catch (error) {
|
|
128
113
|
return { ...base, url: trimmed, reason: normalizeError(error) };
|
|
129
114
|
}
|
|
@@ -180,7 +165,7 @@ async function verifyImageUrls(urls, options = {}) {
|
|
|
180
165
|
|
|
181
166
|
function normalizeError(error) {
|
|
182
167
|
const message = (error && error.message) || String(error);
|
|
183
|
-
if (['TIMEOUT', 'INVALID_URL', 'UNSUPPORTED_PROTOCOL'
|
|
168
|
+
if (['TIMEOUT', 'INVALID_URL', 'UNSUPPORTED_PROTOCOL'].includes(message)) {return message;}
|
|
184
169
|
return (error && error.code) || 'REQUEST_FAILED';
|
|
185
170
|
}
|
|
186
171
|
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { CliError } = require('../core/cli-error');
|
|
4
|
+
const { DINGTALK_OAUTH_CLIENT_ID } = require('../core/env-manager');
|
|
5
|
+
const { requestJson, resolveTokenBaseUrl, normalizeTokenResponse } = require('./token-auth');
|
|
6
|
+
const { isEnvTokenAuthMode, hasEnvTokenCredential, saveDwsTokenSession } = require('./token-store');
|
|
7
|
+
|
|
8
|
+
const FAILURE_STATUSES = new Set([
|
|
9
|
+
'dws_not_enabled', 'invalid_dws_token', 'unsupported_dws_source', 'insufficient_dws_scope',
|
|
10
|
+
'dws_identity_unmapped', 'dws_upstream_unavailable', 'dws_policy_rejected', 'dws_session_expired',
|
|
11
|
+
]);
|
|
12
|
+
|
|
13
|
+
function failure(reason) {
|
|
14
|
+
return new CliError(`DWS sync failed: ${reason}`, {
|
|
15
|
+
code: 'DWS_SYNC_FAILED', details: { reason },
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function readHandoff(input = process.stdin, timeoutMs = 10000) {
|
|
20
|
+
return new Promise((resolve, reject) => {
|
|
21
|
+
if (input.isTTY) { reject(failure('STDIN_REQUIRED')); return; }
|
|
22
|
+
let chunks = [];
|
|
23
|
+
let size = 0;
|
|
24
|
+
const finish = (error, value) => {
|
|
25
|
+
clearTimeout(timer);
|
|
26
|
+
input.removeListener('data', onData);
|
|
27
|
+
input.removeListener('end', onEnd);
|
|
28
|
+
input.removeListener('error', onError);
|
|
29
|
+
input.pause();
|
|
30
|
+
chunks = [];
|
|
31
|
+
if (error) { reject(error); } else { resolve(value); }
|
|
32
|
+
};
|
|
33
|
+
const onData = (chunk) => {
|
|
34
|
+
const bytes = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
35
|
+
size += bytes.length;
|
|
36
|
+
if (size > 16384) { finish(failure('HANDOFF_TOO_LARGE')); return; }
|
|
37
|
+
chunks.push(bytes);
|
|
38
|
+
};
|
|
39
|
+
const onError = () => finish(failure('HANDOFF_READ_FAILED'));
|
|
40
|
+
const onEnd = () => {
|
|
41
|
+
let data;
|
|
42
|
+
try { data = JSON.parse(Buffer.concat(chunks).toString('utf8')); } catch {
|
|
43
|
+
finish(failure('INVALID_HANDOFF')); return;
|
|
44
|
+
}
|
|
45
|
+
if (!data || typeof data !== 'object' || Array.isArray(data)
|
|
46
|
+
|| Object.keys(data).some((key) => !['accessToken', 'corpId', 'environment'].includes(key))
|
|
47
|
+
|| typeof data.accessToken !== 'string' || !data.accessToken || data.accessToken.length > 8192
|
|
48
|
+
|| (/\s/.test(data.accessToken) || [...data.accessToken].some((char) => char.charCodeAt(0) < 32 || char.charCodeAt(0) === 127))
|
|
49
|
+
|| typeof data.corpId !== 'string' || !data.corpId || data.corpId.length > 256
|
|
50
|
+
|| !['pre', 'prod'].includes(data.environment)) {
|
|
51
|
+
finish(failure('INVALID_HANDOFF')); return;
|
|
52
|
+
}
|
|
53
|
+
finish(null, data);
|
|
54
|
+
};
|
|
55
|
+
const timer = setTimeout(() => finish(failure('HANDOFF_TIMEOUT')), timeoutMs);
|
|
56
|
+
input.on('data', onData);
|
|
57
|
+
input.on('end', onEnd);
|
|
58
|
+
input.on('error', onError);
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async function syncDws(options = {}) {
|
|
63
|
+
const env = options.env || process.env;
|
|
64
|
+
if (isEnvTokenAuthMode(env) || hasEnvTokenCredential(env)
|
|
65
|
+
|| options.authProfile || options.profile || options.userId
|
|
66
|
+
|| env.OPENYIDA_AUTH_PROFILE || env.OPENYIDA_AUTH_USER_ID || env.OPENYIDA_AUTH_CORP_ID) {
|
|
67
|
+
throw failure('AUTH_OVERRIDE_CONFLICT');
|
|
68
|
+
}
|
|
69
|
+
const clientId = options.clientId || env.OPENYIDA_DINGTALK_CLIENT_ID || DINGTALK_OAUTH_CLIENT_ID;
|
|
70
|
+
if (clientId !== DINGTALK_OAUTH_CLIENT_ID) { throw failure('CLIENT_CONFLICT'); }
|
|
71
|
+
const baseUrl = resolveTokenBaseUrl(options);
|
|
72
|
+
const target = new URL(baseUrl);
|
|
73
|
+
if (target.protocol !== 'https:' || target.username || target.password || target.search || target.hash
|
|
74
|
+
|| target.pathname !== '/') { throw failure('TRUSTED_HTTPS_ORIGIN_REQUIRED'); }
|
|
75
|
+
const handoff = await readHandoff(options.input || process.stdin, options.stdinTimeoutMs);
|
|
76
|
+
if (options.corpId && options.corpId !== handoff.corpId) { throw failure('ORG_CONFLICT'); }
|
|
77
|
+
let response;
|
|
78
|
+
try {
|
|
79
|
+
response = await requestJson('POST', `${target.origin}/openapi/cli/v1/auth/dws/token`, {
|
|
80
|
+
corpId: handoff.corpId, environment: handoff.environment,
|
|
81
|
+
}, { Authorization: `Bearer ${handoff.accessToken}` }, { timeoutMs: 30000, maxResponseBytes: 65536 });
|
|
82
|
+
} catch {
|
|
83
|
+
throw failure('EXCHANGE_REQUEST_FAILED');
|
|
84
|
+
} finally {
|
|
85
|
+
delete handoff.accessToken;
|
|
86
|
+
}
|
|
87
|
+
const data = response && (response.data || response.content || response);
|
|
88
|
+
if (!data || response.success === false || response.ok === false || data.status !== 'ok') {
|
|
89
|
+
throw failure(data && FAILURE_STATUSES.has(data.status) ? data.status : 'EXCHANGE_REJECTED');
|
|
90
|
+
}
|
|
91
|
+
const session = normalizeTokenResponse(response, baseUrl, clientId);
|
|
92
|
+
const now = Date.now() / 1000;
|
|
93
|
+
if (typeof data.access_token !== 'string' || !data.access_token
|
|
94
|
+
|| typeof data.refresh_token !== 'string' || !data.refresh_token
|
|
95
|
+
|| data.client_id !== DINGTALK_OAUTH_CLIENT_ID || data.corp_id !== handoff.corpId
|
|
96
|
+
|| typeof data.user_id !== 'string' || !data.user_id
|
|
97
|
+
|| data.credential_source !== 'dws' || data.environment !== handoff.environment
|
|
98
|
+
|| typeof data.session_id !== 'string' || !/^[a-f0-9-]{36}$/.test(data.session_id)
|
|
99
|
+
|| !Number.isInteger(data.session_expires_at) || data.session_expires_at <= now
|
|
100
|
+
|| !Number.isInteger(data.expires_in) || data.expires_in <= 0
|
|
101
|
+
|| data.expires_in > data.session_expires_at - now + 2) {
|
|
102
|
+
throw failure('INVALID_EXCHANGE_RESPONSE');
|
|
103
|
+
}
|
|
104
|
+
const business = new URL(session.base_url);
|
|
105
|
+
// Token-bearing business requests must remain within this configured environment.
|
|
106
|
+
if (business.origin !== target.origin) { throw failure('ENVIRONMENT_MISMATCH'); }
|
|
107
|
+
delete session.raw;
|
|
108
|
+
delete session.message;
|
|
109
|
+
session.auth_base_url = target.origin;
|
|
110
|
+
session.credential_source = 'dws';
|
|
111
|
+
session.session_id = data.session_id;
|
|
112
|
+
session.session_expires_at = data.session_expires_at;
|
|
113
|
+
session.environment = data.environment;
|
|
114
|
+
let saved;
|
|
115
|
+
try {
|
|
116
|
+
saved = saveDwsTokenSession(session, options);
|
|
117
|
+
} catch {
|
|
118
|
+
// Best-effort revoke only the newly issued session; never touch an existing profile.
|
|
119
|
+
try {
|
|
120
|
+
await requestJson('POST', `${target.origin}/openapi/cli/v1/auth/logout`, {
|
|
121
|
+
refreshToken: session.refresh_token,
|
|
122
|
+
}, { Authorization: `Bearer ${session.access_token}` }, { timeoutMs: 10000, maxResponseBytes: 65536 });
|
|
123
|
+
} catch { /* The fixed server deadline still bounds an unreachable session. */ }
|
|
124
|
+
throw failure('PERSISTENCE_FAILED');
|
|
125
|
+
}
|
|
126
|
+
return { ok: true, status: 'ready', auth_profile: saved.auth_profile,
|
|
127
|
+
corp_id: saved.corp_id, user_id: saved.user_id };
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
async function run(args, options = {}) {
|
|
131
|
+
if (args.length === 2 && args.includes('--capabilities') && args.includes('--json')) {
|
|
132
|
+
const capabilities = { protocol_version: 1, private_stdin: true, independent_profile: true };
|
|
133
|
+
console.log(JSON.stringify(capabilities));
|
|
134
|
+
return capabilities;
|
|
135
|
+
}
|
|
136
|
+
if (!args.includes('--stdin') || !args.includes('--json')
|
|
137
|
+
|| args.some((arg) => !['--stdin', '--json'].includes(arg))) {
|
|
138
|
+
throw failure('USE_AUTH_SYNC_DWS_STDIN_JSON');
|
|
139
|
+
}
|
|
140
|
+
const result = await syncDws(options);
|
|
141
|
+
console.log(JSON.stringify(result));
|
|
142
|
+
return result;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
module.exports = { run, syncDws, readHandoff };
|
package/lib/auth/token-auth.js
CHANGED
|
@@ -25,6 +25,7 @@ const {
|
|
|
25
25
|
resolveTokenSession,
|
|
26
26
|
saveBusinessContext,
|
|
27
27
|
saveTokenSession,
|
|
28
|
+
saveDwsTokenSession,
|
|
28
29
|
} = require('./token-store');
|
|
29
30
|
|
|
30
31
|
const DEFAULT_AUTH_PATH_PREFIX = '/openapi/cli/v1/auth';
|
|
@@ -83,7 +84,7 @@ function resolveDingtalkLoginOrigin(options = {}) {
|
|
|
83
84
|
return DINGTALK_LOGIN_ORIGIN;
|
|
84
85
|
}
|
|
85
86
|
|
|
86
|
-
function requestJson(method, url, body, headers = {}) {
|
|
87
|
+
function requestJson(method, url, body, headers = {}, options = {}) {
|
|
87
88
|
return new Promise((resolve, reject) => {
|
|
88
89
|
const parsedUrl = new URL(url);
|
|
89
90
|
const data = body === undefined || body === null ? null : Buffer.from(JSON.stringify(body));
|
|
@@ -104,7 +105,16 @@ function requestJson(method, url, body, headers = {}) {
|
|
|
104
105
|
},
|
|
105
106
|
}, (response) => {
|
|
106
107
|
const chunks = [];
|
|
107
|
-
|
|
108
|
+
let size = 0;
|
|
109
|
+
response.on('error', reject);
|
|
110
|
+
response.on('data', (chunk) => {
|
|
111
|
+
size += chunk.length;
|
|
112
|
+
if (options.maxResponseBytes && size > options.maxResponseBytes) {
|
|
113
|
+
response.destroy(new Error('response_too_large'));
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
chunks.push(chunk);
|
|
117
|
+
});
|
|
108
118
|
response.on('end', () => {
|
|
109
119
|
const text = Buffer.concat(chunks).toString('utf8');
|
|
110
120
|
let payload = null;
|
|
@@ -132,6 +142,10 @@ function requestJson(method, url, body, headers = {}) {
|
|
|
132
142
|
});
|
|
133
143
|
});
|
|
134
144
|
|
|
145
|
+
if (options.timeoutMs > 0) {
|
|
146
|
+
const timer = setTimeout(() => request.destroy(new Error('request_timeout')), options.timeoutMs);
|
|
147
|
+
request.once('close', () => clearTimeout(timer));
|
|
148
|
+
}
|
|
135
149
|
request.on('error', reject);
|
|
136
150
|
if (data) {
|
|
137
151
|
request.write(data);
|
|
@@ -368,6 +382,16 @@ function tokenStatus(options = {}) {
|
|
|
368
382
|
}
|
|
369
383
|
return status;
|
|
370
384
|
}
|
|
385
|
+
if (session.credential_source === 'dws'
|
|
386
|
+
&& (!Number.isInteger(session.session_expires_at) || session.session_expires_at <= Date.now() / 1000)) {
|
|
387
|
+
return {
|
|
388
|
+
ok: false, status: 'expired', can_auto_use: false, auth_mode: 'token',
|
|
389
|
+
auth_source: session.auth_source, auth_store: session.auth_store, auth_profile: session.auth_profile,
|
|
390
|
+
base_url: session.base_url, credential_source: 'dws', environment: session.environment,
|
|
391
|
+
session_expires_at: session.session_expires_at, corp_id: session.corp_id, user_id: session.user_id,
|
|
392
|
+
failure_reason: 'dws_session_expired',
|
|
393
|
+
};
|
|
394
|
+
}
|
|
371
395
|
if (!session.access_token && session.refresh_token) {
|
|
372
396
|
return {
|
|
373
397
|
ok: true,
|
|
@@ -381,6 +405,9 @@ function tokenStatus(options = {}) {
|
|
|
381
405
|
status: 'ok',
|
|
382
406
|
can_auto_use: true,
|
|
383
407
|
refresh_token: maskToken(session.refresh_token),
|
|
408
|
+
credential_source: session.credential_source,
|
|
409
|
+
environment: session.environment,
|
|
410
|
+
session_expires_at: session.session_expires_at,
|
|
384
411
|
base_url: session.base_url,
|
|
385
412
|
corp_id: session.corp_id,
|
|
386
413
|
corp_name: session.corp_name,
|
|
@@ -404,6 +431,9 @@ function tokenStatus(options = {}) {
|
|
|
404
431
|
access_token: maskToken(session.access_token),
|
|
405
432
|
refresh_token: maskToken(session.refresh_token),
|
|
406
433
|
expires_at: session.expires_at ? new Date(session.expires_at).toISOString() : undefined,
|
|
434
|
+
credential_source: session.credential_source,
|
|
435
|
+
environment: session.environment,
|
|
436
|
+
session_expires_at: session.session_expires_at,
|
|
407
437
|
base_url: session.base_url,
|
|
408
438
|
corp_id: session.corp_id,
|
|
409
439
|
corp_name: session.corp_name,
|
|
@@ -427,17 +457,31 @@ async function tokenRefresh(options = {}) {
|
|
|
427
457
|
// Token exchange follows the active environment endpoint, just like the
|
|
428
458
|
// legacy login bootstrap. The response may return a different corp base_url
|
|
429
459
|
// for business requests and generated application links.
|
|
430
|
-
const authBaseUrlValue = resolveTokenBaseUrl({
|
|
460
|
+
const authBaseUrlValue = (session.credential_source === 'dws' && session.auth_base_url) || resolveTokenBaseUrl({
|
|
431
461
|
projectRoot: options.projectRoot,
|
|
432
462
|
endpoint: options.endpoint,
|
|
433
463
|
});
|
|
434
464
|
const authBaseUrl = appendPath(authBaseUrlValue, DEFAULT_AUTH_PATH_PREFIX);
|
|
435
465
|
const clientId = options.clientId || session.client_id || DINGTALK_OAUTH_CLIENT_ID;
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
466
|
+
let response;
|
|
467
|
+
if (session.credential_source === 'dws') {
|
|
468
|
+
try {
|
|
469
|
+
response = await requestJson('POST', appendPath(authBaseUrl, '/refresh'), {
|
|
470
|
+
refreshToken: session.refresh_token, clientId,
|
|
471
|
+
}, {}, { timeoutMs: 30000, maxResponseBytes: 65536 });
|
|
472
|
+
requireOkResponse(response);
|
|
473
|
+
} catch (error) {
|
|
474
|
+
if (isRefreshAuthRequired(error)) {
|
|
475
|
+
throw createTokenAuthRequiredError('invalid_refresh_token');
|
|
476
|
+
}
|
|
477
|
+
throw createTokenRefreshFailedError('refresh_request_failed');
|
|
478
|
+
}
|
|
479
|
+
} else {
|
|
480
|
+
response = await requestJson('POST', appendPath(authBaseUrl, '/refresh'), {
|
|
481
|
+
refreshToken: session.refresh_token, clientId,
|
|
482
|
+
});
|
|
483
|
+
requireOkResponse(response);
|
|
484
|
+
}
|
|
441
485
|
const data = getTokenPayload(response);
|
|
442
486
|
const responseBusinessBaseUrl = data.base_url || data.baseUrl;
|
|
443
487
|
const businessBaseUrl = session.base_url || authBaseUrlValue;
|
|
@@ -450,6 +494,9 @@ async function tokenRefresh(options = {}) {
|
|
|
450
494
|
refresh_token: data.refresh_token || data.refreshToken || session.refresh_token,
|
|
451
495
|
}, businessBaseUrl, clientId);
|
|
452
496
|
if (!normalized.access_token) {
|
|
497
|
+
if (session.credential_source === 'dws') {
|
|
498
|
+
throw createTokenRefreshFailedError('refresh_response_invalid');
|
|
499
|
+
}
|
|
453
500
|
return {
|
|
454
501
|
...normalized,
|
|
455
502
|
ok: false,
|
|
@@ -458,6 +505,23 @@ async function tokenRefresh(options = {}) {
|
|
|
458
505
|
message: normalized.message || 'auth service did not return access_token',
|
|
459
506
|
};
|
|
460
507
|
}
|
|
508
|
+
if (session.credential_source === 'dws') {
|
|
509
|
+
if (normalized.corp_id !== session.corp_id || normalized.user_id !== session.user_id
|
|
510
|
+
|| data.client_id !== session.client_id || data.credential_source !== 'dws'
|
|
511
|
+
|| data.session_id !== session.session_id || data.session_expires_at !== session.session_expires_at
|
|
512
|
+
|| data.environment !== session.environment || !data.refresh_token
|
|
513
|
+
|| normalized.base_url !== session.base_url
|
|
514
|
+
|| !Number.isInteger(data.expires_in) || data.expires_in <= 0
|
|
515
|
+
|| data.expires_in > session.session_expires_at - Date.now() / 1000 + 2) {
|
|
516
|
+
throw createTokenAuthRequiredError('dws_refresh_identity_mismatch');
|
|
517
|
+
}
|
|
518
|
+
delete normalized.raw;
|
|
519
|
+
delete normalized.message;
|
|
520
|
+
return saveDwsTokenSession({ ...normalized, credential_source: 'dws',
|
|
521
|
+
auth_base_url: session.auth_base_url, session_id: session.session_id,
|
|
522
|
+
session_expires_at: session.session_expires_at, environment: session.environment,
|
|
523
|
+
}, { ...options, bindProject: false });
|
|
524
|
+
}
|
|
461
525
|
if (responseBusinessBaseUrl && normalized.corp_id) {
|
|
462
526
|
try {
|
|
463
527
|
saveBusinessContext({
|
|
@@ -492,7 +556,7 @@ async function revokeTokenSession(session) {
|
|
|
492
556
|
return;
|
|
493
557
|
}
|
|
494
558
|
try {
|
|
495
|
-
await requestJson('POST', appendPath(appendPath(session.base_url, DEFAULT_AUTH_PATH_PREFIX), '/logout'), {
|
|
559
|
+
await requestJson('POST', appendPath(appendPath(session.auth_base_url || session.base_url, DEFAULT_AUTH_PATH_PREFIX), '/logout'), {
|
|
496
560
|
refreshToken: session.refresh_token,
|
|
497
561
|
}, session.access_token ? {
|
|
498
562
|
authorization: `${session.token_type || 'Bearer'} ${session.access_token}`,
|
package/lib/auth/token-store.js
CHANGED
|
@@ -290,6 +290,11 @@ function normalizeTokenSession(session = {}) {
|
|
|
290
290
|
open_user_id: session.open_user_id || session.openUserId,
|
|
291
291
|
scope: session.scope,
|
|
292
292
|
last_used_at: session.last_used_at || session.lastUsedAt || session.updated_at || session.updatedAt,
|
|
293
|
+
credential_source: session.credential_source,
|
|
294
|
+
session_id: session.session_id,
|
|
295
|
+
session_expires_at: session.session_expires_at,
|
|
296
|
+
environment: session.environment,
|
|
297
|
+
auth_base_url: session.auth_base_url,
|
|
293
298
|
auth_source: session.auth_source || session.authSource,
|
|
294
299
|
auth_store: session.auth_store || session.authStore,
|
|
295
300
|
auth_profile: session.auth_profile || session.authProfile,
|
|
@@ -594,6 +599,50 @@ function saveUserTokenSession(session, options = {}) {
|
|
|
594
599
|
return profileSession;
|
|
595
600
|
}
|
|
596
601
|
|
|
602
|
+
function saveDwsTokenSession(session, options = {}) {
|
|
603
|
+
const normalized = normalizeTokenSession(session);
|
|
604
|
+
const identity = buildAuthProfileIdentity(normalized);
|
|
605
|
+
if (!identity || normalized.credential_source !== 'dws' || !normalized.session_id
|
|
606
|
+
|| !normalized.auth_base_url || !normalized.refresh_token) {
|
|
607
|
+
throw new Error('dws_profile_incomplete');
|
|
608
|
+
}
|
|
609
|
+
// Isolate independent login and each server-side DWS session, including same user/corp.
|
|
610
|
+
const authProfile = crypto.createHash('sha256').update(JSON.stringify({
|
|
611
|
+
...identity, credential_source: 'dws', session_id: normalized.session_id,
|
|
612
|
+
auth_base_url: normalized.auth_base_url,
|
|
613
|
+
})).digest('hex').slice(0, 32);
|
|
614
|
+
const saved = normalizeTokenSession({ ...normalized, auth_profile: authProfile,
|
|
615
|
+
auth_source: 'user_profile', auth_store: 'user', persistence_scope: 'user' });
|
|
616
|
+
const file = getUserProfileFilePath(authProfile, options);
|
|
617
|
+
const previous = fs.existsSync(file) ? JSON.parse(fs.readFileSync(file, 'utf8')) : null;
|
|
618
|
+
const previousPointer = loadAuthProfilePointer(options);
|
|
619
|
+
try {
|
|
620
|
+
writeJsonFileAtomic(file, saved);
|
|
621
|
+
const readback = loadUserProfileFile(authProfile, options);
|
|
622
|
+
if (!readback || readback.access_token !== saved.access_token || readback.refresh_token !== saved.refresh_token) {
|
|
623
|
+
throw new Error('dws_profile_readback_failed');
|
|
624
|
+
}
|
|
625
|
+
// Refresh a pinned running command's session without switching a newer project binding.
|
|
626
|
+
if (options.bindProject !== false) {
|
|
627
|
+
saveAuthProfilePointer(saved, options);
|
|
628
|
+
const effective = loadTokenSession(options);
|
|
629
|
+
if (!effective || effective.auth_profile !== authProfile || effective.access_token !== saved.access_token) {
|
|
630
|
+
throw new Error('dws_binding_readback_failed');
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
return saved;
|
|
634
|
+
} catch (error) {
|
|
635
|
+
const current = loadAuthProfilePointer(options);
|
|
636
|
+
if (current && current.auth_profile === authProfile) {
|
|
637
|
+
if (previousPointer) { writeJsonFileAtomic(getAuthProfilePointerFilePath(options), previousPointer); }
|
|
638
|
+
else { clearAuthProfilePointer(options); }
|
|
639
|
+
}
|
|
640
|
+
if (previous) { writeJsonFileAtomic(file, previous); }
|
|
641
|
+
else if (fs.existsSync(file)) { fs.unlinkSync(file); }
|
|
642
|
+
throw error;
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
|
|
597
646
|
function saveTokenSession(session, options = {}) {
|
|
598
647
|
const env = options.env || process.env;
|
|
599
648
|
const normalized = normalizeTokenSession(session);
|
|
@@ -995,6 +1044,7 @@ module.exports = {
|
|
|
995
1044
|
loadTokenSession,
|
|
996
1045
|
resolveTokenSession,
|
|
997
1046
|
saveTokenSession,
|
|
1047
|
+
saveDwsTokenSession,
|
|
998
1048
|
loadBusinessContext,
|
|
999
1049
|
saveBusinessContext,
|
|
1000
1050
|
loadAuthProfilePointer,
|