@wootsup/yt-builder-mcp 1.12.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +139 -40
- package/bin/node-floor.d.ts +28 -0
- package/bin/node-floor.js +150 -0
- package/bin/yt-builder-mcp.js +51 -10
- package/dist/auth.d.ts +3 -121
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +44 -174
- package/dist/auth.js.map +1 -1
- package/dist/catalog/build-catalog.d.ts +4 -49
- package/dist/catalog/build-catalog.d.ts.map +1 -1
- package/dist/catalog/build-catalog.js +15 -126
- package/dist/catalog/build-catalog.js.map +1 -1
- package/dist/catalog/json-schema.d.ts +3 -0
- package/dist/catalog/json-schema.d.ts.map +1 -0
- package/dist/catalog/json-schema.js +68 -0
- package/dist/catalog/json-schema.js.map +1 -0
- package/dist/catalog/output-shapes.d.ts +6 -0
- package/dist/catalog/output-shapes.d.ts.map +1 -0
- package/dist/catalog/output-shapes.js +89 -0
- package/dist/catalog/output-shapes.js.map +1 -0
- package/dist/catalog/tool-catalog-markdown.d.ts +1 -37
- package/dist/catalog/tool-catalog-markdown.d.ts.map +1 -1
- package/dist/catalog/tool-catalog-markdown.js +8 -52
- package/dist/catalog/tool-catalog-markdown.js.map +1 -1
- package/dist/cli/doctor-command.d.ts +2 -7
- package/dist/cli/doctor-command.d.ts.map +1 -1
- package/dist/cli/doctor-command.js +21 -62
- package/dist/cli/doctor-command.js.map +1 -1
- package/dist/cli/invocation-decision.d.ts +4 -52
- package/dist/cli/invocation-decision.d.ts.map +1 -1
- package/dist/cli/invocation-decision.js +46 -55
- package/dist/cli/invocation-decision.js.map +1 -1
- package/dist/client.d.ts +3 -107
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +34 -115
- package/dist/client.js.map +1 -1
- package/dist/clients/claude-code.d.ts +0 -15
- package/dist/clients/claude-code.d.ts.map +1 -1
- package/dist/clients/claude-code.js +1 -23
- package/dist/clients/claude-code.js.map +1 -1
- package/dist/clients/claude-desktop.d.ts +0 -16
- package/dist/clients/claude-desktop.d.ts.map +1 -1
- package/dist/clients/claude-desktop.js +1 -19
- package/dist/clients/claude-desktop.js.map +1 -1
- package/dist/clients/cline.d.ts +0 -23
- package/dist/clients/cline.d.ts.map +1 -1
- package/dist/clients/cline.js +1 -34
- package/dist/clients/cline.js.map +1 -1
- package/dist/clients/codex-cli.d.ts +0 -39
- package/dist/clients/codex-cli.d.ts.map +1 -1
- package/dist/clients/codex-cli.js +12 -147
- package/dist/clients/codex-cli.js.map +1 -1
- package/dist/clients/config-file.d.ts +15 -0
- package/dist/clients/config-file.d.ts.map +1 -0
- package/dist/clients/config-file.js +86 -0
- package/dist/clients/config-file.js.map +1 -0
- package/dist/clients/continue.d.ts +0 -10
- package/dist/clients/continue.d.ts.map +1 -1
- package/dist/clients/continue.js +3 -30
- package/dist/clients/continue.js.map +1 -1
- package/dist/clients/cursor.d.ts +0 -9
- package/dist/clients/cursor.d.ts.map +1 -1
- package/dist/clients/cursor.js +1 -12
- package/dist/clients/cursor.js.map +1 -1
- package/dist/clients/gemini-cli.d.ts +0 -15
- package/dist/clients/gemini-cli.d.ts.map +1 -1
- package/dist/clients/gemini-cli.js +1 -18
- package/dist/clients/gemini-cli.js.map +1 -1
- package/dist/clients/home.d.ts +0 -12
- package/dist/clients/home.d.ts.map +1 -1
- package/dist/clients/home.js +0 -12
- package/dist/clients/home.js.map +1 -1
- package/dist/clients/index.d.ts +2 -153
- package/dist/clients/index.d.ts.map +1 -1
- package/dist/clients/index.js +3 -153
- package/dist/clients/index.js.map +1 -1
- package/dist/clients/roo-code.d.ts +0 -20
- package/dist/clients/roo-code.d.ts.map +1 -1
- package/dist/clients/roo-code.js +1 -23
- package/dist/clients/roo-code.js.map +1 -1
- package/dist/clients/zed.d.ts +0 -9
- package/dist/clients/zed.d.ts.map +1 -1
- package/dist/clients/zed.js +1 -19
- package/dist/clients/zed.js.map +1 -1
- package/dist/compat-floor.d.ts +35 -0
- package/dist/compat-floor.d.ts.map +1 -0
- package/dist/compat-floor.js +84 -0
- package/dist/compat-floor.js.map +1 -0
- package/dist/compat-probe.d.ts +6 -0
- package/dist/compat-probe.d.ts.map +1 -0
- package/dist/compat-probe.js +20 -0
- package/dist/compat-probe.js.map +1 -0
- package/dist/diagnostics/ca-reexec.d.ts +0 -30
- package/dist/diagnostics/ca-reexec.d.ts.map +1 -1
- package/dist/diagnostics/ca-reexec.js +0 -62
- package/dist/diagnostics/ca-reexec.js.map +1 -1
- package/dist/diagnostics/ca-trust.d.ts +0 -22
- package/dist/diagnostics/ca-trust.d.ts.map +1 -1
- package/dist/diagnostics/ca-trust.js +3 -42
- package/dist/diagnostics/ca-trust.js.map +1 -1
- package/dist/diagnostics/diagnose-network-error.d.ts +1 -38
- package/dist/diagnostics/diagnose-network-error.d.ts.map +1 -1
- package/dist/diagnostics/diagnose-network-error.js +27 -38
- package/dist/diagnostics/diagnose-network-error.js.map +1 -1
- package/dist/diagnostics/doctor.d.ts +1 -85
- package/dist/diagnostics/doctor.d.ts.map +1 -1
- package/dist/diagnostics/doctor.js +7 -92
- package/dist/diagnostics/doctor.js.map +1 -1
- package/dist/diagnostics/startup-self-probe.d.ts +3 -25
- package/dist/diagnostics/startup-self-probe.d.ts.map +1 -1
- package/dist/diagnostics/startup-self-probe.js +11 -48
- package/dist/diagnostics/startup-self-probe.js.map +1 -1
- package/dist/errors/hints.d.ts +0 -44
- package/dist/errors/hints.d.ts.map +1 -1
- package/dist/errors/hints.js +1 -33
- package/dist/errors/hints.js.map +1 -1
- package/dist/errors/mask.d.ts +0 -31
- package/dist/errors/mask.d.ts.map +1 -1
- package/dist/errors/mask.js +0 -32
- package/dist/errors/mask.js.map +1 -1
- package/dist/errors/redact-credentials.d.ts +2 -0
- package/dist/errors/redact-credentials.d.ts.map +1 -0
- package/dist/errors/redact-credentials.js +13 -0
- package/dist/errors/redact-credentials.js.map +1 -0
- package/dist/errors/sanitize.d.ts +0 -29
- package/dist/errors/sanitize.d.ts.map +1 -1
- package/dist/errors/sanitize.js +0 -40
- package/dist/errors/sanitize.js.map +1 -1
- package/dist/errors.d.ts +6 -17
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +16 -21
- package/dist/errors.js.map +1 -1
- package/dist/gateway/advanced-read-tool.d.ts +0 -32
- package/dist/gateway/advanced-read-tool.d.ts.map +1 -1
- package/dist/gateway/advanced-read-tool.js +43 -111
- package/dist/gateway/advanced-read-tool.js.map +1 -1
- package/dist/gateway/advanced-tool/discovery.d.ts +1 -12
- package/dist/gateway/advanced-tool/discovery.d.ts.map +1 -1
- package/dist/gateway/advanced-tool/discovery.js +15 -24
- package/dist/gateway/advanced-tool/discovery.js.map +1 -1
- package/dist/gateway/advanced-tool/domains.d.ts +7 -66
- package/dist/gateway/advanced-tool/domains.d.ts.map +1 -1
- package/dist/gateway/advanced-tool/domains.js +36 -80
- package/dist/gateway/advanced-tool/domains.js.map +1 -1
- package/dist/gateway/advanced-tool/execute.d.ts +0 -25
- package/dist/gateway/advanced-tool/execute.d.ts.map +1 -1
- package/dist/gateway/advanced-tool/execute.js +13 -49
- package/dist/gateway/advanced-tool/execute.js.map +1 -1
- package/dist/gateway/advanced-tool/index.d.ts +1 -32
- package/dist/gateway/advanced-tool/index.d.ts.map +1 -1
- package/dist/gateway/advanced-tool/index.js +1 -35
- package/dist/gateway/advanced-tool/index.js.map +1 -1
- package/dist/gateway/advanced-tool/register.d.ts +0 -14
- package/dist/gateway/advanced-tool/register.d.ts.map +1 -1
- package/dist/gateway/advanced-tool/register.js +22 -58
- package/dist/gateway/advanced-tool/register.js.map +1 -1
- package/dist/gateway/advanced-tool.d.ts +0 -11
- package/dist/gateway/advanced-tool.d.ts.map +1 -1
- package/dist/gateway/advanced-tool.js +0 -11
- package/dist/gateway/advanced-tool.js.map +1 -1
- package/dist/gateway/capturing-server.d.ts +3 -88
- package/dist/gateway/capturing-server.d.ts.map +1 -1
- package/dist/gateway/capturing-server.js +0 -77
- package/dist/gateway/capturing-server.js.map +1 -1
- package/dist/gateway/essentials.d.ts +1 -63
- package/dist/gateway/essentials.d.ts.map +1 -1
- package/dist/gateway/essentials.js +9 -178
- package/dist/gateway/essentials.js.map +1 -1
- package/dist/gateway/test-support.d.ts +0 -28
- package/dist/gateway/test-support.d.ts.map +1 -1
- package/dist/gateway/test-support.js +0 -29
- package/dist/gateway/test-support.js.map +1 -1
- package/dist/health-response.d.ts +2 -0
- package/dist/health-response.d.ts.map +1 -0
- package/dist/health-response.js +7 -0
- package/dist/health-response.js.map +1 -0
- package/dist/icon-data.js +0 -3
- package/dist/icon-data.js.map +1 -1
- package/dist/index.d.ts +7 -23
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +46 -130
- package/dist/index.js.map +1 -1
- package/dist/install-skill.d.ts +0 -44
- package/dist/install-skill.d.ts.map +1 -1
- package/dist/install-skill.js +8 -48
- package/dist/install-skill.js.map +1 -1
- package/dist/net/extra-headers.d.ts +0 -58
- package/dist/net/extra-headers.d.ts.map +1 -1
- package/dist/net/extra-headers.js +0 -67
- package/dist/net/extra-headers.js.map +1 -1
- package/dist/net/internal-host.d.ts +0 -31
- package/dist/net/internal-host.d.ts.map +1 -1
- package/dist/net/internal-host.js +7 -65
- package/dist/net/internal-host.js.map +1 -1
- package/dist/net/proxy-dispatcher.d.ts +0 -37
- package/dist/net/proxy-dispatcher.d.ts.map +1 -1
- package/dist/net/proxy-dispatcher.js +0 -40
- package/dist/net/proxy-dispatcher.js.map +1 -1
- package/dist/net/public-fetch.d.ts +53 -0
- package/dist/net/public-fetch.d.ts.map +1 -0
- package/dist/net/public-fetch.js +143 -0
- package/dist/net/public-fetch.js.map +1 -0
- package/dist/net/public-page-report.d.ts +26 -0
- package/dist/net/public-page-report.d.ts.map +1 -0
- package/dist/net/public-page-report.js +145 -0
- package/dist/net/public-page-report.js.map +1 -0
- package/dist/net/redirect-safe-fetch.d.ts +14 -0
- package/dist/net/redirect-safe-fetch.d.ts.map +1 -0
- package/dist/net/redirect-safe-fetch.js +61 -0
- package/dist/net/redirect-safe-fetch.js.map +1 -0
- package/dist/net/session-recovery-fetch.d.ts +7 -0
- package/dist/net/session-recovery-fetch.d.ts.map +1 -0
- package/dist/net/session-recovery-fetch.js +139 -0
- package/dist/net/session-recovery-fetch.js.map +1 -0
- package/dist/net/site-basic-auth.d.ts +1 -59
- package/dist/net/site-basic-auth.d.ts.map +1 -1
- package/dist/net/site-basic-auth.js +5 -94
- package/dist/net/site-basic-auth.js.map +1 -1
- package/dist/package-version.d.ts +2 -0
- package/dist/package-version.d.ts.map +1 -0
- package/dist/package-version.js +15 -0
- package/dist/package-version.js.map +1 -0
- package/dist/platform/detect.d.ts +0 -54
- package/dist/platform/detect.d.ts.map +1 -1
- package/dist/platform/detect.js +0 -53
- package/dist/platform/detect.js.map +1 -1
- package/dist/platform/index.d.ts +0 -100
- package/dist/platform/index.d.ts.map +1 -1
- package/dist/platform/index.js +0 -106
- package/dist/platform/index.js.map +1 -1
- package/dist/platform/joomla.d.ts +0 -35
- package/dist/platform/joomla.d.ts.map +1 -1
- package/dist/platform/joomla.js +0 -35
- package/dist/platform/joomla.js.map +1 -1
- package/dist/product-name.d.ts +2 -0
- package/dist/product-name.d.ts.map +1 -0
- package/dist/product-name.js +2 -0
- package/dist/product-name.js.map +1 -0
- package/dist/proxy/bridge.d.ts +36 -76
- package/dist/proxy/bridge.d.ts.map +1 -1
- package/dist/proxy/bridge.js +443 -136
- package/dist/proxy/bridge.js.map +1 -1
- package/dist/proxy/confirm-elicitation.d.ts +22 -0
- package/dist/proxy/confirm-elicitation.d.ts.map +1 -0
- package/dist/proxy/confirm-elicitation.js +170 -0
- package/dist/proxy/confirm-elicitation.js.map +1 -0
- package/dist/proxy/confirm-question-contract.d.ts +6 -0
- package/dist/proxy/confirm-question-contract.d.ts.map +1 -0
- package/dist/proxy/confirm-question-contract.js +9 -0
- package/dist/proxy/confirm-question-contract.js.map +1 -0
- package/dist/proxy/confirm-question.d.ts +2 -0
- package/dist/proxy/confirm-question.d.ts.map +1 -0
- package/dist/proxy/confirm-question.js +125 -0
- package/dist/proxy/confirm-question.js.map +1 -0
- package/dist/proxy/instructions.d.ts +4 -0
- package/dist/proxy/instructions.d.ts.map +1 -0
- package/dist/proxy/instructions.js +12 -0
- package/dist/proxy/instructions.js.map +1 -0
- package/dist/proxy/mode.d.ts +14 -9
- package/dist/proxy/mode.d.ts.map +1 -1
- package/dist/proxy/mode.js +58 -34
- package/dist/proxy/mode.js.map +1 -1
- package/dist/proxy/multi-site.d.ts +42 -0
- package/dist/proxy/multi-site.d.ts.map +1 -0
- package/dist/proxy/multi-site.js +134 -0
- package/dist/proxy/multi-site.js.map +1 -0
- package/dist/proxy/request-headers.d.ts +9 -0
- package/dist/proxy/request-headers.d.ts.map +1 -0
- package/dist/proxy/request-headers.js +17 -0
- package/dist/proxy/request-headers.js.map +1 -0
- package/dist/proxy/shadow-tools.d.ts +9 -0
- package/dist/proxy/shadow-tools.d.ts.map +1 -0
- package/dist/proxy/shadow-tools.js +42 -0
- package/dist/proxy/shadow-tools.js.map +1 -0
- package/dist/proxy/tools-list-cap.d.ts +7 -0
- package/dist/proxy/tools-list-cap.d.ts.map +1 -0
- package/dist/proxy/tools-list-cap.js +14 -0
- package/dist/proxy/tools-list-cap.js.map +1 -0
- package/dist/proxy/transport-uncertainty.d.ts +2 -0
- package/dist/proxy/transport-uncertainty.d.ts.map +1 -0
- package/dist/proxy/transport-uncertainty.js +8 -0
- package/dist/proxy/transport-uncertainty.js.map +1 -0
- package/dist/proxy/upstream-error.d.ts +7 -0
- package/dist/proxy/upstream-error.d.ts.map +1 -0
- package/dist/proxy/upstream-error.js +35 -0
- package/dist/proxy/upstream-error.js.map +1 -0
- package/dist/proxy/wire-dialect.d.ts +5 -0
- package/dist/proxy/wire-dialect.d.ts.map +1 -0
- package/dist/proxy/wire-dialect.js +33 -0
- package/dist/proxy/wire-dialect.js.map +1 -0
- package/dist/server.d.ts +14 -82
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +109 -204
- package/dist/server.js.map +1 -1
- package/dist/setup/banner-color.d.ts +13 -0
- package/dist/setup/banner-color.d.ts.map +1 -0
- package/dist/setup/banner-color.js +93 -0
- package/dist/setup/banner-color.js.map +1 -0
- package/dist/setup/banner.d.ts +22 -0
- package/dist/setup/banner.d.ts.map +1 -0
- package/dist/setup/banner.js +120 -0
- package/dist/setup/banner.js.map +1 -0
- package/dist/setup/ca-recovery.d.ts +20 -0
- package/dist/setup/ca-recovery.d.ts.map +1 -0
- package/dist/setup/ca-recovery.js +92 -0
- package/dist/setup/ca-recovery.js.map +1 -0
- package/dist/setup/closing.d.ts +15 -0
- package/dist/setup/closing.d.ts.map +1 -0
- package/dist/setup/closing.js +43 -0
- package/dist/setup/closing.js.map +1 -0
- package/dist/setup/corporate-ca.d.ts +16 -0
- package/dist/setup/corporate-ca.d.ts.map +1 -0
- package/dist/setup/corporate-ca.js +32 -0
- package/dist/setup/corporate-ca.js.map +1 -0
- package/dist/setup/host-platform.d.ts +8 -0
- package/dist/setup/host-platform.d.ts.map +1 -0
- package/dist/setup/host-platform.js +21 -0
- package/dist/setup/host-platform.js.map +1 -0
- package/dist/setup/install-configs.d.ts +18 -0
- package/dist/setup/install-configs.d.ts.map +1 -0
- package/dist/setup/install-configs.js +95 -0
- package/dist/setup/install-configs.js.map +1 -0
- package/dist/setup/multi-site-loop.d.ts +14 -0
- package/dist/setup/multi-site-loop.d.ts.map +1 -0
- package/dist/setup/multi-site-loop.js +20 -0
- package/dist/setup/multi-site-loop.js.map +1 -0
- package/dist/setup/phases.d.ts +18 -0
- package/dist/setup/phases.d.ts.map +1 -0
- package/dist/setup/phases.js +55 -0
- package/dist/setup/phases.js.map +1 -0
- package/dist/setup/registry-remedy.d.ts +12 -0
- package/dist/setup/registry-remedy.d.ts.map +1 -0
- package/dist/setup/registry-remedy.js +37 -0
- package/dist/setup/registry-remedy.js.map +1 -0
- package/dist/setup/resume.d.ts +28 -0
- package/dist/setup/resume.d.ts.map +1 -0
- package/dist/setup/resume.js +99 -0
- package/dist/setup/resume.js.map +1 -0
- package/dist/setup-ca-env.d.ts +0 -30
- package/dist/setup-ca-env.d.ts.map +1 -1
- package/dist/setup-ca-env.js +0 -28
- package/dist/setup-ca-env.js.map +1 -1
- package/dist/setup-ca-reprobe.d.ts +0 -25
- package/dist/setup-ca-reprobe.d.ts.map +1 -1
- package/dist/setup-ca-reprobe.js +0 -34
- package/dist/setup-ca-reprobe.js.map +1 -1
- package/dist/setup-cli-confirm.d.ts +0 -34
- package/dist/setup-cli-confirm.d.ts.map +1 -1
- package/dist/setup-cli-confirm.js +0 -36
- package/dist/setup-cli-confirm.js.map +1 -1
- package/dist/setup-cli.d.ts +6 -167
- package/dist/setup-cli.d.ts.map +1 -1
- package/dist/setup-cli.js +165 -177
- package/dist/setup-cli.js.map +1 -1
- package/dist/setup-npx-spec.d.ts +0 -36
- package/dist/setup-npx-spec.d.ts.map +1 -1
- package/dist/setup-npx-spec.js +0 -38
- package/dist/setup-npx-spec.js.map +1 -1
- package/dist/setup-prompts.d.ts +5 -70
- package/dist/setup-prompts.d.ts.map +1 -1
- package/dist/setup-prompts.js +67 -107
- package/dist/setup-prompts.js.map +1 -1
- package/dist/setup-token.d.ts +0 -34
- package/dist/setup-token.d.ts.map +1 -1
- package/dist/setup-token.js +0 -40
- package/dist/setup-token.js.map +1 -1
- package/dist/setup-wizard-defaults.d.ts +2 -37
- package/dist/setup-wizard-defaults.d.ts.map +1 -1
- package/dist/setup-wizard-defaults.js +28 -93
- package/dist/setup-wizard-defaults.js.map +1 -1
- package/dist/setup-wizard-handshake.d.ts +1 -19
- package/dist/setup-wizard-handshake.d.ts.map +1 -1
- package/dist/setup-wizard-handshake.js +8 -45
- package/dist/setup-wizard-handshake.js.map +1 -1
- package/dist/setup-wizard-types.d.ts +18 -217
- package/dist/setup-wizard-types.d.ts.map +1 -1
- package/dist/setup-wizard-types.js +0 -9
- package/dist/setup-wizard-types.js.map +1 -1
- package/dist/setup-wizard-write-types.d.ts +19 -0
- package/dist/setup-wizard-write-types.d.ts.map +1 -0
- package/dist/setup-wizard-write-types.js +2 -0
- package/dist/setup-wizard-write-types.js.map +1 -0
- package/dist/setup-wizard.d.ts +0 -28
- package/dist/setup-wizard.d.ts.map +1 -1
- package/dist/setup-wizard.js +167 -412
- package/dist/setup-wizard.js.map +1 -1
- package/dist/setup.d.ts +0 -15
- package/dist/setup.d.ts.map +1 -1
- package/dist/setup.js +0 -15
- package/dist/setup.js.map +1 -1
- package/dist/sites/bootstrap.d.ts +0 -38
- package/dist/sites/bootstrap.d.ts.map +1 -1
- package/dist/sites/bootstrap.js +0 -44
- package/dist/sites/bootstrap.js.map +1 -1
- package/dist/sites/cli/add-site.d.ts +0 -76
- package/dist/sites/cli/add-site.d.ts.map +1 -1
- package/dist/sites/cli/add-site.js +0 -71
- package/dist/sites/cli/add-site.js.map +1 -1
- package/dist/sites/cli/list-sites.d.ts +0 -31
- package/dist/sites/cli/list-sites.d.ts.map +1 -1
- package/dist/sites/cli/list-sites.js +0 -45
- package/dist/sites/cli/list-sites.js.map +1 -1
- package/dist/sites/cli/remove-site.d.ts +0 -37
- package/dist/sites/cli/remove-site.d.ts.map +1 -1
- package/dist/sites/cli/remove-site.js +0 -37
- package/dist/sites/cli/remove-site.js.map +1 -1
- package/dist/sites/cli/set-default.d.ts +0 -25
- package/dist/sites/cli/set-default.d.ts.map +1 -1
- package/dist/sites/cli/set-default.js +0 -22
- package/dist/sites/cli/set-default.js.map +1 -1
- package/dist/sites/cli/test-site.d.ts +0 -44
- package/dist/sites/cli/test-site.d.ts.map +1 -1
- package/dist/sites/cli/test-site.js +0 -39
- package/dist/sites/cli/test-site.js.map +1 -1
- package/dist/sites/client-pool.d.ts +11 -164
- package/dist/sites/client-pool.d.ts.map +1 -1
- package/dist/sites/client-pool.js +73 -194
- package/dist/sites/client-pool.js.map +1 -1
- package/dist/sites/config-problem.d.ts +2 -0
- package/dist/sites/config-problem.d.ts.map +1 -0
- package/dist/sites/config-problem.js +13 -0
- package/dist/sites/config-problem.js.map +1 -0
- package/dist/sites/env-bridge.d.ts +0 -69
- package/dist/sites/env-bridge.d.ts.map +1 -1
- package/dist/sites/env-bridge.js +2 -71
- package/dist/sites/env-bridge.js.map +1 -1
- package/dist/sites/op-inject.d.ts +23 -0
- package/dist/sites/op-inject.d.ts.map +1 -0
- package/dist/sites/op-inject.js +131 -0
- package/dist/sites/op-inject.js.map +1 -0
- package/dist/sites/paths.d.ts +0 -19
- package/dist/sites/paths.d.ts.map +1 -1
- package/dist/sites/paths.js +0 -19
- package/dist/sites/paths.js.map +1 -1
- package/dist/sites/probe.d.ts +1 -47
- package/dist/sites/probe.d.ts.map +1 -1
- package/dist/sites/probe.js +4 -46
- package/dist/sites/probe.js.map +1 -1
- package/dist/sites/registry.d.ts +4 -135
- package/dist/sites/registry.d.ts.map +1 -1
- package/dist/sites/registry.js +4 -98
- package/dist/sites/registry.js.map +1 -1
- package/dist/sites/schema.d.ts +3 -51
- package/dist/sites/schema.d.ts.map +1 -1
- package/dist/sites/schema.js +6 -52
- package/dist/sites/schema.js.map +1 -1
- package/dist/sites/secret-resolver.d.ts +6 -76
- package/dist/sites/secret-resolver.d.ts.map +1 -1
- package/dist/sites/secret-resolver.js +39 -104
- package/dist/sites/secret-resolver.js.map +1 -1
- package/dist/sites/store.d.ts +12 -69
- package/dist/sites/store.d.ts.map +1 -1
- package/dist/sites/store.js +37 -86
- package/dist/sites/store.js.map +1 -1
- package/dist/sites/substituted-registry.d.ts +11 -0
- package/dist/sites/substituted-registry.d.ts.map +1 -0
- package/dist/sites/substituted-registry.js +13 -0
- package/dist/sites/substituted-registry.js.map +1 -0
- package/dist/sites/tools/fetch-public-url.d.ts +4 -0
- package/dist/sites/tools/fetch-public-url.d.ts.map +1 -0
- package/dist/sites/tools/fetch-public-url.js +231 -0
- package/dist/sites/tools/fetch-public-url.js.map +1 -0
- package/dist/sites/tools/index.d.ts +2 -26
- package/dist/sites/tools/index.d.ts.map +1 -1
- package/dist/sites/tools/index.js +5 -28
- package/dist/sites/tools/index.js.map +1 -1
- package/dist/sites/tools/sites-list.d.ts +3 -47
- package/dist/sites/tools/sites-list.d.ts.map +1 -1
- package/dist/sites/tools/sites-list.js +65 -79
- package/dist/sites/tools/sites-list.js.map +1 -1
- package/dist/sites/tools/sites-overview.d.ts +4 -0
- package/dist/sites/tools/sites-overview.d.ts.map +1 -0
- package/dist/sites/tools/sites-overview.js +209 -0
- package/dist/sites/tools/sites-overview.js.map +1 -0
- package/dist/sites/tools/sites-test.d.ts +2 -45
- package/dist/sites/tools/sites-test.d.ts.map +1 -1
- package/dist/sites/tools/sites-test.js +2 -56
- package/dist/sites/tools/sites-test.js.map +1 -1
- package/dist/sites/tools/use-site.d.ts +2 -45
- package/dist/sites/tools/use-site.d.ts.map +1 -1
- package/dist/sites/tools/use-site.js +31 -79
- package/dist/sites/tools/use-site.js.map +1 -1
- package/dist/sites/unknown-name-result.d.ts +10 -0
- package/dist/sites/unknown-name-result.d.ts.map +1 -0
- package/dist/sites/unknown-name-result.js +13 -0
- package/dist/sites/unknown-name-result.js.map +1 -0
- package/dist/skill-instructions.d.ts +7 -0
- package/dist/skill-instructions.d.ts.map +1 -0
- package/dist/skill-instructions.js +85 -0
- package/dist/skill-instructions.js.map +1 -0
- package/dist/skill-loader.d.ts +1 -103
- package/dist/skill-loader.d.ts.map +1 -1
- package/dist/skill-loader.js +6 -143
- package/dist/skill-loader.js.map +1 -1
- package/dist/skill-manifest.d.ts +0 -20
- package/dist/skill-manifest.d.ts.map +1 -1
- package/dist/skill-manifest.js +1 -21
- package/dist/skill-manifest.js.map +1 -1
- package/dist/skill-topics.d.ts +19 -0
- package/dist/skill-topics.d.ts.map +1 -0
- package/dist/skill-topics.js +181 -0
- package/dist/skill-topics.js.map +1 -0
- package/dist/tools/active-site.d.ts +16 -0
- package/dist/tools/active-site.d.ts.map +1 -0
- package/dist/tools/active-site.js +55 -0
- package/dist/tools/active-site.js.map +1 -0
- package/dist/tools/bind-guidance.d.ts +2 -72
- package/dist/tools/bind-guidance.d.ts.map +1 -1
- package/dist/tools/bind-guidance.js +2 -72
- package/dist/tools/bind-guidance.js.map +1 -1
- package/dist/tools/budgeted-table.d.ts +14 -19
- package/dist/tools/budgeted-table.d.ts.map +1 -1
- package/dist/tools/budgeted-table.js +171 -105
- package/dist/tools/budgeted-table.js.map +1 -1
- package/dist/tools/elements/bind-output-schema.d.ts +69 -0
- package/dist/tools/elements/bind-output-schema.d.ts.map +1 -0
- package/dist/tools/elements/bind-output-schema.js +64 -0
- package/dist/tools/elements/bind-output-schema.js.map +1 -0
- package/dist/tools/elements/builders.d.ts +1 -22
- package/dist/tools/elements/builders.d.ts.map +1 -1
- package/dist/tools/elements/builders.js +147 -133
- package/dist/tools/elements/builders.js.map +1 -1
- package/dist/tools/elements/index.d.ts +0 -26
- package/dist/tools/elements/index.d.ts.map +1 -1
- package/dist/tools/elements/index.js +0 -25
- package/dist/tools/elements/index.js.map +1 -1
- package/dist/tools/elements/read-output-schemas.d.ts +121 -0
- package/dist/tools/elements/read-output-schemas.d.ts.map +1 -0
- package/dist/tools/elements/read-output-schemas.js +225 -0
- package/dist/tools/elements/read-output-schemas.js.map +1 -0
- package/dist/tools/elements/write-output-schemas.d.ts +185 -0
- package/dist/tools/elements/write-output-schemas.d.ts.map +1 -0
- package/dist/tools/elements/write-output-schemas.js +98 -0
- package/dist/tools/elements/write-output-schemas.js.map +1 -0
- package/dist/tools/elements.d.ts +0 -11
- package/dist/tools/elements.d.ts.map +1 -1
- package/dist/tools/elements.js +0 -11
- package/dist/tools/elements.js.map +1 -1
- package/dist/tools/format/health-format.d.ts +26 -43
- package/dist/tools/format/health-format.d.ts.map +1 -1
- package/dist/tools/format/health-format.js +32 -63
- package/dist/tools/format/health-format.js.map +1 -1
- package/dist/tools/format/inspection-format.d.ts +2 -54
- package/dist/tools/format/inspection-format.d.ts.map +1 -1
- package/dist/tools/format/inspection-format.js +33 -119
- package/dist/tools/format/inspection-format.js.map +1 -1
- package/dist/tools/format/pages-format.d.ts +32 -69
- package/dist/tools/format/pages-format.d.ts.map +1 -1
- package/dist/tools/format/pages-format.js +114 -139
- package/dist/tools/format/pages-format.js.map +1 -1
- package/dist/tools/format/sources-format.d.ts +5 -39
- package/dist/tools/format/sources-format.d.ts.map +1 -1
- package/dist/tools/format/sources-format.js +57 -87
- package/dist/tools/format/sources-format.js.map +1 -1
- package/dist/tools/health-output-schemas.d.ts +84 -0
- package/dist/tools/health-output-schemas.d.ts.map +1 -0
- package/dist/tools/health-output-schemas.js +132 -0
- package/dist/tools/health-output-schemas.js.map +1 -0
- package/dist/tools/health.d.ts +2 -20
- package/dist/tools/health.d.ts.map +1 -1
- package/dist/tools/health.js +47 -233
- package/dist/tools/health.js.map +1 -1
- package/dist/tools/index.d.ts +1 -26
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +12 -39
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/inspection.d.ts +4 -34
- package/dist/tools/inspection.d.ts.map +1 -1
- package/dist/tools/inspection.js +164 -536
- package/dist/tools/inspection.js.map +1 -1
- package/dist/tools/library.d.ts +115 -127
- package/dist/tools/library.d.ts.map +1 -1
- package/dist/tools/library.js +707 -455
- package/dist/tools/library.js.map +1 -1
- package/dist/tools/local-content/builders.d.ts +1 -17
- package/dist/tools/local-content/builders.d.ts.map +1 -1
- package/dist/tools/local-content/builders.js +103 -46
- package/dist/tools/local-content/builders.js.map +1 -1
- package/dist/tools/local-content/index.d.ts +0 -15
- package/dist/tools/local-content/index.d.ts.map +1 -1
- package/dist/tools/local-content/index.js +0 -14
- package/dist/tools/local-content/index.js.map +1 -1
- package/dist/tools/local-content/schemas.d.ts +31 -47
- package/dist/tools/local-content/schemas.d.ts.map +1 -1
- package/dist/tools/local-content/schemas.js +33 -62
- package/dist/tools/local-content/schemas.js.map +1 -1
- package/dist/tools/modules/builders.d.ts +4 -0
- package/dist/tools/modules/builders.d.ts.map +1 -0
- package/dist/tools/modules/builders.js +58 -0
- package/dist/tools/modules/builders.js.map +1 -0
- package/dist/tools/modules/index.d.ts +2 -0
- package/dist/tools/modules/index.d.ts.map +1 -0
- package/dist/tools/modules/index.js +2 -0
- package/dist/tools/modules/index.js.map +1 -0
- package/dist/tools/modules/module-id-schema.d.ts +4 -0
- package/dist/tools/modules/module-id-schema.d.ts.map +1 -0
- package/dist/tools/modules/module-id-schema.js +14 -0
- package/dist/tools/modules/module-id-schema.js.map +1 -0
- package/dist/tools/modules/module-type-fields.d.ts +10 -0
- package/dist/tools/modules/module-type-fields.d.ts.map +1 -0
- package/dist/tools/modules/module-type-fields.js +34 -0
- package/dist/tools/modules/module-type-fields.js.map +1 -0
- package/dist/tools/modules/modules-create.d.ts +59 -0
- package/dist/tools/modules/modules-create.d.ts.map +1 -0
- package/dist/tools/modules/modules-create.js +154 -0
- package/dist/tools/modules/modules-create.js.map +1 -0
- package/dist/tools/modules/modules-delete.d.ts +51 -0
- package/dist/tools/modules/modules-delete.d.ts.map +1 -0
- package/dist/tools/modules/modules-delete.js +180 -0
- package/dist/tools/modules/modules-delete.js.map +1 -0
- package/dist/tools/modules/modules-update.d.ts +43 -0
- package/dist/tools/modules/modules-update.d.ts.map +1 -0
- package/dist/tools/modules/modules-update.js +177 -0
- package/dist/tools/modules/modules-update.js.map +1 -0
- package/dist/tools/modules/placement-hints.d.ts +5 -0
- package/dist/tools/modules/placement-hints.d.ts.map +1 -0
- package/dist/tools/modules/placement-hints.js +90 -0
- package/dist/tools/modules/placement-hints.js.map +1 -0
- package/dist/tools/modules/placement-ways.d.ts +13 -0
- package/dist/tools/modules/placement-ways.d.ts.map +1 -0
- package/dist/tools/modules/placement-ways.js +61 -0
- package/dist/tools/modules/placement-ways.js.map +1 -0
- package/dist/tools/multi-items/builders.d.ts +1 -21
- package/dist/tools/multi-items/builders.d.ts.map +1 -1
- package/dist/tools/multi-items/builders.js +22 -65
- package/dist/tools/multi-items/builders.js.map +1 -1
- package/dist/tools/multi-items/index.d.ts +0 -15
- package/dist/tools/multi-items/index.d.ts.map +1 -1
- package/dist/tools/multi-items/index.js +0 -15
- package/dist/tools/multi-items/index.js.map +1 -1
- package/dist/tools/multi-items/item-container-map.d.ts +0 -35
- package/dist/tools/multi-items/item-container-map.d.ts.map +1 -1
- package/dist/tools/multi-items/item-container-map.js +0 -35
- package/dist/tools/multi-items/item-container-map.js.map +1 -1
- package/dist/tools/multi-items/output-schemas.d.ts +101 -0
- package/dist/tools/multi-items/output-schemas.d.ts.map +1 -0
- package/dist/tools/multi-items/output-schemas.js +74 -0
- package/dist/tools/multi-items/output-schemas.js.map +1 -0
- package/dist/tools/navigation/builders.d.ts +1 -16
- package/dist/tools/navigation/builders.d.ts.map +1 -1
- package/dist/tools/navigation/builders.js +5 -27
- package/dist/tools/navigation/builders.js.map +1 -1
- package/dist/tools/navigation/index.d.ts +0 -13
- package/dist/tools/navigation/index.d.ts.map +1 -1
- package/dist/tools/navigation/index.js +0 -12
- package/dist/tools/navigation/index.js.map +1 -1
- package/dist/tools/navigation/schemas.d.ts +0 -30
- package/dist/tools/navigation/schemas.d.ts.map +1 -1
- package/dist/tools/navigation/schemas.js +0 -30
- package/dist/tools/navigation/schemas.js.map +1 -1
- package/dist/tools/output-schema-metadata.d.ts +5 -0
- package/dist/tools/output-schema-metadata.d.ts.map +1 -0
- package/dist/tools/output-schema-metadata.js +83 -0
- package/dist/tools/output-schema-metadata.js.map +1 -0
- package/dist/tools/pages/builders.d.ts +1 -41
- package/dist/tools/pages/builders.d.ts.map +1 -1
- package/dist/tools/pages/builders.js +320 -405
- package/dist/tools/pages/builders.js.map +1 -1
- package/dist/tools/pages/header-transparency.d.ts +2 -32
- package/dist/tools/pages/header-transparency.d.ts.map +1 -1
- package/dist/tools/pages/header-transparency.js +9 -43
- package/dist/tools/pages/header-transparency.js.map +1 -1
- package/dist/tools/pages/index.d.ts +1 -19
- package/dist/tools/pages/index.d.ts.map +1 -1
- package/dist/tools/pages/index.js +1 -19
- package/dist/tools/pages/index.js.map +1 -1
- package/dist/tools/pages/schemas.d.ts +323 -241
- package/dist/tools/pages/schemas.d.ts.map +1 -1
- package/dist/tools/pages/schemas.js +514 -549
- package/dist/tools/pages/schemas.js.map +1 -1
- package/dist/tools/pages/write-output-schemas.d.ts +139 -0
- package/dist/tools/pages/write-output-schemas.d.ts.map +1 -0
- package/dist/tools/pages/write-output-schemas.js +138 -0
- package/dist/tools/pages/write-output-schemas.js.map +1 -0
- package/dist/tools/pages.d.ts +0 -11
- package/dist/tools/pages.d.ts.map +1 -1
- package/dist/tools/pages.js +0 -11
- package/dist/tools/pages.js.map +1 -1
- package/dist/tools/pool-resolve-helper.d.ts +0 -52
- package/dist/tools/pool-resolve-helper.d.ts.map +1 -1
- package/dist/tools/pool-resolve-helper.js +31 -106
- package/dist/tools/pool-resolve-helper.js.map +1 -1
- package/dist/tools/progress-phases.d.ts +0 -42
- package/dist/tools/progress-phases.d.ts.map +1 -1
- package/dist/tools/progress-phases.js +0 -44
- package/dist/tools/progress-phases.js.map +1 -1
- package/dist/tools/response-budget.d.ts +0 -69
- package/dist/tools/response-budget.d.ts.map +1 -1
- package/dist/tools/response-budget.js +0 -127
- package/dist/tools/response-budget.js.map +1 -1
- package/dist/tools/shared-schemas.d.ts +11 -65
- package/dist/tools/shared-schemas.d.ts.map +1 -1
- package/dist/tools/shared-schemas.js +40 -123
- package/dist/tools/shared-schemas.js.map +1 -1
- package/dist/tools/skill/builders.d.ts +3 -0
- package/dist/tools/skill/builders.d.ts.map +1 -0
- package/dist/tools/skill/builders.js +81 -0
- package/dist/tools/skill/builders.js.map +1 -0
- package/dist/tools/sources/builders.d.ts +1 -11
- package/dist/tools/sources/builders.d.ts.map +1 -1
- package/dist/tools/sources/builders.js +194 -170
- package/dist/tools/sources/builders.js.map +1 -1
- package/dist/tools/sources/index.d.ts +0 -17
- package/dist/tools/sources/index.d.ts.map +1 -1
- package/dist/tools/sources/index.js +0 -16
- package/dist/tools/sources/index.js.map +1 -1
- package/dist/tools/sources/read-output-schemas.d.ts +101 -0
- package/dist/tools/sources/read-output-schemas.d.ts.map +1 -0
- package/dist/tools/sources/read-output-schemas.js +105 -0
- package/dist/tools/sources/read-output-schemas.js.map +1 -0
- package/dist/tools/sources/source-name-argument.d.ts +17 -0
- package/dist/tools/sources/source-name-argument.d.ts.map +1 -0
- package/dist/tools/sources/source-name-argument.js +64 -0
- package/dist/tools/sources/source-name-argument.js.map +1 -0
- package/dist/tools/sources/write-output-schemas.d.ts +100 -0
- package/dist/tools/sources/write-output-schemas.d.ts.map +1 -0
- package/dist/tools/sources/write-output-schemas.js +61 -0
- package/dist/tools/sources/write-output-schemas.js.map +1 -0
- package/dist/tools/sources.d.ts +0 -12
- package/dist/tools/sources.d.ts.map +1 -1
- package/dist/tools/sources.js +0 -12
- package/dist/tools/sources.js.map +1 -1
- package/dist/tools/sparse-fields.d.ts +16 -232
- package/dist/tools/sparse-fields.d.ts.map +1 -1
- package/dist/tools/sparse-fields.js +178 -242
- package/dist/tools/sparse-fields.js.map +1 -1
- package/dist/tools/strict-input.d.ts +5 -0
- package/dist/tools/strict-input.d.ts.map +1 -0
- package/dist/tools/strict-input.js +55 -0
- package/dist/tools/strict-input.js.map +1 -0
- package/dist/tools/sublayout/builders.d.ts +1 -21
- package/dist/tools/sublayout/builders.d.ts.map +1 -1
- package/dist/tools/sublayout/builders.js +10 -39
- package/dist/tools/sublayout/builders.js.map +1 -1
- package/dist/tools/sublayout/index.d.ts +1 -11
- package/dist/tools/sublayout/index.d.ts.map +1 -1
- package/dist/tools/sublayout/index.js +1 -11
- package/dist/tools/sublayout/index.js.map +1 -1
- package/dist/tools/sublayout/output-schemas.d.ts +57 -0
- package/dist/tools/sublayout/output-schemas.d.ts.map +1 -0
- package/dist/tools/sublayout/output-schemas.js +51 -0
- package/dist/tools/sublayout/output-schemas.js.map +1 -0
- package/dist/tools/tool-builder/annotations.d.ts +1 -49
- package/dist/tools/tool-builder/annotations.d.ts.map +1 -1
- package/dist/tools/tool-builder/annotations.js +9 -49
- package/dist/tools/tool-builder/annotations.js.map +1 -1
- package/dist/tools/tool-builder/confirm-preview.d.ts +24 -0
- package/dist/tools/tool-builder/confirm-preview.d.ts.map +1 -0
- package/dist/tools/tool-builder/confirm-preview.js +37 -0
- package/dist/tools/tool-builder/confirm-preview.js.map +1 -0
- package/dist/tools/tool-builder/define.d.ts +6 -26
- package/dist/tools/tool-builder/define.d.ts.map +1 -1
- package/dist/tools/tool-builder/define.js +0 -25
- package/dist/tools/tool-builder/define.js.map +1 -1
- package/dist/tools/tool-builder/index.d.ts +3 -26
- package/dist/tools/tool-builder/index.d.ts.map +1 -1
- package/dist/tools/tool-builder/index.js +2 -25
- package/dist/tools/tool-builder/index.js.map +1 -1
- package/dist/tools/tool-builder/results.d.ts +4 -124
- package/dist/tools/tool-builder/results.d.ts.map +1 -1
- package/dist/tools/tool-builder/results.js +258 -263
- package/dist/tools/tool-builder/results.js.map +1 -1
- package/dist/tools/tool-builder/types.d.ts +5 -59
- package/dist/tools/tool-builder/types.d.ts.map +1 -1
- package/dist/tools/tool-builder/types.js +0 -7
- package/dist/tools/tool-builder/types.js.map +1 -1
- package/dist/tools/tool-builder.d.ts +0 -14
- package/dist/tools/tool-builder.d.ts.map +1 -1
- package/dist/tools/tool-builder.js +0 -14
- package/dist/tools/tool-builder.js.map +1 -1
- package/dist/uninstall-skill.d.ts +0 -41
- package/dist/uninstall-skill.d.ts.map +1 -1
- package/dist/uninstall-skill.js +3 -50
- package/dist/uninstall-skill.js.map +1 -1
- package/dist/uninstall.d.ts +0 -41
- package/dist/uninstall.d.ts.map +1 -1
- package/dist/uninstall.js +1 -29
- package/dist/uninstall.js.map +1 -1
- package/dist/update/update-check.d.ts +0 -26
- package/dist/update/update-check.d.ts.map +1 -1
- package/dist/update/update-check.js +0 -25
- package/dist/update/update-check.js.map +1 -1
- package/manifest.json +7 -12
- package/package.json +6 -11
- package/skills/yt-builder-mcp/SKILL.md +1871 -414
- package/dist/tools/elements/handlers-write.d.ts +0 -89
- package/dist/tools/elements/handlers-write.d.ts.map +0 -1
- package/dist/tools/elements/handlers-write.js +0 -797
- package/dist/tools/elements/handlers-write.js.map +0 -1
- package/dist/tools/elements/handlers.d.ts +0 -130
- package/dist/tools/elements/handlers.d.ts.map +0 -1
- package/dist/tools/elements/handlers.js +0 -581
- package/dist/tools/elements/handlers.js.map +0 -1
- package/dist/tools/elements/layout-hierarchy-rules.d.ts +0 -62
- package/dist/tools/elements/layout-hierarchy-rules.d.ts.map +0 -1
- package/dist/tools/elements/layout-hierarchy-rules.js +0 -154
- package/dist/tools/elements/layout-hierarchy-rules.js.map +0 -1
- package/dist/tools/elements/schema-validation.d.ts +0 -218
- package/dist/tools/elements/schema-validation.d.ts.map +0 -1
- package/dist/tools/elements/schema-validation.js +0 -685
- package/dist/tools/elements/schema-validation.js.map +0 -1
- package/dist/tools/elicitation.d.ts +0 -87
- package/dist/tools/elicitation.d.ts.map +0 -1
- package/dist/tools/elicitation.js +0 -100
- package/dist/tools/elicitation.js.map +0 -1
- package/dist/tools/format/elements-format.d.ts +0 -75
- package/dist/tools/format/elements-format.d.ts.map +0 -1
- package/dist/tools/format/elements-format.js +0 -368
- package/dist/tools/format/elements-format.js.map +0 -1
- package/dist/tools/format/source-schema-format.d.ts +0 -74
- package/dist/tools/format/source-schema-format.d.ts.map +0 -1
- package/dist/tools/format/source-schema-format.js +0 -208
- package/dist/tools/format/source-schema-format.js.map +0 -1
- package/dist/tools/layout-flatten.d.ts +0 -84
- package/dist/tools/layout-flatten.d.ts.map +0 -1
- package/dist/tools/layout-flatten.js +0 -129
- package/dist/tools/layout-flatten.js.map +0 -1
- package/dist/tools/local-content/handlers.d.ts +0 -56
- package/dist/tools/local-content/handlers.d.ts.map +0 -1
- package/dist/tools/local-content/handlers.js +0 -320
- package/dist/tools/local-content/handlers.js.map +0 -1
- package/dist/tools/multi-items/clean-implode-handler.d.ts +0 -37
- package/dist/tools/multi-items/clean-implode-handler.d.ts.map +0 -1
- package/dist/tools/multi-items/clean-implode-handler.js +0 -65
- package/dist/tools/multi-items/clean-implode-handler.js.map +0 -1
- package/dist/tools/multi-items/format/inspection-format.d.ts +0 -41
- package/dist/tools/multi-items/format/inspection-format.d.ts.map +0 -1
- package/dist/tools/multi-items/format/inspection-format.js +0 -64
- package/dist/tools/multi-items/format/inspection-format.js.map +0 -1
- package/dist/tools/multi-items/inspect-handler.d.ts +0 -97
- package/dist/tools/multi-items/inspect-handler.d.ts.map +0 -1
- package/dist/tools/multi-items/inspect-handler.js +0 -323
- package/dist/tools/multi-items/inspect-handler.js.map +0 -1
- package/dist/tools/multi-items/inspection-cache.d.ts +0 -85
- package/dist/tools/multi-items/inspection-cache.d.ts.map +0 -1
- package/dist/tools/multi-items/inspection-cache.js +0 -126
- package/dist/tools/multi-items/inspection-cache.js.map +0 -1
- package/dist/tools/navigation/handlers.d.ts +0 -30
- package/dist/tools/navigation/handlers.d.ts.map +0 -1
- package/dist/tools/navigation/handlers.js +0 -41
- package/dist/tools/navigation/handlers.js.map +0 -1
- package/dist/tools/pages/handlers-audit.d.ts +0 -140
- package/dist/tools/pages/handlers-audit.d.ts.map +0 -1
- package/dist/tools/pages/handlers-audit.js +0 -714
- package/dist/tools/pages/handlers-audit.js.map +0 -1
- package/dist/tools/pages/handlers-read.d.ts +0 -133
- package/dist/tools/pages/handlers-read.d.ts.map +0 -1
- package/dist/tools/pages/handlers-read.js +0 -1061
- package/dist/tools/pages/handlers-read.js.map +0 -1
- package/dist/tools/pages/handlers-write.d.ts +0 -212
- package/dist/tools/pages/handlers-write.d.ts.map +0 -1
- package/dist/tools/pages/handlers-write.js +0 -419
- package/dist/tools/pages/handlers-write.js.map +0 -1
- package/dist/tools/pages/target.d.ts +0 -162
- package/dist/tools/pages/target.d.ts.map +0 -1
- package/dist/tools/pages/target.js +0 -239
- package/dist/tools/pages/target.js.map +0 -1
- package/dist/tools/sources/handlers-bind.d.ts +0 -148
- package/dist/tools/sources/handlers-bind.d.ts.map +0 -1
- package/dist/tools/sources/handlers-bind.js +0 -657
- package/dist/tools/sources/handlers-bind.js.map +0 -1
- package/dist/tools/sources/handlers.d.ts +0 -116
- package/dist/tools/sources/handlers.d.ts.map +0 -1
- package/dist/tools/sources/handlers.js +0 -289
- package/dist/tools/sources/handlers.js.map +0 -1
- package/dist/tools/sublayout/inspect-sublayout-handler.d.ts +0 -95
- package/dist/tools/sublayout/inspect-sublayout-handler.d.ts.map +0 -1
- package/dist/tools/sublayout/inspect-sublayout-handler.js +0 -181
- package/dist/tools/sublayout/inspect-sublayout-handler.js.map +0 -1
package/dist/tools/library.js
CHANGED
|
@@ -1,85 +1,12 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Layout Library tools — browse + fetch + insert + save the YOOtheme Pro
|
|
3
|
-
* Library catalog (layouts / element presets), PLUS the Image Sourcing pair
|
|
4
|
-
* (Task D1) that fills layout image slots with real photography. Task 2
|
|
5
|
-
* (Layout Library, Spec #2) + Task 3 (Spec #3) + Task 6 (Spec #6) + Task 7
|
|
6
|
-
* (Spec #7) + Task D1 (Image Sourcing).
|
|
7
|
-
*
|
|
8
|
-
* yootheme_builder_library_browse
|
|
9
|
-
* → paginated, taxonomy-filtered catalog rows (tableResult — Wave G.2).
|
|
10
|
-
* yootheme_builder_library_get
|
|
11
|
-
* → a SINGLE item's full tree + derived section list (the natural next
|
|
12
|
-
* call after library_browse — id comes straight off a browse row).
|
|
13
|
-
* yootheme_builder_library_insert
|
|
14
|
-
* → compose + persist a browsed/fetched item onto a page/template
|
|
15
|
-
* through the REAL hardened write chain.
|
|
16
|
-
* yootheme_builder_library_save_as
|
|
17
|
-
* → the REVERSE direction: save a page/subtree INTO "My Layouts".
|
|
18
|
-
* yootheme_builder_library_delete
|
|
19
|
-
* → the INVERSE of library_save_as: remove ONE saved entry from the
|
|
20
|
-
* isolated "WootsUp AI Layouts" store (source:"ai" only; the curated
|
|
21
|
-
* catalog is read-only). ADVANCED, like library_save_as.
|
|
22
|
-
* yootheme_builder_image_search
|
|
23
|
-
* → search Unsplash / Pexels (real photography) or YOOtheme Pro Images
|
|
24
|
-
* (curated decorative/background assets) — forwards to the SAME
|
|
25
|
-
* `?key=<theme apikey>` proxy auth `library_browse` uses (Task A0-C1,
|
|
26
|
-
* PHP `ImageSearchService`). Returns candidate rows; `total:0` means
|
|
27
|
-
* no Library API key configured, mirroring browse's empty-catalog hint.
|
|
28
|
-
* yootheme_builder_image_import
|
|
29
|
-
* → sideload ONE chosen photo (typically a `full_url` from
|
|
30
|
-
* image_search) into the site's own Media Library. Returns
|
|
31
|
-
* `{imported:true, local_url}` to bind via element_update_settings,
|
|
32
|
-
* or `{imported:false, reason}` on a caught failure (never a 500).
|
|
33
|
-
*
|
|
34
|
-
* browse/get/image_search are read-only forwards, exactly like the sibling
|
|
35
|
-
* L1 read tools (sources_list / template_summary / pages_list): resolve the
|
|
36
|
-
* site via the pool, GET the plugin REST endpoint, project into a table +
|
|
37
|
-
* strict structuredContent, stamp the result-level `_meta` (site_id/
|
|
38
|
-
* site_url/platform/resolved_via) via withSiteMeta. insert/save_as/
|
|
39
|
-
* image_import are WRITE forwards (withWriteSiteRetry — pre-flights a KNOWN
|
|
40
|
-
* read-only token before any body build, mirroring every other write tool's
|
|
41
|
-
* U2 pattern).
|
|
42
|
-
*
|
|
43
|
-
* ── Tiering (Task 7, deliberate) ─────────────────────────────────────────
|
|
44
|
-
* `library_save_as` is registered as an ADVANCED tool — it is NOT added to
|
|
45
|
-
* `ESSENTIAL_TOOLS` (essentials.ts) and does NOT appear as a first-class
|
|
46
|
-
* `tools/list` entry. `tools/list` sits close to the 28000B token-baseline
|
|
47
|
-
* ceiling (see `tests/perf/token-baseline.test.ts`); a 4th Layout-Library L1
|
|
48
|
-
* tool would risk busting it. `CapturingServer` (gateway/capturing-server.ts)
|
|
49
|
-
* automatically captures ANY registered tool whose name is absent from both
|
|
50
|
-
* `ESSENTIAL_TOOLS` and `DIRECT_TOP_LEVEL_TOOLS` into the L2 advanced
|
|
51
|
-
* registry — reachable via `yootheme_builder_advanced({tool, arguments})` —
|
|
52
|
-
* so simply NOT listing this name in essentials.ts is the entire tiering
|
|
53
|
-
* decision (mirrors how `yootheme_builder_clean_implode_directives` /
|
|
54
|
-
* `yootheme_builder_element_unbind_source` are advanced-only today). The
|
|
55
|
-
* name falls through the gateway's domain taxonomy (domains.ts) to "misc"
|
|
56
|
-
* (no `library` domain exists there) — intentional, NOT a bug (see
|
|
57
|
-
* `domain-order.test.ts`'s own pinned "misc" fallthrough cases): it keeps the
|
|
58
|
-
* gateway's own tools/list-visible description text from growing at all,
|
|
59
|
-
* while `{tool: "yootheme_builder_library_save_as"}` discovery and
|
|
60
|
-
* `{tool, arguments}` execution both remain fully reachable (the gateway
|
|
61
|
-
* dispatches by an exact Map lookup, independent of the domain-grouped
|
|
62
|
-
* DESCRIPTION text — see `gateway/advanced-tool/execute.ts`).
|
|
63
|
-
*
|
|
64
|
-
* @license MIT
|
|
65
|
-
*/
|
|
66
1
|
import { z } from 'zod';
|
|
67
2
|
import { RestError } from '../errors.js';
|
|
68
3
|
import { withSiteRetry, withWriteSiteRetry } from './pool-resolve-helper.js';
|
|
69
|
-
import {
|
|
4
|
+
import { applyMaxChars, MAX_CHARS } from './sparse-fields.js';
|
|
5
|
+
import { ETAG, INCLUDE_META_SCHEMA, SITE_ID_SCHEMA } from './shared-schemas.js';
|
|
70
6
|
import { creating, defineTool, destructive, errorResult, jsonResult, mutating, readOnly, structuredResult, withSiteMeta, } from './tool-builder.js';
|
|
71
7
|
import { budgetedTableResult } from "./budgeted-table.js";
|
|
72
8
|
import { COMPACT_DEFAULT, RESPONSE_BUDGET } from "./response-budget.js";
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Fixed section-purpose enum (Task 8, Section Corpus Phase 1) — mirrors the
|
|
76
|
-
* PHP-side `SectionPurposeClassifier` / `SectionIndexEntry` purpose values
|
|
77
|
-
* byte-for-byte (design spec §A, Global Constraints). Only applies when
|
|
78
|
-
* `kind="section"`. Declared here (ahead of `LIBRARY_ITEM_SCHEMA`, which
|
|
79
|
-
* consumes it for the browse row's `purpose` field) rather than further
|
|
80
|
-
* down where the original Task 8 pass left it, since Task 8b (this file)
|
|
81
|
-
* now needs it at both the item-schema AND the browse-filter-schema sites.
|
|
82
|
-
*/
|
|
9
|
+
import { CONFIRM_PREVIEW_FIELDS, HOST_PROMPT_PROMISE, requireOnExecutedWrite } from './tool-builder/confirm-preview.js';
|
|
83
10
|
export const SECTION_PURPOSE_VALUES = [
|
|
84
11
|
'hero',
|
|
85
12
|
'features',
|
|
@@ -94,39 +21,10 @@ export const SECTION_PURPOSE_VALUES = [
|
|
|
94
21
|
'logos',
|
|
95
22
|
'content',
|
|
96
23
|
];
|
|
97
|
-
/**
|
|
98
|
-
* Track 1 Task 7 (WootsUp AI Layouts) — the `source` axis for
|
|
99
|
-
* browse/get/insert: `"catalog"` (the remote YOOtheme Pro Library, the
|
|
100
|
-
* DEFAULT) or `"ai"` (the plugin's OWN isolated "WootsUp AI Layouts" store,
|
|
101
|
-
* saved via library_save_as, no apikey). Mirrors the PHP-side
|
|
102
|
-
* `LibrarySourceRegistry` keys byte-for-byte.
|
|
103
|
-
*/
|
|
104
24
|
const LIBRARY_SOURCE_VALUES = ['catalog', 'ai'];
|
|
105
|
-
/**
|
|
106
|
-
* The wire default for the `source` axis. It is NEVER forwarded (a catalog
|
|
107
|
-
* browse/get/insert is byte-identical to the pre-Task-7 request — the PHP
|
|
108
|
-
* controller reads the SAME absent-param default), so only `"ai"` ever
|
|
109
|
-
* appears on the wire. Mirrors `LibrarySourceRegistry::DEFAULT_SOURCE`.
|
|
110
|
-
*/
|
|
111
25
|
const DEFAULT_LIBRARY_SOURCE = 'catalog';
|
|
112
|
-
/**
|
|
113
|
-
* Track 1 Task 7 (WootsUp AI Layouts) — the `destination` axis for
|
|
114
|
-
* library_save_as: `"ai"` (save INTO the isolated AI store, the DEFAULT) or
|
|
115
|
-
* `"native"` (the escape hatch — save into YOOtheme's native "My Layouts").
|
|
116
|
-
*/
|
|
117
26
|
const SAVE_DESTINATION_VALUES = ['ai', 'native'];
|
|
118
|
-
/**
|
|
119
|
-
* The wire default for the `destination` axis — the AI store. NEVER forwarded
|
|
120
|
-
* (an ai-default save is byte-identical to the pre-Task-7 body; the PHP
|
|
121
|
-
* `MyLayoutsStore` reads the SAME absent-param `ai` default), so only
|
|
122
|
-
* `"native"` ever appears on the wire.
|
|
123
|
-
*/
|
|
124
27
|
const DEFAULT_SAVE_DESTINATION = 'ai';
|
|
125
|
-
/**
|
|
126
|
-
* Mirrors `CatalogService::mapItem()` (PHP): every key is always PRESENT
|
|
127
|
-
* (never omitted) but each value may be `null` when the raw catalog item
|
|
128
|
-
* did not carry that taxonomy field — hence `.nullable()`, not `.optional()`.
|
|
129
|
-
*/
|
|
130
28
|
const LIBRARY_ITEM_META_SCHEMA = z.object({
|
|
131
29
|
topic: z.string().nullable(),
|
|
132
30
|
type: z.string().nullable(),
|
|
@@ -134,23 +32,13 @@ const LIBRARY_ITEM_META_SCHEMA = z.object({
|
|
|
134
32
|
website: z.string().nullable(),
|
|
135
33
|
image: z.string().nullable(),
|
|
136
34
|
});
|
|
137
|
-
/**
|
|
138
|
-
* Task 8b (Section Corpus Phase 1, PLAN-GAP fix) — a `kind="section"` browse
|
|
139
|
-
* row (`SectionCatalog::mapEntry()`, PHP) carries a DIFFERENT shape than a
|
|
140
|
-
* layout/element row: no `meta` wrapper, TOP-LEVEL `topic`/`type`/`website`,
|
|
141
|
-
* plus `layout_id`/`section_index`/`purpose`/`element_signature`/
|
|
142
|
-
* `visual_effects`/`has_images`/`image_count`/`element_count`. All of these
|
|
143
|
-
* are `.optional()` — additive, never present on a layout/element row — so a
|
|
144
|
-
* strict-outputSchema host still accepts the pre-existing byte-unchanged
|
|
145
|
-
* layout/element shape.
|
|
146
|
-
*/
|
|
147
35
|
const LIBRARY_ITEM_SCHEMA = z.object({
|
|
148
36
|
id: z.string(),
|
|
149
37
|
name: z.string(),
|
|
150
|
-
kind: z.enum(['layout', 'element', 'section']),
|
|
151
|
-
meta: LIBRARY_ITEM_META_SCHEMA,
|
|
152
|
-
thumbnail_url: z.string().nullable(),
|
|
153
|
-
|
|
38
|
+
kind: z.enum(['layout', 'element', 'section']).optional(),
|
|
39
|
+
meta: LIBRARY_ITEM_META_SCHEMA.optional(),
|
|
40
|
+
thumbnail_url: z.string().nullable().optional(),
|
|
41
|
+
library_layout_id: z.string().optional(),
|
|
154
42
|
section_index: z.number().optional(),
|
|
155
43
|
purpose: z.enum(SECTION_PURPOSE_VALUES).optional(),
|
|
156
44
|
element_signature: z.string().optional(),
|
|
@@ -160,24 +48,21 @@ const LIBRARY_ITEM_SCHEMA = z.object({
|
|
|
160
48
|
website: z.string().nullable().optional(),
|
|
161
49
|
has_images: z.boolean().optional(),
|
|
162
50
|
image_count: z.number().optional(),
|
|
163
|
-
|
|
164
|
-
// section/row/column wrappers. Distinct from library_get's
|
|
165
|
-
// `sections[].element_count` (LIBRARY_SECTION_SCHEMA below), which
|
|
166
|
-
// counts ALL descendants including structural nodes — the same
|
|
167
|
-
// section reports a different count from each; do not assume parity.
|
|
168
|
-
element_count: z
|
|
51
|
+
content_element_count: z
|
|
169
52
|
.number()
|
|
170
53
|
.optional()
|
|
171
|
-
.describe('CONTENT elements
|
|
172
|
-
'
|
|
54
|
+
.describe('CONTENT elements in this section — the section/row/column wrappers are NOT counted. ' +
|
|
55
|
+
'library_get\'s sections[].element_count and library_insert\'s element_count count ' +
|
|
56
|
+
'every node including those wrappers, so the same section reads higher there.'),
|
|
173
57
|
});
|
|
174
58
|
export const LIBRARY_BROWSE_OUTPUT_SCHEMA = z.object({
|
|
175
59
|
items: z.array(LIBRARY_ITEM_SCHEMA),
|
|
176
60
|
total: z.number(),
|
|
177
|
-
// `CatalogService::browse()` always emits the key; `null` means "last page".
|
|
178
61
|
next_cursor: z.string().nullable(),
|
|
62
|
+
api_key_configured: z.boolean().optional(),
|
|
63
|
+
images_note: z.string().optional(),
|
|
64
|
+
detail_note: z.string().optional(),
|
|
179
65
|
});
|
|
180
|
-
// ─── library_get (Task 3) ───────────────────────────────────────────
|
|
181
66
|
const LIBRARY_SECTION_SCHEMA = z.object({
|
|
182
67
|
index: z.number(),
|
|
183
68
|
name: z.string(),
|
|
@@ -186,28 +71,14 @@ const LIBRARY_SECTION_SCHEMA = z.object({
|
|
|
186
71
|
});
|
|
187
72
|
export const LIBRARY_GET_OUTPUT_SCHEMA = z.object({
|
|
188
73
|
id: z.string(),
|
|
189
|
-
// Task 8b (Section Corpus Phase 1, PLAN-GAP fix): "section" is a valid
|
|
190
|
-
// resolved kind — `LibraryController::getSection()` (PHP) always
|
|
191
|
-
// returns a literal `kind: "section"` for a composite-id fetch.
|
|
192
74
|
kind: z.enum(['layout', 'element', 'section']),
|
|
193
75
|
name: z.string(),
|
|
194
76
|
version: z.string(),
|
|
195
|
-
// The raw fetched tree (`{type, children, version, images, ...}` for a
|
|
196
|
-
// layout; a single element node for a preset) — an arbitrary JSON
|
|
197
|
-
// object, not a fixed shape.
|
|
198
77
|
tree: z.record(z.string(), z.unknown()),
|
|
199
|
-
// Top-level `tree.children` (layouts only — always `[]` for a preset,
|
|
200
|
-
// even when its own tree happens to carry `children`).
|
|
201
78
|
sections: z.array(LIBRARY_SECTION_SCHEMA),
|
|
202
79
|
images_manifest_count: z.number(),
|
|
80
|
+
element_count_note: z.string().optional(),
|
|
203
81
|
});
|
|
204
|
-
// ─── library_insert (Task 6) ─────────────────────────────────────────
|
|
205
|
-
/**
|
|
206
|
-
* Mirrors `LibraryInsertService::insert()`'s `$target` union (PHP) exactly:
|
|
207
|
-
* a fresh page, a splice into an EXISTING template/page (layout/section on
|
|
208
|
-
* the layout root, or a preset on a Column), or a splice into a
|
|
209
|
-
* sublayout/fragment element.
|
|
210
|
-
*/
|
|
211
82
|
const LIBRARY_INSERT_TARGET_SCHEMA = z.union([
|
|
212
83
|
z.object({
|
|
213
84
|
new_page: z.object({
|
|
@@ -242,33 +113,49 @@ const LIBRARY_INSERT_TARGET_SCHEMA = z.union([
|
|
|
242
113
|
'or a Column for an element preset (kind:"element").'),
|
|
243
114
|
}),
|
|
244
115
|
]);
|
|
245
|
-
/**
|
|
246
|
-
* A DOWNGRADED UI-invariant violation (2026-07-12 fix, "trusted
|
|
247
|
-
* library-insert downgrades enum/type violations to warnings"): a pure
|
|
248
|
-
* value-shape mismatch (`invalid_enum_value` / `type_mismatch`) that
|
|
249
|
-
* YOOtheme's OWN factory content carried but the site's installed YOOtheme
|
|
250
|
-
* schema doesn't declare — a version-skew artifact, allowed through rather
|
|
251
|
-
* than hard-rejecting the whole insert. Every OTHER violation class still
|
|
252
|
-
* 422s (`library.ui_invariant_violation`) exactly as before.
|
|
253
|
-
*/
|
|
254
116
|
const LIBRARY_INSERT_WARNING_SCHEMA = z.object({
|
|
255
117
|
field: z.string(),
|
|
256
118
|
code: z.string(),
|
|
257
119
|
message: z.string(),
|
|
258
120
|
});
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
121
|
+
const LIBRARY_INSERT_IMAGE_FAILURE_SCHEMA = z.object({
|
|
122
|
+
prop_path: z.string(),
|
|
123
|
+
exception_class: z.string(),
|
|
124
|
+
message: z.string(),
|
|
125
|
+
});
|
|
126
|
+
export const LIBRARY_INSERT_OUTPUT_SCHEMA = z
|
|
127
|
+
.object({
|
|
128
|
+
layout_id: z.string(),
|
|
129
|
+
inserted_path: z.string().optional(),
|
|
130
|
+
inserted_paths: z.array(z.string()).optional(),
|
|
131
|
+
element_count: z.number().optional(),
|
|
132
|
+
images_mode: z.enum(['placeholder', 'curated', 'as_saved']).optional(),
|
|
133
|
+
images_resolved: z
|
|
134
|
+
.object({
|
|
265
135
|
downloaded: z.number(),
|
|
266
136
|
placeholders: z.number(),
|
|
267
|
-
})
|
|
268
|
-
|
|
269
|
-
|
|
137
|
+
})
|
|
138
|
+
.optional(),
|
|
139
|
+
etag: z.string().optional(),
|
|
140
|
+
new_etag: z.string().optional(),
|
|
141
|
+
frontend_url: z.string().nullable().optional(),
|
|
142
|
+
images_note: z.string().optional(),
|
|
143
|
+
media_note: z.string().optional(),
|
|
270
144
|
warnings: z.array(LIBRARY_INSERT_WARNING_SCHEMA).optional(),
|
|
271
|
-
|
|
145
|
+
image_import_failures: z.array(LIBRARY_INSERT_IMAGE_FAILURE_SCHEMA).optional(),
|
|
146
|
+
root_type_inserted: z.string().optional(),
|
|
147
|
+
would_remove_count: z.number().optional(),
|
|
148
|
+
would_remove_types: z.array(z.string()).optional(),
|
|
149
|
+
...CONFIRM_PREVIEW_FIELDS,
|
|
150
|
+
})
|
|
151
|
+
.superRefine((v, ctx) => requireOnExecutedWrite(v, ctx, [
|
|
152
|
+
'inserted_path',
|
|
153
|
+
'element_count',
|
|
154
|
+
'images_mode',
|
|
155
|
+
'images_resolved',
|
|
156
|
+
'new_etag',
|
|
157
|
+
'frontend_url',
|
|
158
|
+
]));
|
|
272
159
|
const LIBRARY_TABLE_COLUMNS = [
|
|
273
160
|
{ key: 'id', label: 'ID', width: 10, llmOnly: true },
|
|
274
161
|
{ key: 'name', label: 'NAME', width: 26 },
|
|
@@ -283,17 +170,6 @@ function asNullableString(v) {
|
|
|
283
170
|
function isSectionPurpose(v) {
|
|
284
171
|
return typeof v === 'string' && SECTION_PURPOSE_VALUES.includes(v);
|
|
285
172
|
}
|
|
286
|
-
/**
|
|
287
|
-
* Task 8b (Section Corpus Phase 1, PLAN-GAP fix) — `requestedKind` is the
|
|
288
|
-
* caller's own `library_browse({kind})` argument. It is needed because a
|
|
289
|
-
* `kind="section"` row from `SectionCatalog::mapEntry()` (PHP) carries NO
|
|
290
|
-
* per-row `kind` field at all (unlike layout/element rows, which always do)
|
|
291
|
-
* — the whole response is homogeneous per browse call, since `kind` is a
|
|
292
|
-
* REST-level filter. So: `r.kind` wins when the row DOES carry one
|
|
293
|
-
* (layout/element, and defensively "section" too, should REST ever add it);
|
|
294
|
-
* otherwise fall back to what the caller asked for; otherwise "layout"
|
|
295
|
-
* (today's pre-Task-8b default, byte-unchanged).
|
|
296
|
-
*/
|
|
297
173
|
function mapLibraryItem(raw, requestedKind) {
|
|
298
174
|
if (raw === null || typeof raw !== 'object')
|
|
299
175
|
return undefined;
|
|
@@ -313,15 +189,11 @@ function mapLibraryItem(raw, requestedKind) {
|
|
|
313
189
|
},
|
|
314
190
|
thumbnail_url: asNullableString(r.thumbnail_url),
|
|
315
191
|
};
|
|
316
|
-
// ADDITIVE ONLY: a layout/element row returns exactly `base` — no
|
|
317
|
-
// section keys are ever added, not even as `undefined` — so the
|
|
318
|
-
// pre-Task-8b shape stays byte-unchanged (regression-tested via
|
|
319
|
-
// toStrictEqual in library.test.ts).
|
|
320
192
|
if (kind !== 'section')
|
|
321
193
|
return base;
|
|
322
194
|
return {
|
|
323
195
|
...base,
|
|
324
|
-
|
|
196
|
+
library_layout_id: typeof r.library_layout_id === 'string' ? r.library_layout_id : undefined,
|
|
325
197
|
section_index: typeof r.section_index === 'number' ? r.section_index : undefined,
|
|
326
198
|
purpose: isSectionPurpose(r.purpose) ? r.purpose : undefined,
|
|
327
199
|
element_signature: typeof r.element_signature === 'string' ? r.element_signature : undefined,
|
|
@@ -333,41 +205,17 @@ function mapLibraryItem(raw, requestedKind) {
|
|
|
333
205
|
website: asNullableString(r.website),
|
|
334
206
|
has_images: typeof r.has_images === 'boolean' ? r.has_images : undefined,
|
|
335
207
|
image_count: typeof r.image_count === 'number' ? r.image_count : undefined,
|
|
336
|
-
|
|
208
|
+
content_element_count: typeof r.content_element_count === 'number' ? r.content_element_count : undefined,
|
|
337
209
|
};
|
|
338
210
|
}
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
* plugin 1.10.0). On a Joomla site the REST endpoints do not exist, so a
|
|
343
|
-
* library/image call used to return an empty `{items:[],total:0}` (or a bare
|
|
344
|
-
* 404) with no hint about WHY. This returns an explicit
|
|
345
|
-
* `not_supported_on_platform` (501) instead, so the agent stops retrying and
|
|
346
|
-
* knows the capability simply is not there on this platform. Returns `null` on
|
|
347
|
-
* WordPress so the real forward runs.
|
|
348
|
-
*/
|
|
349
|
-
function joomlaNotSupportedGuard(client, toolName, context) {
|
|
350
|
-
if (client.getPlatform().kind !== 'joomla')
|
|
351
|
-
return null;
|
|
352
|
-
return errorResult({
|
|
353
|
-
error: new Error(`${toolName} is not supported on Joomla — the Library + image domain is ` +
|
|
354
|
-
'WordPress-only in plugin 1.10.0.'),
|
|
355
|
-
context: { ...context, platform: 'joomla', error_code: 'not_supported_on_platform', status: 501 },
|
|
356
|
-
hint: 'This capability exists only on WordPress sites. On Joomla, build layouts with ' +
|
|
357
|
-
'the element/page tools; there is no Library catalog or image proxy to browse.',
|
|
358
|
-
});
|
|
359
|
-
}
|
|
211
|
+
export const BROWSE_IMAGES_NOTE = 'Images: an insert uses generic PLACEHOLDERS by default '
|
|
212
|
+
+ '- has_images describes the catalog entry, not the inserted page. Pass '
|
|
213
|
+
+ 'images:"curated" to yootheme_builder_library_insert to download the real photos.';
|
|
360
214
|
export async function handleLibraryBrowse({ client }, { kind, source, topic, type, role, website, element_type, purpose, has_effects, query, limit, cursor, include_meta }) {
|
|
361
|
-
const guard = joomlaNotSupportedGuard(client, 'yootheme_builder_library_browse', { kind: kind ?? 'layout' });
|
|
362
|
-
if (guard !== null)
|
|
363
|
-
return guard;
|
|
364
215
|
try {
|
|
365
216
|
const qs = new URLSearchParams();
|
|
366
217
|
if (kind !== undefined)
|
|
367
218
|
qs.set('kind', kind);
|
|
368
|
-
// Only forward a NON-default source (the PHP controller reads the same
|
|
369
|
-
// absent-param `catalog` default) so a catalog browse is byte-identical
|
|
370
|
-
// to the pre-Task-7 request.
|
|
371
219
|
if (source !== undefined && source !== DEFAULT_LIBRARY_SOURCE)
|
|
372
220
|
qs.set('source', source);
|
|
373
221
|
if (typeof topic === 'string' && topic !== '')
|
|
@@ -398,26 +246,51 @@ export async function handleLibraryBrowse({ client }, { kind, source, topic, typ
|
|
|
398
246
|
.filter((i) => i !== undefined);
|
|
399
247
|
const total = typeof data.total === 'number' ? data.total : items.length;
|
|
400
248
|
const nextCursor = typeof data.next_cursor === 'string' ? data.next_cursor : null;
|
|
249
|
+
const apiKeyConfigured = typeof data.api_key_configured === 'boolean' ? data.api_key_configured : undefined;
|
|
250
|
+
const detailNote = typeof data.detail_note === 'string' && data.detail_note !== '' ? data.detail_note : undefined;
|
|
401
251
|
const rows = items;
|
|
402
252
|
const toolkitResult = budgetedTableResult(rows, {
|
|
403
253
|
columns: [...LIBRARY_TABLE_COLUMNS],
|
|
404
254
|
header: (count) => `${String(count)} of ${String(total)} library items`,
|
|
405
|
-
footer: 'Activate one on a page with yootheme_builder_element_add /
|
|
406
|
-
'or pass `cursor` (next_cursor) for the next page.'
|
|
255
|
+
footer: 'Activate one on a page with yootheme_builder_element_add / page_generate_dynamic, ' +
|
|
256
|
+
'or pass `cursor` (next_cursor) for the next page. ' +
|
|
257
|
+
BROWSE_IMAGES_NOTE,
|
|
407
258
|
includeMeta: include_meta,
|
|
408
259
|
}, RESPONSE_BUDGET.BULKY);
|
|
409
|
-
// HARD CONSTRAINT (Task 8): browse NEVER returns an image content
|
|
410
|
-
// block, even for kind="section" rows that carry a thumbnail_url —
|
|
411
|
-
// tableResult() only ever emits text rows. Use
|
|
412
|
-
// yootheme_builder_library_get's include_thumbnail on a shortlisted
|
|
413
|
-
// id for an actual image.
|
|
414
260
|
return structuredResult(toolkitResult, {
|
|
415
261
|
items,
|
|
416
262
|
total,
|
|
417
263
|
next_cursor: nextCursor,
|
|
264
|
+
images_note: BROWSE_IMAGES_NOTE,
|
|
265
|
+
...(apiKeyConfigured !== undefined ? { api_key_configured: apiKeyConfigured } : {}),
|
|
266
|
+
...(detailNote !== undefined ? { detail_note: detailNote } : {}),
|
|
418
267
|
});
|
|
419
268
|
}
|
|
420
269
|
catch (e) {
|
|
270
|
+
if (isLibraryNotConfigured(e)) {
|
|
271
|
+
return errorResult({
|
|
272
|
+
error: e,
|
|
273
|
+
context: {
|
|
274
|
+
kind: kind ?? 'layout',
|
|
275
|
+
source: source ?? DEFAULT_LIBRARY_SOURCE,
|
|
276
|
+
error_code: LIBRARY_NOT_CONFIGURED,
|
|
277
|
+
...libraryUnavailableContext(e),
|
|
278
|
+
},
|
|
279
|
+
hint: LIBRARY_NOT_CONFIGURED_HINT,
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
if (isLibraryUpstreamUnavailable(e)) {
|
|
283
|
+
return errorResult({
|
|
284
|
+
error: e,
|
|
285
|
+
context: {
|
|
286
|
+
kind: kind ?? 'layout',
|
|
287
|
+
source: source ?? DEFAULT_LIBRARY_SOURCE,
|
|
288
|
+
error_code: LIBRARY_UPSTREAM_UNAVAILABLE,
|
|
289
|
+
...libraryUnavailableContext(e),
|
|
290
|
+
},
|
|
291
|
+
hint: LIBRARY_UPSTREAM_UNAVAILABLE_HINT,
|
|
292
|
+
});
|
|
293
|
+
}
|
|
421
294
|
return errorResult({
|
|
422
295
|
error: e,
|
|
423
296
|
context: { kind: kind ?? 'layout', source: source ?? DEFAULT_LIBRARY_SOURCE, topic, type, role, website, element_type, purpose, has_effects, query },
|
|
@@ -425,6 +298,69 @@ export async function handleLibraryBrowse({ client }, { kind, source, topic, typ
|
|
|
425
298
|
});
|
|
426
299
|
}
|
|
427
300
|
}
|
|
301
|
+
const LIBRARY_UPSTREAM_UNAVAILABLE = 'library_upstream_unavailable';
|
|
302
|
+
export const LIBRARY_UPSTREAM_UNAVAILABLE_HINT = 'This is a CATALOG FETCH FAILURE, not an empty catalog. Do NOT retry with different '
|
|
303
|
+
+ 'filters or a different id; the YOOtheme Pro Library never answered with data. An HTTP 401 '
|
|
304
|
+
+ 'here means api.yootheme.com REFUSED the Library API key this site has configured. WHY it '
|
|
305
|
+
+ 'was refused is not visible from here, so do NOT name a cause: report that the key was '
|
|
306
|
+
+ 'refused and let the site owner check it with YOOtheme. The keyless source:"ai" store and '
|
|
307
|
+
+ 'yootheme_builder_page_get_layout still work.';
|
|
308
|
+
function restErrorBody(e) {
|
|
309
|
+
if (!(e instanceof RestError))
|
|
310
|
+
return undefined;
|
|
311
|
+
const body = e.body;
|
|
312
|
+
if (body === null || typeof body !== 'object' || Array.isArray(body))
|
|
313
|
+
return undefined;
|
|
314
|
+
return body;
|
|
315
|
+
}
|
|
316
|
+
const LIBRARY_NOT_CONFIGURED = 'library_api_key_not_configured';
|
|
317
|
+
export const LIBRARY_NOT_CONFIGURED_HINT = 'This is a CONFIGURATION GAP, not an empty catalog and not a refusal: no request reached the '
|
|
318
|
+
+ 'upstream catalog, so nothing here tells you what it holds. Do NOT retry with different '
|
|
319
|
+
+ 'filters, and do NOT assume the customer forgot to enter a key. Run yootheme_builder_diagnose '
|
|
320
|
+
+ 'and read library_key.state first: it separates "no key entered", which the customer fixes in '
|
|
321
|
+
+ 'the YOOtheme Customizer theme settings, from a lookup that found no YOOtheme update site at '
|
|
322
|
+
+ 'all, where there is nothing to fix in the key field. Its hint names who has to act. Without a '
|
|
323
|
+
+ 'key you can still BROWSE the bundled section index (library_browse with kind:"section", '
|
|
324
|
+
+ 'including its filters), read the keyless source:"ai" store, and use '
|
|
325
|
+
+ 'yootheme_builder_page_get_layout. What the key unlocks is kind:"layout"/"element" browsing '
|
|
326
|
+
+ 'plus library_get detail and library_insert for every kind.';
|
|
327
|
+
function isLibraryUpstreamUnavailable(e) {
|
|
328
|
+
return libraryCodeMatches(e, LIBRARY_UPSTREAM_UNAVAILABLE);
|
|
329
|
+
}
|
|
330
|
+
function isLibraryNotConfigured(e) {
|
|
331
|
+
return libraryCodeMatches(e, LIBRARY_NOT_CONFIGURED);
|
|
332
|
+
}
|
|
333
|
+
function libraryCodeMatches(e, code) {
|
|
334
|
+
if (e instanceof RestError && typeof e.code === 'string' && e.code.endsWith(code)) {
|
|
335
|
+
return true;
|
|
336
|
+
}
|
|
337
|
+
const body = restErrorBody(e);
|
|
338
|
+
if (body === undefined)
|
|
339
|
+
return false;
|
|
340
|
+
if (body.error_code === code)
|
|
341
|
+
return true;
|
|
342
|
+
const data = body.data;
|
|
343
|
+
return (data !== null
|
|
344
|
+
&& typeof data === 'object'
|
|
345
|
+
&& !Array.isArray(data)
|
|
346
|
+
&& data.error_code === code);
|
|
347
|
+
}
|
|
348
|
+
function libraryUnavailableContext(e) {
|
|
349
|
+
const body = restErrorBody(e);
|
|
350
|
+
if (body === undefined)
|
|
351
|
+
return {};
|
|
352
|
+
const data = body.data !== null && typeof body.data === 'object' && !Array.isArray(body.data)
|
|
353
|
+
? body.data
|
|
354
|
+
: {};
|
|
355
|
+
const out = {};
|
|
356
|
+
const upstream = body.upstream_status ?? data.upstream_status;
|
|
357
|
+
if (typeof upstream === 'number')
|
|
358
|
+
out.upstream_status = upstream;
|
|
359
|
+
const configured = body.api_key_configured ?? data.api_key_configured;
|
|
360
|
+
if (typeof configured === 'boolean')
|
|
361
|
+
out.api_key_configured = configured;
|
|
362
|
+
return out;
|
|
363
|
+
}
|
|
428
364
|
function mapLibrarySection(raw) {
|
|
429
365
|
if (raw === null || typeof raw !== 'object')
|
|
430
366
|
return undefined;
|
|
@@ -437,14 +373,6 @@ function mapLibrarySection(raw) {
|
|
|
437
373
|
}
|
|
438
374
|
return { index: r.index, name: r.name, path: r.path, element_count: r.element_count };
|
|
439
375
|
}
|
|
440
|
-
/**
|
|
441
|
-
* Task 8 (Section Corpus Phase 1) — the REST `/library/get` response's
|
|
442
|
-
* `thumbnail` field (present ONLY when `include_thumbnail=true` was passed
|
|
443
|
-
* AND the server-side fetch succeeded — see `LibraryController::attachThumbnail()`).
|
|
444
|
-
* A missing/malformed field (either key absent, or either value not a
|
|
445
|
-
* non-empty string) is treated as "no thumbnail" — the caller never emits a
|
|
446
|
-
* broken image block.
|
|
447
|
-
*/
|
|
448
376
|
function mapThumbnail(raw) {
|
|
449
377
|
if (raw === null || typeof raw !== 'object')
|
|
450
378
|
return undefined;
|
|
@@ -455,15 +383,76 @@ function mapThumbnail(raw) {
|
|
|
455
383
|
return undefined;
|
|
456
384
|
return { mime_type: r.mime_type, data_base64: r.data_base64 };
|
|
457
385
|
}
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
386
|
+
function asPlainRecord(value) {
|
|
387
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value)
|
|
388
|
+
? value
|
|
389
|
+
: {};
|
|
390
|
+
}
|
|
391
|
+
function collectElementTypeCounts(node, counts) {
|
|
392
|
+
const n = asPlainRecord(node);
|
|
393
|
+
if (typeof n.type === 'string' && n.type !== '') {
|
|
394
|
+
counts.set(n.type, (counts.get(n.type) ?? 0) + 1);
|
|
395
|
+
}
|
|
396
|
+
if (Array.isArray(n.children)) {
|
|
397
|
+
for (const child of n.children)
|
|
398
|
+
collectElementTypeCounts(child, counts);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
function elementTypeCounts(tree) {
|
|
402
|
+
const counts = new Map();
|
|
403
|
+
collectElementTypeCounts(tree, counts);
|
|
404
|
+
return Object.fromEntries([...counts.entries()].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])));
|
|
405
|
+
}
|
|
406
|
+
function encodedLength(payload) {
|
|
407
|
+
return JSON.stringify(payload).length;
|
|
408
|
+
}
|
|
409
|
+
function truncatedHint(sectionCount) {
|
|
410
|
+
const sectionAdvice = sectionCount > 0
|
|
411
|
+
? ' To inspect ONE section instead of the whole item, call this tool again with ' +
|
|
412
|
+
'kind:"section" and an id of "<this id>#<sectionIndex>" (see sections[].index) — a ' +
|
|
413
|
+
'single section is a fraction of the full size.'
|
|
414
|
+
: '';
|
|
415
|
+
const whatSurvived = sectionCount > 0
|
|
416
|
+
? ' What is missing is the ELEMENTS inside the sections; the `sections` list beside ' +
|
|
417
|
+
'this note is complete and unabridged (every section, its name and element count).'
|
|
418
|
+
: '';
|
|
419
|
+
return ('The full tree was replaced by this summary because the payload was too large.' +
|
|
420
|
+
whatSurvived +
|
|
421
|
+
' To INSERT this item you do not need the tree at all — call ' +
|
|
422
|
+
'yootheme_builder_library_insert with the same id directly.' +
|
|
423
|
+
sectionAdvice +
|
|
424
|
+
' Passing a larger max_chars recovers more of the tree.');
|
|
425
|
+
}
|
|
426
|
+
const LIBRARY_GET_DEFAULT_STRUCTURED_BUDGET_CHARS = 20_000;
|
|
427
|
+
function shrinkLibraryGetTree(payload, budget) {
|
|
428
|
+
const bytesFull = encodedLength(payload);
|
|
429
|
+
const tree = asPlainRecord(payload.tree);
|
|
430
|
+
if (bytesFull <= budget)
|
|
431
|
+
return tree;
|
|
432
|
+
const rootType = typeof tree.type === 'string' ? tree.type : undefined;
|
|
433
|
+
const sections = Array.isArray(payload.sections) ? payload.sections : [];
|
|
434
|
+
const counts = elementTypeCounts(tree);
|
|
435
|
+
const summaryTree = {
|
|
436
|
+
...(rootType !== undefined ? { type: rootType } : {}),
|
|
437
|
+
truncated: true,
|
|
438
|
+
bytes_full: bytesFull,
|
|
439
|
+
element_type_counts: counts,
|
|
440
|
+
hint: truncatedHint(sections.length),
|
|
441
|
+
};
|
|
442
|
+
if (encodedLength({ ...payload, tree: summaryTree }) <= budget)
|
|
443
|
+
return summaryTree;
|
|
444
|
+
const top = Object.fromEntries(Object.entries(counts).slice(0, 5));
|
|
445
|
+
return {
|
|
446
|
+
...summaryTree,
|
|
447
|
+
element_type_counts: top,
|
|
448
|
+
element_type_counts_truncated: Object.keys(counts).length > Object.keys(top).length,
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
export async function handleLibraryGet({ client }, { id, kind, source, include_thumbnail, max_chars }) {
|
|
462
452
|
try {
|
|
463
453
|
const qs = new URLSearchParams({ id });
|
|
464
454
|
if (kind !== undefined)
|
|
465
455
|
qs.set('kind', kind);
|
|
466
|
-
// Only forward a NON-default source (see handleLibraryBrowse).
|
|
467
456
|
if (source !== undefined && source !== DEFAULT_LIBRARY_SOURCE)
|
|
468
457
|
qs.set('source', source);
|
|
469
458
|
if (include_thumbnail === true)
|
|
@@ -486,24 +475,58 @@ export async function handleLibraryGet({ client }, { id, kind, source, include_t
|
|
|
486
475
|
sections,
|
|
487
476
|
images_manifest_count: typeof data.images_manifest_count === 'number' ? data.images_manifest_count : 0,
|
|
488
477
|
};
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
478
|
+
const structuredBudget = max_chars ?? LIBRARY_GET_DEFAULT_STRUCTURED_BUDGET_CHARS;
|
|
479
|
+
const finalTree = shrinkLibraryGetTree(payload, structuredBudget);
|
|
480
|
+
const structuredPayload = {
|
|
481
|
+
id: payload.id,
|
|
482
|
+
kind: payload.kind,
|
|
483
|
+
name: payload.name,
|
|
484
|
+
version: payload.version,
|
|
485
|
+
tree: finalTree,
|
|
486
|
+
sections: payload.sections,
|
|
487
|
+
images_manifest_count: payload.images_manifest_count,
|
|
488
|
+
};
|
|
499
489
|
const thumbnail = include_thumbnail === true ? mapThumbnail(data.thumbnail) : undefined;
|
|
500
|
-
const jsonEnvelope = jsonResult(
|
|
490
|
+
const jsonEnvelope = jsonResult(structuredPayload);
|
|
501
491
|
const content = thumbnail !== undefined
|
|
502
492
|
? [...jsonEnvelope.content, { type: 'image', data: thumbnail.data_base64, mimeType: thumbnail.mime_type }]
|
|
503
493
|
: jsonEnvelope.content;
|
|
504
|
-
return structuredResult({ content },
|
|
494
|
+
return structuredResult(applyMaxChars({ content }, max_chars), structuredPayload);
|
|
505
495
|
}
|
|
506
496
|
catch (e) {
|
|
497
|
+
if (isLibraryNotConfigured(e)) {
|
|
498
|
+
return errorResult({
|
|
499
|
+
error: e,
|
|
500
|
+
context: {
|
|
501
|
+
id,
|
|
502
|
+
kind: kind ?? 'layout',
|
|
503
|
+
source: source ?? DEFAULT_LIBRARY_SOURCE,
|
|
504
|
+
error_code: LIBRARY_NOT_CONFIGURED,
|
|
505
|
+
...libraryUnavailableContext(e),
|
|
506
|
+
},
|
|
507
|
+
hint: LIBRARY_NOT_CONFIGURED_HINT,
|
|
508
|
+
});
|
|
509
|
+
}
|
|
510
|
+
if (isLibraryUpstreamUnavailable(e)) {
|
|
511
|
+
const usedDefaultSource = source === undefined || source === DEFAULT_LIBRARY_SOURCE;
|
|
512
|
+
return errorResult({
|
|
513
|
+
error: e,
|
|
514
|
+
context: {
|
|
515
|
+
id,
|
|
516
|
+
kind: kind ?? 'layout',
|
|
517
|
+
source: source ?? DEFAULT_LIBRARY_SOURCE,
|
|
518
|
+
error_code: LIBRARY_UPSTREAM_UNAVAILABLE,
|
|
519
|
+
...libraryUnavailableContext(e),
|
|
520
|
+
},
|
|
521
|
+
hint: usedDefaultSource
|
|
522
|
+
? LIBRARY_UPSTREAM_UNAVAILABLE_HINT
|
|
523
|
+
+ ' If this id came from your OWN store (library_save_as, or '
|
|
524
|
+
+ 'library_browse with source:"ai"), it does not live in the remote '
|
|
525
|
+
+ 'catalog at all: retry with source:"ai". The catalog was queried '
|
|
526
|
+
+ 'because `source` defaults to "catalog".'
|
|
527
|
+
: LIBRARY_UPSTREAM_UNAVAILABLE_HINT,
|
|
528
|
+
});
|
|
529
|
+
}
|
|
507
530
|
return errorResult({
|
|
508
531
|
error: e,
|
|
509
532
|
context: { id, kind: kind ?? 'layout', source: source ?? DEFAULT_LIBRARY_SOURCE },
|
|
@@ -512,15 +535,6 @@ export async function handleLibraryGet({ client }, { id, kind, source, include_t
|
|
|
512
535
|
});
|
|
513
536
|
}
|
|
514
537
|
}
|
|
515
|
-
/**
|
|
516
|
-
* 2026-07-12 fix ("trusted library-insert downgrades enum/type violations to
|
|
517
|
-
* warnings") — maps the REST `warnings[]` array (each `{field, code,
|
|
518
|
-
* message}`) if present. Mirrors the OTHER `map*` helpers in this file:
|
|
519
|
-
* malformed entries are dropped rather than propagated, and an absent/empty
|
|
520
|
-
* source array maps to `undefined` (the key is OMITTED from the result, not
|
|
521
|
-
* emitted as `[]`) — this is the "if present" contract: pre-fix inserts
|
|
522
|
-
* (a REST body carrying no `warnings` key at all) round-trip byte-unchanged.
|
|
523
|
-
*/
|
|
524
538
|
function mapLibraryInsertWarnings(raw) {
|
|
525
539
|
if (!Array.isArray(raw) || raw.length === 0)
|
|
526
540
|
return undefined;
|
|
@@ -535,55 +549,148 @@ function mapLibraryInsertWarnings(raw) {
|
|
|
535
549
|
}
|
|
536
550
|
return warnings.length > 0 ? warnings : undefined;
|
|
537
551
|
}
|
|
552
|
+
function mapLibraryInsertImageFailures(raw) {
|
|
553
|
+
if (!Array.isArray(raw) || raw.length === 0)
|
|
554
|
+
return undefined;
|
|
555
|
+
const failures = [];
|
|
556
|
+
for (const entry of raw) {
|
|
557
|
+
if (entry === null || typeof entry !== 'object')
|
|
558
|
+
continue;
|
|
559
|
+
const f = entry;
|
|
560
|
+
if (typeof f.prop_path !== 'string' || typeof f.exception_class !== 'string')
|
|
561
|
+
continue;
|
|
562
|
+
if (typeof f.message !== 'string')
|
|
563
|
+
continue;
|
|
564
|
+
failures.push({
|
|
565
|
+
prop_path: f.prop_path,
|
|
566
|
+
exception_class: f.exception_class,
|
|
567
|
+
message: f.message,
|
|
568
|
+
});
|
|
569
|
+
}
|
|
570
|
+
return failures.length > 0 ? failures : undefined;
|
|
571
|
+
}
|
|
572
|
+
function isLibraryInsertPreview(raw) {
|
|
573
|
+
return raw !== null && typeof raw === 'object' && raw.preview === true;
|
|
574
|
+
}
|
|
575
|
+
function mapLibraryInsertPreview(r) {
|
|
576
|
+
const types = Array.isArray(r.would_remove_types)
|
|
577
|
+
? r.would_remove_types.filter((t) => typeof t === 'string')
|
|
578
|
+
: undefined;
|
|
579
|
+
return {
|
|
580
|
+
preview: true,
|
|
581
|
+
applied: false,
|
|
582
|
+
requires_confirmation: true,
|
|
583
|
+
requires_confirm: true,
|
|
584
|
+
operation: typeof r.operation === 'string' ? r.operation : 'insert this Library item',
|
|
585
|
+
warning: typeof r.warning === 'string' ? r.warning : '',
|
|
586
|
+
instruction: typeof r.instruction === 'string'
|
|
587
|
+
? r.instruction
|
|
588
|
+
: 'Ask the user to confirm, then call again with `confirm: true`.',
|
|
589
|
+
hint: typeof r.hint === 'string' ? r.hint : '',
|
|
590
|
+
received_confirm: r.received_confirm,
|
|
591
|
+
received_confirm_type: typeof r.received_confirm_type === 'string' ? r.received_confirm_type : 'absent',
|
|
592
|
+
expected_confirm: typeof r.expected_confirm === 'string' ? r.expected_confirm : '',
|
|
593
|
+
...(typeof r.step_reversible === 'boolean' ? { step_reversible: r.step_reversible } : {}),
|
|
594
|
+
layout_id: typeof r.layout_id === 'string' ? r.layout_id : '',
|
|
595
|
+
...(typeof r.etag === 'string' && r.etag !== '' ? { etag: r.etag } : {}),
|
|
596
|
+
...(typeof r.would_remove_count === 'number' ? { would_remove_count: r.would_remove_count } : {}),
|
|
597
|
+
...(types !== undefined ? { would_remove_types: types } : {}),
|
|
598
|
+
};
|
|
599
|
+
}
|
|
538
600
|
function mapLibraryInsertResult(raw) {
|
|
539
601
|
if (raw === null || typeof raw !== 'object')
|
|
540
602
|
return undefined;
|
|
541
603
|
const r = raw;
|
|
542
|
-
if (typeof r.
|
|
604
|
+
if (typeof r.layout_id !== 'string' || typeof r.inserted_path !== 'string')
|
|
543
605
|
return undefined;
|
|
544
606
|
const resolvedRaw = r.images_resolved !== null && typeof r.images_resolved === 'object'
|
|
545
607
|
? r.images_resolved
|
|
546
608
|
: {};
|
|
547
609
|
const warnings = mapLibraryInsertWarnings(r.warnings);
|
|
610
|
+
const imageFailures = mapLibraryInsertImageFailures(r.image_import_failures);
|
|
611
|
+
const mode = r.images_mode === 'curated' || r.images_mode === 'as_saved' ? r.images_mode : 'placeholder';
|
|
612
|
+
const placeholders = typeof resolvedRaw.placeholders === 'number' ? resolvedRaw.placeholders : 0;
|
|
613
|
+
const imagesNote = mode === 'placeholder' && placeholders > 0
|
|
614
|
+
? `${String(placeholders)} image(s) are generic placeholders, not the photos shown on `
|
|
615
|
+
+ 'the catalog thumbnail. The layout renders, but it does not look like its preview. '
|
|
616
|
+
+ 'Re-insert with images:"curated" to download the real images into the Media Library.'
|
|
617
|
+
: undefined;
|
|
548
618
|
return {
|
|
549
|
-
|
|
619
|
+
layout_id: r.layout_id,
|
|
550
620
|
inserted_path: r.inserted_path,
|
|
551
621
|
element_count: typeof r.element_count === 'number' ? r.element_count : 0,
|
|
552
|
-
images_mode:
|
|
622
|
+
images_mode: mode,
|
|
553
623
|
images_resolved: {
|
|
554
624
|
downloaded: typeof resolvedRaw.downloaded === 'number' ? resolvedRaw.downloaded : 0,
|
|
555
|
-
placeholders
|
|
625
|
+
placeholders,
|
|
556
626
|
},
|
|
557
627
|
new_etag: typeof r.new_etag === 'string' ? r.new_etag : '',
|
|
558
628
|
frontend_url: typeof r.frontend_url === 'string' ? r.frontend_url : null,
|
|
629
|
+
...(imagesNote !== undefined ? { images_note: imagesNote } : {}),
|
|
630
|
+
...(typeof r.media_note === 'string' && r.media_note !== '' ? { media_note: r.media_note } : {}),
|
|
559
631
|
...(warnings !== undefined ? { warnings } : {}),
|
|
632
|
+
...(imageFailures !== undefined ? { image_import_failures: imageFailures } : {}),
|
|
633
|
+
root_type_inserted: typeof r.root_type_inserted === 'string' ? r.root_type_inserted : '',
|
|
560
634
|
};
|
|
561
635
|
}
|
|
562
|
-
export async function handleLibraryInsert({ client }, { id, kind, source, target, section_index, images, etag }) {
|
|
563
|
-
const guard = joomlaNotSupportedGuard(client, 'yootheme_builder_library_insert', { id });
|
|
564
|
-
if (guard !== null)
|
|
565
|
-
return guard;
|
|
636
|
+
export async function handleLibraryInsert({ client }, { id, kind, source, target, section_index, images, insert_mode, index, confirm, etag }) {
|
|
566
637
|
const resolvedImages = images ?? 'placeholder';
|
|
567
638
|
try {
|
|
568
639
|
const body = { id, target, images: resolvedImages };
|
|
569
640
|
if (kind !== undefined)
|
|
570
641
|
body.kind = kind;
|
|
571
|
-
// Only forward a NON-default source (see handleLibraryBrowse); the PHP
|
|
572
|
-
// LibraryInsertService reads the same absent-param `catalog` default.
|
|
573
642
|
if (source !== undefined && source !== DEFAULT_LIBRARY_SOURCE)
|
|
574
643
|
body.source = source;
|
|
575
644
|
if (section_index !== undefined)
|
|
576
645
|
body.section_index = section_index;
|
|
646
|
+
if (insert_mode !== undefined)
|
|
647
|
+
body.insert_mode = insert_mode;
|
|
648
|
+
if (index !== undefined)
|
|
649
|
+
body.index = index;
|
|
650
|
+
if (confirm !== undefined)
|
|
651
|
+
body.confirm = confirm;
|
|
577
652
|
if (typeof etag === 'string' && etag !== '')
|
|
578
653
|
body.etag = etag;
|
|
579
654
|
const data = await client.post('/library/insert', { body });
|
|
655
|
+
if (isLibraryInsertPreview(data)) {
|
|
656
|
+
const preview = mapLibraryInsertPreview(data);
|
|
657
|
+
return structuredResult(jsonResult(preview), preview);
|
|
658
|
+
}
|
|
580
659
|
const mapped = mapLibraryInsertResult(data);
|
|
581
660
|
if (mapped === undefined) {
|
|
582
|
-
throw new Error('Malformed /library/insert response — missing
|
|
661
|
+
throw new Error('Malformed /library/insert response — missing layout_id/inserted_path.');
|
|
583
662
|
}
|
|
584
663
|
return structuredResult(jsonResult(mapped), mapped);
|
|
585
664
|
}
|
|
586
665
|
catch (e) {
|
|
666
|
+
if (isLibraryNotConfigured(e)) {
|
|
667
|
+
return errorResult({
|
|
668
|
+
error: e,
|
|
669
|
+
context: {
|
|
670
|
+
id,
|
|
671
|
+
kind: kind ?? 'layout',
|
|
672
|
+
source: source ?? DEFAULT_LIBRARY_SOURCE,
|
|
673
|
+
images: resolvedImages,
|
|
674
|
+
error_code: LIBRARY_NOT_CONFIGURED,
|
|
675
|
+
...libraryUnavailableContext(e),
|
|
676
|
+
},
|
|
677
|
+
hint: LIBRARY_NOT_CONFIGURED_HINT,
|
|
678
|
+
});
|
|
679
|
+
}
|
|
680
|
+
if (isLibraryUpstreamUnavailable(e)) {
|
|
681
|
+
return errorResult({
|
|
682
|
+
error: e,
|
|
683
|
+
context: {
|
|
684
|
+
id,
|
|
685
|
+
kind: kind ?? 'layout',
|
|
686
|
+
source: source ?? DEFAULT_LIBRARY_SOURCE,
|
|
687
|
+
images: resolvedImages,
|
|
688
|
+
error_code: LIBRARY_UPSTREAM_UNAVAILABLE,
|
|
689
|
+
...libraryUnavailableContext(e),
|
|
690
|
+
},
|
|
691
|
+
hint: LIBRARY_UPSTREAM_UNAVAILABLE_HINT,
|
|
692
|
+
});
|
|
693
|
+
}
|
|
587
694
|
return errorResult({
|
|
588
695
|
error: e,
|
|
589
696
|
context: { id, kind: kind ?? 'layout', source: source ?? DEFAULT_LIBRARY_SOURCE, images: resolvedImages },
|
|
@@ -591,26 +698,20 @@ export async function handleLibraryInsert({ client }, { id, kind, source, target
|
|
|
591
698
|
});
|
|
592
699
|
}
|
|
593
700
|
}
|
|
594
|
-
/**
|
|
595
|
-
* PAGES_LIBRARY_MEDIA-4 — status/code-aware catch hint for `library_insert`.
|
|
596
|
-
*
|
|
597
|
-
* The former static hint always said "Verify the id via library_get first" —
|
|
598
|
-
* but a 404 `not_found`/`unavailable` means the catalog item cannot be FETCHED
|
|
599
|
-
* (no Library API key configured, or the id is genuinely absent), so library_get
|
|
600
|
-
* returns an empty success and confirms nothing → a loop. On that class steer
|
|
601
|
-
* to the real fixes (configure the apikey, or use the keyless source:"ai")
|
|
602
|
-
* instead. Keep the target-column / stale-etag guidance for the 422/412 classes
|
|
603
|
-
* they actually describe.
|
|
604
|
-
*/
|
|
605
701
|
function libraryInsertHint(e) {
|
|
606
702
|
if (e instanceof RestError) {
|
|
607
703
|
const code = e.code ?? '';
|
|
608
|
-
if (e
|
|
609
|
-
return
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
704
|
+
if (isLibraryNotConfigured(e)) {
|
|
705
|
+
return LIBRARY_NOT_CONFIGURED_HINT;
|
|
706
|
+
}
|
|
707
|
+
if (isLibraryUpstreamUnavailable(e)) {
|
|
708
|
+
return LIBRARY_UPSTREAM_UNAVAILABLE_HINT;
|
|
709
|
+
}
|
|
710
|
+
if (e.status === 404 || code.includes('not_found')) {
|
|
711
|
+
return ('The Library item could not be fetched (no key configured, or the id is not in ' +
|
|
712
|
+
'the catalog). Do NOT loop on yootheme_builder_library_get. Either configure the ' +
|
|
713
|
+
'YOOtheme Pro Library API key (Customizer, Theme Settings, apikey), or use ' +
|
|
714
|
+
'source:"ai" (your keyless "WootsUp AI Layouts" store).');
|
|
614
715
|
}
|
|
615
716
|
if (e.status === 422) {
|
|
616
717
|
return ('A 422 target_not_a_column means a preset (kind:"element") was pointed at a ' +
|
|
@@ -626,7 +727,6 @@ function libraryInsertHint(e) {
|
|
|
626
727
|
'hull first with yootheme_builder_scaffold_section. A 412 means the etag is stale — ' +
|
|
627
728
|
're-read (get_etag/pages_list) and retry.');
|
|
628
729
|
}
|
|
629
|
-
// ─── library_save_as (Task 7) ────────────────────────────────────────────
|
|
630
730
|
const LIBRARY_SAVE_SOURCE_SCHEMA = z.object({
|
|
631
731
|
template_id: z
|
|
632
732
|
.string()
|
|
@@ -642,6 +742,9 @@ export const LIBRARY_SAVE_AS_OUTPUT_SCHEMA = z.object({
|
|
|
642
742
|
name: z.string(),
|
|
643
743
|
storage: z.string(),
|
|
644
744
|
invalid_source_possible: z.boolean(),
|
|
745
|
+
destination: z.string().optional(),
|
|
746
|
+
kind: z.string().optional(),
|
|
747
|
+
storage_note: z.string().optional(),
|
|
645
748
|
});
|
|
646
749
|
function mapLibrarySaveAsResult(raw) {
|
|
647
750
|
if (raw === null || typeof raw !== 'object')
|
|
@@ -650,25 +753,31 @@ function mapLibrarySaveAsResult(raw) {
|
|
|
650
753
|
if (typeof r.library_id !== 'string' || typeof r.name !== 'string' || typeof r.storage !== 'string') {
|
|
651
754
|
return undefined;
|
|
652
755
|
}
|
|
756
|
+
const storage = r.storage;
|
|
757
|
+
const isOwnStore = storage === 'ai' || storage === 'wootsup-ai-layouts';
|
|
653
758
|
return {
|
|
654
759
|
library_id: r.library_id,
|
|
655
760
|
name: r.name,
|
|
656
|
-
storage
|
|
761
|
+
storage,
|
|
657
762
|
invalid_source_possible: r.invalid_source_possible === true,
|
|
763
|
+
storage_note: isOwnStore
|
|
764
|
+
? 'Saved to your isolated "WootsUp AI Layouts" store, NOT to YOOtheme\'s '
|
|
765
|
+
+ 'built-in My Layouts: it will not appear in the builder\'s Library panel. '
|
|
766
|
+
+ 'Reuse it with library_browse/library_insert using source:"ai", remove it '
|
|
767
|
+
+ 'with library_delete. Saving again with destination:"native" puts a copy '
|
|
768
|
+
+ 'where the builder shows it, but that copy is one-way: no tool here can '
|
|
769
|
+
+ 'find or delete it afterwards.'
|
|
770
|
+
: 'Saved to YOOtheme\'s built-in My Layouts, so the builder shows it under '
|
|
771
|
+
+ 'that panel. This copy is ONE-WAY for this server: library_browse/'
|
|
772
|
+
+ 'library_get/library_insert reach only "catalog" and "ai" entries, and '
|
|
773
|
+
+ 'library_delete removes only "ai" ones, so nothing here can find, reuse '
|
|
774
|
+
+ 'or remove it again. Save it with destination:"ai" as well if you want to '
|
|
775
|
+
+ 'keep working with it from here.',
|
|
658
776
|
};
|
|
659
777
|
}
|
|
660
778
|
export async function handleLibrarySaveAs({ client }, { source, name, destination }) {
|
|
661
|
-
const guard = joomlaNotSupportedGuard(client, 'yootheme_builder_library_save_as', {
|
|
662
|
-
template_id: source.template_id,
|
|
663
|
-
});
|
|
664
|
-
if (guard !== null)
|
|
665
|
-
return guard;
|
|
666
779
|
try {
|
|
667
780
|
const body = { source, name };
|
|
668
|
-
// Only forward a NON-default destination (the PHP MyLayoutsStore reads
|
|
669
|
-
// the same absent-param `ai` default), so an ai-default save is
|
|
670
|
-
// byte-identical to the pre-Task-7 body — only the `native` escape
|
|
671
|
-
// hatch ever appears on the wire.
|
|
672
781
|
if (destination !== undefined && destination !== DEFAULT_SAVE_DESTINATION)
|
|
673
782
|
body.destination = destination;
|
|
674
783
|
const data = await client.post('/library/save', { body });
|
|
@@ -688,12 +797,22 @@ export async function handleLibrarySaveAs({ client }, { source, name, destinatio
|
|
|
688
797
|
});
|
|
689
798
|
}
|
|
690
799
|
}
|
|
691
|
-
|
|
692
|
-
|
|
800
|
+
export const LIBRARY_DELETE_OUTPUT_SCHEMA = z
|
|
801
|
+
.object({
|
|
693
802
|
deleted: z.boolean(),
|
|
694
803
|
id: z.string(),
|
|
695
804
|
deleted_count: z.number(),
|
|
696
805
|
source: z.string(),
|
|
806
|
+
...CONFIRM_PREVIEW_FIELDS,
|
|
807
|
+
})
|
|
808
|
+
.superRefine((v, ctx) => {
|
|
809
|
+
if (v.requires_confirmation === true && v.deleted !== false) {
|
|
810
|
+
ctx.addIssue({
|
|
811
|
+
code: 'custom',
|
|
812
|
+
path: ['deleted'],
|
|
813
|
+
message: 'a confirm preview must report deleted:false — nothing was removed',
|
|
814
|
+
});
|
|
815
|
+
}
|
|
697
816
|
});
|
|
698
817
|
function mapLibraryDeleteResult(raw) {
|
|
699
818
|
if (raw === null || typeof raw !== 'object')
|
|
@@ -709,14 +828,8 @@ function mapLibraryDeleteResult(raw) {
|
|
|
709
828
|
};
|
|
710
829
|
}
|
|
711
830
|
export async function handleLibraryDelete({ client }, { id, source }) {
|
|
712
|
-
const guard = joomlaNotSupportedGuard(client, 'yootheme_builder_library_delete', { id });
|
|
713
|
-
if (guard !== null)
|
|
714
|
-
return guard;
|
|
715
831
|
try {
|
|
716
|
-
const body = { id };
|
|
717
|
-
// Only forward a NON-default source. The PHP controller reads the same
|
|
718
|
-
// absent-param "ai" default; the curated catalog (source:"catalog") is
|
|
719
|
-
// read-only, so forwarding it is what makes the controller answer 403.
|
|
832
|
+
const body = { id, confirm: true };
|
|
720
833
|
if (source !== undefined && source !== 'ai')
|
|
721
834
|
body.source = source;
|
|
722
835
|
const data = await client.post('/library/delete', { body });
|
|
@@ -734,11 +847,6 @@ export async function handleLibraryDelete({ client }, { id, source }) {
|
|
|
734
847
|
});
|
|
735
848
|
}
|
|
736
849
|
}
|
|
737
|
-
/**
|
|
738
|
-
* Status/code-aware catch hint for library_delete. A 404 means the id was
|
|
739
|
-
* already gone (not a failure to act on — the end state is what you wanted); a
|
|
740
|
-
* 403 means a read-only source (only source:"ai" is deletable).
|
|
741
|
-
*/
|
|
742
850
|
function libraryDeleteHint(e) {
|
|
743
851
|
if (e instanceof RestError) {
|
|
744
852
|
if (e.status === 404) {
|
|
@@ -753,57 +861,57 @@ function libraryDeleteHint(e) {
|
|
|
753
861
|
return ('Pass the `id` of an entry from yootheme_builder_library_browse({source:"ai"}). Only ' +
|
|
754
862
|
'source:"ai" entries are deletable — the curated library is read-only.');
|
|
755
863
|
}
|
|
756
|
-
|
|
757
|
-
//
|
|
758
|
-
// Thin GET/POST forwards to `/library/image-search` (Task C1) mirroring
|
|
759
|
-
// library_browse's read-projection pattern, and `/library/image-import`
|
|
760
|
-
// mirroring library_insert's write-forward pattern. Response shapes are
|
|
761
|
-
// pinned by the PHP controller tests
|
|
762
|
-
// (tests/php/unit/Library/LibraryControllerImageSearchTest.php +
|
|
763
|
-
// LibraryControllerImageImportTest.php):
|
|
764
|
-
// GET /library/image-search → {items, total, source, query, hint?}
|
|
765
|
-
// POST /library/image-import → {imported:true, local_url} |
|
|
766
|
-
// {imported:false, reason}
|
|
767
|
-
const IMAGE_SOURCE_VALUES = ['unsplash', 'pexels', 'proimages'];
|
|
768
|
-
/**
|
|
769
|
-
* Cross-source orientation dialect (D1 concern, see the plan doc): the
|
|
770
|
-
* Unsplash proxy accepts "squarish", the Pexels proxy accepts "square" for
|
|
771
|
-
* the SAME concept. `ImageSearchService::buildQueryString()` (PHP) forwards
|
|
772
|
-
* whatever string is supplied verbatim, per-source, with NO translation — so
|
|
773
|
-
* this schema accepts BOTH spellings instead of forcing one source's
|
|
774
|
-
* vocabulary onto the other (which would either reject a legitimate Pexels
|
|
775
|
-
* "square" request or silently mis-filter it).
|
|
776
|
-
*/
|
|
864
|
+
const IMAGE_SOURCE_VALUES = ['unsplash', 'pexels', 'proimages', 'local'];
|
|
777
865
|
const IMAGE_ORIENTATION_VALUES = ['landscape', 'portrait', 'squarish', 'square'];
|
|
866
|
+
const IMAGE_LICENSE_KEYS = {
|
|
867
|
+
license: z.string().nullable().optional(),
|
|
868
|
+
attribution_required: z.boolean().nullable().optional(),
|
|
869
|
+
license_url: z.string().optional(),
|
|
870
|
+
license_note: z.string().optional(),
|
|
871
|
+
};
|
|
778
872
|
const IMAGE_RESULT_SCHEMA = z.object({
|
|
779
873
|
id: z.string(),
|
|
780
874
|
source: z.string(),
|
|
781
875
|
thumb_url: z.string(),
|
|
782
876
|
full_url: z.string(),
|
|
783
|
-
width: z.number(),
|
|
784
|
-
height: z.number(),
|
|
785
|
-
alt: z.string(),
|
|
786
|
-
author: z.string(),
|
|
787
|
-
author_url: z.string(),
|
|
788
|
-
orientation: z.string(),
|
|
877
|
+
width: z.number().nullable(),
|
|
878
|
+
height: z.number().nullable(),
|
|
879
|
+
alt: z.string().optional(),
|
|
880
|
+
author: z.string().optional(),
|
|
881
|
+
author_url: z.string().optional(),
|
|
882
|
+
orientation: z.string().optional(),
|
|
883
|
+
...IMAGE_LICENSE_KEYS,
|
|
789
884
|
});
|
|
790
885
|
export const IMAGE_SEARCH_OUTPUT_SCHEMA = z.object({
|
|
791
886
|
items: z.array(IMAGE_RESULT_SCHEMA),
|
|
792
887
|
total: z.number(),
|
|
793
888
|
source: z.string(),
|
|
794
889
|
query: z.string(),
|
|
795
|
-
// Present ONLY on the empty-result path (no Library API key configured,
|
|
796
|
-
// or genuinely zero matches — the REST layer cannot and must not
|
|
797
|
-
// distinguish the two; see LibraryControllerImageSearchTest).
|
|
798
890
|
hint: z.string().optional(),
|
|
891
|
+
api_key_configured: z.boolean().optional(),
|
|
892
|
+
next_offset: z.number().int().min(0).optional(),
|
|
893
|
+
local_note: z.string().optional(),
|
|
799
894
|
});
|
|
800
895
|
export const IMAGE_IMPORT_OUTPUT_SCHEMA = z.object({
|
|
801
896
|
imported: z.boolean(),
|
|
802
|
-
// Present only when imported:true.
|
|
803
897
|
local_url: z.string().optional(),
|
|
804
|
-
// Present only when imported:false (a caught importer failure — never a 500).
|
|
805
898
|
reason: z.string().optional(),
|
|
899
|
+
failure: z.enum(['rejected', 'unavailable']).optional(),
|
|
900
|
+
media_note: z.string().optional(),
|
|
901
|
+
...IMAGE_LICENSE_KEYS,
|
|
806
902
|
});
|
|
903
|
+
function pickImageLicense(r) {
|
|
904
|
+
return {
|
|
905
|
+
...(typeof r.license === 'string' || r.license === null ? { license: r.license } : {}),
|
|
906
|
+
...(typeof r.attribution_required === 'boolean' || r.attribution_required === null
|
|
907
|
+
? { attribution_required: r.attribution_required }
|
|
908
|
+
: {}),
|
|
909
|
+
...(typeof r.license_url === 'string' ? { license_url: r.license_url } : {}),
|
|
910
|
+
...(typeof r.license_note === 'string' && r.license_note !== ''
|
|
911
|
+
? { license_note: r.license_note }
|
|
912
|
+
: {}),
|
|
913
|
+
};
|
|
914
|
+
}
|
|
807
915
|
function mapImageResult(raw) {
|
|
808
916
|
if (raw === null || typeof raw !== 'object')
|
|
809
917
|
return undefined;
|
|
@@ -815,12 +923,13 @@ function mapImageResult(raw) {
|
|
|
815
923
|
source: typeof r.source === 'string' ? r.source : '',
|
|
816
924
|
thumb_url: typeof r.thumb_url === 'string' ? r.thumb_url : '',
|
|
817
925
|
full_url: typeof r.full_url === 'string' ? r.full_url : '',
|
|
818
|
-
width: typeof r.width === 'number' ? r.width :
|
|
819
|
-
height: typeof r.height === 'number' ? r.height :
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
926
|
+
width: typeof r.width === 'number' ? r.width : null,
|
|
927
|
+
height: typeof r.height === 'number' ? r.height : null,
|
|
928
|
+
...(typeof r.alt === 'string' ? { alt: r.alt } : {}),
|
|
929
|
+
...(typeof r.author === 'string' ? { author: r.author } : {}),
|
|
930
|
+
...(typeof r.author_url === 'string' ? { author_url: r.author_url } : {}),
|
|
931
|
+
...(typeof r.orientation === 'string' ? { orientation: r.orientation } : {}),
|
|
932
|
+
...pickImageLicense(r),
|
|
824
933
|
};
|
|
825
934
|
}
|
|
826
935
|
const IMAGE_TABLE_COLUMNS = [
|
|
@@ -828,42 +937,44 @@ const IMAGE_TABLE_COLUMNS = [
|
|
|
828
937
|
{ key: 'dimensions', label: 'SIZE', width: 10 },
|
|
829
938
|
{ key: 'author', label: 'AUTHOR', width: 18 },
|
|
830
939
|
{ key: 'orientation', label: 'ORIENT', width: 10 },
|
|
831
|
-
// llmOnly: the copyable next-call value (yootheme_builder_image_import's
|
|
832
|
-
// `url` arg) — excluded from the Rich Card UI (too long for a table
|
|
833
|
-
// cell) but present in the ASCII text the LLM reads. Always present in
|
|
834
|
-
// structuredContent.items[].full_url regardless of table membership.
|
|
835
940
|
{ key: 'full_url', label: 'FULL_URL', width: 50, llmOnly: true },
|
|
836
941
|
];
|
|
837
|
-
export async function handleImageSearch({ client }, { source, query, per_page, orientation, color, include_meta }) {
|
|
838
|
-
const guard = joomlaNotSupportedGuard(client, 'yootheme_builder_image_search', { query });
|
|
839
|
-
if (guard !== null)
|
|
840
|
-
return guard;
|
|
942
|
+
export async function handleImageSearch({ client }, { source, query, per_page, orientation, color, offset, include_meta }) {
|
|
841
943
|
const resolvedSource = source ?? 'unsplash';
|
|
842
944
|
try {
|
|
843
945
|
const qs = new URLSearchParams();
|
|
844
946
|
qs.set('source', resolvedSource);
|
|
845
|
-
|
|
947
|
+
if (typeof query === 'string' && query !== '')
|
|
948
|
+
qs.set('query', query);
|
|
846
949
|
if (per_page !== undefined)
|
|
847
950
|
qs.set('per_page', String(per_page));
|
|
848
951
|
if (typeof orientation === 'string' && orientation !== '')
|
|
849
952
|
qs.set('orientation', orientation);
|
|
850
953
|
if (typeof color === 'string' && color !== '')
|
|
851
954
|
qs.set('color', color);
|
|
955
|
+
if (offset !== undefined)
|
|
956
|
+
qs.set('offset', String(offset));
|
|
852
957
|
const data = await client.get(`/library/image-search?${qs.toString()}`);
|
|
853
958
|
const rawItems = Array.isArray(data.items) ? data.items : [];
|
|
854
959
|
const items = rawItems.map(mapImageResult).filter((i) => i !== undefined);
|
|
855
960
|
const total = typeof data.total === 'number' ? data.total : items.length;
|
|
856
961
|
const resolvedResponseSource = typeof data.source === 'string' ? data.source : resolvedSource;
|
|
857
|
-
const resolvedQuery = typeof data.query === 'string' ? data.query : query;
|
|
962
|
+
const resolvedQuery = typeof data.query === 'string' ? data.query : (query ?? '');
|
|
858
963
|
const hint = typeof data.hint === 'string' ? data.hint : undefined;
|
|
964
|
+
const nextOffset = typeof data.next_offset === 'number' && Number.isInteger(data.next_offset) && data.next_offset >= 0
|
|
965
|
+
? data.next_offset
|
|
966
|
+
: undefined;
|
|
967
|
+
const localNote = typeof data.local_note === 'string' && data.local_note !== '' ? data.local_note : undefined;
|
|
968
|
+
const apiKeyConfigured = typeof data.api_key_configured === 'boolean' ? data.api_key_configured : undefined;
|
|
859
969
|
const rows = items.map((i) => ({
|
|
860
970
|
...i,
|
|
861
|
-
dimensions: `${String(i.width)}x${String(i.height)}`,
|
|
971
|
+
dimensions: `${i.width === null ? '?' : String(i.width)}x${i.height === null ? '?' : String(i.height)}`,
|
|
862
972
|
}));
|
|
863
973
|
const toolkitResult = budgetedTableResult(rows, {
|
|
864
974
|
columns: [...IMAGE_TABLE_COLUMNS],
|
|
865
975
|
header: (count) => `${String(count)} of ${String(total)} ${resolvedResponseSource} image${total === 1 ? '' : 's'} for "${resolvedQuery}"`,
|
|
866
976
|
footer: hint
|
|
977
|
+
?? localNote
|
|
867
978
|
?? 'Import a chosen row\'s full_url with yootheme_builder_image_import, then bind '
|
|
868
979
|
+ 'the returned local_url via element_update_settings.',
|
|
869
980
|
includeMeta: include_meta,
|
|
@@ -874,18 +985,108 @@ export async function handleImageSearch({ client }, { source, query, per_page, o
|
|
|
874
985
|
source: resolvedResponseSource,
|
|
875
986
|
query: resolvedQuery,
|
|
876
987
|
...(hint !== undefined ? { hint } : {}),
|
|
988
|
+
...(nextOffset !== undefined ? { next_offset: nextOffset } : {}),
|
|
989
|
+
...(localNote !== undefined ? { local_note: localNote } : {}),
|
|
990
|
+
...(apiKeyConfigured !== undefined ? { api_key_configured: apiKeyConfigured } : {}),
|
|
877
991
|
});
|
|
878
992
|
}
|
|
879
993
|
catch (e) {
|
|
994
|
+
if (isImageSearchNotConfigured(e)) {
|
|
995
|
+
return errorResult({
|
|
996
|
+
error: e,
|
|
997
|
+
context: {
|
|
998
|
+
source: resolvedSource,
|
|
999
|
+
query,
|
|
1000
|
+
error_code: IMAGE_SEARCH_NOT_CONFIGURED,
|
|
1001
|
+
...imageSearchUnavailableContext(e),
|
|
1002
|
+
},
|
|
1003
|
+
hint: IMAGE_SEARCH_NOT_CONFIGURED_HINT,
|
|
1004
|
+
});
|
|
1005
|
+
}
|
|
1006
|
+
if (isImageSearchUnavailable(e)) {
|
|
1007
|
+
return errorResult({
|
|
1008
|
+
error: e,
|
|
1009
|
+
context: {
|
|
1010
|
+
source: resolvedSource,
|
|
1011
|
+
query,
|
|
1012
|
+
error_code: 'image_search_unavailable',
|
|
1013
|
+
...imageSearchUnavailableContext(e),
|
|
1014
|
+
},
|
|
1015
|
+
hint: IMAGE_SEARCH_UNAVAILABLE_HINT,
|
|
1016
|
+
});
|
|
1017
|
+
}
|
|
880
1018
|
return errorResult({
|
|
881
1019
|
error: e,
|
|
882
1020
|
context: { source: resolvedSource, query, per_page, orientation, color },
|
|
883
|
-
hint: '
|
|
884
|
-
+ 'theme apikey library_browse/insert use). Run yootheme_builder_diagnose to verify auth. '
|
|
1021
|
+
hint: 'Run yootheme_builder_diagnose to verify auth and connectivity, then retry. '
|
|
885
1022
|
+ 'yootheme_builder_image_import still works with a direct image URL (no key needed).',
|
|
886
1023
|
});
|
|
887
1024
|
}
|
|
888
1025
|
}
|
|
1026
|
+
const IMAGE_SEARCH_UNAVAILABLE = 'image_search_unavailable';
|
|
1027
|
+
export const IMAGE_SEARCH_UNAVAILABLE_HINT = 'This is a SEARCH FAILURE, not an empty result. Do NOT retry with a broader query; the '
|
|
1028
|
+
+ 'provider never answered with results. An HTTP 401 here means the YOOtheme Pro Library API '
|
|
1029
|
+
+ 'key this site has configured was REFUSED for this call. WHY it was refused is not visible '
|
|
1030
|
+
+ 'from here, so do NOT name a cause: report that the key was refused and let the site owner '
|
|
1031
|
+
+ 'check it with YOOtheme. yootheme_builder_image_import still works with a direct Unsplash, '
|
|
1032
|
+
+ 'Pexels or YOOtheme library image URL (no key needed).';
|
|
1033
|
+
const IMAGE_SEARCH_NOT_CONFIGURED = 'image_search_api_key_not_configured';
|
|
1034
|
+
export const IMAGE_SEARCH_NOT_CONFIGURED_HINT = 'This is a CONFIGURATION GAP, not an empty result: no YOOtheme Pro Library API key could be '
|
|
1035
|
+
+ 'read on this site, so no search request was made and nothing is known about what the '
|
|
1036
|
+
+ 'provider holds. Do NOT retry with a broader query. Run yootheme_builder_diagnose and read '
|
|
1037
|
+
+ 'library_key.state before naming a fix: it separates "no key entered", which the customer '
|
|
1038
|
+
+ 'fixes in the YOOtheme Customizer theme settings, from a lookup that found no YOOtheme update '
|
|
1039
|
+
+ 'site at all, where there is nothing to fix in the key field. yootheme_builder_image_import '
|
|
1040
|
+
+ 'still works with a direct Unsplash, Pexels or YOOtheme library image URL (no key needed).';
|
|
1041
|
+
function imageSearchErrorBody(e) {
|
|
1042
|
+
if (!(e instanceof RestError))
|
|
1043
|
+
return undefined;
|
|
1044
|
+
const body = e.body;
|
|
1045
|
+
if (body === null || typeof body !== 'object' || Array.isArray(body))
|
|
1046
|
+
return undefined;
|
|
1047
|
+
return body;
|
|
1048
|
+
}
|
|
1049
|
+
function isImageSearchUnavailable(e) {
|
|
1050
|
+
return imageSearchCodeMatches(e, IMAGE_SEARCH_UNAVAILABLE);
|
|
1051
|
+
}
|
|
1052
|
+
function isImageSearchNotConfigured(e) {
|
|
1053
|
+
return imageSearchCodeMatches(e, IMAGE_SEARCH_NOT_CONFIGURED);
|
|
1054
|
+
}
|
|
1055
|
+
function imageSearchCodeMatches(e, code) {
|
|
1056
|
+
if (e instanceof RestError && typeof e.code === 'string' && e.code.endsWith(code)) {
|
|
1057
|
+
return true;
|
|
1058
|
+
}
|
|
1059
|
+
const body = imageSearchErrorBody(e);
|
|
1060
|
+
if (body === undefined)
|
|
1061
|
+
return false;
|
|
1062
|
+
if (body.error_code === code)
|
|
1063
|
+
return true;
|
|
1064
|
+
const data = body.data;
|
|
1065
|
+
return (data !== null
|
|
1066
|
+
&& typeof data === 'object'
|
|
1067
|
+
&& !Array.isArray(data)
|
|
1068
|
+
&& data.error_code === code);
|
|
1069
|
+
}
|
|
1070
|
+
function imageSearchUnavailableContext(e) {
|
|
1071
|
+
const body = imageSearchErrorBody(e);
|
|
1072
|
+
if (body === undefined)
|
|
1073
|
+
return {};
|
|
1074
|
+
const data = body.data !== null && typeof body.data === 'object' && !Array.isArray(body.data)
|
|
1075
|
+
? body.data
|
|
1076
|
+
: {};
|
|
1077
|
+
const out = {};
|
|
1078
|
+
const upstream = body.upstream_status ?? data.upstream_status;
|
|
1079
|
+
if (typeof upstream === 'number')
|
|
1080
|
+
out.upstream_status = upstream;
|
|
1081
|
+
const configured = body.api_key_configured ?? data.api_key_configured;
|
|
1082
|
+
if (typeof configured === 'boolean')
|
|
1083
|
+
out.api_key_configured = configured;
|
|
1084
|
+
return out;
|
|
1085
|
+
}
|
|
1086
|
+
const IMAGE_IMPORT_FAILURE_VALUES = ['rejected', 'unavailable'];
|
|
1087
|
+
function mapImageImportFailure(raw) {
|
|
1088
|
+
return IMAGE_IMPORT_FAILURE_VALUES.find((v) => v === raw);
|
|
1089
|
+
}
|
|
889
1090
|
function mapImageImportResult(raw) {
|
|
890
1091
|
if (raw === null || typeof raw !== 'object')
|
|
891
1092
|
return undefined;
|
|
@@ -895,17 +1096,41 @@ function mapImageImportResult(raw) {
|
|
|
895
1096
|
if (r.imported) {
|
|
896
1097
|
if (typeof r.local_url !== 'string' || r.local_url === '')
|
|
897
1098
|
return undefined;
|
|
898
|
-
return {
|
|
1099
|
+
return {
|
|
1100
|
+
imported: true,
|
|
1101
|
+
local_url: r.local_url,
|
|
1102
|
+
...(typeof r.media_note === 'string' && r.media_note !== '' ? { media_note: r.media_note } : {}),
|
|
1103
|
+
...pickImageLicense(r),
|
|
1104
|
+
};
|
|
899
1105
|
}
|
|
1106
|
+
const failure = mapImageImportFailure(r.failure);
|
|
900
1107
|
return {
|
|
901
1108
|
imported: false,
|
|
902
1109
|
reason: typeof r.reason === 'string' && r.reason !== '' ? r.reason : 'Import failed for an unknown reason.',
|
|
1110
|
+
...(failure === undefined ? {} : { failure }),
|
|
903
1111
|
};
|
|
904
1112
|
}
|
|
905
|
-
export async function handleImageImport({ client },
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
1113
|
+
export async function handleImageImport({ client }, args) {
|
|
1114
|
+
if (args.url !== undefined
|
|
1115
|
+
&& args.full_url !== undefined
|
|
1116
|
+
&& args.url !== args.full_url) {
|
|
1117
|
+
return errorResult({
|
|
1118
|
+
error: new Error('The "url" and "full_url" arguments are two names for ONE value and were '
|
|
1119
|
+
+ 'given different ones. Pass only the url to import.'),
|
|
1120
|
+
context: { url: args.url, full_url: args.full_url },
|
|
1121
|
+
hint: 'Drop one of the two. "full_url" is accepted only because that is the name a '
|
|
1122
|
+
+ 'yootheme_builder_image_search row carries.',
|
|
1123
|
+
});
|
|
1124
|
+
}
|
|
1125
|
+
const url = args.url ?? args.full_url;
|
|
1126
|
+
if (url === undefined || url === '') {
|
|
1127
|
+
return errorResult({
|
|
1128
|
+
error: new Error('The "url" argument is required — a full_url from yootheme_builder_image_search '
|
|
1129
|
+
+ 'is accepted under either name.'),
|
|
1130
|
+
context: {},
|
|
1131
|
+
hint: 'Pass a full_url from yootheme_builder_image_search.',
|
|
1132
|
+
});
|
|
1133
|
+
}
|
|
909
1134
|
try {
|
|
910
1135
|
const data = await client.post('/library/image-import', { body: { url } });
|
|
911
1136
|
const mapped = mapImageImportResult(data);
|
|
@@ -915,16 +1140,6 @@ export async function handleImageImport({ client }, { url }) {
|
|
|
915
1140
|
return structuredResult(jsonResult(mapped), mapped);
|
|
916
1141
|
}
|
|
917
1142
|
catch (e) {
|
|
918
|
-
// T3 (2026-07-25, Sheet-0 handover) — the REST route returns the graceful
|
|
919
|
-
// { imported:false, reason } envelope for SSRF-disallowed-host /
|
|
920
|
-
// invalid-url / authz-denied rejections but with a 4xx STATUS, so the
|
|
921
|
-
// client raises a RestError and the raw HTTP error would otherwise hide
|
|
922
|
-
// the reason (the "image_import just 400s with no explanation" report).
|
|
923
|
-
// When the error body IS that graceful envelope, surface it as a normal
|
|
924
|
-
// { imported:false, reason } result — identical to the 200-status
|
|
925
|
-
// graceful paths above — so the agent sees WHY (e.g. "host not on the
|
|
926
|
-
// image-import allowlist") and can recover with an allowlisted full_url
|
|
927
|
-
// from yootheme_builder_image_search.
|
|
928
1143
|
if (e instanceof RestError) {
|
|
929
1144
|
const mapped = mapImageImportResult(e.body);
|
|
930
1145
|
if (mapped !== undefined && !mapped.imported) {
|
|
@@ -936,7 +1151,7 @@ export async function handleImageImport({ client }, { url }) {
|
|
|
936
1151
|
context: { url },
|
|
937
1152
|
hint: 'Verify the url is a reachable http(s) image URL (typically a full_url from '
|
|
938
1153
|
+ 'yootheme_builder_image_search). A read_only_token error means this bearer key '
|
|
939
|
-
+ 'needs the write scope — regenerate it in
|
|
1154
|
+
+ 'needs the write scope — regenerate it in MCP for YOOtheme Pro → Bearer Keys.',
|
|
940
1155
|
});
|
|
941
1156
|
}
|
|
942
1157
|
}
|
|
@@ -945,12 +1160,15 @@ export function buildLibraryTools(pool) {
|
|
|
945
1160
|
defineTool({
|
|
946
1161
|
name: 'yootheme_builder_library_browse',
|
|
947
1162
|
description: 'Browse the YOOtheme Pro Library catalog: pre-designed page LAYOUTS, element ' +
|
|
948
|
-
'PRESETS, or purpose-classified SECTIONS
|
|
949
|
-
'`
|
|
950
|
-
'
|
|
951
|
-
'
|
|
952
|
-
'
|
|
953
|
-
'`total:0`
|
|
1163
|
+
'PRESETS, or purpose-classified SECTIONS. Filter: `kind`, `topic`, `type`/`role`, ' +
|
|
1164
|
+
'`element_type`, `purpose`, `query`. A section id is the composite ' +
|
|
1165
|
+
'"<layoutId>#<sectionIndex>" — feeds library_get/insert. ' +
|
|
1166
|
+
'`source`: "catalog" (default) or "ai" — YOUR saved entries from ' +
|
|
1167
|
+
'library_save_as, which library_delete removes again — both gateway-only, ' +
|
|
1168
|
+
'call via yootheme_builder_advanced({tool, arguments}). `total:0` = the ' +
|
|
1169
|
+
'catalog matched nothing; a read with no data answers ' +
|
|
1170
|
+
'`library_upstream_unavailable` or `library_api_key_not_configured`, never an ' +
|
|
1171
|
+
'empty page.',
|
|
954
1172
|
inputSchema: {
|
|
955
1173
|
site_id: SITE_ID_SCHEMA,
|
|
956
1174
|
kind: z
|
|
@@ -985,7 +1203,13 @@ export function buildLibraryTools(pool) {
|
|
|
985
1203
|
.string()
|
|
986
1204
|
.min(1)
|
|
987
1205
|
.optional()
|
|
988
|
-
.describe('
|
|
1206
|
+
.describe('Filters on how the SOURCE demo website filed this layout in ITS OWN menu ' +
|
|
1207
|
+
'(e.g. "Home", "Contact", "Services") — sourced verbatim from the YOOtheme ' +
|
|
1208
|
+
'Pro Library catalog, NOT a classification of what the layout actually ' +
|
|
1209
|
+
'builds. A layout can carry a role unrelated to its content if that is ' +
|
|
1210
|
+
'where the source site\'s own navigation put it; prefer `query` (name ' +
|
|
1211
|
+
'match) or browsing without this filter when the exact wording is ' +
|
|
1212
|
+
'unknown. Only applies when kind="layout". Exact match.'),
|
|
989
1213
|
website: z
|
|
990
1214
|
.string()
|
|
991
1215
|
.min(1)
|
|
@@ -999,7 +1223,11 @@ export function buildLibraryTools(pool) {
|
|
|
999
1223
|
purpose: z
|
|
1000
1224
|
.enum(SECTION_PURPOSE_VALUES)
|
|
1001
1225
|
.optional()
|
|
1002
|
-
.describe('Section-purpose filter (e.g. "hero", "features", "cta"). Only applies when
|
|
1226
|
+
.describe('Section-purpose filter (e.g. "hero", "features", "cta"). Only applies when ' +
|
|
1227
|
+
'kind="section". Exact match. BROAD buckets, not topics: hundreds of sections ' +
|
|
1228
|
+
'share one, none names a specific subject, so the closest-looking value is a ' +
|
|
1229
|
+
'guess. For a subject ("services") use `query` instead - it matches the ' +
|
|
1230
|
+
'section NAME.'),
|
|
1003
1231
|
has_effects: z
|
|
1004
1232
|
.boolean()
|
|
1005
1233
|
.optional()
|
|
@@ -1029,13 +1257,12 @@ export function buildLibraryTools(pool) {
|
|
|
1029
1257
|
}),
|
|
1030
1258
|
defineTool({
|
|
1031
1259
|
name: 'yootheme_builder_library_get',
|
|
1032
|
-
description: 'Fetch
|
|
1033
|
-
'
|
|
1034
|
-
'
|
|
1035
|
-
'
|
|
1036
|
-
'
|
|
1037
|
-
'
|
|
1038
|
-
'Read-only; does not insert/bind.',
|
|
1260
|
+
description: 'Fetch ONE Library layout or element preset by `id` — call after ' +
|
|
1261
|
+
'library_browse (items[].id feeds this). `include_thumbnail:true` adds an ' +
|
|
1262
|
+
'image (costs tokens). This DETAIL endpoint checks the key, browse does not: no ' +
|
|
1263
|
+
'key gives 503 library_api_key_not_configured, a refused key 503 ' +
|
|
1264
|
+
'library_upstream_unavailable. Report the refusal, not a cause. `source`: ' +
|
|
1265
|
+
'"catalog" (default) or "ai" for your own library_save_as (gateway-only) entries.',
|
|
1039
1266
|
inputSchema: {
|
|
1040
1267
|
site_id: SITE_ID_SCHEMA,
|
|
1041
1268
|
id: z
|
|
@@ -1054,6 +1281,15 @@ export function buildLibraryTools(pool) {
|
|
|
1054
1281
|
.describe('Which library the `id` belongs to: "catalog" (remote YOOtheme Pro Library, ' +
|
|
1055
1282
|
'default) or "ai" (your OWN "WootsUp AI Layouts"). Must match the source the ' +
|
|
1056
1283
|
'id came from in yootheme_builder_library_browse.'),
|
|
1284
|
+
max_chars: MAX_CHARS.describe('Cap (chars) on this result. On THIS tool it ALSO bounds structuredContent — ' +
|
|
1285
|
+
'an oversized `tree` is replaced by a compact summary inside the same key ' +
|
|
1286
|
+
'(element_type_counts, truncated:true, bytes_full, hint) instead of riding ' +
|
|
1287
|
+
'through whole; small items are unaffected. Applies even when omitted ' +
|
|
1288
|
+
'(default safety cap: 20000 chars). Over the plugin-served endpoint both ' +
|
|
1289
|
+
'legs already carry the same shortened payload. ' +
|
|
1290
|
+
'Raising it stops helping at the limit your own client puts on a single tool ' +
|
|
1291
|
+
'result, which this server cannot see. ' +
|
|
1292
|
+
'Out of range, the cap that actually applied comes back as `max_chars`.'),
|
|
1057
1293
|
include_thumbnail: z
|
|
1058
1294
|
.boolean()
|
|
1059
1295
|
.default(false)
|
|
@@ -1069,13 +1305,14 @@ export function buildLibraryTools(pool) {
|
|
|
1069
1305
|
}),
|
|
1070
1306
|
defineTool({
|
|
1071
1307
|
name: 'yootheme_builder_library_insert',
|
|
1072
|
-
description: 'Insert a
|
|
1073
|
-
'
|
|
1074
|
-
'
|
|
1075
|
-
'
|
|
1076
|
-
'
|
|
1077
|
-
'
|
|
1078
|
-
'
|
|
1308
|
+
description: 'Insert a Library item onto a page — call after library_get/browse. `target`: ' +
|
|
1309
|
+
'new_page, {template_id,element_path?} (splices onto the layout root, or a ' +
|
|
1310
|
+
'preset onto a Column), or sublayout. A preset (kind:"element") MUST target a ' +
|
|
1311
|
+
'Column (422 otherwise) — scaffold one with scaffold_section. `images`: ' +
|
|
1312
|
+
'"placeholder" (default) or "curated" (downloads to Media Library). Placement: ' +
|
|
1313
|
+
'`insert_mode` for layouts, `index` for presets. `etag` ' +
|
|
1314
|
+
'locks (412 if stale). `source`: "catalog" (default) or "ai" to insert your own ' +
|
|
1315
|
+
'saved entry.',
|
|
1079
1316
|
inputSchema: {
|
|
1080
1317
|
site_id: SITE_ID_SCHEMA,
|
|
1081
1318
|
id: z
|
|
@@ -1105,29 +1342,42 @@ export function buildLibraryTools(pool) {
|
|
|
1105
1342
|
.enum(['placeholder', 'curated'])
|
|
1106
1343
|
.default('placeholder')
|
|
1107
1344
|
.describe('"placeholder" (default, safe, no network I/O) or "curated" (downloads + sideloads every unique image into the Media Library).'),
|
|
1108
|
-
|
|
1109
|
-
.
|
|
1345
|
+
insert_mode: z
|
|
1346
|
+
.enum(['replace', 'top', 'bottom'])
|
|
1347
|
+
.default('replace')
|
|
1348
|
+
.describe('How the composed content joins the target\'s EXISTING children — mirrors the ' +
|
|
1349
|
+
'Library dialog\'s own picker. "replace" (default) drops them; "top"/"bottom" ' +
|
|
1350
|
+
'prepend/append. Ignored for target:new_page (a fresh page starts empty). ' +
|
|
1351
|
+
'REFUSED for kind:"element" — a preset takes `index`.'),
|
|
1352
|
+
index: z
|
|
1353
|
+
.number()
|
|
1354
|
+
.int()
|
|
1355
|
+
.min(0)
|
|
1110
1356
|
.optional()
|
|
1111
|
-
.describe('
|
|
1357
|
+
.describe('kind:"element" ONLY (refused otherwise): where the preset lands among the ' +
|
|
1358
|
+
'Column\'s children. 0 = first, omit to append, past-end clamps — as in ' +
|
|
1359
|
+
'element_add. A preset never replaces what the Column holds.'),
|
|
1360
|
+
confirm: z
|
|
1361
|
+
.boolean()
|
|
1362
|
+
.optional()
|
|
1363
|
+
.describe('Set true to proceed when insert_mode:"replace" would remove the target\'s ' +
|
|
1364
|
+
'existing content. Omit for a preview naming what would be lost; "top"/' +
|
|
1365
|
+
'"bottom", an empty target and kind:"element" (a preset only ever adds) ' +
|
|
1366
|
+
'never need it. ' +
|
|
1367
|
+
HOST_PROMPT_PROMISE),
|
|
1368
|
+
etag: ETAG,
|
|
1112
1369
|
},
|
|
1113
1370
|
outputSchema: LIBRARY_INSERT_OUTPUT_SCHEMA,
|
|
1114
|
-
annotations:
|
|
1371
|
+
annotations: destructive('Insert Layout Library Item'),
|
|
1115
1372
|
handler: async ({ site_id, ...rest }) => withWriteSiteRetry(pool, site_id, 'yootheme_builder_library_insert', async (client, site) => withSiteMeta(await handleLibraryInsert({ client }, rest), site)),
|
|
1116
1373
|
}),
|
|
1117
|
-
// Task 7 (Layout Library, Spec #7) — ADVANCED tool (deliberately NOT
|
|
1118
|
-
// added to essentials.ts/ESSENTIAL_TOOLS): reachable ONLY via
|
|
1119
|
-
// yootheme_builder_advanced({tool, arguments}), never a first-class
|
|
1120
|
-
// tools/list entry. See the module docblock's "Tiering" note.
|
|
1121
1374
|
defineTool({
|
|
1122
1375
|
name: 'yootheme_builder_library_save_as',
|
|
1123
1376
|
description: 'Save a page or subtree into a reusable library — the REVERSE of ' +
|
|
1124
|
-
'
|
|
1125
|
-
'
|
|
1126
|
-
'
|
|
1127
|
-
'
|
|
1128
|
-
'(YOOtheme\'s built-in My Layouts). Returns `{library_id, name, ' +
|
|
1129
|
-
'storage, invalid_source_possible}` (the last flags bound sources ' +
|
|
1130
|
-
'that may need re-binding after reload). Advanced/gateway-only.',
|
|
1377
|
+
'library_insert. `source`: {template_id, element_path?} (omit element_path to ' +
|
|
1378
|
+
'save the WHOLE page). `destination`: "ai" (default — your isolated "WootsUp AI ' +
|
|
1379
|
+
'Layouts", reusable via library_browse source:"ai") or "native" (YOOtheme\'s ' +
|
|
1380
|
+
'built-in My Layouts).',
|
|
1131
1381
|
inputSchema: {
|
|
1132
1382
|
site_id: SITE_ID_SCHEMA,
|
|
1133
1383
|
source: LIBRARY_SAVE_SOURCE_SCHEMA.describe('The page/subtree to save.'),
|
|
@@ -1135,10 +1385,6 @@ export function buildLibraryTools(pool) {
|
|
|
1135
1385
|
.string()
|
|
1136
1386
|
.min(1)
|
|
1137
1387
|
.describe('Display name for the new library entry (e.g. "Hero Section").'),
|
|
1138
|
-
// PAGES_LIBRARY_MEDIA-15: `.optional()` (not `.default('ai')`) so
|
|
1139
|
-
// the generated schema does NOT advertise a REQUIRED field that
|
|
1140
|
-
// also carries a default. The handler applies the `ai` default
|
|
1141
|
-
// when omitted (it only forwards a NON-default `native`).
|
|
1142
1388
|
destination: z
|
|
1143
1389
|
.enum(SAVE_DESTINATION_VALUES)
|
|
1144
1390
|
.optional()
|
|
@@ -1148,31 +1394,15 @@ export function buildLibraryTools(pool) {
|
|
|
1148
1394
|
'(the escape hatch: save into YOOtheme\'s built-in "My Layouts").'),
|
|
1149
1395
|
},
|
|
1150
1396
|
outputSchema: LIBRARY_SAVE_AS_OUTPUT_SCHEMA,
|
|
1151
|
-
// PAGES_LIBRARY_MEDIA-15: title names the DEFAULT destination (the AI
|
|
1152
|
-
// Layouts store), not "My Layouts" (which is the non-default `native`).
|
|
1153
1397
|
annotations: creating('Save to AI Layouts library'),
|
|
1154
1398
|
handler: async ({ site_id, ...rest }) => withWriteSiteRetry(pool, site_id, 'yootheme_builder_library_save_as', async (client, site) => withSiteMeta(await handleLibrarySaveAs({ client }, rest), site)),
|
|
1155
1399
|
}),
|
|
1156
|
-
// PAGES_LIBRARY_MEDIA-16 — ADVANCED tool (deliberately NOT added to
|
|
1157
|
-
// essentials.ts/ESSENTIAL_TOOLS): reachable ONLY via
|
|
1158
|
-
// yootheme_builder_advanced({tool, arguments}). The INVERSE of
|
|
1159
|
-
// library_save_as. Annotated destructive() — it IRREVERSIBLY removes a
|
|
1160
|
-
// saved AI-Layouts entry (a layout the user deliberately saved via
|
|
1161
|
-
// library_save_as; there is no undo). This matches the codebase's own
|
|
1162
|
-
// "irreversible → destructive → host confirm-gate" precedent:
|
|
1163
|
-
// element_delete + pages_delete are destructive (audit F9), and
|
|
1164
|
-
// clean_implode_directives was re-classified mutating→destructive in
|
|
1165
|
-
// Wave H5 for the same reason (cannot be restored). Deleting saved user
|
|
1166
|
-
// data should fire the host's destructive-approval gate, not slip
|
|
1167
|
-
// through as an ordinary mutating write.
|
|
1168
1400
|
defineTool({
|
|
1169
1401
|
name: 'yootheme_builder_library_delete',
|
|
1170
|
-
description: 'Delete ONE saved entry from your "WootsUp AI Layouts" store (
|
|
1171
|
-
'
|
|
1172
|
-
'
|
|
1173
|
-
'
|
|
1174
|
-
'returns a 403. IRREVERSIBLE — requires confirm:true. Returns `{deleted, id, deleted_count, ' +
|
|
1175
|
-
'source}`; an unknown id returns a 404. Advanced/gateway-only.',
|
|
1402
|
+
description: 'Delete ONE saved entry from your "WootsUp AI Layouts" store (library_save_as ' +
|
|
1403
|
+
'writes into it) — pass the `id` from a library_browse(source:"ai") row. ' +
|
|
1404
|
+
'`source` defaults to "ai", the ONLY deletable store; the curated library is ' +
|
|
1405
|
+
'read-only, source:"catalog" returns 403. IRREVERSIBLE — requires confirm:true.',
|
|
1176
1406
|
inputSchema: {
|
|
1177
1407
|
site_id: SITE_ID_SCHEMA,
|
|
1178
1408
|
id: z
|
|
@@ -1186,9 +1416,6 @@ export function buildLibraryTools(pool) {
|
|
|
1186
1416
|
.describe('Which library to delete from. Only "ai" (your OWN "WootsUp AI Layouts") is ' +
|
|
1187
1417
|
'deletable; "catalog" (the curated remote YOOtheme Pro Library) is read-only ' +
|
|
1188
1418
|
'and returns a 403. Omit to default to "ai".'),
|
|
1189
|
-
// Destructive-tool confirm gate (matches element_delete / pages_delete):
|
|
1190
|
-
// z.literal(true) makes the MCP boundary REJECT any call without
|
|
1191
|
-
// confirm:true, so an irreversible delete can never fire by accident.
|
|
1192
1419
|
confirm: z
|
|
1193
1420
|
.literal(true)
|
|
1194
1421
|
.describe('Required — must be `true`. Deleting a saved AI-Layouts entry is IRREVERSIBLE ' +
|
|
@@ -1198,27 +1425,33 @@ export function buildLibraryTools(pool) {
|
|
|
1198
1425
|
annotations: destructive('Delete AI Layouts entry'),
|
|
1199
1426
|
handler: async ({ site_id, ...rest }) => withWriteSiteRetry(pool, site_id, 'yootheme_builder_library_delete', async (client, site) => withSiteMeta(await handleLibraryDelete({ client }, rest), site)),
|
|
1200
1427
|
}),
|
|
1201
|
-
// Task D1 (Image Sourcing) — read-only forward, mirrors library_browse.
|
|
1202
1428
|
defineTool({
|
|
1203
1429
|
name: 'yootheme_builder_image_search',
|
|
1204
|
-
description: 'Search
|
|
1205
|
-
+ '
|
|
1206
|
-
+ '
|
|
1207
|
-
+ '
|
|
1208
|
-
+ '
|
|
1209
|
-
+ '
|
|
1430
|
+
description: 'Search on-topic photos (Unsplash/Pexels), YOOtheme Pro Images, or (source:"local") '
|
|
1431
|
+
+ 'this site\'s own media library. Rows carry `full_url` (feeds image_import) plus '
|
|
1432
|
+
+ 'dimensions/author. `total:0` is the ONLY shape meaning the provider matched '
|
|
1433
|
+
+ 'nothing; a search that never happened answers an `error_code` instead. No '
|
|
1434
|
+
+ 'apikey gives `image_search_api_key_not_configured`; a provider that REFUSES '
|
|
1435
|
+
+ 'gives `error_code: image_search_unavailable`, which no broader query can fix. '
|
|
1436
|
+
+ 'A key can be CONFIGURED and still be REFUSED (401): report that the upstream '
|
|
1437
|
+
+ 'refused it, never why (source:"local" is exempt — see its field description). '
|
|
1438
|
+
+ 'Works on WordPress and Joomla.',
|
|
1210
1439
|
inputSchema: {
|
|
1211
1440
|
site_id: SITE_ID_SCHEMA,
|
|
1212
1441
|
source: z
|
|
1213
1442
|
.enum(IMAGE_SOURCE_VALUES)
|
|
1214
1443
|
.default('unsplash')
|
|
1215
1444
|
.describe('Image source. unsplash/pexels = real photography; proimages = YOOtheme '
|
|
1216
|
-
+ 'Pro curated decorative/background assets, not topical photography
|
|
1445
|
+
+ 'Pro curated decorative/background assets, not topical photography; '
|
|
1446
|
+
+ 'local = this site\'s own media library — no key, no image_import '
|
|
1447
|
+
+ '(bind full_url directly), paginated via `offset`/`next_offset`.'),
|
|
1217
1448
|
query: z
|
|
1218
1449
|
.string()
|
|
1219
1450
|
.min(1)
|
|
1451
|
+
.optional()
|
|
1220
1452
|
.describe('Search terms, e.g. "coffee roasting", "modern architecture interior". '
|
|
1221
|
-
+ 'Search deliberately by industry + tone, not a generic term like "business".'
|
|
1453
|
+
+ 'Search deliberately by industry + tone, not a generic term like "business". '
|
|
1454
|
+
+ 'Optional only for source:"local" (lists without one); the other three require it.'),
|
|
1222
1455
|
per_page: z
|
|
1223
1456
|
.number()
|
|
1224
1457
|
.int()
|
|
@@ -1229,40 +1462,59 @@ export function buildLibraryTools(pool) {
|
|
|
1229
1462
|
orientation: z
|
|
1230
1463
|
.enum(IMAGE_ORIENTATION_VALUES)
|
|
1231
1464
|
.optional()
|
|
1232
|
-
.describe('landscape for heroes/banners, portrait for cards.
|
|
1233
|
-
+ '
|
|
1234
|
-
+ '
|
|
1465
|
+
.describe('landscape for heroes/banners, portrait for cards. Each square spelling '
|
|
1466
|
+
+ 'belongs to ONE source: "squarish" to unsplash, "square" to pexels. '
|
|
1467
|
+
+ 'Crossing them fails two different ways, and only one is visible: '
|
|
1468
|
+
+ '"square" on unsplash is refused upstream (HTTP 400, '
|
|
1469
|
+
+ 'image_search_unavailable), while "squarish" on pexels is silently '
|
|
1470
|
+
+ 'ignored and you get unfiltered rows back. Even on its own source '
|
|
1471
|
+
+ '"squarish" is approximate: unsplash still returns landscape and '
|
|
1472
|
+
+ 'portrait rows for it. Ignored for proimages and local.'),
|
|
1235
1473
|
color: z
|
|
1236
1474
|
.string()
|
|
1237
1475
|
.min(1)
|
|
1238
1476
|
.optional()
|
|
1239
|
-
.describe('Optional upstream color filter (e.g. "black_and_white"). Passed through as-is.'),
|
|
1477
|
+
.describe('Optional upstream color filter (e.g. "black_and_white"). Passed through as-is; ignored for local.'),
|
|
1478
|
+
offset: z
|
|
1479
|
+
.number()
|
|
1480
|
+
.int()
|
|
1481
|
+
.min(0)
|
|
1482
|
+
.optional()
|
|
1483
|
+
.describe('source:"local" only: pass back a prior response\'s `next_offset` to continue. '
|
|
1484
|
+
+ 'Ignored by the three proxy sources.'),
|
|
1240
1485
|
include_meta: INCLUDE_META_SCHEMA,
|
|
1241
1486
|
},
|
|
1242
1487
|
outputSchema: IMAGE_SEARCH_OUTPUT_SCHEMA,
|
|
1243
1488
|
annotations: readOnly('Search Images'),
|
|
1244
1489
|
handler: async ({ site_id, ...rest }) => withSiteRetry(pool, site_id, async (client, site) => withSiteMeta(await handleImageSearch({ client }, rest), site)),
|
|
1245
1490
|
}),
|
|
1246
|
-
// Task D1 (Image Sourcing) — write forward, mirrors library_insert.
|
|
1247
|
-
// mutating() (writes to the Media Library) but NOT destructive() — it
|
|
1248
|
-
// only creates, never deletes (Global Constraints, plan doc).
|
|
1249
1491
|
defineTool({
|
|
1250
1492
|
name: 'yootheme_builder_image_import',
|
|
1251
|
-
description: 'Sideload a photo into the site\'s
|
|
1252
|
-
+ '
|
|
1253
|
-
+ '
|
|
1254
|
-
+ '
|
|
1255
|
-
+ '
|
|
1256
|
-
|
|
1257
|
-
|
|
1493
|
+
description: 'Sideload a photo into the site\'s Media Library. Only Unsplash, Pexels and the '
|
|
1494
|
+
+ 'YOOtheme library CDN are accepted; other hosts are refused. Returns '
|
|
1495
|
+
+ '`{imported:true, local_url}`; shape is platform-specific: WordPress leads '
|
|
1496
|
+
+ 'with a slash (/wp-content/uploads/...), Joomla does NOT (images/yootheme/...). '
|
|
1497
|
+
+ 'Bind it VERBATIM via element_update_settings({props:{image: local_url}, ' +
|
|
1498
|
+
'merge:true}). Give the address as `url` OR as `full_url`, the field name an ' +
|
|
1499
|
+
'image_search row carries; one of the two is required. Identical re-imports ' +
|
|
1500
|
+
'reuse the existing file in the current upload folder. Failures return ' +
|
|
1501
|
+
'`{imported:false, reason, failure}`; `unavailable` = no url works now.',
|
|
1258
1502
|
inputSchema: {
|
|
1259
1503
|
site_id: SITE_ID_SCHEMA,
|
|
1260
1504
|
url: z
|
|
1261
1505
|
.string()
|
|
1262
1506
|
.url()
|
|
1507
|
+
.optional()
|
|
1263
1508
|
.describe('The image URL to sideload (typically a full_url from '
|
|
1264
1509
|
+ 'yootheme_builder_image_search). Downloads it into the site Media '
|
|
1265
1510
|
+ 'Library and returns a local URL to bind via element_update_settings.'),
|
|
1511
|
+
full_url: z
|
|
1512
|
+
.string()
|
|
1513
|
+
.url()
|
|
1514
|
+
.optional()
|
|
1515
|
+
.describe('Second name for `url` — the field name an image_search row carries, so a '
|
|
1516
|
+
+ 'row can be passed on unchanged. Pass ONE of the two; two different '
|
|
1517
|
+
+ 'values are refused rather than reconciled.'),
|
|
1266
1518
|
},
|
|
1267
1519
|
outputSchema: IMAGE_IMPORT_OUTPUT_SCHEMA,
|
|
1268
1520
|
annotations: mutating('Import Image'),
|