cardbuddy 0.11.0 → 0.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +46 -5
- package/dist/cli.js +51 -1
- package/dist/config.js +51 -16
- package/dist/core.js +2 -2
- package/dist/init.js +425 -0
- package/dist/preview-editor/assets/index-CHuGGIR3.js +276 -0
- package/dist/preview-editor/assets/{index-B4VZgNSK.css → index-CQScMhyg.css} +1 -1
- package/dist/preview-editor/index.html +2 -2
- package/dist/standalone.js +8 -0
- package/dist/vendored/mcp/client.mjs +537 -0
- package/dist/vendored/mcp/tools.mjs +268 -0
- package/dist/vendored/preview/assets/default-image.png +0 -0
- package/dist/{server-core → vendored/server/core}/ai-chat.js +82 -38
- package/dist/{server-core → vendored/server/core}/browser.js +1 -1
- package/dist/{server-core → vendored/server/core}/cardSave.js +8 -3
- package/dist/{server-core → vendored/server/core}/cardSync.js +6 -14
- package/dist/{server-core → vendored/server/core}/cardValidate.js +11 -8
- package/dist/{server-core → vendored/server/core}/index.js +5 -3
- package/dist/vendored/server/core/skillPaths.js +124 -0
- package/dist/{server-core → vendored/server/core}/sliceUpload.js +5 -8
- package/dist/vendored/skills/README.md +93 -0
- package/dist/vendored/skills/card-doctor/SKILL.md +52 -0
- package/dist/vendored/skills/card-skill-trainer/SKILL.md +312 -0
- package/dist/vendored/skills/card-skill-trainer/baselines/v3-r038n.json +43 -0
- package/dist/vendored/skills/card-skill-trainer/baselines/v3-r039n.json +47 -0
- package/dist/vendored/skills/card-skill-trainer/baselines/v3-r050n.json +48 -0
- package/dist/vendored/skills/card-skill-trainer/scripts/build_dataset.py +472 -0
- package/dist/vendored/skills/card-skill-trainer/scripts/build_sample.mjs +162 -0
- package/dist/vendored/skills/card-skill-trainer/scripts/diagnose_case.mjs +241 -0
- package/dist/vendored/skills/card-skill-trainer/scripts/lib/scoring.mjs +525 -0
- package/dist/vendored/skills/card-skill-trainer/scripts/round_diff.mjs +133 -0
- package/dist/vendored/skills/card-skill-trainer/scripts/run_round.mjs +1043 -0
- package/dist/vendored/skills/card-skill-trainer/scripts/score_round.mjs +195 -0
- package/dist/vendored/skills/card-skill-trainer/scripts/test_missing_slice.py +122 -0
- package/dist/vendored/skills/dsl-card-ai/SKILL.md +191 -0
- package/dist/vendored/skills/dsl-card-generator/SKILL.md +480 -0
- package/dist/vendored/skills/dsl-card-generator/config/render-contract.json +11 -0
- package/dist/vendored/skills/dsl-card-generator/references/amount_formatting.md +138 -0
- package/dist/vendored/skills/dsl-card-generator/references/card_index.json +1597 -0
- package/dist/vendored/skills/dsl-card-generator/references/card_templates.json +384 -0
- package/dist/vendored/skills/dsl-card-generator/references/card_to_summary.md +56 -0
- package/dist/vendored/skills/dsl-card-generator/references/component_rules.md +76 -0
- package/dist/vendored/skills/dsl-card-generator/references/component_specs.md +393 -0
- package/dist/vendored/skills/dsl-card-generator/references/dsl_structure.md +208 -0
- package/dist/vendored/skills/dsl-card-generator/references/event_patterns.md +287 -0
- package/dist/vendored/skills/dsl-card-generator/references/expression_engine.md +121 -0
- package/dist/vendored/skills/dsl-card-generator/references/field_name_dict.json +34 -0
- package/dist/vendored/skills/dsl-card-generator/references/field_naming.md +101 -0
- package/dist/vendored/skills/dsl-card-generator/references/generation_pitfalls.md +928 -0
- package/dist/vendored/skills/dsl-card-generator/references/handcrafted/README.md +69 -0
- package/dist/vendored/skills/dsl-card-generator/references/handcrafted/examples_index.json +2967 -0
- package/dist/vendored/skills/dsl-card-generator/references/handcrafted/patterns.json +2835 -0
- package/dist/vendored/skills/dsl-card-generator/references/handcrafted/recipes_v2.json +648 -0
- package/dist/vendored/skills/dsl-card-generator/references/handcrafted/recipes_v2_schema.md +295 -0
- package/dist/vendored/skills/dsl-card-generator/references/image_diff.md +48 -0
- package/dist/vendored/skills/dsl-card-generator/references/image_to_card.md +129 -0
- package/dist/vendored/skills/dsl-card-generator/references/ir-format.md +212 -0
- package/dist/vendored/skills/dsl-card-generator/references/message-center-pattern.md +149 -0
- package/dist/vendored/skills/dsl-card-generator/references/platform_jsapi.md +196 -0
- package/dist/vendored/skills/dsl-card-generator/references/previewer_contract.md +54 -0
- package/dist/vendored/skills/dsl-card-generator/references/quickref.md +197 -0
- package/dist/vendored/skills/dsl-card-generator/references/recipe_snippets/absolute-badge.json +84 -0
- package/dist/vendored/skills/dsl-card-generator/references/recipe_snippets/accordion-group-toggle.json +18 -0
- package/dist/vendored/skills/dsl-card-generator/references/recipe_snippets/alertCard-pair.json +12 -0
- package/dist/vendored/skills/dsl-card-generator/references/recipe_snippets/alertDialog-call.json +12 -0
- package/dist/vendored/skills/dsl-card-generator/references/recipe_snippets/canvas-countdown.json +15 -0
- package/dist/vendored/skills/dsl-card-generator/references/recipe_snippets/canvas-donut.json +15 -0
- package/dist/vendored/skills/dsl-card-generator/references/recipe_snippets/canvas-gauge-score.json +11 -0
- package/dist/vendored/skills/dsl-card-generator/references/recipe_snippets/canvas-radar.json +10 -0
- package/dist/vendored/skills/dsl-card-generator/references/recipe_snippets/canvas-title-head.json +13 -0
- package/dist/vendored/skills/dsl-card-generator/references/recipe_snippets/globalSet.json +12 -0
- package/dist/vendored/skills/dsl-card-generator/references/recipe_snippets/label-col-modes.json +21 -0
- package/dist/vendored/skills/dsl-card-generator/references/recipe_snippets/list-expand-collapse.json +17 -0
- package/dist/vendored/skills/dsl-card-generator/references/recipe_snippets/money-format.json +11 -0
- package/dist/vendored/skills/dsl-card-generator/references/recipe_snippets/opacity-switch.json +130 -0
- package/dist/vendored/skills/dsl-card-generator/references/recipe_snippets/progress-bar.json +25 -0
- package/dist/vendored/skills/dsl-card-generator/references/recipe_snippets/tab-billMode.json +15 -0
- package/dist/vendored/skills/dsl-card-generator/references/recipe_snippets/title-underline-overlay.json +21 -0
- package/dist/vendored/skills/dsl-card-generator/references/recipes.md +797 -0
- package/dist/vendored/skills/dsl-card-generator/references/rules.md +235 -0
- package/dist/vendored/skills/dsl-card-generator/references/slices_convention.md +177 -0
- package/dist/vendored/skills/dsl-card-generator/references/spec_to_schema_patterns.md +609 -0
- package/dist/vendored/skills/dsl-card-generator/references/ui_fidelity_rules.md +131 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/add_component.mjs +118 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/analyze_component_usage.mjs +153 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/apply_opacity_pattern.mjs +343 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/build_card_index.mjs +108 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/build_card_templates.mjs +190 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/check_fidelity.mjs +257 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/check_magic_values.mjs +79 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/crop_slices.mjs +98 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/describe_card.mjs +265 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/diff_bands.mjs +257 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/diff_card.mjs +316 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/expand_ir.mjs +873 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/extract_handcrafted.py +1199 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/extract_subtree.mjs +191 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/generate_cards.mjs +389 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/inventory_schemas.py +160 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/iterate.mjs +700 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/lib/design_image.mjs +56 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/lib/design_sample.mjs +328 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/lib/layout-tokens.mjs +79 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/lib/paths.mjs +103 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/lib/scaffold.mjs +575 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/lib/slicePath.mjs +81 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/lib/slice_dedupe.mjs +147 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/lint_autofix.mjs +363 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/lint_card.mjs +1399 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/preflight.mjs +249 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/register_card_template.mjs +208 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/render_baseline.mjs +231 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/render_card.mjs +801 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/render_diff.mjs +239 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/resolve_card_template.mjs +73 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/route_input.mjs +447 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/scaffold_card.mjs +81 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/slices_normalize.mjs +316 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/spec_audit.mjs +462 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/spec_to_schema_v3.mjs +7735 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/spec_tree.mjs +355 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/validate_all_schemas.py +71 -0
- package/dist/vendored/skills/dsl-card-generator/vendor/preview/assets/default-image-wzZYQ86K.png +0 -0
- package/dist/vendored/skills/dsl-card-generator/vendor/preview/assets/standalone-C9k-07y7.css +1 -0
- package/dist/vendored/skills/dsl-card-generator/vendor/preview/assets/standalone-oFRM4f4V.js +55 -0
- package/dist/vendored/skills/dsl-card-generator/vendor/preview/assets/svg-symbols-B2exBrAr.svg +118 -0
- package/dist/vendored/skills/dsl-card-generator/vendor/preview/meta/component-meta.json +2714 -0
- package/dist/vendored/skills/dsl-card-generator/vendor/preview/meta/directive-meta.json +40 -0
- package/dist/vendored/skills/dsl-card-generator/vendor/preview/meta/style-meta.json +1086 -0
- package/dist/vendored/{standalone.html → skills/dsl-card-generator/vendor/preview/standalone.html} +2 -1
- package/dist/vendored/skills/finmall-card-sync/SKILL.md +262 -0
- package/dist/vendored/skills/finmall-session-navigator/SKILL.md +169 -0
- package/dist/vendored/skills/finmall-session-navigator/scripts/finmall_login.mjs +882 -0
- package/dist/vendored/skills/finmall-session-navigator/scripts/package-lock.json +59 -0
- package/dist/vendored/skills/finmall-session-navigator/scripts/package.json +10 -0
- package/dist/vendored/skills/finmall-session-navigator/scripts/session-manager.mjs +96 -0
- package/dist/vendored/skills/shared/README.md +55 -0
- package/dist/vendored/skills/shared/auth-model.md +79 -0
- package/dist/vendored/skills/sketch-spec-to-card/SKILL.md +329 -0
- package/dist/vendored/skills/sketch-spec-to-card/references/boundary.json +14 -0
- package/dist/vendored/skills/sketch-spec-to-card/references/card_refs.json +13 -0
- package/dist/vendored/skills/sketch-spec-to-card/references/spec-schema.md +103 -0
- package/dist/vendored/skills/sketch-spec-to-card/scripts/build_prompt.py +797 -0
- package/dist/vendored/skills/sketch-spec-to-card/scripts/card_refs.py +94 -0
- package/dist/vendored/skills/sketch-spec-to-card/scripts/cluster_states.py +706 -0
- package/dist/vendored/skills/sketch-spec-to-card/scripts/parse_meaxure.py +1781 -0
- package/dist/vendored/skills/sketch-spec-to-card/scripts/png_utils.py +318 -0
- package/dist/vendored/skills/sketch-spec-to-card/scripts/vision.py +1118 -0
- package/dist/vendored/templates/helloCard/data.json +10 -0
- package/dist/vendored/templates/helloCard/schema.json +238 -0
- package/dist/vendored/tools/link-skills.mjs +485 -0
- package/package.json +2 -1
- package/dist/preview-editor/assets/index-jl4YJzKv.js +0 -180
- package/dist/vendored/App.vue +0 -2858
- package/dist/vendored/ansi.js +0 -185
- package/dist/vendored/assets/apple-touch-icon.png +0 -0
- package/dist/vendored/assets/default-image.png +0 -0
- package/dist/vendored/assets/favicon-16.png +0 -0
- package/dist/vendored/assets/favicon-32.png +0 -0
- package/dist/vendored/assets/site-icon.png +0 -0
- package/dist/vendored/assets/svgIcons/icons.svg +0 -87
- package/dist/vendored/assets/svgIcons/index.js +0 -39
- package/dist/vendored/components/AiSettings.vue +0 -392
- package/dist/vendored/components/AlertCardInstance.vue +0 -58
- package/dist/vendored/components/AlertCardLayer.vue +0 -59
- package/dist/vendored/components/ApiCallPanel.vue +0 -127
- package/dist/vendored/components/ApiPayloadTree.vue +0 -201
- package/dist/vendored/components/AppToast.vue +0 -37
- package/dist/vendored/components/CardGallery.vue +0 -291
- package/dist/vendored/components/CardPreview.vue +0 -72
- package/dist/vendored/components/CardSelect.vue +0 -206
- package/dist/vendored/components/ChatComposer.vue +0 -413
- package/dist/vendored/components/ChatMessage.vue +0 -383
- package/dist/vendored/components/ChatPanel.vue +0 -937
- package/dist/vendored/components/ComponentPalette.vue +0 -301
- package/dist/vendored/components/ConfirmDialog.vue +0 -54
- package/dist/vendored/components/DatePickerOverlay.vue +0 -345
- package/dist/vendored/components/DebugPanel.vue +0 -1734
- package/dist/vendored/components/DeviceOverlay.vue +0 -70
- package/dist/vendored/components/Dropdown.vue +0 -193
- package/dist/vendored/components/EventEditor.vue +0 -273
- package/dist/vendored/components/HoverGuides.vue +0 -122
- package/dist/vendored/components/JsonTree.vue +0 -547
- package/dist/vendored/components/LogValue.vue +0 -52
- package/dist/vendored/components/MarkdownBody.vue +0 -64
- package/dist/vendored/components/NewCardDialog.vue +0 -132
- package/dist/vendored/components/NodeInspector.vue +0 -295
- package/dist/vendored/components/NumberStepper.vue +0 -125
- package/dist/vendored/components/PickerOverlay.vue +0 -131
- package/dist/vendored/components/PropControl.vue +0 -245
- package/dist/vendored/components/PropTree.vue +0 -128
- package/dist/vendored/components/RemotePreview.vue +0 -173
- package/dist/vendored/components/SelectionOverlay.vue +0 -105
- package/dist/vendored/components/StructureTree.vue +0 -1002
- package/dist/vendored/components/Switch.vue +0 -38
- package/dist/vendored/components/setters/SetBgPosition.vue +0 -64
- package/dist/vendored/components/setters/SetBgSize.vue +0 -90
- package/dist/vendored/components/setters/SetCode.vue +0 -198
- package/dist/vendored/components/setters/SetColor.vue +0 -244
- package/dist/vendored/components/setters/SetCustomSelect.vue +0 -58
- package/dist/vendored/components/setters/SetExpr.vue +0 -107
- package/dist/vendored/components/setters/SetGradient.vue +0 -125
- package/dist/vendored/components/setters/SetGrid9.vue +0 -68
- package/dist/vendored/components/setters/SetIconSelect.vue +0 -67
- package/dist/vendored/components/setters/SetIfForm.vue +0 -61
- package/dist/vendored/components/setters/SetImage.vue +0 -43
- package/dist/vendored/components/setters/SetList.vue +0 -139
- package/dist/vendored/components/setters/SetNumber.vue +0 -118
- package/dist/vendored/components/setters/SetNumberStyle.vue +0 -113
- package/dist/vendored/components/setters/SetPadding.vue +0 -77
- package/dist/vendored/components/setters/SetSegment.vue +0 -160
- package/dist/vendored/components/setters/SetSelect.vue +0 -51
- package/dist/vendored/components/setters/SetSwitch.vue +0 -49
- package/dist/vendored/components/setters/SetText.vue +0 -52
- package/dist/vendored/components/setters/props.js +0 -23
- package/dist/vendored/fidelity/baseline-messageOverview.json +0 -87
- package/dist/vendored/fidelity/calibration.json +0 -29
- package/dist/vendored/fidelity/fidelity-messageOverview-after.html +0 -114
- package/dist/vendored/fidelity/fidelity-messageOverview-after.json +0 -272
- package/dist/vendored/fidelity/fidelity-messageOverview.html +0 -185
- package/dist/vendored/fidelity/fidelity-messageOverview.json +0 -336
- package/dist/vendored/fidelity/fidelity-messageOverview_v2.html +0 -262
- package/dist/vendored/fidelity/fidelity-messageOverview_v2.json +0 -502
- package/dist/vendored/fidelity/fidelity-messageOverview_v3.html +0 -201
- package/dist/vendored/fidelity/fidelity-messageOverview_v3.json +0 -417
- package/dist/vendored/fidelity/fidelity-messageOverview_visual.html +0 -125
- package/dist/vendored/fidelity/fidelity-messageOverview_visual.json +0 -338
- package/dist/vendored/index.html +0 -16
- package/dist/vendored/main.js +0 -37
- package/dist/vendored/runtime/ai-api.js +0 -165
- package/dist/vendored/runtime/alert-card.js +0 -125
- package/dist/vendored/runtime/app-toast.js +0 -33
- package/dist/vendored/runtime/clipboard-node.js +0 -78
- package/dist/vendored/runtime/clipboard.js +0 -29
- package/dist/vendored/runtime/event-model.js +0 -300
- package/dist/vendored/runtime/hoverGuide.js +0 -31
- package/dist/vendored/runtime/jsapi-mock.js +0 -256
- package/dist/vendored/runtime/markdown.js +0 -140
- package/dist/vendored/runtime/node-model.js +0 -217
- package/dist/vendored/skills/.manifest.json +0 -1101
- package/dist/vendored/standalone-main.js +0 -36
- package/dist/vendored/styles/ai-chat.css +0 -1850
- package/dist/vendored/styles/app.css +0 -3648
- package/dist/vendored/styles/card-gallery.css +0 -285
- package/dist/vendored/styles/card-select.css +0 -288
- package/dist/vendored/styles/components.css +0 -383
- package/dist/vendored/styles/dropdown.css +0 -324
- package/dist/vendored/styles/json-tree.css +0 -308
- package/dist/vendored/styles/node-inspector.css +0 -800
- package/dist/vendored/styles/setters.css +0 -1050
- package/dist/vendored/styles/standalone.css +0 -27
- package/dist/vendored/styles/structure-tree.css +0 -460
- /package/dist/vendored/{api.js → preview/api.js} +0 -0
- /package/dist/vendored/{assets → preview/assets}/svg-symbols.svg +0 -0
- /package/dist/vendored/{components → preview/components}/AbsoluteLayout.vue +0 -0
- /package/dist/vendored/{components → preview/components}/Button.vue +0 -0
- /package/dist/vendored/{components → preview/components}/Canvas.vue +0 -0
- /package/dist/vendored/{components → preview/components}/CheckGroup.vue +0 -0
- /package/dist/vendored/{components → preview/components}/Column.vue +0 -0
- /package/dist/vendored/{components → preview/components}/CustomPlaceholder.vue +0 -0
- /package/dist/vendored/{components → preview/components}/Divider.vue +0 -0
- /package/dist/vendored/{components → preview/components}/GridLayout.vue +0 -0
- /package/dist/vendored/{components → preview/components}/Icon.vue +0 -0
- /package/dist/vendored/{components → preview/components}/Image.vue +0 -0
- /package/dist/vendored/{components → preview/components}/Input.vue +0 -0
- /package/dist/vendored/{components → preview/components}/ListLayout.vue +0 -0
- /package/dist/vendored/{components → preview/components}/Map.vue +0 -0
- /package/dist/vendored/{components → preview/components}/MultiColumn.vue +0 -0
- /package/dist/vendored/{components → preview/components}/MultiText.vue +0 -0
- /package/dist/vendored/{components → preview/components}/ProgressSlider.vue +0 -0
- /package/dist/vendored/{components → preview/components}/RadioButton.vue +0 -0
- /package/dist/vendored/{components → preview/components}/RenderTree.vue +0 -0
- /package/dist/vendored/{components → preview/components}/Root.vue +0 -0
- /package/dist/vendored/{components → preview/components}/Row.vue +0 -0
- /package/dist/vendored/{components → preview/components}/SvgCode.vue +0 -0
- /package/dist/vendored/{components → preview/components}/SvgSymbols.vue +0 -0
- /package/dist/vendored/{components → preview/components}/Text.vue +0 -0
- /package/dist/vendored/{components → preview/components}/Video.vue +0 -0
- /package/dist/vendored/{components → preview/components}/ViewContainer.vue +0 -0
- /package/dist/vendored/{components → preview/components}/base.js +0 -0
- /package/dist/vendored/{components → preview/components}/icons.js +0 -0
- /package/dist/vendored/{components → preview/components}/index.js +0 -0
- /package/dist/vendored/{meta → preview/meta}/component-meta.json +0 -0
- /package/dist/vendored/{meta → preview/meta}/directive-meta.json +0 -0
- /package/dist/vendored/{meta → preview/meta}/style-meta.json +0 -0
- /package/dist/vendored/{runtime → preview/runtime}/StyleParser.js +0 -0
- /package/dist/vendored/{runtime → preview/runtime}/adapter.js +0 -0
- /package/dist/vendored/{runtime → preview/runtime}/assets.js +0 -0
- /package/dist/vendored/{runtime → preview/runtime}/canvas-context.js +0 -0
- /package/dist/vendored/{runtime → preview/runtime}/card-runtime.js +0 -0
- /package/dist/vendored/{runtime → preview/runtime}/evaluate.js +0 -0
- /package/dist/vendored/{runtime → preview/runtime}/property.js +0 -0
- /package/dist/vendored/{runtime → preview/runtime}/selectionOverlay.js +0 -0
- /package/dist/vendored/{runtime → preview/runtime}/toPreview.js +0 -0
- /package/dist/{server-core → vendored/server/core}/assetProxy.js +0 -0
- /package/dist/{server-core → vendored/server/core}/bbox.js +0 -0
- /package/dist/{server-core → vendored/server/core}/buildDataFromSchema.js +0 -0
- /package/dist/{server-core → vendored/server/core}/cardFiles.js +0 -0
- /package/dist/{server-core → vendored/server/core}/cardHistory.js +0 -0
- /package/dist/{server-core → vendored/server/core}/cardImage.js +0 -0
- /package/dist/{server-core → vendored/server/core}/cardImportMeta.js +0 -0
- /package/dist/{server-core → vendored/server/core}/cardListCache.js +0 -0
- /package/dist/{server-core → vendored/server/core}/cardOpenFolder.js +0 -0
- /package/dist/{server-core → vendored/server/core}/cardSidecar.js +0 -0
- /package/dist/{server-core → vendored/server/core}/codeBridge.js +0 -0
- /package/dist/{server-core → vendored/server/core}/logStream.js +0 -0
- /package/dist/{server-core → vendored/server/core}/logger.js +0 -0
- /package/dist/{server-core → vendored/server/core}/nodeModels.js +0 -0
- /package/dist/{server-core → vendored/server/core}/renderStatus.js +0 -0
- /package/dist/{server-core → vendored/server/core}/screenshot.js +0 -0
- /package/dist/{server-core → vendored/server/core}/sliceStatic.js +0 -0
- /package/dist/vendored/{meta → skills/dsl-card-generator/vendor/preview/meta}/action-meta.json +0 -0
- /package/dist/{server-core/_vendor → vendored/tools}/finmall-env.mjs +0 -0
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
<link rel="icon" type="image/png" sizes="16x16" href="/assets/favicon-16-Cg6Nrosk.png" />
|
|
9
9
|
<link rel="icon" type="image/png" sizes="32x32" href="/assets/favicon-32-B_1744sp.png" />
|
|
10
10
|
<link rel="apple-touch-icon" sizes="180x180" href="/assets/apple-touch-icon-BQcES9QK.png" />
|
|
11
|
-
<script type="module" crossorigin src="/assets/index-
|
|
12
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
11
|
+
<script type="module" crossorigin src="/assets/index-CHuGGIR3.js"></script>
|
|
12
|
+
<link rel="stylesheet" crossorigin href="/assets/index-CQScMhyg.css">
|
|
13
13
|
</head>
|
|
14
14
|
<body>
|
|
15
15
|
<div id="app"></div>
|
package/dist/standalone.js
CHANGED
|
@@ -430,12 +430,20 @@ export async function startServer(userConfig = {}) {
|
|
|
430
430
|
subscribeLog((lines) => events.notify({ lines }, 'service-log'))
|
|
431
431
|
|
|
432
432
|
const vite = await createEmbeddedVite(config)
|
|
433
|
+
// AI 工具调度复用仓库的 mcp/tools.mjs(发布态为包内快照),其 mcp/client.mjs 在 import 时
|
|
434
|
+
// 读取 CARD_ROOT(卡片工程根)与 CARD_API_BASE(dev server 基址)。loadTools() 是懒加载
|
|
435
|
+
// (首次 AI 请求才 import),故这里在启动期注入即可;已有的显式环境变量优先,不覆盖。
|
|
436
|
+
if (!process.env.CARD_ROOT) process.env.CARD_ROOT = config.root
|
|
437
|
+
if (!process.env.CARD_API_BASE) process.env.CARD_API_BASE = apiBase
|
|
433
438
|
// upstream(FINMALL_BASE_URL)透传给资源代理中间件,使其能回源 FinMall 取站内资源
|
|
434
439
|
mountCoreMiddlewares(app, {
|
|
435
440
|
root: config.root,
|
|
436
441
|
viteServer: vite,
|
|
437
442
|
upstream: config.upstream,
|
|
438
443
|
notify: events.notify,
|
|
444
|
+
// card_patch 走 card_write 管线(历史快照 + validate/render_status):本服务的 root 就是
|
|
445
|
+
// 卡片工程根,且 CARD_ROOT 已指向同一处,dispatch 落点一致,故显式开启。
|
|
446
|
+
allowDispatch: true,
|
|
439
447
|
// 代码桥单文件监听注入 chokidar 版:原子保存(改名覆盖)由 add 兜住,awaitWriteFinish 防半截读
|
|
440
448
|
watchFile: (file, onChange) => {
|
|
441
449
|
const watcher = chokidar.watch(file, {
|
|
@@ -0,0 +1,537 @@
|
|
|
1
|
+
import fs from 'node:fs'
|
|
2
|
+
import path from 'node:path'
|
|
3
|
+
import { fileURLToPath } from 'node:url'
|
|
4
|
+
import { dirname, resolve } from 'node:path'
|
|
5
|
+
import { listCards, readCardFiles, writeCardFiles } from '../server/core/cardFiles.js'
|
|
6
|
+
import { validateCard, resolvePython, scriptPath } from '../server/core/cardValidate.js'
|
|
7
|
+
import { listSnapshots, saveSnapshot, HISTORY_DIR } from '../server/core/cardHistory.js'
|
|
8
|
+
import { devApiBase } from '../tools/finmall-env.mjs'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* MCP 薄 HTTP 客户端:只封装对 dev server(端口见 .env.cb 的 FINMALL_DEV_PORT)的请求,不复制任何业务逻辑。
|
|
12
|
+
*
|
|
13
|
+
* 降级策略(与 tools/card-check.mjs 一致):
|
|
14
|
+
* - list / read / write / validate:dev server 未启动(fetch 抛连接错误)时,
|
|
15
|
+
* 降级为直接读盘 / 写盘 / 跑 Python —— 复用 server/* 导出的同一份实现,避免漂移。
|
|
16
|
+
* - render_status / sync / save:依赖 dev server 与 FinMall 登录态,不支持离线降级;
|
|
17
|
+
* screenshot 在 dev server 未启动时降级到独立离线渲染器。
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
21
|
+
const ROOT = process.env.CARD_ROOT ? resolve(process.env.CARD_ROOT) : resolve(__dirname, '..')
|
|
22
|
+
// dev server 端口由 .env.cb 的 FINMALL_DEV_PORT 控制(默认 5180);
|
|
23
|
+
// 仍可用 CARD_API_BASE 覆盖(如 dev server 跑在非默认端口)。
|
|
24
|
+
const BASE = (process.env.CARD_API_BASE || devApiBase()).replace(/\/$/, '')
|
|
25
|
+
|
|
26
|
+
/** 发起 GET,返回 { status, buf, text, json, contentType };任何网络错误向上抛,由调用方决定降级 */
|
|
27
|
+
async function httpGet(p) {
|
|
28
|
+
const res = await fetch(BASE + p, { headers: { Accept: 'application/json, image/png' } })
|
|
29
|
+
const buf = Buffer.from(await res.arrayBuffer())
|
|
30
|
+
let text = null
|
|
31
|
+
let json = null
|
|
32
|
+
try {
|
|
33
|
+
text = buf.toString('utf8')
|
|
34
|
+
json = text ? JSON.parse(text) : null
|
|
35
|
+
} catch {
|
|
36
|
+
/* 非 JSON 响应(如截图 png) */
|
|
37
|
+
}
|
|
38
|
+
return { status: res.status, buf, text, json, contentType: res.headers.get('content-type') || '' }
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** 发起 POST(JSON body) */
|
|
42
|
+
async function httpPost(p, body) {
|
|
43
|
+
const res = await fetch(BASE + p, {
|
|
44
|
+
method: 'POST',
|
|
45
|
+
headers: { 'Content-Type': 'application/json' },
|
|
46
|
+
body: JSON.stringify(body || {}),
|
|
47
|
+
})
|
|
48
|
+
const buf = Buffer.from(await res.arrayBuffer())
|
|
49
|
+
let text = null
|
|
50
|
+
let json = null
|
|
51
|
+
try {
|
|
52
|
+
text = buf.toString('utf8')
|
|
53
|
+
json = text ? JSON.parse(text) : null
|
|
54
|
+
} catch {
|
|
55
|
+
/* 非 JSON 响应 */
|
|
56
|
+
}
|
|
57
|
+
return { status: res.status, buf, text, json, contentType: res.headers.get('content-type') || '' }
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// 需要 FinMall 登录态的远端工具:提示语要额外说明「且已登录 FinMall」。
|
|
61
|
+
const REMOTE_TOOLS = new Set(['card_sync', 'card_save', 'card_slice_upload'])
|
|
62
|
+
|
|
63
|
+
const DEV_REQUIRED = (tool) => ({
|
|
64
|
+
source: null,
|
|
65
|
+
error: 'dev-server-required',
|
|
66
|
+
message: `${tool} 需要 dev server 已启动(npm run dev)${REMOTE_TOOLS.has(tool) ? ' 且已登录 FinMall' : ''}。本工具不支持离线降级。`,
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
// ---- card_list ----
|
|
70
|
+
export async function cardList() {
|
|
71
|
+
try {
|
|
72
|
+
const { status, json } = await httpGet('/__cards')
|
|
73
|
+
if (status === 200 && json && Array.isArray(json.cards)) return { source: 'http', cards: json.cards }
|
|
74
|
+
} catch {
|
|
75
|
+
/* 降级 */
|
|
76
|
+
}
|
|
77
|
+
return { source: 'local', cards: listCards(ROOT) }
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// ---- card_read ----
|
|
81
|
+
export async function cardRead(id) {
|
|
82
|
+
try {
|
|
83
|
+
const { status, json } = await httpGet(`/__card/${encodeURIComponent(id)}`)
|
|
84
|
+
if (status === 200 && json) return { source: 'http', ...json }
|
|
85
|
+
} catch {
|
|
86
|
+
/* 降级 */
|
|
87
|
+
}
|
|
88
|
+
const r = readCardFiles(ROOT, id)
|
|
89
|
+
if (!r) return { source: 'local', error: 'card not found', id }
|
|
90
|
+
return { source: 'local', ...r }
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// ---- card_write(闭环收口:写前自动留历史快照,写完自动校验)----
|
|
94
|
+
export async function cardWrite(id, payload, { verify = true, snapshot = true } = {}) {
|
|
95
|
+
const dir = path.join(ROOT, 'src', 'cards', id)
|
|
96
|
+
let writeResult = null
|
|
97
|
+
let source = 'local'
|
|
98
|
+
|
|
99
|
+
// 安全网:写卡前把「当前版本 schema」落一张历史快照(与编辑器改卡先 POST /__history 同语义),
|
|
100
|
+
// 使 AI 编辑同样可被 card_rollback 撤销。card_rollback 内部写回传 snapshot:false 避免重复留快照。
|
|
101
|
+
let safetyTs = null
|
|
102
|
+
if (snapshot && payload && payload.schema) {
|
|
103
|
+
try {
|
|
104
|
+
const { status, json } = await httpGet(`/__card/${encodeURIComponent(id)}`)
|
|
105
|
+
if (status === 200 && json && json.schema) {
|
|
106
|
+
const h = await httpPost(`/__history/${encodeURIComponent(id)}`, { schema: json.schema })
|
|
107
|
+
if (h.status === 200 && h.json && h.json.ok) safetyTs = h.json.ts
|
|
108
|
+
}
|
|
109
|
+
} catch {
|
|
110
|
+
/* dev server 不在线,走本地降级 */
|
|
111
|
+
}
|
|
112
|
+
if (safetyTs == null) {
|
|
113
|
+
try {
|
|
114
|
+
const sp = path.join(dir, 'schema.json')
|
|
115
|
+
if (fs.existsSync(sp)) {
|
|
116
|
+
const prev = JSON.parse(fs.readFileSync(sp, 'utf8'))
|
|
117
|
+
safetyTs = saveSnapshot(dir, prev).ts
|
|
118
|
+
}
|
|
119
|
+
} catch {
|
|
120
|
+
/* 首次写卡(无旧 schema)或快照失败不阻断写卡 */
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
try {
|
|
126
|
+
const { status, json } = await httpPost(`/__card/${encodeURIComponent(id)}`, payload)
|
|
127
|
+
if (status === 200 && json) {
|
|
128
|
+
writeResult = json
|
|
129
|
+
source = 'http'
|
|
130
|
+
} else if (status === 404) {
|
|
131
|
+
return { source: 'http', error: 'card not found', id }
|
|
132
|
+
} else if (status === 400) {
|
|
133
|
+
return { source: 'http', error: json?.error || 'invalid request', detail: json }
|
|
134
|
+
}
|
|
135
|
+
} catch {
|
|
136
|
+
/* 降级 */
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (!writeResult) {
|
|
140
|
+
if (!fs.existsSync(dir) || !fs.statSync(dir).isDirectory()) {
|
|
141
|
+
return { source: 'local', error: 'card not found', id }
|
|
142
|
+
}
|
|
143
|
+
try {
|
|
144
|
+
writeResult = { ok: true, written: writeCardFiles(dir, payload) }
|
|
145
|
+
try { await import('../server/core/cardImportMeta.js').then((m) => m.markDirty(dir, true)) } catch { /* 无侧车时忽略 */ }
|
|
146
|
+
} catch (e) {
|
|
147
|
+
return { source: 'local', error: 'write failed', message: String(e && e.message) }
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const verifyResult = { validate: null, renderStatus: null }
|
|
152
|
+
if (verify) {
|
|
153
|
+
if (source === 'http') {
|
|
154
|
+
try {
|
|
155
|
+
const v = await httpGet(`/__validate/${encodeURIComponent(id)}`)
|
|
156
|
+
if (v.status === 200 && v.json) verifyResult.validate = v.json
|
|
157
|
+
} catch {
|
|
158
|
+
/* 忽略 */
|
|
159
|
+
}
|
|
160
|
+
try {
|
|
161
|
+
const r = await httpGet(`/__render-status/${encodeURIComponent(id)}`)
|
|
162
|
+
if (r.status === 200 && r.json) verifyResult.renderStatus = r.json
|
|
163
|
+
} catch {
|
|
164
|
+
/* 忽略 */
|
|
165
|
+
}
|
|
166
|
+
} else {
|
|
167
|
+
// 本地降级:只能跑 Python 硬校验;render_status 需 dev server(SSR),标注 skipped
|
|
168
|
+
const python = resolvePython()
|
|
169
|
+
const sp = path.join(dir, 'schema.json')
|
|
170
|
+
if (python && fs.existsSync(sp)) {
|
|
171
|
+
verifyResult.validate = await validateCard(python, scriptPath(ROOT), sp, { checkData: true })
|
|
172
|
+
} else if (!python) {
|
|
173
|
+
verifyResult.validate = { ok: false, errors: ['python-not-found'], warnings: [], toolError: 'python-not-found' }
|
|
174
|
+
}
|
|
175
|
+
verifyResult.renderStatus = { skipped: true, reason: 'dev server 未启动,无法 SSR 自检;请先 npm run dev' }
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return {
|
|
180
|
+
source,
|
|
181
|
+
written: writeResult.written,
|
|
182
|
+
...(safetyTs != null ? { safetySnapshotTs: safetyTs } : {}),
|
|
183
|
+
...(verify ? { verify: verifyResult } : {}),
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// ---- card_validate ----
|
|
188
|
+
export async function cardValidate(id, { noData = false } = {}) {
|
|
189
|
+
try {
|
|
190
|
+
const { status, json } = await httpGet(`/__validate/${encodeURIComponent(id)}${noData ? '?no-data' : ''}`)
|
|
191
|
+
if (status === 200 && json) return { source: 'http', ...json }
|
|
192
|
+
if (status === 404) return { source: 'http', error: 'card not found', id }
|
|
193
|
+
} catch {
|
|
194
|
+
/* 降级 */
|
|
195
|
+
}
|
|
196
|
+
const dir = path.join(ROOT, 'src', 'cards', id)
|
|
197
|
+
const sp = path.join(dir, 'schema.json')
|
|
198
|
+
if (!fs.existsSync(sp)) return { source: 'local', error: 'card not found', id }
|
|
199
|
+
const python = resolvePython()
|
|
200
|
+
if (!python) {
|
|
201
|
+
return {
|
|
202
|
+
source: 'local',
|
|
203
|
+
error: 'python-not-found',
|
|
204
|
+
message: '未找到 python3/python,请安装 Python 或设置 DSL_PYTHON 环境变量',
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
const r = await validateCard(python, scriptPath(ROOT), sp, { checkData: !noData })
|
|
208
|
+
return { source: 'local', id, ...r }
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// ---- card_render_status(需 dev server)----
|
|
212
|
+
export async function cardRenderStatus(id) {
|
|
213
|
+
try {
|
|
214
|
+
const { status, json } = await httpGet(`/__render-status/${encodeURIComponent(id)}`)
|
|
215
|
+
if (status === 200 && json) return { source: 'http', ...json }
|
|
216
|
+
if (status === 404) return { source: 'http', error: 'card not found', id }
|
|
217
|
+
} catch {
|
|
218
|
+
/* 降级不可用 */
|
|
219
|
+
}
|
|
220
|
+
return DEV_REQUIRED('card_render_status')
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// ---- card_node_models(需 dev server,Task #5 暴露的字段结构树)----
|
|
224
|
+
export async function cardNodeModels(id) {
|
|
225
|
+
try {
|
|
226
|
+
const { status, json } = await httpGet(`/__node-models/${encodeURIComponent(id)}`)
|
|
227
|
+
if (status === 200 && json) return { source: 'http', ...json }
|
|
228
|
+
if (status === 404) return { source: 'http', error: 'card not found', id }
|
|
229
|
+
} catch {
|
|
230
|
+
/* 降级不可用 */
|
|
231
|
+
}
|
|
232
|
+
return DEV_REQUIRED('card_node_models')
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// ---- card_import_meta(需 dev server)----
|
|
236
|
+
export async function cardImportMeta(id) {
|
|
237
|
+
try {
|
|
238
|
+
const { status, json } = await httpGet(`/__import-meta/${encodeURIComponent(id)}`)
|
|
239
|
+
if (status === 200 && json) return { source: 'http', ...json }
|
|
240
|
+
if (status === 404) return { source: 'http', error: 'import-meta not found', id, hint: json?.hint }
|
|
241
|
+
} catch {
|
|
242
|
+
/* 降级不可用 */
|
|
243
|
+
}
|
|
244
|
+
return DEV_REQUIRED('card_import_meta')
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// ---- card_route_input(离线:直接复用 skill 的输入路由判定器,无需 dev server)----
|
|
248
|
+
/** 展开 `~` 与工作区相对路径:MCP 调用方常给 `~/Downloads/...` 或相对工程根的路径 */
|
|
249
|
+
function expandInputPath(p) {
|
|
250
|
+
const raw = String(p == null ? '' : p).trim()
|
|
251
|
+
if (!raw) return raw
|
|
252
|
+
if (raw === '~') return process.env.HOME || raw
|
|
253
|
+
if (raw.startsWith('~/') || raw.startsWith('~\\')) return path.join(process.env.HOME || '', raw.slice(2))
|
|
254
|
+
return path.isAbsolute(raw) ? raw : resolve(ROOT, raw)
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
export async function cardRouteInput({ path: inputPath } = {}) {
|
|
258
|
+
if (!inputPath) {
|
|
259
|
+
return {
|
|
260
|
+
source: 'local',
|
|
261
|
+
error: 'missing-args',
|
|
262
|
+
message: 'path 必填:MeaXure 标注导出目录 / UI 要素包 / 卡片目录 / 设计稿 PNG',
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
const mod = await import('../skills/dsl-card-generator/scripts/route_input.mjs')
|
|
266
|
+
const r = mod.routeInput(expandInputPath(inputPath))
|
|
267
|
+
if (r.error) return { source: 'local', ...r }
|
|
268
|
+
const hint =
|
|
269
|
+
r.kind === 'meaxure-export' && r.preprocess && r.preprocess.needed
|
|
270
|
+
? '先执行 next 里的两条命令跑 sketch-spec-to-card 前处理,产出 UI/<稿名>/ 要素包后再逐卡生成;禁止直接读 preview/ 整图建卡'
|
|
271
|
+
: r.kind === 'meaxure-export'
|
|
272
|
+
? '该导出已前处理过,直接消费 next 给出的存量产物,不重复跑管线'
|
|
273
|
+
: r.kind === 'unknown'
|
|
274
|
+
? '判不出输入形态:向用户确认,不要猜(CLI 等价退出码 3)'
|
|
275
|
+
: '按 next 的命令继续(Workflow A 建卡 / Workflow B 改卡)'
|
|
276
|
+
return { source: 'local', ok: r.kind !== 'unknown', ...r, hint }
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// ---- card_scaffold(离线:直接调 scaffold lib,无需 dev server)----
|
|
280
|
+
export async function cardScaffold({ intent, id, name, outBase, platform, force, from } = {}) {
|
|
281
|
+
const lib = await import('../skills/dsl-card-generator/scripts/lib/scaffold.mjs')
|
|
282
|
+
if (!from && (!intent || !lib.SCAFFOLD_INTENTS.includes(intent))) {
|
|
283
|
+
return { error: 'invalid-intent', message: `intent 必须是 ${lib.SCAFFOLD_INTENTS.join('|')}`, intent }
|
|
284
|
+
}
|
|
285
|
+
if (!id) return { error: 'missing-id', message: '缺少 id' }
|
|
286
|
+
const base = outBase ? resolve(outBase) : path.join(ROOT, 'src', 'cards')
|
|
287
|
+
if (force) {
|
|
288
|
+
const dir = path.join(base, id)
|
|
289
|
+
if (fs.existsSync(dir)) fs.rmSync(dir, { recursive: true, force: true })
|
|
290
|
+
}
|
|
291
|
+
try {
|
|
292
|
+
if (from) {
|
|
293
|
+
const dir = lib.cloneCardFrom(from, id, { name, outBase: base })
|
|
294
|
+
return { source: 'local', ok: true, from, id, name: name || undefined, dir, note: '整卡模板克隆:pageId/name 已重写,封面未复制(定稿后重新渲染生成)' }
|
|
295
|
+
}
|
|
296
|
+
const dir = lib.writeSkeleton(id, intent, { name, platform, outBase: base })
|
|
297
|
+
return { source: 'local', ok: true, intent, id, name: name || '新建卡片', dir }
|
|
298
|
+
} catch (e) {
|
|
299
|
+
return { source: 'local', error: 'scaffold-failed', message: String((e && e.message) || e) }
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// ---- card_add_component(离线:直接调 scaffold lib + 离线硬校验)----
|
|
304
|
+
/** 归一化 --set-prop 风格的属性覆盖:支持 "key=value" 字符串数组或 {key,value} 对象数组 */
|
|
305
|
+
function normalizeSetProps(setProps) {
|
|
306
|
+
const list = Array.isArray(setProps) ? setProps : setProps ? [setProps] : []
|
|
307
|
+
return list.map((p) => {
|
|
308
|
+
if (typeof p === 'string') {
|
|
309
|
+
const i = p.indexOf('=')
|
|
310
|
+
if (i < 0) throw new Error(`setProps 需 "key=value" 形式:${p}`)
|
|
311
|
+
const key = p.slice(0, i).trim()
|
|
312
|
+
const value = p.slice(i + 1)
|
|
313
|
+
if (!key) throw new Error(`setProps 缺 key:${p}`)
|
|
314
|
+
return { key, value, isVar: value.startsWith('$') }
|
|
315
|
+
}
|
|
316
|
+
const key = String((p && p.key) || '').trim()
|
|
317
|
+
const value = p ? String(p.value ?? '') : ''
|
|
318
|
+
if (!key) throw new Error('setProps 元素缺 key')
|
|
319
|
+
return { key, value, isVar: value.startsWith('$') }
|
|
320
|
+
})
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
export async function cardAddComponent({ id, parent, className, text, click, setProps } = {}) {
|
|
324
|
+
if (!id || !parent || !className) {
|
|
325
|
+
return { error: 'missing-args', message: 'id / parent / className 必填' }
|
|
326
|
+
}
|
|
327
|
+
const lib = await import('../skills/dsl-card-generator/scripts/lib/scaffold.mjs')
|
|
328
|
+
let r
|
|
329
|
+
let propOverrides = []
|
|
330
|
+
try {
|
|
331
|
+
// insertComponent 签名是 (id, opts),与 CLI add_component.mjs 保持一致
|
|
332
|
+
propOverrides = normalizeSetProps(setProps)
|
|
333
|
+
r = lib.insertComponent(id, { parentId: parent, className, text, click, propOverrides })
|
|
334
|
+
} catch (e) {
|
|
335
|
+
return {
|
|
336
|
+
source: 'local',
|
|
337
|
+
error: 'insert-failed',
|
|
338
|
+
message: String((e && e.message) || e),
|
|
339
|
+
hint: '确认 parent 是容器节点(有 slots,或 ListLayout/GridLayout);用 card_read 查 componentId',
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
// 插入成功后离线硬校验,把结果一并带回(python 缺失时 validate 会标注 python-not-found)
|
|
343
|
+
const validate = await cardValidate(id, { noData: false })
|
|
344
|
+
return {
|
|
345
|
+
source: 'local',
|
|
346
|
+
ok: true,
|
|
347
|
+
inserted: { componentId: r.id, className: r.className, parentId: r.parentId, parentClassName: r.parentClassName },
|
|
348
|
+
setProps: r.setProps,
|
|
349
|
+
dir: r.dir,
|
|
350
|
+
validate,
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// ---- card_screenshot(优先 dev server;未启动时降级到独立渲染器)----
|
|
355
|
+
export async function cardScreenshot(id, { format = 'json', width = 390 } = {}) {
|
|
356
|
+
try {
|
|
357
|
+
const { status, buf, json, contentType } = await httpGet(
|
|
358
|
+
`/__screenshot/${encodeURIComponent(id)}?width=${width}&format=${format}`,
|
|
359
|
+
)
|
|
360
|
+
if (status === 200) {
|
|
361
|
+
if (format === 'json') return { source: 'http', ...(json || {}) }
|
|
362
|
+
return { source: 'http', mime: contentType || 'image/png', base64: buf.toString('base64') }
|
|
363
|
+
}
|
|
364
|
+
if (status === 404) return { source: 'http', error: 'card not found', id }
|
|
365
|
+
return { source: 'http', error: json?.error || 'screenshot-failed', message: json?.message, fix: json?.fix }
|
|
366
|
+
} catch {
|
|
367
|
+
/* dev server 不可用:走独立离线渲染器 */
|
|
368
|
+
}
|
|
369
|
+
try {
|
|
370
|
+
const { renderCardScreenshot } = await import('../skills/dsl-card-generator/scripts/render_card.mjs')
|
|
371
|
+
const r = await renderCardScreenshot({ id, width, format })
|
|
372
|
+
if (r.error) return { source: 'standalone', error: r.error, message: r.message }
|
|
373
|
+
return { source: 'standalone', ...(format === 'json' ? r : { mime: r.mime, base64: r.base64 }) }
|
|
374
|
+
} catch (e) {
|
|
375
|
+
return DEV_REQUIRED('card_screenshot')
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// ---- card_sync(需 dev server + FinMall 登录态)----
|
|
380
|
+
export async function cardSync({ cardCode } = {}) {
|
|
381
|
+
try {
|
|
382
|
+
const { status, json } = await httpPost('/__sync', { cardCode })
|
|
383
|
+
if (status === 200) return { source: 'http', ...(json || {}) }
|
|
384
|
+
return { source: 'http', error: json?.error || 'sync-failed', message: json?.message }
|
|
385
|
+
} catch {
|
|
386
|
+
/* 降级不可用 */
|
|
387
|
+
}
|
|
388
|
+
return DEV_REQUIRED('card_sync')
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// ---- card_save(需 dev server + FinMall 登录态)----
|
|
392
|
+
export async function cardSave({ schema, code, skeletonScreen } = {}) {
|
|
393
|
+
try {
|
|
394
|
+
const { status, json } = await httpPost('/__save', { schema, code, skeletonScreen })
|
|
395
|
+
if (status === 200) return { source: 'http', ...(json || {}) }
|
|
396
|
+
return { source: 'http', error: json?.error || 'save-failed', message: json?.message }
|
|
397
|
+
} catch {
|
|
398
|
+
/* 降级不可用 */
|
|
399
|
+
}
|
|
400
|
+
return DEV_REQUIRED('card_save')
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// ---- card_slice_upload(需 dev server + FinMall 登录态)----
|
|
404
|
+
// 切图上传只按单卡范围执行:端点内部复用 finmall-card-sync 的 uploadSlices({ cardId }),
|
|
405
|
+
// 上传 src/slices 里该卡引用的图并把 schema/data 的 /slices/ 引用改写为 @filestore/ 线上地址。
|
|
406
|
+
export async function sliceUpload({ cardId, force } = {}) {
|
|
407
|
+
try {
|
|
408
|
+
const { status, json } = await httpPost('/__slice-upload', { cardId, force })
|
|
409
|
+
if (status === 200) return { source: 'http', ...(json || {}) }
|
|
410
|
+
return { source: 'http', error: json?.error || 'slice-upload-failed', message: json?.message }
|
|
411
|
+
} catch {
|
|
412
|
+
/* 降级不可用 */
|
|
413
|
+
}
|
|
414
|
+
return DEV_REQUIRED('card_slice_upload')
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// ---- 历史快照:内部共用读取 ----
|
|
418
|
+
// 与编辑器撤销/重做同源(.history/ 由 cardWrite/编辑器写卡时自动累积)。
|
|
419
|
+
// card_history 只返回元数据(ts/时间/字节数/节点数),不把整份 schema 倒进上下文。
|
|
420
|
+
|
|
421
|
+
/** 递归统计节点数(view 即根节点;子节点双通道:.components 直连 + .slots[].components 插槽) */
|
|
422
|
+
function countNodes(node) {
|
|
423
|
+
if (!node || typeof node !== 'object') return 0
|
|
424
|
+
let n = 1
|
|
425
|
+
for (const c of Array.isArray(node.components) ? node.components : []) n += countNodes(c)
|
|
426
|
+
for (const s of Array.isArray(node.slots) ? node.slots : []) {
|
|
427
|
+
for (const c of Array.isArray(s && s.components) ? s.components : []) n += countNodes(c)
|
|
428
|
+
}
|
|
429
|
+
return n
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
function snapshotMeta(s) {
|
|
433
|
+
const schema = s.schema != null ? s.schema : s
|
|
434
|
+
return {
|
|
435
|
+
ts: s.ts,
|
|
436
|
+
time: new Date(s.ts).toISOString(),
|
|
437
|
+
bytes: Buffer.byteLength(JSON.stringify(schema), 'utf8'),
|
|
438
|
+
components: countNodes(schema.view),
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
/** 读取全部快照 [{ts, schema}](旧→新)。优先 dev server,离线降级直接读 .history/ */
|
|
443
|
+
async function readSnapshots(id) {
|
|
444
|
+
try {
|
|
445
|
+
const { status, json } = await httpGet(`/__history/${encodeURIComponent(id)}`)
|
|
446
|
+
if (status === 200 && json && Array.isArray(json.snapshots)) {
|
|
447
|
+
return { source: 'http', snapshots: json.snapshots.map((s) => ({ ts: s.ts, schema: s.schema })) }
|
|
448
|
+
}
|
|
449
|
+
if (status === 404) return { source: 'http', error: 'card not found', id }
|
|
450
|
+
} catch {
|
|
451
|
+
/* 降级 */
|
|
452
|
+
}
|
|
453
|
+
const dir = path.join(ROOT, 'src', 'cards', id)
|
|
454
|
+
if (!fs.existsSync(dir) || !fs.statSync(dir).isDirectory()) {
|
|
455
|
+
return { source: 'local', error: 'card not found', id }
|
|
456
|
+
}
|
|
457
|
+
const histDir = path.join(dir, HISTORY_DIR)
|
|
458
|
+
const out = []
|
|
459
|
+
for (const s of listSnapshots(histDir)) {
|
|
460
|
+
try {
|
|
461
|
+
const data = JSON.parse(fs.readFileSync(path.join(histDir, s.file), 'utf8'))
|
|
462
|
+
out.push({ ts: s.ts, schema: data.schema != null ? data.schema : data })
|
|
463
|
+
} catch {
|
|
464
|
+
/* 单文件损坏跳过 */
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
return { source: 'local', snapshots: out }
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
// ---- card_history(列出编辑历史快照元数据)----
|
|
471
|
+
export async function cardHistory(id) {
|
|
472
|
+
const r = await readSnapshots(id)
|
|
473
|
+
if (r.error) return r
|
|
474
|
+
const snapshots = r.snapshots.map(snapshotMeta)
|
|
475
|
+
return {
|
|
476
|
+
source: r.source,
|
|
477
|
+
id,
|
|
478
|
+
count: snapshots.length,
|
|
479
|
+
latestTs: snapshots.length ? snapshots[snapshots.length - 1].ts : null,
|
|
480
|
+
snapshots,
|
|
481
|
+
hint: snapshots.length
|
|
482
|
+
? '用 card_rollback 回滚到指定 ts(默认 latestTs);回滚前会自动把当前版本另存为新快照,回滚本身可撤销。'
|
|
483
|
+
: '暂无历史快照——快照在编辑器或 card_write 每次写卡时自动累积。',
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
// ---- card_rollback(回滚到指定快照,默认最新)----
|
|
488
|
+
export async function cardRollback(id, { ts, verify = true } = {}) {
|
|
489
|
+
const r = await readSnapshots(id)
|
|
490
|
+
if (r.error) return r
|
|
491
|
+
if (!r.snapshots.length) {
|
|
492
|
+
return { source: r.source, id, error: 'no-history', message: '该卡片暂无历史快照,无法回滚。' }
|
|
493
|
+
}
|
|
494
|
+
const target = ts != null ? r.snapshots.find((s) => s.ts === Number(ts)) : r.snapshots[r.snapshots.length - 1]
|
|
495
|
+
if (!target) {
|
|
496
|
+
return {
|
|
497
|
+
source: r.source,
|
|
498
|
+
id,
|
|
499
|
+
error: 'snapshot-not-found',
|
|
500
|
+
message: `快照 ts=${ts} 不存在。`,
|
|
501
|
+
available: r.snapshots.map((s) => s.ts),
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
// 安全网:先把「回滚前的当前版本」另存为新快照,保证回滚本身可撤销。
|
|
506
|
+
let safetyTs = null
|
|
507
|
+
const current = await cardRead(id)
|
|
508
|
+
if (current && current.schema && typeof current.schema === 'object') {
|
|
509
|
+
if (r.source === 'http') {
|
|
510
|
+
try {
|
|
511
|
+
const { status, json } = await httpPost(`/__history/${encodeURIComponent(id)}`, { schema: current.schema })
|
|
512
|
+
if (status === 200 && json && json.ok) safetyTs = json.ts
|
|
513
|
+
} catch {
|
|
514
|
+
/* 安全快照失败不阻断回滚,但会在结果中标注 */
|
|
515
|
+
}
|
|
516
|
+
} else {
|
|
517
|
+
try {
|
|
518
|
+
safetyTs = saveSnapshot(path.join(ROOT, 'src', 'cards', id), current.schema).ts
|
|
519
|
+
} catch {
|
|
520
|
+
/* 同上 */
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
const written = await cardWrite(id, { schema: target.schema }, { verify, snapshot: false })
|
|
526
|
+
if (written.error) return { source: written.source, id, error: written.error, message: written.message }
|
|
527
|
+
return {
|
|
528
|
+
source: written.source,
|
|
529
|
+
id,
|
|
530
|
+
rolledBackTo: target.ts,
|
|
531
|
+
rolledBackToTime: new Date(target.ts).toISOString(),
|
|
532
|
+
safetySnapshotTs: safetyTs,
|
|
533
|
+
...(safetyTs == null ? { warning: '回滚前版本未能另存为安全快照,本次回滚不可通过快照撤销。' } : {}),
|
|
534
|
+
written: written.written,
|
|
535
|
+
...(verify ? { verify: written.verify } : {}),
|
|
536
|
+
}
|
|
537
|
+
}
|