@uipath/skills 1.196.0 → 1.197.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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +5 -2
- package/CODEOWNERS +22 -7
- package/README.md +14 -1
- package/assets/skill-status.json +14 -1
- package/assets/uip-catalog-snapshot.json +13 -2
- package/hooks/hooks.json +12 -0
- package/hooks/send-telemetry.sh +445 -0
- package/hooks/suggest-permissions.sh +6 -0
- package/package.json +1 -1
- package/skills/uipath-admin/SKILL.md +24 -4
- package/skills/uipath-admin/references/audit-commands.md +25 -21
- package/skills/uipath-admin/references/audit-workflow-guide.md +26 -25
- package/skills/uipath-admin/references/diagnose/CAPABILITY.md +74 -0
- package/skills/uipath-admin/references/diagnose/references/failure-modes.md +228 -0
- package/skills/uipath-admin/references/diagnose/references/troubleshooting-guide.md +106 -0
- package/skills/uipath-admin/references/identity-troubleshoot-guide.md +251 -0
- package/skills/uipath-agents/SKILL.md +12 -8
- package/skills/uipath-agents/references/coded/capabilities/context-grounding.md +2 -0
- package/skills/uipath-agents/references/coded/capabilities/conversational-agents.md +2 -0
- package/skills/uipath-agents/references/coded/capabilities/guardrails/guardrails-recommend.md +12 -7
- package/skills/uipath-agents/references/coded/capabilities/guardrails/guardrails.md +141 -1
- package/skills/uipath-agents/references/coded/capabilities/human-in-the-loop.md +2 -0
- package/skills/uipath-agents/references/coded/capabilities/integration-service.md +19 -33
- package/skills/uipath-agents/references/coded/lifecycle/bindings-reference.md +54 -22
- package/skills/uipath-agents/references/coded/lifecycle/build.md +1 -0
- package/skills/uipath-agents/references/coded/lifecycle/evaluations/evaluation-sets.md +5 -1
- package/skills/uipath-agents/references/coded/lifecycle/setup.md +3 -1
- package/skills/uipath-agents/references/coded/quickstart.md +11 -10
- package/skills/uipath-agents/references/coded-vs-lowcode-guide.md +1 -0
- package/skills/uipath-agents/references/context-grounding-patterns.md +1 -1
- package/skills/uipath-agents/references/lowcode/agent-definition.md +97 -12
- package/skills/uipath-agents/references/lowcode/capabilities/built-in-tools/analyze-attachments.md +2 -2
- package/skills/uipath-agents/references/lowcode/capabilities/built-in-tools/batch-transform/impl-json.md +40 -14
- package/skills/uipath-agents/references/lowcode/capabilities/built-in-tools/built-in-tools.md +4 -3
- package/skills/uipath-agents/references/lowcode/capabilities/built-in-tools/deeprag/impl-json.md +38 -6
- package/skills/uipath-agents/references/lowcode/capabilities/context/attachments.md +1 -1
- package/skills/uipath-agents/references/lowcode/capabilities/context/context.md +1 -1
- package/skills/uipath-agents/references/lowcode/capabilities/context/datafabric.md +1 -1
- package/skills/uipath-agents/references/lowcode/capabilities/context/index.md +7 -3
- package/skills/uipath-agents/references/lowcode/capabilities/escalation/escalation.md +49 -38
- package/skills/uipath-agents/references/lowcode/capabilities/guardrails/guardrails-recommend.md +3 -1
- package/skills/uipath-agents/references/lowcode/capabilities/guardrails/guardrails.md +49 -49
- package/skills/uipath-agents/references/lowcode/capabilities/inline-in-flow/inline-in-flow.md +34 -14
- package/skills/uipath-agents/references/lowcode/capabilities/integration-service/integration-service.md +1 -1
- package/skills/uipath-agents/references/lowcode/capabilities/memory/memory.md +2 -0
- package/skills/uipath-agents/references/lowcode/capabilities/process/process.md +2 -2
- package/skills/uipath-agents/references/lowcode/capabilities/process/solution-files.md +1 -1
- package/skills/uipath-agents/references/lowcode/critical-rules/autonomous-critical-rules.md +15 -0
- package/skills/uipath-agents/references/lowcode/critical-rules/conversational-critical-rules.md +19 -0
- package/skills/uipath-agents/references/lowcode/{critical-rules.md → critical-rules/critical-rules.md} +25 -27
- package/skills/uipath-agents/references/lowcode/debug.md +6 -3
- package/skills/uipath-agents/references/lowcode/evaluations/evaluate.md +2 -0
- package/skills/uipath-agents/references/lowcode/lowcode.md +29 -6
- package/skills/uipath-agents/references/lowcode/model-selection-guide.md +4 -1
- package/skills/uipath-agents/references/lowcode/project-lifecycle.md +20 -8
- package/skills/uipath-agents/references/lowcode/prompting/agent-prompting-guide.md +17 -0
- package/skills/uipath-agents/references/lowcode/{agent-prompting-guide.md → prompting/autonomous-agent-prompting-guide.md} +9 -11
- package/skills/uipath-agents/references/lowcode/prompting/conversational-agent-prompting-guide.md +123 -0
- package/skills/uipath-agents/references/lowcode/solution-resources.md +1 -1
- package/skills/uipath-api-workflow/SKILL.md +32 -12
- package/skills/uipath-api-workflow/references/cli-reference.md +110 -22
- package/skills/uipath-api-workflow/references/connector-activity-discovery.md +43 -4
- package/skills/uipath-api-workflow/references/troubleshooting.md +23 -7
- package/skills/uipath-api-workflow/references/workflow-file-format.md +25 -7
- package/skills/uipath-coded-apps/SKILL.md +55 -12
- package/skills/uipath-coded-apps/assets/fixtures/governance-dashboard-starter-kit.tar.gz +0 -0
- package/skills/uipath-coded-apps/assets/scripts/dashboards/build-dashboard.mjs +1371 -0
- package/skills/uipath-coded-apps/assets/scripts/dashboards/capability-registry.json +845 -0
- package/skills/uipath-coded-apps/assets/scripts/dashboards/flows/build.mjs +338 -0
- package/skills/uipath-coded-apps/assets/scripts/dashboards/flows/edit.mjs +248 -0
- package/skills/uipath-coded-apps/assets/scripts/dashboards/flows/template.mjs +82 -0
- package/skills/uipath-coded-apps/assets/scripts/dashboards/flows/upgrade.mjs +73 -0
- package/skills/uipath-coded-apps/assets/scripts/dashboards/lifecycle.mjs +207 -0
- package/skills/uipath-coded-apps/assets/scripts/dashboards/setup-admin-folder.mjs +146 -0
- package/skills/uipath-coded-apps/assets/scripts/dashboards/tests/regime-eject.test.mjs +124 -0
- package/skills/uipath-coded-apps/assets/scripts/dashboards/tests/resolution.test.mjs +1635 -0
- package/skills/uipath-coded-apps/assets/templates/action-app/app-template.css +9 -0
- package/skills/uipath-coded-apps/assets/templates/action-app/document-tab-template.css +139 -0
- package/skills/uipath-coded-apps/assets/templates/action-app/form-template.css +442 -0
- package/skills/uipath-coded-apps/assets/templates/action-app/index-template.css +111 -0
- package/skills/uipath-coded-apps/assets/templates/action-app-template.md +357 -152
- package/skills/uipath-coded-apps/assets/templates/web-app-template.md +255 -78
- package/skills/uipath-coded-apps/references/create-action-app.md +284 -195
- package/skills/uipath-coded-apps/references/create-web-app.md +64 -26
- package/skills/uipath-coded-apps/references/dashboards/CAPABILITY.md +179 -0
- package/skills/uipath-coded-apps/references/dashboards/aesthetic/charting.md +138 -0
- package/skills/uipath-coded-apps/references/dashboards/aesthetic/layout-patterns.md +152 -0
- package/skills/uipath-coded-apps/references/dashboards/plugins/build/impl.md +423 -0
- package/skills/uipath-coded-apps/references/dashboards/plugins/deploy/impl.md +322 -0
- package/skills/uipath-coded-apps/references/dashboards/primitives/customization.md +66 -0
- package/skills/uipath-coded-apps/references/dashboards/primitives/detail-views.md +64 -0
- package/skills/uipath-coded-apps/references/dashboards/primitives/incremental-editor.md +92 -0
- package/skills/uipath-coded-apps/references/dashboards/primitives/state-file.md +64 -0
- package/skills/uipath-coded-apps/references/dashboards/primitives/tier-resolution.md +474 -0
- package/skills/uipath-coded-apps/references/debug.md +15 -21
- package/skills/uipath-coded-apps/references/oauth-client-setup.md +5 -5
- package/skills/uipath-coded-apps/references/oauth-scopes.md +64 -2
- package/skills/uipath-coded-apps/references/pack-publish-deploy.md +12 -5
- package/skills/uipath-coded-apps/references/patterns.md +10 -5
- package/skills/uipath-coded-apps/references/sdk/action-center.md +32 -6
- package/skills/uipath-coded-apps/references/sdk/agents.md +241 -0
- package/skills/uipath-coded-apps/references/sdk/governance-traces.md +186 -0
- package/skills/uipath-coded-apps/references/sdk/governance.md +72 -0
- package/skills/uipath-coded-apps/references/sdk/imports.md +4 -0
- package/skills/uipath-coded-apps/references/sdk/maestro.md +114 -0
- package/skills/uipath-coded-apps/references/sdk/orchestrator.md +80 -4
- package/skills/uipath-coded-apps/references/sdk/pagination.md +2 -0
- package/skills/uipath-coded-apps/references/sdk/traces.md +89 -0
- package/skills/uipath-coded-apps/references/widgets/validation-station.md +308 -0
- package/skills/uipath-coded-apps/scripts/validate-action-schema.js +241 -0
- package/skills/uipath-connector-builder/SKILL.md +192 -0
- package/skills/uipath-connector-builder/references/auth.md +171 -0
- package/skills/uipath-connector-builder/references/configuration.md +130 -0
- package/skills/uipath-connector-builder/references/debugging.md +80 -0
- package/skills/uipath-connector-builder/references/element-json.md +148 -0
- package/skills/uipath-connector-builder/references/events.md +132 -0
- package/skills/uipath-connector-builder/references/hooks.md +198 -0
- package/skills/uipath-connector-builder/references/overview.md +78 -0
- package/skills/uipath-connector-builder/references/standard-resources.md +111 -0
- package/skills/uipath-connector-builder/references/system-resources.md +85 -0
- package/skills/uipath-feedback/SKILL.md +43 -29
- package/skills/{uipath-agents/references/coded/frameworks/coded-functions.md → uipath-functions/SKILL.md} +36 -23
- package/skills/uipath-governance/SKILL.md +49 -8
- package/skills/uipath-governance/references/aops-policy/aops-policy-commands.md +10 -10
- package/skills/uipath-governance/references/aops-policy/aops-policy-deploy-guide.md +7 -7
- package/skills/uipath-governance/references/auth-context.md +101 -0
- package/skills/uipath-governance/references/cli-cheatsheet.md +207 -0
- package/skills/uipath-governance/references/compliance-pack/catalog/impl.md +79 -0
- package/skills/uipath-governance/references/compliance-pack/coverage/impl.md +141 -0
- package/skills/uipath-governance/references/compliance-pack/disable/impl.md +39 -0
- package/skills/uipath-governance/references/compliance-pack/full-apply/impl.md +164 -0
- package/skills/uipath-governance/references/compliance-pack/partial-apply/impl.md +266 -0
- package/skills/uipath-governance/references/compliance-pack/partial-apply/merge-overrides-guide.md +28 -0
- package/skills/uipath-governance/references/compliance-pack/partial-apply/planning.md +82 -0
- package/skills/uipath-governance/references/compliance-pack/partial-apply/synthesize-formdata-guide.md +22 -0
- package/skills/uipath-governance/references/compliance-pack/preview-gate.md +43 -0
- package/skills/uipath-governance/references/compliance-pack/query/impl.md +75 -0
- package/skills/uipath-governance/references/diagnose/CAPABILITY.md +71 -0
- package/skills/uipath-governance/references/diagnose/references/failure-modes.md +201 -0
- package/skills/uipath-governance/references/diagnose/references/troubleshooting-guide.md +137 -0
- package/skills/uipath-governance/references/disambiguation-guide.md +15 -0
- package/skills/uipath-governance/scripts/merge-overrides.mjs +81 -0
- package/skills/uipath-governance/scripts/synthesize-formdata.mjs +86 -0
- package/skills/uipath-human-in-the-loop/SKILL.md +30 -25
- package/skills/uipath-human-in-the-loop/references/hitl-node-apptask.md +38 -4
- package/skills/uipath-human-in-the-loop/references/hitl-node-coded-action-app.md +2 -3
- package/skills/uipath-human-in-the-loop/references/hitl-node-quickform.md +92 -34
- package/skills/uipath-ixp/SKILL.md +120 -0
- package/skills/uipath-ixp/references/cli-reference.md +121 -0
- package/skills/uipath-ixp/references/improve-prompts-guide.md +283 -0
- package/skills/uipath-ixp/references/label-documents-guide.md +207 -0
- package/skills/uipath-ixp/references/project-setup-guide.md +90 -0
- package/skills/uipath-maestro-bpmn/SKILL.md +167 -68
- package/skills/uipath-maestro-bpmn/references/cli-conventions.md +47 -0
- package/skills/uipath-maestro-bpmn/references/diagnose/CAPABILITY.md +6 -6
- package/skills/uipath-maestro-bpmn/references/operate/CAPABILITY.md +3 -3
- package/skills/uipath-maestro-bpmn/references/registry-workflow.md +199 -0
- package/skills/uipath-maestro-bpmn/references/structural-bpmn.md +500 -0
- package/skills/uipath-maestro-bpmn/validator/README.md +214 -0
- package/skills/uipath-maestro-bpmn/validator/bpmn-spec.json +2477 -0
- package/skills/uipath-maestro-bpmn/validator/model.mjs +419 -0
- package/skills/uipath-maestro-bpmn/validator/package.json +17 -0
- package/skills/uipath-maestro-bpmn/validator/rules.mjs +1403 -0
- package/skills/uipath-maestro-bpmn/validator/samples/invalid-conditional-and-variable.bpmn +25 -0
- package/skills/uipath-maestro-bpmn/validator/samples/valid-baseline.bpmn +52 -0
- package/skills/uipath-maestro-bpmn/validator/test/fixtures/expected-findings/A.2.0.bpmn +157 -0
- package/skills/uipath-maestro-bpmn/validator/test/fixtures/expected-findings/A.2.1.bpmn +333 -0
- package/skills/uipath-maestro-bpmn/validator/test/fixtures/expected-findings/B.1.0.bpmn +598 -0
- package/skills/uipath-maestro-bpmn/validator/test/fixtures/expected-findings/B.2.0.bpmn +1709 -0
- package/skills/uipath-maestro-bpmn/validator/test/fixtures/expected-findings/C.2.0.bpmn +564 -0
- package/skills/uipath-maestro-bpmn/validator/test/fixtures/expected-findings/C.3.0.bpmn +671 -0
- package/skills/uipath-maestro-bpmn/validator/test/fixtures/expected-findings/C.4.0.bpmn +1045 -0
- package/skills/uipath-maestro-bpmn/validator/test/fixtures/expected-findings/C.5.0.bpmn +1176 -0
- package/skills/uipath-maestro-bpmn/validator/test/fixtures/expected-findings/C.6.0.bpmn +670 -0
- package/skills/uipath-maestro-bpmn/validator/test/fixtures/expected-findings/C.7.0.bpmn +466 -0
- package/skills/uipath-maestro-bpmn/validator/test/fixtures/expected-findings/Can_Parse_Complex_Process_With_Task_Gateway_BoundaryEvent_etc.bpmn +74 -0
- package/skills/uipath-maestro-bpmn/validator/test/fixtures/expected-findings/ExclusiveGatewayDefaultFlow.bpmn +32 -0
- package/skills/uipath-maestro-bpmn/validator/test/fixtures/expected-findings/ExternalAgentWorkflow.bpmn +62 -0
- package/skills/uipath-maestro-bpmn/validator/test/fixtures/expected-findings/Golden_Scenario.initial.bpmn +361 -0
- package/skills/uipath-maestro-bpmn/validator/test/fixtures/expected-findings/InclusiveJoinRouteAwayBranch.bpmn +62 -0
- package/skills/uipath-maestro-bpmn/validator/test/fixtures/expected-findings/Parse_AsyncExecution_And_Create_CorrectModel.bpmn +74 -0
- package/skills/uipath-maestro-bpmn/validator/test/fixtures/expected-findings/Parse_IXP_ExtractionValidation_And_Create_CorrectModel.bpmn +35 -0
- package/skills/uipath-maestro-bpmn/validator/test/fixtures/expected-findings/Parse_IXP_Extraction_FileUpload_And_Create_CorrectModel.bpmn +33 -0
- package/skills/uipath-maestro-bpmn/validator/test/fixtures/expected-findings/Parse_IXP_Extraction_JobAttachment_And_Create_CorrectModel.bpmn +35 -0
- package/skills/uipath-maestro-bpmn/validator/test/fixtures/expected-findings/Parse_SubProcess_With_Multiple_Element_Types.bpmn +76 -0
- package/skills/uipath-maestro-bpmn/validator/test/fixtures/expected-findings/StartEventWithOutputs.bpmn +36 -0
- package/skills/uipath-maestro-bpmn/validator/test/fixtures/expected-findings/StartGatewayEnd.bpmn +44 -0
- package/skills/uipath-maestro-bpmn/validator/test/fixtures/expected-findings/all elements.bpmn +516 -0
- package/skills/uipath-maestro-bpmn/validator/test/fixtures/expected-findings/all_sequence_flow_types.bpmn +173 -0
- package/skills/uipath-maestro-bpmn/validator/test/fixtures/expected-findings/demo.bpmn +181 -0
- package/skills/uipath-maestro-bpmn/validator/test/fixtures/expected-findings/subprocess-example-001-collapsed.bpmn +126 -0
- package/skills/uipath-maestro-bpmn/validator/test/fixtures/expected-findings/subprocess-example-001-expanded.bpmn +122 -0
- package/skills/uipath-maestro-bpmn/validator/test/fixtures/expected-findings/subprocess-example-003-collapsed_deeply-nested.bpmn +438 -0
- package/skills/uipath-maestro-bpmn/validator/test/fixtures/known-good/A.1.0.bpmn +87 -0
- package/skills/uipath-maestro-bpmn/validator/test/fixtures/known-good/A.3.0.bpmn +165 -0
- package/skills/uipath-maestro-bpmn/validator/test/fixtures/known-good/ApiWorkflow.bpmn +39 -0
- package/skills/uipath-maestro-bpmn/validator/test/fixtures/known-good/BpmnNestedSubProcessTests.bpmn +102 -0
- package/skills/uipath-maestro-bpmn/validator/test/fixtures/known-good/BpmnTimerBoundaryEvents.bpmn +160 -0
- package/skills/uipath-maestro-bpmn/validator/test/fixtures/known-good/BpmnXmlWithCatchAllErrorEventSubProcess.bpmn +45 -0
- package/skills/uipath-maestro-bpmn/validator/test/fixtures/known-good/BpmnXmlWithSpecificErrorEventSubProcess.bpmn +46 -0
- package/skills/uipath-maestro-bpmn/validator/test/fixtures/known-good/CaseManagementWithConstantIdentifier.bpmn +218 -0
- package/skills/uipath-maestro-bpmn/validator/test/fixtures/known-good/ErrorBoundary.bpmn +78 -0
- package/skills/uipath-maestro-bpmn/validator/test/fixtures/known-good/ErrorPropagationInEventSubprocess.bpmn +340 -0
- package/skills/uipath-maestro-bpmn/validator/test/fixtures/known-good/EventBasedGatewayFirstCatcherWins.bpmn +56 -0
- package/skills/uipath-maestro-bpmn/validator/test/fixtures/known-good/Example-EventBasedGateway.bpmn +117 -0
- package/skills/uipath-maestro-bpmn/validator/test/fixtures/known-good/ExclusiveGatewayConditional.bpmn +45 -0
- package/skills/uipath-maestro-bpmn/validator/test/fixtures/known-good/ExclusiveGatewaySharedEndEvent.bpmn +45 -0
- package/skills/uipath-maestro-bpmn/validator/test/fixtures/known-good/ExclusiveWithParallelGateway.bpmn +68 -0
- package/skills/uipath-maestro-bpmn/validator/test/fixtures/known-good/FourScriptTasks.bpmn +84 -0
- package/skills/uipath-maestro-bpmn/validator/test/fixtures/known-good/HitlTaskOnly.bpmn +55 -0
- package/skills/uipath-maestro-bpmn/validator/test/fixtures/known-good/InclusiveGatewayForkJoin.bpmn +69 -0
- package/skills/uipath-maestro-bpmn/validator/test/fixtures/known-good/Parse_BPMN_Elements_And_Create_CorrectModel.bpmn +20 -0
- package/skills/uipath-maestro-bpmn/validator/test/fixtures/known-good/Parse_HttpRequest_ServiceTask_And_Create_Correct_Model.bpmn +34 -0
- package/skills/uipath-maestro-bpmn/validator/test/fixtures/known-good/Parse_MessageBoundaryEvent_And_Create_CorrectModel.bpmn +87 -0
- package/skills/uipath-maestro-bpmn/validator/test/fixtures/known-good/Parse_Script_Task_V2_And_Create_Correct_Model.bpmn +31 -0
- package/skills/uipath-maestro-bpmn/validator/test/fixtures/known-good/Parse_Sets_Containers_Properly.bpmn +129 -0
- package/skills/uipath-maestro-bpmn/validator/test/fixtures/known-good/ScriptWritesVariableThenGatewayBranches.bpmn +55 -0
- package/skills/uipath-maestro-bpmn/validator/test/fixtures/known-good/boundaryevent.bpmn +30 -0
- package/skills/uipath-maestro-bpmn/validator/test/fixtures/known-good/collapsed-subprocess.bpmn +83 -0
- package/skills/uipath-maestro-bpmn/validator/test/fixtures/known-good/connectable-types.bpmn +85 -0
- package/skills/uipath-maestro-bpmn/validator/test/fixtures/known-good/extensions-orchestrator-start-job.bpmn +80 -0
- package/skills/uipath-maestro-bpmn/validator/test/fixtures/known-good/multiparticipantpool.bpmn +77 -0
- package/skills/uipath-maestro-bpmn/validator/test/fixtures/known-good/nestedsubprocess.bpmn +36 -0
- package/skills/uipath-maestro-bpmn/validator/test/fixtures/known-good/simple.bpmn +59 -0
- package/skills/uipath-maestro-bpmn/validator/test/integration.test.mjs +146 -0
- package/skills/uipath-maestro-bpmn/validator/test/model-helpers.mjs +42 -0
- package/skills/uipath-maestro-bpmn/validator/test/ported-rule-tests.mjs +983 -0
- package/skills/uipath-maestro-bpmn/validator/test/run-tests.mjs +530 -0
- package/skills/uipath-maestro-bpmn/validator/uipath-moddle.v1.json +715 -0
- package/skills/uipath-maestro-bpmn/validator/validate-bpmn.mjs +164 -0
- package/skills/uipath-maestro-case/SKILL.md +5 -1
- package/skills/uipath-maestro-case/assets/templates/sdd-template-examples.md +10 -0
- package/skills/uipath-maestro-case/assets/templates/sdd-template.md +89 -17
- package/skills/uipath-maestro-case/references/bindings-and-expressions.md +26 -0
- package/skills/uipath-maestro-case/references/case-editing-operations.md +3 -0
- package/skills/uipath-maestro-case/references/implementation.md +11 -4
- package/skills/uipath-maestro-case/references/phase-0-interview.md +8 -7
- package/skills/uipath-maestro-case/references/phased-execution.md +2 -1
- package/skills/uipath-maestro-case/references/plugins/conditions/stage-exit-conditions/impl-json.md +21 -0
- package/skills/uipath-maestro-case/references/plugins/conditions/stage-exit-conditions/planning.md +2 -0
- package/skills/uipath-maestro-case/references/plugins/variables/global-vars/impl-json.md +15 -9
- package/skills/uipath-maestro-case/references/plugins/variables/io-binding/impl-json.md +131 -4
- package/skills/uipath-maestro-case/references/sdd-generation-rules.md +94 -17
- package/skills/uipath-maestro-flow/references/author/CAPABILITY.md +1 -1
- package/skills/uipath-maestro-flow/references/author/references/planning-arch.md +2 -2
- package/skills/uipath-maestro-flow/references/author/references/plugins/hitl/impl.md +11 -11
- package/skills/uipath-maestro-flow/references/author/references/plugins/hitl/planning.md +6 -6
- package/skills/uipath-maestro-flow/references/author/references/plugins/http/impl-connector.md +4 -1
- package/skills/uipath-maestro-flow/references/author/references/plugins/inline-agent/impl.md +157 -122
- package/skills/uipath-maestro-flow/references/author/references/plugins/inline-agent/planning.md +16 -4
- package/skills/uipath-maestro-flow/references/evaluate/references/commands-reference.md +8 -8
- package/skills/uipath-maestro-flow/references/evaluate/references/running-guide.md +3 -3
- package/skills/uipath-maestro-flow/references/shared/cli-commands.md +1 -1
- package/skills/uipath-planner/SKILL.md +19 -8
- package/skills/uipath-planner/assets/templates/agent-sdd-template.md +14 -0
- package/skills/uipath-planner/assets/templates/api-workflow-sdd-template.md +14 -0
- package/skills/uipath-planner/assets/templates/case-sdd-template.md +14 -0
- package/skills/uipath-planner/assets/templates/coded-app-sdd-template.md +14 -0
- package/skills/uipath-planner/assets/templates/flow-sdd-template.md +14 -0
- package/skills/uipath-planner/assets/templates/rpa-sdd-template.md +40 -5
- package/skills/uipath-planner/references/asdd-crosswalk-guide.md +55 -0
- package/skills/uipath-planner/references/attended-reauth-pattern-guide.md +43 -0
- package/skills/uipath-planner/references/multi-skill-patterns-guide.md +4 -0
- package/skills/uipath-planner/references/non-pdd-lane-guide.md +2 -0
- package/skills/uipath-planner/references/pdd-analysis-guide.md +24 -1
- package/skills/uipath-planner/references/pdd-driven-lane-guide.md +6 -2
- package/skills/uipath-planner/references/plan-and-tasks-format.md +2 -0
- package/skills/uipath-planner/references/platform-availability-guide.md +43 -0
- package/skills/uipath-planner/references/product-selection-guide.md +20 -0
- package/skills/uipath-planner/references/sdd-generation-guide.md +90 -21
- package/skills/uipath-planner/references/tenant-library-search-guide.md +5 -1
- package/skills/uipath-planner/scripts/docx-extract.sh +54 -0
- package/skills/uipath-planner/scripts/sdd-to-docx.sh +76 -0
- package/skills/uipath-platform/SKILL.md +54 -7
- package/skills/uipath-platform/references/context-grounding/index-management.md +123 -0
- package/skills/uipath-platform/references/data-fabric/bulk-import.md +86 -0
- package/skills/{uipath-data-fabric/references → uipath-platform/references/data-fabric}/choice-sets.md +33 -12
- package/skills/uipath-platform/references/data-fabric/data-fabric.md +352 -0
- package/skills/uipath-platform/references/data-fabric/entity-schema.md +444 -0
- package/skills/uipath-platform/references/data-fabric/file-attachments.md +122 -0
- package/skills/{uipath-data-fabric/references → uipath-platform/references/data-fabric}/filter-platform-contract.md +3 -3
- package/skills/{uipath-data-fabric/references → uipath-platform/references/data-fabric}/records-query.md +46 -10
- package/skills/uipath-platform/references/integration-service/http-request.md +15 -1
- package/skills/uipath-platform/references/integration-service/vendor-docs-registry.json +173 -0
- package/skills/uipath-platform/references/uip-commands.md +12 -4
- package/skills/uipath-review/SKILL.md +53 -12
- package/skills/uipath-review/references/agents/agent-common-issues.md +1 -1
- package/skills/uipath-review/references/agents/agent-grading-rubric.md +133 -0
- package/skills/uipath-review/references/agents/agent-review-checklist.md +23 -3
- package/skills/uipath-review/references/agents/agents-coded-rules.md +7 -0
- package/skills/uipath-review/references/agents/agents-lowcode-rules.md +13 -3
- package/skills/uipath-review/references/agents/guardrails/guardrails-review.md +198 -0
- package/skills/uipath-review/references/architecture-assessment-guide.md +2 -0
- package/skills/uipath-review/references/review-workflow-guide.md +9 -5
- package/skills/uipath-review/references/rule-catalog-workflow.md +3 -3
- package/skills/uipath-rpa/SKILL.md +18 -6
- package/skills/uipath-rpa/references/cli-reference.md +57 -3
- package/skills/uipath-rpa/references/common-activity-card.md +1 -1
- package/skills/uipath-rpa/references/connector-capabilities.md +2 -0
- package/skills/uipath-rpa/references/data-manipulation-guide.md +311 -0
- package/skills/uipath-rpa/references/environment-setup.md +0 -2
- package/skills/uipath-rpa/references/error-handling-guide.md +381 -0
- package/skills/uipath-rpa/references/is-connector-xaml-guide.md +127 -11
- package/skills/uipath-rpa/references/reframework-guide.md +2 -0
- package/skills/uipath-rpa/references/testing-guide.md +19 -0
- package/skills/uipath-rpa/references/ui-automation-guide.md +129 -18
- package/skills/uipath-rpa/references/uia-configure-target-workflows.md +22 -1
- package/skills/uipath-rpa/references/uia-elements-interaction-guide.md +93 -0
- package/skills/uipath-rpa/references/xaml/canvas-layout-guide.md +41 -4
- package/skills/uipath-rpa/references/xaml/common-pitfalls.md +12 -3
- package/skills/uipath-rpa/references/xaml/flowchart-guide.md +113 -0
- package/skills/uipath-rpa/references/xaml/xaml-basics-and-rules.md +5 -9
- package/skills/uipath-solution/SKILL.md +11 -8
- package/skills/uipath-solution/references/pack-and-deploy.md +18 -1
- package/skills/uipath-solution/references/solution-overview.md +3 -0
- package/skills/uipath-test/SKILL.md +2 -1
- package/skills/uipath-troubleshoot/SKILL.md +24 -26
- package/skills/uipath-troubleshoot/agents/depth-verifier.md +24 -25
- package/skills/uipath-troubleshoot/agents/hypothesis-generator.md +8 -4
- package/skills/uipath-troubleshoot/agents/hypothesis-tester.md +85 -38
- package/skills/uipath-troubleshoot/agents/presenter.md +7 -7
- package/skills/uipath-troubleshoot/agents/scope-checker.md +3 -6
- package/skills/uipath-troubleshoot/agents/shared.md +26 -21
- package/skills/uipath-troubleshoot/agents/triage.md +117 -49
- package/skills/uipath-troubleshoot/references/activity-packages/cv-activities/investigation_guide.md +43 -0
- package/skills/uipath-troubleshoot/references/activity-packages/cv-activities/overview.md +57 -0
- package/skills/uipath-troubleshoot/references/activity-packages/cv-activities/playbooks/cv-action-failed-after-find.md +100 -0
- package/skills/uipath-troubleshoot/references/activity-packages/cv-activities/playbooks/cv-cell-targeting-failures.md +111 -0
- package/skills/uipath-troubleshoot/references/activity-packages/cv-activities/playbooks/cv-element-not-found.md +125 -0
- package/skills/uipath-troubleshoot/references/activity-packages/cv-activities/playbooks/cv-get-text-empty-or-wrong-result.md +117 -0
- package/skills/uipath-troubleshoot/references/activity-packages/cv-activities/playbooks/cv-invalid-descriptor.md +102 -0
- package/skills/uipath-troubleshoot/references/activity-packages/cv-activities/playbooks/cv-scope-setup-failures.md +136 -0
- package/skills/uipath-troubleshoot/references/activity-packages/cv-activities/playbooks/cv-scroll-search-failures.md +114 -0
- package/skills/uipath-troubleshoot/references/activity-packages/cv-activities/playbooks/cv-server-auth-throttling-network.md +95 -0
- package/skills/uipath-troubleshoot/references/activity-packages/cv-activities/playbooks/cv-silent-failures-and-false-results.md +124 -0
- package/skills/uipath-troubleshoot/references/activity-packages/cv-activities/presentation.md +8 -0
- package/skills/uipath-troubleshoot/references/activity-packages/cv-activities/summary.md +15 -0
- package/skills/uipath-troubleshoot/references/activity-packages/database-activities/investigation_guide.md +42 -0
- package/skills/uipath-troubleshoot/references/activity-packages/database-activities/overview.md +41 -0
- package/skills/uipath-troubleshoot/references/activity-packages/database-activities/playbooks/connect-to-database-failures.md +104 -0
- package/skills/uipath-troubleshoot/references/activity-packages/database-activities/playbooks/execute-non-query-failures.md +97 -0
- package/skills/uipath-troubleshoot/references/activity-packages/database-activities/playbooks/execute-query-failures.md +134 -0
- package/skills/uipath-troubleshoot/references/activity-packages/database-activities/playbooks/start-transaction-failures.md +94 -0
- package/skills/uipath-troubleshoot/references/activity-packages/database-activities/presentation.md +10 -0
- package/skills/uipath-troubleshoot/references/activity-packages/database-activities/summary.md +10 -0
- package/skills/uipath-troubleshoot/references/activity-packages/excel-activities/investigation_guide.md +27 -9
- package/skills/uipath-troubleshoot/references/activity-packages/excel-activities/overview.md +51 -10
- package/skills/uipath-troubleshoot/references/activity-packages/excel-activities/playbooks/append-range-failures.md +146 -0
- package/skills/uipath-troubleshoot/references/activity-packages/excel-activities/playbooks/delete-range-failures.md +126 -0
- package/skills/uipath-troubleshoot/references/activity-packages/excel-activities/playbooks/excel-application-card-failures.md +139 -0
- package/skills/uipath-troubleshoot/references/activity-packages/excel-activities/playbooks/excel-application-scope-failures.md +118 -0
- package/skills/uipath-troubleshoot/references/activity-packages/excel-activities/playbooks/execute-macro-failures.md +149 -0
- package/skills/uipath-troubleshoot/references/activity-packages/excel-activities/playbooks/write-range-failures.md +145 -0
- package/skills/uipath-troubleshoot/references/activity-packages/excel-activities/presentation.md +12 -7
- package/skills/uipath-troubleshoot/references/activity-packages/excel-activities/summary.md +6 -0
- package/skills/uipath-troubleshoot/references/activity-packages/gsuite-activities/investigation_guide.md +1 -0
- package/skills/uipath-troubleshoot/references/activity-packages/gsuite-activities/overview.md +12 -4
- package/skills/uipath-troubleshoot/references/activity-packages/gsuite-activities/playbooks/connection-and-auth-failures.md +55 -0
- package/skills/uipath-troubleshoot/references/activity-packages/gsuite-activities/playbooks/drive-file-not-found.md +18 -10
- package/skills/uipath-troubleshoot/references/activity-packages/gsuite-activities/playbooks/get-newest-email-not-found.md +9 -8
- package/skills/uipath-troubleshoot/references/activity-packages/gsuite-activities/playbooks/invalid-or-null-input.md +68 -0
- package/skills/uipath-troubleshoot/references/activity-packages/gsuite-activities/playbooks/transient-and-timeout-errors.md +52 -0
- package/skills/uipath-troubleshoot/references/activity-packages/gsuite-activities/playbooks/upload-storage-quota-exceeded.md +1 -1
- package/skills/uipath-troubleshoot/references/activity-packages/gsuite-activities/summary.md +5 -2
- package/skills/uipath-troubleshoot/references/activity-packages/mail-activities/overview.md +28 -0
- package/skills/uipath-troubleshoot/references/activity-packages/mail-activities/playbooks/delete-outlook-mail-failures.md +105 -0
- package/skills/uipath-troubleshoot/references/activity-packages/mail-activities/playbooks/get-outlook-mail-failures.md +109 -0
- package/skills/uipath-troubleshoot/references/activity-packages/mail-activities/playbooks/move-outlook-mail-failures.md +97 -0
- package/skills/uipath-troubleshoot/references/activity-packages/mail-activities/playbooks/send-outlook-mail-failures.md +95 -0
- package/skills/uipath-troubleshoot/references/activity-packages/mail-activities/summary.md +8 -0
- package/skills/uipath-troubleshoot/references/activity-packages/o365-activities/investigation_guide.md +23 -0
- package/skills/uipath-troubleshoot/references/activity-packages/o365-activities/overview.md +9 -0
- package/skills/uipath-troubleshoot/references/activity-packages/o365-activities/playbooks/application-scope-misconfigured.md +49 -0
- package/skills/uipath-troubleshoot/references/activity-packages/o365-activities/playbooks/authentication-token-invalid.md +53 -0
- package/skills/uipath-troubleshoot/references/activity-packages/o365-activities/playbooks/copy-item-argument-null.md +34 -0
- package/skills/uipath-troubleshoot/references/activity-packages/o365-activities/playbooks/create-folder-invalid-path.md +36 -0
- package/skills/uipath-troubleshoot/references/activity-packages/o365-activities/playbooks/download-file-conversion-or-destination.md +40 -0
- package/skills/uipath-troubleshoot/references/activity-packages/o365-activities/playbooks/drive-item-not-found.md +1 -1
- package/skills/uipath-troubleshoot/references/activity-packages/o365-activities/playbooks/email-trigger-connection-event-failure.md +45 -0
- package/skills/uipath-troubleshoot/references/activity-packages/o365-activities/playbooks/insufficient-graph-scope.md +44 -0
- package/skills/uipath-troubleshoot/references/activity-packages/o365-activities/playbooks/legacy-mail-null-reference.md +43 -0
- package/skills/uipath-troubleshoot/references/activity-packages/o365-activities/playbooks/mail-folder-not-found.md +4 -3
- package/skills/uipath-troubleshoot/references/activity-packages/o365-activities/playbooks/mail-invalid-odata-query.md +43 -0
- package/skills/uipath-troubleshoot/references/activity-packages/o365-activities/playbooks/mail-message-not-found.md +45 -0
- package/skills/uipath-troubleshoot/references/activity-packages/o365-activities/playbooks/request-throttled.md +45 -0
- package/skills/uipath-troubleshoot/references/activity-packages/o365-activities/playbooks/send-mail-rejected.md +52 -0
- package/skills/uipath-troubleshoot/references/activity-packages/o365-activities/playbooks/transient-service-error.md +48 -0
- package/skills/uipath-troubleshoot/references/activity-packages/o365-activities/playbooks/upload-file-quota-or-size.md +47 -0
- package/skills/uipath-troubleshoot/references/activity-packages/o365-activities/summary.md +15 -1
- package/skills/uipath-troubleshoot/references/activity-packages/python-activities/investigation_guide.md +32 -0
- package/skills/uipath-troubleshoot/references/activity-packages/python-activities/overview.md +38 -0
- package/skills/uipath-troubleshoot/references/activity-packages/python-activities/playbooks/invoke-method-failures.md +57 -0
- package/skills/uipath-troubleshoot/references/activity-packages/python-activities/playbooks/invoke-python-method-pipe-is-broken.md +44 -0
- package/skills/uipath-troubleshoot/references/activity-packages/python-activities/playbooks/load-script-failures.md +62 -0
- package/skills/uipath-troubleshoot/references/activity-packages/python-activities/playbooks/python-path-not-valid.md +39 -0
- package/skills/uipath-troubleshoot/references/activity-packages/python-activities/playbooks/python-scope-architecture-version-mismatch.md +41 -0
- package/skills/uipath-troubleshoot/references/activity-packages/python-activities/playbooks/working-folder-relative-path.md +34 -0
- package/skills/uipath-troubleshoot/references/activity-packages/python-activities/presentation.md +9 -0
- package/skills/uipath-troubleshoot/references/activity-packages/python-activities/summary.md +12 -0
- package/skills/uipath-troubleshoot/references/activity-packages/system-activities/playbooks/get-asset-activity-bug-silent-failure.md +1 -1
- package/skills/uipath-troubleshoot/references/activity-packages/system-activities/playbooks/get-asset-robot-not-authenticated.md +29 -22
- package/skills/uipath-troubleshoot/references/activity-packages/system-activities/summary.md +1 -1
- package/skills/uipath-troubleshoot/references/activity-packages/web-activities/investigation_guide.md +30 -0
- package/skills/uipath-troubleshoot/references/activity-packages/web-activities/overview.md +36 -0
- package/skills/uipath-troubleshoot/references/activity-packages/web-activities/playbooks/deserialize-malformed-input.md +36 -0
- package/skills/uipath-troubleshoot/references/activity-packages/web-activities/playbooks/deserialize-null-input.md +35 -0
- package/skills/uipath-troubleshoot/references/activity-packages/web-activities/playbooks/deserialize-type-mismatch.md +40 -0
- package/skills/uipath-troubleshoot/references/activity-packages/web-activities/playbooks/http-client-null-reference.md +34 -0
- package/skills/uipath-troubleshoot/references/activity-packages/web-activities/playbooks/http-request-connection-failure.md +48 -0
- package/skills/uipath-troubleshoot/references/activity-packages/web-activities/playbooks/http-request-timeout.md +40 -0
- package/skills/uipath-troubleshoot/references/activity-packages/web-activities/playbooks/net-http-request-aggregate-failure.md +38 -0
- package/skills/uipath-troubleshoot/references/activity-packages/web-activities/summary.md +13 -0
- package/skills/uipath-troubleshoot/references/activity-packages/word-activities/investigation_guide.md +55 -0
- package/skills/uipath-troubleshoot/references/activity-packages/word-activities/overview.md +82 -0
- package/skills/uipath-troubleshoot/references/activity-packages/word-activities/playbooks/add-picture-failures.md +51 -0
- package/skills/uipath-troubleshoot/references/activity-packages/word-activities/playbooks/append-text-missing-container.md +31 -0
- package/skills/uipath-troubleshoot/references/activity-packages/word-activities/playbooks/append-text-zero-byte-file.md +32 -0
- package/skills/uipath-troubleshoot/references/activity-packages/word-activities/playbooks/export-pdf-com-hang.md +46 -0
- package/skills/uipath-troubleshoot/references/activity-packages/word-activities/playbooks/export-pdf-missing-output-dir.md +32 -0
- package/skills/uipath-troubleshoot/references/activity-packages/word-activities/playbooks/export-pdf-output-path-format.md +35 -0
- package/skills/uipath-troubleshoot/references/activity-packages/word-activities/playbooks/read-text-doc-format.md +30 -0
- package/skills/uipath-troubleshoot/references/activity-packages/word-activities/playbooks/read-text-missing-container.md +29 -0
- package/skills/uipath-troubleshoot/references/activity-packages/word-activities/playbooks/read-text-protected-view.md +32 -0
- package/skills/uipath-troubleshoot/references/activity-packages/word-activities/playbooks/replace-text-com-busy.md +35 -0
- package/skills/uipath-troubleshoot/references/activity-packages/word-activities/playbooks/replace-text-file-locked.md +33 -0
- package/skills/uipath-troubleshoot/references/activity-packages/word-activities/playbooks/replace-text-headers-textboxes-skipped.md +30 -0
- package/skills/uipath-troubleshoot/references/activity-packages/word-activities/playbooks/replace-text-length-limit.md +30 -0
- package/skills/uipath-troubleshoot/references/activity-packages/word-activities/playbooks/replace-text-loop-template-overwrite.md +31 -0
- package/skills/uipath-troubleshoot/references/activity-packages/word-activities/playbooks/replace-text-multiline-formatting.md +31 -0
- package/skills/uipath-troubleshoot/references/activity-packages/word-activities/playbooks/replace-text-silent-no-substitution.md +32 -0
- package/skills/uipath-troubleshoot/references/activity-packages/word-activities/playbooks/replace-text-version-mismatch.md +30 -0
- package/skills/uipath-troubleshoot/references/activity-packages/word-activities/playbooks/word-com-interop-failures.md +52 -0
- package/skills/uipath-troubleshoot/references/activity-packages/word-activities/playbooks/word-com-start-background-session0.md +50 -0
- package/skills/uipath-troubleshoot/references/activity-packages/word-activities/playbooks/word-export-pdf-com-wrong-thread.md +46 -0
- package/skills/uipath-troubleshoot/references/activity-packages/word-activities/playbooks/word-open-sharepoint-url-com-command-failed.md +45 -0
- package/skills/uipath-troubleshoot/references/activity-packages/word-activities/playbooks/word-scope-cannot-create-unknown-type.md +30 -0
- package/skills/uipath-troubleshoot/references/activity-packages/word-activities/playbooks/word-scope-com-not-installed.md +35 -0
- package/skills/uipath-troubleshoot/references/activity-packages/word-activities/playbooks/word-scope-file-corrupted.md +34 -0
- package/skills/uipath-troubleshoot/references/activity-packages/word-activities/playbooks/word-scope-file-path-not-found.md +36 -0
- package/skills/uipath-troubleshoot/references/activity-packages/word-activities/playbooks/word-scope-hangs-background-prompt.md +34 -0
- package/skills/uipath-troubleshoot/references/activity-packages/word-activities/presentation.md +11 -0
- package/skills/uipath-troubleshoot/references/activity-packages/word-activities/summary.md +38 -0
- package/skills/uipath-troubleshoot/references/products/integration-service/investigation_guide.md +9 -0
- package/skills/uipath-troubleshoot/references/products/integration-service/overview.md +7 -0
- package/skills/uipath-troubleshoot/references/products/integration-service/playbooks/connection-invalid.md +9 -0
- package/skills/uipath-troubleshoot/references/products/integration-service/playbooks/connector-aggregate-exception.md +30 -0
- package/skills/uipath-troubleshoot/references/products/integration-service/playbooks/connector-general-exception.md +43 -0
- package/skills/uipath-troubleshoot/references/products/integration-service/playbooks/connector-null-reference.md +36 -0
- package/skills/uipath-troubleshoot/references/products/integration-service/playbooks/connector-remote-exception.md +31 -0
- package/skills/uipath-troubleshoot/references/products/integration-service/playbooks/connector-runtime-exception.md +40 -0
- package/skills/uipath-troubleshoot/references/products/integration-service/summary.md +5 -0
- package/skills/uipath-troubleshoot/references/products/maestro/investigation_guide.md +2 -1
- package/skills/uipath-troubleshoot/references/products/orchestrator/investigation_guide.md +105 -10
- package/skills/uipath-troubleshoot/references/products/orchestrator/playbooks/job-faulted-logon-failure.md +1 -1
- package/skills/uipath-troubleshoot/references/summary.md +59 -3
- package/skills/uipath-troubleshoot/schemas/evidence.schema.md +48 -0
- package/skills/uipath-troubleshoot/schemas/hypotheses.schema.md +22 -7
- package/skills/uipath-troubleshoot/schemas/scope-check.schema.md +1 -1
- package/skills/uipath-troubleshoot/schemas/state.schema.md +81 -6
- package/version-manifest.json +2 -2
- package/skills/uipath-data-fabric/SKILL.md +0 -239
- package/skills/uipath-data-fabric/references/bulk-import.md +0 -73
- package/skills/uipath-data-fabric/references/entity-schema.md +0 -391
- package/skills/uipath-data-fabric/references/file-attachments.md +0 -65
- package/skills/uipath-maestro-bpmn/.maintenance/README.md +0 -241
- package/skills/uipath-maestro-bpmn/.maintenance/check-all.sh +0 -41
- package/skills/uipath-maestro-bpmn/.maintenance/check-anchors.sh +0 -83
- package/skills/uipath-maestro-bpmn/.maintenance/check-depth.sh +0 -132
- package/skills/uipath-maestro-bpmn/.maintenance/check-link-text.sh +0 -123
- package/skills/uipath-maestro-bpmn/.maintenance/check-links.sh +0 -70
- package/skills/uipath-maestro-bpmn/.maintenance/check-orphans.sh +0 -93
- package/skills/uipath-maestro-bpmn/.maintenance/check-plugin-pairs.sh +0 -39
- package/skills/uipath-maestro-bpmn/.maintenance/check-real-pack.sh +0 -79
- package/skills/uipath-maestro-bpmn/.maintenance/check-template.sh +0 -42
- package/skills/uipath-maestro-bpmn/.maintenance/check-uip-commands.sh +0 -266
- package/skills/uipath-maestro-bpmn/.maintenance/check-validation-fixtures.py +0 -1255
- package/skills/uipath-maestro-bpmn/.maintenance/check-validation-fixtures.sh +0 -8
- package/skills/uipath-maestro-bpmn/fixtures/validation/README.md +0 -94
- package/skills/uipath-maestro-bpmn/fixtures/validation/agent-invocation/agent-invocation.bpmn +0 -62
- package/skills/uipath-maestro-bpmn/fixtures/validation/agent-invocation/bindings_v2.json +0 -37
- package/skills/uipath-maestro-bpmn/fixtures/validation/agent-invocation/entry-points.json +0 -21
- package/skills/uipath-maestro-bpmn/fixtures/validation/agent-invocation/operate.json +0 -9
- package/skills/uipath-maestro-bpmn/fixtures/validation/agent-invocation/package-descriptor.json +0 -10
- package/skills/uipath-maestro-bpmn/fixtures/validation/agent-invocation/project.uiproj +0 -7
- package/skills/uipath-maestro-bpmn/fixtures/validation/contract-variants/bindings_v2.json +0 -68
- package/skills/uipath-maestro-bpmn/fixtures/validation/contract-variants/contract-variants.bpmn +0 -270
- package/skills/uipath-maestro-bpmn/fixtures/validation/contract-variants/entry-points.json +0 -25
- package/skills/uipath-maestro-bpmn/fixtures/validation/contract-variants/operate.json +0 -9
- package/skills/uipath-maestro-bpmn/fixtures/validation/contract-variants/package-descriptor.json +0 -10
- package/skills/uipath-maestro-bpmn/fixtures/validation/contract-variants/project.uiproj +0 -7
- package/skills/uipath-maestro-bpmn/fixtures/validation/gateway-boundary-error/bindings_v2.json +0 -19
- package/skills/uipath-maestro-bpmn/fixtures/validation/gateway-boundary-error/entry-points.json +0 -25
- package/skills/uipath-maestro-bpmn/fixtures/validation/gateway-boundary-error/gateway-boundary-error.bpmn +0 -91
- package/skills/uipath-maestro-bpmn/fixtures/validation/gateway-boundary-error/operate.json +0 -9
- package/skills/uipath-maestro-bpmn/fixtures/validation/gateway-boundary-error/package-descriptor.json +0 -10
- package/skills/uipath-maestro-bpmn/fixtures/validation/gateway-boundary-error/project.uiproj +0 -7
- package/skills/uipath-maestro-bpmn/fixtures/validation/imported-brownfield-preservation/bindings_v2.json +0 -4
- package/skills/uipath-maestro-bpmn/fixtures/validation/imported-brownfield-preservation/entry-points.json +0 -25
- package/skills/uipath-maestro-bpmn/fixtures/validation/imported-brownfield-preservation/imported-brownfield-preservation.bpmn +0 -63
- package/skills/uipath-maestro-bpmn/fixtures/validation/imported-brownfield-preservation/operate.json +0 -9
- package/skills/uipath-maestro-bpmn/fixtures/validation/imported-brownfield-preservation/package-descriptor.json +0 -10
- package/skills/uipath-maestro-bpmn/fixtures/validation/imported-brownfield-preservation/project.uiproj +0 -7
- package/skills/uipath-maestro-bpmn/fixtures/validation/integration-service-enriched/bindings_v2.json +0 -52
- package/skills/uipath-maestro-bpmn/fixtures/validation/integration-service-enriched/entry-points.json +0 -21
- package/skills/uipath-maestro-bpmn/fixtures/validation/integration-service-enriched/integration-service-enriched.bpmn +0 -65
- package/skills/uipath-maestro-bpmn/fixtures/validation/integration-service-enriched/operate.json +0 -9
- package/skills/uipath-maestro-bpmn/fixtures/validation/integration-service-enriched/package-descriptor.json +0 -10
- package/skills/uipath-maestro-bpmn/fixtures/validation/integration-service-enriched/project.uiproj +0 -7
- package/skills/uipath-maestro-bpmn/fixtures/validation/linear-process/bindings_v2.json +0 -4
- package/skills/uipath-maestro-bpmn/fixtures/validation/linear-process/entry-points.json +0 -25
- package/skills/uipath-maestro-bpmn/fixtures/validation/linear-process/linear-process.bpmn +0 -55
- package/skills/uipath-maestro-bpmn/fixtures/validation/linear-process/operate.json +0 -9
- package/skills/uipath-maestro-bpmn/fixtures/validation/linear-process/package-descriptor.json +0 -10
- package/skills/uipath-maestro-bpmn/fixtures/validation/linear-process/project.uiproj +0 -7
- package/skills/uipath-maestro-bpmn/fixtures/validation/registry-coverage-matrix/bindings_v2.json +0 -20
- package/skills/uipath-maestro-bpmn/fixtures/validation/registry-coverage-matrix/entry-points.json +0 -17
- package/skills/uipath-maestro-bpmn/fixtures/validation/registry-coverage-matrix/operate.json +0 -9
- package/skills/uipath-maestro-bpmn/fixtures/validation/registry-coverage-matrix/package-descriptor.json +0 -10
- package/skills/uipath-maestro-bpmn/fixtures/validation/registry-coverage-matrix/project.uiproj +0 -7
- package/skills/uipath-maestro-bpmn/fixtures/validation/registry-coverage-matrix/registry-coverage-matrix.bpmn +0 -207
- package/skills/uipath-maestro-bpmn/fixtures/validation/subprocess-multi-instance/bindings_v2.json +0 -4
- package/skills/uipath-maestro-bpmn/fixtures/validation/subprocess-multi-instance/entry-points.json +0 -33
- package/skills/uipath-maestro-bpmn/fixtures/validation/subprocess-multi-instance/operate.json +0 -9
- package/skills/uipath-maestro-bpmn/fixtures/validation/subprocess-multi-instance/package-descriptor.json +0 -10
- package/skills/uipath-maestro-bpmn/fixtures/validation/subprocess-multi-instance/project.uiproj +0 -7
- package/skills/uipath-maestro-bpmn/fixtures/validation/subprocess-multi-instance/subprocess-multi-instance.bpmn +0 -94
- package/skills/uipath-maestro-bpmn/fixtures/validation/wrapper-family-contract/bindings_v2.json +0 -124
- package/skills/uipath-maestro-bpmn/fixtures/validation/wrapper-family-contract/entry-points.json +0 -25
- package/skills/uipath-maestro-bpmn/fixtures/validation/wrapper-family-contract/operate.json +0 -9
- package/skills/uipath-maestro-bpmn/fixtures/validation/wrapper-family-contract/package-descriptor.json +0 -10
- package/skills/uipath-maestro-bpmn/fixtures/validation/wrapper-family-contract/project.uiproj +0 -7
- package/skills/uipath-maestro-bpmn/fixtures/validation/wrapper-family-contract/wrapper-family-contract.bpmn +0 -151
- package/skills/uipath-maestro-bpmn/references/author/CAPABILITY.md +0 -141
- package/skills/uipath-maestro-bpmn/references/author/references/brownfield.md +0 -48
- package/skills/uipath-maestro-bpmn/references/author/references/editing-operations.md +0 -75
- package/skills/uipath-maestro-bpmn/references/author/references/greenfield.md +0 -41
- package/skills/uipath-maestro-bpmn/references/author/references/planning-arch.md +0 -82
- package/skills/uipath-maestro-bpmn/references/author/references/planning-impl.md +0 -104
- package/skills/uipath-maestro-bpmn/references/author/references/plugins/agents/impl.md +0 -34
- package/skills/uipath-maestro-bpmn/references/author/references/plugins/agents/planning.md +0 -38
- package/skills/uipath-maestro-bpmn/references/author/references/plugins/api-workflows/impl.md +0 -37
- package/skills/uipath-maestro-bpmn/references/author/references/plugins/api-workflows/planning.md +0 -28
- package/skills/uipath-maestro-bpmn/references/author/references/plugins/business-rules/impl.md +0 -36
- package/skills/uipath-maestro-bpmn/references/author/references/plugins/business-rules/planning.md +0 -29
- package/skills/uipath-maestro-bpmn/references/author/references/plugins/call-activity-subprocess/impl.md +0 -67
- package/skills/uipath-maestro-bpmn/references/author/references/plugins/call-activity-subprocess/planning.md +0 -29
- package/skills/uipath-maestro-bpmn/references/author/references/plugins/connectors/impl.md +0 -29
- package/skills/uipath-maestro-bpmn/references/author/references/plugins/connectors/planning.md +0 -33
- package/skills/uipath-maestro-bpmn/references/author/references/plugins/gateways/impl.md +0 -32
- package/skills/uipath-maestro-bpmn/references/author/references/plugins/gateways/planning.md +0 -34
- package/skills/uipath-maestro-bpmn/references/author/references/plugins/hitl/impl.md +0 -42
- package/skills/uipath-maestro-bpmn/references/author/references/plugins/hitl/planning.md +0 -30
- package/skills/uipath-maestro-bpmn/references/author/references/plugins/integration-service/impl.md +0 -134
- package/skills/uipath-maestro-bpmn/references/author/references/plugins/integration-service/planning.md +0 -67
- package/skills/uipath-maestro-bpmn/references/author/references/plugins/multi-instance/impl.md +0 -50
- package/skills/uipath-maestro-bpmn/references/author/references/plugins/multi-instance/planning.md +0 -29
- package/skills/uipath-maestro-bpmn/references/author/references/plugins/queues/impl.md +0 -29
- package/skills/uipath-maestro-bpmn/references/author/references/plugins/queues/planning.md +0 -29
- package/skills/uipath-maestro-bpmn/references/author/references/plugins/rpa-jobs/impl.md +0 -35
- package/skills/uipath-maestro-bpmn/references/author/references/plugins/rpa-jobs/planning.md +0 -29
- package/skills/uipath-maestro-bpmn/references/author/references/plugins/script/impl.md +0 -118
- package/skills/uipath-maestro-bpmn/references/author/references/plugins/script/planning.md +0 -30
- package/skills/uipath-maestro-bpmn/references/author/references/plugins/sequence-flows/impl.md +0 -30
- package/skills/uipath-maestro-bpmn/references/author/references/plugins/sequence-flows/planning.md +0 -30
- package/skills/uipath-maestro-bpmn/references/author/references/plugins/service-tasks/impl.md +0 -33
- package/skills/uipath-maestro-bpmn/references/author/references/plugins/service-tasks/planning.md +0 -31
- package/skills/uipath-maestro-bpmn/references/author/references/plugins/signals/impl.md +0 -33
- package/skills/uipath-maestro-bpmn/references/author/references/plugins/signals/planning.md +0 -46
- package/skills/uipath-maestro-bpmn/references/author/references/plugins/start-end-events/impl.md +0 -59
- package/skills/uipath-maestro-bpmn/references/author/references/plugins/start-end-events/planning.md +0 -37
- package/skills/uipath-maestro-bpmn/references/author/references/plugins/waits-triggers/impl.md +0 -40
- package/skills/uipath-maestro-bpmn/references/author/references/plugins/waits-triggers/planning.md +0 -34
- package/skills/uipath-maestro-bpmn/references/author/references/supported-elements.md +0 -123
- package/skills/uipath-maestro-bpmn/references/author/references/task-recipes/README.md +0 -29
- package/skills/uipath-maestro-bpmn/references/author/references/task-recipes/agent-job.md +0 -36
- package/skills/uipath-maestro-bpmn/references/author/references/task-recipes/api-workflow.md +0 -29
- package/skills/uipath-maestro-bpmn/references/author/references/task-recipes/business-rule.md +0 -18
- package/skills/uipath-maestro-bpmn/references/author/references/task-recipes/call-activity.md +0 -56
- package/skills/uipath-maestro-bpmn/references/author/references/task-recipes/hitl.md +0 -17
- package/skills/uipath-maestro-bpmn/references/author/references/task-recipes/http-request.md +0 -119
- package/skills/uipath-maestro-bpmn/references/author/references/task-recipes/queue.md +0 -21
- package/skills/uipath-maestro-bpmn/references/author/references/task-recipes/rpa-job.md +0 -23
- package/skills/uipath-maestro-bpmn/references/author/references/validation.md +0 -118
- package/skills/uipath-maestro-bpmn/references/shared/bpmn-xml-contract.md +0 -197
- package/skills/uipath-maestro-bpmn/references/shared/cli-conventions.md +0 -46
- package/skills/uipath-maestro-bpmn/references/shared/error-handling.md +0 -82
- package/skills/uipath-maestro-bpmn/references/shared/variables-bindings-expressions.md +0 -150
- package/skills/uipath-maestro-bpmn/references/shared/wrapper-shells.md +0 -554
- /package/skills/uipath-maestro-bpmn/references/{shared/expression-authoring.md → expression-authoring.md} +0 -0
- /package/skills/uipath-maestro-bpmn/references/{shared/public-safety.md → public-safety.md} +0 -0
- /package/{agents → skills/uipath-rpa/agents}/uipath-project-discovery-agent.md +0 -0
|
@@ -0,0 +1,1371 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* build-dashboard.mjs — Dashboard build pipeline
|
|
4
|
+
*
|
|
5
|
+
* Accepts an intent.json or edit-intent.json as a file path argument and
|
|
6
|
+
* generates a complete React dashboard project.
|
|
7
|
+
*
|
|
8
|
+
* Input routing:
|
|
9
|
+
* intent.json (has "metrics" field) → runDashboardBuild()
|
|
10
|
+
* edit-intent.json (has "op" field) → runIncrementalEdit()
|
|
11
|
+
*
|
|
12
|
+
* Usage:
|
|
13
|
+
* node build-dashboard.mjs <path-to-json>
|
|
14
|
+
*
|
|
15
|
+
* Exit codes:
|
|
16
|
+
* 0 — success
|
|
17
|
+
* 1 — fatal error (message on stderr)
|
|
18
|
+
* 2 — metric modules need retry (fix the named metrics/<name>.ts files and re-run)
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync, renameSync, unlinkSync } from 'fs'
|
|
22
|
+
import { join, dirname, resolve } from 'path'
|
|
23
|
+
import { fileURLToPath, pathToFileURL } from 'url'
|
|
24
|
+
import { execSync } from 'child_process'
|
|
25
|
+
import { createHash } from 'crypto'
|
|
26
|
+
// Project-lifecycle utilities (versioning, prewarm, sdk-check, dev-server teardown)
|
|
27
|
+
// live in a sibling module. Imported for internal use here; the test-facing ones are
|
|
28
|
+
// re-exported so `import … from build-dashboard.mjs` keeps working. (Benign runtime
|
|
29
|
+
// import cycle — lifecycle.mjs imports primitives/consts back, used only at call time.)
|
|
30
|
+
import {
|
|
31
|
+
readScaffoldVersion, buildVersions, scaffoldDrift, runIntentMigrations, runPrewarm,
|
|
32
|
+
} from './lifecycle.mjs'
|
|
33
|
+
export { readScaffoldVersion, buildVersions, scaffoldDrift, runIntentMigrations }
|
|
34
|
+
|
|
35
|
+
// Use-case flow orchestrators (extracted to ./flows/*). Imported here for the
|
|
36
|
+
// entry-point dispatcher; packTemplate is also re-exported (tests import it from
|
|
37
|
+
// this module). Benign cycle: the flows import shared primitives back from here,
|
|
38
|
+
// referenced only inside their function bodies (call time), never at module eval.
|
|
39
|
+
import { runDashboardBuild } from './flows/build.mjs'
|
|
40
|
+
import { runIncrementalEdit } from './flows/edit.mjs'
|
|
41
|
+
import { packTemplate } from './flows/template.mjs'
|
|
42
|
+
export { packTemplate }
|
|
43
|
+
|
|
44
|
+
// ── Path constants ─────────────────────────────────────────────────────────────
|
|
45
|
+
|
|
46
|
+
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
47
|
+
|
|
48
|
+
// Widget generator templates ship INSIDE the starter-kit archive (under _gen/widgets),
|
|
49
|
+
// not in the skill — the skill carries no scaffold/template source and no zip code.
|
|
50
|
+
// The orchestration points this at <project>/_gen/widgets after the agent extracts
|
|
51
|
+
// the kit; tests set it via setWidgetsDir(). null until set.
|
|
52
|
+
let WIDGETS_DIR = null
|
|
53
|
+
export function setWidgetsDir(dir) { WIDGETS_DIR = dir }
|
|
54
|
+
function widgetsDir() {
|
|
55
|
+
if (!WIDGETS_DIR) fail('Widget templates dir not set — extract the starter kit (assertScaffoldExtracted) before generating widgets.')
|
|
56
|
+
return WIDGETS_DIR
|
|
57
|
+
}
|
|
58
|
+
function t3ShellTemplatePath() { return join(widgetsDir(), 't3-shell.tsx.template') }
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Fixed dev server port. Kept BELOW the ephemeral ranges (Linux 32768+, Windows/macOS
|
|
62
|
+
* 49152+) so it can't land in an OS-reserved block — Windows (WinNAT/Hyper-V/WSL2)
|
|
63
|
+
* carves reserved chunks out of the ephemeral range, and a dev server can't bind one
|
|
64
|
+
* even when it's free (`An attempt was made to access a socket in a way forbidden by
|
|
65
|
+
* its access permissions`). 25173 is high enough to dodge common dev servers yet clear
|
|
66
|
+
* of every ephemeral range. Must match the scaffold's vite.config.ts `server.port`.
|
|
67
|
+
*/
|
|
68
|
+
export const DASHBOARD_PORT = 25173
|
|
69
|
+
|
|
70
|
+
// ── Capability registry ────────────────────────────────────────────────────────
|
|
71
|
+
|
|
72
|
+
const REGISTRY = JSON.parse(readFileSync(resolve(__dirname, 'capability-registry.json'), 'utf8'))
|
|
73
|
+
|
|
74
|
+
// ── Type definitions (JSDoc only — this file is plain JavaScript) ────────────
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* @typedef {'T1'|'T2'|'T3'} MetricTier
|
|
78
|
+
*/
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* A single metric entry inside intent.json.
|
|
82
|
+
* @typedef {Object} IntentMetric
|
|
83
|
+
* @property {string} name - Kebab-case metric identifier
|
|
84
|
+
* @property {MetricTier} tier - Resolution tier
|
|
85
|
+
* @property {string} [title] - Display title (required for all tiers)
|
|
86
|
+
* @property {string} [subtitle] - CardDescription line; auto-filled from time range when absent
|
|
87
|
+
* @property {string} [description] - Fallback for subtitle
|
|
88
|
+
* @property {string} [componentName] - Override PascalCase component name
|
|
89
|
+
* @property {string} [icon] - lucide-react icon name
|
|
90
|
+
* @property {string} [detailRoute] - HashRouter path for drilldown
|
|
91
|
+
* @property {Object} [params] - T2 filter params (validation only — the filter logic lives in the metric module)
|
|
92
|
+
* @property {string} [module] - Relative path to the metric's data-fetch module (defaults to metrics/<name>.ts)
|
|
93
|
+
* @property {string} [displayAs] - One of VALID_DISPLAY_TYPES
|
|
94
|
+
* @property {string} [valueField] - kpi-card: field shown as the headline number
|
|
95
|
+
* @property {string} [valueLabel] - kpi-card: label under the headline
|
|
96
|
+
* @property {string} [xKey] - Chart x-axis field
|
|
97
|
+
* @property {string} [yKey] - Chart value field
|
|
98
|
+
* @property {string} [headlineMode] - VALID_HEADLINE_MODES — how the chart headline aggregates
|
|
99
|
+
* @property {string} [deltaPolarity] - VALID_DELTA_POLARITIES — is an increase good?
|
|
100
|
+
* @property {string} [rateNum] - rate-chart: numerator field per bucket
|
|
101
|
+
* @property {string} [rateDen] - rate-chart: denominator field per bucket
|
|
102
|
+
* @property {string} [columns] - ColumnDef array literal string (tables)
|
|
103
|
+
* @property {Array<{key:string,label:string,align?:string,format?:string,color?:string}>} [columnDefs] - Structured columns; compiled to formatted/coloured cells
|
|
104
|
+
* @property {boolean} [detail] - kpi-card only: opt into a record-grain drill-down (module must export fetchDetail)
|
|
105
|
+
* @property {Array} [detailColumns] - Structured columns for the detail view (falls back to registry defaults.detailColumns)
|
|
106
|
+
* @property {string} [detailSortKey] - Raw field the detail table sorts on
|
|
107
|
+
* @property {string} [series] - multi-line-chart series literal
|
|
108
|
+
* @property {string} [pivotExpression] - multi-line-chart pivot expression
|
|
109
|
+
* @property {DetailViewSpec} [detailView]
|
|
110
|
+
* @property {{key:string}} [rowLink]
|
|
111
|
+
*/
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* The full intent.json structure.
|
|
115
|
+
* @typedef {Object} DashboardIntent
|
|
116
|
+
* @property {string} dashboardName
|
|
117
|
+
* @property {string} [dashboardDescription] - One sentence for the dashboard header
|
|
118
|
+
* @property {'1d'|'7d'|'30d'|'90d'} timeRange
|
|
119
|
+
* @property {IntentMetric[]} metrics
|
|
120
|
+
* @property {string} projectDir - Project dir; "." by contract (build runs from inside the pre-warmed <routingName> folder; must already exist)
|
|
121
|
+
* @property {string} routingName - Permanent URL slug (e.g. "agent-health-x7k2")
|
|
122
|
+
* @property {string} orgName
|
|
123
|
+
* @property {string} tenantName
|
|
124
|
+
* @property {string} cloudUrl - e.g. https://alpha.uipath.com
|
|
125
|
+
* @property {string} apiUrl - e.g. https://alpha.api.uipath.com
|
|
126
|
+
* @property {string} [clientId] - External OAuth app client ID
|
|
127
|
+
*/
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Derived chart-widget spec — all fields resolved, ready for template substitution.
|
|
131
|
+
* @typedef {Object} WidgetSpec
|
|
132
|
+
* @property {string} componentName
|
|
133
|
+
* @property {string} template
|
|
134
|
+
* @property {string} title
|
|
135
|
+
* @property {string} subtitle
|
|
136
|
+
* @property {string} icon
|
|
137
|
+
* @property {string} detailRoute
|
|
138
|
+
* @property {string} dataHook
|
|
139
|
+
* @property {string} dataSelector
|
|
140
|
+
* @property {string} xKey
|
|
141
|
+
* @property {string} yKey
|
|
142
|
+
* @property {string} headlineMode
|
|
143
|
+
* @property {string} deltaPolarity
|
|
144
|
+
* @property {string} rateNum
|
|
145
|
+
* @property {string} rateDen
|
|
146
|
+
* @property {string} series
|
|
147
|
+
* @property {string} pivotExpression
|
|
148
|
+
*/
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Minimal widget descriptor used for layout classification.
|
|
152
|
+
* @typedef {Object} WidgetMeta
|
|
153
|
+
* @property {string} componentName
|
|
154
|
+
* @property {string} template
|
|
155
|
+
*/
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Per-widget entry persisted in state.json.
|
|
159
|
+
* @typedef {Object} WidgetHashEntry
|
|
160
|
+
* @property {string} hash - SHA-256 prefix of generated file content
|
|
161
|
+
* @property {MetricTier} tier
|
|
162
|
+
* @property {string} metric - Original metric name from intent
|
|
163
|
+
* @property {string} template - Template used for layout classification
|
|
164
|
+
* @property {IntentMetric} intentMetric - Full intent entry, persisted for CHANGE/REBUILD
|
|
165
|
+
*/
|
|
166
|
+
|
|
167
|
+
// ── Constants ──────────────────────────────────────────────────────────────────
|
|
168
|
+
|
|
169
|
+
const TIME_RANGE_CONSTANTS = {
|
|
170
|
+
'1d': 'ONE_DAY_AGO',
|
|
171
|
+
'7d': 'SEVEN_DAYS_AGO',
|
|
172
|
+
'30d': 'THIRTY_DAYS_AGO',
|
|
173
|
+
'90d': 'NINETY_DAYS_AGO',
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const KNOWN_EVENTS = new Set([
|
|
177
|
+
'PREWARM_START', 'PREWARM_DONE', 'PREWARM_FAILED', 'SCAFFOLD_READY', 'ENV_WRITTEN',
|
|
178
|
+
'WIDGET_READY', 'METRICS_PASS', 'METRICS_RETRY', 'CHART_DETAIL_MISSING', 'T3_FAILED', 'TSC_PASS', 'TSC_FAIL',
|
|
179
|
+
'BUILD_RESULT', 'PARTIAL_BUILD_DETECTED', 'AUTH_MISSING',
|
|
180
|
+
'HAND_EDIT_DETECTED', 'T2_SCHEMA_ERROR', 'INCREMENTAL_READY', 'UPGRADE_AVAILABLE', 'UPGRADE_DONE',
|
|
181
|
+
'TEMPLATE_BUILD', 'EJECTED', 'EJECTED_PROJECT', 'TEMPLATE_PACKED',
|
|
182
|
+
])
|
|
183
|
+
|
|
184
|
+
export const VALID_EDIT_OPS = ['ADD', 'REMOVE', 'CHANGE', 'REBUILD', 'UPGRADE', 'EJECT']
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Display types supported for all widget tiers.
|
|
188
|
+
* Table/KPI types use t3-shell.tsx.template.
|
|
189
|
+
* Chart types use the standard chart templates with customDataFn injected.
|
|
190
|
+
*/
|
|
191
|
+
export const VALID_DISPLAY_TYPES = [
|
|
192
|
+
'kpi-card', 'ranked-table', 'data-table',
|
|
193
|
+
'area-chart', 'line-chart', 'bar-chart', 'donut-chart', 'multi-line-chart', 'rate-chart',
|
|
194
|
+
]
|
|
195
|
+
|
|
196
|
+
/** Detail-view sub-widget displayAs vocabularies (rich drill-down). */
|
|
197
|
+
const DETAIL_CHART_TYPES = new Set(['donut-chart', 'bar-chart', 'area-chart', 'line-chart', 'multi-line-chart'])
|
|
198
|
+
const DETAIL_TABLE_TYPES = new Set(['data-table', 'ranked-table'])
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* @typedef {Object} DetailViewWidget
|
|
202
|
+
* @property {'donut-chart'|'bar-chart'|'area-chart'|'line-chart'|'multi-line-chart'|'data-table'|'ranked-table'} displayAs
|
|
203
|
+
* @property {string} title
|
|
204
|
+
* @property {string} source Key into the detail fetch's named-source map.
|
|
205
|
+
* @property {string} [xKey]
|
|
206
|
+
* @property {string} [yKey]
|
|
207
|
+
* @property {Array<{key:string,color:string}>} [series] Required for multi-line-chart.
|
|
208
|
+
* @property {Array<object>} [columns] Table column defs.
|
|
209
|
+
*/
|
|
210
|
+
/**
|
|
211
|
+
* @typedef {Object} DetailViewSpec
|
|
212
|
+
* @property {DetailViewWidget[]} widgets
|
|
213
|
+
*/
|
|
214
|
+
/** Validate a metric's optional detailView spec. Throws with a precise message. */
|
|
215
|
+
export function validateDetailView(metric) {
|
|
216
|
+
const dv = metric.detailView
|
|
217
|
+
if (dv == null) return
|
|
218
|
+
if (!metric.rowLink?.key && metric.detail !== true) {
|
|
219
|
+
throw new Error(`metric "${metric.name}": detailView requires the metric to have rowLink.key (table) or detail:true (chart)`)
|
|
220
|
+
}
|
|
221
|
+
if (!Array.isArray(dv.widgets) || dv.widgets.length === 0) {
|
|
222
|
+
throw new Error(`metric "${metric.name}": detailView.widgets must be a non-empty array`)
|
|
223
|
+
}
|
|
224
|
+
for (const w of dv.widgets) {
|
|
225
|
+
if (!w.source || typeof w.source !== 'string') throw new Error(`metric "${metric.name}": each detailView widget needs a "source" string`)
|
|
226
|
+
if (!w.title) throw new Error(`metric "${metric.name}": detailView widget (source "${w.source}") needs a title`)
|
|
227
|
+
const isChart = DETAIL_CHART_TYPES.has(w.displayAs)
|
|
228
|
+
const isTable = DETAIL_TABLE_TYPES.has(w.displayAs)
|
|
229
|
+
if (!isChart && !isTable) throw new Error(`metric "${metric.name}": detailView widget displayAs "${w.displayAs}" invalid`)
|
|
230
|
+
if (w.displayAs === 'multi-line-chart') {
|
|
231
|
+
if (!w.xKey || !Array.isArray(w.series) || w.series.length === 0) throw new Error(`metric "${metric.name}": detailView multi-line-chart needs xKey + series[]`)
|
|
232
|
+
} else if (isChart) {
|
|
233
|
+
if (!w.xKey || !w.yKey) throw new Error(`metric "${metric.name}": detailView ${w.displayAs} needs xKey and yKey`)
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/** Headline aggregate modes — how a chart's big number is computed from its series. */
|
|
239
|
+
export const VALID_HEADLINE_MODES = ['sum', 'avg', 'latest', 'count', 'max', 'min']
|
|
240
|
+
|
|
241
|
+
/** Delta polarity — whether an increase is good, bad, or neutral (drives badge colour). */
|
|
242
|
+
export const VALID_DELTA_POLARITIES = ['up-good', 'up-bad', 'neutral']
|
|
243
|
+
|
|
244
|
+
/** Column value formatters supported in table column defs. */
|
|
245
|
+
export const VALID_COLUMN_FORMATS = ['number', 'percent', 'duration', 'timeAgo', 'text']
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Minimum SDK version the catalog metrics require — the agent-memory and
|
|
249
|
+
* governance subpaths ship in 1.4.1; the Agents insights aggregates
|
|
250
|
+
* (getSummary / getTopErrorCount / getTopConsumption / getIncidentDistribution
|
|
251
|
+
* / getUnitConsumptionSummary) ship in 1.5.0. A stale lockfile/registry
|
|
252
|
+
* otherwise surfaces as cryptic tsc "module not found" failures mid-build.
|
|
253
|
+
*/
|
|
254
|
+
export const MIN_SDK_VERSION = '1.5.1'
|
|
255
|
+
|
|
256
|
+
export const SKILL_VERSION = '2.2.0' // 2.2 = runtime governance on AgentTraces getGovernanceDecisions/getGovernanceSummary (SDK 1.5.1)
|
|
257
|
+
const FIXTURE_ARCHIVE_PATH = resolve(__dirname, '../../fixtures/governance-dashboard-starter-kit.tar.gz')
|
|
258
|
+
|
|
259
|
+
export const INTENT_SCHEMA_VERSION = 2
|
|
260
|
+
export const STATE_SCHEMA_VERSION = 2
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Generate (or remove) a table widget's keyed row-click detail view. Writes
|
|
264
|
+
* <Component>DetailView.tsx when the metric is a table with `rowLink`, and removes
|
|
265
|
+
* a stale one otherwise. Shared by fresh build, ADD, CHANGE, REBUILD, and upgrade so
|
|
266
|
+
* the row-click drill-down stays consistent on every path. Returns true if a keyed
|
|
267
|
+
* view now exists on disk.
|
|
268
|
+
* @returns {boolean}
|
|
269
|
+
*/
|
|
270
|
+
export function writeKeyedViewIfRowLink(P, componentName, metric, entry, timeRange) {
|
|
271
|
+
const detailPath = join(P, 'src', 'dashboard', 'views', `${componentName}DetailView.tsx`)
|
|
272
|
+
const displayAs = metric.displayAs ?? entry?.template ?? ''
|
|
273
|
+
// rowLink and detailView fall back to the registry entry's defaults — same as
|
|
274
|
+
// title/columns — so a cataloged metric (e.g. agent-compliance-report) ships its
|
|
275
|
+
// drill-down without the intent having to restate it.
|
|
276
|
+
const rowLink = metric.rowLink ?? entry?.defaults?.rowLink
|
|
277
|
+
const detailView = metric.detailView ?? entry?.defaults?.detailView ?? null
|
|
278
|
+
if (rowLink?.key && widgetLayoutGroup(displayAs) === 'table') {
|
|
279
|
+
writeAtomic(detailPath, generateKeyedDetailViewFile({
|
|
280
|
+
componentName,
|
|
281
|
+
title: metric.title ?? entry?.defaults?.title ?? componentName,
|
|
282
|
+
subtitle: autoSubtitle(metric, entry?.defaults ?? {}, timeRange),
|
|
283
|
+
moduleSpecifier: metricModuleSpecifier(metric),
|
|
284
|
+
detailColumns: metric.detailColumns ? compileColumns(metric.detailColumns) : null,
|
|
285
|
+
detailView,
|
|
286
|
+
}))
|
|
287
|
+
return true
|
|
288
|
+
}
|
|
289
|
+
if (existsSync(detailPath)) unlinkSync(detailPath)
|
|
290
|
+
return false
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/** Route descriptors for every widget that currently has a keyed DetailView file on disk. */
|
|
294
|
+
export function collectKeyedViews(P, state) {
|
|
295
|
+
return Object.keys(state.widgets ?? {})
|
|
296
|
+
.filter(name => existsSync(join(P, 'src', 'dashboard', 'views', `${name}DetailView.tsx`)))
|
|
297
|
+
.map(name => ({ componentName: name, routeBase: `/${name.toLowerCase()}` }))
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Regenerate every widget + chart view from each widget's persisted intentMetric
|
|
302
|
+
* (metadata) against the on-disk metric modules. Used by the REBUILD op and by upgrade.
|
|
303
|
+
* @param {string} P resolved project dir
|
|
304
|
+
* @param {object} state parsed state.json (widget hashes refreshed in place)
|
|
305
|
+
* @param {string} timeRange
|
|
306
|
+
*/
|
|
307
|
+
export function rebuildAllWidgets(P, state, timeRange) {
|
|
308
|
+
for (const [componentName, info] of Object.entries(state.widgets ?? {})) {
|
|
309
|
+
const m = info.intentMetric
|
|
310
|
+
if (!m) {
|
|
311
|
+
log(`⚠ Cannot rebuild "${componentName}" — built before intent persistence. Re-run a fresh build to refresh it.`)
|
|
312
|
+
continue
|
|
313
|
+
}
|
|
314
|
+
const { entry } = resolveMetric(m)
|
|
315
|
+
const content = buildWidgetFile(m, entry, timeRange)
|
|
316
|
+
writeAtomic(join(P, 'src', 'dashboard', 'widgets', `${componentName}.tsx`), content)
|
|
317
|
+
info.hash = hashContent(content)
|
|
318
|
+
const viewPath = join(P, 'src', 'dashboard', 'views', `${componentName}View.tsx`)
|
|
319
|
+
if (widgetGetsDetailView(info.template ?? '', m, entry)) {
|
|
320
|
+
writeAtomic(viewPath, generateViewFile(buildViewSpec(componentName, m, entry, timeRange)))
|
|
321
|
+
} else if (existsSync(viewPath)) {
|
|
322
|
+
unlinkSync(viewPath)
|
|
323
|
+
}
|
|
324
|
+
writeKeyedViewIfRowLink(P, componentName, m, entry, timeRange)
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
/** Map a time range to a human subtitle fragment. */
|
|
330
|
+
function timeRangeLabel(timeRange) {
|
|
331
|
+
return { '1d': 'last 24 hours', '7d': 'last 7 days', '30d': 'last 30 days', '90d': 'last 90 days' }[timeRange] ?? timeRange
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* OAuth scopes required for the dashboard.
|
|
336
|
+
* These MUST match what is registered on the external OAuth app.
|
|
337
|
+
* Use parent scopes only — .Read sub-scopes are not reliably registered.
|
|
338
|
+
*/
|
|
339
|
+
export const DASHBOARD_SCOPES = 'OR.Assets OR.Jobs OR.Folders OR.Buckets OR.Execution OR.Tasks OR.Queues OR.Users Insights Insights.RealTimeData Traces.Api PIMS'
|
|
340
|
+
|
|
341
|
+
// ── Low-level utilities ────────────────────────────────────────────────────────
|
|
342
|
+
|
|
343
|
+
export function fail(msg) {
|
|
344
|
+
process.stderr.write(`ERROR: ${msg}\n`)
|
|
345
|
+
process.exit(1)
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
export function log(msg) {
|
|
349
|
+
process.stdout.write(msg + '\n')
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* The agent extracts the starter-kit .tar.gz into the project dir before building,
|
|
354
|
+
* using the OS `tar` (built into Windows 10+, macOS, Linux). This verifies the kit
|
|
355
|
+
* landed (scaffold app + the _gen/widgets generator templates) and fails loud with
|
|
356
|
+
* the extract command if not.
|
|
357
|
+
* @param {string} projectPath
|
|
358
|
+
*/
|
|
359
|
+
export function assertScaffoldExtracted(projectPath) {
|
|
360
|
+
const hasScaffold = existsSync(join(projectPath, 'package.json'))
|
|
361
|
+
const hasTemplates = existsSync(join(projectPath, '_gen', 'widgets'))
|
|
362
|
+
if (hasScaffold && hasTemplates) return
|
|
363
|
+
fail([
|
|
364
|
+
`Starter kit not extracted into: ${projectPath}`,
|
|
365
|
+
`Extract it first with tar (built into Windows 10+, macOS, Linux).`,
|
|
366
|
+
`Feed the archive on stdin (-f -) so GNU tar doesn't misread the C:\\ drive colon as a remote host:`,
|
|
367
|
+
` mkdir -p "${projectPath}" && tar -xz -C "${projectPath}" -f - < "${FIXTURE_ARCHIVE_PATH}"`,
|
|
368
|
+
`Then re-run the build.`,
|
|
369
|
+
].join('\n'))
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Atomic file write — write to .tmp then rename on success.
|
|
374
|
+
*
|
|
375
|
+
* On Windows the rename can fail with EPERM/EBUSY when a watcher (e.g. the Vite
|
|
376
|
+
* dev server during a REBUILD) holds the target file open. Retry the rename a
|
|
377
|
+
* few times with a short backoff, then fall back to a direct overwrite so the
|
|
378
|
+
* write still lands. A scoped CHANGE op touches one file and rarely races; a
|
|
379
|
+
* full REBUILD rewrites every widget — prefer stopping the dev server first.
|
|
380
|
+
*/
|
|
381
|
+
export function writeAtomic(filePath, content) {
|
|
382
|
+
mkdirSync(dirname(filePath), { recursive: true })
|
|
383
|
+
const tmp = filePath + '.tmp'
|
|
384
|
+
writeFileSync(tmp, content, 'utf8')
|
|
385
|
+
const RETRYABLE = new Set(['EPERM', 'EBUSY', 'EACCES'])
|
|
386
|
+
for (let attempt = 0; ; attempt++) {
|
|
387
|
+
try {
|
|
388
|
+
renameSync(tmp, filePath)
|
|
389
|
+
return
|
|
390
|
+
} catch (err) {
|
|
391
|
+
if (!RETRYABLE.has(err?.code) || attempt >= 4) {
|
|
392
|
+
// Out of retries (or a non-lock error): overwrite in place so the new
|
|
393
|
+
// content still lands, then drop the tmp. Less atomic, but a held-open
|
|
394
|
+
// widget file would otherwise block the whole build.
|
|
395
|
+
try {
|
|
396
|
+
writeFileSync(filePath, content, 'utf8')
|
|
397
|
+
try { unlinkSync(tmp) } catch { /* ignore */ }
|
|
398
|
+
return
|
|
399
|
+
} catch {
|
|
400
|
+
throw err
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
// Busy-wait briefly (sync context): 50ms · 100ms · 150ms · 200ms.
|
|
404
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 50 * (attempt + 1))
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
export function hashContent(content) {
|
|
410
|
+
return createHash('sha256').update(content).digest('hex').slice(0, 16)
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
export function toPascalCase(str) {
|
|
414
|
+
return str.split('-').map(w => w[0].toUpperCase() + w.slice(1)).join('')
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// ── Pre-warm ───────────────────────────────────────────────────────────────────
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* Stage A — isolated type-check of the agent-written metric modules, before any
|
|
421
|
+
* widget is generated. Fast (no React graph) and maps errors directly to the
|
|
422
|
+
* offending src/metrics/<name>.ts file.
|
|
423
|
+
* @param {string} projectPath
|
|
424
|
+
* @returns {{ ok: true } | { ok: false, errors: string[], files: string[] }}
|
|
425
|
+
*/
|
|
426
|
+
export function runMetricsTypecheck(projectPath) {
|
|
427
|
+
try {
|
|
428
|
+
execSync('npx tsc -p tsconfig.metrics.json', { cwd: projectPath, stdio: 'pipe' })
|
|
429
|
+
return { ok: true }
|
|
430
|
+
} catch (e) {
|
|
431
|
+
const out = (e.stdout?.toString() ?? '') + (e.stderr?.toString() ?? '')
|
|
432
|
+
const errors = out.split('\n').filter(l => l.includes('error TS')).slice(0, 20)
|
|
433
|
+
const files = [...new Set((out.match(/src[/\\]metrics[/\\][\w-]+\.ts/g) ?? []).map(p => p.replace(/\\/g, '/')))]
|
|
434
|
+
return { ok: false, errors, files }
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
// ── Event streaming ────────────────────────────────────────────────────────────
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* Emit a structured event line to stdout (or a custom writer for testing).
|
|
442
|
+
* @param {string} type - Must be one of KNOWN_EVENTS
|
|
443
|
+
* @param {object|null} [payload=null]
|
|
444
|
+
* @param {{ write: (s: string) => void }} [writer=process.stdout]
|
|
445
|
+
*/
|
|
446
|
+
export function emit(type, payload = null, writer = process.stdout) {
|
|
447
|
+
const line = payload != null ? `${type}:${JSON.stringify(payload)}` : type
|
|
448
|
+
writer.write(line + '\n')
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
/**
|
|
452
|
+
* Parse a stdout line back to a structured event, or null if not a known event.
|
|
453
|
+
* @param {string} line
|
|
454
|
+
* @returns {{ type: string, payload: object|null }|null}
|
|
455
|
+
*/
|
|
456
|
+
export function parseEvent(line) {
|
|
457
|
+
const trimmed = line.trim()
|
|
458
|
+
const colonIdx = trimmed.indexOf(':')
|
|
459
|
+
if (colonIdx === -1) {
|
|
460
|
+
return KNOWN_EVENTS.has(trimmed) ? { type: trimmed, payload: null } : null
|
|
461
|
+
}
|
|
462
|
+
const type = trimmed.slice(0, colonIdx)
|
|
463
|
+
if (!KNOWN_EVENTS.has(type)) return null
|
|
464
|
+
try {
|
|
465
|
+
return { type, payload: JSON.parse(trimmed.slice(colonIdx + 1)) }
|
|
466
|
+
} catch {
|
|
467
|
+
return null
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
// ── Template helpers ───────────────────────────────────────────────────────────
|
|
472
|
+
|
|
473
|
+
/**
|
|
474
|
+
* Load a widget template and apply <PLACEHOLDER> substitutions.
|
|
475
|
+
* Injects time constants after the last import line.
|
|
476
|
+
* @param {string} templateName
|
|
477
|
+
* @param {Record<string, string>} subs
|
|
478
|
+
* @returns {string}
|
|
479
|
+
*/
|
|
480
|
+
function applyTemplate(templateName, subs) {
|
|
481
|
+
const templatePath = join(widgetsDir(), `${templateName}.tsx`)
|
|
482
|
+
if (!existsSync(templatePath)) fail(`Template not found: ${templateName}.tsx in ${widgetsDir()}`)
|
|
483
|
+
let content = readFileSync(templatePath, 'utf8')
|
|
484
|
+
|
|
485
|
+
for (const [key, value] of Object.entries(subs)) {
|
|
486
|
+
content = content.split(`<${key}>`).join(value)
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
// Detect unresolved placeholders — a remaining <UPPER_CASE> token means a
|
|
490
|
+
// placeholder was in the template but not supplied in the substitution map.
|
|
491
|
+
// Fail loudly now rather than generating broken TypeScript that confuses tsc.
|
|
492
|
+
const unresolved = [...new Set((content.match(/<[A-Z][A-Z_]+>/g) ?? []))]
|
|
493
|
+
if (unresolved.length > 0) {
|
|
494
|
+
fail(
|
|
495
|
+
`Template "${templateName}.tsx" has unresolved placeholders: ${unresolved.join(', ')}. ` +
|
|
496
|
+
`These must be added to the substitution map passed to applyTemplate().`
|
|
497
|
+
)
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
return content
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
/**
|
|
504
|
+
* Generate a detail view file for a widget.
|
|
505
|
+
*
|
|
506
|
+
* Drills to RECORD GRAIN: the view runs the module's `fetchDetail` export — the
|
|
507
|
+
* individual records behind the chart (e.g. each job run), not the aggregate. When
|
|
508
|
+
* `detailColumns` (a compiled ColumnDef literal) is supplied, it drives the table
|
|
509
|
+
* (formatted/coloured cells); otherwise columns are auto-detected at runtime.
|
|
510
|
+
* `defaultSortKey` keys the initial sort on the raw field (e.g. an ISO timestamp)
|
|
511
|
+
* so chronological order is correct even when a column renders a friendly label.
|
|
512
|
+
*
|
|
513
|
+
* @param {{ componentName: string, title: string, subtitle?: string, moduleSpecifier: string, detailExport: string, detailColumns?: string|null, defaultSortKey?: string }} widget
|
|
514
|
+
* @returns {string} Full TypeScript file content
|
|
515
|
+
*/
|
|
516
|
+
export function generateViewFile(widget) {
|
|
517
|
+
const { componentName, title, subtitle = '', moduleSpecifier, detailExport, detailColumns, defaultSortKey, detailView = null } = widget
|
|
518
|
+
|
|
519
|
+
const columnsExpr = detailColumns ? detailColumns : 'autoColumns(rows)'
|
|
520
|
+
const sortKeyExpr = defaultSortKey ? JSON.stringify(defaultSortKey) : '(columns[0]?.key as string)'
|
|
521
|
+
|
|
522
|
+
const compiled = detailView ? compileDetailWidgets(detailView, 'd') : null
|
|
523
|
+
const extraImports = compiled ? '\n' + [...compiled.imports].filter(i => !i.includes("'@/dashboard/chrome/RecordsTable'")).join('\n') : ''
|
|
524
|
+
|
|
525
|
+
const body = detailView
|
|
526
|
+
? ` const d = (data && !Array.isArray(data)) ? (data as Record<string, unknown[]>) : { rows: toRows(data ?? []) }
|
|
527
|
+
|
|
528
|
+
if (loading) return (
|
|
529
|
+
<DetailViewShell title="${title ?? componentName}" description="${subtitle}">
|
|
530
|
+
<LoadingState height="h-96" />
|
|
531
|
+
</DetailViewShell>
|
|
532
|
+
)
|
|
533
|
+
if (error) return (
|
|
534
|
+
<DetailViewShell title="${title ?? componentName}" description="${subtitle}">
|
|
535
|
+
<EmptyState message={error.message} />
|
|
536
|
+
</DetailViewShell>
|
|
537
|
+
)
|
|
538
|
+
return (
|
|
539
|
+
<DetailViewShell title="${title ?? componentName}" description="${subtitle}">
|
|
540
|
+
<div className="space-y-6">
|
|
541
|
+
${compiled.jsx}
|
|
542
|
+
</div>
|
|
543
|
+
</DetailViewShell>
|
|
544
|
+
)`
|
|
545
|
+
: ` const rows = toRows(data ?? [])
|
|
546
|
+
const columns = ${columnsExpr}
|
|
547
|
+
|
|
548
|
+
if (loading) return (
|
|
549
|
+
<DetailViewShell title="${title ?? componentName}" description="${subtitle}">
|
|
550
|
+
<LoadingState height="h-96" />
|
|
551
|
+
</DetailViewShell>
|
|
552
|
+
)
|
|
553
|
+
if (error) return (
|
|
554
|
+
<DetailViewShell title="${title ?? componentName}" description="${subtitle}">
|
|
555
|
+
<EmptyState message={error.message} />
|
|
556
|
+
</DetailViewShell>
|
|
557
|
+
)
|
|
558
|
+
return (
|
|
559
|
+
<DetailViewShell title="${title ?? componentName}" description="${subtitle}">
|
|
560
|
+
<RecordsTable rows={rows} columns={columns} defaultSortKey={${sortKeyExpr}} />
|
|
561
|
+
</DetailViewShell>
|
|
562
|
+
)`
|
|
563
|
+
|
|
564
|
+
return `import React from 'react'
|
|
565
|
+
import { DetailViewShell } from '@/dashboard/chrome/DetailViewShell'
|
|
566
|
+
import { RecordsTable, type ColumnDef } from '@/dashboard/chrome/RecordsTable'
|
|
567
|
+
import { useWidgetData } from '@/hooks/useWidgetData'
|
|
568
|
+
import { LoadingState, EmptyState } from '@/dashboard/chrome'
|
|
569
|
+
import { fmtNumber, fmtPercent, fmtDuration, fmtTimeAgo } from '@/lib/format'
|
|
570
|
+
import { toneClass } from '@/lib/widget'
|
|
571
|
+
import { ${detailExport} } from '${moduleSpecifier}'${extraImports}
|
|
572
|
+
|
|
573
|
+
type Row = Record<string, unknown>
|
|
574
|
+
|
|
575
|
+
/** Auto-detect columns from the first row when explicit detailColumns aren't given. */
|
|
576
|
+
function autoColumns(rows: Row[]): ColumnDef<Row>[] {
|
|
577
|
+
if (rows.length === 0) return [{ key: 'value', label: 'Value' }]
|
|
578
|
+
return Object.entries(rows[0])
|
|
579
|
+
.filter(([, v]) => v !== null && v !== undefined && typeof v !== 'object')
|
|
580
|
+
.map(([k, v]) => ({
|
|
581
|
+
key: k,
|
|
582
|
+
label: k.replace(/([A-Z])/g, ' $1').replace(/^(.)/, (s: string) => s.toUpperCase()).trim(),
|
|
583
|
+
...(typeof v === 'number' && { align: 'right' as const }),
|
|
584
|
+
}))
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
export function ${componentName}View() {
|
|
588
|
+
const { data, loading, error } = useWidgetData(${detailExport}, [])
|
|
589
|
+
|
|
590
|
+
/** Safely extract a row array from any response shape */
|
|
591
|
+
function toRows(raw: unknown): Row[] {
|
|
592
|
+
if (!raw) return []
|
|
593
|
+
if (Array.isArray(raw)) return raw as Row[]
|
|
594
|
+
if (typeof raw === 'object') {
|
|
595
|
+
const obj = raw as Record<string, unknown>
|
|
596
|
+
if (Array.isArray(obj.items)) return obj.items as Row[]
|
|
597
|
+
if (Array.isArray(obj.data)) return obj.data as Row[]
|
|
598
|
+
if (obj.data && typeof obj.data === 'object') return toRows(obj.data)
|
|
599
|
+
}
|
|
600
|
+
return []
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
${body}
|
|
604
|
+
}
|
|
605
|
+
`
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
/**
|
|
609
|
+
* Generate a KEYED detail view for a table widget with `rowLink` — reads the
|
|
610
|
+
* route param and calls the module's `fetchDetailByKey(sdk, key, getToken)`.
|
|
611
|
+
* @param {{componentName:string,title?:string,subtitle?:string,moduleSpecifier:string,detailColumns?:string|null}} widget
|
|
612
|
+
* @returns {string}
|
|
613
|
+
*/
|
|
614
|
+
export function generateKeyedDetailViewFile(widget) {
|
|
615
|
+
const { componentName, title, subtitle = '', moduleSpecifier, detailColumns, detailView = null } = widget
|
|
616
|
+
const columnsExpr = detailColumns ? detailColumns : 'autoColumns(rows)'
|
|
617
|
+
|
|
618
|
+
const compiled = detailView ? compileDetailWidgets(detailView, 'd') : null
|
|
619
|
+
const extraImports = compiled ? '\n' + [...compiled.imports].filter(i => !i.includes("'@/dashboard/chrome/RecordsTable'")).join('\n') : ''
|
|
620
|
+
|
|
621
|
+
// Body branches: rich detailView renders compiled sub-widgets over a normalized
|
|
622
|
+
// source map; otherwise today's single RecordsTable (backward-compat, unchanged).
|
|
623
|
+
const stateInit = detailView ? `const [data, setData] = useState<unknown>(null)` : `const [data, setData] = useState<Row[]>([])`
|
|
624
|
+
const fetchThen = detailView
|
|
625
|
+
? `.then((res: unknown) => { setData(res); setLoading(false) })`
|
|
626
|
+
: `.then((rows: Row[]) => { setData(rows); setLoading(false) })`
|
|
627
|
+
|
|
628
|
+
const body = detailView
|
|
629
|
+
? ` const d = (data && !Array.isArray(data)) ? (data as Record<string, unknown[]>) : { rows: toRows(data as unknown) }
|
|
630
|
+
const heading = key ? \`${title ?? componentName}: \${decodeURIComponent(key)}\` : '${title ?? componentName}'
|
|
631
|
+
|
|
632
|
+
if (loading) return (
|
|
633
|
+
<DetailViewShell title={heading} description="${subtitle}">
|
|
634
|
+
<LoadingState height="h-96" />
|
|
635
|
+
</DetailViewShell>
|
|
636
|
+
)
|
|
637
|
+
if (error) return (
|
|
638
|
+
<DetailViewShell title={heading} description="${subtitle}">
|
|
639
|
+
<EmptyState message={error.message} />
|
|
640
|
+
</DetailViewShell>
|
|
641
|
+
)
|
|
642
|
+
return (
|
|
643
|
+
<DetailViewShell title={heading} description="${subtitle}">
|
|
644
|
+
<div className="space-y-6">
|
|
645
|
+
${compiled.jsx}
|
|
646
|
+
</div>
|
|
647
|
+
</DetailViewShell>
|
|
648
|
+
)`
|
|
649
|
+
: ` const rows = data
|
|
650
|
+
const columns = ${columnsExpr}
|
|
651
|
+
const heading = key ? \`${title ?? componentName}: \${decodeURIComponent(key)}\` : '${title ?? componentName}'
|
|
652
|
+
|
|
653
|
+
if (loading) return (
|
|
654
|
+
<DetailViewShell title={heading} description="${subtitle}">
|
|
655
|
+
<LoadingState height="h-96" />
|
|
656
|
+
</DetailViewShell>
|
|
657
|
+
)
|
|
658
|
+
if (error) return (
|
|
659
|
+
<DetailViewShell title={heading} description="${subtitle}">
|
|
660
|
+
<EmptyState message={error.message} />
|
|
661
|
+
</DetailViewShell>
|
|
662
|
+
)
|
|
663
|
+
return (
|
|
664
|
+
<DetailViewShell title={heading} description="${subtitle}">
|
|
665
|
+
<RecordsTable rows={rows} columns={columns} />
|
|
666
|
+
</DetailViewShell>
|
|
667
|
+
)`
|
|
668
|
+
|
|
669
|
+
return `import React from 'react'
|
|
670
|
+
import { useParams } from 'react-router-dom'
|
|
671
|
+
import { useState, useEffect } from 'react'
|
|
672
|
+
import { DetailViewShell } from '@/dashboard/chrome/DetailViewShell'
|
|
673
|
+
import { RecordsTable, type ColumnDef } from '@/dashboard/chrome/RecordsTable'
|
|
674
|
+
import { useAuth } from '@/hooks/useAuth'
|
|
675
|
+
import { LoadingState, EmptyState } from '@/dashboard/chrome'
|
|
676
|
+
import { fmtNumber, fmtPercent, fmtDuration, fmtTimeAgo } from '@/lib/format'
|
|
677
|
+
import { toneClass } from '@/lib/widget'
|
|
678
|
+
import { fetchDetailByKey } from '${moduleSpecifier}'${extraImports}
|
|
679
|
+
|
|
680
|
+
type Row = Record<string, unknown>
|
|
681
|
+
|
|
682
|
+
/** Auto-detect columns from the first row when explicit detailColumns aren't given. */
|
|
683
|
+
function autoColumns(rows: Row[]): ColumnDef<Row>[] {
|
|
684
|
+
if (rows.length === 0) return [{ key: 'value', label: 'Value' }]
|
|
685
|
+
return Object.entries(rows[0])
|
|
686
|
+
.filter(([, v]) => v !== null && v !== undefined && typeof v !== 'object')
|
|
687
|
+
.map(([k, v]) => ({
|
|
688
|
+
key: k,
|
|
689
|
+
label: k.replace(/([A-Z])/g, ' $1').replace(/^(.)/, (s: string) => s.toUpperCase()).trim(),
|
|
690
|
+
...(typeof v === 'number' && { align: 'right' as const }),
|
|
691
|
+
}))
|
|
692
|
+
}
|
|
693
|
+
${detailView ? `
|
|
694
|
+
/** Safely extract a row array from any response shape (legacy fallback / bare arrays). */
|
|
695
|
+
function toRows(raw: unknown): Row[] {
|
|
696
|
+
if (!raw) return []
|
|
697
|
+
if (Array.isArray(raw)) return raw as Row[]
|
|
698
|
+
if (typeof raw === 'object') {
|
|
699
|
+
const obj = raw as Record<string, unknown>
|
|
700
|
+
if (Array.isArray(obj.items)) return obj.items as Row[]
|
|
701
|
+
if (Array.isArray(obj.data)) return obj.data as Row[]
|
|
702
|
+
if (obj.data && typeof obj.data === 'object') return toRows(obj.data)
|
|
703
|
+
}
|
|
704
|
+
return []
|
|
705
|
+
}
|
|
706
|
+
` : ''}
|
|
707
|
+
export function ${componentName}DetailView() {
|
|
708
|
+
const { key } = useParams<{ key: string }>()
|
|
709
|
+
const { sdk, getToken } = useAuth()
|
|
710
|
+
${stateInit}
|
|
711
|
+
const [loading, setLoading] = useState(true)
|
|
712
|
+
const [error, setError] = useState<Error | null>(null)
|
|
713
|
+
|
|
714
|
+
useEffect(() => {
|
|
715
|
+
if (!sdk || !key) return
|
|
716
|
+
setLoading(true)
|
|
717
|
+
fetchDetailByKey(sdk, key, getToken)
|
|
718
|
+
${fetchThen}
|
|
719
|
+
.catch((err: unknown) => { setError(err instanceof Error ? err : new Error(String(err))); setLoading(false) })
|
|
720
|
+
}, [sdk, key])
|
|
721
|
+
|
|
722
|
+
${body}
|
|
723
|
+
}
|
|
724
|
+
`
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
/**
|
|
728
|
+
* Generate Dashboard.tsx and widgets/index.ts from resolved widget metadata.
|
|
729
|
+
* @param {string} projectPath
|
|
730
|
+
* @param {WidgetMeta[]} widgetMeta
|
|
731
|
+
* @param {string} dashboardName
|
|
732
|
+
*/
|
|
733
|
+
export function generateDashboardFiles(projectPath, widgetMeta, dashboardName, dashboardDescription = '') {
|
|
734
|
+
const widgetNames = widgetMeta.map(w => w.componentName)
|
|
735
|
+
|
|
736
|
+
const kpis = widgetMeta.filter(w => widgetLayoutGroup(w.template) === 'kpi')
|
|
737
|
+
const charts = widgetMeta.filter(w => widgetLayoutGroup(w.template) === 'chart')
|
|
738
|
+
const tables = widgetMeta.filter(w => widgetLayoutGroup(w.template) === 'table')
|
|
739
|
+
|
|
740
|
+
const kpiCols = Math.min(kpis.length, 4)
|
|
741
|
+
const kpiGrid = kpiCols === 1 ? 'grid-cols-1'
|
|
742
|
+
: kpiCols === 2 ? 'grid-cols-1 sm:grid-cols-2'
|
|
743
|
+
: kpiCols === 3 ? 'grid-cols-1 sm:grid-cols-2 lg:grid-cols-3'
|
|
744
|
+
: 'grid-cols-1 sm:grid-cols-2 lg:grid-cols-4'
|
|
745
|
+
|
|
746
|
+
const imports = widgetNames.map(n => `import { ${n} } from './widgets/${n}'`).join('\n')
|
|
747
|
+
const indexTs = widgetNames.map(n => `export { ${n} } from './${n}'`).join('\n') + '\n'
|
|
748
|
+
|
|
749
|
+
const kpiSection = kpis.length ? `
|
|
750
|
+
{/* KPI row */}
|
|
751
|
+
<div className="grid ${kpiGrid} gap-6">
|
|
752
|
+
${kpis.map(w => `<${w.componentName} />`).join('\n ')}
|
|
753
|
+
</div>` : ''
|
|
754
|
+
|
|
755
|
+
const chartSection = charts.length ? `
|
|
756
|
+
{/* Charts */}
|
|
757
|
+
<div className="grid grid-cols-1 ${charts.length > 1 ? 'lg:grid-cols-2' : ''} gap-6 mt-10">
|
|
758
|
+
${charts.map(w => `<${w.componentName} />`).join('\n ')}
|
|
759
|
+
</div>` : ''
|
|
760
|
+
|
|
761
|
+
const tableSection = tables.length ? `
|
|
762
|
+
{/* Tables */}
|
|
763
|
+
<div className="space-y-6 mt-10">
|
|
764
|
+
${tables.map(w => `<${w.componentName} />`).join('\n ')}
|
|
765
|
+
</div>` : ''
|
|
766
|
+
|
|
767
|
+
const dashboardJsx = `import React from 'react'
|
|
768
|
+
import { Header } from '@/dashboard/chrome/Header'
|
|
769
|
+
${imports}
|
|
770
|
+
|
|
771
|
+
export function Dashboard() {
|
|
772
|
+
return (
|
|
773
|
+
<div className="min-h-screen bg-background">
|
|
774
|
+
<div className="mx-auto max-w-screen-2xl px-4 py-8 md:px-8 md:py-10">
|
|
775
|
+
<Header title="${dashboardName}" description="${dashboardDescription || 'Operational metrics dashboard'}" />
|
|
776
|
+
${kpiSection}${chartSection}${tableSection}
|
|
777
|
+
</div>
|
|
778
|
+
</div>
|
|
779
|
+
)
|
|
780
|
+
}
|
|
781
|
+
`
|
|
782
|
+
writeAtomic(join(projectPath, 'src', 'dashboard', 'Dashboard.tsx'), dashboardJsx)
|
|
783
|
+
writeAtomic(join(projectPath, 'src', 'dashboard', 'widgets', 'index.ts'), indexTs)
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
/**
|
|
787
|
+
* Classify a widget template into its layout group for Dashboard.tsx grid placement.
|
|
788
|
+
* @param {string} template
|
|
789
|
+
* @returns {'kpi'|'table'|'chart'}
|
|
790
|
+
*/
|
|
791
|
+
export function widgetLayoutGroup(template) {
|
|
792
|
+
if (template === 'kpi-card') return 'kpi'
|
|
793
|
+
if (['data-table', 'ranked-table'].includes(template)) return 'table'
|
|
794
|
+
return 'chart'
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
/**
|
|
798
|
+
* Decide whether a widget gets a generated record-grain detail view (+ route).
|
|
799
|
+
* Single source of truth for fresh build, incremental ADD/CHANGE, rebuild, and
|
|
800
|
+
* upgrade — keeps the "is this card clickable / does a view file exist" decision
|
|
801
|
+
* consistent everywhere.
|
|
802
|
+
*
|
|
803
|
+
* - Chart widgets get a detail view UNLESS opted out with `noDetail: true` — set
|
|
804
|
+
* on the registry entry (T1/T2 catalog) OR on the metric in intent.json (T3,
|
|
805
|
+
* which has no registry entry). Used when the endpoint returns only
|
|
806
|
+
* pre-aggregated data, so a record drill-down would be fake. Opted-out charts
|
|
807
|
+
* render non-clickable.
|
|
808
|
+
* - KPI cards get a detail view when `detail` is true — either set on the metric
|
|
809
|
+
* (`detail: true` in intent.json) or defaulted on by the registry entry
|
|
810
|
+
* (`defaults.detail: true`, for cataloged KPIs that have a feasible record-grain
|
|
811
|
+
* query). A metric can force it off with `detail: false`. The module must export
|
|
812
|
+
* `fetchDetail`. A KPI with no `detail` signal links nowhere.
|
|
813
|
+
* - Tables never get a chart-style view here (they use `rowLink` keyed views).
|
|
814
|
+
*
|
|
815
|
+
* @param {string} template displayAs / registry template
|
|
816
|
+
* @param {object} [metric] intent metric (KPI `detail`, T3 `noDetail`)
|
|
817
|
+
* @param {object|null} [entry] registry entry (catalog `noDetail` / `defaults.detail`)
|
|
818
|
+
* @returns {boolean}
|
|
819
|
+
*/
|
|
820
|
+
export function widgetGetsDetailView(template, metric = {}, entry = null) {
|
|
821
|
+
if (entry?.noDetail === true || metric?.noDetail === true) return false
|
|
822
|
+
if (widgetLayoutGroup(template) === 'chart') return true
|
|
823
|
+
if (template === 'kpi-card') {
|
|
824
|
+
// Metric override wins; otherwise the registry entry may default it on for a
|
|
825
|
+
// cataloged KPI whose drill-down records are queryable.
|
|
826
|
+
return (metric?.detail ?? entry?.defaults?.detail ?? false) === true
|
|
827
|
+
}
|
|
828
|
+
return false
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
/**
|
|
832
|
+
* Inject generated import and route blocks into App.tsx markers.
|
|
833
|
+
* @param {string} projectPath
|
|
834
|
+
* @param {string[]} viewWidgetNames - Widget names that have a generated view file
|
|
835
|
+
*/
|
|
836
|
+
export function injectAppRoutes(projectPath, viewWidgetNames, keyedViews = []) {
|
|
837
|
+
const appPath = join(projectPath, 'src', 'App.tsx')
|
|
838
|
+
if (!existsSync(appPath)) {
|
|
839
|
+
emit('PARTIAL_BUILD_DETECTED', { message: 'App.tsx not found — scaffold may not be copied yet' })
|
|
840
|
+
return
|
|
841
|
+
}
|
|
842
|
+
let content = readFileSync(appPath, 'utf8')
|
|
843
|
+
|
|
844
|
+
const viewNames = viewWidgetNames
|
|
845
|
+
|
|
846
|
+
const imports = [
|
|
847
|
+
`import { Dashboard } from '@/dashboard/Dashboard'`,
|
|
848
|
+
...viewNames.map(n => `import { ${n}View } from '@/dashboard/views/${n}View'`),
|
|
849
|
+
...keyedViews.map(k => `import { ${k.componentName}DetailView } from '@/dashboard/views/${k.componentName}DetailView'`),
|
|
850
|
+
].join('\n')
|
|
851
|
+
|
|
852
|
+
const routes = [
|
|
853
|
+
` <Route path="/" element={<Dashboard />} />`,
|
|
854
|
+
...viewNames.map(n => ` <Route path="/${n.toLowerCase()}" element={<${n}View />} />`),
|
|
855
|
+
...keyedViews.map(k => ` <Route path="${k.routeBase}/:key" element={<${k.componentName}DetailView />} />`),
|
|
856
|
+
].join('\n')
|
|
857
|
+
|
|
858
|
+
content = content.replace(
|
|
859
|
+
/\/\/ GENERATED_IMPORTS_START[\s\S]*?\/\/ GENERATED_IMPORTS_END/,
|
|
860
|
+
`// GENERATED_IMPORTS_START\n${imports}\n// GENERATED_IMPORTS_END`
|
|
861
|
+
)
|
|
862
|
+
content = content.replace(
|
|
863
|
+
/\{\/\* GENERATED_ROUTES_START \*\/\}[\s\S]*?\{\/\* GENERATED_ROUTES_END \*\/\}/,
|
|
864
|
+
`{/* GENERATED_ROUTES_START */}\n${routes}\n {/* GENERATED_ROUTES_END */}`
|
|
865
|
+
)
|
|
866
|
+
|
|
867
|
+
writeAtomic(appPath, content)
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
// ── Resolution Engine ──────────────────────────────────────────────────────────
|
|
871
|
+
|
|
872
|
+
/**
|
|
873
|
+
* Validate a DashboardIntent object and return an array of error messages.
|
|
874
|
+
* Returns an empty array when the intent is valid.
|
|
875
|
+
* @param {DashboardIntent} intent
|
|
876
|
+
* @returns {string[]}
|
|
877
|
+
*/
|
|
878
|
+
export function validateIntent(intent) {
|
|
879
|
+
const errors = []
|
|
880
|
+
if (intent.schemaVersion !== 2) errors.push('intent.json must have "schemaVersion": 2')
|
|
881
|
+
if (!intent.dashboardName || typeof intent.dashboardName !== 'string') errors.push('dashboardName must be a non-empty string')
|
|
882
|
+
if (!['1d', '7d', '30d', '90d'].includes(intent.timeRange)) errors.push(`timeRange must be one of: 1d, 7d, 30d, 90d`)
|
|
883
|
+
if (!Array.isArray(intent.metrics) || intent.metrics.length === 0) errors.push('metrics must be a non-empty array')
|
|
884
|
+
for (const m of (intent.metrics ?? [])) {
|
|
885
|
+
if (!m.name) errors.push(`metric ${JSON.stringify(m.title ?? m)} missing name (needed to resolve its metrics/<name>.ts module)`)
|
|
886
|
+
if (!['T1', 'T2', 'T3'].includes(m.tier)) errors.push(`metric "${m.name}" has invalid tier: ${m.tier}`)
|
|
887
|
+
if (m.tier === 'T1' || m.tier === 'T2') {
|
|
888
|
+
if (!m.title) errors.push(`${m.tier} metric "${m.name}" missing title`)
|
|
889
|
+
}
|
|
890
|
+
if (m.tier === 'T2') {
|
|
891
|
+
if (!m.params) {
|
|
892
|
+
errors.push(`T2 metric "${m.name}" missing params`)
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
if (m.tier === 'T3') {
|
|
896
|
+
if (!m.title) errors.push(`T3 metric "${m.name}" missing title`)
|
|
897
|
+
if (!m.displayAs) errors.push(`T3 metric "${m.name}" needs displayAs — valid values: ${VALID_DISPLAY_TYPES.join(', ')}`)
|
|
898
|
+
else if (!VALID_DISPLAY_TYPES.includes(m.displayAs)) {
|
|
899
|
+
errors.push(`T3 metric "${m.name}" has unsupported displayAs "${m.displayAs}". Valid: ${VALID_DISPLAY_TYPES.join(', ')}`)
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
// Presentation hints (all tiers, all optional) — validate enums when present
|
|
903
|
+
if (m.headlineMode && !VALID_HEADLINE_MODES.includes(m.headlineMode)) {
|
|
904
|
+
errors.push(`metric "${m.name}" has invalid headlineMode "${m.headlineMode}". Valid: ${VALID_HEADLINE_MODES.join(', ')}`)
|
|
905
|
+
}
|
|
906
|
+
if (m.deltaPolarity && !VALID_DELTA_POLARITIES.includes(m.deltaPolarity)) {
|
|
907
|
+
errors.push(`metric "${m.name}" has invalid deltaPolarity "${m.deltaPolarity}". Valid: ${VALID_DELTA_POLARITIES.join(', ')}`)
|
|
908
|
+
}
|
|
909
|
+
if (m.displayAs === 'rate-chart' && (!m.rateNum || !m.rateDen)) {
|
|
910
|
+
errors.push(`rate-chart metric "${m.name}" needs rateNum and rateDen (the numerator/denominator field names its fnBody returns per bucket)`)
|
|
911
|
+
}
|
|
912
|
+
if (Array.isArray(m.detailColumns)) {
|
|
913
|
+
for (const c of m.detailColumns) {
|
|
914
|
+
if (!c.key || !c.label) errors.push(`metric "${m.name}" detailColumns entry missing key/label`)
|
|
915
|
+
if (c.format && !VALID_COLUMN_FORMATS.includes(c.format)) {
|
|
916
|
+
errors.push(`metric "${m.name}" detailColumns "${c.key}" has invalid format "${c.format}". Valid: ${VALID_COLUMN_FORMATS.join(', ')}`)
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
// Row-click drill-down config
|
|
921
|
+
if (m.rowLink !== undefined && (typeof m.rowLink !== 'object' || !m.rowLink.key || typeof m.rowLink.key !== 'string')) {
|
|
922
|
+
errors.push(`metric "${m.name}" rowLink must be an object with a string "key" (the row field used as the drill-down route param). The module must export fetchDetailByKey.`)
|
|
923
|
+
}
|
|
924
|
+
// Rich detail-view spec — throws with a precise message on any violation.
|
|
925
|
+
validateDetailView(m)
|
|
926
|
+
}
|
|
927
|
+
return errors
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
/**
|
|
931
|
+
* The module specifier a generated widget imports its data function from.
|
|
932
|
+
* Default convention: metrics/<metric-name>.ts → '@/metrics/<metric-name>'.
|
|
933
|
+
* An explicit `module` field (e.g. "metrics/custom.ts") overrides; the .ts is stripped.
|
|
934
|
+
* @param {{ name: string, module?: string }} metric
|
|
935
|
+
* @returns {string}
|
|
936
|
+
*/
|
|
937
|
+
export function metricModuleSpecifier(metric) {
|
|
938
|
+
const rel = metric.module ?? `metrics/${metric.name}.ts`
|
|
939
|
+
return '@/' + rel.replace(/\.ts$/, '')
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
/**
|
|
943
|
+
* Look up a metric in the capability registry.
|
|
944
|
+
* T3 metrics always resolve (they carry their own generation logic).
|
|
945
|
+
* Returns null entry for unknown T1/T2 metrics (agent provides fnBody, registry lookup is for display hints only).
|
|
946
|
+
* @param {IntentMetric} metric
|
|
947
|
+
* @returns {{ tier: MetricTier, key: string, entry: object|null }}
|
|
948
|
+
*/
|
|
949
|
+
export function resolveMetric(metric) {
|
|
950
|
+
if (metric.tier === 'T3') return { tier: 'T3', key: metric.name, entry: null }
|
|
951
|
+
const registrySection = metric.tier === 'T1' ? REGISTRY.t1 : REGISTRY.t2
|
|
952
|
+
const entry = registrySection[metric.name]
|
|
953
|
+
if (!entry) {
|
|
954
|
+
throw new Error(`Metric "${metric.name}" (${metric.tier}) not found in registry. Available: ${Object.keys(registrySection).join(', ')}`)
|
|
955
|
+
}
|
|
956
|
+
return { tier: metric.tier, key: metric.name, entry }
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
/**
|
|
960
|
+
* Resolve a widget subtitle (CardDescription). Falls back to a window label.
|
|
961
|
+
* @param {IntentMetric} metric
|
|
962
|
+
* @param {object} defaults - registry entry defaults
|
|
963
|
+
* @param {string} timeRange
|
|
964
|
+
* @returns {string}
|
|
965
|
+
*/
|
|
966
|
+
export function autoSubtitle(metric, defaults, timeRange) {
|
|
967
|
+
const explicit = metric.subtitle ?? defaults.subtitle ?? metric.description ?? defaults.description
|
|
968
|
+
if (explicit) return explicit
|
|
969
|
+
const label = timeRangeLabel(timeRange)
|
|
970
|
+
return label.charAt(0).toUpperCase() + label.slice(1)
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
/**
|
|
974
|
+
* Compile a column spec into a TS array literal for RecordsTable.
|
|
975
|
+
* Accepts either a ready literal string (passed through) or an array of
|
|
976
|
+
* { key, label, align?, format?, color? } objects, for which it emits `render`
|
|
977
|
+
* functions that format (number/percent/duration/timeAgo) and optionally colour cells.
|
|
978
|
+
* @param {string|Array<object>} input
|
|
979
|
+
* @returns {string}
|
|
980
|
+
*/
|
|
981
|
+
export function compileColumns(input) {
|
|
982
|
+
if (typeof input === 'string') return input
|
|
983
|
+
if (!Array.isArray(input) || input.length === 0) return '[{key:"name",label:"Name"}]'
|
|
984
|
+
const cols = input.map((c) => {
|
|
985
|
+
const parts = [`key:${JSON.stringify(c.key)}`, `label:${JSON.stringify(c.label)}`]
|
|
986
|
+
if (c.align) parts.push(`align:${JSON.stringify(c.align)} as const`)
|
|
987
|
+
const render = columnRender(c)
|
|
988
|
+
if (render) parts.push(`render:${render}`)
|
|
989
|
+
return `{${parts.join(',')}}`
|
|
990
|
+
})
|
|
991
|
+
return `[${cols.join(',')}]`
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
/**
|
|
995
|
+
* Compile a metric's `detailView` spec into the imports + JSX for a rich
|
|
996
|
+
* drill-down view. Chart sub-widgets render the matching primitive from
|
|
997
|
+
* `@/dashboard/charts`; table sub-widgets render a titled Card wrapping a
|
|
998
|
+
* RecordsTable. Each sub-widget reads its named source off `dataVar`
|
|
999
|
+
* (the normalized `{ rows, byHook, ... }` map).
|
|
1000
|
+
* @param {{ widgets: Array<object> }} detailView
|
|
1001
|
+
* @param {string} [dataVar='d']
|
|
1002
|
+
* @returns {{ imports: Set<string>, jsx: string }}
|
|
1003
|
+
*/
|
|
1004
|
+
export function compileDetailWidgets(detailView, dataVar = 'd') {
|
|
1005
|
+
const imports = new Set()
|
|
1006
|
+
const blocks = []
|
|
1007
|
+
const CHART_IMPORT = {
|
|
1008
|
+
'donut-chart': 'Donut',
|
|
1009
|
+
'bar-chart': 'Bars',
|
|
1010
|
+
'area-chart': 'TrendArea',
|
|
1011
|
+
'line-chart': 'TrendArea',
|
|
1012
|
+
'multi-line-chart': 'MultiLine',
|
|
1013
|
+
}
|
|
1014
|
+
for (const w of (detailView.widgets ?? [])) {
|
|
1015
|
+
const srcExpr = `(${dataVar}[${JSON.stringify(w.source)}] ?? []) as Record<string, unknown>[]`
|
|
1016
|
+
if (DETAIL_CHART_TYPES.has(w.displayAs)) {
|
|
1017
|
+
const comp = CHART_IMPORT[w.displayAs]
|
|
1018
|
+
imports.add(`import { ${comp} } from '@/dashboard/charts'`)
|
|
1019
|
+
imports.add(`import { Card, CardHeader, CardTitle, CardContent } from '@/components/ui/card'`)
|
|
1020
|
+
const emptyProp = typeof w.emptyMessage === 'string' ? ` emptyMessage={${JSON.stringify(w.emptyMessage)}}` : ''
|
|
1021
|
+
let chartEl
|
|
1022
|
+
if (w.displayAs === 'multi-line-chart') {
|
|
1023
|
+
chartEl = `<MultiLine data={${srcExpr}} xKey={${JSON.stringify(w.xKey)}} series={${compileSeries(w.series)}}${emptyProp} />`
|
|
1024
|
+
} else if (w.displayAs === 'donut-chart') {
|
|
1025
|
+
chartEl = `<Donut data={${srcExpr}} nameKey={${JSON.stringify(w.xKey)}} valueKey={${JSON.stringify(w.yKey)}}${emptyProp} />`
|
|
1026
|
+
} else if (w.displayAs === 'bar-chart') {
|
|
1027
|
+
chartEl = `<Bars data={${srcExpr}} nameKey={${JSON.stringify(w.xKey)}} valueKey={${JSON.stringify(w.yKey)}}${emptyProp} />`
|
|
1028
|
+
} else if (w.displayAs === 'area-chart') {
|
|
1029
|
+
chartEl = `<TrendArea data={${srcExpr}} xKey={${JSON.stringify(w.xKey)}} yKey={${JSON.stringify(w.yKey)}}${emptyProp} />`
|
|
1030
|
+
} else { // line-chart
|
|
1031
|
+
chartEl = `<TrendArea data={${srcExpr}} xKey={${JSON.stringify(w.xKey)}} yKey={${JSON.stringify(w.yKey)}} area={false}${emptyProp} />`
|
|
1032
|
+
}
|
|
1033
|
+
blocks.push(` <Card>
|
|
1034
|
+
<CardHeader><CardTitle>{${JSON.stringify(w.title)}}</CardTitle></CardHeader>
|
|
1035
|
+
<CardContent>
|
|
1036
|
+
${chartEl}
|
|
1037
|
+
</CardContent>
|
|
1038
|
+
</Card>`)
|
|
1039
|
+
} else {
|
|
1040
|
+
// table sub-widget
|
|
1041
|
+
imports.add(`import { RecordsTable, type ColumnDef } from '@/dashboard/chrome/RecordsTable'`)
|
|
1042
|
+
imports.add(`import { Card, CardHeader, CardTitle, CardContent } from '@/components/ui/card'`)
|
|
1043
|
+
const colsExpr = w.columns ? compileColumns(w.columns) : `autoColumns(${srcExpr})`
|
|
1044
|
+
blocks.push(` <Card>
|
|
1045
|
+
<CardHeader><CardTitle>{${JSON.stringify(w.title)}}</CardTitle></CardHeader>
|
|
1046
|
+
<CardContent>
|
|
1047
|
+
<RecordsTable rows={${srcExpr}} columns={${colsExpr}} />
|
|
1048
|
+
</CardContent>
|
|
1049
|
+
</Card>`)
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
return { imports, jsx: blocks.join('\n') }
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
/**
|
|
1056
|
+
* Compile a multi-line-chart `series` spec into a TS array literal.
|
|
1057
|
+
* Accepts either a ready literal string (passed through) or an array of
|
|
1058
|
+
* `{ key, color, label? }` objects — the natural form an author writes in
|
|
1059
|
+
* intent.json. Without this, naive string coercion of the array writes the JS
|
|
1060
|
+
* default `[object Object],[object Object]` into the SERIES constant and tsc fails.
|
|
1061
|
+
* @param {string|Array<{key:string,color:string,label?:string}>} input
|
|
1062
|
+
* @returns {string}
|
|
1063
|
+
*/
|
|
1064
|
+
export function compileSeries(input) {
|
|
1065
|
+
if (typeof input === 'string') return input
|
|
1066
|
+
if (!Array.isArray(input) || input.length === 0) return '[{key:"value",color:"hsl(var(--chart-1))"}]'
|
|
1067
|
+
const items = input.map((s) => {
|
|
1068
|
+
const parts = [`key:${JSON.stringify(s.key)}`, `color:${JSON.stringify(s.color)}`]
|
|
1069
|
+
if (s.label) parts.push(`label:${JSON.stringify(s.label)}`)
|
|
1070
|
+
return `{${parts.join(',')}}`
|
|
1071
|
+
})
|
|
1072
|
+
return `[${items.join(',')}]`
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
/** Build a `render` function body for a column with `format` and/or `color`. Returns null if neither. */
|
|
1076
|
+
function columnRender(c) {
|
|
1077
|
+
if (!c.format && !c.color) return null
|
|
1078
|
+
let valExpr
|
|
1079
|
+
switch (c.format) {
|
|
1080
|
+
case 'number': valExpr = 'fmtNumber(Number(v))'; break
|
|
1081
|
+
case 'percent': valExpr = 'fmtPercent(Number(v))'; break
|
|
1082
|
+
case 'duration': valExpr = 'fmtDuration(Number(v))'; break
|
|
1083
|
+
case 'timeAgo': valExpr = 'fmtTimeAgo(String(v))'; break
|
|
1084
|
+
default: valExpr = 'String(v ?? "—")'
|
|
1085
|
+
}
|
|
1086
|
+
if (c.color) {
|
|
1087
|
+
// color: 'goodHigh' (higher is better) | 'goodLow' (lower is better)
|
|
1088
|
+
return `(v:unknown)=>React.createElement('span',{className:toneClass(Number(v),${JSON.stringify(c.color)})},${valExpr})`
|
|
1089
|
+
}
|
|
1090
|
+
return `(v:unknown)=>${valExpr}`
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
/**
|
|
1094
|
+
* Build the spec passed to generateViewFile for a widget's detail view.
|
|
1095
|
+
* Shared by the fresh build and incremental ADD/CHANGE so detail views stay
|
|
1096
|
+
* consistent. The view always runs the module's record-grain `fetchDetail` export.
|
|
1097
|
+
* @param {string} componentName
|
|
1098
|
+
* @param {IntentMetric} metric
|
|
1099
|
+
* @param {object|null} entry - registry entry (for defaults)
|
|
1100
|
+
* @param {string} timeRange
|
|
1101
|
+
*/
|
|
1102
|
+
export function buildViewSpec(componentName, metric, entry, timeRange) {
|
|
1103
|
+
// Detail views show RECORD GRAIN: a chart/KPI exports `fetchData` (the
|
|
1104
|
+
// aggregate the widget renders) AND `fetchDetail` (the individual records
|
|
1105
|
+
// behind it). The view always runs `fetchDetail` — every chart metric is
|
|
1106
|
+
// required to export it (enforced by the CHART_DETAIL_MISSING gate), and a
|
|
1107
|
+
// KPI reaches this path only with `detail: true`. detailColumns fall back to
|
|
1108
|
+
// the registry defaults so a cataloged metric's drill-down is styled with no
|
|
1109
|
+
// intent.json config.
|
|
1110
|
+
const detailColumns = metric.detailColumns ?? entry?.defaults?.detailColumns
|
|
1111
|
+
return {
|
|
1112
|
+
componentName,
|
|
1113
|
+
title: metric.title ?? entry?.defaults?.title ?? componentName,
|
|
1114
|
+
subtitle: autoSubtitle(metric, entry?.defaults ?? {}, timeRange),
|
|
1115
|
+
moduleSpecifier: metricModuleSpecifier(metric),
|
|
1116
|
+
detailExport: 'fetchDetail',
|
|
1117
|
+
detailColumns: detailColumns ? compileColumns(detailColumns) : null,
|
|
1118
|
+
defaultSortKey: metric.detailSortKey ?? entry?.defaults?.detailSortKey,
|
|
1119
|
+
detailView: metric.detailView ?? entry?.defaults?.detailView ?? null,
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1123
|
+
/**
|
|
1124
|
+
* Generate the TypeScript source for any widget file.
|
|
1125
|
+
* All tiers (T1, T2, T3) use fnBody — the agent writes the SDK call.
|
|
1126
|
+
* Registry provides display hints; agent provides the data fetching logic.
|
|
1127
|
+
* @param {IntentMetric} metric - Must have fnBody, displayAs (or registry template), title
|
|
1128
|
+
* @param {object|null} registryEntry - Registry entry for display hints (null for T3)
|
|
1129
|
+
* @param {'1d'|'7d'|'30d'|'90d'} [timeRange='30d']
|
|
1130
|
+
* @returns {string} Full TypeScript file content
|
|
1131
|
+
*/
|
|
1132
|
+
export function buildWidgetFile(metric, registryEntry = null, timeRange = '30d') {
|
|
1133
|
+
if (!metric.title) throw new Error(`metric "${metric.name}" missing title`)
|
|
1134
|
+
|
|
1135
|
+
const defaults = registryEntry?.defaults ?? {}
|
|
1136
|
+
const componentName = metric.componentName ?? toPascalCase(metric.name)
|
|
1137
|
+
const displayAs = metric.displayAs ?? registryEntry?.template
|
|
1138
|
+
const iconName = metric.icon ?? defaults.icon ?? 'Activity'
|
|
1139
|
+
|
|
1140
|
+
if (!displayAs) throw new Error(`metric "${metric.name}" needs displayAs`)
|
|
1141
|
+
|
|
1142
|
+
const CHART_TYPES = new Set(['area-chart', 'line-chart', 'bar-chart', 'donut-chart', 'multi-line-chart', 'rate-chart'])
|
|
1143
|
+
|
|
1144
|
+
// ── Chart path ─────────────────────────────────────────────────────────────
|
|
1145
|
+
if (CHART_TYPES.has(displayAs)) {
|
|
1146
|
+
const moduleSpecifier = metricModuleSpecifier(metric)
|
|
1147
|
+
// noDetail charts (Insights endpoints with only pre-aggregated data) get NO
|
|
1148
|
+
// drill-down: an empty DETAIL_ROUTE makes the template render a non-clickable
|
|
1149
|
+
// card (no cursor-pointer, no ViewAllLink). Step 5a then skips the view file.
|
|
1150
|
+
const hasDetail = widgetGetsDetailView(displayAs, metric, registryEntry)
|
|
1151
|
+
const spec = {
|
|
1152
|
+
componentName,
|
|
1153
|
+
template: displayAs,
|
|
1154
|
+
detailRoute: hasDetail ? (metric.detailRoute ?? `/${componentName.toLowerCase()}`) : '',
|
|
1155
|
+
icon: iconName,
|
|
1156
|
+
title: metric.title,
|
|
1157
|
+
subtitle: autoSubtitle(metric, defaults, timeRange),
|
|
1158
|
+
dataHook: 'useWidgetData(fetchData, [])',
|
|
1159
|
+
hookImport: [
|
|
1160
|
+
"import { useWidgetData } from '@/hooks/useWidgetData'",
|
|
1161
|
+
`import { fetchData } from '${moduleSpecifier}'`,
|
|
1162
|
+
].join('\n'),
|
|
1163
|
+
sdkImportLine: '',
|
|
1164
|
+
responseTypeImport: '',
|
|
1165
|
+
dataSelector: 'data ?? []',
|
|
1166
|
+
xKey: metric.xKey ?? defaults.xKey ?? 'date',
|
|
1167
|
+
yKey: metric.yKey ?? defaults.yKey ?? 'value',
|
|
1168
|
+
headlineMode: metric.headlineMode ?? defaults.headlineMode ?? 'sum',
|
|
1169
|
+
deltaPolarity: metric.deltaPolarity ?? defaults.deltaPolarity ?? 'neutral',
|
|
1170
|
+
rateNum: metric.rateNum ?? defaults.rateNum ?? 'num',
|
|
1171
|
+
rateDen: metric.rateDen ?? defaults.rateDen ?? 'den',
|
|
1172
|
+
series: compileSeries(metric.series ?? defaults.series ?? '[{key:"value",color:"hsl(var(--chart-1))"}]'),
|
|
1173
|
+
pivotExpression: metric.pivotExpression ?? defaults.pivotExpression ?? 'rawData',
|
|
1174
|
+
emptyMessage: metric.emptyMessage ?? defaults.emptyMessage ?? 'No data',
|
|
1175
|
+
}
|
|
1176
|
+
return applyTemplate(spec.template, specToSubs(spec))
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
// ── KPI / table path (shell template) ──────────────────────────────────────
|
|
1180
|
+
const t3ShellPath = t3ShellTemplatePath()
|
|
1181
|
+
if (!existsSync(t3ShellPath)) {
|
|
1182
|
+
throw new Error(`T3 shell template not found at ${t3ShellPath}`)
|
|
1183
|
+
}
|
|
1184
|
+
// No explicit columns → emit an empty literal; the widget auto-detects columns
|
|
1185
|
+
// from the actual row data at runtime (real keys + labels), instead of a static
|
|
1186
|
+
// name/value placeholder that renders every cell as "—".
|
|
1187
|
+
const columns = compileColumns(metric.columnDefs ?? metric.columns ?? defaults.columnDefs ?? defaults.columns ?? '[]')
|
|
1188
|
+
// kpi-card defaults to the conventional { value, previous } shape. Without a
|
|
1189
|
+
// valueField the shell renders data.length (the "headline 1" bug) — invisible
|
|
1190
|
+
// until you look at live data. Default it; if a metric explicitly clears it,
|
|
1191
|
+
// fail loud rather than silently show the row count.
|
|
1192
|
+
const isKpiCard = displayAs === 'kpi-card'
|
|
1193
|
+
const valueField = metric.valueField ?? defaults.valueField ?? (isKpiCard ? 'value' : '')
|
|
1194
|
+
const valueLabel = metric.valueLabel ?? defaults.valueLabel ?? ''
|
|
1195
|
+
// previousField defaults only for the conventional { value, previous } shape;
|
|
1196
|
+
// a custom valueField (e.g. "count") gets no unsolicited badge field. The
|
|
1197
|
+
// badge still renders only when the module actually returns a previous value.
|
|
1198
|
+
const previousField = metric.previousField ?? defaults.previousField
|
|
1199
|
+
?? (isKpiCard && valueField === 'value' ? 'previous' : '')
|
|
1200
|
+
if (isKpiCard && !valueField) {
|
|
1201
|
+
throw new Error(
|
|
1202
|
+
`kpi-card metric "${metric.name}" has no valueField — it would silently render the ` +
|
|
1203
|
+
`row count instead of the metric. Set valueField (e.g. "value") in the metric or ` +
|
|
1204
|
+
`registry defaults, and have the module return [{ value, previous? }].`,
|
|
1205
|
+
)
|
|
1206
|
+
}
|
|
1207
|
+
const deltaPolarity = metric.deltaPolarity ?? defaults.deltaPolarity ?? 'neutral'
|
|
1208
|
+
const rowLinkKey = metric.rowLink?.key ?? defaults.rowLink?.key ?? ''
|
|
1209
|
+
const rowLinkRoute = rowLinkKey ? `/${componentName.toLowerCase()}` : ''
|
|
1210
|
+
// KPI drill-down: a kpi-card with `detail: true` (module exports fetchDetail)
|
|
1211
|
+
// becomes a clickable card that navigates to its record-grain view. Empty for
|
|
1212
|
+
// a plain KPI or any table (tables drill via ROW_LINK, not the card). Step 5a
|
|
1213
|
+
// generates the matching view + route when this is set.
|
|
1214
|
+
const kpiDetailRoute = widgetGetsDetailView(displayAs, metric, registryEntry)
|
|
1215
|
+
? (metric.detailRoute ?? `/${componentName.toLowerCase()}`) : ''
|
|
1216
|
+
const defaultSortAsc = metric.defaultSortAsc === true ? 'true' : 'false'
|
|
1217
|
+
const emptyMessage = metric.emptyMessage ?? defaults.emptyMessage ?? 'No data'
|
|
1218
|
+
const subtitle = autoSubtitle(metric, defaults, timeRange)
|
|
1219
|
+
|
|
1220
|
+
let content = readFileSync(t3ShellPath, 'utf8')
|
|
1221
|
+
content = content
|
|
1222
|
+
.split('<<METRIC_IMPORT>>').join(`import { fetchData } from '${metricModuleSpecifier(metric)}'`)
|
|
1223
|
+
.split('<<COMPONENT_NAME>>').join(componentName)
|
|
1224
|
+
.split('<<TITLE>>').join(metric.title ?? componentName)
|
|
1225
|
+
.split('<<DESCRIPTION>>').join(subtitle)
|
|
1226
|
+
.split('<<ICON_NAME>>').join(iconName)
|
|
1227
|
+
.split('<<DISPLAY_AS>>').join(displayAs ?? 'ranked-table')
|
|
1228
|
+
.split('<<COLUMNS>>').join(columns)
|
|
1229
|
+
.split('<<VALUE_FIELD>>').join(valueField)
|
|
1230
|
+
.split('<<VALUE_LABEL>>').join(valueLabel)
|
|
1231
|
+
.split('<<PREVIOUS_FIELD>>').join(previousField)
|
|
1232
|
+
.split('<<DELTA_POLARITY>>').join(deltaPolarity)
|
|
1233
|
+
.split('<<ROW_LINK_KEY>>').join(rowLinkKey)
|
|
1234
|
+
.split('<<ROW_LINK_ROUTE>>').join(rowLinkRoute)
|
|
1235
|
+
.split('<<KPI_DETAIL_ROUTE>>').join(kpiDetailRoute)
|
|
1236
|
+
.split('<<DEFAULT_SORT_ASC>>').join(defaultSortAsc)
|
|
1237
|
+
.split('<<EMPTY_MESSAGE>>').join(emptyMessage)
|
|
1238
|
+
return content
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1241
|
+
/**
|
|
1242
|
+
* Convert a WidgetSpec to the substitution map used by applyTemplate.
|
|
1243
|
+
* @param {WidgetSpec} spec
|
|
1244
|
+
* @returns {Record<string, string>}
|
|
1245
|
+
*/
|
|
1246
|
+
function specToSubs(spec) {
|
|
1247
|
+
return {
|
|
1248
|
+
COMPONENT_NAME: spec.componentName,
|
|
1249
|
+
TITLE: spec.title,
|
|
1250
|
+
SUBTITLE: spec.subtitle,
|
|
1251
|
+
DETAIL_ROUTE: spec.detailRoute,
|
|
1252
|
+
ICON: spec.icon,
|
|
1253
|
+
DATA_HOOK: spec.dataHook,
|
|
1254
|
+
DATA_SELECTOR: spec.dataSelector,
|
|
1255
|
+
X_KEY: spec.xKey,
|
|
1256
|
+
Y_KEY: spec.yKey,
|
|
1257
|
+
DATA_KEY: spec.yKey,
|
|
1258
|
+
NAME_KEY: spec.xKey,
|
|
1259
|
+
HEADLINE_MODE: spec.headlineMode,
|
|
1260
|
+
DELTA_POLARITY: spec.deltaPolarity,
|
|
1261
|
+
RATE_NUM: spec.rateNum,
|
|
1262
|
+
RATE_DEN: spec.rateDen,
|
|
1263
|
+
SERIES: spec.series,
|
|
1264
|
+
PIVOT_EXPRESSION: spec.pivotExpression,
|
|
1265
|
+
EMPTY_MESSAGE: spec.emptyMessage ?? 'No data',
|
|
1266
|
+
SDK_IMPORT_LINE: spec.sdkImportLine ?? '',
|
|
1267
|
+
RESPONSE_TYPE_IMPORT: spec.responseTypeImport ?? '',
|
|
1268
|
+
HOOK_IMPORT: spec.hookImport ?? "import { useWidgetData } from '@/hooks/useWidgetData'",
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
|
|
1272
|
+
// ── Build pipelines ────────────────────────────────────────────────────────────
|
|
1273
|
+
|
|
1274
|
+
/**
|
|
1275
|
+
* Parse tsc error output to find which widget file caused the error.
|
|
1276
|
+
* Falls back to defaultName if the path can't be extracted.
|
|
1277
|
+
* @param {string} tscOutput
|
|
1278
|
+
* @param {string} defaultName
|
|
1279
|
+
* @returns {string}
|
|
1280
|
+
*/
|
|
1281
|
+
function widgetNameFromTscErrors(tscOutput, defaultName) {
|
|
1282
|
+
const match = tscOutput.match(/widgets[/\\](\w+)\.tsx\(\d+/)
|
|
1283
|
+
return match?.[1] ?? defaultName
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1286
|
+
|
|
1287
|
+
/**
|
|
1288
|
+
* Validate an edit-intent.json and normalize it to a batch.
|
|
1289
|
+
* Accepts either a single operation ({ op, ... } — legacy shape) or a batch
|
|
1290
|
+
* ({ ops: [{ op, ... }, ...] }). Throws for unknown operation types.
|
|
1291
|
+
* @param {{ projectDir: string, op?: string, ops?: Array<object>, target?: string, metric?: IntentMetric, delta?: object }} editIntent
|
|
1292
|
+
* @returns {{ projectDir: string, ops: Array<{ op: string, target?: string, metric?: IntentMetric, delta?: object }> }}
|
|
1293
|
+
*/
|
|
1294
|
+
export function classifyEditIntent(editIntent) {
|
|
1295
|
+
const ops = Array.isArray(editIntent.ops)
|
|
1296
|
+
? editIntent.ops
|
|
1297
|
+
: [{ op: editIntent.op, target: editIntent.target, metric: editIntent.metric, delta: editIntent.delta }]
|
|
1298
|
+
if (ops.length === 0) throw new Error('classifyEditIntent: ops array is empty')
|
|
1299
|
+
ops.forEach((o, i) => {
|
|
1300
|
+
if (!VALID_EDIT_OPS.includes(o.op)) {
|
|
1301
|
+
throw new Error(`classifyEditIntent: invalid op "${o.op}" (ops[${i}]). Must be one of: ${VALID_EDIT_OPS.join(', ')}`)
|
|
1302
|
+
}
|
|
1303
|
+
})
|
|
1304
|
+
return { projectDir: editIntent.projectDir, ops }
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1307
|
+
/**
|
|
1308
|
+
* Build the metric used to regenerate a widget for a CHANGE op.
|
|
1309
|
+
* Starts from the persisted intentMetric (full metadata: title/displayAs/hints) and merges
|
|
1310
|
+
* the delta on top. Falls back to a minimal ref for legacy state files that
|
|
1311
|
+
* predate intentMetric persistence — then the delta itself must carry title + displayAs.
|
|
1312
|
+
* The data-fetch code lives in the metric module on disk (resolved by name); it is not part of this ref.
|
|
1313
|
+
* @param {object|undefined} stored - state.widgets[target]
|
|
1314
|
+
* @param {string} target - widget component name
|
|
1315
|
+
* @param {object|undefined} delta - fields to change
|
|
1316
|
+
* @returns {IntentMetric}
|
|
1317
|
+
*/
|
|
1318
|
+
export function resolveChangeMetric(stored, target, delta) {
|
|
1319
|
+
const base = stored?.intentMetric ?? { name: stored?.metric ?? target.toLowerCase(), tier: stored?.tier ?? 'T1' }
|
|
1320
|
+
return { ...base, ...(delta ?? {}) }
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1323
|
+
|
|
1324
|
+
|
|
1325
|
+
|
|
1326
|
+
// ── Entry point ────────────────────────────────────────────────────────────────
|
|
1327
|
+
|
|
1328
|
+
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
|
|
1329
|
+
|
|
1330
|
+
// --prewarm <routingName|projectDir> mode: run npm ci in an already-extracted
|
|
1331
|
+
// project, then exit. The agent extracts the starter-kit archive into
|
|
1332
|
+
// <cwd>/<routingName> first. resolve() (not join()) so an ABSOLUTE path is
|
|
1333
|
+
// honored as-is — join(cwd, '/abs/path') concatenates into a broken path on
|
|
1334
|
+
// Windows (C:\cwd\C:\abs\path) and npm ci runs in the wrong place.
|
|
1335
|
+
if (process.argv[2] === '--prewarm' && process.argv[3]) {
|
|
1336
|
+
const routingName = process.argv[3]
|
|
1337
|
+
const prewarmDir = resolve(process.cwd(), routingName)
|
|
1338
|
+
assertScaffoldExtracted(prewarmDir)
|
|
1339
|
+
await runPrewarm(prewarmDir)
|
|
1340
|
+
process.exit(0)
|
|
1341
|
+
}
|
|
1342
|
+
|
|
1343
|
+
// --pack-template <projectDir> mode: stage modify-face source + template.json
|
|
1344
|
+
// into dist/_source for a one-artifact template package (run after `npm run build`).
|
|
1345
|
+
if (process.argv[2] === '--pack-template' && process.argv[3]) {
|
|
1346
|
+
packTemplate(process.argv[3])
|
|
1347
|
+
process.exit(0)
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1350
|
+
const planArg = process.argv[2]
|
|
1351
|
+
if (!planArg) fail('Usage: node build-dashboard.mjs <intent.json|edit-intent.json>')
|
|
1352
|
+
|
|
1353
|
+
let plan
|
|
1354
|
+
try {
|
|
1355
|
+
plan = JSON.parse(readFileSync(planArg, 'utf8'))
|
|
1356
|
+
} catch (e) {
|
|
1357
|
+
fail(`Could not read plan JSON from ${planArg}: ${e.message}`)
|
|
1358
|
+
}
|
|
1359
|
+
|
|
1360
|
+
if (plan.metrics) {
|
|
1361
|
+
const intentErrors = validateIntent(plan)
|
|
1362
|
+
if (intentErrors.length > 0) fail(`Invalid intent.json:\n${intentErrors.map(e => ' • ' + e).join('\n')}`)
|
|
1363
|
+
await runDashboardBuild(plan, planArg)
|
|
1364
|
+
} else if (plan.op || plan.ops) {
|
|
1365
|
+
classifyEditIntent(plan)
|
|
1366
|
+
await runIncrementalEdit(plan, planArg)
|
|
1367
|
+
} else {
|
|
1368
|
+
fail('Unrecognised input format. Expected intent.json (has "metrics") or edit-intent.json (has "op").')
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1371
|
+
} // end entry-point guard
|