@sarjallab09/figma-intelligence 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +67 -36
- package/dist/bin/cli.js +2 -0
- package/dist/design-bridge/bridge.js +2 -0
- package/dist/figma-bridge-plugin/bridge-relay.js +2 -0
- package/dist/figma-bridge-plugin/code.js +1 -0
- package/{figma-bridge-plugin → dist/figma-bridge-plugin}/package-lock.json +0 -3
- package/dist/figma-bridge-plugin/ui.html +4970 -0
- package/dist/figma-intelligence-layer/dist/index.js +2 -0
- package/dist/scripts/clean-existing-chunks.js +2 -0
- package/dist/scripts/connect-ai-tool.js +2 -0
- package/dist/scripts/convert-hub-pdfs.js +2 -0
- package/dist/scripts/figma-mcp-status.js +2 -0
- package/dist/scripts/register-codex-mcp.js +2 -0
- package/dist/scripts/test-copilot-chat.js +2 -0
- package/package.json +11 -8
- package/bin/cli.js +0 -859
- package/design-bridge/bridge.js +0 -196
- package/design-bridge/lib/assets.js +0 -367
- package/design-bridge/lib/prompt.js +0 -85
- package/design-bridge/lib/server.js +0 -66
- package/design-bridge/lib/stitch.js +0 -37
- package/design-bridge/lib/tokens.js +0 -82
- package/design-bridge/package-lock.json +0 -579
- package/figma-bridge-plugin/README.md +0 -97
- package/figma-bridge-plugin/anthropic-chat-runner.js +0 -192
- package/figma-bridge-plugin/bridge-relay.js +0 -2505
- package/figma-bridge-plugin/chat-runner.js +0 -485
- package/figma-bridge-plugin/code.js +0 -1534
- package/figma-bridge-plugin/codex-runner.js +0 -505
- package/figma-bridge-plugin/component-schemas.js +0 -110
- package/figma-bridge-plugin/content-context.js +0 -869
- package/figma-bridge-plugin/create-button.js +0 -216
- package/figma-bridge-plugin/gemini-cli-runner.js +0 -291
- package/figma-bridge-plugin/gemini-runner.js +0 -187
- package/figma-bridge-plugin/html-to-figma.js +0 -927
- package/figma-bridge-plugin/knowledge-hub/.gitkeep +0 -0
- package/figma-bridge-plugin/knowledge-hub/uspec-references/anatomy-spec.md +0 -159
- package/figma-bridge-plugin/knowledge-hub/uspec-references/api-spec.md +0 -162
- package/figma-bridge-plugin/knowledge-hub/uspec-references/color-spec.md +0 -148
- package/figma-bridge-plugin/knowledge-hub/uspec-references/full-spec-template.md +0 -314
- package/figma-bridge-plugin/knowledge-hub/uspec-references/property-spec.md +0 -175
- package/figma-bridge-plugin/knowledge-hub/uspec-references/screen-reader-spec.md +0 -180
- package/figma-bridge-plugin/knowledge-hub/uspec-references/structure-spec.md +0 -165
- package/figma-bridge-plugin/perplexity-runner.js +0 -188
- package/figma-bridge-plugin/references/SKILL.md +0 -178
- package/figma-bridge-plugin/references/anatomy-spec.md +0 -159
- package/figma-bridge-plugin/references/api-spec.md +0 -162
- package/figma-bridge-plugin/references/color-spec.md +0 -148
- package/figma-bridge-plugin/references/full-spec-template.md +0 -314
- package/figma-bridge-plugin/references/property-spec.md +0 -175
- package/figma-bridge-plugin/references/screen-reader-spec.md +0 -180
- package/figma-bridge-plugin/references/structure-spec.md +0 -165
- package/figma-bridge-plugin/shared-prompt-config.js +0 -645
- package/figma-bridge-plugin/spec-helpers/build-table.js +0 -269
- package/figma-bridge-plugin/spec-helpers/classify-elements.js +0 -189
- package/figma-bridge-plugin/spec-helpers/index.js +0 -35
- package/figma-bridge-plugin/spec-helpers/parse-figma-link.js +0 -49
- package/figma-bridge-plugin/spec-helpers/position-markers.js +0 -158
- package/figma-bridge-plugin/stitch-auth.js +0 -322
- package/figma-bridge-plugin/stitch-runner.js +0 -1427
- package/figma-bridge-plugin/token-resolver.js +0 -107
- package/figma-bridge-plugin/ui.html +0 -4542
- package/figma-intelligence-layer/.env.example +0 -39
- package/figma-intelligence-layer/docs/local-image-generation.md +0 -60
- package/figma-intelligence-layer/examples/comfyui-workflow-template.example.json +0 -101
- package/figma-intelligence-layer/jest.config.js +0 -14
- package/figma-intelligence-layer/mcp-config.json +0 -19
- package/figma-intelligence-layer/package-lock.json +0 -5892
- package/figma-intelligence-layer/scripts/setup-comfyui-local.sh +0 -67
- package/figma-intelligence-layer/scripts/start-comfyui.sh +0 -33
- package/figma-intelligence-layer/src/index.ts +0 -2233
- package/figma-intelligence-layer/src/shared/auto-layout-validator.ts +0 -404
- package/figma-intelligence-layer/src/shared/cache.ts +0 -187
- package/figma-intelligence-layer/src/shared/color-operations.ts +0 -533
- package/figma-intelligence-layer/src/shared/color-utils.ts +0 -138
- package/figma-intelligence-layer/src/shared/component-script-builder.ts +0 -413
- package/figma-intelligence-layer/src/shared/component-templates.ts +0 -2767
- package/figma-intelligence-layer/src/shared/concept-taxonomy.ts +0 -694
- package/figma-intelligence-layer/src/shared/decision-log.ts +0 -128
- package/figma-intelligence-layer/src/shared/design-system-context.ts +0 -568
- package/figma-intelligence-layer/src/shared/design-system-intelligence.ts +0 -131
- package/figma-intelligence-layer/src/shared/design-system-matcher.ts +0 -184
- package/figma-intelligence-layer/src/shared/design-system-normalizers.ts +0 -196
- package/figma-intelligence-layer/src/shared/design-system-tokens.ts +0 -295
- package/figma-intelligence-layer/src/shared/dtcg-validator.ts +0 -530
- package/figma-intelligence-layer/src/shared/enrichment-pipeline.ts +0 -671
- package/figma-intelligence-layer/src/shared/figma-bridge.ts +0 -1418
- package/figma-intelligence-layer/src/shared/font-config.ts +0 -126
- package/figma-intelligence-layer/src/shared/icon-catalog.ts +0 -360
- package/figma-intelligence-layer/src/shared/icon-fetch.ts +0 -80
- package/figma-intelligence-layer/src/shared/prototype-script-builder.ts +0 -162
- package/figma-intelligence-layer/src/shared/response-compression.ts +0 -440
- package/figma-intelligence-layer/src/shared/semantic-token-catalog.ts +0 -324
- package/figma-intelligence-layer/src/shared/token-binder.ts +0 -505
- package/figma-intelligence-layer/src/shared/token-math.ts +0 -427
- package/figma-intelligence-layer/src/shared/token-naming.ts +0 -468
- package/figma-intelligence-layer/src/shared/token-utils.ts +0 -420
- package/figma-intelligence-layer/src/shared/types.ts +0 -346
- package/figma-intelligence-layer/src/shared/typography-presets.ts +0 -94
- package/figma-intelligence-layer/src/shared/unsplash.ts +0 -165
- package/figma-intelligence-layer/src/shared/vision-client.ts +0 -607
- package/figma-intelligence-layer/src/shared/vision-provider-anthropic.ts +0 -334
- package/figma-intelligence-layer/src/shared/vision-provider-openai.ts +0 -446
- package/figma-intelligence-layer/src/tools/phase1-vision/a11y-audit/a11y-annotate-handler.ts +0 -782
- package/figma-intelligence-layer/src/tools/phase1-vision/a11y-audit/a11y-annotate-renderer.ts +0 -496
- package/figma-intelligence-layer/src/tools/phase1-vision/a11y-audit/a11y-annotation-kit.ts +0 -230
- package/figma-intelligence-layer/src/tools/phase1-vision/a11y-audit/colorblind-sim.ts +0 -66
- package/figma-intelligence-layer/src/tools/phase1-vision/a11y-audit/index.ts +0 -810
- package/figma-intelligence-layer/src/tools/phase1-vision/a11y-audit/keyboard-sr-order-analyzer.ts +0 -1191
- package/figma-intelligence-layer/src/tools/phase1-vision/a11y-audit/keyboard-sr-order-figma-page.ts +0 -1346
- package/figma-intelligence-layer/src/tools/phase1-vision/a11y-audit/keyboard-sr-order-handler.ts +0 -148
- package/figma-intelligence-layer/src/tools/phase1-vision/a11y-audit/vpat-figma-page.ts +0 -499
- package/figma-intelligence-layer/src/tools/phase1-vision/a11y-audit/vpat-report.ts +0 -910
- package/figma-intelligence-layer/src/tools/phase1-vision/a11y-audit/wcag-checker.ts +0 -989
- package/figma-intelligence-layer/src/tools/phase1-vision/a11y-audit/wcag-criteria.ts +0 -1160
- package/figma-intelligence-layer/src/tools/phase1-vision/design-from-ref/index.ts +0 -424
- package/figma-intelligence-layer/src/tools/phase1-vision/screen-cloner/component-recognizer.ts +0 -38
- package/figma-intelligence-layer/src/tools/phase1-vision/screen-cloner/ds-matcher.ts +0 -111
- package/figma-intelligence-layer/src/tools/phase1-vision/screen-cloner/font-matcher.ts +0 -114
- package/figma-intelligence-layer/src/tools/phase1-vision/screen-cloner/icon-resolver.ts +0 -103
- package/figma-intelligence-layer/src/tools/phase1-vision/screen-cloner/index.ts +0 -1060
- package/figma-intelligence-layer/src/tools/phase1-vision/screen-cloner/layout-segmenter.ts +0 -18
- package/figma-intelligence-layer/src/tools/phase1-vision/screen-cloner/token-inferencer.ts +0 -39
- package/figma-intelligence-layer/src/tools/phase1-vision/screen-cloner/vision-pipeline.ts +0 -58
- package/figma-intelligence-layer/src/tools/phase1-vision/sketch-to-design/index.ts +0 -298
- package/figma-intelligence-layer/src/tools/phase1-vision/visual-audit/index.ts +0 -197
- package/figma-intelligence-layer/src/tools/phase2-accuracy/component-audit/index.ts +0 -494
- package/figma-intelligence-layer/src/tools/phase2-accuracy/intent-translator/index.ts +0 -356
- package/figma-intelligence-layer/src/tools/phase2-accuracy/layout-intelligence/container-patterns.ts +0 -123
- package/figma-intelligence-layer/src/tools/phase2-accuracy/layout-intelligence/index.ts +0 -663
- package/figma-intelligence-layer/src/tools/phase2-accuracy/lint-rules/built-in-rules.yaml +0 -56
- package/figma-intelligence-layer/src/tools/phase2-accuracy/lint-rules/index.ts +0 -614
- package/figma-intelligence-layer/src/tools/phase2-accuracy/lint-rules/rule-engine.ts +0 -113
- package/figma-intelligence-layer/src/tools/phase2-accuracy/theme-generator/color-theory.ts +0 -178
- package/figma-intelligence-layer/src/tools/phase2-accuracy/theme-generator/index.ts +0 -470
- package/figma-intelligence-layer/src/tools/phase2-accuracy/variant-expander/index.ts +0 -429
- package/figma-intelligence-layer/src/tools/phase2-accuracy/variant-expander/token-override-maps.ts +0 -226
- package/figma-intelligence-layer/src/tools/phase3-generation/ai-image-insert/index.ts +0 -535
- package/figma-intelligence-layer/src/tools/phase3-generation/component-archaeologist/index.ts +0 -660
- package/figma-intelligence-layer/src/tools/phase3-generation/component-archaeologist/pattern-fingerprints.ts +0 -209
- package/figma-intelligence-layer/src/tools/phase3-generation/composition-builder/index.ts +0 -540
- package/figma-intelligence-layer/src/tools/phase3-generation/figma-animated-build.ts +0 -391
- package/figma-intelligence-layer/src/tools/phase3-generation/page-architect/index.ts +0 -2019
- package/figma-intelligence-layer/src/tools/phase3-generation/page-architect/screen-templates.ts +0 -131
- package/figma-intelligence-layer/src/tools/phase3-generation/prototype-map/index.ts +0 -381
- package/figma-intelligence-layer/src/tools/phase3-generation/prototype-wire/index.ts +0 -565
- package/figma-intelligence-layer/src/tools/phase3-generation/swarm-build/index.ts +0 -764
- package/figma-intelligence-layer/src/tools/phase3-generation/system-drift/index.ts +0 -535
- package/figma-intelligence-layer/src/tools/phase3-generation/unsplash-search/index.ts +0 -84
- package/figma-intelligence-layer/src/tools/phase3-generation/url-to-frame/index.ts +0 -401
- package/figma-intelligence-layer/src/tools/phase4-sync/animation-specifier/code-generators/css-animations.ts +0 -68
- package/figma-intelligence-layer/src/tools/phase4-sync/animation-specifier/code-generators/framer-motion.ts +0 -78
- package/figma-intelligence-layer/src/tools/phase4-sync/animation-specifier/code-generators/swift-animations.ts +0 -93
- package/figma-intelligence-layer/src/tools/phase4-sync/animation-specifier/index.ts +0 -596
- package/figma-intelligence-layer/src/tools/phase4-sync/ci-check/index.ts +0 -462
- package/figma-intelligence-layer/src/tools/phase4-sync/export-tokens/index.ts +0 -1470
- package/figma-intelligence-layer/src/tools/phase4-sync/generate-component-code/index.ts +0 -829
- package/figma-intelligence-layer/src/tools/phase4-sync/handoff-spec/index.ts +0 -702
- package/figma-intelligence-layer/src/tools/phase4-sync/icon-library-sync/index.ts +0 -483
- package/figma-intelligence-layer/src/tools/phase4-sync/sync-from-code/index.ts +0 -501
- package/figma-intelligence-layer/src/tools/phase4-sync/sync-from-code/storybook-parser.ts +0 -106
- package/figma-intelligence-layer/src/tools/phase4-sync/watch-docs/index.ts +0 -676
- package/figma-intelligence-layer/src/tools/phase4-sync/webhook-listener/index.ts +0 -560
- package/figma-intelligence-layer/src/tools/phase5-governance/apg-doc/index.ts +0 -1043
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/component-detection.ts +0 -620
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/extractors/anatomy.ts +0 -331
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/extractors/color-tokens.ts +0 -77
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/extractors/properties.ts +0 -54
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/extractors/snapshot.ts +0 -287
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/extractors/spacing.ts +0 -71
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/extractors/states.ts +0 -43
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/extractors/typography.ts +0 -71
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/index.ts +0 -221
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/_default.ts +0 -166
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/accordion.ts +0 -232
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/alert.ts +0 -234
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/avatar-group.ts +0 -270
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/avatar.ts +0 -249
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/badge.ts +0 -231
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/banner.ts +0 -293
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/breadcrumb.ts +0 -240
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/button.ts +0 -243
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/calendar.ts +0 -307
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/card.ts +0 -143
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/checkbox.ts +0 -227
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/chip.ts +0 -233
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/combobox.ts +0 -282
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/datepicker.ts +0 -276
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/divider.ts +0 -223
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/drawer.ts +0 -255
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/dropdown-menu.ts +0 -289
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/empty-state.ts +0 -261
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/file-uploader.ts +0 -290
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/form.ts +0 -265
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/grid.ts +0 -238
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/icon.ts +0 -255
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/index.ts +0 -128
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/inline-edit.ts +0 -286
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/inline-message.ts +0 -255
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/input.ts +0 -330
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/link.ts +0 -247
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/list.ts +0 -250
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/menu.ts +0 -247
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/modal.ts +0 -144
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/navbar.ts +0 -264
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/navigation.ts +0 -251
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/number-input.ts +0 -261
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/pagination.ts +0 -248
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/popover.ts +0 -270
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/progress.ts +0 -251
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/radio.ts +0 -142
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/range-slider.ts +0 -282
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/rating.ts +0 -250
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/search.ts +0 -258
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/segmented-control.ts +0 -265
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/select.ts +0 -319
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/skeleton.ts +0 -256
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/slider.ts +0 -232
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/spinner.ts +0 -239
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/status-dot.ts +0 -252
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/stepper.ts +0 -270
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/table.ts +0 -244
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/tabs.ts +0 -143
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/tag.ts +0 -243
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/textarea.ts +0 -259
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/time-picker.ts +0 -293
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/toast.ts +0 -144
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/toggle.ts +0 -289
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/toolbar.ts +0 -267
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/tooltip.ts +0 -232
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/treeview.ts +0 -257
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/typography.ts +0 -319
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/legacy-compat.ts +0 -121
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/renderers/anatomy-diagram.ts +0 -430
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/renderers/figma-page.ts +0 -312
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/renderers/json.ts +0 -129
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/renderers/markdown.ts +0 -78
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/renderers/visual-doc.ts +0 -2333
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/sections/accessibility.ts +0 -100
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/sections/anatomy.ts +0 -32
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/sections/color-tokens.ts +0 -59
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/sections/content-guidance.ts +0 -18
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/sections/design-tokens.ts +0 -53
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/sections/interaction-rules.ts +0 -19
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/sections/overview.ts +0 -91
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/sections/properties-api.ts +0 -71
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/sections/qa-criteria.ts +0 -19
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/sections/related-components.ts +0 -110
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/sections/responsive.ts +0 -19
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/sections/size-specs.ts +0 -67
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/sections/spacing-structure.ts +0 -58
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/sections/state-specs.ts +0 -79
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/sections/states.ts +0 -50
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/sections/type-hierarchy.ts +0 -33
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/sections/typography.ts +0 -55
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/sections/usage-guidelines.ts +0 -73
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/sections/variants.ts +0 -81
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/types.ts +0 -409
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec-sheet/index.ts +0 -198
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec-sheet/renderer.ts +0 -701
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec-sheet/types.ts +0 -88
- package/figma-intelligence-layer/src/tools/phase5-governance/decision-log/index.ts +0 -135
- package/figma-intelligence-layer/src/tools/phase5-governance/design-decision-log/index.ts +0 -491
- package/figma-intelligence-layer/src/tools/phase5-governance/ds-primitives/index.ts +0 -416
- package/figma-intelligence-layer/src/tools/phase5-governance/ds-scaffolder/index.ts +0 -722
- package/figma-intelligence-layer/src/tools/phase5-governance/ds-variables/index.ts +0 -449
- package/figma-intelligence-layer/src/tools/phase5-governance/health-report/index.ts +0 -393
- package/figma-intelligence-layer/src/tools/phase5-governance/taxonomy-docs/index.ts +0 -406
- package/figma-intelligence-layer/src/tools/phase5-governance/taxonomy-docs/renderers/figma-page.ts +0 -292
- package/figma-intelligence-layer/src/tools/phase5-governance/taxonomy-docs/renderers/json.ts +0 -24
- package/figma-intelligence-layer/src/tools/phase5-governance/taxonomy-docs/renderers/markdown.ts +0 -172
- package/figma-intelligence-layer/src/tools/phase5-governance/taxonomy-docs/renderers/naming-guide.ts +0 -409
- package/figma-intelligence-layer/src/tools/phase5-governance/token-analytics/index.ts +0 -594
- package/figma-intelligence-layer/src/tools/phase5-governance/token-docs/index.ts +0 -710
- package/figma-intelligence-layer/src/tools/phase5-governance/token-migrate/index.ts +0 -458
- package/figma-intelligence-layer/src/tools/phase5-governance/token-naming/index.ts +0 -134
- package/figma-intelligence-layer/tests/apg-doc.test.ts +0 -101
- package/figma-intelligence-layer/tests/design-system-context.test.ts +0 -152
- package/figma-intelligence-layer/tests/design-system-matcher.test.ts +0 -144
- package/figma-intelligence-layer/tests/figma-bridge.test.ts +0 -83
- package/figma-intelligence-layer/tests/generate-image-and-insert.test.ts +0 -56
- package/figma-intelligence-layer/tests/screen-cloner-regression.test.ts +0 -69
- package/figma-intelligence-layer/tests/smoke.test.ts +0 -174
- package/figma-intelligence-layer/tests/spec-generator.test.ts +0 -127
- package/figma-intelligence-layer/tests/token-migrate.test.ts +0 -21
- package/figma-intelligence-layer/tests/token-naming.test.ts +0 -30
- package/figma-intelligence-layer/tsconfig.json +0 -19
- package/scripts/clean-existing-chunks.js +0 -179
- package/scripts/connect-ai-tool.js +0 -490
- package/scripts/convert-hub-pdfs.js +0 -425
- package/scripts/figma-mcp-status.js +0 -349
- package/scripts/register-codex-mcp.js +0 -96
- /package/{design-bridge → dist/design-bridge}/.env.example +0 -0
- /package/{design-bridge → dist/design-bridge}/package.json +0 -0
- /package/{figma-bridge-plugin → dist/figma-bridge-plugin}/manifest.json +0 -0
- /package/{figma-bridge-plugin → dist/figma-bridge-plugin}/package.json +0 -0
- /package/{figma-intelligence-layer → dist/figma-intelligence-layer}/package.json +0 -0
|
@@ -1,560 +0,0 @@
|
|
|
1
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
2
|
-
// Webhook Listener
|
|
3
|
-
// Manages Figma webhook subscriptions and routes incoming events to configured
|
|
4
|
-
// triggers (lint runs, Slack notifications, GitHub PR comments, etc.).
|
|
5
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
6
|
-
|
|
7
|
-
import fs from "fs/promises";
|
|
8
|
-
import path from "path";
|
|
9
|
-
import { decisionLog } from "../../../shared/decision-log.js";
|
|
10
|
-
|
|
11
|
-
// ─── Public types ─────────────────────────────────────────────────────────────
|
|
12
|
-
|
|
13
|
-
export type WebhookEvent = "FILE_UPDATE" | "LIBRARY_PUBLISH" | "COMMENT_CREATED";
|
|
14
|
-
export type WebhookAction = "subscribe" | "unsubscribe" | "list" | "test";
|
|
15
|
-
|
|
16
|
-
export interface WebhookListenerArgs {
|
|
17
|
-
action: WebhookAction;
|
|
18
|
-
fileKey: string;
|
|
19
|
-
events?: WebhookEvent[];
|
|
20
|
-
triggers?: {
|
|
21
|
-
onTokenChange?: "run-lint" | "notify-slack" | "run-parity-check" | "all";
|
|
22
|
-
onComponentChange?: "run-audit" | "notify-slack" | "sync-storybook" | "all";
|
|
23
|
-
onLibraryPublish?: "run-health-report" | "notify-all" | "create-pr-comment";
|
|
24
|
-
};
|
|
25
|
-
notificationChannels?: {
|
|
26
|
-
slack?: string;
|
|
27
|
-
github?: string;
|
|
28
|
-
email?: string[];
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export interface WebhookSubscription {
|
|
33
|
-
webhookId: string;
|
|
34
|
-
fileKey: string;
|
|
35
|
-
events: WebhookEvent[];
|
|
36
|
-
triggers: WebhookListenerArgs["triggers"];
|
|
37
|
-
notificationChannels: WebhookListenerArgs["notificationChannels"];
|
|
38
|
-
createdAt: string;
|
|
39
|
-
passcode: string;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export interface WebhookListenerResult {
|
|
43
|
-
action: WebhookAction;
|
|
44
|
-
subscriptions: WebhookSubscription[];
|
|
45
|
-
message: string;
|
|
46
|
-
testResult?: Record<string, unknown>;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// ─── Persistence ──────────────────────────────────────────────────────────────
|
|
50
|
-
|
|
51
|
-
const SUBSCRIPTIONS_PATH =
|
|
52
|
-
process.env.WEBHOOK_SUBSCRIPTIONS_PATH ||
|
|
53
|
-
path.join(process.cwd(), ".figma-webhooks.json");
|
|
54
|
-
|
|
55
|
-
async function readSubscriptions(): Promise<WebhookSubscription[]> {
|
|
56
|
-
try {
|
|
57
|
-
const data = await fs.readFile(SUBSCRIPTIONS_PATH, "utf-8");
|
|
58
|
-
return JSON.parse(data) as WebhookSubscription[];
|
|
59
|
-
} catch {
|
|
60
|
-
return [];
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
async function writeSubscriptions(subs: WebhookSubscription[]): Promise<void> {
|
|
65
|
-
await fs.writeFile(SUBSCRIPTIONS_PATH, JSON.stringify(subs, null, 2), "utf-8");
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// ─── Figma REST API helpers ───────────────────────────────────────────────────
|
|
69
|
-
|
|
70
|
-
const FIGMA_API_BASE = "https://api.figma.com";
|
|
71
|
-
|
|
72
|
-
function figmaToken(): string {
|
|
73
|
-
const token = process.env.FIGMA_ACCESS_TOKEN;
|
|
74
|
-
if (!token) throw new Error("webhookListener: FIGMA_ACCESS_TOKEN environment variable is required.");
|
|
75
|
-
return token;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
function webhookEndpoint(): string {
|
|
79
|
-
const endpoint = process.env.FIGMA_WEBHOOK_ENDPOINT;
|
|
80
|
-
if (!endpoint) {
|
|
81
|
-
// Fall back to a localhost endpoint for local testing
|
|
82
|
-
return "https://localhost:3000/figma-webhook";
|
|
83
|
-
}
|
|
84
|
-
return endpoint;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
async function figmaPost(
|
|
88
|
-
path_: string,
|
|
89
|
-
body: Record<string, unknown>
|
|
90
|
-
): Promise<Record<string, unknown>> {
|
|
91
|
-
const response = await fetch(`${FIGMA_API_BASE}${path_}`, {
|
|
92
|
-
method: "POST",
|
|
93
|
-
headers: {
|
|
94
|
-
"Content-Type": "application/json",
|
|
95
|
-
"X-FIGMA-TOKEN": figmaToken(),
|
|
96
|
-
},
|
|
97
|
-
body: JSON.stringify(body),
|
|
98
|
-
signal: AbortSignal.timeout(15000),
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
if (!response.ok) {
|
|
102
|
-
const text = await response.text().catch(() => "");
|
|
103
|
-
throw new Error(`Figma API POST ${path_} failed: HTTP ${response.status} — ${text}`);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
return (await response.json()) as Record<string, unknown>;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
async function figmaDelete(path_: string): Promise<void> {
|
|
110
|
-
const response = await fetch(`${FIGMA_API_BASE}${path_}`, {
|
|
111
|
-
method: "DELETE",
|
|
112
|
-
headers: { "X-FIGMA-TOKEN": figmaToken() },
|
|
113
|
-
signal: AbortSignal.timeout(15000),
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
if (!response.ok && response.status !== 404) {
|
|
117
|
-
const text = await response.text().catch(() => "");
|
|
118
|
-
throw new Error(`Figma API DELETE ${path_} failed: HTTP ${response.status} — ${text}`);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
async function figmaGet(path_: string): Promise<Record<string, unknown>> {
|
|
123
|
-
const response = await fetch(`${FIGMA_API_BASE}${path_}`, {
|
|
124
|
-
headers: { "X-FIGMA-TOKEN": figmaToken() },
|
|
125
|
-
signal: AbortSignal.timeout(15000),
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
if (!response.ok) {
|
|
129
|
-
const text = await response.text().catch(() => "");
|
|
130
|
-
throw new Error(`Figma API GET ${path_} failed: HTTP ${response.status} — ${text}`);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
return (await response.json()) as Record<string, unknown>;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
function generatePasscode(): string {
|
|
137
|
-
return Math.random().toString(36).slice(2) + Math.random().toString(36).slice(2);
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
// ─── Notification senders ─────────────────────────────────────────────────────
|
|
141
|
-
|
|
142
|
-
async function postSlackNotification(
|
|
143
|
-
webhookUrl: string,
|
|
144
|
-
message: string,
|
|
145
|
-
fields: Array<{ title: string; value: string; short?: boolean }>
|
|
146
|
-
): Promise<boolean> {
|
|
147
|
-
try {
|
|
148
|
-
const body = {
|
|
149
|
-
text: message,
|
|
150
|
-
attachments: [
|
|
151
|
-
{
|
|
152
|
-
color: "#6366f1",
|
|
153
|
-
fields: fields.map((f) => ({
|
|
154
|
-
title: f.title,
|
|
155
|
-
value: f.value,
|
|
156
|
-
short: f.short ?? true,
|
|
157
|
-
})),
|
|
158
|
-
ts: Math.floor(Date.now() / 1000),
|
|
159
|
-
},
|
|
160
|
-
],
|
|
161
|
-
};
|
|
162
|
-
|
|
163
|
-
const response = await fetch(webhookUrl, {
|
|
164
|
-
method: "POST",
|
|
165
|
-
headers: { "Content-Type": "application/json" },
|
|
166
|
-
body: JSON.stringify(body),
|
|
167
|
-
signal: AbortSignal.timeout(10000),
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
return response.ok;
|
|
171
|
-
} catch {
|
|
172
|
-
return false;
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
async function postGitHubComment(
|
|
177
|
-
repoSlug: string,
|
|
178
|
-
body: string
|
|
179
|
-
): Promise<boolean> {
|
|
180
|
-
const githubToken = process.env.GITHUB_TOKEN;
|
|
181
|
-
if (!githubToken) return false;
|
|
182
|
-
|
|
183
|
-
// repoSlug format: "owner/repo#issue_or_pr_number"
|
|
184
|
-
const match = repoSlug.match(/^([^/]+)\/([^#]+)#(\d+)$/);
|
|
185
|
-
if (!match) return false;
|
|
186
|
-
|
|
187
|
-
const [, owner, repo, number] = match;
|
|
188
|
-
|
|
189
|
-
try {
|
|
190
|
-
const response = await fetch(
|
|
191
|
-
`https://api.github.com/repos/${owner}/${repo}/issues/${number}/comments`,
|
|
192
|
-
{
|
|
193
|
-
method: "POST",
|
|
194
|
-
headers: {
|
|
195
|
-
Authorization: `Bearer ${githubToken}`,
|
|
196
|
-
"Content-Type": "application/json",
|
|
197
|
-
Accept: "application/vnd.github.v3+json",
|
|
198
|
-
},
|
|
199
|
-
body: JSON.stringify({ body }),
|
|
200
|
-
signal: AbortSignal.timeout(10000),
|
|
201
|
-
}
|
|
202
|
-
);
|
|
203
|
-
return response.ok;
|
|
204
|
-
} catch {
|
|
205
|
-
return false;
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
// ─── Webhook event handler ────────────────────────────────────────────────────
|
|
210
|
-
|
|
211
|
-
export interface WebhookEventPayload {
|
|
212
|
-
event_type: WebhookEvent;
|
|
213
|
-
file_key?: string;
|
|
214
|
-
file_name?: string;
|
|
215
|
-
description?: string;
|
|
216
|
-
timestamp?: string;
|
|
217
|
-
passcode?: string;
|
|
218
|
-
created_at?: string;
|
|
219
|
-
comment?: unknown;
|
|
220
|
-
library_action?: string;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
export async function handleWebhookEvent(
|
|
224
|
-
event: WebhookEventPayload,
|
|
225
|
-
subscription: WebhookSubscription
|
|
226
|
-
): Promise<Record<string, unknown>> {
|
|
227
|
-
const results: Record<string, unknown> = {
|
|
228
|
-
eventType: event.event_type,
|
|
229
|
-
handled: false,
|
|
230
|
-
actions: [] as string[],
|
|
231
|
-
notifications: [] as string[],
|
|
232
|
-
};
|
|
233
|
-
|
|
234
|
-
const actions = results["actions"] as string[];
|
|
235
|
-
const notifications = results["notifications"] as string[];
|
|
236
|
-
|
|
237
|
-
const { triggers, notificationChannels } = subscription;
|
|
238
|
-
|
|
239
|
-
const isTokenChange =
|
|
240
|
-
event.event_type === "FILE_UPDATE" &&
|
|
241
|
-
event.description?.toLowerCase().includes("token");
|
|
242
|
-
|
|
243
|
-
const isComponentChange =
|
|
244
|
-
event.event_type === "FILE_UPDATE" &&
|
|
245
|
-
!isTokenChange;
|
|
246
|
-
|
|
247
|
-
const isLibraryPublish = event.event_type === "LIBRARY_PUBLISH";
|
|
248
|
-
|
|
249
|
-
// ── TOKEN CHANGE routing ─────────────────────────────────────────────────
|
|
250
|
-
if (isTokenChange && triggers?.onTokenChange) {
|
|
251
|
-
const trigger = triggers.onTokenChange;
|
|
252
|
-
|
|
253
|
-
if (trigger === "run-lint" || trigger === "all") {
|
|
254
|
-
actions.push("Triggered lint rule run on updated file tokens.");
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
if (trigger === "run-parity-check" || trigger === "all") {
|
|
258
|
-
actions.push("Triggered token parity check against canonical library.");
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
if ((trigger === "notify-slack" || trigger === "all") && notificationChannels?.slack) {
|
|
262
|
-
const sent = await postSlackNotification(
|
|
263
|
-
notificationChannels.slack,
|
|
264
|
-
`Figma Token Change Detected in ${event.file_name ?? event.file_key}`,
|
|
265
|
-
[
|
|
266
|
-
{ title: "File", value: event.file_name ?? event.file_key ?? "Unknown" },
|
|
267
|
-
{ title: "Time", value: event.timestamp ?? new Date().toISOString() },
|
|
268
|
-
{ title: "Action", value: "Token values were modified" },
|
|
269
|
-
]
|
|
270
|
-
);
|
|
271
|
-
notifications.push(sent ? "Slack notification sent" : "Slack notification failed");
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
// ── COMPONENT CHANGE routing ─────────────────────────────────────────────
|
|
276
|
-
if (isComponentChange && triggers?.onComponentChange) {
|
|
277
|
-
const trigger = triggers.onComponentChange;
|
|
278
|
-
|
|
279
|
-
if (trigger === "run-audit" || trigger === "all") {
|
|
280
|
-
actions.push("Triggered component audit on updated file.");
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
if (trigger === "sync-storybook" || trigger === "all") {
|
|
284
|
-
actions.push("Triggered Storybook sync check for modified components.");
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
if ((trigger === "notify-slack" || trigger === "all") && notificationChannels?.slack) {
|
|
288
|
-
const sent = await postSlackNotification(
|
|
289
|
-
notificationChannels.slack,
|
|
290
|
-
`Figma Component Change Detected in ${event.file_name ?? event.file_key}`,
|
|
291
|
-
[
|
|
292
|
-
{ title: "File", value: event.file_name ?? event.file_key ?? "Unknown" },
|
|
293
|
-
{ title: "Time", value: event.timestamp ?? new Date().toISOString() },
|
|
294
|
-
{ title: "Action", value: "Component definitions were modified" },
|
|
295
|
-
]
|
|
296
|
-
);
|
|
297
|
-
notifications.push(sent ? "Slack notification sent" : "Slack notification failed");
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
// ── LIBRARY PUBLISH routing ──────────────────────────────────────────────
|
|
302
|
-
if (isLibraryPublish && triggers?.onLibraryPublish) {
|
|
303
|
-
const trigger = triggers.onLibraryPublish;
|
|
304
|
-
|
|
305
|
-
if (trigger === "run-health-report" || trigger === "notify-all") {
|
|
306
|
-
actions.push("Triggered design system health report after library publish.");
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
if (trigger === "create-pr-comment" || trigger === "notify-all") {
|
|
310
|
-
if (notificationChannels?.github) {
|
|
311
|
-
const body = [
|
|
312
|
-
"## Figma Library Published",
|
|
313
|
-
"",
|
|
314
|
-
`A new version of the Figma design library has been published.`,
|
|
315
|
-
"",
|
|
316
|
-
`**File:** ${event.file_name ?? event.file_key ?? "Unknown"}`,
|
|
317
|
-
`**Time:** ${event.timestamp ?? new Date().toISOString()}`,
|
|
318
|
-
`**Description:** ${event.description ?? "No description provided"}`,
|
|
319
|
-
"",
|
|
320
|
-
"Please review the changes and update component implementations accordingly.",
|
|
321
|
-
].join("\n");
|
|
322
|
-
|
|
323
|
-
const sent = await postGitHubComment(notificationChannels.github, body);
|
|
324
|
-
notifications.push(sent ? "GitHub PR comment posted" : "GitHub PR comment failed");
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
if (trigger === "notify-all" && notificationChannels?.slack) {
|
|
329
|
-
const sent = await postSlackNotification(
|
|
330
|
-
notificationChannels.slack,
|
|
331
|
-
`Figma Library Published: ${event.file_name ?? event.file_key}`,
|
|
332
|
-
[
|
|
333
|
-
{ title: "File", value: event.file_name ?? event.file_key ?? "Unknown" },
|
|
334
|
-
{ title: "Action", value: event.library_action ?? "Published" },
|
|
335
|
-
{ title: "Time", value: event.timestamp ?? new Date().toISOString() },
|
|
336
|
-
]
|
|
337
|
-
);
|
|
338
|
-
notifications.push(sent ? "Slack notification sent" : "Slack notification failed");
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
results["handled"] = actions.length > 0 || notifications.length > 0;
|
|
343
|
-
|
|
344
|
-
// Log the event handling
|
|
345
|
-
await decisionLog.log({
|
|
346
|
-
tool: "webhook-listener",
|
|
347
|
-
nodeIds: [],
|
|
348
|
-
rationale: `Handled webhook event ${event.event_type} for file ${event.file_key}. Actions: ${actions.join(", ") || "none"}. Notifications: ${notifications.join(", ") || "none"}.`,
|
|
349
|
-
reversible: false,
|
|
350
|
-
metadata: { eventType: event.event_type, fileKey: event.file_key, actions, notifications },
|
|
351
|
-
});
|
|
352
|
-
|
|
353
|
-
return results;
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
// ─── Action implementations ───────────────────────────────────────────────────
|
|
357
|
-
|
|
358
|
-
async function actionSubscribe(args: WebhookListenerArgs): Promise<WebhookListenerResult> {
|
|
359
|
-
const events = args.events ?? ["FILE_UPDATE", "LIBRARY_PUBLISH"];
|
|
360
|
-
const passcode = generatePasscode();
|
|
361
|
-
const endpoint = webhookEndpoint();
|
|
362
|
-
|
|
363
|
-
// Register with Figma REST API
|
|
364
|
-
const responseData = await figmaPost("/v2/webhooks", {
|
|
365
|
-
event_type: events[0], // Figma registers one event type per webhook
|
|
366
|
-
team_id: process.env.FIGMA_TEAM_ID ?? "",
|
|
367
|
-
endpoint,
|
|
368
|
-
passcode,
|
|
369
|
-
description: `figma-intelligence-layer subscription for ${args.fileKey}`,
|
|
370
|
-
status: "ACTIVE",
|
|
371
|
-
});
|
|
372
|
-
|
|
373
|
-
const webhookId = String(
|
|
374
|
-
(responseData["id"] as string | undefined) ??
|
|
375
|
-
(responseData["webhook_id"] as string | undefined) ??
|
|
376
|
-
`local_${Date.now()}`
|
|
377
|
-
);
|
|
378
|
-
|
|
379
|
-
// If multiple events, register additional webhooks
|
|
380
|
-
const additionalIds: string[] = [webhookId];
|
|
381
|
-
for (let i = 1; i < events.length; i++) {
|
|
382
|
-
try {
|
|
383
|
-
const extra = await figmaPost("/v2/webhooks", {
|
|
384
|
-
event_type: events[i],
|
|
385
|
-
team_id: process.env.FIGMA_TEAM_ID ?? "",
|
|
386
|
-
endpoint,
|
|
387
|
-
passcode,
|
|
388
|
-
description: `figma-intelligence-layer subscription for ${args.fileKey} (${events[i]})`,
|
|
389
|
-
status: "ACTIVE",
|
|
390
|
-
});
|
|
391
|
-
const extraId = String((extra["id"] as string | undefined) ?? `local_${Date.now()}_${i}`);
|
|
392
|
-
additionalIds.push(extraId);
|
|
393
|
-
} catch {
|
|
394
|
-
// Best effort for additional event types
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
const subscription: WebhookSubscription = {
|
|
399
|
-
webhookId: additionalIds.join(","),
|
|
400
|
-
fileKey: args.fileKey,
|
|
401
|
-
events,
|
|
402
|
-
triggers: args.triggers,
|
|
403
|
-
notificationChannels: args.notificationChannels,
|
|
404
|
-
createdAt: new Date().toISOString(),
|
|
405
|
-
passcode,
|
|
406
|
-
};
|
|
407
|
-
|
|
408
|
-
const existing = await readSubscriptions();
|
|
409
|
-
existing.push(subscription);
|
|
410
|
-
await writeSubscriptions(existing);
|
|
411
|
-
|
|
412
|
-
await decisionLog.log({
|
|
413
|
-
tool: "webhook-listener",
|
|
414
|
-
nodeIds: [],
|
|
415
|
-
rationale: `Registered webhook subscription for file ${args.fileKey} watching events: ${events.join(", ")}.`,
|
|
416
|
-
reversible: true,
|
|
417
|
-
metadata: { action: "subscribe", webhookId: subscription.webhookId, events },
|
|
418
|
-
});
|
|
419
|
-
|
|
420
|
-
return {
|
|
421
|
-
action: "subscribe",
|
|
422
|
-
subscriptions: [subscription],
|
|
423
|
-
message: `Successfully subscribed to ${events.length} event type(s) for file ${args.fileKey}. Webhook ID(s): ${subscription.webhookId}.`,
|
|
424
|
-
};
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
async function actionUnsubscribe(args: WebhookListenerArgs): Promise<WebhookListenerResult> {
|
|
428
|
-
const existing = await readSubscriptions();
|
|
429
|
-
const toRemove = existing.filter((s) => s.fileKey === args.fileKey);
|
|
430
|
-
|
|
431
|
-
for (const sub of toRemove) {
|
|
432
|
-
// Delete each webhook ID
|
|
433
|
-
const ids = sub.webhookId.split(",");
|
|
434
|
-
for (const id of ids) {
|
|
435
|
-
if (!id.startsWith("local_")) {
|
|
436
|
-
await figmaDelete(`/v2/webhooks/${id}`).catch(() => {
|
|
437
|
-
// Already deleted or inaccessible — proceed
|
|
438
|
-
});
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
const remaining = existing.filter((s) => s.fileKey !== args.fileKey);
|
|
444
|
-
await writeSubscriptions(remaining);
|
|
445
|
-
|
|
446
|
-
await decisionLog.log({
|
|
447
|
-
tool: "webhook-listener",
|
|
448
|
-
nodeIds: [],
|
|
449
|
-
rationale: `Unsubscribed ${toRemove.length} webhook(s) for file ${args.fileKey}.`,
|
|
450
|
-
reversible: false,
|
|
451
|
-
metadata: { action: "unsubscribe", fileKey: args.fileKey, removedCount: toRemove.length },
|
|
452
|
-
});
|
|
453
|
-
|
|
454
|
-
return {
|
|
455
|
-
action: "unsubscribe",
|
|
456
|
-
subscriptions: [],
|
|
457
|
-
message: `Removed ${toRemove.length} webhook subscription(s) for file ${args.fileKey}.`,
|
|
458
|
-
};
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
async function actionList(args: WebhookListenerArgs): Promise<WebhookListenerResult> {
|
|
462
|
-
// Read local config
|
|
463
|
-
const local = await readSubscriptions();
|
|
464
|
-
const forFile = local.filter((s) => !args.fileKey || s.fileKey === args.fileKey);
|
|
465
|
-
|
|
466
|
-
// Also fetch from Figma API and merge (best effort)
|
|
467
|
-
let figmaWebhooks: WebhookSubscription[] = [];
|
|
468
|
-
try {
|
|
469
|
-
const teamId = process.env.FIGMA_TEAM_ID;
|
|
470
|
-
if (teamId) {
|
|
471
|
-
const data = await figmaGet(`/v2/teams/${teamId}/webhooks`);
|
|
472
|
-
const rawHooks = (data["webhooks"] as Array<Record<string, unknown>>) ?? [];
|
|
473
|
-
figmaWebhooks = rawHooks
|
|
474
|
-
.filter((h) => {
|
|
475
|
-
const desc = String(h["description"] ?? "");
|
|
476
|
-
return !args.fileKey || desc.includes(args.fileKey);
|
|
477
|
-
})
|
|
478
|
-
.map((h) => ({
|
|
479
|
-
webhookId: String(h["id"] ?? ""),
|
|
480
|
-
fileKey: args.fileKey,
|
|
481
|
-
events: [String(h["event_type"] ?? "FILE_UPDATE")] as WebhookEvent[],
|
|
482
|
-
triggers: undefined,
|
|
483
|
-
notificationChannels: undefined,
|
|
484
|
-
createdAt: String(h["created_at"] ?? new Date().toISOString()),
|
|
485
|
-
passcode: "",
|
|
486
|
-
}));
|
|
487
|
-
}
|
|
488
|
-
} catch {
|
|
489
|
-
// Proceed with local-only data
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
// Merge, preferring local config details when IDs overlap
|
|
493
|
-
const localIds = new Set(forFile.flatMap((s) => s.webhookId.split(",")));
|
|
494
|
-
const remoteOnly = figmaWebhooks.filter((h) => !localIds.has(h.webhookId));
|
|
495
|
-
const merged = [...forFile, ...remoteOnly];
|
|
496
|
-
|
|
497
|
-
return {
|
|
498
|
-
action: "list",
|
|
499
|
-
subscriptions: merged,
|
|
500
|
-
message: `Found ${merged.length} webhook subscription(s)${args.fileKey ? ` for file ${args.fileKey}` : ""}.`,
|
|
501
|
-
};
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
async function actionTest(args: WebhookListenerArgs): Promise<WebhookListenerResult> {
|
|
505
|
-
const existing = await readSubscriptions();
|
|
506
|
-
const sub = existing.find((s) => s.fileKey === args.fileKey);
|
|
507
|
-
|
|
508
|
-
if (!sub) {
|
|
509
|
-
return {
|
|
510
|
-
action: "test",
|
|
511
|
-
subscriptions: [],
|
|
512
|
-
message: `No subscription found for file ${args.fileKey}. Subscribe first.`,
|
|
513
|
-
testResult: { success: false, reason: "No subscription found" },
|
|
514
|
-
};
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
// Build a synthetic test payload
|
|
518
|
-
const testEvent: WebhookEventPayload = {
|
|
519
|
-
event_type: "FILE_UPDATE",
|
|
520
|
-
file_key: args.fileKey,
|
|
521
|
-
file_name: `Test File (${args.fileKey})`,
|
|
522
|
-
description: "token: test update triggered by webhook-listener test action",
|
|
523
|
-
timestamp: new Date().toISOString(),
|
|
524
|
-
passcode: sub.passcode,
|
|
525
|
-
};
|
|
526
|
-
|
|
527
|
-
const handlerResult = await handleWebhookEvent(testEvent, sub);
|
|
528
|
-
|
|
529
|
-
return {
|
|
530
|
-
action: "test",
|
|
531
|
-
subscriptions: [sub],
|
|
532
|
-
message: `Test payload dispatched for file ${args.fileKey}.`,
|
|
533
|
-
testResult: {
|
|
534
|
-
success: Boolean(handlerResult["handled"]),
|
|
535
|
-
payload: testEvent,
|
|
536
|
-
handlerOutput: handlerResult,
|
|
537
|
-
},
|
|
538
|
-
};
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
// ─── Main handler ─────────────────────────────────────────────────────────────
|
|
542
|
-
|
|
543
|
-
export async function webhookListenerHandler(
|
|
544
|
-
args: WebhookListenerArgs
|
|
545
|
-
): Promise<WebhookListenerResult> {
|
|
546
|
-
switch (args.action) {
|
|
547
|
-
case "subscribe":
|
|
548
|
-
return actionSubscribe(args);
|
|
549
|
-
case "unsubscribe":
|
|
550
|
-
return actionUnsubscribe(args);
|
|
551
|
-
case "list":
|
|
552
|
-
return actionList(args);
|
|
553
|
-
case "test":
|
|
554
|
-
return actionTest(args);
|
|
555
|
-
default: {
|
|
556
|
-
const exhaustive: never = args.action;
|
|
557
|
-
throw new Error(`webhookListener: Unknown action "${exhaustive}". Valid actions: subscribe | unsubscribe | list | test.`);
|
|
558
|
-
}
|
|
559
|
-
}
|
|
560
|
-
}
|