@wootsup/yt-builder-mcp 1.11.0 → 2.0.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 +131 -33
- package/bin/node-floor.d.ts +28 -0
- package/bin/node-floor.js +123 -0
- package/bin/yt-builder-mcp.js +52 -11
- package/dist/auth.d.ts +3 -121
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +40 -170
- 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 +9 -41
- 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 +33 -54
- 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 +45 -54
- 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 +0 -22
- 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 +0 -18
- 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 +0 -33
- 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 +0 -137
- package/dist/clients/codex-cli.js.map +1 -1
- package/dist/clients/continue.d.ts +0 -10
- package/dist/clients/continue.d.ts.map +1 -1
- package/dist/clients/continue.js +0 -26
- 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 +0 -11
- 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 +0 -17
- 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 +0 -150
- package/dist/clients/index.d.ts.map +1 -1
- package/dist/clients/index.js +0 -112
- 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 +0 -22
- 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 +0 -18
- 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 +26 -37
- package/dist/diagnostics/diagnose-network-error.js.map +1 -1
- package/dist/diagnostics/doctor.d.ts +13 -65
- package/dist/diagnostics/doctor.d.ts.map +1 -1
- package/dist/diagnostics/doctor.js +46 -70
- 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 +0 -32
- 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 +18 -16
- 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 +10 -174
- 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 +5 -46
- 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/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 +107 -203
- 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 +119 -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 +70 -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 +161 -173
- 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 +0 -36
- package/dist/setup-wizard-defaults.d.ts.map +1 -1
- package/dist/setup-wizard-defaults.js +0 -75
- 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 +16 -209
- 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.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 +68 -80
- 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 +1 -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 -80
- 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 +84 -0
- package/dist/skill-instructions.js.map +1 -0
- package/dist/skill-loader.d.ts +0 -102
- package/dist/skill-loader.d.ts.map +1 -1
- package/dist/skill-loader.js +4 -142
- 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 +16 -0
- package/dist/tools/budgeted-table.d.ts.map +1 -0
- package/dist/tools/budgeted-table.js +203 -0
- package/dist/tools/budgeted-table.js.map +1 -0
- 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 +171 -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 +42 -43
- package/dist/tools/format/health-format.d.ts.map +1 -1
- package/dist/tools/format/health-format.js +32 -55
- package/dist/tools/format/health-format.js.map +1 -1
- package/dist/tools/format/inspection-format.d.ts +4 -49
- package/dist/tools/format/inspection-format.d.ts.map +1 -1
- package/dist/tools/format/inspection-format.js +46 -114
- 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 +115 -133
- 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 +82 -0
- package/dist/tools/health-output-schemas.d.ts.map +1 -0
- package/dist/tools/health-output-schemas.js +130 -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 +59 -184
- 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 +13 -33
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/inspection.d.ts +21 -17
- package/dist/tools/inspection.d.ts.map +1 -1
- package/dist/tools/inspection.js +431 -268
- package/dist/tools/inspection.js.map +1 -1
- package/dist/tools/library.d.ts +118 -126
- package/dist/tools/library.d.ts.map +1 -1
- package/dist/tools/library.js +723 -456
- 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 +105 -48
- 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 -67
- 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 +3 -0
- package/dist/tools/navigation/builders.d.ts.map +1 -0
- package/dist/tools/navigation/builders.js +25 -0
- package/dist/tools/navigation/builders.js.map +1 -0
- package/dist/tools/navigation/index.d.ts +2 -0
- package/dist/tools/navigation/index.d.ts.map +1 -0
- package/dist/tools/navigation/index.js +2 -0
- package/dist/tools/navigation/index.js.map +1 -0
- package/dist/tools/navigation/schemas.d.ts +16 -0
- package/dist/tools/navigation/schemas.d.ts.map +1 -0
- package/dist/tools/navigation/schemas.js +11 -0
- package/dist/tools/navigation/schemas.js.map +1 -0
- 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 +373 -359
- package/dist/tools/pages/builders.js.map +1 -1
- package/dist/tools/pages/header-transparency.d.ts +34 -0
- package/dist/tools/pages/header-transparency.d.ts.map +1 -0
- package/dist/tools/pages/header-transparency.js +85 -0
- package/dist/tools/pages/header-transparency.js.map +1 -0
- 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 +392 -224
- package/dist/tools/pages/schemas.d.ts.map +1 -1
- package/dist/tools/pages/schemas.js +605 -458
- 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 +29 -104
- 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 +8 -0
- package/dist/tools/response-budget.d.ts.map +1 -0
- package/dist/tools/response-budget.js +7 -0
- package/dist/tools/response-budget.js.map +1 -0
- package/dist/tools/shared-schemas.d.ts +12 -43
- package/dist/tools/shared-schemas.d.ts.map +1 -1
- package/dist/tools/shared-schemas.js +42 -97
- 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 +195 -173
- 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 +19 -194
- package/dist/tools/sparse-fields.d.ts.map +1 -1
- package/dist/tools/sparse-fields.js +212 -196
- 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 -40
- 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 -111
- package/dist/tools/tool-builder/results.d.ts.map +1 -1
- package/dist/tools/tool-builder/results.js +258 -250
- 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 +0 -49
- 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 +4 -5
- package/package.json +5 -5
- package/skills/yt-builder-mcp/SKILL.md +2958 -382
- package/dist/tools/elements/handlers-write.d.ts +0 -70
- package/dist/tools/elements/handlers-write.d.ts.map +0 -1
- package/dist/tools/elements/handlers-write.js +0 -700
- package/dist/tools/elements/handlers-write.js.map +0 -1
- package/dist/tools/elements/handlers.d.ts +0 -125
- package/dist/tools/elements/handlers.d.ts.map +0 -1
- package/dist/tools/elements/handlers.js +0 -571
- 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 -96
- package/dist/tools/elements/schema-validation.d.ts.map +0 -1
- package/dist/tools/elements/schema-validation.js +0 -249
- 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 -324
- 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 -54
- package/dist/tools/local-content/handlers.d.ts.map +0 -1
- package/dist/tools/local-content/handlers.js +0 -316
- 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/pages/handlers-audit.d.ts +0 -129
- package/dist/tools/pages/handlers-audit.d.ts.map +0 -1
- package/dist/tools/pages/handlers-audit.js +0 -704
- package/dist/tools/pages/handlers-audit.js.map +0 -1
- package/dist/tools/pages/handlers-read.d.ts +0 -95
- package/dist/tools/pages/handlers-read.d.ts.map +0 -1
- package/dist/tools/pages/handlers-read.js +0 -929
- 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 -418
- 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 -655
- package/dist/tools/sources/handlers-bind.js.map +0 -1
- package/dist/tools/sources/handlers.d.ts +0 -114
- package/dist/tools/sources/handlers.d.ts.map +0 -1
- package/dist/tools/sources/handlers.js +0 -285
- 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,84 +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
|
-
import { tableResult } from '@getimo/mcp-toolkit';
|
|
67
1
|
import { z } from 'zod';
|
|
68
2
|
import { RestError } from '../errors.js';
|
|
69
3
|
import { withSiteRetry, withWriteSiteRetry } from './pool-resolve-helper.js';
|
|
70
|
-
import {
|
|
4
|
+
import { applyMaxChars, MAX_CHARS } from './sparse-fields.js';
|
|
5
|
+
import { ETAG, INCLUDE_META_SCHEMA, SITE_ID_SCHEMA } from './shared-schemas.js';
|
|
71
6
|
import { creating, defineTool, destructive, errorResult, jsonResult, mutating, readOnly, structuredResult, withSiteMeta, } from './tool-builder.js';
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
* PHP-side `SectionPurposeClassifier` / `SectionIndexEntry` purpose values
|
|
76
|
-
* byte-for-byte (design spec §A, Global Constraints). Only applies when
|
|
77
|
-
* `kind="section"`. Declared here (ahead of `LIBRARY_ITEM_SCHEMA`, which
|
|
78
|
-
* consumes it for the browse row's `purpose` field) rather than further
|
|
79
|
-
* down where the original Task 8 pass left it, since Task 8b (this file)
|
|
80
|
-
* now needs it at both the item-schema AND the browse-filter-schema sites.
|
|
81
|
-
*/
|
|
7
|
+
import { budgetedTableResult } from "./budgeted-table.js";
|
|
8
|
+
import { COMPACT_DEFAULT, RESPONSE_BUDGET } from "./response-budget.js";
|
|
9
|
+
import { CONFIRM_PREVIEW_FIELDS, HOST_PROMPT_PROMISE, requireOnExecutedWrite } from './tool-builder/confirm-preview.js';
|
|
82
10
|
export const SECTION_PURPOSE_VALUES = [
|
|
83
11
|
'hero',
|
|
84
12
|
'features',
|
|
@@ -93,39 +21,10 @@ export const SECTION_PURPOSE_VALUES = [
|
|
|
93
21
|
'logos',
|
|
94
22
|
'content',
|
|
95
23
|
];
|
|
96
|
-
/**
|
|
97
|
-
* Track 1 Task 7 (WootsUp AI Layouts) — the `source` axis for
|
|
98
|
-
* browse/get/insert: `"catalog"` (the remote YOOtheme Pro Library, the
|
|
99
|
-
* DEFAULT) or `"ai"` (the plugin's OWN isolated "WootsUp AI Layouts" store,
|
|
100
|
-
* saved via library_save_as, no apikey). Mirrors the PHP-side
|
|
101
|
-
* `LibrarySourceRegistry` keys byte-for-byte.
|
|
102
|
-
*/
|
|
103
24
|
const LIBRARY_SOURCE_VALUES = ['catalog', 'ai'];
|
|
104
|
-
/**
|
|
105
|
-
* The wire default for the `source` axis. It is NEVER forwarded (a catalog
|
|
106
|
-
* browse/get/insert is byte-identical to the pre-Task-7 request — the PHP
|
|
107
|
-
* controller reads the SAME absent-param default), so only `"ai"` ever
|
|
108
|
-
* appears on the wire. Mirrors `LibrarySourceRegistry::DEFAULT_SOURCE`.
|
|
109
|
-
*/
|
|
110
25
|
const DEFAULT_LIBRARY_SOURCE = 'catalog';
|
|
111
|
-
/**
|
|
112
|
-
* Track 1 Task 7 (WootsUp AI Layouts) — the `destination` axis for
|
|
113
|
-
* library_save_as: `"ai"` (save INTO the isolated AI store, the DEFAULT) or
|
|
114
|
-
* `"native"` (the escape hatch — save into YOOtheme's native "My Layouts").
|
|
115
|
-
*/
|
|
116
26
|
const SAVE_DESTINATION_VALUES = ['ai', 'native'];
|
|
117
|
-
/**
|
|
118
|
-
* The wire default for the `destination` axis — the AI store. NEVER forwarded
|
|
119
|
-
* (an ai-default save is byte-identical to the pre-Task-7 body; the PHP
|
|
120
|
-
* `MyLayoutsStore` reads the SAME absent-param `ai` default), so only
|
|
121
|
-
* `"native"` ever appears on the wire.
|
|
122
|
-
*/
|
|
123
27
|
const DEFAULT_SAVE_DESTINATION = 'ai';
|
|
124
|
-
/**
|
|
125
|
-
* Mirrors `CatalogService::mapItem()` (PHP): every key is always PRESENT
|
|
126
|
-
* (never omitted) but each value may be `null` when the raw catalog item
|
|
127
|
-
* did not carry that taxonomy field — hence `.nullable()`, not `.optional()`.
|
|
128
|
-
*/
|
|
129
28
|
const LIBRARY_ITEM_META_SCHEMA = z.object({
|
|
130
29
|
topic: z.string().nullable(),
|
|
131
30
|
type: z.string().nullable(),
|
|
@@ -133,23 +32,13 @@ const LIBRARY_ITEM_META_SCHEMA = z.object({
|
|
|
133
32
|
website: z.string().nullable(),
|
|
134
33
|
image: z.string().nullable(),
|
|
135
34
|
});
|
|
136
|
-
/**
|
|
137
|
-
* Task 8b (Section Corpus Phase 1, PLAN-GAP fix) — a `kind="section"` browse
|
|
138
|
-
* row (`SectionCatalog::mapEntry()`, PHP) carries a DIFFERENT shape than a
|
|
139
|
-
* layout/element row: no `meta` wrapper, TOP-LEVEL `topic`/`type`/`website`,
|
|
140
|
-
* plus `layout_id`/`section_index`/`purpose`/`element_signature`/
|
|
141
|
-
* `visual_effects`/`has_images`/`image_count`/`element_count`. All of these
|
|
142
|
-
* are `.optional()` — additive, never present on a layout/element row — so a
|
|
143
|
-
* strict-outputSchema host still accepts the pre-existing byte-unchanged
|
|
144
|
-
* layout/element shape.
|
|
145
|
-
*/
|
|
146
35
|
const LIBRARY_ITEM_SCHEMA = z.object({
|
|
147
36
|
id: z.string(),
|
|
148
37
|
name: z.string(),
|
|
149
|
-
kind: z.enum(['layout', 'element', 'section']),
|
|
150
|
-
meta: LIBRARY_ITEM_META_SCHEMA,
|
|
151
|
-
thumbnail_url: z.string().nullable(),
|
|
152
|
-
|
|
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(),
|
|
153
42
|
section_index: z.number().optional(),
|
|
154
43
|
purpose: z.enum(SECTION_PURPOSE_VALUES).optional(),
|
|
155
44
|
element_signature: z.string().optional(),
|
|
@@ -159,24 +48,21 @@ const LIBRARY_ITEM_SCHEMA = z.object({
|
|
|
159
48
|
website: z.string().nullable().optional(),
|
|
160
49
|
has_images: z.boolean().optional(),
|
|
161
50
|
image_count: z.number().optional(),
|
|
162
|
-
|
|
163
|
-
// section/row/column wrappers. Distinct from library_get's
|
|
164
|
-
// `sections[].element_count` (LIBRARY_SECTION_SCHEMA below), which
|
|
165
|
-
// counts ALL descendants including structural nodes — the same
|
|
166
|
-
// section reports a different count from each; do not assume parity.
|
|
167
|
-
element_count: z
|
|
51
|
+
content_element_count: z
|
|
168
52
|
.number()
|
|
169
53
|
.optional()
|
|
170
|
-
.describe('CONTENT elements
|
|
171
|
-
'
|
|
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.'),
|
|
172
57
|
});
|
|
173
58
|
export const LIBRARY_BROWSE_OUTPUT_SCHEMA = z.object({
|
|
174
59
|
items: z.array(LIBRARY_ITEM_SCHEMA),
|
|
175
60
|
total: z.number(),
|
|
176
|
-
// `CatalogService::browse()` always emits the key; `null` means "last page".
|
|
177
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(),
|
|
178
65
|
});
|
|
179
|
-
// ─── library_get (Task 3) ───────────────────────────────────────────
|
|
180
66
|
const LIBRARY_SECTION_SCHEMA = z.object({
|
|
181
67
|
index: z.number(),
|
|
182
68
|
name: z.string(),
|
|
@@ -185,28 +71,14 @@ const LIBRARY_SECTION_SCHEMA = z.object({
|
|
|
185
71
|
});
|
|
186
72
|
export const LIBRARY_GET_OUTPUT_SCHEMA = z.object({
|
|
187
73
|
id: z.string(),
|
|
188
|
-
// Task 8b (Section Corpus Phase 1, PLAN-GAP fix): "section" is a valid
|
|
189
|
-
// resolved kind — `LibraryController::getSection()` (PHP) always
|
|
190
|
-
// returns a literal `kind: "section"` for a composite-id fetch.
|
|
191
74
|
kind: z.enum(['layout', 'element', 'section']),
|
|
192
75
|
name: z.string(),
|
|
193
76
|
version: z.string(),
|
|
194
|
-
// The raw fetched tree (`{type, children, version, images, ...}` for a
|
|
195
|
-
// layout; a single element node for a preset) — an arbitrary JSON
|
|
196
|
-
// object, not a fixed shape.
|
|
197
77
|
tree: z.record(z.string(), z.unknown()),
|
|
198
|
-
// Top-level `tree.children` (layouts only — always `[]` for a preset,
|
|
199
|
-
// even when its own tree happens to carry `children`).
|
|
200
78
|
sections: z.array(LIBRARY_SECTION_SCHEMA),
|
|
201
79
|
images_manifest_count: z.number(),
|
|
80
|
+
element_count_note: z.string().optional(),
|
|
202
81
|
});
|
|
203
|
-
// ─── library_insert (Task 6) ─────────────────────────────────────────
|
|
204
|
-
/**
|
|
205
|
-
* Mirrors `LibraryInsertService::insert()`'s `$target` union (PHP) exactly:
|
|
206
|
-
* a fresh page, a splice into an EXISTING template/page (layout/section on
|
|
207
|
-
* the layout root, or a preset on a Column), or a splice into a
|
|
208
|
-
* sublayout/fragment element.
|
|
209
|
-
*/
|
|
210
82
|
const LIBRARY_INSERT_TARGET_SCHEMA = z.union([
|
|
211
83
|
z.object({
|
|
212
84
|
new_page: z.object({
|
|
@@ -241,33 +113,49 @@ const LIBRARY_INSERT_TARGET_SCHEMA = z.union([
|
|
|
241
113
|
'or a Column for an element preset (kind:"element").'),
|
|
242
114
|
}),
|
|
243
115
|
]);
|
|
244
|
-
/**
|
|
245
|
-
* A DOWNGRADED UI-invariant violation (2026-07-12 fix, "trusted
|
|
246
|
-
* library-insert downgrades enum/type violations to warnings"): a pure
|
|
247
|
-
* value-shape mismatch (`invalid_enum_value` / `type_mismatch`) that
|
|
248
|
-
* YOOtheme's OWN factory content carried but the site's installed YOOtheme
|
|
249
|
-
* schema doesn't declare — a version-skew artifact, allowed through rather
|
|
250
|
-
* than hard-rejecting the whole insert. Every OTHER violation class still
|
|
251
|
-
* 422s (`library.ui_invariant_violation`) exactly as before.
|
|
252
|
-
*/
|
|
253
116
|
const LIBRARY_INSERT_WARNING_SCHEMA = z.object({
|
|
254
117
|
field: z.string(),
|
|
255
118
|
code: z.string(),
|
|
256
119
|
message: z.string(),
|
|
257
120
|
});
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
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({
|
|
264
135
|
downloaded: z.number(),
|
|
265
136
|
placeholders: z.number(),
|
|
266
|
-
})
|
|
267
|
-
|
|
268
|
-
|
|
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(),
|
|
269
144
|
warnings: z.array(LIBRARY_INSERT_WARNING_SCHEMA).optional(),
|
|
270
|
-
|
|
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
|
+
]));
|
|
271
159
|
const LIBRARY_TABLE_COLUMNS = [
|
|
272
160
|
{ key: 'id', label: 'ID', width: 10, llmOnly: true },
|
|
273
161
|
{ key: 'name', label: 'NAME', width: 26 },
|
|
@@ -282,17 +170,6 @@ function asNullableString(v) {
|
|
|
282
170
|
function isSectionPurpose(v) {
|
|
283
171
|
return typeof v === 'string' && SECTION_PURPOSE_VALUES.includes(v);
|
|
284
172
|
}
|
|
285
|
-
/**
|
|
286
|
-
* Task 8b (Section Corpus Phase 1, PLAN-GAP fix) — `requestedKind` is the
|
|
287
|
-
* caller's own `library_browse({kind})` argument. It is needed because a
|
|
288
|
-
* `kind="section"` row from `SectionCatalog::mapEntry()` (PHP) carries NO
|
|
289
|
-
* per-row `kind` field at all (unlike layout/element rows, which always do)
|
|
290
|
-
* — the whole response is homogeneous per browse call, since `kind` is a
|
|
291
|
-
* REST-level filter. So: `r.kind` wins when the row DOES carry one
|
|
292
|
-
* (layout/element, and defensively "section" too, should REST ever add it);
|
|
293
|
-
* otherwise fall back to what the caller asked for; otherwise "layout"
|
|
294
|
-
* (today's pre-Task-8b default, byte-unchanged).
|
|
295
|
-
*/
|
|
296
173
|
function mapLibraryItem(raw, requestedKind) {
|
|
297
174
|
if (raw === null || typeof raw !== 'object')
|
|
298
175
|
return undefined;
|
|
@@ -312,15 +189,11 @@ function mapLibraryItem(raw, requestedKind) {
|
|
|
312
189
|
},
|
|
313
190
|
thumbnail_url: asNullableString(r.thumbnail_url),
|
|
314
191
|
};
|
|
315
|
-
// ADDITIVE ONLY: a layout/element row returns exactly `base` — no
|
|
316
|
-
// section keys are ever added, not even as `undefined` — so the
|
|
317
|
-
// pre-Task-8b shape stays byte-unchanged (regression-tested via
|
|
318
|
-
// toStrictEqual in library.test.ts).
|
|
319
192
|
if (kind !== 'section')
|
|
320
193
|
return base;
|
|
321
194
|
return {
|
|
322
195
|
...base,
|
|
323
|
-
|
|
196
|
+
library_layout_id: typeof r.library_layout_id === 'string' ? r.library_layout_id : undefined,
|
|
324
197
|
section_index: typeof r.section_index === 'number' ? r.section_index : undefined,
|
|
325
198
|
purpose: isSectionPurpose(r.purpose) ? r.purpose : undefined,
|
|
326
199
|
element_signature: typeof r.element_signature === 'string' ? r.element_signature : undefined,
|
|
@@ -332,41 +205,17 @@ function mapLibraryItem(raw, requestedKind) {
|
|
|
332
205
|
website: asNullableString(r.website),
|
|
333
206
|
has_images: typeof r.has_images === 'boolean' ? r.has_images : undefined,
|
|
334
207
|
image_count: typeof r.image_count === 'number' ? r.image_count : undefined,
|
|
335
|
-
|
|
208
|
+
content_element_count: typeof r.content_element_count === 'number' ? r.content_element_count : undefined,
|
|
336
209
|
};
|
|
337
210
|
}
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
* library/image call used to return an empty `{items:[],total:0}` (or a bare
|
|
343
|
-
* 404) with no hint about WHY. This returns an explicit
|
|
344
|
-
* `not_supported_on_platform` (501) instead, so the agent stops retrying and
|
|
345
|
-
* knows the capability simply is not there on this platform. Returns `null` on
|
|
346
|
-
* WordPress so the real forward runs.
|
|
347
|
-
*/
|
|
348
|
-
function joomlaNotSupportedGuard(client, toolName, context) {
|
|
349
|
-
if (client.getPlatform().kind !== 'joomla')
|
|
350
|
-
return null;
|
|
351
|
-
return errorResult({
|
|
352
|
-
error: new Error(`${toolName} is not supported on Joomla — the Library + image domain is ` +
|
|
353
|
-
'WordPress-only in plugin 1.10.0.'),
|
|
354
|
-
context: { ...context, platform: 'joomla', error_code: 'not_supported_on_platform', status: 501 },
|
|
355
|
-
hint: 'This capability exists only on WordPress sites. On Joomla, build layouts with ' +
|
|
356
|
-
'the element/page tools; there is no Library catalog or image proxy to browse.',
|
|
357
|
-
});
|
|
358
|
-
}
|
|
359
|
-
export async function handleLibraryBrowse({ client }, { kind, source, topic, type, role, website, element_type, purpose, has_effects, query, limit, cursor }) {
|
|
360
|
-
const guard = joomlaNotSupportedGuard(client, 'yootheme_builder_library_browse', { kind: kind ?? 'layout' });
|
|
361
|
-
if (guard !== null)
|
|
362
|
-
return guard;
|
|
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.';
|
|
214
|
+
export async function handleLibraryBrowse({ client }, { kind, source, topic, type, role, website, element_type, purpose, has_effects, query, limit, cursor, include_meta }) {
|
|
363
215
|
try {
|
|
364
216
|
const qs = new URLSearchParams();
|
|
365
217
|
if (kind !== undefined)
|
|
366
218
|
qs.set('kind', kind);
|
|
367
|
-
// Only forward a NON-default source (the PHP controller reads the same
|
|
368
|
-
// absent-param `catalog` default) so a catalog browse is byte-identical
|
|
369
|
-
// to the pre-Task-7 request.
|
|
370
219
|
if (source !== undefined && source !== DEFAULT_LIBRARY_SOURCE)
|
|
371
220
|
qs.set('source', source);
|
|
372
221
|
if (typeof topic === 'string' && topic !== '')
|
|
@@ -397,25 +246,51 @@ export async function handleLibraryBrowse({ client }, { kind, source, topic, typ
|
|
|
397
246
|
.filter((i) => i !== undefined);
|
|
398
247
|
const total = typeof data.total === 'number' ? data.total : items.length;
|
|
399
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;
|
|
400
251
|
const rows = items;
|
|
401
|
-
const toolkitResult =
|
|
252
|
+
const toolkitResult = budgetedTableResult(rows, {
|
|
402
253
|
columns: [...LIBRARY_TABLE_COLUMNS],
|
|
403
254
|
header: (count) => `${String(count)} of ${String(total)} library items`,
|
|
404
|
-
footer: 'Activate one on a page with yootheme_builder_element_add /
|
|
405
|
-
'or pass `cursor` (next_cursor) for the next page.'
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
// tableResult() only ever emits text rows. Use
|
|
410
|
-
// yootheme_builder_library_get's include_thumbnail on a shortlisted
|
|
411
|
-
// id for an actual image.
|
|
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,
|
|
258
|
+
includeMeta: include_meta,
|
|
259
|
+
}, RESPONSE_BUDGET.BULKY);
|
|
412
260
|
return structuredResult(toolkitResult, {
|
|
413
261
|
items,
|
|
414
262
|
total,
|
|
415
263
|
next_cursor: nextCursor,
|
|
264
|
+
images_note: BROWSE_IMAGES_NOTE,
|
|
265
|
+
...(apiKeyConfigured !== undefined ? { api_key_configured: apiKeyConfigured } : {}),
|
|
266
|
+
...(detailNote !== undefined ? { detail_note: detailNote } : {}),
|
|
416
267
|
});
|
|
417
268
|
}
|
|
418
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
|
+
}
|
|
419
294
|
return errorResult({
|
|
420
295
|
error: e,
|
|
421
296
|
context: { kind: kind ?? 'layout', source: source ?? DEFAULT_LIBRARY_SOURCE, topic, type, role, website, element_type, purpose, has_effects, query },
|
|
@@ -423,6 +298,69 @@ export async function handleLibraryBrowse({ client }, { kind, source, topic, typ
|
|
|
423
298
|
});
|
|
424
299
|
}
|
|
425
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
|
+
}
|
|
426
364
|
function mapLibrarySection(raw) {
|
|
427
365
|
if (raw === null || typeof raw !== 'object')
|
|
428
366
|
return undefined;
|
|
@@ -435,14 +373,6 @@ function mapLibrarySection(raw) {
|
|
|
435
373
|
}
|
|
436
374
|
return { index: r.index, name: r.name, path: r.path, element_count: r.element_count };
|
|
437
375
|
}
|
|
438
|
-
/**
|
|
439
|
-
* Task 8 (Section Corpus Phase 1) — the REST `/library/get` response's
|
|
440
|
-
* `thumbnail` field (present ONLY when `include_thumbnail=true` was passed
|
|
441
|
-
* AND the server-side fetch succeeded — see `LibraryController::attachThumbnail()`).
|
|
442
|
-
* A missing/malformed field (either key absent, or either value not a
|
|
443
|
-
* non-empty string) is treated as "no thumbnail" — the caller never emits a
|
|
444
|
-
* broken image block.
|
|
445
|
-
*/
|
|
446
376
|
function mapThumbnail(raw) {
|
|
447
377
|
if (raw === null || typeof raw !== 'object')
|
|
448
378
|
return undefined;
|
|
@@ -453,15 +383,76 @@ function mapThumbnail(raw) {
|
|
|
453
383
|
return undefined;
|
|
454
384
|
return { mime_type: r.mime_type, data_base64: r.data_base64 };
|
|
455
385
|
}
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
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 }) {
|
|
460
452
|
try {
|
|
461
453
|
const qs = new URLSearchParams({ id });
|
|
462
454
|
if (kind !== undefined)
|
|
463
455
|
qs.set('kind', kind);
|
|
464
|
-
// Only forward a NON-default source (see handleLibraryBrowse).
|
|
465
456
|
if (source !== undefined && source !== DEFAULT_LIBRARY_SOURCE)
|
|
466
457
|
qs.set('source', source);
|
|
467
458
|
if (include_thumbnail === true)
|
|
@@ -484,24 +475,58 @@ export async function handleLibraryGet({ client }, { id, kind, source, include_t
|
|
|
484
475
|
sections,
|
|
485
476
|
images_manifest_count: typeof data.images_manifest_count === 'number' ? data.images_manifest_count : 0,
|
|
486
477
|
};
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
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
|
+
};
|
|
497
489
|
const thumbnail = include_thumbnail === true ? mapThumbnail(data.thumbnail) : undefined;
|
|
498
|
-
const jsonEnvelope = jsonResult(
|
|
490
|
+
const jsonEnvelope = jsonResult(structuredPayload);
|
|
499
491
|
const content = thumbnail !== undefined
|
|
500
492
|
? [...jsonEnvelope.content, { type: 'image', data: thumbnail.data_base64, mimeType: thumbnail.mime_type }]
|
|
501
493
|
: jsonEnvelope.content;
|
|
502
|
-
return structuredResult({ content },
|
|
494
|
+
return structuredResult(applyMaxChars({ content }, max_chars), structuredPayload);
|
|
503
495
|
}
|
|
504
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
|
+
}
|
|
505
530
|
return errorResult({
|
|
506
531
|
error: e,
|
|
507
532
|
context: { id, kind: kind ?? 'layout', source: source ?? DEFAULT_LIBRARY_SOURCE },
|
|
@@ -510,15 +535,6 @@ export async function handleLibraryGet({ client }, { id, kind, source, include_t
|
|
|
510
535
|
});
|
|
511
536
|
}
|
|
512
537
|
}
|
|
513
|
-
/**
|
|
514
|
-
* 2026-07-12 fix ("trusted library-insert downgrades enum/type violations to
|
|
515
|
-
* warnings") — maps the REST `warnings[]` array (each `{field, code,
|
|
516
|
-
* message}`) if present. Mirrors the OTHER `map*` helpers in this file:
|
|
517
|
-
* malformed entries are dropped rather than propagated, and an absent/empty
|
|
518
|
-
* source array maps to `undefined` (the key is OMITTED from the result, not
|
|
519
|
-
* emitted as `[]`) — this is the "if present" contract: pre-fix inserts
|
|
520
|
-
* (a REST body carrying no `warnings` key at all) round-trip byte-unchanged.
|
|
521
|
-
*/
|
|
522
538
|
function mapLibraryInsertWarnings(raw) {
|
|
523
539
|
if (!Array.isArray(raw) || raw.length === 0)
|
|
524
540
|
return undefined;
|
|
@@ -533,55 +549,148 @@ function mapLibraryInsertWarnings(raw) {
|
|
|
533
549
|
}
|
|
534
550
|
return warnings.length > 0 ? warnings : undefined;
|
|
535
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
|
+
}
|
|
536
600
|
function mapLibraryInsertResult(raw) {
|
|
537
601
|
if (raw === null || typeof raw !== 'object')
|
|
538
602
|
return undefined;
|
|
539
603
|
const r = raw;
|
|
540
|
-
if (typeof r.
|
|
604
|
+
if (typeof r.layout_id !== 'string' || typeof r.inserted_path !== 'string')
|
|
541
605
|
return undefined;
|
|
542
606
|
const resolvedRaw = r.images_resolved !== null && typeof r.images_resolved === 'object'
|
|
543
607
|
? r.images_resolved
|
|
544
608
|
: {};
|
|
545
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;
|
|
546
618
|
return {
|
|
547
|
-
|
|
619
|
+
layout_id: r.layout_id,
|
|
548
620
|
inserted_path: r.inserted_path,
|
|
549
621
|
element_count: typeof r.element_count === 'number' ? r.element_count : 0,
|
|
550
|
-
images_mode:
|
|
622
|
+
images_mode: mode,
|
|
551
623
|
images_resolved: {
|
|
552
624
|
downloaded: typeof resolvedRaw.downloaded === 'number' ? resolvedRaw.downloaded : 0,
|
|
553
|
-
placeholders
|
|
625
|
+
placeholders,
|
|
554
626
|
},
|
|
555
627
|
new_etag: typeof r.new_etag === 'string' ? r.new_etag : '',
|
|
556
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 } : {}),
|
|
557
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 : '',
|
|
558
634
|
};
|
|
559
635
|
}
|
|
560
|
-
export async function handleLibraryInsert({ client }, { id, kind, source, target, section_index, images, etag }) {
|
|
561
|
-
const guard = joomlaNotSupportedGuard(client, 'yootheme_builder_library_insert', { id });
|
|
562
|
-
if (guard !== null)
|
|
563
|
-
return guard;
|
|
636
|
+
export async function handleLibraryInsert({ client }, { id, kind, source, target, section_index, images, insert_mode, index, confirm, etag }) {
|
|
564
637
|
const resolvedImages = images ?? 'placeholder';
|
|
565
638
|
try {
|
|
566
639
|
const body = { id, target, images: resolvedImages };
|
|
567
640
|
if (kind !== undefined)
|
|
568
641
|
body.kind = kind;
|
|
569
|
-
// Only forward a NON-default source (see handleLibraryBrowse); the PHP
|
|
570
|
-
// LibraryInsertService reads the same absent-param `catalog` default.
|
|
571
642
|
if (source !== undefined && source !== DEFAULT_LIBRARY_SOURCE)
|
|
572
643
|
body.source = source;
|
|
573
644
|
if (section_index !== undefined)
|
|
574
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;
|
|
575
652
|
if (typeof etag === 'string' && etag !== '')
|
|
576
653
|
body.etag = etag;
|
|
577
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
|
+
}
|
|
578
659
|
const mapped = mapLibraryInsertResult(data);
|
|
579
660
|
if (mapped === undefined) {
|
|
580
|
-
throw new Error('Malformed /library/insert response — missing
|
|
661
|
+
throw new Error('Malformed /library/insert response — missing layout_id/inserted_path.');
|
|
581
662
|
}
|
|
582
663
|
return structuredResult(jsonResult(mapped), mapped);
|
|
583
664
|
}
|
|
584
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
|
+
}
|
|
585
694
|
return errorResult({
|
|
586
695
|
error: e,
|
|
587
696
|
context: { id, kind: kind ?? 'layout', source: source ?? DEFAULT_LIBRARY_SOURCE, images: resolvedImages },
|
|
@@ -589,26 +698,20 @@ export async function handleLibraryInsert({ client }, { id, kind, source, target
|
|
|
589
698
|
});
|
|
590
699
|
}
|
|
591
700
|
}
|
|
592
|
-
/**
|
|
593
|
-
* PAGES_LIBRARY_MEDIA-4 — status/code-aware catch hint for `library_insert`.
|
|
594
|
-
*
|
|
595
|
-
* The former static hint always said "Verify the id via library_get first" —
|
|
596
|
-
* but a 404 `not_found`/`unavailable` means the catalog item cannot be FETCHED
|
|
597
|
-
* (no Library API key configured, or the id is genuinely absent), so library_get
|
|
598
|
-
* returns an empty success and confirms nothing → a loop. On that class steer
|
|
599
|
-
* to the real fixes (configure the apikey, or use the keyless source:"ai")
|
|
600
|
-
* instead. Keep the target-column / stale-etag guidance for the 422/412 classes
|
|
601
|
-
* they actually describe.
|
|
602
|
-
*/
|
|
603
701
|
function libraryInsertHint(e) {
|
|
604
702
|
if (e instanceof RestError) {
|
|
605
703
|
const code = e.code ?? '';
|
|
606
|
-
if (e
|
|
607
|
-
return
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
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).');
|
|
612
715
|
}
|
|
613
716
|
if (e.status === 422) {
|
|
614
717
|
return ('A 422 target_not_a_column means a preset (kind:"element") was pointed at a ' +
|
|
@@ -624,7 +727,6 @@ function libraryInsertHint(e) {
|
|
|
624
727
|
'hull first with yootheme_builder_scaffold_section. A 412 means the etag is stale — ' +
|
|
625
728
|
're-read (get_etag/pages_list) and retry.');
|
|
626
729
|
}
|
|
627
|
-
// ─── library_save_as (Task 7) ────────────────────────────────────────────
|
|
628
730
|
const LIBRARY_SAVE_SOURCE_SCHEMA = z.object({
|
|
629
731
|
template_id: z
|
|
630
732
|
.string()
|
|
@@ -640,6 +742,9 @@ export const LIBRARY_SAVE_AS_OUTPUT_SCHEMA = z.object({
|
|
|
640
742
|
name: z.string(),
|
|
641
743
|
storage: z.string(),
|
|
642
744
|
invalid_source_possible: z.boolean(),
|
|
745
|
+
destination: z.string().optional(),
|
|
746
|
+
kind: z.string().optional(),
|
|
747
|
+
storage_note: z.string().optional(),
|
|
643
748
|
});
|
|
644
749
|
function mapLibrarySaveAsResult(raw) {
|
|
645
750
|
if (raw === null || typeof raw !== 'object')
|
|
@@ -648,25 +753,31 @@ function mapLibrarySaveAsResult(raw) {
|
|
|
648
753
|
if (typeof r.library_id !== 'string' || typeof r.name !== 'string' || typeof r.storage !== 'string') {
|
|
649
754
|
return undefined;
|
|
650
755
|
}
|
|
756
|
+
const storage = r.storage;
|
|
757
|
+
const isOwnStore = storage === 'ai' || storage === 'wootsup-ai-layouts';
|
|
651
758
|
return {
|
|
652
759
|
library_id: r.library_id,
|
|
653
760
|
name: r.name,
|
|
654
|
-
storage
|
|
761
|
+
storage,
|
|
655
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.',
|
|
656
776
|
};
|
|
657
777
|
}
|
|
658
778
|
export async function handleLibrarySaveAs({ client }, { source, name, destination }) {
|
|
659
|
-
const guard = joomlaNotSupportedGuard(client, 'yootheme_builder_library_save_as', {
|
|
660
|
-
template_id: source.template_id,
|
|
661
|
-
});
|
|
662
|
-
if (guard !== null)
|
|
663
|
-
return guard;
|
|
664
779
|
try {
|
|
665
780
|
const body = { source, name };
|
|
666
|
-
// Only forward a NON-default destination (the PHP MyLayoutsStore reads
|
|
667
|
-
// the same absent-param `ai` default), so an ai-default save is
|
|
668
|
-
// byte-identical to the pre-Task-7 body — only the `native` escape
|
|
669
|
-
// hatch ever appears on the wire.
|
|
670
781
|
if (destination !== undefined && destination !== DEFAULT_SAVE_DESTINATION)
|
|
671
782
|
body.destination = destination;
|
|
672
783
|
const data = await client.post('/library/save', { body });
|
|
@@ -686,12 +797,22 @@ export async function handleLibrarySaveAs({ client }, { source, name, destinatio
|
|
|
686
797
|
});
|
|
687
798
|
}
|
|
688
799
|
}
|
|
689
|
-
|
|
690
|
-
|
|
800
|
+
export const LIBRARY_DELETE_OUTPUT_SCHEMA = z
|
|
801
|
+
.object({
|
|
691
802
|
deleted: z.boolean(),
|
|
692
803
|
id: z.string(),
|
|
693
804
|
deleted_count: z.number(),
|
|
694
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
|
+
}
|
|
695
816
|
});
|
|
696
817
|
function mapLibraryDeleteResult(raw) {
|
|
697
818
|
if (raw === null || typeof raw !== 'object')
|
|
@@ -707,14 +828,8 @@ function mapLibraryDeleteResult(raw) {
|
|
|
707
828
|
};
|
|
708
829
|
}
|
|
709
830
|
export async function handleLibraryDelete({ client }, { id, source }) {
|
|
710
|
-
const guard = joomlaNotSupportedGuard(client, 'yootheme_builder_library_delete', { id });
|
|
711
|
-
if (guard !== null)
|
|
712
|
-
return guard;
|
|
713
831
|
try {
|
|
714
|
-
const body = { id };
|
|
715
|
-
// Only forward a NON-default source. The PHP controller reads the same
|
|
716
|
-
// absent-param "ai" default; the curated catalog (source:"catalog") is
|
|
717
|
-
// read-only, so forwarding it is what makes the controller answer 403.
|
|
832
|
+
const body = { id, confirm: true };
|
|
718
833
|
if (source !== undefined && source !== 'ai')
|
|
719
834
|
body.source = source;
|
|
720
835
|
const data = await client.post('/library/delete', { body });
|
|
@@ -732,11 +847,6 @@ export async function handleLibraryDelete({ client }, { id, source }) {
|
|
|
732
847
|
});
|
|
733
848
|
}
|
|
734
849
|
}
|
|
735
|
-
/**
|
|
736
|
-
* Status/code-aware catch hint for library_delete. A 404 means the id was
|
|
737
|
-
* already gone (not a failure to act on — the end state is what you wanted); a
|
|
738
|
-
* 403 means a read-only source (only source:"ai" is deletable).
|
|
739
|
-
*/
|
|
740
850
|
function libraryDeleteHint(e) {
|
|
741
851
|
if (e instanceof RestError) {
|
|
742
852
|
if (e.status === 404) {
|
|
@@ -751,57 +861,57 @@ function libraryDeleteHint(e) {
|
|
|
751
861
|
return ('Pass the `id` of an entry from yootheme_builder_library_browse({source:"ai"}). Only ' +
|
|
752
862
|
'source:"ai" entries are deletable — the curated library is read-only.');
|
|
753
863
|
}
|
|
754
|
-
|
|
755
|
-
//
|
|
756
|
-
// Thin GET/POST forwards to `/library/image-search` (Task C1) mirroring
|
|
757
|
-
// library_browse's read-projection pattern, and `/library/image-import`
|
|
758
|
-
// mirroring library_insert's write-forward pattern. Response shapes are
|
|
759
|
-
// pinned by the PHP controller tests
|
|
760
|
-
// (tests/php/unit/Library/LibraryControllerImageSearchTest.php +
|
|
761
|
-
// LibraryControllerImageImportTest.php):
|
|
762
|
-
// GET /library/image-search → {items, total, source, query, hint?}
|
|
763
|
-
// POST /library/image-import → {imported:true, local_url} |
|
|
764
|
-
// {imported:false, reason}
|
|
765
|
-
const IMAGE_SOURCE_VALUES = ['unsplash', 'pexels', 'proimages'];
|
|
766
|
-
/**
|
|
767
|
-
* Cross-source orientation dialect (D1 concern, see the plan doc): the
|
|
768
|
-
* Unsplash proxy accepts "squarish", the Pexels proxy accepts "square" for
|
|
769
|
-
* the SAME concept. `ImageSearchService::buildQueryString()` (PHP) forwards
|
|
770
|
-
* whatever string is supplied verbatim, per-source, with NO translation — so
|
|
771
|
-
* this schema accepts BOTH spellings instead of forcing one source's
|
|
772
|
-
* vocabulary onto the other (which would either reject a legitimate Pexels
|
|
773
|
-
* "square" request or silently mis-filter it).
|
|
774
|
-
*/
|
|
864
|
+
const IMAGE_SOURCE_VALUES = ['unsplash', 'pexels', 'proimages', 'local'];
|
|
775
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
|
+
};
|
|
776
872
|
const IMAGE_RESULT_SCHEMA = z.object({
|
|
777
873
|
id: z.string(),
|
|
778
874
|
source: z.string(),
|
|
779
875
|
thumb_url: z.string(),
|
|
780
876
|
full_url: z.string(),
|
|
781
|
-
width: z.number(),
|
|
782
|
-
height: z.number(),
|
|
783
|
-
alt: z.string(),
|
|
784
|
-
author: z.string(),
|
|
785
|
-
author_url: z.string(),
|
|
786
|
-
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,
|
|
787
884
|
});
|
|
788
885
|
export const IMAGE_SEARCH_OUTPUT_SCHEMA = z.object({
|
|
789
886
|
items: z.array(IMAGE_RESULT_SCHEMA),
|
|
790
887
|
total: z.number(),
|
|
791
888
|
source: z.string(),
|
|
792
889
|
query: z.string(),
|
|
793
|
-
// Present ONLY on the empty-result path (no Library API key configured,
|
|
794
|
-
// or genuinely zero matches — the REST layer cannot and must not
|
|
795
|
-
// distinguish the two; see LibraryControllerImageSearchTest).
|
|
796
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(),
|
|
797
894
|
});
|
|
798
895
|
export const IMAGE_IMPORT_OUTPUT_SCHEMA = z.object({
|
|
799
896
|
imported: z.boolean(),
|
|
800
|
-
// Present only when imported:true.
|
|
801
897
|
local_url: z.string().optional(),
|
|
802
|
-
// Present only when imported:false (a caught importer failure — never a 500).
|
|
803
898
|
reason: z.string().optional(),
|
|
899
|
+
failure: z.enum(['rejected', 'unavailable']).optional(),
|
|
900
|
+
media_note: z.string().optional(),
|
|
901
|
+
...IMAGE_LICENSE_KEYS,
|
|
804
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
|
+
}
|
|
805
915
|
function mapImageResult(raw) {
|
|
806
916
|
if (raw === null || typeof raw !== 'object')
|
|
807
917
|
return undefined;
|
|
@@ -813,12 +923,13 @@ function mapImageResult(raw) {
|
|
|
813
923
|
source: typeof r.source === 'string' ? r.source : '',
|
|
814
924
|
thumb_url: typeof r.thumb_url === 'string' ? r.thumb_url : '',
|
|
815
925
|
full_url: typeof r.full_url === 'string' ? r.full_url : '',
|
|
816
|
-
width: typeof r.width === 'number' ? r.width :
|
|
817
|
-
height: typeof r.height === 'number' ? r.height :
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
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),
|
|
822
933
|
};
|
|
823
934
|
}
|
|
824
935
|
const IMAGE_TABLE_COLUMNS = [
|
|
@@ -826,63 +937,156 @@ const IMAGE_TABLE_COLUMNS = [
|
|
|
826
937
|
{ key: 'dimensions', label: 'SIZE', width: 10 },
|
|
827
938
|
{ key: 'author', label: 'AUTHOR', width: 18 },
|
|
828
939
|
{ key: 'orientation', label: 'ORIENT', width: 10 },
|
|
829
|
-
// llmOnly: the copyable next-call value (yootheme_builder_image_import's
|
|
830
|
-
// `url` arg) — excluded from the Rich Card UI (too long for a table
|
|
831
|
-
// cell) but present in the ASCII text the LLM reads. Always present in
|
|
832
|
-
// structuredContent.items[].full_url regardless of table membership.
|
|
833
940
|
{ key: 'full_url', label: 'FULL_URL', width: 50, llmOnly: true },
|
|
834
941
|
];
|
|
835
|
-
export async function handleImageSearch({ client }, { source, query, per_page, orientation, color }) {
|
|
836
|
-
const guard = joomlaNotSupportedGuard(client, 'yootheme_builder_image_search', { query });
|
|
837
|
-
if (guard !== null)
|
|
838
|
-
return guard;
|
|
942
|
+
export async function handleImageSearch({ client }, { source, query, per_page, orientation, color, offset, include_meta }) {
|
|
839
943
|
const resolvedSource = source ?? 'unsplash';
|
|
840
944
|
try {
|
|
841
945
|
const qs = new URLSearchParams();
|
|
842
946
|
qs.set('source', resolvedSource);
|
|
843
|
-
|
|
947
|
+
if (typeof query === 'string' && query !== '')
|
|
948
|
+
qs.set('query', query);
|
|
844
949
|
if (per_page !== undefined)
|
|
845
950
|
qs.set('per_page', String(per_page));
|
|
846
951
|
if (typeof orientation === 'string' && orientation !== '')
|
|
847
952
|
qs.set('orientation', orientation);
|
|
848
953
|
if (typeof color === 'string' && color !== '')
|
|
849
954
|
qs.set('color', color);
|
|
955
|
+
if (offset !== undefined)
|
|
956
|
+
qs.set('offset', String(offset));
|
|
850
957
|
const data = await client.get(`/library/image-search?${qs.toString()}`);
|
|
851
958
|
const rawItems = Array.isArray(data.items) ? data.items : [];
|
|
852
959
|
const items = rawItems.map(mapImageResult).filter((i) => i !== undefined);
|
|
853
960
|
const total = typeof data.total === 'number' ? data.total : items.length;
|
|
854
961
|
const resolvedResponseSource = typeof data.source === 'string' ? data.source : resolvedSource;
|
|
855
|
-
const resolvedQuery = typeof data.query === 'string' ? data.query : query;
|
|
962
|
+
const resolvedQuery = typeof data.query === 'string' ? data.query : (query ?? '');
|
|
856
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;
|
|
857
969
|
const rows = items.map((i) => ({
|
|
858
970
|
...i,
|
|
859
|
-
dimensions: `${String(i.width)}x${String(i.height)}`,
|
|
971
|
+
dimensions: `${i.width === null ? '?' : String(i.width)}x${i.height === null ? '?' : String(i.height)}`,
|
|
860
972
|
}));
|
|
861
|
-
const toolkitResult =
|
|
973
|
+
const toolkitResult = budgetedTableResult(rows, {
|
|
862
974
|
columns: [...IMAGE_TABLE_COLUMNS],
|
|
863
975
|
header: (count) => `${String(count)} of ${String(total)} ${resolvedResponseSource} image${total === 1 ? '' : 's'} for "${resolvedQuery}"`,
|
|
864
976
|
footer: hint
|
|
977
|
+
?? localNote
|
|
865
978
|
?? 'Import a chosen row\'s full_url with yootheme_builder_image_import, then bind '
|
|
866
979
|
+ 'the returned local_url via element_update_settings.',
|
|
867
|
-
|
|
980
|
+
includeMeta: include_meta,
|
|
981
|
+
}, COMPACT_DEFAULT);
|
|
868
982
|
return structuredResult(toolkitResult, {
|
|
869
983
|
items,
|
|
870
984
|
total,
|
|
871
985
|
source: resolvedResponseSource,
|
|
872
986
|
query: resolvedQuery,
|
|
873
987
|
...(hint !== undefined ? { hint } : {}),
|
|
988
|
+
...(nextOffset !== undefined ? { next_offset: nextOffset } : {}),
|
|
989
|
+
...(localNote !== undefined ? { local_note: localNote } : {}),
|
|
990
|
+
...(apiKeyConfigured !== undefined ? { api_key_configured: apiKeyConfigured } : {}),
|
|
874
991
|
});
|
|
875
992
|
}
|
|
876
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
|
+
}
|
|
877
1018
|
return errorResult({
|
|
878
1019
|
error: e,
|
|
879
1020
|
context: { source: resolvedSource, query, per_page, orientation, color },
|
|
880
|
-
hint: '
|
|
881
|
-
+ '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. '
|
|
882
1022
|
+ 'yootheme_builder_image_import still works with a direct image URL (no key needed).',
|
|
883
1023
|
});
|
|
884
1024
|
}
|
|
885
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
|
+
}
|
|
886
1090
|
function mapImageImportResult(raw) {
|
|
887
1091
|
if (raw === null || typeof raw !== 'object')
|
|
888
1092
|
return undefined;
|
|
@@ -892,17 +1096,41 @@ function mapImageImportResult(raw) {
|
|
|
892
1096
|
if (r.imported) {
|
|
893
1097
|
if (typeof r.local_url !== 'string' || r.local_url === '')
|
|
894
1098
|
return undefined;
|
|
895
|
-
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
|
+
};
|
|
896
1105
|
}
|
|
1106
|
+
const failure = mapImageImportFailure(r.failure);
|
|
897
1107
|
return {
|
|
898
1108
|
imported: false,
|
|
899
1109
|
reason: typeof r.reason === 'string' && r.reason !== '' ? r.reason : 'Import failed for an unknown reason.',
|
|
1110
|
+
...(failure === undefined ? {} : { failure }),
|
|
900
1111
|
};
|
|
901
1112
|
}
|
|
902
|
-
export async function handleImageImport({ client },
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
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
|
+
}
|
|
906
1134
|
try {
|
|
907
1135
|
const data = await client.post('/library/image-import', { body: { url } });
|
|
908
1136
|
const mapped = mapImageImportResult(data);
|
|
@@ -912,6 +1140,12 @@ export async function handleImageImport({ client }, { url }) {
|
|
|
912
1140
|
return structuredResult(jsonResult(mapped), mapped);
|
|
913
1141
|
}
|
|
914
1142
|
catch (e) {
|
|
1143
|
+
if (e instanceof RestError) {
|
|
1144
|
+
const mapped = mapImageImportResult(e.body);
|
|
1145
|
+
if (mapped !== undefined && !mapped.imported) {
|
|
1146
|
+
return structuredResult(jsonResult(mapped), mapped);
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
915
1149
|
return errorResult({
|
|
916
1150
|
error: e,
|
|
917
1151
|
context: { url },
|
|
@@ -926,13 +1160,15 @@ export function buildLibraryTools(pool) {
|
|
|
926
1160
|
defineTool({
|
|
927
1161
|
name: 'yootheme_builder_library_browse',
|
|
928
1162
|
description: 'Browse the YOOtheme Pro Library catalog: pre-designed page LAYOUTS, element ' +
|
|
929
|
-
'PRESETS, or purpose-classified SECTIONS
|
|
930
|
-
'`
|
|
931
|
-
'
|
|
932
|
-
'
|
|
933
|
-
'
|
|
934
|
-
'`total:0`
|
|
935
|
-
'
|
|
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.',
|
|
936
1172
|
inputSchema: {
|
|
937
1173
|
site_id: SITE_ID_SCHEMA,
|
|
938
1174
|
kind: z
|
|
@@ -967,7 +1203,13 @@ export function buildLibraryTools(pool) {
|
|
|
967
1203
|
.string()
|
|
968
1204
|
.min(1)
|
|
969
1205
|
.optional()
|
|
970
|
-
.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.'),
|
|
971
1213
|
website: z
|
|
972
1214
|
.string()
|
|
973
1215
|
.min(1)
|
|
@@ -981,7 +1223,11 @@ export function buildLibraryTools(pool) {
|
|
|
981
1223
|
purpose: z
|
|
982
1224
|
.enum(SECTION_PURPOSE_VALUES)
|
|
983
1225
|
.optional()
|
|
984
|
-
.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.'),
|
|
985
1231
|
has_effects: z
|
|
986
1232
|
.boolean()
|
|
987
1233
|
.optional()
|
|
@@ -1003,6 +1249,7 @@ export function buildLibraryTools(pool) {
|
|
|
1003
1249
|
.string()
|
|
1004
1250
|
.optional()
|
|
1005
1251
|
.describe('Opaque cursor from a previous response\'s next_cursor — fetches the next page.'),
|
|
1252
|
+
include_meta: INCLUDE_META_SCHEMA,
|
|
1006
1253
|
},
|
|
1007
1254
|
outputSchema: LIBRARY_BROWSE_OUTPUT_SCHEMA,
|
|
1008
1255
|
annotations: readOnly('Browse Layout Library'),
|
|
@@ -1010,13 +1257,12 @@ export function buildLibraryTools(pool) {
|
|
|
1010
1257
|
}),
|
|
1011
1258
|
defineTool({
|
|
1012
1259
|
name: 'yootheme_builder_library_get',
|
|
1013
|
-
description: 'Fetch
|
|
1014
|
-
'
|
|
1015
|
-
'
|
|
1016
|
-
'
|
|
1017
|
-
'
|
|
1018
|
-
'
|
|
1019
|
-
'Read-only; does not insert/bind. Operates on the default site unless site_id is provided.',
|
|
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.',
|
|
1020
1266
|
inputSchema: {
|
|
1021
1267
|
site_id: SITE_ID_SCHEMA,
|
|
1022
1268
|
id: z
|
|
@@ -1035,6 +1281,15 @@ export function buildLibraryTools(pool) {
|
|
|
1035
1281
|
.describe('Which library the `id` belongs to: "catalog" (remote YOOtheme Pro Library, ' +
|
|
1036
1282
|
'default) or "ai" (your OWN "WootsUp AI Layouts"). Must match the source the ' +
|
|
1037
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`.'),
|
|
1038
1293
|
include_thumbnail: z
|
|
1039
1294
|
.boolean()
|
|
1040
1295
|
.default(false)
|
|
@@ -1050,14 +1305,14 @@ export function buildLibraryTools(pool) {
|
|
|
1050
1305
|
}),
|
|
1051
1306
|
defineTool({
|
|
1052
1307
|
name: 'yootheme_builder_library_insert',
|
|
1053
|
-
description: 'Insert a
|
|
1054
|
-
'
|
|
1055
|
-
'
|
|
1056
|
-
'
|
|
1057
|
-
'
|
|
1058
|
-
'
|
|
1059
|
-
'
|
|
1060
|
-
'
|
|
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.',
|
|
1061
1316
|
inputSchema: {
|
|
1062
1317
|
site_id: SITE_ID_SCHEMA,
|
|
1063
1318
|
id: z
|
|
@@ -1087,30 +1342,42 @@ export function buildLibraryTools(pool) {
|
|
|
1087
1342
|
.enum(['placeholder', 'curated'])
|
|
1088
1343
|
.default('placeholder')
|
|
1089
1344
|
.describe('"placeholder" (default, safe, no network I/O) or "curated" (downloads + sideloads every unique image into the Media Library).'),
|
|
1090
|
-
|
|
1091
|
-
.
|
|
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)
|
|
1092
1356
|
.optional()
|
|
1093
|
-
.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,
|
|
1094
1369
|
},
|
|
1095
1370
|
outputSchema: LIBRARY_INSERT_OUTPUT_SCHEMA,
|
|
1096
|
-
annotations:
|
|
1371
|
+
annotations: destructive('Insert Layout Library Item'),
|
|
1097
1372
|
handler: async ({ site_id, ...rest }) => withWriteSiteRetry(pool, site_id, 'yootheme_builder_library_insert', async (client, site) => withSiteMeta(await handleLibraryInsert({ client }, rest), site)),
|
|
1098
1373
|
}),
|
|
1099
|
-
// Task 7 (Layout Library, Spec #7) — ADVANCED tool (deliberately NOT
|
|
1100
|
-
// added to essentials.ts/ESSENTIAL_TOOLS): reachable ONLY via
|
|
1101
|
-
// yootheme_builder_advanced({tool, arguments}), never a first-class
|
|
1102
|
-
// tools/list entry. See the module docblock's "Tiering" note.
|
|
1103
1374
|
defineTool({
|
|
1104
1375
|
name: 'yootheme_builder_library_save_as',
|
|
1105
1376
|
description: 'Save a page or subtree into a reusable library — the REVERSE of ' +
|
|
1106
|
-
'
|
|
1107
|
-
'
|
|
1108
|
-
'
|
|
1109
|
-
'
|
|
1110
|
-
'(YOOtheme\'s built-in My Layouts). Returns `{library_id, name, ' +
|
|
1111
|
-
'storage, invalid_source_possible}` (the last flags bound sources ' +
|
|
1112
|
-
'that may need re-binding after reload). Advanced/gateway-only. ' +
|
|
1113
|
-
'Operates on the default site unless site_id is provided.',
|
|
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).',
|
|
1114
1381
|
inputSchema: {
|
|
1115
1382
|
site_id: SITE_ID_SCHEMA,
|
|
1116
1383
|
source: LIBRARY_SAVE_SOURCE_SCHEMA.describe('The page/subtree to save.'),
|
|
@@ -1118,10 +1385,6 @@ export function buildLibraryTools(pool) {
|
|
|
1118
1385
|
.string()
|
|
1119
1386
|
.min(1)
|
|
1120
1387
|
.describe('Display name for the new library entry (e.g. "Hero Section").'),
|
|
1121
|
-
// PAGES_LIBRARY_MEDIA-15: `.optional()` (not `.default('ai')`) so
|
|
1122
|
-
// the generated schema does NOT advertise a REQUIRED field that
|
|
1123
|
-
// also carries a default. The handler applies the `ai` default
|
|
1124
|
-
// when omitted (it only forwards a NON-default `native`).
|
|
1125
1388
|
destination: z
|
|
1126
1389
|
.enum(SAVE_DESTINATION_VALUES)
|
|
1127
1390
|
.optional()
|
|
@@ -1131,32 +1394,15 @@ export function buildLibraryTools(pool) {
|
|
|
1131
1394
|
'(the escape hatch: save into YOOtheme\'s built-in "My Layouts").'),
|
|
1132
1395
|
},
|
|
1133
1396
|
outputSchema: LIBRARY_SAVE_AS_OUTPUT_SCHEMA,
|
|
1134
|
-
// PAGES_LIBRARY_MEDIA-15: title names the DEFAULT destination (the AI
|
|
1135
|
-
// Layouts store), not "My Layouts" (which is the non-default `native`).
|
|
1136
1397
|
annotations: creating('Save to AI Layouts library'),
|
|
1137
1398
|
handler: async ({ site_id, ...rest }) => withWriteSiteRetry(pool, site_id, 'yootheme_builder_library_save_as', async (client, site) => withSiteMeta(await handleLibrarySaveAs({ client }, rest), site)),
|
|
1138
1399
|
}),
|
|
1139
|
-
// PAGES_LIBRARY_MEDIA-16 — ADVANCED tool (deliberately NOT added to
|
|
1140
|
-
// essentials.ts/ESSENTIAL_TOOLS): reachable ONLY via
|
|
1141
|
-
// yootheme_builder_advanced({tool, arguments}). The INVERSE of
|
|
1142
|
-
// library_save_as. Annotated destructive() — it IRREVERSIBLY removes a
|
|
1143
|
-
// saved AI-Layouts entry (a layout the user deliberately saved via
|
|
1144
|
-
// library_save_as; there is no undo). This matches the codebase's own
|
|
1145
|
-
// "irreversible → destructive → host confirm-gate" precedent:
|
|
1146
|
-
// element_delete + pages_delete are destructive (audit F9), and
|
|
1147
|
-
// clean_implode_directives was re-classified mutating→destructive in
|
|
1148
|
-
// Wave H5 for the same reason (cannot be restored). Deleting saved user
|
|
1149
|
-
// data should fire the host's destructive-approval gate, not slip
|
|
1150
|
-
// through as an ordinary mutating write.
|
|
1151
1400
|
defineTool({
|
|
1152
1401
|
name: 'yootheme_builder_library_delete',
|
|
1153
|
-
description: 'Delete ONE saved entry from your "WootsUp AI Layouts" store (
|
|
1154
|
-
'
|
|
1155
|
-
'
|
|
1156
|
-
'
|
|
1157
|
-
'returns a 403. IRREVERSIBLE — requires confirm:true. Returns `{deleted, id, deleted_count, ' +
|
|
1158
|
-
'source}`; an unknown id returns a 404. Advanced/gateway-only. Operates on the default site ' +
|
|
1159
|
-
'unless site_id is provided.',
|
|
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.',
|
|
1160
1406
|
inputSchema: {
|
|
1161
1407
|
site_id: SITE_ID_SCHEMA,
|
|
1162
1408
|
id: z
|
|
@@ -1170,9 +1416,6 @@ export function buildLibraryTools(pool) {
|
|
|
1170
1416
|
.describe('Which library to delete from. Only "ai" (your OWN "WootsUp AI Layouts") is ' +
|
|
1171
1417
|
'deletable; "catalog" (the curated remote YOOtheme Pro Library) is read-only ' +
|
|
1172
1418
|
'and returns a 403. Omit to default to "ai".'),
|
|
1173
|
-
// Destructive-tool confirm gate (matches element_delete / pages_delete):
|
|
1174
|
-
// z.literal(true) makes the MCP boundary REJECT any call without
|
|
1175
|
-
// confirm:true, so an irreversible delete can never fire by accident.
|
|
1176
1419
|
confirm: z
|
|
1177
1420
|
.literal(true)
|
|
1178
1421
|
.describe('Required — must be `true`. Deleting a saved AI-Layouts entry is IRREVERSIBLE ' +
|
|
@@ -1182,28 +1425,33 @@ export function buildLibraryTools(pool) {
|
|
|
1182
1425
|
annotations: destructive('Delete AI Layouts entry'),
|
|
1183
1426
|
handler: async ({ site_id, ...rest }) => withWriteSiteRetry(pool, site_id, 'yootheme_builder_library_delete', async (client, site) => withSiteMeta(await handleLibraryDelete({ client }, rest), site)),
|
|
1184
1427
|
}),
|
|
1185
|
-
// Task D1 (Image Sourcing) — read-only forward, mirrors library_browse.
|
|
1186
1428
|
defineTool({
|
|
1187
1429
|
name: 'yootheme_builder_image_search',
|
|
1188
|
-
description: 'Search
|
|
1189
|
-
+ '
|
|
1190
|
-
+ '
|
|
1191
|
-
+ '
|
|
1192
|
-
+ '
|
|
1193
|
-
+ '
|
|
1194
|
-
+ '
|
|
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.',
|
|
1195
1439
|
inputSchema: {
|
|
1196
1440
|
site_id: SITE_ID_SCHEMA,
|
|
1197
1441
|
source: z
|
|
1198
1442
|
.enum(IMAGE_SOURCE_VALUES)
|
|
1199
1443
|
.default('unsplash')
|
|
1200
1444
|
.describe('Image source. unsplash/pexels = real photography; proimages = YOOtheme '
|
|
1201
|
-
+ '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`.'),
|
|
1202
1448
|
query: z
|
|
1203
1449
|
.string()
|
|
1204
1450
|
.min(1)
|
|
1451
|
+
.optional()
|
|
1205
1452
|
.describe('Search terms, e.g. "coffee roasting", "modern architecture interior". '
|
|
1206
|
-
+ '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.'),
|
|
1207
1455
|
per_page: z
|
|
1208
1456
|
.number()
|
|
1209
1457
|
.int()
|
|
@@ -1214,40 +1462,59 @@ export function buildLibraryTools(pool) {
|
|
|
1214
1462
|
orientation: z
|
|
1215
1463
|
.enum(IMAGE_ORIENTATION_VALUES)
|
|
1216
1464
|
.optional()
|
|
1217
|
-
.describe('landscape for heroes/banners, portrait for cards.
|
|
1218
|
-
+ '
|
|
1219
|
-
+ '
|
|
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.'),
|
|
1220
1473
|
color: z
|
|
1221
1474
|
.string()
|
|
1222
1475
|
.min(1)
|
|
1223
1476
|
.optional()
|
|
1224
|
-
.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.'),
|
|
1485
|
+
include_meta: INCLUDE_META_SCHEMA,
|
|
1225
1486
|
},
|
|
1226
1487
|
outputSchema: IMAGE_SEARCH_OUTPUT_SCHEMA,
|
|
1227
1488
|
annotations: readOnly('Search Images'),
|
|
1228
1489
|
handler: async ({ site_id, ...rest }) => withSiteRetry(pool, site_id, async (client, site) => withSiteMeta(await handleImageSearch({ client }, rest), site)),
|
|
1229
1490
|
}),
|
|
1230
|
-
// Task D1 (Image Sourcing) — write forward, mirrors library_insert.
|
|
1231
|
-
// mutating() (writes to the Media Library) but NOT destructive() — it
|
|
1232
|
-
// only creates, never deletes (Global Constraints, plan doc).
|
|
1233
1491
|
defineTool({
|
|
1234
1492
|
name: 'yootheme_builder_image_import',
|
|
1235
|
-
description: 'Sideload a photo into the site\'s
|
|
1236
|
-
+ '
|
|
1237
|
-
+ '
|
|
1238
|
-
+ '
|
|
1239
|
-
+ '
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
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.',
|
|
1243
1502
|
inputSchema: {
|
|
1244
1503
|
site_id: SITE_ID_SCHEMA,
|
|
1245
1504
|
url: z
|
|
1246
1505
|
.string()
|
|
1247
1506
|
.url()
|
|
1507
|
+
.optional()
|
|
1248
1508
|
.describe('The image URL to sideload (typically a full_url from '
|
|
1249
1509
|
+ 'yootheme_builder_image_search). Downloads it into the site Media '
|
|
1250
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.'),
|
|
1251
1518
|
},
|
|
1252
1519
|
outputSchema: IMAGE_IMPORT_OUTPUT_SCHEMA,
|
|
1253
1520
|
annotations: mutating('Import Image'),
|